aitable-workflow-cli 0.1.15 → 0.1.16-beta.1786022310508
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +3 -3
- package/templates/support-qa/lib/__tests__/declarative-matcher.test.ts +240 -0
- package/templates/support-qa/lib/__tests__/dingtalk-group-membership.test.ts +127 -0
- package/templates/support-qa/lib/__tests__/memory-events.test.ts +90 -0
- package/templates/support-qa/lib/action-dispatch.ts +1 -1
- package/templates/support-qa/lib/declarative-matcher.ts +398 -0
- package/templates/support-qa/lib/dingtalk-group-membership.ts +151 -0
- package/templates/support-qa/lib/guards.ts +18 -0
- package/templates/support-qa/lib/memory-events.ts +60 -41
- package/templates/support-qa/lib/preflight.ts +294 -0
- package/templates/support-qa/package.json +2 -4
- package/templates/support-qa/profiles/aitable/profile/fastpath/known-answers.yml +1 -1
- package/templates/support-qa/profiles/aitable/profile/troubleshoot/classifiers.yml +1 -1
- package/templates/support-qa/workflows/config-sync/recipes/config_sync.recipe.ts +10 -8
- package/templates/support-qa/workflows/daily-review/recipes/daily_review.recipe.ts +1 -1
- package/templates/support-qa/workflows/group-qa/recipes/answer.recipe.ts +2 -7
- package/templates/support-qa/workflows/group-qa/recipes/conversation_handler.recipe.ts +3 -3
- package/templates/support-qa/workflows/group-qa/recipes/fastpath.recipe.ts +5 -11
- package/templates/support-qa/workflows/group-qa/recipes/on-action.recipe.ts +4 -2
- package/templates/support-qa/workflows/group-qa/recipes/reply-to-group.recipe.ts +13 -7
- package/templates/support-qa/workflows/group-qa/recipes/troubleshoot.recipe.ts +12 -18
- package/templates/support-qa/workspaces/support/AGENTS.md +1 -1
- package/templates/aitable-handoff/.env.example +0 -17
- package/templates/aitable-handoff/aitable-workflow.config.yml +0 -64
- package/templates/aitable-handoff/coding-agents.config.json +0 -17
- package/templates/aitable-handoff/npmrc +0 -1
- package/templates/aitable-handoff/package.json +0 -16
- package/templates/aitable-handoff/roles/config-syncer.role.yml +0 -4
- package/templates/aitable-handoff/roles/daily-review.role.yml +0 -4
- package/templates/aitable-handoff/roles/faq-support.role.yml +0 -9
- package/templates/aitable-handoff/roles/faq-troubleshooter.role.yml +0 -17
- package/templates/aitable-handoff/roles/kb-editor.role.yml +0 -19
- package/templates/aitable-handoff/wiki/faq-support/01-/345/277/253/351/200/237/345/205/245/351/227/250/6/350/212/202/350/257/276/345/270/246/344/275/240/345/277/253/351/200/237/344/270/212/346/211/213AI/350/241/250/346/240/274.md +0 -43
- package/templates/aitable-handoff/wiki/faq-support/01-/345/277/253/351/200/237/345/205/245/351/227/250/AI/350/241/250/346/240/274/350/241/214/346/225/260/350/257/264/346/230/216.md +0 -85
- package/templates/aitable-handoff/wiki/faq-support/01-/345/277/253/351/200/237/345/205/245/351/227/250//344/275/277/347/224/250/345/277/253/346/215/267/351/224/256.md +0 -34
- package/templates/aitable-handoff/wiki/faq-support/01-/345/277/253/351/200/237/345/205/245/351/227/250//345/260/206Excel/346/226/207/344/273/266/345/257/274/345/205/245AI/350/241/250/346/240/274.md +0 -48
- package/templates/aitable-handoff/wiki/faq-support/01-/345/277/253/351/200/237/345/205/245/351/227/250//345/270/270/350/247/201/351/227/256/351/242/230.md +0 -88
- package/templates/aitable-handoff/wiki/faq-support/01-/345/277/253/351/200/237/345/205/245/351/227/250//346/250/241/346/235/277/344/270/255/345/277/203.md +0 -59
- package/templates/aitable-handoff/wiki/faq-support/01-/345/277/253/351/200/237/345/205/245/351/227/250//351/222/211/351/222/211AI/350/241/250/346/240/274/345/270/270/350/247/201/344/270/212/351/231/220.md +0 -181
- package/templates/aitable-handoff/wiki/faq-support/01-/345/277/253/351/200/237/345/205/245/351/227/250//351/222/211/351/222/211AI/350/241/250/346/240/274/345/277/253/351/200/237/344/270/212/346/211/213.md +0 -177
- package/templates/aitable-handoff/wiki/faq-support/02-/345/237/272/347/241/200/346/223/215/344/275/234/AI/350/241/250/346/240/274/347/232/204/345/257/274/345/207/272.md +0 -27
- package/templates/aitable-handoff/wiki/faq-support/02-/345/237/272/347/241/200/346/223/215/344/275/234//344/275/277/347/224/250/345/210/206/347/273/204.md +0 -67
- package/templates/aitable-handoff/wiki/faq-support/02-/345/237/272/347/241/200/346/223/215/344/275/234//344/275/277/347/224/250/345/241/253/350/211/262.md +0 -99
- package/templates/aitable-handoff/wiki/faq-support/02-/345/237/272/347/241/200/346/223/215/344/275/234//344/275/277/347/224/250/346/216/222/345/272/217.md +0 -66
- package/templates/aitable-handoff/wiki/faq-support/02-/345/237/272/347/241/200/346/223/215/344/275/234//344/275/277/347/224/250/347/255/233/351/200/211/345/222/214/347/255/233/351/200/211/346/235/241/344/273/266/347/273/204.md +0 -104
- package/templates/aitable-handoff/wiki/faq-support/02-/345/237/272/347/241/200/346/223/215/344/275/234//346/237/245/347/234/213/345/216/206/345/217/262/350/256/260/345/275/225.md +0 -79
- package/templates/aitable-handoff/wiki/faq-support/02-/345/237/272/347/241/200/346/223/215/344/275/234//347/224/250AI/350/241/250/346/240/274/344/270/200/350/241/250/344/270/200/345/212/251/347/220/206/345/217/221/346/266/210/346/201/257.md +0 -94
- package/templates/aitable-handoff/wiki/faq-support/02-/345/237/272/347/241/200/346/223/215/344/275/234//350/207/252/345/256/232/344/271/211/350/256/260/345/275/225/345/220/215/347/247/260.md +0 -35
- package/templates/aitable-handoff/wiki/faq-support/02-/345/237/272/347/241/200/346/223/215/344/275/234//350/207/252/345/256/232/344/271/211/350/256/260/345/275/225/350/257/246/346/203/205/351/241/265/345/222/214/345/241/253/345/206/231/351/241/265.md +0 -141
- package/templates/aitable-handoff/wiki/faq-support/02-/345/237/272/347/241/200/346/223/215/344/275/234//350/260/203/346/225/264/345/210/227/345/256/275.md +0 -42
- package/templates/aitable-handoff/wiki/faq-support/02-/345/237/272/347/241/200/346/223/215/344/275/234//350/260/203/346/225/264/350/241/214/351/253/230.md +0 -40
- package/templates/aitable-handoff/wiki/faq-support/03-/344/275/277/347/224/250/345/255/227/346/256/265//344/270/272/350/241/250/346/240/274/346/267/273/345/212/240/345/255/227/346/256/265.md +0 -188
- package/templates/aitable-handoff/wiki/faq-support/03-/344/275/277/347/224/250/345/255/227/346/256/265//345/215/225/351/200/211/345/244/232/351/200/211/345/255/227/346/256/265/345/220/214/346/255/245/345/205/266/344/273/226/350/241/250/351/200/211/351/241/271.md +0 -142
- package/templates/aitable-handoff/wiki/faq-support/03-/344/275/277/347/224/250/345/255/227/346/256/265//345/255/227/346/256/265/347/256/241/347/220/206.md +0 -44
- package/templates/aitable-handoff/wiki/faq-support/03-/344/275/277/347/224/250/345/255/227/346/256/265//345/255/227/346/256/265/347/261/273/345/236/213/345/210/227/350/241/250.md +0 -95
- package/templates/aitable-handoff/wiki/faq-support/03-/344/275/277/347/224/250/345/255/227/346/256/265//345/255/227/346/256/265/347/274/226/347/273/204.md +0 -138
- package/templates/aitable-handoff/wiki/faq-support/04-/344/275/277/347/224/250/350/241/250/345/215/225//350/241/250/345/215/225/345/221/250/346/234/237/345/210/206/350/241/250.md +0 -42
- package/templates/aitable-handoff/wiki/faq-support/04-/344/275/277/347/224/250/350/241/250/345/215/225//350/241/250/345/215/225/345/237/272/347/241/200/346/223/215/344/275/234.md +0 -344
- package/templates/aitable-handoff/wiki/faq-support/04-/344/275/277/347/224/250/350/241/250/345/215/225//350/241/250/345/215/225/346/230/216/347/273/206/350/241/250-/344/270/200/346/254/241/345/241/253/345/206/231/345/244/232/346/235/241/346/217/220/344/272/244.md +0 -174
- package/templates/aitable-handoff/wiki/faq-support/04-/344/275/277/347/224/250/350/241/250/345/215/225//350/241/250/345/215/225/351/242/204/345/241/253/351/273/230/350/256/244/345/200/274.md +0 -97
- package/templates/aitable-handoff/wiki/faq-support/05-/345/205/254/345/274/217/345/207/275/346/225/260//344/275/277/347/224/250AI/347/224/237/346/210/220/345/205/254/345/274/217.md +0 -26
- package/templates/aitable-handoff/wiki/faq-support/05-/345/205/254/345/274/217/345/207/275/346/225/260//344/275/277/347/224/250/345/205/254/345/274/217/345/207/275/346/225/260/345/270/270/350/247/201/351/227/256/351/242/230.md +0 -179
- package/templates/aitable-handoff/wiki/faq-support/05-/345/205/254/345/274/217/345/207/275/346/225/260//344/275/277/347/224/250/345/205/254/345/274/217/345/255/227/346/256/265.md +0 -81
- package/templates/aitable-handoff/wiki/faq-support/05-/345/205/254/345/274/217/345/207/275/346/225/260//344/275/277/347/224/250/345/207/275/346/225/260.md +0 -21
- package/templates/aitable-handoff/wiki/faq-support/05-/345/205/254/345/274/217/345/207/275/346/225/260//345/205/254/345/274/217/345/207/275/346/225/260/345/244/247/345/205/250.md +0 -433
- package/templates/aitable-handoff/wiki/faq-support/05-/345/205/254/345/274/217/345/207/275/346/225/260//345/205/254/345/274/217/346/224/257/346/214/201/346/230/276/347/244/272/346/240/274/345/274/217.md +0 -126
- package/templates/aitable-handoff/wiki/faq-support/05-/345/205/254/345/274/217/345/207/275/346/225/260//345/205/254/345/274/217/351/224/231/350/257/257/344/273/243/347/240/201/345/210/227/350/241/250.md +0 -30
- package/templates/aitable-handoff/wiki/faq-support/05-/345/205/254/345/274/217/345/207/275/346/225/260//345/207/275/346/225/260/345/256/236/350/267/265.md +0 -13
- package/templates/aitable-handoff/wiki/faq-support/06-/346/225/260/346/215/256/350/277/236/346/216/245/344/270/255/345/277/203//346/225/260/346/215/256/350/277/236/346/216/245/344/270/255/345/277/203.md +0 -169
- package/templates/aitable-handoff/wiki/faq-support/06-/346/225/260/346/215/256/350/277/236/346/216/245/344/270/255/345/277/203//347/273/204/347/273/207/345/206/205/345/205/261/344/272/253/346/225/260/346/215/256/346/272/220.md +0 -106
- package/templates/aitable-handoff/wiki/faq-support/06-/346/225/260/346/215/256/350/277/236/346/216/245/344/270/255/345/277/203//350/267/250AI/350/241/250/346/240/274/346/225/260/346/215/256/345/220/214/346/255/245.md +0 -176
- package/templates/aitable-handoff/wiki/faq-support/06-/346/225/260/346/215/256/350/277/236/346/216/245/344/270/255/345/277/203//351/222/211/351/222/211AI/345/220/254/350/256/260/346/225/260/346/215/256/345/220/214/346/255/245.md +0 -100
- package/templates/aitable-handoff/wiki/faq-support/06-/346/225/260/346/215/256/350/277/236/346/216/245/344/270/255/345/277/203//351/222/211/351/222/211OA/345/256/241/346/211/271/346/225/260/346/215/256/345/220/214/346/255/245.md +0 -194
- package/templates/aitable-handoff/wiki/faq-support/06-/346/225/260/346/215/256/350/277/236/346/216/245/344/270/255/345/277/203//351/222/211/351/222/211/345/276/205/345/212/236/346/225/260/346/215/256/345/220/214/346/255/245.md +0 -87
- package/templates/aitable-handoff/wiki/faq-support/06-/346/225/260/346/215/256/350/277/236/346/216/245/344/270/255/345/277/203//351/222/211/351/222/211/346/227/245/347/250/213/346/225/260/346/215/256/345/220/214/346/255/245.md +0 -92
- package/templates/aitable-handoff/wiki/faq-support/06-/346/225/260/346/215/256/350/277/236/346/216/245/344/270/255/345/277/203//351/222/211/351/222/211/350/200/203/345/213/244/346/211/223/345/215/241/346/225/260/346/215/256/345/220/214/346/255/245.md +0 -129
- package/templates/aitable-handoff/wiki/faq-support/06-/346/225/260/346/215/256/350/277/236/346/216/245/344/270/255/345/277/203//351/222/211/351/222/211/350/241/250/346/240/274/346/225/260/346/215/256/345/220/214/346/255/245.md +0 -229
- package/templates/aitable-handoff/wiki/faq-support/06-/346/225/260/346/215/256/350/277/236/346/216/245/344/270/255/345/277/203//351/222/211/351/222/211/351/200/232/350/256/257/345/275/225/346/225/260/346/215/256/345/220/214/346/255/245.md +0 -120
- package/templates/aitable-handoff/wiki/faq-support/07-/344/275/277/347/224/250/350/247/206/345/233/276//344/275/277/347/224/250AI/350/241/250/346/240/274/350/247/206/345/233/276.md +0 -219
- package/templates/aitable-handoff/wiki/faq-support/07-/344/275/277/347/224/250/350/247/206/345/233/276//345/210/206/344/272/253/350/247/206/345/233/276.md +0 -71
- package/templates/aitable-handoff/wiki/faq-support/07-/344/275/277/347/224/250/350/247/206/345/233/276//346/225/260/346/215/256/350/241/250/345/222/214/350/247/206/345/233/276/347/232/204/345/205/263/347/263/273.md +0 -48
- package/templates/aitable-handoff/wiki/faq-support/07-/344/275/277/347/224/250/350/247/206/345/233/276//346/227/245/345/216/206/350/247/206/345/233/276.md +0 -89
- package/templates/aitable-handoff/wiki/faq-support/07-/344/275/277/347/224/250/350/247/206/345/233/276//346/237/245/350/257/242/351/241/265/351/235/242.md +0 -116
- package/templates/aitable-handoff/wiki/faq-support/07-/344/275/277/347/224/250/350/247/206/345/233/276//347/224/230/347/211/271/345/233/276.md +0 -135
- package/templates/aitable-handoff/wiki/faq-support/07-/344/275/277/347/224/250/350/247/206/345/233/276//347/224/273/345/206/214/350/247/206/345/233/276.md +0 -28
- package/templates/aitable-handoff/wiki/faq-support/07-/344/275/277/347/224/250/350/247/206/345/233/276//347/234/213/346/235/277/350/247/206/345/233/276.md +0 -35
- package/templates/aitable-handoff/wiki/faq-support/07-/344/275/277/347/224/250/350/247/206/345/233/276//350/241/250/346/240/274/350/247/206/345/233/276.md +0 -116
- package/templates/aitable-handoff/wiki/faq-support/07-/344/275/277/347/224/250/350/247/206/345/233/276//351/200/217/350/247/206/350/241/250/350/247/206/345/233/276.md +0 -123
- package/templates/aitable-handoff/wiki/faq-support/08-/344/275/277/347/224/250/344/273/252/350/241/250/347/233/230/TOP_N/345/210/206/346/236/220.md +0 -78
- package/templates/aitable-handoff/wiki/faq-support/08-/344/275/277/347/224/250/344/273/252/350/241/250/347/233/230//344/273/252/350/241/250/347/233/230/344/270/255/346/267/273/345/212/240/345/244/232/344/270/252/346/225/260/346/215/256/346/272/220.md +0 -129
- package/templates/aitable-handoff/wiki/faq-support/08-/344/275/277/347/224/250/344/273/252/350/241/250/347/233/230//344/273/252/350/241/250/347/233/230/345/215/225/345/233/276/350/241/250/345/222/214AI/350/247/243/350/257/273/345/206/205/345/256/271/345/210/206/344/272/253.md +0 -104
- package/templates/aitable-handoff/wiki/faq-support/08-/344/275/277/347/224/250/344/273/252/350/241/250/347/233/230//344/273/252/350/241/250/347/233/230/345/270/270/350/247/201/351/227/256/351/242/230.md +0 -31
- package/templates/aitable-handoff/wiki/faq-support/08-/344/275/277/347/224/250/344/273/252/350/241/250/347/233/230//344/273/252/350/241/250/347/233/230/346/231/272/350/203/275/346/200/273/347/273/223/345/210/206/346/236/220.md +0 -57
- package/templates/aitable-handoff/wiki/faq-support/08-/344/275/277/347/224/250/344/273/252/350/241/250/347/233/230//344/273/252/350/241/250/347/233/230/347/273/204/344/273/266.md +0 -47
- package/templates/aitable-handoff/wiki/faq-support/08-/344/275/277/347/224/250/344/273/252/350/241/250/347/233/230//344/275/277/347/224/250AI/350/241/250/346/240/274/344/273/252/350/241/250/347/233/230.md +0 -226
- package/templates/aitable-handoff/wiki/faq-support/08-/344/275/277/347/224/250/344/273/252/350/241/250/347/233/230//344/275/277/347/224/250/350/207/252/345/212/250/345/214/226/346/265/201/347/250/213/345/217/221/351/200/201/344/273/252/350/241/250/347/233/230.md +0 -49
- package/templates/aitable-handoff/wiki/faq-support/08-/344/275/277/347/224/250/344/273/252/350/241/250/347/233/230//345/210/206/344/272/253/344/273/252/350/241/250/347/233/230.md +0 -74
- package/templates/aitable-handoff/wiki/faq-support/08-/344/275/277/347/224/250/344/273/252/350/241/250/347/233/230//345/234/250/344/273/252/350/241/250/347/233/230/344/270/255/347/255/233/351/200/211/346/225/260/346/215/256.md +0 -65
- package/templates/aitable-handoff/wiki/faq-support/08-/344/275/277/347/224/250/344/273/252/350/241/250/347/233/230//350/207/252/345/256/232/344/271/211/344/273/252/350/241/250/347/233/230/344/270/255/345/277/203.md +0 -93
- package/templates/aitable-handoff/wiki/faq-support/08-/344/275/277/347/224/250/344/273/252/350/241/250/347/233/230//350/267/237/351/232/217/350/256/277/351/227/256/350/200/205/346/235/203/351/231/220/347/273/237/350/256/241.md +0 -48
- package/templates/aitable-handoff/wiki/faq-support/09-/345/272/224/347/224/250/346/250/241/345/274/217//345/205/250/346/226/260/345/272/224/347/224/250/346/250/241/345/274/217.md +0 -155
- package/templates/aitable-handoff/wiki/faq-support/09-/345/272/224/347/224/250/346/250/241/345/274/217//345/210/227/350/241/250/347/273/204/344/273/266.md +0 -228
- package/templates/aitable-handoff/wiki/faq-support/09-/345/272/224/347/224/250/346/250/241/345/274/217//345/233/276/347/211/207.md +0 -93
- package/templates/aitable-handoff/wiki/faq-support/09-/345/272/224/347/224/250/346/250/241/345/274/217//346/214/211/351/222/256.md +0 -86
- package/templates/aitable-handoff/wiki/faq-support/09-/345/272/224/347/224/250/346/250/241/345/274/217//346/240/207/347/255/276/351/241/265.md +0 -97
- package/templates/aitable-handoff/wiki/faq-support/09-/345/272/224/347/224/250/346/250/241/345/274/217//350/247/206/345/233/276/347/273/204/344/273/266.md +0 -125
- package/templates/aitable-handoff/wiki/faq-support/09-/345/272/224/347/224/250/346/250/241/345/274/217//350/277/207/346/273/244/345/231/250.md +0 -110
- package/templates/aitable-handoff/wiki/faq-support/09-/345/272/224/347/224/250/346/250/241/345/274/217//351/200/217/350/247/206/350/241/250/347/273/204/344/273/266.md +0 -142
- package/templates/aitable-handoff/wiki/faq-support/10-/350/207/252/345/212/250/345/214/226/345/267/245/344/275/234/346/265/201/Webhook/345/217/202/346/225/260/347/232/204/350/257/246/347/273/206/350/257/264/346/230/216.md +0 -76
- package/templates/aitable-handoff/wiki/faq-support/10-/350/207/252/345/212/250/345/214/226/345/267/245/344/275/234/346/265/201//344/270/200/350/241/250/344/270/200/345/217/267Agent/346/224/257/346/214/201/347/276/244/346/224/266/351/233/206.md +0 -150
- package/templates/aitable-handoff/wiki/faq-support/10-/350/207/252/345/212/250/345/214/226/345/267/245/344/275/234/346/265/201//344/275/277/347/224/250webhook/350/247/246/345/217/221/350/207/252/345/212/250/345/214/226.md +0 -163
- package/templates/aitable-handoff/wiki/faq-support/10-/350/207/252/345/212/250/345/214/226/345/267/245/344/275/234/346/265/201//344/275/277/347/224/250/344/270/215/345/220/214/350/272/253/344/273/275/345/217/221/351/200/201/351/222/211/351/222/211/346/266/210/346/201/257.md +0 -60
- package/templates/aitable-handoff/wiki/faq-support/10-/350/207/252/345/212/250/345/214/226/345/267/245/344/275/234/346/265/201//344/275/277/347/224/250/350/207/252/345/212/250/345/214/226/345/267/245/344/275/234/346/265/201.md +0 -219
- package/templates/aitable-handoff/wiki/faq-support/10-/350/207/252/345/212/250/345/214/226/345/267/245/344/275/234/346/265/201//344/275/277/347/224/250/350/207/252/345/212/250/345/214/226/345/276/252/347/216/257/350/212/202/347/202/271.md +0 -63
- package/templates/aitable-handoff/wiki/faq-support/10-/350/207/252/345/212/250/345/214/226/345/267/245/344/275/234/346/265/201//345/234/250/346/265/201/347/250/213/344/270/255/344/275/277/347/224/250AI/351/227/256/347/255/224.md +0 -69
- package/templates/aitable-handoff/wiki/faq-support/10-/350/207/252/345/212/250/345/214/226/345/267/245/344/275/234/346/265/201//345/234/250/346/266/210/346/201/257/345/215/241/347/211/207/344/270/212/346/267/273/345/212/240/346/214/211/351/222/256.md +0 -93
- package/templates/aitable-handoff/wiki/faq-support/10-/350/207/252/345/212/250/345/214/226/345/267/245/344/275/234/346/265/201//346/265/201/347/250/213/345/217/230/351/207/217/350/247/243/351/207/212/344/270/216/345/270/270/350/247/201/346/212/245/351/224/231.md +0 -158
- package/templates/aitable-handoff/wiki/faq-support/10-/350/207/252/345/212/250/345/214/226/345/267/245/344/275/234/346/265/201//346/265/201/347/250/213/350/247/246/345/217/221/346/235/241/344/273/266/344/270/216/346/211/247/350/241/214/345/212/250/344/275/234/344/270/200/350/247/210.md +0 -310
- package/templates/aitable-handoff/wiki/faq-support/10-/350/207/252/345/212/250/345/214/226/345/267/245/344/275/234/346/265/201//347/224/250AI/350/241/250/346/240/274/344/270/200/350/241/250/344/270/200/345/212/251/347/220/206/345/217/221/346/266/210/346/201/257-/350/207/252/345/212/250/345/214/226.md +0 -129
- package/templates/aitable-handoff/wiki/faq-support/10-/350/207/252/345/212/250/345/214/226/345/267/245/344/275/234/346/265/201//347/224/250/346/237/245/346/211/276/350/256/260/345/275/225/345/217/221/351/200/201/347/273/204/345/220/210/345/255/227/346/256/265/351/200/232/347/237/245.md +0 -66
- package/templates/aitable-handoff/wiki/faq-support/10-/350/207/252/345/212/250/345/214/226/345/267/245/344/275/234/346/265/201//350/207/252/345/212/250/345/214/226/345/210/233/345/273/272/351/222/211/351/222/211/345/276/205/345/212/236/346/210/226/346/227/245/347/250/213.md +0 -84
- package/templates/aitable-handoff/wiki/faq-support/10-/350/207/252/345/212/250/345/214/226/345/267/245/344/275/234/346/265/201//350/207/252/345/212/250/345/214/226/345/217/221/350/265/267AI/347/224/265/350/257/235/345/244/226/345/221/274.md +0 -322
- package/templates/aitable-handoff/wiki/faq-support/10-/350/207/252/345/212/250/345/214/226/345/267/245/344/275/234/346/265/201//350/207/252/345/212/250/345/214/226/345/217/221/350/265/267OA/345/256/241/346/211/271.md +0 -93
- package/templates/aitable-handoff/wiki/faq-support/10-/350/207/252/345/212/250/345/214/226/345/267/245/344/275/234/346/265/201//350/207/252/345/212/250/345/214/226/345/217/221/351/200/201HTTP/350/257/267/346/261/202.md +0 -168
- package/templates/aitable-handoff/wiki/faq-support/10-/350/207/252/345/212/250/345/214/226/345/267/245/344/275/234/346/265/201//350/207/252/345/212/250/345/214/226/345/270/270/350/247/201/351/227/256/351/242/230.md +0 -83
- package/templates/aitable-handoff/wiki/faq-support/10-/350/207/252/345/212/250/345/214/226/345/267/245/344/275/234/346/265/201//350/207/252/345/212/250/345/214/226/346/211/271/351/207/217/345/217/221/351/202/256/344/273/266.md +0 -79
- package/templates/aitable-handoff/wiki/faq-support/10-/350/207/252/345/212/250/345/214/226/345/267/245/344/275/234/346/265/201//350/207/252/345/212/250/345/214/226/346/224/257/346/214/201/346/217/222/345/205/245/345/223/252/344/272/233/345/217/230/351/207/217/345/206/205/345/256/271.md +0 -80
- package/templates/aitable-handoff/wiki/faq-support/10-/350/207/252/345/212/250/345/214/226/345/267/245/344/275/234/346/265/201//350/207/252/345/212/250/345/214/226/346/233/264/345/244/232/346/211/247/350/241/214/345/212/250/344/275/234/344/275/277/347/224/250/346/214/207/345/215/227.md +0 -21
- package/templates/aitable-handoff/wiki/faq-support/10-/350/207/252/345/212/250/345/214/226/345/267/245/344/275/234/346/265/201//350/260/203/347/224/250/344/270/211/346/226/271/345/267/245/344/275/234/346/265/201/345/272/224/347/224/250.md +0 -161
- package/templates/aitable-handoff/wiki/faq-support/11-AI/350/241/250/346/240/274/345/212/251/347/220/206/AI/350/241/250/346/240/274/344/270/207/350/203/275/350/264/264/344/274/230/345/205/210/344/275/277/347/224/250/347/224/263/350/257/267.md +0 -67
- package/templates/aitable-handoff/wiki/faq-support/11-AI/350/241/250/346/240/274/345/212/251/347/220/206/AI/350/241/250/346/240/274/344/270/207/350/203/275/350/264/264/344/275/277/347/224/250/350/257/264/346/230/216.md +0 -63
- package/templates/aitable-handoff/wiki/faq-support/11-AI/350/241/250/346/240/274/345/212/251/347/220/206/AI/350/241/250/346/240/274/345/212/251/347/220/206.md +0 -107
- package/templates/aitable-handoff/wiki/faq-support/12-/351/253/230/347/272/247/346/235/203/351/231/220//344/275/277/347/224/250AI/350/241/250/346/240/274/351/253/230/347/272/247/346/235/203/351/231/220.md +0 -194
- package/templates/aitable-handoff/wiki/faq-support/12-/351/253/230/347/272/247/346/235/203/351/231/220//350/241/214/345/210/227/346/235/203/351/231/220/350/256/276/347/275/256/345/217/212/351/253/230/347/272/247/346/235/203/351/231/220/345/257/271/350/241/250/345/215/225/345/205/254/345/274/217/347/232/204/345/275/261/345/223/215.md +0 -32
- package/templates/aitable-handoff/wiki/faq-support/12-/351/253/230/347/272/247/346/235/203/351/231/220//351/242/204/350/247/210/351/253/230/347/272/247/346/235/203/351/231/220/347/273/223/346/236/234.md +0 -43
- package/templates/aitable-handoff/wiki/faq-support/12-/351/253/230/347/272/247/346/235/203/351/231/220//351/253/230/347/272/247/346/235/203/351/231/220/345/270/270/350/247/201/351/227/256/351/242/230.md +0 -47
- package/templates/aitable-handoff/wiki/faq-support/12-/351/253/230/347/272/247/346/235/203/351/231/220//351/253/230/347/272/247/346/235/203/351/231/220/347/232/204/351/273/230/350/256/244/350/247/222/350/211/262.md +0 -36
- package/templates/aitable-handoff/wiki/faq-support/13-/346/217/222/344/273/266/344/270/255/345/277/203/AI/350/241/250/346/240/274/344/270/211/346/226/271/346/217/222/344/273/266/344/275/277/347/224/250/346/216/210/346/235/203.md +0 -19
- package/templates/aitable-handoff/wiki/faq-support/13-/346/217/222/344/273/266/344/270/255/345/277/203/AI/350/241/250/346/240/274/346/217/222/344/273/266-/350/204/232/346/234/254/346/217/222/344/273/266.md +0 -42
- package/templates/aitable-handoff/wiki/faq-support/13-/346/217/222/344/273/266/344/270/255/345/277/203/AI/350/241/250/346/240/274/346/217/222/344/273/266-/350/207/252/345/256/232/344/271/211/346/217/222/344/273/266.md +0 -51
- package/templates/aitable-handoff/wiki/faq-support/13-/346/217/222/344/273/266/344/270/255/345/277/203/AI/350/241/250/346/240/274/346/217/222/344/273/266/344/270/255/345/277/203-/344/275/277/347/224/250/346/214/207/345/215/227.md +0 -97
- package/templates/aitable-handoff/wiki/faq-support/14-/347/256/241/347/220/206/345/220/216/345/217/260//344/275/277/347/224/250/346/225/260/346/215/256/350/257/264/346/230/216.md +0 -82
- package/templates/aitable-handoff/wiki/faq-support/14-/347/256/241/347/220/206/345/220/216/345/217/260//346/235/203/347/233/212/346/225/260/346/215/256/350/257/264/346/230/216.md +0 -111
- package/templates/aitable-handoff/wiki/faq-support/15-/345/234/272/346/231/257/345/256/236/350/267/265/AI/350/241/250/346/240/274/350/257/257/345/210/240/346/201/242/345/244/215/346/214/207/345/215/227.md +0 -37
- package/templates/aitable-handoff/wiki/faq-support/15-/345/234/272/346/231/257/345/256/236/350/267/265//345/234/250/350/241/250/346/240/274/344/270/255/346/240/207/350/256/260/351/207/215/345/244/215/345/206/205/345/256/271.md +0 -43
- package/templates/aitable-handoff/wiki/faq-support/15-/345/234/272/346/231/257/345/256/236/350/267/265//347/233/264/346/222/255/345/205/254/345/274/200/347/263/273/345/210/227/350/257/276.md +0 -21
- package/templates/aitable-handoff/wiki/faq-support/16-/344/273/230/350/264/271/346/235/203/347/233/212//351/222/211/351/222/211AI/350/241/250/346/240/274/344/273/230/350/264/271/346/235/203/347/233/212.md +0 -144
- package/templates/aitable-handoff/wiki/faq-support/16-/344/273/230/350/264/271/346/235/203/347/233/212//351/222/211/351/222/211AI/350/241/250/346/240/274/346/220/255/345/273/272/346/234/215/345/212/241.md +0 -95
- package/templates/aitable-handoff/wiki/faq-support/17-/345/205/254/345/221/212/AI/350/241/250/346/240/274-/346/216/222/347/211/210/346/211/223/345/215/260/345/215/207/347/272/247/350/257/264/346/230/216.md +0 -40
- package/templates/aitable-handoff/wiki/faq-support/17-/345/205/254/345/221/212/AI/350/241/250/346/240/274-/347/211/210/346/234/254/345/215/207/347/272/247/351/200/232/347/237/245.md +0 -21
- package/templates/aitable-handoff/wiki/faq-support/17-/345/205/254/345/221/212//350/267/250AI/350/241/250/346/240/274/346/225/260/346/215/256/345/220/214/346/255/245/350/260/203/346/225/264/350/257/264/346/230/216.md +0 -55
- package/templates/aitable-handoff/wiki/faq-support/18-/345/274/200/345/217/221/350/200/205/346/214/207/345/215/227/AI /350/241/250/346/240/274/346/217/222/344/273/266 - /344/274/201/344/270/232/345/206/205/346/217/222/344/273/266/345/217/221/345/270/203/346/214/207/345/215/227.md" +0 -159
- package/templates/aitable-handoff/wiki/faq-support/18-/345/274/200/345/217/221/350/200/205/346/214/207/345/215/227/AI/350/241/250/346/240/274 AI /345/255/227/346/256/265 /345/274/200/345/217/221/346/214/207/345/215/227/AI/350/241/250/346/240/274 AI /345/255/227/346/256/265/345/274/200/345/217/221/346/214/207/345/215/227/357/274/210FaaS/347/211/210/357/274/211.md" +0 -1362
- package/templates/aitable-handoff/wiki/faq-support/18-/345/274/200/345/217/221/350/200/205/346/214/207/345/215/227/AI/350/241/250/346/240/274 AI /345/255/227/346/256/265 /345/274/200/345/217/221/346/214/207/345/215/227/AI/350/241/250/346/240/274 AI /345/255/227/346/256/265/345/274/200/345/217/221/346/214/207/345/215/227/357/274/210/345/205/254/345/274/217/347/211/210/357/274/211.md" +0 -556
- package/templates/aitable-handoff/wiki/faq-support/18-/345/274/200/345/217/221/350/200/205/346/214/207/345/215/227/AI/350/241/250/346/240/274 AI /345/255/227/346/256/265 /345/274/200/345/217/221/346/214/207/345/215/227.md" +0 -15
- package/templates/aitable-handoff/wiki/faq-support/18-/345/274/200/345/217/221/350/200/205/346/214/207/345/215/227/AI/350/241/250/346/240/274/345/255/227/346/256/265/346/250/241/347/211/210 /344/275/277/347/224/250/346/214/207/345/215/227/AI /350/241/250/346/240/274 AI /345/255/227/346/256/265/345/270/270/350/247/201/346/212/245/351/224/231/350/257/264/346/230/216.md" +0 -649
- package/templates/aitable-handoff/wiki/faq-support/18-/345/274/200/345/217/221/350/200/205/346/214/207/345/215/227/AI/350/241/250/346/240/274/345/255/227/346/256/265/346/250/241/347/211/210 /344/275/277/347/224/250/346/214/207/345/215/227/Dify/346/231/272/350/203/275/344/275/223.md" +0 -55
- package/templates/aitable-handoff/wiki/faq-support/18-/345/274/200/345/217/221/350/200/205/346/214/207/345/215/227/AI/350/241/250/346/240/274/345/255/227/346/256/265/346/250/241/347/211/210 /344/275/277/347/224/250/346/214/207/345/215/227/GPTPro /345/255/227/346/256/265/346/215/267/345/276/204 /345/257/206/351/222/245/350/216/267/345/217/226/346/214/207/345/215/227.md" +0 -23
- package/templates/aitable-handoff/wiki/faq-support/18-/345/274/200/345/217/221/350/200/205/346/214/207/345/215/227/AI/350/241/250/346/240/274/345/255/227/346/256/265/346/250/241/347/211/210 /344/275/277/347/224/250/346/214/207/345/215/227/OpenClaw /345/260/217/351/276/231/350/231/276/345/212/251/346/211/213/351/205/215/347/275/256/346/225/231/347/250/213.md" +0 -27
- package/templates/aitable-handoff/wiki/faq-support/18-/345/274/200/345/217/221/350/200/205/346/214/207/345/215/227/AI/350/241/250/346/240/274/345/255/227/346/256/265/346/250/241/347/211/210 /344/275/277/347/224/250/346/214/207/345/215/227//345/233/275/345/206/205/345/244/226/345/277/253/351/200/222/347/211/251/346/265/201/344/277/241/346/201/257/346/237/245/350/257/242/344/275/277/347/224/250/346/214/207/345/215/227.md" +0 -907
- package/templates/aitable-handoff/wiki/faq-support/18-/345/274/200/345/217/221/350/200/205/346/214/207/345/215/227/AI/350/241/250/346/240/274/345/255/227/346/256/265/346/250/241/347/211/210 /344/275/277/347/224/250/346/214/207/345/215/227//345/277/253/351/200/222/347/211/251/346/265/201/346/237/245/350/257/242/345/212/251/346/211/213/344/275/277/347/224/250/346/214/207/345/215/227.md" +0 -1
- package/templates/aitable-handoff/wiki/faq-support/18-/345/274/200/345/217/221/350/200/205/346/214/207/345/215/227/AI/350/241/250/346/240/274/345/257/271/350/261/241/346/250/241/345/236/213API/345/217/202/350/200/203/346/226/207/346/241/243/AI/350/241/250/346/240/274JSAPI /347/261/273/345/236/213/346/217/217/350/277/260.xlsx +0 -0
- package/templates/aitable-handoff/wiki/faq-support/18-/345/274/200/345/217/221/350/200/205/346/214/207/345/215/227/AI/350/241/250/346/240/274/345/257/271/350/261/241/346/250/241/345/236/213API/345/217/202/350/200/203/346/226/207/346/241/243/Base /346/250/241/345/235/227.md" +0 -391
- package/templates/aitable-handoff/wiki/faq-support/18-/345/274/200/345/217/221/350/200/205/346/214/207/345/215/227/AI/350/241/250/346/240/274/345/257/271/350/261/241/346/250/241/345/236/213API/345/217/202/350/200/203/346/226/207/346/241/243/Field /346/250/241/345/235/227/AttachmentField /351/231/204/344/273/266/345/255/227/346/256/265.md" +0 -200
- package/templates/aitable-handoff/wiki/faq-support/18-/345/274/200/345/217/221/350/200/205/346/214/207/345/215/227/AI/350/241/250/346/240/274/345/257/271/350/261/241/346/250/241/345/236/213API/345/217/202/350/200/203/346/226/207/346/241/243/Field /346/250/241/345/235/227/AutoNumberField /350/207/252/345/212/250/347/274/226/345/217/267/345/255/227/346/256/265.md" +0 -75
- package/templates/aitable-handoff/wiki/faq-support/18-/345/274/200/345/217/221/350/200/205/346/214/207/345/215/227/AI/350/241/250/346/240/274/345/257/271/350/261/241/346/250/241/345/236/213API/345/217/202/350/200/203/346/226/207/346/241/243/Field /346/250/241/345/235/227/BarcodeField /346/235/241/347/240/201/345/255/227/346/256/265.md" +0 -95
- package/templates/aitable-handoff/wiki/faq-support/18-/345/274/200/345/217/221/350/200/205/346/214/207/345/215/227/AI/350/241/250/346/240/274/345/257/271/350/261/241/346/250/241/345/236/213API/345/217/202/350/200/203/346/226/207/346/241/243/Field /346/250/241/345/235/227/BidirectionalLinkField /345/217/214/345/220/221/345/205/263/350/201/224/345/255/227/346/256/265.md" +0 -294
- package/templates/aitable-handoff/wiki/faq-support/18-/345/274/200/345/217/221/350/200/205/346/214/207/345/215/227/AI/350/241/250/346/240/274/345/257/271/350/261/241/346/250/241/345/236/213API/345/217/202/350/200/203/346/226/207/346/241/243/Field /346/250/241/345/235/227/CheckboxField /345/244/215/351/200/211/346/241/206/345/255/227/346/256/265.md" +0 -95
- package/templates/aitable-handoff/wiki/faq-support/18-/345/274/200/345/217/221/350/200/205/346/214/207/345/215/227/AI/350/241/250/346/240/274/345/257/271/350/261/241/346/250/241/345/236/213API/345/217/202/350/200/203/346/226/207/346/241/243/Field /346/250/241/345/235/227/CreatedTimeField /345/210/233/345/273/272/346/227/266/351/227/264/345/255/227/346/256/265.md" +0 -134
- package/templates/aitable-handoff/wiki/faq-support/18-/345/274/200/345/217/221/350/200/205/346/214/207/345/215/227/AI/350/241/250/346/240/274/345/257/271/350/261/241/346/250/241/345/236/213API/345/217/202/350/200/203/346/226/207/346/241/243/Field /346/250/241/345/235/227/CurrencyField /350/264/247/345/270/201/345/255/227/346/256/265.md" +0 -260
- package/templates/aitable-handoff/wiki/faq-support/18-/345/274/200/345/217/221/350/200/205/346/214/207/345/215/227/AI/350/241/250/346/240/274/345/257/271/350/261/241/346/250/241/345/236/213API/345/217/202/350/200/203/346/226/207/346/241/243/Field /346/250/241/345/235/227/DateField /346/227/245/346/234/237/345/255/227/346/256/265.md" +0 -183
- package/templates/aitable-handoff/wiki/faq-support/18-/345/274/200/345/217/221/350/200/205/346/214/207/345/215/227/AI/350/241/250/346/240/274/345/257/271/350/261/241/346/250/241/345/236/213API/345/217/202/350/200/203/346/226/207/346/241/243/Field /346/250/241/345/235/227/DepartmentField /351/203/250/351/227/250/345/255/227/346/256/265.md" +0 -111
- package/templates/aitable-handoff/wiki/faq-support/18-/345/274/200/345/217/221/350/200/205/346/214/207/345/215/227/AI/350/241/250/346/240/274/345/257/271/350/261/241/346/250/241/345/236/213API/345/217/202/350/200/203/346/226/207/346/241/243/Field /346/250/241/345/235/227/EmailField /351/202/256/347/256/261/345/255/227/346/256/265.md" +0 -99
- package/templates/aitable-handoff/wiki/faq-support/18-/345/274/200/345/217/221/350/200/205/346/214/207/345/215/227/AI/350/241/250/346/240/274/345/257/271/350/261/241/346/250/241/345/236/213API/345/217/202/350/200/203/346/226/207/346/241/243/Field /346/250/241/345/235/227/FilterUpField /346/237/245/346/211/276/345/274/225/347/224/250/345/255/227/346/256/265.md" +0 -73
- package/templates/aitable-handoff/wiki/faq-support/18-/345/274/200/345/217/221/350/200/205/346/214/207/345/215/227/AI/350/241/250/346/240/274/345/257/271/350/261/241/346/250/241/345/236/213API/345/217/202/350/200/203/346/226/207/346/241/243/Field /346/250/241/345/235/227/FormulaField /345/205/254/345/274/217/345/255/227/346/256/265.md" +0 -109
- package/templates/aitable-handoff/wiki/faq-support/18-/345/274/200/345/217/221/350/200/205/346/214/207/345/215/227/AI/350/241/250/346/240/274/345/257/271/350/261/241/346/250/241/345/236/213API/345/217/202/350/200/203/346/226/207/346/241/243/Field /346/250/241/345/235/227/GeoLocationField /345/234/260/347/220/206/344/275/215/347/275/256/345/255/227/346/256/265.md" +0 -33
- package/templates/aitable-handoff/wiki/faq-support/18-/345/274/200/345/217/221/350/200/205/346/214/207/345/215/227/AI/350/241/250/346/240/274/345/257/271/350/261/241/346/250/241/345/236/213API/345/217/202/350/200/203/346/226/207/346/241/243/Field /346/250/241/345/235/227/GroupField /347/276/244/347/273/204/345/255/227/346/256/265.md" +0 -111
- package/templates/aitable-handoff/wiki/faq-support/18-/345/274/200/345/217/221/350/200/205/346/214/207/345/215/227/AI/350/241/250/346/240/274/345/257/271/350/261/241/346/250/241/345/236/213API/345/217/202/350/200/203/346/226/207/346/241/243/Field /346/250/241/345/235/227/LastModifiedTimeField /346/234/200/345/220/216/346/233/264/346/226/260/346/227/266/351/227/264/345/255/227/346/256/265.md" +0 -134
- package/templates/aitable-handoff/wiki/faq-support/18-/345/274/200/345/217/221/350/200/205/346/214/207/345/215/227/AI/350/241/250/346/240/274/345/257/271/350/261/241/346/250/241/345/236/213API/345/217/202/350/200/203/346/226/207/346/241/243/Field /346/250/241/345/235/227/LookupField /345/205/263/350/201/224/345/274/225/347/224/250/345/255/227/346/256/265.md" +0 -71
- package/templates/aitable-handoff/wiki/faq-support/18-/345/274/200/345/217/221/350/200/205/346/214/207/345/215/227/AI/350/241/250/346/240/274/345/257/271/350/261/241/346/250/241/345/236/213API/345/217/202/350/200/203/346/226/207/346/241/243/Field /346/250/241/345/235/227/MultiSelectField /345/244/232/351/200/211/345/255/227/346/256/265.md" +0 -349
- package/templates/aitable-handoff/wiki/faq-support/18-/345/274/200/345/217/221/350/200/205/346/214/207/345/215/227/AI/350/241/250/346/240/274/345/257/271/350/261/241/346/250/241/345/236/213API/345/217/202/350/200/203/346/226/207/346/241/243/Field /346/250/241/345/235/227/NumberField /346/225/260/345/255/227/345/255/227/346/256/265.md" +0 -175
- package/templates/aitable-handoff/wiki/faq-support/18-/345/274/200/345/217/221/350/200/205/346/214/207/345/215/227/AI/350/241/250/346/240/274/345/257/271/350/261/241/346/250/241/345/236/213API/345/217/202/350/200/203/346/226/207/346/241/243/Field /346/250/241/345/235/227/PrimaryField /344/270/273/351/224/256/346/226/207/346/241/243/345/255/227/346/256/265.md" +0 -91
- package/templates/aitable-handoff/wiki/faq-support/18-/345/274/200/345/217/221/350/200/205/346/214/207/345/215/227/AI/350/241/250/346/240/274/345/257/271/350/261/241/346/250/241/345/236/213API/345/217/202/350/200/203/346/226/207/346/241/243/Field /346/250/241/345/235/227/ProgressField /350/277/233/345/272/246/345/255/227/346/256/265.md" +0 -324
- package/templates/aitable-handoff/wiki/faq-support/18-/345/274/200/345/217/221/350/200/205/346/214/207/345/215/227/AI/350/241/250/346/240/274/345/257/271/350/261/241/346/250/241/345/236/213API/345/217/202/350/200/203/346/226/207/346/241/243/Field /346/250/241/345/235/227/RatingField /350/257/204/345/210/206/345/255/227/346/256/265.md" +0 -320
- package/templates/aitable-handoff/wiki/faq-support/18-/345/274/200/345/217/221/350/200/205/346/214/207/345/215/227/AI/350/241/250/346/240/274/345/257/271/350/261/241/346/250/241/345/236/213API/345/217/202/350/200/203/346/226/207/346/241/243/Field /346/250/241/345/235/227/RichTextField /345/257/214/346/226/207/346/234/254/345/255/227/346/256/265.md" +0 -45
- package/templates/aitable-handoff/wiki/faq-support/18-/345/274/200/345/217/221/350/200/205/346/214/207/345/215/227/AI/350/241/250/346/240/274/345/257/271/350/261/241/346/250/241/345/236/213API/345/217/202/350/200/203/346/226/207/346/241/243/Field /346/250/241/345/235/227/SingleSelectField /345/215/225/351/200/211/345/255/227/346/256/265.md" +0 -336
- package/templates/aitable-handoff/wiki/faq-support/18-/345/274/200/345/217/221/350/200/205/346/214/207/345/215/227/AI/350/241/250/346/240/274/345/257/271/350/261/241/346/250/241/345/236/213API/345/217/202/350/200/203/346/226/207/346/241/243/Field /346/250/241/345/235/227/TelephoneField /347/224/265/350/257/235/345/255/227/346/256/265.md" +0 -99
- package/templates/aitable-handoff/wiki/faq-support/18-/345/274/200/345/217/221/350/200/205/346/214/207/345/215/227/AI/350/241/250/346/240/274/345/257/271/350/261/241/346/250/241/345/236/213API/345/217/202/350/200/203/346/226/207/346/241/243/Field /346/250/241/345/235/227/TextField /346/226/207/346/234/254/345/255/227/346/256/265.md" +0 -93
- package/templates/aitable-handoff/wiki/faq-support/18-/345/274/200/345/217/221/350/200/205/346/214/207/345/215/227/AI/350/241/250/346/240/274/345/257/271/350/261/241/346/250/241/345/236/213API/345/217/202/350/200/203/346/226/207/346/241/243/Field /346/250/241/345/235/227/UnidirectionalLinkFields /345/215/225/345/220/221/345/205/263/350/201/224/345/255/227/346/256/265.md" +0 -295
- package/templates/aitable-handoff/wiki/faq-support/18-/345/274/200/345/217/221/350/200/205/346/214/207/345/215/227/AI/350/241/250/346/240/274/345/257/271/350/261/241/346/250/241/345/236/213API/345/217/202/350/200/203/346/226/207/346/241/243/Field /346/250/241/345/235/227/UrlField /351/223/276/346/216/245/345/255/227/346/256/265.md" +0 -163
- package/templates/aitable-handoff/wiki/faq-support/18-/345/274/200/345/217/221/350/200/205/346/214/207/345/215/227/AI/350/241/250/346/240/274/345/257/271/350/261/241/346/250/241/345/236/213API/345/217/202/350/200/203/346/226/207/346/241/243/Field /346/250/241/345/235/227/UserField /344/272/272/345/221/230/345/255/227/346/256/265.md" +0 -266
- package/templates/aitable-handoff/wiki/faq-support/18-/345/274/200/345/217/221/350/200/205/346/214/207/345/215/227/AI/350/241/250/346/240/274/345/257/271/350/261/241/346/250/241/345/236/213API/345/217/202/350/200/203/346/226/207/346/241/243/Field /346/250/241/345/235/227.md" +0 -379
- package/templates/aitable-handoff/wiki/faq-support/18-/345/274/200/345/217/221/350/200/205/346/214/207/345/215/227/AI/350/241/250/346/240/274/345/257/271/350/261/241/346/250/241/345/236/213API/345/217/202/350/200/203/346/226/207/346/241/243/Record /346/250/241/345/235/227.md" +0 -277
- package/templates/aitable-handoff/wiki/faq-support/18-/345/274/200/345/217/221/350/200/205/346/214/207/345/215/227/AI/350/241/250/346/240/274/345/257/271/350/261/241/346/250/241/345/236/213API/345/217/202/350/200/203/346/226/207/346/241/243/Sheet /346/250/241/345/235/227.md" +0 -1013
- package/templates/aitable-handoff/wiki/faq-support/18-/345/274/200/345/217/221/350/200/205/346/214/207/345/215/227/AI/350/241/250/346/240/274/345/257/271/350/261/241/346/250/241/345/236/213API/345/217/202/350/200/203/346/226/207/346/241/243/UI /346/250/241/345/235/227.md" +0 -472
- package/templates/aitable-handoff/wiki/faq-support/18-/345/274/200/345/217/221/350/200/205/346/214/207/345/215/227/AI/350/241/250/346/240/274/345/257/271/350/261/241/346/250/241/345/236/213API/345/217/202/350/200/203/346/226/207/346/241/243/View /346/250/241/345/235/227/GridView /350/241/250/346/240/274/350/247/206/345/233/276.md" +0 -835
- package/templates/aitable-handoff/wiki/faq-support/18-/345/274/200/345/217/221/350/200/205/346/214/207/345/215/227/AI/350/241/250/346/240/274/345/257/271/350/261/241/346/250/241/345/236/213API/345/217/202/350/200/203/346/226/207/346/241/243/View /346/250/241/345/235/227.md" +0 -203
- package/templates/aitable-handoff/wiki/faq-support/18-/345/274/200/345/217/221/350/200/205/346/214/207/345/215/227/AI/350/241/250/346/240/274/345/257/271/350/261/241/346/250/241/345/236/213API/345/217/202/350/200/203/346/226/207/346/241/243//345/255/227/346/256/265/346/225/260/346/215/256/347/273/223/346/236/204.md +0 -65
- package/templates/aitable-handoff/wiki/faq-support/18-/345/274/200/345/217/221/350/200/205/346/214/207/345/215/227/AI/350/241/250/346/240/274/345/257/271/350/261/241/346/250/241/345/236/213API/345/217/202/350/200/203/346/226/207/346/241/243//350/257/273/345/206/231AI /350/241/250/346/240/274/346/250/241/345/236/213.md" +0 -29
- package/templates/aitable-handoff/wiki/faq-support/18-/345/274/200/345/217/221/350/200/205/346/214/207/345/215/227/AI/350/241/250/346/240/274/346/225/260/346/215/256/346/272/220 /345/274/200/345/217/221/346/214/207/345/215/227.md" +0 -607
- package/templates/aitable-handoff/wiki/faq-support/18-/345/274/200/345/217/221/350/200/205/346/214/207/345/215/227/AI/350/241/250/346/240/274/350/204/232/346/234/254/346/217/222/344/273/266 /345/274/200/345/217/221/346/214/207/345/215/227/Base /346/250/241/345/235/227.md" +0 -391
- package/templates/aitable-handoff/wiki/faq-support/18-/345/274/200/345/217/221/350/200/205/346/214/207/345/215/227/AI/350/241/250/346/240/274/350/204/232/346/234/254/346/217/222/344/273/266 /345/274/200/345/217/221/346/214/207/345/215/227/Field /346/250/241/345/235/227.md" +0 -379
- package/templates/aitable-handoff/wiki/faq-support/18-/345/274/200/345/217/221/350/200/205/346/214/207/345/215/227/AI/350/241/250/346/240/274/350/204/232/346/234/254/346/217/222/344/273/266 /345/274/200/345/217/221/346/214/207/345/215/227/Input/346/250/241/345/235/227.md" +0 -331
- package/templates/aitable-handoff/wiki/faq-support/18-/345/274/200/345/217/221/350/200/205/346/214/207/345/215/227/AI/350/241/250/346/240/274/350/204/232/346/234/254/346/217/222/344/273/266 /345/274/200/345/217/221/346/214/207/345/215/227/Output/346/250/241/345/235/227.md" +0 -197
- package/templates/aitable-handoff/wiki/faq-support/18-/345/274/200/345/217/221/350/200/205/346/214/207/345/215/227/AI/350/241/250/346/240/274/350/204/232/346/234/254/346/217/222/344/273/266 /345/274/200/345/217/221/346/214/207/345/215/227/Record /346/250/241/345/235/227.md" +0 -277
- package/templates/aitable-handoff/wiki/faq-support/18-/345/274/200/345/217/221/350/200/205/346/214/207/345/215/227/AI/350/241/250/346/240/274/350/204/232/346/234/254/346/217/222/344/273/266 /345/274/200/345/217/221/346/214/207/345/215/227/Sheet /346/250/241/345/235/227.md" +0 -1013
- package/templates/aitable-handoff/wiki/faq-support/18-/345/274/200/345/217/221/350/200/205/346/214/207/345/215/227/AI/350/241/250/346/240/274/350/204/232/346/234/254/346/217/222/344/273/266 /345/274/200/345/217/221/346/214/207/345/215/227/UI /346/250/241/345/235/227.md" +0 -472
- package/templates/aitable-handoff/wiki/faq-support/18-/345/274/200/345/217/221/350/200/205/346/214/207/345/215/227/AI/350/241/250/346/240/274/350/204/232/346/234/254/346/217/222/344/273/266 /345/274/200/345/217/221/346/214/207/345/215/227/View /346/250/241/345/235/227.md" +0 -203
- package/templates/aitable-handoff/wiki/faq-support/18-/345/274/200/345/217/221/350/200/205/346/214/207/345/215/227/AI/350/241/250/346/240/274/350/204/232/346/234/254/346/217/222/344/273/266 /345/274/200/345/217/221/346/214/207/345/215/227//345/255/227/346/256/265/346/225/260/346/215/256/347/273/223/346/236/204.md" +0 -65
- package/templates/aitable-handoff/wiki/faq-support/18-/345/274/200/345/217/221/350/200/205/346/214/207/345/215/227/AI/350/241/250/346/240/274/350/204/232/346/234/254/346/217/222/344/273/266 /345/274/200/345/217/221/346/214/207/345/215/227.md" +0 -142
- package/templates/aitable-handoff/wiki/faq-support/18-/345/274/200/345/217/221/350/200/205/346/214/207/345/215/227/AI/350/241/250/346/240/274/350/276/271/346/240/217/346/217/222/344/273/266 /345/274/200/345/217/221/346/214/207/345/215/227/Base /346/250/241/345/235/227.md" +0 -391
- package/templates/aitable-handoff/wiki/faq-support/18-/345/274/200/345/217/221/350/200/205/346/214/207/345/215/227/AI/350/241/250/346/240/274/350/276/271/346/240/217/346/217/222/344/273/266 /345/274/200/345/217/221/346/214/207/345/215/227/Event/344/272/213/344/273/266/346/250/241/345/235/227.md" +0 -182
- package/templates/aitable-handoff/wiki/faq-support/18-/345/274/200/345/217/221/350/200/205/346/214/207/345/215/227/AI/350/241/250/346/240/274/350/276/271/346/240/217/346/217/222/344/273/266 /345/274/200/345/217/221/346/214/207/345/215/227/Field /346/250/241/345/235/227.md" +0 -379
- package/templates/aitable-handoff/wiki/faq-support/18-/345/274/200/345/217/221/350/200/205/346/214/207/345/215/227/AI/350/241/250/346/240/274/350/276/271/346/240/217/346/217/222/344/273/266 /345/274/200/345/217/221/346/214/207/345/215/227/Record /346/250/241/345/235/227.md" +0 -277
- package/templates/aitable-handoff/wiki/faq-support/18-/345/274/200/345/217/221/350/200/205/346/214/207/345/215/227/AI/350/241/250/346/240/274/350/276/271/346/240/217/346/217/222/344/273/266 /345/274/200/345/217/221/346/214/207/345/215/227/Sheet /346/250/241/345/235/227.md" +0 -1013
- package/templates/aitable-handoff/wiki/faq-support/18-/345/274/200/345/217/221/350/200/205/346/214/207/345/215/227/AI/350/241/250/346/240/274/350/276/271/346/240/217/346/217/222/344/273/266 /345/274/200/345/217/221/346/214/207/345/215/227/UI /346/250/241/345/235/227.md" +0 -472
- package/templates/aitable-handoff/wiki/faq-support/18-/345/274/200/345/217/221/350/200/205/346/214/207/345/215/227/AI/350/241/250/346/240/274/350/276/271/346/240/217/346/217/222/344/273/266 /345/274/200/345/217/221/346/214/207/345/215/227/View /346/250/241/345/235/227.md" +0 -203
- package/templates/aitable-handoff/wiki/faq-support/18-/345/274/200/345/217/221/350/200/205/346/214/207/345/215/227/AI/350/241/250/346/240/274/350/276/271/346/240/217/346/217/222/344/273/266 /345/274/200/345/217/221/346/214/207/345/215/227//345/205/266/344/273/226/346/216/245/345/217/243.md" +0 -146
- package/templates/aitable-handoff/wiki/faq-support/18-/345/274/200/345/217/221/350/200/205/346/214/207/345/215/227/AI/350/241/250/346/240/274/350/276/271/346/240/217/346/217/222/344/273/266 /345/274/200/345/217/221/346/214/207/345/215/227//345/255/227/346/256/265/346/225/260/346/215/256/347/273/223/346/236/204.md" +0 -65
- package/templates/aitable-handoff/wiki/faq-support/18-/345/274/200/345/217/221/350/200/205/346/214/207/345/215/227/AI/350/241/250/346/240/274/350/276/271/346/240/217/346/217/222/344/273/266 /345/274/200/345/217/221/346/214/207/345/215/227//346/240/270/345/277/203/347/237/245/350/257/206.md" +0 -54
- package/templates/aitable-handoff/wiki/faq-support/18-/345/274/200/345/217/221/350/200/205/346/214/207/345/215/227/AI/350/241/250/346/240/274/350/276/271/346/240/217/346/217/222/344/273/266 /345/274/200/345/217/221/346/214/207/345/215/227.md" +0 -192
- package/templates/aitable-handoff/wiki/faq-support//351/222/211/351/222/211AI/350/241/250/346/240/274FAQ.md +0 -611
- package/templates/aitable-handoff/workflows/admin-kb-update/recipes/kb-refine.recipe.ts +0 -131
- package/templates/aitable-handoff/workflows/admin-kb-update/recipes/kb_sync_handler.recipe.ts +0 -130
- package/templates/aitable-handoff/workflows/admin-kb-update/workflow.yml +0 -125
- package/templates/aitable-handoff/workflows/config-sync/recipes/config_sync.recipe.ts +0 -448
- package/templates/aitable-handoff/workflows/config-sync/recipes/config_sync_handler.recipe.ts +0 -16
- package/templates/aitable-handoff/workflows/config-sync/workflow.yml +0 -73
- package/templates/aitable-handoff/workflows/daily-review/recipes/daily_review.recipe.ts +0 -529
- package/templates/aitable-handoff/workflows/daily-review/recipes/daily_review_handler.recipe.ts +0 -26
- package/templates/aitable-handoff/workflows/daily-review/recipes/memory-events.ts +0 -56
- package/templates/aitable-handoff/workflows/daily-review/workflow.yml +0 -92
- package/templates/aitable-handoff/workflows/group-qa/error-replies.json +0 -53
- package/templates/aitable-handoff/workflows/group-qa/recipes/answer.recipe.ts +0 -197
- package/templates/aitable-handoff/workflows/group-qa/recipes/conversation_handler.recipe.ts +0 -294
- package/templates/aitable-handoff/workflows/group-qa/recipes/issue-classifier.ts +0 -120
- package/templates/aitable-handoff/workflows/group-qa/recipes/known-error-match.recipe.ts +0 -75
- package/templates/aitable-handoff/workflows/group-qa/recipes/memory-events.ts +0 -55
- package/templates/aitable-handoff/workflows/group-qa/recipes/reply-to-group.recipe.ts +0 -132
- package/templates/aitable-handoff/workflows/group-qa/recipes/troubleshoot-issue.recipe.ts +0 -195
- package/templates/aitable-handoff/workflows/group-qa/workflow.yml +0 -210
- package/templates/aitable-handoff/workspaces/daily-review/AGENTS.md +0 -189
- package/templates/aitable-handoff/workspaces/faq-support/AGENTS.md +0 -448
- package/templates/aitable-handoff/workspaces/faq-support/contacts.json +0 -14
- package/templates/aitable-handoff/workspaces/faq-support/owner-map.json +0 -18
- package/templates/aitable-handoff/workspaces/faq-support/skills/bug-triage/SKILL.md +0 -130
- package/templates/aitable-handoff/workspaces/faq-troubleshooter/AGENTS.md +0 -53
- package/templates/aitable-handoff/workspaces/faq-troubleshooter/skills/troubleshooting/SKILL.md +0 -58
- package/templates/aitable-handoff/workspaces/faq-troubleshooter/skills/troubleshooting/aitable-import-troubleshooter/SKILL.md +0 -485
- package/templates/aitable-handoff/workspaces/faq-troubleshooter/skills/troubleshooting/aitable-openapi-troubleshooter/SKILL.md +0 -183
- package/templates/aitable-handoff/workspaces/faq-troubleshooter/skills/troubleshooting/import-openapi-e2e-troubleshooter/SKILL.md +0 -459
- package/templates/aitable-handoff/workspaces/faq-troubleshooter/skills/troubleshooting/notable-datasource-troubleshooter/SKILL.md +0 -1286
- package/templates/aitable-handoff/workspaces/faq-troubleshooter/skills/troubleshooting/notable-field-troubleshooter/SKILL.md +0 -673
- package/templates/aitable-handoff/workspaces/faq-troubleshooter/skills/troubleshooting/spreadsheet-datasync-troubleshooter/SKILL.md +0 -322
- package/templates/aitable-handoff/workspaces/kb-editor/AGENTS.md +0 -32
- package/templates/support-qa/lib/assemble-cli.ts +0 -30
- package/templates/support-qa/lib/assemble.ts +0 -279
- package/templates/support-qa/lib/echo-guard.ts +0 -50
- package/templates/support-qa/lib/fastpath.ts +0 -227
- package/templates/support-qa/lib/owner-mention.ts +0 -140
- package/templates/support-qa/lib/profile-guard.ts +0 -55
- package/templates/support-qa/lib/send-failure-guard.ts +0 -59
- package/templates/support-qa/lib/troubleshoot.ts +0 -236
|
@@ -0,0 +1,398 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { parse as parseYaml } from "yaml";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* aitable-workflow — 声明式规则匹配引擎
|
|
7
|
+
*
|
|
8
|
+
* 把「命中哪条规则」从代码里的硬编码正则收敛为 YAML 声明。两个 preset 共用同一内核:
|
|
9
|
+
*
|
|
10
|
+
* loadReplyRules 确定性秒回(命中即固定话术):safety 关键词兜底 + rules[template|contains|regex]
|
|
11
|
+
* loadClassifierRules 问题分类(命中即打标签并抽取证据):classifiers[patterns, require_all, extract]
|
|
12
|
+
*
|
|
13
|
+
* 前者本质是后者的特例(无 require_all、无 extract、命中即回复),故合为一个模块。
|
|
14
|
+
*
|
|
15
|
+
* ─── 硬不变量(务必保留,都是踩过的坑)───
|
|
16
|
+
*
|
|
17
|
+
* 1. **单向匹配**:一律「消息包含/匹配规则模式」,绝不反向(规则模式包含消息)。
|
|
18
|
+
* 双向 includes 会让短消息错误命中长规则——这是早期 known-error-match
|
|
19
|
+
* `errorMessage.includes(k) || k.includes(errorMessage)` 的实际 bug。
|
|
20
|
+
* 2. **只看用户本轮新话**:普通规则匹配前剥离钉钉「引用消息:」块。用户带引用追问时,
|
|
21
|
+
* 引用块里往往完整包含上一轮报错与机器人回复,不剥离就会复读同一段话术。
|
|
22
|
+
* 3. **safety 看全文**:安全兜底必须对含引用的原文匹配,防注入/敏感内容藏在引用块里绕过。
|
|
23
|
+
* 4. **优雅关闭**:未配置、文件缺失、解析失败 → config=null(调用方透传),不阻断主环。
|
|
24
|
+
* 单条规则非法只跳该条并记 issue,不整档失效。
|
|
25
|
+
* 5. **校验单一事实源**:合法性只由本文件的 normalize 逐条判定并产出 issues,
|
|
26
|
+
* 不再另外维护一份 zod schema(两套规则双写必然漂移)。
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
// ─── 匹配条件 ────────────────────────────────────────────
|
|
30
|
+
|
|
31
|
+
export type MatchPattern = {
|
|
32
|
+
contains?: string;
|
|
33
|
+
regex?: string;
|
|
34
|
+
/** 加载期预编译(与校验同一实例),避免逐消息重编 */
|
|
35
|
+
compiledRegex?: RegExp;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
/** 钉钉「引用消息:」标记:其后内容是被引用的历史消息,不属于用户本轮诉求。
|
|
39
|
+
* 含半角/全角冒号两种变体(不同客户端文案不一致)。 */
|
|
40
|
+
const QUOTE_MARKERS = ["引用消息:", "引用消息:"];
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* 剥离引用块,只保留用户本轮新话。
|
|
44
|
+
* 边界:剥离后为空(用户只转发引用、自己没打字)→ 返回原文。
|
|
45
|
+
*/
|
|
46
|
+
export function stripQuotedBlock(message: string): string {
|
|
47
|
+
const msg = message ?? "";
|
|
48
|
+
let cut = msg.length;
|
|
49
|
+
for (const marker of QUOTE_MARKERS) {
|
|
50
|
+
const i = msg.indexOf(marker);
|
|
51
|
+
if (i >= 0 && i < cut) cut = i;
|
|
52
|
+
}
|
|
53
|
+
if (cut >= msg.length) return msg;
|
|
54
|
+
const head = msg.slice(0, cut).trim();
|
|
55
|
+
return head || msg;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** 空白归一(template 全等匹配用) */
|
|
59
|
+
function normalizeWs(s: string): string {
|
|
60
|
+
return s.replace(/\s+/g, " ").trim();
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** 单向测试:消息包含/匹配该 pattern */
|
|
64
|
+
function testPattern(p: MatchPattern, message: string): boolean {
|
|
65
|
+
if (p.contains !== undefined) return message.includes(p.contains);
|
|
66
|
+
if (p.compiledRegex) return p.compiledRegex.test(message);
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** 归一化 patterns 列表:每条必须且只能有 contains/regex 之一 */
|
|
71
|
+
function normalizePatterns(raw: unknown, label: string, issues: string[]): MatchPattern[] {
|
|
72
|
+
const out: MatchPattern[] = [];
|
|
73
|
+
for (const p of Array.isArray(raw) ? raw : []) {
|
|
74
|
+
if (!p || typeof p !== "object") continue;
|
|
75
|
+
const rec = p as Record<string, unknown>;
|
|
76
|
+
const hasContains = typeof rec.contains === "string" && rec.contains.trim();
|
|
77
|
+
const hasRegex = typeof rec.regex === "string" && String(rec.regex).trim();
|
|
78
|
+
if (Boolean(hasContains) === Boolean(hasRegex)) {
|
|
79
|
+
issues.push(`${label} 每条 pattern 必须且只能有 contains/regex 之一`);
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
if (hasContains) {
|
|
83
|
+
out.push({ contains: String(rec.contains) });
|
|
84
|
+
} else {
|
|
85
|
+
const rx = String(rec.regex);
|
|
86
|
+
try {
|
|
87
|
+
out.push({ regex: rx, compiledRegex: new RegExp(rx) });
|
|
88
|
+
} catch {
|
|
89
|
+
issues.push(`${label} 非法正则: ${rx}`);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return out;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/** 按绝对路径缓存加载结果(每种 preset 一张表,避免 key 冲突) */
|
|
97
|
+
function makeLoader<T>(
|
|
98
|
+
subdir: string,
|
|
99
|
+
parse: (raw: Record<string, unknown>, issues: string[]) => T | null,
|
|
100
|
+
labels: { missing: string; parseFail: string },
|
|
101
|
+
) {
|
|
102
|
+
const cache = new Map<string, { config: T | null; issues: string[] }>();
|
|
103
|
+
return (configDir: string, rel: string | undefined): { config: T | null; issues: string[] } => {
|
|
104
|
+
const trimmed = String(rel ?? "").trim();
|
|
105
|
+
if (!trimmed) return { config: null, issues: [] }; // 未配置 → 关闭
|
|
106
|
+
const abs = path.resolve(configDir, subdir, trimmed);
|
|
107
|
+
const hit = cache.get(abs);
|
|
108
|
+
if (hit) return hit;
|
|
109
|
+
|
|
110
|
+
let res: { config: T | null; issues: string[] };
|
|
111
|
+
if (!existsSync(abs)) {
|
|
112
|
+
// 文件缺失 → 优雅关闭(不阻断主环);preflight 另行告警
|
|
113
|
+
res = { config: null, issues: [`${labels.missing}: ${subdir}/${trimmed}`] };
|
|
114
|
+
} else {
|
|
115
|
+
try {
|
|
116
|
+
const raw = (parseYaml(readFileSync(abs, "utf8")) ?? {}) as Record<string, unknown>;
|
|
117
|
+
const issues: string[] = [];
|
|
118
|
+
res = { config: parse(raw, issues), issues };
|
|
119
|
+
} catch (err) {
|
|
120
|
+
res = {
|
|
121
|
+
config: null,
|
|
122
|
+
issues: [`${labels.parseFail} ${subdir}/${trimmed}: ${err instanceof Error ? err.message : String(err)}`],
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
cache.set(abs, res);
|
|
127
|
+
return res;
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// ─── preset 1:确定性秒回规则 ─────────────────────────────
|
|
132
|
+
|
|
133
|
+
export type ReplyRule = {
|
|
134
|
+
/** 规则标识(报错/日志文案含此 key,便于排障与追溯) */
|
|
135
|
+
key: string;
|
|
136
|
+
template?: string;
|
|
137
|
+
contains?: string;
|
|
138
|
+
regex?: string;
|
|
139
|
+
compiledRegex?: RegExp;
|
|
140
|
+
reply: string;
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
export type SafetyRule = { keywords: string[]; reply: string };
|
|
144
|
+
|
|
145
|
+
export type ReplyRulesConfig = {
|
|
146
|
+
safety?: SafetyRule;
|
|
147
|
+
rules: ReplyRule[];
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
export type ReplyMatch = {
|
|
151
|
+
key: string;
|
|
152
|
+
reply: string;
|
|
153
|
+
kind: "safety" | "template" | "contains" | "regex";
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
function parseReplyRules(raw: Record<string, unknown>, issues: string[]): ReplyRulesConfig {
|
|
157
|
+
const rules: ReplyRule[] = [];
|
|
158
|
+
const rawRules = Array.isArray(raw.rules) ? raw.rules : [];
|
|
159
|
+
for (let i = 0; i < rawRules.length; i++) {
|
|
160
|
+
const r = rawRules[i] as Record<string, unknown> | null;
|
|
161
|
+
if (!r || typeof r !== "object") {
|
|
162
|
+
issues.push(`rules[${i}] 不是对象`);
|
|
163
|
+
continue;
|
|
164
|
+
}
|
|
165
|
+
const key = String(r.key ?? "").trim();
|
|
166
|
+
const reply = String(r.reply ?? "").trim();
|
|
167
|
+
if (!key) {
|
|
168
|
+
issues.push(`rules[${i}] 缺少 key`);
|
|
169
|
+
continue;
|
|
170
|
+
}
|
|
171
|
+
if (!reply) {
|
|
172
|
+
issues.push(`rules[${key}] 缺少 reply`);
|
|
173
|
+
continue;
|
|
174
|
+
}
|
|
175
|
+
const present = (["template", "contains", "regex"] as const).filter(
|
|
176
|
+
(k) => typeof r[k] === "string" && String(r[k]).trim(),
|
|
177
|
+
);
|
|
178
|
+
if (present.length !== 1) {
|
|
179
|
+
issues.push(`rules[${key}] 必须且只能有 template/contains/regex 之一(当前 ${present.length} 个)`);
|
|
180
|
+
continue;
|
|
181
|
+
}
|
|
182
|
+
const rule: ReplyRule = { key, reply };
|
|
183
|
+
if (typeof r.template === "string") rule.template = r.template;
|
|
184
|
+
if (typeof r.contains === "string") rule.contains = r.contains;
|
|
185
|
+
if (typeof r.regex === "string") {
|
|
186
|
+
try {
|
|
187
|
+
rule.compiledRegex = new RegExp(r.regex);
|
|
188
|
+
} catch {
|
|
189
|
+
issues.push(`rules[${key}] 非法正则: ${r.regex}`);
|
|
190
|
+
continue;
|
|
191
|
+
}
|
|
192
|
+
rule.regex = r.regex;
|
|
193
|
+
}
|
|
194
|
+
rules.push(rule);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
let safety: SafetyRule | undefined;
|
|
198
|
+
const rawSafety = raw.safety as Record<string, unknown> | undefined;
|
|
199
|
+
if (rawSafety && typeof rawSafety === "object") {
|
|
200
|
+
const keywords = Array.isArray(rawSafety.keywords)
|
|
201
|
+
? rawSafety.keywords.map((k) => String(k)).filter(Boolean)
|
|
202
|
+
: [];
|
|
203
|
+
const reply = String(rawSafety.reply ?? "").trim();
|
|
204
|
+
if (keywords.length > 0 && reply) safety = { keywords, reply };
|
|
205
|
+
else issues.push("safety 需同时提供非空 keywords 与 reply(否则忽略)");
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
return { safety, rules };
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/** 加载秒回规则(相对 `<configDir>/profile/` 解析),按绝对路径缓存 */
|
|
212
|
+
export const loadReplyRules = makeLoader<ReplyRulesConfig>(
|
|
213
|
+
"profile",
|
|
214
|
+
parseReplyRules,
|
|
215
|
+
{ missing: "规则文件不存在", parseFail: "规则解析失败" },
|
|
216
|
+
);
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* 匹配秒回规则(单向):安全兜底优先,其次规则按声明顺序。
|
|
220
|
+
*
|
|
221
|
+
* safety 看全文(敏感内容/注入藏在引用块里也要拦);普通规则只看剥离引用后的用户新话。
|
|
222
|
+
*/
|
|
223
|
+
export function matchReplyRules(config: ReplyRulesConfig | null, message: string): ReplyMatch | null {
|
|
224
|
+
if (!config) return null;
|
|
225
|
+
const msg = message ?? "";
|
|
226
|
+
if (!msg.trim()) return null;
|
|
227
|
+
|
|
228
|
+
if (config.safety) {
|
|
229
|
+
for (const kw of config.safety.keywords) {
|
|
230
|
+
if (kw && msg.includes(kw)) return { key: "safety", reply: config.safety.reply, kind: "safety" };
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
const own = stripQuotedBlock(msg);
|
|
235
|
+
if (!own.trim()) return null;
|
|
236
|
+
|
|
237
|
+
for (const rule of config.rules) {
|
|
238
|
+
if (rule.template !== undefined) {
|
|
239
|
+
if (normalizeWs(own) === normalizeWs(rule.template)) {
|
|
240
|
+
return { key: rule.key, reply: rule.reply, kind: "template" };
|
|
241
|
+
}
|
|
242
|
+
} else if (rule.contains !== undefined) {
|
|
243
|
+
if (own.includes(rule.contains)) return { key: rule.key, reply: rule.reply, kind: "contains" }; // 单向
|
|
244
|
+
} else if (rule.regex !== undefined) {
|
|
245
|
+
if (rule.compiledRegex?.test(own)) return { key: rule.key, reply: rule.reply, kind: "regex" };
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
return null;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// ─── preset 2:问题分类器 ─────────────────────────────────
|
|
252
|
+
|
|
253
|
+
export type ClassifierRule = {
|
|
254
|
+
issueType: string;
|
|
255
|
+
/** 可选:命中时透传的路由模块名 */
|
|
256
|
+
module?: string;
|
|
257
|
+
reason: string;
|
|
258
|
+
patterns: MatchPattern[];
|
|
259
|
+
/** 默认 false(任一命中);true 时需全部命中 */
|
|
260
|
+
requireAll: boolean;
|
|
261
|
+
/** 字段名 → 正则(取第一个捕获组);未命中该字段填 null */
|
|
262
|
+
extract: Record<string, string>;
|
|
263
|
+
};
|
|
264
|
+
|
|
265
|
+
export type ClassifierRulesConfig = { classifiers: ClassifierRule[] };
|
|
266
|
+
|
|
267
|
+
export type ClassifyMatch = {
|
|
268
|
+
issueType: string;
|
|
269
|
+
module?: string;
|
|
270
|
+
reason: string;
|
|
271
|
+
extracted: Record<string, string | null>;
|
|
272
|
+
};
|
|
273
|
+
|
|
274
|
+
function parseClassifierRules(raw: Record<string, unknown>, issues: string[]): ClassifierRulesConfig | null {
|
|
275
|
+
const out: ClassifierRule[] = [];
|
|
276
|
+
const rawList = Array.isArray(raw.classifiers) ? raw.classifiers : [];
|
|
277
|
+
for (let i = 0; i < rawList.length; i++) {
|
|
278
|
+
const c = rawList[i] as Record<string, unknown> | null;
|
|
279
|
+
if (!c || typeof c !== "object") {
|
|
280
|
+
issues.push(`classifiers[${i}] 不是对象`);
|
|
281
|
+
continue;
|
|
282
|
+
}
|
|
283
|
+
const issueType = String(c.issueType ?? "").trim();
|
|
284
|
+
if (!issueType) {
|
|
285
|
+
issues.push(`classifiers[${i}] 缺少 issueType`);
|
|
286
|
+
continue;
|
|
287
|
+
}
|
|
288
|
+
const patterns = normalizePatterns(c.patterns, `classifiers[${issueType}]`, issues);
|
|
289
|
+
if (patterns.length === 0) {
|
|
290
|
+
issues.push(`classifiers[${issueType}] 无有效 patterns`);
|
|
291
|
+
continue;
|
|
292
|
+
}
|
|
293
|
+
const extract: Record<string, string> = {};
|
|
294
|
+
const rawExtract = c.extract as Record<string, unknown> | undefined;
|
|
295
|
+
if (rawExtract && typeof rawExtract === "object") {
|
|
296
|
+
for (const [field, rx] of Object.entries(rawExtract)) {
|
|
297
|
+
const pattern = String(rx ?? "");
|
|
298
|
+
try {
|
|
299
|
+
new RegExp(pattern);
|
|
300
|
+
extract[field] = pattern;
|
|
301
|
+
} catch {
|
|
302
|
+
issues.push(`classifiers[${issueType}].extract.${field} 非法正则: ${pattern}`);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
out.push({
|
|
307
|
+
issueType,
|
|
308
|
+
...(String(c.module ?? "").trim() ? { module: String(c.module).trim() } : {}),
|
|
309
|
+
reason: String(c.reason ?? `命中 ${issueType} 分类规则`).trim(),
|
|
310
|
+
patterns,
|
|
311
|
+
requireAll: c.require_all === true,
|
|
312
|
+
extract,
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
// 无有效分类器 = 关闭(交给下游语义处理)
|
|
316
|
+
return out.length > 0 ? { classifiers: out } : null;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/** 加载分类器(相对 `<configDir>/profile/` 解析),按绝对路径缓存 */
|
|
320
|
+
export const loadClassifierRules = makeLoader<ClassifierRulesConfig>(
|
|
321
|
+
"profile",
|
|
322
|
+
parseClassifierRules,
|
|
323
|
+
{ missing: "分类器文件不存在", parseFail: "分类器解析失败" },
|
|
324
|
+
);
|
|
325
|
+
|
|
326
|
+
function extractFields(extract: Record<string, string>, message: string): Record<string, string | null> {
|
|
327
|
+
const out: Record<string, string | null> = {};
|
|
328
|
+
for (const [field, rx] of Object.entries(extract)) {
|
|
329
|
+
try {
|
|
330
|
+
const m = message.match(new RegExp(rx));
|
|
331
|
+
out[field] = m?.[1] ?? m?.[0] ?? null;
|
|
332
|
+
} catch {
|
|
333
|
+
out[field] = null;
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
return out;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
/**
|
|
340
|
+
* 分类当前消息:按声明顺序返回第一个满足的分类器
|
|
341
|
+
* (require_all → 全部 pattern 命中;否则任一命中)。无命中返回 null。
|
|
342
|
+
*/
|
|
343
|
+
export function classifyMessage(config: ClassifierRulesConfig | null, message: string): ClassifyMatch | null {
|
|
344
|
+
if (!config) return null;
|
|
345
|
+
const msg = message ?? "";
|
|
346
|
+
if (!msg.trim()) return null;
|
|
347
|
+
for (const c of config.classifiers) {
|
|
348
|
+
const ok = c.requireAll
|
|
349
|
+
? c.patterns.every((p) => testPattern(p, msg))
|
|
350
|
+
: c.patterns.some((p) => testPattern(p, msg));
|
|
351
|
+
if (ok) {
|
|
352
|
+
return {
|
|
353
|
+
issueType: c.issueType,
|
|
354
|
+
...(c.module ? { module: c.module } : {}),
|
|
355
|
+
reason: c.reason,
|
|
356
|
+
extracted: extractFields(c.extract, msg),
|
|
357
|
+
};
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
return null;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
// ─── 整档 / 按群开关 ─────────────────────────────────────
|
|
364
|
+
|
|
365
|
+
/**
|
|
366
|
+
* 某个可选能力是否整档启用:所需目录(如 skills_dir)已配置且存在。
|
|
367
|
+
* 未配置或目录缺失 → 关闭。路径相对实例根解析。
|
|
368
|
+
*/
|
|
369
|
+
export function isCapabilityEnabled(configDir: string, dirRel: string | undefined): boolean {
|
|
370
|
+
const rel = String(dirRel ?? "").trim();
|
|
371
|
+
if (!rel) return false;
|
|
372
|
+
return existsSync(path.resolve(configDir, rel));
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
type GroupToggleRow = Record<string, unknown>;
|
|
376
|
+
const TRUE_VALUES = new Set(["true", "1", "yes", "on", "是", "开启", "启用", "已勾选"]);
|
|
377
|
+
|
|
378
|
+
function explicitTrue(value: unknown): boolean {
|
|
379
|
+
if (value === true || value === 1) return true;
|
|
380
|
+
return typeof value === "string" && TRUE_VALUES.has(value.trim().toLowerCase());
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
/**
|
|
384
|
+
* 按群开关:仅当群配置快照里该群的开关字段**明确为真**才启用。
|
|
385
|
+
* 群缺失、字段缺失、未知值一律关闭(宁可不做,不可乱做)。
|
|
386
|
+
*/
|
|
387
|
+
export function isGroupToggleEnabled(
|
|
388
|
+
raw: unknown,
|
|
389
|
+
conversationId: string,
|
|
390
|
+
field: string,
|
|
391
|
+
idField = "groupId",
|
|
392
|
+
): boolean {
|
|
393
|
+
if (!conversationId || !Array.isArray(raw)) return false;
|
|
394
|
+
const group = (raw as GroupToggleRow[]).find(
|
|
395
|
+
(g) => String(g?.[idField] ?? "").trim() === conversationId,
|
|
396
|
+
);
|
|
397
|
+
return explicitTrue(group?.[field]);
|
|
398
|
+
}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import { DwsClient, logger } from "aitable-workflow-core";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* aitable-workflow — 钉钉群成员在群判定(能不能真实 @ 到这个人)
|
|
5
|
+
*
|
|
6
|
+
* 用途:升级找人时要在群里 @ 负责人,但负责人未必在该群。@ 一个不在群的人钉钉不会
|
|
7
|
+
* 真正提醒到他,还会在消息里留下无意义的数字工号。故发送前先判定:
|
|
8
|
+
* - 在群 → { mode: "at", userId },由发送层配合 mentionUserIds 真实 @;
|
|
9
|
+
* - 判不了 → { mode: "name" },调用方只渲染花名,绝不写假 @、绝不出数字 uid。
|
|
10
|
+
*
|
|
11
|
+
* 硬不变量:**任何失败都降级为 `{ mode: "name" }`,绝不向主流程抛错。**
|
|
12
|
+
* 参数缺失、命令失败、超时、响应解析失败、人不在群——一律 name。找人是加分项,
|
|
13
|
+
* 不能因为它挂掉而让整条答疑链路失败。
|
|
14
|
+
*
|
|
15
|
+
* 命令统一走 DwsClient(而非自建 spawn):拿到退避重试(消化 dws 后端偶发 TIMEOUT /
|
|
16
|
+
* 伪 AUTH_PERMISSION_DENIED)与连续失败升级告警。降级时打一条 debug/warn,不再无声失败。
|
|
17
|
+
*
|
|
18
|
+
* 结果按 (conversationId, userId, openDingTalkId) 缓存:TTL 10 分钟、上限 500 条、
|
|
19
|
+
* Map 插入顺序作轻量 LRU。群成员变动不频繁,缓存显著削减 dws 调用。
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
export type GroupMention =
|
|
23
|
+
| { mode: "at"; userId: string }
|
|
24
|
+
| { mode: "name" };
|
|
25
|
+
|
|
26
|
+
/** 待判定的联系人:userId 用于真实 @,openDingTalkId 用于群成员查询 */
|
|
27
|
+
export type MentionCandidate = {
|
|
28
|
+
userId: string;
|
|
29
|
+
openDingTalkId?: string;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export type GroupMembershipDeps = {
|
|
33
|
+
/** 注入 dws 执行器(测试用);缺省新建 DwsClient */
|
|
34
|
+
dws?: Pick<DwsClient, "runJson">;
|
|
35
|
+
/** dws 可执行文件名(对齐 settings.tracker.dws_bin),缺省 "dws" */
|
|
36
|
+
dwsBin?: string;
|
|
37
|
+
/** 注入时钟(测试用) */
|
|
38
|
+
now?: () => number;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
type CacheEntry = { value: GroupMention; expiresAt: number };
|
|
42
|
+
|
|
43
|
+
const DWS_TIMEOUT_MS = 8_000;
|
|
44
|
+
const CACHE_TTL_MS = 10 * 60_000;
|
|
45
|
+
const CACHE_MAX = 500;
|
|
46
|
+
|
|
47
|
+
const memberCache = new Map<string, CacheEntry>();
|
|
48
|
+
|
|
49
|
+
function getCached(key: string, now: number): GroupMention | undefined {
|
|
50
|
+
const hit = memberCache.get(key);
|
|
51
|
+
if (!hit) return undefined;
|
|
52
|
+
if (hit.expiresAt <= now) {
|
|
53
|
+
memberCache.delete(key);
|
|
54
|
+
return undefined;
|
|
55
|
+
}
|
|
56
|
+
// Map 插入顺序作为轻量 LRU;命中后移到队尾
|
|
57
|
+
memberCache.delete(key);
|
|
58
|
+
memberCache.set(key, hit);
|
|
59
|
+
return hit.value;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function setCached(key: string, value: GroupMention, expiresAt: number): void {
|
|
63
|
+
memberCache.delete(key);
|
|
64
|
+
memberCache.set(key, { value, expiresAt });
|
|
65
|
+
while (memberCache.size > CACHE_MAX) {
|
|
66
|
+
const oldest = memberCache.keys().next().value as string | undefined;
|
|
67
|
+
if (oldest === undefined) break;
|
|
68
|
+
memberCache.delete(oldest);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** 从 dws 响应里取 result.members 数组;结构不符一律视为空 */
|
|
73
|
+
function memberList(payload: unknown): Array<Record<string, unknown>> {
|
|
74
|
+
if (!payload || typeof payload !== "object") return [];
|
|
75
|
+
const result = (payload as Record<string, unknown>).result;
|
|
76
|
+
if (!result || typeof result !== "object" || Array.isArray(result)) return [];
|
|
77
|
+
const members = (result as Record<string, unknown>).members;
|
|
78
|
+
return Array.isArray(members)
|
|
79
|
+
? members.filter(
|
|
80
|
+
(item): item is Record<string, unknown> =>
|
|
81
|
+
Boolean(item) && typeof item === "object" && !Array.isArray(item),
|
|
82
|
+
)
|
|
83
|
+
: [];
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/** 兼容两种大小写拼写(不同 dws 版本返回不一致) */
|
|
87
|
+
function readOpenDingTalkId(item: Record<string, unknown>): string {
|
|
88
|
+
const value = item.openDingTalkId ?? item.openDingtalkId;
|
|
89
|
+
return typeof value === "string" ? value.trim() : "";
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* 判定某联系人能否在目标群被真实 @。
|
|
94
|
+
*
|
|
95
|
+
* userId / openDingTalkId 由调用方从自己的联系人快照传入(本函数不做联系人解析),
|
|
96
|
+
* 这里只做「在不在这个群」的校验。
|
|
97
|
+
*/
|
|
98
|
+
export async function resolveGroupMention(
|
|
99
|
+
conversationId: string,
|
|
100
|
+
candidate: MentionCandidate,
|
|
101
|
+
deps: GroupMembershipDeps = {},
|
|
102
|
+
): Promise<GroupMention> {
|
|
103
|
+
const cid = String(conversationId ?? "").trim();
|
|
104
|
+
const userId = String(candidate?.userId ?? "").trim();
|
|
105
|
+
const openDingTalkId = String(candidate?.openDingTalkId ?? "").trim();
|
|
106
|
+
const fallback: GroupMention = { mode: "name" };
|
|
107
|
+
|
|
108
|
+
if (!cid || !userId || !openDingTalkId) {
|
|
109
|
+
logger.debug("group-membership", "参数不足,降级为花名", {
|
|
110
|
+
hasCid: Boolean(cid), hasUserId: Boolean(userId), hasOpenId: Boolean(openDingTalkId),
|
|
111
|
+
});
|
|
112
|
+
return fallback;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const now = (deps.now ?? Date.now)();
|
|
116
|
+
const cacheKey = `${cid}\u0000${userId}\u0000${openDingTalkId}`;
|
|
117
|
+
const cached = getCached(cacheKey, now);
|
|
118
|
+
if (cached !== undefined) return cached;
|
|
119
|
+
|
|
120
|
+
const dws = deps.dws ?? new DwsClient({ bin: deps.dwsBin ?? "dws", timeoutMs: DWS_TIMEOUT_MS });
|
|
121
|
+
|
|
122
|
+
let result: GroupMention = fallback;
|
|
123
|
+
try {
|
|
124
|
+
// DwsClient.runJson 自动追加 --format json,这里不再手写
|
|
125
|
+
const payload = await dws.runJson(
|
|
126
|
+
["chat", "group", "members", "list-by-ids", "--id", cid, "--users", openDingTalkId],
|
|
127
|
+
{ timeoutMs: DWS_TIMEOUT_MS },
|
|
128
|
+
);
|
|
129
|
+
const matched = memberList(payload).some((m) => readOpenDingTalkId(m) === openDingTalkId);
|
|
130
|
+
if (matched) {
|
|
131
|
+
result = { mode: "at", userId };
|
|
132
|
+
} else {
|
|
133
|
+
logger.debug("group-membership", "联系人不在该群,降级为花名", { conversationId: cid });
|
|
134
|
+
}
|
|
135
|
+
} catch (error) {
|
|
136
|
+
// 找人是加分项:查询失败不影响主流程,但要留痕(此前是完全静默)
|
|
137
|
+
logger.warn("group-membership", "群成员查询失败,降级为花名", {
|
|
138
|
+
conversationId: cid,
|
|
139
|
+
error: error instanceof Error ? error.message : String(error),
|
|
140
|
+
});
|
|
141
|
+
result = fallback;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
setCached(cacheKey, result, now + CACHE_TTL_MS);
|
|
145
|
+
return result;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/** 仅供单测隔离模块级缓存 */
|
|
149
|
+
export function resetGroupMembershipCache(): void {
|
|
150
|
+
memberCache.clear();
|
|
151
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { createFingerprintWindow } from "aitable-workflow-core";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* support-qa 出站回声防护(echo guard)
|
|
5
|
+
*
|
|
6
|
+
* 背景:webhook 发出的机器人回复可能被 dws 轮询以"某个真人"的名义重新拉回
|
|
7
|
+
* (真实案例:机器人答案以创建者名义回流,身份匹配失效,被当新消息判定建行)。
|
|
8
|
+
* 方案:reply 发送成功后发布内容指纹;conversation_handler 同步比对,
|
|
9
|
+
* 命中即按机器人消息处理(只记历史,不建单)。
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/** 出站内容指纹窗口:单 part = 回复正文(截断 300) */
|
|
13
|
+
export const echoWindow = createFingerprintWindow({
|
|
14
|
+
key: "sent_reply_fingerprints",
|
|
15
|
+
ttlMs: 10 * 60 * 1000,
|
|
16
|
+
capacity: 50,
|
|
17
|
+
maxPartLength: 300,
|
|
18
|
+
});
|
|
@@ -1,12 +1,64 @@
|
|
|
1
1
|
import type { MemoryEntry, MemoryScope } from "aitable-workflow-core";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* support-qa
|
|
4
|
+
* support-qa 记忆事件词表 + 工厂(场景层单一事实源)
|
|
5
5
|
*
|
|
6
6
|
* 写入方:conversation_handler(user/admin 消息)、answer(agent 决策)、reply(发送结果)。
|
|
7
7
|
* 读取方:answer(长期记忆注入)、daily-review(复盘/学习提炼)。
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
+
// ─── 记忆事件工厂(从 core 下沉)────────────────────────────
|
|
11
|
+
|
|
12
|
+
type MemoryEventsOptions = {
|
|
13
|
+
scenario: string;
|
|
14
|
+
channel: string;
|
|
15
|
+
questionKeyLength?: number;
|
|
16
|
+
summaryContentLength?: number;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
type MemoryEntryArgs<Kind extends string = string, Role extends string = string> = {
|
|
20
|
+
kind: Kind;
|
|
21
|
+
content: string;
|
|
22
|
+
sender: string;
|
|
23
|
+
role: Role;
|
|
24
|
+
conversationId: string;
|
|
25
|
+
timestamp?: string;
|
|
26
|
+
summary?: string;
|
|
27
|
+
meta?: Record<string, unknown>;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
type MemoryEvents<Kind extends string = string, Role extends string = string> = {
|
|
31
|
+
scope: (conversationId?: string) => MemoryScope;
|
|
32
|
+
entry: (args: MemoryEntryArgs<Kind, Role>) => MemoryEntry;
|
|
33
|
+
questionKey: (content: string) => string;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
function createMemoryEvents<Kind extends string = string, Role extends string = string>(
|
|
37
|
+
options: MemoryEventsOptions,
|
|
38
|
+
): MemoryEvents<Kind, Role> {
|
|
39
|
+
const { scenario, channel } = options;
|
|
40
|
+
const questionKeyLength = options.questionKeyLength ?? 120;
|
|
41
|
+
const summaryContentLength = options.summaryContentLength ?? 80;
|
|
42
|
+
|
|
43
|
+
return {
|
|
44
|
+
scope(conversationId?: string): MemoryScope {
|
|
45
|
+
return { channel, scenario, ...(conversationId ? { conversationId } : {}) };
|
|
46
|
+
},
|
|
47
|
+
entry(args: MemoryEntryArgs<Kind, Role>): MemoryEntry {
|
|
48
|
+
return {
|
|
49
|
+
timestamp: args.timestamp ?? new Date().toISOString(),
|
|
50
|
+
kind: args.kind,
|
|
51
|
+
content: args.content,
|
|
52
|
+
summary: args.summary ?? `${args.sender}: ${args.content.slice(0, summaryContentLength)}`,
|
|
53
|
+
meta: { sender: args.sender, role: args.role, conversationId: args.conversationId, ...args.meta },
|
|
54
|
+
};
|
|
55
|
+
},
|
|
56
|
+
questionKey(content: string): string {
|
|
57
|
+
return content.replace(/\s+/g, " ").trim().slice(0, questionKeyLength);
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
10
62
|
export const SUPPORT_SCENARIO = "support-qa";
|
|
11
63
|
export const SUPPORT_CHANNEL = "dingtalk";
|
|
12
64
|
|
|
@@ -51,44 +103,11 @@ export const SUPPORT_DECISION_CONTENT = {
|
|
|
51
103
|
silent: "(静默)",
|
|
52
104
|
} as const;
|
|
53
105
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
* 复盘据此配对首次响应时长;没有它就只能按时序 FIFO 猜,一旦有问题被静默或回复乱序就错位。
|
|
59
|
-
*/
|
|
60
|
-
export function questionKey(content: string): string {
|
|
61
|
-
return content.replace(/\s+/g, " ").trim().slice(0, 120);
|
|
62
|
-
}
|
|
106
|
+
const events = createMemoryEvents<SupportMemoryKind, SupportMemoryRole>({
|
|
107
|
+
scenario: SUPPORT_SCENARIO,
|
|
108
|
+
channel: SUPPORT_CHANNEL,
|
|
109
|
+
});
|
|
63
110
|
|
|
64
|
-
export
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
scenario: SUPPORT_SCENARIO,
|
|
68
|
-
...(conversationId ? { conversationId } : {}),
|
|
69
|
-
};
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
export function supportEntry(args: {
|
|
73
|
-
kind: SupportMemoryKind;
|
|
74
|
-
content: string;
|
|
75
|
-
sender: string;
|
|
76
|
-
role: SupportMemoryRole;
|
|
77
|
-
conversationId: string;
|
|
78
|
-
timestamp?: string;
|
|
79
|
-
summary?: string;
|
|
80
|
-
meta?: Record<string, unknown>;
|
|
81
|
-
}): MemoryEntry {
|
|
82
|
-
return {
|
|
83
|
-
timestamp: args.timestamp ?? new Date().toISOString(),
|
|
84
|
-
kind: args.kind,
|
|
85
|
-
content: args.content,
|
|
86
|
-
summary: args.summary ?? `${args.sender}: ${args.content.slice(0, 80)}`,
|
|
87
|
-
meta: {
|
|
88
|
-
sender: args.sender,
|
|
89
|
-
role: args.role,
|
|
90
|
-
conversationId: args.conversationId,
|
|
91
|
-
...args.meta,
|
|
92
|
-
},
|
|
93
|
-
};
|
|
94
|
-
}
|
|
111
|
+
export const supportScope = events.scope;
|
|
112
|
+
export const supportEntry = events.entry;
|
|
113
|
+
export const questionKey = events.questionKey;
|