@zhushanwen/subagent-core 0.4.0 → 0.5.1

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 (272) hide show
  1. package/dist/{chunk-4KN4TTG7.js → chunk-43ONBFZX.js} +1 -1
  2. package/dist/{chunk-X7SZ5HLQ.js → chunk-V3VHZ2VX.js} +20 -27
  3. package/dist/execution/engine/engines/zcode/constants.cjs +29 -41
  4. package/dist/execution/engine/engines/zcode/constants.d.cts +65 -53
  5. package/dist/execution/engine/engines/zcode/constants.d.ts +65 -53
  6. package/dist/execution/engine/engines/zcode/constants.js +17 -27
  7. package/dist/execution/engine/engines/zcode/reader.d.cts +2 -1
  8. package/dist/execution/engine/engines/zcode/reader.d.ts +2 -1
  9. package/dist/execution/engine/engines/zcode/reader.js +2 -2
  10. package/dist/index.cjs +16418 -14070
  11. package/dist/index.d.cts +5136 -4016
  12. package/dist/index.d.ts +5136 -4016
  13. package/dist/index.js +16343 -14048
  14. package/dist/{types-DpUO16pj.d.cts → types-Dv4QhSJ_.d.cts} +215 -112
  15. package/dist/{types-DpUO16pj.d.ts → types-Dv4QhSJ_.d.ts} +215 -112
  16. package/package.json +3 -3
  17. package/src/__tests__/agent-opts-resolver-schema-prompt.test.ts +0 -1
  18. package/src/__tests__/append-system-prompt-assembly.test.ts +8 -7
  19. package/src/__tests__/fr4-get-state-handshake.test.ts +2 -2
  20. package/src/__tests__/m2-append-content-probe.test.ts +0 -1
  21. package/src/__tests__/manifest-store.test.ts +10 -9
  22. package/src/__tests__/record-store-cache.test.ts +0 -2
  23. package/src/__tests__/record-store-index.test.ts +1 -2
  24. package/src/__tests__/review-fix-loop-script.test.ts +4 -2
  25. package/src/__tests__/review-fix-loop-utils.test.ts +10 -10
  26. package/src/__tests__/robustness-low-batch1.test.ts +6 -5
  27. package/src/__tests__/robustness-medium-batch1.test.ts +2 -2
  28. package/src/__tests__/robustness-medium-batch2.test.ts +17 -6
  29. package/src/__tests__/robustness-medium-batch4.test.ts +2 -2
  30. package/src/__tests__/session-runner.test.ts +1 -1
  31. package/src/core/__tests__/host-services.test.ts +2 -2
  32. package/src/core/__tests__/logger.test.ts +1 -1
  33. package/src/core/error-message.ts +9 -0
  34. package/src/core/host-services.ts +21 -5
  35. package/src/core/notify-ports.ts +18 -5
  36. package/src/execution/__tests__/agent-profile.test.ts +4 -4
  37. package/src/execution/__tests__/agent-registry.test.ts +370 -0
  38. package/src/execution/__tests__/agent-result-mapper.test.ts +18 -2
  39. package/src/execution/__tests__/agents-assembly.test.ts +2 -2
  40. package/src/execution/__tests__/ask-user-transit-e2e.test.ts +6 -6
  41. package/src/execution/__tests__/chat-engine-routing.test.ts +68 -33
  42. package/src/execution/__tests__/chatmode-first-round-closure-spawn.test.ts +4 -4
  43. package/src/execution/__tests__/cold-resurrect.test.ts +283 -0
  44. package/src/execution/__tests__/delivery-methods.test.ts +75 -62
  45. package/src/execution/__tests__/descendant-sweep-guards.test.ts +220 -0
  46. package/src/execution/__tests__/descendant-sweep.test.ts +6 -6
  47. package/src/execution/__tests__/dialog-queue.test.ts +2 -2
  48. package/src/execution/__tests__/engine-model-validation.test.ts +741 -0
  49. package/src/execution/__tests__/epipe-fallback.test.ts +29 -24
  50. package/src/execution/__tests__/execute-and-await-worktree.test.ts +21 -91
  51. package/src/execution/__tests__/execute-nesting.test.ts +34 -188
  52. package/src/execution/__tests__/execution-record.test.ts +217 -60
  53. package/src/execution/__tests__/execution-runtime-face.test.ts +5 -5
  54. package/src/execution/__tests__/explicit-agent-ref-guard.test.ts +3 -3
  55. package/src/execution/__tests__/finalize-record.test.ts +173 -17
  56. package/src/execution/__tests__/gc-timer.test.ts +55 -2
  57. package/src/execution/__tests__/get-record-for-action-restart.test.ts +12 -12
  58. package/src/execution/__tests__/get-state-handshake.test.ts +1 -1
  59. package/src/execution/__tests__/helpers/mock-extension-api.ts +1 -1
  60. package/src/execution/__tests__/helpers/session-runner-mocks.ts +113 -0
  61. package/src/execution/__tests__/helpers/spawn-mock.ts +150 -3
  62. package/src/execution/__tests__/helpers/subagent-service-mocks.ts +137 -0
  63. package/src/execution/__tests__/keep-alive-no-progress.test.ts +50 -148
  64. package/src/execution/__tests__/kill-all-escalation.test.ts +2 -2
  65. package/src/execution/__tests__/lifecycle-predicates.test.ts +1 -2
  66. package/src/execution/__tests__/manifest-parentid.test.ts +4 -4
  67. package/src/execution/__tests__/max-turns-to-watchdog-ms.test.ts +1 -1
  68. package/src/execution/__tests__/model-resolver.test.ts +2 -2
  69. package/src/execution/__tests__/nested-visibility-env-propagation.test.ts +23 -121
  70. package/src/execution/__tests__/nested-visibility.test.ts +2 -0
  71. package/src/execution/__tests__/notify-ledger.test.ts +973 -18
  72. package/src/execution/__tests__/output-collector.test.ts +117 -6
  73. package/src/execution/__tests__/pi-invocation.test.ts +1 -1
  74. package/src/execution/__tests__/record-store.test.ts +10 -9
  75. package/src/execution/__tests__/records-cwd-isolation.test.ts +2 -2
  76. package/src/execution/__tests__/recursive-visibility-baseline.test.ts +29 -132
  77. package/src/execution/__tests__/recursive-visibility-env.test.ts +55 -179
  78. package/src/execution/__tests__/rpc-mode.test.ts +2 -2
  79. package/src/execution/__tests__/run-and-finalize-anchoring.test.ts +387 -0
  80. package/src/execution/__tests__/run-and-finalize-chatmode.test.ts +15 -9
  81. package/src/execution/__tests__/run-spawn-chatmode-settled.test.ts +4 -4
  82. package/src/execution/__tests__/run-spawn-edges.test.ts +17 -15
  83. package/src/execution/__tests__/run-spawn-integration.test.ts +2 -2
  84. package/src/execution/__tests__/run-spawn-resume.test.ts +2 -2
  85. package/src/execution/__tests__/run-spawn-rpc-mode.test.ts +2 -2
  86. package/src/execution/__tests__/run-spawn-stdout-callback-throw.test.ts +4 -4
  87. package/src/execution/__tests__/service-kill-escalation.test.ts +7 -5
  88. package/src/execution/__tests__/session-context-resolver.test.ts +0 -2
  89. package/src/execution/__tests__/session-file-gc.test.ts +35 -0
  90. package/src/execution/__tests__/session-pending.test.ts +257 -14
  91. package/src/execution/__tests__/session-reconstructor.test.ts +366 -15
  92. package/src/execution/__tests__/session-runner-branch-cache-lru.test.ts +1 -1
  93. package/src/execution/__tests__/session-runner-close-prune.test.ts +2 -2
  94. package/src/execution/__tests__/session-runner-dispatch.test.ts +489 -0
  95. package/src/execution/__tests__/session-runner-epipe.test.ts +3 -3
  96. package/src/execution/__tests__/session-runner-heartbeat-idle-fallback.test.ts +5 -4
  97. package/src/execution/__tests__/session-runner-lifecycle-helpers.test.ts +290 -0
  98. package/src/execution/__tests__/session-runner-schema-env.test.ts +7 -5
  99. package/src/execution/__tests__/settled-watchdog.test.ts +282 -47
  100. package/src/execution/__tests__/spawn-args.test.ts +11 -10
  101. package/src/execution/__tests__/spawn-event-adapter-rpc.test.ts +26 -23
  102. package/src/execution/__tests__/spawn-event-adapter.test.ts +1 -1
  103. package/src/execution/__tests__/spawn-worktree-guidance.test.ts +5 -4
  104. package/src/execution/__tests__/spawned-children.test.ts +2 -1
  105. package/src/execution/__tests__/start-sync-model-guard.test.ts +4 -4
  106. package/src/execution/__tests__/stdin-writer.test.ts +6 -4
  107. package/src/execution/__tests__/stream-sink-retirement.test.ts +6 -6
  108. package/src/execution/__tests__/subagent-actions-core.test.ts +42 -22
  109. package/src/execution/__tests__/subagent-service-message-close.test.ts +29 -26
  110. package/src/execution/__tests__/subagent-service-multiproc-guard.test.ts +8 -8
  111. package/src/execution/__tests__/subagent-service-notify-gate.test.ts +27 -22
  112. package/src/execution/__tests__/subagent-service-parent-guard.test.ts +30 -29
  113. package/src/execution/__tests__/subagent-service-recovery-bounds.test.ts +45 -20
  114. package/src/execution/__tests__/subagent-service.test.ts +37 -30
  115. package/src/execution/__tests__/subprocess-agent-runner-routing.test.ts +88 -12
  116. package/src/execution/__tests__/subprocess-agent-runner-timeout.test.ts +75 -0
  117. package/src/execution/__tests__/subprocess-agent-runner.test.ts +22 -20
  118. package/src/execution/__tests__/temp-prompt.test.ts +1 -1
  119. package/src/execution/__tests__/timeout-integration.test.ts +6 -5
  120. package/src/execution/__tests__/turn-limiter-semantics.test.ts +1 -1
  121. package/src/execution/__tests__/turn-limiter.test.ts +1 -1
  122. package/src/execution/__tests__/ui-request-handler-factory.test.ts +12 -6
  123. package/src/execution/__tests__/ui-request-handler.test.ts +4 -3
  124. package/src/execution/__tests__/ui-request-queue.test.ts +142 -2
  125. package/src/execution/__tests__/worktree-manager.test.ts +14 -5
  126. package/src/execution/__tests__/worktree-pid-registration.integration.test.ts +4 -3
  127. package/src/execution/agent-registry.ts +74 -30
  128. package/src/execution/agent-result-mapper.ts +3 -0
  129. package/src/execution/cold-resurrect.ts +199 -0
  130. package/src/execution/engine/__tests__/common/capability-gate.test.ts +91 -0
  131. package/src/execution/engine/__tests__/common/data-dir.test.ts +1 -1
  132. package/src/execution/engine/__tests__/common/journal-wiring.test.ts +84 -0
  133. package/src/execution/engine/__tests__/common/kill-chain.test.ts +34 -7
  134. package/src/execution/engine/__tests__/common/persona-router.test.ts +4 -5
  135. package/src/execution/engine/__tests__/common/pool-manager.test.ts +211 -51
  136. package/src/execution/engine/__tests__/common/session-view-service-zcode-dbpath.test.ts +149 -0
  137. package/src/execution/engine/__tests__/common/session-view-service.test.ts +499 -0
  138. package/src/execution/engine/__tests__/conformance/contract.abort.test.ts +22 -112
  139. package/src/execution/engine/__tests__/conformance/contract.agent-events.test.ts +15 -28
  140. package/src/execution/engine/__tests__/conformance/engine-conformance.live.test.ts +12 -12
  141. package/src/execution/engine/__tests__/conformance/golden-replay.pi.test.ts +1 -1
  142. package/src/execution/engine/__tests__/conformance/golden-replay.zcode.test.ts +21 -82
  143. package/src/execution/engine/__tests__/conformance/zcode-appserver-harness.ts +4 -7
  144. package/src/execution/engine/__tests__/engine-discovery.test.ts +15 -3
  145. package/src/execution/engine/__tests__/model-prompt.test.ts +15 -3
  146. package/src/execution/engine/__tests__/registry.test.ts +4 -2
  147. package/src/execution/engine/__tests__/routing.test.ts +115 -6
  148. package/src/execution/engine/common/capability-gate.ts +92 -0
  149. package/src/execution/engine/common/journal-wiring.ts +97 -0
  150. package/src/execution/engine/common/kill-chain.ts +42 -10
  151. package/src/execution/engine/common/nesting-guard.ts +66 -0
  152. package/src/execution/engine/common/persona-router.ts +18 -8
  153. package/src/execution/engine/common/pool-manager.ts +332 -78
  154. package/src/execution/engine/common/session-view-service.ts +506 -0
  155. package/src/execution/engine/common/session-view-types.ts +125 -0
  156. package/src/execution/engine/engines/pi/__tests__/pi-engine.test.ts +174 -28
  157. package/src/execution/engine/engines/pi/__tests__/spawn-opts-direct.test.ts +257 -0
  158. package/src/execution/{argv-mirror.ts → engine/engines/pi/argv-mirror.ts} +43 -21
  159. package/src/execution/{get-state-handshake.ts → engine/engines/pi/get-state-handshake.ts} +1 -1
  160. package/src/execution/{output-collector.ts → engine/engines/pi/output-collector.ts} +108 -19
  161. package/src/execution/engine/engines/pi/pi-engine.ts +434 -38
  162. package/src/execution/{pi-invocation.ts → engine/engines/pi/pi-invocation.ts} +2 -2
  163. package/src/execution/engine/engines/pi/reader.ts +11 -0
  164. package/src/execution/engine/engines/pi/registration.ts +4 -2
  165. package/src/execution/{session-runner.ts → engine/engines/pi/session-runner.ts} +617 -412
  166. package/src/execution/{spawn-event-adapter.ts → engine/engines/pi/spawn-event-adapter.ts} +75 -34
  167. package/src/execution/{stdin-writer.ts → engine/engines/pi/stdin-writer.ts} +10 -9
  168. package/src/execution/{temp-prompt.ts → engine/engines/pi/temp-prompt.ts} +2 -2
  169. package/src/execution/{turn-limiter.ts → engine/engines/pi/turn-limiter.ts} +1 -1
  170. package/src/execution/engine/engines/zcode/__tests__/__fixtures__/fake-appserver.mjs +54 -27
  171. package/src/execution/engine/engines/zcode/__tests__/appserver-launcher.test.ts +321 -0
  172. package/src/execution/engine/engines/zcode/__tests__/connection.test.ts +13 -12
  173. package/src/execution/engine/engines/zcode/__tests__/parser.test.ts +7 -210
  174. package/src/execution/engine/engines/zcode/__tests__/preparer.test.ts +6 -110
  175. package/src/execution/engine/engines/zcode/__tests__/session-channel-dispose-harvest.test.ts +247 -0
  176. package/src/execution/engine/engines/zcode/__tests__/session-channel-turn-timers.test.ts +451 -0
  177. package/src/execution/engine/engines/zcode/__tests__/session-channel.test.ts +20 -8
  178. package/src/execution/engine/engines/zcode/__tests__/zcode-engine-appserver.test.ts +77 -110
  179. package/src/execution/engine/engines/zcode/__tests__/zcode-engine-degrade.test.ts +25 -319
  180. package/src/execution/engine/engines/zcode/__tests__/zcode-engine-dispose.test.ts +210 -0
  181. package/src/execution/engine/engines/zcode/__tests__/zcode-engine-retry.test.ts +353 -0
  182. package/src/execution/engine/engines/zcode/__tests__/zcode-engine-status.test.ts +282 -0
  183. package/src/execution/engine/engines/zcode/__tests__/zcode-engine-timeout.test.ts +372 -0
  184. package/src/execution/engine/engines/zcode/__tests__/zcode-engine.live.test.ts +48 -148
  185. package/src/execution/engine/engines/zcode/appserver-launcher.ts +167 -0
  186. package/src/execution/engine/engines/zcode/connection.ts +24 -11
  187. package/src/execution/engine/engines/zcode/constants.ts +75 -71
  188. package/src/execution/engine/engines/zcode/golden-sample.ts +8 -46
  189. package/src/execution/engine/engines/zcode/parser.ts +12 -346
  190. package/src/execution/engine/engines/zcode/preparer.ts +12 -143
  191. package/src/execution/engine/engines/zcode/session-channel.ts +351 -39
  192. package/src/execution/engine/engines/zcode/zcode-engine.ts +665 -760
  193. package/src/execution/engine/host-task-spec.ts +32 -36
  194. package/src/execution/engine/model-validation.ts +177 -0
  195. package/src/execution/engine/port.ts +24 -6
  196. package/src/execution/engine/routing.ts +75 -1
  197. package/src/execution/engine/types.ts +30 -103
  198. package/src/execution/execution-record.ts +222 -144
  199. package/src/execution/finalize-record.ts +131 -93
  200. package/src/execution/idle-gc.ts +28 -0
  201. package/src/execution/lifecycle-predicates.ts +1 -1
  202. package/src/execution/notify-host.ts +212 -0
  203. package/src/execution/record-store.ts +119 -73
  204. package/src/execution/round-settlement.ts +93 -0
  205. package/src/execution/session-file-gc.ts +96 -62
  206. package/src/execution/session-reconstructor.ts +256 -143
  207. package/src/execution/sessions-index.ts +117 -47
  208. package/src/execution/settled-watchdog.ts +264 -52
  209. package/src/execution/subagent-actions-core.ts +22 -22
  210. package/src/execution/subagent-service.ts +815 -963
  211. package/src/execution/subprocess-agent-runner.ts +144 -97
  212. package/src/execution/types.ts +9 -0
  213. package/src/execution/ui-request-handler-factory.ts +102 -74
  214. package/src/execution/ui-request-queue.ts +50 -19
  215. package/src/index.ts +442 -316
  216. package/src/orchestration/__tests__/agent-call-catch-fallback.test.ts +1 -1
  217. package/src/orchestration/__tests__/agent-call-stream.test.ts +2 -2
  218. package/src/orchestration/__tests__/args-validator.test.ts +14 -0
  219. package/src/orchestration/__tests__/error-recovery-rebuild-failure.test.ts +1 -1
  220. package/src/orchestration/__tests__/error-recovery-terminal-hardening.test.ts +11 -9
  221. package/src/orchestration/__tests__/execute-agent-call.test.ts +151 -11
  222. package/src/orchestration/__tests__/file-run-store.test.ts +7 -6
  223. package/src/orchestration/__tests__/launcher-nested-workflow.test.ts +2 -2
  224. package/src/orchestration/__tests__/lifecycle-abort-broadcast-signal.test.ts +1 -1
  225. package/src/orchestration/__tests__/lifecycle-runid-injection.test.ts +3 -3
  226. package/src/orchestration/__tests__/lifecycle.test.ts +7 -5
  227. package/src/orchestration/__tests__/non-cloneable-return-e2e.test.ts +1 -1
  228. package/src/orchestration/__tests__/run-snapshot.test.ts +8 -5
  229. package/src/orchestration/__tests__/script-lint.test.ts +17 -0
  230. package/src/orchestration/__tests__/test-mocks.ts +2 -0
  231. package/src/orchestration/__tests__/worker-exit-without-result.test.ts +7 -6
  232. package/src/orchestration/__tests__/worker-message-pump-finalize-run.test.ts +229 -0
  233. package/src/orchestration/__tests__/{error-recovery-handlers.test.ts → worker-message-pump-handlers.test.ts} +27 -19
  234. package/src/orchestration/__tests__/{error-recovery-postmessage-defense.test.ts → worker-message-pump-postmessage-defense.test.ts} +8 -8
  235. package/src/orchestration/__tests__/{error-recovery-serialize-failed-result.test.ts → worker-message-pump-serialize-failed-result.test.ts} +2 -2
  236. package/src/orchestration/__tests__/{error-recovery-workflow-call.test.ts → worker-message-pump-workflow-call.test.ts} +1 -1
  237. package/src/orchestration/__tests__/worker-pending-timeout-abort.test.ts +1 -1
  238. package/src/orchestration/__tests__/worker-script-builder.test.ts +1 -1
  239. package/src/orchestration/__tests__/workflow-nesting-e2e.test.ts +1 -1
  240. package/src/orchestration/agent-opts-resolver.ts +1 -1
  241. package/src/orchestration/args-validator.ts +53 -31
  242. package/src/orchestration/execute-agent-call.ts +19 -73
  243. package/src/orchestration/launcher.ts +128 -81
  244. package/src/orchestration/lifecycle.ts +43 -45
  245. package/src/orchestration/models/ports.ts +4 -4
  246. package/src/orchestration/models/run-runtime.ts +2 -2
  247. package/src/orchestration/models/trace.ts +1 -1
  248. package/src/orchestration/models/types.ts +111 -22
  249. package/src/orchestration/models/workflow-run.ts +1 -1
  250. package/src/orchestration/script-lint.ts +91 -58
  251. package/src/orchestration/{error-recovery.ts → worker-message-pump.ts} +157 -150
  252. package/src/shared/__tests__/meta-parser.test.ts +151 -0
  253. package/src/shared/__tests__/resource-discovery.test.ts +258 -215
  254. package/src/shared/atomic-write.ts +12 -9
  255. package/src/shared/meta-parser.ts +158 -84
  256. package/src/shared/model-ref.ts +15 -2
  257. package/src/shared/resource-discovery.ts +9 -183
  258. package/src/shared/schema-jsonify.ts +1 -1
  259. package/workflows/review-fix-loop-utils.cjs +151 -89
  260. package/workflows/review-fix-loop-utils.d.cts +287 -0
  261. package/src/execution/__tests__/execute-options-mapper.test.ts +0 -200
  262. package/src/execution/engine/engines/pi/__tests__/task-spec-mapper.test.ts +0 -164
  263. package/src/execution/engine/engines/pi/task-spec-mapper.ts +0 -100
  264. package/src/execution/engine/engines/zcode/__tests__/__fixtures__/zcode-golden-spawn.json +0 -39
  265. package/src/execution/engine/engines/zcode/__tests__/launcher.test.ts +0 -150
  266. package/src/execution/engine/engines/zcode/__tests__/preparer-appserver.test.ts +0 -387
  267. package/src/execution/engine/engines/zcode/__tests__/zcode-engine.test.ts +0 -710
  268. package/src/execution/engine/engines/zcode/appserver-home.ts +0 -442
  269. package/src/execution/engine/engines/zcode/appserver-probe.ts +0 -141
  270. package/src/execution/engine/engines/zcode/launcher.ts +0 -161
  271. package/src/execution/execute-options-mapper.ts +0 -115
  272. package/src/shared/__tests__/resource-discovery-manifest-cache.test.ts +0 -288
