@zhushanwen/pi-subagent-workflow 8.5.0 → 8.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (336) hide show
  1. package/package.json +13 -16
  2. package/relay/relay.mjs +1 -1
  3. package/src/host/__tests__/pi-host.test.ts +214 -0
  4. package/src/host/pi-host.ts +109 -0
  5. package/src/index.ts +142 -32
  6. package/src/injectors/__tests__/engine-awareness.test.ts +292 -0
  7. package/src/injectors/__tests__/engine-section-stability.test.ts +341 -0
  8. package/src/injectors/__tests__/subagent-list-injector.test.ts +5 -5
  9. package/src/injectors/__tests__/workflow-list-injector.test.ts +5 -5
  10. package/src/injectors/engine-awareness.ts +123 -0
  11. package/src/injectors/model-list-injector.ts +1 -1
  12. package/src/injectors/subagent-list-injector.ts +8 -8
  13. package/src/injectors/workflow-list-injector.ts +8 -8
  14. package/src/interface/__tests__/detectors.test.ts +3 -3
  15. package/src/interface/__tests__/subagent-tool-path-guard.test.ts +157 -0
  16. package/src/interface/__tests__/subagent-tool-prompt.test.ts +12 -0
  17. package/src/interface/__tests__/tool-workflow-script-generate.test.ts +2 -2
  18. package/src/interface/__tests__/tool-workflow-throw-paths.test.ts +1 -1
  19. package/src/interface/bg-notify-render.ts +34 -13
  20. package/src/interface/commands.ts +3 -3
  21. package/src/interface/format.ts +2 -2
  22. package/src/interface/helpers.ts +3 -3
  23. package/src/interface/list-component.ts +4 -4
  24. package/src/interface/list-shared.ts +2 -2
  25. package/src/interface/list-view.ts +2 -2
  26. package/src/interface/subagent-actions.ts +288 -19
  27. package/src/interface/subagent-tool-schema.ts +163 -0
  28. package/src/interface/subagent-tool.ts +65 -129
  29. package/src/interface/subagents.ts +5 -5
  30. package/src/interface/tool-render.ts +34 -2
  31. package/src/interface/tool-workflow-script.ts +4 -4
  32. package/src/interface/tool-workflow.ts +6 -6
  33. package/src/interface/views/WorkflowsView.ts +5 -5
  34. package/src/interface/views/__tests__/WorkflowsView-signature.test.ts +3 -3
  35. package/src/interface/views/__tests__/detail-content-session-file.test.ts +2 -2
  36. package/src/interface/views/detail-content.ts +4 -4
  37. package/src/interface/views/format.ts +4 -4
  38. package/src/{orchestration/jsonl-run-store.ts → jsonl-run-store.ts} +132 -18
  39. package/src/execution/__tests__/__fixtures__/notifier-golden-snapshots.json +0 -53
  40. package/src/execution/__tests__/__fixtures__/truncline.snapshot.json +0 -1
  41. package/src/execution/__tests__/agent-registry.test.ts +0 -363
  42. package/src/execution/__tests__/agent-result-mapper.test.ts +0 -150
  43. package/src/execution/__tests__/alive-store.test.ts +0 -147
  44. package/src/execution/__tests__/ask-user-transit-e2e.test.ts +0 -490
  45. package/src/execution/__tests__/before-agent-start-injection.test.ts +0 -132
  46. package/src/execution/__tests__/bg-notify-render.test.ts +0 -256
  47. package/src/execution/__tests__/channel-registry-handshake.test.ts +0 -243
  48. package/src/execution/__tests__/chat-engine-routing.test.ts +0 -597
  49. package/src/execution/__tests__/chatmode-first-round-closure-service.test.ts +0 -365
  50. package/src/execution/__tests__/chatmode-first-round-closure-spawn.test.ts +0 -190
  51. package/src/execution/__tests__/chatmode-round-notify-real-chain.test.ts +0 -215
  52. package/src/execution/__tests__/concurrency-pool.test.ts +0 -250
  53. package/src/execution/__tests__/config.test.ts +0 -110
  54. package/src/execution/__tests__/conversation-wiring.test.ts +0 -198
  55. package/src/execution/__tests__/crash-recovery.test.ts +0 -320
  56. package/src/execution/__tests__/delivery-methods.test.ts +0 -385
  57. package/src/execution/__tests__/dialog-queue.test.ts +0 -299
  58. package/src/execution/__tests__/epipe-fallback.test.ts +0 -241
  59. package/src/execution/__tests__/execute-and-await-worktree.test.ts +0 -243
  60. package/src/execution/__tests__/execute-nesting.test.ts +0 -323
  61. package/src/execution/__tests__/execute-options-mapper.test.ts +0 -189
  62. package/src/execution/__tests__/execution-record.test.ts +0 -1284
  63. package/src/execution/__tests__/finalize-record.test.ts +0 -368
  64. package/src/execution/__tests__/finalized-marker.test.ts +0 -82
  65. package/src/execution/__tests__/format-schema-instruction.test.ts +0 -138
  66. package/src/execution/__tests__/format.test.ts +0 -458
  67. package/src/execution/__tests__/gc-timer.test.ts +0 -184
  68. package/src/execution/__tests__/get-record-for-action-restart.test.ts +0 -254
  69. package/src/execution/__tests__/gui-mode-dispatch.test.ts +0 -59
  70. package/src/execution/__tests__/helpers/mock-extension-api.ts +0 -30
  71. package/src/execution/__tests__/helpers/spawn-mock.ts +0 -238
  72. package/src/execution/__tests__/host-mode.test.ts +0 -87
  73. package/src/execution/__tests__/index-session-start-identity.test.ts +0 -371
  74. package/src/execution/__tests__/index-session-start.test.ts +0 -593
  75. package/src/execution/__tests__/lifecycle-manager-lock.test.ts +0 -211
  76. package/src/execution/__tests__/lifecycle-manager.test.ts +0 -337
  77. package/src/execution/__tests__/lifecycle-predicates.test.ts +0 -116
  78. package/src/execution/__tests__/list-component.test.ts +0 -401
  79. package/src/execution/__tests__/list-fields.test.ts +0 -109
  80. package/src/execution/__tests__/manifest-parentid.test.ts +0 -117
  81. package/src/execution/__tests__/model-resolver.test.ts +0 -413
  82. package/src/execution/__tests__/nested-visibility-env-propagation.test.ts +0 -287
  83. package/src/execution/__tests__/nested-visibility.test.ts +0 -325
  84. package/src/execution/__tests__/notifier-flush.test.ts +0 -574
  85. package/src/execution/__tests__/notifier-golden-snapshot.test.ts +0 -261
  86. package/src/execution/__tests__/one-shot-upgrade.test.ts +0 -205
  87. package/src/execution/__tests__/output-collector.test.ts +0 -61
  88. package/src/execution/__tests__/parent-child-matrix.test.ts +0 -336
  89. package/src/execution/__tests__/path-encoding.test.ts +0 -104
  90. package/src/execution/__tests__/pi-invocation.test.ts +0 -134
  91. package/src/execution/__tests__/record-store.test.ts +0 -1057
  92. package/src/execution/__tests__/records-cwd-isolation.test.ts +0 -91
  93. package/src/execution/__tests__/recursive-visibility-baseline.test.ts +0 -339
  94. package/src/execution/__tests__/recursive-visibility-env.test.ts +0 -273
  95. package/src/execution/__tests__/relay-agent.test.ts +0 -448
  96. package/src/execution/__tests__/relay-env.test.ts +0 -42
  97. package/src/execution/__tests__/resource-policy.test.ts +0 -109
  98. package/src/execution/__tests__/rpc-mode.test.ts +0 -89
  99. package/src/execution/__tests__/run-and-finalize-chatmode.test.ts +0 -267
  100. package/src/execution/__tests__/run-spawn-chatmode-settled.test.ts +0 -253
  101. package/src/execution/__tests__/run-spawn-edges.test.ts +0 -644
  102. package/src/execution/__tests__/run-spawn-integration.test.ts +0 -933
  103. package/src/execution/__tests__/run-spawn-resume.test.ts +0 -322
  104. package/src/execution/__tests__/run-spawn-rpc-mode.test.ts +0 -196
  105. package/src/execution/__tests__/sdk-contract.test.ts +0 -273
  106. package/src/execution/__tests__/session-context-resolver.test.ts +0 -167
  107. package/src/execution/__tests__/session-file-gc.test.ts +0 -293
  108. package/src/execution/__tests__/session-pending.test.ts +0 -197
  109. package/src/execution/__tests__/session-reconstructor.test.ts +0 -379
  110. package/src/execution/__tests__/session-runner-epipe.test.ts +0 -178
  111. package/src/execution/__tests__/session-runner-schema-env.test.ts +0 -311
  112. package/src/execution/__tests__/session-start-reaper.test.ts +0 -233
  113. package/src/execution/__tests__/spawn-args.test.ts +0 -434
  114. package/src/execution/__tests__/spawn-event-adapter-rpc.test.ts +0 -189
  115. package/src/execution/__tests__/spawn-event-adapter.test.ts +0 -167
  116. package/src/execution/__tests__/spawn-worktree-guidance.test.ts +0 -207
  117. package/src/execution/__tests__/spawned-children.test.ts +0 -92
  118. package/src/execution/__tests__/startup-config-declaration.test.ts +0 -35
  119. package/src/execution/__tests__/status-refactor.test.ts +0 -345
  120. package/src/execution/__tests__/stdin-writer.test.ts +0 -463
  121. package/src/execution/__tests__/stream-sink-retirement.test.ts +0 -261
  122. package/src/execution/__tests__/subagent-service-abort.test.ts +0 -60
  123. package/src/execution/__tests__/subagent-service-message-close.test.ts +0 -629
  124. package/src/execution/__tests__/subagent-service-parent-guard.test.ts +0 -180
  125. package/src/execution/__tests__/subagent-service.test.ts +0 -788
  126. package/src/execution/__tests__/subprocess-agent-runner-routing.test.ts +0 -310
  127. package/src/execution/__tests__/subprocess-agent-runner.test.ts +0 -506
  128. package/src/execution/__tests__/temp-prompt.test.ts +0 -53
  129. package/src/execution/__tests__/timeout-integration.test.ts +0 -397
  130. package/src/execution/__tests__/tombstone-store.test.ts +0 -73
  131. package/src/execution/__tests__/tool-action.test.ts +0 -385
  132. package/src/execution/__tests__/truncline-snapshot.test.ts +0 -81
  133. package/src/execution/__tests__/turn-limiter-semantics.test.ts +0 -194
  134. package/src/execution/__tests__/turn-limiter.test.ts +0 -65
  135. package/src/execution/__tests__/ui-channels.test.ts +0 -187
  136. package/src/execution/__tests__/ui-interaction-model.test.ts +0 -67
  137. package/src/execution/__tests__/ui-request-handler-factory.test.ts +0 -370
  138. package/src/execution/__tests__/ui-request-handler.test.ts +0 -204
  139. package/src/execution/__tests__/ui-request-observability.test.ts +0 -114
  140. package/src/execution/__tests__/ui-request-queue.test.ts +0 -133
  141. package/src/execution/__tests__/worktree-manager.test.ts +0 -633
  142. package/src/execution/__tests__/worktree-pid-registration.integration.test.ts +0 -229
  143. package/src/execution/__tests__/worktree-reconcile.integration.test.ts +0 -181
  144. package/src/execution/__tests__/worktree-registry.test.ts +0 -199
  145. package/src/execution/agent-registry.ts +0 -193
  146. package/src/execution/agent-result-mapper.ts +0 -90
  147. package/src/execution/alive-store.ts +0 -92
  148. package/src/execution/argv-mirror.ts +0 -109
  149. package/src/execution/best-effort.ts +0 -38
  150. package/src/execution/channel-registry-access.ts +0 -144
  151. package/src/execution/concurrency-pool.ts +0 -116
  152. package/src/execution/config.ts +0 -96
  153. package/src/execution/dialog-queue.ts +0 -330
  154. package/src/execution/engine/__tests__/common/data-dir.test.ts +0 -53
  155. package/src/execution/engine/__tests__/common/errors.test.ts +0 -132
  156. package/src/execution/engine/__tests__/common/event-journal.test.ts +0 -177
  157. package/src/execution/engine/__tests__/common/kill-chain.test.ts +0 -192
  158. package/src/execution/engine/__tests__/common/nesting-guard.test.ts +0 -81
  159. package/src/execution/engine/__tests__/common/persona-router.test.ts +0 -123
  160. package/src/execution/engine/__tests__/common/pool-manager.test.ts +0 -154
  161. package/src/execution/engine/__tests__/common/schema-emulation.test.ts +0 -128
  162. package/src/execution/engine/__tests__/conformance/__fixtures__/pi-golden-events.json +0 -28
  163. package/src/execution/engine/__tests__/conformance/agent-event-invariants.ts +0 -141
  164. package/src/execution/engine/__tests__/conformance/contract.abort.test.ts +0 -109
  165. package/src/execution/engine/__tests__/conformance/contract.agent-events.test.ts +0 -101
  166. package/src/execution/engine/__tests__/conformance/contract.probe.test.ts +0 -77
  167. package/src/execution/engine/__tests__/conformance/contract.read-degradation.test.ts +0 -104
  168. package/src/execution/engine/__tests__/conformance/contract.relay.test.ts +0 -342
  169. package/src/execution/engine/__tests__/conformance/engine-conformance.live.test.ts +0 -201
  170. package/src/execution/engine/__tests__/conformance/golden-replay.pi.test.ts +0 -76
  171. package/src/execution/engine/__tests__/conformance/golden-replay.zcode.test.ts +0 -79
  172. package/src/execution/engine/__tests__/engine-discovery.test.ts +0 -87
  173. package/src/execution/engine/__tests__/engines-declaration.test.ts +0 -36
  174. package/src/execution/engine/__tests__/model-prompt.test.ts +0 -85
  175. package/src/execution/engine/__tests__/paths.test.ts +0 -39
  176. package/src/execution/engine/__tests__/registry.test.ts +0 -120
  177. package/src/execution/engine/__tests__/routing.test.ts +0 -231
  178. package/src/execution/engine/common/data-dir.ts +0 -62
  179. package/src/execution/engine/common/errors.ts +0 -183
  180. package/src/execution/engine/common/event-journal.ts +0 -254
  181. package/src/execution/engine/common/journal-replay.ts +0 -62
  182. package/src/execution/engine/common/kill-chain.ts +0 -221
  183. package/src/execution/engine/common/nesting-guard.ts +0 -50
  184. package/src/execution/engine/common/persona-router.ts +0 -108
  185. package/src/execution/engine/common/pool-manager.ts +0 -226
  186. package/src/execution/engine/common/schema-emulation.ts +0 -189
  187. package/src/execution/engine/common/session-view-projection.ts +0 -51
  188. package/src/execution/engine/engine-discovery.ts +0 -65
  189. package/src/execution/engine/engines/pi/__tests__/pi-engine.test.ts +0 -469
  190. package/src/execution/engine/engines/pi/__tests__/reader.test.ts +0 -155
  191. package/src/execution/engine/engines/pi/__tests__/task-spec-mapper.test.ts +0 -164
  192. package/src/execution/engine/engines/pi/pi-engine.ts +0 -415
  193. package/src/execution/engine/engines/pi/reader.ts +0 -48
  194. package/src/execution/engine/engines/pi/registration.ts +0 -35
  195. package/src/execution/engine/engines/pi/task-spec-mapper.ts +0 -100
  196. package/src/execution/engine/engines/zcode/__tests__/__fixtures__/zcode-golden-spawn.json +0 -39
  197. package/src/execution/engine/engines/zcode/__tests__/launcher.test.ts +0 -150
  198. package/src/execution/engine/engines/zcode/__tests__/parser.test.ts +0 -246
  199. package/src/execution/engine/engines/zcode/__tests__/preparer.test.ts +0 -228
  200. package/src/execution/engine/engines/zcode/__tests__/reader.test.ts +0 -210
  201. package/src/execution/engine/engines/zcode/__tests__/registration.test.ts +0 -64
  202. package/src/execution/engine/engines/zcode/__tests__/zcode-engine.live.test.ts +0 -127
  203. package/src/execution/engine/engines/zcode/__tests__/zcode-engine.test.ts +0 -567
  204. package/src/execution/engine/engines/zcode/constants.ts +0 -43
  205. package/src/execution/engine/engines/zcode/golden-sample.ts +0 -39
  206. package/src/execution/engine/engines/zcode/launcher.ts +0 -161
  207. package/src/execution/engine/engines/zcode/parser.ts +0 -436
  208. package/src/execution/engine/engines/zcode/preparer.ts +0 -363
  209. package/src/execution/engine/engines/zcode/reader.ts +0 -381
  210. package/src/execution/engine/engines/zcode/registration.ts +0 -37
  211. package/src/execution/engine/engines/zcode/zcode-engine.ts +0 -648
  212. package/src/execution/engine/host-task-spec.ts +0 -47
  213. package/src/execution/engine/model-prompt.ts +0 -59
  214. package/src/execution/engine/paths.ts +0 -42
  215. package/src/execution/engine/port.ts +0 -153
  216. package/src/execution/engine/registry.ts +0 -123
  217. package/src/execution/engine/routing.ts +0 -218
  218. package/src/execution/engine/types.ts +0 -304
  219. package/src/execution/execute-options-mapper.ts +0 -111
  220. package/src/execution/execution-record.ts +0 -906
  221. package/src/execution/finalize-record.ts +0 -251
  222. package/src/execution/finalized-marker.ts +0 -51
  223. package/src/execution/get-state-handshake.ts +0 -104
  224. package/src/execution/host-mode.ts +0 -56
  225. package/src/execution/idle-gc.ts +0 -47
  226. package/src/execution/lifecycle-manager.ts +0 -491
  227. package/src/execution/lifecycle-predicates.ts +0 -65
  228. package/src/execution/manifest-store.ts +0 -256
  229. package/src/execution/model-config-service.ts +0 -204
  230. package/src/execution/model-resolver.ts +0 -276
  231. package/src/execution/notifier.ts +0 -260
  232. package/src/execution/output-collector.ts +0 -88
  233. package/src/execution/path-encoding.ts +0 -60
  234. package/src/execution/pi-invocation.ts +0 -120
  235. package/src/execution/record-entry.ts +0 -132
  236. package/src/execution/record-store.ts +0 -1234
  237. package/src/execution/relay-env.ts +0 -37
  238. package/src/execution/session-context-resolver.ts +0 -64
  239. package/src/execution/session-file-gc.ts +0 -120
  240. package/src/execution/session-pending.ts +0 -164
  241. package/src/execution/session-reconstructor.ts +0 -678
  242. package/src/execution/session-runner.ts +0 -1548
  243. package/src/execution/sessions-index.ts +0 -304
  244. package/src/execution/spawn-event-adapter.ts +0 -363
  245. package/src/execution/stdin-writer.ts +0 -198
  246. package/src/execution/stream-sink.ts +0 -126
  247. package/src/execution/subagent-service.ts +0 -2119
  248. package/src/execution/subprocess-agent-runner.ts +0 -303
  249. package/src/execution/temp-prompt.ts +0 -62
  250. package/src/execution/tombstone-store.ts +0 -72
  251. package/src/execution/turn-limiter.ts +0 -102
  252. package/src/execution/types.ts +0 -910
  253. package/src/execution/ui-channels.ts +0 -216
  254. package/src/execution/ui-interaction-model.ts +0 -48
  255. package/src/execution/ui-request-handler-factory.ts +0 -212
  256. package/src/execution/ui-request-observability.ts +0 -81
  257. package/src/execution/ui-request-queue.ts +0 -174
  258. package/src/execution/worktree-manager.ts +0 -688
  259. package/src/execution/worktree-registry.ts +0 -222
  260. package/src/orchestration/__tests__/__fixtures__/worker-template.snapshot.txt +0 -336
  261. package/src/orchestration/__tests__/agent-call-catch-fallback.test.ts +0 -208
  262. package/src/orchestration/__tests__/agent-call-stream.test.ts +0 -157
  263. package/src/orchestration/__tests__/args-validator.test.ts +0 -143
  264. package/src/orchestration/__tests__/concurrency-gate.test.ts +0 -125
  265. package/src/orchestration/__tests__/config-loader.test.ts +0 -503
  266. package/src/orchestration/__tests__/error-recovery-handlers.test.ts +0 -724
  267. package/src/orchestration/__tests__/error-recovery-postmessage-defense.test.ts +0 -405
  268. package/src/orchestration/__tests__/error-recovery-serialize-failed-result.test.ts +0 -56
  269. package/src/orchestration/__tests__/error-recovery-workflow-call.test.ts +0 -166
  270. package/src/orchestration/__tests__/execute-agent-call.test.ts +0 -314
  271. package/src/orchestration/__tests__/jsonl-run-store-loadall-sources.test.ts +0 -171
  272. package/src/orchestration/__tests__/jsonl-run-store-session-file.test.ts +0 -1030
  273. package/src/orchestration/__tests__/launcher-nested-workflow.test.ts +0 -277
  274. package/src/orchestration/__tests__/lifecycle-runid-injection.test.ts +0 -96
  275. package/src/orchestration/__tests__/lifecycle.test.ts +0 -625
  276. package/src/orchestration/__tests__/review-fix-loop-e2e.test.ts +0 -1971
  277. package/src/orchestration/__tests__/script-lint.test.ts +0 -831
  278. package/src/orchestration/__tests__/skill-discovery.test.ts +0 -157
  279. package/src/orchestration/__tests__/test-mocks.ts +0 -197
  280. package/src/orchestration/__tests__/worker-host.test.ts +0 -120
  281. package/src/orchestration/__tests__/worker-returnmeta-passthrough.test.ts +0 -164
  282. package/src/orchestration/__tests__/worker-script-builder-runtime.test.ts +0 -520
  283. package/src/orchestration/__tests__/worker-script-builder.test.ts +0 -309
  284. package/src/orchestration/__tests__/worker-script-template-snapshot.test.ts +0 -110
  285. package/src/orchestration/__tests__/workflow-nesting-e2e.test.ts +0 -317
  286. package/src/orchestration/__tests__/workflow-script-lint-memo.test.ts +0 -110
  287. package/src/orchestration/__tests__/workflows-e2e.test.ts +0 -537
  288. package/src/orchestration/agent-opts-resolver.ts +0 -93
  289. package/src/orchestration/args-validator.ts +0 -127
  290. package/src/orchestration/concurrency-gate.ts +0 -69
  291. package/src/orchestration/config-loader.ts +0 -315
  292. package/src/orchestration/error-recovery.ts +0 -862
  293. package/src/orchestration/execute-agent-call.ts +0 -217
  294. package/src/orchestration/launcher.ts +0 -410
  295. package/src/orchestration/lifecycle.ts +0 -396
  296. package/src/orchestration/models/__tests__/budget.test.ts +0 -367
  297. package/src/orchestration/models/__tests__/trace.test.ts +0 -408
  298. package/src/orchestration/models/agent-call.ts +0 -83
  299. package/src/orchestration/models/budget.ts +0 -148
  300. package/src/orchestration/models/ports.ts +0 -164
  301. package/src/orchestration/models/run-runtime.ts +0 -89
  302. package/src/orchestration/models/run-spec.ts +0 -83
  303. package/src/orchestration/models/run-state.ts +0 -44
  304. package/src/orchestration/models/trace.ts +0 -183
  305. package/src/orchestration/models/types.ts +0 -292
  306. package/src/orchestration/models/workflow-run.ts +0 -254
  307. package/src/orchestration/models/workflow-script-registry.ts +0 -35
  308. package/src/orchestration/models/workflow-script.ts +0 -117
  309. package/src/orchestration/script-lint.ts +0 -766
  310. package/src/orchestration/skill-discovery.ts +0 -92
  311. package/src/orchestration/worker-handle.ts +0 -115
  312. package/src/orchestration/worker-host.ts +0 -98
  313. package/src/orchestration/worker-script-builder.ts +0 -408
  314. package/src/orchestration/workflow-files.ts +0 -85
  315. package/src/orchestration/workflow-script-registry-impl.ts +0 -133
  316. package/src/shared/__tests__/agent-ref.test.ts +0 -34
  317. package/src/shared/__tests__/meta-parser.test.ts +0 -304
  318. package/src/shared/__tests__/resource-discovery-manifest-cache.test.ts +0 -280
  319. package/src/shared/__tests__/resource-discovery.test.ts +0 -736
  320. package/src/shared/__tests__/resource-meta.test.ts +0 -51
  321. package/src/shared/__tests__/schema-jsonify.test.ts +0 -81
  322. package/src/shared/agent-event.ts +0 -13
  323. package/src/shared/agent-ref.ts +0 -57
  324. package/src/shared/meta-parser.ts +0 -261
  325. package/src/shared/resource-discovery.ts +0 -811
  326. package/src/shared/resource-meta.ts +0 -65
  327. package/src/shared/schema-jsonify.ts +0 -56
  328. package/src/shared/xml-injection.ts +0 -35
  329. package/workflows/README.md +0 -81
  330. package/workflows/_shared/agent-refs.cjs +0 -40
  331. package/workflows/chain.js +0 -131
  332. package/workflows/map-reduce.js +0 -174
  333. package/workflows/parallel.js +0 -148
  334. package/workflows/review-fix-loop-utils.cjs +0 -1307
  335. package/workflows/review-fix-loop.js +0 -1396
  336. package/workflows/scatter-gather.js +0 -178
