@zhushanwen/pi-subagent-workflow 8.6.0 → 8.8.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 (364) hide show
  1. package/README.md +5 -11
  2. package/package.json +13 -20
  3. package/relay/relay.mjs +1 -1
  4. package/skills/workflow-script-format/SKILL.md +32 -13
  5. package/src/host/__tests__/pi-host.test.ts +235 -0
  6. package/src/host/pi-host.ts +163 -0
  7. package/src/index.ts +141 -138
  8. package/src/injectors/__tests__/engine-awareness.test.ts +292 -0
  9. package/src/injectors/__tests__/engine-section-stability.test.ts +343 -0
  10. package/src/injectors/__tests__/model-list-injector.test.ts +18 -21
  11. package/src/injectors/__tests__/subagent-list-injector.test.ts +59 -19
  12. package/src/injectors/__tests__/workflow-list-injector.test.ts +31 -17
  13. package/src/injectors/engine-awareness.ts +119 -0
  14. package/src/injectors/model-list-injector.ts +15 -58
  15. package/src/injectors/subagent-list-injector.ts +56 -114
  16. package/src/injectors/workflow-list-injector.ts +37 -70
  17. package/src/interface/__tests__/detectors.test.ts +48 -37
  18. package/src/interface/__tests__/subagent-tool-path-guard.test.ts +2 -2
  19. package/src/interface/__tests__/subagent-tool-prompt.test.ts +6 -3
  20. package/src/interface/__tests__/tool-workflow-run-builtin-name.test.ts +216 -0
  21. package/src/interface/__tests__/tool-workflow-script-generate.test.ts +17 -6
  22. package/src/interface/__tests__/tool-workflow-throw-paths.test.ts +38 -2
  23. package/src/interface/bg-notify-render.ts +7 -17
  24. package/src/interface/command-actions.ts +5 -15
  25. package/src/interface/commands.ts +4 -4
  26. package/src/interface/format.ts +17 -6
  27. package/src/interface/gui-mappers.ts +18 -22
  28. package/src/interface/helpers.ts +9 -130
  29. package/src/interface/list-component.ts +5 -5
  30. package/src/interface/list-shared.ts +3 -3
  31. package/src/interface/list-view.ts +3 -3
  32. package/src/interface/subagent-actions.ts +66 -439
  33. package/src/interface/subagent-tool-schema.ts +21 -17
  34. package/src/interface/subagent-tool.ts +12 -7
  35. package/src/interface/subagents.ts +4 -4
  36. package/src/interface/tool-render.ts +37 -15
  37. package/src/interface/tool-workflow-script.ts +34 -76
  38. package/src/interface/tool-workflow.ts +57 -94
  39. package/src/interface/views/WorkflowsView.ts +8 -16
  40. package/src/interface/views/__tests__/WorkflowsView-signature.test.ts +3 -3
  41. package/src/interface/views/__tests__/detail-content-session-file.test.ts +2 -2
  42. package/src/interface/views/detail-content.ts +4 -4
  43. package/src/interface/views/format.ts +23 -62
  44. package/src/{orchestration/jsonl-run-store.ts → jsonl-run-store.ts} +137 -225
  45. package/agents/analyst.md +0 -61
  46. package/agents/coder.md +0 -70
  47. package/agents/debugger.md +0 -67
  48. package/agents/doc-reviewer.md +0 -50
  49. package/agents/explorer.md +0 -64
  50. package/agents/general-purpose.md +0 -32
  51. package/agents/orchestrator.md +0 -63
  52. package/agents/planner.md +0 -54
  53. package/agents/researcher.md +0 -65
  54. package/agents/reviewer.md +0 -74
  55. package/src/execution/__tests__/__fixtures__/notifier-golden-snapshots.json +0 -53
  56. package/src/execution/__tests__/__fixtures__/truncline.snapshot.json +0 -1
  57. package/src/execution/__tests__/agent-registry.test.ts +0 -363
  58. package/src/execution/__tests__/agent-result-mapper.test.ts +0 -150
  59. package/src/execution/__tests__/alive-store.test.ts +0 -147
  60. package/src/execution/__tests__/ask-user-transit-e2e.test.ts +0 -490
  61. package/src/execution/__tests__/before-agent-start-injection.test.ts +0 -132
  62. package/src/execution/__tests__/bg-notify-render.test.ts +0 -329
  63. package/src/execution/__tests__/channel-registry-handshake.test.ts +0 -243
  64. package/src/execution/__tests__/chat-engine-routing.test.ts +0 -601
  65. package/src/execution/__tests__/chatmode-first-round-closure-service.test.ts +0 -365
  66. package/src/execution/__tests__/chatmode-first-round-closure-spawn.test.ts +0 -190
  67. package/src/execution/__tests__/chatmode-round-notify-real-chain.test.ts +0 -215
  68. package/src/execution/__tests__/concurrency-pool.test.ts +0 -250
  69. package/src/execution/__tests__/config.test.ts +0 -110
  70. package/src/execution/__tests__/conversation-wiring.test.ts +0 -198
  71. package/src/execution/__tests__/crash-recovery.test.ts +0 -320
  72. package/src/execution/__tests__/delivery-methods.test.ts +0 -422
  73. package/src/execution/__tests__/dialog-queue.test.ts +0 -299
  74. package/src/execution/__tests__/epipe-fallback.test.ts +0 -241
  75. package/src/execution/__tests__/execute-and-await-worktree.test.ts +0 -243
  76. package/src/execution/__tests__/execute-nesting.test.ts +0 -323
  77. package/src/execution/__tests__/execute-options-mapper.test.ts +0 -200
  78. package/src/execution/__tests__/execution-record.test.ts +0 -1394
  79. package/src/execution/__tests__/explicit-agent-ref-guard.test.ts +0 -171
  80. package/src/execution/__tests__/finalize-record.test.ts +0 -368
  81. package/src/execution/__tests__/finalized-marker.test.ts +0 -82
  82. package/src/execution/__tests__/format-schema-instruction.test.ts +0 -169
  83. package/src/execution/__tests__/format.test.ts +0 -458
  84. package/src/execution/__tests__/gc-timer.test.ts +0 -184
  85. package/src/execution/__tests__/get-record-for-action-restart.test.ts +0 -254
  86. package/src/execution/__tests__/gui-mode-dispatch.test.ts +0 -59
  87. package/src/execution/__tests__/helpers/mock-extension-api.ts +0 -30
  88. package/src/execution/__tests__/helpers/spawn-mock.ts +0 -242
  89. package/src/execution/__tests__/host-mode.test.ts +0 -87
  90. package/src/execution/__tests__/index-session-start-identity.test.ts +0 -371
  91. package/src/execution/__tests__/index-session-start.test.ts +0 -672
  92. package/src/execution/__tests__/lifecycle-manager-lock.test.ts +0 -211
  93. package/src/execution/__tests__/lifecycle-manager.test.ts +0 -383
  94. package/src/execution/__tests__/lifecycle-predicates.test.ts +0 -116
  95. package/src/execution/__tests__/list-component.test.ts +0 -401
  96. package/src/execution/__tests__/list-fields.test.ts +0 -140
  97. package/src/execution/__tests__/manifest-parentid.test.ts +0 -117
  98. package/src/execution/__tests__/model-resolver.test.ts +0 -465
  99. package/src/execution/__tests__/nested-visibility-env-propagation.test.ts +0 -287
  100. package/src/execution/__tests__/nested-visibility.test.ts +0 -325
  101. package/src/execution/__tests__/notifier-flush.test.ts +0 -612
  102. package/src/execution/__tests__/notifier-golden-snapshot.test.ts +0 -261
  103. package/src/execution/__tests__/notify-ledger.test.ts +0 -826
  104. package/src/execution/__tests__/one-shot-upgrade.test.ts +0 -205
  105. package/src/execution/__tests__/output-collector.test.ts +0 -358
  106. package/src/execution/__tests__/parent-child-matrix.test.ts +0 -336
  107. package/src/execution/__tests__/path-encoding.test.ts +0 -104
  108. package/src/execution/__tests__/pi-invocation.test.ts +0 -134
  109. package/src/execution/__tests__/record-store.test.ts +0 -1057
  110. package/src/execution/__tests__/records-cwd-isolation.test.ts +0 -91
  111. package/src/execution/__tests__/recursive-visibility-baseline.test.ts +0 -339
  112. package/src/execution/__tests__/recursive-visibility-env.test.ts +0 -273
  113. package/src/execution/__tests__/relay-agent.test.ts +0 -448
  114. package/src/execution/__tests__/relay-env.test.ts +0 -42
  115. package/src/execution/__tests__/resource-policy.test.ts +0 -109
  116. package/src/execution/__tests__/rpc-mode.test.ts +0 -89
  117. package/src/execution/__tests__/run-and-finalize-chatmode.test.ts +0 -267
  118. package/src/execution/__tests__/run-spawn-chatmode-settled.test.ts +0 -253
  119. package/src/execution/__tests__/run-spawn-edges.test.ts +0 -687
  120. package/src/execution/__tests__/run-spawn-integration.test.ts +0 -933
  121. package/src/execution/__tests__/run-spawn-resume.test.ts +0 -322
  122. package/src/execution/__tests__/run-spawn-rpc-mode.test.ts +0 -196
  123. package/src/execution/__tests__/run-spawn-stdout-callback-throw.test.ts +0 -199
  124. package/src/execution/__tests__/sdk-contract.test.ts +0 -273
  125. package/src/execution/__tests__/session-context-resolver.test.ts +0 -167
  126. package/src/execution/__tests__/session-file-gc.test.ts +0 -293
  127. package/src/execution/__tests__/session-pending.test.ts +0 -197
  128. package/src/execution/__tests__/session-reconstructor.test.ts +0 -379
  129. package/src/execution/__tests__/session-runner-epipe.test.ts +0 -178
  130. package/src/execution/__tests__/session-runner-schema-env.test.ts +0 -350
  131. package/src/execution/__tests__/session-start-reaper.test.ts +0 -233
  132. package/src/execution/__tests__/spawn-args.test.ts +0 -445
  133. package/src/execution/__tests__/spawn-event-adapter-rpc.test.ts +0 -189
  134. package/src/execution/__tests__/spawn-event-adapter.test.ts +0 -167
  135. package/src/execution/__tests__/spawn-worktree-guidance.test.ts +0 -207
  136. package/src/execution/__tests__/spawned-children.test.ts +0 -92
  137. package/src/execution/__tests__/start-sync-model-guard.test.ts +0 -150
  138. package/src/execution/__tests__/startup-config-declaration.test.ts +0 -35
  139. package/src/execution/__tests__/status-refactor.test.ts +0 -345
  140. package/src/execution/__tests__/stdin-writer.test.ts +0 -463
  141. package/src/execution/__tests__/stream-sink-retirement.test.ts +0 -261
  142. package/src/execution/__tests__/subagent-service-abort.test.ts +0 -60
  143. package/src/execution/__tests__/subagent-service-message-close.test.ts +0 -629
  144. package/src/execution/__tests__/subagent-service-parent-guard.test.ts +0 -180
  145. package/src/execution/__tests__/subagent-service.test.ts +0 -788
  146. package/src/execution/__tests__/subprocess-agent-runner-routing.test.ts +0 -310
  147. package/src/execution/__tests__/subprocess-agent-runner.test.ts +0 -599
  148. package/src/execution/__tests__/temp-prompt.test.ts +0 -53
  149. package/src/execution/__tests__/timeout-integration.test.ts +0 -615
  150. package/src/execution/__tests__/tombstone-store.test.ts +0 -73
  151. package/src/execution/__tests__/tool-action.test.ts +0 -476
  152. package/src/execution/__tests__/truncline-snapshot.test.ts +0 -81
  153. package/src/execution/__tests__/turn-limiter-semantics.test.ts +0 -194
  154. package/src/execution/__tests__/turn-limiter.test.ts +0 -65
  155. package/src/execution/__tests__/ui-channels.test.ts +0 -187
  156. package/src/execution/__tests__/ui-interaction-model.test.ts +0 -67
  157. package/src/execution/__tests__/ui-request-handler-factory.test.ts +0 -370
  158. package/src/execution/__tests__/ui-request-handler.test.ts +0 -204
  159. package/src/execution/__tests__/ui-request-observability.test.ts +0 -114
  160. package/src/execution/__tests__/ui-request-queue.test.ts +0 -133
  161. package/src/execution/__tests__/worktree-manager.test.ts +0 -633
  162. package/src/execution/__tests__/worktree-pid-registration.integration.test.ts +0 -229
  163. package/src/execution/__tests__/worktree-reconcile.integration.test.ts +0 -181
  164. package/src/execution/__tests__/worktree-registry.test.ts +0 -199
  165. package/src/execution/agent-registry.ts +0 -199
  166. package/src/execution/agent-result-mapper.ts +0 -90
  167. package/src/execution/alive-store.ts +0 -92
  168. package/src/execution/argv-mirror.ts +0 -113
  169. package/src/execution/best-effort.ts +0 -38
  170. package/src/execution/channel-registry-access.ts +0 -144
  171. package/src/execution/concurrency-pool.ts +0 -116
  172. package/src/execution/config.ts +0 -96
  173. package/src/execution/dialog-queue.ts +0 -330
  174. package/src/execution/engine/__tests__/common/data-dir.test.ts +0 -53
  175. package/src/execution/engine/__tests__/common/errors.test.ts +0 -132
  176. package/src/execution/engine/__tests__/common/event-journal.test.ts +0 -177
  177. package/src/execution/engine/__tests__/common/kill-chain.test.ts +0 -192
  178. package/src/execution/engine/__tests__/common/nesting-guard.test.ts +0 -81
  179. package/src/execution/engine/__tests__/common/persona-router.test.ts +0 -123
  180. package/src/execution/engine/__tests__/common/pool-manager.test.ts +0 -154
  181. package/src/execution/engine/__tests__/common/schema-emulation.test.ts +0 -128
  182. package/src/execution/engine/__tests__/conformance/__fixtures__/pi-golden-events.json +0 -28
  183. package/src/execution/engine/__tests__/conformance/agent-event-invariants.ts +0 -141
  184. package/src/execution/engine/__tests__/conformance/contract.abort.test.ts +0 -109
  185. package/src/execution/engine/__tests__/conformance/contract.agent-events.test.ts +0 -101
  186. package/src/execution/engine/__tests__/conformance/contract.probe.test.ts +0 -77
  187. package/src/execution/engine/__tests__/conformance/contract.read-degradation.test.ts +0 -104
  188. package/src/execution/engine/__tests__/conformance/contract.relay.test.ts +0 -342
  189. package/src/execution/engine/__tests__/conformance/engine-conformance.live.test.ts +0 -201
  190. package/src/execution/engine/__tests__/conformance/golden-replay.pi.test.ts +0 -76
  191. package/src/execution/engine/__tests__/conformance/golden-replay.zcode.test.ts +0 -79
  192. package/src/execution/engine/__tests__/engine-discovery.test.ts +0 -87
  193. package/src/execution/engine/__tests__/engines-declaration.test.ts +0 -36
  194. package/src/execution/engine/__tests__/model-prompt.test.ts +0 -85
  195. package/src/execution/engine/__tests__/paths.test.ts +0 -39
  196. package/src/execution/engine/__tests__/registry.test.ts +0 -120
  197. package/src/execution/engine/__tests__/routing.test.ts +0 -231
  198. package/src/execution/engine/common/data-dir.ts +0 -62
  199. package/src/execution/engine/common/errors.ts +0 -183
  200. package/src/execution/engine/common/event-journal.ts +0 -254
  201. package/src/execution/engine/common/journal-replay.ts +0 -62
  202. package/src/execution/engine/common/kill-chain.ts +0 -221
  203. package/src/execution/engine/common/nesting-guard.ts +0 -50
  204. package/src/execution/engine/common/persona-router.ts +0 -108
  205. package/src/execution/engine/common/pool-manager.ts +0 -226
  206. package/src/execution/engine/common/schema-emulation.ts +0 -189
  207. package/src/execution/engine/common/session-view-projection.ts +0 -51
  208. package/src/execution/engine/engine-discovery.ts +0 -65
  209. package/src/execution/engine/engines/pi/__tests__/pi-engine.test.ts +0 -469
  210. package/src/execution/engine/engines/pi/__tests__/reader.test.ts +0 -155
  211. package/src/execution/engine/engines/pi/__tests__/task-spec-mapper.test.ts +0 -164
  212. package/src/execution/engine/engines/pi/pi-engine.ts +0 -415
  213. package/src/execution/engine/engines/pi/reader.ts +0 -48
  214. package/src/execution/engine/engines/pi/registration.ts +0 -35
  215. package/src/execution/engine/engines/pi/task-spec-mapper.ts +0 -100
  216. package/src/execution/engine/engines/zcode/__tests__/__fixtures__/zcode-golden-spawn.json +0 -39
  217. package/src/execution/engine/engines/zcode/__tests__/launcher.test.ts +0 -150
  218. package/src/execution/engine/engines/zcode/__tests__/parser.test.ts +0 -246
  219. package/src/execution/engine/engines/zcode/__tests__/preparer.test.ts +0 -228
  220. package/src/execution/engine/engines/zcode/__tests__/reader.test.ts +0 -210
  221. package/src/execution/engine/engines/zcode/__tests__/registration.test.ts +0 -64
  222. package/src/execution/engine/engines/zcode/__tests__/zcode-engine.live.test.ts +0 -127
  223. package/src/execution/engine/engines/zcode/__tests__/zcode-engine.test.ts +0 -580
  224. package/src/execution/engine/engines/zcode/constants.ts +0 -43
  225. package/src/execution/engine/engines/zcode/golden-sample.ts +0 -39
  226. package/src/execution/engine/engines/zcode/launcher.ts +0 -161
  227. package/src/execution/engine/engines/zcode/parser.ts +0 -436
  228. package/src/execution/engine/engines/zcode/preparer.ts +0 -363
  229. package/src/execution/engine/engines/zcode/reader.ts +0 -381
  230. package/src/execution/engine/engines/zcode/registration.ts +0 -37
  231. package/src/execution/engine/engines/zcode/zcode-engine.ts +0 -658
  232. package/src/execution/engine/host-task-spec.ts +0 -47
  233. package/src/execution/engine/model-prompt.ts +0 -59
  234. package/src/execution/engine/paths.ts +0 -42
  235. package/src/execution/engine/port.ts +0 -153
  236. package/src/execution/engine/registry.ts +0 -123
  237. package/src/execution/engine/routing.ts +0 -218
  238. package/src/execution/engine/types.ts +0 -309
  239. package/src/execution/execute-options-mapper.ts +0 -112
  240. package/src/execution/execution-record.ts +0 -965
  241. package/src/execution/finalize-record.ts +0 -251
  242. package/src/execution/finalized-marker.ts +0 -51
  243. package/src/execution/get-state-handshake.ts +0 -104
  244. package/src/execution/host-mode.ts +0 -56
  245. package/src/execution/idle-gc.ts +0 -47
  246. package/src/execution/lifecycle-manager.ts +0 -513
  247. package/src/execution/lifecycle-predicates.ts +0 -65
  248. package/src/execution/manifest-store.ts +0 -256
  249. package/src/execution/model-config-service.ts +0 -219
  250. package/src/execution/model-resolver.ts +0 -248
  251. package/src/execution/notifier.ts +0 -330
  252. package/src/execution/notify-ledger.ts +0 -580
  253. package/src/execution/output-collector.ts +0 -228
  254. package/src/execution/path-encoding.ts +0 -60
  255. package/src/execution/pi-invocation.ts +0 -120
  256. package/src/execution/record-entry.ts +0 -132
  257. package/src/execution/record-store.ts +0 -1234
  258. package/src/execution/relay-env.ts +0 -37
  259. package/src/execution/session-context-resolver.ts +0 -64
  260. package/src/execution/session-file-gc.ts +0 -120
  261. package/src/execution/session-pending.ts +0 -164
  262. package/src/execution/session-reconstructor.ts +0 -678
  263. package/src/execution/session-runner.ts +0 -1781
  264. package/src/execution/sessions-index.ts +0 -304
  265. package/src/execution/spawn-event-adapter.ts +0 -363
  266. package/src/execution/stdin-writer.ts +0 -198
  267. package/src/execution/stream-sink.ts +0 -126
  268. package/src/execution/subagent-service.ts +0 -2141
  269. package/src/execution/subprocess-agent-runner.ts +0 -317
  270. package/src/execution/temp-prompt.ts +0 -62
  271. package/src/execution/tombstone-store.ts +0 -72
  272. package/src/execution/turn-limiter.ts +0 -102
  273. package/src/execution/types.ts +0 -973
  274. package/src/execution/ui-channels.ts +0 -216
  275. package/src/execution/ui-interaction-model.ts +0 -48
  276. package/src/execution/ui-request-handler-factory.ts +0 -212
  277. package/src/execution/ui-request-observability.ts +0 -81
  278. package/src/execution/ui-request-queue.ts +0 -184
  279. package/src/execution/worktree-manager.ts +0 -688
  280. package/src/execution/worktree-registry.ts +0 -222
  281. package/src/orchestration/__tests__/__fixtures__/worker-template.snapshot.txt +0 -349
  282. package/src/orchestration/__tests__/agent-call-catch-fallback.test.ts +0 -202
  283. package/src/orchestration/__tests__/agent-call-stream.test.ts +0 -152
  284. package/src/orchestration/__tests__/args-validator.test.ts +0 -143
  285. package/src/orchestration/__tests__/config-loader.test.ts +0 -503
  286. package/src/orchestration/__tests__/error-recovery-handlers.test.ts +0 -809
  287. package/src/orchestration/__tests__/error-recovery-postmessage-defense.test.ts +0 -405
  288. package/src/orchestration/__tests__/error-recovery-serialize-failed-result.test.ts +0 -56
  289. package/src/orchestration/__tests__/error-recovery-workflow-call.test.ts +0 -166
  290. package/src/orchestration/__tests__/execute-agent-call.test.ts +0 -451
  291. package/src/orchestration/__tests__/jsonl-run-store-corrupt-entry.test.ts +0 -150
  292. package/src/orchestration/__tests__/jsonl-run-store-loadall-sources.test.ts +0 -171
  293. package/src/orchestration/__tests__/jsonl-run-store-retention.test.ts +0 -202
  294. package/src/orchestration/__tests__/jsonl-run-store-session-file.test.ts +0 -1030
  295. package/src/orchestration/__tests__/launcher-nested-workflow.test.ts +0 -600
  296. package/src/orchestration/__tests__/lifecycle-runid-injection.test.ts +0 -96
  297. package/src/orchestration/__tests__/lifecycle.test.ts +0 -659
  298. package/src/orchestration/__tests__/non-cloneable-return-e2e.test.ts +0 -95
  299. package/src/orchestration/__tests__/review-fix-loop-e2e.test.ts +0 -2025
  300. package/src/orchestration/__tests__/script-lint.test.ts +0 -831
  301. package/src/orchestration/__tests__/skill-discovery.test.ts +0 -201
  302. package/src/orchestration/__tests__/test-mocks.ts +0 -197
  303. package/src/orchestration/__tests__/worker-exit-without-result.test.ts +0 -368
  304. package/src/orchestration/__tests__/worker-host.test.ts +0 -120
  305. package/src/orchestration/__tests__/worker-returnmeta-passthrough.test.ts +0 -164
  306. package/src/orchestration/__tests__/worker-script-builder-runtime.test.ts +0 -563
  307. package/src/orchestration/__tests__/worker-script-builder.test.ts +0 -309
  308. package/src/orchestration/__tests__/worker-script-template-snapshot.test.ts +0 -129
  309. package/src/orchestration/__tests__/workflow-nesting-e2e.test.ts +0 -317
  310. package/src/orchestration/__tests__/workflow-script-lint-memo.test.ts +0 -110
  311. package/src/orchestration/__tests__/workflows-e2e.test.ts +0 -537
  312. package/src/orchestration/agent-opts-resolver.ts +0 -174
  313. package/src/orchestration/args-validator.ts +0 -127
  314. package/src/orchestration/config-loader.ts +0 -315
  315. package/src/orchestration/error-recovery.ts +0 -1018
  316. package/src/orchestration/execute-agent-call.ts +0 -256
  317. package/src/orchestration/launcher.ts +0 -455
  318. package/src/orchestration/lifecycle.ts +0 -399
  319. package/src/orchestration/models/__tests__/budget.test.ts +0 -307
  320. package/src/orchestration/models/__tests__/trace.test.ts +0 -408
  321. package/src/orchestration/models/agent-call.ts +0 -83
  322. package/src/orchestration/models/budget.ts +0 -118
  323. package/src/orchestration/models/ports.ts +0 -164
  324. package/src/orchestration/models/run-runtime.ts +0 -104
  325. package/src/orchestration/models/run-spec.ts +0 -83
  326. package/src/orchestration/models/run-state.ts +0 -44
  327. package/src/orchestration/models/trace.ts +0 -183
  328. package/src/orchestration/models/types.ts +0 -301
  329. package/src/orchestration/models/workflow-run.ts +0 -254
  330. package/src/orchestration/models/workflow-script-registry.ts +0 -35
  331. package/src/orchestration/models/workflow-script.ts +0 -117
  332. package/src/orchestration/script-lint.ts +0 -766
  333. package/src/orchestration/skill-discovery.ts +0 -115
  334. package/src/orchestration/worker-handle.ts +0 -115
  335. package/src/orchestration/worker-host.ts +0 -98
  336. package/src/orchestration/worker-script-builder.ts +0 -421
  337. package/src/orchestration/workflow-files.ts +0 -85
  338. package/src/orchestration/workflow-script-registry-impl.ts +0 -133
  339. package/src/shared/__tests__/agent-ref.test.ts +0 -34
  340. package/src/shared/__tests__/meta-parser.test.ts +0 -304
  341. package/src/shared/__tests__/model-ref.test.ts +0 -306
  342. package/src/shared/__tests__/resource-discovery-manifest-cache.test.ts +0 -280
  343. package/src/shared/__tests__/resource-discovery.test.ts +0 -736
  344. package/src/shared/__tests__/resource-meta.test.ts +0 -51
  345. package/src/shared/__tests__/schema-jsonify.test.ts +0 -81
  346. package/src/shared/__tests__/timer-delay.test.ts +0 -61
  347. package/src/shared/agent-event.ts +0 -13
  348. package/src/shared/agent-ref.ts +0 -57
  349. package/src/shared/meta-parser.ts +0 -261
  350. package/src/shared/model-ref.ts +0 -286
  351. package/src/shared/resource-discovery.ts +0 -811
  352. package/src/shared/resource-meta.ts +0 -65
  353. package/src/shared/schema-env.ts +0 -44
  354. package/src/shared/schema-jsonify.ts +0 -58
  355. package/src/shared/timer-delay.ts +0 -54
  356. package/src/shared/xml-injection.ts +0 -35
  357. package/workflows/README.md +0 -81
  358. package/workflows/_shared/agent-refs.cjs +0 -40
  359. package/workflows/chain.js +0 -131
  360. package/workflows/map-reduce.js +0 -174
  361. package/workflows/parallel.js +0 -148
  362. package/workflows/review-fix-loop-utils.cjs +0 -1309
  363. package/workflows/review-fix-loop.js +0 -1404
  364. package/workflows/scatter-gather.js +0 -178
