weaver-work-cli 0.1.26 → 0.1.29

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.
Files changed (163) hide show
  1. package/README.md +7 -2
  2. package/dist/cmd/setup/index.js +1 -1
  3. package/dist/internal/e10/auth/commands.js +1 -6
  4. package/dist/internal/e10/auth/messages.js +3 -0
  5. package/dist/internal/e10/auth/session.js +8 -0
  6. package/dist/internal/e10/auth-recovery.js +148 -0
  7. package/dist/internal/e10/client.js +47 -15
  8. package/dist/internal/e10/context.js +8 -8
  9. package/dist/internal/e10/index.js +1 -0
  10. package/dist/internal/e10/request-runtime.js +51 -9
  11. package/dist/shortcuts/archive/host.js +3 -2
  12. package/dist/shortcuts/archive/manifest.js +1 -1
  13. package/dist/shortcuts/archive-oa/host.js +2 -1
  14. package/dist/shortcuts/archive-oa/manifest.js +1 -1
  15. package/dist/shortcuts/asset/host.js +7 -6
  16. package/dist/shortcuts/blog/host.js +4 -3
  17. package/dist/shortcuts/caizhilian/host.js +3 -2
  18. package/dist/shortcuts/caizhilian/manifest.js +1 -1
  19. package/dist/shortcuts/calendar/host.js +2 -1
  20. package/dist/shortcuts/contract/host.js +34 -59
  21. package/dist/shortcuts/ehr/host.js +2 -1
  22. package/dist/shortcuts/esb/host.js +2 -1
  23. package/dist/shortcuts/fna/host.js +6 -3
  24. package/dist/shortcuts/fna/manifest.js +10 -5
  25. package/dist/shortcuts/fna/operations/expense-chart.js +162 -0
  26. package/dist/shortcuts/fna/operations/registry.js +2 -0
  27. package/dist/shortcuts/hrm/host.js +2 -1
  28. package/dist/shortcuts/im/host.js +3 -2
  29. package/dist/shortcuts/invoice/host.js +9 -7
  30. package/dist/shortcuts/invoice/manifest.js +23 -142
  31. package/dist/shortcuts/invoice/operations/issuing-qys.js +266 -203
  32. package/dist/shortcuts/invoice/operations/registry.js +1 -6
  33. package/dist/shortcuts/jiuchuanhui/host.js +2 -1
  34. package/dist/shortcuts/mail/host.js +2 -1
  35. package/dist/shortcuts/meeting/host.js +2 -1
  36. package/dist/shortcuts/meeting/operations/eb/conflict.js +78 -12
  37. package/dist/shortcuts/okr/host.js +2 -1
  38. package/dist/shortcuts/plan/host.js +2 -1
  39. package/dist/shortcuts/project/host.js +2 -1
  40. package/dist/shortcuts/workflow/host.js +3 -5
  41. package/dist/shortcuts/workflow/index.js +4 -1
  42. package/dist/shortcuts/workflow/operations/chatscene.js +4 -22
  43. package/dist/shortcuts/workflow/operations/form.js +281 -166
  44. package/dist/shortcuts/workflow/operations/read.js +39 -8
  45. package/dist/shortcuts/workflow/operations/registry.js +2 -0
  46. package/dist/shortcuts/workflow/operations/todoSmartConvert/businessRules.js +331 -0
  47. package/dist/shortcuts/workflow/operations/todoSmartConvert/detailFieldRules.js +133 -0
  48. package/dist/shortcuts/workflow/operations/todoSmartConvert/summaryCache.js +326 -0
  49. package/dist/shortcuts/workflow/operations/todoSmartConvert/todoDetailCache.js +113 -0
  50. package/dist/shortcuts/workflow/operations/todoSmartConvert/todoDetailFetcher.js +259 -0
  51. package/dist/shortcuts/workflow/operations/todoSmartConvert/todoListRows.js +64 -0
  52. package/dist/shortcuts/workflow/operations/todoSmartConvert/todoOutputPlanner.js +28 -0
  53. package/dist/shortcuts/workflow/operations/todoSmartConvert/todoSmartConvert.aggregate.js +70 -0
  54. package/dist/shortcuts/workflow/operations/todoSmartConvert/todoSmartConvert.collect.js +267 -0
  55. package/dist/shortcuts/workflow/operations/todoSmartConvert/todoSmartConvert.core.js +392 -0
  56. package/dist/shortcuts/workflow/operations/todoSmartConvert/todoSmartConvert.detail.js +165 -0
  57. package/dist/shortcuts/workflow/operations/todoSmartConvert/todoSmartConvert.group.js +448 -0
  58. package/dist/shortcuts/workflow/operations/todoSmartConvert/todoSmartConvert.groupSummary.js +348 -0
  59. package/dist/shortcuts/workflow/operations/todoSmartConvert/todoSmartConvert.js +222 -0
  60. package/dist/shortcuts/workflow/operations/todoSmartConvert/todoSmartConvert.pipeline.js +66 -0
  61. package/dist/shortcuts/workflow/operations/todoSmartConvert/todoSmartConvert.smartGroup.js +502 -0
  62. package/dist/shortcuts/workflow/operations/todoSmartConvert/todoSmartConvert.summary.js +540 -0
  63. package/dist/shortcuts/workflow/stdout-compact.js +7 -0
  64. package/docs/SKILL.md +1 -1
  65. package/docs/_catalog.md +2 -1
  66. package/docs/agent-invoice.md +1 -1
  67. package/docs/caizhilian.md +1 -1
  68. package/docs/e10-auth.md +1 -1
  69. package/docs/fna.md +7 -3
  70. package/docs/invoice.md +4 -6
  71. package/docs/todo-smart-convert/README.md +171 -0
  72. package/docs/todo-smart-convert/data-contract.md +236 -0
  73. package/docs/todo-smart-convert/mock/ai-todo-query-target.json +209 -0
  74. package/docs/todo-smart-convert/subgroup-rules.md +245 -0
  75. package/package.json +1 -1
  76. package/scripts/package-skill.mjs +1 -1
  77. package/skill-template/business-info.json +4 -1
  78. package/skill-template/domains/caizhilian.md +2 -1
  79. package/skill-template/domains/qiyecheng.md +1 -0
  80. package/skill-template/domains/yepiaotong.md +6 -2
  81. package/skill-template/skill-template.md +1 -0
  82. package/skills/weaver-e10-blog/SKILL.md +6 -1
  83. package/skills/weaver-e10-blog/references/blog-query.md +3 -1
  84. package/skills/weaver-e10-caizhilian/SKILL.md +18 -9
  85. package/skills/weaver-e10-caizhilian/references/action-flow.md +70 -0
  86. package/skills/weaver-e10-caizhilian/references/agent-entry.md +6 -2
  87. package/skills/weaver-e10-caizhilian/references/auth-and-install.md +207 -0
  88. package/skills/weaver-e10-caizhilian/references/file-collection.md +56 -54
  89. package/skills/weaver-e10-caizhilian/references/file-upload.md +4 -4
  90. package/skills/weaver-e10-caizhilian/references/host-executor-prompt.md +32 -0
  91. package/skills/weaver-e10-caizhilian/references/host-platform-integration.md +46 -0
  92. package/skills/weaver-e10-caizhilian/references/rule-index.md +56 -0
  93. package/skills/weaver-e10-caizhilian/references/setting-attach.md +5 -16
  94. package/skills/weaver-e10-caizhilian/references/setting-backup.md +4 -13
  95. package/skills/weaver-e10-caizhilian/references/setting-encrypt.md +6 -15
  96. package/skills/weaver-e10-caizhilian/references/setting-storage-docs-summary.md +126 -0
  97. package/skills/weaver-e10-caizhilian/references/setting-storage-field-map.md +57 -0
  98. package/skills/weaver-e10-caizhilian/references/setting-storage.md +142 -482
  99. package/skills/weaver-e10-caizhilian/references/source-manifest.json +7 -7
  100. package/skills/weaver-e10-caizhilian/references/task-workflow.md +87 -0
  101. package/skills/weaver-e10-caizhilian/references/withheld.md +2 -2
  102. package/skills/weaver-e10-caizhilian/references/write-protocol.md +7 -0
  103. package/skills/weaver-e10-calendar/SKILL.md +6 -1
  104. package/skills/weaver-e10-esb/SKILL.md +8 -0
  105. package/skills/weaver-e10-esb/references/input-format.md +3 -1
  106. package/skills/weaver-e10-esb/references/source-manifest.json +5 -5
  107. package/skills/weaver-e10-hrm/SKILL.md +6 -1
  108. package/skills/weaver-e10-hrm/references/employee.md +1 -1
  109. package/skills/weaver-e10-hrm/references/org.md +1 -1
  110. package/skills/weaver-e10-hrm/references/position.md +1 -1
  111. package/skills/weaver-e10-jinchengda/SKILL.md +2 -1
  112. package/skills/weaver-e10-jinchengda/references/contract-agent-entry.md +1 -1
  113. package/skills/weaver-e10-jiuchuanhui/SKILL.md +6 -1
  114. package/skills/weaver-e10-jucailin/SKILL.md +6 -1
  115. package/skills/weaver-e10-jucailin/references/hr-contact.md +1 -1
  116. package/skills/weaver-e10-jucailin/references/salary.md +1 -1
  117. package/skills/weaver-e10-mail/SKILL.md +6 -1
  118. package/skills/weaver-e10-meeting/SKILL.md +11 -6
  119. package/skills/weaver-e10-meeting/references/meeting-eb.md +7 -3
  120. package/skills/weaver-e10-meeting/references/meeting-queries.md +3 -1
  121. package/skills/weaver-e10-meeting/references/meeting-verification.md +4 -2
  122. package/skills/weaver-e10-okr/SKILL.md +6 -1
  123. package/skills/weaver-e10-okr/references/source-manifest.json +5 -5
  124. package/skills/weaver-e10-plan/SKILL.md +6 -1
  125. package/skills/weaver-e10-plan/references/plan-overview-and-routing.md +3 -1
  126. package/skills/weaver-e10-plan/references/plan-report-query.md +3 -1
  127. package/skills/weaver-e10-plan/references/source-manifest.json +7 -7
  128. package/skills/weaver-e10-qiyecheng/SKILL.md +16 -6
  129. package/skills/weaver-e10-qiyecheng/references/catalog.md +6 -3
  130. package/skills/weaver-e10-qiyecheng/references/expense-center.md +2 -0
  131. package/skills/weaver-e10-qiyecheng/references/expense-chart.md +78 -0
  132. package/skills/weaver-e10-qiyecheng/references/source-manifest.json +87 -33
  133. package/skills/weaver-e10-shared/SKILL.md +37 -14
  134. package/skills/weaver-e10-shared/references/e10-auth-and-session.md +11 -8
  135. package/skills/weaver-e10-shared/references/non-interactive-environment.md +4 -4
  136. package/skills/weaver-e10-shared/references/weaver-e10-installation.md +3 -1
  137. package/skills/weaver-e10-wenshuding/SKILL.md +6 -1
  138. package/skills/weaver-e10-wenshuding/references/source-manifest.json +3 -3
  139. package/skills/weaver-e10-workflow/SKILL.md +23 -1
  140. package/skills/weaver-e10-workflow/references/source-manifest.json +1 -2
  141. package/skills/weaver-e10-workflow/references/todo-smart-convert.md +282 -0
  142. package/skills/weaver-e10-workflow/references/workflow-create.md +30 -55
  143. package/skills/weaver-e10-workflow/scripts/todo-summary.cjs +695 -0
  144. package/skills/weaver-e10-yepiaotong/SKILL.md +19 -17
  145. package/skills/weaver-e10-yepiaotong/references/invoice-issuing-examples.md +75 -78
  146. package/skills/weaver-e10-yepiaotong/references/invoice-issuing-form-fill-rules.md +179 -0
  147. package/skills/weaver-e10-yepiaotong/references/invoice-issuing-workflow.md +238 -0
  148. package/skills/weaver-e10-yepiaotong/references/invoice-reim.md +7 -7
  149. package/skills/weaver-e10-yepiaotong/references/reim-api-reference.md +11 -5
  150. package/skills/weaver-e10-yepiaotong/references/reim-examples.md +55 -33
  151. package/skills/weaver-e10-yepiaotong/references/reim-form-fill-rules.md +102 -23
  152. package/skills/weaver-e10-yepiaotong/references/reim-phone-subsidy-rules.md +1 -1
  153. package/skills/weaver-e10-yepiaotong/references/reim-workflow.md +53 -42
  154. package/skills/weaver-e10-yepiaotong/references/source-manifest.json +94 -214
  155. package/skills/weaver-e10-yimiaoban/SKILL.md +9 -2
  156. package/skills/weaver-e10-yimiaoban/references/file-user-i18n.md +1 -1
  157. package/skills/weaver-e10-yimiaoban/references/msg-read.md +1 -1
  158. package/skills/weaver-e10-yimiaoban/references/source-manifest.json +9 -9
  159. package/skills/weaver-e10-ziguanjia/SKILL.md +6 -1
  160. package/dist/shortcuts/invoice/operations/issuing.js +0 -198
  161. package/skills/weaver-e10-yepiaotong/references/invoice-issuing-field-rules.md +0 -161
  162. package/skills/weaver-e10-yepiaotong/references/invoice-issuing-qys-ledger.md +0 -66
  163. package/skills/weaver-e10-yepiaotong/references/invoice-issuing.md +0 -107