@@ -11,10 +11,10 @@
11
11
  import type { Component } from "@earendil-works/pi-tui";
12
12
  import { visibleWidth } from "@earendil-works/pi-tui";
13
13
 
14
- import { computeElapsedSeconds } from "../execution/execution-record.ts";
15
- import type { SubagentService } from "../execution/subagent-service.ts";
16
- import type { SubagentRecord } from "../execution/types.ts";
17
- import { displayAgentName } from "../shared/agent-ref.ts";
14
+ import { computeElapsedSeconds } from "@zhushanwen/subagent-core/execution/execution-record.ts";
15
+ import type { SubagentService } from "@zhushanwen/subagent-core/execution/subagent-service.ts";
16
+ import type { SubagentRecord } from "@zhushanwen/subagent-core/execution/types.ts";
17
+ import { displayAgentName } from "@zhushanwen/subagent-core/shared/agent-ref.ts";
18
18
  import {
19
19
  firstLine,
20
20
  formatDisplayItem,
@@ -6,8 +6,8 @@
6
6
  // 按键处理 processKey 仍留 list-view,由 list-view factory 注入给组件(KeyHandler 类型在此),
7
7
  // 故 list-component 无需 import list-view,循环消除。
8
8
 
9
- import type { SubagentService } from "../execution/subagent-service.ts";
10
- import type { SubagentRecord } from "../execution/types.ts";
9
+ import type { SubagentService } from "@zhushanwen/subagent-core/execution/subagent-service.ts";
10
+ import type { SubagentRecord } from "@zhushanwen/subagent-core/execution/types.ts";
11
11
 
12
12
  // ============================================================
13
13
  // 常量
@@ -35,8 +35,8 @@
35
35
  import { matchesKey } from "@earendil-works/pi-tui";
36
36
  import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
37
37
 
38
- import type { SubagentService } from "../execution/subagent-service.ts";
39
- import type { SubagentRecord } from "../execution/types.ts";
38
+ import type { SubagentService } from "@zhushanwen/subagent-core/execution/subagent-service.ts";
39
+ import type { SubagentRecord } from "@zhushanwen/subagent-core/execution/types.ts";
40
40
  import { type ThemeLike } from "./format.ts";
41
41
  import { SubagentsListComponent } from "./list-component.ts";
42
42
  import {
@@ -16,25 +16,27 @@ import {
16
16
  isGuiCapable,
17
17
  } from "@xyz-agent/extension-protocol";
18
18
 
19
- import { SLUG_MAX_LENGTH } from "../execution/execute-options-mapper.ts";
20
- import { computeElapsedSeconds } from "../execution/execution-record.ts";
21
- import { isResumable } from "../execution/lifecycle-predicates.ts";
22
- import type { ExecutionRecord } from "../execution/types.ts";
23
- import type { ModelInfo } from "../execution/model-resolver.ts";
24
- import type { SubagentService } from "../execution/subagent-service.ts";
25
- import { displayAgentName } from "../shared/agent-ref.ts";
19
+ import { SLUG_MAX_LENGTH } from "@zhushanwen/subagent-core/execution/execute-options-mapper.ts";
20
+ import { computeElapsedSeconds, projectOutcome } from "@zhushanwen/subagent-core/execution/execution-record.ts";
21
+ import { isResumable } from "@zhushanwen/subagent-core/execution/lifecycle-predicates.ts";
22
+ import type { ExecutionRecord } from "@zhushanwen/subagent-core/execution/types.ts";
23
+ import type { ModelInfo } from "@zhushanwen/subagent-core/execution/model-resolver.ts";
24
+ import type { SubagentService } from "@zhushanwen/subagent-core/execution/subagent-service.ts";
25
+ import { displayAgentName } from "@zhushanwen/subagent-core/shared/agent-ref.ts";
26
26
  import type {
27
27
  BgResponse,
28
28
  CancelResponse,
29
29
  CloseResponse,
30
30
  ExecutionStatus,
31
31
  ExternalState,
32
+ ForkFromResponse,
32
33
  ListResponse,
33
34
  MessageResponse,
34
35
  SubagentListItem,
35
36
  SubagentRecord,
36
37
  SubagentToolResult,
37
- } from "../execution/types.ts";
38
+ } from "@zhushanwen/subagent-core/execution/types.ts";
39
+ import { ResurrectDeniedError } from "@zhushanwen/subagent-core/execution/types.ts";
38
40
  import { mapRunIcon, mapRunStatus } from "./gui-mappers.ts";
39
41
 
40
42
  // ============================================================
@@ -49,6 +51,9 @@ const MAX_LIST_LIMIT = 100;
49
51
  /** background 启动提示文案(spec FR-3 bgResponse.message)。 */
50
52
  const BG_MESSAGE = "detached, will notify on completion (auto-injected message, do not poll)";
51
53
 
54
+ /** 通知投递契约回显恒值(U1 预置,U2 账本兑现,见 BgResponse.notifyContract)。 */
55
+ const NOTIFY_CONTRACT = "ledger+at-least-once" as const;
56
+
52
57
  /** subagentId(UUID)在 GUI header 的截断显示长度。 */
53
58
  const SUBAGENT_ID_PREVIEW = 8;
54
59
 
@@ -79,7 +84,10 @@ export interface StartHandlerInput {
79
84
  cwd?: string;
80
85
  /** 可持续对话模式(true = chatMode,轮次完成进 idle 等续聊)。 */
81
86
  conversation?: boolean;
82
- /** 空闲超时毫秒数(仅 conversation 模式有意义,覆盖默认 5min)。 */
87
+ /**
88
+ * 空闲超时毫秒数(仅 conversation 模式有意义,覆盖默认 5min)。
89
+ * 显式传 0/负数 = 禁用 idle GC(不挂 timer);不传走 env/默认优先级。
90
+ */
83
91
  idleTimeoutMs?: number;
84
92
  /** 执行引擎(D4 三层路由第一层:本参数 > agent frontmatter engine > config defaultEngine)。 */
85
93
  engine?: string;
@@ -92,6 +100,11 @@ export type StartHandlerResult = {
92
100
  sessionFile: string | undefined;
93
101
  /** 短标签,来自 record(handle.details.slug)。用于 result 行展示。 */
94
102
  slug: string;
103
+ /**
104
+ * registry 全等回显(U1):handle.details.model = record.model = `${provider}/${id}`,
105
+ * 源头是 resolveModel 裁决放行的条目——通过校验 = 子进程必然按此名执行。
106
+ */
107
+ model: string;
95
108
  response: BgResponse;
96
109
  };
97
110
 
@@ -115,6 +128,100 @@ export interface CancelHandlerResult {
115
128
  response: CancelResponse;
116
129
  }
117
130
 
131
+ // ============================================================
132
+ // [v8.5 A1/B] message 拒绝文案分流 + fork-from 引导语框架
133
+ // ============================================================
134
+
135
+ /**
136
+ * [v8.5 A1] message 拒绝时的可行动文案分流。
137
+ *
138
+ * 背景:getRecordForAction 冷查只认 status==='running'(SP-2 可续聊重建),任何终态/
139
+ * 异归属记录都落到同一个「not found or not owned」错误,把两类完全不同的场景混为一谈:
140
+ * - user-close/cancelled:用户主动告别,记录真没了 → 引导 start 新的
141
+ * - parent-shutdown/gc/orphan 等:父会话重启/进程退出导致的断联,对话 jsonl 完好,
142
+ * resume/fork 基建现成 → 引导 fork-from 从旧记录接续
143
+ *
144
+ * 分流规则(消费方是 LLM,保持正交简单):
145
+ * - 找不到记录 → 原样透传 getRecordForAction 错误(id 打错最常见,原文案最准)
146
+ * - closed + user-close/cancelled →「已主动关闭,无法续聊」文案
147
+ * - 其余(closed 其他 reason / running 但异归属)→「断联可接续」文案
148
+ */
149
+ export function endedMessageGuard(service: SubagentService, id: string, original: unknown): Error {
150
+ // [v8.5 D] 透明重生守卫拒绝(worktree/异进程占用)原样透传——错误自带完整行动语言,
151
+ // 若被下方 fork-from 指引改写会误导 agent 走已被判死的通道(types.ts 契约声明)。
152
+ if (original instanceof ResurrectDeniedError) return original;
153
+ let snap: SubagentRecord | undefined;
154
+ try {
155
+ snap = service.lookupRecordAnyState(id);
156
+ } catch {
157
+ snap = undefined;
158
+ }
159
+ if (!snap) {
160
+ return original instanceof Error ? original : new Error(String(original));
161
+ }
162
+ if (snap.status === "closed") {
163
+ if (snap.closedReason === "cancelled" || snap.closedReason === "user-close") {
164
+ return new Error(
165
+ `subagent ${id} was deliberately closed by user (closedReason: ${snap.closedReason}) — ` +
166
+ `it cannot be messaged or resumed; nothing can reattach to it. ` +
167
+ `Recovery: start a new subagent (action:'start'); use action:'list' with includeFinished:true to review its final output.`,
168
+ );
169
+ }
170
+ return new Error(
171
+ `subagent ${id} is ended but reconnectable (closedReason: ${snap.closedReason ?? "unknown"}` +
172
+ `${describeClosedContext(snap)}). Its conversation history is intact at ${snap.sessionFile ?? "(session file unavailable)"}. ` +
173
+ `Recovery: resume from that history with {"action":"fork-from","forkFromParam":{"sourceSubagentId":"${id}"}}, ` +
174
+ `or read key points directly from the session file.`,
175
+ );
176
+ }
177
+ // running 但未通过 getRecordForAction:记录属于当前进程外的另一个 session 树
178
+ //(主会话重启前的遗留态,或其他并发 pi 进程的活跃 subagent)。无论哪种,历史
179
+ // jsonl 只读安全,fork-from 快照接续均有效。
180
+ return new Error(
181
+ `subagent ${id} is alive but belongs to a different session tree than this one` +
182
+ `${describeClosedContext(snap)}. You cannot message it from here. ` +
183
+ `Recovery: branch from its history with {"action":"fork-from","forkFromParam":{"sourceSubagentId":"${id}"}}` +
184
+ `${snap.sessionFile ? ` (source session: ${snap.sessionFile})` : ``}; otherwise start a new subagent.`,
185
+ );
186
+ }
187
+
188
+ /** 终态快照的上下文人话短语(仅作补充描述,主分支逻辑在 endedMessageGuard)。 */
189
+ function describeClosedContext(r: SubagentRecord): string {
190
+ switch (r.closedReason) {
191
+ case "parent-shutdown":
192
+ return " — it was disconnected when the previous parent session exited";
193
+ case "parent-fork":
194
+ return " — it was detached when the previous parent session forked";
195
+ case "parent-new":
196
+ return " — it was detached when the previous parent session switched";
197
+ case "disconnected":
198
+ return " — it ended in a previous session (exact cause unknown)";
199
+ default:
200
+ return "";
201
+ }
202
+ }
203
+
204
+ /**
205
+ * [v8.5 B] fork-from 开场引导语框架(prompt 未指定时注入):先从继承的历史重建状态
206
+ * 再继续,防猜。
207
+ */
208
+ const FORK_FROM_DEFAULT_PROMPT =
209
+ "You are taking over work from a previous subagent whose full conversation history you inherited (--fork). " +
210
+ "First reconstruct state from that history: list what was already done, decided, and left unfinished (a few bullet lines). " +
211
+ "Then continue the remaining work to completion.";
212
+
213
+ /**
214
+ * [v8.5 B] 有显式接续指令时的包裹框架:指令在前、上下文重建要求在后——指令首见即达,
215
+ * 不湮没在元说明里(弱模型友好)。
216
+ */
217
+ function wrapForkFromPrompt(prompt: string): string {
218
+ return (
219
+ prompt.trim() +
220
+ "\n\n(You are continuing a previous subagent's inherited conversation via --fork. " +
221
+ "Reconstruct state from that history first — what was done, decided, and remains — then execute the instruction above.)"
222
+ );
223
+ }
224
+
118
225
  // ============================================================
119
226
  // helpers(模块内)
120
227
  // ============================================================
@@ -154,9 +261,11 @@ export function mapExternalState(status: ExecutionStatus): ExternalState {
154
261
  }
155
262
 
156
263
  /** SubagentRecord → SubagentListItem(state 四态主字段 + status 调试字段,duration 实时计算)。
157
- * [v4 A-6] 新增 parent/resumable/closedReason:parent 从 record.parentRecordId 派生
158
- * (配合 A-5 直接父守卫),resumable 从 isResumable 派生(B-1「可续聊」对外表达),
159
- * closedReason 透传(SP-4 级联关闭告知替代 before_agent_start 注入)。
264
+ * [v4 A-6] 新增 parent/resumable:parent 从 record.parentRecordId 派生(配合 A-5 直接父
265
+ * 守卫),resumable 从 isResumable 派生(B-1「可续聊」对外表达)。
266
+ * [U3 C-outcome] 新增 outcome 一等终态语义(projectOutcome 唯一出口);closedReason
267
+ * 退出对外 JSON(保留为 record 内部诊断字段),对外成败判读收口到 outcome,消费方
268
+ * 零手写推导(三处同构 switch 已收敛删除)。
160
269
  * agent 是 GUI/TUI list 共用的显示名——取 basename 短名(displayAgentName),
161
270
  * 完整路径保留在 record.agent(数据层)。 */
162
271
  export function recordToListItem(r: SubagentRecord): SubagentListItem {
@@ -173,7 +282,7 @@ export function recordToListItem(r: SubagentRecord): SubagentListItem {
173
282
  sessionFile: r.sessionFile,
174
283
  parent: r.parentRecordId,
175
284
  resumable: isResumable(r),
176
- closedReason: r.closedReason,
285
+ outcome: projectOutcome(r),
177
286
  };
178
287
  }
179
288
 
@@ -232,10 +341,13 @@ export async function startHandler(
232
341
  subagentId: handle.subagentId,
233
342
  sessionFile: handle.sessionFile,
234
343
  slug: handle.details.slug,
344
+ // [U1] registry 全等回显:record.model 由 resolved(裁决放行条目)拼接,原样透出。
345
+ model: handle.details.model,
235
346
  response: {
236
347
  status: "running",
237
348
  mode: "background",
238
349
  message: BG_MESSAGE,
350
+ notifyContract: NOTIFY_CONTRACT,
239
351
  },
240
352
  };
241
353
  }
@@ -370,8 +482,16 @@ export async function messageHandler(
370
482
  );
371
483
  const interrupt = input?.interrupt === true;
372
484
 
373
- // 归属守卫(决策 3):getRecordForAction 内部校验 rootSessionId
374
- const record = service.getRecordForAction(id);
485
+ // 归属守卫(决策 3):getRecordForAction 内部校验 rootSessionId
486
+ // [v8.5 A1] 拒绝时经 endedMessageGuard 分流:找不到 → 原错误;user-close/cancelled
487
+ // → 「已主动关闭」;断联/已完成/异归属 → fork-from 可行动指引。仅 message action
488
+ // 升级文案——close/cancel 维持原语义(它们不需要恢复通道)。
489
+ let record: ExecutionRecord;
490
+ try {
491
+ record = service.getRecordForAction(id, { allowReconnect: true });
492
+ } catch (err) {
493
+ throw endedMessageGuard(service, id, err);
494
+ }
375
495
 
376
496
  // SP-5 one-shot upgrade:非 chatMode 的 active record(running/idle)收到 message 时
377
497
  // 自动升级为 chatMode,后续走 deliverMessage 统一投递路径(热路径或冷路径 resume)。
@@ -446,6 +566,140 @@ export async function closeHandler(
446
566
  return { kind: "close", subagentId: id, response: { closed: true } };
447
567
  }
448
568
 
569
+ // ============================================================
570
+ // fork-from handler([v8.5 B] 断联恢复通道)
571
+ // ============================================================
572
+
573
+ export interface ForkFromHandlerInput {
574
+ sourceSubagentId?: string;
575
+ prompt?: string;
576
+ }
577
+
578
+ /** fork-from 领域对象(adapter 包成 forkFromResponse)。 */
579
+ export type ForkFromHandlerResult = {
580
+ kind: "fork-from";
581
+ /** 新 subagent 的 record id(后续续聊用 action:'message')。 */
582
+ subagentId: string;
583
+ /** 作为 --fork 继承源的旧记录 session 文件。 */
584
+ sourceSessionFile: string;
585
+ response: ForkFromResponse;
586
+ };
587
+
588
+ /**
589
+ * fork-from action handler:从旧 subagent 的会话历史 spawn 新 id 接续。
590
+ *
591
+ * 用于 subagent 因会话重启/进程退出而断联后的恢复:新进程以 --fork 指向旧 session
592
+ * 文件(copy-on-write 建分支会话),继承全部对话历史;源文件只读不续写。
593
+ * 旧记录本身不动——closed 单向状态机不变量、tryTransition 语义均不触碰。
594
+ *
595
+ * 守卫链(拒绝原因与行动语言对齐现有 MF-4 风格):
596
+ * 1. 本进程内存 running → 还活着,应走 message(防双写同一子 session 文件)
597
+ * 2. 不存在 → 引导 list 确认
598
+ * 3. 异进程活跃(磁盘 .alive pid 存活 / running 快照)→ 别处正跑,不可从此接续
599
+ * (同 id 双写风险;等待其结束或在其所属会话内操作)
600
+ * 4. tombstone/cancelled → 用户主动取消,真没了(不提供接续通道)
601
+ * 5. worktree 记录 → checkout 不可复用,fork 子进程 cwd 会回落主仓破坏隔离
602
+ * (对齐 deliverMessage 的 hadWorktree 守卫语义)
603
+ * 6. 无子 session 文件 → 无历史可继承(entry-only 孤儿:spawn 窗口期中断)
604
+ *
605
+ * @throws Error 各守卫命中 / service.execute 失败(引擎不支持等)
606
+ */
607
+ export async function forkFromHandler(
608
+ service: SubagentService,
609
+ input: ForkFromHandlerInput | undefined,
610
+ ): Promise<ForkFromHandlerResult> {
611
+ const id = input?.sourceSubagentId?.trim();
612
+ if (!id) throw new Error("forkFromParam.sourceSubagentId is required for action:'fork-from'");
613
+ const prompt = input?.prompt?.trim() ?? "";
614
+ const task = prompt ? wrapForkFromPrompt(prompt) : FORK_FROM_DEFAULT_PROMPT;
615
+
616
+ const source = assertAndLookupForkFromSource(service, id);
617
+
618
+ // slug 派生:源 slug + -resumed 后缀(截断到上限)。仅展示标签,不需唯一。
619
+ const baseSlug = (source.slug || source.agent || "resumed").slice(0, SLUG_MAX_LENGTH - "-resumed".length);
620
+ const handle = await service.execute({
621
+ task,
622
+ slug: `${baseSlug}-resumed`,
623
+ forkFromSessionFile: source.sessionFile,
624
+ });
625
+
626
+ return {
627
+ kind: "fork-from",
628
+ subagentId: handle.subagentId,
629
+ sourceSessionFile: source.sessionFile,
630
+ response: { newSubagentId: handle.subagentId, sourceSessionFile: source.sessionFile },
631
+ };
632
+ }
633
+
634
+ /** forkFromHandler 的守卫链(fork-from handler doc 的守卫 1–6 原样提取):按序校验
635
+ * 源记录可接续,命中即抛带行动语言的 Error;全部通过则返回源 SubagentRecord
636
+ * (守卫 6 已保证 sessionFile 非空,返回类型随之收窄)。 */
637
+ function assertAndLookupForkFromSource(service: SubagentService, id: string): SubagentRecord & { sessionFile: string } {
638
+ // 守卫 1:本进程内存 running —— 直接 message 即可,fork-from 会双写其 session 文件。
639
+ if (service.findRecord(id)) {
640
+ throw new Error(
641
+ `subagent ${id} is still active in this process — use action:'message' to continue it directly. ` +
642
+ `If you want a parallel branch from its history, close it first (action:'close'), then fork-from.`,
643
+ );
644
+ }
645
+
646
+ // 守卫 2:全态查找(内存 archived + 磁盘重建)。
647
+ const source = service.lookupRecordAnyState(id);
648
+ if (!source) {
649
+ throw new Error(
650
+ `No subagent record with id "${id}". It may never have existed or been garbage-collected — ` +
651
+ `use action:'list' with includeFinished:true to verify the id.`,
652
+ );
653
+ }
654
+
655
+ // 守卫 3:异进程活跃(磁盘分支 3 externalInstance = 另一进程的活 pid marker)。
656
+ // 双写防护:fork 虽 copy-on-write(历史 jsonl 只读),但源仍在异进程运行时接续容易
657
+ // 读到半截历史,等它结束再接更安全。判据只认 externalInstance(真实活 pid 探针
658
+ // 命中),不拦 status==='running' 的快照——后者含 B-1 跨重启回退重建的 running 记录
659
+ //(无活 pid,历史已完整落盘),它们正是 endedMessageGuard 指引 fork-from 的目标;
660
+ // 拦了会让 agent 在「建议 fork-from」与「fork-from 拒绝 running」两条错误间死循环。
661
+ if (source.externalInstance !== undefined) {
662
+ throw new Error(
663
+ `subagent ${id} is still running in another process (alive pid marker present). ` +
664
+ `Recovery: wait until it finishes, or operate it in its own session; then retry fork-from.`,
665
+ );
666
+ }
667
+
668
+ // 守卫 4:主动告别(cancelled tombstone / user-close 正式关闭)——close 语义无旁路:
669
+ // fork-from 与 message 一致拒绝(guard 一致性规格),文案升级为统一「主动关闭」形态
670
+ // (含 closedReason 显式列入),与 deliverMessage 的 X 分支同语系不同落地(此处强调不可 branch)。
671
+ if (source.status === "closed" && (source.closedReason === "cancelled" || source.closedReason === "user-close")) {
672
+ throw new Error(
673
+ `subagent ${id} was deliberately closed by user (closedReason: ${source.closedReason}) — ` +
674
+ `deliberately-closed records cannot be resumed or branched from; nothing can reattach to them. ` +
675
+ `Recovery: start a fresh subagent (action:'start'); use action:'list' with includeFinished:true to review its final output.`,
676
+ );
677
+ }
678
+
679
+ // 守卫 5:worktree 记录 —— WorktreeHandle 不可序列化,checkout 已被 reaper/cleanup
680
+ // 回收;fork 子进程若复用旧路径会回落主 repo(破坏文件隔离)。与 deliverMessage 的
681
+ // hadWorktree 守卫同一判据同一理由。
682
+ if (source.worktree === true) {
683
+ throw new Error(
684
+ `subagent ${id} was created with worktree isolation; that binding was lost when its parent process ended. ` +
685
+ `Resuming from its history would run outside the original worktree isolation. ` +
686
+ `Recovery: start a new subagent with action:'start' and carry over key findings manually ` +
687
+ `(read ${source.sessionFile ?? "its session file"} if needed).`,
688
+ );
689
+ }
690
+
691
+ // 守卫 6:无子 session 文件(entry-born 孤儿:spawn 窗口期中断,从未开跑)。
692
+ const sessionFile = source.sessionFile;
693
+ if (!sessionFile) {
694
+ throw new Error(
695
+ `subagent ${id} has no child session file to inherit from (it never started successfully). ` +
696
+ `Recovery: start a fresh subagent (action:'start') describing the task again.`,
697
+ );
698
+ }
699
+
700
+ return { ...source, sessionFile };
701
+ }
702
+
449
703
  // ============================================================
450
704
  // adapter(领域对象 → SubagentToolResult + {content, details})
451
705
  // ============================================================
@@ -459,7 +713,8 @@ type AdapterInput =
459
713
  | { action: "list"; domain: ListHandlerResult }
460
714
  | { action: "cancel"; domain: CancelHandlerResult }
461
715
  | { action: "message"; domain: MessageHandlerResult }
462
- | { action: "close"; domain: CloseHandlerResult };
716
+ | { action: "close"; domain: CloseHandlerResult }
717
+ | { action: "fork-from"; domain: ForkFromHandlerResult };
463
718
 
464
719
  export function adapter(
465
720
  input: AdapterInput,
@@ -471,13 +726,19 @@ export function adapter(
471
726
  const d = input.domain;
472
727
  // MF-3(决策 10 细则 4):LLM content (text) 用 null 瘦身,防诱导 agent 用 read 绕过工具
473
728
  // 直接读 session 文件。真实 sessionFile 仅 details 保留(供 GUI/程序化消费)。
474
- result = { action, subagentId: d.subagentId, sessionFile: null, slug: d.slug, bgResponse: d.response };
729
+ // [U1] model registry 全等回显(放行即全等)。
730
+ result = { action, subagentId: d.subagentId, sessionFile: null, slug: d.slug, model: d.model, bgResponse: d.response };
475
731
  } else if (action === "list") {
476
732
  result = { action, subagentId: null, sessionFile: null, listResponse: input.domain.response };
477
733
  } else if (action === "cancel") {
478
734
  result = { action, subagentId: input.domain.subagentId, sessionFile: null, cancelResponse: input.domain.response };
479
735
  } else if (action === "message") {
480
736
  result = { action, subagentId: input.domain.subagentId, sessionFile: null, messageResponse: input.domain.response };
737
+ } else if (action === "fork-from") {
738
+ // MF-3 同款瘦身:sourceSessionFile 真实路径只在 forkFromResponse 内层(LLM 接续
739
+ // 不需要拼路径,需要时 list/details 可取);顶层 sessionFile 保持 null 一致性。
740
+ const d = input.domain;
741
+ result = { action, subagentId: d.subagentId, sessionFile: null, forkFromResponse: d.response };
481
742
  } else {
482
743
  // action === "close"
483
744
  result = { action, subagentId: input.domain.subagentId, sessionFile: null, closeResponse: input.domain.response };
@@ -491,7 +752,7 @@ export function adapter(
491
752
  let detailsBase: SubagentToolResult = result;
492
753
  if (action === "start") {
493
754
  const d = input.domain;
494
- detailsBase = { action: "start", subagentId: d.subagentId, sessionFile: d.sessionFile ?? null, slug: d.slug, bgResponse: d.response };
755
+ detailsBase = { action: "start", subagentId: d.subagentId, sessionFile: d.sessionFile ?? null, slug: d.slug, model: d.model, bgResponse: d.response };
495
756
  }
496
757
 
497
758
  // GUI 协议:RPC 模式下附加结构化渲染数据(union 各成员已声明 __gui__?,无需强转)
@@ -503,7 +764,7 @@ export function adapter(
503
764
  // reminder 作为第二个 text block(独立追加,不污染 details/JSON schema)。
504
765
  // 只有 list 触发——start 的 reminder 已在 BG_MESSAGE 里;cancel 无需。
505
766
  const reminder = action === "list"
506
- ? "\n\nReminder: Subagent completion is auto-notified via injected message (deliverAs: steer). Do NOT poll in a loop — there is no poll action. Use action:'list' only when you concretely need state, then continue working or stop."
767
+ ? "\n\nReminder: Subagent completion is auto-notified via auto-injected message (turn-triggering on idle). Do NOT poll in a loop — there is no poll action. Use action:'list' only when you concretely need state, then continue working or stop." // g4-allow: 契约文案——reminder 字符串描述自动注入通道(triggerTurn 单通道,U2/D5 无 deliverAs),非实际投递调用
507
768
  : "";
508
769
 
509
770
  return {
@@ -548,6 +809,14 @@ export function buildGuiComponent(
548
809
  items: [{ label: "messaged", value: input.domain.subagentId, severity: "ok" }],
549
810
  });
550
811
  }
812
+ if (action === "fork-from") {
813
+ return guiComponent("stats-line", {
814
+ items: [
815
+ { label: "forked-from", value: input.domain.sourceSessionFile },
816
+ { label: "new subagent", value: input.domain.subagentId, severity: "ok" },
817
+ ],
818
+ });
819
+ }
551
820
  if (action === "close") {
552
821
  return guiComponent("stats-line", {
553
822
  items: [{ label: "closed", value: input.domain.subagentId, severity: "warn" }],
@@ -0,0 +1,163 @@
1
+ // src/interface/subagent-tool-schema.ts
2
+ //
3
+ // `subagent` 工具的参数 schema 纯常量叶子(零运行时依赖,先例 shared/schema-env.ts)。
4
+ //
5
+ // 抽取自 subagent-tool.ts(跨包契约另一半):subagent-tool 依赖树沉重(pi SDK /
6
+ // handler / render 链),structured-output 侧的跨包契约测试若从它 import schema
7
+ // 会把整条依赖树拖进测试进程。本模块只含 schema 常量,运行时 import 仅
8
+ // typebox(Type 构造)与 pi-ai(StringEnum helper),为 structured-output 侧
9
+ //(及任何消费者)的跨包契约测试提供稳定 import 点。
10
+ //
11
+ // [跨包契约] structured-output 的 cross-package-contract.test.ts 经真实 typebox
12
+ // 编译本 schema 并断言 required/description/enum/pattern 存活——SW 自身测试环境
13
+ // 把 typebox alias 到 mock(丢 options),SO 侧测试以真实构造为对照基准。
14
+ //
15
+ // 层归属:Interface(工具 schema 的家)。SLUG_MAX_LENGTH 权威定义回归 core 侧
16
+ // execution/execute-options-mapper.ts(subagent-core 包抽离 u1-move:core 切面不得
17
+ // 反向 import 壳侧 interface,常量随之内化)——此处 re-export 保持既有 import 路径。
18
+
19
+ import { StringEnum } from "@earendil-works/pi-ai";
20
+ import { type Static, Type } from "typebox";
21
+
22
+ import { THINKING_ORDER } from "@zhushanwen/subagent-core/shared/model-ref.ts";
23
+
24
+ import { SLUG_MAX_LENGTH } from "@zhushanwen/subagent-core/execution/execute-options-mapper.ts";
25
+
26
+ export { SLUG_MAX_LENGTH };
27
+
28
+ // Params schema(跨包契约测试的真实 typebox 校验入口)。
29
+ //
30
+ // action:"start" 的 13 字段(task/slug/agent/model/...)拍平在顶层,不再用 startParam
31
+ // 嵌套容器包。原因:弱模型(GLM/DeepSeek)信任 schema 结构信号 > 文本信号,经常省略
32
+ // startParam 嵌套层把 task/slug 直接平铺到顶层导致调用失败。拍平后 schema 结构与模型
33
+ // 的自然倾向一致,消除这层误用。task/slug 必填性由 startHandler runtime 校验(flat
34
+ // JSON Schema 无法表达「action 条件必填」)。
35
+ //
36
+ // TODO(long-term, option-A): listParam/cancelParam 仍标 Optional 也是 flat JSON Schema
37
+ // 表达「action 分发条件必填」的妥协——长期方案是拆成 3 个独立 tool
38
+ // (subagent_start / subagent_list / subagent_cancel),让每个 tool 的 schema 真实
39
+ // 反映必填性。勿在此基础上继续堆 action 条件逻辑——要加就拆 tool。
40
+ export const SubagentParams = Type.Object({
41
+ action: StringEnum(["start", "list", "cancel", "message", "close", "fork-from"], {
42
+ description: "Operation: 'start' runs a subagent, 'list' shows subagents, 'cancel' stops a background subagent, 'message' sends a follow-up to a running subagent (one-shot subagents are auto-upgraded to conversation mode on first message), 'close' ends a running subagent (conversation-mode or one-shot), 'fork-from' spawns a NEW subagent inheriting an older one's history (recovery for restart-disconnected subagents; the old record is untouched).",
43
+ }),
44
+ // ── action:"start" fields (flattened to top level). task/slug REQUIRED for start. ──
45
+ // Missing/empty task or slug throws at runtime (startHandler).
46
+ // (flat JSON Schema can't express conditional requirement — see file-level TODO.)
47
+ task: Type.Optional(Type.String({
48
+ description: "REQUIRED for action:'start'. The task for the subagent to execute. Throws if missing or whitespace-only.",
49
+ })),
50
+ slug: Type.Optional(Type.String({
51
+ description:
52
+ "REQUIRED for action:'start'. Short label (≤35 chars) for this subagent, e.g. 'fix-login', 'extract-urls'. " +
53
+ "Shown in TUI to distinguish concurrent subagents.",
54
+ maxLength: SLUG_MAX_LENGTH,
55
+ })),
56
+ agent: Type.Optional(Type.String({
57
+ description: 'Agent ref: absolute path to the agent .md file (use <location> from <available_subagents>). If omitted, defaults to "general-purpose" — a generic agent that inherits the main agent\'s model and project context. Do not invent names — only use paths from the injected list.',
58
+ })),
59
+ model: Type.Optional(Type.String({
60
+ description: 'Model override in "provider/modelId" format. CASE-SENSITIVE: the string must equal a registry entry exactly, including letter case (e.g. "zai-coding-cn/GLM-5.3-Flash", NOT "zai-coding-cn/glm-5.3-flash"). A non-exact match is rejected immediately with "Did you mean" suggestions — retry with the exact suggested string; the system never auto-corrects your input. Resolution order (top wins): (1) this param, (2) agent .md frontmatter model, (3) the main agent\'s current model (zero-config default). An explicit model (param or frontmatter) that is missing or unauthorized THROWS — there is no silent fallback to the main model. Omit this param to inherit the main model.',
61
+ })),
62
+ thinkingLevel: Type.Optional(StringEnum(THINKING_ORDER, {
63
+ description: "Thinking depth override (derived from THINKING_ORDER SSOT, includes 'max'). Omit to default to the model's highest available level (not the main agent's level).",
64
+ })),
65
+ skillPath: Type.Optional(Type.String({
66
+ description:
67
+ "Absolute path to a skill directory, injected into the subagent's pi process via --skill " +
68
+ "(e.g. a path under .agents/skills/ already resolved for the caller). Must be an absolute path; " +
69
+ "'..' traversal segments are rejected.",
70
+ pattern: "^/",
71
+ })),
72
+ appendSystemPrompt: Type.Optional(Type.Array(Type.String())),
73
+ schema: Type.Optional(Type.Record(Type.String(), Type.Unknown())),
74
+ maxTurns: Type.Optional(Type.Number({
75
+ description: "Turn limit. The subagent is terminated via SIGTERM after maxTurns turn_end events + graceTurns of slack. There is no graceful wrap-up message — the process is killed. 0 or omitted = unlimited.",
76
+ })),
77
+ graceTurns: Type.Optional(Type.Number({
78
+ description: "Extra turns allowed after maxTurns is reached before SIGTERM (default 2). Only meaningful when maxTurns is set.",
79
+ })),
80
+ fork: Type.Optional(Type.Boolean({
81
+ description: "Fork mode: inherit the parent's conversation context. When true, the subagent receives the parent's session file via --fork and builds a branched conversation (it sees prior turns/messages). The subagent still runs in a separate spawned child process (process isolation) — fork is about context inheritance, not process sharing; independent of worktree (file-system isolation, see worktree param). When to use: only when the task extends from the parent and genuinely needs key information from the parent's conversation history that a self-contained task prompt cannot carry — most tasks a plain prompt can describe do NOT need fork, so keep false by default and enable only when the user explicitly asks or the task truly depends on seeing prior turns. Caveat: fork drags in the parent's dispatch records and unrelated task context, polluting the subagent (it cannot tell 'context meant for me' from 'parent dispatching me'); when state lives in an external store the subagent can query (e.g., cw handoff), prefer that over fork.",
82
+ })),
83
+ worktree: Type.Optional(Type.Boolean({
84
+ description: "Worktree isolation: run the subagent in a dedicated git worktree, providing file-system level isolation from the parent session (prevents concurrent file-write conflicts). Independent of fork — worktree may be combined with fork:false (file isolation does not require context inheritance). When to use: parallel development scenarios where multiple agents write files concurrently and need isolated working directories (each gets its own checkout; merge later); leave false for single-agent or read-only tasks.",
85
+ })),
86
+ cwd: Type.Optional(Type.String({
87
+ description: 'Override the working directory for the subagent execution. Must be an absolute path (no "~" shorthand, no relative paths); ".." segments are rejected. Defaults to the parent session\'s cwd.',
88
+ pattern: "^/",
89
+ })),
90
+ conversation: Type.Optional(Type.Boolean({
91
+ description:
92
+ "Enable continuous chat with this subagent. When true, the subagent stays available after each reply — you can send follow-up messages (action:'message') and it keeps the full conversation context across rounds, with no need to re-spawn or re-explain. " +
93
+ "\nUse conversation:true for: multi-round collaboration (iterative review-fix loops, back-and-forth refinement), any task where you expect to send follow-up messages after the initial result. " +
94
+ "\nOmit (or false) for: one-shot tasks — single exploration, lookup, file read, code generation that needs no follow-up. The subagent runs once, notifies on completion, and is cleaned up automatically (default). " +
95
+ "\nFor long-interval collaboration (each round spaced >5min apart), set conversation:true AND increase idleTimeoutMs to avoid premature timeout. " +
96
+ "Cost: a conversation-mode subagent holds resources (memory, and a worktree if enabled) until you explicitly end it with action:'close'. Always close when done.",
97
+ })),
98
+ idleTimeoutMs: Type.Optional(Type.Number({
99
+ description:
100
+ "Idle timeout in milliseconds for conversation-mode subagents. Controls how long an idle subagent (between rounds) stays alive before automatic cleanup. " +
101
+ "Default: 300000 (5min). Override for long-interval collaboration where each round is spaced >5min apart. " +
102
+ "Pass 0 or a negative value to DISABLE idle cleanup entirely (subagent stays alive until explicitly closed). " +
103
+ "Only meaningful with conversation:true; ignored for one-shot subagents.",
104
+ })),
105
+ engine: Type.Optional(StringEnum(["pi", "zcode"], {
106
+ description:
107
+ "Execution engine for this subagent. Omit to inherit the global config. " +
108
+ "Three-layer priority: this parameter > agent .md frontmatter engine > config.json defaultEngine. " +
109
+ "Non-pi engines do not support conversation/fork/worktree (rejected before the subagent is created).",
110
+ })),
111
+ // action:"list" → listParam OPTIONAL (all fields optional, defaults apply). Ignored by other actions.
112
+ listParam: Type.Optional(Type.Object({
113
+ includeFinished: Type.Optional(Type.Boolean({
114
+ description: "Include finished (done/failed/cancelled) records. Default false (running only).",
115
+ })),
116
+ limit: Type.Optional(Type.Number({
117
+ description: "Max items to return. Default 20, clamped to [1, 100].",
118
+ })),
119
+ })),
120
+ // action:"cancel" → cancelParam.subagentId REQUIRED. Throws if missing. Ignored by other actions.
121
+ cancelParam: Type.Optional(Type.Object({
122
+ subagentId: Type.String({
123
+ description: "REQUIRED for action:'cancel'. The subagentId to cancel. Throws if missing. Only background subagents can be cancelled.",
124
+ }),
125
+ })),
126
+ // action:"message" → messageParam.subagentId + text REQUIRED. Any RUNNING subagent works —
127
+ // one-shot subagents are auto-upgraded to conversation mode on first message (SP-5); ended ones throw.
128
+ messageParam: Type.Optional(Type.Object({
129
+ subagentId: Type.String({
130
+ description: "REQUIRED for action:'message'. The subagentId to message (any running subagent; a one-shot subagent is auto-upgraded to conversation mode on first message, so you may also message one-shot subagents that are still running).",
131
+ }),
132
+ text: Type.String({
133
+ description: "REQUIRED for action:'message'. The message to send. Whitespace-only throws.",
134
+ }),
135
+ interrupt: Type.Optional(Type.Boolean({
136
+ description: "If true, interrupt the subagent's current work immediately (in-progress output stops, it switches to your new message). If false (default), the message is queued and processed after the current round completes. When the subagent is idle (between rounds), interrupt has no effect — the message always starts a new round.",
137
+ })),
138
+ })),
139
+ // action:"close" → closeParam.subagentId REQUIRED. Ends a running subagent (conversation-mode
140
+ // or one-shot — closeSubagent behavior split covers both).
141
+ closeParam: Type.Optional(Type.Object({
142
+ subagentId: Type.String({
143
+ description: "REQUIRED for action:'close'. The subagentId to close (any running subagent, conversation-mode or one-shot).",
144
+ }),
145
+ force: Type.Optional(Type.Boolean({
146
+ description: "If true, terminate immediately even if mid-round (in-progress work is lost). If false (default), let the current round finish, then close. When idle, the subagent closes immediately regardless.",
147
+ })),
148
+ })),
149
+ // action:"fork-from" → forkFromParam.sourceSubagentId REQUIRED ([v8.5 B] 断联恢复通道).
150
+ // 从旧 subagent 的会话历史 spawn 新 id:新进程 --fork 指向旧 session 文件(copy-on-write,
151
+ // 源文件只读不续写);旧记录/状态机不动。pi 引擎限定(非 pi 在 execute 层拒绝)。
152
+ forkFromParam: Type.Optional(Type.Object({
153
+ sourceSubagentId: Type.String({
154
+ description: "REQUIRED for action:'fork-from'. The OLD subagentId whose conversation history becomes the inherited context of the new subagent. Works for records disconnected by a session restart or already finished; cancelled/worktree-bound/still-running ones are rejected with guidance.",
155
+ }),
156
+ prompt: Type.Optional(Type.String({
157
+ description: "Continuation instruction for the new subagent (what to do next on top of the inherited history). When omitted, a standard handover frame is injected: reconstruct done/decided/remaining from the inherited history, then continue to completion. Whitespace-only treated as omitted.",
158
+ })),
159
+ })),
160
+ });
161
+
162
+ /** Params schema 的 Static 投影(消费方经 `Static<typeof SubagentParams>` 使用,见 subagent-tool.ts)。 */
163
+ export type SubagentParamsStatic = Static<typeof SubagentParams>;