weaver-work-cli 0.1.8 → 0.1.21

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 (271) hide show
  1. package/README.md +25 -17
  2. package/dist/cmd/action.js +76 -0
  3. package/dist/cmd/index.js +2 -0
  4. package/dist/cmd/refs/index.js +103 -0
  5. package/dist/cmd/register.js +31 -0
  6. package/dist/cmd/root.js +1 -0
  7. package/dist/cmd/setup/index.js +25 -5
  8. package/dist/cmd/skills/index.js +37 -5
  9. package/dist/core/connector.js +54 -0
  10. package/dist/core/context.js +2 -1
  11. package/dist/core/input.js +48 -0
  12. package/dist/core/output.js +298 -3
  13. package/dist/core/refs.js +435 -0
  14. package/dist/core/state.js +26 -0
  15. package/dist/internal/e10/auth/commands.js +34 -19
  16. package/dist/internal/e10/auth/session.js +74 -2
  17. package/dist/internal/e10/auth/xiaoe.js +359 -94
  18. package/dist/internal/e10/context.js +18 -6
  19. package/dist/internal/e10/request-runtime.js +8 -1
  20. package/dist/internal/skills/install.js +148 -28
  21. package/dist/internal/skills/reader.js +12 -9
  22. package/dist/shortcuts/blog/continuation.js +74 -0
  23. package/dist/shortcuts/blog/errors.js +60 -0
  24. package/dist/shortcuts/blog/host.js +175 -0
  25. package/dist/shortcuts/blog/index.js +324 -0
  26. package/dist/shortcuts/blog/manifest.js +415 -0
  27. package/dist/shortcuts/blog/operations/comment.js +125 -0
  28. package/dist/shortcuts/blog/operations/comments.js +31 -0
  29. package/dist/shortcuts/blog/operations/edit.js +131 -0
  30. package/dist/shortcuts/blog/operations/export.js +46 -0
  31. package/dist/shortcuts/blog/operations/like.js +80 -0
  32. package/dist/shortcuts/blog/operations/publish.js +157 -0
  33. package/dist/shortcuts/blog/operations/query-user.js +58 -0
  34. package/dist/shortcuts/blog/operations/query.js +96 -0
  35. package/dist/shortcuts/blog/operations/registry.js +54 -0
  36. package/dist/shortcuts/blog/operations/remind.js +176 -0
  37. package/dist/shortcuts/blog/operations/shared.js +330 -0
  38. package/dist/shortcuts/blog/operations/types.js +16 -0
  39. package/dist/shortcuts/blog/operations.js +9 -0
  40. package/dist/shortcuts/caizhilian/continuation.js +70 -0
  41. package/dist/shortcuts/caizhilian/errors.js +53 -0
  42. package/dist/shortcuts/caizhilian/host.js +190 -0
  43. package/dist/shortcuts/caizhilian/index.js +186 -0
  44. package/dist/shortcuts/caizhilian/manifest.js +42 -0
  45. package/dist/shortcuts/caizhilian/operations/api.js +801 -0
  46. package/dist/shortcuts/caizhilian/operations/confirm.js +75 -0
  47. package/dist/shortcuts/caizhilian/operations/files.js +306 -0
  48. package/dist/shortcuts/caizhilian/operations/form.js +170 -0
  49. package/dist/shortcuts/caizhilian/operations/handlers/collab.js +611 -0
  50. package/dist/shortcuts/caizhilian/operations/handlers/document.js +890 -0
  51. package/dist/shortcuts/caizhilian/operations/handlers/file-collection.js +1227 -0
  52. package/dist/shortcuts/caizhilian/operations/handlers/file-upload.js +192 -0
  53. package/dist/shortcuts/caizhilian/operations/handlers/fileops.js +570 -0
  54. package/dist/shortcuts/caizhilian/operations/handlers/folder.js +1024 -0
  55. package/dist/shortcuts/caizhilian/operations/handlers/search.js +318 -0
  56. package/dist/shortcuts/caizhilian/operations/handlers/setting-attach.js +294 -0
  57. package/dist/shortcuts/caizhilian/operations/handlers/setting-backup.js +423 -0
  58. package/dist/shortcuts/caizhilian/operations/handlers/setting-encrypt.js +238 -0
  59. package/dist/shortcuts/caizhilian/operations/handlers/setting-storage.js +364 -0
  60. package/dist/shortcuts/caizhilian/operations/handlers/system.js +106 -0
  61. package/dist/shortcuts/caizhilian/operations/registry.js +151 -0
  62. package/dist/shortcuts/caizhilian/operations/shared.js +408 -0
  63. package/dist/shortcuts/caizhilian/operations/specs.js +698 -0
  64. package/dist/shortcuts/caizhilian/operations/types.js +57 -0
  65. package/dist/shortcuts/caizhilian/operations.js +21 -0
  66. package/dist/shortcuts/contract/continuation.js +70 -0
  67. package/dist/shortcuts/contract/errors.js +62 -0
  68. package/dist/shortcuts/contract/host.js +205 -0
  69. package/dist/shortcuts/contract/index.js +318 -0
  70. package/dist/shortcuts/contract/manifest.js +596 -0
  71. package/dist/shortcuts/contract/operations/clause-reference.js +78 -0
  72. package/dist/shortcuts/contract/operations/form-info.js +371 -0
  73. package/dist/shortcuts/contract/operations/read.js +118 -0
  74. package/dist/shortcuts/contract/operations/registry.js +43 -0
  75. package/dist/shortcuts/contract/operations/request.js +300 -0
  76. package/dist/shortcuts/contract/operations/shared.js +133 -0
  77. package/dist/shortcuts/contract/operations/subject.js +157 -0
  78. package/dist/shortcuts/contract/operations/template.js +81 -0
  79. package/dist/shortcuts/contract/operations/types.js +7 -0
  80. package/dist/shortcuts/contract/operations/workflow.js +52 -0
  81. package/dist/shortcuts/contract/operations/write.js +131 -0
  82. package/dist/shortcuts/contract/operations.js +8 -0
  83. package/dist/shortcuts/hrm/index.js +83 -2
  84. package/dist/shortcuts/hrm/manifest.js +16 -9
  85. package/dist/shortcuts/hrm/operations/employee.js +160 -30
  86. package/dist/shortcuts/hrm/operations/org.js +17 -3
  87. package/dist/shortcuts/hrm/operations/position.js +18 -2
  88. package/dist/shortcuts/hrm/operations/registry.js +2 -0
  89. package/dist/shortcuts/hrm/operations/resolve.js +449 -0
  90. package/dist/shortcuts/index.js +5 -1
  91. package/dist/shortcuts/invoice/index.js +74 -36
  92. package/dist/shortcuts/invoice/manifest.js +674 -4
  93. package/dist/shortcuts/invoice/markdown.js +172 -0
  94. package/dist/shortcuts/invoice/operations/add.js +3 -3
  95. package/dist/shortcuts/invoice/operations/delete.js +2 -2
  96. package/dist/shortcuts/invoice/operations/get.js +10 -2
  97. package/dist/shortcuts/invoice/operations/import.js +2 -2
  98. package/dist/shortcuts/invoice/operations/registry.js +8 -1
  99. package/dist/shortcuts/invoice/operations/reim-auto.js +719 -0
  100. package/dist/shortcuts/invoice/operations/reim.js +85 -2
  101. package/dist/shortcuts/invoice/operations/shared.js +75 -8
  102. package/dist/shortcuts/invoice/operations/update.js +2 -2
  103. package/dist/shortcuts/invoice/operations/upload-and-validate.js +93 -0
  104. package/dist/shortcuts/invoice/operations/validate-preview.js +2 -1
  105. package/dist/shortcuts/invoice/projection.js +328 -0
  106. package/dist/shortcuts/meeting/host.js +18 -1
  107. package/dist/shortcuts/meeting/manifest.js +30 -18
  108. package/dist/shortcuts/meeting/operations/eb/catalog.js +390 -0
  109. package/dist/shortcuts/meeting/operations/eb/conflict.js +389 -0
  110. package/dist/shortcuts/meeting/operations/eb/factory.js +389 -0
  111. package/dist/shortcuts/meeting/operations/eb/index.js +22 -0
  112. package/dist/shortcuts/meeting/operations/env.js +8 -1
  113. package/dist/shortcuts/meeting/operations/registry.js +2 -0
  114. package/dist/shortcuts/meeting/operations/signs.js +216 -2
  115. package/dist/shortcuts/workflow/endpoints.js +2 -1
  116. package/dist/shortcuts/workflow/errors.js +8 -2
  117. package/dist/shortcuts/workflow/host.js +11 -4
  118. package/dist/shortcuts/workflow/operations/form.js +1016 -590
  119. package/dist/shortcuts/workflow/operations/read.js +913 -231
  120. package/dist/shortcuts/workflow/operations/shared.js +21 -0
  121. package/dist/shortcuts/workflow/operations/write.js +750 -52
  122. package/dist/shortcuts/workflow/operations.js +36 -10
  123. package/docs/_catalog.md +6 -0
  124. package/docs/agent-invoice.md +5 -1
  125. package/docs/agent-io-channel-design.md +468 -0
  126. package/docs/agent-skill-install.md +9 -4
  127. package/docs/blog.md +123 -0
  128. package/docs/caizhilian.md +304 -0
  129. package/docs/contract.md +91 -0
  130. package/docs/e10-auth.md +4 -2
  131. package/docs/hrm.md +53 -11
  132. package/docs/invoice.md +5 -1
  133. package/docs/meeting.md +1 -1
  134. package/docs/operation-manual.md +15 -3
  135. package/docs/skill-review-2026-09-19.md +2 -2
  136. package/package.json +1 -1
  137. package/scripts/package-skill.mjs +32 -20
  138. package/scripts/xiaoe_env.py +118 -0
  139. package/skill-template/business-info.json +113 -14
  140. package/skill-template/domains/blog.md +24 -0
  141. package/skill-template/domains/caizhilian.md +22 -0
  142. package/skill-template/domains/hrm.md +2 -1
  143. package/skill-template/domains/jinchengda.md +23 -0
  144. package/skill-template/domains/meeting.md +1 -1
  145. package/skill-template/domains/workflow.md +1 -1
  146. package/skill-template/skill-template.md +1 -1
  147. package/skills/weaver-e10-blog/SKILL.md +111 -0
  148. package/skills/weaver-e10-blog/references/blog-comment.md +51 -0
  149. package/skills/weaver-e10-blog/references/blog-comments.md +39 -0
  150. package/skills/weaver-e10-blog/references/blog-default-permission.md +38 -0
  151. package/skills/weaver-e10-blog/references/blog-disabled-capabilities.md +11 -0
  152. package/skills/weaver-e10-blog/references/blog-edit.md +51 -0
  153. package/skills/weaver-e10-blog/references/blog-export.md +42 -0
  154. package/skills/weaver-e10-blog/references/blog-like.md +50 -0
  155. package/skills/weaver-e10-blog/references/blog-publish.md +52 -0
  156. package/skills/weaver-e10-blog/references/blog-query-user.md +45 -0
  157. package/skills/weaver-e10-blog/references/blog-query.md +49 -0
  158. package/skills/weaver-e10-blog/references/blog-remind.md +51 -0
  159. package/skills/weaver-e10-blog/references/blog-resolve-users.md +38 -0
  160. package/skills/weaver-e10-blog/references/source-manifest.json +1540 -0
  161. package/skills/weaver-e10-caizhilian/SKILL.md +217 -0
  162. package/skills/weaver-e10-caizhilian/references/agent-entry.md +106 -0
  163. package/skills/weaver-e10-caizhilian/references/collaboration.md +112 -0
  164. package/skills/weaver-e10-caizhilian/references/document.md +111 -0
  165. package/skills/weaver-e10-caizhilian/references/file-collection.md +173 -0
  166. package/skills/weaver-e10-caizhilian/references/file-upload.md +309 -0
  167. package/skills/weaver-e10-caizhilian/references/files.md +120 -0
  168. package/skills/weaver-e10-caizhilian/references/folder.md +122 -0
  169. package/skills/weaver-e10-caizhilian/references/org-browser.md +104 -0
  170. package/skills/weaver-e10-caizhilian/references/setting-attach.md +440 -0
  171. package/skills/weaver-e10-caizhilian/references/setting-backup.md +372 -0
  172. package/skills/weaver-e10-caizhilian/references/setting-encrypt.md +399 -0
  173. package/skills/weaver-e10-caizhilian/references/setting-storage.md +603 -0
  174. package/skills/weaver-e10-caizhilian/references/source-manifest.json +5694 -0
  175. package/skills/weaver-e10-caizhilian/references/withheld.md +93 -0
  176. package/skills/weaver-e10-caizhilian/references/write-protocol.md +120 -0
  177. package/skills/weaver-e10-calendar/SKILL.md +4 -3
  178. package/skills/weaver-e10-ebuilder-form/SKILL.md +4 -9
  179. package/skills/weaver-e10-ebuilder-form/references/context-routing.md +0 -9
  180. package/skills/weaver-e10-ebuilder-form/references/custom-api.md +1 -5
  181. package/skills/weaver-e10-ebuilder-form/references/fields-browser.md +1 -5
  182. package/skills/weaver-e10-ebuilder-form/references/list-nlist.md +1 -5
  183. package/skills/weaver-e10-ebuilder-form/references/openapi-read.md +1 -5
  184. package/skills/weaver-e10-ebuilder-form/references/openapi-write.md +1 -5
  185. package/skills/weaver-e10-esb/SKILL.md +2 -2
  186. package/skills/weaver-e10-esb/references/source-manifest.json +9 -9
  187. package/skills/weaver-e10-hrm/SKILL.md +104 -9
  188. package/skills/weaver-e10-hrm/references/employee.md +89 -15
  189. package/skills/weaver-e10-hrm/references/org.md +114 -5
  190. package/skills/weaver-e10-hrm/references/position.md +14 -7
  191. package/skills/weaver-e10-hrm/references/source-manifest.json +1 -1
  192. package/skills/weaver-e10-jinchengda/SKILL.md +167 -0
  193. package/skills/weaver-e10-jinchengda/references/contract-agent-entry.md +82 -0
  194. package/skills/weaver-e10-jinchengda/references/contract-clause-recommender.md +69 -0
  195. package/skills/weaver-e10-jinchengda/references/contract-clause-reference-save.md +78 -0
  196. package/skills/weaver-e10-jinchengda/references/contract-disabled-capabilities.md +27 -0
  197. package/skills/weaver-e10-jinchengda/references/contract-request-status.md +71 -0
  198. package/skills/weaver-e10-jinchengda/references/contract-request-write.md +94 -0
  199. package/skills/weaver-e10-jinchengda/references/contract-signing-subjects.md +89 -0
  200. package/skills/weaver-e10-jinchengda/references/contract-subject-create.md +77 -0
  201. package/skills/weaver-e10-jinchengda/references/contract-template-selector.md +66 -0
  202. package/skills/weaver-e10-jinchengda/references/contract-workflow-discovery.md +94 -0
  203. package/skills/weaver-e10-jinchengda/references/source-manifest.json +259 -0
  204. package/skills/weaver-e10-jiuchuanhui/SKILL.md +5 -11
  205. package/skills/weaver-e10-jucailin/SKILL.md +4 -3
  206. package/skills/weaver-e10-mail/SKILL.md +4 -11
  207. package/skills/weaver-e10-meeting/SKILL.md +66 -32
  208. package/skills/weaver-e10-meeting/references/meeting-change.md +5 -2
  209. package/skills/weaver-e10-meeting/references/meeting-create.md +2 -1
  210. package/skills/weaver-e10-meeting/references/meeting-eb.md +114 -0
  211. package/skills/weaver-e10-meeting/references/meeting-lifecycle.md +9 -8
  212. package/skills/weaver-e10-meeting/references/meeting-receipts-signs.md +35 -7
  213. package/skills/weaver-e10-meeting/references/meeting-update.md +1 -1
  214. package/skills/weaver-e10-okr/SKILL.md +6 -11
  215. package/skills/weaver-e10-okr/references/source-manifest.json +3 -3
  216. package/skills/weaver-e10-plan/SKILL.md +3 -3
  217. package/skills/weaver-e10-plan/references/plan-collect-and-compose.md +1 -1
  218. package/skills/weaver-e10-plan/references/plan-overview-and-routing.md +1 -1
  219. package/skills/weaver-e10-plan/references/source-manifest.json +13 -13
  220. package/skills/weaver-e10-qiyecheng/SKILL.md +4 -10
  221. package/skills/weaver-e10-qiyecheng/references/source-manifest.json +9 -9
  222. package/skills/weaver-e10-shared/SKILL.md +42 -34
  223. package/skills/weaver-e10-shared/references/e10-auth-and-session.md +16 -43
  224. package/skills/weaver-e10-shared/references/json-output-contract.md +20 -11
  225. package/skills/weaver-e10-shared/references/non-interactive-environment.md +21 -14
  226. package/skills/weaver-e10-shijingran/SKILL.md +2 -1
  227. package/skills/weaver-e10-shijingran/references/source-manifest.json +9 -9
  228. package/skills/weaver-e10-skill-maker/SKILL.md +4 -10
  229. package/skills/weaver-e10-skill-maker/references/business-skill-generation.md +5 -3
  230. package/skills/weaver-e10-skill-maker/references/module-cli-generation.md +12 -27
  231. package/skills/weaver-e10-skill-maker/references/module-gap-audit-and-repair.md +17 -5
  232. package/skills/weaver-e10-skill-maker/references/source-update-detection.md +4 -27
  233. package/skills/weaver-e10-skill-maker/references/weaver-skill-style.md +1 -1
  234. package/skills/weaver-e10-wenshuding/SKILL.md +5 -16
  235. package/skills/weaver-e10-wenshuding/references/source-manifest.json +7 -7
  236. package/skills/weaver-e10-workflow/SKILL.md +41 -46
  237. package/skills/weaver-e10-workflow/references/source-manifest.json +39 -75
  238. package/skills/weaver-e10-workflow/references/workflow-batch.md +90 -29
  239. package/skills/weaver-e10-workflow/references/workflow-create.md +83 -109
  240. package/skills/weaver-e10-workflow/references/workflow-memory.md +19 -9
  241. package/skills/weaver-e10-workflow/references/workflow-operate.md +2 -2
  242. package/skills/weaver-e10-workflow/references/workflow-query.md +40 -27
  243. package/skills/weaver-e10-workflow/references/workflow-share.md +18 -2
  244. package/skills/weaver-e10-workflow/references/workflow-view.md +3 -4
  245. package/skills/weaver-e10-yepiaotong/SKILL.md +74 -39
  246. package/skills/weaver-e10-yepiaotong/references/invoice-detail.md +22 -1
  247. package/skills/weaver-e10-yepiaotong/references/invoice-enterprise-list.md +3 -1
  248. package/skills/weaver-e10-yepiaotong/references/invoice-output-view.md +135 -0
  249. package/skills/weaver-e10-yepiaotong/references/invoice-personal-list.md +15 -3
  250. package/skills/weaver-e10-yepiaotong/references/invoice-reim.md +1 -1
  251. package/skills/weaver-e10-yepiaotong/references/invoice-summarize.py +217 -0
  252. package/skills/weaver-e10-yepiaotong/references/invoice-upload-and-validate.md +88 -0
  253. package/skills/weaver-e10-yepiaotong/references/invoice-validation-preview.md +7 -2
  254. package/skills/weaver-e10-yepiaotong/references/reim-api-reference.md +89 -9
  255. package/skills/weaver-e10-yepiaotong/references/reim-examples.md +1 -0
  256. package/skills/weaver-e10-yepiaotong/references/reim-expense-subject-rules.md +70 -49
  257. package/skills/weaver-e10-yepiaotong/references/reim-fast-path.md +119 -0
  258. package/skills/weaver-e10-yepiaotong/references/reim-form-fill-rules.md +55 -36
  259. package/skills/weaver-e10-yepiaotong/references/reim-matching-rules.md +4 -0
  260. package/skills/weaver-e10-yepiaotong/references/reim-phone-subsidy-rules.md +149 -0
  261. package/skills/weaver-e10-yepiaotong/references/reim-workflow.md +125 -5
  262. package/skills/weaver-e10-yepiaotong/references/source-manifest.json +430 -63
  263. package/skills/weaver-e10-yimiaoban/SKILL.md +4 -12
  264. package/skills/weaver-e10-yimiaoban/references/source-manifest.json +3 -3
  265. package/skills/weaver-e10-ziguanjia/SKILL.md +2 -1
  266. package/skills/weaver-e10-ziguanjia/references/ziguanjia-agent-entry.md +3 -3
  267. package/tools/weaver-skill-detector-1.0.13/SKILL.md +9 -9
  268. package/tools/weaver-skill-detector-1.0.13/references/checklist.md +3 -3
  269. package/tools/weaver-skill-detector-1.0.13/scripts/__pycache__/validate_skill.cpython-313.pyc +0 -0
  270. package/tools/weaver-skill-detector-1.0.13/scripts/validate_skill.py +98 -91
  271. package/scripts/sync-connector-skills.mjs +0 -172