@@ -12,10 +12,10 @@
12
12
 
13
13
  import { Key, matchesKey } from "@earendil-works/pi-tui";
14
14
 
15
- import { getAllToolCalls, projectLiveProgress } from "../../execution/execution-record.ts";
16
- import type { AgentEventLogEntry } from "../../execution/types.ts";
17
- import type { ExecutionTraceNode } from "../../orchestration/models/types.ts";
18
- import type { WorkflowRun } from "../../orchestration/models/workflow-run.ts";
15
+ import { getAllToolCalls, projectLiveProgress } from "@zhushanwen/subagent-core/execution/execution-record.ts";
16
+ import type { AgentEventLogEntry } from "@zhushanwen/subagent-core/execution/types.ts";
17
+ import type { ExecutionTraceNode } from "@zhushanwen/subagent-core/orchestration/models/types.ts";
18
+ import type { WorkflowRun } from "@zhushanwen/subagent-core/orchestration/models/workflow-run.ts";
19
19
  import {
20
20
  BOX_BORDER_CHARS,
21
21
  BUDGET_TOKENS_DIVISOR,
@@ -7,10 +7,12 @@
7
7
 
8
8
  import { truncateToWidth, visibleWidth } from "@earendil-works/pi-tui";
9
9
 
10
- import type { AgentEventLogEntry } from "../../execution/types.ts";
11
- import type { ExecutionTraceNode, ToolCallEntry } from "../../orchestration/models/types.ts";
12
- import type { DoneReason, RunStatus } from "../../orchestration/models/types.ts";
13
- import { displayAgentName } from "../../shared/agent-ref.ts";
10
+ import type { AgentEventLogEntry } from "@zhushanwen/subagent-core/execution/types.ts";
11
+ import type { ExecutionTraceNode, ToolCallEntry } from "@zhushanwen/subagent-core/orchestration/models/types.ts";
12
+ import type { DoneReason, RunStatus } from "@zhushanwen/subagent-core/orchestration/models/types.ts";
13
+ import { displayAgentName } from "@zhushanwen/subagent-core/shared/agent-ref.ts";
14
+
15
+ import type { ThemeLike as BaseThemeLike } from "../format.ts";
14
16
 
15
17
  // ── Constants ─────────────────────────────────────────────────
16
18
 
@@ -53,10 +55,12 @@ type StatusText =
53
55
 
54
56
  // ── Theme interface (avoids importing Pi runtime) ─────────────
55
57
 
56
- export interface ThemeLike {
57
- fg(token: string, text: string): string;
58
- bold(text: string): string;
59
- }
58
+ /**
59
+ * views 侧主题形状:仅 fg/bold——本文件所有函数只消费这两个方法。
60
+ * 单源派生自 src/interface/format.ts 的 ThemeLike(Pick 收窄),不再独立声明;
61
+ * 保留窄形状是为了让只有 fg/bold 的 duck-typed theme(含测试 stub)可直接传入。
62
+ */
63
+ export type ThemeLike = Pick<BaseThemeLike, "fg" | "bold">;
60
64
 
61
65
  // ── Status helpers ────────────────────────────────────────────
62
66
 
@@ -128,17 +132,7 @@ export function formatElapsed(startedAt?: string, now: number = Date.now()): str
128
132
  return `${mins}m${remSecs}s`;
129
133
  }