package/docs/fna.md CHANGED
@@ -1,16 +1,16 @@
1
1
  # 齐业成费控中心(FNA)CLI
2
2
 
3
- 对应 `src/shortcuts/fna`,把泛微齐业成 FNA 费控中心 11 个源接口固化为 13 个 operation(预算 2 / 费用中心 3 / 借款管理 4 / 发票核销 4,其中提醒还款与催票为 prepare/apply 写链)。源资料:`/Users/zhufangyuan/Desktop/weaver-e10-qiyecheng`(指纹见 `skills/weaver-e10-qiyecheng/references/source-manifest.json`)。
3
+ 对应 `src/shortcuts/fna`,把泛微齐业成 FNA 费控中心 12 个源接口固化为 14 个 operation(预算 2 / 费用中心 3 + 报销报表 AI 问答 1 / 借款管理 4 / 发票核销 4,其中提醒还款与催票为 prepare/apply 写链)。源资料:`/Users/zhufangyuan/Desktop/weaver-e10-qiyecheng`(指纹见 `skills/weaver-e10-qiyecheng/references/source-manifest.json`)。
4
4
 
5
5
  ## 公共前置
6
6
 
7
- - 认证、profile、HTTP client 由 `src/internal/e10` 托管;业务 host 只适配 `/api/fna/` 与 `/api/app/fna/` 路径(`src/shortcuts/fna/host.ts` 的 `assertPath` 白名单)。
7
+ - 认证、profile、HTTP client 由 `src/internal/e10` 托管;业务 host 只适配 `/api/fna/`、`/api/app/fna/` 与 `/api/secondev/fna/` 路径(`src/shortcuts/fna/host.ts` 的 `assertPath` 白名单)。
8
8
  - 成功判定统一走 WeaResult(`success` 或 `status` 布尔 / `code==200`);费用中心 AI 查询用 `success`,预算/借款/核销用 `status`。