@@ -1,7 +1,7 @@
1
1
  // src/execution/__tests__/subagent-service-notify-gate.test.ts
2
2
  //
3
3
  // [u-svc / T4] 通知可靠性(subagent-service 侧三措施):
4
- // - T4①/PS-2:kickOffBackground.then notify 门按 closedReason 白名单放行——
4
+ // - T4①/PS-2:kickOffChatRound.then notify 门按 closedReason 白名单放行——
5
5
  // parent-new/parent-fork 不注入(可能已切换的)新 session(A-6 决策落实);
6
6
  // - T4②/PS-4:idleTimeoutMs 非法值(>2^31-1 / 非有限值)在 spawn 入口同步 fail-fast,
7
7
  // 错误含合法范围(不静默 clamp、不静默不挂);
@@ -23,7 +23,7 @@ const { loggerMock } = vi.hoisted(() => ({
23
23
  vi.mock("../../core/logger.ts", () => ({ getLogger: () => loggerMock }));
24
24
 
25
25
  const { killChildSpy } = vi.hoisted(() => ({ killChildSpy: vi.fn() }));
26
- vi.mock("../session-runner.ts", () => ({
26
+ vi.mock("../engine/engines/pi/session-runner.ts", () => ({
27
27
  runSpawn: vi.fn(),
28
28
  killAllSpawnedChildren: vi.fn(),
29
29
  getChildByRecord: vi.fn(() => undefined),
@@ -76,7 +76,7 @@ function setup(initOverrides: Partial<{ isIdle: () => boolean }> = {}): {
76
76
  pi: MockPi;
77
77
  } {
78
78
  const agentDir = makeTmpAgentDir();
79
- const modelService = new ModelConfigService({ agentDir });
79
+ const modelService = new ModelConfigService({ agentDir, cwd: agentDir });
80
80
  const service = new SubagentService({ cwd: agentDir, modelService });
81
81
  const pi = makePi();
82
82
  service.initSession({ pi, sessionId: "root-session", isIdle: initOverrides.isIdle });
@@ -95,18 +95,19 @@ describe("T4① notify gate closedReason whitelist", () => {
95
95
  expect(notifyGateAllowsDelivery(undefined)).toBe(true);
96
96
  });
97
97
 
98
- it("kickOffBackground.then does not inject parent-new closed records into the session", async () => {
98
+ it("kickOffChatRound.then does not inject parent-new closed records into the session", async () => {
99
99
  const { agentDir, service, pi } = setup();
100
100
  const record = createRecord("sa-gate-new", {
101
101
  agent: "general-purpose",
102
102
  model: "test/model",
103
103
  mode: "background",
104
+ slug: "t",
104
105
  task: "test",
105
106
  startedAt: 1000,
106
107
  rootSessionId: "root-session",
107
108
  controller: new AbortController(),
108
109
  });
109
- // 模拟 disposeAllRecords 先行编排性关闭后,迟到的 kickOffBackground.then 回注
110
+ // 模拟 disposeAllRecords 先行编排性关闭后,迟到的 kickOffChatRound.then 回注
110
111
  record.closedReason = "parent-new";
111
112
  const stub = vi.fn().mockResolvedValue({
112
113
  text: "",
@@ -118,23 +119,27 @@ describe("T4① notify gate closedReason whitelist", () => {
118
119
  toolCalls: [],
119
120
  });
120
121
  (service as unknown as Record<string, unknown>).runAndFinalize = stub;
121
- const kickOffBackground = (
122
+ // [D4 后形态] 轮次编排入口 kickOffChatRound(私有,bracket 调用先例见
123
+ // subagent-service-recovery-bounds.test.ts privateFn);opts 形参仍是 ExecuteOptions
124
+ //(task/slug),chat 分支由 ticket lossless 携带——task 声明形参不参与校验。
125
+ const kickOffChatRound = (
122
126
  service as unknown as Record<string, (...args: unknown[]) => void>
123
- )["kickOffBackground"];
124
- kickOffBackground.call(service, record, { task: "t" }, {}, {}, undefined, 1000, undefined);
127
+ )["kickOffChatRound"];
128
+ kickOffChatRound.call(service, record, { task: "t", slug: "gate" }, {}, {}, undefined, 1000, undefined);
125
129
  await vi.waitFor(() => expect(stub).toHaveBeenCalled());
126
130
  await Promise.resolve();
127
131
  await Promise.resolve();
128
132
  expect(pi.sendMessage).not.toHaveBeenCalled();
129
- fs.rmSync(agentDir, { recursive: true, force: true });
133
+ fs.rmSync(agentDir, { recursive: true, force: true, maxRetries: 5, retryDelay: 20 });
130
134
  });
131
135
 
132
- it("kickOffBackground.then still notifies for real failure closures (gc)", async () => {
136
+ it("kickOffChatRound.then still notifies for real failure closures (gc)", async () => {
133
137
  const { agentDir, service, pi } = setup();
134
138
  const record = createRecord("sa-gate-gc", {
135
139
  agent: "general-purpose",
136
140
  model: "test/model",
137
141
  mode: "background",
142
+ slug: "t",
138
143
  task: "test",
139
144
  startedAt: 1000,
140
145
  rootSessionId: "root-session",
@@ -142,12 +147,12 @@ describe("T4① notify gate closedReason whitelist", () => {
142
147
  });
143
148
  record.closedReason = "gc";
144
149
  (service as unknown as Record<string, unknown>).runAndFinalize = vi.fn().mockResolvedValue({});
145
- const kickOffBackground = (
150
+ const kickOffChatRound = (
146
151
  service as unknown as Record<string, (...args: unknown[]) => void>
147
- )["kickOffBackground"];
148
- kickOffBackground.call(service, record, { task: "t" }, {}, {}, undefined, 1000, undefined);
152
+ )["kickOffChatRound"];
153
+ kickOffChatRound.call(service, record, { task: "t", slug: "gate" }, {}, {}, undefined, 1000, undefined);
149
154
  await vi.waitFor(() => expect(pi.sendMessage).toHaveBeenCalled());
150
- fs.rmSync(agentDir, { recursive: true, force: true });
155
+ fs.rmSync(agentDir, { recursive: true, force: true, maxRetries: 5, retryDelay: 20 });
151
156
  });
152
157
  });
153
158
 
@@ -160,30 +165,30 @@ describe("T4② idleTimeoutMs entry fail-fast", () => {
160
165
  });
161
166
 
162
167
  afterEach(() => {
163
- fs.rmSync(agentDir, { recursive: true, force: true });
168
+ fs.rmSync(agentDir, { recursive: true, force: true, maxRetries: 5, retryDelay: 20 });
164
169
  });
165
170
 
166
171
  it("execute rejects idleTimeoutMs above the setTimeout limit with the valid range in the message", async () => {
167
- await expect(service.execute({ task: "x", idleTimeoutMs: MAX_TIMER_DELAY_MS + 1 })).rejects.toThrow(
172
+ await expect(service.execute({ task: "x", slug: "test", idleTimeoutMs: MAX_TIMER_DELAY_MS + 1 })).rejects.toThrow(
168
173
  new RegExp(`${MAX_TIMER_DELAY_MS}`),
169
174
  );
170
- await expect(service.execute({ task: "x", idleTimeoutMs: 3_000_000_000 })).rejects.toThrow(/2\^31-1/);
175
+ await expect(service.execute({ task: "x", slug: "test", idleTimeoutMs: 3_000_000_000 })).rejects.toThrow(/2\^31-1/);
171
176
  });
172
177
 
173
178
  it("execute rejects non-finite idleTimeoutMs", async () => {
174
- await expect(service.execute({ task: "x", idleTimeoutMs: Number.NaN })).rejects.toThrow(/not a finite number/);
179
+ await expect(service.execute({ task: "x", slug: "test", idleTimeoutMs: Number.NaN })).rejects.toThrow(/not a finite number/);
175
180
  });
176
181
 
177
182
  it("rejects before any record is created (no side effects)", async () => {
178
183
  const internals = service as unknown as ServiceInternals;
179
- await expect(service.executeAndAwait({ task: "x", idleTimeoutMs: 3_000_000_000 })).rejects.toThrow();
184
+ await expect(service.executeAndAwait({ task: "x", slug: "test", idleTimeoutMs: 3_000_000_000 })).rejects.toThrow();
180
185
  expect(internals.store.listRunning()).toHaveLength(0);
181
186
  });
182
187
 
183
188
  it("accepts valid values (0 = explicit disable, positive within limit)", async () => {
184
189
  // 校验通过后执行链继续(runSpawn 被 mock,返回 undefined result 会在后续流程抛错/
185
190
  // 返回——但绝不能是 idleTimeoutMs 校验错误)
186
- await expect(service.execute({ task: "x", idleTimeoutMs: 0 })).rejects.not.toThrow(/idleTimeoutMs/);
191
+ await expect(service.execute({ task: "x", slug: "test", idleTimeoutMs: 0 })).rejects.not.toThrow(/idleTimeoutMs/);
187
192
  });
188
193
  });
189
194
 
@@ -199,7 +204,7 @@ describe("T4④ shutdown flush blocked → pending persisted for replay", () =>
199
204
 
200
205
  afterEach(() => {
201
206
  _resetNotifyLedgerForTest();
202
- fs.rmSync(agentDir, { recursive: true, force: true });
207
+ fs.rmSync(agentDir, { recursive: true, force: true, maxRetries: 5, retryDelay: 20 });
203
208
  });
204
209
 
205
210
  it("persists undelivered pending notifications as ledger entries on dispose when main agent is busy", () => {
@@ -256,7 +261,7 @@ describe("T4④ shutdown flush blocked → pending persisted for replay", () =>
256
261
  // idle service:flush 投出(无门拦),dispose 无复写动作
257
262
  const idleService = new SubagentService({
258
263
  cwd: agentDir,
259
- modelService: new ModelConfigService({ agentDir }),
264
+ modelService: new ModelConfigService({ agentDir, cwd: agentDir }),
260
265
  });
261
266
  const piIdle = makePi();
262
267
  idleService.initSession({ pi: piIdle, sessionId: "root-session", isIdle: () => true });
@@ -26,7 +26,7 @@ const { loggerMock } = vi.hoisted(() => ({
26
26
  vi.mock("../../core/logger.ts", () => ({ getLogger: () => loggerMock }));
27
27
 
28
28
  // mock session-runner(import 链需要 runSpawn/killAllSpawnedChildren/getChildByRecord 存在)
29
- vi.mock("../session-runner.ts", () => ({
29
+ vi.mock("../engine/engines/pi/session-runner.ts", () => ({
30
30
  runSpawn: vi.fn(),
31
31
  killAllSpawnedChildren: vi.fn(),
32
32
  getChildByRecord: vi.fn(() => undefined),
@@ -59,11 +59,12 @@ function makePi(): PiLike {
59
59
  } as unknown as PiLike;
60
60
  }
61
61
 
62
- /** 暴露私有字段供测试注入身份。 */
62
+ /** 暴露私有字段供测试注入身份。[D3-⑤] 基线住进 execNesting(公共层
63
+ * ExecutionNestingContext),注入经 setBaseline 测试面。 */
63
64
  interface ServiceInternals {
64
65
  store: RecordStore;
65
66
  sessionRootId: string | null;
66
- execCtxBaseline: { recordId: string | undefined; depth: number } | null;
67
+ execNesting: { setBaseline(state: { recordId: string | undefined; depth: number } | null): void };
67
68
  }
68
69
 
69
70
  /** 构造一个 chatMode idle record(message/close action 的典型目标态)。 */
@@ -77,12 +78,12 @@ function makeRecord(
77
78
  model: "test/model",
78
79
  mode: "background",
79
80
  task: "test",
80
- slug: "test",
81
81
  startedAt: 1000,
82
82
  rootSessionId: sessionRootId,
83
83
  chatMode: true,
84
84
  controller: new AbortController(),
85
85
  ...overrides,
86
+ slug: overrides.slug ?? "test",
86
87
  });
87
88
  }
88
89
 
@@ -95,7 +96,7 @@ describe("[v4 A-5 / P7] getRecordForAction 直接父校验", () => {
95
96
 
96
97
  beforeEach(() => {
97
98
  agentDir = makeTmpAgentDir();
98
- const modelService = new ModelConfigService({ agentDir });
99
+ const modelService = new ModelConfigService({ agentDir, cwd: agentDir });
99
100
  service = new SubagentService({ cwd: agentDir, modelService });
100
101
  // initSession 建立 sessionRootId=sessionId(本 session 树的根)。execCtxBaseline 的
101
102
  // 真实值由 initSession 从 env 读取(见 recursive-visibility-baseline.test.ts),但本测试
@@ -108,65 +109,65 @@ describe("[v4 A-5 / P7] getRecordForAction 直接父校验", () => {
108
109
 
109
110
  afterEach(() => {
110
111
  service.dispose();
111
- fs.rmSync(agentDir, { recursive: true, force: true });
112
+ fs.rmSync(agentDir, { recursive: true, force: true, maxRetries: 5, retryDelay: 20 });
112
113
  });
113
114
 
114
115
  // ---------- 身份 sa-A:直接父,放行孙级 B ----------
115
116
 
116
117
  it("身份=sa-A(直接父)→ 放行操作孙级 record B(parentRecordId=sa-A)", () => {
117
- const recordB = makeRecord("sa-B", sessionRootId, { parentRecordId: "sa-A", depth: 2, status: "idle" });
118
+ const recordB = makeRecord("sa-B", sessionRootId, { parentRecordId: "sa-A", depth: 2, status: "running" });
118
119
  store.register(recordB);
119
- internals.execCtxBaseline = PARENT_A;
120
+ internals.execNesting.setBaseline(PARENT_A);
120
121
 
121
- expect(service.getRecordForAction("sa-B")).toBe(recordB);
122
+ expect(service.chatActions.getRecordForAction("sa-B")).toBe(recordB);
122
123
  });
123
124
 
124
125
  // ---------- 身份 undefined(主进程):拒绝孙级,放行根层 ----------
125
126
 
126
127
  it("身份=undefined(主进程)拒绝孙级 record B,错误含 'direct parent' + 'parent=sa-A'", () => {
127
- const recordB = makeRecord("sa-B", sessionRootId, { parentRecordId: "sa-A", depth: 2, status: "idle" });
128
+ const recordB = makeRecord("sa-B", sessionRootId, { parentRecordId: "sa-A", depth: 2, status: "running" });
128
129
  store.register(recordB);
129
- internals.execCtxBaseline = MAIN_PROCESS;
130
+ internals.execNesting.setBaseline(MAIN_PROCESS);
130
131
 
131
- expect(() => service.getRecordForAction("sa-B")).toThrow(/direct parent/);
132
- expect(() => service.getRecordForAction("sa-B")).toThrow(/see \/subagents list, parent=sa-A/);
132
+ expect(() => service.chatActions.getRecordForAction("sa-B")).toThrow(/direct parent/);
133
+ expect(() => service.chatActions.getRecordForAction("sa-B")).toThrow(/see \/subagents list, parent=sa-A/);
133
134
  });
134
135
 
135
136
  it("身份=undefined(主进程)放行顶层 record(parentRecordId=undefined 视为根层)", () => {
136
- const topRecord = makeRecord("sa-top", sessionRootId, { parentRecordId: undefined, depth: 0, status: "idle" });
137
+ const topRecord = makeRecord("sa-top", sessionRootId, { parentRecordId: undefined, depth: 0, status: "running" });
137
138
  store.register(topRecord);
138
- internals.execCtxBaseline = MAIN_PROCESS;
139
+ internals.execNesting.setBaseline(MAIN_PROCESS);
139
140
 
140
- expect(service.getRecordForAction("sa-top")).toBe(topRecord);
141
+ expect(service.chatActions.getRecordForAction("sa-top")).toBe(topRecord);
141
142
  });
142
143
 
143
144
  // ---------- 身份 sa-B(兄弟进程):拒绝孙级 ----------
144
145
 
145
146
  it("身份=sa-B(兄弟进程)拒绝孙级 record B(parentRecordId=sa-A ≠ baseline sa-B)", () => {
146
- const recordB = makeRecord("sa-B", sessionRootId, { parentRecordId: "sa-A", depth: 2, status: "idle" });
147
+ const recordB = makeRecord("sa-B", sessionRootId, { parentRecordId: "sa-A", depth: 2, status: "running" });
147
148
  store.register(recordB);
148
- internals.execCtxBaseline = SIBLING_B;
149
+ internals.execNesting.setBaseline(SIBLING_B);
149
150
 
150
- expect(() => service.getRecordForAction("sa-B")).toThrow(/direct parent/);
151
- expect(() => service.getRecordForAction("sa-B")).toThrow(/see \/subagents list, parent=sa-A/);
151
+ expect(() => service.chatActions.getRecordForAction("sa-B")).toThrow(/direct parent/);
152
+ expect(() => service.chatActions.getRecordForAction("sa-B")).toThrow(/see \/subagents list, parent=sa-A/);
152
153
  });
153
154
 
154
155
  // ---------- 身份缺省语义 + 更深递归 ----------
155
156
 
156
157
  it("子进程(baseline=sa-A)拒绝顶层 record(身份缺省视为根层,仅主进程可操作)", () => {
157
- const topRecord = makeRecord("sa-top", sessionRootId, { parentRecordId: undefined, depth: 0, status: "idle" });
158
+ const topRecord = makeRecord("sa-top", sessionRootId, { parentRecordId: undefined, depth: 0, status: "running" });
158
159
  store.register(topRecord);
159
- internals.execCtxBaseline = PARENT_A;
160
+ internals.execNesting.setBaseline(PARENT_A);
160
161
 
161
- expect(() => service.getRecordForAction("sa-top")).toThrow(/direct parent/);
162
+ expect(() => service.chatActions.getRecordForAction("sa-top")).toThrow(/direct parent/);
162
163
  });
163
164
 
164
165
  it("P7 场景:主进程拒绝曾孙 record C(parentRecordId=sa-B),错误含 baseline=root + parent=sa-B", () => {
165
- const recordC = makeRecord("sa-C", sessionRootId, { parentRecordId: "sa-B", depth: 3, status: "idle" });
166
+ const recordC = makeRecord("sa-C", sessionRootId, { parentRecordId: "sa-B", depth: 3, status: "running" });
166
167
  store.register(recordC);
167
- internals.execCtxBaseline = MAIN_PROCESS;
168
+ internals.execNesting.setBaseline(MAIN_PROCESS);
168
169
 
169
- const fn = () => service.getRecordForAction("sa-C");
170
+ const fn = () => service.chatActions.getRecordForAction("sa-C");
170
171
  expect(fn).toThrow(/direct parent/);
171
172
  expect(fn).toThrow(/see \/subagents list, parent=sa-B/);
172
173
  expect(fn).toThrow(/baseline=\(root\)/);
@@ -174,10 +175,10 @@ describe("[v4 A-5 / P7] getRecordForAction 直接父校验", () => {
174
175
 
175
176
  it("rootSessionId 不匹配仍优先拒绝(直接父校验在 rootSessionId 校验之后,不绕过归属守卫)", () => {
176
177
  // 跨 session 树的 record:rootSessionId 不匹配 → 走首个 throw(not found or not owned)
177
- const foreignRecord = makeRecord("sa-foreign", "other-session", { parentRecordId: "sa-A", depth: 2, status: "idle" });
178
+ const foreignRecord = makeRecord("sa-foreign", "other-session", { parentRecordId: "sa-A", depth: 2, status: "running" });
178
179
  store.register(foreignRecord);
179
- internals.execCtxBaseline = PARENT_A;
180
+ internals.execNesting.setBaseline(PARENT_A);
180
181
 
181
- expect(() => service.getRecordForAction("sa-foreign")).toThrow(/not found or not owned/);
182
+ expect(() => service.chatActions.getRecordForAction("sa-foreign")).toThrow(/not found or not owned/);
182
183
  });
183
184
  });
@@ -5,9 +5,12 @@
5
5
  // 收敛到 killRecordChildWithEscalation(spy 断言调用点与参数);
6
6
  // - T2⑥/PS-1:disposeAllRecords 补三回收面(controller.abort + kill + disarm idle timer
7
7
  // + disarm settled watchdog);
8
- // - T2③/LC-1:deliverMessage 热路径 armSettledWatchdog(挂载 + 到期 onTimeout 处置:
9
- // kill + 失败终态化 + error 含 'settled watchdog' 标记与恢复指引);
10
- // - T2⑧/PS-3:deliverMessage EPIPE 失败 re-arm idle timer(进程不再裸奔)。
8
+ // - T2③/LC-1 + D9:热路径投递 armMidRoundNoProgress(挂载点 D2 下沉后在编排层
9
+ // deliverChatMessage interact 返回点;挂载中段无进展检测 + 到期 onMidTimeout/
10
+ // onSettleTimeout 处置:kill + 失败终态化 + error 'settled watchdog' 标记与恢复
11
+ // 指引;agent_end 交棒收尾段的接线在 session-runner stdout pump,两路径共用);
12
+ // - T2⑧/PS-3:非 EPIPE 热路径写失败 re-arm idle timer(进程不再裸奔;挂载位 D2
13
+ // 下沉后在 PiEngine.deliverPrompt 的非 EPIPE catch——编排层无法区分错误类别)。
11
14
  //
12
15
  // mock 形态沿用 subagent-service-message-close.test.ts(真实 SubagentService + ServiceInternals
13
16
  // cast 暴露 store + mock session-runner/stdin-writer/logger)。
@@ -24,8 +27,8 @@ const { loggerMock } = vi.hoisted(() => ({
24
27
  vi.mock("../../core/logger.ts", () => ({ getLogger: () => loggerMock }));
25
28
 
26
29
  // killRecordChildWithEscalation 换 spy(收敛调用点断言);spawnedChildren 用真 Map
27
- //(deliverMessage 热路径判定 / EPIPE 兜底按值删除的消费者);getChildByRecord 从同
28
- // Map 查(mock 工厂与测试共享同一实例)。
30
+ //(热路径投递判定 / EPIPE 兜底按值删除的消费者);getChildByRecord 从同一 Map 查
31
+ //(mock 工厂与测试共享同一实例)。D2 后模块物理下沉 engines/pi/(mock 路径随迁)。
29
32
  const { killChildSpy, spawnedMap, getChildByRecordMock } = vi.hoisted(() => {
30
33
  const map = new Map<string, unknown>();
31
34
  return {
@@ -34,7 +37,7 @@ const { killChildSpy, spawnedMap, getChildByRecordMock } = vi.hoisted(() => {
34
37
  getChildByRecordMock: vi.fn((recordId: string) => map.get(recordId)),
35
38
  };
36
39
  });
37
- vi.mock("../session-runner.ts", () => ({
40
+ vi.mock("../engine/engines/pi/session-runner.ts", () => ({
38
41
  runSpawn: vi.fn(),
39
42
  killAllSpawnedChildren: vi.fn(),
40
43
  getChildByRecord: getChildByRecordMock,
@@ -45,7 +48,7 @@ vi.mock("../session-runner.ts", () => ({
45
48
 
46
49
  // sendPromptCommand 换 spy(控制热路径写成功 / 抛非 EPIPE 错误)。
47
50
  const { sendPromptCommandMock } = vi.hoisted(() => ({ sendPromptCommandMock: vi.fn() }));
48
- vi.mock("../stdin-writer.ts", () => ({
51
+ vi.mock("../engine/engines/pi/stdin-writer.ts", () => ({
49
52
  sendPromptCommand: sendPromptCommandMock,
50
53
  clearEpipeFailure: vi.fn(),
51
54
  recordEpipeFailure: vi.fn(() => 1),
@@ -59,7 +62,7 @@ import { ModelConfigService } from "../model-config-service.ts";
59
62
  import type { RecordStore } from "../record-store.ts";
60
63
  import { SubagentService } from "../subagent-service.ts";
61
64
  import type { PiLike } from "../subagent-service.ts";
62
- import { armSettledWatchdog, hasSettledWatchdog, SETTLED_WATCHDOG_TIMEOUT_MS, _resetSettledWatchdogsForTest } from "../settled-watchdog.ts";
65
+ import { armSettledWatchdog, hasSettledWatchdog, SETTLED_MID_ROUND_NO_PROGRESS_MS, _resetSettledWatchdogsForTest } from "../settled-watchdog.ts";
63
66
  import { armIdleTimer, hasIdleTimer, _resetLifecycleState } from "../lifecycle-manager.ts";
64
67
  import type { ExecutionRecord } from "../types.ts";
65
68
 
@@ -108,7 +111,7 @@ type MockPi = ReturnType<typeof makePi>;
108
111
 
109
112
  function setup(): { agentDir: string; service: SubagentService; store: RecordStore; pi: MockPi } {
110
113
  const agentDir = makeTmpAgentDir();
111
- const modelService = new ModelConfigService({ agentDir });
114
+ const modelService = new ModelConfigService({ agentDir, cwd: agentDir });
112
115
  const service = new SubagentService({ cwd: agentDir, modelService });
113
116
  const pi = makePi();
114
117
  service.initSession({ pi, sessionId: "root-session" });
@@ -121,6 +124,26 @@ function privateFn<K extends string>(service: SubagentService, key: K): (...args
121
124
  return (service as unknown as Record<string, (...args: never[]) => unknown>)[key];
122
125
  }
123
126
 
127
+ /**
128
+ * message 投递入口(D2 后形态:编排层私有 deliverChatMessage → PiEngine.interactRecord
129
+ * → deliverPrompt;旧直调形态 deliverMessage 已随协议知识下沉删除)。
130
+ */
131
+ async function deliverChat(
132
+ service: SubagentService,
133
+ record: ExecutionRecord,
134
+ text: string,
135
+ interrupt: boolean,
136
+ ): Promise<void> {
137
+ return (
138
+ privateFn(service, "deliverChatMessage") as (
139
+ this: SubagentService,
140
+ r: ExecutionRecord,
141
+ t: string,
142
+ i: boolean,
143
+ ) => Promise<void>
144
+ ).call(service, record, text, interrupt);
145
+ }
146
+
124
147
  describe("T2④ service-side kill convergence", () => {
125
148
  let agentDir: string;
126
149
  let service: SubagentService;
@@ -138,7 +161,7 @@ describe("T2④ service-side kill convergence", () => {
138
161
  _resetLifecycleState();
139
162
  _resetSettledWatchdogsForTest();
140
163
  spawnedMap.clear();
141
- fs.rmSync(agentDir, { recursive: true, force: true });
164
+ fs.rmSync(agentDir, { recursive: true, force: true, maxRetries: 5, retryDelay: 20 });
142
165
  });
143
166
 
144
167
  it("cancelBackground (via cancel) routes through killRecordChildWithEscalation", async () => {
@@ -156,7 +179,7 @@ describe("T2④ service-side kill convergence", () => {
156
179
  spawnedMap.set(record.id, new FakeChild());
157
180
  armIdleTimer(record.id, () => {}); // Path A:idle timer armed(进程保活)
158
181
  armSettledWatchdog(record.id, () => {});
159
- await service.closeSubagent(record, false);
182
+ await service["closeSubagent"](record, false);
160
183
  expect(killChildSpy).toHaveBeenCalledWith(record.id, "closeChatIdle");
161
184
  expect(hasIdleTimer(record.id)).toBe(false);
162
185
  expect(hasSettledWatchdog(record.id)).toBe(false);
@@ -223,28 +246,30 @@ describe("T2③ hot-path settled watchdog", () => {
223
246
  _resetLifecycleState();
224
247
  _resetSettledWatchdogsForTest();
225
248
  spawnedMap.clear();
226
- fs.rmSync(agentDir, { recursive: true, force: true });
249
+ fs.rmSync(agentDir, { recursive: true, force: true, maxRetries: 5, retryDelay: 20 });
227
250
  });
228
251
 
229
252
  it("arms the settled watchdog after a successful hot-path prompt", async () => {
230
253
  const record = makeRecord({ id: "sa-hot-arm" });
231
254
  store.register(record);
232
255
  spawnedMap.set(record.id, new FakeChild());
233
- await service.deliverMessage(record, "hello", false);
256
+ await deliverChat(service, record, "hello", false);
234
257
  expect(sendPromptCommandMock).toHaveBeenCalledTimes(1);
235
258
  expect(hasSettledWatchdog(record.id)).toBe(true);
236
259
  });
237
260
 
238
- it("onTimeout: kills the child, fails the round, error carries 'settled watchdog' marker and recovery hint", async () => {
261
+ it("onMidTimeout: kills the child, fails the round, error carries 'settled watchdog' marker and recovery hint", async () => {
239
262
  // fake timers 必须先于 arm 生效(useFakeTimers 不接管已存在的真实 timer)
240
263
  vi.useFakeTimers({ toFake: ["setTimeout", "clearTimeout"] });
241
264
  const record = makeRecord({ id: "sa-hot-timeout" });
242
265
  store.register(record);
243
266
  spawnedMap.set(record.id, new FakeChild());
244
- await service.deliverMessage(record, "hello", false);
267
+ await deliverChat(service, record, "hello", false);
245
268
  expect(hasSettledWatchdog(record.id)).toBe(true);
246
269
 
247
- await vi.advanceTimersByTimeAsync(SETTLED_WATCHDOG_TIMEOUT_MS + 1);
270
+ // [D9 两段式] 热路径 prompt 发出后挂中段:静默满中段窗长触发(本测试 mock 了
271
+ // session-runner,无 stdout pump 驱动事件刷新/交棒,中段静默形态直达)
272
+ await vi.advanceTimersByTimeAsync(SETTLED_MID_ROUND_NO_PROGRESS_MS + 1);
248
273
 
249
274
  // kill 收敛入口被触发(onTimeout 第一步)
250
275
  expect(killChildSpy).toHaveBeenCalledWith(record.id, "settled watchdog (hot path)");
@@ -278,7 +303,7 @@ describe("T2⑧ non-EPIPE hot-path failure re-arms idle timer", () => {
278
303
  _resetLifecycleState();
279
304
  _resetSettledWatchdogsForTest();
280
305
  spawnedMap.clear();
281
- fs.rmSync(agentDir, { recursive: true, force: true });
306
+ fs.rmSync(agentDir, { recursive: true, force: true, maxRetries: 5, retryDelay: 20 });
282
307
  });
283
308
 
284
309
  it("re-arms the idle timer (default duration) and rethrows on non-EPIPE write failure", async () => {
@@ -289,7 +314,7 @@ describe("T2⑧ non-EPIPE hot-path failure re-arms idle timer", () => {
289
314
  throw new Error("write after end: ERR_STREAM_DESTROYED");
290
315
  });
291
316
 
292
- await expect(service.deliverMessage(record, "hello", false)).rejects.toThrow(/ERR_STREAM_DESTROYED/);
317
+ await expect(deliverChat(service, record, "hello", false)).rejects.toThrow(/ERR_STREAM_DESTROYED/);
293
318
 
294
319
  // 防泄漏前提恢复:入口 disarm 过的 idle timer 被 re-arm(进程不再裸奔)
295
320
  expect(hasIdleTimer(record.id)).toBe(true);
@@ -303,14 +328,14 @@ describe("T2⑧ non-EPIPE hot-path failure re-arms idle timer", () => {
303
328
  const record = makeRecord({ id: "sa-nonpipe-bad" });
304
329
  // 直接注入非法值模拟「入口校验前已存在的 record」/ 回归形态:re-arm 首选值非法 →
305
330
  // 降级挂 DEFAULT(防御性兜底可见,不静默不挂)
306
- record.idleTimeoutMs = 3_000_000_000;
331
+ (record as { idleTimeoutMs: number }).idleTimeoutMs = 3_000_000_000;
307
332
  store.register(record);
308
333
  spawnedMap.set(record.id, new FakeChild());
309
334
  sendPromptCommandMock.mockImplementation(() => {
310
335
  throw new Error("ERR_STREAM_DESTROYED");
311
336
  });
312
337
 
313
- await expect(service.deliverMessage(record, "hello", false)).rejects.toThrow(/ERR_STREAM_DESTROYED/);
338
+ await expect(deliverChat(service, record, "hello", false)).rejects.toThrow(/ERR_STREAM_DESTROYED/);
314
339
  expect(hasIdleTimer(record.id)).toBe(true);
315
340
  });
316
341
  });