130
134
 
131
- /**
132
- * Format elapsed time from integer seconds(live 路径用)。
133
- * 与 formatElapsed 输出格式一致,但输入是 computeElapsedSeconds 的秒数(非时间戳)。
134
- */
135
- export function formatElapsedSeconds(seconds: number): string {
136
- if (seconds < 1) return "0s";
137
- if (seconds < SECS_PER_MIN) return `${seconds}s`;
138
- const mins = Math.floor(seconds / SECS_PER_MIN);
139
- const remSecs = seconds % SECS_PER_MIN;
140
- return `${mins}m${remSecs}s`;
141
- }
135
+ // formatElapsedSeconds 单源自 src/interface/format.ts(见文末「单源折叠垫片」)。
142
136
 
143
137
  /**
144
138
  * Format a live eventLog entry(live 路径 Activity 区用)。
@@ -204,48 +198,15 @@ export function formatActivityLine(entry: ToolCallEntry, maxWidth: number): stri
204
198
  return `${entry.name}(${truncated})`;
205
199
  }
206
200
 
207
- // ── ANSI helpers ──────────────────────────────────────────────
208
-
209
- /** Measure visible width of a string (strips ANSI escapes, handles CJK/emoji).
210
- * Delegates to pi-tui's visibleWidth for accurate width calculation.
211
- */
212
- export function visibleLen(s: string): number {
213
- return visibleWidth(s);
214
- }
215
-
216
- /** Pad an ANSI-escaped string to a target *visible* width.
217
- * 只 pad 不截断:超宽时原样返回(调用方负责先截断)。
218
- * 对齐 subagents 的 padToVisible 语义。
219
- */
220
- export function padVisible(s: string, width: number): string {
221
- const vl = visibleLen(s);
222
- if (vl >= width) return s;
223
- return s + " ".repeat(width - vl);
224
- }
225
-
226
- /**
227
- * 分段着色版填充:title 和 fill 都已着色(含 ANSI),拼接时各自 ANSI 延续。
228
- * 解决 ANSI 嵌套失色:若用 fg("c1", fill(title, "─", n)),
229
- * title 内的 \x1b[0m 会重置外层 c1,导致 title 之后的 ─ 失去 c1。
230
- * 改成 title + fill.repeat(后),fill 整段保持自己的 ANSI,不依赖外层包裹。
231
- *
232
- * 对齐 subagents format.ts segFillColored(同源移植)。
233
- */
234
- export function segFillColored(
235
- titleStyled: string | undefined,
236
- fillStyled: string,
237
- width: number,
238
- ): string {
239
- if (width <= 0) return "";
240
- const fillW = visibleLen(fillStyled);
241
- if (!titleStyled || fillW === 0) {
242
- return fillStyled.repeat(width);
243
- }
244
- const tw = visibleLen(titleStyled);
245
- if (tw >= width) return truncateToWidth(titleStyled, width);
246
- const fillCount = width - tw;
247
- return titleStyled + fillStyled.repeat(fillCount);
248
- }
201
+ // ── 单源折叠垫片(设计 U11 宿主内合并)─────────────────────────
202
+ // 通用文本 helper 已折叠单源到 src/interface/format.ts——truncLine 为自研
203
+ // ANSI 安全实现(pi-tui truncateToWidth 会在省略号处插 \x1b[0m 断裂背景色,
204
+ // 见 interface/format.ts truncLine 注释),统一取 interface 侧实现。
205
+ // 此处按原名 re-export:WorkflowsView / detail-content(本 wave 只读)的
206
+ // import 面不变即完成单源切换。segFillColored 无 views 侧消费方,直接删除。
207
+ export { formatElapsedSeconds } from "../format.ts";
208
+ export { padToVisible as padVisible } from "../format.ts";
209
+ export { visibleWidth as visibleLen } from "@earendil-works/pi-tui";
249
210
 
