@zhushanwen/subagent-core 0.2.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 (347) 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-43ONBFZX.js +240 -0
  13. package/dist/chunk-APZY4IME.js +27 -0
  14. package/dist/chunk-V3VHZ2VX.js +59 -0
  15. package/dist/execution/engine/engines/zcode/constants.cjs +62 -9
  16. package/dist/execution/engine/engines/zcode/constants.d.cts +116 -12
  17. package/dist/execution/engine/engines/zcode/constants.d.ts +116 -12
  18. package/dist/execution/engine/engines/zcode/constants.js +39 -7
  19. package/dist/execution/engine/engines/zcode/reader.d.cts +2 -1
  20. package/dist/execution/engine/engines/zcode/reader.d.ts +2 -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 +18234 -1277
  24. package/dist/index.d.cts +5646 -673
  25. package/dist/index.d.ts +5646 -673
  26. package/dist/index.js +17828 -1277
  27. package/dist/{types-BxyAidGf.d.cts → types-Dv4QhSJ_.d.cts} +678 -112
  28. package/dist/{types-BxyAidGf.d.ts → types-Dv4QhSJ_.d.ts} +678 -112
  29. package/package.json +7 -4
  30. package/src/__tests__/agent-opts-resolver-schema-prompt.test.ts +0 -1
  31. package/src/__tests__/append-system-prompt-assembly.test.ts +8 -7
  32. package/src/__tests__/fr4-get-state-handshake.test.ts +2 -2
  33. package/src/__tests__/m2-append-content-probe.test.ts +0 -1
  34. package/src/__tests__/manifest-store.test.ts +10 -9
  35. package/src/__tests__/record-store-cache.test.ts +0 -2
  36. package/src/__tests__/record-store-index.test.ts +1 -2
  37. package/src/__tests__/review-fix-loop-script.test.ts +93 -3
  38. package/src/__tests__/review-fix-loop-utils.test.ts +31 -10
  39. package/src/__tests__/robustness-low-batch1.test.ts +6 -5
  40. package/src/__tests__/robustness-medium-batch1.test.ts +2 -2
  41. package/src/__tests__/robustness-medium-batch2.test.ts +17 -6
  42. package/src/__tests__/robustness-medium-batch4.test.ts +2 -2
  43. package/src/__tests__/session-runner.test.ts +1 -1
  44. package/src/__tests__/smoke.test.ts +9 -2
  45. package/src/core/__tests__/host-services.test.ts +2 -2
  46. package/src/core/__tests__/logger.test.ts +1 -1
  47. package/src/core/error-message.ts +9 -0
  48. package/src/core/host-services.ts +21 -5
  49. package/src/core/notify-ports.ts +18 -5
  50. package/src/execution/__tests__/agent-profile.test.ts +232 -0
  51. package/src/execution/__tests__/agent-registry.test.ts +370 -0
  52. package/src/execution/__tests__/agent-result-mapper.test.ts +18 -2
  53. package/src/execution/__tests__/agents-assembly.test.ts +219 -0
  54. package/src/execution/__tests__/ask-user-transit-e2e.test.ts +6 -6
  55. package/src/execution/__tests__/chat-engine-routing.test.ts +136 -30
  56. package/src/execution/__tests__/chatmode-first-round-closure-spawn.test.ts +5 -4
  57. package/src/execution/__tests__/cold-resurrect.test.ts +283 -0
  58. package/src/execution/__tests__/create-concurrency-pool.test.ts +194 -0
  59. package/src/execution/__tests__/delivery-methods.test.ts +77 -62
  60. package/src/execution/__tests__/descendant-sweep-guards.test.ts +220 -0
  61. package/src/execution/__tests__/descendant-sweep.test.ts +269 -0
  62. package/src/execution/__tests__/dialog-queue.test.ts +199 -1
  63. package/src/execution/__tests__/engine-model-validation.test.ts +741 -0
  64. package/src/execution/__tests__/epipe-fallback.test.ts +31 -24
  65. package/src/execution/__tests__/execute-and-await-worktree.test.ts +21 -91
  66. package/src/execution/__tests__/execute-nesting.test.ts +34 -188
  67. package/src/execution/__tests__/execution-record.test.ts +217 -60
  68. package/src/execution/__tests__/execution-runtime-face.test.ts +272 -0
  69. package/src/execution/__tests__/explicit-agent-ref-guard.test.ts +3 -3
  70. package/src/execution/__tests__/finalize-record.test.ts +278 -16
  71. package/src/execution/__tests__/gc-timer.test.ts +57 -2
  72. package/src/execution/__tests__/get-record-for-action-restart.test.ts +14 -12
  73. package/src/execution/__tests__/get-state-handshake.test.ts +127 -0
  74. package/src/execution/__tests__/helpers/mock-extension-api.ts +1 -1
  75. package/src/execution/__tests__/helpers/session-runner-mocks.ts +113 -0
  76. package/src/execution/__tests__/helpers/spawn-mock.ts +150 -3
  77. package/src/execution/__tests__/helpers/subagent-service-mocks.ts +137 -0
  78. package/src/execution/__tests__/keep-alive-no-progress.test.ts +280 -0
  79. package/src/execution/__tests__/kill-all-escalation.test.ts +196 -0
  80. package/src/execution/__tests__/lifecycle-manager-idle-timer-identity.test.ts +117 -0
  81. package/src/execution/__tests__/lifecycle-manager.test.ts +39 -0
  82. package/src/execution/__tests__/lifecycle-predicates.test.ts +1 -2
  83. package/src/execution/__tests__/manifest-parentid.test.ts +4 -4
  84. package/src/execution/__tests__/manifest-store-tmp-recovery.test.ts +99 -0
  85. package/src/execution/__tests__/max-turns-to-watchdog-ms.test.ts +113 -0
  86. package/src/execution/__tests__/model-resolver.test.ts +2 -2
  87. package/src/execution/__tests__/nested-visibility-env-propagation.test.ts +23 -121
  88. package/src/execution/__tests__/nested-visibility.test.ts +2 -0
  89. package/src/execution/__tests__/notify-ledger.test.ts +1249 -0
  90. package/src/execution/__tests__/output-collector.test.ts +117 -6
  91. package/src/execution/__tests__/pi-invocation.test.ts +1 -1
  92. package/src/execution/__tests__/record-store-orphan-revive.test.ts +159 -0
  93. package/src/execution/__tests__/record-store.test.ts +89 -5
  94. package/src/execution/__tests__/records-cwd-isolation.test.ts +2 -2
  95. package/src/execution/__tests__/recursive-visibility-baseline.test.ts +29 -132
  96. package/src/execution/__tests__/recursive-visibility-env.test.ts +55 -179
  97. package/src/execution/__tests__/rpc-mode.test.ts +2 -2
  98. package/src/execution/__tests__/run-and-finalize-anchoring.test.ts +387 -0
  99. package/src/execution/__tests__/run-and-finalize-chatmode.test.ts +19 -9
  100. package/src/execution/__tests__/run-spawn-chatmode-settled.test.ts +5 -4
  101. package/src/execution/__tests__/run-spawn-edges.test.ts +418 -26
  102. package/src/execution/__tests__/run-spawn-integration.test.ts +3 -3
  103. package/src/execution/__tests__/run-spawn-resume.test.ts +2 -2
  104. package/src/execution/__tests__/run-spawn-rpc-mode.test.ts +2 -2
  105. package/src/execution/__tests__/run-spawn-stdout-callback-throw.test.ts +16 -8
  106. package/src/execution/__tests__/service-kill-escalation.test.ts +91 -0
  107. package/src/execution/__tests__/session-context-resolver.test.ts +0 -2
  108. package/src/execution/__tests__/session-file-gc.test.ts +35 -0
  109. package/src/execution/__tests__/session-pending.test.ts +471 -0
  110. package/src/execution/__tests__/session-reconstructor.test.ts +366 -15
  111. package/src/execution/__tests__/session-runner-branch-cache-lru.test.ts +110 -0
  112. package/src/execution/__tests__/session-runner-close-prune.test.ts +181 -0
  113. package/src/execution/__tests__/session-runner-dispatch.test.ts +489 -0
  114. package/src/execution/__tests__/session-runner-epipe.test.ts +4 -3
  115. package/src/execution/__tests__/session-runner-heartbeat-idle-fallback.test.ts +227 -0
  116. package/src/execution/__tests__/session-runner-lifecycle-helpers.test.ts +290 -0
  117. package/src/execution/__tests__/session-runner-schema-env.test.ts +7 -5
  118. package/src/execution/__tests__/settled-watchdog.test.ts +524 -0
  119. package/src/execution/__tests__/spawn-args.test.ts +11 -10
  120. package/src/execution/__tests__/spawn-event-adapter-rpc.test.ts +26 -23
  121. package/src/execution/__tests__/spawn-event-adapter.test.ts +1 -1
  122. package/src/execution/__tests__/spawn-worktree-guidance.test.ts +5 -4
  123. package/src/execution/__tests__/spawned-children.test.ts +155 -11
  124. package/src/execution/__tests__/start-sync-model-guard.test.ts +4 -4
  125. package/src/execution/__tests__/stdin-writer.test.ts +6 -4
  126. package/src/execution/__tests__/stream-sink-retirement.test.ts +6 -6
  127. package/src/execution/__tests__/subagent-actions-core.test.ts +1017 -0
  128. package/src/execution/__tests__/subagent-service-message-close.test.ts +99 -35
  129. package/src/execution/__tests__/subagent-service-multiproc-guard.test.ts +174 -0
  130. package/src/execution/__tests__/subagent-service-notify-gate.test.ts +271 -0
  131. package/src/execution/__tests__/subagent-service-parent-guard.test.ts +33 -29
  132. package/src/execution/__tests__/subagent-service-recovery-bounds.test.ts +341 -0
  133. package/src/execution/__tests__/subagent-service.test.ts +37 -30
  134. package/src/execution/__tests__/subprocess-agent-runner-routing.test.ts +88 -12
  135. package/src/execution/__tests__/subprocess-agent-runner-timeout.test.ts +75 -0
  136. package/src/execution/__tests__/subprocess-agent-runner.test.ts +22 -20
  137. package/src/execution/__tests__/temp-prompt.test.ts +1 -1
  138. package/src/execution/__tests__/timeout-integration.test.ts +13 -12
  139. package/src/execution/__tests__/turn-limiter-semantics.test.ts +1 -1
  140. package/src/execution/__tests__/turn-limiter.test.ts +1 -1
  141. package/src/execution/__tests__/ui-request-handler-factory.test.ts +107 -7
  142. package/src/execution/__tests__/ui-request-handler.test.ts +4 -3
  143. package/src/execution/__tests__/ui-request-queue.test.ts +167 -2
  144. package/src/execution/__tests__/worktree-git-ops.test.ts +571 -0
  145. package/src/execution/__tests__/worktree-manager.test.ts +14 -5
  146. package/src/execution/__tests__/worktree-pid-registration.integration.test.ts +4 -3
  147. package/src/execution/__tests__/worktree-reconcile-aging.test.ts +204 -0
  148. package/src/execution/agent-registry.ts +298 -30
  149. package/src/execution/agent-result-mapper.ts +3 -0
  150. package/src/execution/agents-assembly.ts +88 -0
  151. package/src/execution/cold-resurrect.ts +199 -0
  152. package/src/execution/concurrency-pool.ts +71 -9
  153. package/src/execution/dialog-queue.ts +101 -3
  154. package/src/execution/engine/__tests__/common/capability-gate.test.ts +91 -0
  155. package/src/execution/engine/__tests__/common/data-dir.test.ts +1 -1
  156. package/src/execution/engine/__tests__/common/journal-wiring.test.ts +84 -0
  157. package/src/execution/engine/__tests__/common/kill-chain.test.ts +34 -7
  158. package/src/execution/engine/__tests__/common/persona-router.test.ts +4 -5
  159. package/src/execution/engine/__tests__/common/pool-manager.test.ts +211 -51
  160. package/src/execution/engine/__tests__/common/session-view-service-zcode-dbpath.test.ts +149 -0
  161. package/src/execution/engine/__tests__/common/session-view-service.test.ts +499 -0
  162. package/src/execution/engine/__tests__/conformance/contract.abort.test.ts +56 -99
  163. package/src/execution/engine/__tests__/conformance/contract.agent-events.test.ts +88 -15
  164. package/src/execution/engine/__tests__/conformance/engine-conformance.live.test.ts +33 -7
  165. package/src/execution/engine/__tests__/conformance/golden-replay.pi.test.ts +1 -1
  166. package/src/execution/engine/__tests__/conformance/golden-replay.zcode.test.ts +75 -54
  167. package/src/execution/engine/__tests__/conformance/zcode-appserver-harness.ts +128 -0
  168. package/src/execution/engine/__tests__/engine-discovery.test.ts +15 -3
  169. package/src/execution/engine/__tests__/model-prompt.test.ts +15 -3
  170. package/src/execution/engine/__tests__/registry.test.ts +94 -3
  171. package/src/execution/engine/__tests__/routing.test.ts +115 -6
  172. package/src/execution/engine/common/capability-gate.ts +92 -0
  173. package/src/execution/engine/common/journal-wiring.ts +97 -0
  174. package/src/execution/engine/common/kill-chain.ts +42 -10
  175. package/src/execution/engine/common/nesting-guard.ts +66 -0
  176. package/src/execution/engine/common/persona-router.ts +18 -8
  177. package/src/execution/engine/common/pool-manager.ts +332 -78
  178. package/src/execution/engine/common/session-view-service.ts +506 -0
  179. package/src/execution/engine/common/session-view-types.ts +125 -0
  180. package/src/execution/engine/engine-discovery.ts +12 -16
  181. package/src/execution/engine/engines/pi/__tests__/pi-engine.test.ts +174 -28
  182. package/src/execution/engine/engines/pi/__tests__/spawn-opts-direct.test.ts +257 -0
  183. package/src/execution/{argv-mirror.ts → engine/engines/pi/argv-mirror.ts} +43 -21
  184. package/src/execution/{get-state-handshake.ts → engine/engines/pi/get-state-handshake.ts} +86 -11
  185. package/src/execution/{output-collector.ts → engine/engines/pi/output-collector.ts} +108 -19
  186. package/src/execution/engine/engines/pi/pi-engine.ts +434 -38
  187. package/src/execution/{pi-invocation.ts → engine/engines/pi/pi-invocation.ts} +2 -2
  188. package/src/execution/engine/engines/pi/reader.ts +11 -0
  189. package/src/execution/engine/engines/pi/registration.ts +4 -2
  190. package/src/execution/engine/engines/pi/session-runner.ts +2840 -0
  191. package/src/execution/{spawn-event-adapter.ts → engine/engines/pi/spawn-event-adapter.ts} +75 -34
  192. package/src/execution/{stdin-writer.ts → engine/engines/pi/stdin-writer.ts} +10 -9
  193. package/src/execution/{temp-prompt.ts → engine/engines/pi/temp-prompt.ts} +2 -2
  194. package/src/execution/{turn-limiter.ts → engine/engines/pi/turn-limiter.ts} +1 -1
  195. package/src/execution/engine/engines/zcode/__tests__/__fixtures__/fake-appserver.mjs +301 -0
  196. package/src/execution/engine/engines/zcode/__tests__/__fixtures__/zcode-golden-appserver.json +36 -0
  197. package/src/execution/engine/engines/zcode/__tests__/appserver-launcher.test.ts +321 -0
  198. package/src/execution/engine/engines/zcode/__tests__/connection.test.ts +473 -0
  199. package/src/execution/engine/engines/zcode/__tests__/parser.test.ts +7 -210
  200. package/src/execution/engine/engines/zcode/__tests__/preparer.test.ts +6 -110
  201. package/src/execution/engine/engines/zcode/__tests__/session-channel-dispose-harvest.test.ts +247 -0
  202. package/src/execution/engine/engines/zcode/__tests__/session-channel-turn-timers.test.ts +451 -0
  203. package/src/execution/engine/engines/zcode/__tests__/session-channel.test.ts +792 -0
  204. package/src/execution/engine/engines/zcode/__tests__/zcode-engine-appserver.test.ts +782 -0
  205. package/src/execution/engine/engines/zcode/__tests__/zcode-engine-degrade.test.ts +168 -0
  206. package/src/execution/engine/engines/zcode/__tests__/zcode-engine-dispose.test.ts +210 -0
  207. package/src/execution/engine/engines/zcode/__tests__/zcode-engine-retry.test.ts +353 -0
  208. package/src/execution/engine/engines/zcode/__tests__/zcode-engine-status.test.ts +282 -0
  209. package/src/execution/engine/engines/zcode/__tests__/zcode-engine-timeout.test.ts +372 -0
  210. package/src/execution/engine/engines/zcode/__tests__/zcode-engine.live.test.ts +65 -47
  211. package/src/execution/engine/engines/zcode/appserver-launcher.ts +167 -0
  212. package/src/execution/engine/engines/zcode/connection.ts +598 -0
  213. package/src/execution/engine/engines/zcode/constants.ts +147 -14
  214. package/src/execution/engine/engines/zcode/golden-sample.ts +42 -34
  215. package/src/execution/engine/engines/zcode/parser.ts +12 -346
  216. package/src/execution/engine/engines/zcode/preparer.ts +22 -150
  217. package/src/execution/engine/engines/zcode/session-channel.ts +968 -0
  218. package/src/execution/engine/engines/zcode/zcode-engine.ts +1023 -250
  219. package/src/execution/engine/host-task-spec.ts +32 -36
  220. package/src/execution/engine/model-validation.ts +177 -0
  221. package/src/execution/engine/port.ts +65 -7
  222. package/src/execution/engine/registry.ts +53 -0
  223. package/src/execution/engine/routing.ts +75 -1
  224. package/src/execution/engine/types.ts +30 -103
  225. package/src/execution/execution-record.ts +222 -144
  226. package/src/execution/finalize-record.ts +181 -85
  227. package/src/execution/idle-gc.ts +28 -0
  228. package/src/execution/lifecycle-manager.ts +27 -3
  229. package/src/execution/lifecycle-predicates.ts +1 -1
  230. package/src/execution/manifest-store.ts +53 -62
  231. package/src/execution/notifier.ts +10 -10
  232. package/src/execution/notify-host.ts +212 -0
  233. package/src/execution/notify-ledger.ts +117 -16
  234. package/src/execution/record-entry.ts +8 -2
  235. package/src/execution/record-store.ts +148 -88
  236. package/src/execution/round-settlement.ts +93 -0
  237. package/src/execution/session-file-gc.ts +96 -62
  238. package/src/execution/session-pending.ts +213 -62
  239. package/src/execution/session-reconstructor.ts +256 -143
  240. package/src/execution/sessions-index.ts +127 -102
  241. package/src/execution/settled-watchdog.ts +311 -0
  242. package/src/execution/subagent-actions-core.ts +686 -0
  243. package/src/execution/subagent-service.ts +1106 -907
  244. package/src/execution/subprocess-agent-runner.ts +144 -97
  245. package/src/execution/types.ts +9 -0
  246. package/src/execution/ui-request-handler-factory.ts +126 -75
  247. package/src/execution/ui-request-queue.ts +50 -19
  248. package/src/execution/worktree-git-ops.ts +397 -0
  249. package/src/execution/worktree-manager.ts +72 -10
  250. package/src/execution/worktree-registry.ts +6 -12
  251. package/src/index.ts +542 -9
  252. package/src/orchestration/__tests__/__fixtures__/worker-template.snapshot.txt +42 -3
  253. package/src/orchestration/__tests__/agent-call-catch-fallback.test.ts +3 -5
  254. package/src/orchestration/__tests__/agent-call-stream.test.ts +2 -2
  255. package/src/orchestration/__tests__/args-meta.test.ts +358 -0
  256. package/src/orchestration/__tests__/args-validator.test.ts +14 -0
  257. package/src/orchestration/__tests__/error-recovery-rebuild-failure.test.ts +312 -0
  258. package/src/orchestration/__tests__/error-recovery-terminal-hardening.test.ts +384 -0
  259. package/src/orchestration/__tests__/execute-agent-call.test.ts +151 -11
  260. package/src/orchestration/__tests__/file-run-store-prune.test.ts +168 -0
  261. package/src/orchestration/__tests__/file-run-store-throttle.test.ts +168 -0
  262. package/src/orchestration/__tests__/file-run-store.test.ts +390 -0
  263. package/src/orchestration/__tests__/helpers/flush-microtasks.ts +13 -0
  264. package/src/orchestration/__tests__/launcher-nested-workflow.test.ts +34 -0
  265. package/src/orchestration/__tests__/lifecycle-abort-broadcast-signal.test.ts +357 -0
  266. package/src/orchestration/__tests__/lifecycle-recover-crashed.test.ts +289 -0
  267. package/src/orchestration/__tests__/lifecycle-runid-injection.test.ts +3 -3
  268. package/src/orchestration/__tests__/lifecycle.test.ts +84 -5
  269. package/src/orchestration/__tests__/non-cloneable-return-e2e.test.ts +1 -1
  270. package/src/orchestration/__tests__/run-snapshot.test.ts +356 -0
  271. package/src/orchestration/__tests__/script-generate.test.ts +314 -0
  272. package/src/orchestration/__tests__/script-lint.test.ts +17 -0
  273. package/src/orchestration/__tests__/test-mocks.ts +2 -0
  274. package/src/orchestration/__tests__/worker-exit-without-result.test.ts +7 -6
  275. package/src/orchestration/__tests__/worker-message-pump-finalize-run.test.ts +229 -0
  276. package/src/orchestration/__tests__/{error-recovery-handlers.test.ts → worker-message-pump-handlers.test.ts} +28 -27
  277. package/src/orchestration/__tests__/{error-recovery-postmessage-defense.test.ts → worker-message-pump-postmessage-defense.test.ts} +8 -8
  278. package/src/orchestration/__tests__/{error-recovery-serialize-failed-result.test.ts → worker-message-pump-serialize-failed-result.test.ts} +2 -2
  279. package/src/orchestration/__tests__/{error-recovery-workflow-call.test.ts → worker-message-pump-workflow-call.test.ts} +1 -1
  280. package/src/orchestration/__tests__/worker-pending-timeout-abort.test.ts +275 -0
  281. package/src/orchestration/__tests__/worker-script-builder-runtime.test.ts +78 -1
  282. package/src/orchestration/__tests__/worker-script-builder.test.ts +1 -1
  283. package/src/orchestration/__tests__/workflow-files.test.ts +186 -0
  284. package/src/orchestration/__tests__/workflow-nesting-e2e.test.ts +1 -1
  285. package/src/orchestration/__tests__/workflow-run-summary.test.ts +119 -0
  286. package/src/orchestration/__tests__/workflow-script-registry-impl.test.ts +124 -0
  287. package/src/orchestration/agent-opts-resolver.ts +8 -8
  288. package/src/orchestration/args-meta.ts +198 -0
  289. package/src/orchestration/args-validator.ts +53 -31
  290. package/src/orchestration/execute-agent-call.ts +25 -79
  291. package/src/orchestration/file-run-store.ts +327 -0
  292. package/src/orchestration/launcher.ts +160 -104
  293. package/src/orchestration/lifecycle.ts +371 -97
  294. package/src/orchestration/models/agent-call.ts +7 -7
  295. package/src/orchestration/models/budget.ts +5 -5
  296. package/src/orchestration/models/ports.ts +4 -4
  297. package/src/orchestration/models/run-runtime.ts +15 -15
  298. package/src/orchestration/models/trace.ts +9 -9
  299. package/src/orchestration/models/types.ts +111 -22
  300. package/src/orchestration/models/workflow-run.ts +28 -28
  301. package/src/orchestration/models/workflow-script.ts +4 -4
  302. package/src/orchestration/run-snapshot.ts +266 -0
  303. package/src/orchestration/script-generate.ts +154 -0
  304. package/src/orchestration/script-lint.ts +123 -90
  305. package/src/orchestration/worker-handle.ts +10 -10
  306. package/src/orchestration/worker-host.ts +10 -10
  307. package/src/orchestration/{error-recovery.ts → worker-message-pump.ts} +498 -221
  308. package/src/orchestration/worker-script-builder.ts +401 -346
  309. package/src/orchestration/workflow-files.ts +37 -11
  310. package/src/orchestration/workflow-run-summary.ts +69 -0
  311. package/src/orchestration/workflow-script-registry-impl.ts +31 -9
  312. package/src/shared/__tests__/agent-ref.test.ts +209 -2
  313. package/src/shared/__tests__/atomic-write.test.ts +267 -0
  314. package/src/shared/__tests__/bounded-serialize.test.ts +236 -0
  315. package/src/shared/__tests__/injection-render.test.ts +518 -0
  316. package/src/shared/__tests__/meta-parser.test.ts +151 -0
  317. package/src/shared/__tests__/resource-discovery-host-roots.test.ts +474 -0
  318. package/src/shared/__tests__/resource-discovery.test.ts +261 -217
  319. package/src/shared/agent-ref.ts +143 -2
  320. package/src/shared/atomic-write.ts +323 -0
  321. package/src/shared/bounded-serialize.ts +154 -0
  322. package/src/shared/injection-render.ts +279 -0
  323. package/src/shared/meta-parser.ts +182 -68
  324. package/src/shared/model-ref.ts +15 -2
  325. package/src/shared/resource-discovery.ts +97 -204
  326. package/src/shared/resource-meta.ts +14 -0
  327. package/src/shared/schema-jsonify.ts +1 -1
  328. package/src/shared/xml-injection.ts +9 -9
  329. package/workflows/README.md +9 -9
  330. package/workflows/chain.js +4 -2
  331. package/workflows/map-reduce.js +5 -3
  332. package/workflows/parallel.js +5 -3
  333. package/workflows/review-fix-loop-utils.cjs +165 -92
  334. package/workflows/review-fix-loop-utils.d.cts +287 -0
  335. package/workflows/review-fix-loop.js +18 -7
  336. package/workflows/scatter-gather.js +4 -2
  337. package/dist/chunk-3VOERJPJ.js +0 -22
  338. package/src/execution/__tests__/execute-options-mapper.test.ts +0 -200
  339. package/src/execution/engine/engines/pi/__tests__/task-spec-mapper.test.ts +0 -164
  340. package/src/execution/engine/engines/pi/task-spec-mapper.ts +0 -100
  341. package/src/execution/engine/engines/zcode/__tests__/__fixtures__/zcode-golden-spawn.json +0 -39
  342. package/src/execution/engine/engines/zcode/__tests__/launcher.test.ts +0 -150
  343. package/src/execution/engine/engines/zcode/__tests__/zcode-engine.test.ts +0 -580
  344. package/src/execution/engine/engines/zcode/launcher.ts +0 -161
  345. package/src/execution/execute-options-mapper.ts +0 -115
  346. package/src/execution/session-runner.ts +0 -1789
  347. package/src/shared/__tests__/resource-discovery-manifest-cache.test.ts +0 -288
