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
@@ -15,7 +15,7 @@ import {
15
15
  } from "./chunk-FMPJJYYI.js";
16
16
  import {
17
17
  stringifyForStdout
18
- } from "./chunk-EG6ZU444.js";
18
+ } from "./chunk-ZUX3P6BQ.js";
19
19
  import {
20
20
  stateRoot
21
21
  } from "./chunk-AJ62BGAW.js";
@@ -24,7 +24,7 @@ import {
24
24
  createFetchTimingSink,
25
25
  hasXiaoEEnv,
26
26
  runWithFetchTiming
27
- } from "./chunk-JNG42JKG.js";
27
+ } from "./chunk-XXJOSZTQ.js";
28
28
 
29
29
  // src/shortcuts/workflow/operations/form.ts
30
30
  import { randomUUID } from "node:crypto";
@@ -5079,17 +5079,47 @@ async function runWorkflowSearch(args, env) {
5079
5079
  var TEXTAREA_COMPONENTS = /* @__PURE__ */ new Set(["textarea", "richtext", "editor", "html", "htmltext"]);
5080
5080
  var SELECT_COMPONENTS = /* @__PURE__ */ new Set(["select", "selectmultiple", "radiobox", "checkbox", "radio", "multiselect"]);
5081
5081
  var BROWSER_COMPONENTS = /* @__PURE__ */ new Set([
5082
+ // 人员 / 组织类
5082
5083
  "employee",
5083
5084
  "department",
5084
5085
  "subcompany",
5085
5086
  "jobtitle",
5086
5087
  "job",
5087
- "relatebrowser",
5088
- "browser",
5089
- "workflow",
5088
+ "employeeorganization",
5089
+ "employeeshare",
5090
+ "employeeauthshare",
5091
+ "odocreceivecomponent",
5092
+ // 关联类型(设计器「表单组件 / 关联类型」面板)
5090
5093
  "ebuilder",
5094
+ "mainline",
5095
+ "project",
5096
+ "task",
5097
+ "document",
5098
+ "workflow",
5099
+ "formcomponent",
5091
5100
  "customer",
5092
- "project"
5101
+ "customercomponent",
5102
+ "cluecomponent",
5103
+ "ordercomponent",
5104
+ "contactcomponent",
5105
+ "productioncomponent",
5106
+ "contractcomponent",
5107
+ "activitycomponent",
5108
+ "competitorcomponent",
5109
+ "kpiflowcomponent",
5110
+ "workreportcomponent",
5111
+ "attendcomponent",
5112
+ "agendacomponent",
5113
+ "chancecomponent",
5114
+ "relatebrowser",
5115
+ "relateodoc",
5116
+ "relatetag",
5117
+ "relateintegrate",
5118
+ "modulecomponent",
5119
+ "paas",
5120
+ "quotecomponent",
5121
+ "drivingrecord",
5122
+ "browser"
5093
5123
  ]);
5094
5124
  var NON_INPUT_COMPONENTS = /* @__PURE__ */ new Set([
5095
5125
  "paragraph",
@@ -8228,7 +8258,8 @@ var XIAOE_FILL_FORM_SDK_OPTIONS = {
8228
8258
  };
8229
8259
  var XIAOE_INITIAL_VALUES_MULTI_SEPARATOR = ",";
8230
8260
  var XIAOE_INITIAL_VALUES_ROW_SEPARATOR = ";";
8231
- function xiaoEUrlParamScalar(value) {
8261
+ var XIAOE_BROWSER_ID_PREFIX = "##";
8262
+ function xiaoEUrlParamScalar(value, prefixBrowserId = false) {
8232
8263
  if (value === null || value === void 0) return "";
8233
8264
  if (typeof value === "object") {
8234
8265
  const obj = value;
@@ -8236,18 +8267,19 @@ function xiaoEUrlParamScalar(value) {
8236
8267
  const picked = obj[key];
8237
8268
  if (picked === null || picked === void 0) continue;
8238
8269
  const text = String(picked).trim();
8239
- if (text) return text;
8270
+ if (!text) continue;
8271
+ return prefixBrowserId ? `${XIAOE_BROWSER_ID_PREFIX}${text}` : text;
8240
8272
  }
8241
8273
  return "";
8242
8274
  }
8243
8275
  return String(value).trim();
8244
8276
  }
8245
- function xiaoEUrlParamValue(value) {
8277
+ function xiaoEUrlParamValue(value, prefixBrowserId = false) {
8246
8278
  if (value === null || value === void 0) return "";
8247
8279
  if (Array.isArray(value)) {
8248
- return value.map((item) => xiaoEUrlParamScalar(item)).filter(Boolean).join(XIAOE_INITIAL_VALUES_MULTI_SEPARATOR);
8280
+ return value.map((item) => xiaoEUrlParamScalar(item, prefixBrowserId)).filter(Boolean).join(XIAOE_INITIAL_VALUES_MULTI_SEPARATOR);
8249
8281
  }
8250
- return xiaoEUrlParamScalar(value);
8282
+ return xiaoEUrlParamScalar(value, prefixBrowserId);
8251
8283
  }
8252
8284
  function editablePreviewForXiaoE(preview, fields, visibility) {
8253
8285
  const fieldById = /* @__PURE__ */ new Map();
@@ -8272,14 +8304,19 @@ function editablePreviewForXiaoE(preview, fields, visibility) {
8272
8304
  }
8273
8305
  return kept;
8274
8306
  }
8275
- function xiaoECustomParams(preview) {
8307
+ function xiaoECustomParams(preview, fields) {
8276
8308
  const ordered = [];
8277
8309
  const mainValues = /* @__PURE__ */ new Map();
8278
8310
  const detailValues = /* @__PURE__ */ new Map();
8311
+ const browserFieldIds = /* @__PURE__ */ new Set();
8312
+ for (const field of toArray(fields)) {
8313
+ const id = String(field?.id ?? "").trim();
8314
+ if (id && field?.isBrowser) browserFieldIds.add(id);
8315
+ }
8279
8316
  for (const item of toArray(preview)) {
8280
8317
  const fieldId = String(item?.fieldId ?? "").trim();
8281
8318
  if (!fieldId) continue;
8282
- const text = xiaoEUrlParamValue(item?.value);
8319
+ const text = xiaoEUrlParamValue(item?.value, browserFieldIds.has(fieldId));
8283
8320
  if (!text) continue;
8284
8321
  const isDetail = item?.dataIndex !== void 0 && item?.dataIndex !== null;
8285
8322
  if (!isDetail) {
@@ -8563,7 +8600,7 @@ async function runCreateRequest(args, env, options = {}) {
8563
8600
  baseUrl: xiaoEBaseUrl,
8564
8601
  title: String(context.workflow?.name ?? ""),
8565
8602
  workflowId: String(context.workflow?.id ?? context.runtime?.workflow_id ?? ""),
8566
- customParams: xiaoECustomParams(fillPreview)
8603
+ customParams: xiaoECustomParams(fillPreview, fields)
8567
8604
  })
8568
8605
  });
8569
8606
  }
@@ -10297,90 +10334,30 @@ import { existsSync as existsSync3, readFileSync } from "node:fs";
10297
10334
  import { join as join2 } from "node:path";
10298
10335
 
10299
10336
  // src/shortcuts/workflow/operations/todoSmartConvert/summaryCache.ts
10300
- import { existsSync as existsSync2, readdirSync, statSync } from "node:fs";
10337
+ import { existsSync as existsSync2 } from "node:fs";
10301
10338
  import { chmod as chmod2, mkdir as mkdir2, readFile as readFile2, writeFile as writeFile2 } from "node:fs/promises";
10302
- import { homedir as homedir2 } from "node:os";
10303
10339
  import { join, resolve as resolve2 } from "node:path";
10304
- var SKILL_DIR_NAME = "weaver-e10-workflow";
10305
10340
  var CACHE_DIR_NAME = "cache";
10306
- var AGENT_HOME_DIRS = [".workbuddy", ".claude", ".codebuddy", ".codex", ".cursor", ".gemini"];
10307
10341
  var SUMMARY_CACHE_FILE_NAME = "todo-summary-cache.json";
10308
10342
  var SUMMARY_CACHE_VERSION = 2;
10309
10343
  function uniqueDirs(dirs) {
10310
- return [...new Set(dirs.filter(Boolean))];
10311
- }
10312
- function stateCacheDir(ctx) {
10313
- return resolve2(stateRoot(ctx), CACHE_DIR_NAME);
10314
- }
10315
- function discoverThisSkillDirs(home) {
10316
- const marker = join("references", "todo-smart-convert.md");
10317
- const found = [];
10318
- for (const agent of AGENT_HOME_DIRS) {
10319
- const root = join(home, agent, "skills");
10320
- const entries = (() => {
10321
- try {
10322
- return readdirSync(root, { withFileTypes: true });
10323
- } catch {
10324
- return [];
10325
- }
10326
- })();
10327
- for (const entry of entries) {
10328
- if (!entry.isDirectory()) continue;
10329
- const dir = join(root, entry.name);
10330
- if (existsSync2(join(dir, marker))) found.push(dir);
10331
- }
10332
- }
10333
- return found;
10334
- }
10335
- function mtimeOf(file) {
10336
- try {
10337
- return statSync(file).mtimeMs;
10338
- } catch {
10339
- return 0;
10340
- }
10344
+ return [...new Set(dirs.filter((dir) => Boolean(dir)))];
10341
10345
  }
10342
- function rankSkillDirs(dirs) {
10343
- const scored = dirs.map((dir, index) => ({
10344
- dir,
10345
- index,
10346
- hasCache: existsSync2(join(dir, CACHE_DIR_NAME, SUMMARY_CACHE_FILE_NAME)) ? 1 : 0,
10347
- cacheMtime: mtimeOf(join(dir, CACHE_DIR_NAME, SUMMARY_CACHE_FILE_NAME)),
10348
- hasMarker: existsSync2(join(dir, "references", "todo-smart-convert.md")) ? 1 : 0,
10349
- skillMtime: mtimeOf(join(dir, "SKILL.md"))
10350
- }));
10351
- scored.sort(
10352
- (a, b) => b.hasCache - a.hasCache || b.cacheMtime - a.cacheMtime || b.hasMarker - a.hasMarker || b.skillMtime - a.skillMtime || a.index - b.index
10353
- );
10354
- return scored.map((item) => item.dir);
10346
+ function resolveSkillDir() {
10347
+ const explicit = process.env.WEAVER_WORK_CLI_SKILL_DIR?.trim();
10348
+ return explicit ? resolve2(explicit) : null;
10355
10349
  }
10356
10350
  function cacheRoots(ctx) {
10357
- const fallback = stateCacheDir(ctx);
10358
- const explicitCache = process.env.WEAVER_WORK_CLI_CACHE_DIR?.trim();
10359
- if (explicitCache) {
10360
- const dir = resolve2(explicitCache);
10361
- return { preferred: dir, fallback: dir, inSkillDir: false, skillDir: null };
10362
- }
10363
- const home = homedir2();
10364
- const explicitSkill = process.env.WEAVER_WORK_CLI_SKILL_DIR?.trim();
10365
- const candidates = [];
10366
- if (explicitSkill) candidates.push(resolve2(explicitSkill));
10367
- for (const agent of AGENT_HOME_DIRS) {
10368
- candidates.push(join(home, agent, "skills", SKILL_DIR_NAME));
10369
- }
10370
- candidates.push(...discoverThisSkillDirs(home));
10371
- const installed = uniqueDirs(candidates).filter((dir) => existsSync2(join(dir, "SKILL.md")));
10372
- const [skillDir] = rankSkillDirs(installed);
10373
- if (skillDir) {
10374
- return {
10375
- preferred: join(skillDir, CACHE_DIR_NAME),
10376
- fallback,
10377
- inSkillDir: true,
10378
- skillDir
10379
- };
10351
+ void ctx;
10352
+ const skillDir = resolveSkillDir();
10353
+ if (!skillDir) {
10354
+ return { preferred: null, fallback: null, inSkillDir: false, skillDir: null, refused: true };
10380
10355
  }
10381
- return { preferred: fallback, fallback, inSkillDir: false, skillDir: null };
10356
+ const cacheDir = join(skillDir, CACHE_DIR_NAME);
10357
+ return { preferred: cacheDir, fallback: cacheDir, inSkillDir: true, skillDir, refused: false };
10382
10358
  }
10383
10359
  async function readCacheFile(roots, fileName) {
10360
+ if (roots.refused || !roots.preferred) return null;
10384
10361
  for (const dir of uniqueDirs([roots.preferred, roots.fallback])) {
10385
10362
  const file = join(dir, fileName);
10386
10363
  if (!existsSync2(file)) continue;
@@ -10393,6 +10370,7 @@ async function readCacheFile(roots, fileName) {
10393
10370
  return null;
10394
10371
  }
10395
10372
  async function writeCacheFile(roots, fileName, payload) {
10373
+ if (roots.refused || !roots.preferred) return null;
10396
10374
  for (const dir of uniqueDirs([roots.preferred, roots.fallback])) {
10397
10375
  const file = join(dir, fileName);
10398
10376
  try {
@@ -10412,9 +10390,14 @@ function summaryCacheKey(requestId, lastFeedBackDateTime) {
10412
10390
  const stamp = String(lastFeedBackDateTime ?? "").trim();
10413
10391
  return `${requestId}__${stamp}`;
10414
10392
  }
10415
- function entryMatchesMaxChars(entry, maxChars) {
10416
- if (maxChars === void 0) return true;
10417
- return (entry.maxChars ?? 50) === maxChars;
10393
+ function promptTagOf(prompt) {
10394
+ return String(prompt ?? "").trim();
10395
+ }
10396
+ function entryMatches(entry, maxChars, pk, dv) {
10397
+ if (maxChars !== void 0 && (entry.maxChars ?? 50) !== maxChars) return false;
10398
+ if (pk !== void 0 && (entry.pk ?? "") !== pk) return false;
10399
+ if (entry.dv !== void 0 && entry.dv !== dv) return false;
10400
+ return true;
10418
10401
  }
10419
10402
  function normalizeSummaryEntry(raw) {
10420
10403
  if (!raw || typeof raw !== "object") return null;
@@ -10422,7 +10405,15 @@ function normalizeSummaryEntry(raw) {
10422
10405
  if (typeof entry.summary !== "string" || !entry.summary.trim()) return null;
10423
10406
  if (typeof entry.cachedAt !== "number" || !Number.isFinite(entry.cachedAt)) return null;
10424
10407
  const maxChars = typeof entry.maxChars === "number" && Number.isFinite(entry.maxChars) ? entry.maxChars : void 0;
10425
- return { summary: entry.summary, cachedAt: entry.cachedAt, ...maxChars === void 0 ? {} : { maxChars } };
10408
+ const pk = typeof entry.pk === "string" && entry.pk !== "" ? entry.pk : void 0;
10409
+ const dv = typeof entry.dv === "string" && entry.dv !== "" ? entry.dv : void 0;
10410
+ return {
10411
+ summary: entry.summary,
10412
+ cachedAt: entry.cachedAt,
10413
+ ...maxChars === void 0 ? {} : { maxChars },
10414
+ ...pk === void 0 ? {} : { pk },
10415
+ ...dv === void 0 ? {} : { dv }
10416
+ };
10426
10417
  }
10427
10418
  async function openSummaryCache(ctx) {
10428
10419
  const roots = cacheRoots(ctx);
@@ -10459,27 +10450,29 @@ async function openSummaryCache(ctx) {
10459
10450
  misses: 0,
10460
10451
  pruned,
10461
10452
  dirty: false,
10462
- get(requestId, lastFeedBackDateTime, maxChars) {
10453
+ get(requestId, lastFeedBackDateTime, maxChars, pk, dv) {
10463
10454
  const key = summaryCacheKey(requestId, lastFeedBackDateTime);
10464
10455
  const entry = entries.get(key);
10465
- if (entry && now - entry.cachedAt <= SUMMARY_CACHE_TTL_MS && entryMatchesMaxChars(entry, maxChars)) {
10456
+ if (entry && now - entry.cachedAt <= SUMMARY_CACHE_TTL_MS && entryMatches(entry, maxChars, pk, dv)) {
10466
10457
  cache.hits += 1;
10467
10458
  return entry.summary;
10468
10459
  }
10469
10460
  cache.misses += 1;
10470
10461
  return null;
10471
10462
  },
10472
- has(requestId, lastFeedBackDateTime, maxChars) {
10463
+ has(requestId, lastFeedBackDateTime, maxChars, pk, dv) {
10473
10464
  const entry = entries.get(summaryCacheKey(requestId, lastFeedBackDateTime));
10474
- return entry !== void 0 && entryMatchesMaxChars(entry, maxChars);
10465
+ return entry !== void 0 && entryMatches(entry, maxChars, pk, dv);
10475
10466
  },
10476
- set(requestId, lastFeedBackDateTime, summary, maxChars) {
10467
+ set(requestId, lastFeedBackDateTime, summary, maxChars, pk, dv) {
10477
10468
  const text = String(summary ?? "").trim();
10478
10469
  if (!requestId || !text) return false;
10479
10470
  entries.set(summaryCacheKey(requestId, lastFeedBackDateTime), {
10480
10471
  summary: text,
10481
10472
  cachedAt: Date.now(),
10482
- ...maxChars === void 0 ? {} : { maxChars }
10473
+ ...maxChars === void 0 ? {} : { maxChars },
10474
+ ...pk === void 0 || pk === "" ? {} : { pk },
10475
+ ...dv === void 0 || dv === "" ? {} : { dv }
10483
10476
  });
10484
10477
  dirty = true;
10485
10478
  cache.dirty = true;
@@ -10531,7 +10524,8 @@ var FALLBACK_DEFAULTS = {
10531
10524
  requestSummaryPrompt: "",
10532
10525
  summaryMaxChars: 150,
10533
10526
  groupSummaryMaxChars: 100,
10534
- groupSummaryTail: "\u53EF\u5C55\u5F00\u9010\u6761\u67E5\u770B\u6216\u76F4\u63A5\u6C47\u603B\u5904\u7406"
10527
+ groupSummaryTail: "\u53EF\u5C55\u5F00\u9010\u6761\u67E5\u770B\u6216\u76F4\u63A5\u6C47\u603B\u5904\u7406",
10528
+ excludeOriginFormDataFields: []
10535
10529
  };
10536
10530
  function emptyRuleFile(error = null) {
10537
10531
  return {
@@ -10560,14 +10554,33 @@ function validateFocusKeyword(raw, index) {
10560
10554
  if (raw.length !== 3) return `focusKeyword[${index}] \u5FC5\u987B\u662F [\u6307\u6807, \u7B97\u5B50, \u503C] \u4E09\u5143\u7EC4`;
10561
10555
  return null;
10562
10556
  }
10557
+ function validateExcludeFields(raw, where) {
10558
+ if (raw === void 0) return null;
10559
+ if (typeof raw === "string") {
10560
+ return raw.trim() === "" ? `${where} \u4E0D\u80FD\u662F\u7A7A\u5B57\u7B26\u4E32\uFF08\u4E0D\u6392\u9664\u5C31\u522B\u7ED9\u8FD9\u4E2A\u952E\uFF09` : null;
10561
+ }
10562
+ if (!Array.isArray(raw)) return `${where} \u5FC5\u987B\u662F\u5B57\u7B26\u4E32\u6570\u7EC4`;
10563
+ for (const item of raw) {
10564
+ if (typeof item !== "string" || item.trim() === "") return `${where} \u7684\u6BCF\u4E00\u9879\u90FD\u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32`;
10565
+ }
10566
+ return null;
10567
+ }
10563
10568
  function validateRuleFile(raw) {
10564
10569
  const errors = [];
10565
10570
  const file = raw;
10566
10571
  if (!file || typeof file !== "object") return ["\u89C4\u5219\u6587\u4EF6\u5FC5\u987B\u662F JSON \u5BF9\u8C61"];
10572
+ if (file.schemaVersion !== void 0 && file.schemaVersion !== 2 && file.schemaVersion !== 3) {
10573
+ errors.push(`schemaVersion \u53EA\u652F\u6301 2 \u6216 3\uFF0C\u6536\u5230 ${JSON.stringify(file.schemaVersion ?? null)}`);
10574
+ }
10567
10575
  if (!Array.isArray(file.rules)) {
10568
10576
  errors.push("rules \u5FC5\u987B\u662F\u6570\u7EC4");
10569
10577
  return errors;
10570
10578
  }
10579
+ const defaultsExclude = validateExcludeFields(
10580
+ file.defaults?.excludeOriginFormDataFields,
10581
+ "defaults.excludeOriginFormDataFields"
10582
+ );
10583
+ if (defaultsExclude) errors.push(defaultsExclude);
10571
10584
  const seen = /* @__PURE__ */ new Set();
10572
10585
  file.rules.forEach((rule, index) => {
10573
10586
  if (!rule || typeof rule !== "object") {
@@ -10589,6 +10602,13 @@ function validateRuleFile(raw) {
10589
10602
  if (rule.summaryMaxChars !== void 0 && (typeof rule.summaryMaxChars !== "number" || rule.summaryMaxChars <= 0)) {
10590
10603
  errors.push(`rules[${index}].summaryMaxChars \u5FC5\u987B\u662F\u6B63\u6570`);
10591
10604
  }
10605
+ const excludeReason = validateExcludeFields(rule.excludeOriginFormDataFields, `rules[${index}].excludeOriginFormDataFields`);
10606
+ if (excludeReason) errors.push(excludeReason);
10607
+ if (rule.summaryRequiredFields !== void 0) {
10608
+ if (!Array.isArray(rule.summaryRequiredFields) || rule.summaryRequiredFields.length === 0 || rule.summaryRequiredFields.some((f) => typeof f !== "string" || !f.trim() || f.trim().length > 50) || rule.summaryRequiredFields.length > 20) {
10609
+ errors.push(`rules[${index}].summaryRequiredFields \u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32\u6570\u7EC4\uFF081~20 \u9879\u3001\u6BCF\u9879 1~50 \u5B57\uFF09`);
10610
+ }
10611
+ }
10592
10612
  const groupRule = rule.aiGroup?.groupRule;
10593
10613
  if (groupRule) {
10594
10614
  if (typeof groupRule.focus !== "boolean") errors.push(`rules[${index}].aiGroup.groupRule.focus \u5FC5\u987B\u662F\u5E03\u5C14`);
@@ -10642,29 +10662,28 @@ function loadRuleFile(file) {
10642
10662
  return { file: parsed, error: null };
10643
10663
  }
10644
10664
  function aigroupDir(ctx) {
10645
- const explicit = process.env.WEAVER_WORK_CLI_AIGROUP_DIR?.trim();
10646
- if (explicit) return join2(explicit);
10647
- const roots = cacheRoots(ctx);
10648
- return roots.skillDir ? join2(roots.skillDir, AIGROUP_DIR_NAME) : join2(stateRoot(ctx), AIGROUP_DIR_NAME);
10665
+ const skillDir = resolveSkillDir();
10666
+ return skillDir ? join2(skillDir, AIGROUP_DIR_NAME) : null;
10649
10667
  }
10650
10668
  function aigroupUserDir(ctx) {
10651
- const explicit = process.env.WEAVER_WORK_CLI_AIGROUP_DIR?.trim();
10652
- if (explicit) return join2(explicit);
10653
- return join2(stateRoot(ctx), AIGROUP_DIR_NAME);
10669
+ return aigroupDir(ctx);
10654
10670
  }
10655
10671
  function loadRules(options = {}) {
10656
- const dir = options.dir ?? aigroupDir(options.ctx);
10657
- const userDir = options.dir ?? aigroupUserDir(options.ctx);
10658
10672
  const warnings = [];
10673
+ const dir = options.dir ?? aigroupDir(options.ctx);
10674
+ if (!dir) {
10675
+ warnings.push("\u65E0\u5F53\u524D skill \u4E0A\u4E0B\u6587\uFF08WEAVER_WORK_CLI_SKILL_DIR \u672A\u8BBE\u7F6E\uFF09\uFF1Aaigroup \u89C4\u5219\u672A\u52A0\u8F7D\uFF0C\u6309\u65E0\u89C4\u5219\u5904\u7406");
10676
+ return { file: emptyRuleFile(null).file, warnings, dir: null };
10677
+ }
10659
10678
  const base = loadRuleFile(join2(dir, DEFAULT_RULE_FILE));
10660
10679
  if (base.error) warnings.push(base.error);
10661
10680
  const rules = [...base.file.rules];
10662
- const userIntentFile = join2(userDir, USER_INTENT_RULE_FILE);
10663
- const legacyIntentFile = join2(dir, USER_INTENT_RULE_FILE);
10681
+ const userIntentFile = join2(dir, USER_INTENT_RULE_FILE);
10682
+ const legacyIntentFile = join2(stateRoot(options.ctx), AIGROUP_DIR_NAME, USER_INTENT_RULE_FILE);
10664
10683
  let intentFile = userIntentFile;
10665
10684
  if (!options.dir && !existsSync3(userIntentFile) && existsSync3(legacyIntentFile)) {
10666
10685
  intentFile = legacyIntentFile;
10667
- warnings.push(`\u7528\u6237\u610F\u56FE\u4ECD\u8BFB\u81EA skill \u76EE\u5F55\uFF08${legacyIntentFile}\uFF09\uFF1A\u65B0\u4F4D\u7F6E\u662F ${userIntentFile}\uFF0C\u91CD\u65B0\u6267\u884C aigroup set \u5373\u53EF\u8FC1\u79FB`);
10686
+ warnings.push(`\u7528\u6237\u610F\u56FE\u4ECD\u8BFB\u81EA CLI \u72B6\u6001\u76EE\u5F55\uFF08${legacyIntentFile}\uFF09\uFF1A\u65B0\u4F4D\u7F6E\u662F ${userIntentFile}\uFF0C\u91CD\u65B0\u6267\u884C aigroup set \u5373\u53EF\u8FC1\u79FB`);
10668
10687
  }
10669
10688
  const intent = loadRuleFile(intentFile);
10670
10689
  if (intent.error) {
@@ -10726,12 +10745,13 @@ function resolveRule(file, workflowName) {
10726
10745
  summaryMaxChars: raw.summaryMaxChars ?? defaults.summaryMaxChars,
10727
10746
  groupSummaryMaxChars: defaults.groupSummaryMaxChars,
10728
10747
  groupSummaryTail: defaults.groupSummaryTail,
10748
+ // 显式给 `[]` = 规则级覆盖(清空 defaults 的清单);未给才落 defaults
10749
+ excludeOriginFormDataFields: raw.excludeOriginFormDataFields === void 0 ? defaults.excludeOriginFormDataFields : asStringArray3(raw.excludeOriginFormDataFields) ?? [],
10729
10750
  aiGroup: raw.aiGroup ? {
10730
10751
  groupSummary: {
10731
10752
  needAI: groupSummary?.needAI ?? false,
10732
10753
  ...groupSummary?.prompt === void 0 ? {} : { prompt: groupSummary.prompt },
10733
10754
  ...groupSummary?.template === void 0 ? {} : { template: groupSummary.template },
10734
- maxChars: defaults.groupSummaryMaxChars,
10735
10755
  tail: defaults.groupSummaryTail
10736
10756
  },
10737
10757
  groupRule: groupRule ?? EMPTY_GROUP_RULE
@@ -10858,6 +10878,15 @@ function resetActiveRules() {
10858
10878
  activeFile = null;
10859
10879
  activeDir = null;
10860
10880
  }
10881
+ function excludeFieldsFor(workflowName) {
10882
+ return activeRuleFor(workflowName)?.excludeOriginFormDataFields ?? [];
10883
+ }
10884
+ function excludeTagOf(fields) {
10885
+ return [...fields].join("");
10886
+ }
10887
+ function excludeTagFor(workflowName) {
10888
+ return excludeTagOf(excludeFieldsFor(workflowName));
10889
+ }
10861
10890
 
10862
10891
  // src/shortcuts/workflow/operations/todoSmartConvert/todoListRows.ts
10863
10892
  var DISPLAY_ROW_FIELDS = [
@@ -10918,6 +10947,107 @@ function projectDisplayRows(response) {
10918
10947
  return projected;
10919
10948
  }
10920
10949
 
10950
+ // src/shortcuts/workflow/operations/todoSmartConvert/todoDetailCache.ts
10951
+ var DETAIL_CACHE_FILE_NAME = "todo-detail-cache.json";
10952
+ var DETAIL_CACHE_TTL_MS = SUMMARY_CACHE_TTL_MS;
10953
+ var DETAIL_CACHE_PRUNE_INTERVAL_MS = SUMMARY_CACHE_PRUNE_INTERVAL_MS;
10954
+ var DETAIL_CACHE_ENTRY_VERSION = 3;
10955
+ function detailCacheKey(requestId, lastFeedBackDateTime) {
10956
+ const stamp = String(lastFeedBackDateTime ?? "").trim();
10957
+ return `${requestId}__${stamp}`;
10958
+ }
10959
+ function detailMaterialTagOf(entry) {
10960
+ const basic = entry.basicInfo ?? {};
10961
+ const basicKeys = Object.keys(basic).sort();
10962
+ const material = JSON.stringify([
10963
+ basicKeys.map((key) => [key, basic[key]]),
10964
+ (entry.fields ?? []).map((field) => [field.fieldName, field.value])
10965
+ ]);
10966
+ let hash = 5381;
10967
+ for (let i = 0; i < material.length; i += 1) hash = (hash << 5) + hash + material.charCodeAt(i) >>> 0;
10968
+ return hash.toString(16);
10969
+ }
10970
+ function normalizeEntry(raw) {
10971
+ if (!raw || typeof raw !== "object") return null;
10972
+ const value = raw;
10973
+ if (value.v !== DETAIL_CACHE_ENTRY_VERSION) return null;
10974
+ if (typeof value.cachedAt !== "number" || !Number.isFinite(value.cachedAt)) return null;
10975
+ if (typeof value.excludeTag !== "string") return null;
10976
+ const basicInfo = {};
10977
+ if (value.basicInfo && typeof value.basicInfo === "object") {
10978
+ for (const [key, item] of Object.entries(value.basicInfo)) {
10979
+ if (typeof item === "string") basicInfo[key] = item;
10980
+ }
10981
+ }
10982
+ const fields = Array.isArray(value.fields) ? value.fields.filter((item) => Boolean(item) && typeof item.fieldName === "string" && typeof item.value === "string").map((item) => ({ fieldName: item.fieldName, value: item.value })) : [];
10983
+ const metrics = value.metrics && typeof value.metrics === "object" ? value.metrics : {};
10984
+ return {
10985
+ v: DETAIL_CACHE_ENTRY_VERSION,
10986
+ basicInfo,
10987
+ fields,
10988
+ metrics,
10989
+ excludeTag: value.excludeTag,
10990
+ cachedAt: value.cachedAt
10991
+ };
10992
+ }
10993
+ async function openDetailCache(ctx) {
10994
+ const roots = cacheRoots(ctx);
10995
+ const stored = await readCacheFile(roots, DETAIL_CACHE_FILE_NAME);
10996
+ const now = Date.now();
10997
+ const entries = /* @__PURE__ */ new Map();
10998
+ let prunedAt = 0;
10999
+ let loaded = 0;
11000
+ let pruned = 0;
11001
+ if (stored) {
11002
+ try {
11003
+ const parsed = JSON.parse(stored.text);
11004
+ prunedAt = typeof parsed.prunedAt === "number" ? parsed.prunedAt : 0;
11005
+ for (const [key, raw] of Object.entries(parsed.entries ?? {})) {
11006
+ const entry = normalizeEntry(raw);
11007
+ if (!entry) continue;
11008
+ if (now - entry.cachedAt > DETAIL_CACHE_TTL_MS) {
11009
+ pruned += 1;
11010
+ continue;
11011
+ }
11012
+ entries.set(key, entry);
11013
+ loaded += 1;
11014
+ }
11015
+ } catch {
11016
+ }
11017
+ }
11018
+ const cache = {
11019
+ file: stored?.file ?? null,
11020
+ pruned: 0,
11021
+ loaded,
11022
+ get(requestId, lastFeedBackDateTime) {
11023
+ const entry = entries.get(detailCacheKey(requestId, lastFeedBackDateTime));
11024
+ if (!entry) return null;
11025
+ if (Date.now() - entry.cachedAt > DETAIL_CACHE_TTL_MS) return null;
11026
+ return entry;
11027
+ },
11028
+ has(requestId, lastFeedBackDateTime) {
11029
+ return cache.get(requestId, lastFeedBackDateTime) !== null;
11030
+ },
11031
+ set(requestId, lastFeedBackDateTime, entry) {
11032
+ if (!requestId) return false;
11033
+ entries.set(detailCacheKey(requestId, lastFeedBackDateTime), { ...entry, cachedAt: Date.now() });
11034
+ return true;
11035
+ },
11036
+ async save() {
11037
+ const shouldPrune = pruned > 0 && Date.now() - prunedAt > DETAIL_CACHE_PRUNE_INTERVAL_MS;
11038
+ const payload = {
11039
+ prunedAt: shouldPrune ? Date.now() : prunedAt,
11040
+ entries: Object.fromEntries(entries)
11041
+ };
11042
+ const file = await writeCacheFile(roots, DETAIL_CACHE_FILE_NAME, JSON.stringify(payload));
11043
+ if (file) cache.file = file;
11044
+ if (shouldPrune) cache.pruned = pruned;
11045
+ return file;
11046
+ }
11047
+ };
11048
+ return cache;
11049
+ }
11050
+
10921
11051
  // src/shortcuts/workflow/operations/todoSmartConvert/detailFieldRules.ts
10922
11052
  function normalizeDetailFieldName(raw) {
10923
11053
  return String(raw ?? "").replace(/<[^>]*>/g, " ").replace(/[【】[\]()()]/g, "").replace(/\s+/g, "").trim();
@@ -11135,7 +11265,8 @@ function renderGroupSummaryTemplate(input) {
11135
11265
  var DETAIL_BATCH_CONCURRENCY = 10;
11136
11266
  var DETAIL_BATCH_TIMEOUT_MS = 1e4;
11137
11267
  var DETAIL_BATCH_RETRIES = 1;
11138
- var DETAIL_BATCH_FIELD_LIMIT = 12;
11268
+ var DETAIL_MATERIAL_FIELD_LIMIT = 200;
11269
+ var DETAIL_MATERIAL_VALUE_LIMIT = 500;
11139
11270
  var HTML_ENTITIES = {
11140
11271
  amp: "&",
11141
11272
  lt: "<",
@@ -11224,6 +11355,9 @@ async function fetchOneDetail(env, requestId, userId, options) {
11224
11355
  const normalized = normalizeDetailResponse(response);
11225
11356
  const base = normalized?.basicInfo ?? {};
11226
11357
  const rawFields = Array.isArray(normalized?.formData?.fields) ? normalized.formData.fields : [];
11358
+ const basicInfo = Object.fromEntries(
11359
+ Object.entries(base).filter(([key]) => key !== "requestTitle" && key !== "flowName")
11360
+ );
11227
11361
  const usableFields = dropDistributionDetailFields(rawFields);
11228
11362
  const internalMessage = isInternalMessageDetail(
11229
11363
  base.workflowName,
@@ -11233,25 +11367,19 @@ async function fetchOneDetail(env, requestId, userId, options) {
11233
11367
  ...field,
11234
11368
  value: cleanFieldText(String(field?.value ?? ""))
11235
11369
  }));
11236
- const picked = internalMessage ? pickInternalMessageFieldPairs(cleanedFields, { creatorName: base.creatorName, createdAt: base.createdAt }) : selectSummaryFields(cleanedFields, options.fieldLimit ?? DETAIL_BATCH_FIELD_LIMIT, void 0, { fullValue: true });
11237
- const summaryFields = picked.map((field) => ({
11370
+ const excludeFields = excludeFieldsFor(base.workflowName);
11371
+ const excludeSet = new Set(excludeFields.map((name) => normalizeDetailFieldName(name)));
11372
+ const picked = (internalMessage ? pickInternalMessageFieldPairs(cleanedFields, base) : cleanedFields.filter((field) => !excludeSet.has(normalizeDetailFieldName(field?.fieldName)))).slice(0, DETAIL_MATERIAL_FIELD_LIMIT).map((field) => ({
11238
11373
  fieldName: String(field?.fieldName ?? "").trim(),
11239
- value: String(field?.value ?? "")
11374
+ value: String(field?.value ?? "").slice(0, DETAIL_MATERIAL_VALUE_LIMIT)
11240
11375
  })).filter((field) => field.fieldName && field.value);
11241
11376
  const metrics = extractRowMetrics(usableFields);
11242
11377
  return {
11243
11378
  requestId,
11244
- basicInfo: {
11245
- requestTitle: String(base.requestTitle ?? ""),
11246
- workflowName: String(base.workflowName ?? ""),
11247
- creatorName: String(base.creatorName ?? ""),
11248
- createdAt: String(base.createdAt ?? ""),
11249
- currentNode: String(base.currentNode ?? ""),
11250
- requestLevel: String(base.requestLevel ?? ""),
11251
- flowStatus: String(base.flowStatus ?? "")
11252
- },
11253
- summaryFields,
11254
- contextAvailable: summaryFields.length > 0,
11379
+ basicInfo,
11380
+ formFields: picked,
11381
+ contextAvailable: picked.length > 0,
11382
+ excludeTag: excludeTagOf(excludeFields),
11255
11383
  metrics
11256
11384
  };
11257
11385
  }
@@ -11303,17 +11431,10 @@ async function fetchRequestDetailsBatch(env, requestIds, options = {}) {
11303
11431
  failures.push({ requestId, error: reason });
11304
11432
  details.set(requestId, {
11305
11433
  requestId,
11306
- basicInfo: {
11307
- requestTitle: "",
11308
- workflowName: "",
11309
- creatorName: "",
11310
- createdAt: "",
11311
- currentNode: "",
11312
- requestLevel: "",
11313
- flowStatus: ""
11314
- },
11315
- summaryFields: [],
11434
+ basicInfo: {},
11435
+ formFields: [],
11316
11436
  contextAvailable: false,
11437
+ excludeTag: "",
11317
11438
  metrics: {},
11318
11439
  error: reason
11319
11440
  });
@@ -11336,90 +11457,11 @@ async function fetchRequestDetailsBatch(env, requestIds, options = {}) {
11336
11457
  };
11337
11458
  }
11338
11459
 
11339
- // src/shortcuts/workflow/operations/todoSmartConvert/todoDetailCache.ts
11340
- var DETAIL_CACHE_FILE_NAME = "todo-detail-cache.json";
11341
- var DETAIL_CACHE_TTL_MS = SUMMARY_CACHE_TTL_MS;
11342
- var DETAIL_CACHE_PRUNE_INTERVAL_MS = SUMMARY_CACHE_PRUNE_INTERVAL_MS;
11343
- function detailCacheKey(requestId, lastFeedBackDateTime) {
11344
- const stamp = String(lastFeedBackDateTime ?? "").trim();
11345
- return `${requestId}__${stamp}`;
11346
- }
11347
- function normalizeEntry(raw) {
11348
- if (!raw || typeof raw !== "object") return null;
11349
- const value = raw;
11350
- if (typeof value.cachedAt !== "number" || !Number.isFinite(value.cachedAt)) return null;
11351
- if (typeof value.context !== "string") return null;
11352
- const fields = Array.isArray(value.fields) ? value.fields.filter((item) => Boolean(item) && typeof item.fieldName === "string" && typeof item.value === "string").map((item) => ({ fieldName: item.fieldName, value: item.value })) : [];
11353
- const metrics = value.metrics && typeof value.metrics === "object" ? value.metrics : {};
11354
- return {
11355
- context: value.context,
11356
- contextAvailable: value.contextAvailable === true,
11357
- metrics,
11358
- fields,
11359
- cachedAt: value.cachedAt
11360
- };
11361
- }
11362
- async function openDetailCache(ctx) {
11363
- const roots = cacheRoots(ctx);
11364
- const stored = await readCacheFile(roots, DETAIL_CACHE_FILE_NAME);
11365
- const now = Date.now();
11366
- const entries = /* @__PURE__ */ new Map();
11367
- let prunedAt = 0;
11368
- let loaded = 0;
11369
- let pruned = 0;
11370
- if (stored) {
11371
- try {
11372
- const parsed = JSON.parse(stored.text);
11373
- prunedAt = typeof parsed.prunedAt === "number" ? parsed.prunedAt : 0;
11374
- for (const [key, raw] of Object.entries(parsed.entries ?? {})) {
11375
- const entry = normalizeEntry(raw);
11376
- if (!entry) continue;
11377
- if (now - entry.cachedAt > DETAIL_CACHE_TTL_MS) {
11378
- pruned += 1;
11379
- continue;
11380
- }
11381
- entries.set(key, entry);
11382
- loaded += 1;
11383
- }
11384
- } catch {
11385
- }
11386
- }
11387
- const cache = {
11388
- file: stored?.file ?? null,
11389
- pruned: 0,
11390
- loaded,
11391
- get(requestId, lastFeedBackDateTime) {
11392
- const entry = entries.get(detailCacheKey(requestId, lastFeedBackDateTime));
11393
- if (!entry) return null;
11394
- if (Date.now() - entry.cachedAt > DETAIL_CACHE_TTL_MS) return null;
11395
- return entry;
11396
- },
11397
- has(requestId, lastFeedBackDateTime) {
11398
- return cache.get(requestId, lastFeedBackDateTime) !== null;
11399
- },
11400
- set(requestId, lastFeedBackDateTime, entry) {
11401
- if (!requestId) return false;
11402
- entries.set(detailCacheKey(requestId, lastFeedBackDateTime), { ...entry, cachedAt: Date.now() });
11403
- return true;
11404
- },
11405
- async save() {
11406
- const shouldPrune = pruned > 0 && Date.now() - prunedAt > DETAIL_CACHE_PRUNE_INTERVAL_MS;
11407
- const payload = {
11408
- prunedAt: shouldPrune ? Date.now() : prunedAt,
11409
- entries: Object.fromEntries(entries)
11410
- };
11411
- const file = await writeCacheFile(roots, DETAIL_CACHE_FILE_NAME, JSON.stringify(payload));
11412
- if (file) cache.file = file;
11413
- if (shouldPrune) cache.pruned = pruned;
11414
- return file;
11415
- }
11416
- };
11417
- return cache;
11418
- }
11419
-
11420
11460
  // src/shortcuts/workflow/operations/todoSmartConvert/todoSmartConvert.detail.ts
11421
11461
  function contextOfDetail(detail) {
11422
- return detail.summaryFields.map((field) => `${field.fieldName}\uFF1A${field.value}`).join("\uFF1B");
11462
+ const basicLines = Object.entries(detail.basicInfo ?? {}).filter(([, value]) => String(value ?? "").trim() !== "").map(([key, value]) => `${key}:${String(value).trim()}`);
11463
+ const fieldLines = (detail.formFields ?? []).map((field) => `${field.fieldName}:${field.value}`);
11464
+ return [...basicLines, ...fieldLines].join("\n");
11423
11465
  }
11424
11466
  var EMPTY_DETAIL_STATS = {
11425
11467
  requested: 0,
@@ -11431,8 +11473,6 @@ var EMPTY_DETAIL_STATS = {
11431
11473
  concurrency: 0,
11432
11474
  durationMs: 0
11433
11475
  };
11434
- var DETAIL_MATERIAL_FIELD_LIMIT = 8;
11435
- var DETAIL_MATERIAL_VALUE_LIMIT = 80;
11436
11476
  function stampsOfRows(rows) {
11437
11477
  const stamps = /* @__PURE__ */ new Map();
11438
11478
  for (const row of rows) {
@@ -11445,7 +11485,7 @@ function stampsOfRows(rows) {
11445
11485
  return stamps;
11446
11486
  }
11447
11487
  function materialFieldsOf(detail) {
11448
- return detail.summaryFields.filter((field) => field.value.length <= 200).slice(0, DETAIL_MATERIAL_FIELD_LIMIT).map((field) => ({ fieldName: field.fieldName, value: field.value.slice(0, DETAIL_MATERIAL_VALUE_LIMIT) }));
11488
+ return detail.formFields.map((field) => ({ fieldName: field.fieldName, value: field.value }));
11449
11489
  }
11450
11490
  async function fetchDetailFacts(env, requestIds, args, stamps) {
11451
11491
  const unique = [...new Set(requestIds.filter(Boolean))];
@@ -11459,13 +11499,13 @@ async function fetchDetailFacts(env, requestIds, args, stamps) {
11459
11499
  const missing = [];
11460
11500
  for (const requestId of unique) {
11461
11501
  const hit = cache.get(requestId, stamps?.get(requestId));
11462
- if (!hit) {
11502
+ if (!hit || hit.excludeTag !== excludeTagFor(hit.basicInfo.workflowName ?? "")) {
11463
11503
  missing.push(requestId);
11464
11504
  continue;
11465
11505
  }
11466
11506
  facts.set(requestId, {
11467
- context: hit.context,
11468
- contextAvailable: hit.contextAvailable,
11507
+ context: contextOfDetail({ basicInfo: hit.basicInfo, formFields: hit.fields }),
11508
+ contextAvailable: hit.fields.length > 0,
11469
11509
  metrics: hit.metrics,
11470
11510
  fields: hit.fields
11471
11511
  });
@@ -11483,7 +11523,7 @@ async function fetchDetailFacts(env, requestIds, args, stamps) {
11483
11523
  for (const [requestId, detail] of details) {
11484
11524
  const fact = {
11485
11525
  context: contextOfDetail(detail),
11486
- contextAvailable: detail.summaryFields.length > 0,
11526
+ contextAvailable: detail.formFields.length > 0,
11487
11527
  metrics: detail.metrics ?? {},
11488
11528
  fields: materialFieldsOf(detail)
11489
11529
  };
@@ -11492,10 +11532,11 @@ async function fetchDetailFacts(env, requestIds, args, stamps) {
11492
11532
  if (fact.contextAvailable) stats.contextAvailable += 1;
11493
11533
  else stats.contextEmpty += 1;
11494
11534
  if (cache.set(requestId, stamps?.get(requestId), {
11495
- context: fact.context,
11496
- contextAvailable: fact.contextAvailable,
11535
+ v: DETAIL_CACHE_ENTRY_VERSION,
11536
+ basicInfo: detail.basicInfo,
11497
11537
  metrics: fact.metrics,
11498
- fields: fact.fields
11538
+ fields: fact.fields,
11539
+ excludeTag: detail.excludeTag
11499
11540
  })) written += 1;
11500
11541
  }
11501
11542
  } catch (error) {
@@ -11976,16 +12017,32 @@ async function collectRawTodos(args, env) {
11976
12017
  });
11977
12018
  wfIdGroup = grouped.wfIdGroup ?? [];
11978
12019
  const summaryCache = await openSummaryCache(env.ctx);
11979
- const collectSummaryMaxChars = readBoundedInt(
11980
- args.summaryMaxChars,
11981
- "summaryMaxChars",
11982
- DEFAULT_REQUEST_SUMMARY_MAX,
11983
- 10,
11984
- 500
11985
- );
12020
+ const collectMaxCharsByGroup = /* @__PURE__ */ new Map();
12021
+ const collectMaxCharsFor = (workflowName) => {
12022
+ const key = workflowName ?? "";
12023
+ const hit = collectMaxCharsByGroup.get(key);
12024
+ if (hit !== void 0) return hit;
12025
+ const value = readBoundedInt(
12026
+ args.summaryMaxChars ?? activeRuleFor(key)?.summaryMaxChars,
12027
+ "summaryMaxChars",
12028
+ DEFAULT_REQUEST_SUMMARY_MAX,
12029
+ 10,
12030
+ 500
12031
+ );
12032
+ collectMaxCharsByGroup.set(key, value);
12033
+ return value;
12034
+ };
11986
12035
  const cachedSummaryIds = /* @__PURE__ */ new Set();
12036
+ const detailCacheForTag = await openDetailCache(env.ctx);
11987
12037
  for (const row of items) {
11988
- const cached = summaryCache.get(row.requestId, row.lastFeedBackDateTime, collectSummaryMaxChars);
12038
+ const detailEntry = detailCacheForTag.get(row.requestId, row.lastFeedBackDateTime);
12039
+ const cached = summaryCache.get(
12040
+ row.requestId,
12041
+ row.lastFeedBackDateTime,
12042
+ collectMaxCharsFor(row.workflowName),
12043
+ promptTagOf(activeRuleFor(row.workflowName ?? "")?.requestSummaryPrompt ?? ""),
12044
+ detailEntry ? detailMaterialTagOf(detailEntry) : void 0
12045
+ );
11989
12046
  if (!cached) continue;
11990
12047
  row.summary = cached;
11991
12048
  cachedSummaryIds.add(row.requestId);
@@ -12062,7 +12119,7 @@ async function collectRawTodos(args, env) {
12062
12119
  }
12063
12120
  if (!summaryCache.inSkillDir) {
12064
12121
  warnings.push(
12065
- "\u6CA1\u6709\u627E\u5230\u672C\u673A Agent \u7684 weaver-e10-workflow skill \u76EE\u5F55\uFF0C\u6458\u8981\u7F13\u5B58\u5DF2\u9000\u56DE CLI \u72B6\u6001\u76EE\u5F55\uFF1B\u5982\u9700\u7F13\u5B58\u843D\u5728 skill \u76EE\u5F55\uFF0C\u53EF\u7528 WEAVER_WORK_CLI_SKILL_DIR \u6307\u5B9A"
12122
+ "\u65E0\u5F53\u524D skill \u4E0A\u4E0B\u6587\uFF08WEAVER_WORK_CLI_SKILL_DIR \u672A\u8BBE\u7F6E\uFF09\uFF1A\u6458\u8981\u7F13\u5B58\u4E0E aigroup \u89C4\u5219\u5747\u672A\u8BFB\u5199\uFF0C\u672C\u6B21\u6309\u300C\u4ECE\u672A\u751F\u6210\u8FC7\u300D\u8D70\u5B9E\u65F6\u903B\u8F91\uFF1B\u8BF7\u7ECF skill \u7684 tools \u811A\u672C\u8C03\u7528"
12066
12123
  );
12067
12124
  }
12068
12125
  }
@@ -12349,12 +12406,23 @@ async function runSmartGroup(args, env, options = {}) {
12349
12406
  );
12350
12407
  const skipDetail = args.withDetailContext === false;
12351
12408
  const summaryCache = await openSummaryCache(env.ctx);
12409
+ const detailCacheForTag = skipDetail ? null : await openDetailCache(env.ctx);
12410
+ const materialTagOfRow = (requestId, stamp) => {
12411
+ const entry = detailCacheForTag?.get(requestId, stamp);
12412
+ return entry ? detailMaterialTagOf(entry) : void 0;
12413
+ };
12352
12414
  const cachedIds = /* @__PURE__ */ new Set();
12353
12415
  const allRows = [];
12354
12416
  for (const group of groups) {
12355
12417
  for (const row of group.requestDatas ?? []) {
12356
12418
  allRows.push(row);
12357
- if (summaryCache.get(row.requestId, row.lastFeedBackDateTime, summaryMaxChars)) cachedIds.add(row.requestId);
12419
+ if (summaryCache.get(
12420
+ row.requestId,
12421
+ row.lastFeedBackDateTime,
12422
+ summaryMaxChars,
12423
+ promptTagOf(activeRuleFor(group.title)?.requestSummaryPrompt ?? ""),
12424
+ materialTagOfRow(row.requestId, row.lastFeedBackDateTime)
12425
+ )) cachedIds.add(row.requestId);
12358
12426
  }
12359
12427
  }
12360
12428
  const detailIds = [...new Set(allRows.map((row) => row.requestId).filter(Boolean))];
@@ -12604,6 +12672,21 @@ async function runSmartGroup(args, env, options = {}) {
12604
12672
  function needAiSummary(workflowName) {
12605
12673
  return activeRuleFor(workflowName)?.needSummary ?? true;
12606
12674
  }
12675
+ function requestMaxCharsFor(args, workflowName) {
12676
+ return readBoundedInt(
12677
+ args.summaryMaxChars ?? activeRuleFor(workflowName)?.summaryMaxChars,
12678
+ "summaryMaxChars",
12679
+ DEFAULT_REQUEST_SUMMARY_MAX3,
12680
+ 10,
12681
+ 500
12682
+ );
12683
+ }
12684
+ function requestPromptFor(workflowName) {
12685
+ return (activeRuleFor(workflowName)?.requestSummaryPrompt ?? "").trim();
12686
+ }
12687
+ function distinctBatchMaxChars(batches) {
12688
+ return [...new Set(batches.map((batch) => batch.suggestedMaxChars))].sort((left, right) => left - right);
12689
+ }
12607
12690
  var DEFAULT_MAX_CHARS_PER_BATCH = 24e3;
12608
12691
  var DEFAULT_MAX_ITEMS_PER_BATCH = 100;
12609
12692
  var DEFAULT_REQUEST_SUMMARY_MAX3 = 50;
@@ -12774,20 +12857,35 @@ async function runSummaryContext(args, env, prefetched) {
12774
12857
  const units = collectSummaryUnits(groups);
12775
12858
  const skippedSatisfaction = groups.filter((group) => !needAiSummary(group.title)).reduce((sum, group) => sum + rowsOfGroup(group).length, 0);
12776
12859
  const requestIds = [...new Set(units.flatMap((unit) => unit.rows.map((row) => row.requestId)))].filter(Boolean);
12777
- const summaryMaxChars = readBoundedInt(
12778
- args.summaryMaxChars,
12779
- "summaryMaxChars",
12780
- DEFAULT_REQUEST_SUMMARY_MAX3,
12781
- 10,
12782
- 500
12783
- );
12784
12860
  const summaryCache = await openSummaryCache(env.ctx);
12861
+ const detailCacheForTag = await openDetailCache(env.ctx);
12862
+ const materialTagOfRow = (requestId, stamp) => {
12863
+ const entry = detailCacheForTag.get(requestId, stamp);
12864
+ return entry ? detailMaterialTagOf(entry) : void 0;
12865
+ };
12785
12866
  let prefilled = 0;
12786
12867
  const prefilledIds = /* @__PURE__ */ new Set();
12787
12868
  const needsDetail = [];
12869
+ const workflowTitleByRequest = /* @__PURE__ */ new Map();
12788
12870
  for (const unit of units) {
12871
+ if (unit.groupId === "priority") continue;
12789
12872
  for (const row of unit.rows) {
12790
- const cached = summaryCache.get(row.requestId, row.lastFeedBackDateTime, summaryMaxChars);
12873
+ if (row.requestId && !workflowTitleByRequest.has(row.requestId)) {
12874
+ workflowTitleByRequest.set(row.requestId, unit.groupTitle);
12875
+ }
12876
+ }
12877
+ }
12878
+ const ruleNameForRow = (unit, row) => unit.groupId === "priority" ? workflowTitleByRequest.get(row.requestId ?? "") ?? unit.groupTitle : unit.groupTitle;
12879
+ for (const unit of units) {
12880
+ for (const row of unit.rows) {
12881
+ const name = ruleNameForRow(unit, row);
12882
+ const cached = summaryCache.get(
12883
+ row.requestId,
12884
+ row.lastFeedBackDateTime,
12885
+ requestMaxCharsFor(args, name),
12886
+ promptTagOf(requestPromptFor(name)),
12887
+ materialTagOfRow(row.requestId, row.lastFeedBackDateTime)
12888
+ );
12791
12889
  if (cached) {
12792
12890
  row.summary = cached;
12793
12891
  prefilled += 1;
@@ -12806,13 +12904,17 @@ async function runSummaryContext(args, env, prefetched) {
12806
12904
  let contextEmpty = 0;
12807
12905
  let titleCopied = 0;
12808
12906
  const claimed = /* @__PURE__ */ new Set();
12809
- for (const unit of units) {
12907
+ const orderedUnits = [...units].sort(
12908
+ (a, b) => Number(a.groupId === "priority") - Number(b.groupId === "priority")
12909
+ );
12910
+ for (const unit of orderedUnits) {
12810
12911
  let bucket = [];
12811
12912
  let chars = 0;
12812
12913
  let seq = 0;
12813
12914
  const flush = () => {
12814
12915
  if (bucket.length === 0) return;
12815
12916
  seq += 1;
12917
+ const firstName = ruleNameForRow(unit, { requestId: bucket[0].requestId });
12816
12918
  batches.push({
12817
12919
  batchId: `${unit.subGroupId}#${seq}`,
12818
12920
  groupId: unit.groupId,
@@ -12823,7 +12925,8 @@ async function runSummaryContext(args, env, prefetched) {
12823
12925
  charCount: chars,
12824
12926
  kind: unit.kind,
12825
12927
  ...unit.kind === "internal-message" ? { hint: INTERNAL_MESSAGE_SUMMARY_HINT } : {},
12826
- suggestedMaxChars: summaryMaxChars,
12928
+ ...requestPromptFor(firstName) ? { prompt: requestPromptFor(firstName) } : {},
12929
+ suggestedMaxChars: requestMaxCharsFor(args, firstName),
12827
12930
  items: bucket
12828
12931
  });
12829
12932
  bucket = [];
@@ -12894,8 +12997,8 @@ async function runSummaryContext(args, env, prefetched) {
12894
12997
  titleCopied,
12895
12998
  /** 本地摘要命中、跳过 AI 的条数 */
12896
12999
  prefilled,
12897
- /** 单条摘要的目标字数(默认 50,可由 summaryMaxChars 覆盖) */
12898
- suggestedMaxChars: summaryMaxChars,
13000
+ /** 单条摘要的目标字数:口径一致时给数字,各组口径不同时给 distinct 列表 */
13001
+ ...distinctBatchMaxChars(batches).length === 1 ? { suggestedMaxChars: distinctBatchMaxChars(batches)[0] } : { suggestedMaxCharsList: distinctBatchMaxChars(batches) },
12899
13002
  /** 内部留言类批次:摘要要按「最低推理强度」写 */
12900
13003
  internalMessageBatches: batches.filter((batch) => batch.kind === "internal-message").length,
12901
13004
  skippedSatisfaction,
@@ -12959,16 +13062,30 @@ function readAiSummaries(value) {
12959
13062
  async function runSummary(args, env) {
12960
13063
  const groups = await readFinalGroups(args, env);
12961
13064
  const summaries = readAiSummaries(args.aiSummaries);
12962
- const summaryMaxChars = readBoundedInt(
12963
- args.summaryMaxChars,
12964
- "summaryMaxChars",
12965
- DEFAULT_REQUEST_SUMMARY_MAX3,
12966
- 10,
12967
- 500
12968
- );
13065
+ const maxCharsByGroup = /* @__PURE__ */ new Map();
13066
+ const maxCharsFor = (workflowName) => {
13067
+ const hit = maxCharsByGroup.get(workflowName);
13068
+ if (hit !== void 0) return hit;
13069
+ const value = requestMaxCharsFor(args, workflowName);
13070
+ maxCharsByGroup.set(workflowName, value);
13071
+ return value;
13072
+ };
12969
13073
  const summaryCache = await openSummaryCache(env.ctx);
13074
+ const detailCacheForTag = await openDetailCache(env.ctx);
13075
+ const materialTagOfRow = (requestId, stamp) => {
13076
+ const entry = detailCacheForTag.get(requestId, stamp);
13077
+ return entry ? detailMaterialTagOf(entry) : void 0;
13078
+ };
12970
13079
  let cacheWritten = 0;
12971
13080
  let titleSummary = 0;
13081
+ const wfTitleByRequest = /* @__PURE__ */ new Map();
13082
+ for (const group of groups) {
13083
+ if (group.topPriority) continue;
13084
+ for (const row of rowsOfGroup(group)) {
13085
+ if (row?.requestId && !wfTitleByRequest.has(row.requestId)) wfTitleByRequest.set(row.requestId, group.title);
13086
+ }
13087
+ }
13088
+ const ruleNameForGroup = (group, row) => group.topPriority ? wfTitleByRequest.get(row?.requestId ?? "") ?? group.title : group.title;
12972
13089
  for (const group of groups) {
12973
13090
  if (!needAiSummary(group.title)) {
12974
13091
  for (const row of rowsOfGroup(group)) {
@@ -12980,7 +13097,14 @@ async function runSummary(args, env) {
12980
13097
  }
12981
13098
  for (const row of rowsOfGroup(group)) {
12982
13099
  if (!row?.requestId || (row.summary ?? "").trim()) continue;
12983
- const cached = summaryCache.get(row.requestId, row.lastFeedBackDateTime, summaryMaxChars);
13100
+ const name = ruleNameForGroup(group, row);
13101
+ const cached = summaryCache.get(
13102
+ row.requestId,
13103
+ row.lastFeedBackDateTime,
13104
+ maxCharsFor(name),
13105
+ promptTagOf(requestPromptFor(name)),
13106
+ materialTagOfRow(row.requestId, row.lastFeedBackDateTime)
13107
+ );
12984
13108
  if (cached) {
12985
13109
  row.summary = cached;
12986
13110
  }
@@ -13008,7 +13132,7 @@ async function runSummary(args, env) {
13008
13132
  let titleKept = 0;
13009
13133
  let prefilledKept = 0;
13010
13134
  const countedIds = /* @__PURE__ */ new Set();
13011
- const applyAll = (rows) => {
13135
+ const applyAll = (rows, meta) => {
13012
13136
  for (const row of rows) {
13013
13137
  const summary = summaries.get(row.requestId) ?? "";
13014
13138
  const firstTime = !row.requestId || !countedIds.has(row.requestId);
@@ -13023,9 +13147,9 @@ async function runSummary(args, env) {
13023
13147
  if (summary) {
13024
13148
  if (firstTime) {
13025
13149
  filled += 1;
13026
- if (summary.length > summaryMaxChars) tooLong += 1;
13150
+ if (summary.length > meta(row).maxChars) tooLong += 1;
13027
13151
  }
13028
- if (summaryCache.set(row.requestId, row.lastFeedBackDateTime, summary, summaryMaxChars)) cacheWritten += 1;
13152
+ if (summaryCache.set(row.requestId, row.lastFeedBackDateTime, summary, meta(row).maxChars, meta(row).pk, materialTagOfRow(row.requestId, row.lastFeedBackDateTime))) cacheWritten += 1;
13029
13153
  } else if (firstTime) {
13030
13154
  empty += 1;
13031
13155
  }
@@ -13035,17 +13159,28 @@ async function runSummary(args, env) {
13035
13159
  for (const group of groups) {
13036
13160
  if (!needAiSummary(group.title)) continue;
13037
13161
  if (group.topPriority) {
13038
- applyAll(rowsOfGroup(group));
13162
+ applyAll(rowsOfGroup(group), (row) => {
13163
+ const name = ruleNameForGroup(group, row);
13164
+ return { maxChars: maxCharsFor(name), pk: promptTagOf(requestPromptFor(name)) };
13165
+ });
13039
13166
  continue;
13040
13167
  }
13041
13168
  for (const item of group.displayItems) {
13042
13169
  if (item.type === "group") {
13043
- applyAll(item.requestDatas);
13170
+ applyAll(item.requestDatas, (row) => {
13171
+ const name = ruleNameForGroup(group, row);
13172
+ return { maxChars: maxCharsFor(name), pk: promptTagOf(requestPromptFor(name)) };
13173
+ });
13044
13174
  continue;
13045
13175
  }
13046
- applyAll([item.requestData]);
13176
+ applyAll([item.requestData], (row) => {
13177
+ const name = ruleNameForGroup(group, row);
13178
+ return { maxChars: maxCharsFor(name), pk: promptTagOf(requestPromptFor(name)) };
13179
+ });
13047
13180
  }
13048
13181
  }
13182
+ const usedMaxChars = [...new Set(maxCharsByGroup.values())].sort((left, right) => left - right);
13183
+ const maxCharsHint = usedMaxChars.length > 0 ? usedMaxChars.join("/") : String(DEFAULT_REQUEST_SUMMARY_MAX3);
13049
13184
  if (empty > 0) {
13050
13185
  warnings.push(`\u6709 ${empty} \u6761\u6CA1\u6709\u62FF\u5230 AI \u6458\u8981\uFF0Csummary \u4FDD\u6301\u7A7A\u4E32\uFF08\u672A\u7528\u6807\u9898\u515C\u5E95\u3001\u672A\u7F16\u9020\uFF09`);
13051
13186
  }
@@ -13054,7 +13189,7 @@ async function runSummary(args, env) {
13054
13189
  }
13055
13190
  if (tooLong > 0) {
13056
13191
  warnings.push(
13057
- `\u6709 ${tooLong} \u6761\u6458\u8981\u8D85\u8FC7\u76EE\u6807\u957F\u5EA6 ${summaryMaxChars} \u5B57\uFF08\u53EF\u6309\u7528\u6237\u53E3\u5F84\u8C03\u6574\uFF09\uFF0C\u5DF2\u539F\u6837\u4FDD\u7559\uFF1B\u5982\u9700\u63A7\u5236\u957F\u5EA6\u8BF7\u91CD\u8DD1\u6A21\u578B\u4FA7\uFF08CLI \u4E0D\u6539\u5199\u6587\u6848\uFF09`
13192
+ `\u6709 ${tooLong} \u6761\u6458\u8981\u8D85\u8FC7\u76EE\u6807\u957F\u5EA6 ${maxCharsHint} \u5B57\uFF08\u53EF\u6309\u7528\u6237\u53E3\u5F84\u8C03\u6574\uFF09\uFF0C\u5DF2\u539F\u6837\u4FDD\u7559\uFF1B\u5982\u9700\u63A7\u5236\u957F\u5EA6\u8BF7\u91CD\u8DD1\u6A21\u578B\u4FA7\uFF08CLI \u4E0D\u6539\u5199\u6587\u6848\uFF09`
13058
13193
  );
13059
13194
  }
13060
13195
  if (prefilledKept > 0) {
@@ -13079,8 +13214,8 @@ async function runSummary(args, env) {
13079
13214
  meta: {
13080
13215
  summarized: filled,
13081
13216
  empty,
13082
- /** 单条摘要的目标字数(默认 50,可由 summaryMaxChars 覆盖) */
13083
- suggestedMaxChars: summaryMaxChars,
13217
+ /** 单条摘要的目标字数:各组口径一致时给数字,不一致时给 distinct 列表 */
13218
+ ...usedMaxChars.length === 1 ? { suggestedMaxChars: usedMaxChars[0] } : { suggestedMaxCharsList: usedMaxChars },
13084
13219
  titleCopied: titleKept,
13085
13220
  /** 本地摘要缓存命中、未重新生成的条数 */
13086
13221
  prefilled: prefilledKept,
@@ -13099,6 +13234,22 @@ async function runSummary(args, env) {
13099
13234
  var INTERNAL_MESSAGE_SUMMARY_HINT2 = "\u5185\u90E8\u7559\u8A00\uFF1A\u53EA\u6309\u539F\u6587\u76F4\u63A5\u6982\u62EC\u300C\u8C01\u8BF4\u4E86\u4EC0\u4E48\u300D\uFF0C\u4E0D\u8981\u8FC7\u5EA6\u89E3\u8BFB\u3001\u4E0D\u8981\u63A8\u6D4B\u52A8\u673A\u6216\u5F71\u54CD\uFF0C\u4E25\u683C\u6309\u672C\u6279 suggestedMaxChars \u63A7\u5236\u5B57\u6570";
13100
13235
  var GROUP_SUMMARY_TAIL = GROUP_SUMMARY_TAIL_TEXT;
13101
13236
  var DEFAULT_GROUP_SUMMARY_MAX = 100;
13237
+ function makeGroupMaxCharsResolver(args) {
13238
+ const cache = /* @__PURE__ */ new Map();
13239
+ return (workflowName) => {
13240
+ const hit = cache.get(workflowName);
13241
+ if (hit !== void 0) return hit;
13242
+ const value = readBoundedInt(
13243
+ args.groupSummaryMaxChars ?? activeRuleFor(workflowName)?.groupSummaryMaxChars,
13244
+ "groupSummaryMaxChars",
13245
+ DEFAULT_GROUP_SUMMARY_MAX,
13246
+ 20,
13247
+ 1e3
13248
+ );
13249
+ cache.set(workflowName, value);
13250
+ return value;
13251
+ };
13252
+ }
13102
13253
  var GROUP_AMOUNT_PATTERNS = [
13103
13254
  /[¥¥]\s*(\d[\d,]*(?:\.\d{1,2})?)/u,
13104
13255
  /(\d[\d,]*(?:\.\d{1,2})?)\s*元/u
@@ -13117,7 +13268,7 @@ function createrPhraseOf(creators) {
13117
13268
  if (creators.length <= 3) return `\u7531${creators.join("\u3001")} \u53D1\u8D77`;
13118
13269
  return `\u7531${creators.slice(0, 3).join("\u3001")} \u7B49 ${creators.length} \u4EBA\u53D1\u8D77`;
13119
13270
  }
13120
- function collectGroupSummaryBatches(groups, isCtxEmpty, maxChars = DEFAULT_GROUP_SUMMARY_MAX) {
13271
+ function collectGroupSummaryBatches(groups, isCtxEmpty, maxCharsFor = () => DEFAULT_GROUP_SUMMARY_MAX) {
13121
13272
  const batches = [];
13122
13273
  for (const group of groups) {
13123
13274
  if (group.topPriority) continue;
@@ -13146,6 +13297,7 @@ function collectGroupSummaryBatches(groups, isCtxEmpty, maxChars = DEFAULT_GROUP
13146
13297
  if (amount !== null) amounts.push(amount);
13147
13298
  }
13148
13299
  const amountReliable = items.length >= 2 && amounts.length === items.length;
13300
+ const groupPrompt = (activeRuleFor(group.title)?.aiGroup?.groupSummary?.prompt ?? "").trim();
13149
13301
  batches.push({
13150
13302
  groupId: sub.id,
13151
13303
  parentGroupId: group.id,
@@ -13155,7 +13307,8 @@ function collectGroupSummaryBatches(groups, isCtxEmpty, maxChars = DEFAULT_GROUP
13155
13307
  totalCount: allRows.length,
13156
13308
  kind,
13157
13309
  ...kind === "internal-message" ? { hint: INTERNAL_MESSAGE_SUMMARY_HINT2 } : {},
13158
- suggestedMaxChars: maxChars,
13310
+ ...groupPrompt ? { prompt: groupPrompt } : {},
13311
+ suggestedMaxChars: maxCharsFor(group.title),
13159
13312
  ...emptySkipped > 0 ? { emptySkipped } : {},
13160
13313
  items,
13161
13314
  stats: {
@@ -13200,14 +13353,9 @@ async function loadCtxEmptyPredicate(env, groups, args = {}) {
13200
13353
  async function runGroupSummaryContext(args, env) {
13201
13354
  const groups = await readFinalGroups(args, env);
13202
13355
  const { isCtxEmpty, warnings: emptyWarnings } = await loadCtxEmptyPredicate(env, groups, args);
13203
- const groupSummaryMaxChars = readBoundedInt(
13204
- args.groupSummaryMaxChars,
13205
- "groupSummaryMaxChars",
13206
- DEFAULT_GROUP_SUMMARY_MAX,
13207
- 20,
13208
- 1e3
13209
- );
13210
- const batches = collectGroupSummaryBatches(groups, isCtxEmpty, groupSummaryMaxChars);
13356
+ const groupMaxCharsFor = makeGroupMaxCharsResolver(args);
13357
+ const batches = collectGroupSummaryBatches(groups, isCtxEmpty, groupMaxCharsFor);
13358
+ const distinctChars = [...new Set(batches.map((batch) => batch.suggestedMaxChars))].sort((left, right) => left - right);
13211
13359
  const totalCount = totalTodoCount(groups);
13212
13360
  const warnings = [...emptyWarnings];
13213
13361
  const skippedEmpty = batches.reduce((sum, batch) => sum + (batch.emptySkipped ?? 0), 0);
@@ -13243,7 +13391,7 @@ async function runGroupSummaryContext(args, env) {
13243
13391
  /** CLI 固定文案的汇总项个数(不进批次,`smartGroup` 阶段已写好) */
13244
13392
  deterministicGroups,
13245
13393
  tail: GROUP_SUMMARY_TAIL,
13246
- suggestedMaxChars: groupSummaryMaxChars
13394
+ ...distinctChars.length === 1 ? { suggestedMaxChars: distinctChars[0] } : { suggestedMaxCharsList: distinctChars }
13247
13395
  }
13248
13396
  },
13249
13397
  warnings
@@ -13294,14 +13442,9 @@ async function runGroupSummary(args, env) {
13294
13442
  const groups = await readFinalGroups(args, env);
13295
13443
  const summaries = readAiGroupSummaries(args.aiGroupSummaries);
13296
13444
  const { isCtxEmpty, warnings: emptyWarnings } = await loadCtxEmptyPredicate(env, groups, args);
13297
- const groupSummaryMaxChars = readBoundedInt(
13298
- args.groupSummaryMaxChars,
13299
- "groupSummaryMaxChars",
13300
- DEFAULT_GROUP_SUMMARY_MAX,
13301
- 20,
13302
- 1e3
13303
- );
13304
- const batches = collectGroupSummaryBatches(groups, isCtxEmpty, groupSummaryMaxChars);
13445
+ const groupMaxCharsFor = makeGroupMaxCharsResolver(args);
13446
+ const batches = collectGroupSummaryBatches(groups, isCtxEmpty, groupMaxCharsFor);
13447
+ const distinctChars = [...new Set(batches.map((batch) => batch.suggestedMaxChars))].sort((left, right) => left - right);
13305
13448
  const knownGroupIds = new Set(batches.map((batch) => batch.groupId));
13306
13449
  for (const groupId of summaries.keys()) {
13307
13450
  if (!knownGroupIds.has(groupId)) {
@@ -13322,6 +13465,7 @@ async function runGroupSummary(args, env) {
13322
13465
  for (const group of groups) {
13323
13466
  if (group.topPriority) continue;
13324
13467
  const kind = detectBusinessKind(group.title);
13468
+ const groupMaxChars = groupMaxCharsFor(group.title);
13325
13469
  for (const sub of groupItemsOf(group)) {
13326
13470
  if (isDeterministicGroupKind(kind)) {
13327
13471
  if ((sub.summary ?? "").trim() === "") {
@@ -13333,7 +13477,7 @@ async function runGroupSummary(args, env) {
13333
13477
  }
13334
13478
  filled += 1;
13335
13479
  summarizedGroupCount += 1;
13336
- if (summary2.length > groupSummaryMaxChars) tooLong += 1;
13480
+ if (summary2.length > groupMaxChars) tooLong += 1;
13337
13481
  const batch2 = batches.find((item) => item.groupId === sub.id);
13338
13482
  if (batch2 && batch2.stats.createrPhrase && !summary2.includes(batch2.stats.createrPhrase)) {
13339
13483
  missingCreater += 1;
@@ -13352,7 +13496,7 @@ async function runGroupSummary(args, env) {
13352
13496
  }
13353
13497
  filled += 1;
13354
13498
  summarizedGroupCount += 1;
13355
- if (summary.length > groupSummaryMaxChars) tooLong += 1;
13499
+ if (summary.length > groupMaxChars) tooLong += 1;
13356
13500
  const batch = batches.find((item) => item.groupId === sub.id);
13357
13501
  if (batch && batch.stats.createrPhrase && !summary.includes(batch.stats.createrPhrase)) {
13358
13502
  missingCreater += 1;
@@ -13361,7 +13505,7 @@ async function runGroupSummary(args, env) {
13361
13505
  }
13362
13506
  if (empty > 0) warnings.push(`\u6709 ${empty} \u4E2A\u6C47\u603B\u9879\u6CA1\u6709\u62FF\u5230\u7EC4\u6458\u8981\uFF0Csummary \u4FDD\u6301\u7A7A\u4E32\uFF08\u672A\u7F16\u9020\uFF09`);
13363
13507
  if (tooLong > 0) {
13364
- warnings.push(`\u6709 ${tooLong} \u4E2A\u7EC4\u6458\u8981\u8D85\u8FC7\u5EFA\u8BAE\u7684 ${groupSummaryMaxChars} \u5B57\uFF0C\u5DF2\u539F\u6837\u4FDD\u7559\uFF08CLI \u4E0D\u6539\u5199\u6587\u6848\uFF09`);
13508
+ warnings.push(`\u6709 ${tooLong} \u4E2A\u7EC4\u6458\u8981\u8D85\u8FC7\u5EFA\u8BAE\u7684 ${distinctChars.length > 0 ? distinctChars.join("/") : DEFAULT_GROUP_SUMMARY_MAX} \u5B57\uFF0C\u5DF2\u539F\u6837\u4FDD\u7559\uFF08CLI \u4E0D\u6539\u5199\u6587\u6848\uFF09`);
13365
13509
  }
13366
13510
  if (missingCreater > 0) {
13367
13511
  warnings.push(
@@ -13465,8 +13609,10 @@ var inputSchema = schemaOf({
13465
13609
  groupFocus: { type: "string" },
13466
13610
  maxCharsPerBatch: { type: "integer", minimum: 500, maximum: 2e5, default: 24e3 },
13467
13611
  maxItemsPerBatch: { type: "integer", minimum: 1, maximum: 500, default: 100 },
13468
- summaryMaxChars: { type: "integer", minimum: 10, maximum: 500, default: 50 },
13469
- groupSummaryMaxChars: { type: "integer", minimum: 20, maximum: 1e3, default: 100 },
13612
+ // 字数口径不设 schema 默认:默认值来自 aigroup 规则(default.json 的 defaults),
13613
+ // 入参只在用户当轮明确给口径时覆盖规则(2026-10-30 用户要求「摘要规则必须从 JSON 取」)。
13614
+ summaryMaxChars: { type: "integer", minimum: 10, maximum: 500 },
13615
+ groupSummaryMaxChars: { type: "integer", minimum: 20, maximum: 1e3 },
13470
13616
  detailConcurrency: { type: "integer", minimum: 1, maximum: 32, default: 10 },
13471
13617
  detailTimeoutMs: { type: "integer", minimum: 1e3, maximum: 6e4, default: 1e4 },
13472
13618
  skillTiming: { type: "object" },