@selesai/code 0.12.0 → 0.13.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (466) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/dist/core/agent-session.d.ts +1 -1
  3. package/dist/core/agent-session.js +1 -1
  4. package/dist/core/sdk.d.ts +2 -2
  5. package/dist/core/sdk.js +1 -1
  6. package/dist/core/system-prompt.d.ts +1 -1
  7. package/dist/core/system-prompt.js +1 -1
  8. package/dist/core/usage-totals.d.ts +2 -0
  9. package/dist/core/usage-totals.js +10 -0
  10. package/dist/extensions/ascii.txt +9 -0
  11. package/dist/extensions/package.json +1 -2
  12. package/dist/extensions/pi-hermes-memory/CHANGELOG.md +371 -0
  13. package/dist/extensions/pi-hermes-memory/LICENSE +21 -0
  14. package/dist/extensions/pi-hermes-memory/README.md +643 -0
  15. package/dist/extensions/pi-hermes-memory/docs/0.1/TASKS.md +197 -0
  16. package/dist/extensions/pi-hermes-memory/docs/0.2/PLAN.md +290 -0
  17. package/dist/extensions/pi-hermes-memory/docs/0.2/TASKS.md +134 -0
  18. package/dist/extensions/pi-hermes-memory/docs/0.2/TEST-PLAN.md +216 -0
  19. package/dist/extensions/pi-hermes-memory/docs/0.3/PLAN.md +330 -0
  20. package/dist/extensions/pi-hermes-memory/docs/0.3/TASKS.md +125 -0
  21. package/dist/extensions/pi-hermes-memory/docs/0.4/PLAN.md +160 -0
  22. package/dist/extensions/pi-hermes-memory/docs/0.4/TASKS.md +113 -0
  23. package/dist/extensions/pi-hermes-memory/docs/0.6/CHANGELOG.md +57 -0
  24. package/dist/extensions/pi-hermes-memory/docs/0.6/PLAN.md +202 -0
  25. package/dist/extensions/pi-hermes-memory/docs/0.6/TASKS.md +144 -0
  26. package/dist/extensions/pi-hermes-memory/docs/0.7/PLAN.md +349 -0
  27. package/dist/extensions/pi-hermes-memory/docs/0.7/TASKS.md +110 -0
  28. package/dist/extensions/pi-hermes-memory/docs/PUBLISHING.md +149 -0
  29. package/dist/extensions/pi-hermes-memory/docs/ROADMAP.md +468 -0
  30. package/dist/extensions/pi-hermes-memory/docs/images/memory-architecture.svg +1 -0
  31. package/dist/extensions/pi-hermes-memory/docs/images/pi-logo.svg +22 -0
  32. package/dist/extensions/pi-hermes-memory/docs/images/pi_memory.png +0 -0
  33. package/dist/extensions/pi-hermes-memory/docs/images/pi_memory.svg +19 -0
  34. package/dist/extensions/pi-hermes-memory/docs/images/security-flow.svg +1 -0
  35. package/dist/extensions/pi-hermes-memory/docs/images/session-lifecycle.svg +1 -0
  36. package/dist/extensions/pi-hermes-memory/docs/images/source-architecture.svg +1 -0
  37. package/dist/extensions/pi-hermes-memory/docs/mermaid/memory-architecture.mmd +49 -0
  38. package/dist/extensions/pi-hermes-memory/docs/mermaid/security-flow.mmd +40 -0
  39. package/dist/extensions/pi-hermes-memory/docs/mermaid/session-lifecycle.mmd +56 -0
  40. package/dist/extensions/pi-hermes-memory/docs/mermaid/source-architecture.mmd +54 -0
  41. package/dist/extensions/pi-hermes-memory/package-lock.json +3799 -0
  42. package/dist/extensions/pi-hermes-memory/package.json +66 -0
  43. package/dist/extensions/pi-hermes-memory/scripts/check-min-sdk.mjs +99 -0
  44. package/dist/extensions/pi-hermes-memory/scripts/ensure-dev.mjs +57 -0
  45. package/dist/extensions/pi-hermes-memory/src/auto-consolidation-warning.ts +6 -0
  46. package/dist/extensions/pi-hermes-memory/src/config.ts +178 -0
  47. package/dist/extensions/pi-hermes-memory/src/constants.ts +424 -0
  48. package/dist/extensions/pi-hermes-memory/src/extension-root-migration.ts +639 -0
  49. package/dist/extensions/pi-hermes-memory/src/handlers/auto-consolidate.ts +401 -0
  50. package/dist/extensions/pi-hermes-memory/src/handlers/background-review.ts +297 -0
  51. package/dist/extensions/pi-hermes-memory/src/handlers/child-process-watchdog.mjs +90 -0
  52. package/dist/extensions/pi-hermes-memory/src/handlers/correction-detector.ts +300 -0
  53. package/dist/extensions/pi-hermes-memory/src/handlers/index-sessions.ts +86 -0
  54. package/dist/extensions/pi-hermes-memory/src/handlers/insights.ts +78 -0
  55. package/dist/extensions/pi-hermes-memory/src/handlers/interview.ts +37 -0
  56. package/dist/extensions/pi-hermes-memory/src/handlers/learn-memory.ts +186 -0
  57. package/dist/extensions/pi-hermes-memory/src/handlers/message-parts.ts +27 -0
  58. package/dist/extensions/pi-hermes-memory/src/handlers/pi-child-process.ts +485 -0
  59. package/dist/extensions/pi-hermes-memory/src/handlers/preview-context.ts +105 -0
  60. package/dist/extensions/pi-hermes-memory/src/handlers/review-memory-ops.ts +506 -0
  61. package/dist/extensions/pi-hermes-memory/src/handlers/session-backfill.ts +145 -0
  62. package/dist/extensions/pi-hermes-memory/src/handlers/session-flush.ts +151 -0
  63. package/dist/extensions/pi-hermes-memory/src/handlers/session-live-index.ts +94 -0
  64. package/dist/extensions/pi-hermes-memory/src/handlers/skills-command.ts +1334 -0
  65. package/dist/extensions/pi-hermes-memory/src/handlers/standing-pin.ts +111 -0
  66. package/dist/extensions/pi-hermes-memory/src/handlers/switch-project.ts +75 -0
  67. package/dist/extensions/pi-hermes-memory/src/handlers/sync-markdown-memories.ts +266 -0
  68. package/dist/extensions/pi-hermes-memory/src/index.ts +378 -0
  69. package/dist/extensions/pi-hermes-memory/src/lifecycle-timing.ts +54 -0
  70. package/dist/extensions/pi-hermes-memory/src/paths.ts +61 -0
  71. package/dist/extensions/pi-hermes-memory/src/project-context.ts +13 -0
  72. package/dist/extensions/pi-hermes-memory/src/project-memory-migration.ts +93 -0
  73. package/dist/extensions/pi-hermes-memory/src/project.ts +157 -0
  74. package/dist/extensions/pi-hermes-memory/src/prompt-context.ts +55 -0
  75. package/dist/extensions/pi-hermes-memory/src/store/atomic-lock-coordinator.ts +375 -0
  76. package/dist/extensions/pi-hermes-memory/src/store/canonical-storage-path.ts +54 -0
  77. package/dist/extensions/pi-hermes-memory/src/store/content-scanner.ts +101 -0
  78. package/dist/extensions/pi-hermes-memory/src/store/db.ts +1180 -0
  79. package/dist/extensions/pi-hermes-memory/src/store/fts-query.ts +101 -0
  80. package/dist/extensions/pi-hermes-memory/src/store/markdown-mutation-lock.ts +38 -0
  81. package/dist/extensions/pi-hermes-memory/src/store/memory-lookup.ts +24 -0
  82. package/dist/extensions/pi-hermes-memory/src/store/memory-store.ts +1191 -0
  83. package/dist/extensions/pi-hermes-memory/src/store/recovery-maintenance.ts +66 -0
  84. package/dist/extensions/pi-hermes-memory/src/store/schema.ts +118 -0
  85. package/dist/extensions/pi-hermes-memory/src/store/session-anchor-search.ts +472 -0
  86. package/dist/extensions/pi-hermes-memory/src/store/session-indexer.ts +511 -0
  87. package/dist/extensions/pi-hermes-memory/src/store/session-parser.ts +213 -0
  88. package/dist/extensions/pi-hermes-memory/src/store/session-search.ts +234 -0
  89. package/dist/extensions/pi-hermes-memory/src/store/skill-store.ts +950 -0
  90. package/dist/extensions/pi-hermes-memory/src/store/skill-utils.ts +133 -0
  91. package/dist/extensions/pi-hermes-memory/src/store/sqlite-memory-store.ts +1002 -0
  92. package/dist/extensions/pi-hermes-memory/src/store/sqlite-native.ts +241 -0
  93. package/dist/extensions/pi-hermes-memory/src/store/standing-instructions.ts +247 -0
  94. package/dist/extensions/pi-hermes-memory/src/tools/memory-search-tool.ts +95 -0
  95. package/dist/extensions/pi-hermes-memory/src/tools/memory-tool.ts +469 -0
  96. package/dist/extensions/pi-hermes-memory/src/tools/session-search-tool.ts +246 -0
  97. package/dist/extensions/pi-hermes-memory/src/tools/shared-output-view.ts +170 -0
  98. package/dist/extensions/pi-hermes-memory/src/tools/skill-tool.ts +333 -0
  99. package/dist/extensions/pi-hermes-memory/src/tools/tool-result-views.ts +105 -0
  100. package/dist/extensions/pi-hermes-memory/src/types.ts +214 -0
  101. package/dist/extensions/pi-hermes-memory/tests/config.test.ts +434 -0
  102. package/dist/extensions/pi-hermes-memory/tests/extension-root-migration.test.ts +368 -0
  103. package/dist/extensions/pi-hermes-memory/tests/handlers/auto-consolidate.test.ts +984 -0
  104. package/dist/extensions/pi-hermes-memory/tests/handlers/background-review.test.ts +950 -0
  105. package/dist/extensions/pi-hermes-memory/tests/handlers/correction-detector.test.ts +671 -0
  106. package/dist/extensions/pi-hermes-memory/tests/handlers/insights.test.ts +157 -0
  107. package/dist/extensions/pi-hermes-memory/tests/handlers/interview.test.ts +127 -0
  108. package/dist/extensions/pi-hermes-memory/tests/handlers/pi-child-process.test.ts +905 -0
  109. package/dist/extensions/pi-hermes-memory/tests/handlers/preview-context.test.ts +152 -0
  110. package/dist/extensions/pi-hermes-memory/tests/handlers/prompt-context.test.ts +166 -0
  111. package/dist/extensions/pi-hermes-memory/tests/handlers/resources-discover.test.ts +71 -0
  112. package/dist/extensions/pi-hermes-memory/tests/handlers/review-memory-ops.test.ts +614 -0
  113. package/dist/extensions/pi-hermes-memory/tests/handlers/session-backfill.test.ts +192 -0
  114. package/dist/extensions/pi-hermes-memory/tests/handlers/session-flush.test.ts +677 -0
  115. package/dist/extensions/pi-hermes-memory/tests/handlers/session-live-index.test.ts +225 -0
  116. package/dist/extensions/pi-hermes-memory/tests/handlers/skills-command.test.ts +827 -0
  117. package/dist/extensions/pi-hermes-memory/tests/handlers/standing-pin.test.ts +134 -0
  118. package/dist/extensions/pi-hermes-memory/tests/handlers/sync-markdown-memories.test.ts +491 -0
  119. package/dist/extensions/pi-hermes-memory/tests/handlers/system-prompt.test.ts +199 -0
  120. package/dist/extensions/pi-hermes-memory/tests/handlers/thinking-max.test.ts +64 -0
  121. package/dist/extensions/pi-hermes-memory/tests/index.test.ts +30 -0
  122. package/dist/extensions/pi-hermes-memory/tests/integration/flow.test.ts +126 -0
  123. package/dist/extensions/pi-hermes-memory/tests/lifecycle-timing.test.ts +76 -0
  124. package/dist/extensions/pi-hermes-memory/tests/paths.test.ts +23 -0
  125. package/dist/extensions/pi-hermes-memory/tests/project-memory-migration.test.ts +80 -0
  126. package/dist/extensions/pi-hermes-memory/tests/project-rebinding.test.ts +115 -0
  127. package/dist/extensions/pi-hermes-memory/tests/project.test.ts +120 -0
  128. package/dist/extensions/pi-hermes-memory/tests/run-all-timeout.test.ts +107 -0
  129. package/dist/extensions/pi-hermes-memory/tests/run-all.sh +45 -0
  130. package/dist/extensions/pi-hermes-memory/tests/store/atomic-lock-coordinator.test.ts +381 -0
  131. package/dist/extensions/pi-hermes-memory/tests/store/content-scanner.test.ts +388 -0
  132. package/dist/extensions/pi-hermes-memory/tests/store/db.test.ts +1046 -0
  133. package/dist/extensions/pi-hermes-memory/tests/store/markdown-mutation-lock.test.ts +34 -0
  134. package/dist/extensions/pi-hermes-memory/tests/store/memory-store.test.ts +2150 -0
  135. package/dist/extensions/pi-hermes-memory/tests/store/recovery-maintenance.test.ts +170 -0
  136. package/dist/extensions/pi-hermes-memory/tests/store/session-anchor-search.test.ts +210 -0
  137. package/dist/extensions/pi-hermes-memory/tests/store/session-indexer.test.ts +643 -0
  138. package/dist/extensions/pi-hermes-memory/tests/store/session-parser.test.ts +282 -0
  139. package/dist/extensions/pi-hermes-memory/tests/store/session-search.test.ts +287 -0
  140. package/dist/extensions/pi-hermes-memory/tests/store/skill-store.test.ts +678 -0
  141. package/dist/extensions/pi-hermes-memory/tests/store/skill-utils.test.ts +21 -0
  142. package/dist/extensions/pi-hermes-memory/tests/store/sqlite-lazy-load.test.ts +78 -0
  143. package/dist/extensions/pi-hermes-memory/tests/store/sqlite-memory-store.test.ts +543 -0
  144. package/dist/extensions/pi-hermes-memory/tests/store/sqlite-native.test.ts +164 -0
  145. package/dist/extensions/pi-hermes-memory/tests/store/standing-instructions.test.ts +171 -0
  146. package/dist/extensions/pi-hermes-memory/tests/tools/memory-search-tool.test.ts +84 -0
  147. package/dist/extensions/pi-hermes-memory/tests/tools/memory-tool.test.ts +709 -0
  148. package/dist/extensions/pi-hermes-memory/tests/tools/session-search-tool.test.ts +345 -0
  149. package/dist/extensions/pi-hermes-memory/tests/tools/shared-output-view.test.ts +362 -0
  150. package/dist/extensions/pi-hermes-memory/tests/tools/skill-tool.test.ts +478 -0
  151. package/dist/extensions/pi-hermes-memory/tests/tools/tool-result-renderer-wiring.test.ts +40 -0
  152. package/dist/extensions/pi-hermes-memory/tsconfig.json +19 -0
  153. package/dist/extensions/pi-subagents/.oxlintrc.json +43 -0
  154. package/dist/extensions/pi-subagents/AGENTS.md +5 -0
  155. package/dist/extensions/pi-subagents/CHANGELOG.md +157 -0
  156. package/dist/extensions/pi-subagents/README.md +12 -6
  157. package/dist/extensions/pi-subagents/VISION.md +81 -0
  158. package/dist/extensions/pi-subagents/agents/claude-code-writer.md +15 -0
  159. package/dist/extensions/pi-subagents/agents/claude-code.md +15 -0
  160. package/dist/extensions/pi-subagents/agents/codex-exec-writer.md +15 -0
  161. package/dist/extensions/pi-subagents/agents/codex-exec.md +15 -0
  162. package/dist/extensions/pi-subagents/agents/cursor-agent-writer.md +14 -0
  163. package/dist/extensions/pi-subagents/agents/cursor-agent.md +14 -0
  164. package/dist/extensions/pi-subagents/agents/reviewer.md +1 -3
  165. package/dist/extensions/pi-subagents/docs/agents.md +488 -0
  166. package/dist/extensions/pi-subagents/docs/configuration.md +46 -9
  167. package/dist/extensions/pi-subagents/docs/extension-api.md +42 -3
  168. package/dist/extensions/pi-subagents/docs/models.md +27 -4
  169. package/dist/extensions/pi-subagents/docs/observability.md +10 -9
  170. package/dist/extensions/pi-subagents/docs/tool-reference.md +102 -6
  171. package/dist/extensions/pi-subagents/docs/workflows.md +135 -0
  172. package/dist/extensions/pi-subagents/package-lock.json +389 -2
  173. package/dist/extensions/pi-subagents/package.json +3 -1
  174. package/dist/extensions/pi-subagents/prompts/review-loop.md +2 -2
  175. package/dist/extensions/pi-subagents/skills/council-mode/SKILL.md +1 -1
  176. package/dist/extensions/pi-subagents/skills/pi-subagents/SKILL.md +3 -1
  177. package/dist/extensions/pi-subagents/skills/pi-subagents/references/execution-controls.md +7 -3
  178. package/dist/extensions/pi-subagents/skills/pi-subagents/references/management-authoring-rpc.md +2 -2
  179. package/dist/extensions/pi-subagents/skills/pi-subagents/references/prompting-and-roles.md +26 -5
  180. package/dist/extensions/pi-subagents/src/agents/agent-management.ts +41 -22
  181. package/dist/extensions/pi-subagents/src/agents/agent-refinements.ts +4 -4
  182. package/dist/extensions/pi-subagents/src/agents/agent-serializer.ts +9 -2
  183. package/dist/extensions/pi-subagents/src/agents/agents.ts +322 -75
  184. package/dist/extensions/pi-subagents/src/agents/builtin-names.ts +6 -0
  185. package/dist/extensions/pi-subagents/src/agents/runtime-agent-events.ts +70 -0
  186. package/dist/extensions/pi-subagents/src/agents/runtime-agent-registry.ts +27 -20
  187. package/dist/extensions/pi-subagents/src/api/agents.ts +10 -5
  188. package/dist/extensions/pi-subagents/src/api/background-work.ts +5 -1
  189. package/dist/extensions/pi-subagents/src/api/delegation.ts +0 -7
  190. package/dist/extensions/pi-subagents/src/api/preflight.ts +34 -12
  191. package/dist/extensions/pi-subagents/src/extension/config.ts +70 -0
  192. package/dist/extensions/pi-subagents/src/extension/doctor.ts +3 -3
  193. package/dist/extensions/pi-subagents/src/extension/fanout-child.ts +5 -3
  194. package/dist/extensions/pi-subagents/src/extension/index.ts +83 -30
  195. package/dist/extensions/pi-subagents/src/extension/public-execution.ts +44 -15
  196. package/dist/extensions/pi-subagents/src/extension/rpc.ts +55 -19
  197. package/dist/extensions/pi-subagents/src/extension/schemas.ts +41 -15
  198. package/dist/extensions/pi-subagents/src/extension/tool-description.ts +28 -31
  199. package/dist/extensions/pi-subagents/src/inspectors/herdr/actions.ts +2 -1
  200. package/dist/extensions/pi-subagents/src/inspectors/herdr/inspector-runner.ts +2 -10
  201. package/dist/extensions/pi-subagents/src/inspectors/herdr/session-roots-codec.ts +42 -0
  202. package/dist/extensions/pi-subagents/src/integrations/herdr-status.ts +51 -3
  203. package/dist/extensions/pi-subagents/src/intercom/result-intercom.ts +2 -0
  204. package/dist/extensions/pi-subagents/src/profiles/profiles.ts +5 -6
  205. package/dist/extensions/pi-subagents/src/runs/background/active-async-capacity.ts +77 -10
  206. package/dist/extensions/pi-subagents/src/runs/background/async-execution.ts +176 -53
  207. package/dist/extensions/pi-subagents/src/runs/background/async-job-tracker.ts +19 -12
  208. package/dist/extensions/pi-subagents/src/runs/background/async-resume.ts +29 -27
  209. package/dist/extensions/pi-subagents/src/runs/background/async-retention.ts +20 -3
  210. package/dist/extensions/pi-subagents/src/runs/background/async-status-snapshot.ts +23 -261
  211. package/dist/extensions/pi-subagents/src/runs/background/async-status.ts +73 -7
  212. package/dist/extensions/pi-subagents/src/runs/background/chain-append.ts +8 -3
  213. package/dist/extensions/pi-subagents/src/runs/background/chain-root-attachment.ts +74 -8
  214. package/dist/extensions/pi-subagents/src/runs/background/fleet-view.ts +37 -21
  215. package/dist/extensions/pi-subagents/src/runs/background/inspect-rpc.ts +8 -8
  216. package/dist/extensions/pi-subagents/src/runs/background/notify.ts +184 -9
  217. package/dist/extensions/pi-subagents/src/runs/background/result-delivery-ownership.ts +45 -0
  218. package/dist/extensions/pi-subagents/src/runs/background/result-files.ts +28 -14
  219. package/dist/extensions/pi-subagents/src/runs/background/result-watcher.ts +38 -15
  220. package/dist/extensions/pi-subagents/src/runs/background/resume-guidance.ts +1 -1
  221. package/dist/extensions/pi-subagents/src/runs/background/retained-children.ts +1 -1
  222. package/dist/extensions/pi-subagents/src/runs/background/run-status.ts +50 -13
  223. package/dist/extensions/pi-subagents/src/runs/background/scheduled-runs.ts +93 -8
  224. package/dist/extensions/pi-subagents/src/runs/background/stale-run-reconciler.ts +10 -4
  225. package/dist/extensions/pi-subagents/src/runs/background/steering.ts +4 -14
  226. package/dist/extensions/pi-subagents/src/runs/background/subagent-runner.ts +549 -377
  227. package/dist/extensions/pi-subagents/src/runs/background/subagent-wait.ts +58 -10
  228. package/dist/extensions/pi-subagents/src/runs/background/terminal-run-index.ts +1 -1
  229. package/dist/extensions/pi-subagents/src/runs/background/wait-completions.ts +26 -1
  230. package/dist/extensions/pi-subagents/src/runs/background/wait-config.ts +23 -9
  231. package/dist/extensions/pi-subagents/src/runs/background/wait-tool.ts +9 -2
  232. package/dist/extensions/pi-subagents/src/runs/foreground/async-steering-action.ts +2 -2
  233. package/dist/extensions/pi-subagents/src/runs/foreground/execution.ts +204 -129
  234. package/dist/extensions/pi-subagents/src/runs/foreground/foreground-control.ts +9 -0
  235. package/dist/extensions/pi-subagents/src/runs/foreground/foreground-history.ts +23 -1
  236. package/dist/extensions/pi-subagents/src/runs/foreground/subagent-executor.ts +773 -311
  237. package/dist/extensions/pi-subagents/src/runs/foreground/workflow-detach-reconcile.ts +140 -115
  238. package/dist/extensions/pi-subagents/src/runs/shared/abort-recovery.ts +119 -0
  239. package/dist/extensions/pi-subagents/src/runs/shared/async-status-projection.ts +463 -0
  240. package/dist/extensions/pi-subagents/src/runs/shared/child-identity.ts +19 -4
  241. package/dist/extensions/pi-subagents/src/runs/shared/child-launch-plan.ts +151 -0
  242. package/dist/extensions/pi-subagents/src/runs/shared/child-protocol.ts +21 -7
  243. package/dist/extensions/pi-subagents/src/runs/shared/claude-code-adapter.ts +129 -0
  244. package/dist/extensions/pi-subagents/src/runs/shared/codex-exec-adapter.ts +129 -0
  245. package/dist/extensions/pi-subagents/src/runs/shared/completion-evidence.ts +89 -0
  246. package/dist/extensions/pi-subagents/src/runs/shared/completion-guard.ts +5 -4
  247. package/dist/extensions/pi-subagents/src/runs/shared/cursor-agent-adapter.ts +114 -0
  248. package/dist/extensions/pi-subagents/src/runs/shared/dynamic-fanout.ts +3 -3
  249. package/dist/extensions/pi-subagents/src/runs/shared/external-cli-contract.ts +167 -0
  250. package/dist/extensions/pi-subagents/src/runs/shared/external-cli-preflight.ts +122 -0
  251. package/dist/extensions/pi-subagents/src/runs/shared/external-cli-runner.ts +348 -55
  252. package/dist/extensions/pi-subagents/src/runs/shared/fast-mode-extension.ts +5 -5
  253. package/dist/extensions/pi-subagents/src/runs/shared/host-step-status.ts +230 -0
  254. package/dist/extensions/pi-subagents/src/runs/shared/lane-metadata.ts +105 -0
  255. package/dist/extensions/pi-subagents/src/runs/shared/launch-cwd.ts +16 -0
  256. package/dist/extensions/pi-subagents/src/runs/shared/long-running-guard.ts +2 -1
  257. package/dist/extensions/pi-subagents/src/runs/shared/mcp-config-sources.ts +422 -0
  258. package/dist/extensions/pi-subagents/src/runs/shared/mcp-direct-tool-allowlist.ts +221 -158
  259. package/dist/extensions/pi-subagents/src/runs/shared/mcp-direct-tool-grant.ts +197 -0
  260. package/dist/extensions/pi-subagents/src/runs/shared/model-exclusions.ts +69 -7
  261. package/dist/extensions/pi-subagents/src/runs/shared/model-fallback.ts +47 -6
  262. package/dist/extensions/pi-subagents/src/runs/shared/mutation-evidence.ts +7 -2
  263. package/dist/extensions/pi-subagents/src/runs/shared/nested-events.ts +9 -3
  264. package/dist/extensions/pi-subagents/src/runs/shared/nested-render.ts +9 -5
  265. package/dist/extensions/pi-subagents/src/runs/shared/parallel-handoff.ts +419 -7
  266. package/dist/extensions/pi-subagents/src/runs/shared/parallel-utils.ts +15 -1
  267. package/dist/extensions/pi-subagents/src/runs/shared/pi-args.ts +81 -8
  268. package/dist/extensions/pi-subagents/src/runs/shared/process-signal.ts +13 -0
  269. package/dist/extensions/pi-subagents/src/runs/shared/run-history.ts +21 -1
  270. package/dist/extensions/pi-subagents/src/runs/shared/single-output.ts +44 -4
  271. package/dist/extensions/pi-subagents/src/runs/shared/subagent-prompt-runtime.ts +98 -13
  272. package/dist/extensions/pi-subagents/src/runs/shared/subagent-startup-retry.ts +1 -1
  273. package/dist/extensions/pi-subagents/src/runs/shared/worktree-cleanup-plan.ts +847 -0
  274. package/dist/extensions/pi-subagents/src/runs/shared/worktree.ts +18 -0
  275. package/dist/extensions/pi-subagents/src/shared/child-session-name.ts +46 -0
  276. package/dist/extensions/pi-subagents/src/shared/extension-context.ts +24 -0
  277. package/dist/extensions/pi-subagents/src/shared/fork-context.ts +21 -0
  278. package/dist/extensions/pi-subagents/src/shared/formatters.ts +18 -3
  279. package/dist/extensions/pi-subagents/src/shared/launch-contract.ts +5 -1
  280. package/dist/extensions/pi-subagents/src/shared/pruned-fork.ts +450 -0
  281. package/dist/extensions/pi-subagents/src/shared/session-file-trust.ts +19 -0
  282. package/dist/extensions/pi-subagents/src/shared/session-tokens.ts +14 -3
  283. package/dist/extensions/pi-subagents/src/shared/settings.ts +19 -105
  284. package/dist/extensions/pi-subagents/src/shared/shortcuts.ts +17 -0
  285. package/dist/extensions/pi-subagents/src/shared/types.ts +359 -40
  286. package/dist/extensions/pi-subagents/src/shared/utils.ts +41 -84
  287. package/dist/extensions/pi-subagents/src/shared/workflow-child-permit.ts +116 -0
  288. package/dist/extensions/pi-subagents/src/slash/delegation-adapters.ts +1 -9
  289. package/dist/extensions/pi-subagents/src/slash/delegation-request.ts +0 -4
  290. package/dist/extensions/pi-subagents/src/slash/slash-bridge.ts +1 -2
  291. package/dist/extensions/pi-subagents/src/slash/slash-commands.ts +378 -88
  292. package/dist/extensions/pi-subagents/src/slash/slash-live-state.ts +22 -11
  293. package/dist/extensions/pi-subagents/src/slash/subagents-admin.ts +3 -0
  294. package/dist/extensions/pi-subagents/src/tui/fleet-status.ts +147 -79
  295. package/dist/extensions/pi-subagents/src/tui/fleet-transcript.ts +11 -5
  296. package/dist/extensions/pi-subagents/src/tui/fleet.ts +39 -18
  297. package/dist/extensions/pi-subagents/src/tui/render.ts +369 -46
  298. package/dist/extensions/pi-subagents/src/watchdog/turn-delta.ts +1 -1
  299. package/dist/extensions/pi-subagents/src/workflows/chat-progress.ts +6 -3
  300. package/dist/extensions/pi-subagents/src/workflows/host-command.ts +230 -0
  301. package/dist/extensions/pi-subagents/src/workflows/scripted-workflow.ts +740 -58
  302. package/dist/extensions/pi-subagents/src/workflows/workflow-child-summary.ts +121 -0
  303. package/dist/extensions/pi-subagents/src/workflows/workflow-preflight.ts +270 -0
  304. package/dist/extensions/pi-subagents/src/workflows/workflow-receipt.ts +195 -6
  305. package/dist/extensions/pi-subagents/src/workflows/workflow-settlement.ts +246 -0
  306. package/dist/extensions/pi-subagents/test/e2e/real-session-subagent.test.ts +2 -2
  307. package/dist/extensions/pi-subagents/test/integration/async-execution.test.ts +1114 -260
  308. package/dist/extensions/pi-subagents/test/integration/async-job-tracker.test.ts +34 -0
  309. package/dist/extensions/pi-subagents/test/integration/async-status.test.ts +150 -3
  310. package/dist/extensions/pi-subagents/test/integration/claude-code-smoke.test.ts +52 -0
  311. package/dist/extensions/pi-subagents/test/integration/claude-code-writer-smoke.test.ts +55 -0
  312. package/dist/extensions/pi-subagents/test/integration/codex-exec-smoke.test.ts +53 -0
  313. package/dist/extensions/pi-subagents/test/integration/codex-exec-writer-smoke.test.ts +57 -0
  314. package/dist/extensions/pi-subagents/test/integration/cursor-agent-smoke.test.ts +59 -0
  315. package/dist/extensions/pi-subagents/test/integration/cursor-agent-writer-smoke.test.ts +62 -0
  316. package/dist/extensions/pi-subagents/test/integration/error-handling.test.ts +57 -1
  317. package/dist/extensions/pi-subagents/test/integration/external-cli-runner.test.ts +1 -0
  318. package/dist/extensions/pi-subagents/test/integration/fork-context-execution.test.ts +20 -0
  319. package/dist/extensions/pi-subagents/test/integration/intercom-result-delivery.test.ts +4 -3
  320. package/dist/extensions/pi-subagents/test/integration/orca-progress-tabs.test.ts +2 -2
  321. package/dist/extensions/pi-subagents/test/integration/render-fork-badge.test.ts +54 -4
  322. package/dist/extensions/pi-subagents/test/integration/render-widget.test.ts +482 -4
  323. package/dist/extensions/pi-subagents/test/integration/result-watcher.test.ts +157 -9
  324. package/dist/extensions/pi-subagents/test/integration/session-tokens.test.ts +5 -5
  325. package/dist/extensions/pi-subagents/test/integration/single-execution.test.ts +1508 -291
  326. package/dist/extensions/pi-subagents/test/integration/slash-commands.test.ts +423 -1
  327. package/dist/extensions/pi-subagents/test/integration/template-resolution.test.ts +8 -0
  328. package/dist/extensions/pi-subagents/test/support/cursor-smoke-inputs.ts +51 -0
  329. package/dist/extensions/pi-subagents/test/support/helpers.ts +2 -0
  330. package/dist/extensions/pi-subagents/test/support/mock-pi.ts +5 -5
  331. package/dist/extensions/pi-subagents/test/support/real-session-runner.ts +6 -6
  332. package/dist/extensions/pi-subagents/test/unit/abort-recovery.test.ts +152 -0
  333. package/dist/extensions/pi-subagents/test/unit/active-async-capacity.test.ts +48 -0
  334. package/dist/extensions/pi-subagents/test/unit/agent-frontmatter.test.ts +184 -6
  335. package/dist/extensions/pi-subagents/test/unit/agent-management.test.ts +66 -6
  336. package/dist/extensions/pi-subagents/test/unit/agent-overrides.test.ts +77 -0
  337. package/dist/extensions/pi-subagents/test/unit/agent-refinements.test.ts +18 -0
  338. package/dist/extensions/pi-subagents/test/unit/anti-slop-oxlint.test.ts +86 -0
  339. package/dist/extensions/pi-subagents/test/unit/artifacts.test.ts +1 -1
  340. package/dist/extensions/pi-subagents/test/unit/async-execution.test.ts +101 -1
  341. package/dist/extensions/pi-subagents/test/unit/async-interrupt-action.test.ts +121 -0
  342. package/dist/extensions/pi-subagents/test/unit/async-recovery-descriptor.test.ts +28 -0
  343. package/dist/extensions/pi-subagents/test/unit/async-resume.test.ts +14 -7
  344. package/dist/extensions/pi-subagents/test/unit/async-retention.test.ts +53 -0
  345. package/dist/extensions/pi-subagents/test/unit/async-status-projection.test.ts +190 -0
  346. package/dist/extensions/pi-subagents/test/unit/async-status-snapshot.test.ts +42 -0
  347. package/dist/extensions/pi-subagents/test/unit/auto-drain.test.ts +8 -4
  348. package/dist/extensions/pi-subagents/test/unit/background-work.test.ts +23 -0
  349. package/dist/extensions/pi-subagents/test/unit/chain-root-attachment.test.ts +78 -1
  350. package/dist/extensions/pi-subagents/test/unit/child-launch-plan.test.ts +94 -0
  351. package/dist/extensions/pi-subagents/test/unit/child-protocol.test.ts +21 -0
  352. package/dist/extensions/pi-subagents/test/unit/child-session-name.test.ts +50 -0
  353. package/dist/extensions/pi-subagents/test/unit/claude-code-adapter.test.ts +245 -0
  354. package/dist/extensions/pi-subagents/test/unit/codex-exec-adapter.test.ts +192 -0
  355. package/dist/extensions/pi-subagents/test/unit/compaction-resume.test.ts +27 -0
  356. package/dist/extensions/pi-subagents/test/unit/completion-evidence.test.ts +150 -0
  357. package/dist/extensions/pi-subagents/test/unit/completion-guard.test.ts +21 -0
  358. package/dist/extensions/pi-subagents/test/unit/cursor-agent-adapter.test.ts +259 -0
  359. package/dist/extensions/pi-subagents/test/unit/cursor-smoke-inputs.test.ts +77 -0
  360. package/dist/extensions/pi-subagents/test/unit/delegation-api.test.ts +1 -4
  361. package/dist/extensions/pi-subagents/test/unit/doctor.test.ts +1 -1
  362. package/dist/extensions/pi-subagents/test/unit/extension-context.test.ts +27 -0
  363. package/dist/extensions/pi-subagents/test/unit/external-cli-runner.test.ts +223 -0
  364. package/dist/extensions/pi-subagents/test/unit/fast-mode-extension.test.ts +27 -0
  365. package/dist/extensions/pi-subagents/test/unit/fleet-status.test.ts +208 -2
  366. package/dist/extensions/pi-subagents/test/unit/fleet-transcript.test.ts +29 -1
  367. package/dist/extensions/pi-subagents/test/unit/fleet.test.ts +110 -1
  368. package/dist/extensions/pi-subagents/test/unit/foreground-control.test.ts +10 -0
  369. package/dist/extensions/pi-subagents/test/unit/formatters.test.ts +14 -0
  370. package/dist/extensions/pi-subagents/test/unit/herdr-inspector.test.ts +18 -9
  371. package/dist/extensions/pi-subagents/test/unit/herdr-session-roots-codec.test.ts +63 -0
  372. package/dist/extensions/pi-subagents/test/unit/herdr-shell-command.test.ts +2 -2
  373. package/dist/extensions/pi-subagents/test/unit/herdr-status-bridge.test.ts +132 -0
  374. package/dist/extensions/pi-subagents/test/unit/host-command.test.ts +156 -0
  375. package/dist/extensions/pi-subagents/test/unit/host-step-status.test.ts +98 -0
  376. package/dist/extensions/pi-subagents/test/unit/index-child-registration.test.ts +178 -26
  377. package/dist/extensions/pi-subagents/test/unit/index-segment.test.ts +2 -1
  378. package/dist/extensions/pi-subagents/test/unit/inspect-rpc.test.ts +21 -0
  379. package/dist/extensions/pi-subagents/test/unit/mcp-direct-tool-grant.test.ts +138 -0
  380. package/dist/extensions/pi-subagents/test/unit/mission-lifecycle.test.ts +1 -1
  381. package/dist/extensions/pi-subagents/test/unit/model-exclusions.test.ts +91 -0
  382. package/dist/extensions/pi-subagents/test/unit/model-fallback.test.ts +72 -7
  383. package/dist/extensions/pi-subagents/test/unit/mutation-evidence.test.ts +21 -0
  384. package/dist/extensions/pi-subagents/test/unit/nested-control.test.ts +1 -0
  385. package/dist/extensions/pi-subagents/test/unit/nested-events.test.ts +20 -2
  386. package/dist/extensions/pi-subagents/test/unit/notify.test.ts +129 -1
  387. package/dist/extensions/pi-subagents/test/unit/orca-progress-tabs.test.ts +2 -2
  388. package/dist/extensions/pi-subagents/test/unit/package-manifest.test.ts +4 -0
  389. package/dist/extensions/pi-subagents/test/unit/parallel-handoff.test.ts +358 -0
  390. package/dist/extensions/pi-subagents/test/unit/pi-args.test.ts +574 -21
  391. package/dist/extensions/pi-subagents/test/unit/pi-coding-agent-dir.test.ts +109 -1
  392. package/dist/extensions/pi-subagents/test/unit/preflight.test.ts +71 -1
  393. package/dist/extensions/pi-subagents/test/unit/profiles.test.ts +7 -7
  394. package/dist/extensions/pi-subagents/test/unit/pruned-fork.test.ts +214 -0
  395. package/dist/extensions/pi-subagents/test/unit/public-execution.test.ts +29 -8
  396. package/dist/extensions/pi-subagents/test/unit/render-helpers.test.ts +53 -0
  397. package/dist/extensions/pi-subagents/test/unit/result-delivery-ownership.test.ts +34 -0
  398. package/dist/extensions/pi-subagents/test/unit/result-files.test.ts +101 -3
  399. package/dist/extensions/pi-subagents/test/unit/retained-children.test.ts +1 -0
  400. package/dist/extensions/pi-subagents/test/unit/rpc.test.ts +10 -8
  401. package/dist/extensions/pi-subagents/test/unit/run-fanout-budget.test.ts +71 -1
  402. package/dist/extensions/pi-subagents/test/unit/run-status.test.ts +41 -7
  403. package/dist/extensions/pi-subagents/test/unit/runtime-agent-registration.test.ts +136 -2
  404. package/dist/extensions/pi-subagents/test/unit/scheduled-runs.test.ts +137 -0
  405. package/dist/extensions/pi-subagents/test/unit/schemas.test.ts +33 -12
  406. package/dist/extensions/pi-subagents/test/unit/scripted-workflow.test.ts +504 -1
  407. package/dist/extensions/pi-subagents/test/unit/single-output.test.ts +8 -0
  408. package/dist/extensions/pi-subagents/test/unit/skills-fallback.test.ts +1 -1
  409. package/dist/extensions/pi-subagents/test/unit/stale-run-reconciler.test.ts +12 -1
  410. package/dist/extensions/pi-subagents/test/unit/steering-action.test.ts +3 -5
  411. package/dist/extensions/pi-subagents/test/unit/steering.test.ts +5 -6
  412. package/dist/extensions/pi-subagents/test/unit/subagent-action-recovery.test.ts +0 -8
  413. package/dist/extensions/pi-subagents/test/unit/subagent-guide.test.ts +5 -0
  414. package/dist/extensions/pi-subagents/test/unit/subagent-prompt-runtime.test.ts +157 -2
  415. package/dist/extensions/pi-subagents/test/unit/subagent-startup-retry.test.ts +1 -1
  416. package/dist/extensions/pi-subagents/test/unit/subagent-wait.test.ts +63 -4
  417. package/dist/extensions/pi-subagents/test/unit/tool-description.test.ts +27 -23
  418. package/dist/extensions/pi-subagents/test/unit/wait-completions.test.ts +44 -0
  419. package/dist/extensions/pi-subagents/test/unit/wait-subscriptions.test.ts +1 -0
  420. package/dist/extensions/pi-subagents/test/unit/widget-nested-render.test.ts +7 -5
  421. package/dist/extensions/pi-subagents/test/unit/workflow-chat-progress.test.ts +106 -0
  422. package/dist/extensions/pi-subagents/test/unit/workflow-detach-reconcile.test.ts +484 -9
  423. package/dist/extensions/pi-subagents/test/unit/workflow-launch-params.test.ts +38 -4
  424. package/dist/extensions/pi-subagents/test/unit/workflow-preflight.test.ts +155 -0
  425. package/dist/extensions/pi-subagents/test/unit/workflow-receipt.test.ts +318 -0
  426. package/dist/extensions/pi-subagents/test/unit/workflow-resume-hint.test.ts +365 -0
  427. package/dist/extensions/pi-subagents/test/unit/worktree-cleanup-plan.test.ts +349 -0
  428. package/dist/extensions/pi-subagents/test/unit/worktree.test.ts +21 -0
  429. package/dist/extensions/pi-subagents/test/unit/writer-budget-guidance.test.ts +2 -7
  430. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/effect/index.ts +13 -0
  431. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/effect/rules/no-service-constructor-imports.ts +52 -0
  432. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/index.ts +41 -0
  433. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-chained-type-assertions.ts +77 -0
  434. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-conditional-empty-object-spread.ts +49 -0
  435. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-known-value-widening.ts +247 -0
  436. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-module-mocking.ts +91 -0
  437. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-object-parameters.ts +126 -0
  438. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-reflect-apply.ts +28 -0
  439. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-reflect-get.ts +28 -0
  440. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-runtime-typeof.ts +67 -0
  441. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-shape-in-symbol-names.ts +39 -0
  442. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-unknown-parameters.ts +83 -0
  443. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-unknown-returns.ts +115 -0
  444. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-unknown-type-aliases.ts +72 -0
  445. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-unsafe-dictionary-type.ts +134 -0
  446. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-widen-then-assert.ts +366 -0
  447. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/require-safety-comment-for-type-assertion.ts +62 -0
  448. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/shared/dictionary-types.ts +502 -0
  449. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/shared/lexical-type-parameters.ts +60 -0
  450. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/shared/reflect-method.ts +35 -0
  451. package/dist/extensions/pi-tool-display/tests/capabilities.test.ts +1 -1
  452. package/dist/extensions/pi-tool-display/tests/settings-inspector.test.ts +2 -2
  453. package/dist/extensions/pi-tool-display/tests/tool-overrides-mcp.test.ts +1 -1
  454. package/dist/extensions/pi-zentui/extensions/zentui/format.ts +2 -1
  455. package/dist/extensions/pi-zentui/test/format.test.ts +15 -1
  456. package/dist/modes/interactive/components/footer.js +2 -2
  457. package/dist/modes/interactive/interactive-mode.js +11 -11
  458. package/dist/skills/workflow/SKILL.md +65 -50
  459. package/package.json +112 -110
  460. package/dist/extensions/enable-readonly-tools.test.ts +0 -63
  461. package/dist/extensions/enable-readonly-tools.ts +0 -24
  462. package/dist/extensions/model-prompt-injector/config.json +0 -16
  463. package/dist/extensions/model-prompt-injector/index.test.ts +0 -102
  464. package/dist/extensions/model-prompt-injector/index.ts +0 -148
  465. package/dist/extensions/pi-subagents/src/runs/shared/turn-budget.ts +0 -98
  466. package/dist/extensions/pi-subagents/test/unit/turn-budget.test.ts +0 -250