@@ -17,46 +17,88 @@
17
17
 
18
18
  **6 个批量操作全部是单命令**:没有 `Prepare` / `Apply` 两阶段,也没有 `continuation` 句柄;`confirm: true` 与 `requestIds`(及其余参数)都在**同一次调用**里传。
19
19
 
20
- ## 确认链(6 个批量操作完全一致)
20
+ ## 确认链:按**目标的来源**分两条路径(6 个批量操作一致)
21
21
 
22
- 1. **向用户确认**:**一句话**说明将执行什么(动作 + 条数 + 目标来源,如"把上一轮列表的前 2 条批量提交掉"),然后**等用户明确确认**。**这一步就是确认本身**——不要为它再另发一段"摘要 / 计划";目标条数、requestId 来源、操作差异、风险只在用户主动问起、或目标确实不明确时才展开。
23
- 2. 用户明确确认后**一次调用**对应命令,同一份入参里带 `confirm: true` 与 `requestIds`(及该操作自身参数)。
24
- **不需要先跑一次 Prepare,也不需要把 requestIds 先"准备"到某个句柄里**——门禁校验、名称解析与写入都在这一次调用内完成。
25
- 3. 返回 `partial/write_uncertain` 时**立即停止**,先只读回查(`workflow.search --category done --requestId <目标ID>`,或看 `--category mine` 是否已出现目标流程),**不自动重放**。核对确认**没生效**后,等约 1 分钟再原样重跑同一条命令——同一批在保护窗口内还会被拦一次(防止机械式重复执行),窗口过去即可正常执行。
26
- 4. 返回 `ok:true` + `data.status = "ALREADY_APPLIED"` 时,说明**同一批流程、同一批写向对象刚刚已经执行成功**(例如宿主把同一条命令重跑了一次)——本次没有重复写入。直接按返回值里的 `previousResult` 汇报即可,**不要重复执行同一条命令**。
27
- 5. 报 `validation/batch_gate_required` 时,说明 `details.missingRequestIds` 里的流程**不在最近一次查询结果里**(门禁只认查询落下的那份结果,不认别的口径)。正确做法:先做 `workflow.search --batchType <类型> --pageSize 100` 查询一页,再立即用该页返回的 ID 调用批量命令。若这些流程其实已被处理过,先只读核对 `workflow.search --category done --requestId <目标ID>`,已处理就按成功汇报,**不要重复执行同一条命令**。
28
- 6. 同一操作连续 2 次失败必须停止。
22
+ **先判断目标从哪来,再决定要不要多一轮确认**——两条路径的确认成本不同:
23
+
24
+ **① 目标是具体几条(`requestIds` 已知)** → **不需要再问一次**:用户已经指明要处理什么,直接把 `requestIds` + `confirm: true` 一次调用完成。
25
+ - 只有当用户主动问起条数、来源、差异或风险时,才补一句说明。
26
+ - 不要先发一段"摘要/计划"等用户点头(那会白多一轮往返),也不要把 `requestIds` 先"准备"到某个句柄里——门禁校验、名称解析与写入都在这一次调用内完成。
27
+
28
+ **② 目标是一个范围(没有准确的 `requestId`)** → **必须先数条数、再执行,两轮**:
29
+ 1. 用 `scopeCondition` + **`action: "total"`** 只查条数(不发写入),把「范围 + 条数」报给用户;
30
+ 2. 用户确认后,用**同一条 `scopeCondition`** 配 **`action: "execute"`**(+ `confirm: true`)执行。
31
+
32
+ > **范围驱动**没有"直接执行"这条捷径:`action` 在给了 `scopeCondition` 时是**必填**参数,漏写直接报 `batch_action_required`——"先数条数给用户确认"这一步不允许靠漏写绕过。只有用户**在同一句话里已经给出了明确条数**(如"把刚才那 2 条提交掉"——那其实属于路径 ①,用 `requestIds`)才不需要再数。
33
+
34
+ **`action:"total"` 返回三个数,都要看**:`total`(**当前能实际处理**的条数)、`scopeTotal`(该范围下的流程总数)、`executable`(能否真取到行)。
35
+ - `total = 0 且 scopeTotal > 0` ⇒ **范围内有数据,但没有一条适用本次操作**(常见:都是抄送/传阅/转发类,例如 `batchSubmit` 对它们等同置为已办,应改用 `workflow.batchRead`)。**必须如实向用户说明"有多少条、为什么不适用",不要报成"没有数据/0 条"**。
36
+
37
+ **执行后的处理(两条路径共用)**:
38
+
39
+ - 范围驱动的返回是**聚合结果**:`status`(`COMPLETE` / `PARTIAL`)、`succeededCount`、`failedCount`、**`failures:[{requestId, reason}]`(带逐条原因的失败清单)**、`stoppedReason`(取空时还有 `scopeTotal`;整页抛错时另有 `lastError`)。**照这个汇报**:完成多少条、失败多少条、**每条失败的原因**(`failures[].reason`)——不要只报个数,也不要编造逐条明细。
40
+ - **不要向用户提"批次数、分了几批、循环了几次、页数"这类实现细节**——那是 CLI 内部怎么分批取数的事,对用户没有意义(返回里也已经不含这些字段)。用户只关心"处理了多少条、有没有失败"。
41
+ - **`stoppedReason` 必须按语义转述,不要一律说成"没有数据"**:
42
+ - `scope_exhausted` ⇒ 该范围**确实没有**可处理的数据了;
43
+ - `scope_not_batch_eligible` ⇒ 范围里**有数据**(`scopeTotal` 条)但本次操作一条都取不到 —— 说明条数与原因,并给替代方案;
44
+ - `max_batches_reached` / `max_items_reached` ⇒ 触达单次调用上限(20 批 / 2000 条),**没跑完**,可用同一 `scopeCondition` + `exRequestIds` 续跑;
45
+ - `batch_failed_stop` ⇒ 某批整体失败已停止。
46
+ - 只有 `COMPLETE` 才代表"该范围已处理完";其余都要说明未跑完的原因。
47
+ - 返回 `partial`/`write_uncertain` 时**立即停止**,先只读回查(`workflow.search --category done --requestId <目标ID>`,或看 `--category mine` 是否已出现目标流程),**不自动重放已发出写入的那一批**。核对确认**没生效**后,等约 1 分钟再原样重跑——同一批在保护窗口内还会被拦一次(防止机械式重复执行),窗口过去即可正常执行。
48
+ - 返回 `ok:true` + `data.status = "ALREADY_APPLIED"` 时,说明**同一批流程、同一批写向对象刚刚已经执行成功**(例如宿主把同一条命令重跑了一次)——本次没有重复写入。直接按返回值里的 `previousResult` 汇报即可,**不要重复执行同一条命令**。
49
+ - 报 `validation/batch_gate_required` 时,说明 `details.missingRequestIds` 里的流程**不在最近一次查询结果里**(门禁只认查询落下的那份结果,不认别的口径)。这只可能发生在**点名 `requestIds`** 的路径上(范围驱动的 ID 就是 CLI 刚查出来的,不存在这种情况)。正确做法:先做 `workflow.search --batchType <类型> --pageSize 100` 查询一页,再立即用该页返回的 ID 调用批量命令;若这些流程其实已被处理过,先只读核对 `workflow.search --category done --requestId <目标ID>`,已处理就按成功汇报。
50
+ - 同一操作连续 2 次失败必须停止。
29
51
 
