weaver-work-cli 0.1.36 → 0.1.39

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 (169) hide show
  1. package/README.md +4 -2
  2. package/dist/chunks/{archive-XBVH7JU7.js → archive-ENZYBBME.js} +559 -68
  3. package/dist/chunks/{archive-oa-MZM3EKFQ.js → archive-oa-YWLEC5V4.js} +4 -3
  4. package/dist/chunks/{asset-4XO7UOUW.js → asset-OOV6DWPA.js} +8 -7
  5. package/dist/chunks/asset-idle-RE632MY5.js +670 -0
  6. package/dist/chunks/{attachment-content-VXSWANAC.js → attachment-content-RWN2FCLL.js} +3 -3
  7. package/dist/chunks/{blog-PGZKOPSC.js → blog-PEPOJAL7.js} +5 -4
  8. package/dist/chunks/{caizhilian-BH4ZVGUN.js → caizhilian-WYIQDVPJ.js} +5 -4
  9. package/dist/chunks/{calendar-MXEMTIO4.js → calendar-CIARBAOX.js} +4 -3
  10. package/dist/chunks/{chunk-NCOVUAZK.js → chunk-36DIFZAH.js} +4 -3
  11. package/dist/chunks/{chunk-I6QGIBDM.js → chunk-BJGXMFGD.js} +4 -3
  12. package/dist/chunks/{chunk-TC6MZAO3.js → chunk-CQHGNCK2.js} +52 -2
  13. package/dist/chunks/{chunk-WXFPWCXG.js → chunk-EINRJHQ3.js} +3 -3
  14. package/dist/chunks/{chunk-JUXVYKII.js → chunk-EZGFHJFW.js} +201 -19
  15. package/dist/chunks/{chunk-VPUTVEMC.js → chunk-F2EQXUEE.js} +436 -290
  16. package/dist/chunks/{chunk-JYKABYCV.js → chunk-GVPJZXJ2.js} +3 -3
  17. package/dist/chunks/{chunk-6LLJFKJE.js → chunk-HJYVHXUQ.js} +1 -1
  18. package/dist/chunks/{chunk-T72N74ZT.js → chunk-I2HB737U.js} +5 -4
  19. package/dist/chunks/{chunk-VPXOPQJI.js → chunk-K3RUEEOL.js} +4 -2
  20. package/dist/chunks/{chunk-HXJQI5NM.js → chunk-KTCB5R5H.js} +3 -2
  21. package/dist/chunks/{chunk-XMGSYD6S.js → chunk-L4N7B7ZD.js} +4 -4
  22. package/dist/chunks/{chunk-QYGO5EE2.js → chunk-MUQUHKPG.js} +4 -3
  23. package/dist/chunks/{chunk-QHFANTX7.js → chunk-NLK2RZKG.js} +1348 -90
  24. package/dist/chunks/{chunk-F746NKNF.js → chunk-P2DAISYW.js} +1 -1
  25. package/dist/chunks/{chunk-BPE37FPB.js → chunk-T7JDJJ7H.js} +3 -3
  26. package/dist/chunks/{chunk-JNG42JKG.js → chunk-XXJOSZTQ.js} +6 -6
  27. package/dist/chunks/{chunk-5DQEEGCC.js → chunk-YS5DPN4T.js} +1 -1
  28. package/dist/chunks/{chunk-EG6ZU444.js → chunk-ZUX3P6BQ.js} +10 -5
  29. package/dist/chunks/{chunk-JCSN3JGM.js → chunk-ZXRNHZTE.js} +7 -6
  30. package/dist/chunks/{conflict-JAB2LJEZ.js → conflict-DRMDOL56.js} +2 -2
  31. package/dist/chunks/{contract-XI2EEIZU.js → contract-73MERZWN.js} +4 -3
  32. package/dist/chunks/{cowork-SCPGLBA4.js → cowork-HNGBPD7A.js} +4 -3
  33. package/dist/chunks/{ebuilder-form-EYLHNOTA.js → ebuilder-form-OY6DVNRK.js} +3 -3
  34. package/dist/chunks/{ehr-EAP77IMT.js → ehr-RAQNEQO7.js} +4 -3
  35. package/dist/chunks/{esb-2TGGXEE7.js → esb-6XV7C7YV.js} +4 -4
  36. package/dist/chunks/{fna-2X7ZE7BU.js → fna-XTSRB7G7.js} +4 -3
  37. package/dist/chunks/{host-Q4LZGCRU.js → host-3F5UM5GN.js} +3 -3
  38. package/dist/chunks/{host-XCLDDXKQ.js → host-A7J6GDUP.js} +3 -3
  39. package/dist/chunks/{host-I4NRK4MX.js → host-VGPVP7YF.js} +3 -3
  40. package/dist/chunks/{host-C6EL2RKL.js → host-YAQAPYNP.js} +3 -3
  41. package/dist/chunks/{host-UEI5FGDM.js → host-YMBXWFWX.js} +3 -3
  42. package/dist/chunks/{hrm-YXERJM76.js → hrm-TLHKC545.js} +3 -3
  43. package/dist/chunks/{im-A4VZN2TQ.js → im-YOUCFMUK.js} +3 -3
  44. package/dist/chunks/{invoice-2Z6YPMIS.js → invoice-WNBS2B4C.js} +3 -3
  45. package/dist/chunks/{jiuchuanhui-AR5GMKZB.js → jiuchuanhui-XABWS5RN.js} +4 -3
  46. package/dist/chunks/{lazy-dispatch-QXW7P4X3.js → lazy-dispatch-NLZ5UQQI.js} +5 -5
  47. package/dist/chunks/{mail-7GZ6CPBJ.js → mail-LRYMBFNL.js} +4 -3
  48. package/dist/chunks/{meeting-3NMGA3MB.js → meeting-ESXL6MFI.js} +13 -12
  49. package/dist/chunks/{okr-BZ6XUOAG.js → okr-EOV65SSF.js} +4 -3
  50. package/dist/chunks/{plan-DR5CMUYP.js → plan-YFPVKW6Y.js} +4 -3
  51. package/dist/chunks/{project-6J63MK33.js → project-MDVZUYQW.js} +4 -3
  52. package/dist/chunks/{registry-VRTNC6UR.js → registry-QCLZEDAP.js} +8 -8
  53. package/dist/chunks/{registry-KDD7DHNK.js → registry-VT5Y2HLA.js} +4 -4
  54. package/dist/chunks/{registry-TKX2E7C7.js → registry-YX4CSEMD.js} +3 -3
  55. package/dist/chunks/{room-recommend-XCWRMB5P.js → room-recommend-L3MWH4JW.js} +4 -4
  56. package/dist/chunks/{schedule-IVBICP32.js → schedule-UASAVDVD.js} +4 -4
  57. package/dist/chunks/{workflow-D74NFK3C.js → workflow-KDGIUCNW.js} +9 -6
  58. package/dist/core/output.js +16 -6
  59. package/dist/index.js +33 -28
  60. package/dist/internal/e10/auth/messages.js +1 -0
  61. package/dist/internal/e10/auth-recovery.js +127 -7
  62. package/dist/internal/e10/client.js +16 -2
  63. package/dist/internal/e10/context.js +16 -3
  64. package/dist/internal/e10/request-runtime.js +106 -8
  65. package/dist/shortcuts/archive/host.js +5 -2
  66. package/dist/shortcuts/archive/manifest.js +1 -1
  67. package/dist/shortcuts/archive/operations/borrow-list.js +13 -1
  68. package/dist/shortcuts/archive/operations/flow-url.js +1 -1
  69. package/dist/shortcuts/archive/operations/prelib.js +472 -0
  70. package/dist/shortcuts/archive/operations/registry.js +2 -0
  71. package/dist/shortcuts/archive/operations/upload.js +1 -1
  72. package/dist/shortcuts/archive-oa/host.js +2 -1
  73. package/dist/shortcuts/asset/host.js +6 -5
  74. package/dist/shortcuts/asset-idle/errors.js +44 -0
  75. package/dist/shortcuts/asset-idle/host.js +161 -0
  76. package/dist/shortcuts/asset-idle/index.js +114 -0
  77. package/dist/shortcuts/asset-idle/manifest.js +159 -0
  78. package/dist/shortcuts/asset-idle/operations/idle.js +47 -0
  79. package/dist/shortcuts/asset-idle/operations/registry.js +33 -0
  80. package/dist/shortcuts/asset-idle/operations/shared.js +142 -0
  81. package/dist/shortcuts/asset-idle/operations/types.js +9 -0
  82. package/dist/shortcuts/asset-idle/operations.js +15 -0
  83. package/dist/shortcuts/blog/host.js +3 -2
  84. package/dist/shortcuts/caizhilian/host.js +3 -2
  85. package/dist/shortcuts/calendar/host.js +2 -1
  86. package/dist/shortcuts/contract/host.js +2 -1
  87. package/dist/shortcuts/cowork/host.js +2 -1
  88. package/dist/shortcuts/ebuilder-form/host.js +3 -1
  89. package/dist/shortcuts/ehr/host.js +2 -1
  90. package/dist/shortcuts/esb/host.js +2 -1
  91. package/dist/shortcuts/fna/host.js +2 -1
  92. package/dist/shortcuts/hrm/host.js +2 -1
  93. package/dist/shortcuts/im/host.js +3 -2
  94. package/dist/shortcuts/index.js +5 -0
  95. package/dist/shortcuts/invoice/host.js +5 -4
  96. package/dist/shortcuts/jiuchuanhui/host.js +2 -1
  97. package/dist/shortcuts/mail/host.js +2 -1
  98. package/dist/shortcuts/meeting/host.js +3 -2
  99. package/dist/shortcuts/meeting/invitation/text.js +71 -0
  100. package/dist/shortcuts/meeting/operations/eb/create-flow.js +1073 -50
  101. package/dist/shortcuts/meeting/operations/eb/crm-search.js +478 -0
  102. package/dist/shortcuts/meeting/operations/eb/factory.js +60 -1
  103. package/dist/shortcuts/meeting/operations/eb/index.js +2 -0
  104. package/dist/shortcuts/meeting/operations/invitation.js +122 -9
  105. package/dist/shortcuts/okr/host.js +2 -1
  106. package/dist/shortcuts/plan/host.js +2 -1
  107. package/dist/shortcuts/project/host.js +2 -1
  108. package/dist/shortcuts/workflow/host.js +3 -0
  109. package/dist/shortcuts/workflow/index.js +1 -1
  110. package/dist/shortcuts/workflow/operations/form.js +44 -11
  111. package/dist/shortcuts/workflow/operations/todoSmartConvert/aigroupRules.js +77 -26
  112. package/dist/shortcuts/workflow/operations/todoSmartConvert/aigroupStore.js +6 -0
  113. package/dist/shortcuts/workflow/operations/todoSmartConvert/summaryCache.js +59 -117
  114. package/dist/shortcuts/workflow/operations/todoSmartConvert/todoDetailCache.js +44 -7
  115. package/dist/shortcuts/workflow/operations/todoSmartConvert/todoDetailFetcher.js +37 -29
  116. package/dist/shortcuts/workflow/operations/todoSmartConvert/todoSmartConvert.collect.js +20 -6
  117. package/dist/shortcuts/workflow/operations/todoSmartConvert/todoSmartConvert.detail.js +22 -18
  118. package/dist/shortcuts/workflow/operations/todoSmartConvert/todoSmartConvert.groupSummary.js +34 -10
  119. package/dist/shortcuts/workflow/operations/todoSmartConvert/todoSmartConvert.js +4 -2
  120. package/dist/shortcuts/workflow/operations/todoSmartConvert/todoSmartConvert.smartGroup.js +9 -2
  121. package/dist/shortcuts/workflow/operations/todoSmartConvert/todoSmartConvert.summary.js +110 -20
  122. package/docs/_catalog.md +3 -0
  123. package/docs/asset-idle.md +45 -0
  124. package/docs/e10-auth.md +1 -1
  125. package/docs/todo-smart-convert/subgroup-rules.md +1 -1
  126. package/package.json +1 -1
  127. package/scripts/package-skill.mjs +2 -0
  128. package/skill-template/business-info.json +14 -1
  129. package/skill-template/domains/asset-idle.md +13 -0
  130. package/skill-template/domains/wenshuding.md +3 -0
  131. package/skills/weaver-e10-archive-oa/SKILL.md +1 -0
  132. package/skills/weaver-e10-archive-oa/references/my-borrow.md +49 -0
  133. package/skills/weaver-e10-asset-idle/SKILL.md +73 -0
  134. package/skills/weaver-e10-asset-idle/references/idle-list.md +62 -0
  135. package/skills/weaver-e10-asset-idle/references/idle-stats-and-charts.md +65 -0
  136. package/skills/weaver-e10-asset-idle/references/source-manifest.json +193 -0
  137. package/skills/weaver-e10-asset-idle/references/widget-style.md +205 -0
  138. package/skills/weaver-e10-caizhilian/SKILL.md +29 -11
  139. package/skills/weaver-e10-caizhilian/references/auth-and-install.md +12 -0
  140. package/skills/weaver-e10-caizhilian/references/files.md +1 -1
  141. package/skills/weaver-e10-caizhilian/references/setting-encrypt.md +1 -1
  142. package/skills/weaver-e10-caizhilian/references/source-manifest.json +45 -45
  143. package/skills/weaver-e10-cowork/SKILL.md +2 -0
  144. package/skills/weaver-e10-jucailin/SKILL.md +2 -0
  145. package/skills/weaver-e10-jucailin/references/recruit-read.md +2 -0
  146. package/skills/weaver-e10-jucailin/references/recruit-write.md +1 -1
  147. package/skills/weaver-e10-mail/SKILL.md +3 -0
  148. package/skills/weaver-e10-meeting/SKILL.md +250 -301
  149. package/skills/weaver-e10-meeting/references/meeting-eb.md +203 -0
  150. package/skills/weaver-e10-meeting/references/meeting-invitation.md +37 -5
  151. package/skills/weaver-e10-meeting.zip +0 -0
  152. package/skills/weaver-e10-okr/references/source-manifest.json +3 -3
  153. package/skills/weaver-e10-shared/SKILL.md +5 -5
  154. package/skills/weaver-e10-shared/references/e10-auth-and-session.md +3 -3
  155. package/skills/weaver-e10-wenshuding/SKILL.md +29 -6
  156. package/skills/weaver-e10-wenshuding/references/archive-borrow-list.md +14 -0
  157. package/skills/weaver-e10-wenshuding/references/archive-download.md +2 -0
  158. package/skills/weaver-e10-wenshuding/references/archive-flow.md +1 -1
  159. package/skills/weaver-e10-wenshuding/references/archive-prelib-arrange.md +76 -0
  160. package/skills/weaver-e10-wenshuding/references/archive-prelib-list.md +107 -0
  161. package/skills/weaver-e10-wenshuding/references/archive-prelib-open.md +59 -0
  162. package/skills/weaver-e10-wenshuding/references/archive-upload.md +4 -1
  163. package/skills/weaver-e10-wenshuding/references/source-manifest.json +108 -65
  164. package/skills/weaver-e10-workflow/SKILL.md +5 -2
  165. package/skills/weaver-e10-workflow/aigroup/default.json +154 -19
  166. package/skills/weaver-e10-workflow/references/todo-smart-convert.md +59 -27
  167. package/skills/weaver-e10-workflow/tools/todo-serve.cjs +113 -25
  168. package/skills/weaver-e10-workflow/tools/todo-summary.cjs +292 -42
  169. package/skills/weaver-e10-yimiaoban/references/source-manifest.json +3 -3