@@ -1,13 +1,17 @@
1
1
  import * as fs from "node:fs";
2
2
  import * as path from "node:path";
3
3
  import { writePrivateAtomicJson } from "../shared/atomic-json.ts";
4
- import type { WorkflowReceipt, WorkflowReceiptEntry, WorkflowReceiptState } from "../shared/types.ts";
4
+ import type { ExternalCliReceiptMetadata, WorkflowReceipt, WorkflowReceiptEntry, WorkflowReceiptState, WorkflowRecoveryAction, WorkflowTerminalOutcome, WorkflowTerminalResolution } from "../shared/types.ts";
5
5
  import type { WorkflowReceiptResumeReference, WorkflowScriptChildResult } from "./scripted-workflow.ts";
6
+ import { parseWorkflowChildSummary } from "./workflow-child-summary.ts";
7
+ import { HOST_STEP_MAX_COUNT, assertUniqueHostStepIds, parseHostStepNode } from "../runs/shared/host-step-status.ts";
8
+ import { assertWorkflowLaneKey, normalizeWorkflowLaneMetadata } from "../runs/shared/lane-metadata.ts";
6
9
 
7
10
  export type { WorkflowReceipt, WorkflowReceiptEntry, WorkflowReceiptState } from "../shared/types.ts";
8
11
 