30
52
  ## 输入
31
53
 
54
+ **三条路径互斥**:`requestIds`(点名处理)|`scopeCondition` + `action:"total"`(先数条数)|`scopeCondition` + `action:"execute"`(按范围执行)。`requestIds` 与 `scopeCondition` 同时给会报 `batch_scope_conflict`——要给范围就**不要**自己先查 ID。
55
+
32
56
  - `requestIds`:待操作流程 ID,类型可为数组(元素为数字或仅数字字符串)或逗号分隔串;**必须来自查询索引结果,模型不能自己编**;拿到后直接用——CLI 按本机索引核对,**不需要为了"校验"再查一次服务端**。
57
+ - `action`(string,**给了 `scopeCondition` 时必填**):`"total"` 只查条数(不发写入,用于先向用户确认)|`"execute"` 查询并执行。**漏写直接报 `batch_action_required`**——不给默认值,"先数条数"不允许靠漏写绕过。
58
+ - `scopeCondition`(object,可选):**范围条件,与 `workflow.search` 入参同形**(`category` / `workflow` / `creator` / 时间条件 / `filterFields` 等;写法查 [`workflow-query.md`](workflow-query.md))。CLI 会自动补上本操作的 `batchType`,不需要自己传。给了它就**不要**再自己查 requestIds——循环由 CLI 内部完成。
59
+ - **未显式给 `subCategory`/`tabName`/`tabid` 时,CLI 自动按「全部」子分类取数**(`todo` → `tabid 0`)。原因:低版本模式下 `workflow.search` 对未指定子分类的 todo 默认查「待处理(7)」,而**批量过滤列表端点**在 tabid=7 下只返待处理 ⇒ 范围里全是抄送/传阅/转发类时**恒 0 条**(2026-09-20 真实事故)。范围驱动默认取「全部」,再由 `batchType` 过滤可操作性;**scope 显式给了子分类就用指定的**(只处理待处理就写 `subCategory:"待处理"`)。
60
+ - `exRequestIds`(可选):本次要**排除**的 requestId(数组或逗号分隔串)。用于**续跑**:上一次执行里失败或未处理的 ID 传进来即可跳过(CLI 会把它合并进查询的排除集)。
33
61
  - `remark`:批量统一意见(submit / supervise / forward / reject 适用;**置已读没有意见参数**)。
34
- - `batchSize`:单批最大条数,`integer` 1–100,默认 100;CLI 自动分批循环。
35
- - `confirm`:固定为 `true`,与 `requestIds` 在**同一次调用**里带上。
62
+ - `batchSize`:单批最大条数,`integer` 1–100,默认 100;CLI 自动分批循环(范围驱动时它同时是**内部每页的取数条数**)。
63
+ - `confirm`:固定为 `true`。
36
64
  - `category` / `async`:仅 `workflow.batchSubmit` 使用(分类判断、异步提交开关)。
37
65
  - 各操作专属字段见下方分节。
38
66
  - **6 个批量操作都没有 `continuation` 字段**:它们是单命令,不存在需要模型搬运的句柄。
39
67
 
40
- ## 请求来源与分批循环
68
+ ## 请求来源与执行方式
41
69
 
42
- **先判断操作目标的来源:**
70
+ **先判断操作目标的来源,再选路径:**
43
71
 
44
- **① 目标就是「上一轮查询结果」**(用户直接用指代——"前两条 / 刚才那些 / 上一次列表里的流程 / 都处理掉",或点名该轮里的条目)→ **直接从最近一次查询结果中取对应流程作为操作目标并执行批量命令**。
72
+ **① 目标就是「上一轮查询结果」里的具体条目**(用户直接用指代——"前两条 / 刚才那些 / 上一次列表里的流程 / 都处理掉",或点名该轮里的条目)→ **直接从最近一次查询结果中取对应流程作为操作目标并执行批量命令**。
45
73
  **指代基准只有一份:最近一次查询结果**(与批量门禁的放行快照同源);"前两条"=该列表第 1、2 条(按列表顺序数)。**不要跨轮回溯更早的列表,也不要反问"是哪一份列表"**。**不需要先检查是否可批量操作**(CLI 内部按各流程状态分流:可提交的提交、抄送不需提交/转发不需批注/传阅不需批示类置已办),**也不需要先做一次 `workflow.search --batchType` 来"校验"这些 ID**(门禁按本机索引放行,这一次查询对放行没有帮助)。
46
74
 
47
- **② 目标是一个范围**("把这些待办都处理了"且未限定上一轮结果,因此没有准确的 requestId)→ 必须**先查询、再循环**(**无需先查数量生成计划再等确认**),条件与顺序如下:
75
+ **② 目标是一个范围**("把这些待办都处理了"且未限定上一轮结果,因此没有准确的 requestId)→ **用 `scopeCondition` 交给 CLI**,不要再自己写循环:
48
76
 
49
- 1. 用 `workflow.search` 取数,且**必须带 `batchType`**(提交用 `submit`,其余按操作取对应值)与 `pageSize: 100`——不带 `batchType` 查出来的数据不是可批量操作的那一批,门禁会判成不在查询结果里。
50
- 2. 拿到这一页的 requestId 后**立即**用它们调用批量命令,**不要**先翻完所有页、也**不要**先把全部 requestId 收集起来一次提交。
51
- 3. 执行完再查一次(批量提交 / 置已读会清掉已处理的流程,通常仍从第 1 页开始;催办 / 共享 / 转发 / 退回后流程不消失,多页时用 `current: 2/3/...` 逐页推进)。
52
- 4. 重复 2–3,直到 `total` 归零或没有可操作数据为止。
77
+ ```bash
78
+ # 第一步:只数条数(不发写入),把条数报给用户确认
79
+ weaver-work-cli --json workflow run workflow.batchSubmit --input-json '{"confirm":true,"scopeCondition":{"category":"todo","receiveTime":"2026-09-20 2026-09-20"},"action":"total"}'
80
+ # 第二步:用户确认后执行(同一条 scopeCondition)
81
+ weaver-work-cli --json workflow run workflow.batchSubmit --input-json '{"confirm":true,"scopeCondition":{"category":"todo","receiveTime":"2026-09-20 2026-09-20"},"action":"execute","remark":"批量同意"}'
82
+ ```
83
+
84
+ CLI 内部的循环口径(了解即可,**不要自己复刻**):
85
+
86
+ 1. 用 `scopeCondition` + 自动补上的 `batchType` 查一页(`batchSize` 即每页条数);
87
+ 2. **立即**执行该页,**不先翻完所有页、也不先把全部 requestId 收集起来一次提交**;
88
+ 3. 把这一页**已处理**与**失败**的 ID 并入排除集,带着排除集(`exRequestId`)**再查下一页**;
89
+ 4. 重复 2–3,直到取不到数据(`stoppedReason: scope_exhausted`)或触达上限(单次调用最多 20 批 / 2000 条)。
53
90
 
54
- **为什么要循环、且不许先收集全部**:批量门禁的放行依据是**最近一次查询结果**——每次查询都会覆盖上一份结果;若先把所有页的 ID 收集起来再一次性提交,前面几页的 ID 已经被后来的查询挤掉,门禁会判成"不在最近一次查询结果里"。且流程一旦被处理就从查询结果里消失。**只有来源 ② 以及命令报 `batch_gate_required` 时,才需要 `--batchType` 查询。**
91
+ **为什么必须由 CLI 循环、且排除集不能省**:批量门禁的放行依据是**最近一次查询结果**——每次查询都会覆盖上一份结果;若先把所有页的 ID 收集起来再一次性提交,前面几页的 ID 已经被后来的查询挤掉,门禁会判成"不在最近一次查询结果里"(真实事故:3 条一起提交全部 `batch_gate_required`)。而排除集是**两类操作都成立**的翻页依据:提交/置已读会把已处理流程从待办里清掉(不排除也不会重复),催办/共享/转发/退回**不会**消失(不排除就会反复处理同一批)。
55
92
 
