@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
@@ -1,906 +0,0 @@
1
- // src/core/execution-record.ts
2
- //
3
- // 唯一执行状态对象 + 唯一创建/更新/完成/投影入口。
4
- //
5
- // 收口设计(2026-06-22 重构):
6
- // 一次执行的完整内容(text/thinking/toolCalls/usage)按 turn 收口在 record.turns[]。
7
- // eventLog / currentActivity / result 文本均从 turns[] 派生(getEventLog /
8
- // getCurrentActivity / getFullText),不再独立存储切片或缓冲。
9
- //
10
- // createRecord 唯一创建入口(model 创建时必填,消灭 poll 路径 model 丢失)
11
- // updateFromEvent 唯一事件更新入口(累积进 turns[],消灭闭包旁路累积器)
12
- // completeRecord 唯一完成入口(冻结状态)
13
- // project/snapshot 唯一投影入口(两路径字段一致)
14
- //
15
- // Core 层叶子原语:仅依赖 types.ts。零 Pi / Runtime / TUI 依赖。
16
-
17
- import type {
18
- AgentEvent,
19
- AgentEventLogEntry,
20
- AgentResult,
21
- AgentUsage,
22
- AgentUsageTotal,
23
- ClosedReason,
24
- DisplayItem,
25
- ExecutionMode,
26
- ExecutionRecord,
27
- ExecutionStatus,
28
- InternalToolCall,
29
- RecordSnapshot,
30
- SubagentToolDetails,
31
- ToolCall,
32
- ToolCallResult,
33
- Turn,
34
- } from "./types.ts";
35
-
36
- // ============================================================
37
- // 常量
38
- // ============================================================
39
-
40
- /** currentActivity label 的前缀截断长度(与旧 ACTIVITY_LABEL_MAX 对齐)。 */
41
- const ACTIVITY_LABEL_MAX = 60;
42
- /** turn_end 派生条目 label 的最大长度(取本 turn 文本开头)。 */
43
- const TURN_SUMMARY_MAX = 80;
44
- /** tool label 的最大长度(command/query/url/basename 截断,保持 TUI 列宽稳定)。 */
45
- const TOOL_LABEL_MAX = 100;
46
- /** ms → s 换算。elapsedSeconds 唯一计算点用。 */
47
- const MS_PER_SECOND = 1000;
48
-
49
- // ============================================================
50
- // Label 提取(eventLog 派生的伴生逻辑,co-locate 于 Core)
51
- // ============================================================
52
-
53
- /**
54
- * 从 toolName + args 提取 eventLog label(人类可读)。
55
- *
56
- * read/edit/write → "{tool} {basename}"(取 path 参数)
57
- * bash → "{tool} {command 首行}"(截断)
58
- * web_search → "{tool} {query}"
59
- * web_fetch → "{tool} {url}"
60
- * 其他 / 无 args → 裸 toolName
61
- *
62
- * 纯函数(零依赖),由 getEventLog 派生 tool 条目时调用。
63
- * 所有取自参数的字符串都经 truncateLabel 截断到 TOOL_LABEL_MAX——
64
- * 保持 TUI 列宽稳定(避免一条 10KB bash 命令撑爆 compact view)。
65
- */
66
- export function extractLabelFromArgs(toolName: string, args: unknown): string {
67
- if (typeof args !== "object" || args === null) return toolName;
68
- const a = args as Record<string, unknown>;
69
-
70
- // 读/写/编辑类:取路径 basename(~/.pi/.../foo.ts → foo.ts)
71
- // 兼容 Pi tool 的多种路径参数名:path / file_path / filePath
72
- const pathLike = (a.path ?? a.file_path ?? a.filePath) as unknown;
73
- if (typeof pathLike === "string" && pathLike.length > 0) {
74
- const base = pathLike.split(/[\\/]/).pop() ?? pathLike;
75
- return `${toolName} ${truncateLabel(base)}`;
76
- }
77
-
78
- // bash:command 首行(截断)
79
- const cmd = a.command as unknown;
80
- if (typeof cmd === "string" && cmd.length > 0) {
81
- const firstLine = cmd.split("\n", 1)[0].trim();
82
- return `${toolName} ${truncateLabel(firstLine)}`;
83
- }
84
-
85
- // web_search:query
86
- const query = a.query as unknown;
87
- if (typeof query === "string" && query.length > 0) {
88
- return `${toolName} ${truncateLabel(query)}`;
89
- }
90
-
91
- // web_fetch:url
92
- const url = a.url as unknown;
93
- if (typeof url === "string" && url.length > 0) {
94
- return `${toolName} ${truncateLabel(url)}`;
95
- }
96
-
97
- return toolName;
98
- }
99
-
100
- /** 截断 label 到 maxLen(非省略号——保持列宽稳定,避免长命令/路径撑爆 TUI 列宽)。 */
101
- function truncateLabel(label: string): string {
102
- return label.length > TOOL_LABEL_MAX ? label.slice(0, TOOL_LABEL_MAX) : label;
103
- }
104
-
105
- /**
106
- * 累加两个 AgentUsage(field-wise)。prev 为空时返回 next 的拷贝。
107
- * 供 message_end 把 usage 增量并入 turn.usageDelta。
108
- */
109
- function addUsage(prev: AgentUsage | undefined, next: AgentUsage): AgentUsage {
110
- if (prev === undefined) {
111
- return {
112
- input: next.input ?? 0,
113
- output: next.output ?? 0,
114
- cacheRead: next.cacheRead ?? 0,
115
- cacheWrite: next.cacheWrite ?? 0,
116
- cost: next.cost,
117
- };
118
- }
119
- return {
120
- input: (prev.input ?? 0) + (next.input ?? 0),
121
- output: (prev.output ?? 0) + (next.output ?? 0),
122
- cacheRead: (prev.cacheRead ?? 0) + (next.cacheRead ?? 0),
123
- cacheWrite: (prev.cacheWrite ?? 0) + (next.cacheWrite ?? 0),
124
- cost: (prev.cost ?? 0) + (next.cost ?? 0),
125
- };
126
- }
127
-
128
- // ============================================================
129
- // 创建(唯一入口)
130
- // ============================================================
131
-
132
- /** 创建一个空 turn(text/thinking 空,无 toolCalls,未闭合)。 */
133
- function emptyTurn(): Turn {
134
- return { text: "", thinking: "", toolCalls: [], usageDelta: undefined, closed: false };
135
- }
136
-
137
- /**
138
- * 唯一创建入口。identity 字段(agent/model/thinkingLevel/mode/task)一次确定不可变。
139
- *
140
- * model 创建时必填——这是 poll 路径 model 丢失的架构修复
141
- * (旧实现 background record 运行时丢 model,poll 返回缺字段)。
142
- */
143
- export function createRecord(
144
- id: string,
145
- identity: {
146
- agent: string;
147
- model: string;
148
- thinkingLevel?: string;
149
- mode: ExecutionMode;
150
- task: string;
151
- /** 短标签(≤20 字符),必填。持久化兜底空串。 */
152
- slug: string;
153
- startedAt: number;
154
- /** 根 Pi session ID(session 隔离过滤用)。递归链上所有层同值。 */
155
- rootSessionId?: string;
156
- /** 直接父 subagent record ID。顶层为 undefined。 */
157
- parentRecordId?: string;
158
- /** subagent 递归深度。顶层=0。 */
159
- depth?: number;
160
- /** 对话模式标志(true = 可持续对话,轮次完成进 idle)。默认 undefined/false = 一次性。 */
161
- chatMode?: boolean;
162
- /** 空闲超时毫秒数(仅 chatMode 有意义)。覆盖默认 5min。 */
163
- idleTimeoutMs?: number;
164
- /** 实际执行引擎 id(P4 路由留痕,D9①)。缺省 = pi 投影(存量零迁移)。 */
165
- engine?: string;
166
- /** 引擎 fallback 留痕(probe 失败路由回默认引擎)。GUI 警告条数据源。 */
167
- engineFallback?: { from: string; reason: string };
168
- controller?: AbortController;
169
- },
170
- ): ExecutionRecord {
171
- return {
172
- id,
173
- agent: identity.agent,
174
- model: identity.model,
175
- thinkingLevel: identity.thinkingLevel,
176
- mode: identity.mode,
177
- task: identity.task,
178
- slug: identity.slug,
179
- startedAt: identity.startedAt,
180
- rootSessionId: identity.rootSessionId,
181
- parentRecordId: identity.parentRecordId,
182
- depth: identity.depth ?? 0,
183
- chatMode: identity.chatMode,
184
- idleTimeoutMs: identity.idleTimeoutMs,
185
- engine: identity.engine,
186
- engineFallback: identity.engineFallback,
187
-
188
- // 状态(实时更新)
189
- status: "running",
190
- // turns[] 初始化为 [空 turn]——第一个 turn 从创建即存在,
191
- // updateFromEvent 直接往 turns[last] 累积,无需「无 turn」分支判断。
192
- turns: [emptyTurn()],
193
- turnCount: 0,
194
- totalTokens: 0,
195
- lastError: undefined,
196
- // 对话轮次计数(首轮 = 0,每完成一轮 finalizeRoundToIdle +1)。非 chatMode 不自增。
197
- round: 0,
198
-
199
- // 完成(completeRecord 唯一写点)
200
- endedAt: undefined,
201
- result: undefined,
202
- error: undefined,
203
- agentResult: undefined,
204
-
205
- // 控制(仅 background 持有 controller;sync 为 undefined)
206
- controller: identity.controller,
207
- };
208
- }
209
-
210
- // ============================================================
211
- // 事件更新(唯一更新点)
212
- // ============================================================
213
-
214
- /**
215
- * 取当前正在进行(未 closed)的 turn;若全部 closed 则开新 turn。
216
- * 保证调用后返回的 turn 一定 closed===false,可安全累积内容。
217
- */
218
- function currentTurn(record: ExecutionRecord): Turn {
219
- const last = record.turns[record.turns.length - 1];
220
- if (last !== undefined && !last.closed) return last;
221
- const fresh = emptyTurn();
222
- record.turns.push(fresh);
223
- return fresh;
224
- }
225
-
226
- /**
227
- * 在 record.turns[] 范围内倒序找最后一个同名且仍 running 的 toolCall。
228
- *
229
- * 扫描所有 turn(非仅当前 turn)——SDK 在 turn_end 后仍可能补发滞后的 tool_end,
230
- * 仅扫当前 turn 会漏配对、误 push 幽灵 ToolCall。跨 turn 扫描兜底滞后事件。
231
- *
232
- * 返回 [turn, index];未找到返回 undefined。
233
- */
234
- function findRunningToolCall(
235
- record: ExecutionRecord,
236
- toolName: string,
237
- ): readonly [Turn, number] | undefined {
238
- for (let t = record.turns.length - 1; t >= 0; t--) {
239
- const turn = record.turns[t];
240
- if (turn === undefined) continue;
241
- for (let i = turn.toolCalls.length - 1; i >= 0; i--) {
242
- const tc = turn.toolCalls[i];
243
- if (tc?._status === "running" && tc.toolName === toolName) {
244
- return [turn, i] as const;
245
- }
246
- }
247
- }
248
- return undefined;
249
- }
250
-
251
- /**
252
- * [perf] running toolCall 倒序索引:tool_start push 位置入索引,tool_end 弹尾定位
253
- *(尾部 = 最后 push 的同名项,与 findRunningToolCall 倒序全扫的语义等价),把每次
254
- * tool_end 的 O(所有 turns × toolCalls) 扫描降为 O(1)。WeakMap 按 record 实例隔离
255
- *(createRecord 新实例从空索引开始,不影响旧实例)。
256
- * 索引 miss(重建 record 的历史 running toolCall / 外部注入工具无 tool_start)
257
- * 回退 findRunningToolCall 全扫兜底——正确性不依赖索引完整性。
258
- */
259
- const runningToolIndex = new WeakMap<ExecutionRecord, Map<string, Array<{ turn: Turn; idx: number }>>>();
260
-
261
- function indexToolStart(record: ExecutionRecord, turn: Turn, toolName: string): void {
262
- let byName = runningToolIndex.get(record);
263
- if (byName === undefined) {
264
- byName = new Map();
265
- runningToolIndex.set(record, byName);
266
- }
267
- const arr = byName.get(toolName);
268
- if (arr === undefined) {
269
- byName.set(toolName, [{ turn, idx: turn.toolCalls.length - 1 }]);
270
- } else {
271
- arr.push({ turn, idx: turn.toolCalls.length - 1 });
272
- }
273
- }
274
-
275
- /**
276
- * 从 AgentEvent 更新 record。所有数据收口进 record.turns[]。
277
- * - text/thinking:流式累积进 currentTurn()(完整内容,非切片)
278
- * - tool_start/end:push 进 currentTurn().toolCalls(含完整 result)
279
- * tool_end 跨 turn 扫描找 running 同名 toolCall(兜底滞后事件)
280
- * - turn_end:闭合当前 turn,记 closedTs(真实墙钟,供 getEventLog);
281
- * 正常闭合清 lastError(瞬态 error 恢复后不应误判 success=false)
282
- * - message_end:usage 增量存进末 turn.usageDelta(直接写末 turn,不开新 turn);
283
- * totalTokens 累加
284
- * - error:存 record.lastError(getEventLog 派生 error 条目用)
285
- *
286
- * 唯一写点——session-runner 闭包不再旁路累积,collectResult 从 record 读。
287
- *
288
- * 穷尽性:switch 覆盖 AgentEvent 全部 variant;default 的 `never` 断言保证
289
- * 新增 variant 时编译期报错(而非静默 no-op)。
290
- */
291
- export function updateFromEvent(record: ExecutionRecord, event: AgentEvent): void {
292
- switch (event.type) {
293
- // ── text / thinking:流式累积进当前 turn ──
294
- case "text_delta": {
295
- currentTurn(record).text += event.delta;
296
- return;
297
- }
298
- case "thinking_delta": {
299
- currentTurn(record).thinking += event.delta;
300
- return;
301
- }
302
-
303
- // ── tool_start:push 一个 running 的 InternalToolCall(带 startedTs)──
304
- case "tool_start": {
305
- const tc: InternalToolCall = {
306
- toolName: event.toolName,
307
- args: event.args,
308
- result: undefined,
309
- isError: false,
310
- _status: "running",
311
- startedTs: Date.now(),
312
- };
313
- const turn = currentTurn(record);
314
- turn.toolCalls.push(tc);
315
- indexToolStart(record, turn, event.toolName);
316
- return;
317
- }
318
-
319
- // ── tool_end:索引弹尾 O(1) 定位 running 同名 toolCall,miss 回退全扫兜底 ──
320
- case "tool_end": {
321
- let matched: readonly [Turn, number] | undefined;
322
- const byName = runningToolIndex.get(record);
323
- const arr = byName?.get(event.toolName);
324
- if (arr !== undefined && arr.length > 0) {
325
- const item = arr[arr.length - 1];
326
- arr.pop();
327
- const tc = item.turn.toolCalls[item.idx];
328
- if (tc !== undefined && tc._status === "running") {
329
- matched = [item.turn, item.idx] as const;
330
- }
331
- }
332
- if (matched === undefined) {
333
- // 兜底:重建 record 的历史 running toolCall(索引未覆盖)、索引项被外部路径
334
- // 置非 running 等场景——保持与旧实现一致的跨 turn 倒序全扫。
335
- matched = findRunningToolCall(record, event.toolName);
336
- }
337
- if (matched !== undefined) {
338
- const [turn, i] = matched;
339
- const tc = turn.toolCalls[i]!;
340
- tc.args = event.args ?? tc.args;
341
- tc.result = event.result;
342
- tc.isError = event.isError ?? false;
343
- tc._status = event.isError ? "failed" : "done";
344
- return;
345
- }
346
- // 匹配失败(SDK 发了 tool_end 但无对应 tool_start,如外部注入的工具):
347
- // 直接 push 一个已完成的 InternalToolCall,避免数据丢失。
348
- currentTurn(record).toolCalls.push({
349
- toolName: event.toolName,
350
- args: event.args,
351
- result: event.result,
352
- isError: event.isError ?? false,
353
- _status: event.isError ? "failed" : "done",
354
- startedTs: Date.now(),
355
- });
356
- return;
357
- }
358
-
359
- // ── turn_end:闭合当前 turn,记 closedTs,turnCount++,清 lastError ──
360
- case "turn_end": {
361
- const turn = currentTurn(record);
362
- turn.closed = true;
363
- turn.closedTs = Date.now();
364
- record.turnCount += 1;
365
- // turn 正常闭合意味着本段执行成功——清掉运行期可能记录的瞬态 error,
366
- // 避免瞬态 error 恢复后 session-runner 仍据 lastError 误判 success=false。
367
- // (若 turn_end 后 message_end 报 error,会在 message_end 分支重新写回 lastError。)
368
- record.lastError = undefined;
369
- return;
370
- }
371
-
372
- // ── message_end:usage 增量累加进 currentTurn().usageDelta,totalTokens 累加 ──
373
- //
374
- // usageDelta 按 message_end **累加**(非覆盖)——同一 turn 内若多次 message_end
375
- // 到达(或 turn_end 后的滞后 message_end 落到 currentTurn 开的新 turn),
376
- // 累加保证不丢 usage。getTotalUsage 扁平求和所有 turn,归属 turn 的精确性
377
- // 不影响最终 total(无消费方读单 turn usage)。
378
- case "message_end": {
379
- if (event.usage) {
380
- const turn = currentTurn(record);
381
- turn.usageDelta = addUsage(turn.usageDelta, event.usage);
382
- // totalTokens 累加四项之和(保留旧语义,投影直接读)
383
- record.totalTokens +=
384
- (event.usage.input ?? 0) + (event.usage.output ?? 0) +
385
- (event.usage.cacheRead ?? 0) + (event.usage.cacheWrite ?? 0);
386
- }
387
- // message_end 的 error(stopReason=error)也记进 lastError
388
- if (event.error) {
389
- record.lastError = event.error;
390
- }
391
- return;
392
- }
393
-
394
- // ── error:存 record.lastError(getEventLog 派生 error 条目)──
395
- case "error": {
396
- record.lastError = event.message;
397
- return;
398
- }
399
-
400
- // ── compaction:不产生数据(不变)──
401
- case "compaction": {
402
- return;
403
- }
404
-
405
- default: {
406
- // 穷尽性检查:新增 AgentEvent variant 时编译期报错
407
- const _exhaustive: never = event;
408
- return _exhaustive;
409
- }
410
- }
411
- }
412
-
413
- // ============================================================
414
- // 派生视图(从 turns[] 推导,不存储)
415
- // ============================================================
416
-
417
- /**
418
- * 从 turns[] 派生有序事件序列(eventLog)。
419
- *
420
- * 每个 turn 产出:tool_start/tool_end 对(按 toolCalls 顺序)+ turn_end。
421
- * 若有 lastError,末尾追加 error 条目。
422
- *
423
- * [turn1{toolCalls:[A,B]}, turn2{toolCalls:[C]}] + lastError
424
- * → [tool_start A, tool_end A, tool_start B, tool_end B, turn_end,
425
- * tool_start C, tool_end C, turn_end, error]
426
- *
427
- * ts 为真实墙钟时间戳:tool 条目用 tc.startedTs,turn_end 用 turn.closedTs。
428
- * (旧实现派生时 ts += 1 是合成值,无法表达真实时序——现已改为存真实时间戳。)
429
- *
430
- * 纯函数:每次调用重新生成,不缓存。消费方按需调(投影时用)。
431
- */
432
- export function getEventLog(record: ExecutionRecord): AgentEventLogEntry[] {
433
- const log: AgentEventLogEntry[] = [];
434
- for (const turn of record.turns) {
435
- for (const tc of turn.toolCalls) {
436
- const label = extractLabelFromArgs(tc.toolName, tc.args);
437
- const ts = tc.startedTs;
438
- log.push({ type: "tool_start", label, ts, status: "running" });
439
- if (tc._status !== "running") {
440
- log.push({ type: "tool_end", label, ts, status: tc._status });
441
- }
442
- }
443
- if (turn.closed) {
444
- const summary = turn.text.length > 0
445
- ? (turn.text.length > TURN_SUMMARY_MAX ? turn.text.slice(0, TURN_SUMMARY_MAX) : turn.text)
446
- : "turn";
447
- log.push({ type: "turn_end", label: summary, ts: turn.closedTs ?? record.startedAt });
448
- }
449
- }
450
- if (record.lastError) {
451
- log.push({ type: "error", label: record.lastError, ts: Date.now() });
452
- }
453
- return log;
454
- }
455
-
456
- /**
457
- * [STEP3] 从 turns[] 派生 displayItems(对齐 nicobailon getDisplayItems)。
458
- *
459
- * 与 getEventLog 的区别:产出可渲染单元(toolCall 含完整 name+args,text 含正文),
460
- * 而非离散事件。renderResult compact 用此数据 + formatToolCall 生成与 nicobailon
461
- * 一致的 `→ formatToolCall` 行格式。
462
- *
463
- * 派生规则(与 nicobailon getDisplayItems(messages) 等价):
464
- * - 遍历 turns[],每个 turn:先 text(如果有),再 toolCalls
465
- * - toolCall:{ type:"toolCall", name, args, status }
466
- * - text:{ type:"text", text }(取首行,避免撑爆 compact)
467
- * - 跳过 thinking(与 nicobailon 一致,不在 compact 展示推理)
468
- *
469
- * 参数类型放宽为 `{ turns: readonly Turn[] }` 结构子集——ExecutionRecord 和
470
- * ReconstructedRecord 都满足,磁盘重建路径(record-store)可复用此函数派生
471
- * displayItems,而非给空数组(否则终态 record 详情看不到 text)。
472
- */
473
- export function getDisplayItems(record: { turns: readonly Turn[] }): DisplayItem[] {
474
- const items: DisplayItem[] = [];
475
- for (const turn of record.turns) {
476
- // assistant 正文(与 nicobailon 顺序一致:先 text 后 toolCall)
477
- if (turn.text.length > 0) {
478
- items.push({ type: "text", text: turn.text });
479
- }
480
- for (const tc of turn.toolCalls) {
481
- items.push({
482
- type: "toolCall",
483
- name: tc.toolName,
484
- args: (tc.args ?? {}) as Record<string, unknown>,
485
- status: tc._status,
486
- });
487
- }
488
- }
489
- return items;
490
- }
491
-
492
- /**
493
- * 从 turns[] 末尾推导当前活动行(running 时)。
494
- *
495
- * 优先级:最后一个未闭合 turn 的末尾 running toolCall → thinking → text → undefined
496
- *
497
- * 仅 status==="running" 时返回;terminal 态返回 undefined。
498
- *
499
- * 注意:返回的 type 联合("tool"|"text"|"thinking")是手写的,未通过类型守卫从
500
- * AgentEvent 派生——它映射的是累积的 turn 状态(InternalToolCall._status + turn.thinking/text),
501
- * 而非单个事件。若未来新增 turn 内容模式(如 reasoning_summary),须同步扩展本函数,
502
- * 否则会静默返回 undefined(活动行运行中途消失)。updateFromEvent 的 switch 有 never 穷尽
503
- * 检查,但本函数没有,依赖人工同步。
504
- */
505
- export function getCurrentActivity(
506
- record: ExecutionRecord,
507
- ): { type: "tool" | "text" | "thinking"; label: string } | undefined {
508
- if (record.status !== "running") return undefined;
509
- const turn = record.turns[record.turns.length - 1];
510
- if (turn === undefined || turn.closed) return undefined;
511
-
512
- // 1. 倒序找最后一个 running 的 toolCall
513
- for (let i = turn.toolCalls.length - 1; i >= 0; i--) {
514
- const tc = turn.toolCalls[i];
515
- if (tc?._status === "running") {
516
- return { type: "tool", label: extractLabelFromArgs(tc.toolName, tc.args) };
517
- }
518
- }
519
- // 2. 正在 thinking
520
- if (turn.thinking) {
521
- return { type: "thinking", label: turn.thinking.slice(0, ACTIVITY_LABEL_MAX) };
522
- }
523
- // 3. 正在输出 text
524
- if (turn.text) {
525
- return { type: "text", label: turn.text.slice(0, ACTIVITY_LABEL_MAX) };
526
- }
527
- return undefined;
528
- }
529
-
530
- /**
531
- * 聚合所有 turn 的 text 为完整文本(替代旧 collectResponseText)。
532
- *
533
- * 单一数据源:不再读 session.messages,text 完全来自 record.turns[] 的流式累积。
534
- * 多 turn 用空行分隔(每个 turn 是一段独立的 assistant 输出)。
535
- *
536
- * 语义对齐旧 collectResponseText:后者只取最后一条 assistant message 的 text。
537
- * turns[] 收口后,每条 assistant message 对应一个 turn,故 join 所有非空 turn 文本
538
- * 与「拼接所有 assistant message」语义一致。单 turn 场景两者完全等价。
539
- */
540
- export function getFullText(record: ExecutionRecord): string {
541
- return record.turns
542
- .map((t) => t.text)
543
- .filter((text) => text.length > 0)
544
- .join("\n\n");
545
- }
546
-
547
- /**
548
- * [增量通知] 从 fromTurnIndex 起聚合 turn 文本(getFullText 的增量视图)。
549
- *
550
- * 语义:record.turns.slice(Math.max(0, fromTurnIndex)) 的 text 数组
551
- * filter(text => text.length > 0)(判据与 getFullText 逐字对齐——空白串 " " 按非空
552
- * 处理,不发散)后 join("\n\n")。
553
- *
554
- * 不变式(单测锁死):
555
- * - fromTurnIndex = 0 与 getFullText(record) 逐字节等价(one-shot/首轮回退零成本)
556
- * - fromTurnIndex >= turns.length 返回 ""(不抛错、不回绕)
557
- * - fromTurnIndex < 0 规范化为 0(防御,Math.max 不产生 slice 负偏移语义)
558
- *
559
- * 纯函数只读;getFullText 本体与既有调用方(collectResult 等)零改动。
560
- * 唯一调用点 onRoundSettled 的增量派生(record.roundBaseTurnIndex ?? 0)。
561
- */
562
- export function getFullTextFrom(record: ExecutionRecord, fromTurnIndex: number): string {
563
- return record.turns
564
- .slice(Math.max(0, fromTurnIndex))
565
- .map((t) => t.text)
566
- .filter((text) => text.length > 0)
567
- .join("\n\n");
568
- }
569
-
570
- /**
571
- * [增量通知] 轮次边界推进公式(D1):返回下一轮增量的 turns[] 起始下标。
572
- *
573
- * record.turns.length - (末 turn 存在且 !closed 且 text.length === 0 ? 1 : 0)
574
- *
575
- * - 末 turn 已闭合(pi 现序常态:带 usage 的 message_end 恒先于 turn_end,settle 时
576
- * 全闭合)→ 返回 length,下一轮从新 turn 起。
577
- * - 末 turn 是滞后 message_end 开出的空 turn(防御分支,pi 现序不可达)→ 不计入边界
578
- * (-1),留在下一轮增量内:新轮首个 text_delta 经 currentTurn 复用该空 turn,复用
579
- * 累积被 slice(from) 覆盖;若直用 turns.length 会把这段文本挤出 slice 范围静默丢失(D1)。
580
- * - 末 turn 未闭合且 text 非空(防御形态,pi 现序不可达)→ 计入本轮返回 length(该形态
581
- * 即 onRoundSettled 推进前观测哨 logger.warn 的触发条件)。
582
- *
583
- * 纯函数只读不写 record;唯一调用点 onRoundSettled 第 5 步(notify 之后推进)。
584
- */
585
- export function nextRoundBaseTurnIndex(record: ExecutionRecord): number {
586
- const last = record.turns[record.turns.length - 1];
587
- const trailingOpenEmpty =
588
- last !== undefined && !last.closed && last.text.length === 0 ? 1 : 0;
589
- return record.turns.length - trailingOpenEmpty;
590
- }
591
-
592
- /**
593
- * 聚合所有 turn 的 toolCalls(扁平化),并 strip InternalToolCall 的内部字段。
594
- * 供 collectResult / schema enforcement 读,替代旧闭包 toolCalls 旁路。
595
- *
596
- * 返回 ToolCall[](不含 _status / startedTs)——跨边界导出形状清洁,
597
- * 避免内部状态机字段泄漏到 AgentResult.toolCalls / 持久化层。
598
- */
599
- export function getAllToolCalls(record: ExecutionRecord): ToolCall[] {
600
- return record.turns.flatMap((t) => t.toolCalls.map(stripInternal));
601
- }
602
-
603
- /**
604
- * 聚合所有 turn 的 toolCalls 总数(免克隆计数)。
605
- *
606
- * getAllToolCalls 会 flatMap + strip 克隆出完整数组——只需计数的调用方(渲染签名
607
- * 每 200ms tick 调用一次)用它是纯浪费;本函数 reduce 累加各 turn 的 length,零分配。
608
- * 与 getAllToolCalls(...).length 恒等(同一 turns 源)。
609
- */
610
- export function countAllToolCalls(record: ExecutionRecord): number {
611
- return record.turns.reduce((sum, t) => sum + t.toolCalls.length, 0);
612
- }
613
-
614
- /** 把 InternalToolCall 映射回纯净的 ToolCall(丢弃 _status / startedTs)。 */
615
- function stripInternal(tc: InternalToolCall): ToolCall {
616
- return {
617
- toolName: tc.toolName,
618
- args: tc.args,
619
- result: tc.result,
620
- isError: tc.isError,
621
- };
622
- }
623
-
624
- /**
625
- * 聚合所有 turn 的 usageDelta 为完整 usage(含 total + cost)。
626
- * 全零则返回 undefined(与旧 toUsageTotal 语义一致)。
627
- *
628
- * cost 来自 SdkEvent.message.usage.cost.total(message_end 时透传到 usageDelta)。
629
- * 旧 toUsageTotal/session-runner 累积 cost;本重构保留该行为。
630
- */
631
- export function getTotalUsage(record: ExecutionRecord): AgentUsageTotal | undefined {
632
- let input = 0, output = 0, cacheRead = 0, cacheWrite = 0, cost = 0;
633
- for (const turn of record.turns) {
634
- const u = turn.usageDelta;
635
- if (u) {
636
- input += u.input ?? 0;
637
- output += u.output ?? 0;
638
- cacheRead += u.cacheRead ?? 0;
639
- cacheWrite += u.cacheWrite ?? 0;
640
- cost += u.cost ?? 0;
641
- }
642
- }
643
- const total = input + output + cacheRead + cacheWrite;
644
- if (total === 0) return undefined;
645
- return { input, output, cacheRead, cacheWrite, total, cost };
646
- }
647
-
648
- // ============================================================
649
- // 完成(唯一入口)
650
- // ============================================================
651
-
652
- /**
653
- * status 状态机的 CAS 互斥锁。仅当 `record.status === "running"` 时改为 target
654
- * 并返回 true,否则返回 false。**status 状态机本身就是互斥锁**——终态
655
- * (closed/cancelled)不可逆,check-then-set 在 JS 单线程事件循环里天然原子。
656
- *
657
- * 用途:executor 的收尾竞争。cancelBackground 与 background detached 完成回调
658
- * 都调 tryTransition 抢锁:抢到负责完整收尾,没抢到闭嘴不做事。
659
- *
660
- * target 仅限正常执行流的两终态(closed + cancelled)。
661
- * closed 携带 closedReason(L2 原因子枚举),由重建路径或正常执行流写入。
662
- * 重建路径也可用 markReconstructedStatus 直接赋值(跳过 CAS)。
663
- *
664
- * @param closedReason closed 终态的 L2 关闭原因。仅 target="closed" 时有意义;
665
- * target="cancelled" 时忽略。缺省 "gc"(通用完成/失败)。
666
- */
667
- export function tryTransition(
668
- record: ExecutionRecord,
669
- target: "closed",
670
- closedReason?: ClosedReason,
671
- ): boolean {
672
- if (record.status !== "running") return false;
673
- record.status = target;
674
- record.closedReason = closedReason ?? "gc";
675
- return true;
676
- }
677
-
678
- /**
679
- * 重建专用收口:跳过 CAS 直接赋值 status。
680
- *
681
- * 仅用于 session-reconstructor 从 session.jsonl 重建终态 record 时——
682
- * 重建的 record 没有 running 状态需要保护,直接赋值即可。
683
- * 禁止在正常执行流程中使用此函数(应使用 tryTransition)。
684
- */
685
- export function markReconstructedStatus(
686
- record: { status: ExecutionStatus },
687
- status: ExecutionStatus,
688
- ): void {
689
- record.status = status;
690
- }
691
-
692
- /**
693
- * 唯一完成入口。冻结状态(写 endedAt/agentResult/result/error)。
694
- * 不修改 turns/totalTokens——已由 updateFromEvent 累积,completeRecord 只读不重置。
695
- *
696
- * ⚠ 前置条件:调用方必须先通过 tryTransition 抢到锁(status 已被 CAS 设为 target)。
697
- *
698
- * @param closedReason closed 终态的 L2 关闭原因。仅 status="closed" 时写入;
699
- * "cancelled" 时忽略。
700
- */
701
- export function completeRecord(
702
- record: ExecutionRecord,
703
- result: AgentResult,
704
- status: "closed",
705
- closedReason?: ClosedReason,
706
- ): void {
707
- record.status = status;
708
- record.closedReason = closedReason ?? "gc";
709
- record.endedAt = Date.now();
710
- record.agentResult = result;
711
- record.result = result.text;
712
- record.error = result.error;
713
- }
714
-
715
- // ============================================================
716
- // 投影(唯一 → Details / Snapshot / Persisted)
717
- // ============================================================
718
-
719
- /** elapsedSeconds 唯一计算点(共享 helper,消除三处发散)。endedAt 缺失用 Date.now()。 */
720
- export function computeElapsedSeconds(record: { startedAt: number; endedAt?: number }): number {
721
- const end = record.endedAt ?? Date.now();
722
- return Math.floor((end - record.startedAt) / MS_PER_SECOND);
723
- }
724
-
725
- /**
726
- * 投影到 SubagentToolDetails。elapsedSeconds/currentActivity/eventLog 均现算派生。
727
- */
728
- export function project(record: ExecutionRecord): SubagentToolDetails {
729
- return {
730
- status: record.status,
731
- mode: record.mode,
732
- agent: record.agent,
733
- model: record.model,
734
- thinkingLevel: record.thinkingLevel,
735
- slug: record.slug,
736
- turns: record.turnCount,
737
- totalTokens: record.totalTokens,
738
- elapsedSeconds: computeElapsedSeconds(record),
739
- eventLog: getEventLog(record),
740
- displayItems: getDisplayItems(record),
741
- result: record.result,
742
- error: record.error,
743
- currentActivity: getCurrentActivity(record),
744
- parsedOutput: record.agentResult?.parsedOutput,
745
- sessionFile: record.sessionFile,
746
- patchFile: record.patchFile,
747
- };
748
- }
749
-
750
- /**
751
- * 投影到 live 进度快照。elapsedSeconds/currentActivity/eventLog 均现算派生。
752
- * 供 WorkflowsView 在 agent 运行期间读取实时进度。
753
- */
754
- export function projectLiveProgress(record: ExecutionRecord): {
755
- status: ExecutionRecord["status"];
756
- turns: number;
757
- totalTokens: number;
758
- elapsedSeconds: number;
759
- eventLog: AgentEventLogEntry[];
760
- currentActivity: ReturnType<typeof getCurrentActivity>;
761
- lastError: string | undefined;
762
- } {
763
- return {
764
- status: record.status,
765
- turns: record.turnCount,
766
- totalTokens: record.totalTokens,
767
- elapsedSeconds: computeElapsedSeconds(record),
768
- eventLog: getEventLog(record),
769
- currentActivity: getCurrentActivity(record),
770
- lastError: record.lastError,
771
- };
772
- }
773
-
774
- /**
775
- * 投影到只读快照(TUI list / poll 消费)。
776
- * 浅拷贝 turns[],字段标 readonly 阻止 TUI 回写。
777
- */
778
- export function snapshot(record: ExecutionRecord): RecordSnapshot {
779
- return {
780
- id: record.id,
781
- agent: record.agent,
782
- model: record.model,
783
- thinkingLevel: record.thinkingLevel,
784
- mode: record.mode,
785
- task: record.task,
786
- slug: record.slug,
787
- status: record.status,
788
- chatMode: record.chatMode,
789
- turns: record.turnCount,
790
- totalTokens: record.totalTokens,
791
- startedAt: record.startedAt,
792
- endedAt: record.endedAt,
793
- result: record.result,
794
- error: record.error,
795
- sessionFile: record.sessionFile,
796
- };
797
- }
798
-
799
- // ============================================================
800
- // JSONL → AgentEvent 翻译(从 live/jsonl-to-agent-event 迁入)
801
- // ============================================================
802
-
803
- /** subprocess JSONL 事件(JSON.parse 结果)。duck-typed,对应 SDK SdkEvent。 */
804
- type JsonlEvent = Record<string, unknown>;
805
-
806
- /**
807
- * 把一条 JSONL 事件翻译成 AgentEvent。
808
- *
809
- * 返回 undefined 表示该事件不映射到任何 AgentEvent(如 session header、message_start、
810
- * tool_execution_update),调用方应跳过。
811
- *
812
- * 一个 JSONL 事件可能产出**多条** AgentEvent(message_end 的 usage + error 各一条),
813
- * 故返回数组。绝大多数情况长度为 0 或 1;message_end 最多 2 条。
814
- */
815
- export function jsonlToAgentEvent(raw: JsonlEvent): AgentEvent[] {
816
- const type = raw.type;
817
-
818
- switch (type) {
819
- case "session":
820
- case "message_start":
821
- case "turn_start":
822
- case "tool_execution_update":
823
- return [];
824
-
825
- case "tool_execution_start": {
826
- const toolName = typeof raw.toolName === "string" ? raw.toolName : "";
827
- return [{ type: "tool_start", toolName, args: raw.args }];
828
- }
829
-
830
- case "tool_execution_end": {
831
- const toolName = typeof raw.toolName === "string" ? raw.toolName : "";
832
- const isError = raw.isError === true;
833
- return [{
834
- type: "tool_end",
835
- toolName,
836
- args: raw.args,
837
- result: raw.result as ToolCallResult | undefined,
838
- isError,
839
- }];
840
- }
841
-
842
- case "message_update": {
843
- const ame = raw.assistantMessageEvent as Record<string, unknown> | undefined;
844
- if (ame?.type === "thinking_delta") {
845
- const delta = typeof ame.delta === "string" ? ame.delta : "";
846
- return [{ type: "thinking_delta", delta }];
847
- }
848
- if (ame !== undefined && ame.delta !== undefined) {
849
- const delta = typeof ame.delta === "string" ? ame.delta : String(ame.delta);
850
- return [{ type: "text_delta", delta }];
851
- }
852
- return [];
853
- }
854
-
855
- case "turn_end": {
856
- return [{ type: "turn_end" }];
857
- }
858
-
859
- case "message_end": {
860
- return accumulateMessageEndForRecord(raw);
861
- }
862
-
863
- case "compaction_start": {
864
- return [{ type: "compaction" }];
865
- }
866
-
867
- default:
868
- return [];
869
- }
870
- }
871
-
872
- /** message_end 翻译:usage 拍平 + stopReason=error/aborted 额外产 error 事件。 */
873
- function accumulateMessageEndForRecord(raw: JsonlEvent): AgentEvent[] {
874
- const events: AgentEvent[] = [];
875
- const msg = raw.message as Record<string, unknown> | undefined;
876
- const usageRaw = (typeof msg?.usage === "object" && msg.usage !== null)
877
- ? msg.usage as Record<string, unknown>
878
- : undefined;
879
-
880
- if (usageRaw) {
881
- const costObj = (typeof usageRaw.cost === "object" && usageRaw.cost !== null)
882
- ? usageRaw.cost as Record<string, unknown>
883
- : undefined;
884
- // MF-3 fix: 显式提取字段 + Number.isFinite 守卫,不使用 spread + as 断言
885
- const numOrZero = (v: unknown): number =>
886
- typeof v === "number" && Number.isFinite(v) ? v : 0;
887
- const usage: AgentUsage = {
888
- input: numOrZero(usageRaw.input),
889
- output: numOrZero(usageRaw.output),
890
- cacheRead: numOrZero(usageRaw.cacheRead),
891
- cacheWrite: numOrZero(usageRaw.cacheWrite),
892
- cost: typeof costObj?.total === "number" ? costObj.total : undefined,
893
- };
894
- events.push({ type: "message_end", usage });
895
- }
896
-
897
- const stopReason = msg?.stopReason;
898
- if (stopReason === "error" || stopReason === "aborted") {
899
- const errorMessage = typeof msg?.errorMessage === "string"
900
- ? msg.errorMessage
901
- : (typeof raw.reason === "string" ? raw.reason : String(stopReason));
902
- events.push({ type: "error", message: errorMessage });
903
- }
904
-
905
- return events;
906
- }