250
211
  // ── Phase group (filters empty phases) ────────────────────────
251
212
 
@@ -290,7 +251,7 @@ export function formatPhaseLine(
290
251
  // pointer(2) + dot(1) + space(1)
291
252
  const PHASE_PREFIX_WIDTH = 4;
292
253
  const budget = maxWidth - PHASE_PREFIX_WIDTH;
293
- const truncated = visibleLen(label) > budget
254
+ const truncated = visibleWidth(label) > budget
294
255
  ? truncateToWidth(label, budget - 1) + ELLIPSIS
295
256
  : label;
296
257
  return `${pointer}${dot} ${truncated}`;
@@ -17,6 +17,9 @@
17
17
  * 快照 entry(pi.appendEntry)——pi 文件(session JSONL)是 workflow 数据持久化
18
18
  * 权威,state 文件降级为纯性能缓存(读序 = entry > state 文件 > 空,写路径保留)。
19
19
  * 旧 `workflow-state-link` 指针 entry 退役(loadAll 保留兼容读,存量 run 不丢)。
20
+ * [B-1/OR-5 同源] entry append 按 runId 节流(见 save 注释「entry append 节流」),
21
+ * pi session JSONL 是 append-only 文件,节流前每次 flush 全量 append 会随 run
22
+ * 时长累积出单 run O(n²) 磁盘占用。
20
23
  *
21
24
  * save 去抖语义(cw swf-perf wave2):
22
25
  * - **热路径**(running 中间态,本实例已写过):per-runId pending 批合并——窗口内
@@ -27,18 +30,25 @@
27
30
  * 同步挂链 flush 绕过 timer——首写立即可见(跨 session 重启后 loadAll 从 entry
28
31
  * 发现 run)、done 立即落盘(终态优先持久化:transition("done") 后的 save
29
32
  * 不进去抖批,去抖窗口内的崩溃不吞终态)。
30
- * - workflow-record entry 每次成功 flush 都 append(含热路径中间态 flush)——
31
- * entry 流 = 落盘历史(最后一条 = 最后一次成功 flush,崩溃丢失边界语义与
32
- * state 文件路径一致);去抖已把 flush 频率控制在与 agent-call 周期同量级。
33
+ * - workflow-record entry append 节流([B-1],语义对齐 core FileRunStore.save
34
+ * 的 OR-5 ⑥a 节流,间隔常量单源复用 core DEFAULT_SAVE_MIN_INTERVAL_MS):
35
+ * running 中间态的 entry append 有最小间隔(缺省 60s;0 = 禁用),终态 flush
36
+ * 的 entry 永不节流(最终状态必进 pi 权威文件,loadAll/恢复不丢终态);
37
+ * state 文件 writeFile 不节流(rewrite mode 覆盖写,无累积)。节流窗口内的
38
+ * entry 跳过 = pi 文件最后一条 entry 最多落后真实状态一个窗口,崩溃语义与
39
+ * 未落盘 running 尾部丢失同源(kill-9 恢复收编)。
33
40
  * - per-runId 串行 flush 链:同 runId 的 flush 排队顺序执行(不跳过、永不并发
34
41
  * writeFile),链尾吞错防断链——错误只经各 save() Promise 的 settlers 传播。
35
42
  * - dispose():幂等(缓存自身 Promise);刷全部 pending 批 + await 全部 in-flight
36
43
  * 链后返回。dispose 后 save 静默 no-op + debug 日志(session_shutdown 编排收尾)。
37
44
  *
38
- * 序列化策略:
39
- * - WorkflowRun 是带方法的 class 聚合根——序列化只取公共字段快照。
40
- * - Budget/Trace/AgentCall 都有公共构造器或 fromArray 工厂,反序列化时重建实例。
41
- * - Snapshot 形态用 SnapshotVersion 守护(D-5:格式识别)。
45
+ * 序列化策略(下沉收口 D4 后):
46
+ * - 快照投影/重水合/版本 guard 全部消费 core run-snapshot codec(toRunSnapshot/
47
+ * fromRunSnapshot)——字段演进单点(G2);本 store 只保留 IO 策略(rewrite/
48
+ * 去抖/append,D4 裁决:IO 差异归属宿主 store 层)。
49
+ * - 版本值沿用 core SNAPSHOT_VERSION "wf-run-v2"(D4 裁决①:pi 存量逐字节可读)。
50
+ * - pi 侧版本不匹配静默跳过语义保持(D-5);「缺 v 宽容」是 core FileRunStore
51
+ * 侧的存量预处理职责,不内聚进 codec(D4 裁决②),故本侧零改动即保持。
42
52
  *
43
53
  * [S3 查证结论] pi 0.84.1 实装(node_modules/@earendil-works/pi-coding-agent/dist,
44
54
  * core/session-manager.js,PS-19)的 session 生命周期管理不含自动 GC:
@@ -49,9 +59,10 @@
49
59
  * 里用户手动删除选中的单个顶层 session 文件(trash CLI → unlink fallback,
50
60
  * dist/modes/interactive/components/session-selector.js:539-550),非自动、
51
61
  * 不递归子目录。**推论:workflow-state state 文件无限累积,
52
- * 保留策略由本包自担**——磁盘侧保留现为 opt-in(B1):设 {@link STATE_MAX_RUNS_ENV}
53
- * 后每次新 run state 文件首写成功即按 mtime 裁剪到上限(默认关,见
54
- * pruneStateFilesBeyondCap);内存侧由 evictDoneRunsBeyondCap 淘汰。W17 后 state 文件
62
+ * 保留策略由本包自担**——磁盘侧保留默认开(OR-5 ⑥b):每次新 run state 文件首写
63
+ * 成功即按 mtime 裁剪到上限(未设 {@link STATE_MAX_RUNS_ENV} 时取
64
+ * {@link DEFAULT_STATE_MAX_RUNS} 默认值,见 pruneStateFilesBeyondCap;显式非法值
65
+ * 是 opt-out 通道);内存侧由 evictDoneRunsBeyondCap 淘汰。W17 后 state 文件
55
66
  * 已降级为纯性能缓存(权威数据在 session JSONL 的 workflow-record entry),随 session
56
67
  * 文件被用户删除时一并消失。
57
68
  *
@@ -62,76 +73,19 @@ import * as fs from "node:fs";
62
73
  import * as path from "node:path";
63
74
 
64
75
  import type { CustomEntry, ExtensionAPI, ExtensionContext, SessionEntry } from "@earendil-works/pi-coding-agent";
65
- import { getLogger } from "@zhushanwen/pi-extension-logger";
66
-
67
- import { AgentCall } from "./models/agent-call.ts";
68
- import { Budget } from "./models/budget.ts";
69
- import type { RunSpec } from "./models/run-spec.ts";
70
- import type { RunState } from "./models/run-state.ts";
71
- import { Trace } from "./models/trace.ts";
72
- import type { DoneReason, RunStatus, WorkerLogEntry } from "./models/types.ts";
73
- import type { AgentCallOpts, AgentResult, ExecutionTraceNode } from "./models/types.ts";
74
- import type { WorkflowRunMeta } from "./models/workflow-run.ts";
75
- import { WorkflowRun } from "./models/workflow-run.ts";
76
-
77
- // ── Snapshot format (D-5 version guard) ──────────────────────
78
-
79
- /**
80
- * 快照格式版本。D-5:旧 session(无此字段或值不匹配)被 loadAll 忽略。
81
- *
82
- * 版本历史:
83
- * - wf-run-v1:status 三态(含 paused)、meta 含 pausedAt。
84
- * - wf-run-v2(当前):status 两态(running/done)、meta 无 pausedAt(随一次性
85
- * 生命周期收窄,F6/F8)。v1 文件 loadAll 静默跳过——含 v1 running 残留跳过 =
86
- * 静默消失不显示,接受(父文档 D-5 边界声明:旧 run 历史价值低,不做兼容迁移)。
87
- *
88
- * 升级格式时 bump 此常量并在 deserializeRun 中适配——旧文件返回 null(被 loadAll 跳过)。
89
- */
90
- export const SNAPSHOT_VERSION = "wf-run-v2" as const;
91
-
92
- /**
93
- * 持久化快照形态——WorkflowRun 公共字段的 JSON 可序列化投影。
94
- *
95
- * calls 序列化为数组(Map 不能直接 JSON.stringify);反序列化时重建 Map。
96
- * budget/trace 在 deserialize 时重建实例(带方法的 class)。
97
- */
98
- interface RunSnapshot {
99
- v: typeof SNAPSHOT_VERSION;
100
- runId: string;
101
- spec: RunSpec;
102
- state: {
103
- status: RunStatus;
104
- reason?: DoneReason;
105
- budget: {
106
- maxTokens?: number;
107
- maxCost?: number;
108
- maxTimeMs?: number;
109
- usedTokens: number;
110
- usedCost: number;
111
- totalCallCount: number;
112
- };
113
- calls: Array<{
114
- id: number;
115
- opts: AgentCallOpts;
116
- status: "pending" | "running" | "done";
117
- attempts: number;
118
- result?: AgentResult;
119
- sessionId?: string;
120
- sessionFile?: string;
121
- traceNode: ExecutionTraceNode;
122
- }>;
123
- trace: ExecutionTraceNode[];
124
- errorLogs: WorkerLogEntry[];
125
- error?: string;
126
- scriptResult?: unknown;
127
- };
128
- meta: {
129
- startedAt: string;
130
- completedAt?: string;
131
- workerErrorCount?: number;
132
- scriptErrorCount?: number;
133
- };
134
- }
76
+ import {
77
+ DEFAULT_SAVE_MIN_INTERVAL_MS,
78
+ DEFAULT_STATE_MAX_RUNS,
79
+ } from "@zhushanwen/subagent-core/orchestration/file-run-store.ts";
80
+ import { getLogger } from "@zhushanwen/subagent-core/core/logger.ts";
81
+
82
+ import { WorkflowRun } from "@zhushanwen/subagent-core/orchestration/models/workflow-run.ts";
83
+ import {
84
+ SNAPSHOT_VERSION,
85
+ fromRunSnapshot,
86
+ toRunSnapshot,
87
+ type RunSnapshot,
88
+ } from "@zhushanwen/subagent-core/orchestration/run-snapshot.ts";
135
89
 
136
90
  // ── Workflow-record self-describing entry (W17, D4) ─────────
137
91
 
@@ -147,7 +101,7 @@ export const WORKFLOW_RECORD_CUSTOM_TYPE = "workflow-record";
147
101
  *
148
102
  * = 完整 RunSnapshot 快照(runId/status/calls/trace 等全部重建需要的字段)+ 版本号。
149
103
  * 读取方无需逆向解析 state 文件或指针(D4 自描述原则);snapshot 内部自带 D-5
150
- * snapshotVersion guard(deserializeRun 检查),entry 层 v 与 snapshot 层 v 是两级
104
+ * snapshotVersion guard(fromRunSnapshot 检查),entry 层 v 与 snapshot 层 v 是两级
151
105
  * 独立版本(entry schema 演化 vs 快照格式演化)。
152
106
  */
153
107
  // 模块内类型(不导出:无外部消费方,fallow unused_types/private_type_leaks 双轨判定;
@@ -166,117 +120,25 @@ function toWorkflowRecordEntryData(snapshot: RunSnapshot): WorkflowRecordEntryDa
166
120
  return { v: 1, snapshot, updatedAt: new Date().toISOString() };
167
121
  }