56
- **requestId 提取**:优先用查询返回的**结构化字段** `cards[].requestId` 取本页全部 requestId;只有在需要从 `finalMarkdown` 文本里提取时才用正则 `` \]\(https?://[^\)]*requestId=(\d+)\) ``(**只匹配卡片链接**,避开描述文字里嵌套的 requestId)。批量查询默认跳过卡片业务摘要(只返回系统字段卡片,提速显著)。
93
+ **失败时的口径**:分两种——① **内层单批"部分失败"但接口不报错**(转发/退回返回 `status:"PARTIAL"` + `failures`、催办返回 `success:false`、提交返回 CLI **本地**跳过清单 `skippedRequestIds`(`isRemark>=100` 不可批量提交的条目,**不是接口字段**)):CLI 会解析信封,**把失败项从成功数里剔除**、连原因一起收进 `failures`,并把它们并入排除集后继续下一页(不会重复写已失败的条)。② **整页失败且定位不到逐条失败项**(认证/网络/门禁/写结果不确定):**立即停止**并返回 `PARTIAL` —— 这类失败继续跑只会把同一个错误重复 N 次;此时 `lastError` 给出批次级原因,`failures` 里每条也会带上该原因。
94
+
95
+ > **不要把"部分失败"当成成功**:`succeededCount` 已扣除失败项。汇报失败时必须带上 `failures[].reason`(接口给的逐条原因,或批次级原因摊派),否则用户只知道"有几条没成"却不知道"为什么"。
96
+
97
+ **续跑**:带上**同一条 `scopeCondition`** 再执行一次即可——本次已处理的会被自动跳过(排除集同源)。**要重试本次失败的那几条**:先用 `workflow.search --category done --requestId <id>` **只读核对它们是否其实已经生效**(部分失败可能是"已写入但回执异常"),确认没生效再用 `requestIds` 单独重试——**不要**直接把 `failures[].requestId` 当 `exRequestIds` 传回去(那等于永久跳过它们)。
98
+
99
+ **requestId 提取**:走 `requestIds` 路径时优先用查询返回的**结构化字段** `cards[].requestId` 取本页全部 requestId;只有在需要从 `finalMarkdown` 文本里提取时才用正则 `` \]\(https?://[^\)]*requestId=(\d+)\) ``(**只匹配卡片链接**,避开描述文字里嵌套的 requestId)。批量查询默认跳过卡片业务摘要(只返回系统字段卡片,提速显著)。
57
100
 
58
101
  - `requestIds` 必须取自上一轮查询结果,或跟随查询分页逐页取本页 ID;**禁止模型臆造 ID**。
59
- - `batchSize`(默认 100)由 CLI 自动分批;任一批失败立即停止,如实转述该批结果(含已成功条数),不自行补造。
60
102
 
61
103
  ## 置已读与提交的区别
62
104
 
@@ -70,7 +112,9 @@
70
112
 
71
113
  适用:"批量提交 / 全部提交 / 一起提交"多条待办。入参 = `confirm: true` + `requestIds`(+ `remark` / `batchSize` / `async`)。
72
114
 
73
- 返回 `readCount`、`submittedCount`、`skippedRequestIds`(`isRemark=100` 不可批量提交被跳过时出现)、`batchRead`、`submitResponse`;原样转述返回的结果与提示文案。
115
+ 写端点= **`POST /api/workflow/core/flow/batchSubmit`**(前端同款;2026-09-20 由 `/api/workflow/core/paService/v1/batchSubmit` 换过来)。请求体形态(CLI 内部拼好,**模型不需要自己构造**):`{isMobile, isAsync, isSelectAll, conditionId, batchSubmitListParams, requestIds, operateUserIds, remark, handWrittenSign}`,其中 `batchSubmitListParams` 是一段 **JSON 字符串**(内含 `params` 查询上下文与 `searchParams.formDatas` 的空壳条件),`requestIds`/`operateUserIds` 为逗号分隔串。响应是**统一的逐条结果结构**(`data.submitResult[]` 含 `status`/`requestId`/`requestName`/`resultMessage`/`data.requestNameTitle`,另有 `data.failMsg` 映射与 `data.operateId`)——CLI 已按它算成功/失败条数与 `failures`。
116
+
117
+ 返回 `readCount`、`submittedCount`、`skippedRequestIds`(**CLI 本地判定**:`isRemark=100` 不可批量提交、被跳过的条目;接口本身不返回该属性)、`batchRead`、`submitResponse`;原样转述返回的结果与提示文案。
74
118
 
75
119
  ### 批量置已读 batchRead
76
120
 
@@ -108,7 +152,7 @@
108
152
  | `position` | 岗位 |
109
153
  | `all` | 所有人(无 name) |
110
154
 
111
- 名称自动转 ID;`name` 支持 `"名称(ID)"`(此时直接采用括号里的 ID、不再搜名称)。多命中返回候选列表让用户确认,禁止静默全选;0 命中报错提示核对。可选 `minSeclevel` / `maxSeclevel`(用户明确要求限制时传)。返回透传每批 addRequestShare2 响应(含每流程 `sourceId` / `result` / `msg`),任一批失败立即停止。
155
+ 名称自动转 ID;`name` 支持 `"名称(ID)"`(此时直接采用括号里的 ID、不再搜名称)。多命中返回候选列表让用户确认,禁止静默全选;0 命中报错提示核对。可选 `minSeclevel` / `maxSeclevel`(用户明确要求限制时传)。返回按每批 `addRequestShare2` 的**逐条结果**计价:`sharedCount` 只数 `data[]` 里 `result === "true"` 的条目,`result === "false"` 的进 `failures`(原因取 `msg`)。
112
156
 
113
157
  ### 批量转发 batchForward
114
158
 
@@ -143,13 +187,17 @@ macOS/Linux(bash/zsh):
143
187
  weaver-work-cli --json workflow run workflow.batchSubmit --input-json '{"confirm":true,"requestIds":["id1","id2","id3"],"remark":"批量同意","batchSize":100}'
144
188
  ```
145
189
 
146
- 目标是一个范围时,先查一页、再立即提交该页:
190
+ 目标是一个范围时,**用 `scopeCondition` 交给 CLI 循环**(先数后做):
147
191
 
148
192
  ```bash
149
- weaver-work-cli --json workflow run workflow.search --input-json '{"category":"todo","batchType":"submit","pageSize":100}'
150
- # 用返回的 cards[].requestId 组成 requestIds,立即提交这一页
151
- weaver-work-cli --json workflow run workflow.batchSubmit --input-json '{"confirm":true,"requestIds":["<本页 id1>","<本页 id2>"]}'
152
- # 再查一次、再提交这一页,循环到 total 归零为止
193
+ # ① 只查条数,报给用户确认
194
+ weaver-work-cli --json workflow run workflow.batchSubmit --input-json '{"confirm":true,"scopeCondition":{"category":"todo","batchType":"submit"},"action":"total"}'
195
+ # ② 用户确认后执行(同一条 scopeCondition;CLI 内部查一页→执行→排除→再查)
196
+ weaver-work-cli --json workflow run workflow.batchSubmit --input-json '{"confirm":true,"scopeCondition":{"category":"todo"},"action":"execute"}'
197
+ # ③ 若上一轮有失败/未处理的,带上它们的 ID 续跑
198
+ weaver-work-cli --json workflow run workflow.batchSubmit --input-json '{"confirm":true,"scopeCondition":{"category":"todo"},"action":"execute"}'
199
+ # 重试个别失败项(先只读核对确认未生效)
200
+ weaver-work-cli --json workflow run workflow.batchSubmit --input-json '{"confirm":true,"requestIds":["<确认未生效的那条>"]}'
153
201
  ```
154
202
 
155
203
  ### 批量置已读
@@ -258,7 +306,20 @@ weaver-work-cli --json workflow run workflow.batchReject --input-json '{"confirm
258
306
 
259
307
  ## 返回
260
308
 
261
- - 6 个命令都**直接返回批量结果**:提交(`readCount` / `submittedCount` / `batchRead` / `submitResponse` / 可选 `skippedRequestIds`)、置已读(`readCount`)、催办(`successNum` / `failNum` / `batches`)、共享(`sharedCount`)、转发(`forwardedCount`)、退回(`rejectedCount`);分批失败时返回 `status: "PARTIAL"` 与逐条失败明细。**没有任何命令返回 `continuation`。**
309
+ - 6 个命令都**直接返回批量结果**:提交(`readCount` / `submittedCount` / `batchRead` / `submitResponse` / 可选 `skippedRequestIds`——CLI 本地的跳过清单)、置已读(`readCount`)、催办(`successNum` / `failNum` / `batches`)、共享(`sharedCount` / 有失败时 `failedCount` + `failures`)、转发(`forwardedCount`)、退回(`rejectedCount`);**有逐条失败时 `status` 为 `"PARTIAL"`**,并给出 `failures`。**没有任何命令返回 `continuation`。**
310
+
311
+ > **★ 计数只认服务端逐条结果(2026-09-20 起)**:不再"按发出去的条数"算成功。真机实证过一次 **0 成功被报成 1 成功**——`batchSubmit` 的响应是
312
+ > `{"data":{"successRequest":[], "failRequest":{"<id>":"当前工作流未开启允许批量提交"}}}`,
313
+ > 当时 CLI 却回 `status:"COMPLETE"` + `submittedCount:1`。现在的口径:
314
+ > 1. 成功条数只认逐条结果:`successRequest`(**成功清单**)/ 逐条 `submitResult`·`operateResult` 里 `status===true` 的 ⇒ `submittedCount`;`failRequest` / `failMsg`(**{流程ID: 失败原因} 映射**,两种键名都真机见过)与 `status===false` 的逐条行 ⇒ **`failures:[{requestId, reason, name}]`**(原因原样带出,不再只给 ID);
315
+ > 2. 转发/退回的逐条结果在 `data.data.submitResult` / `operateResult`(`status !== true` 即失败,同样带上 `resultMessage`);
316
+ > 3. **拿不到逐条结果时不得默认"全成功"**:`batchSubmit` 返回 `unverified: true` + `unverifiedRequestIds` + `warnings` 且 `status:"PARTIAL"`;`batchRead` 用 `resultUnverified`;转发/退回返回 `unverified: true`。这类条目要**只读核对**(`workflow.search --category done --requestId <id>`)后再决定重试,**不要按已成功上报**。
317
+ > 4. `failures[].name` 是**流程标题**:优先取逐条结果的 `requestName` / `data.requestNameTitle`;那里没有就回退到**最近一次查询结果的快照**(`flowName`,其原始字段是 `requestnameTitle`/`requestnametitle`,真机确认两个键名都存在且同值)。都取不到就只给 ID,**不编造标题**。
318
+ > 5. **共享(`batchShare`)的返回结构与其他批量接口不一样**:逐条结果是**根级数组** `data: [{sourceId, result, msg}]`——`data` 不是对象、ID 键名是 **`sourceId`**(不是 `requestId`)、成功标记是**字符串** `"true"`/`"false"`(不是布尔 `status`)、原因在 **`msg`**。CLI 已按它逐条计价:`sharedCount` = `result === "true"` 的条数;`result === "false"` 的进 `failures`(标题回退快照 `flowName`,共享响应本身不带标题),`failedCount` 同步给出。
319
+ > 5. 汇报时**失败必须连原因一起说**(`failures[].reason`),并带上标题/编号让用户能对上号,不要只报个数。
320
+ - **范围驱动(给了 `scopeCondition`)返回的是聚合结果,不是单批结果**:
321
+ - `action:"total"` → `status:"SCOPE_TOTAL"` + `total`(**当前能实际处理**的条数)+ `scopeTotal`(该范围下的流程总数)+ `executable`(能否真取到行)(**没有写入**)。`total=0 且 scopeTotal>0` 表示"有数据但没有一条适用本次操作",**不能报成"没有数据"**。
322
+ - `action:"execute"` → `status`(`COMPLETE`/`PARTIAL`)、`succeededCount`、`failedCount`、`failures:[{requestId, reason}]`、`stoppedReason`(`scope_exhausted` / `scope_not_batch_eligible` / `max_batches_reached` / `max_items_reached` / `batch_failed_stop`),取空时另有 `scopeTotal`。**不含 `batches`/`batchSummaries`/`pageSize`**(内部实现细节,2026-09-20 起不再返回)。汇报口径见「确认链」。
262
323
  - 被重复执行、且上一次已经成功时,返回 `ok:true` + `data.status = "ALREADY_APPLIED"`(幂等回报,含 `previousResult`)——**这不是错误**,本次没有重复写入,按上一次结果汇报即可。
263
324
  - 任一批失败:CLI 立即停止并在 **stderr** 返回 `ok:false` + `error.message`(含已成功条数),Agent 如实转述,不自行补造;**没有** `success`/`userMessage` 字段。
264
325
  - **结果展示**:动作与条数用一句话说明;**逐条结果可用 Markdown 表格展示**(序号 / 流程名称 / 动作 / 接收人 / 状态 / 说明);不展示 `requestId`,除非用户明确要技术定位信息。幂等回报(`ALREADY_APPLIED`)同样**只报业务结果**,不要向用户提及"此前已执行过/没有重复写入"。
@@ -2,181 +2,155 @@
2
2
 
3
3
  ## 什么时候读取
4
4
 
5
- 用户要"发起/创建/提交一个 XX 流程""按某工作流填写表单并发起"时读取本文件。覆盖 `workflow.createList` / `workflow.createForm` / `workflow.createPrepare` / `workflow.createApply`。
5
+ 用户要"发起/创建/提交一个 XX 流程""按某工作流填写表单并发起"时读取本文件。覆盖 `workflow.createForm` / `workflow.createRequest` 两条命令。
6
6
 
7
7
  ## Operation
8
8
 
9
9
  | Operation | 风险 | 固定规则 |
10
10
  | --- | --- | --- |
11
- | `workflow.createList` | read | 查询可发起工作流列表;不传凭据;`name`/`workflowId` **至少传一个**;`workflowId` 与 `name` 可同传精确校验 |
12
- | `workflow.createForm` | read | 必传 `workflowId`;`relatesTo` 仅透传不影响表单初始化;返回 `context`/runtime/frame/字段清单/fileUploadParams |
13
- | `workflow.createPrepare` | read-before-write | requiresConfirmation;必传 `context`/`data`/`intent`;`requestLevel` 取 `systemFields.levelOptions` 的 code,`secLevel` 取 `systemFields.secLevelOptions` 的 id;不落业务数据 |
14
- | `workflow.createApply` | high-risk-write | requiresConfirmation;必传 `confirm:true`/`context`/`intent`;`intent` 不继承 prepare;遇 `partial`/`write_uncertain` 禁止重试 |
15
-
16
- ## 确认链(写操作)
17
-
18
- 1. `workflow.createList` → `workflow.createForm` 拿到 `context`(表单会话短句柄,载荷由 CLI 托管在本机)。
19
- 2. `workflow.createPrepare` 收集字段,向用户摘要目标/差异/风险(预览 + 联动结果;明细表按 Excel 列展示,不要平铺)。
20
- 3. 等用户明确确认。
21
- 4. `workflow.createApply` 带 `confirm:true` 与同一 `context`、显式 `intent` 提交。
22
- 5. 成功时命令**直接返回 `requestId` / `requestName` / `url`**(`status` 为 `CREATED` = 已建草稿、`CREATED_SUBMITTED` = 已提交审批)——**按返回值汇报即可,不需要再去查询反查**。
23
- 6. 遇 `partial`/`write_uncertain` 停止,先只读回查(detail/summary),不自动重放。
24
- 7. Apply 被重复执行时按句柄状态分流(**不要一看到"用过"就重跑 Prepare**):
25
- - `ok:true` + `data.status = "ALREADY_APPLIED"`:该 `context` 上一次已经**发起成功**,本次没有重复写入——按返回值里的 `requestId` / `previousResult` 作为成功结果汇报,**不要重跑 Prepare、不要再 apply**;
26
- - `partial`/`write_uncertain`:上一次已经发出写入、结果未确认——先只读回查(`workflow.search --category mine --requestId <目标ID>`)确认是否已发起,确认没发起才重跑 Prepare;
27
- - `validation/continuation_busy`:该 `context` 正被另一次执行占用、上一次还没发出写入——**稍后原样重试同一条命令**即可(不要重跑 Prepare、不要换句柄);
28
- - `validation/continuation_consumed`:占用该句柄的执行停在无法判定的旧状态——先只读回查确认是否已发起,确认没发起才重跑 Prepare。
29
-
30
- **省时纪律(这几件事一次都别做,每违反一条就多烧一轮工具往返)**:① 拿字段清单**不要**写脚本解析 `resultFile`(`fields` 被移出时用同伴的 `writableFields`);② `data` 的键**不要**写 `"字段名(字段ID)"`(只认字段 id);③ 只读字段**不要**传(看 `editableFields`);④ 浏览 / 人员 / 发票类必填项**不要**先猜一个值试试;⑤ 读命令输出**不要**用 `head`/`tail` 截断;⑥ 取表单默认值**不要**去 `workflow.detail` 草稿(读不到,默认值以 `initialValues` 为准);⑦ 输出被宿主截断时**不要**去找宿主的工具结果目录(`~/.workbuddy/projects/**/tool-results/**`:读不了,去了必白跑一轮),按输出最前面的 `resultFile` 取。
11
+ | `workflow.createForm` | read | 必传 `workflow`(`名称(ID)` / 名称(**可一次给多个说法**:数组多元素,或一个元素内用英文逗号分隔)/ ID):内部解析出**可发起**的工作流并载入发起表单(runtime/frame/字段清单/fileUploadParams),顺带返回该工作流的发起偏好。不落任何业务数据。解析不出唯一目标时回澄清(`NEEDS_CLARIFICATION`,候选见 `candidates`)。**已给 `名称(ID)` / 纯 ID 时不查可发起列表**(名称与可发起性直接由表单载入接口给出),该 ID 不存在 / 无发起权限时同样回澄清,但带 `clarificationReason` 区分 |
12
+ | `workflow.createRequest` | high-risk-write | requiresConfirmation;必传 `context`/`data`/`action`;`action` **必填**(`draft` 存草稿 / `submit` 提交审批);遇 `partial`/`write_uncertain` 禁止重试 |
13
+
14
+ ## 发起链路(两条命令,模型不需要自己判断必填)
15
+
16
+ 1. `workflow.createForm`:按用户说法解析可发起的工作流并载入表单,拿回 `continuation`(表单会话短句柄,载荷由 CLI 托管在本机)。
17
+ - 返回 `NEEDS_CLARIFICATION` 时按 `candidates` 向用户确认,确认后用 `名称(ID)` 重发**同一条命令**。
18
+ 2. 按用户意图把字段值填进 `data`(键用字段 id)。
19
+ 3. `workflow.createRequest`:`context` 传第 1 步返回的 `continuation`,`action` 按用户意图取 `draft`/`submit`。
20
+ - `action: "submit"` 且仍有必填缺失 → **命令不会创建流程**,返回 `NEEDS_INPUT` + `missingFields`;把缺失项**一次问齐**后带同一 `context` 重发 `action: "submit"`。
21
+ - `action: "draft"` 且仍有必填缺失 → 照常建草稿,`missingFields` 只作提醒(必须原样转述给用户)。
22
+ - **不要(也无法)自己判断"必填是否齐"**:缺失与否由命令按**实际所填数据**(含联动带出值)实时算出,照 `missingFields` 走即可。
23
+ 4. 成功时命令**直接返回 `requestId` / `requestName` / `url`**(`status` 为 `CREATED` = 已建草稿、`CREATED_SUBMITTED` = 已提交审批)——**按返回值汇报即可,不需要再去查询反查**。
24
+ - **发起/查询完成后不要主动打开任何页面**:`url` **只在回复文本里以可点链接给出**,**不要**把它交给浏览器预览面板、也**不要**顺手调 `workflow.open` / `workflow.pageOpen` 去"打开看看"。**只有用户明确说"打开这条 / 打开看看 / 给我链接"时**才打开——用 `workflow.pageOpen`(动态页 `page:"view"` 配 `requestId`)或 `workflow.open` 走正规入口,**地址一律由 CLI 按内置地址表生成,不要自行拼接 OA URL**。
25
+ 5. 遇 `partial`/`write_uncertain` 停止,先只读回查(detail/summary),不自动重放。
26
+ 6. 被重复执行时按句柄状态分流(**不要一看到"用过"就重跑整条链路**):
27
+ - `ok:true` + `data.status = "ALREADY_APPLIED"`:该 `context` 上一次已经**发起成功**,本次没有重复写入——按返回值里的 `requestId` / `previousResult` 作为成功结果汇报,**不要重跑 createForm、不要再 createRequest**;
28
+ - `partial`/`write_uncertain`:上一次已经发出写入、结果未确认——先只读回查(`workflow.search --category mine --requestId <目标ID>`)确认是否已发起,确认没发起才重跑;
29
+ - `validation/continuation_busy`:该 `context` 正被另一次执行占用、上一次还没发出写入——**稍后原样重试同一条命令**即可(不要重跑 createForm、不要换句柄);
30
+ - `validation/continuation_consumed`:占用该句柄的执行停在无法判定的旧状态——先只读回查确认是否已发起,确认没发起才重跑。
31
+
32
+ **省时纪律(这几件事一次都别做,每违反一条就多烧一轮工具往返)**:① 拿字段清单**不要**写脚本解析 `resultFile`(`fields` 被移出时用同伴的 `writableFields`);② `data` 的键**不要**写 `"字段名(字段ID)"`(只认字段 id);③ 只读字段**不要**传(看 `editableFields`);④ 浏览 / 人员 / 发票类必填项**不要**先猜一个值试试;⑤ 读命令输出**不要**用 `head`/`tail` 截断;⑥ 输出被宿主截断时**不要**去找宿主的工具结果目录(`~/.workbuddy/projects/**/tool-results/**`:读不了,去了必白跑一轮),按输出最前面的 `resultFile` 取。
31
33
 
32
34
  ## 输入
33
35
 
34
- ### workflow.createList
35
-
36
- - `name`(string):工作流名称关键字,模糊匹配。**与 `workflowId` 至少传一个**——两个都不传时 CLI **不做查询**(不发列表请求),直接返回 `MISSING_QUERY` 和提示。
37
- - `workflowId`(string,可选):记忆中已确认的工作流 ID。记忆有 ID 时名称与 ID 同传,按当前用户可发起权限列表精确校验并优先选中;命中 `SELECTED`,未命中 `NOT_FOUND`,禁止去掉 ID 退回名称候选。
38
-
39
- **查询词策略(第 ① 步必读)**:
40
-
41
- - **优先用记忆别名改写后查询**:用户给出的表达命中记忆别名(如"手机话费报销"含别名"费用报销")→ **直接查记忆别名**,不要先试用户原词、再试宽泛词。
42
- - **禁止用宽泛词撞无关工作流**:`name:"话费"` 这类宽泛词可能唯一命中**语义无关**的工作流(如"话费补贴标准审批流程")。命中 `SELECTED` 后**先核对工作流名称/类型与用户意图是否一致**(报销类意图 → 名称应含"报销/费用",不应是"补贴标准/行政事务"类);不一致则**不要进入第 ② 步初始化表单**,改用记忆别名重查。
43
- - `NOT_FOUND`(含记忆别名也查不到)→ 此时才反问用户确认工作流,不要自行换词试探。
44
- - `SELECTED` 的 `selectionSource: "exactName"` 表示"候选里恰好有一个名字与所写完全一致"(如所写"总部费用报销",候选是「总部费用报销 / 总部费用报销流程 / 总部费用报销(自定义)」)——CLI 已替用户选定,**不要再反问"您指的是哪一个"**,按正常流程继续即可。`selectionSource: "workflowId"` 表示按记忆 ID 精确命中。
45
- - `NEEDS_SELECTION` 只出现在"多命中且候选中没有与所写完全一致的"(CLI 只回这一批,最多不限量但已按类型+名称排序):多候选**原样展示工作流名,不要添加 "(Recommended)"/"推荐" 等后缀**(由用户明确选择)。
46
-
47
36
  ### workflow.createForm
48
37
 
49
- - `workflowId`(string|integer,必填):工作流 ID。
38
+ - `workflow`(string[],必填):工作流条件——`"名称(ID)"` / 纯名称 / 纯数字 ID。
39
+ - **自带显式 ID(`"名称(ID)"` 或纯数字)时不查可发起列表**:流程名与"你能不能发起"都由表单载入接口直接给出,因此比"按名称解析"少一整次往返。该 ID 不可用时仍回澄清,但会带 `clarificationReason` 说明是**哪一种**(见下)。
40
+ - **纯名称**按当前用户**可发起**的工作流做模糊匹配,**可一次给多个说法**:数组多个元素(`["报销","报销流程"]`)与一个元素内用英文逗号分隔(`["报销,报销流程"]`)**完全等价**,CLI 会把各说法拆成关键词、命中取并集去重。
41
+ - **直接选定并载入表单**时,`selectionSource` 标明是如何选定的:`workflowId`(按 ID 命中)/ `single`(唯一命中)/ `exactName`(全名精确匹配)。`exactName` 表示 CLI 已替用户选定,**不要再反问“您指的是哪一个”**。
42
+ - **0 命中**,或**多命中且没有与所写完全一致的** → **澄清**:返回 `status: "NEEDS_CLARIFICATION"` + `candidates`(每项含 `id`/`name`/`display`,`display` 是 `名称(ID)`),此时**不返回任何表单数据**。把候选**原样**列给用户(不加 "(Recommended)"/"推荐" 等后缀,由用户明确选择),选定后用 `display` 的 `名称(ID)` 重发同一条命令。
43
+ - **自带显式 ID 但该 ID 不可用** → 同样回澄清(`candidates` 为空),并带 `clarificationReason` 标明是**哪一种**——这两句话**必须分开说、不许混**:
44
+ - `clarificationReason = "workflow_not_exist"`:该 ID 不存在(可能已删除或被停用)。如实告知"这个 ID 不存在",让用户核对 ID 或改用流程名称重发;**不要**说成"没有可发起的工作流"这类模糊说法。
45
+ - `clarificationReason = "no_create_right"`:流程**存在**,但当前账号**没有发起权限**。**必须明确说出「存在但没有发起权限」**,绝不能转述成"流程不存在";并给出下一步:改用其他可发起的流程,或联系管理员为该流程授予发起权限。
50
46
  - `relatesTo`(string,可选):关联对象 id,仅透传不影响表单初始化。
51
- - 返回 `context`(表单会话短句柄 `wc-…`,形如 `wc-1a2b3c4d5e6f7a8b`,载荷由 CLI 托管在本机;prepare/apply 复用)、`runtime`/`frame`、字段清单、`initialValues`、`systemFields`、`fileUploadParams`。
52
- - **字段多的表单有"大输出"形态**(字段少时无此字段):结果最前面多出 `resultFile` / `persistedResult` / `outputBytes` / `largeOutputNotice`,且 `fields` 二选一——① 紧凑编码(`columns` 是列名,`rows` 每行一个字段、各列用 `|` 分隔、顺序同 `columns`,非列信息按字段 id 放 `extra`,**不在 `extra` 里就是默认值**);② `fieldsOmitted: true` + `fieldCount`,`fields` 整体移出 stdout,**同一份输出里给 `writableFields` 补偿索引**(`columns` = `id|name|subFormId|componentKey|flags`,`flags` 含义见 `flagLegend`,带选项字段的选项名见 `optionIndex`)。
47
+ - 返回 `continuation`(表单会话短句柄 `wc-…`,形如 `wc-1a2b3c4d5e6f7a8b`;载荷由 CLI 托管在本机,**它就是第 3 步 `createRequest` 的 `context` 入参**)、`runtime`/`frame`、字段清单、`initialValues`、`systemFields`、`fileUploadParams`、`workflow`、`launchPreferences`。
48
+ - **字段多的表单有"大输出"形态**(字段少时无此字段):结果最前面多出 `resultFile` / `persistedResult` / `outputBytes` / `largeOutputNotice`,且 `fields` 二选一——① 紧凑编码(`columns` 是列名,`rows` 每行一个字段、各列用 `|` 分隔、顺序同 `columns`,非列信息按字段 id 放 `extra`,**不在 `extra` 里就是默认值**);② `fieldsOmitted: true` + `fieldCount`,`fields` 整体移出 stdout,**同一份输出里给 `writableFields` 补偿索引**(`columns` = `id|name|subFormId|componentKey|flags`,`flags` 含义见 `flagLegend`,带选项字段的选项名见 `optionIndex`,**日期/时间字段的格式见 `formatIndex`,附件字段的上传限制见 `uploadLimits`**)。
49
+ - **填值前要核对的属性都已在 `writableFields` 里,不要再为它们读 `resultFile`**:`formatIndex`(字段 id → `format`,如 `yyyy-MM-dd HH:mm`)与 `uploadLimits`(字段 id → `大小上限|格式限制|数量上限`,如 `100MB|白名单:jpg,pdf|100个`;`不限格式` 表示不限制后缀,`-` 表示该项未配置)。这两份只在 `fields` 被移出的档位出现,**没有对应条目就是该字段没有这类要求**。
50
+ - **`fields` 是「布局级字段全集」,不等于"页面上肉眼可见的字段数"**:它含联动隐藏、以及未在当前页展示的字段(真机实例:某表单 `fields` 278 个,其中 178 个是明细列,而真正能填的只有 60 个)。**判断"哪些字段能填"只看 `requiredFields` + `subFormRequired` + `editableFields`(大输出时用 `writableFields`)**,不要拿 `fields` 的条数当可填字段数,也不要因为"表单上看着没这么多"就怀疑字段清单不对。
53
51
  - **有 `writableFields` 就直接用**:全部可写字段的「字段名 ↔ 字段 id」都在里面,**直接用它的字段 id 构造 `data` 的键**。
54
- - **不要为了拿字段清单去写脚本解析 `resultFile`**:只有需要某字段更细的元数据(`browserModule` / `fileUploadParams` / 完整 `options` 结构)时才按该字段 id 去取。读任何输出都**不要用 `head`/`tail` 截断**(截断会破坏 JSON,还会诱发重复执行同一条命令)。**输出被宿主截断时**(只看到 2048 字节预览、或提示"已保存到文件"),**不要去读宿主的工具结果目录**(`~/.workbuddy/projects/**/tool-results/**`:没有读权限,去了必白跑一轮),直接按同一份输出最前面的 `resultFile` 取完整结果。
55
- - 两种形态都**照常拼 `data` 并调 `createPrepare`**,**不要重跑 `createForm`**。
52
+ - **不要为了拿字段清单去写脚本解析 `resultFile`**:只有需要某字段更细的元数据(`browserModule` / `browserType` / 完整 `options` 结构)时才按该字段 id 去取。读任何输出都**不要用 `head`/`tail` 截断**(截断会破坏 JSON,还会诱发重复执行同一条命令)。**输出被宿主截断时**(只看到 2048 字节预览、或提示"已保存到文件"),**不要去读宿主的工具结果目录**(`~/.workbuddy/projects/**/tool-results/**`:没有读权限,去了必白跑一轮),直接按同一份输出最前面的 `resultFile` 取完整结果。
53
+ - **不要为了"看清字段"重跑 `createForm`**:返回什么就按什么拼 `data`。
56
54
 
57
- **拿到 form 返回后直接按用户已给信息拼 `data` 并立即调用 `createPrepare`**——不分析字段、不猜映射、不比对选项,**也不要用空 `data` 先探测必填清单**(用户说了什么字段就填什么、用用户原词;必填缺失由 prepare 的 `missing_fields` 告知)。
55
+ **查询词策略**:
58
56
 
59
- **`initialValues`**:OA 初始化表单时预填的字段值(含 onLoadForm 联动带出值),`data` 应包含这些值(用户明确填写的以用户为准);**带 `subFormId` 的是明细表字段**(放进 `details` 对应行对象),**不带的是主表字段**(放 `main`)。这些默认值 **CLI 会自动并入**(主表进 `main`,明细按你建的行补),**漏带不会因此被报成缺必填**——不要为了"怕丢默认值"多跑一轮 prepare。反过来:**`missingFields` 里出现的字段就是真的没值**(OA 对浏览按钮类默认值可能只下发类型标记、不下发值),此时必须问用户,不要猜值、也不要翻 `workflow.detail` 草稿。
57
+ - **优先用记忆别名改写后再传**:用户给出的表达命中记忆别名(如"手机话费报销"含别名"费用报销")→ **直接传记忆别名**(可与原词并列成一次多关键词,见下条),**不要分两次命令试探**(先只传原词、没命中再换一个词)。
58
+ - **一次给多个说法**:`workflow` 同一批次可并列多个说法(数组多元素 或 英文逗号分隔,见输入节);**不确定完整名称时,把①用户说法 ②可能的全名/同义说法 ③一个更宽的「上位词」都并列进这一批**——用户说"会议室预定",批次里就一并带上"会议"(如 `["会议室预定","会议室预订","会议室申请","会议"]`)。**同一批次加词不额外花往返**:命中取并集去重,某个词命不中只是白给,命中了就多一条线索。
59
+ - **上位词可以放进批次,但命中后必须核对**:`"话费"` / `"会议"` 这类宽词可能命中**语义无关**的工作流(如“话费”→“话费补贴标准审批流程”)。载入表单后**先核对 `workflow` 里的名称/类型与用户意图是否一致**(报销类意图 → 名称应含“报销/费用”,不应是“补贴标准/行政事务”类);**不一致就绝不要往下填字段**,改用记忆别名或更准的说法重发 createForm。**`selectionSource: "single"`(唯一命中即选定、不澄清)是最该核对的一档**。
60
+ - **真正被禁的是"分多次命令逐词去撞"**:禁止先甲、再乙、再丙地反复试(把"换词"当检索策略,每试一次多烧一轮往返);**同一条命令里并列多个词、其中包括一个上位词,都不属于被禁的情形**——那是**补充召回**,不是拿宽泛词当主力检索。
61
+ - **不要再继续换词试探**(一次多关键词后仍未定,就反问用户确认)。
62
+
63
+ **拿到 form 返回后直接按用户已给信息拼 `data` 并立即调用 `createRequest`**——不分析字段、不猜映射、不比对选项,**也不要用空 `data` 先探测必填清单**(用户说了什么字段就填什么、用用户原词;必填缺失由 `createRequest` 的 `missingFields` 告知)。
64
+
65
+ **`initialValues`**:OA 初始化表单时预填的字段值(含 onLoadForm 联动带出值),`data` 应包含这些值(用户明确填写的以用户为准);**带 `subFormId` 的是明细表字段**(放进 `details` 对应行对象),**不带的是主表字段**(放 `main`)。这些默认值 **CLI 会自动并入**(主表进 `main`,明细按你建的行补),**漏带不会因此被报成缺必填**——不要为了"怕丢默认值"多跑一轮。反过来:**`missingFields` 里出现的字段就是真的没值**(OA 对浏览按钮类默认值可能只下发类型标记、不下发值),此时必须问用户,不要猜值。
60
66
 
61
67
  **`systemFields`**:`permissions` 给各系统字段的 isview/isedit/ismandatory,`defaults` 是默认值,`levelOptions`/`secLevelOptions` 是紧急程度/密级选项。**只有 `isview=true` 且 `isedit=true` 的系统字段才需要填**;紧急程度/密级**只从这两个 options 取**(不得使用记忆、表单字段或其他来源);**`defaults` 已有非空值即视为已满足,不要询问用户、直接采用**;仅当 ismandatory=true 且默认值为空时才需要用户提供。
62
68
 
63
- ### workflow.createPrepare
69
+ **`launchPreferences`**:该工作流的发起偏好(`available` + `promptText`)。`promptText` **只是「该工作流维度」那一段记忆**——全局画像 / 全局别名(`user.md` / `memory.md`)已由入口的 `workflow.memoryPrompt`(无参)给过,这里不再重复下发,所以**不要再调一次无参 `memoryPrompt` 来"补全"、也不要把两份拼接**(会重复)。`available: true` 时**优先按 `promptText` 里用户过往的常用取值/口径来补当前 `data` 里用户没说的字段**;与用户本次明说的冲突时以用户本次为准。`available: false`(或带 `note`)说明该工作流还没有偏好或读取失败——**按新用户处理,不要重试读取**。
64
70
 
65
- - `context`(string,必填,minLength 1):复用 createForm 返回的 `context`;prepare 与 apply 必须同一值,原样回传,禁止手工构造/修改/复用他处。
71
+ ### workflow.createRequest
72
+
73
+ - `context`(string,必填,minLength 1):**第 1 步 `createForm` 返回的 `continuation`**,原样回传;两条命令必须同一值,禁止手工构造/修改/复用他处。
66
74
  - `data`(object,必填):表单字段值,结构 `{"main":{...},"details":{"<subFormId>":[{...}]}}`。
67
- - `requestName`(string,可选):流程标题。form 返回 `requestName` 非空(OA 已有标题)时用其值;为空时**只有用户原话明确给了标题**(如"标题叫 X")才用用户给的;**用户没给就不要自己编**——标题 `ismandatory=true` 时 `createPrepare` 会返回 `NEEDS_INPUT`,按 `missing_fields` 向用户问一次并补齐。**标题不得用其他字段值(留言内容/工作流名/事由等)替代**:标题是用户可见的流程名称,用别的字段内容做标题会与流程内容耦合,OA 列表里难以辨识。
75
+ - `action`(enum `submit`|`draft`,必填):`submit`=提交审批(必填缺失阻断,返回 `NEEDS_INPUT`,**不创建流程**);`draft`=仅保存草稿(必填缺失不阻断、状态仍是 `CREATED`,**但 `missingFields` 仍随结果返回**,供模型查看并提示用户提交前补齐)。
76
+ - `requestName`(string,可选):流程标题。form 返回 `requestName` 非空(OA 已有标题)时用其值;为空时**只有用户原话明确给了标题**(如"标题叫 X")才用用户给的;**用户没给就不要自己编**——标题 `ismandatory=true` 且没有可用的默认值时,`createRequest` 会返回 `NEEDS_INPUT`,按 `missingFields` 向用户问一次并补齐。**标题不得用其他字段值(留言内容/工作流名/事由等)替代**:标题是用户可见的流程名称,用别的字段内容做标题会与流程内容耦合,OA 列表里难以辨识。
68
77
  - `requestLevel`(string,可选):紧急程度,取 `systemFields.levelOptions` 的 code。
69
78
  - `secLevel`(string,可选):密级,取 `systemFields.secLevelOptions` 的 id。
70
79
  - `remark`(string,可选):签字意见。
71
- - `intent`(enum `submit`|`draft`,必填):`submit`=提交审批(必填缺失阻断,返回 `NEEDS_INPUT`);`draft`=仅保存草稿(必填缺失不阻断、状态仍是 `READY`,**但 `missing_fields` 仍随结果返回**,供模型查看并提示用户提交前补齐)。
72
-
73
- ### workflow.createApply
74
-
75
- - `context`(string,必填):复用 createPrepare 的 `context` 原样回传。
76
- - `confirm`(const true,必填):声明用户已明确确认本次发起。
77
- - `intent`(enum `submit`|`draft`,必填):不继承 prepare 的 intent,必须显式传入。
78
- - `question`(string,可选):用户发起时的原话(记忆保存参考)。
79
80
  - `verificationConfirmed`(boolean,可选):节点字段校验需用户确认时显式确认继续提交。
80
81
 
81
82
  ## 示例
82
83
 
83
- ### ① 查询可发起工作流
84
-
85
- Windows PowerShell:
86
-
87
- ```powershell
88
- weaver-work-cli --json workflow run workflow.createList --input-json '{"name":"请假"}'
89
- ```
90
-
91
- macOS/Linux(bash/zsh):
92
-
93
- ```bash
94
- weaver-work-cli --json workflow run workflow.createList --input-json '{"name":"请假"}'
95
- ```
96
-
97
- ### ② 初始化表单(拿到 context)
98
-
99
- Windows PowerShell:
100
-
101
- ```powershell
102
- weaver-work-cli --json workflow run workflow.createForm --input-json '{"workflowId":"123456"}'
103
- ```
104
-
105
- macOS/Linux(bash/zsh):
106
-
107
- ```bash
108
- weaver-work-cli --json workflow run workflow.createForm --input-json '{"workflowId":"123456"}'
109
- ```
110
-
111
- ### ③ 收集并准备表单数据(intent 必填)
84
+ ### ① 解析工作流并载入表单(拿到 continuation)
112
85
 
113
86
  Windows PowerShell:
114
87
 
115
88
  ```powershell
116
- weaver-work-cli --json workflow run workflow.createPrepare --input-json '{"context":"<createForm返回的context>","data":{"main":{"报销事由":"客户约谈"}},"intent":"submit"}'
89
+ weaver-work-cli --json workflow run workflow.createForm --input-json '{"workflow":["请假"]}'
117
90
  ```
118
91
 
119
92
  macOS/Linux(bash/zsh):
120
93
 
121
94
  ```bash
122
- weaver-work-cli --json workflow run workflow.createPrepare --input-json '{"context":"<createForm返回的context>","data":{"main":{"报销事由":"客户约谈"}},"intent":"submit"}'
95
+ weaver-work-cli --json workflow run workflow.createForm --input-json '{"workflow":["请假"]}'
123
96
  ```
124
97
 
125
- ### ④ 明确确认后发起并提交审批
98
+ ### ② 填好字段后一次完成发起(action 必填)
126
99
 
127
100
  Windows PowerShell:
128
101
 
129
102
  ```powershell
130
- weaver-work-cli --json workflow run workflow.createApply --input-json '{"context":"<同一context>","confirm":true,"intent":"submit","question":"帮我发起一个报销流程"}'
103
+ weaver-work-cli --json workflow run workflow.createRequest --input-json '{"context":"<createForm返回的continuation>","data":{"main":{"报销事由":"客户约谈"}},"action":"submit"}'
131
104
  ```
132
105
 
133
106
  macOS/Linux(bash/zsh):
134
107
 
135
108
  ```bash
136
- weaver-work-cli --json workflow run workflow.createApply --input-json '{"context":"<同一context>","confirm":true,"intent":"submit","question":"帮我发起一个报销流程"}'
109
+ weaver-work-cli --json workflow run workflow.createRequest --input-json '{"context":"<createForm返回的continuation>","data":{"main":{"报销事由":"客户约谈"}},"action":"submit"}'
137
110
  ```
138
111
 
139
112
  ## 返回
140
113
 
141
- - createList:`SELECTED`/`NEEDS_SELECTION`/`NOT_FOUND`/`MISSING_QUERY`;候选工作流名原样展示,不加"推荐"后缀。`MISSING_QUERY` = `name` 与 `workflowId` 都没传,CLI **未发起查询**、`workflows` 为空 —— 补上名称或 ID 后重调即可(**不要试图拉全量再自己筛**)。**大清单形态**(`name` 命中太多、清单超 24 KB 时):结果最前面多出 `resultFile` / `persistedResult` / `outputBytes` / `workflowsTotal` / `workflowsTruncated` / `largeOutputNotice`,且 `workflows` 只保留**前 100 条**——需要更多候选时从 `resultFile` 按需提取(**不要整份读入上下文**),更推荐把 `name` 写得更精确、或直接传 `workflowId` 来定位。
142
- - createForm:`context`、`runtime`、`frame`、`requiredFields`(字段 id 数组)、`subFormRequired`(subFormId → 字段 id 数组)、`editableFields`(**可写字段 id 白名单**)、`fields`(大表单时可能换成紧凑编码、或被整体移出 stdout —— 移出时同伴有 `writableFields`)、`initialValues`、`systemFields`、`fileUploadParams`、`requiredInfoWarning`、`conditionalRequired`(存在条件必填规则时才返回,见「注意」;**输出过大时它与 `editableFields` 一起移出 stdout、只留 `conditionalRequiredCount` 条数**,需要时按 `resultFile` 取)。
143
- - createPrepare:确认预览(`READY` 时在 `fields`)、`previewDetails`(明细表按列组织:`columns` 表头 + `rows` 每行明细,**仅 `READY` 时返回**;明细表按它排 Excel 列表格)、`resolvedValues`(`NEEDS_INPUT`/`NEEDS_CONFIRMATION` 时的解析回显)、`missing_fields`、`linkageResult`(联动带出/清空,原样转述)、`attachmentFolderIds`。
144
- - createApply:`finalMarkdown`、`requestId`、`requestMark`、标题、`url`;`finalMarkdown` 直接原样输出。`requestMark` 是用户可见流程编号(`finalMarkdown` 已按接口返回渲染:有值显示、无值不显示),`requestId` 只是系统内部流程请求 ID。**明细表展示**:`finalMarkdown` 中明细表已按 Excel 列展示(表头 + 每行明细),直接原样输出。
114
+ - createForm:`status` 为 `FORM_READY` 时给 `continuation`、`workflow`、`runtime`、`frame`、`requiredFields`(字段 id 数组)、`subFormRequired`(subFormId → 字段 id 数组)、`editableFields`(**可写字段 id 白名单**)、`fields`(**布局级字段全集**,含联动隐藏/未在当前页展示的字段,**不等于"页面上肉眼可见的字段数"**;大表单时可能换成紧凑编码、或被整体移出 stdout —— 移出时同伴有 `writableFields`)、`initialValues`、`systemFields`、`fileUploadParams`、`launchPreferences`、`requiredInfoWarning`。**不返回 OA 的联动(显示属性)规则**——必填一律以 `createRequest` 的 `missingFields` 为准(见「注意」)。`status` 为 `NEEDS_CLARIFICATION` 时只给 `candidates` / `clarificationKeyword` / `clarificationReason`(存在时,见「输入」里的 `workflow_not_exist` / `no_create_right`)/ `candidateCount` / `clarificationQuestion` / `note`,**不含任何表单数据**——它不是失败(`ok:true`),也不代表查询已执行。
115
+ - createRequest:`status`(`CREATED` / `CREATED_SUBMITTED` / `CREATED_SUBMIT_FAILED` / `NEEDS_INPUT` / `NEEDS_CONFIRMATION` / `ALREADY_APPLIED`)、`requestId`、`requestMark`、`requestName`、`url`、`contextId`、`fields`(确认预览:本次所填字段的落库结果,"我填的值到底存成了什么"就看这里)、`previewDetails`(明细表按列组织:`columns` 表头 + `rows` 每行明细;明细表按它排 Excel 列表格)、`missingFields`(**恒有值**:submit 成功时是空数组,draft 时是"提交前还需补齐"的提醒)、`missingFieldsNotice`(draft 且有缺失时的一段可直接转述的提醒文本)、`linkageResult`(联动带出/清空,原样转述)、`verification`(节点字段校验结果)、`attachmentFolderIds`、`submitResult`(提交结果,`success` / `failed`;**只在本命令真正发起过提交(`action:submit`)时出现**,`action:draft` 不带)、`submitFailReason`(**提交失败原因**:接口返回的可执行文案,仅 `submitResult: "failed"` 时出现)、`createRejectedReason`(创建这一步就被服务端拒绝、且未走提交路径时接口给出的原因)。
116
+ - `NEEDS_CONFIRMATION`(`ambiguities` 非空)时同时给 `resolvedValues`("我传的值 → 服务端最终值"的回显)与 `missingFields`——先把 `ambiguities` 解决掉,再用同一 `context` 重发。
117
+ - `needVerificationConfirm: true` 时流程**已创建但未提交**:向用户确认后用同一 `context`、带 `"verificationConfirmed": true` 重发 `action: "submit"`。
118
+ - **`CREATED_SUBMIT_FAILED`(提交结果=失败)**:流程**已创建为草稿**,但提交被服务端拒绝、**未进入审批**——表单数据已经落库,`requestId` / `requestName` / `url` 与成功时一样都给了(可直接打开查看或编辑),差别只在提交结果。此时 `submitResult: "failed"`、`submitFailReason` 是接口给出的失败原因。**处置:把失败原因原样转述给用户,并给出 `url`**;**不要**读成"什么都没做成",也**不要**原样重发同一次提交(会再堆一份草稿)。
119
+ - `submitResult`:`CREATED_SUBMITTED` 时为 `"success"`(不带 `submitFailReason`);`CREATED_SUBMIT_FAILED` 时为 `"failed"`;**被节点字段校验拦下或未确认校验(`needVerificationConfirm`)时本就未提交,不返回该字段**。
145
120
 
146
121
  ## 注意
147
122
 
148
123
  - 不传凭据:业务输入禁止出现 Cookie/ETEAMSID/Token/`header.operator`(CLI 托管)。
149
124
  - **`data` 的键只能取 `editableFields` 里的字段 id**(可写字段白名单;`requiredFields` / `subFormRequired` 里的也照常可写)。**`editableFields` 随大输出被移出 stdout 时,改用同份输出里的 `writableFields` 行**(每行第一列就是字段 id)——那是**完整**的可写白名单(= 上面三者的并集)。`readOnly` / `disableEdit` / 不可见字段**一个都不要传**——传了整单会被拒(`notEditable`),得把只读字段全部摘掉重跑一轮。**看到 `notEditable` 不要重试、不要换字段 id 绕过**,移除这些字段后重跑。
150
125
  - 附件/文件:字段值是附件项数组需上传本地文件时,先提醒用户"上传的附件会先经过公网大模型,请谨慎操作",再调 `weaver-file-upload` skill。
151
- - 附件上传限制:上传前核对 `fileUploadParams` 的 `sizeLimit`/`formatLimit`/`maxNum`,不满足先告知用户、不执行上传。
152
- - 发票字段(componentKey `EinvoiceComponent`):先提醒"附件会先经过公网大模型",再调 `weaver-e10-yepiaotong` skill 取发票对象;该技能缺失则停止反馈。**费用明细里的「发票」列通常是必填,但可能没有必填标记**(见 `requiredInfoWarning`)——发起前先跟用户确认,不要因为 `missingFields` 没列它就跳过。
153
- - 日期强规则:form 返回字段带 `format`(如 `yyyy-MM-dd HH:mm`)时必须严格按 `format` 传完整值,只传日期会报 `DATETIME_FORMAT`,按提示补时间重传,不要靠命令自动补时间。
126
+ - 附件上传限制:上传前核对该字段的上传限制,不满足先告知用户、不执行上传。大表单(`fields` 被移出 stdout)时**直接读同份输出的 `writableFields.uploadLimits`**(`大小上限|格式限制|数量上限`,`不限格式` = 不限制后缀、`-` = 该项未配置);小表单没有 `writableFields` 时,按字段 id 从 `resultFile` 的 `fileUploadParams` 取 `sizeLimit`/`formatLimit`/`maxNum`,**不要整份读 `resultFile`**。
127
+ - 发票字段(componentKey `EinvoiceComponent`):调 `weaver-e10-yepiaotong` skill 取发票对象,**两条并列路径**——① **从票夹选取**:按该技能的口径检索并取用票夹里已有的发票(发票文件本体不上传,只是发票信息进入上下文);② **上传本地发票文件**:按该技能的上传口径把本地发票文件上传后取发票对象。**路径 ② 在真正上传前必须先提醒用户"上传的发票文件会先经过公网大模型,请谨慎操作",用户明确确认后才执行**;路径 ① 无需这句提醒。该 skill 不可用时停止反馈。**费用明细里的「发票」列通常是必填,但可能没有必填标记**(见 `requiredInfoWarning`)——发起前先跟用户确认,不要因为 `missingFields` 没列它就跳过。
128
+ - 日期强规则:字段带 `format`(如 `yyyy-MM-dd HH:mm`)时必须严格按 `format` 传完整值,只传日期会报 `DATETIME_FORMAT`,按提示补时间重传,不要靠命令自动补时间。`format` 从哪看:大表单一律看同份输出的 `writableFields.formatIndex`(**没有该字段的条目就是没有格式要求**);只有小表单(无 `writableFields`)或该档没给索引时,才按字段 id 去 `resultFile` 取。**不要为了拿 `format` 再读一次 `resultFile`**。
154
129
  - **富文本字段(componentKey `RichText`)**:支持 HTML 内容、不受 maxLength 限制。**内容来自本地文档(docx/pptx/md 等)或结构化材料时,必须先把源内容转成 HTML(保留 h1-h3 层级、table 表格、ul/ol 列表结构)再填入,禁止直接填纯文本**;用户明确只要纯文本时除外。文本 / 富文本字段直接传字符串即可(CLI 按字段元数据落库),不需要也不允许把它包装成 `{id,name}` 之类的浏览按钮结构。
155
130
  - **字段键**:`main` 的键、`details[subFormId]` 行对象的键**都必须是字段 id**(`fields[].id`,或 `writableFields` 的 `id` 列)。`"字段名(字段ID)"` 这种写法**表单 data 不支持**(那是查询条件 `filterFields` 的写法),混用会报「存在重名或无法识别的表单字段,请改用字段 id」并白跑一轮。
156
131
  - 字段**值**格式:选项传 `name`;浏览按钮/人员/部门传名称自动转 ID;人员范围(EmployeeScope)传对象数组 `{"name":名称,"type":类型}`,类型取 user/dept/subcompany/group/role/position/external/all/allExternal。
157
- - **`workflow.detail` 读不到草稿**(草稿的 `basicInfo`/`formData` 全空)——不要为了"抄一份历史单据的默认值"去 detail 草稿,那是白跑一轮;表单默认值一律以 `createForm` 的 `initialValues` 为准。
158
- - **人员范围(EmployeeScope)解析失败**:`createPrepare` 返回 `NEEDS_CONFIRMATION`,`ambiguities` 里 `kind = "EMPLOYEE_SCOPE"`:
132
+ - **人员范围(EmployeeScope)解析失败**:`createRequest` 返回 `NEEDS_CONFIRMATION`,`ambiguities` 里 `kind = "EMPLOYEE_SCOPE"`:
159
133
  - `reason = "NOT_FOUND"` → 系统里没有该名称,**一次明确告知用户**“没找到「XX(类型)」,请给出系统里的准确名称或 `{"optionId":"候选id","content":"候选名称","type":"类型"}`”,最多重试 1 次;
160
134
  - `reason = "MULTIPLE_MATCHES"` → 列出候选(`candidates`)让用户选,选定后按 `{"optionId","content","type"}` 回传;
161
135
  - `reason = "NOT_RESOLVED"` → 名称/类型齐了但没拿到 ID,通常上一次解析没成功,按上面两种口径让用户确认;
162
136
  - **禁止**因为"命令返回 ok"就当作字段已填好——出现 `ambiguities` 时必须先解决,否则该字段不会进入保存体。
163
- - **静态必填标记未下发(`requiredInfoWarning`)**:`createForm` 返回的 `requiredInfoWarning` 非空时,说明 OA 没下发该表单的**静态**必填配置,此时 `requiredFields` / `subFormRequired` 只含系统字段(流程标题等)——**不代表该表单没有业务必填项**。按 `conditionalRequired` + `missing_fields` 处理即可:
164
- - **`conditionalRequired`**:本表单的**条件必填**清单(OA 联动规则原样结构化)。每项 = 一个会变必填的字段(`fieldId`/`fieldName`/`subFormId`)+ 触发条件 `when[]`(`fieldId`/`fieldName`/`term`/`values[]`),`match` 为 `AND`/`OR`。**发起前用它主动向用户问齐**(例:"若「报销方式」选银行(付公司),则「账号」「开户机构名称」为必填"),不要等用户先填对触发字段。它是"可能的必填",不是当前必填。**它随大输出被移出 stdout 时**(只剩 `conditionalRequiredCount`)不必回头取:直接照下一条按 `missing_fields` 走即可。
165
- - **`missing_fields` 可信**:条件命中与否由 `createPrepare` 按**实际所填数据**(含联动带出值)实时计算,`missing_fields` 就是当前真实缺失的必填项——照常 `createPrepare`,按它的返回补齐。**禁止**对用户说"系统不会校验必填项",也**禁止**用 `requiredInfoWarning` 非空当作"无法校验必填"的理由跳过校验。
137
+ - **静态必填标记未下发(`requiredInfoWarning`)**:`createForm` 返回的 `requiredInfoWarning` 非空时,说明 OA 没下发该表单的**静态**必填配置,此时 `requiredFields` / `subFormRequired` 只含系统字段(流程标题等)——**不代表该表单没有业务必填项**(本租户的业务必填多为**条件必填**:OA 的联动规则只在字段组合命中时才要求必填)。处置一句话:**照常调 `createRequest`,按 `missingFields` 补齐**。
138
+ - **不要自己推演"必填是否齐"**:`createForm` **不下发** OA 的联动(显示属性)规则——规则的触发条件往往取决于你还没填的字段,**发起前无从判断**(本文档不列任何具体条件;**也不要把你从文档或记忆里看到的任何条件样例当作本次的命中原因**)。缺失与否由 `createRequest` 按**实际所填数据**实时算出,看它返回的 `missingFields` 即可,**禁止**为了"提前问齐必填"去猜条件、也**禁止**逐个字段推演。
139
+ - **`missingFields` 可信**:条件命中与否由 `createRequest` 按**实际所填数据**(含联动带出值)实时计算,`missingFields` 就是当前真实缺失的必填项——照常调用,按它的返回补齐。**禁止**对用户说"系统不会校验必填项",也**禁止**用 `requiredInfoWarning` 非空当作"无法校验必填"的理由跳过校验。
166
140
  - 只有需要向用户交代"OA 页面上带红色星号的字段"时,才提示用户对照表单红星核对(含明细表里的列,如费用明细的「发票」)。
167
- - **必填缺失一次问齐**:`createPrepare` 返回 `NEEDS_INPUT`(`missing_fields`)时,**把主表 + 明细表全部缺失字段一次性列给用户**(每项注明需要的值类型/建议默认值),**禁止逐字段推演或逐个追问**;联动/默认值/记忆已覆盖的字段不会出现在 missing 里,直接采用即可。**`intent: draft` 时缺失不阻断、状态仍是 `READY`,但 `missing_fields` 同样会返回——必须原样转述(提交前需补齐),不要把 `READY` 读成"没有缺失"**。
168
- - **必填的浏览 / 人员 / 发票类字段不要"先猜一个值试试"**:这类值要么来自用户(准确名称或 `{id,name}`),要么来自外部技能(发票走 `weaver-e10-yepiaotong`)。**第一次 `createPrepare` 就在 `data` 里留空**,让 `missing_fields` 把它们列出来,再连同其它缺失项**一次性问用户**;用猜测值去 prepare 只会白跑一轮——就算被接受,你也不知道它解析成了**哪个**对象,仍然要回头问用户。
169
- - **`resolvedValues` 用来自查解析结果**:`createPrepare` 返回 `NEEDS_INPUT` / `NEEDS_CONFIRMATION` 时,`resolvedValues` 就是"我传的值 → 服务端最终值"的回显(选项给名称,浏览/人员/部门给 `{id,name}` 数组);`READY` 时同一内容在 `fields`(确认预览)。**解析结果与预期不符就不要往下走**(例如传「泛微」被解析成了另一个主体),按上面的口径向用户确认后续用准确名称/ID。
170
- - **浏览按钮值匹配不上(BROWSER NOT FOUND)**:`createPrepare` 返回 `NEEDS_CONFIRMATION`(`ambiguities` 里 `kind = "BROWSER"`)时按 `reason` 分别处理:
171
- - `reason = "NOT_FOUND"` 且 `candidatesAvailable = true` → CLI 已把该浏览控件首页的合法取值放进 `candidates`,**逐条列给用户挑**,选定后按 `{"id":"候选id","name":"候选名称"}` 重新 prepare;
141
+ - **必填缺失一次问齐**:`createRequest` 返回 `NEEDS_INPUT`(`missingFields`)时,**把主表 + 明细表全部缺失字段一次性列给用户**(每项注明需要的值类型/建议默认值),**禁止逐字段推演或逐个追问**;联动/默认值/记忆已覆盖的字段不会出现在 missing 里,直接采用即可。**`action: draft` 时缺失不阻断、状态是 `CREATED`,但 `missingFields` 同样会返回——必须原样转述(提交前需补齐),不要把 `CREATED` 读成"没有缺失"**(此时命令还会附一段 `missingFieldsNotice`,可直接用它)。
142
+ - **必填的浏览 / 人员 / 发票类字段不要"先猜一个值试试"**:这类值要么来自用户(准确名称或 `{id,name}`),要么来自外部技能(发票走 `weaver-e10-yepiaotong`)。**第一次调用就在 `data` 里留空**,让 `missingFields` 把它们列出来,再连同其它缺失项**一次性问用户**;用猜测值去调用只会白跑一轮——就算被接受,你也不知道它解析成了**哪个**对象,仍然要回头问用户。
143
+ - **`resolvedValues` / `fields` 用来自查解析结果**:`NEEDS_INPUT` / `NEEDS_CONFIRMATION` 时看 `resolvedValues`,成功时看 `fields`——都是"我传的值 → 服务端最终值"的回显(选项给名称,浏览/人员/部门给 `{id,name}` 数组)。**解析结果与预期不符就不要往下走**(例如传「泛微」被解析成了另一个主体),按上面的口径向用户确认后续用准确名称/ID。
144
+ - **浏览按钮值匹配不上(BROWSER NOT FOUND)**:`createRequest` 返回 `NEEDS_CONFIRMATION`(`ambiguities` 里 `kind = "BROWSER"`)时按 `reason` 分别处理:
145
+ - `reason = "NOT_FOUND"` 且 `candidatesAvailable = true` → CLI 已把该浏览控件首页的合法取值放进 `candidates`,**逐条列给用户挑**,选定后按 `{"id":"候选id","name":"候选名称"}` 重新提交;
172
146
  - `reason = "NOT_FOUND"` 且 `candidatesAvailable = false` → **一次明确告知用户"系统里没有「XX」,请提供系统里准确的名称或 `{id,name}`"**,用户给出后最多重试 1 次;
173
147
  - `reason = "MULTIPLE_MATCHES"` → 列出 `candidates` 让用户选,选定后按 `{"id":"候选id","name":"候选名称"}` 回传;
174
- - **禁止自行换相近词反复 prepare**(如「差旅费」查不到就试「差旅」),也**禁止凭猜测直接构造 `{id,name}` 绕过澄清**。
148
+ - **禁止自行换相近词反复提交**(如「差旅费」查不到就试「差旅」),也**禁止凭猜测直接构造 `{id,name}` 绕过澄清**。
175
149
  - 明细表:`details` 的 key 是 subFormId,value 是行对象数组。
176
- - **明细表(子表单)展示规则(Excel 列展示)**:`createPrepare` 返回的 `previewDetails`(明细表按列组织:`columns` 表头 + `rows` 每行明细)——明细表**必须用 Excel 列展示**(表头 + 每行一条明细,形如 `| 序号 | 费用日期 | 费用类型 | 费用说明 | 申请金额 |`,每行一行),**禁止把明细字段按"字段\|内容"平铺**;`previewDetails` 为空时才用 `fields` 平铺兜底。多条明细时逐行展示(序号递增)。
177
- - 目录联动:附件字段 `folderDependsOnOption=true` 时,按最近一次 prepare 返回的 `attachmentFolderIds` 取 `folderId`(null=不指定目录)再上传。
178
- - 初始化值:`createForm` 返回 `initialValues`(OA 预填值,含 onLoadForm 联动带出),prepare 的 `data` 应包含这些初始化字段值(用户明确填写的以用户为准);含 subFormId 的放 details 对应行,主表字段放 main。**漏带不会导致缺必填**(默认值由 CLI 自动并入主表与明细行);`missingFields` 里出现它就是真的没值,按上面口径问用户。
150
+ - **明细表(子表单)展示规则(Excel 列展示)**:以 `previewDetails`(明细表按列组织:`columns` 表头 + `rows` 每行明细)为准——明细表**必须用 Excel 列展示**(表头 + 每行一条明细,形如 `| 序号 | 费用日期 | 费用类型 | 费用说明 | 申请金额 |`,每行一行),**禁止把明细字段按"字段\|内容"平铺**;`previewDetails` 为空时才用 `fields` 平铺兜底。多条明细时逐行展示(序号递增)。
151
+ - 目录联动:附件字段 `folderDependsOnOption=true` 时,按最近一次返回的 `attachmentFolderIds` 取 `folderId`(null=不指定目录)再上传。
152
+ - 初始化值:`createForm` 返回 `initialValues`(OA 预填值,含 onLoadForm 联动带出),`data` 应包含这些初始化字段值(用户明确填写的以用户为准);含 subFormId 的放 details 对应行,主表字段放 main。**漏带不会导致缺必填**(默认值由 CLI 自动并入主表与明细行);`missingFields` 里出现它就是真的没值,按上面口径问用户。
179
153
  - 不重试:saveFormData/flow/create 失败、超时或结果不确定(`SAVE_UNCERTAIN`/`PARTIAL_SAVED`/`CONTEXT_ALREADY_WRITTEN`/`WRITE_UNCERTAIN`)禁止重试,返回恢复信息并让用户人工检查。
180
154
  - 写操作遇登录失效:禁止自动重登重放,返回 `WRITE_UNCERTAIN` 停止,先只读回查。
181
- - `context` 与工作流强绑定:prepare/apply 只用最终选定工作流那次 createForm 返回的 `context`。
182
- - 发起成功且用户有新的语义记忆时,按 memory 规则主动调用 `workflow.memoryExtract` 保存本次工作流与字段偏好(见 workflow-memory.md)。
155
+ - `context` 与工作流强绑定:只用最终选定工作流那次 `createForm` 返回的 `continuation`;澄清后换了工作流就要重新 `createForm` 拿新句柄。
156
+ - 本次发起的工作流本身、业务别名、以及字段口径偏好等**语义记忆**,按 memory 规则在任务结束时主动调用 `workflow.memoryExtract` 保存(见 workflow-memory.md)。