@@ -0,0 +1,212 @@
1
+ // [D4-① 通知簇职责轴] background 完成通知的 host 面——原 SubagentService 私有通知簇
2
+ // (notifyComplete / notifyClosed / piAdapter / toNotifyRecord / emitPendingRegister /
3
+ // emitPendingUnregister + notifier 实例持有)整体搬移至此(行为逐字节等价:搬移 +
4
+ // 依赖注入,不重写逻辑)。变化轴:改通知文案 / dedup 身份 / 放行守卫 / pending 注册
5
+ // 注销协议,只改本文件;Service 只保留编排与依赖注入(getPi / listRunning / isIdle)。
6
+
7
+ import { displayAgentName } from "../shared/agent-ref.ts";
8
+ import { snapshot } from "./execution-record.ts";
9
+ import { hasIdleTimer } from "./lifecycle-manager.ts";
10
+ import { hasLiveProcessHandle, isIdle, isResumable } from "./lifecycle-predicates.ts";
11
+ import type { BgNotifier, NotifierHost } from "./notifier.ts";
12
+ import { createNotifier } from "./notifier.ts";
13
+ import type { BgNotifyRecord } from "./notifier.ts";
14
+ import type { ExecutionRecord, RecordSnapshot } from "./types.ts";
15
+
16
+ /** Pi ExtensionAPI 的最小接口(duck-typed)——原定义于 subagent-service.ts,随通知簇
17
+ * (piAdapter / emitPending* 的依赖)搬移至此并导出(Service 的 session 注入参数仍引用)。
18
+ * subagent-service 直接调 pi.sendMessage 发 background 完成通知(BgNotifier 滑动窗口合并),
19
+ * 不委托 pending-notifications EventBus 中继——后者只管 registry 不参与通知发送。 */
20
+ export interface PiLike {
21
+ appendEntry(customType: string, data?: unknown): void;
22
+ events: { emit(channel: string, data: unknown): void };
23
+ sendMessage(
24
+ message: { customType: string; content: string; display: boolean; details?: unknown },
25
+ options?: { triggerTurn?: boolean; deliverAs?: "steer" | "followUp" | "nextTurn" }, // g4-allow: 类型注解——PiLike 接口形状(pi.sendMessage 签面子集),非投递调用
26
+ ): void;
27
+ /** 订阅 pi 事件(D8:notifier 的 settled 边沿订阅用 'agent_settled')。
28
+ * pi 0.84.1 的 on 返回 void 且无 off——退订语义由调用侧 disposed 标志包装兑现。
29
+ * 可选:旧测试 mock pi 可能未实现 on,缺省时 notifier 退化为内核退避路径。 */
30
+ on?(event: "agent_settled", handler: () => void): void;
31
+ }
32
+
33
+ /** NotifyHost 的依赖注入(Service 侧供给,全部惰性求值——pi/session 级状态运行时可变)。 */
34
+ export interface NotifyHostDeps {
35
+ /** 当前 pi session handle(session_start 注入 / shutdown 置 null)。 */
36
+ getPi: () => PiLike | null;
37
+ /** store 的 running record 快照(piAdapter.hasRunningBackground 用)。 */
38
+ listRunning: () => RecordSnapshot[];
39
+ /** 主 agent isIdle 查询(ctx.isIdle,initSession 注入;未注入时 undefined)。 */
40
+ getIsIdle: () => (() => boolean) | undefined;
41
+ }
42
+
43
+ /** 通知簇 host 面(Service 经此消费;notifier 实例封装在内部不外露)。 */
44
+ export interface NotifyHost {
45
+ /** background 完成回注(原 Service.notifyComplete)。 */
46
+ notifyComplete(record: ExecutionRecord): void;
47
+ /** [C-1] chatMode close 终态通知(原 Service.notifyClosed)。 */
48
+ notifyClosed(record: ExecutionRecord, emptyBody?: boolean): void;
49
+ /** pending-notifications 注册(原模块函数 emitPendingRegister,pi 经 deps 取)。 */
50
+ emitPendingRegister(id: string, name?: string): void;
51
+ /** pending-notifications 注销(原模块函数 emitPendingUnregister)。 */
52
+ emitPendingUnregister(id: string, reason: string): void;
53
+ /** dispose 的逆操作(initSession 复活,原 notifier.revive 委托)。 */
54
+ revive(): void;
55
+ /** dispose 前冲刷待发通知(原 notifier.flushPendingNotifications 委托)。 */
56
+ flushPendingNotifications(): void;
57
+ /** 丢弃 pending 通知(原 notifier.dispose 委托)。 */
58
+ dispose(): void;
59
+ }
60
+
61
+ /** pending-notifications 注册/注销 helper(避免重复代码)。
62
+ * name 是 GUI pending 通知的显示名——取 basename 短名(displayAgentName),
63
+ * 完整路径仍走 record.agent(env 注入 / 持久化)。 */
64
+ function emitPendingRegister(pi: PiLike | null, id: string, name?: string): void {
65
+ pi?.events.emit("pending:register", {
66
+ id,
67
+ type: "subagent",
68
+ name: name ? displayAgentName(name) : id,
69
+ });
70
+ }
71
+
72
+ function emitPendingUnregister(
73
+ pi: PiLike | null,
74
+ id: string,
75
+ reason: string,
76
+ ): void {
77
+ pi?.events.emit("pending:unregister", {
78
+ id,
79
+ reason,
80
+ });
81
+ }
82
+
83
+ /** 通知簇工厂:notifier 实例随本函数创建并封装(原 Service 构造器
84
+ * `createNotifier(this.piAdapter())` 的等价形态)。 */
85
+ export function createNotifyHost(deps: NotifyHostDeps): NotifyHost {
86
+ /** notifier 的 NotifierHost 适配器(绑定到 pi.sendMessage + store 查询)。 */
87
+ const piAdapter = (): NotifierHost => {
88
+ return {
89
+ sendMessage: (message, options) => {
90
+ deps.getPi()?.sendMessage(message, options);
91
+ },
92
+ hasRunningBackground: () => {
93
+ // [M3] 「在跑的 background 工作」= 有活进程且非等待续聊(idle timer armed)。
94
+ // v4 B-1 把旧 idle 折入 running 且 record 留 store:轮次完成的 chatMode record
95
+ // (timer armed、无在跑轮)与 one-shot 完成后等待 message 升级的 record 都不再计入。
96
+ // 旧判定 `mode === "background"` 对这两类恒 true → 轮次完成通知恒挂 60s 合并窗口
97
+ // (notifier MERGE_WINDOW_MS),主 agent 的续聊回复固定延迟 60s 送达,持续对话(G1)失效。
98
+ return deps.listRunning().some(
99
+ (r) => r.mode === "background" && hasLiveProcessHandle(r.id) && !hasIdleTimer(r.id),
100
+ );
101
+ },
102
+ isIdle: () => deps.getIsIdle()?.() ?? true,
103
+ // [must-fix #4 / D8] settled 边沿订阅,与 isIdle 同源(session_start 注入的 pi)。
104
+ // 只注入原生订阅能力;disposed 标志包装(退订语义)在 notifier 的 port 装配完成。
105
+ onAgentSettled: (handler) => { deps.getPi()?.on?.("agent_settled", handler); },
106
+ };
107
+ };
108
+
109
+ const notifier: BgNotifier = createNotifier(piAdapter());
110
+
111
+ /** record → BgNotifyRecord(notifier.notify 入参映射,内部不外露)。
112
+ * v4 B-1:守卫放行 closed(终态,含 cancelled)、isIdle(对话模式轮次完成,notify 主 agent G1)
113
+ * 或 isResumable(running + 无活进程——SP-5 one-shot 成功完成 / MF-6 失败轮回退)。
114
+ * 正在执行(running + 活进程 + 非 timer-armed)返回 undefined(调用方 notifyComplete 跳过)。
115
+ * SP-1: closed 统一终态,closedReason 由 BgNotifyRecord 携带。 */
116
+ const toNotifyRecord = (record: ExecutionRecord): BgNotifyRecord | undefined => {
117
+ const snap = snapshot(record);
118
+ const s = snap.status;
119
+ // [N1] isResumable 放行:SP-5 one-shot 成功完成后 finalizeRoundToIdle 把 record 回退
120
+ // running-resumable——进程已死且永不 arm idle timer(armIdleTimer 只在 agent_settled 的
121
+ // chatMode 分支调用),旧守卫(closed / isIdle only)对其恒拒绝 → 完成通知静默丢失,
122
+ // 而 one-shot 失败走 finalizeRecord 保持 closed 反而通知——与 tool 契约「runs once,
123
+ // notifies on completion」完全倒置。isResumable = running + 无活进程,恰为该完成态;
124
+ // 在跑轮的 record 有活进程,不会被误放行。
125
+ if (s !== "closed" && !isIdle(record) && !isResumable(record)) return undefined;
126
+ // closed → BgNotifyRecord.closed(cancelled 区分靠 closedReason);chatMode 的 isIdle/
127
+ // isResumable(轮次完成或 MF-6 失败轮回退,对话可续)→ running(轮次完成)。
128
+ // isResumable 且非 chatMode(SP-5 one-shot 成功完成)→ closed:对主 agent 的语义是
129
+ // completed(非对话轮次),且只有 closed 分支文案携带 worktree patchFile 的 git apply
130
+ // 提示——one-shot worktree 模式的改动回收依赖该提示(running 分支文案不含 patchFile)。
131
+ const notifyStatus: BgNotifyRecord["status"] =
132
+ s === "closed" || !record.chatMode ? "closed" : "running";
133
+ return {
134
+ id: snap.id,
135
+ status: notifyStatus,
136
+ agent: snap.agent,
137
+ model: snap.model,
138
+ result: snap.result,
139
+ error: snap.error,
140
+ startedAt: snap.startedAt,
141
+ endedAt: snap.endedAt,
142
+ patchFile: record.patchFile,
143
+ // round 透传给 notifier 的 dedup key(对话模式按轮次去重,G1 决策 9)。
144
+ round: record.round,
145
+ // SP-1: closedReason 透传给 notifier(L2 原因,供通知文案按需展示)。
146
+ closedReason: record.closedReason,
147
+ // [wave2] chatMode 条件透传 sessionFile:通知末尾追加 Full transcript 指针行
148
+ //(增量语义的全文恢复通道,见 notifier.buildLlmContent)。one-shot(chatMode
149
+ // falsy)不透传——通知输出逐字节不变(G4),该条件由 message-close 测试的
150
+ // 必选用例锁死(漏加条件时 notifier 单测不红——notifier 层只见最终字段)。
151
+ sessionFile: record.chatMode ? record.sessionFile : undefined,
152
+ };
153
+ };
154
+
155
+ return {
156
+ /** background 完成回注(record → BgNotifyRecord 映射 + notifier.notify)。
157
+ * 正在执行(running + 活进程 + 非 timer-armed)静默跳过——notify 只对 closed(终态)、
158
+ * isIdle(chatMode 轮次完成)或 isResumable(SP-5 one-shot 成功完成 / MF-6 失败轮回退)有意义。
159
+ * SP-1: closed 统一终态(done/failed/crashed 合并),closedReason 携带 L2 原因。 */
160
+ notifyComplete(record: ExecutionRecord): void {
161
+ const notify = toNotifyRecord(record);
162
+ if (notify) notifier.notify(notify);
163
+ },
164
+
165
+ /** [C-1] chatMode close 终态通知(设计 D2:正文空/本轮增量 + sessionFile 指针行)。
166
+ *
167
+ * 与 notifyComplete 的差异只在 dedup 身份与轮次统计:终态通知必须与最后一轮的轮次通知
168
+ * 区分(轮次通知 key=`id:round`),否则同 key 被 60s dedup 吞——close 后父 agent 永远
169
+ * 收不到带指针行的终态通知(审查 C-1)。故 round 置 undefined(key 回退为裸 id),
170
+ * 轮数改经 totalRounds 进文案 "completed after N rounds."(C-2)。
171
+ *
172
+ * 仅 chatMode close 语义调用(closeChatIdle / closeAfterRoundSettled 终态化成功后)。
173
+ * one-shot 显式拒绝(G4:one-shot close 路径现状无终态通知,字节不变);cancel 走
174
+ * cancelBackground 自己的 notifyComplete,不经本方法。幂等性:两条 close 路径均由
175
+ * closeSubagent 的 status 分流守卫(closed 后幂等 no-op)/ CAS 抢锁保证只执行一次,
176
+ * 本方法自身不重复发送;迟到的轮次收尾 .then 通知与轮次通知同 key=`id:round`,
177
+ * 60s 窗内仍被吞,不构成第三条。 */
178
+ /** @param emptyBody true = 终态通知正文置空串(D2 路径②)。W16 P-1 修复后
179
+ * closeChatIdle 的 doneResult.text 改用 record.result 保真(close 终态
180
+ * subagent-record entry 的 result 不抹空轮终真实值),「正文空」不再由合成空
181
+ * text 的副作用承载,改为显式参数——持久化 result 与通知正文两个关注点解耦。 */
182
+ notifyClosed(record: ExecutionRecord, emptyBody = false): void {
183
+ if (!record.chatMode) return;
184
+ const notify = toNotifyRecord(record);
185
+ if (!notify) return;
186
+ notify.round = undefined;
187
+ if (emptyBody) notify.result = "";
188
+ if (record.round != null) notify.totalRounds = record.round;
189
+ notifier.notify(notify);
190
+ },
191
+
192
+ emitPendingRegister(id: string, name?: string): void {
193
+ emitPendingRegister(deps.getPi(), id, name);
194
+ },
195
+
196
+ emitPendingUnregister(id: string, reason: string): void {
197
+ emitPendingUnregister(deps.getPi(), id, reason);
198
+ },
199
+
200
+ revive(): void {
201
+ notifier.revive();
202
+ },
203
+
204
+ flushPendingNotifications(): void {
205
+ notifier.flushPendingNotifications();
206
+ },
207
+
208
+ dispose(): void {
209
+ notifier.dispose();
210
+ },
211
+ };
212
+ }
@@ -21,10 +21,16 @@
21
21
  // D5);同一边沿的多条 pending 合并为一条注入
