@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,469 +0,0 @@
1
- // src/execution/engine/engines/pi/__tests__/pi-engine.test.ts
2
- //
3
- // PiEngine 适配层测试(P1 回填):capabilities 链路接通口径(D3)、run 委托与
4
- // 中立声明→ExecuteOptions 映射、interact 三 action 直通、read 三级降级、probe 形状
5
- // (C1:ok=false 时 error.recovery 非空)。fake PiEngineService 注入,不 spawn 真进程。
6
-
7
- import * as fs from "node:fs";
8
- import * as os from "node:os";
9
- import * as path from "node:path";
10
-
11
- import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
12
-
13
- import { createRecord } from "../../../../execution-record.ts";
14
- import type { ExecuteOptions, SubagentRecord } from "../../../../types.ts";
15
- import { IDENTITY_CUSTOM_TYPE } from "../../../../session-reconstructor.ts";
16
- import { SubagentStream } from "../../../../stream-sink.ts";
17
- import type { RunContext } from "../../../port.ts";
18
- import type { AgentTaskSpec, EngineHandle } from "../../../types.ts";
19
- import {
20
- PI_ADAPTER_VERSION,
21
- PI_ENGINE_ID,
22
- PiEngine,
23
- type PiEngineService,
24
- } from "../pi-engine.ts";
25
-
26
- // ── fake 编排服务(结构子集,记录调用) ──
27
-
28
- interface ServiceCalls {
29
- executeOpts?: ExecuteOptions;
30
- signal?: AbortSignal;
31
- onEvent?: (event: unknown) => void;
32
- stream?: unknown;
33
- deliverMessage?: { recordId: string; text: string; interrupt: boolean };
34
- close?: { recordId: string; force: boolean };
35
- cancelId?: string;
36
- getRecordId?: string;
37
- }
38
-
39
- /** 构造最小完整 SubagentRecord(collectRecords 返回项——resolveRecordId 只读 id/sessionFile)。 */
40
- function makeListedRecord(id: string, sessionFile: string | undefined): SubagentRecord {
41
- return {
42
- id,
43
- agent: "reviewer",
44
- task: "t",
45
- slug: "s",
46
- status: "running",
47
- mode: "background",
48
- startedAt: 1,
49
- rootSessionId: undefined,
50
- parentRecordId: undefined,
51
- depth: 0,
52
- endedAt: undefined,
53
- turns: 0,
54
- totalTokens: 0,
55
- model: "p/m",
56
- thinkingLevel: undefined,
57
- eventLog: [],
58
- displayItems: [],
59
- ...(sessionFile !== undefined ? { sessionFile } : {}),
60
- };
61
- }
62
-
63
- function makeFakeService(overrides?: {
64
- executeResult?: Awaited<ReturnType<PiEngineService["executeAndAwait"]>>;
65
- executeReject?: Error;
66
- recordsById?: Map<string, ReturnType<typeof createRecord>>;
67
- listed?: Array<{ id: string; sessionFile?: string }>;
68
- cancelResult?: boolean;
69
- deliverReject?: Error;
70
- }): { service: PiEngineService; calls: ServiceCalls } {
71
- const calls: ServiceCalls = {};
72
- const record = createRecord("sa-fake", {
73
- agent: "reviewer",
74
- model: "p/m",
75
- thinkingLevel: "high",
76
- mode: "background",
77
- task: "t",
78
- slug: "s",
79
- startedAt: 1,
80
- controller: new AbortController(),
81
- });
82
- const service: PiEngineService = {
83
- executeAndAwait: async (opts, signal, onEvent, stream) => {
84
- calls.executeOpts = opts;
85
- calls.signal = signal;
86
- calls.onEvent = onEvent;
87
- calls.stream = stream;
88
- if (overrides?.executeReject) throw overrides.executeReject;
89
- return (
90
- overrides?.executeResult ?? {
91
- content: "done",
92
- durationMs: 42,
93
- toolCalls: [],
94
- }
95
- );
96
- },
97
- getRecordForAction: (id) => {
98
- calls.getRecordId = id;
99
- // recordsById 提供时严格查表(模拟「record 不存在 → throw」路径);缺省返回默认 record
100
- if (overrides?.recordsById) {
101
- const found = overrides.recordsById.get(id);
102
- if (!found) {
103
- throw new Error(`subagent not found or not owned: ${id}`);
104
- }
105
- return found;
106
- }
107
- return record;
108
- },
109
- deliverMessage: async (rec, text, interrupt) => {
110
- calls.deliverMessage = { recordId: rec.id, text, interrupt };
111
- if (overrides?.deliverReject) throw overrides.deliverReject;
112
- },
113
- closeSubagent: async (rec, force) => {
114
- calls.close = { recordId: rec.id, force };
115
- },
116
- cancel: (id) => {
117
- calls.cancelId = id;
118
- return overrides?.cancelResult ?? true;
119
- },
120
- collectRecords: () =>
121
- (overrides?.listed ?? []).map(({ id, sessionFile }) => makeListedRecord(id, sessionFile)),
122
- };
123
- return { service, calls };
124
- }
125
-
126
- /** 全字段任务声明(覆盖全部泛化点)。 */
127
- function makeSpec(): AgentTaskSpec {
128
- return {
129
- task: "do the thing",
130
- slug: "do-thing",
131
- agent: "reviewer",
132
- model: "zai-coding-cn/glm-5.2",
133
- effort: "high",
134
- persona: { skillPath: "/skills/r/SKILL.md", appendSystemPrompt: ["extra"] },
135
- schema: { type: "object", properties: { ok: { type: "boolean" } } },
136
- maxTurns: 5,
137
- graceTurns: 2,
138
- fork: true,
139
- worktree: false,
140
- cwd: "/work",
141
- conversation: true,
142
- idleTimeoutMs: 99,
143
- };
144
- }
145
-
146
- function makeHandle(sessionRef: Record<string, string>): EngineHandle {
147
- return { data: { v: 1, engineId: PI_ENGINE_ID, sessionRef, poolKey: "shared", adapterVersion: PI_ADAPTER_VERSION } };
148
- }
149
-
150
- function makeRunCtx(overrides?: Partial<RunContext>): RunContext {
151
- return { taskId: "sa-test", poolKey: "shared", ...overrides };
152
- }
153
-
154
- // ── capabilities ──
155
-
156
- describe("PiEngine.capabilities(D3 链路接通口径)", () => {
157
- it("id = 'pi';声明本仓 subagent 链路实际接通的能力(非 RPC 理论能力)", () => {
158
- const engine = new PiEngine({ getService: () => null });
159
- expect(engine.id).toBe(PI_ENGINE_ID);
160
- expect(engine.capabilities()).toEqual({
161
- schemaEnforcement: "native", // PI_WORKFLOW_SCHEMA env 注入链路(方案 A 唯一权威)
162
- steer: "unsupported", // RPC 有 steer 但 spawn 链路未接通(session-runner no-op)
163
- conversation: "native", // chatMode idle 复用 + message/close/cancel 已接通
164
- personaInjection: "flag", // --skill / --append-system-prompt
165
- eventGranularity: "stream", // 30+ 事件流
166
- sandbox: "emulated", // 无 OS sandbox;worktree 隔离
167
- sessionRead: "full", // pi JSONL 完整重建
168
- resume: "native", // 同进程 idle 复用 + --session 冷续写
169
- interrupt: "kill-only", // 现链路 abort = SIGTERM,rpc abort 未接通
170
- permissionMode: "native", // argv-mirror 镜像主进程 flag
171
- });
172
- });
173
- });
174
-
175
- // ── run ──
176
-
177
- describe("PiEngine.run", () => {
178
- it("中立声明 → ExecuteOptions 映射 + signal/onEvent/stream/ctxModel 直通(行为零变化)", async () => {
179
- const { service, calls } = makeFakeService();
180
- const engine = new PiEngine({ getService: () => service });
181
- const spec = makeSpec();
182
- const ctxModel = { id: "m", name: "M", provider: "p", reasoning: false };
183
- const onEvent = (e: unknown) => void e;
184
- const stream = new SubagentStream("sa-test", { setWidget: () => {} });
185
- const signal = new AbortController().signal;
186
-
187
- const { handle, outcome } = await engine.run(spec, makeRunCtx({ signal, onEvent, ctxModel, stream }));
188
-
189
- // 传参恒等(SAR 接线前的直接调用形态)
190
- expect(calls.signal).toBe(signal);
191
- expect(calls.onEvent).toBe(onEvent);
192
- expect(calls.stream).toBe(stream);
193
- expect(calls.executeOpts).toEqual({
194
- task: "do the thing",
195
- slug: "do-thing",
196
- agent: "reviewer",
197
- model: "zai-coding-cn/glm-5.2",
198
- thinkingLevel: "high", // effort 恒等映射回
199
- skillPath: "/skills/r/SKILL.md", // persona 还原
200
- appendSystemPrompt: ["extra"],
201
- schema: spec.schema,
202
- schemaEnv: JSON.stringify(spec.schema), // schema 派生(D-A6 bridge)
203
- maxTurns: 5,
204
- graceTurns: 2,
205
- ctxModel,
206
- fork: true,
207
- worktree: false,
208
- cwd: "/work",
209
- conversation: true,
210
- idleTimeoutMs: 99,
211
- engine: "pi", // P4 引擎留痕(D9①):实际执行引擎 id 进 record 投影链
212
- });
213
-
214
- // outcome 字段全集映射(缺字段在此转红——SAR outcomeToRunnerResult 的完整性依赖)
215
- expect(outcome).toEqual({
216
- content: "done",
217
- parsedOutput: undefined,
218
- usage: undefined,
219
- durationMs: 42,
220
- error: undefined,
221
- sessionId: undefined,
222
- sessionFile: undefined,
223
- worktreePath: undefined,
224
- toolCalls: [],
225
- engineId: "pi",
226
- });
227
-
228
- // handle 自描述(engineId + sessionRef + poolKey + adapter 版本)
229
- expect(handle.data).toMatchObject({
230
- v: 1,
231
- engineId: "pi",
232
- poolKey: "shared",
233
- adapterVersion: PI_ADAPTER_VERSION,
234
- });
235
- });
236
-
237
- it("sessionFile 回填进 handle.sessionRef;ctx.poolKey 透传(空串兜底 'shared')", async () => {
238
- const { service } = makeFakeService({
239
- executeResult: { content: "x", sessionFile: "/tmp/s.jsonl", sessionId: "sess-1", toolCalls: [] },
240
- });
241
- const engine = new PiEngine({ getService: () => service });
242
- const r1 = await engine.run({ task: "t", slug: "s" }, makeRunCtx({ poolKey: "agent-reviewer" }));
243
- expect(r1.handle.data.sessionRef).toEqual({ sessionFile: "/tmp/s.jsonl" });
244
- expect(r1.handle.data.poolKey).toBe("agent-reviewer");
245
- expect(r1.outcome.sessionId).toBe("sess-1");
246
- const r2 = await engine.run({ task: "t", slug: "s" }, makeRunCtx({ poolKey: "" }));
247
- expect(r2.handle.data.poolKey).toBe("shared");
248
- });
249
-
250
- it("服务不可用(prepare 期)→ reject 且不产生 handle", async () => {
251
- const engine = new PiEngine({ getService: () => null });
252
- await expect(engine.run({ task: "t", slug: "s" }, makeRunCtx())).rejects.toThrow(/SubagentService unavailable/);
253
- });
254
-
255
- it("executeAndAwait throw(嵌套超限等创建期异常)→ 向上传播(SAR catch 兜底不变)", async () => {
256
- const { service } = makeFakeService({ executeReject: new Error("nesting depth exceeded") });
257
- const engine = new PiEngine({ getService: () => service });
258
- await expect(engine.run({ task: "t", slug: "s" }, makeRunCtx())).rejects.toThrow("nesting depth exceeded");
259
- });
260
- });
261
-
262
- // ── interact ──
263
-
264
- describe("PiEngine.interact(chatMode 交互面直通)", () => {
265
- it("message → deliverMessage(record, text, false)(interrupt 中立 action 未携带,P1 恒 followUp)", async () => {
266
- const { service, calls } = makeFakeService();
267
- const engine = new PiEngine({ getService: () => service });
268
- const res = await engine.interact(makeHandle({ recordId: "sa-fake" }), { kind: "message", payload: "next round" });
269
- expect(res).toEqual({ ok: true, delivered: true });
270
- expect(calls.deliverMessage).toEqual({ recordId: "sa-fake", text: "next round", interrupt: false });
271
- });
272
-
273
- it("close → closeSubagent(record, force);payload 缺省 force=false", async () => {
274
- const { service, calls } = makeFakeService();
275
- const engine = new PiEngine({ getService: () => service });
276
- await engine.interact(makeHandle({ recordId: "sa-fake" }), { kind: "close", payload: { force: true } });
277
- expect(calls.close).toEqual({ recordId: "sa-fake", force: true });
278
- await engine.interact(makeHandle({ recordId: "sa-fake" }), { kind: "close" });
279
- expect(calls.close).toEqual({ recordId: "sa-fake", force: false });
280
- });
281
-
282
- it("cancel → service.cancel(recordId)", async () => {
283
- const { service, calls } = makeFakeService();
284
- const engine = new PiEngine({ getService: () => service });
285
- const res = await engine.interact(makeHandle({ recordId: "sa-fake" }), { kind: "cancel" });
286
- expect(res).toEqual({ ok: true, delivered: true });
287
- expect(calls.cancelId).toBe("sa-fake");
288
- });
289
-
290
- it("cancel 返回 false → engine_interact_failed", async () => {
291
- const { service } = makeFakeService({ cancelResult: false });
292
- const engine = new PiEngine({ getService: () => service });
293
- const res = await engine.interact(makeHandle({ recordId: "sa-fake" }), { kind: "cancel" });
294
- expect(res.ok).toBe(false);
295
- if (!res.ok) {
296
- expect(res.code).toBe("engine_interact_failed");
297
- expect(res.message).toContain("sa-fake");
298
- }
299
- });
300
-
301
- it("recordId 缺席时按 sessionFile 扫描兜底定位(collectRecords 匹配)", async () => {
302
- const { service, calls } = makeFakeService({ listed: [{ id: "sa-scan-hit", sessionFile: "/x/s.jsonl" }] });
303
- const engine = new PiEngine({ getService: () => service });
304
- await engine.interact(makeHandle({ sessionFile: "/x/s.jsonl" }), { kind: "message", payload: "hi" });
305
- expect(calls.getRecordId).toBe("sa-scan-hit");
306
- });
307
-
308
- it("死 handle(无定位符 / record 不存在)→ engine_session_not_resumable(含 cold resume 指引)", async () => {
309
- const { service } = makeFakeService();
310
- const engine = new PiEngine({ getService: () => service });
311
- const res = await engine.interact(makeHandle({}), { kind: "message", payload: "hi" });
312
- expect(res.ok).toBe(false);
313
- if (!res.ok) {
314
- expect(res.code).toBe("engine_session_not_resumable");
315
- expect(res.message).toContain("--session");
316
- }
317
- });
318
-
319
- it("交互面 throw → engine_interact_failed(message 文案透传行动语言)", async () => {
320
- const { service } = makeFakeService({
321
- deliverReject: new Error("subagent sa-fake is not ready for a new message"),
322
- });
323
- const engine = new PiEngine({ getService: () => service });
324
- const res = await engine.interact(makeHandle({ recordId: "sa-fake" }), { kind: "message", payload: "hi" });
325
- expect(res).toEqual({
326
- ok: false,
327
- code: "engine_interact_failed",
328
- message: "subagent sa-fake is not ready for a new message",
329
- });
330
- });
331
-
332
- it("服务不可用 → reject", async () => {
333
- const engine = new PiEngine({ getService: () => null });
334
- await expect(
335
- engine.interact(makeHandle({ recordId: "x" }), { kind: "message", payload: "hi" }),
336
- ).rejects.toThrow(/SubagentService unavailable/);
337
- });
338
- });
339
-
340
- // ── read ──
341
-
342
- describe("PiEngine.read(D6 降级链)", () => {
343
- let tmpDir: string;
344
-
345
- beforeEach(() => {
346
- tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "pi-engine-read-"));
347
- });
348
- afterEach(() => {
349
- fs.rmSync(tmpDir, { recursive: true, force: true });
350
- });
351
-
352
- it("无 sessionRef → outcome-only(turns 空)", async () => {
353
- const engine = new PiEngine({ getService: () => null });
354
- const view = await engine.read(makeHandle({}));
355
- expect(view).toEqual({ engineId: "pi", turns: [], source: "outcome-only" });
356
- });
357
-
358
- it("sessionFile 不存在/损坏 → outcome-only(不 throw)", async () => {
359
- const engine = new PiEngine({ getService: () => null });
360
- const view = await engine.read(makeHandle({ sessionFile: path.join(tmpDir, "missing.jsonl") }));
361
- expect(view.source).toBe("outcome-only");
362
- });
363
-
364
- it("pi JSONL 原生读取(第①级):turns 重建 + usage 聚合 + 内部态剥离", async () => {
365
- const file = path.join(tmpDir, "s.jsonl");
366
- const lines: string[] = [
367
- JSON.stringify({ type: "session", version: 3, id: "sess-uuid", timestamp: "2026-01-01T00:00:00.000Z", cwd: tmpDir }),
368
- JSON.stringify({
369
- type: "custom", id: "id-1", parentId: null, timestamp: "2026-01-01T00:00:00.000Z",
370
- customType: IDENTITY_CUSTOM_TYPE,
371
- data: { id: "bg-1", agent: "worker", mode: "background", task: "t", startedAt: 500 },
372
- }),
373
- JSON.stringify({
374
- type: "message", id: "msg-1", parentId: "id-1", timestamp: new Date(1000).toISOString(),
375
- message: {
376
- role: "assistant",
377
- content: [
378
- { type: "text", text: "hello" },
379
- { type: "toolCall", id: "tc-1", name: "bash", arguments: { cmd: "ls" } },
380
- ],
381
- usage: { input: 10, output: 20, cacheRead: 1, cacheWrite: 2, totalTokens: 33, cost: { total: 0.5 } },
382
- stopReason: "stop",
383
- timestamp: 1000,
384
- },
385
- }),
386
- JSON.stringify({
387
- type: "message", id: "tr-1", parentId: "msg-1", timestamp: new Date(2000).toISOString(),
388
- message: {
389
- role: "toolResult", toolCallId: "tc-1", toolName: "bash",
390
- content: [{ type: "text", text: "ok" }], timestamp: 2000,
391
- },
392
- }),
393
- ];
394
- fs.writeFileSync(file, lines.join("\n") + "\n");
395
-
396
- const engine = new PiEngine({ getService: () => null });
397
- const view = await engine.read(makeHandle({ sessionFile: file }));
398
- expect(view.source).toBe("native");
399
- expect(view.engineId).toBe("pi");
400
- expect(view.sessionId).toBe("bg-1");
401
- expect(view.turns).toHaveLength(1);
402
- expect(view.turns[0].text).toBe("hello");
403
- expect(view.turns[0].closed).toBe(true);
404
- // toolCall 纯净形状(无 _status/startedTs 内部态)
405
- expect(view.turns[0].toolCalls[0]).toMatchObject({ toolName: "bash" });
406
- expect("_status" in view.turns[0].toolCalls[0]).toBe(false);
407
- // usage 聚合(turn usageDelta → AgentUsageTotal)
408
- expect(view.usage).toEqual({ input: 10, output: 20, cacheRead: 1, cacheWrite: 2, cost: 0.5, total: 33 });
409
- });
410
- });
411
-
412
- // ── probe ──
413
-
414
- describe("PiEngine.probe(D7 + C1 形状)", () => {
415
- const originalArgv = process.argv;
416
- const originalExecPath = process.execPath;
417
- const originalPath = process.env.PATH;
418
-
419
- afterEach(() => {
420
- Object.defineProperty(process, "argv", { value: originalArgv, configurable: true });
421
- Object.defineProperty(process, "execPath", { value: originalExecPath, configurable: true });
422
- process.env.PATH = originalPath;
423
- });
424
-
425
- it("探针成功:invocation + version 两级 check,engineVersion 实测", async () => {
426
- const probeVersion = vi.fn(async () => "pi 0.84.1");
427
- const engine = new PiEngine({ getService: () => null, probeVersion });
428
- const report = await engine.probe();
429
- expect(report.ok).toBe(true);
430
- expect(report.engineVersion).toBe("pi 0.84.1");
431
- expect(report.checks.map((c) => c.name)).toEqual(["invocation", "version"]);
432
- expect(report.error).toBeUndefined();
433
- expect(probeVersion).toHaveBeenCalledTimes(1);
434
- });
435
-
436
- it("版本探测失败 → ok=false 且 error.recovery 非空(C1:恢复指引必填)", async () => {
437
- const engine = new PiEngine({ getService: () => null, probeVersion: async () => undefined });
438
- const report = await engine.probe();
439
- expect(report.ok).toBe(false);
440
- expect(report.error?.code).toBe("engine_probe_failed");
441
- expect(report.error?.recovery.length).toBeGreaterThan(0);
442
- });
443
-
444
- it("结果缓存:二次 probe 不重跑;force 强探重跑", async () => {
445
- const probeVersion = vi.fn(async () => "pi 1.0");
446
- const engine = new PiEngine({ getService: () => null, probeVersion });
447
- const first = await engine.probe();
448
- const second = await engine.probe();
449
- expect(second).toBe(first); // 缓存命中(同对象)
450
- expect(probeVersion).toHaveBeenCalledTimes(1);
451
- await engine.probe({ force: true });
452
- expect(probeVersion).toHaveBeenCalledTimes(2);
453
- });
454
-
455
- it("invocation 不可解析(PATH 无 pi)→ 第 1 check 失败,不再尝试版本探测", async () => {
456
- // bun 虚拟脚本 + 通用 runtime → 退化为 PATH 依赖形态(command === "pi")
457
- Object.defineProperty(process, "argv", { value: ["bun", "/$bunfs/root/pi"], configurable: true });
458
- Object.defineProperty(process, "execPath", { value: "/usr/bin/bun", configurable: true });
459
- process.env.PATH = path.join(os.tmpdir(), `no-pi-here-${Date.now()}`);
460
- const probeVersion = vi.fn(async () => "should-not-run");
461
- const engine = new PiEngine({ getService: () => null, probeVersion });
462
- const report = await engine.probe();
463
- expect(report.ok).toBe(false);
464
- expect(report.checks).toHaveLength(1);
465
- expect(report.checks[0].name).toBe("invocation");
466
- expect(report.checks[0].ok).toBe(false);
467
- expect(probeVersion).not.toHaveBeenCalled();
468
- });
469
- });
@@ -1,155 +0,0 @@
1
- // reader.test.ts —— PiEngine reader(P5):readPiSessionView 对 session-reconstructor
2
- // 的投影包装。断言三件事:①合法 JSONL → SessionView(turns/toolCalls strip 内部态/
3
- // usage 聚合/source='native');②reconstructor 降级条件(文件缺失/缺 identity/无
4
- // assistant)→ undefined 不 throw;③不泄漏 InternalToolCall 内部态(_status/startedTs)。
5
-
6
- import * as fs from "node:fs";
7
- import * as os from "node:os";
8
- import * as path from "node:path";
9
-
10
- import { beforeAll, describe, expect, it } from "vitest";
11
-
12
- import { readPiSessionView } from "../reader.ts";
13
-
14
- let tmpDir: string;
15
-
16
- beforeAll(() => {
17
- tmpDir = fs.mkdtempSync(join(os.tmpdir(), "pi-reader-"));
18
- });
19
-
20
- // node:path 的 join 在 ESM 测试里直接用(避免顶部 import 与 beforeAll 顺序问题)
21
- function join(...segs: string[]): string {
22
- return path.join(...segs);
23
- }
24
-
25
- /** 最小合法 subagent JSONL:header + identity + 一轮 assistant(text/thinking/toolCall)+ toolResult。 */
26
- function writeValidSession(file: string): void {
27
- const lines = [
28
- JSON.stringify({ type: "session", id: "pi-sess-1", cwd: "/proj", timestamp: "2026-08-25T10:00:00Z" }),
29
- JSON.stringify({
30
- type: "custom",
31
- customType: "subagent-identity",
32
- data: { id: "bg-1-abc", agent: "reviewer", mode: "background", task: "review code", slug: "rev", startedAt: 1756000000000 },
33
- }),
34
- JSON.stringify({
35
- type: "message",
36
- timestamp: "2026-08-25T10:00:01Z",
37
- message: {
38
- role: "assistant",
39
- timestamp: 1756000001000,
40
- content: [
41
- { type: "thinking", thinking: "let me look" },
42
- { type: "toolCall", id: "tc-1", name: "bash", arguments: { command: "ls" } },
43
- ],
44
- usage: { input: 10, output: 5, cacheRead: 1, cacheWrite: 2, cost: { total: 0.1 } },
45
- stopReason: "toolUse",
46
- },
47
- }),
48
- JSON.stringify({
49
- type: "message",
50
- timestamp: "2026-08-25T10:00:02Z",
51
- message: { role: "toolResult", toolCallId: "tc-1", toolName: "bash", content: [{ type: "text", text: "file-a" }], timestamp: 1756000002000 },
52
- }),
53
- JSON.stringify({
54
- type: "message",
55
- timestamp: "2026-08-25T10:00:03Z",
56
- message: {
57
- role: "assistant",
58
- timestamp: 1756000003000,
59
- content: [{ type: "text", text: "all good" }],
60
- usage: { input: 20, output: 8, cacheRead: 0, cacheWrite: 0, cost: { total: 0.05 } },
61
- stopReason: "stop",
62
- },
63
- }),
64
- ];
65
- fs.writeFileSync(file, `${lines.join("\n")}\n`, "utf8");
66
- }
67
-
68
- describe("readPiSessionView", () => {
69
- it("projects a valid subagent JSONL to a native SessionView", async () => {
70
- const file = join(tmpDir, "valid.jsonl");
71
- writeValidSession(file);
72
-
73
- const view = await readPiSessionView(file);
74
-
75
- expect(view).toBeDefined();
76
- expect(view?.engineId).toBe("pi");
77
- expect(view?.source).toBe("native");
78
- // 两轮 assistant message → 两个 turn(toolResult 不产 turn)
79
- expect(view?.turns).toHaveLength(2);
80
-
81
- const [turn1, turn2] = view?.turns ?? [];
82
- expect(turn1?.text).toBe("");
83
- expect(turn1?.thinking).toBe("let me look");
84
- expect(turn1?.toolCalls).toHaveLength(1);
85
- const tc = turn1?.toolCalls[0];
86
- expect(tc?.toolName).toBe("bash");
87
- expect(tc?.args).toEqual({ command: "ls" });
88
- expect(tc?.result).toEqual({ content: [{ type: "text", text: "file-a" }] });
89
- // InternalToolCall 内部态不泄漏(导出纯净形状)
90
- expect(tc).not.toHaveProperty("_status");
91
- expect(tc).not.toHaveProperty("startedTs");
92
- expect(turn1?.closed).toBe(true);
93
-
94
- expect(turn2?.text).toBe("all good");
95
- expect(turn2?.closed).toBe(true);
96
-
97
- // usage 聚合:两轮 usageDelta 之和(cost 浮点累加有精度噪声,用 toBeCloseTo)
98
- expect(view?.usage?.input).toBe(30)
99
- expect(view?.usage?.output).toBe(13)
100
- expect(view?.usage?.cacheRead).toBe(1)
101
- expect(view?.usage?.cacheWrite).toBe(2)
102
- expect(view?.usage?.cost).toBeCloseTo(0.15, 10)
103
- expect(view?.usage?.total).toBe(46);
104
- });
105
-
106
- it("returns undefined (no throw) when file is missing", async () => {
107
- await expect(readPiSessionView(join(tmpDir, "nope.jsonl"))).resolves.toBeUndefined();
108
- });
109
-
110
- it("returns undefined when identity entry is missing", async () => {
111
- const file = join(tmpDir, "no-identity.jsonl");
112
- fs.writeFileSync(
113
- file,
114
- `${JSON.stringify({ type: "session", id: "s" })}\n${JSON.stringify({
115
- type: "message",
116
- message: { role: "assistant", content: [{ type: "text", text: "hi" }], timestamp: 1 },
117
- })}\n`,
118
- "utf8",
119
- );
120
- await expect(readPiSessionView(file)).resolves.toBeUndefined();
121
- });
122
-
123
- it("returns undefined when no assistant message exists", async () => {
124
- const file = join(tmpDir, "no-assistant.jsonl");
125
- fs.writeFileSync(
126
- file,
127
- `${JSON.stringify({ type: "session", id: "s" })}\n${JSON.stringify({
128
- type: "custom",
129
- customType: "subagent-identity",
130
- data: { id: "bg-2", agent: "w", mode: "background", task: "t", startedAt: 1 },
131
- })}\n`,
132
- "utf8",
133
- );
134
- await expect(readPiSessionView(file)).resolves.toBeUndefined();
135
- });
136
-
137
- it("omits usage when no message carries it", async () => {
138
- const file = join(tmpDir, "no-usage.jsonl");
139
- fs.writeFileSync(
140
- file,
141
- `${JSON.stringify({ type: "session", id: "s" })}\n${JSON.stringify({
142
- type: "custom",
143
- customType: "subagent-identity",
144
- data: { id: "bg-3", agent: "w", mode: "background", task: "t", startedAt: 1 },
145
- })}\n${JSON.stringify({
146
- type: "message",
147
- message: { role: "assistant", content: [{ type: "text", text: "hi" }], stopReason: "stop", timestamp: 1 },
148
- })}\n`,
149
- "utf8",
150
- );
151
- const view = await readPiSessionView(file);
152
- expect(view?.turns).toHaveLength(1);
153
- expect(view?.usage).toBeUndefined();
154
- });
155
- });