@@ -13,6 +13,23 @@ const INTERNAL_MESSAGE_SUMMARY_HINT = '内部留言:只按原文直接概括
13
13
  const GROUP_SUMMARY_TAIL = GROUP_SUMMARY_TAIL_TEXT;
14
14
  /** 组级摘要建议长度上限。 */
15
15
  const DEFAULT_GROUP_SUMMARY_MAX = 100;
16
+ /**
17
+ * 组摘要目标字数解析器:**入参 > aigroup 规则(`groupSummaryMaxChars`)> 兜底 100**。
18
+ *
19
+ * 规则是主要来源(2026-10-30 用户要求「摘要规则必须从 JSON 取」),入参只在用户当轮明确给了
20
+ * 口径时覆盖它。按工作流名缓存,避免逐组重复解析规则。
21
+ */
22
+ function makeGroupMaxCharsResolver(args) {
23
+ const cache = new Map();
24
+ return (workflowName) => {
25
+ const hit = cache.get(workflowName);
26
+ if (hit !== undefined)
27
+ return hit;
28
+ const value = readBoundedInt(args.groupSummaryMaxChars ?? activeRuleFor(workflowName)?.groupSummaryMaxChars, 'groupSummaryMaxChars', DEFAULT_GROUP_SUMMARY_MAX, 20, 1000);
29
+ cache.set(workflowName, value);
30
+ return value;
31
+ };
32
+ }
16
33
  /** `¥123` / `¥1,280.50` / `123元` 三种写法;每条只取第一个命中,避免把「14 小时」当金额。 */