9
9
  - JSON 输出与错误 envelope、退出码遵循共享规则;写操作不满足确认条件返回 `confirmation.required`(exit 10),请求发出后结果不确定返回 `partial/write_uncertain`(exit 11)。
10
10
 
11
11
  ## Agent 入口
12
12
 
13
- 面向 Agent 时先读 `skills/weaver-e10-qiyecheng/SKILL.md`,再按入口路由读取 `references/`(catalog/budget/expense-center/loan-management/invoice-writeoff)。
13
+ 面向 Agent 时先读 `skills/weaver-e10-qiyecheng/SKILL.md`,再按入口路由读取 `references/`(catalog/budget/expense-center/expense-chart/loan-management/invoice-writeoff)。
14
14
 
15
15
  ## Operation 目录
16
16
 
@@ -21,6 +21,7 @@
21
21
  | `fna.expense.reimbursed.sum` | read | POST `expense/center/getSumReimbursedAmountByAi`;body 可空 |
22
22
  | `fna.expense.loan.sum` | read | POST `expense/center/getSumLoanAmountByAi`;只认数字 ID;空 data 自动重试 2 次;已知仅统计首条记录缺陷 |
23
23
  | `fna.expense.prepay.writeoff.sum` | read | POST `expense/center/selectPrepayWriteOffAmountBySkill`;body 可空 |
24
+ | `fna.expense.chart` | read | GET `getExpenseChart`(`/api/secondev/fna/`);`userInput` 必填 ≤500 字;`maxRows` 本地截断 1-5000,默认 200;超时 180s;返回 `kind=rows/empty/clarify/blocked/unexpected` |
24
25
  | `fna.loan.main.page` | read | POST `loanManagementMenu/mainPage` |
25
26
  | `fna.loan.main.list` | read | POST `loanManagementMenu/mainPageList`;`current`/`pageSize`/`menuCode`/`permissionSetCode` 必填 |
26
27
  | `fna.loan.remind.repay.prepare` | write | 校验 displayData 行并预览,不发送 |
@@ -39,6 +40,7 @@ Windows PowerShell:
39
40
  ```powershell
40
41
  weaver-work-cli --json fna run fna.budget.application.detail --input-json '{"requestId":"1001"}'
41
42
  weaver-work-cli --json fna run fna.expense.reimbursed.sum --input-json '{"startDate":"2026-01-01","endDate":"2026-08-31"}'
43
+ weaver-work-cli --json fna run fna.expense.chart --input-json '{"userInput":"2026-08 各部门报销金额排名前三"}'
42
44
  weaver-work-cli --json fna run fna.loan.main.list --input-json '{"current":1,"pageSize":20,"menuCode":"fesx_fna_borrowingManagement","permissionSetCode":"fesx_fna_borrowingManagement.default"}'
43
45
  weaver-work-cli --json fna run fna.invoice.writeoff.list --input-json '{"current":1,"pageSize":20,"invoiceStatus":"unarrivedTicket"}'
44
46
  ```
@@ -48,6 +50,7 @@ macOS/Linux(bash/zsh):
48
50
  ```bash
49
51
  weaver-work-cli --json fna run fna.budget.application.detail --input-json '{"requestId":"1001"}'
50
52
  weaver-work-cli --json fna run fna.expense.reimbursed.sum --input-json '{"startDate":"2026-01-01","endDate":"2026-08-31"}'
53
+ weaver-work-cli --json fna run fna.expense.chart --input-json '{"userInput":"2026-08 各部门报销金额排名前三"}'
51
54
  weaver-work-cli --json fna run fna.loan.main.list --input-json '{"current":1,"pageSize":20,"menuCode":"fesx_fna_borrowingManagement","permissionSetCode":"fesx_fna_borrowingManagement.default"}'
52
55
  weaver-work-cli --json fna run fna.invoice.writeoff.list --input-json '{"current":1,"pageSize":20,"invoiceStatus":"unarrivedTicket"}'
53
56
  ```
@@ -61,6 +64,7 @@ weaver-work-cli --json fna run fna.invoice.writeoff.list --input-json '{"current
61
64
  - 日期口径:报销按明细费用日期、借款按流程创建时间、预付核销按核销记录创建时间。
62
65
  - `menuCode`/`permissionSetCode` 环境相关,示例值需与环境核对。
63
66
  - 超 2^53 的 requestId 无法用 JS 数字精确表达,保留字符串并提示人工核验(真实环境 ID 一般在安全整数内,已按数字发送)。
67
+ - `fna.expense.chart`:`code` 恒为 200,必须按 `data` 类型/`kind` 判成败;追问与安全拦截都是字符串 `data`,`meta.messageKindInferred=true` 表示归类为 CLI 推断。实测真实查询 3.3~66.6s,CLI 超时设 180s,禁止自动重试(每次都会消耗模型额度)。不做当前人强制过滤,结果可能是全租户范围;`userInput` 为商品化输入,个人口径必须改用 `fna.expense.reimbursed.sum`。二开包未安装时该 operation 会 404/500,需与其它 `fna.*` 正常但本 operation 失败区分。
64
68
 
65
69
  ## 验证
66
70
 
package/docs/invoice.md CHANGED
@@ -77,7 +77,9 @@ weaver-work-cli --json invoice list --fields number,detailUrl
77
77
  | `invoice.reim.field.search` | 只读 | 搜索表单关联字段值,用于用户修正或出差客户确认 |
78
78
  | `invoice.reim.flow.create.prepare/apply` | 高风险写 | continuation 绑定已组装报销单 JSON;`work_flow_id/mainFormId/request_name/main_fields/detail_rows` 等字段在 schema 顶层逐项暴露,确认后调用 `createFlowNew` |
79
79
  | `invoice.reim.flow.update.prepare/apply` | 高风险写 | continuation 绑定更新报销单 JSON;确认后调用 `updateFlow` |
80
- | `invoice.issuing.make.prepare/apply` | 高风险写 | 固定销方信息,确认后调用 `makeInvoice` 开票或存台账 |
80
+ | `invoice.issuing.qys.list` | 只读 | 获取待开票合同数据(含契约锁、泛微及其他合同),返回可开票行 `items` 与按「合同号 + 购方」汇总的 `groups`(开票金额合计按分累加、`blockedRows` 计不可开票行、`sellerNames` 为核算公司);AI 开票据此调用 `weaver-e10-workflow` 发起开票流程草稿 |
81
+
82
+ > 直接开票类 operation(`invoice.issuing.make.prepare/apply`、`invoice.issuing.qys.prepare`、`invoice.issuing.result`)已于 0.1.27 移除:开票按销售方选择流程草稿(销售方取台账「核算公司」,名称必须完全相同:「上海亘岩网络科技有限公司」走「契约锁合同收款发票开具及快递申请」;泛微网络、泛微软件、上海田亩、成都泛微、泛微国际、北京泛微、浙江泛微走「合同收款发票开具及快递申请」;为空或不在名单里时先问用户;退票、冲红、红冲走「合同开票冲红流程」),不再支持绕过流程直接调用 `makeInvoice`。
81
83
 
