@zhushanwen/subagent-core 0.2.0 → 0.4.0

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 (195) hide show
  1. package/README.md +45 -2
  2. package/agents/analyst.md +60 -0
  3. package/agents/coder.md +69 -0
  4. package/agents/debugger.md +66 -0
  5. package/agents/doc-reviewer.md +49 -0
  6. package/agents/explorer.md +63 -0
  7. package/agents/general-purpose.md +32 -0
  8. package/agents/orchestrator.md +61 -0
  9. package/agents/planner.md +53 -0
  10. package/agents/researcher.md +67 -0
  11. package/agents/reviewer.md +73 -0
  12. package/dist/chunk-4KN4TTG7.js +240 -0
  13. package/dist/chunk-APZY4IME.js +27 -0
  14. package/dist/chunk-X7SZ5HLQ.js +66 -0
  15. package/dist/execution/engine/engines/zcode/constants.cjs +65 -0
  16. package/dist/execution/engine/engines/zcode/constants.d.cts +93 -1
  17. package/dist/execution/engine/engines/zcode/constants.d.ts +93 -1
  18. package/dist/execution/engine/engines/zcode/constants.js +43 -1
  19. package/dist/execution/engine/engines/zcode/reader.d.cts +1 -1
  20. package/dist/execution/engine/engines/zcode/reader.d.ts +1 -1
  21. package/dist/execution/engine/engines/zcode/reader.js +4 -234
  22. package/dist/execution/engine/paths.js +7 -19
  23. package/dist/index.cjs +14709 -100
  24. package/dist/index.d.cts +4053 -200
  25. package/dist/index.d.ts +4053 -200
  26. package/dist/index.js +14358 -102
  27. package/dist/{types-BxyAidGf.d.cts → types-DpUO16pj.d.cts} +464 -1
  28. package/dist/{types-BxyAidGf.d.ts → types-DpUO16pj.d.ts} +464 -1
  29. package/package.json +6 -3
  30. package/src/__tests__/review-fix-loop-script.test.ts +90 -2
  31. package/src/__tests__/review-fix-loop-utils.test.ts +21 -0
  32. package/src/__tests__/smoke.test.ts +9 -2
  33. package/src/execution/__tests__/agent-profile.test.ts +232 -0
  34. package/src/execution/__tests__/agents-assembly.test.ts +219 -0
  35. package/src/execution/__tests__/chat-engine-routing.test.ts +71 -0
  36. package/src/execution/__tests__/chatmode-first-round-closure-spawn.test.ts +1 -0
  37. package/src/execution/__tests__/create-concurrency-pool.test.ts +194 -0
  38. package/src/execution/__tests__/delivery-methods.test.ts +2 -0
  39. package/src/execution/__tests__/descendant-sweep.test.ts +269 -0
  40. package/src/execution/__tests__/dialog-queue.test.ts +199 -1
  41. package/src/execution/__tests__/epipe-fallback.test.ts +2 -0
  42. package/src/execution/__tests__/execution-runtime-face.test.ts +272 -0
  43. package/src/execution/__tests__/finalize-record.test.ts +106 -0
  44. package/src/execution/__tests__/gc-timer.test.ts +2 -0
  45. package/src/execution/__tests__/get-record-for-action-restart.test.ts +2 -0
  46. package/src/execution/__tests__/get-state-handshake.test.ts +127 -0
  47. package/src/execution/__tests__/keep-alive-no-progress.test.ts +378 -0
  48. package/src/execution/__tests__/kill-all-escalation.test.ts +196 -0
  49. package/src/execution/__tests__/lifecycle-manager-idle-timer-identity.test.ts +117 -0
  50. package/src/execution/__tests__/lifecycle-manager.test.ts +39 -0
  51. package/src/execution/__tests__/manifest-store-tmp-recovery.test.ts +99 -0
  52. package/src/execution/__tests__/max-turns-to-watchdog-ms.test.ts +113 -0
  53. package/src/execution/__tests__/notify-ledger.test.ts +294 -0
  54. package/src/execution/__tests__/record-store-orphan-revive.test.ts +159 -0
  55. package/src/execution/__tests__/record-store.test.ts +83 -0
  56. package/src/execution/__tests__/run-and-finalize-chatmode.test.ts +4 -0
  57. package/src/execution/__tests__/run-spawn-chatmode-settled.test.ts +1 -0
  58. package/src/execution/__tests__/run-spawn-edges.test.ts +407 -17
  59. package/src/execution/__tests__/run-spawn-integration.test.ts +1 -1
  60. package/src/execution/__tests__/run-spawn-stdout-callback-throw.test.ts +12 -4
  61. package/src/execution/__tests__/service-kill-escalation.test.ts +89 -0
  62. package/src/execution/__tests__/session-pending.test.ts +228 -0
  63. package/src/execution/__tests__/session-runner-branch-cache-lru.test.ts +110 -0
  64. package/src/execution/__tests__/session-runner-close-prune.test.ts +181 -0
  65. package/src/execution/__tests__/session-runner-epipe.test.ts +1 -0
  66. package/src/execution/__tests__/session-runner-heartbeat-idle-fallback.test.ts +226 -0
  67. package/src/execution/__tests__/settled-watchdog.test.ts +289 -0
  68. package/src/execution/__tests__/spawned-children.test.ts +153 -10
  69. package/src/execution/__tests__/subagent-actions-core.test.ts +997 -0
  70. package/src/execution/__tests__/subagent-service-message-close.test.ts +71 -10
  71. package/src/execution/__tests__/subagent-service-multiproc-guard.test.ts +174 -0
  72. package/src/execution/__tests__/subagent-service-notify-gate.test.ts +266 -0
  73. package/src/execution/__tests__/subagent-service-parent-guard.test.ts +3 -0
  74. package/src/execution/__tests__/subagent-service-recovery-bounds.test.ts +316 -0
  75. package/src/execution/__tests__/timeout-integration.test.ts +7 -7
  76. package/src/execution/__tests__/ui-request-handler-factory.test.ts +95 -1
  77. package/src/execution/__tests__/ui-request-queue.test.ts +25 -0
  78. package/src/execution/__tests__/worktree-git-ops.test.ts +571 -0
  79. package/src/execution/__tests__/worktree-reconcile-aging.test.ts +204 -0
  80. package/src/execution/agent-registry.ts +225 -1
  81. package/src/execution/agents-assembly.ts +88 -0
  82. package/src/execution/concurrency-pool.ts +71 -9
  83. package/src/execution/dialog-queue.ts +101 -3
  84. package/src/execution/engine/__tests__/conformance/contract.abort.test.ts +49 -2
  85. package/src/execution/engine/__tests__/conformance/contract.agent-events.test.ts +87 -1
  86. package/src/execution/engine/__tests__/conformance/engine-conformance.live.test.ts +26 -0
  87. package/src/execution/engine/__tests__/conformance/golden-replay.zcode.test.ts +83 -1
  88. package/src/execution/engine/__tests__/conformance/zcode-appserver-harness.ts +131 -0
  89. package/src/execution/engine/__tests__/registry.test.ts +90 -1
  90. package/src/execution/engine/engine-discovery.ts +12 -16
  91. package/src/execution/engine/engines/pi/task-spec-mapper.ts +3 -3
  92. package/src/execution/engine/engines/zcode/__tests__/__fixtures__/fake-appserver.mjs +274 -0
  93. package/src/execution/engine/engines/zcode/__tests__/__fixtures__/zcode-golden-appserver.json +36 -0
  94. package/src/execution/engine/engines/zcode/__tests__/connection.test.ts +472 -0
  95. package/src/execution/engine/engines/zcode/__tests__/preparer-appserver.test.ts +387 -0
  96. package/src/execution/engine/engines/zcode/__tests__/session-channel.test.ts +780 -0
  97. package/src/execution/engine/engines/zcode/__tests__/zcode-engine-appserver.test.ts +815 -0
  98. package/src/execution/engine/engines/zcode/__tests__/zcode-engine-degrade.test.ts +462 -0
  99. package/src/execution/engine/engines/zcode/__tests__/zcode-engine.live.test.ts +119 -1
  100. package/src/execution/engine/engines/zcode/__tests__/zcode-engine.test.ts +136 -6
  101. package/src/execution/engine/engines/zcode/appserver-home.ts +442 -0
  102. package/src/execution/engine/engines/zcode/appserver-probe.ts +141 -0
  103. package/src/execution/engine/engines/zcode/connection.ts +585 -0
  104. package/src/execution/engine/engines/zcode/constants.ts +129 -0
  105. package/src/execution/engine/engines/zcode/golden-sample.ts +54 -8
  106. package/src/execution/engine/engines/zcode/preparer.ts +25 -22
  107. package/src/execution/engine/engines/zcode/session-channel.ts +656 -0
  108. package/src/execution/engine/engines/zcode/zcode-engine.ts +909 -41
  109. package/src/execution/engine/host-task-spec.ts +3 -3
  110. package/src/execution/engine/port.ts +41 -1
  111. package/src/execution/engine/registry.ts +53 -0
  112. package/src/execution/finalize-record.ts +63 -5
  113. package/src/execution/get-state-handshake.ts +85 -10
  114. package/src/execution/lifecycle-manager.ts +27 -3
  115. package/src/execution/manifest-store.ts +53 -62
  116. package/src/execution/notifier.ts +10 -10
  117. package/src/execution/notify-ledger.ts +117 -16
  118. package/src/execution/record-entry.ts +8 -2
  119. package/src/execution/record-store.ts +29 -15
  120. package/src/execution/session-pending.ts +213 -62
  121. package/src/execution/session-runner.ts +974 -128
  122. package/src/execution/sessions-index.ts +10 -55
  123. package/src/execution/settled-watchdog.ts +99 -0
  124. package/src/execution/subagent-actions-core.ts +686 -0
  125. package/src/execution/subagent-service.ts +396 -49
  126. package/src/execution/ui-request-handler-factory.ts +27 -4
  127. package/src/execution/worktree-git-ops.ts +397 -0
  128. package/src/execution/worktree-manager.ts +72 -10
  129. package/src/execution/worktree-registry.ts +6 -12
  130. package/src/index.ts +413 -6
  131. package/src/orchestration/__tests__/__fixtures__/worker-template.snapshot.txt +42 -3
  132. package/src/orchestration/__tests__/agent-call-catch-fallback.test.ts +2 -4
  133. package/src/orchestration/__tests__/args-meta.test.ts +358 -0
  134. package/src/orchestration/__tests__/error-recovery-handlers.test.ts +1 -8
  135. package/src/orchestration/__tests__/error-recovery-rebuild-failure.test.ts +312 -0
  136. package/src/orchestration/__tests__/error-recovery-terminal-hardening.test.ts +382 -0
  137. package/src/orchestration/__tests__/file-run-store-prune.test.ts +168 -0
  138. package/src/orchestration/__tests__/file-run-store-throttle.test.ts +168 -0
  139. package/src/orchestration/__tests__/file-run-store.test.ts +389 -0
  140. package/src/orchestration/__tests__/helpers/flush-microtasks.ts +13 -0
  141. package/src/orchestration/__tests__/launcher-nested-workflow.test.ts +34 -0
  142. package/src/orchestration/__tests__/lifecycle-abort-broadcast-signal.test.ts +357 -0
  143. package/src/orchestration/__tests__/lifecycle-recover-crashed.test.ts +289 -0
  144. package/src/orchestration/__tests__/lifecycle.test.ts +77 -0
  145. package/src/orchestration/__tests__/run-snapshot.test.ts +353 -0
  146. package/src/orchestration/__tests__/script-generate.test.ts +314 -0
  147. package/src/orchestration/__tests__/worker-pending-timeout-abort.test.ts +275 -0
  148. package/src/orchestration/__tests__/worker-script-builder-runtime.test.ts +78 -1
  149. package/src/orchestration/__tests__/workflow-files.test.ts +186 -0
  150. package/src/orchestration/__tests__/workflow-run-summary.test.ts +119 -0
  151. package/src/orchestration/__tests__/workflow-script-registry-impl.test.ts +124 -0
  152. package/src/orchestration/agent-opts-resolver.ts +7 -7
  153. package/src/orchestration/args-meta.ts +198 -0
  154. package/src/orchestration/error-recovery.ts +416 -146
  155. package/src/orchestration/execute-agent-call.ts +9 -9
  156. package/src/orchestration/file-run-store.ts +327 -0
  157. package/src/orchestration/launcher.ts +35 -26
  158. package/src/orchestration/lifecycle.ts +355 -79
  159. package/src/orchestration/models/agent-call.ts +7 -7
  160. package/src/orchestration/models/budget.ts +5 -5
  161. package/src/orchestration/models/run-runtime.ts +13 -13
  162. package/src/orchestration/models/trace.ts +8 -8
  163. package/src/orchestration/models/workflow-run.ts +27 -27
  164. package/src/orchestration/models/workflow-script.ts +4 -4
  165. package/src/orchestration/run-snapshot.ts +266 -0
  166. package/src/orchestration/script-generate.ts +154 -0
  167. package/src/orchestration/script-lint.ts +33 -33
  168. package/src/orchestration/worker-handle.ts +10 -10
  169. package/src/orchestration/worker-host.ts +10 -10
  170. package/src/orchestration/worker-script-builder.ts +401 -346
  171. package/src/orchestration/workflow-files.ts +37 -11
  172. package/src/orchestration/workflow-run-summary.ts +69 -0
  173. package/src/orchestration/workflow-script-registry-impl.ts +31 -9
  174. package/src/shared/__tests__/agent-ref.test.ts +209 -2
  175. package/src/shared/__tests__/atomic-write.test.ts +267 -0
  176. package/src/shared/__tests__/bounded-serialize.test.ts +236 -0
  177. package/src/shared/__tests__/injection-render.test.ts +518 -0
  178. package/src/shared/__tests__/resource-discovery-host-roots.test.ts +474 -0
  179. package/src/shared/__tests__/resource-discovery.test.ts +3 -2
  180. package/src/shared/agent-ref.ts +143 -2
  181. package/src/shared/atomic-write.ts +320 -0
  182. package/src/shared/bounded-serialize.ts +154 -0
  183. package/src/shared/injection-render.ts +279 -0
  184. package/src/shared/meta-parser.ts +41 -1
  185. package/src/shared/resource-discovery.ts +104 -37
  186. package/src/shared/resource-meta.ts +14 -0
  187. package/src/shared/xml-injection.ts +9 -9
  188. package/workflows/README.md +9 -9
  189. package/workflows/chain.js +4 -2
  190. package/workflows/map-reduce.js +5 -3
  191. package/workflows/parallel.js +5 -3
  192. package/workflows/review-fix-loop-utils.cjs +14 -3
  193. package/workflows/review-fix-loop.js +18 -7
  194. package/workflows/scatter-gather.js +4 -2
  195. package/dist/chunk-3VOERJPJ.js +0 -22