168
122
 
169
- // ── Serialization ────────────────────────────────────────────
170
-
171
- function serializeRun(run: WorkflowRun): RunSnapshot {
172
- return {
173
- v: SNAPSHOT_VERSION,
174
- runId: run.runId,
175
- spec: run.spec,
176
- state: {
177
- status: run.state.status,
178
- reason: run.state.reason,
179
- budget: {
180
- maxTokens: run.state.budget.maxTokens,
181
- maxCost: run.state.budget.maxCost,
182
- maxTimeMs: run.state.budget.maxTimeMs,
183
- usedTokens: run.state.budget.usedTokens,
184
- usedCost: run.state.budget.usedCost,
185
- totalCallCount: run.state.budget.totalCallCount,
186
- },
187
- calls: Array.from(run.state.calls.values()).map((c) => {
188
- // strip live(同 trace 序列化,不持久化运行期对象)
189
- const { live: _live, ...traceNodeRest } = c.traceNode;
190
- return {
191
- id: c.id,
192
- opts: c.opts,
193
- status: c.status,
194
- attempts: c.attempts,
195
- result: c.result,
196
- sessionId: c.sessionId,
197
- sessionFile: c.sessionFile,
198
- traceNode: traceNodeRest,
199
- };
200
- }),
201
- // trace 节点浅拷贝时 strip live 字段——ExecutionRecord 含可变 turns[]/controller,
202
- // 不适合序列化;live 是运行期对象(done 时由 dispatchAgentCall 清除,重跑时重建)。
203
- trace: run.state.trace.toArray().map(({ live: _live, ...rest }) => rest),
204
- errorLogs: run.state.errorLogs,
205
- error: run.state.error,
206
- scriptResult: run.state.scriptResult,
207
- },
208
- meta: run.meta,
209
- };
210
- }
211
-
212
- /**
213
- * 反序列化快照为 WorkflowRun。D-5:版本不匹配返回 null(旧 session)。
214
- */
215
- function deserializeRun(snapshot: RunSnapshot): WorkflowRun | null {
216
- // D-5 version guard
217
- if (snapshot.v !== SNAPSHOT_VERSION) return null;
218
-
219
- const budget = new Budget({
220
- maxTokens: snapshot.state.budget.maxTokens,
221
- maxCost: snapshot.state.budget.maxCost,
222
- maxTimeMs: snapshot.state.budget.maxTimeMs,
223
- usedTokens: snapshot.state.budget.usedTokens,
224
- usedCost: snapshot.state.budget.usedCost,
225
- });
226
- budget.totalCallCount = snapshot.state.budget.totalCallCount;
227
-
228
- const calls = new Map<number, AgentCall>();
229
- for (const c of snapshot.state.calls) {
230
- const call = new AgentCall(c.id, c.opts, c.traceNode);
231
- call.status = c.status;
232
- call.attempts = c.attempts;
233
- // Restore result directly — bypasses markRunning/markDone state-machine guards
234
- // because we're reconstructing a known-good persisted state, not transitioning.
235
- if (c.result !== undefined) {
236
- call.result = c.result;
237
- }
238
- if (c.sessionId !== undefined) {
239
- call.setSessionId(c.sessionId);
240
- }
241
- if (c.sessionFile !== undefined) {
242
- call.setSessionFile(c.sessionFile);
243
- }
244
- calls.set(c.id, call);
245
- }
246
-
247
- const trace = Trace.fromArray(snapshot.state.trace);
248
-
249
- const state: RunState = {
250
- status: snapshot.state.status,
251
- reason: snapshot.state.reason,
252
- budget,
253
- calls,
254
- trace,
255
- errorLogs: snapshot.state.errorLogs,
256
- error: snapshot.state.error,
257
- scriptResult: snapshot.state.scriptResult,
258
- };
259
-
260
- const meta: WorkflowRunMeta = {
261
- startedAt: snapshot.meta.startedAt,
262
- completedAt: snapshot.meta.completedAt,
263
- workerErrorCount: snapshot.meta.workerErrorCount,
264
- scriptErrorCount: snapshot.meta.scriptErrorCount,
265
- };
266
-
267
- // WorkflowRun.reconstruct 跳过 I1 校验——持久化的 running 状态没有 worker
268
- // (进程被杀后 worker 不可能还活着),违反 I1。D-4 kill-9 恢复在 session_start
269
- // 时把残留 running 转 done,failed,恢复 I1(见 index.ts session_start handler)。
270
- return WorkflowRun.reconstruct(snapshot.runId, snapshot.spec, state, meta);
271
- }
123
+ // ── Serialization → core codec(下沉收口 D4)──────────────────
124
+ //
125
+ // serializeRun/deserializeRun 本地投影已退役:快照投影/重水合/版本 guard 单源消费
126
+ // core run-snapshot codec(toRunSnapshot/fromRunSnapshot)。键序与 strip live 语义
127
+ // 与原本地实现逐字节一致(⛔5 快照锚定:__tests__/jsonl-run-store-snapshot-codec.test.ts);
128
+ // 唯一投影差异 = spec.budgetRef 剔除(codec 单源裁决,嵌套 run 落盘少一脏字段,
129
+ // 性质同 strip live——同偏差登记)。
272
130
 
