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
@@ -0,0 +1,540 @@
1
+ import { workflowValidationError } from '../../errors.js';
2
+ import { asRecord, readBoundedInt } from './todoSmartConvert.core.js';
3
+ import { orderGroupsByIntent, readGroupOrder } from './todoSmartConvert.group.js';
4
+ import { detectBusinessKind, needsRequestSummary, usesTitleSummary } from './businessRules.js';
5
+ import { openSummaryCache } from './summaryCache.js';
6
+ import { loadDetailContextProvider, stampsOfRows } from './todoSmartConvert.detail.js';
7
+ import { requestItemsOf, rowsOfGroup, runSmartGroup, totalTodoCount, } from './todoSmartConvert.smartGroup.js';
8
+ const DEFAULT_MAX_CHARS_PER_BATCH = 24_000;
9
+ const DEFAULT_MAX_ITEMS_PER_BATCH = 100;
10
+ const DEFAULT_REQUEST_SUMMARY_MAX = 50;
11
+ const INTERNAL_MESSAGE_SUMMARY_HINT = '内部留言:只按原文直接概括「谁说了什么」,不要过度解读、不要推测动机或影响,严格按本批 suggestedMaxChars 控制字数';
12
+ /** 兼容两种拼写:新结构用 `smartSummaryTitle`,早前产物里是 `smartSmmaryTitle`(少一个 m)。 */
13
+ export function readSmartSummaryTitle(args, fallback) {
14
+ const value = args.smartSummaryTitle ?? args.smartSmmaryTitle;
15
+ return typeof value === 'string' && value.trim() !== '' ? value : fallback;
16
+ }
17
+ /**
18
+ * 读 `displayItems`,同时兼容旧回灌结构。
19
+ *
20
+ * 新结构:`[{type:"request",requestData}|{type:"group",id,title,count,summary,defaultExpanded,requestDatas}]`
21
+ * 旧结构:优先组 `requestDatas[]`、普通组 `smartGroup[]`(无 `type`、无 `defaultExpanded`)。
22
+ *
23
+ * 旧结构只可能出现在回灌数据里。这里统一归一成新结构,
24
+ * 免得为了几个字段名把已有产物全部作废。`defaultExpanded` 一律**重算**、不信任输入值。
25
+ */
26
+ export function readDisplayItems(record, index, title, topPriority) {
27
+ if (Array.isArray(record.displayItems)) {
28
+ return record.displayItems.map((rawItem, itemIndex) => {
29
+ const label = `wfIdGroup[${index}]「${title}」.displayItems[${itemIndex}]`;
30
+ const item = asRecord(rawItem);
31
+ if (!item) {
32
+ throw workflowValidationError('wfidgroup_invalid', `${label} 必须是对象`, { retryAllowed: false });
33
+ }
34
+ if (item.type === 'request') {
35
+ const requestData = asRecord(item.requestData);
36
+ if (!requestData) {
37
+ throw workflowValidationError('wfidgroup_invalid', `${label} 是 type:"request",必须带 requestData 对象(一条待办的完整字段)`, { retryAllowed: false });
38
+ }
39
+ return { type: 'request', requestData: requestData };
40
+ }
41
+ if (item.type === 'group') {
42
+ const requestDatas = Array.isArray(item.requestDatas) ? item.requestDatas : null;
43
+ if (!requestDatas) {
44
+ throw workflowValidationError('wfidgroup_invalid', `${label} 是 type:"group",必须带 requestDatas 数组(折叠的明细)`, { retryAllowed: false });
45
+ }
46
+ return {
47
+ type: 'group',
48
+ id: typeof item.id === 'string' ? item.id : '',
49
+ title: typeof item.title === 'string' ? item.title : '',
50
+ count: requestDatas.length,
51
+ summary: typeof item.summary === 'string' ? item.summary : '',
52
+ defaultExpanded: false,
53
+ requestDatas,
54
+ };
55
+ }
56
+ throw workflowValidationError('wfidgroup_invalid', `${label}.type 只支持 "request"(单独一条)/ "group"(汇总项),收到 ${JSON.stringify(item.type)}`, { retryAllowed: false });
57
+ });
58
+ }
59
+ // —— 旧结构兜底 ——
60
+ if (topPriority) {
61
+ const requestDatas = Array.isArray(record.requestDatas) ? record.requestDatas : null;
62
+ if (!requestDatas) {
63
+ throw workflowValidationError('wfidgroup_invalid', `wfIdGroup[${index}]「${title}」是优先组,必须带 displayItems 或旧结构的 requestDatas 数组`, { retryAllowed: false });
64
+ }
65
+ return requestItemsOf(requestDatas);
66
+ }
67
+ const legacy = Array.isArray(record.smartGroup) ? record.smartGroup : null;
68
+ if (!legacy) {
69
+ throw workflowValidationError('wfidgroup_invalid', `wfIdGroup[${index}]「${title}」是普通组,必须带 displayItems 或旧结构的 smartGroup 数组(先跑 stage=smartGroup)`, { retryAllowed: false });
70
+ }
71
+ return legacy.map((rawSub, subIndex) => {
72
+ const requestDatas = Array.isArray(rawSub?.requestDatas) ? rawSub.requestDatas : [];
73
+ return {
74
+ type: 'group',
75
+ id: typeof rawSub?.id === 'string' ? rawSub.id : `${record.id}-${subIndex + 1}`,
76
+ title: typeof rawSub?.title === 'string' ? rawSub.title : '',
77
+ count: requestDatas.length,
78
+ summary: typeof rawSub?.summary === 'string' ? rawSub.summary : '',
79
+ defaultExpanded: false,
80
+ requestDatas,
81
+ };
82
+ });
83
+ }
84
+ /**
85
+ * 最终结构的轻量校验:只认上面那两种组形态(优先组 / 普通组),其它一律报错(不静默修正)。
86
+ * `count` 一律**重算**成 `displayItems` 摊平后的条数——这是唯一计数规则,不采信输入里的 count。
87
+ */
88
+ export function validateFinalGroups(value) {
89
+ return value.map((raw, index) => {
90
+ const record = asRecord(raw);
91
+ if (!record) {
92
+ throw workflowValidationError('wfidgroup_invalid', `wfIdGroup[${index}] 必须是对象`, { retryAllowed: false });
93
+ }
94
+ const id = typeof record.id === 'string' ? record.id.trim() : '';
95
+ const title = typeof record.title === 'string' ? record.title : '';
96
+ if (!id) {
97
+ throw workflowValidationError('wfidgroup_invalid', `wfIdGroup[${index}].id 必填且必须是字符串(19 位 id 用数字会失真)`, { retryAllowed: false });
98
+ }
99
+ const topPriority = record.topPriority === true;
100
+ const displayItems = readDisplayItems(record, index, title, topPriority);
101
+ const count = displayItems.reduce((sum, item) => sum + (item.type === 'group' ? item.requestDatas.length : 1), 0);
102
+ return topPriority
103
+ ? { id, topPriority: true, title, count, defaultExpanded: true, displayItems }
104
+ : { id, topPriority: false, title, count, defaultExpanded: false, displayItems };
105
+ });
106
+ }
107
+ /**
108
+ * 取最终结构:优先用回灌进来的 `wfIdGroup`(`stage=smartGroup` / `stage=summaryContext` 的产出),
109
+ * 否则由 `items` 现场重建(有 `aiGroups` 走兼容模式,没有就走确定性模式)。两条路都落到同一种
110
+ * `FinalWfIdGroup[]`。
111
+ *
112
+ * 重建要从详情里读指标(请假天数/金额),所以这里是 `async`——指标由 `runSmartGroup` 内部
113
+ * 现拉详情得到(详情缓存已删除),本函数不再自己算一份。
114
+ */
115
+ export async function readFinalGroups(args, env) {
116
+ if (Array.isArray(args.wfIdGroup)) {
117
+ // 回灌的结构通常已经是排好序的;这里再套一次 `groupOrder` 只是为了「顺序意图在后续阶段才给出」
118
+ // 也能生效(重排是幂等的,顺序一致时结果不变)。
119
+ return orderGroupsByIntent(validateFinalGroups(args.wfIdGroup), readGroupOrder(args)).groups;
120
+ }
121
+ if (Array.isArray(args.items)) {
122
+ const { data } = await runSmartGroup(args, env);
123
+ return data.wfIdGroup;
124
+ }
125
+ throw workflowValidationError('final_groups_required', '缺少最终结构:用 --input-ref 回灌上一步(stage=smartGroup 或 stage=summaryContext)的输出,'
126
+ + '或传 items 现场重建(传 aiGroups 走 AI 分组,不传按业务类型确定性分组)', { retryAllowed: false });
127
+ }
128
+ export function collectSummaryUnits(groups) {
129
+ const units = [];
130
+ for (const group of groups) {
131
+ if (group.topPriority) {
132
+ if (!needsRequestSummary(detectBusinessKind(group.title)))
133
+ continue;
134
+ units.push({
135
+ groupId: group.id,
136
+ groupTitle: group.title,
137
+ // 优先组没有汇总项,用组自身 id 当键——组摘要那一步同样按这个键取
138
+ subGroupId: group.id,
139
+ subGroupTitle: group.title,
140
+ kind: detectBusinessKind(group.title),
141
+ rows: rowsOfGroup(group),
142
+ });
143
+ continue;
144
+ }
145
+ const kind = detectBusinessKind(group.title);
146
+ if (!needsRequestSummary(kind))
147
+ continue;
148
+ for (const item of group.displayItems) {
149
+ if (item.type === 'group') {
150
+ units.push({
151
+ groupId: group.id,
152
+ groupTitle: group.title,
153
+ subGroupId: item.id,
154
+ subGroupTitle: item.title,
155
+ kind,
156
+ rows: item.requestDatas,
157
+ });
158
+ continue;
159
+ }
160
+ // 单独展示的一条:键用 `request-<requestId>`,只参与单条摘要批次,不出组摘要
161
+ units.push({
162
+ groupId: group.id,
163
+ groupTitle: group.title,
164
+ subGroupId: `request-${item.requestData.requestId}`,
165
+ subGroupTitle: item.requestData.requestNameTitle ?? '',
166
+ kind,
167
+ rows: [item.requestData],
168
+ });
169
+ }
170
+ }
171
+ return units;
172
+ }
173
+ /**
174
+ * `stage=summaryContext`:按最终分组打包摘要批次,并按需获取详情。
175
+ *
176
+ * - **详情为空的条目不喂给 AI**:不进批次,CLI 直接把 `requestNameTitle` 复制到它的
177
+ * `summary`(单条数据也需要摘要,但没有上下文可概括,标题即摘要);
178
+ * - 某个分组下**详情全空** ⇒ 整组不产生批次,结构上保留父组。
179
+ *
180
+ * 输出带着完整的最终结构,因为下一步 `stage=summary`
181
+ * 既要摘要也要结构,而 `--input-ref` 一次只能回灌一份 data —— 把两者放在同一份里,
182
+ * 回灌链才只需要一次引用。
183
+ */
184
+ export async function runSummaryContext(args, env, prefetched) {
185
+ const groups = await readFinalGroups(args, env);
186
+ const units = collectSummaryUnits(groups);
187
+ const skippedSatisfaction = groups
188
+ .filter((group) => usesTitleSummary(detectBusinessKind(group.title)))
189
+ .reduce((sum, group) => sum + rowsOfGroup(group).length, 0);
190
+ // 优先组是叠加视图 ⇒ 同一条待办会在两个位置各出现一次,统计口径必须去重。
191
+ const requestIds = [...new Set(units.flatMap((unit) => unit.rows.map((row) => row.requestId)))].filter(Boolean);
192
+ // 单条摘要目标字数:用户给了口径(如「每条 30 字」)就用用户的,没给才用默认 50。
193
+ const summaryMaxChars = readBoundedInt(args.summaryMaxChars, 'summaryMaxChars', DEFAULT_REQUEST_SUMMARY_MAX, 10, 500);
194
+ // 摘要缓存命中时直接回填,未命中的条目才进入 AI 批次。
195
+ // 预填必须在「准备详情」之前做——已有摘要的条目根本不需要详情(详情只为写摘要服务)。
196
+ const summaryCache = await openSummaryCache(env.ctx);
197
+ /** 已有摘要(缓存命中 / collect 已回填)而跳过 AI 的条数 */
198
+ let prefilled = 0;
199
+ const prefilledIds = new Set();
200
+ const needsDetail = [];
201
+ for (const unit of units) {
202
+ for (const row of unit.rows) {
203
+ const cached = summaryCache.get(row.requestId, row.lastFeedBackDateTime, summaryMaxChars);
204
+ if (cached) {
205
+ row.summary = cached;
206
+ prefilled += 1;
207
+ prefilledIds.add(row.requestId);
208
+ continue;
209
+ }
210
+ needsDetail.push(row.requestId);
211
+ }
212
+ }
213
+ // 详情缓存键依赖 lastFeedBackDateTime:从本轮条目里建 stamps,否则缓存永远 miss。
214
+ const stamps = stampsOfRows(units.flatMap((unit) => unit.rows));
215
+ const provider = await loadDetailContextProvider(env, [...new Set(needsDetail)], args, prefetched, stamps);
216
+ const maxChars = readBoundedInt(args.maxCharsPerBatch, 'maxCharsPerBatch', DEFAULT_MAX_CHARS_PER_BATCH, 500, 200_000);
217
+ const maxItems = readBoundedInt(args.maxItemsPerBatch, 'maxItemsPerBatch', DEFAULT_MAX_ITEMS_PER_BATCH, 1, 500);
218
+ const batches = [];
219
+ let contextAvailable = 0;
220
+ let contextEmpty = 0;
221
+ let titleCopied = 0;
222
+ /**
223
+ * 已经进过 AI 批次的 requestId。
224
+ *
225
+ * 优先组是叠加视图 ⇒ 同一条待办会先在优先组出现、再在工作流组出现。同一条只喂一次模型,
226
+ * 另一份副本由 `stage=summary` 回填时按 requestId 一并写上(`runSummary` 就是按 id 遍历的),
227
+ * 所以这里跳过不会让任何一份摘要空缺,只是省掉一次重复的上下文。
228
+ */
229
+ const claimed = new Set();
230
+ for (const unit of units) {
231
+ let bucket = [];
232
+ let chars = 0;
233
+ let seq = 0;
234
+ const flush = () => {
235
+ if (bucket.length === 0)
236
+ return;
237
+ seq += 1;
238
+ batches.push({
239
+ batchId: `${unit.subGroupId}#${seq}`,
240
+ groupId: unit.groupId,
241
+ groupTitle: unit.groupTitle,
242
+ subGroupId: unit.subGroupId,
243
+ subGroupTitle: unit.subGroupTitle,
244
+ itemCount: bucket.length,
245
+ charCount: chars,
246
+ kind: unit.kind,
247
+ ...(unit.kind === 'internal-message' ? { hint: INTERNAL_MESSAGE_SUMMARY_HINT } : {}),
248
+ suggestedMaxChars: summaryMaxChars,
249
+ items: bucket,
250
+ });
251
+ bucket = [];
252
+ chars = 0;
253
+ };
254
+ for (const row of unit.rows) {
255
+ // 摘要缓存已命中(上一步预填)⇒ 不再喂 AI
256
+ if (prefilledIds.has(row.requestId))
257
+ continue;
258
+ const { context, contextAvailable: available } = provider.get(row.requestId);
259
+ if (!available) {
260
+ // ★ 详情为空 ⇒ 不喂 AI,summary 直接复制标题(确定性操作,不走模型)。
261
+ // 这里**不做** claimed 跳过:叠加视图下每个副本各自持有 summary 字段,都得填一次。
262
+ const firstSeen = !row.requestId || !claimed.has(row.requestId);
263
+ if (!(row.summary ?? '').trim()) {
264
+ row.summary = row.requestNameTitle ?? '';
265
+ if (firstSeen)
266
+ titleCopied += 1;
267
+ }
268
+ // 统计按 requestId 去重,避免同一条在优先组和工作流组里被数两遍
269
+ if (firstSeen) {
270
+ contextEmpty += 1;
271
+ if (row.requestId)
272
+ claimed.add(row.requestId);
273
+ }
274
+ continue;
275
+ }
276
+ // 叠加视图:这条已经在别处进过批次,跳过当前副本(摘要回填按 requestId 覆盖两份)
277
+ if (row.requestId && claimed.has(row.requestId))
278
+ continue;
279
+ if (row.requestId)
280
+ claimed.add(row.requestId);
281
+ contextAvailable += 1;
282
+ const item = {
283
+ requestId: row.requestId,
284
+ requestNameTitle: row.requestNameTitle,
285
+ context,
286
+ contextAvailable: true,
287
+ };
288
+ const itemChars = context.length + row.requestNameTitle.length + 40;
289
+ if (bucket.length > 0 && (bucket.length >= maxItems || chars + itemChars > maxChars))
290
+ flush();
291
+ bucket.push(item);
292
+ chars += itemChars;
293
+ }
294
+ flush();
295
+ }
296
+ const warnings = [...provider.warnings];
297
+ if (skippedSatisfaction > 0) {
298
+ warnings.push(`有 ${skippedSatisfaction} 条满意度类待办不走 AI,summary 将在回填阶段直接使用条目标题`);
299
+ }
300
+ if (titleCopied > 0) {
301
+ warnings.push(`有 ${titleCopied} 条详情数据为空,已不喂给 AI,summary 直接使用标题(requestNameTitle)填充`);
302
+ }
303
+ if (prefilled > 0) {
304
+ warnings.push(`有 ${prefilled} 条已有本地摘要(key=requestId__lastFeedBackDateTime),未再调用 AI;`
305
+ + `本次需要 AI 写摘要的只剩 ${requestIds.length - prefilled - titleCopied} 条`);
306
+ }
307
+ return {
308
+ data: {
309
+ smartSummaryTitle: readSmartSummaryTitle(args, `您当前有 ${totalTodoCount(groups)} 条待办,清单如下:`),
310
+ totalCount: totalTodoCount(groups),
311
+ wfIdGroup: groups,
312
+ summaryBatches: batches,
313
+ summaryStats: {
314
+ requestTotal: requestIds.length,
315
+ /** 本次真正发出去的详情请求条数(摘要缓存命中的不发) */
316
+ detailFetched: provider.stats.fetched + provider.reused,
317
+ /** 其中由上一阶段(smartGroup)facts 直接复用、未发请求的条数 */
318
+ detailReused: provider.reused,
319
+ /** 其中真正走了 HTTP 的条数 */
320
+ detailNetworkFetched: provider.stats.fetched,
321
+ contextAvailable,
322
+ contextEmpty,
323
+ failed: provider.stats.failed,
324
+ titleCopied,
325
+ /** 本地摘要命中、跳过 AI 的条数 */
326
+ prefilled,
327
+ /** 单条摘要的目标字数(默认 50,可由 summaryMaxChars 覆盖) */
328
+ suggestedMaxChars: summaryMaxChars,
329
+ /** 内部留言类批次:摘要要按「最低推理强度」写 */
330
+ internalMessageBatches: batches.filter((batch) => batch.kind === 'internal-message').length,
331
+ skippedSatisfaction,
332
+ summaryCacheFile: summaryCache.file,
333
+ batchCount: batches.length,
334
+ /** 本次详情取数的并发与耗时(现场拉,不再是占位值) */
335
+ concurrency: provider.stats.concurrency,
336
+ durationMs: provider.stats.durationMs,
337
+ maxCharsPerBatch: maxChars,
338
+ maxItemsPerBatch: maxItems,
339
+ },
340
+ },
341
+ warnings,
342
+ };
343
+ }
344
+ /** 校验并索引 `aiSummaries`:扁平 `[{ requestId, summary }]`(与 `aiGroups` 同风格)。 */
345
+ export function readAiSummaries(value) {
346
+ if (!Array.isArray(value)) {
347
+ throw workflowValidationError('ai_summaries_invalid', 'aiSummaries 必须是数组,每项形如 { requestId, summary }', { retryAllowed: false });
348
+ }
349
+ const map = new Map();
350
+ value.forEach((raw, index) => {
351
+ const label = `aiSummaries[${index}]`;
352
+ const record = asRecord(raw);
353
+ if (!record) {
354
+ throw workflowValidationError('ai_summaries_invalid', `${label} 必须是对象 { requestId, summary }`, { retryAllowed: false });
355
+ }
356
+ const unknownKeys = Object.keys(record).filter((key) => !['requestId', 'summary'].includes(key));
357
+ if (unknownKeys.length > 0) {
358
+ throw workflowValidationError('ai_summaries_invalid', `${label} 不接受字段:${unknownKeys.join(', ')};只支持 requestId / summary`, { retryAllowed: false, details: { unknownKeys } });
359
+ }
360
+ const requestId = typeof record.requestId === 'string' ? record.requestId.trim() : '';
361
+ if (!requestId) {
362
+ throw workflowValidationError('ai_summaries_invalid', `${label}.requestId 必填且必须是字符串(19 位 id 用数字会失真)`, { retryAllowed: false });
363
+ }
364
+ if (typeof record.summary !== 'string') {
365
+ throw workflowValidationError('ai_summaries_invalid', `${label}.summary 必须是字符串`, { retryAllowed: false });
366
+ }
367
+ if (map.has(requestId)) {
368
+ throw workflowValidationError('ai_summary_duplicate_request', `requestId「${requestId}」在 aiSummaries 里出现了多次,一条数据只能有一个摘要`, { retryAllowed: true, details: { requestId } });
369
+ }
370
+ map.set(requestId, record.summary.trim());
371
+ });
372
+ return map;
373
+ }
374
+ /**
375
+ * `stage=summary`:把模型写的摘要填进最终结构中对应待办的 `summary`。
376
+ *
377
+ * 输出沿用目标 demo 的唯一业务结构:`smartSummaryTitle + totalCount + wfIdGroup`。
378
+ * 摘要只写入对应条目的 `summary`;后续组摘要直接消费 `wfIdGroup`,不额外输出重复映射。
379
+ *
380
+ * 漏写不报错(记 warning、留空串)——模型漏几条不该让整批 300 条重跑;但 id 不存在、
381
+ * 重复出现属于「结果与源数据对不上」,硬失败。
382
+ */
383
+ export async function runSummary(args, env) {
384
+ const groups = await readFinalGroups(args, env);
385
+ const summaries = readAiSummaries(args.aiSummaries);
386
+ // 单条摘要目标字数:与 `runSummaryContext` 保持同一口径(用户给了就用用户的)。
387
+ const summaryMaxChars = readBoundedInt(args.summaryMaxChars, 'summaryMaxChars', DEFAULT_REQUEST_SUMMARY_MAX, 10, 500);
388
+ // ★ 本次 AI 写出来的摘要顺手进缓存(key=requestId__lastFeedBackDateTime),
389
+ // 下次同一条流程的待办再来时可以复用摘要。
390
+ const summaryCache = await openSummaryCache(env.ctx);
391
+ let cacheWritten = 0;
392
+ // 缓存预填(与 `summaryContext` 同口径):只跑 `summary` 阶段时也能拿到已写好的摘要,
393
+ // 否则重跑 finish(不带 aiSummaries)会把整份摘要清成空串。
394
+ // 满意度类不走 AI、也不读缓存:直接用标题当摘要,保证 `summary` 不为空。
395
+ let titleSummary = 0;
396
+ for (const group of groups) {
397
+ if (usesTitleSummary(detectBusinessKind(group.title))) {
398
+ for (const row of rowsOfGroup(group)) {
399
+ if ((row.summary ?? '').trim())
400
+ continue;
401
+ row.summary = row.requestNameTitle ?? '';
402
+ if ((row.summary ?? '').trim())
403
+ titleSummary += 1;
404
+ }
405
+ continue;
406
+ }
407
+ for (const row of rowsOfGroup(group)) {
408
+ if (!row?.requestId || (row.summary ?? '').trim())
409
+ continue;
410
+ const cached = summaryCache.get(row.requestId, row.lastFeedBackDateTime, summaryMaxChars);
411
+ if (cached) {
412
+ row.summary = cached;
413
+ }
414
+ }
415
+ }
416
+ const knownRequestIds = new Set();
417
+ for (const group of groups) {
418
+ for (const row of rowsOfGroup(group)) {
419
+ if (row?.requestId)
420
+ knownRequestIds.add(row.requestId);
421
+ }
422
+ }
423
+ for (const requestId of summaries.keys()) {
424
+ if (!knownRequestIds.has(requestId)) {
425
+ throw workflowValidationError('ai_summary_unknown_request', `aiSummaries 里的 requestId「${requestId}」不在本次最终结构里(可能 id 传错,或摘要是上一轮数据留下的)`, { retryAllowed: true, details: { requestId } });
426
+ }
427
+ }
428
+ const warnings = [];
429
+ let filled = 0;
430
+ let empty = 0;
431
+ let tooLong = 0;
432
+ /** 详情为空、CLI 已用标题填充并被本次保留的条数 */
433
+ let titleKept = 0;
434
+ /** 本地摘要缓存命中(不是标题兜底)并被本次保留下来的条数 */
435
+ let prefilledKept = 0;
436
+ /** 统计用:同一条待办在叠加视图里出现两次,计数只算一次 */
437
+ const countedIds = new Set();
438
+ const applyAll = (rows) => {
439
+ for (const row of rows) {
440
+ const summary = summaries.get(row.requestId) ?? '';
441
+ const firstTime = !row.requestId || !countedIds.has(row.requestId);
442
+ if (row.requestId)
443
+ countedIds.add(row.requestId);
444
+ if (!summary && (row.summary ?? '').trim() !== '') {
445
+ // 没有 AI 摘要,但结构里已经有摘要:
446
+ // - 详情为空 ⇒ CLI 用标题填的(确定性操作、不覆盖、不计空);
447
+ // - 本地缓存命中 ⇒ 已经是写过的摘要,同样保留。
448
+ if (firstTime) {
449
+ if ((row.summary ?? '').trim() === (row.requestNameTitle ?? '').trim())
450
+ titleKept += 1;
451
+ else
452
+ prefilledKept += 1;
453
+ }
454
+ continue;
455
+ }
456
+ if (summary) {
457
+ if (firstTime) {
458
+ filled += 1;
459
+ if (summary.length > summaryMaxChars)
460
+ tooLong += 1;
461
+ }
462
+ if (summaryCache.set(row.requestId, row.lastFeedBackDateTime, summary, summaryMaxChars))
463
+ cacheWritten += 1;
464
+ }
465
+ else if (firstTime) {
466
+ empty += 1;
467
+ }
468
+ // 空串就是空串:不拿标题凑、不编造
469
+ row.summary = summary;
470
+ }
471
+ };
472
+ for (const group of groups) {
473
+ if (!needsRequestSummary(detectBusinessKind(group.title)))
474
+ continue;
475
+ if (group.topPriority) {
476
+ applyAll(rowsOfGroup(group));
477
+ continue;
478
+ }
479
+ for (const item of group.displayItems) {
480
+ if (item.type === 'group') {
481
+ applyAll(item.requestDatas);
482
+ continue;
483
+ }
484
+ // 单独展示的一条:只回填它自己
485
+ applyAll([item.requestData]);
486
+ }
487
+ }
488
+ if (empty > 0) {
489
+ warnings.push(`有 ${empty} 条没有拿到 AI 摘要,summary 保持空串(未用标题兜底、未编造)`);
490
+ }
491
+ if (titleKept > 0) {
492
+ warnings.push(`有 ${titleKept} 条详情为空的条目保留 CLI 填充的标题摘要(不参与 AI 回填)`);
493
+ }
494
+ if (tooLong > 0) {
495
+ warnings.push(`有 ${tooLong} 条摘要超过目标长度 ${summaryMaxChars} 字(可按用户口径调整),已原样保留;`
496
+ + '如需控制长度请重跑模型侧(CLI 不改写文案)');
497
+ }
498
+ if (prefilledKept > 0) {
499
+ warnings.push(`有 ${prefilledKept} 条沿用本地摘要缓存(未重新生成)`);
500
+ }
501
+ const titleSummaryCount = groups
502
+ .filter((group) => usesTitleSummary(detectBusinessKind(group.title)))
503
+ .reduce((sum, group) => sum + rowsOfGroup(group).length, 0);
504
+ if (titleSummaryCount > 0) {
505
+ warnings.push(`有 ${titleSummaryCount} 条满意度类待办不调用 AI,summary 直接使用条目标题(保证非空)`);
506
+ }
507
+ // 缓存写不进去不致命:只是下次还得重新问 AI,不能因此让本轮结果失败。
508
+ let summaryCacheFile = summaryCache.file;
509
+ if (cacheWritten > 0) {
510
+ const written = await summaryCache.save();
511
+ if (written)
512
+ summaryCacheFile = written;
513
+ else
514
+ warnings.push(`摘要缓存写入失败(${cacheWritten} 条),本次结果不受影响;下次仍会重新生成`);
515
+ }
516
+ return {
517
+ data: {
518
+ smartSummaryTitle: readSmartSummaryTitle(args, `您当前有 ${totalTodoCount(groups)} 条待办,清单如下:`),
519
+ totalCount: totalTodoCount(groups),
520
+ wfIdGroup: groups,
521
+ },
522
+ meta: {
523
+ summarized: filled,
524
+ empty,
525
+ /** 单条摘要的目标字数(默认 50,可由 summaryMaxChars 覆盖) */
526
+ suggestedMaxChars: summaryMaxChars,
527
+ titleCopied: titleKept,
528
+ /** 本地摘要缓存命中、未重新生成的条数 */
529
+ prefilled: prefilledKept,
530
+ skippedSatisfaction: titleSummaryCount,
531
+ /** 满意度类用条目标题当摘要、未调用 AI 的条数 */
532
+ titleSummary,
533
+ /** 本次新写入缓存的条数 */
534
+ summaryCacheWritten: cacheWritten,
535
+ summaryCacheFile,
536
+ },
537
+ warnings,
538
+ };
539
+ }
540
+ // ---------------------------------------------------------------------------
@@ -34,6 +34,13 @@ export const COMPACT_STDOUT_PATHS = {
34
34
  'data.systemFields',
35
35
  'data.data',
36
36
  ],