9
12
  export const WORKFLOW_RECEIPT_VERSION = 1;
10
13
  export const WORKFLOW_RECEIPT_FILE = "workflow-receipt.json";
14
+ const MAX_WORKFLOW_RECEIPT_BYTES = 2 * 1024 * 1024;
11
15
 
12
16
  const KEY_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;
13
17
 
@@ -32,30 +36,42 @@ export function buildWorkflowReceipt(input: {
32
36
  workflowRunId: string;
33
37
  state: WorkflowReceiptState;
34
38
  children: WorkflowScriptChildResult[];
39
+ hostSteps?: WorkflowReceipt["hostSteps"];
40
+ workflowChildren?: WorkflowReceipt["workflowChildren"];
41
+ terminalOutcome?: WorkflowTerminalOutcome;
35
42
  createdAt?: number;
36
43
  }): WorkflowReceipt {
37
44
  const workflowRunId = assertSafeRunId(input.workflowRunId, "workflowRunId");
45
+ if (input.workflowChildren?.workflowRunId !== undefined && input.workflowChildren.workflowRunId !== workflowRunId) throw new Error("workflowChildren workflowRunId does not match its receipt.");
38
46
  const entries: Record<string, WorkflowReceiptEntry> = Object.create(null) as Record<string, WorkflowReceiptEntry>;
39
47
  for (const child of input.children) {
40
48
  const key = assertKey(child.key, "workflow receipt child key");
41
49
  if (entries[key]) throw new Error(`Workflow receipt has duplicate child key '${key}'.`);
50
+ const lane = normalizeWorkflowLaneMetadata(child.lane, `workflow receipt child '${key}'.lane`);
51
+ assertWorkflowLaneKey(lane, key, `workflow receipt child '${key}'.lane`);
42
52
  const runIds = [...new Set((child.continuation?.runIds ?? (child.runId ? [child.runId] : [])).filter((runId) => typeof runId === "string" && runId.trim()).map((runId) => runId.trim()))];
43
53
  const latestRunId = runIds.at(-1);
44
54
  const resumability = child.resumability ?? { state: "not-resumable", reason: child.runId ? "resumability was not recorded" : "child produced no run id" };
45
55
  if (resumability.state === "resumable" && !latestRunId) throw new Error(`Workflow receipt child '${key}' is resumable but has no retained run id.`);
46
56
  const base = {
47
57
  key,
58
+ ...(lane ? { lane } : {}),
59
+ ...(child.terminalOutcome ? { terminalOutcome: child.terminalOutcome } : {}),
48
60
  ...(child.agent ? { agent: child.agent } : {}),
49
61
  ...(child.requestedContext ? { requestedContext: child.requestedContext } : {}),
50
62
  ...(child.resolvedContext ? { resolvedContext: child.resolvedContext } : {}),
51
63
  ...(child.outputReference ? { outputReference: child.outputReference } : {}),
64
+ ...(child.externalAdapter ? { externalAdapter: child.externalAdapter } : {}),
52
65
  continuation: { runIds },
53
66
  };
54
67
  entries[key] = resumability.state === "resumable"
55
68
  ? { ...base, latestRunId: latestRunId!, resumability }
56
69
  : { ...base, ...(latestRunId ? { latestRunId } : {}), resumability };
57
70
  }
58
- return { version: WORKFLOW_RECEIPT_VERSION, workflowRunId, state: input.state, createdAt: input.createdAt ?? Date.now(), entries };
71
+ if (input.hostSteps && input.hostSteps.length > HOST_STEP_MAX_COUNT) throw new Error(`Workflow receipt has more than ${HOST_STEP_MAX_COUNT} host steps.`);
72
+ const hostSteps = input.hostSteps?.map((hostStep, index) => parseHostStepNode(hostStep, `workflow receipt hostSteps[${index}]`));
73
+ if (hostSteps) assertUniqueHostStepIds(hostSteps, "workflow receipt");
74
+ return { version: WORKFLOW_RECEIPT_VERSION, workflowRunId, state: input.state, createdAt: input.createdAt ?? Date.now(), entries, ...(hostSteps?.length ? { hostSteps } : {}), ...(input.workflowChildren ? { workflowChildren: input.workflowChildren } : {}), ...(input.terminalOutcome ? { terminalOutcome: input.terminalOutcome } : {}) };
59
75
  }