17
34
  const GROUP_AMOUNT_PATTERNS = [
18
35
  /[¥¥]\s*(\d[\d,]*(?:\.\d{1,2})?)/u,
@@ -45,7 +62,7 @@ export function createrPhraseOf(creators) {
45
62
  * `smartGroup` 已为确定性路径的二次分组汇总项写入固定 `summary`;AI 路径和内部留言仍可进入
46
63
  * `groupSummaryBatches`,由后续阶段补写组摘要。
47
64
  */
48
- export function collectGroupSummaryBatches(groups, isCtxEmpty, maxChars = DEFAULT_GROUP_SUMMARY_MAX) {
65
+ export function collectGroupSummaryBatches(groups, isCtxEmpty, maxCharsFor = () => DEFAULT_GROUP_SUMMARY_MAX) {
49
66
  const batches = [];
50
67
  for (const group of groups) {
51
68
  if (group.topPriority)
@@ -82,6 +99,8 @@ export function collectGroupSummaryBatches(groups, isCtxEmpty, maxChars = DEFAUL
82
99
  amounts.push(amount);
83
100
  }
84
101
  const amountReliable = items.length >= 2 && amounts.length === items.length;
102
+ /** 组摘要口径(规则 `groupSummary.prompt`):`needAI:true` 的组必有,没配则不下发 */
103
+ const groupPrompt = (activeRuleFor(group.title)?.aiGroup?.groupSummary?.prompt ?? '').trim();
85
104
  batches.push({
86
105
  groupId: sub.id,
87
106
  parentGroupId: group.id,
@@ -91,7 +110,8 @@ export function collectGroupSummaryBatches(groups, isCtxEmpty, maxChars = DEFAUL
91
110
  totalCount: allRows.length,
92
111
  kind,
93
112
  ...(kind === 'internal-message' ? { hint: INTERNAL_MESSAGE_SUMMARY_HINT } : {}),
94
- suggestedMaxChars: maxChars,
113
+ ...(groupPrompt ? { prompt: groupPrompt } : {}),
114
+ suggestedMaxChars: maxCharsFor(group.title),
95
115
  ...(emptySkipped > 0 ? { emptySkipped } : {}),
96
116
  items,
97
117
  stats: {
@@ -174,8 +194,9 @@ export async function runGroupSummaryContext(args, env) {
174
194
  const groups = await readFinalGroups(args, env);
175
195
  const { isCtxEmpty, warnings: emptyWarnings } = await loadCtxEmptyPredicate(env, groups, args);
176
196
  // 组摘要目标字数:用户给了就用用户的,没给才用默认 100。
177
- const groupSummaryMaxChars = readBoundedInt(args.groupSummaryMaxChars, 'groupSummaryMaxChars', DEFAULT_GROUP_SUMMARY_MAX, 20, 1000);
178
- const batches = collectGroupSummaryBatches(groups, isCtxEmpty, groupSummaryMaxChars);
197
+ const groupMaxCharsFor = makeGroupMaxCharsResolver(args);
198
+ const batches = collectGroupSummaryBatches(groups, isCtxEmpty, groupMaxCharsFor);
199
+ const distinctChars = [...new Set(batches.map((batch) => batch.suggestedMaxChars))].sort((left, right) => left - right);
179
200
  const totalCount = totalTodoCount(groups);
180
201
  const warnings = [...emptyWarnings];
181
202
  const skippedEmpty = batches.reduce((sum, batch) => sum + (batch.emptySkipped ?? 0), 0);
@@ -208,7 +229,7 @@ export async function runGroupSummaryContext(args, env) {
208
229
  /** CLI 固定文案的汇总项个数(不进批次,`smartGroup` 阶段已写好) */
209
230
  deterministicGroups,
210
231
  tail: GROUP_SUMMARY_TAIL,
211
- suggestedMaxChars: groupSummaryMaxChars,
232
+ ...(distinctChars.length === 1 ? { suggestedMaxChars: distinctChars[0] } : { suggestedMaxCharsList: distinctChars }),
212
233
  },
213
234
  },
214
235
  warnings,
@@ -259,8 +280,9 @@ export async function runGroupSummary(args, env) {
259
280
  const summaries = readAiGroupSummaries(args.aiGroupSummaries);
260
281
  const { isCtxEmpty, warnings: emptyWarnings } = await loadCtxEmptyPredicate(env, groups, args);
261
282
  // 组摘要目标字数:与 `runGroupSummaryContext` 保持同一口径(用户给了就用用户的)。
262
- const groupSummaryMaxChars = readBoundedInt(args.groupSummaryMaxChars, 'groupSummaryMaxChars', DEFAULT_GROUP_SUMMARY_MAX, 20, 1000);
263
- const batches = collectGroupSummaryBatches(groups, isCtxEmpty, groupSummaryMaxChars);
283
+ const groupMaxCharsFor = makeGroupMaxCharsResolver(args);
284
+ const batches = collectGroupSummaryBatches(groups, isCtxEmpty, groupMaxCharsFor);
285
+ const distinctChars = [...new Set(batches.map((batch) => batch.suggestedMaxChars))].sort((left, right) => left - right);
264
286
  const knownGroupIds = new Set(batches.map((batch) => batch.groupId));
265
287
  for (const groupId of summaries.keys()) {
266
288
  if (!knownGroupIds.has(groupId)) {
@@ -282,6 +304,8 @@ export async function runGroupSummary(args, env) {
282
304
  if (group.topPriority)
283
305
  continue;
284
306
  const kind = detectBusinessKind(group.title);
307
+ /** 本组的组摘要字数口径(入参 > 规则 > 兜底) */
308
+ const groupMaxChars = groupMaxCharsFor(group.title);
285
309
  for (const sub of groupItemsOf(group)) {
286
310
  if (isDeterministicGroupKind(kind)) {
287
311
  // ★ 固定文案:`smartGroup` 阶段已写好,这里**不覆盖**。
@@ -294,7 +318,7 @@ export async function runGroupSummary(args, env) {
294
318
  }
295
319
  filled += 1;
296
320
  summarizedGroupCount += 1;
297
- if (summary.length > groupSummaryMaxChars)
321
+ if (summary.length > groupMaxChars)
298
322
  tooLong += 1;
299
323
  const batch = batches.find((item) => item.groupId === sub.id);
300
324
  if (batch && batch.stats.createrPhrase && !summary.includes(batch.stats.createrPhrase)) {
@@ -314,7 +338,7 @@ export async function runGroupSummary(args, env) {
314
338
  }
315
339
  filled += 1;
316
340
  summarizedGroupCount += 1;
317
- if (summary.length > groupSummaryMaxChars)
341
+ if (summary.length > groupMaxChars)
318
342
  tooLong += 1;
319
343
  const batch = batches.find((item) => item.groupId === sub.id);
320
344
  if (batch && batch.stats.createrPhrase && !summary.includes(batch.stats.createrPhrase)) {
@@ -325,7 +349,7 @@ export async function runGroupSummary(args, env) {
325
349
  if (empty > 0)
326
350
  warnings.push(`有 ${empty} 个汇总项没有拿到组摘要,summary 保持空串(未编造)`);
327
351
  if (tooLong > 0) {
328
- warnings.push(`有 ${tooLong} 个组摘要超过建议的 ${groupSummaryMaxChars} 字,已原样保留(CLI 不改写文案)`);
352
+ warnings.push(`有 ${tooLong} 个组摘要超过建议的 ${distinctChars.length > 0 ? distinctChars.join('/') : DEFAULT_GROUP_SUMMARY_MAX} 字,已原样保留(CLI 不改写文案)`);
329
353
  }
330
354
  if (missingCreater > 0) {
331
355
  warnings.push(`有 ${missingCreater} 个组摘要没有包含算好的「由 … 发起」短语(发起人由 CLI 统计,模型不该自己数),`
@@ -88,8 +88,10 @@ const inputSchema = schemaOf({
88
88
  groupFocus: { type: 'string' },
89
89
  maxCharsPerBatch: { type: 'integer', minimum: 500, maximum: 200000, default: 24000 },
90
90
  maxItemsPerBatch: { type: 'integer', minimum: 1, maximum: 500, default: 100 },
91
- summaryMaxChars: { type: 'integer', minimum: 10, maximum: 500, default: 50 },
92
- groupSummaryMaxChars: { type: 'integer', minimum: 20, maximum: 1000, default: 100 },
91
+ // 字数口径不设 schema 默认:默认值来自 aigroup 规则(default.json 的 defaults),
92
+ // 入参只在用户当轮明确给口径时覆盖规则(2026-10-30 用户要求「摘要规则必须从 JSON 取」)。
93
+ summaryMaxChars: { type: 'integer', minimum: 10, maximum: 500 },
94
+ groupSummaryMaxChars: { type: 'integer', minimum: 20, maximum: 1000 },
93
95
  detailConcurrency: { type: 'integer', minimum: 1, maximum: 32, default: 10 },
94
96
  detailTimeoutMs: { type: 'integer', minimum: 1000, maximum: 60000, default: 10000 },
95
97
  skillTiming: { type: 'object' },
@@ -4,7 +4,8 @@ import { asRecord, readBoundedInt, } from './todoSmartConvert.core.js';
4
4
  import { PRIORITY_GROUP_ID, PRIORITY_GROUP_TITLE, runGroup, GROUP_MIN_ITEMS_FOR_SUBGROUP, } from './todoSmartConvert.group.js';
5
5
  import { fetchDetailFacts, stampsOfRows } from './todoSmartConvert.detail.js';
6
6
  import { activeRuleFor, evaluateFocusKeywords, primeRules } from './aigroupRules.js';
7
- import { openSummaryCache } from './summaryCache.js';
7
+ import { openSummaryCache, promptTagOf } from './summaryCache.js';
8
+ import { openDetailCache, detailMaterialTagOf } from './todoDetailCache.js';
8
9
  const DEFAULT_REQUEST_SUMMARY_MAX = 50;
9
10
  /** 把一批条目包成「单独展示」项(优先组、以及普通组里没同类可合并的条目都用它)。 */
10
11
  export function requestItemsOf(rows) {
@@ -176,12 +177,18 @@ export async function runSmartGroup(args, env, options = {}) {
176
177
  // withDetailContext=false 时只生成结构、不获取详情(此时没有指标,阈值规则自然不生效)。
177
178
  const skipDetail = args.withDetailContext === false;
178
179
  const summaryCache = await openSummaryCache(env.ctx);
180
+ // 摘要条目的素材特征值:详情素材变了 ⇒ 旧摘要视为不存在(该条重新写摘要)
181
+ const detailCacheForTag = skipDetail ? null : await openDetailCache(env.ctx);
182
+ const materialTagOfRow = (requestId, stamp) => {
183
+ const entry = detailCacheForTag?.get(requestId, stamp);
184
+ return entry ? detailMaterialTagOf(entry) : undefined;
185
+ };
179
186
  const cachedIds = new Set();
180
187
  const allRows = [];
181
188
  for (const group of groups) {
182
189
  for (const row of group.requestDatas ?? []) {
183
190
  allRows.push(row);
184
- if (summaryCache.get(row.requestId, row.lastFeedBackDateTime, summaryMaxChars))
191
+ if (summaryCache.get(row.requestId, row.lastFeedBackDateTime, summaryMaxChars, promptTagOf(activeRuleFor(group.title)?.requestSummaryPrompt ?? ''), materialTagOfRow(row.requestId, row.lastFeedBackDateTime)))
185
192
  cachedIds.add(row.requestId);
186
193
  }
187
194
  }
@@ -3,13 +3,31 @@ import { workflowValidationError } from '../../errors.js';
3
3
  import { asRecord, readBoundedInt } from './todoSmartConvert.core.js';
4
4
  import { orderGroupsByIntent, readGroupOrder } from './todoSmartConvert.group.js';
5
5
  import { detectBusinessKind, needsRequestSummary } from './businessRules.js';
6
- import { openSummaryCache } from './summaryCache.js';
6
+ import { openSummaryCache, promptTagOf } from './summaryCache.js';
7
+ import { openDetailCache, detailMaterialTagOf } from './todoDetailCache.js';
7
8
  import { loadDetailContextProvider, stampsOfRows } from './todoSmartConvert.detail.js';
8
9
  import { requestItemsOf, rowsOfGroup, runSmartGroup, totalTodoCount, } from './todoSmartConvert.smartGroup.js';
9
10
  /** 该组是否需要 AI 生成单条摘要(规则 needSummary 决定,默认需要)。 */
10
11
  function needAiSummary(workflowName) {
11
12
  return activeRuleFor(workflowName)?.needSummary ?? true;
12
13
  }
14
+ /**
15
+ * 单条摘要的目标字数:**入参 > aigroup 规则(default.json / user-intents.json)> 内置兜底**。
16
+ *
17
+ * 规则是主要来源(2026-10-30 用户要求「摘要规则必须从 JSON 取」),入参只在用户当轮明确给了
18
+ * 口径(如「每条 30 字」)时覆盖它;两者都没有才用兜底 50。
19
+ */
20
+ function requestMaxCharsFor(args, workflowName) {
21
+ return readBoundedInt(args.summaryMaxChars ?? activeRuleFor(workflowName)?.summaryMaxChars, 'summaryMaxChars', DEFAULT_REQUEST_SUMMARY_MAX, 10, 500);
22
+ }
23
+ /** 该组的单条摘要口径提示(规则 `requestSummaryPrompt`,空串表示没有)。 */
24
+ function requestPromptFor(workflowName) {
25
+ return (activeRuleFor(workflowName)?.requestSummaryPrompt ?? '').trim();
26
+ }
27
+ /** 批次里出现过的目标字数(去重、升序);用于在 meta 里表达「各组口径可能不同」。 */
28
+ function distinctBatchMaxChars(batches) {
29
+ return [...new Set(batches.map((batch) => batch.suggestedMaxChars))].sort((left, right) => left - right);
30
+ }
13
31
  const DEFAULT_MAX_CHARS_PER_BATCH = 24_000;
14
32
  const DEFAULT_MAX_ITEMS_PER_BATCH = 100;
15
33
  const DEFAULT_REQUEST_SUMMARY_MAX = 50;
@@ -194,18 +212,42 @@ export async function runSummaryContext(args, env, prefetched) {
194
212
  .reduce((sum, group) => sum + rowsOfGroup(group).length, 0);
195
213
  // 优先组是叠加视图 ⇒ 同一条待办会在两个位置各出现一次,统计口径必须去重。
196
214
  const requestIds = [...new Set(units.flatMap((unit) => unit.rows.map((row) => row.requestId)))].filter(Boolean);
197
- // 单条摘要目标字数:用户给了口径(如「每条 30 字」)就用用户的,没给才用默认 50。
198
- const summaryMaxChars = readBoundedInt(args.summaryMaxChars, 'summaryMaxChars', DEFAULT_REQUEST_SUMMARY_MAX, 10, 500);
199
215
  // 摘要缓存命中时直接回填,未命中的条目才进入 AI 批次。
200
216
  // 预填必须在「准备详情」之前做——已有摘要的条目根本不需要详情(详情只为写摘要服务)。
201
217
  const summaryCache = await openSummaryCache(env.ctx);
218
+ // 摘要条目的素材特征值(dv):读详情缓存(本地文件,不发请求)算当前素材哈希,
219
+ // 与条目写入时的素材比对——素材变了 ⇒ 旧摘要失效重写(2026-11-01 根治)。
220
+ const detailCacheForTag = await openDetailCache(env.ctx);
221
+ const materialTagOfRow = (requestId, stamp) => {
222
+ const entry = detailCacheForTag.get(requestId, stamp);
223
+ return entry ? detailMaterialTagOf(entry) : undefined;
224
+ };
202
225
  /** 已有摘要(缓存命中 / collect 已回填)而跳过 AI 的条数 */
203
226
  let prefilled = 0;
204
227
  const prefilledIds = new Set();
205
228
  const needsDetail = [];
229
+ /**
230
+ * 优先组里的条目在**工作流组**里还有一份副本——它们的规则口径必须按**原工作流**解析,
231
+ * 而不是按「建议优先处理的流程」这个组名(它匹配不到规则 ⇒ pk/字数都取错)。
232
+ * 先建 requestId → 工作流组名 的映射,优先组的行查这张表。
233
+ */
234
+ const workflowTitleByRequest = new Map();
235
+ for (const unit of units) {
236
+ if (unit.groupId === 'priority')
237
+ continue;
238
+ for (const row of unit.rows) {
239
+ if (row.requestId && !workflowTitleByRequest.has(row.requestId)) {
240
+ workflowTitleByRequest.set(row.requestId, unit.groupTitle);
241
+ }
242
+ }
243
+ }
244
+ const ruleNameForRow = (unit, row) => unit.groupId === 'priority'
245
+ ? (workflowTitleByRequest.get(row.requestId ?? '') ?? unit.groupTitle)
246
+ : unit.groupTitle;
206
247
  for (const unit of units) {
207
248
  for (const row of unit.rows) {
208
- const cached = summaryCache.get(row.requestId, row.lastFeedBackDateTime, summaryMaxChars);
249
+ const name = ruleNameForRow(unit, row);
250
+ const cached = summaryCache.get(row.requestId, row.lastFeedBackDateTime, requestMaxCharsFor(args, name), promptTagOf(requestPromptFor(name)), materialTagOfRow(row.requestId, row.lastFeedBackDateTime));
209
251
  if (cached) {
210
252
  row.summary = cached;
211
253
  prefilled += 1;
@@ -232,7 +274,10 @@ export async function runSummaryContext(args, env, prefetched) {
232
274
  * 所以这里跳过不会让任何一份摘要空缺,只是省掉一次重复的上下文。
233
275
  */
234
276
  const claimed = new Set();
235
- for (const unit of units) {
277
+ // 优先组**最后**处理:它的条目在工作流批次里已经带着各自规则的口径(requestSummaryPrompt)
278
+ // 被写过一遍;若优先组先处理,口径会丢失(优先组标题匹配不到规则)。剩下的未认领条目才进优先批次。
279
+ const orderedUnits = [...units].sort((a, b) => Number(a.groupId === 'priority') - Number(b.groupId === 'priority'));
280
+ for (const unit of orderedUnits) {
236
281
  let bucket = [];
237
282
  let chars = 0;
238
283
  let seq = 0;
@@ -240,6 +285,9 @@ export async function runSummaryContext(args, env, prefetched) {
240
285
  if (bucket.length === 0)
241
286
  return;
242
287
  seq += 1;
288
+ // 批次口径按本批**第一条**所属工作流的规则取(正常批次不跨工作流;
289
+ // 优先组批次兜底时可能混多条工作流,此时以首条为准,逐条口径随 items 上下文仍可辨)
290
+ const firstName = ruleNameForRow(unit, { requestId: bucket[0].requestId });
243
291
  batches.push({
244
292
  batchId: `${unit.subGroupId}#${seq}`,
245
293
  groupId: unit.groupId,
@@ -250,7 +298,8 @@ export async function runSummaryContext(args, env, prefetched) {
250
298
  charCount: chars,
251
299
  kind: unit.kind,
252
300
  ...(unit.kind === 'internal-message' ? { hint: INTERNAL_MESSAGE_SUMMARY_HINT } : {}),
253
- suggestedMaxChars: summaryMaxChars,
301
+ ...(requestPromptFor(firstName) ? { prompt: requestPromptFor(firstName) } : {}),
302
+ suggestedMaxChars: requestMaxCharsFor(args, firstName),
254
303
  items: bucket,
255
304
  });
256
305
  bucket = [];
@@ -329,8 +378,10 @@ export async function runSummaryContext(args, env, prefetched) {
329
378
  titleCopied,
330
379
  /** 本地摘要命中、跳过 AI 的条数 */
331
380
  prefilled,
332
- /** 单条摘要的目标字数(默认 50,可由 summaryMaxChars 覆盖) */
333
- suggestedMaxChars: summaryMaxChars,
381
+ /** 单条摘要的目标字数:口径一致时给数字,各组口径不同时给 distinct 列表 */
382
+ ...(distinctBatchMaxChars(batches).length === 1
383
+ ? { suggestedMaxChars: distinctBatchMaxChars(batches)[0] }
384
+ : { suggestedMaxCharsList: distinctBatchMaxChars(batches) }),
334
385
  /** 内部留言类批次:摘要要按「最低推理强度」写 */
335
386
  internalMessageBatches: batches.filter((batch) => batch.kind === 'internal-message').length,
336
387
  skippedSatisfaction,
@@ -388,16 +439,42 @@ export function readAiSummaries(value) {
388
439
  export async function runSummary(args, env) {
389
440
  const groups = await readFinalGroups(args, env);
390
441
  const summaries = readAiSummaries(args.aiSummaries);
391
- // 单条摘要目标字数:与 `runSummaryContext` 保持同一口径(用户给了就用用户的)。
392
- const summaryMaxChars = readBoundedInt(args.summaryMaxChars, 'summaryMaxChars', DEFAULT_REQUEST_SUMMARY_MAX, 10, 500);
442
+ // 单条摘要目标字数:与 `runSummaryContext` 保持同一口径(入参 > 规则 > 兜底)。
443
+ /** 按工作流名缓存本组的字数口径,避免逐行解析规则。 */
444
+ const maxCharsByGroup = new Map();
445
+ const maxCharsFor = (workflowName) => {
446
+ const hit = maxCharsByGroup.get(workflowName);
447
+ if (hit !== undefined)
448
+ return hit;
449
+ const value = requestMaxCharsFor(args, workflowName);
450
+ maxCharsByGroup.set(workflowName, value);
451
+ return value;
452
+ };
393
453
  // ★ 本次 AI 写出来的摘要顺手进缓存(key=requestId__lastFeedBackDateTime),
394
454
  // 下次同一条流程的待办再来时可以复用摘要。
395
455
  const summaryCache = await openSummaryCache(env.ctx);
456
+ // 素材特征值(与 summaryContext 同口径):写入时锚定当前详情素材,读取时比对失效
457
+ const detailCacheForTag = await openDetailCache(env.ctx);
458
+ const materialTagOfRow = (requestId, stamp) => {
459
+ const entry = detailCacheForTag.get(requestId, stamp);
460
+ return entry ? detailMaterialTagOf(entry) : undefined;
461
+ };
396
462
  let cacheWritten = 0;
397
463
  // 缓存预填(与 `summaryContext` 同口径):只跑 `summary` 阶段时也能拿到已写好的摘要,
398
464
  // 否则重跑 finish(不带 aiSummaries)会把整份摘要清成空串。
399
465
  // 满意度类不走 AI、也不读缓存:直接用标题当摘要,保证 `summary` 不为空。
400
466
  let titleSummary = 0;
467
+ // 优先组的行按**原工作流**解析口径(组名「建议优先处理的流程」匹配不到规则)。
468
+ const wfTitleByRequest = new Map();
469
+ for (const group of groups) {
470
+ if (group.topPriority)
471
+ continue;
472
+ for (const row of rowsOfGroup(group)) {
473
+ if (row?.requestId && !wfTitleByRequest.has(row.requestId))
474
+ wfTitleByRequest.set(row.requestId, group.title);
475
+ }
476
+ }
477
+ const ruleNameForGroup = (group, row) => group.topPriority ? (wfTitleByRequest.get(row?.requestId ?? '') ?? group.title) : group.title;
401
478
  for (const group of groups) {
402
479
  if (!needAiSummary(group.title)) {
403
480
  for (const row of rowsOfGroup(group)) {
@@ -412,7 +489,8 @@ export async function runSummary(args, env) {
412
489
  for (const row of rowsOfGroup(group)) {
413
490
  if (!row?.requestId || (row.summary ?? '').trim())
414
491
  continue;
415
- const cached = summaryCache.get(row.requestId, row.lastFeedBackDateTime, summaryMaxChars);
492
+ const name = ruleNameForGroup(group, row);
493
+ const cached = summaryCache.get(row.requestId, row.lastFeedBackDateTime, maxCharsFor(name), promptTagOf(requestPromptFor(name)), materialTagOfRow(row.requestId, row.lastFeedBackDateTime));
416
494
  if (cached) {
417
495
  row.summary = cached;
418
496
  }
@@ -440,7 +518,7 @@ export async function runSummary(args, env) {
440
518
  let prefilledKept = 0;
441
519
  /** 统计用:同一条待办在叠加视图里出现两次,计数只算一次 */
442
520
  const countedIds = new Set();
443
- const applyAll = (rows) => {
521
+ const applyAll = (rows, meta) => {
444
522
  for (const row of rows) {
445
523
  const summary = summaries.get(row.requestId) ?? '';
446
524
  const firstTime = !row.requestId || !countedIds.has(row.requestId);
@@ -461,10 +539,10 @@ export async function runSummary(args, env) {
461
539
  if (summary) {
462
540
  if (firstTime) {
463
541
  filled += 1;
464
- if (summary.length > summaryMaxChars)
542
+ if (summary.length > meta(row).maxChars)
465
543
  tooLong += 1;
466
544
  }
467
- if (summaryCache.set(row.requestId, row.lastFeedBackDateTime, summary, summaryMaxChars))
545
+ if (summaryCache.set(row.requestId, row.lastFeedBackDateTime, summary, meta(row).maxChars, meta(row).pk, materialTagOfRow(row.requestId, row.lastFeedBackDateTime)))
468
546
  cacheWritten += 1;
469
547
  }
470
548
  else if (firstTime) {
@@ -478,18 +556,30 @@ export async function runSummary(args, env) {
478
556
  if (!needAiSummary(group.title))
479
557
  continue;
480
558
  if (group.topPriority) {
481
- applyAll(rowsOfGroup(group));
559
+ applyAll(rowsOfGroup(group), (row) => {
560
+ const name = ruleNameForGroup(group, row);
561
+ return { maxChars: maxCharsFor(name), pk: promptTagOf(requestPromptFor(name)) };
562
+ });
482
563
  continue;
483
564
  }
484
565
  for (const item of group.displayItems) {
485
566
  if (item.type === 'group') {
486
- applyAll(item.requestDatas);
567
+ applyAll(item.requestDatas, (row) => {
568
+ const name = ruleNameForGroup(group, row);
569
+ return { maxChars: maxCharsFor(name), pk: promptTagOf(requestPromptFor(name)) };
570
+ });
487
571
  continue;
488
572
  }
489
573
  // 单独展示的一条:只回填它自己
490
- applyAll([item.requestData]);
574
+ applyAll([item.requestData], (row) => {
575
+ const name = ruleNameForGroup(group, row);
576
+ return { maxChars: maxCharsFor(name), pk: promptTagOf(requestPromptFor(name)) };
577
+ });
491
578
  }
492
579
  }
580
+ /** 告警与 meta 里展示的口径:各组一致就给数字,不一致就给 `a/b` 形式。 */
581
+ const usedMaxChars = [...new Set(maxCharsByGroup.values())].sort((left, right) => left - right);
582
+ const maxCharsHint = usedMaxChars.length > 0 ? usedMaxChars.join('/') : String(DEFAULT_REQUEST_SUMMARY_MAX);
493
583
  if (empty > 0) {
494
584
  warnings.push(`有 ${empty} 条没有拿到 AI 摘要,summary 保持空串(未用标题兜底、未编造)`);
495
585
  }
@@ -497,7 +587,7 @@ export async function runSummary(args, env) {
497
587
  warnings.push(`有 ${titleKept} 条详情为空的条目保留 CLI 填充的标题摘要(不参与 AI 回填)`);
498
588
  }
499
589
  if (tooLong > 0) {
500
- warnings.push(`有 ${tooLong} 条摘要超过目标长度 ${summaryMaxChars} 字(可按用户口径调整),已原样保留;`
590
+ warnings.push(`有 ${tooLong} 条摘要超过目标长度 ${maxCharsHint} 字(可按用户口径调整),已原样保留;`
501
591
  + '如需控制长度请重跑模型侧(CLI 不改写文案)');
502
592
  }
503
593
  if (prefilledKept > 0) {
@@ -527,8 +617,8 @@ export async function runSummary(args, env) {
527
617
  meta: {
528
618
  summarized: filled,
529
619
  empty,
530
- /** 单条摘要的目标字数(默认 50,可由 summaryMaxChars 覆盖) */
531
- suggestedMaxChars: summaryMaxChars,
620
+ /** 单条摘要的目标字数:各组口径一致时给数字,不一致时给 distinct 列表 */
621
+ ...(usedMaxChars.length === 1 ? { suggestedMaxChars: usedMaxChars[0] } : { suggestedMaxCharsList: usedMaxChars }),
532
622
  titleCopied: titleKept,
533
623
  /** 本地摘要缓存命中、未重新生成的条数 */
534
624
  prefilled: prefilledKept,
package/docs/_catalog.md CHANGED
@@ -50,3 +50,6 @@
50
50
  | Agent 调用采知连文档中心 CLI 的入口路由 | `skills/weaver-e10-caizhilian/SKILL.md` |
51
51
  | Agent 调用采知连文档中心 CLI 的具体 reference | `skills/weaver-e10-caizhilian/references/` |
52
52
  | 将新业务模块文档或代码同步转换为 weaver-work-cli 业务 CLI 与 Skill、检测源资料更新 | `skills/weaver-e10-skill-maker/SKILL.md` |
53
+ | E10 闲置资产判定结果查询(分页获取/聚合统计/三张图口径)、asset-idle operation | `docs/asset-idle.md` |
54
+ | Agent 调用闲置资产统计 CLI 的入口路由 | `skills/weaver-e10-asset-idle/SKILL.md` |
55
+ | Agent 调用闲置资产统计 CLI 的具体 reference | `skills/weaver-e10-asset-idle/references/` |
@@ -0,0 +1,45 @@
1
+ # asset-idle — E10 闲置资产判定结果查询 CLI
2
+
3
+ ## 模块来源
4
+
5
+ 源资料为用户级技能 `weaver-e10-ziguanjia-idle`(SKILL.md + references/api.md + references/widget-style.md,2026-10-27 起实测固化的闲置资产统计口径)。按 `weaver-e10-skill-maker` 流程同步生成 CLI 模块与 Skill `weaver-e10-asset-idle`(CLI 命令后缀与 Skill 后缀均为 `asset-idle`,2026-11-02 用户确认)。
6
+
7
+ - 源接口:`POST {baseurl}/api/esb/server/event/triggerActionFlow`(动作流「自用-skill获取闲置资产」)
8
+ - 源口径:闲置判定由服务端动作流完成,CLI 只取数与聚合,不重算
9
+
10
+ ## 命令结构
11
+
12
+ ```text
13
+ weaver-work-cli asset-idle schema
14
+ weaver-work-cli asset-idle run <operation> --input-json '<json>'
15
+ ```
16
+
17
+ ## operation 表
18
+
19
+ | operation | 风险 | 说明 |
20
+ | --- | --- | --- |
21
+ | `asset-idle.idle.list` | read | 触发动作流,按 `hasMore` 自动翻页(每页 1000 条,服务端固定),合并各页 `idleAssets` 为结果集 |
22
+ | `asset-idle.idle.stats` | read | 在合并结果上聚合:`czjyCounts`(0=待调拨/1=待调剂/2=待评估/缺失=无法判断)、`typeCounts`(zclx 降序)、`agingBuckets`(≤30/31–90/91–180/181–365/>365 天,边界归前一档,0 档不返回) |
23
+
24
+ ## 何时用哪个 operation
25
+
26
+ - 只要统计出图 → `idle.stats`(一次拿到全部分组,不要先 list 再自己数)
27
+ - 要明细、逐条判定依据 → `idle.list`
28
+ - 两个 operation 输入一致:`esbFlowId`(必填,1-32 位数字字符串)、`maxPages`(默认 20,1-100)
29
+
30
+ ## 输入与字段口径
31
+
32
+ - `esbFlowId` 必须由调用方传入;按名称解析动作流 ID 需要的 ESB 应用列表查询接口在源资料中只有旧版 e10-cli 命令名,未实现(schema `withheldOperations: asset-idle.flow.resolve`)
33
+ - 判定字段直接使用:`xzsz`(闲置时长/天)、`pdyj`(判定依据)、`zxd`(置信度)、`czjy`(处置建议编码);缺失标「无法判断」并排除出统计
34
+ - 恒为空展示字段(`czjy_obj`/`xzdj_obj`/`yjzczt_obj`/`czlc`/`czlc_obj`/`zcxzczrq`)不透出;禁止反推 `idle_date`/`stockindate`/`use_term`(`disabledCapabilities`)
35
+
36
+ ## 已知限制
37
+
38
+ - 会话失效判据为接口返回 `{"error":"authentication required","code":-2}`(HTTP 200),与标准 401 并行识别
39
+ - `truncated=true`(达到 maxPages 且 hasMore 仍 true)时结果不是全集,调用方应增大 `maxPages` 重查
40
+ - 动作流数据规模随系统台账变化(2026-10-30 起为判定结果集,384 条;此前为原始台账 2885 条)
41
+
42
+ ## 安全边界
43
+
44
+ - 只读模块,无写链;认证请求头由 `weaver-work-cli` 统一托管,模块代码不读取、不拼装任何凭据
45
+ - 两个 operation 均为 `risk: read`,Agent 可直接执行;失败处置见 `skills/weaver-e10-asset-idle/references/`
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 登录态,首次使用仍需显式执行 `auth xiaoe`。已有 XiaoE 会话被 E10 接口明确拒绝为失效时,公共请求运行时会根据持久化会话的 `agentType` / `loginSource` 静默换票并重放原请求一次;这条路径不依赖 shared Skill、Agent、`XIAOE_*` 或 `--runtime-agent xiaoe`。它不会打印中间登录步骤,不会自动打开浏览器,也不会对流式请求、网络错误、`partial` / `write_uncertain` 做重放。`--runtime-agent xiaoe` 仅用于其他 XiaoE 专属业务行为,不携带也不选择凭据。WorkBuddy、Codex 及其他智能体首次认证使用 `auth login --base-url <url> --agent_type <agent_type>` 的浏览器 OAuth 流程。
72
+ 业务命令缺少可读取的本地会话时,若当前运行时已通过 `XIAOE_*`、`--runtime-agent xiaoe` 等标记识别为 XiaoE,公共会话加载层会先静默尝试统一登录,成功后继续业务操作。失败时返回 `authentication/xiaoe_credential_unavailable`,并明确提示已尝试自动获取。显式 `--auth` 不触发此恢复;未识别到 XiaoE 的首次认证仍需显式选择登录方式。`auth status --no-check` 等本地状态查询不会主动登录。已有 XiaoE 会话被 E10 接口明确拒绝为失效时,公共请求运行时会根据持久化会话的 `agentType` / `loginSource` 静默换票并重放原请求一次;这条路径不依赖 shared Skill、Agent、`XIAOE_*` 或 `--runtime-agent xiaoe`。它不会打印中间登录步骤,不会自动打开浏览器,也不会对流式请求、网络错误、`partial` / `write_uncertain` 做重放。`--runtime-agent xiaoe` 表示当前运行时身份,不携带凭据。WorkBuddy、Codex 及其他智能体首次认证使用 `auth login --base-url <url> --agent_type <agent_type>` 的浏览器 OAuth 流程。
73
73
 
74
74
  无法使用浏览器回调时,可以用 `auth set` 直接写入浏览器中复制到的
75
75
  `ETEAMSID`。命令会调用 `teamsCheck` 解析当前用户、租户和组织信息,并写入
@@ -6,7 +6,7 @@
6
6
 
7
7
  > **⚠️ 2026-10-28 起:分组规则已外置到 JSON,代码不再按业务类型分支。**
8
8
  > 唯一真源是 `skills/weaver-e10-workflow/aigroup/default.json`(`{defaults, rules[]}`,随 skill 分发、只读),用户意图另存 **CLI 状态目录**下的 `aigroup/user-intents.json`(`weaver-work-cli aigroup set` 写入,刻意不落 skill 目录,避免被打包带走)。
9
- > 现行规则要点:①没有 `aiGroup` ⇒ 全平铺;②`focus:true` ⇒ 按 `focusKeyword`(`[指标, 算子, 值]`,十种算子)确定性成一组,`groupTitle` 为组名,`sysArg` 指明指标取自列表属性还是流程详情;③`focus:false` ⇒ `aiContext`(流程标题/流程详情)交 AI 分组,**必须**给 `aiGroups`;④`focus:true` 收到多个计划子组时**只保留条数最多的一个**,其余平铺;⑤`aiGroups.seq` 已删除,只认 `requestIds`;⑥组摘要 `groupSummary.needAI:false` 时由 CLI 按 JSON `template` 渲染,`true` 时交 AI;⑦单条摘要由 `needSummary` 决定(false ⇒ 用标题)。
9
+ > 现行规则要点:①没有 `aiGroup` ⇒ 全平铺;②`focus:true` ⇒ 按 `focusKeyword`(`[指标, 算子, 值]`,十种算子)确定性成一组,`groupTitle` 为组名,`sysArg` 指明指标取自列表属性还是流程详情;③`focus:false` ⇒ `aiContext`(流程标题/流程详情)交 AI 分组,**必须**给 `aiGroups`;④`focus:true` 收到多个计划子组时**只保留条数最多的一个**,其余平铺;⑤`aiGroups.seq` 已删除,只认 `requestIds`;⑥组摘要 `groupSummary.needAI:false` 时由 CLI 按 JSON `template` 渲染,`true` 时交 AI;⑦单条摘要由 `needSummary` 决定(false ⇒ 用标题);⑧详情素材要排掉哪些 formData 字段由 **`excludeOriginFormDataFields`**(字段名清单,fieldName 精确匹配,2026-10-30 定稿)维护,过滤后 ≤200 个字段、单值 ≤500 字。
10
10
  > 下面各节是按类型描述的**历史口径**,仅作背景参考;与本节冲突时以本节与 `default.json` 为准。
11
11
 
12
12
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "weaver-work-cli",
3
- "version": "0.1.36",
3
+ "version": "0.1.39",
4
4
  "description": "面向泛微工作工具链的可扩展 TypeScript CLI 基础框架(E10 业票通等能力)",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -77,6 +77,8 @@ function shouldSkip(entry, relativePath) {
77
77
  if (entry.name === '.DS_Store' || entry.name.startsWith('.')) return true
78
78
  if (relativePath === 'product.json') return true
79
79
  if (relativePath === 'references/source-manifest.json') return true
80
+ // 用户意图是本机产物(2026-11-01 起与 default.json 同目录),不随包分发
81
+ if (relativePath.endsWith('user-intents.json')) return true
80
82
  if (entry.isDirectory() && ['cache', 'assets', 'node_modules', 'scripts'].includes(entry.name)) return true
81
83
  return false
82
84
  }
@@ -512,5 +512,18 @@
512
512
  "合同借阅",
513
513
  "档案记录",
514
514
  "合同编号"
515
+ ],
516
+ "weaver-e10-asset-idle": [
517
+ "闲置资产",
518
+ "资产闲置",
519
+ "闲置判定",
520
+ "闲置统计",
521
+ "待调拨",
522
+ "待调剂",
523
+ "待评估",
524
+ "闲置时长",
525
+ "置信度",
526
+ "处置建议",
527
+ "资管家闲置"
515
528
  ]
516
- }
529
+ }
@@ -0,0 +1,13 @@
1
+ 本业务域用于泛微 E10 资管家的**闲置资产判定结果查询与统计出图**。入口 `SKILL.md` 先路由到 `weaver-e10-shared/SKILL.md`,再给出判定口径语义(服务端判定不重算)、操作路由和出图流程。具体用法拆到 `references/`:
2
+
3
+ - `idle-list.md`:`asset-idle.idle.list` 分页拉取服务端已判定的闲置资产结果集、字段口径与失败处置。
4
+ - `idle-stats-and-charts.md`:`asset-idle.idle.stats` 聚合统计(czjy 处置建议 / zclx 类型 / xzsz 时长分档)与三张图输出流程、系统页面地址行规则。
5
+ - `widget-style.md`:三张图的唯一样式来源(尺寸、字号、色值、文案结构)。
6
+ - `source-manifest.json`:源资料清单与 sha256 指纹,用于源资料更新检测。
7
+
8
+ 调优重点:
9
+
10
+ - `weaver-work-cli asset-idle schema` 是 operation 合约第一信息源;`withheldOperations` 声明按名称解析动作流 ID(`asset-idle.flow.resolve`)为禁用能力,`esbFlowId` 必须由调用方传入,不引导用户猜 ID。
11
+ - **判定口径红线**:xzsz / pdyj / zxd / czjy 服务端已判定,CLI 与 Agent 都不重算、不补算;xzsz/czjy 缺失标「无法判断」并排除出对应统计(`disabledCapabilities.idle-date-recalculation`)。
12
+ - 统计出图按固定流程:`idle.stats` → widget_guidelines → 三张图连续输出(图前图间无正文文字)→ 图③ widget 内部最后一行输出 `闲置资产详情:{url}`。
13
+ - 与 `weaver-e10-ziguanjia`(`weaver-work-cli asset`,资产台账管理)的路由区分:闲置统计/出图走本域,入库/领用/归还/维保走资管家。
@@ -2,6 +2,7 @@
2
2
 
3
3
  - `archive-agent-entry.md`:schema、统一 `archive run` 入口、stdin/文件 JSON 调用方式和输出约定。
4
4
  - `archive-fonds.md`:全宗列表与预归档电子文件库节点识别(上传的 treeId 来源)。
5
+ - `archive-prelib-list.md`:电子文件库列表查询(`treeId -> getTreeDetail -> getListDatas`)与智能识别的 `prepare -> apply`。
5
6
  - `archive-search.md`:全文检索可借档案,结果行的 `id`/`formId` 复用规则。
6
7
  - `archive-info.md`:档案基本信息查询(`{档案ID}_{表单ID}` 与 `dataId+formId` 两种输入)。
7
8
  - `archive-borrow-car.md`:借阅车列表(首选自定义接口、失败自动回退)与加入借阅车的 `prepare -> apply` 协议。
@@ -20,3 +21,5 @@ Agent 调优重点:
20
21
  - 下载为异步任务:`.apply` 会轮询打包进度并落盘,不要按超时立即重试。
21
22
  - 发起借阅/续借只返回 URL,不自动打开浏览器;必须把 URL 交给用户并说明需在浏览器完成填写与提交。
22
23
  - 结果为 `partial/write_uncertain` 时停止,不自动重放写请求。
24
+ - 电子文件库列表(`archive.prelib.list`)属于预归档库数据,与全文检索的可借档案不是同一批,不要混用;行 `id` 是智能识别 `checkIds` 的来源,展示时保留。
25
+ - 智能识别(`intelligentCate`)是异步任务:返回 `taskId` 只表示已提交,不得声称识别完成,也不要重复提交。
@@ -81,6 +81,7 @@ Agent 不得自行拼接、改写或向用户索取上述凭证(禁止要求
81
81
  | 查契约锁(案例)合同 | `archive-oa run archive-oa.qys.contract.list` | [`qys-contract-search.md`](references/qys-contract-search.md) |
82
82
  | 加入借阅车 / 看借阅车 | `archive-oa run archive-oa.borrow-car.add.prepare` → `.apply` / `archive-oa.borrow-car.list` | [`borrow-car.md`](references/borrow-car.md) |
83
83
  | 发起借阅(**生成流程页 URL 并打开浏览器**) | `archive-oa run archive-oa.borrow.start.url` / 契约锁 `archive-oa.qys.borrow.start.url`(或 `archive-oa borrow start-url [--source qys]`) | [`borrow-flow.md`](references/borrow-flow.md) |
84
+ | 查询我的借阅 / 我的借阅单 | 转到 `weaver-e10-workflow` 的 `workflow.search` | [`my-borrow.md`](references/my-borrow.md) |
84
85
  | 其余未收录能力 | 不在范围 | [`disabled-capabilities.md`](references/disabled-capabilities.md) |
85
86
 
86
87
  契约锁链路把 operation 前缀换成 `archive-oa.qys.*`(借阅车 `archive-oa.qys.borrow-car.*`、发起借阅 `archive-oa.qys.borrow.start.url`)。两套的流程页 URL 规则相同,`fb` 取借阅车记录自身的值(泛微 `0`、契约锁 `1`),禁止手工指定;契约锁客户名称字段是 `qymc`,泛微是 `htmc`。
@@ -0,0 +1,49 @@
1
+ # 查询我的借阅 / 我的借阅单
2
+
3
+ 用户说「我的借阅」「我的借阅单」「查一下我借的合同」时,**本技能不自己查流程数据**——`archive-oa` 只有合同查询与借阅车能力,没有流程查询能力。改用 **`weaver-e10-workflow`** 技能的 `workflow.search`。
4
+
5
+ 执行前按共享规则确认 `weaver-e10-workflow` 已安装;未安装时如实告知,不要用 `archive-oa` 的 operation 冒充流程查询。
6
+
7
+ ## 固定限制(强制)
8
+
9
+ | 参数 | 取值 | 说明 |
10
+ | --- | --- | --- |
11
+ | `workflow` | `["泛微合同电子档借阅申请流程"]` | **必须且只能是这一个工作流**;纯名称由 CLI 调 workflowPathBrowser 解析。多命中/0 命中时 CLI 返回澄清信封(`candidates`),此时用自然语言请用户确认完整名称,再按「名称(ID)」重发;**不要自己挑一个、不要向用户索取 ID** |
12
+ | `filterFields` 前提 | 必须传 `workflow` 且**只能是单个** | 缺 workflow 报 `field_require_workflow`,多个报 `field_require_single_workflow` |
13
+ | `category` | `mine`(默认) | 「我的借阅」= 我发起的 → `mine`。用户说「待我审批的借阅」才是 `todo`;说「我处理过的」→ `done`;都不确定且给了条件 → `all` |
14
+ | mine 为 0 时 | 提示后改查 `done` | 实测当前账号 `mine`=0、`done`=14(借阅流程多为他人发起、本人作为档案/审批方处理)。**`mine` 查到 0 条不要直接下结论「没有借阅」**,要如实说明「我发起的没有,我处理过的有 N 条,要看吗?」,用户确认后再用 `done` 查 |
15
+
16
+ ## 默认:只查「授权结束日期」未到期
17
+
18
+ **已实测可用的写法**(直接照用):
19
+
20
+ ```json
21
+ {"category":"...","workflow":["泛微合同电子档借阅申请流程"],
22
+ "filterFields":[{"fieldName":"授权结束日期","term":"like","value":["<今天 YYYY-MM-DD>","9999-12-31"]}]}
23
+ ```
24
+
25
+ - 「授权结束日期」是**日期字段**,`term` **只支持 `like` / `null` / `notnull`**;传 `ge` / `gt` 会报 `field_term_unsupported`。
26
+ - `like` 的 `value` 是**日期区间 `[开始, 结束]`**(YYYY-MM-DD)。**单边空串代表最小值**,所以:
27
+ - 未到期(≥ 今天)→ `["<今天>","9999-12-31"]`(**不要写 `["<今天>",""]`**,会被判「起点晚于终点」而报 `time_invalid`);
28
+ - 已到期(< 今天)→ `["","<昨天>"]`。
29
+ - 「今天」取当前真实日期,**禁止写死日期**。
30
+ - 实测对照(同一工作流):已办共 14 条,未到期区间返回 **0**、已到期区间返回 **14**,证明该区间过滤真实生效。
31
+ - 字段名以 CLI 实际解析为准:`fieldName` 支持 `字段名(字段ID)` 或纯字段名;多命中/0 命中返回澄清信封(候选为 `字段名(字段ID)`),**按候选选择,不得臆造**;字段元数据读不到(`field_meta`)时如实告知并改为不带 `filterFields` 查询。
32
+ - **不要逐条 `workflow.detail` 去取授权结束日期**:列表 `cards` 不带表单字段,且详情返回的表单值实测可能为空;用上面的 `filterFields` 区间即可,无需逐条调用。
33
+
34
+ ## 与用户交互:查全部
35
+
36
+ - 默认只查未到期;**每次汇报都要说明**「当前只含未到期,要看全部请说『查全部』」。
37
+ - 用户说「全部」「所有」「包括已到期的」「不限」「都列出来」时,**去掉授权结束日期条件**重新查一次(其余限制不变,仍限定该工作流)。
38
+ - 未到期查到 0 条时,主动提示「要不要看全部(含已到期)」,不要直接下结论说没有借阅记录。
39
+
40
+ ## 展示
41
+
42
+ - 按 `workflow.search` 的返回原样渲染流程列表(标题、编号、当前节点、状态、发起/结束时间等),**不新增衍生列**。
43
+ - 需要看某条详情时用 `workflow.detail`;需要在 OA 打开时用 `workflow.open`。
44
+
45
+ ## 禁止事项
46
+
47
+ - **禁止用 `archive-oa` 的 operation 查询借阅流程**(本技能无此能力),也不要把借阅车当成「我的借阅」——借阅车只是待发起的临时清单,不是已发起的流程。
48
+ - 禁止伪造工作流名称、字段 ID 或流程数据。
49
+ - 禁止把「授权结束日期」写成其它字段名(如「到期日期」「结束日期」),以 CLI 返回的候选为准。