273
131
  /** workflow-record entry → 重建 run 写入 recordRuns(v1 entry guard + D-5 版本不匹配
274
132
  * 跳过;同 runId 后写覆盖 = 最后一条 entry 胜出)。返回 entry 是否命中该类型。
275
133
  *
276
- * [SO-DATA-2] per-entry 隔离:deserializeRun 在 v guard 之后直接读 snapshot.state.budget
277
- * 等嵌套字段,残缺 entry(截断/手改/半写)抛 TypeError 会沿 collectEntrySources 穿透
278
- * loadAll 的 catch → 返回空——单条损坏让全部 run 不可见。现单条 try/catch:损坏
279
- * entry 跳过 + warn 留证(含 entry 索引与原因),其余 entry 正常重建。
134
+ * 版本可见性分层(D4 裁决③宿主侧落地):v 不匹配(v1 存量/未来版本)→ 静默跳过
135
+ * (既有语义);v 匹配但形状损坏(codec 返回 undefined——codec 形状校验不抛)→
136
+ * warn 留证。
137
+ *
138
+ * [SO-DATA-2] per-entry 隔离:残缺 entry(截断/手改/半写)不得让 loadAll 返回空。
139
+ * 原实现靠「deserializeRun 抛 TypeError → catch → warn」;codec 收敛后形状损坏
140
+ * 走 undefined 返回(warn 分支保持同等留证),try/catch 保留兜底 codec 唯一抛点
141
+ * (done 快照缺 reason 的 WorkflowRun I2 不变式)。
280
142
  */