@@ -0,0 +1,358 @@
1
+ // src/orchestration/__tests__/args-meta.test.ts
2
+ //
3
+ // U9(args-meta)单元测试。fixture = 既有 workflow 资产 @pi-meta parameters
4
+ // (packages/subagent-core/workflows/,经 core 自有 parseResourceMeta 解析——与
5
+ // pi-sw detectors.test.ts 同源同解析器,防 fixture 漂移)。
6
+ //
7
+ // 等值对照:PI_TOOL_TOP_LEVEL 注入后复刻 pi-sw detectors.test.ts TC3a-TC3i 场景
8
+ // (u-core-args 验收条款②「pi-sw 既有 findFlattenedArgKeys 行为等值」的锚定面)。
9
+ // 对照锚常量抄自 extensions/universal/subagent-workflow/src/interface/tool-workflow.ts
10
+ // TOOL_TOP_LEVEL(m6 定稿值)——pi-sw 侧若演进,对照测试同步刷新即暴露语义分叉。
11
+
12
+ import { readFileSync } from "node:fs";
13
+ import { dirname, join } from "node:path";
14
+ import { fileURLToPath } from "node:url";
15
+ import { describe, expect, it } from "vitest";
16
+
17
+ import { parseResourceMeta } from "../../shared/meta-parser.ts";
18
+ import {
19
+ argKeysFromMeta,
20
+ findFlattenedArgKeys,
21
+ normalizeArgsByMeta,
22
+ } from "../args-meta.ts";
23
+
24
+ const here = dirname(fileURLToPath(import.meta.url));
25
+ const WORKFLOWS_DIR = join(here, "..", "..", "..", "workflows");
26
+
27
+ /** 解析既有 workflow 资产的 @pi-meta parameters(真实资产 fixture,禁合成替代)。 */
28
+ function loadWorkflowParameters(name: string): Record<string, unknown> {
29
+ const src = readFileSync(join(WORKFLOWS_DIR, `${name}.js`), "utf8");
30
+ const meta = parseResourceMeta(src, "workflow");
31
+ if (!meta || meta.kind !== "workflow" || !meta.parameters) {
32
+ throw new Error(`fixture 损坏:${name} 无 workflow meta 或无 parameters`);
33
+ }
34
+ return meta.parameters;
35
+ }
36
+
37
+ const CHAIN_PARAMS = loadWorkflowParameters("chain");
38
+ const RFL_PARAMS = loadWorkflowParameters("review-fix-loop");
39
+ const MAP_REDUCE_PARAMS = loadWorkflowParameters("map-reduce");
40
+
41
+ /**
42
+ * pi workflow tool 顶层键集(对照锚,抄自 pi-sw tool-workflow.ts TOOL_TOP_LEVEL,
43
+ * 含 model/thinkingLevel 升格注释)——reservedKeys 宿主注入形态的等值对照基准。
44
+ */
45
+ const PI_TOOL_TOP_LEVEL = new Set([
46
+ "action",
47
+ "name",
48
+ "slug",
49
+ "runId",
50
+ "args",
51
+ "tokens",
52
+ "time",
53
+ "error",
54
+ "model",
55
+ "thinkingLevel",
56
+ ]);
57
+
58
+ /** pi 等值形态:reservedKeys 注入 pi TOOL_TOP_LEVEL 后的平铺检测(宿主注入消费位)。 */
59
+ function piFindFlattened(params: unknown, meta: Record<string, unknown>): string[] {
60
+ return findFlattenedArgKeys(params, meta, { reservedKeys: PI_TOOL_TOP_LEVEL });
61
+ }
62
+
63
+ describe("argKeysFromMeta(schema → 已知参数键集)", () => {
64
+ it("真实资产 chain.js:exact = properties 键集(task/agents)", () => {
65
+ const { exact, patterns } = argKeysFromMeta(CHAIN_PARAMS);
66
+ expect([...exact].sort()).toEqual(["agents", "task"]);
67
+ expect(patterns).toEqual([]);
68
+ });
69
+
70
+ it("真实资产 review-fix-loop.js:exact 16 键 + patternProperties 正则(^batch\\d+$)", () => {
71
+ const { exact, patterns } = argKeysFromMeta(RFL_PARAMS);
72
+ // properties 键(schema 定义序)
73
+ expect([...exact]).toEqual([
74
+ "targetType",
75
+ "target",
76
+ "autoCommit",
77
+ "maxRounds",
78
+ "stuckThreshold",
79
+ "skipCleanAgents",
80
+ "recheckAfterFix",
81
+ "fixAgent",
82
+ "maxFixAttempts",
83
+ "convergeNewIssues",
84
+ "convergeRounds",
85
+ "aggregatorModel",
86
+ "reviewPrompt",
87
+ "fixPrompt",
88
+ "fallowScan",
89
+ "agents",
90
+ "batchNames",
91
+ ]);
92
+ expect(patterns).toHaveLength(1);
93
+ expect(patterns[0].source).toBe("^batch\\d+$");
94
+ // pattern 语义:数字后缀命中、拼错不命中(m6 评审 C-1)
95
+ expect(patterns[0].test("batch1")).toBe(true);
96
+ expect(patterns[0].test("batch12")).toBe(true);
97
+ expect(patterns[0].test("batchl")).toBe(false);
98
+ });
99
+
100
+ it("空 meta(undefined / null / 非对象)→ 空键集(legacy const-meta 检测跳过)", () => {
101
+ expect(argKeysFromMeta(undefined)).toEqual({ exact: new Set(), patterns: [] });
102
+ expect(argKeysFromMeta(null)).toEqual({ exact: new Set(), patterns: [] });
103
+ expect(argKeysFromMeta("not-an-object" as unknown as Record<string, unknown>)).toEqual({
104
+ exact: new Set(),
105
+ patterns: [],
106
+ });
107
+ });
108
+
109
+ it("meta 无 properties/patternProperties(如仅 type:object)→ 空键集", () => {
110
+ const { exact, patterns } = argKeysFromMeta({ type: "object" });
111
+ expect(exact.size).toBe(0);
112
+ expect(patterns).toEqual([]);
113
+ });
114
+
115
+ it("reservedKeys 注入:撞名参数键从 exact 排除(m6 评审 M-3 撞名保护)", () => {
116
+ const synthetic = {
117
+ type: "object",
118
+ properties: { name: { type: "string" }, task: { type: "string" } },
119
+ required: ["name"],
120
+ };
121
+ const keys = argKeysFromMeta(synthetic, { reservedKeys: PI_TOOL_TOP_LEVEL });
122
+ expect(keys.exact.has("task")).toBe(true);
123
+ expect(keys.exact.has("name")).toBe(false); // pi TOOL_TOP_LEVEL 排除(M-3 真回归锁定)
124
+ // 缺省(无 reservedKeys)不排除——core 中性形态
125
+ const bare = argKeysFromMeta(synthetic);
126
+ expect(bare.exact.has("name")).toBe(true);
127
+ });
128
+
129
+ it("能命中 reservedKeys 的 pattern 整条跳过(m6 exec-review S1:^run.*$ 不误伤 runId)", () => {
130
+ const synthetic = {
131
+ type: "object",
132
+ properties: { task: { type: "string" } },
133
+ patternProperties: { "^run.*$": { type: "string" }, "^batch\\d+$": { type: "string" } },
134
+ };
135
+ const keys = argKeysFromMeta(synthetic, { reservedKeys: PI_TOOL_TOP_LEVEL });
136
+ expect(keys.patterns).toHaveLength(1);
137
+ expect(keys.patterns[0].source).toBe("^batch\\d+$");
138
+ });
139
+
140
+ it("非法 pattern 跳过不抛(双保险通路,与 pi logger.warn+跳过等值)", () => {
141
+ const synthetic = {
142
+ type: "object",
143
+ patternProperties: { "([": { type: "string" }, "^ok\\d+$": { type: "string" } },
144
+ };
145
+ const keys = argKeysFromMeta(synthetic);
146
+ expect(keys.patterns).toHaveLength(1);
147
+ expect(keys.patterns[0].source).toBe("^ok\\d+$");
148
+ });
149
+ });
150
+
151
+ describe("findFlattenedArgKeys(args 平铺检测——pi-sw 行为等值对照)", () => {
152
+ it("TC3a: trigger——chain 参数集平铺 task 被识别;跨 workflow 区分(items 非 chain 参数不触发)", () => {
153
+ expect(piFindFlattened({ action: "run", name: "chain", task: "x" }, CHAIN_PARAMS)).toEqual(["task"]);
154
+ expect(piFindFlattened({ action: "run", name: "chain", items: ["a"] }, CHAIN_PARAMS)).toEqual([]);
155
+ });
156
+
157
+ it("TC3b: 嵌套 args 不触发", () => {
158
+ expect(
159
+ piFindFlattened({ action: "run", name: "x", args: { task: "x", items: ["a"] } }, CHAIN_PARAMS),
160
+ ).toEqual([]);
161
+ });
162
+
163
+ it("TC3c: 顶层 + args 内共存不触发(args-排除保留)", () => {
164
+ expect(
165
+ piFindFlattened({ action: "run", name: "x", args: { task: "x" }, task: "y" }, CHAIN_PARAMS),
166
+ ).toEqual([]);
167
+ });
168
+
169
+ it("TC3d: 无已知键不触发;空集不触发", () => {
170
+ expect(piFindFlattened({ action: "run", name: "x", args: {} }, CHAIN_PARAMS)).toEqual([]);
171
+ expect(findFlattenedArgKeys({ action: "status" }, undefined)).toEqual([]);
172
+ });
173
+
174
+ it("TC3e: review-fix-loop fixAgent 平铺被识别(动态集 exact)", () => {
175
+ expect(
176
+ piFindFlattened({ action: "run", name: "review-fix-loop", fixAgent: "worker" }, RFL_PARAMS),
177
+ ).toEqual(["fixAgent"]);
178
+ expect(
179
+ piFindFlattened({ action: "run", name: "review-fix-loop", args: { fixAgent: "worker" } }, RFL_PARAMS),
180
+ ).toEqual([]);
181
+ });
182
+
183
+ it("TC3f: batchN 经 pattern 触发 + batchl 拼错不触发(数字后缀语义)", () => {
184
+ expect(
185
+ piFindFlattened({ action: "run", name: "review-fix-loop", targetType: "git-diff", target: "main" }, RFL_PARAMS),
186
+ ).toEqual(["targetType", "target"]);
187
+ expect(
188
+ piFindFlattened({ action: "run", name: "review-fix-loop", batch1: "reviewer", autoCommit: true }, RFL_PARAMS),
189
+ ).toEqual(["batch1", "autoCommit"]);
190
+ expect(
191
+ piFindFlattened({ action: "run", name: "review-fix-loop", args: { targetType: "git-diff", target: "main" } }, RFL_PARAMS),
192
+ ).toEqual([]);
193
+ expect(piFindFlattened({ action: "run", name: "x", batchl: "reviewer" }, RFL_PARAMS)).toEqual([]);
194
+ });
195
+
196
+ it("TC3g: 收敛参数平铺被识别(model 升格 TOOL_TOP_LEVEL 后排除)", () => {
197
+ expect(
198
+ piFindFlattened(
199
+ { action: "run", name: "review-fix-loop", model: "ds-flash", maxFixAttempts: 3, convergeNewIssues: 2, convergeRounds: 3 },
200
+ RFL_PARAMS,
201
+ ),
202
+ ).toEqual(["maxFixAttempts", "convergeNewIssues", "convergeRounds"]);
203
+ expect(
204
+ piFindFlattened(
205
+ { action: "run", name: "review-fix-loop", args: { model: "ds-flash", maxFixAttempts: 3 }, convergeRounds: 3 },
206
+ RFL_PARAMS,
207
+ ),
208
+ ).toEqual(["convergeRounds"]);
209
+ });
210
+
211
+ it("TC3h: non-object 输入返回 []", () => {
212
+ expect(findFlattenedArgKeys(null, undefined)).toEqual([]);
213
+ expect(findFlattenedArgKeys(undefined, undefined)).toEqual([]);
214
+ });
215
+
216
+ it("TC3i: tool 顶层键不误报(M-3 回归——合成 parameters 直测 reservedKeys 排除)", () => {
217
+ const synthetic = {
218
+ type: "object",
219
+ properties: { name: { type: "string" }, task: { type: "string" } },
220
+ required: ["name"],
221
+ };
222
+ expect(
223
+ piFindFlattened({ action: "run", name: "mywf", args: { name: "x" } }, synthetic),
224
+ ).toEqual([]);
225
+ // 真实 meta 不含 name(探针口径)——附加验证
226
+ expect(argKeysFromMeta(RFL_PARAMS, { reservedKeys: PI_TOOL_TOP_LEVEL }).exact.has("name")).toBe(false);
227
+ });
228
+
229
+ it("args 为非对象标量时 args-排除失效(顶层已知键全报——与 pi 谓词等值)", () => {
230
+ expect(
231
+ piFindFlattened({ action: "run", name: "chain", args: "oops", task: "x" }, CHAIN_PARAMS),
232
+ ).toEqual(["task"]);
233
+ });
234
+ });
235
+
236
+ describe("normalizeArgsByMeta(归一组装 + 警告收集)", () => {
237
+ it("正常嵌套:args 原样归一、无警告(chain 真实契约)", () => {
238
+ const r = normalizeArgsByMeta(
239
+ { action: "run", name: "chain", args: { task: "x", agents: "/a.md" } },
240
+ CHAIN_PARAMS,
241
+ { reservedKeys: PI_TOOL_TOP_LEVEL },
242
+ );
243
+ expect(r.args).toEqual({ task: "x", agents: "/a.md" });
244
+ expect(r.warnings).toEqual([]);
245
+ });
246
+
247
+ it("args 缺省归一为空对象(params.args ?? {}——pi actionRun 等值)", () => {
248
+ const r = normalizeArgsByMeta(
249
+ { action: "run", name: "chain" },
250
+ CHAIN_PARAMS,
251
+ { reservedKeys: PI_TOOL_TOP_LEVEL },
252
+ );
253
+ expect(r.args).toEqual({});
254
+ expect(r.warnings).toEqual([]);
255
+ });
256
+
257
+ it("平铺误用:flattened_args 警告含键名列表(机读 keys + 中立 message)", () => {
258
+ const r = normalizeArgsByMeta(
259
+ { action: "run", name: "chain", task: "x" },
260
+ CHAIN_PARAMS,
261
+ { reservedKeys: PI_TOOL_TOP_LEVEL },
262
+ );
263
+ expect(r.args).toEqual({}); // 平铺键不静默收编(pi 现行为是 throw,修复动作留宿主)
264
+ expect(r.warnings).toEqual([
265
+ {
266
+ code: "flattened_args",
267
+ message: "Detected task at top level — they belong inside 'args'.",
268
+ keys: ["task"],
269
+ },
270
+ ]);
271
+ });
272
+
273
+ it("平铺 + args 共存:args-排除生效,无警告(与 pi TC3c 对齐)", () => {
274
+ const r = normalizeArgsByMeta(
275
+ { action: "run", name: "x", args: { task: "x" }, task: "y" },
276
+ CHAIN_PARAMS,
277
+ { reservedKeys: PI_TOOL_TOP_LEVEL },
278
+ );
279
+ expect(r.args).toEqual({ task: "x" });
280
+ expect(r.warnings).toEqual([]);
281
+ });
282
+
283
+ it("未知键:不产生警告(检测只对已知键——类型错误归 args-validator)", () => {
284
+ const r = normalizeArgsByMeta(
285
+ { action: "run", name: "chain", unknownKey: "v" },
286
+ CHAIN_PARAMS,
287
+ { reservedKeys: PI_TOOL_TOP_LEVEL },
288
+ );
289
+ expect(r.args).toEqual({});
290
+ expect(r.warnings).toEqual([]);
291
+ });
292
+
293
+ it("空 meta:no_parameter_contract 警告 + args 仍归一(M-2 显式信号)", () => {
294
+ const r = normalizeArgsByMeta({ action: "run", name: "legacy", args: { task: "x" } }, undefined);
295
+ expect(r.args).toEqual({ task: "x" });
296
+ expect(r.warnings).toEqual([
297
+ { code: "no_parameter_contract", message: "未声明参数契约(或解析为空)——平铺检测跳过,args 不校验" },
298
+ ]);
299
+ });
300
+
301
+ it("pattern 键平铺:batch1 经 pattern 命中进警告(review-fix-loop 真实契约)", () => {
302
+ const r = normalizeArgsByMeta(
303
+ { action: "run", name: "review-fix-loop", batch1: "reviewer" },
304
+ RFL_PARAMS,
305
+ { reservedKeys: PI_TOOL_TOP_LEVEL },
306
+ );
307
+ expect(r.warnings).toEqual([
308
+ {
309
+ code: "flattened_args",
310
+ message: "Detected batch1 at top level — they belong inside 'args'.",
311
+ keys: ["batch1"],
312
+ },
313
+ ]);
314
+ });
315
+
316
+ it("reservedKeys 撞名:workflow 声明 model 参数时顶层 model 不误报(宿主键集注入生效)", () => {
317
+ const synthetic = {
318
+ type: "object",
319
+ properties: { model: { type: "string" }, task: { type: "string" } },
320
+ };
321
+ const withReserved = normalizeArgsByMeta(
322
+ { action: "run", model: "ds-flash", task: "x" },
323
+ synthetic,
324
+ { reservedKeys: PI_TOOL_TOP_LEVEL },
325
+ );
326
+ expect(withReserved.warnings[0]).toMatchObject({ code: "flattened_args", keys: ["task"] });
327
+ // 不注入 reservedKeys(core 中性形态):model 是已知参数 → 平铺报 model+task
328
+ const bare = normalizeArgsByMeta({ action: "run", model: "ds-flash", task: "x" }, synthetic);
329
+ expect(bare.warnings[0]).toMatchObject({ code: "flattened_args", keys: ["model", "task"] });
330
+ });
331
+
332
+ it("多键平铺按 Object.keys 序产出(与 pi throw 文案键序一致)", () => {
333
+ const r = normalizeArgsByMeta(
334
+ { action: "run", name: "review-fix-loop", targetType: "git-diff", target: "main" },
335
+ RFL_PARAMS,
336
+ { reservedKeys: PI_TOOL_TOP_LEVEL },
337
+ );
338
+ expect(r.warnings[0]).toMatchObject({ code: "flattened_args", keys: ["targetType", "target"] });
339
+ expect((r.warnings[0] as { message: string }).message).toBe(
340
+ "Detected targetType, target at top level — they belong inside 'args'.",
341
+ );
342
+ });
343
+
344
+ it("params 非对象:args = {},无警告(防御分支,不抛)", () => {
345
+ expect(normalizeArgsByMeta(null, CHAIN_PARAMS)).toEqual({ args: {}, warnings: [] });
346
+ expect(normalizeArgsByMeta(undefined, CHAIN_PARAMS)).toEqual({ args: {}, warnings: [] });
347
+ });
348
+
349
+ it("args 为非对象标量:原样透传(类型校验责任在 args-validator,不发明约束)", () => {
350
+ const r = normalizeArgsByMeta({ action: "run", name: "chain", args: "oops" }, CHAIN_PARAMS);
351
+ expect(r.args).toBe("oops");
352
+ });
353
+
354
+ it("map-reduce oneOf 契约:oneOf 不参与键集(properties 才是键集来源——与 pi 等值)", () => {
355
+ const { exact } = argKeysFromMeta(MAP_REDUCE_PARAMS);
356
+ expect([...exact].sort()).toEqual(["agents", "items", "itemsJson", "operation"]);
357
+ });
358
+ });
@@ -31,6 +31,7 @@ import type { AgentResult } from "../models/types.ts";
31
31
  import { WorkflowRun } from "../models/workflow-run.ts";