22
22
  // ③ ack:回执判定成功(主 session 出现 notifyId 匹配的 subagent-bg-notify
23
23
  // custom_message entry)后 appendEntry("subagent-bg-notify-ack")
24
- // ④ replay:重启恢复扫描 ledger/ack 两列 entry 差集重放;重放按 notifyId 幂等去重
24
+ // ④ replay:重启恢复扫描 ledger/ack/abandoned entry 差集重放;重放按 notifyId 幂等去重
25
25
  // (details 携带 notifyId,重复条目可识别)
26
26
  //
27
- // 通道分工(D4):ledger/ack 用 plain appendEntry(type=custom 不进 LLM 上下文——
27
+ // 止损上半场(T4③/PS-6):④只保证「送达保证」下半场,回执确认不可达时(如送达
28
+ // entry 被 compaction 清除)attempts 无上限 = 同一条通知每 120s 重复注入并
29
+ // triggerTurn 无限唤醒 LLM。收敛:重投达 NOTIFY_REDELIVERY_MAX_ATTEMPTS 仍无回执 →
30
+ // 写 abandoned 终态 entry 放弃(warn 含 subagent 标识与 subagents action:"list"
31
+ // 手动核对指引),放弃号跨重启不复活(恢复扫描视同已销账)。
32
+ //
33
+ // 通道分工(D4):ledger/ack/abandoned 用 plain appendEntry(type=custom 不进 LLM 上下文——
28
34
  // session-manager sessionEntryToContextMessages 对 custom 返回 []);送达消息用