281
143
  function collectRecordRun(entry: CustomEntry, entryIndex: number, recordRuns: Map<string, WorkflowRun>): boolean {
282
144
  if (entry.customType !== WORKFLOW_RECORD_CUSTOM_TYPE) return false;
@@ -284,9 +146,17 @@ function collectRecordRun(entry: CustomEntry, entryIndex: number, recordRuns: Ma
284
146
  const data = entry.data as WorkflowRecordEntryData | undefined;
285
147
  if (data?.v !== 1 || !data.snapshot) return true;
286
148
  try {
287
- const run = deserializeRun(data.snapshot);
288
- // D-5: null = old snapshot format / version mismatch — skip silently
289
- if (run) recordRuns.set(run.runId, run); // 后写覆盖 = 最后一条 entry 胜出
149
+ if (data.snapshot.v === SNAPSHOT_VERSION) {
150
+ const run = fromRunSnapshot(data.snapshot);
151
+ if (run) {
152
+ recordRuns.set(run.runId, run); // 后写覆盖 = 最后一条 entry 胜出
153
+ } else {
154
+ logger.warn(
155
+ `[subagent-workflow] workflow-record entry #${entryIndex} corrupted, skipped run rebuild: snapshot shape invalid`,
156
+ );
157
+ }
158
+ }
159
+ // D-5: 版本不匹配 = old snapshot format / future version — skip silently
290
160
  } catch (err) {
291
161
  const reason = err instanceof Error ? err.message : String(err);
292
162
  logger.warn(
@@ -325,24 +195,24 @@ function collectEntrySources(entries: SessionEntry[]): {
325
195
  return { recordRuns, pointers };
326
196
  }
327
197
 
328
- /** 旧 link 指针指向的 state 文件读取:末行 JSON 解析重建。损坏/不可读返回 null
329
- * (单文件失败不阻断其余 run 重建)。 */
198
+ /** 旧 link 指针指向的 state 文件读取:末行 JSON 解析重建。损坏/不可读/版本不匹配
199
+ * 返回 null(单文件失败不阻断其余 run 重建;D-5 静默跳过语义保持)。 */
330
200
  async function loadRunFromStateFile(filePath: string): Promise<WorkflowRun | null> {
331
201
  try {
332
202
  const content = await fs.promises.readFile(filePath, "utf8");
333
203
  const lines = content.split("\n").filter((l) => l.trim());
334
204
  const lastLine = lines[lines.length - 1];
335
205
  if (!lastLine) return null;
336
- const parsed = JSON.parse(lastLine) as RunSnapshot;
337
- // D-5: null = old format / version mismatch — skip silently
338
- return deserializeRun(parsed);
206
+ const parsed: unknown = JSON.parse(lastLine);
207
+ // D-5: undefined = old format / version mismatch / corrupt shape — skip silently
208
+ return fromRunSnapshot(parsed) ?? null;
339
209
  } catch {
340
210
  // Corrupt/unreadable state file — skip (don't crash loadAll).
341
211
  return null;
342
212
  }
343
213
  }
344
214
 
345
- // ── State file retention (B1, opt-in) ────────────────────────
215
+ // ── State file retention (OR-5 ⑥b, default-on) ───────────────
346
216
 
347
217
  /** run state 文件名 glob:runId 形如 `wf-<ts>-<rand>`(lifecycle.ts 生成),只删命中者。
348
218
  * 同目录可能存在的非 state 文件(及 session JSONL——在父目录,本就不在扫描范围)永不碰。 */
@@ -412,16 +282,21 @@ const logger = getLogger("subagents");
412
282
  * save 去抖窗口默认值(ms)。区间 100-250 内取值——agent-call 间隔秒级,
413
283
  * 200ms 足以合并同一 call 周期内的多次状态 mutation,又不至于让崩溃窗口
414
284
  * (未 flush 的 running 尾部丢失,等价崩溃链由 kill-9 恢复收编)明显放大。
415
- * export 供测试边界构造(对齐 TRACE_RESULT_MAX_CHARS export 先例)。
285
+ * 模块私有:无外部消费方(构造参数 saveDebounceMs 可调窗口,测试经其注入)。
416
286
  */
417
- export const DEFAULT_SAVE_DEBOUNCE_MS = 200;
287
+ const DEFAULT_SAVE_DEBOUNCE_MS = 200;
418
288
 
419
289
  /**
420
- * 磁盘保留清理的 opt-in 开关 env(B1):workflow-state 目录内 run state 文件上限。
290
+ * 磁盘保留清理的上限 env(OR-5 ⑥b 默认开):workflow-state 目录内 run state
291
+ * 文件上限。
421
292
  *
422
- * 默认关——未设/空/非有限数/≤0 都不清理(「limits 默认关」裁决;解析对齐
423
- * session-runner 的 SPAWN_WATCHDOG_ENV watchdog 风格:Number() + Number.isFinite
424
- * 过滤,非法值回落 undefined = 不启用,而非抛错或取默认上限)。
293
+ * 解析语义与 core FileRunStore envName 通道一致(两实现面单源 {@link
294
+ * DEFAULT_STATE_MAX_RUNS}):
295
+ * - 未设/空 → 按默认上限 {@link DEFAULT_STATE_MAX_RUNS} 裁剪(**默认开**——
296
+ * OR-5 修复前的 opt-in「默认关」正是跨 run 无界累积缺陷本身);
297
+ * - 有限正数 → 上限 = env 值(显式覆盖默认值);
298
+ * - 非法值(非有限数/≤0)→ 不清理(显式 opt-out 通道:用户意图不明时不动
299
+ * 磁盘,对齐 prune 内部「任何失败都不抛」的保守哲学)。
425
300
  *
426
301
  * 用 XYZ_ 前缀而非 PI_:本 env 是 pi 进程内读的配置 env,xyz-agent 桌面 spawn 链按
427
302
  * ENV_WHITELIST_PREFIXES(只有 XYZ_ 等)过滤,PI_ 前缀在桌面场景被静默丢弃——
@@ -429,10 +304,10 @@ export const DEFAULT_SAVE_DEBOUNCE_MS = 200;
429
304
  */
430
305
  export const STATE_MAX_RUNS_ENV = "XYZ_SUBAGENT_STATE_MAX_RUNS";
431
306
 
432
- /** 解析保留上限;env 未设/非法/≤0 返回 undefined(调用方不清理)。 */
307
+ /** 解析保留上限;env 未设/空 → 默认上限,显式非法/≤0 → undefined(不清理)。 */
433
308
  function getEnvStateMaxRuns(): number | undefined {
434
309
  const raw = process.env[STATE_MAX_RUNS_ENV];
435
- if (!raw) return undefined;
310
+ if (raw === undefined || raw === "") return DEFAULT_STATE_MAX_RUNS;
436
311
  const parsed = Number(raw);
437
312
  if (!Number.isFinite(parsed) || parsed <= 0) return undefined;
438
313
  return parsed;
@@ -453,7 +328,7 @@ interface PendingSaveBatch {
453
328
  settlers: Array<{ resolve: () => void; reject: (e: unknown) => void }>;
454
329
  }
455
330
 
456
- export interface JsonlRunStoreOptions {
331
+ interface JsonlRunStoreOptions {
457
332
  /** Session directory root (state files live under <sessionDir>/workflow-state/). */
458
333
  sessionDir: string;
459
334
  /** Pi ExtensionAPI for workflow-record appendEntry writes (optional for testing). */
@@ -462,6 +337,12 @@ export interface JsonlRunStoreOptions {
462
337
  ctx?: ExtensionContext;
463
338
  /** save 去抖窗口(ms),默认 {@link DEFAULT_SAVE_DEBOUNCE_MS}。 */
464
339
  saveDebounceMs?: number;
340
+ /**
341
+ * workflow-record entry append 节流最小间隔(ms);0 = 禁用节流。缺省
342
+ * {@link DEFAULT_SAVE_MIN_INTERVAL_MS}(单源复用 core 常量)。测试经此注入小窗口
343
+ * (fake timers 推进)。
344
+ */
345
+ entryAppendMinIntervalMs?: number;
465
346
  }
466
347
 
467
348
  export class JsonlRunStore {
@@ -469,10 +350,19 @@ export class JsonlRunStore {
469
350
  private readonly pi?: ExtensionAPI;
470
351
  private readonly ctx?: ExtensionContext;
471
352
  private readonly saveDebounceMs: number;
353
+ /** workflow-record entry append 节流最小间隔(ms),0 = 禁用。 */
354
+ private readonly entryAppendMinIntervalMs: number;
472
355
  /** per-runId 去抖批(热路径)。 */
473
356
  private readonly pending = new Map<string, PendingSaveBatch>();
474
357
  /** 本实例已至少成功发起过一次 flush 的 runId(冷/热路径判据)。 */
475
358
  private readonly writtenOnce = new Set<string>();
359
+ /**
360
+ * per-runId 上次 workflow-record entry append 时刻(节流判据,时间源 Date.now()——
361
+ * fake timers 下可推进)。终态 append 后删(终态后 runId 不再 save);残留条目
362
+ * 只出现在 running 中 run 消失场景,单条可忽略(对齐 core FileRunStore.lastSavedAt
363
+ * 的取舍先例)。
364
+ */
365
+ private readonly lastEntryAppendAt = new Map<string, number>();
476
366
  /**
477
367
  * per-runId 串行 flush 链。同 runId 的 flush 排队顺序执行(排队不跳过——
478
368
  * 跳过会丢最新状态且打破后写覆盖前写的单调性),不同 runId 互不阻塞。
@@ -488,6 +378,10 @@ export class JsonlRunStore {
488
378
  this.pi = opts.pi;
489
379
  this.ctx = opts.ctx;
490
380
  this.saveDebounceMs = opts.saveDebounceMs ?? DEFAULT_SAVE_DEBOUNCE_MS;
381
+ this.entryAppendMinIntervalMs = Math.max(
382
+ 0,
383
+ opts.entryAppendMinIntervalMs ?? DEFAULT_SAVE_MIN_INTERVAL_MS,
384
+ );
491
385
  }
492
386
 
493
387
  /** State directory: <sessionDir>/workflow-state/ */
@@ -646,16 +540,36 @@ export class JsonlRunStore {
646
540
  throw err;
647
541
  }
648
542
  // serialize-at-flush:写 flush 时刻的最新聚合状态(latestRun 语义)
649
- const snapshot = serializeRun(run);
543
+ const snapshot = toRunSnapshot(run);
650
544
  await fs.promises.writeFile(filePath, JSON.stringify(snapshot) + "\n", "utf8");
651
- // W17 [D4]:每次成功 flush 同步 append 自描述 workflow-record entry(同一份
652
- // snapshot,entry 与 state 文件内容一致)。pi 文件是 workflow 数据持久化权威
653
- //(loadAll 优先从 entry 重建),state 文件降级纯性能缓存。pi 未注入(测试)时跳过。
654
- this.pi?.appendEntry(
655
- WORKFLOW_RECORD_CUSTOM_TYPE,
656
- toWorkflowRecordEntryData(snapshot),
657
- );
658
- // B1 磁盘保留清理(opt-in):新 run state 文件首写成功后触发(rollbackFirstWrite
545
+ // W17 [D4]:成功 flush 同步 append 自描述 workflow-record entry(同一份 snapshot,
546
+ // entry 与 state 文件内容一致)。pi 文件是 workflow 数据持久化权威(loadAll 优先
547
+ // 从 entry 重建),state 文件降级纯性能缓存。pi 未注入(测试)时跳过。
548
+ // [B-1] entry append 节流(语义对齐 core FileRunStore.save OR-5 ⑥a):running
549
+ // 中间态距上次 append 不足间隔 → 跳过(pi session JSONL append-only,节流前
550
+ // 每次 flush 全量 append 累积单 run O(n²) 磁盘);终态永不节流(最终状态必进
551
+ // pi 权威文件);间隔 0 禁用。判据在 append 成功后更新——本调用点位于 writeFile
552
+ // 成功之后,writeFile 抛错时判据不更新,不吞下一次重试机会。
553
+ const isTerminal = run.state.status !== "running";
554
+ const now = Date.now();
555
+ const lastAppendAt = this.lastEntryAppendAt.get(runId);
556
+ if (
557
+ this.entryAppendMinIntervalMs <= 0 ||
558
+ isTerminal ||
559
+ lastAppendAt === undefined ||
560
+ now - lastAppendAt >= this.entryAppendMinIntervalMs
561
+ ) {
562
+ this.pi?.appendEntry(
563
+ WORKFLOW_RECORD_CUSTOM_TYPE,
564
+ toWorkflowRecordEntryData(snapshot),
565
+ );
566
+ if (isTerminal) {
567
+ this.lastEntryAppendAt.delete(runId);
568
+ } else {
569
+ this.lastEntryAppendAt.set(runId, now);
570
+ }
571
+ }
572
+ // OR-5 ⑥b 磁盘保留清理(默认开):新 run state 文件首写成功后触发(rollbackFirstWrite
659
573
  // 即 save() 冷路径传入的 isFirstWrite——「本实例首次写该 runId」≈ 新文件落盘时刻,
660
574
  // 每个 run 只清一次,热路径 flush 不重复扫描目录)。prune 内部吞错不抛,
661
575
  // 在串行链上 await:save 返回即清理已定,测试可同步断言目录终态。
@@ -711,15 +625,13 @@ export class JsonlRunStore {
711
625
 
712
626
  private async doDispose(): Promise<void> {
713
627
  // 同步置位:阻断新 save 进入去抖/冷路径(R5 no-op 分支接住 shutdown 后
714
- // in-flight 链的迟到 save)。doDispose 被调用后同步执行到第一个 await 前。
628
+ // in-flight 链的迟到 save)。置位必须先于 flushPendingSaves——async 函数体
629
+ // 在调用时同步执行到第一个 await,批收集发生在置位后的同一同步段,时序与
630
+ // 折叠前的内联收集逐分支等值。
715
631
  this.disposed = true;
716
- const flushes: Promise<void>[] = [];
717
- for (const [runId, batch] of Array.from(this.pending.entries())) {
718
- clearTimeout(batch.timer);
719
- this.pending.delete(runId);
720
- flushes.push(this.enqueueFlush(runId, batch.latestRun, batch.settlers, false));
721
- }
722
- await Promise.allSettled(flushes);
632
+ // 复用 flushPendingSaves(批收集循环与 await allSettled 与折叠前内联实现逐行等价;
633
+ // flushPendingSaves 自身不动 disposed——dispose 语义仍由本方法的置位与缓存 Promise 承担)
634
+ await this.flushPendingSaves();
723
635
  // await 全部 in-flight 链(ES4:flush 全部落定后才返回)
724
636
  await Promise.allSettled(Array.from(this.chains.values()));
725
637
  }
@@ -729,8 +641,8 @@ export class JsonlRunStore {
729
641
  *
730
642
  * 1. 优先扫描自描述 `workflow-record` entry(重建源——同一 runId 多条时最后一条
731
643
  * 胜出,等价「最后一次成功 flush」)。entry 层 v1 guard:不认识的版本跳过而非
732
- * 猜测;snapshot 层 D-5 snapshotVersion guard 保持(版本不匹配 → deserializeRun
733
- * 返回 null → 跳过,不做兼容迁移)。
644
+ * 猜测;snapshot 层 D-5 snapshotVersion guard 保持(版本不匹配 → fromRunSnapshot
645
+ * 返回 undefined → 跳过,不做兼容迁移)。
734
646
  * 2. 旧 `workflow-state-link` 指针 entry 兼容读取(优先级低——存量 run 不静默
735
647
  * 丢失,父文档 #9 踩坑):entry 未覆盖的 runId 经指针读 state 文件最后行。
736
648
  *