32
32
  import type { LifecycleDeps, WorkerHandlers } from "../models/ports.ts";
33
33
  import type { WorkerHandle } from "../worker-handle.ts";
34
+ import { flushMicrotasks } from "./helpers/flush-microtasks.ts";
34
35
 
35
36
  // ── helpers ──────────────────────────────────────────────────
36
37
 
@@ -481,14 +482,6 @@ function createDeferred<T>(): Deferred<T> {
481
482
  return { promise, resolve, reject };
482
483
  }
483
484
 
484
- /** flush 微任务队列(dispatch 的 fire-and-forget promise 链推进到稳定态)。 */
485
- async function flushMicrotasks(ticks = 10): Promise<void> {
486
- for (let i = 0; i < ticks; i++) {
487
- // eslint-disable-next-line no-await-in-loop -- 排空微任务队列的固定 tick 循环(fire-and-forget promise 链推进到稳定态),非逐项等待
488
- await Promise.resolve();
489
- }
490
- }
491
-
492
485
  /** 构造真实 WorkflowRun(真实状态机/replaceRuntime/Trace/Budget)+ 初始 RunRuntime。 */
493
486
  function makeRealRun(runId: string, opts: { budgetTimeMs?: number } = {}): WorkflowRun {
494
487
  const run = new WorkflowRun(