29
35
  // pi.sendMessage({triggerTurn:true})(custom_message 进上下文)。两通道不得混用。
30
36
  //
@@ -49,6 +55,11 @@ const logger = getLogger("subagents");
49
55
  export const NOTIFY_LEDGER_CUSTOM_TYPE = "subagent-bg-notify-ledger";
50
56
  /** 销账 entry customType(plain custom entry,不进 LLM 上下文)。 */
51
57
  export const NOTIFY_ACK_CUSTOM_TYPE = "subagent-bg-notify-ack";
58
+ /**
59
+ * [T4③/PS-6] 放弃终态 entry customType(plain custom entry,不进 LLM 上下文)。
60
+ * 与 ack 同形同域:恢复/compaction 扫描把它视同已销账——放弃条目跨重启不复活。
61
+ */
62
+ export const NOTIFY_ABANDONED_CUSTOM_TYPE = "subagent-bg-notify-abandoned";
52
63
 
53
64
  /**
54
65
  * 送达消息的 customType。notifier.ts / bg-notify-render / index.ts(messageRenderer
@@ -65,6 +76,14 @@ export const NOTIFY_CUSTOM_TYPE = "subagent-bg-notify";
65
76
  */
66
77
  export const NOTIFY_WATCHDOG_MS = 120_000;