37
+ // `createRequestDirect`(2026-10-22 起只取数、不发起):返回面就是「fields + historyRequestData +
38
+ // userMemory」,前两个同样是"逐条长、层级深"的数组 —— 压掉缩进省 24%~31%,直接把大量表单从
39
+ // 触线降到线下。`userMemory` 是单行 Markdown 字符串,本身没有缩进可压,不必登记。
40
+ 'workflow.createRequestDirect': [
41
+ 'data.fields',
42
+ 'data.historyRequestData',
43
+ ],
37
44
  };
38
45
  /** 该 operation 的紧凑路径(不声明 = 维持全量缩进)。导出让用例锁住接线。 */
39
46
  export function compactStdoutPaths(operation) {
package/docs/SKILL.md CHANGED
@@ -32,7 +32,7 @@ skills/weaver-e10-<name>/
32
32
  ## 已内置业务模块
33
33
 
34
34
  - `invoice`:E10 业票通发票管理。源码说明先读 `docs/invoice.md`;面向 Agent 时先读 `skills/weaver-e10-yepiaotong/SKILL.md`,再按入口路由读取对应 reference。
35
- - `fna`:E10 齐业成费控中心(预算查询/费用中心/借款管理/发票核销)。源码说明先读 `docs/fna.md`;面向 Agent 时先读 `skills/weaver-e10-qiyecheng/SKILL.md`,再按入口路由读取对应 reference。写操作(提醒还款/催票)走 prepare/apply 确认链。
35
+ - `fna`:E10 齐业成费控中心(预算查询/费用中心/报销报表 AI 问答/借款管理/发票核销)。源码说明先读 `docs/fna.md`;面向 Agent 时先读 `skills/weaver-e10-qiyecheng/SKILL.md`,再按入口路由读取对应 reference。写操作(提醒还款/催票)走 prepare/apply 确认链。
36
36
  - `plan`:E10 计划报告(报告查询/新建/修改/发布/撤回/提交提醒/评论),自动适配标准版与 e10-ebuilder 两套报告接口链路。源码说明先读 `docs/plan.md`;面向 Agent 时先读 `skills/weaver-e10-plan/SKILL.md`,再按入口路由读取对应 reference。写操作走 prepare/apply 确认链。
37
37
  - `esb`:E10 ESB 中心自定义触发动作流(入参模板查询 + 触发)。源码说明先读 `docs/esb.md`;面向 Agent 时先读 `skills/weaver-e10-esb/SKILL.md`,再按入口路由读取对应 reference。触发动作流是写操作,走 prepare/apply 确认链。
38
38
  - `okr`:E10 OKR 目标管理(目标分页查询/详情、关键成果查询与保存、目标对齐、目标评论、详情页链接),按 `ver_name` 自动适配 e10 标准版与 e10-ebuilder 两套接口链路。源码说明先读 `docs/okr.md`;面向 Agent 时先读 `skills/weaver-e10-okr/SKILL.md`,再按入口路由读取对应 reference。写操作(新建/编辑目标、保存关键成果、添加目标对齐)走 prepare/apply 确认链。
package/docs/_catalog.md CHANGED
@@ -10,12 +10,13 @@
10
10
  | 业票通发票管理、报销填单、智能开票、invoice operation、写操作 prepare/apply | `docs/invoice.md` |
11
11
  | E10 会议管理(标准版:查询/冲突/预约/回执/签到)、meeting operation、写操作 prepare/apply | `docs/meeting.md` |
12
12
  | 聚才林人事云(人事档案/出勤/薪酬)、ehr operation、写操作 prepare/apply | `docs/ehr.md` |
13
- | 齐业成费控中心(预算/费用中心/借款管理/发票核销)、fna operation、提醒还款与催票 prepare/apply | `docs/fna.md` |
13
+ | 齐业成费控中心(预算/费用中心/报销报表 AI 问答/借款管理/发票核销)、fna operation、提醒还款与催票 prepare/apply | `docs/fna.md` |
14
14
  | 文书定档案管理(全宗/检索/借阅车/借阅单/发起借阅/预归档上传)、wenshuding operation | `docs/wenshuding.md` |
15
15
  | 计划报告管理(报告查询/新建/修改/发布/撤回/提交提醒/评论)、plan operation、写操作 prepare/apply | `docs/plan.md` |
16
16
  | OKR 目标管理(目标查询/详情、关键成果、目标对齐、评论)、okr operation、标准版与 ebuilder 双链路、写操作 prepare/apply | `docs/okr.md` |
17
17
  | eBuilder 表单(菜单定位、List/NList、字段/浏览、OpenAPI 与自定义接口)、ebuilder-form operation、写操作 prepare/apply | `docs/ebuilder-form.md` |
18
18
  | E10 ESB 中心自定义触发动作流(入参模板查询 / prepare-apply 触发)、esb operation | `docs/esb.md` |
19
+ | 待办智能汇总(workflow 内独立分支:跨条目取数/分组/摘要/金额合计,与列表查询解耦) | `docs/todo-smart-convert/README.md` |
19
20
  | 事井然项目管理(项目/任务/字典/字段解析)、project operation、写操作 prepare/apply | `docs/project.md` |
20
21
  | 快速安装 CLI,并使用 CLI/Skill Maker 生成、验证、打包和安装业务模块 Skill | `docs/operation-manual.md` |
21
22
  | 把随包 Agent Skill 安装/升级到本机 Agent(Codex/WorkBuddy 目录、从本地仓库发布最新版) | `docs/agent-skill-install.md` |
@@ -67,7 +67,7 @@ weaver-work-cli --json invoice run invoice.list --input-json '{"page_size":10,"s
67
67
 
68
68
  删除、编辑、新增同样必须使用 `prepare -> apply`。出现 `partial/write_uncertain` 时停止,不能自动重试写操作。
69
69
 
70
- 处理“把发票填进流程表单”的场景,使用 `invoice.browse-field.data` 生成浏览按钮控件所需 JSON。处理“报销发票/自动填单”时,先读取 `invoice-reim.md`,用报销相关只读 operation 获取事前申请、工作流、表单结构、预填信息和人员上级;Agent 组装报销单结构化 JSON 后,必须通过 `invoice.reim.flow.create.prepare/apply` 或 `invoice.reim.flow.update.prepare/apply` 写入,创建报销单的 `work_flow_id/mainFormId/request_name/main_fields/detail_rows` 等字段在 operation 顶层传入,不包通用 `payload`。处理“开票/存台账”时,先读取 `invoice-issuing.md` 和 `invoice-issuing-field-rules.md`,从用户输入或附件提取购方、税号、明细、税率和金额;资料不足时追问用户补齐,再走 `invoice.issuing.make.prepare/apply`。当前修正资料包没有客户档案查询或红字发票红冲接口,`invoice.customer.*` 和 `invoice.red.*` 不能调用或绕过。
70
+ 处理“把发票填进流程表单”的场景,使用 `invoice.browse-field.data` 生成浏览按钮控件所需 JSON。处理“报销发票/自动填单”时,先读取 `invoice-reim.md`,用报销相关只读 operation 获取事前申请、工作流、表单结构、预填信息和人员上级;Agent 组装报销单结构化 JSON 后,必须通过 `invoice.reim.flow.create.prepare/apply` 或 `invoice.reim.flow.update.prepare/apply` 写入,创建报销单的 `work_flow_id/mainFormId/request_name/main_fields/detail_rows` 等字段在 operation 顶层传入,不包通用 `payload`。处理“开票”时默认读取 `invoice-issuing-workflow.md`:用 `invoice.issuing.qys.list` 的 `groups` 按合同号选定一组,确认后调用 `weaver-e10-workflow` 的 `workflow.createForm` / `workflow.createRequest`(`action` 固定 `draft`)发起开票流程草稿。只有用户明确要求“直接开票/不走流程/存待开台账”时,才读取 `invoice-issuing-qys-ledger.md`(台账行)或 `invoice-issuing.md` + `invoice-issuing-field-rules.md`(口述字段),走 `invoice.issuing.make.prepare/apply`。当前修正资料包没有客户档案查询或红字发票红冲接口,`invoice.customer.*` 和 `invoice.red.*` 不能调用或绕过。
71
71
 
72
72
  ## 暂缓能力
73
73
 
@@ -4,7 +4,7 @@
4
4
 
5
5
  源码位于 `src/shortcuts/caizhilian`。命令注册、`operation manifest` 和业务执行逻辑分离:`manifest.ts` 汇总对外契约(含 `withheldOperations`),`operations/registry.ts` 由 `operations/specs.ts` 的 60 条操作规格展开成 87 条对外 operation(写操作展开为 `.prepare` / `.apply`),具体执行逻辑在 `src/shortcuts/caizhilian/operations/handlers` 中按业务分组维护(document、folder、collab、search、fileops、system),公共能力在 `operations/api.ts`、`operations/files.ts`、`operations/form.ts`、`operations/shared.ts`。
6
6
 
7
- 面向 Agent 的说明位于 `skills/weaver-e10-caizhilian`。入口 `SKILL.md` 只负责共享规则和 reference 路由;具体细节拆在 `skills/weaver-e10-caizhilian/references/`。
7
+ 面向 Agent 的说明位于 `skills/weaver-e10-caizhilian`。入口 `SKILL.md` 只负责接入前置和 reference 路由;具体细节拆在 `skills/weaver-e10-caizhilian/references/`。
8
8
 
9
9
  ## 公共前置
10
10
 
package/docs/e10-auth.md CHANGED
@@ -69,7 +69,7 @@ E10 目标地址按“显式 `--base-url` → 专用 `WEAVER_XIAOE_*` E10 配置
69
69
 
70
70
  `auth xiaoe` 会先打印不含凭证的 preflight 摘要,给出 `authMode=xiaoe`、`credentialSourceDetected`、`attempted`、各凭据源检测结果和目标 `baseUrl`。进入脚本分支时会打印 `Trying XiaoE token script credential...`;进入内置 Node 会话分支时会打印 `Trying built-in XiaoE client session credential...`。成功结果和后续 `auth status` 会显示 `credentialSourceStep`、`credentialSourceType`、`credentialSourceLabel`。
71
71
 
72
- 业务命令不会自动建立 XiaoE 登录态。桌面客户端需要 XiaoE 专属业务行为时,Agent 必须显式传全局参数,例如 `weaver-work-cli --runtime-agent xiaoe workflow ...`;网页端由原有环境变量规则自动识别,不要求补这个参数。`--runtime-agent` 只控制运行时行为,不携带也不选择凭据。WorkBuddy、Codex 及其他智能体使用 `auth login --base-url <url> --agent_type <agent_type>` 的浏览器 OAuth 流程。
72
+ 业务命令不会在缺少本地会话时主动建立 XiaoE 登录态,首次使用仍需显式执行 `auth xiaoe`。已有会话被 E10 接口明确拒绝为失效时,公共请求运行时会静默换票并重放原请求一次;不会打印中间登录步骤,不会自动打开浏览器,也不会对流式请求、网络错误、`partial` / `write_uncertain` 做重放。桌面客户端需要 XiaoE 专属业务行为时,Agent 必须显式传全局参数,例如 `weaver-work-cli --runtime-agent xiaoe workflow ...`;网页端由原有环境变量规则自动识别,不要求补这个参数。`--runtime-agent` 只控制运行时行为,不携带也不选择凭据。WorkBuddy、Codex 及其他智能体使用 `auth login --base-url <url> --agent_type <agent_type>` 的浏览器 OAuth 流程。
73
73
 
74
74
  无法使用浏览器回调时,可以用 `auth set` 直接写入浏览器中复制到的
75
75
  `ETEAMSID`。命令会调用 `teamsCheck` 解析当前用户、租户和组织信息,并写入