60
76
 
61
77
  export function writeWorkflowReceipt(asyncDir: string, receipt: WorkflowReceipt): string {
@@ -64,6 +80,136 @@ export function writeWorkflowReceipt(asyncDir: string, receipt: WorkflowReceipt)
64
80
  return receiptPath;
65
81
  }
66
82
 
83
+ function readWorkflowReceiptFile(receiptPath: string): unknown {
84
+ let fd: number | undefined;
85
+ try {
86
+ fd = fs.openSync(receiptPath, "r");
87
+ const stat = fs.fstatSync(fd);
88
+ if (!stat.isFile()) throw new Error("workflow receipt is not a regular file.");
89
+ if (stat.size > MAX_WORKFLOW_RECEIPT_BYTES) throw new Error(`workflow receipt exceeds the ${MAX_WORKFLOW_RECEIPT_BYTES}-byte limit.`);
90
+ const buffer = Buffer.allocUnsafe(stat.size);
91
+ let offset = 0;
92
+ while (offset < buffer.length) {
93
+ const bytesRead = fs.readSync(fd, buffer, offset, buffer.length - offset, null);
94
+ if (bytesRead <= 0) throw new Error("workflow receipt ended before its declared size.");
95
+ offset += bytesRead;
96
+ }
97
+ return JSON.parse(buffer.toString("utf-8")) as unknown;
98
+ } finally {
99
+ if (fd !== undefined) fs.closeSync(fd);
100
+ }
101
+ }
102
+
103
+ const EXTERNAL_CLI_CAPABILITIES = {
104
+ stop: true,
105
+ steer: false,
106
+ resume: false,
107
+ structuredOutput: false,
108
+ toolEvents: false,
109
+ supervisor: "unsupported",
110
+ forkContext: false,
111
+ extensionBindings: false,
112
+ } as const;
113
+
114
+ function parseExternalCliReceiptMetadata(value: unknown, key: string, source: string): ExternalCliReceiptMetadata | undefined {
115
+ if (value === undefined) return undefined;
116
+ const label = `Invalid workflow receipt '${source}': entry '${key}' externalAdapter`;
117
+ if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error(`${label} must be an object.`);
118
+ const metadata = value as Record<string, unknown>;
119
+ const unknownMetadata = Object.keys(metadata).filter((field) => !["adapter", "capabilities", "safety", "outputArtifacts", "handoff", "supervisor", "nonResumableReason"].includes(field));
120
+ if (unknownMetadata.length > 0) throw new Error(`${label} has unsupported fields: ${unknownMetadata.join(", ")}.`);
121
+ const adapter = metadata.adapter;
122
+ if (!adapter || typeof adapter !== "object" || Array.isArray(adapter)) throw new Error(`${label}.adapter must be an object.`);
123
+ const adapterRecord = adapter as Record<string, unknown>;
124
+ const unknownAdapter = Object.keys(adapterRecord).filter((field) => !["id", "version", "executionMode"].includes(field));
125
+ if (unknownAdapter.length > 0) throw new Error(`${label}.adapter has unsupported fields: ${unknownAdapter.join(", ")}.`);
126
+ if ((adapterRecord.id !== "external-cli" && adapterRecord.id !== "codex-exec" && adapterRecord.id !== "codex-exec-writer" && adapterRecord.id !== "claude-code" && adapterRecord.id !== "claude-code-writer" && adapterRecord.id !== "cursor-agent" && adapterRecord.id !== "cursor-agent-writer" && adapterRecord.id !== "grok-build") || adapterRecord.version !== 1 || adapterRecord.executionMode !== (adapterRecord.id === "cursor-agent" || adapterRecord.id === "cursor-agent-writer" || adapterRecord.id === "grok-build" ? "one-shot-prompt-file" : "one-shot-stdin")) throw new Error(`${label}.adapter is invalid.`);
127
+ const capabilities = metadata.capabilities;
128
+ if (!capabilities || typeof capabilities !== "object" || Array.isArray(capabilities)) throw new Error(`${label}.capabilities must be an object.`);
129
+ const capabilityRecord = capabilities as Record<string, unknown>;
130
+ const unknownCapabilities = Object.keys(capabilityRecord).filter((field) => !(field in EXTERNAL_CLI_CAPABILITIES));
131
+ if (unknownCapabilities.length > 0) throw new Error(`${label}.capabilities has unsupported fields: ${unknownCapabilities.join(", ")}.`);
132
+ for (const [capability, expected] of Object.entries(EXTERNAL_CLI_CAPABILITIES)) {
133
+ if (capabilityRecord[capability] !== expected) throw new Error(`${label}.capabilities.${capability} is invalid.`);
134
+ }
135
+ const safety = metadata.safety;
136
+ if (adapterRecord.id === "codex-exec") {
137
+ if (!safety || typeof safety !== "object" || Array.isArray(safety)) throw new Error(`${label}.safety is missing.`);
138
+ const safetyRecord = safety as Record<string, unknown>;
139
+ const unknownSafety = Object.keys(safetyRecord).filter((field) => !["sandbox", "approvalPolicy", "ephemeral"].includes(field));
140
+ if (unknownSafety.length > 0) throw new Error(`${label}.safety has unsupported fields: ${unknownSafety.join(", ")}.`);
141
+ if (safetyRecord.sandbox !== "read-only" || safetyRecord.approvalPolicy !== "never" || safetyRecord.ephemeral !== true) throw new Error(`${label}.safety is invalid.`);
142
+ } else if (adapterRecord.id === "codex-exec-writer") {
143
+ if (!safety || typeof safety !== "object" || Array.isArray(safety)) throw new Error(`${label}.safety is missing.`);
144
+ const safetyRecord = safety as Record<string, unknown>;
145
+ const unknownSafety = Object.keys(safetyRecord).filter((field) => !["access", "sandbox", "approvalPolicy", "ephemeral"].includes(field));
146
+ if (unknownSafety.length > 0) throw new Error(`${label}.safety has unsupported fields: ${unknownSafety.join(", ")}.`);
147
+ if (safetyRecord.access !== "workspace-write" || safetyRecord.sandbox !== "workspace-write" || safetyRecord.approvalPolicy !== "never" || safetyRecord.ephemeral !== true) throw new Error(`${label}.safety is invalid.`);
148
+ } else if (adapterRecord.id === "claude-code") {
149
+ if (!safety || typeof safety !== "object" || Array.isArray(safety)) throw new Error(`${label}.safety is missing.`);
150
+ const safetyRecord = safety as Record<string, unknown>;
151
+ const legacy = safetyRecord.authentication === undefined;
152
+ const unknownSafety = Object.keys(safetyRecord).filter((field) => !(legacy
153
+ ? ["permissionMode", "tools", "mcp", "settingSources", "sessionPersistence"]
154
+ : ["access", "authentication", "permissionMode", "tools", "mcp", "settingSources", "userSettingsTrust", "sessionPersistence"]).includes(field));
155
+ if (unknownSafety.length > 0) throw new Error(`${label}.safety has unsupported fields: ${unknownSafety.join(", ")}.`);
156
+ if (legacy) {
157
+ if (safetyRecord.permissionMode !== "plan" || safetyRecord.tools !== "none" || safetyRecord.mcp !== "empty-strict" || safetyRecord.settingSources !== "none" || safetyRecord.sessionPersistence !== false) throw new Error(`${label}.safety is invalid.`);
158
+ } else if (safetyRecord.access !== "read-only" || safetyRecord.authentication !== "existing-cli-required" || safetyRecord.permissionMode !== "plan" || safetyRecord.tools !== "none" || safetyRecord.mcp !== "empty-strict" || safetyRecord.settingSources !== "user" || safetyRecord.userSettingsTrust !== "required" || safetyRecord.sessionPersistence !== false) throw new Error(`${label}.safety is invalid.`);
159
+ } else if (adapterRecord.id === "claude-code-writer") {
160
+ if (!safety || typeof safety !== "object" || Array.isArray(safety)) throw new Error(`${label}.safety is missing.`);
161
+ const safetyRecord = safety as Record<string, unknown>;
162
+ const unknownSafety = Object.keys(safetyRecord).filter((field) => !["access", "authentication", "permissionMode", "tools", "mcp", "settingSources", "userSettingsTrust", "sessionPersistence"].includes(field));
163
+ if (unknownSafety.length > 0) throw new Error(`${label}.safety has unsupported fields: ${unknownSafety.join(", ")}.`);
164
+ if (safetyRecord.access !== "workspace-write" || safetyRecord.authentication !== "existing-cli-required" || safetyRecord.permissionMode !== "acceptEdits" || safetyRecord.tools !== "Read,Write,Edit,Glob,Grep" || safetyRecord.mcp !== "empty-strict" || safetyRecord.settingSources !== "user" || safetyRecord.userSettingsTrust !== "required" || safetyRecord.sessionPersistence !== false) throw new Error(`${label}.safety is invalid.`);
165
+ } else if (adapterRecord.id === "cursor-agent" || adapterRecord.id === "cursor-agent-writer") {
166
+ if (!safety || typeof safety !== "object" || Array.isArray(safety)) throw new Error(`${label}.safety is missing.`);
167
+ const safetyRecord = safety as Record<string, unknown>;
168
+ const unknownSafety = Object.keys(safetyRecord).filter((field) => !["access", "authentication", "mode", "sandbox", "workspaceTrust", "sessionReuse"].includes(field));
169
+ if (unknownSafety.length > 0) throw new Error(`${label}.safety has unsupported fields: ${unknownSafety.join(", ")}.`);
170
+ const writer = adapterRecord.id === "cursor-agent-writer";
171
+ if (safetyRecord.access !== (writer ? "workspace-write" : "read-only") || safetyRecord.authentication !== "cursor-api-key-or-existing-login" || safetyRecord.mode !== (writer ? "print" : "ask") || safetyRecord.sandbox !== "enabled" || safetyRecord.workspaceTrust !== "existing-required" || safetyRecord.sessionReuse !== false) throw new Error(`${label}.safety is invalid.`);
172
+ } else if (adapterRecord.id === "grok-build") {
173
+ if (!safety || typeof safety !== "object" || Array.isArray(safety)) throw new Error(`${label}.safety is missing.`);
174
+ const safetyRecord = safety as Record<string, unknown>;
175
+ const unknownSafety = Object.keys(safetyRecord).filter((field) => !["access", "authentication", "permissionMode", "tools", "deniedTools", "sandbox", "webSearch", "subagents", "config", "updates", "sessionPersistence"].includes(field));
176
+ if (unknownSafety.length > 0) throw new Error(`${label}.safety has unsupported fields: ${unknownSafety.join(", ")}.`);
177
+ if (safetyRecord.access !== "read-only" || safetyRecord.authentication !== "xai-api-key-required" || safetyRecord.permissionMode !== "plan" || safetyRecord.tools !== "read_file,grep,list_dir" || safetyRecord.deniedTools !== "run_terminal_cmd,search_replace,Agent,Bash,Edit,Write,MCPTool" || safetyRecord.sandbox !== "read-only" || safetyRecord.webSearch !== false || safetyRecord.subagents !== false || safetyRecord.config !== "temporary-home" || safetyRecord.updates !== "disabled" || safetyRecord.sessionPersistence !== false) throw new Error(`${label}.safety is invalid.`);
178
+ } else if (safety !== undefined) throw new Error(`${label}.safety is invalid for the generic adapter.`);
179
+ const handoff = metadata.handoff;
180
+ if (!handoff || typeof handoff !== "object" || Array.isArray(handoff)) throw new Error(`${label}.handoff must be an object.`);
181
+ const handoffRecord = handoff as Record<string, unknown>;
182
+ const unknownHandoff = Object.keys(handoffRecord).filter((field) => field !== "mode");
183
+ if (unknownHandoff.length > 0) throw new Error(`${label}.handoff has unsupported fields: ${unknownHandoff.join(", ")}.`);
184
+ if (handoffRecord.mode !== "fresh") throw new Error(`${label}.handoff is invalid.`);
185
+ const supervisor = metadata.supervisor;
186
+ if (!supervisor || typeof supervisor !== "object" || Array.isArray(supervisor)) throw new Error(`${label}.supervisor must be an object.`);
187
+ const supervisorRecord = supervisor as Record<string, unknown>;
188
+ const unknownSupervisor = Object.keys(supervisorRecord).filter((field) => !["mode", "reason"].includes(field));
189
+ if (unknownSupervisor.length > 0) throw new Error(`${label}.supervisor has unsupported fields: ${unknownSupervisor.join(", ")}.`);
190
+ if (supervisorRecord.mode !== "unsupported" || typeof supervisorRecord.reason !== "string" || !supervisorRecord.reason.trim()) throw new Error(`${label}.supervisor is invalid.`);
191
+ if (typeof metadata.nonResumableReason !== "string" || !metadata.nonResumableReason.trim()) throw new Error(`${label}.nonResumableReason is missing.`);
192
+ const outputArtifacts = metadata.outputArtifacts;
193
+ if (outputArtifacts !== undefined) {
194
+ if (!outputArtifacts || typeof outputArtifacts !== "object" || Array.isArray(outputArtifacts)) throw new Error(`${label}.outputArtifacts must be an object.`);
195
+ const unknownArtifacts = Object.keys(outputArtifacts).filter((field) => !["stdoutPath", "stderrPath", "finalOutputPath"].includes(field));
196
+ if (unknownArtifacts.length > 0) throw new Error(`${label}.outputArtifacts has unsupported fields: ${unknownArtifacts.join(", ")}.`);
197
+ for (const field of ["stdoutPath", "stderrPath", "finalOutputPath"] as const) {
198
+ const artifactPath = (outputArtifacts as Record<string, unknown>)[field];
199
+ if (artifactPath !== undefined && (typeof artifactPath !== "string" || !artifactPath.trim())) throw new Error(`${label}.outputArtifacts.${field} must be a non-empty string.`);
200
+ }
201
+ }
202
+ return value as ExternalCliReceiptMetadata;
203
+ }
204
+
205
+ function parseTerminalOutcome(value: unknown, label: string): WorkflowTerminalOutcome | undefined {
206
+ if (value === undefined) return undefined;
207
+ if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error(`${label} must be an object.`);
208
+ const outcome = value as Record<string, unknown>;
209
+ if (outcome.state !== "partial" || (outcome.reason !== "budget_exhausted" && outcome.reason !== "timeout")) throw new Error(`${label} is invalid.`);
210
+ return { state: "partial", reason: outcome.reason };
211
+ }
212
+
67
213
  function parseEntry(value: unknown, key: string, source: string): WorkflowReceiptEntry {
68
214
  if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error(`Invalid workflow receipt '${source}': entry '${key}' must be an object.`);
69
215
  const entry = value as Record<string, unknown>;
@@ -84,16 +230,47 @@ function parseEntry(value: unknown, key: string, source: string): WorkflowReceip
84
230
  const reason = (resumability as Record<string, unknown>).reason;
85
231
  if (state === "resumable" && latestRunId === undefined) throw new Error(`Invalid workflow receipt '${source}': entry '${key}' resumable entry has no retained run id.`);
86
232
  if (state === "not-resumable" && (typeof reason !== "string" || !reason.trim())) throw new Error(`Invalid workflow receipt '${source}': entry '${key}' non-resumable reason is missing.`);
87
- return value as WorkflowReceiptEntry;
233
+ const terminalOutcome = parseTerminalOutcome(entry.terminalOutcome, `Invalid workflow receipt '${source}': entry '${key}' terminalOutcome`);
234
+ parseExternalCliReceiptMetadata(entry.externalAdapter, key, source);
235
+ const lane = normalizeWorkflowLaneMetadata(entry.lane, `Invalid workflow receipt '${source}': entry '${key}'.lane`);
236
+ assertWorkflowLaneKey(lane, key, `Invalid workflow receipt '${source}': entry '${key}'.lane`);
237
+ return { ...(value as WorkflowReceiptEntry), ...(lane ? { lane } : {}), ...(terminalOutcome ? { terminalOutcome } : {}) };
238
+ }
239
+
240
+ function parseWorkflowResolution(value: unknown, source: string): WorkflowTerminalResolution | undefined {
241
+ if (value === undefined) return undefined;
242
+ if (value !== "settled-awaiting-resume" && value !== "failed-child" && value !== "interrupted-child") throw new Error(`Invalid workflow receipt '${source}': workflowResolution is invalid.`);
243
+ return value;
244
+ }
245
+
246
+ function parseRecovery(value: unknown, workflowRunId: string, entries: Record<string, WorkflowReceiptEntry>, source: string): WorkflowRecoveryAction[] | undefined {
247
+ if (value === undefined) return undefined;
248
+ if (!Array.isArray(value)) throw new Error(`Invalid workflow receipt '${source}': recovery must be an array.`);
249
+ return value.map((item, index) => {
250
+ if (!item || typeof item !== "object" || Array.isArray(item)) throw new Error(`Invalid workflow receipt '${source}': recovery[${index}] must be an object.`);
251
+ const action = item as Record<string, unknown>;
252
+ const key = action.key;
253
+ const resume = action.resume;
254
+ if (typeof key !== "string" || !entries[key] || action.call !== "runs.run" || action.taskRequired !== true || !resume || typeof resume !== "object" || Array.isArray(resume)) throw new Error(`Invalid workflow receipt '${source}': recovery[${index}] is invalid.`);
255
+ const reference = resume as Record<string, unknown>;
256
+ if (reference.workflowRunId !== workflowRunId || reference.key !== key || reference.latest !== true || entries[key].resumability.state !== "resumable") throw new Error(`Invalid workflow receipt '${source}': recovery[${index}] does not identify a resumable entry.`);
257
+ return { key, call: "runs.run", resume: { workflowRunId, key, latest: true }, taskRequired: true };
258
+ });
88
259
  }