67
78
 
79
+ /**
80
+ * [T4③/PS-6] 单条通知投递尝试上限(attempts 字段含首次投递):看门狗把 sent 超期
81
+ * 条目转回 pending 前,attempts 已达本上限 → 不再重投,转放弃终态(abandoned entry
82
+ * + warn 恢复指引)。被否语义:「重投无限但幂等」——幂等只防重复入账,不防重复
83
+ * 投递唤醒 LLM(设计 docs/design/subagent-core-unbounded-wait-audit.md §7.2 T4③)。
84
+ */
85
+ export const NOTIFY_REDELIVERY_MAX_ATTEMPTS = 5;
86
+
68
87
  /**
69
88
  * U4 投递计数分桶(设计 §5 U4:分桶口径与 §2.2 三条丢失路径一一对应,回归时定位到
70
89
  * 具体环节):
@@ -103,16 +122,22 @@ export interface NotifyAckEntryData {
103
122
  notifyId: string;
104
123
  }
105
124
 
125
+ /** [T4③/PS-6] abandoned entry 的 data schema(v1,与 ack 同形——终态标记只需身份键)。 */
126
+ export interface NotifyAbandonedEntryData {
127
+ v: 1;
128
+ notifyId: string;
129
+ }
130
+
106
131
  /** ledger 依赖的宿主最小接口(index.ts session_start 装配;解耦便于测试)。 */