82
84
  共享、转让和标签仍暂不暴露给 Agent:新版源资料给出了提交接口,但还缺少稳定的接收人 `appid/cid/uid` 解析来源、标签关系查询和写后回查契约。
83
85
 
@@ -107,8 +109,6 @@ weaver-work-cli --json invoice import prepare --input-json '{"file":"./invoice.p
107
109
  weaver-work-cli --json invoice import apply --input-json '{"file":"./invoice.pdf","continuation":"PREPARE_CONTINUATION","confirm":true}'
108
110
 
109
111
  weaver-work-cli --json invoice run invoice.reim.flow.create.prepare --input .\reim-create.json
110
-
111
- weaver-work-cli --json invoice run invoice.issuing.make.prepare --input .\issuing.json
112
112
  ```
113
113
 
114
114
  macOS/Linux(bash/zsh):
@@ -121,13 +121,11 @@ weaver-work-cli --json invoice import prepare --input-json '{"file":"./invoice.p
121
121
  weaver-work-cli --json invoice import apply --input-json '{"file":"./invoice.pdf","continuation":"PREPARE_CONTINUATION","confirm":true}'
122
122
 
123
123
  weaver-work-cli --json invoice run invoice.reim.flow.create.prepare --input ./reim-create.json
124
-
125
- weaver-work-cli --json invoice run invoice.issuing.make.prepare --input ./issuing.json
126
124
  ```
127
125
 
128
126
  导入发票文件或远程 URL 时,面向 Agent 的正常链路是先 `invoice.ocr.preview`,再 `invoice.import.prepare` 使用 `validate=true`、`syncToOa=true`。只有用户明确要求跳过查验或离线导入时才传 `validate=false`;`validate=false` 表示跳过查验,不能描述为“查验通过”。`filedMap`、嵌套字段映射等 JSON 参数在 `--input-json` 中保持对象,CLI 只在 multipart 提交前序列化。
129
127
 
130
- 报销单创建和开票/存台账只接受 Agent 已组装好的结构化 JSON。CLI 不负责猜测事前申请匹配、出差客户或税率;这些规则在 Skill reference 中指导 Agent 组装和展示预览。报销单创建的字段必须在 operation 顶层逐项传入,不再包一层通用 `payload`。当前修正资料包没有客户档案查询或红字发票红冲接口,`invoice.customer.*` 和 `invoice.red.*` 不暴露为 operation,Agent 不能绕过 CLI 调用原始接口。写操作出现网络中断、部分成功、缺少回执 ID 或回查失败时,CLI 返回 `partial/write_uncertain`,Agent 禁止自动重复提交。
128
+ 报销单创建只接受 Agent 已组装好的结构化 JSON;开票按销售方选择流程后,通过 `weaver-e10-workflow` 发起对应开票流程草稿(退票、冲红、红冲发起「合同开票冲红流程」),CLI 不再提供直接开票接口。CLI 不负责猜测事前申请匹配、出差客户或税率;这些规则在 Skill reference 中指导 Agent 组装和展示预览。报销单创建的字段必须在 operation 顶层逐项传入,不再包一层通用 `payload`。当前修正资料包没有客户档案查询或红字发票红冲接口,`invoice.customer.*` 和 `invoice.red.*` 不暴露为 operation,Agent 不能绕过 CLI 调用原始接口。写操作出现网络中断、部分成功、缺少回执 ID 或回查失败时,CLI 返回 `partial/write_uncertain`,Agent 禁止自动重复提交。
131
129
 
132
130
  ## 编辑白名单
133
131
 