89
260
 
90
261
  export function readWorkflowReceipt(asyncDirRoot: string, workflowRunId: string): WorkflowReceipt {
91
262
  const receiptPath = workflowReceiptPath(asyncDirRoot, workflowRunId);
92
263
  let value: unknown;
93
264
  try {
94
- value = JSON.parse(fs.readFileSync(receiptPath, "utf-8")) as unknown;
265
+ value = readWorkflowReceiptFile(receiptPath);
95
266
  } catch (error) {
96
- if ((error as NodeJS.ErrnoException).code === "ENOENT") throw new Error(`Workflow receipt '${workflowRunId}' was not found.`);
267
+ if ((error as NodeJS.ErrnoException).code === "ENOENT") {
268
+ const workflowDir = path.dirname(receiptPath);
269
+ if (fs.existsSync(path.join(workflowDir, "status.json")) || fs.existsSync(path.join(workflowDir, "events.jsonl"))) {
270
+ throw new Error(`Workflow receipt '${workflowRunId}' is not available because the workflow may still be active or terminal receipt writing failed. Use direct child run IDs from status/events for direct resume after the normal retained-child checks.`);
271
+ }
272
+ throw new Error(`Workflow receipt '${workflowRunId}' was not found.`);
273
+ }
97
274
  throw new Error(`Workflow receipt '${workflowRunId}' could not be read: ${error instanceof Error ? error.message : String(error)}`, { cause: error instanceof Error ? error : undefined });
98
275
  }
99
276
  if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error(`Invalid workflow receipt '${receiptPath}': expected an object.`);
@@ -107,7 +284,19 @@ export function readWorkflowReceipt(asyncDirRoot: string, workflowRunId: string)
107
284
  if (!receipt.entries || typeof receipt.entries !== "object" || Array.isArray(receipt.entries)) throw new Error(`Invalid workflow receipt '${receiptPath}': entries must be an object.`);
108
285
  const entries: Record<string, WorkflowReceiptEntry> = Object.create(null) as Record<string, WorkflowReceiptEntry>;
109
286
  for (const [key, entry] of Object.entries(receipt.entries as Record<string, unknown>)) entries[assertKey(key, "workflow receipt key")] = parseEntry(entry, key, receiptPath);
110
- return { version: 1, workflowRunId, state: receipt.state, createdAt: receipt.createdAt, entries };
287
+ const workflowChildren = parseWorkflowChildSummary(receipt.workflowChildren);
288
+ if (workflowChildren && workflowChildren.workflowRunId !== workflowRunId) throw new Error(`Workflow receipt '${receiptPath}' is stale: workflowChildren.workflowRunId does not match.`);
289
+ let hostSteps: NonNullable<WorkflowReceipt["hostSteps"]> | undefined;
290
+ if (receipt.hostSteps !== undefined) {
291
+ if (!Array.isArray(receipt.hostSteps)) throw new Error(`Invalid workflow receipt '${receiptPath}': hostSteps must be an array.`);
292
+ if (receipt.hostSteps.length > HOST_STEP_MAX_COUNT) throw new Error(`Invalid workflow receipt '${receiptPath}': hostSteps exceeds ${HOST_STEP_MAX_COUNT} entries.`);
293
+ hostSteps = receipt.hostSteps.map((hostStep, index) => parseHostStepNode(hostStep, `${receiptPath} hostSteps[${index}]`));
294
+ assertUniqueHostStepIds(hostSteps, receiptPath);
295
+ }
296
+ const workflowResolution = parseWorkflowResolution(receipt.workflowResolution, receiptPath);
297
+ const terminalOutcome = parseTerminalOutcome(receipt.terminalOutcome, `Invalid workflow receipt '${receiptPath}': terminalOutcome`);
298
+ const recovery = parseRecovery(receipt.recovery, workflowRunId, entries, receiptPath);
299
+ return { version: 1, workflowRunId, state: receipt.state, createdAt: receipt.createdAt, entries, ...(hostSteps?.length ? { hostSteps } : {}), ...(workflowChildren ? { workflowChildren } : {}), ...(workflowResolution ? { workflowResolution } : {}), ...(terminalOutcome ? { terminalOutcome } : {}), ...(recovery ? { recovery } : {}) };
111
300
  }