107
132
  export interface NotifyLedgerHost {
108
- /** 写 plain custom entry(ledger/ack 通道;pi.appendEntry)。 */
133
+ /** 写 plain custom entry(ledger/ack/abandoned 通道;pi.appendEntry)。 */
109
134
  appendLedgerEntry(customType: string, data: unknown): void;
110
135
  /** 读当前 session 全部 entry(回执扫描 + 恢复扫描;ctx.sessionManager.getEntries())。 */
111
136
  readSessionEntries(): readonly unknown[];
112
137
  /** 主 agent 是否空闲(发送前二次复查用;ctx.isIdle())。 */
113
138
  isIdle(): boolean;
114
139
  /** 订阅 settled 边沿(pi.on("agent_settled")——无退订语义:createExtensionAPI.on
115
- * 只 push 进 extension.handlers、无 off(pi 0.84.1 loader.js:209-214),由 ledger
140
+ * 只 push 进 extension.handlers、无 off(pi 0.84.4 loader.js:209-214),由 ledger
116
141
  * disposed 标志包装)。注意与 ctx.events.on(channel) 消歧:后者走 eventBus、有
117
142
  * trackEventBusSubscription 退订通路(loader.js:174、:338),语义不同。 */
118
143
  onAgentSettled(handler: () => void): void;
@@ -128,7 +153,7 @@ interface NotifyLedgerItem {
128
153
  recordedAt: number;
129
154
  /** 最近一次投递受理时刻;undefined = 尚未投递(pending,等下一边沿)。 */
130
155
  sentAt: number | undefined;
131
- /** 投递尝试次数(看门狗重投计数,诊断用)。 */
156
+ /** 投递尝试次数(含首次;达 NOTIFY_REDELIVERY_MAX_ATTEMPTS 后超期即放弃,诊断用)。 */
132
157
  attempts: number;
133
158
  }
134
159
 
@@ -141,16 +166,24 @@ export interface NotifyLedger {
141
166
  * 同批 pending 合并单条送达(triggerTurn 直达)。 */
142
167
  attemptDeliver(): void;
143
168
  /** ③ 回执销账:扫 session entries,出现 notifyId 匹配的送达 custom_message entry
144
- * → appendEntry(ack) + 摘账(内存态不承担销账职责,权威 = 两列 entry 差集)。 */
169
+ * → appendEntry(ack) + 摘账(内存态不承担销账职责,权威 = 多列 entry 差集)。 */
145
170
  checkReceipts(): void;
146
- /** ④ 重启恢复:扫 ledger/ack 两列 entry 差集,未销账号重新入账并投递(已销账零重发)。
171
+ /** ④ 重启恢复:扫 ledger/ack/abandoned entry 差集,未销账且未放弃号重新入账并投递
172
+ * (已销账零重发;已放弃号不复活——[T4③/PS-6] 止损终态跨重启生效)。
147
173
  * @returns 重放条数。 */
148
174
  recoverFromSession(): number;
149
- /** compaction 降级(P-B4 未验证):检测 ledger/ack entry 被清除 → 按内存态补写。
175
+ /** compaction 降级(P-B4 未验证):检测 ledger/ack/abandoned entry 被清除 → 按内存态补写。
150
176
  * @returns 补写条数。 */
151
177
  compactionCheck(): number;
152
178
  /** 诊断/测试:pending(已记账未投递)条数。 */
153
179
  pendingCount(): number;
180
+ /**
181
+ * [T4④/PS-5] pending 只读快照(notifyId/content/record,副本非活引用):
182
+ * 供 SubagentService.dispose 在「shutdown flush 被 isIdle 门拦」时把未投递
183
+ * pending 复写落盘(同一 ledger entry 通道,notifyId 幂等)供重启 replay。
184
+ * 消费侧配合面仅此只读方法——不改变 attemptDeliver/abandon 既有语义。
185
+ */
186
+ pendingEntries(): ReadonlyArray<{ notifyId: string; content: string; record: object }>;
154
187
  /** 诊断/测试:已投递待回执条数。 */
155
188
  waitingReceiptCount(): number;
156
189
  /** U4 诊断:三桶计数快照(副本;增量同时经 extensionLogger 通道落日志)。 */
@@ -166,13 +199,15 @@ function isPlainObject(v: unknown): v is Record<string, unknown> {
166
199
  return typeof v === "object" && v !== null && !Array.isArray(v);
167
200
  }
168
201
 
169
- /** 扫 ledger/ack 两列 plain custom entry(恢复 / compaction 检查共用)。 */
202
+ /** 扫 ledger/ack/abandoned 三列 plain custom entry(恢复 / compaction 检查共用)。 */
170
203
  function scanSessionLedgerEntries(entries: readonly unknown[]): {
171
204
  ledger: Map<string, NotifyLedgerEntryData>;
172
205
  acked: Set<string>;
206
+ abandoned: Set<string>;
173
207
  } {
174
208
  const ledger = new Map<string, NotifyLedgerEntryData>();
175
209
  const acked = new Set<string>();
210
+ const abandoned = new Set<string>();
176
211
  for (const entry of entries) {
177
212
  if (!isPlainObject(entry) || entry["type"] !== "custom") continue;
178
213
  const customType = entry["customType"];
@@ -189,9 +224,11 @@ function scanSessionLedgerEntries(entries: readonly unknown[]): {
189
224
  }
190
225
  } else if (customType === NOTIFY_ACK_CUSTOM_TYPE) {
191
226
  acked.add(notifyId);
227
+ } else if (customType === NOTIFY_ABANDONED_CUSTOM_TYPE) {
228
+ abandoned.add(notifyId);
192
229
  }
193
230
  }
194
- return { ledger, acked };
231
+ return { ledger, acked, abandoned };
195
232
  }
196
233
 
197
234
  /** 收集 wanted 集合中已送达(custom_message entry 出现)的 notifyId。
@@ -232,6 +269,11 @@ export function createNotifyLedger(
232
269
  const items = new Map<string, NotifyLedgerItem>();
233
270
  /** 已销账内存索引(notifyId 幂等判重 + compaction 补写源;权威 = ack entry 列)。 */
234
271
  const ackedIds = new Set<string>();
272
+ /**
273
+ * [T4③/PS-6] 已放弃终态内存索引(幂等判重 + compaction 补写源;权威 = abandoned
274
+ * entry 列)。与 ackedIds 同型:放弃是终态,同 notifyId 绝不再入账/重投/复活。
275
+ */
276
+ const abandonedIds = new Set<string>();
235
277
  /** U4 投递计数三桶(诊断快照源;增量经 emitBucketLog 落 extensionLogger)。 */
236
278
  const buckets: NotifyDeliveryBucketMetrics = {
237
279
  settleRejected: 0,
@@ -244,7 +286,8 @@ export function createNotifyLedger(
244
286
  const api: NotifyLedger = {
245
287
  record(notifyId, content, record): boolean {
246
288
  if (disposed) return false;
247
- if (items.has(notifyId) || ackedIds.has(notifyId)) return false;
289
+ // 幂等去重:在账(pending/sent)/ 已销账 / 已放弃([T4③] 终态绝不重发)→ false
290
+ if (items.has(notifyId) || ackedIds.has(notifyId) || abandonedIds.has(notifyId)) return false;
248
291
  host.appendLedgerEntry(NOTIFY_LEDGER_CUSTOM_TYPE, {
249
292
  v: 1,
250
293
  notifyId,
@@ -297,9 +340,13 @@ export function createNotifyLedger(
297
340
  if (disposed) return 0;
298
341
  const state = scanSessionLedgerEntries(host.readSessionEntries());
299
342
  for (const notifyId of state.acked) ackedIds.add(notifyId);
343
+ // [T4③/PS-6] 放弃终态跨重启生效:abandoned entry 视同已销账,下方差集重放跳过
344
+ // ——放弃号重启后不复活不重投(止损语义不以进程边界失效)。
345
+ for (const notifyId of state.abandoned) abandonedIds.add(notifyId);
300
346
  let replayed = 0;
301
347
  for (const entry of state.ledger.values()) {
302
348
  if (state.acked.has(entry.notifyId)) continue; // 已销账零重发
349
+ if (abandonedIds.has(entry.notifyId)) continue; // 已放弃不复活(止损终态)
303
350
  if (items.has(entry.notifyId) || ackedIds.has(entry.notifyId)) continue; // 幂等
304
351
  items.set(entry.notifyId, {
305
352
  ...entry,
@@ -345,6 +392,14 @@ export function createNotifyLedger(
345
392
  rewritten += 1;
346
393
  }
347
394
  }
395
+ // [T4③/PS-6] 放弃终态同享补写:内存是权威,compaction 清掉 abandoned entry 后
396
+ // 不补写会让放弃号在下次重启按差集复活(重投重启)。
397
+ for (const notifyId of abandonedIds) {
398
+ if (!state.abandoned.has(notifyId)) {
399
+ host.appendLedgerEntry(NOTIFY_ABANDONED_CUSTOM_TYPE, { v: 1, notifyId } satisfies NotifyAbandonedEntryData);
400
+ rewritten += 1;
401
+ }
402
+ }
348
403
  return rewritten;
349
404
  },
350
405
 
@@ -356,6 +411,16 @@ export function createNotifyLedger(
356
411
  return n;
357
412
  },
358
413
 
414
+ pendingEntries(): ReadonlyArray<{ notifyId: string; content: string; record: object }> {
415
+ const out: Array<{ notifyId: string; content: string; record: object }> = [];
416
+ for (const item of items.values()) {
417
+ if (item.sentAt !== undefined) continue;
418
+ // 逐条浅拷贝:消费方(dispose 落盘复写)不得持有内部可变态。
419
+ out.push({ notifyId: item.notifyId, content: item.content, record: { ...item.record } });
420
+ }
421
+ return out;
422
+ },
423
+
359
424
  waitingReceiptCount(): number {
360
425
  let n = 0;
361
426
  for (const item of items.values()) {
@@ -408,6 +473,35 @@ export function createNotifyLedger(
408
473
  };
409
474
  }
410
475
 
476
+ /** [T4③/PS-6] 放弃终态:appendEntry(abandoned) 落盘终态标记(重启恢复不复活)+
477
+ * warn 恢复指引。放弃后账面摘除(pending/waiting 计数归零、看门狗可停),同
478
+ * notifyId 被 record 幂等拒绝——「确认不可达」的止损上半场;通知内容本身仍可
479
+ * 经 subagents action:"list" 手动核对(账本 entry 与 result 落盘不受影响)。
480
+ * 放弃不计入 watchdogReplays 桶(该桶口径 = 实际发生重投的条数)。 */
481
+ function abandonItem(item: NotifyLedgerItem): void {
482
+ host.appendLedgerEntry(NOTIFY_ABANDONED_CUSTOM_TYPE, { v: 1, notifyId: item.notifyId } satisfies NotifyAbandonedEntryData);
483
+ items.delete(item.notifyId);
484
+ abandonedIds.add(item.notifyId);
485
+ // 消息含 subagent 标识与恢复指引(S-E 验收面);notifyId/attempts 等动态值按
486
+ // D4 约定放 data 参数(msg 近固定 key,限流命中面)。
487
+ logger.warn(
488
+ `Subagent "${itemLabel(item)}" notification abandoned - no receipt after ` +
489
+ `${NOTIFY_REDELIVERY_MAX_ATTEMPTS} delivery attempts; verify manually via subagents action:"list"`,
490
+ { notifyId: item.notifyId, attempts: item.attempts },
491
+ );
492
+ maybeStopWatchdog();
493
+ }
494
+
495
+ /** 放弃 warn 的 subagent 标识:优先 record.agent(人可读);record 形态异常时
496
+ * (ledger 对 record 不透明,见 NotifyLedgerEntryData)退回 notifyId(仍可检索)。 */
497
+ function itemLabel(item: NotifyLedgerItem): string {
498
+ if (isPlainObject(item.record)) {
499
+ const agent = item.record["agent"];
500
+ if (typeof agent === "string" && agent.length > 0) return agent;
501
+ }
502
+ return item.notifyId;
503
+ }
504
+
411
505
  function ensureWatchdog(): void {
412
506
  if (watchdogTimer !== undefined || disposed) return;
413
507
  // 看门狗(D5 ②兜底触发面):回执检查 + 超时重投 + pending 补投。
@@ -418,12 +512,19 @@ export function createNotifyLedger(
418
512
  // attemptDeliver 即重投——正常时序 settled 边沿早已销账,只有消息真丢失才到这
419
513
  const cutoff = Date.now() - NOTIFY_WATCHDOG_MS;
420
514
  let timedOut = 0;
515
+ // [T4③/PS-6] 止损:attempts 已达上限仍超期 = 确认不可达 → 放弃转终态,
516
+ // 不再重投(同一条完成通知不无限重复注入唤醒 LLM)。
517
+ const givenUp: NotifyLedgerItem[] = [];
421
518
  for (const item of items.values()) {
422
- if (item.sentAt !== undefined && item.sentAt <= cutoff) {
423
- item.sentAt = undefined;
424
- timedOut += 1;
519
+ if (item.sentAt === undefined || item.sentAt > cutoff) continue;
520
+ if (item.attempts >= NOTIFY_REDELIVERY_MAX_ATTEMPTS) {
521
+ givenUp.push(item);
522
+ continue;
425
523
  }
524
+ item.sentAt = undefined;
525
+ timedOut += 1;
426
526
  }
527
+ for (const item of givenUp) abandonItem(item);
427
528
  if (timedOut > 0) {
428
529
  // U4 ①watchdogReplays 桶:busy 窗口滞留兜底的重投条数(同一消息反复超时累计)
429
530
  buckets.watchdogReplays += timedOut;
@@ -467,7 +568,7 @@ export function createNotifyLedger(
467
568
  // (P-B0 相邻机制已源码级锚定,agent-session.js:327-331,PS-07)。
468
569
  // [MF-5] registerSettledListener=false(bind 路径)时跳过注册:pi.on("agent_settled")
469
570
  // 无退订语义——createExtensionAPI.on 只 push 进 extension.handlers、无 off
470
- //(pi 0.84.1 dist/core/extensions/loader.js:209-214)。注意消歧两类订阅面:此处的
571
+ //(pi 0.84.4 dist/core/extensions/loader.js:209-214)。注意消歧两类订阅面:此处的
471
572
  // pi.on 是生命周期事件订阅(无退订);ctx.events.on(channel) 走 eventBus、有
472
573
  // trackEventBusSubscription 退订通路(loader.js:174、:338)——勿按后者的可退订
473
574
  // 语义「修复」此处。per-bind 注册会随 session 切换累积死 handler
@@ -543,7 +644,7 @@ function settledEdgeDispatch(): void {
543
644
  /**
544
645
  * session_start 装配(index.ts):构造新 ledger 并绑定为模块级单例(notifier.notify
545
646
  * 经 getBoundNotifyLedger 消费)。重复 bind(/resume /fork /new 的 session_start)
546
- * 替换旧实例——内存态清零符合「内存不承担销账职责」,权威 = entry 两列差集
647
+ * 替换旧实例——内存态清零符合「内存不承担销账职责」,权威 = entry 多列差集
547
648
  * (调用方随后 recoverFromSession 重建)。
548
649
  *
549
650
  * [MF-5] 监听单例化:首次 bind 经当次 host 注册模块级单例 handler
@@ -21,7 +21,9 @@ import type {
21
21
  } from "./types.ts";
22
22
 
23
23
  /** 自描述 record entry 的 customType。写点字面量与本常量的等值由
24
- * __tests__/record-store.test.ts 断言钉住(消费方引用本常量,勿用裸字符串)。 */
24
+ * __tests__/record-store.test.ts 断言钉住(消费方引用本常量,勿用裸字符串)。
25
+ *
26
+ * @experimental execution 运行时面(U10① D6):一个 minor 周期内允许签名微调。 */
25
27
  export const SUBAGENT_RECORD_CUSTOM_TYPE = "subagent-record";
26
28
 
27
29
  /**
@@ -34,6 +36,8 @@ export const SUBAGENT_RECORD_CUSTOM_TYPE = "subagent-record";
34
36
  * - worktreeHandle:不可 JSON 序列化的运行时句柄(布尔投影 worktree 保留)。
35
37
  *
36
38
  * undefined 字段经 JSON.stringify 自然缺省(与 SubagentRecord 重建侧语义一致)。
39
+ *
40
+ * @experimental execution 运行时面(U10① D6):一个 minor 周期内允许签名微调。
37
41
  */
38
42
  export interface SubagentRecordEntryData {
39
43
  /** schema 版本(W16 起 v1)。消费方按 v 判别解析,不认识的版本跳过而非猜测。 */
@@ -95,7 +99,9 @@ export interface SubagentRecordEntryData {
95
99
  engineHandle?: { sessionRef: Record<string, string>; journalPath?: string; poolKey: string };
96
100
  }
97
101
 
98
- /** SubagentRecord → 自描述 entry data(快照投影,不 mutate 源)。 */
102
+ /** SubagentRecord → 自描述 entry data(快照投影,不 mutate 源)。
103
+ *
104
+ * @experimental execution 运行时面(U10① D6):一个 minor 周期内允许签名微调。 */
99
105
  export function toSubagentRecordEntry(record: SubagentRecord): SubagentRecordEntryData {
100
106
  return {
101
107
  v: 1,