@@ -0,0 +1,171 @@
1
+ # todoSmartConvert
2
+
3
+ `workflow.todoSmartConvert` 是 workflow 模块中的只读待办汇总 operation。它负责取数、归一化、首次分组、按需获取详情、单条摘要回填和兼容 `aggregate` 阶段的本地汇总;需要语义侧重点时校验调用方提供的 AI 分组计划,否则由 CLI 按业务规则确定性分组。
4
+
5
+ ## 1. 文档和代码边界
6
+
7
+ 本文件面向 CLI 维护者,记录当前行为、不变量和阶段边界。三份文档各自只承担一类职责:
8
+
9
+ - Agent 如何判断意图、组织多轮回灌:[`skills/weaver-e10-workflow/references/todo-smart-convert.md`](../../skills/weaver-e10-workflow/references/todo-smart-convert.md)。
10
+ - 各源码文件的详细职责:[`src/shortcuts/workflow/operations/todoSmartConvert/README.md`](../../src/shortcuts/workflow/operations/todoSmartConvert/README.md)。
11
+ - 本文只保留实现约束,避免把调用指令和源码文件清单重复维护两遍。
12
+
13
+ 主入口只保留输入 schema、阶段路由、统一信封和耗时输出;实现按四层组织:
14
+
15
+ | 层 | 负责内容 |
16
+ | --- | --- |
17
+ | 入口层 | operation 注册、schema、stage 路由、统一输出 |
18
+ | 阶段层 | collect、group、smartGroup、summary、groupSummary、aggregate |
19
+ | 领域层 | 首次/二次分组、业务类型识别、详情字段清洗、摘要素材 |
20
+ | 基础设施层 | HTTP 详情抓取、摘要缓存、输入归一化、输出告警 |
21
+
22
+ 详情抓取和两个缓存是独立边界:列表阶段只按需要预取详情(有适用意图的组);详情与摘要都以 `requestId__lastFeedBackDateTime` 为 key 落盘(7 天);摘要缓存只省 AI,不省详情。
23
+
24
+ ## 2. 阶段
25
+
26
+ | 阶段 | 作用 | 主要结果 |
27
+ | --- | --- | --- |
28
+ | `collect` | 调列表接口取数并完成首次分组 | `items`、`wfIdGroup`、`groupingBrief` |
29
+ | `group` | 对已传入的 `items` 重新做首次分组 | 中间分组结构、统计和 Markdown |
30
+ | `smartGroup` | 校验 AI 分组计划并组装最终结构 | `smartSummaryTitle`、`totalCount`、`wfIdGroup` |
31
+ | `summaryContext` | 现场获取详情并按展示项生成摘要素材 | `summaryBatches`、`summaryStats` |
32
+ | `summary` | 回填 `aiSummaries` | 最终结构,必要时附组摘要素材 |
33
+ | `groupSummaryContext` | 生成汇总项摘要素材 | `groupSummaryBatches`、`groupSummaryStats` |
34
+ | `groupSummary` | 回填 `aiGroupSummaries` | 最终结构 |
35
+ | `aggregate` | 兼容本地汇总输入 | 分组、金额和 Markdown |
36
+
37
+ 常规调用链是:
38
+
39
+ ```text
40
+ collect
41
+ -> smartGroup (可合并 withDetailContext)
42
+ -> summary (可合并 withGroupSummaryContext)
43
+ -> groupSummary(仅存在待回填批次时)
44
+ ```
45
+
46
+ 未传 `stage` 时:没有 `items` 走 `collect`;有 `items` 走兼容的本地汇总;回灌最终结构时按结构推断阶段。显式 `stage` 优先。
47
+
48
+ ## 3. 取数规则
49
+
50
+ - 只调用对应分类的列表接口,默认待办使用 `getTodoListData`;collect 不调用总数、分组统计接口。
51
+ - 时间条件一次传入,通过 `current` 递增翻页。默认 `limit=200`,最大 `limit=500`,单页最多 100 条,最多 5 页;`pageSize` 仅为兼容参数,实际由 `limit` 推导。
52
+ - 页数不足、达到 limit 或页内无新增数据时停止;跨页按 `requestId` 去重。
53
+ - 只读取响应中的 `displayData`。技术行不作为 `displayData` 缺失时的回退来源。
54
+ - 长 ID 全程按字符串处理。`requestData` 只包含稳定业务字段,`workflowName`、接收时间和金额等辅助字段只留在内部行中。
55
+ - **collect 只在有必要时预取详情**:用户给了 `groupFocus` 且意图适用于 `detectBusinessKind(组名)` 的组,才把该组条目的详情字段放进 `groupingBrief[].items[].fields`(≤8 字段、每值 ≤80 字)。预取走详情缓存,命中则零请求;`detailStats.groupingMaterial` 记录 `requested/cacheHit/fetched/failed/withFields`。
56
+ - collect 会刷新本地查询快照,供后续批量操作的门禁使用;本 operation 本身不执行写操作。
57
+
58
+ collect 输出的 `groupingBrief` 使用组内 1-based `idx`。模型生成 `aiGroups` 时优先使用 `seq`,避免手抄长 ID。
59
+
60
+ ## 4. 首次分组和二次分组
61
+
62
+ ### 首次分组
63
+
64
+ - 默认按 `workflowName` 分组;也支持 `urgency`、`date`、`none`。
65
+ - 优先组固定为 `id=priority`、标题“建议优先处理的流程”,排在第一位。超时条目必入;没有紧急级别 2 时才退到级别 1,级别 0 不因等级入选。
66
+ - 优先组是**叠加视图**,不是筛选。入选条目仍保留在各自的工作流分组里(优先组只是置顶入口),因此各大组 `count` 之和会大于 `totalCount`;`totalCount` 按去重条数计,等于 collect 取到的全部条数。
67
+ - 普通组默认保持接口首次出现顺序;`groupOrder` 只有在调用方明确提供顺序意图时才使用。
68
+
69
+ ### `aiGroups`
70
+
71
+ `aiGroups` 是扁平数组,每项只能有 `groupId`、`title` 和 `requestIds` 或 `seq`:
72
+
73
+ ```json
74
+ [
75
+ { "groupId": "workflow-id", "title": "安全通知", "seq": [1, 3] },
76
+ { "groupId": "workflow-id", "title": "制度通知", "seq": [2, 4] }
77
+ ]
78
+ ```
79
+
80
+ - 优先组不接受二次分组。
81
+ - 组内少于 3 条时全部单条展示,**即使给了 `aiGroups` 也忽略**(硬门槛优先,写 warning)。
82
+ - 组内至少 3 条时:`aiGroups` 里属于本组的计划**只有在「意图适用本组」或「本组是内部留言」时才会被采纳**;其余情况等同于没传(写 warning,走确定性分组)。采纳时才校验未知组、未知条目、重复分配和越界序号。
83
+ - 无侧重点时,普通子组按条数降序;有 `groupFocus` 时,子组顺序沿用 AI 数组顺序。
84
+ - `aiGroups` 省略或传 `[]` 一律走确定性分组:只有请假(≤1 天)、财务(≤100 元)和客户问题支持类(已过期/今日到期)会产汇总项,其他类**全部平铺单条**(不再造同名汇总项);满意度类全部单条,`summary` 用条目标题兜底。无法识别请假天数或金额的条目按单条展示并告警。
85
+ **内部留言是例外**:≥3 条时必须有 `aiGroups`(无侧重点 / 侧重点不适用 ⇒ 按**流程标题**分组;侧重点适用 ⇒ 按侧重点+详情),缺计划报 `ai_groups_required`,没有「平铺」这个第三态。
86
+ - **意图按表判定**(`INTENT_RULES`):专属意图(请假/财务/问题支持维度)只作用于对应类型,通用意图(客户/发起人/时间/紧急/标题主题…)只作用于内部留言与其他类;都不命中 = 没有意图,退回确定性规则,**不硬失败**。
87
+ - AI 漏分、子组只剩 1 条、保留标题里超阈值的条目,**一律落成单条**,不造 `other`,并通过 warnings 说明条数。
88
+
89
+ 最终结构固定为:
90
+
91
+ ```json
92
+ {
93
+ "smartSummaryTitle": "您当前有 N 条待办,清单如下:",
94
+ "totalCount": 0,
95
+ "wfIdGroup": [
96
+ {
97
+ "id": "workflow-id",
98
+ "topPriority": false,
99
+ "title": "工作流名称",
100
+ "count": 0,
101
+ "defaultExpanded": false,
102
+ "displayItems": [
103
+ { "type": "request", "requestData": {} },
104
+ {
105
+ "type": "group",
106
+ "id": "workflow-id-1",
107
+ "title": "子组名称",
108
+ "count": 0,
109
+ "summary": "",
110
+ "defaultExpanded": false,
111
+ "requestDatas": []
112
+ }
113
+ ]
114
+ }
115
+ ]
116
+ }
117
+ ```
118
+
119
+ `totalCount` = **按 `requestId` 去重后的条数**(= collect 取到的全部条数),**不是** `wfIdGroup[].count` 之和(优先组是叠加视图)。只有优先组 `defaultExpanded=true`;普通组先放汇总项,再放单条。
120
+
121
+ ## 5. 详情、单条摘要和组摘要
122
+
123
+ - 详情接口固定使用 `isReadRequest:false`,默认并发 10、单条超时 10 秒;单条失败不拖垮整批。
124
+ - 详情字段先清洗 HTML/CSS,再按业务规则筛选;正文不做固定长度截断,也不因为长度被丢弃。
125
+ - **两个持久化缓存**,key 都是 `requestId__lastFeedBackDateTime`,有效期 7 天、清理间隔 3 天、无版本号:
126
+ - `todo-detail-cache.json`:`context` / `contextAvailable` / `metrics` / `fields`,**详情为空也写**(否则每次都要重拉);
127
+ - `todo-summary-cache.json`:单条摘要文本(`SUMMARY_CACHE_VERSION = 2`)。
128
+ 位置优先级:`WEAVER_WORK_CLI_CACHE_DIR` → `WEAVER_WORK_CLI_SKILL_DIR` → 已知 Agent skill 目录 → CLI 状态目录兜底。
129
+ - **摘要缓存只省 AI,不省详情**:阈值指标(请假天数/报销金额)只认流程详情数据,所以 `smartGroup` 对全部条目走一次「缓存优先」的详情取数(命中即零请求)。详情为空的条目不进入 `summaryBatches`,CLI 用标题兜底。
130
+ - 详情请求**发生在 `smartGroup`**(`summary` 阶段复用同一份缓存,实测暖缓存 0 请求),因此看耗时与请求数要看 `smartGroup` 的 `summaryStats` / `detailStats`,不要再看 `summary` 阶段。
131
+ - 单条摘要默认建议 50 字,组摘要默认建议 100 字;调用方可通过 `summaryMaxChars`、`groupSummaryMaxChars` 覆盖。CLI 只校验、告警和回填,不截断模型文案。
132
+ - 摘要批次默认按 `maxCharsPerBatch=24000` 或 `maxItemsPerBatch=100` 切分;这两个参数只影响模型批次,不改变最终业务数据。
133
+ - 确定性路径下请假、财务、客户问题支持的固定组摘要由 CLI 生成,其余类型无汇总项;显式 AI 分组路径的组摘要先留空,再由 `groupSummary` 回填。
134
+
135
+ ## 6. 输出和兼容边界
136
+
137
+ - 最终业务数据完整返回,不因输出大小裁剪或改写;warnings 可在 operation 信封和 `meta.warnings` 中出现,其他诊断字段只放在 `meta`,不混入业务对象。
138
+ - `detailPrefetch`、`pageSize` 和旧拼写 `smartSmmaryTitle` 仅为兼容入口,新调用使用当前字段。
139
+ - `workflow.todoSmartConvert` 只读。提交、置已读、催办、共享、转发、退回等操作继续使用 `workflow.batch*`。
140
+
141
+ ## 7. 验证
142
+
143
+ macOS/Linux:
144
+
145
+ ```bash
146
+ npm run build
147
+ npx tsc --noEmit --pretty false
148
+ git diff --check
149
+ ```
150
+
151
+ Windows PowerShell:
152
+
153
+ ```powershell
154
+ npm run build
155
+ npx tsc --noEmit --pretty false
156
+ git diff --check
157
+ ```
158
+
159
+ 专项测试:
160
+
161
+ macOS/Linux:
162
+
163
+ ```bash
164
+ node --import tsx --test test/business/todoSmartConvert/todoSmartConvert.test.ts
165
+ ```
166
+
167
+ Windows PowerShell:
168
+
169
+ ```powershell
170
+ node --import tsx --test test/business/todoSmartConvert/todoSmartConvert.test.ts
171
+ ```
@@ -0,0 +1,236 @@
1
+ # todoSmartConvert 返回数据结构契约
2
+
3
+ > 核对来源:`src/shortcuts/workflow/operations/todoSmartConvert/*`、`src/shortcuts/workflow/index.ts`、`src/shortcuts/workflow/operations.ts`
4
+ > 核对日期:2026-10-27(ai二次分组改造收口)|已用本地 `stage=smartGroup` 实跑验证(不走 OA 接口)
5
+
6
+ ## 0. 三层结构,别混着看
7
+
8
+ ```
9
+ CLI 输出信封 { schemaVersion, ok, operation, data, meta, warnings }
10
+ └─ data(业务对象) { smartSummaryTitle, totalCount, wfIdGroup } ← 只有这三个键
11
+ └─ wfIdGroup[] { id, topPriority, title, count, defaultExpanded, displayItems }
12
+ └─ displayItems[] { type:"request", requestData } | { type:"group", …, requestDatas }
13
+ └─ requestData 14 个固定字段
14
+ ```
15
+
16
+ **诊断信息(warnings / timing / 各类 stats)一律在信封与 `meta` 里,不进 `data`。**
17
+
18
+ ## 1. 输出信封
19
+
20
+ | 字段 | 类型 | 规则 |
21
+ |---|---|---|
22
+ | `schemaVersion` | number | 固定 `1` |
23
+ | `ok` | boolean | 成功恒 `true`;失败时进程退出码非 0,**错误 JSON 只写 stderr** |
24
+ | `operation` | string | 固定 `workflow.todoSmartConvert` |
25
+ | `data` | object | 业务对象,见 §2 |
26
+ | `meta` | object | `branch` / `stage` / 阶段标记(`aiGrouped`、`aiSummarized`、`deterministic`、`withDetailContext`、`resumed`)/ `timing` / **`warnings`** |
27
+ | `warnings` | array | ⚠️ 实测**恒为空数组**,真实告警在 `meta.warnings`(见 §12-1) |
28
+
29
+ `meta.timing` 形态:
30
+
31
+ ```json
32
+ { "startedAt": "…", "finishedAt": "…", "stage": "smartGroup",
33
+ "steps": [{ "name": "smartGroup", "kind": "cli|ai|stage", "durationMs": 4 }],
34
+ "durationMs": 4 }
35
+ ```
36
+
37
+ - `kind:"ai"` 的步骤只有在调用方传了 `skillTiming.aiDurationMs > 0` 时才出现。
38
+ - `durationMs` = `max(各步之和, 传入 startedAt 到现在的墙钟时间)`。
39
+
40
+ 该 operation 在输出层显式 `persist:false`:**不会产生 `refId` / `resultFile`,也不做 24 KB 折叠**。
41
+
42
+ ## 2. 业务对象(唯一合法形态)
43
+
44
+ | 字段 | 类型 | 规则 |
45
+ |---|---|---|
46
+ | `smartSummaryTitle` | string | 默认 `您当前有 N 条待办,清单如下:`;上游传了 `smartSummaryTitle`(或旧拼写 `smartSmmaryTitle`)则沿用 |
47
+ | `totalCount` | number | **按 `requestId` 去重后的条数**(= collect 取到的全部条数)。**不是** Σ `count`:优先组是叠加视图,同一条会同时出现在优先组和工作流组 |
48
+ | `wfIdGroup` | array | 大分组数组,顺序 = 首次出现顺序(优先组恒第一) |
49
+
50
+ 键顺序固定为 `smartSummaryTitle → totalCount → wfIdGroup`,**不接受第四个键**。
51
+
52
+ ## 3. 组对象 `wfIdGroup[i]`
53
+
54
+ | 字段 | 类型 | 规则 |
55
+ |---|---|---|
56
+ | `id` | string | 优先组恒 `"priority"`;普通组取 `workflowId`(同名多版本取第一个);非 workflow 维度取分组键;未分类组可能回落成某条真实 `workflowId`(见 §12-3) |
57
+ | `topPriority` | boolean | 只有「建议优先处理的流程」为 `true` |
58
+ | `title` | string | 优先组固定文案;普通组 = `workflowName` |
59
+ | `count` | number | **= 该组 `displayItems` 摊平后的条数**(回灌时会重算,不采信输入里的值) |
60
+ | `defaultExpanded` | boolean | 只有优先组 `true`;普通组与汇总项恒 `false` |
61
+ | `displayItems` | array | 混合数组:汇总项(`type:"group"`)在前,单条(`type:"request"`)在后 |
62
+
63
+ 优先组是**叠加视图**:入选优先组的条目**仍保留在各自工作流组里**(优先组只是置顶入口)。因此 Σ`count` > `totalCount` 是正常现象,差值 = 优先组条数。
64
+
65
+ ## 4. `displayItems[i]` 两种元素
66
+
67
+ **单独一条:**
68
+
69
+ ```json
70
+ { "type": "request", "requestData": { … §5 } }
71
+ ```
72
+
73
+ **汇总项(折叠):**
74
+
75
+ | 字段 | 类型 | 规则 |
76
+ |---|---|---|
77
+ | `type` | `"group"` | 固定 |
78
+ | `id` | string | `父组id-序号`,如 `1003-1`,序号从 1 起(同组内重排后会变) |
79
+ | `title` | string | 子组名:AI 给的名字 / CLI 三类固定标题(`1天以内` / `100元以内` / `已过期及今天即将过期的流程`)。**不存在** `other`、也**不存在**「与父组同名」的兜底标题 |
80
+ | `count` | number | = `requestDatas.length` |
81
+ | `summary` | string | 组摘要;确定性路径可能有固定文案,AI 路径未回填时为空串 |
82
+ | `defaultExpanded` | boolean | 恒 `false`(汇总项一律收缩) |
83
+ | `requestDatas` | array | 明细,元素同 §5 |
84
+
85
+ ## 5. `requestData` 14 个字段(全部必出)
86
+
87
+ | 字段 | 类型 | 说明 |
88
+ |---|---|---|
89
+ | `requestId` | string | 19 位,**禁止转数字** |
90
+ | `workflowId` | string | 同上 |
91
+ | `userMasterId` | string | 当前处理人 |
92
+ | `isremark` | number | 0/1,是否已读语义位 |
93
+ | `nodeId` | string | 当前节点 |
94
+ | `overTime` | boolean | 是否超时(原始布尔位) |
95
+ | `userOverTime` | string | 超时显示文案(`昨天到期` / `11-01到期`),**不一定代表已超时** |
96
+ | `requestLevel` | number | 0 正常 / 1 重要 / 2 紧急 |
97
+ | `requestLevelShow` | string | 已去掉 HTML 的等级文案 |
98
+ | `requestNameTitle` | string | 标题(纯文本) |
99
+ | `creater` | string | 发起人 ID |
100
+ | `createrName` | string | 发起人姓名 |
101
+ | `lastFeedBackDateTime` | string | 最后反馈时间,空串 = 无反馈;**摘要缓存与详情缓存 key 的一半**(`requestId__lastFeedBackDateTime`,流程有新反馈 ⇒ key 变 ⇒ 缓存自然失效) |
102
+ | `summary` | string | 单条摘要,**恒为 string**,未写时 `""` |
103
+
104
+ **不出现**在 `requestData` 里的字段(只在中间态 `items` 上):`workflowName`、`receivedTime`、`flowStatus`、`requestMark`、`amount`、`stats`。
105
+
106
+ ## 6. 八条不变式(review 重点)
107
+
108
+ | # | 不变式 | 校验位置 |
109
+ |---|---|---|
110
+ | 1 | `totalCount` = 按 `requestId` 去重后的条数(**不是** Σ `count`) | `totalTodoCount()` |
111
+ | 2 | 每个 `count` = 该组 `displayItems` 摊平条数,**不采信输入** | `validateFinalGroups()` |
112
+ | 3 | 每条待办在**一个组内**只出现一次;优先组的条目会同时出现在其工作流组(叠加视图) | `used` 集合 / `selectPriorityRows()` |
113
+ | 4 | 组内 <3 条一律平铺单条,**不产生任何汇总项**(给了 `aiGroups` 也忽略) | `GROUP_MIN_ITEMS_FOR_SUBGROUP = 3` |
114
+ | 5 | 汇总项 `id` = `父组id-序号`,序号从 1 起且仅在组内唯一 | `groupItemsOf()` |
115
+ | 6 | 只有优先组 `defaultExpanded = true`;汇总项恒 `false` | `runSmartGroup()` 返回处 |
116
+ | 7 | 业务对象只有三个键;诊断信息只能在 `meta` | `finish()` + `planTodoOutput()` |
117
+ | 8 | 所有 ID 一律 string;除计数类(`count`/`totalCount`)与标记类(`isremark`/`requestLevel`)外不得是 number | `readAiGroups()` 等校验 |
118
+ | 9 | 子组至少 2 条(<2 解散后条目平铺);不存在 `other` 兜底汇总项 | `runSmartGroup()` 子组收口 |
119
+
120
+ ## 7. 各 stage 的 `data` 形态不同(最易踩)
121
+
122
+ | stage | `data` 的键 | 分组是最终态吗 |
123
+ |---|---|---|
124
+ | `collect` | `stage, query{…}, fetched, items[], wfIdGroup[], groupingBrief[], groupInfo, detailStats, warnings, markdown` | ❌ 中间态:`wfIdGroup[]` 用 `requestDatas` + `stats`,**没有** `displayItems` |
125
+ | `group` | `stage, groupBy, smartSummaryTitle, totalInput, total, duplicateCount, groupCount, totals, wfIdGroup[], restTotal, restGroupCount, restGroups, warnings, markdown` | ❌ 同上 |
126
+ | `smartGroup` | `smartSummaryTitle, totalCount, wfIdGroup[]`(+ 可选 `summaryBatches`/`summaryStats`) | ✅ 最终三键 |
127
+ | `summaryContext` | 三键 + `summaryBatches` + `summaryStats` | ✅ |
128
+ | `summary` | 三键(统计在 `meta`) | ✅ |
129
+ | `groupSummaryContext` | 三键 + `groupSummaryBatches` + `groupSummaryStats` | ✅ |
130
+ | `groupSummary` | 三键(统计在 `meta`) | ✅ |
131
+ | `aggregate` | `groupBy, totalInput, total, duplicateCount, groupCount, totals, groups, restGroups, warnings, markdown` | ❌ 旧兼容结构,**不产三键** |
132
+
133
+ > 只有 `smartGroup` 之后才是前端要的 `Comp.WflTodoSummary` 载荷;`collect` 的 `wfIdGroup` 直接拿去渲染会缺 `displayItems`。
134
+
135
+ ## 8. 回灌输入契约
136
+
137
+ - `stage` 显式优先;**三键结构里故意不带 `stage`**,未传时按入参形态推断:
138
+
139
+ | 入参形态 | 推断结果 |
140
+ |---|---|
141
+ | 有 `wfIdGroup` + 有 `aiGroupSummaries` | `groupSummary` |
142
+ | 有 `wfIdGroup` + 有 `aiSummaries` | `summary` |
143
+ | 有 `wfIdGroup`,且已有非空单条 `summary` | `groupSummaryContext` |
144
+ | 有 `wfIdGroup`,单条摘要全空 | `summaryContext` |
145
+ | 其它 | `auto` |
146
+
147
+ - 输入字段白名单 `assertKnownKeys`,多一个键 → `input_unknown_key` 硬失败。
148
+ - ⚠️ **`items` 必须带 `workflowName`**:分组键取的是 `workflowName`,不是 `workflowId`。实测漏传时 4 个不同工作流全部塌进「未分类」一组(`id` 还会回落成第一条的 `workflowId`)。
149
+ - `aiGroups` 每项只允许 `groupId` / `title` / `requestIds`(与 `seq` 二选一);`priority` 组不可二次分组。
150
+ - `aiSummaries` 每项只允许 `requestId` / `summary`;`aiGroupSummaries` 每项只允许 `groupId` / `summary`(用汇总项 id,不是父组 id)。
151
+
152
+ ## 9. 摘要字段的填充规则(三处语义不同)
153
+
154
+ | 阶段 | 行为 |
155
+ |---|---|
156
+ | `summaryContext` | 详情为空的条目**不喂 AI**,CLI 直接把 `requestNameTitle` 写进 `summary`(确定性兜底) |
157
+ | `summary` | 只写 `aiSummaries` 里给的;**漏写保留空串,不拿标题凑**;已有的标题/缓存摘要会被保留 |
158
+ | `groupSummary` | 只填 `displayItems[].summary`;确定性路径(请假 / 财务 / 客户问题支持)的固定文案**不会被覆盖** |
159
+
160
+ 满意度类不走 AI:单条 `summary` 直接用 `requestNameTitle` 兜底(**不留空串**)。
161
+
162
+ 摘要缓存**只省 AI,不省详情**:命中摘要缓存的条目仍会按 `requestId__lastFeedBackDateTime` 查详情缓存(命中则零请求),因为阈值指标只认流程详情数据。
163
+
164
+ ## 10. 错误码(`ok:false` + `error.subtype`)
165
+
166
+ | 错误 | 含义 |
167
+ |---|---|
168
+ | `stage_invalid` / `input_unknown_key` | stage 或输入字段不合法 |
169
+ | `items_required` / `items_invalid` / `items_too_many` | 缺 items / 形态错 / 超 500 条 |
170
+ | `ai_groups_invalid` | aiGroups 结构、字段、seq 类型不合法 |
171
+ | `ai_group_unknown_group` | groupId 不在本次分组结果中 |
172
+ | `ai_group_priority_unsupported` | 想给优先组做二次分组 |
173
+ | `ai_group_seq_out_of_range` | seq 越界 |
174
+ | `ai_group_unknown_request` / `ai_group_duplicate_request` | 条目不存在 / 被分进多个子组 |
175
+ | `ai_groups_required` | **内部留言 ≥3 条缺本组计划**:硬失败(`retryAllowed:true`),报错带 `groupId` 与 `1..N` 序号;非内部留言的类型不适用此错误 |
176
+ | ~~`ai_groups_missing_group`~~ | **已删除(2026-10-27)**:非内部留言的类型「意图适用但没给计划」改为「视为无意图 + warning」 |
177
+ | `ai_summaries_required` / `ai_summary_invalid` | 缺回填数据 / 结构错 |
178
+ | `ai_summary_duplicate_request` / `ai_summary_unknown_request` | 重复 / 未知 requestId |
179
+ | `ai_group_summaries_required` / `ai_group_summary_unknown_group` | 同上,按汇总项 id |
180
+ | `final_groups_required` / `wfidgroup_invalid` | 缺最终结构 / 结构不合法 |
181
+ | `forbidden_endpoint` | 取数阶段误调了 `getTodo*` 其它接口 |
182
+
183
+ ## 11. 示例(合成数据,按 2026-10-27 规则构造:5 条 / 3 组,`withDetailContext:false`)
184
+
185
+ ```json
186
+ {
187
+ "schemaVersion": 1,
188
+ "ok": true,
189
+ "operation": "workflow.todoSmartConvert",
190
+ "data": {
191
+ "smartSummaryTitle": "您当前有 5 条待办,清单如下:",
192
+ "totalCount": 5,
193
+ "wfIdGroup": [
194
+ { "id": "priority", "topPriority": true, "title": "建议优先处理的流程", "count": 1,
195
+ "defaultExpanded": true,
196
+ "displayItems": [{ "type": "request", "requestData": { "requestId": "8800000000000000001", "…": "共 14 个字段", "summary": "" } }] },
197
+ { "id": "1003", "topPriority": false, "title": "费用报销", "count": 4,
198
+ "defaultExpanded": false,
199
+ "displayItems": [
200
+ { "type": "group", "id": "1003-1", "title": "差旅报销", "count": 2,
201
+ "summary": "", "defaultExpanded": false, "requestDatas": [{ "…": "" }, { "…": "" }] },
202
+ { "type": "request", "requestData": { "requestId": "8800000000000000001", "…": "" } },
203
+ { "type": "request", "requestData": { "requestId": "8800000000000000005", "…": "" } }
204
+ ] },
205
+ { "id": "1001", "topPriority": false, "title": "内部留言", "count": 1,
206
+ "defaultExpanded": false,
207
+ "displayItems": [{ "type": "request", "requestData": { "requestId": "8800000000000000002", "…": "" } }] }
208
+ ]
209
+ },
210
+ "meta": {
211
+ "branch": "todo-smart-convert", "stage": "smartGroup", "aiGrouped": true,
212
+ "timing": { "stage": "smartGroup", "steps": [{ "name": "smartGroup", "kind": "cli", "durationMs": 4 }], "durationMs": 4 },
213
+ "warnings": [
214
+ "「建议优先处理的流程」筛出 1 条:超时 1 条 / 等级命中 1 条(两者都满足 1 条),等级口径=紧急(requestLevel=2);这 1 条同时保留在各自工作流分组中(叠加视图:Σcount 会大于 totalCount,totalCount 按去重条数计 = 5)",
215
+ "withDetailContext=false:本次未拉取任何详情——请假/财务阈值改为按标题识别、跳过整组空判定,也不产 summaryBatches",
216
+ "「费用报销」按财务类 100元以内确定性汇总,其余条目平铺"
217
+ ]
218
+ },
219
+ "warnings": []
220
+ }
221
+ ```
222
+
223
+ `totalCount = 5`,但 Σ`count` = 1 + 4 + 1 = **6**:那条超时件(`8800000000000000001`,属于"费用报销")同时在优先组和"费用报销"组里各出现一次。组顺序是「优先组 → 费用报销 → 内部留言」,普通组按源数据首次出现顺序排列,优先条目不再被剔除。
224
+
225
+ 构造要点(对齐当前规则):
226
+ - 子组 `差旅报销` 是 **2 条**(≥2 才成立;1 条会被解散成单条);
227
+ - 阈值组之外的 2 条是**单条**(`type:"request"`),不是 `other` 汇总项;
228
+ - `内部留言` 只有 1 条(<3 硬门槛),所以平铺单条、不给它汇总项。
229
+
230
+ ## 12. review 时值得盯的五个点
231
+
232
+ 1. **顶层 `warnings` 恒空**:`finish()` 把告警同时写进 `WorkflowEnvelope.warnings` 和 `meta.warnings`,但 `workflow/index.ts` 的 `successEnvelope()` 硬编码 `warnings: []`,前者被丢弃。任何「有没有告警」的判断都必须看 `meta.warnings`。要么删掉信封级字段,要么让它透传。
233
+ 2. **`requestData` 里混进了技术字段 `lastFeedBackDateTime`**:它现在是**摘要缓存 + 详情缓存**共同的 key 组成部分(`requestId__lastFeedBackDateTime`),前端渲染不需要。是否应该留在业务对象里值得确认——但注意删掉它会让两个缓存都失去「流程一动就换键」的能力。
234
+ 3. **「未分类」组的 `id` 可能等于某个真实 `workflowId`**:`group.ts` 里 `id = row.workflowId || name:${key}`,所以当 `workflowName` 缺失时,这一组的 `id` 会回落成该组第一条的 workflowId。二次分组传 `groupId` 时容易与真实工作流组混淆。
235
+ 4. **与前端 mock 目标的差异**(`docs/todo-smart-convert/mock/ai-todo-query-target.json`):mock 的 `requestData` 带 `requestNameHtml`,实际输出**没有**这个字段;mock 里 ID 是数字,实际是字符串。若前端组件依赖 `requestNameHtml`,需要回退到 `requestNameTitle`。
236
+ 5. **详情请求的耗时从 `summary` 挪到了 `smartGroup`**:冷缓存下 `smartGroup` 承担全部详情请求(近一年 77 条 = 77 次 `/getWorkflowRequest`),`summary` 阶段实测 **0 请求**(12ms)。所以看 `meta.timing` 时别再用「`summary` 阶段请求数」判断有没有拉详情,要看 `smartGroup` 的 `summaryStats.detailNetworkFetched` / `detailFetchStats`。