112
301
 
113
302
  export function resolveWorkflowReceiptResumeEntry(input: {
@@ -0,0 +1,246 @@
1
+ import type {
2
+ AsyncStatus,
3
+ WorkflowRecoveryAction,
4
+ WorkflowTerminalOutcome,
5
+ WorkflowTerminalResolution,
6
+ Usage,
7
+ } from "../shared/types.ts";
8
+ import type { WorkflowReceipt } from "./workflow-receipt.ts";
9
+ import { workflowChildSummary } from "./workflow-child-summary.ts";
10
+
11
+ export const UNSUPPORTED_DETACHED_WORKFLOW_CONTINUATION = "unsupported-continuation: detached workflow child settled, but JavaScript workflow continuation was not persisted. Resume the workflow explicitly instead of treating the completed child as top-level workflow completion.";
12
+ export const INTERRUPTED_DETACHED_CHILD = "Interrupted. Waiting for explicit next action.";
13
+ export const EVIDENCE_PERSISTENCE_FAILED = "evidence-persistence-failed";
14
+
15
+ export type WorkflowStatusStep = NonNullable<AsyncStatus["steps"]>[number] & {
16
+ outputPathMapping?: { requestedPath: string; savedPath: string };
17
+ interrupted?: boolean;
18
+ };
19
+
20
+ export interface WorkflowPublicChild {
21
+ workflowKey?: string;
22
+ agent?: string;
23
+ /** Human-readable display name for the child session, when derived at launch. */
24
+ sessionName?: string;
25
+ runId?: string;
26
+ usage?: Usage;
27
+ sessionFile?: string;
28
+ output: string;
29
+ outputState: "present" | "absent";
30
+ structuredOutput?: unknown;
31
+ success: boolean;
32
+ terminalOutcome?: WorkflowTerminalOutcome;
33
+ outputReference?: string;
34
+ outputPathMapping?: { requestedPath: string; savedPath: string };
35
+ stopped?: boolean;
36
+ interrupted?: boolean;
37
+ detached?: boolean;
38
+ error?: string;
39
+ artifactPaths?: { outputPath: string };
40
+ }
41
+
42
+ export interface WorkflowSettlementPlan {
43
+ status: AsyncStatus;
44
+ publicResult: Record<string, unknown>;
45
+ receipt?: WorkflowReceipt;
46
+ recovery: WorkflowRecoveryAction[];
47
+ completionEvent?: Record<string, unknown>;
48
+ }
49
+
50
+ function cloneWorkflowStatus(status: AsyncStatus): AsyncStatus {
51
+ return {
52
+ ...status,
53
+ steps: status.steps?.map((step) => ({ ...step })),
54
+ workflow: status.workflow ? { ...status.workflow, trace: [...(status.workflow.trace ?? [])] } : status.workflow,
55
+ };
56
+ }
57
+
58
+ function workflowState(status: AsyncStatus): "completed" | "failed" | "paused" | "stopped" {
59
+ return status.state === "complete" ? "completed" : status.state === "paused" ? "paused" : status.state === "stopped" ? "stopped" : "failed";
60
+ }
61
+
62
+ function withWorkflowChildren(status: AsyncStatus): AsyncStatus {
63
+ return {
64
+ ...status,
65
+ workflowChildren: workflowChildSummary({
66
+ parentToolCallId: status.toolCallId ?? status.runId,
67
+ workflowRunId: status.runId,
68
+ workflowState: workflowState(status),
69
+ inventoryComplete: true,
70
+ trace: status.workflow?.trace,
71
+ steps: status.steps,
72
+ }),
73
+ };
74
+ }
75
+
76
+ export function findWorkflowSettlementStep(status: AsyncStatus, childRunId: string, workflowKey?: string, sessionFile?: string): WorkflowStatusStep | undefined {
77
+ const exact = status.steps?.find((candidate) => candidate.runId === childRunId);
78
+ if (exact || !workflowKey || !sessionFile) return exact;
79
+ const candidates = status.steps?.filter((candidate) => candidate.runId === undefined
80
+ && candidate.workflowKey === workflowKey
81
+ && candidate.sessionFile === sessionFile) ?? [];
82
+ return candidates.length === 1 ? candidates[0] : undefined;
83
+ }
84
+
85
+ export function promoteSettledPausedWorkflow(status: AsyncStatus, now = Date.now()): AsyncStatus | undefined {
86
+ if (status.mode !== "workflow" || status.state !== "paused") return undefined;
87
+ const next = cloneWorkflowStatus(status);
88
+ const stillOpen = next.steps?.some((candidate) =>
89
+ candidate.status === "running"
90
+ || (candidate.status === "paused" && candidate.activityState === "needs_attention")
91
+ ) === true;
92
+ if (stillOpen || !next.steps?.length) return undefined;
93
+ const failed = next.steps.some((candidate) => candidate.status === "failed");
94
+ next.lastUpdate = now;
95
+ next.state = "failed";
96
+ next.endedAt = now;
97
+ delete next.activityState;
98
+ if (!failed) next.error = UNSUPPORTED_DETACHED_WORKFLOW_CONTINUATION;
99
+ return withWorkflowChildren(next);
100
+ }
101
+
102
+ export function applyDetachedChildSettlement(
103
+ status: AsyncStatus,
104
+ input: { childRunId: string; result: { exitCode: number | null; error?: string; interrupted?: boolean; sessionFile?: string; sessionName?: string; stopped?: boolean }; workflowKey?: string; now?: number },
105
+ ): AsyncStatus | undefined {
106
+ if (status.mode !== "workflow" || status.state !== "paused") return undefined;
107
+ const next = cloneWorkflowStatus(status);
108
+ const step = findWorkflowSettlementStep(next, input.childRunId, input.workflowKey, input.result.sessionFile);
109
+ if (!step) return undefined;
110
+ step.runId ??= input.childRunId;
111
+ const succeeded = input.result.exitCode === 0 && !input.result.error && !input.result.interrupted;
112
+ const failedSiblingError = next.steps?.find((candidate) => {
113
+ const candidateStep = candidate as WorkflowStatusStep;
114
+ return candidateStep !== step && candidateStep.status === "failed" && !candidateStep.interrupted && candidateStep.error;
115
+ })?.error;
116
+ const interruptedChildError = next.steps?.find((candidate) => {
117
+ const candidateStep = candidate as WorkflowStatusStep;
118
+ return candidateStep.status === "failed" && candidateStep.interrupted && candidateStep.error;
119
+ })?.error;
120
+ const now = input.now ?? Date.now();
121
+ step.status = succeeded ? "completed" : "failed";
122
+ step.endedAt = now;
123
+ delete step.activityState;
124
+ delete step.currentTool;
125
+ delete step.currentToolStartedAt;
126
+ if (input.result.sessionFile) step.sessionFile = input.result.sessionFile;
127
+ if (input.result.sessionName) step.sessionName = input.result.sessionName;
128
+ if (succeeded) {
129
+ delete step.error;
130
+ delete step.interrupted;
131
+ } else if (input.result.interrupted || input.result.stopped) {
132
+ step.error = input.result.error ?? INTERRUPTED_DETACHED_CHILD;
133
+ step.interrupted = true;
134
+ if (input.result.stopped) step.stopped = true;
135
+ } else {
136
+ delete step.interrupted;
137
+ delete step.stopped;
138
+ if (input.result.error) step.error = input.result.error;
139
+ }
140
+ next.lastUpdate = now;
141
+ const promoted = promoteSettledPausedWorkflow(next, now);
142
+ if (promoted?.state === "failed" && failedSiblingError) promoted.error = failedSiblingError;
143
+ else if (promoted?.state === "failed" && (input.result.interrupted || input.result.stopped)) promoted.error = input.result.error ?? INTERRUPTED_DETACHED_CHILD;
144
+ else if (promoted?.state === "failed" && input.result.error) promoted.error = input.result.error;
145
+ else if (promoted?.state === "failed" && interruptedChildError) promoted.error = interruptedChildError;
146
+ return withWorkflowChildren(promoted ?? next);
147
+ }
148
+
149
+ export function classifyWorkflowSettlement(status: AsyncStatus, interrupted = false): WorkflowTerminalResolution | undefined {
150
+ if (status.state !== "complete" && status.state !== "failed") return undefined;
151
+ if (status.steps?.some((step) => {
152
+ const candidate = step as WorkflowStatusStep;
153
+ return candidate.status === "failed" && !candidate.interrupted;
154
+ })) return "failed-child";
155
+ if (interrupted || status.steps?.some((step) => {
156
+ const candidate = step as WorkflowStatusStep;
157
+ return candidate.status === "stopped" || candidate.stopped || candidate.interrupted || candidate.error === INTERRUPTED_DETACHED_CHILD;
158
+ })) return "interrupted-child";
159
+ return "settled-awaiting-resume";
160
+ }
161
+
162
+ export function workflowRecoveryActions(receipt: WorkflowReceipt | undefined): WorkflowRecoveryAction[] {
163
+ if (!receipt) return [];
164
+ return Object.values(receipt.entries).flatMap((entry) => entry.resumability.state === "resumable"
165
+ ? [{ key: entry.key, call: "runs.run" as const, resume: { workflowRunId: receipt.workflowRunId, key: entry.key, latest: true as const }, taskRequired: true as const }]
166
+ : []);
167
+ }
168
+
169
+ export function workflowTerminalOutcomeForResult(result: { timedOut?: boolean; turnBudgetExceeded?: boolean; toolBudgetBlocked?: boolean }): WorkflowTerminalOutcome | undefined {
170
+ if (result.timedOut) return { state: "partial", reason: "timeout" };
171
+ if (result.turnBudgetExceeded || result.toolBudgetBlocked) return { state: "partial", reason: "budget_exhausted" };
172
+ return undefined;
173
+ }
174
+
175
+ export function workflowOutputPathMappingSummary(children: readonly unknown[]): string {
176
+ const mappings = children.flatMap((entry) => {
177
+ if (!entry || typeof entry !== "object" || Array.isArray(entry)) return [];
178
+ const child = entry as Partial<WorkflowPublicChild>;
179
+ return child.outputPathMapping
180
+ ? [`'${child.workflowKey ?? "child"}': requested ${child.outputPathMapping.requestedPath} -> saved ${child.outputPathMapping.savedPath}`]
181
+ : [];
182
+ });
183
+ return mappings.length > 0 ? ` Output path mappings: ${mappings.join("; ")}.` : "";
184
+ }
185
+
186
+ export function planWorkflowSettlement(input: {
187
+ status: AsyncStatus;
188
+ summary: string;
189
+ children: WorkflowPublicChild[];
190
+ baseResult: Record<string, unknown>;
191
+ receipt?: WorkflowReceipt;
192
+ receiptPath?: string;
193
+ receiptPersistenceError?: string;
194
+ resolution?: WorkflowTerminalResolution;
195
+ terminalOutcome?: WorkflowTerminalOutcome;
196
+ now?: number;
197
+ eventMetadata?: Record<string, unknown>;
198
+ }): WorkflowSettlementPlan {
199
+ const now = input.now ?? Date.now();
200
+ let status = withWorkflowChildren(cloneWorkflowStatus(input.status));
201
+ let summary = input.summary;
202
+ let receipt = input.receipt;
203
+ let resolution = input.resolution;
204
+ if (input.receiptPersistenceError) {
205
+ const diagnostic = `${EVIDENCE_PERSISTENCE_FAILED}: ${input.receiptPersistenceError}`;
206
+ status = withWorkflowChildren({ ...status, state: "failed", error: diagnostic, activityState: undefined, endedAt: now, lastUpdate: now });
207
+ summary = `${diagnostic} Available child evidence was preserved, but workflow completion was not accepted.`;
208
+ receipt = undefined;
209
+ if (!resolution) resolution = classifyWorkflowSettlement(status);
210
+ }
211
+ const recovery = workflowRecoveryActions(receipt);
212
+ if (receipt) {
213
+ receipt = {
214
+ ...receipt,
215
+ state: status.state === "complete" ? "complete" : status.state === "paused" ? "paused" : status.state === "stopped" ? "stopped" : "failed",
216
+ workflowChildren: status.workflowChildren,
217
+ ...(resolution ? { workflowResolution: resolution, recovery } : {}),
218
+ };
219
+ }
220
+ const publicResult: Record<string, unknown> = {
221
+ ...input.baseResult,
222
+ success: status.state === "complete",
223
+ state: status.state,
224
+ summary,
225
+ error: status.state === "complete" ? undefined : status.error ?? summary,
226
+ stopped: status.stopped ? true : undefined,
227
+ activityState: status.activityState,
228
+ workflowChildren: status.workflowChildren,
229
+ results: input.children,
230
+ ...(input.terminalOutcome ? { terminalOutcome: input.terminalOutcome } : {}),
231
+ ...(resolution ? { workflowResolution: resolution, recovery } : {}),
232
+ ...(receipt && input.receiptPath ? { workflowReceipt: { path: input.receiptPath, receipt } } : {}),
233
+ timestamp: now,
234
+ };
235
+ const terminal = status.state === "complete" || status.state === "failed" || status.state === "partial" || status.state === "stopped";
236
+ const completionEvent = terminal ? {
237
+ type: "subagent.workflow.completed",
238
+ state: status.state,
239
+ ...(resolution ? { workflowResolution: resolution } : {}),
240
+ ...(input.terminalOutcome ? { terminalOutcome: input.terminalOutcome } : {}),
241
+ ...(status.error ? { error: status.error } : {}),
242
+ ...(status.activityState ? { activityState: status.activityState } : {}),
243
+ ...input.eventMetadata,
244
+ } : undefined;
245
+ return { status, publicResult, receipt, recovery, completionEvent };
246
+ }
@@ -36,7 +36,7 @@ const ISOLATED_ENV_KEYS = [
36
36
  "SELESAI_SUBAGENT_MAX_DEPTH",
37
37
  "SELESAI_SUBAGENT_EXTRA_AGENT_DIRS",
38
38
  "SELESAI_SUBAGENT_PARENT_SESSION",
39
- "SELESAI_SUBAGENT_PI_BINARY",
39
+ "SELESAI_SUBAGENT_SELESAI_BINARY",
40
40
  "SELESAI_SUBAGENTS_CODING_AGENT_PACKAGE_ROOT",
41
41
  ] as const;
42
42
 
@@ -186,7 +186,7 @@ Report active tools.`;
186
186
  process.env.SELESAI_SUBAGENT_MAX_DEPTH = "1";
187
187
  process.env.SELESAI_SUBAGENT_EXTRA_AGENT_DIRS = BOGUS_EXTRA_DIRS;
188
188
  process.env.SELESAI_SUBAGENT_PARENT_SESSION = "polluted-parent";
189
- process.env.SELESAI_SUBAGENT_PI_BINARY = BOGUS_PI_BINARY;
189
+ process.env.SELESAI_SUBAGENT_SELESAI_BINARY = BOGUS_PI_BINARY;
190
190
  process.env.SELESAI_SUBAGENTS_CODING_AGENT_PACKAGE_ROOT = BOGUS_PI_PACKAGE_ROOT;
191
191
 
192
192
  try {