@selesai/code 0.13.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 (461) hide show
  1. package/CHANGELOG.md +12 -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/extensions/ascii.txt +9 -0
  9. package/dist/extensions/package.json +1 -2
  10. package/dist/extensions/pi-hermes-memory/CHANGELOG.md +371 -0
  11. package/dist/extensions/pi-hermes-memory/LICENSE +21 -0
  12. package/dist/extensions/pi-hermes-memory/README.md +643 -0
  13. package/dist/extensions/pi-hermes-memory/docs/0.1/TASKS.md +197 -0
  14. package/dist/extensions/pi-hermes-memory/docs/0.2/PLAN.md +290 -0
  15. package/dist/extensions/pi-hermes-memory/docs/0.2/TASKS.md +134 -0
  16. package/dist/extensions/pi-hermes-memory/docs/0.2/TEST-PLAN.md +216 -0
  17. package/dist/extensions/pi-hermes-memory/docs/0.3/PLAN.md +330 -0
  18. package/dist/extensions/pi-hermes-memory/docs/0.3/TASKS.md +125 -0
  19. package/dist/extensions/pi-hermes-memory/docs/0.4/PLAN.md +160 -0
  20. package/dist/extensions/pi-hermes-memory/docs/0.4/TASKS.md +113 -0
  21. package/dist/extensions/pi-hermes-memory/docs/0.6/CHANGELOG.md +57 -0
  22. package/dist/extensions/pi-hermes-memory/docs/0.6/PLAN.md +202 -0
  23. package/dist/extensions/pi-hermes-memory/docs/0.6/TASKS.md +144 -0
  24. package/dist/extensions/pi-hermes-memory/docs/0.7/PLAN.md +349 -0
  25. package/dist/extensions/pi-hermes-memory/docs/0.7/TASKS.md +110 -0
  26. package/dist/extensions/pi-hermes-memory/docs/PUBLISHING.md +149 -0
  27. package/dist/extensions/pi-hermes-memory/docs/ROADMAP.md +468 -0
  28. package/dist/extensions/pi-hermes-memory/docs/images/memory-architecture.svg +1 -0
  29. package/dist/extensions/pi-hermes-memory/docs/images/pi-logo.svg +22 -0
  30. package/dist/extensions/pi-hermes-memory/docs/images/pi_memory.png +0 -0
  31. package/dist/extensions/pi-hermes-memory/docs/images/pi_memory.svg +19 -0
  32. package/dist/extensions/pi-hermes-memory/docs/images/security-flow.svg +1 -0
  33. package/dist/extensions/pi-hermes-memory/docs/images/session-lifecycle.svg +1 -0
  34. package/dist/extensions/pi-hermes-memory/docs/images/source-architecture.svg +1 -0
  35. package/dist/extensions/pi-hermes-memory/docs/mermaid/memory-architecture.mmd +49 -0
  36. package/dist/extensions/pi-hermes-memory/docs/mermaid/security-flow.mmd +40 -0
  37. package/dist/extensions/pi-hermes-memory/docs/mermaid/session-lifecycle.mmd +56 -0
  38. package/dist/extensions/pi-hermes-memory/docs/mermaid/source-architecture.mmd +54 -0
  39. package/dist/extensions/pi-hermes-memory/package-lock.json +3799 -0
  40. package/dist/extensions/pi-hermes-memory/package.json +66 -0
  41. package/dist/extensions/pi-hermes-memory/scripts/check-min-sdk.mjs +99 -0
  42. package/dist/extensions/pi-hermes-memory/scripts/ensure-dev.mjs +57 -0
  43. package/dist/extensions/pi-hermes-memory/src/auto-consolidation-warning.ts +6 -0
  44. package/dist/extensions/pi-hermes-memory/src/config.ts +178 -0
  45. package/dist/extensions/pi-hermes-memory/src/constants.ts +424 -0
  46. package/dist/extensions/pi-hermes-memory/src/extension-root-migration.ts +639 -0
  47. package/dist/extensions/pi-hermes-memory/src/handlers/auto-consolidate.ts +401 -0
  48. package/dist/extensions/pi-hermes-memory/src/handlers/background-review.ts +297 -0
  49. package/dist/extensions/pi-hermes-memory/src/handlers/child-process-watchdog.mjs +90 -0
  50. package/dist/extensions/pi-hermes-memory/src/handlers/correction-detector.ts +300 -0
  51. package/dist/extensions/pi-hermes-memory/src/handlers/index-sessions.ts +86 -0
  52. package/dist/extensions/pi-hermes-memory/src/handlers/insights.ts +78 -0
  53. package/dist/extensions/pi-hermes-memory/src/handlers/interview.ts +37 -0
  54. package/dist/extensions/pi-hermes-memory/src/handlers/learn-memory.ts +186 -0
  55. package/dist/extensions/pi-hermes-memory/src/handlers/message-parts.ts +27 -0
  56. package/dist/extensions/pi-hermes-memory/src/handlers/pi-child-process.ts +485 -0
  57. package/dist/extensions/pi-hermes-memory/src/handlers/preview-context.ts +105 -0
  58. package/dist/extensions/pi-hermes-memory/src/handlers/review-memory-ops.ts +506 -0
  59. package/dist/extensions/pi-hermes-memory/src/handlers/session-backfill.ts +145 -0
  60. package/dist/extensions/pi-hermes-memory/src/handlers/session-flush.ts +151 -0
  61. package/dist/extensions/pi-hermes-memory/src/handlers/session-live-index.ts +94 -0
  62. package/dist/extensions/pi-hermes-memory/src/handlers/skills-command.ts +1334 -0
  63. package/dist/extensions/pi-hermes-memory/src/handlers/standing-pin.ts +111 -0
  64. package/dist/extensions/pi-hermes-memory/src/handlers/switch-project.ts +75 -0
  65. package/dist/extensions/pi-hermes-memory/src/handlers/sync-markdown-memories.ts +266 -0
  66. package/dist/extensions/pi-hermes-memory/src/index.ts +378 -0
  67. package/dist/extensions/pi-hermes-memory/src/lifecycle-timing.ts +54 -0
  68. package/dist/extensions/pi-hermes-memory/src/paths.ts +61 -0
  69. package/dist/extensions/pi-hermes-memory/src/project-context.ts +13 -0
  70. package/dist/extensions/pi-hermes-memory/src/project-memory-migration.ts +93 -0
  71. package/dist/extensions/pi-hermes-memory/src/project.ts +157 -0
  72. package/dist/extensions/pi-hermes-memory/src/prompt-context.ts +55 -0
  73. package/dist/extensions/pi-hermes-memory/src/store/atomic-lock-coordinator.ts +375 -0
  74. package/dist/extensions/pi-hermes-memory/src/store/canonical-storage-path.ts +54 -0
  75. package/dist/extensions/pi-hermes-memory/src/store/content-scanner.ts +101 -0
  76. package/dist/extensions/pi-hermes-memory/src/store/db.ts +1180 -0
  77. package/dist/extensions/pi-hermes-memory/src/store/fts-query.ts +101 -0
  78. package/dist/extensions/pi-hermes-memory/src/store/markdown-mutation-lock.ts +38 -0
  79. package/dist/extensions/pi-hermes-memory/src/store/memory-lookup.ts +24 -0
  80. package/dist/extensions/pi-hermes-memory/src/store/memory-store.ts +1191 -0
  81. package/dist/extensions/pi-hermes-memory/src/store/recovery-maintenance.ts +66 -0
  82. package/dist/extensions/pi-hermes-memory/src/store/schema.ts +118 -0
  83. package/dist/extensions/pi-hermes-memory/src/store/session-anchor-search.ts +472 -0
  84. package/dist/extensions/pi-hermes-memory/src/store/session-indexer.ts +511 -0
  85. package/dist/extensions/pi-hermes-memory/src/store/session-parser.ts +213 -0
  86. package/dist/extensions/pi-hermes-memory/src/store/session-search.ts +234 -0
  87. package/dist/extensions/pi-hermes-memory/src/store/skill-store.ts +950 -0
  88. package/dist/extensions/pi-hermes-memory/src/store/skill-utils.ts +133 -0
  89. package/dist/extensions/pi-hermes-memory/src/store/sqlite-memory-store.ts +1002 -0
  90. package/dist/extensions/pi-hermes-memory/src/store/sqlite-native.ts +241 -0
  91. package/dist/extensions/pi-hermes-memory/src/store/standing-instructions.ts +247 -0
  92. package/dist/extensions/pi-hermes-memory/src/tools/memory-search-tool.ts +95 -0
  93. package/dist/extensions/pi-hermes-memory/src/tools/memory-tool.ts +469 -0
  94. package/dist/extensions/pi-hermes-memory/src/tools/session-search-tool.ts +246 -0
  95. package/dist/extensions/pi-hermes-memory/src/tools/shared-output-view.ts +170 -0
  96. package/dist/extensions/pi-hermes-memory/src/tools/skill-tool.ts +333 -0
  97. package/dist/extensions/pi-hermes-memory/src/tools/tool-result-views.ts +105 -0
  98. package/dist/extensions/pi-hermes-memory/src/types.ts +214 -0
  99. package/dist/extensions/pi-hermes-memory/tests/config.test.ts +434 -0
  100. package/dist/extensions/pi-hermes-memory/tests/extension-root-migration.test.ts +368 -0
  101. package/dist/extensions/pi-hermes-memory/tests/handlers/auto-consolidate.test.ts +984 -0
  102. package/dist/extensions/pi-hermes-memory/tests/handlers/background-review.test.ts +950 -0
  103. package/dist/extensions/pi-hermes-memory/tests/handlers/correction-detector.test.ts +671 -0
  104. package/dist/extensions/pi-hermes-memory/tests/handlers/insights.test.ts +157 -0
  105. package/dist/extensions/pi-hermes-memory/tests/handlers/interview.test.ts +127 -0
  106. package/dist/extensions/pi-hermes-memory/tests/handlers/pi-child-process.test.ts +905 -0
  107. package/dist/extensions/pi-hermes-memory/tests/handlers/preview-context.test.ts +152 -0
  108. package/dist/extensions/pi-hermes-memory/tests/handlers/prompt-context.test.ts +166 -0
  109. package/dist/extensions/pi-hermes-memory/tests/handlers/resources-discover.test.ts +71 -0
  110. package/dist/extensions/pi-hermes-memory/tests/handlers/review-memory-ops.test.ts +614 -0
  111. package/dist/extensions/pi-hermes-memory/tests/handlers/session-backfill.test.ts +192 -0
  112. package/dist/extensions/pi-hermes-memory/tests/handlers/session-flush.test.ts +677 -0
  113. package/dist/extensions/pi-hermes-memory/tests/handlers/session-live-index.test.ts +225 -0
  114. package/dist/extensions/pi-hermes-memory/tests/handlers/skills-command.test.ts +827 -0
  115. package/dist/extensions/pi-hermes-memory/tests/handlers/standing-pin.test.ts +134 -0
  116. package/dist/extensions/pi-hermes-memory/tests/handlers/sync-markdown-memories.test.ts +491 -0
  117. package/dist/extensions/pi-hermes-memory/tests/handlers/system-prompt.test.ts +199 -0
  118. package/dist/extensions/pi-hermes-memory/tests/handlers/thinking-max.test.ts +64 -0
  119. package/dist/extensions/pi-hermes-memory/tests/index.test.ts +30 -0
  120. package/dist/extensions/pi-hermes-memory/tests/integration/flow.test.ts +126 -0
  121. package/dist/extensions/pi-hermes-memory/tests/lifecycle-timing.test.ts +76 -0
  122. package/dist/extensions/pi-hermes-memory/tests/paths.test.ts +23 -0
  123. package/dist/extensions/pi-hermes-memory/tests/project-memory-migration.test.ts +80 -0
  124. package/dist/extensions/pi-hermes-memory/tests/project-rebinding.test.ts +115 -0
  125. package/dist/extensions/pi-hermes-memory/tests/project.test.ts +120 -0
  126. package/dist/extensions/pi-hermes-memory/tests/run-all-timeout.test.ts +107 -0
  127. package/dist/extensions/pi-hermes-memory/tests/run-all.sh +45 -0
  128. package/dist/extensions/pi-hermes-memory/tests/store/atomic-lock-coordinator.test.ts +381 -0
  129. package/dist/extensions/pi-hermes-memory/tests/store/content-scanner.test.ts +388 -0
  130. package/dist/extensions/pi-hermes-memory/tests/store/db.test.ts +1046 -0
  131. package/dist/extensions/pi-hermes-memory/tests/store/markdown-mutation-lock.test.ts +34 -0
  132. package/dist/extensions/pi-hermes-memory/tests/store/memory-store.test.ts +2150 -0
  133. package/dist/extensions/pi-hermes-memory/tests/store/recovery-maintenance.test.ts +170 -0
  134. package/dist/extensions/pi-hermes-memory/tests/store/session-anchor-search.test.ts +210 -0
  135. package/dist/extensions/pi-hermes-memory/tests/store/session-indexer.test.ts +643 -0
  136. package/dist/extensions/pi-hermes-memory/tests/store/session-parser.test.ts +282 -0
  137. package/dist/extensions/pi-hermes-memory/tests/store/session-search.test.ts +287 -0
  138. package/dist/extensions/pi-hermes-memory/tests/store/skill-store.test.ts +678 -0
  139. package/dist/extensions/pi-hermes-memory/tests/store/skill-utils.test.ts +21 -0
  140. package/dist/extensions/pi-hermes-memory/tests/store/sqlite-lazy-load.test.ts +78 -0
  141. package/dist/extensions/pi-hermes-memory/tests/store/sqlite-memory-store.test.ts +543 -0
  142. package/dist/extensions/pi-hermes-memory/tests/store/sqlite-native.test.ts +164 -0
  143. package/dist/extensions/pi-hermes-memory/tests/store/standing-instructions.test.ts +171 -0
  144. package/dist/extensions/pi-hermes-memory/tests/tools/memory-search-tool.test.ts +84 -0
  145. package/dist/extensions/pi-hermes-memory/tests/tools/memory-tool.test.ts +709 -0
  146. package/dist/extensions/pi-hermes-memory/tests/tools/session-search-tool.test.ts +345 -0
  147. package/dist/extensions/pi-hermes-memory/tests/tools/shared-output-view.test.ts +362 -0
  148. package/dist/extensions/pi-hermes-memory/tests/tools/skill-tool.test.ts +478 -0
  149. package/dist/extensions/pi-hermes-memory/tests/tools/tool-result-renderer-wiring.test.ts +40 -0
  150. package/dist/extensions/pi-hermes-memory/tsconfig.json +19 -0
  151. package/dist/extensions/pi-subagents/.oxlintrc.json +43 -0
  152. package/dist/extensions/pi-subagents/AGENTS.md +5 -0
  153. package/dist/extensions/pi-subagents/CHANGELOG.md +157 -0
  154. package/dist/extensions/pi-subagents/README.md +12 -6
  155. package/dist/extensions/pi-subagents/VISION.md +81 -0
  156. package/dist/extensions/pi-subagents/agents/claude-code-writer.md +15 -0
  157. package/dist/extensions/pi-subagents/agents/claude-code.md +15 -0
  158. package/dist/extensions/pi-subagents/agents/codex-exec-writer.md +15 -0
  159. package/dist/extensions/pi-subagents/agents/codex-exec.md +15 -0
  160. package/dist/extensions/pi-subagents/agents/cursor-agent-writer.md +14 -0
  161. package/dist/extensions/pi-subagents/agents/cursor-agent.md +14 -0
  162. package/dist/extensions/pi-subagents/agents/reviewer.md +1 -3
  163. package/dist/extensions/pi-subagents/docs/agents.md +488 -0
  164. package/dist/extensions/pi-subagents/docs/configuration.md +46 -9
  165. package/dist/extensions/pi-subagents/docs/extension-api.md +42 -3
  166. package/dist/extensions/pi-subagents/docs/models.md +27 -4
  167. package/dist/extensions/pi-subagents/docs/observability.md +10 -9
  168. package/dist/extensions/pi-subagents/docs/tool-reference.md +102 -6
  169. package/dist/extensions/pi-subagents/docs/workflows.md +135 -0
  170. package/dist/extensions/pi-subagents/package-lock.json +389 -2
  171. package/dist/extensions/pi-subagents/package.json +3 -1
  172. package/dist/extensions/pi-subagents/prompts/review-loop.md +2 -2
  173. package/dist/extensions/pi-subagents/skills/council-mode/SKILL.md +1 -1
  174. package/dist/extensions/pi-subagents/skills/pi-subagents/SKILL.md +3 -1
  175. package/dist/extensions/pi-subagents/skills/pi-subagents/references/execution-controls.md +7 -3
  176. package/dist/extensions/pi-subagents/skills/pi-subagents/references/management-authoring-rpc.md +2 -2
  177. package/dist/extensions/pi-subagents/skills/pi-subagents/references/prompting-and-roles.md +26 -5
  178. package/dist/extensions/pi-subagents/src/agents/agent-management.ts +41 -22
  179. package/dist/extensions/pi-subagents/src/agents/agent-refinements.ts +4 -4
  180. package/dist/extensions/pi-subagents/src/agents/agent-serializer.ts +9 -2
  181. package/dist/extensions/pi-subagents/src/agents/agents.ts +322 -75
  182. package/dist/extensions/pi-subagents/src/agents/builtin-names.ts +6 -0
  183. package/dist/extensions/pi-subagents/src/agents/runtime-agent-events.ts +70 -0
  184. package/dist/extensions/pi-subagents/src/agents/runtime-agent-registry.ts +27 -20
  185. package/dist/extensions/pi-subagents/src/api/agents.ts +10 -5
  186. package/dist/extensions/pi-subagents/src/api/background-work.ts +5 -1
  187. package/dist/extensions/pi-subagents/src/api/delegation.ts +0 -7
  188. package/dist/extensions/pi-subagents/src/api/preflight.ts +34 -12
  189. package/dist/extensions/pi-subagents/src/extension/config.ts +70 -0
  190. package/dist/extensions/pi-subagents/src/extension/doctor.ts +3 -3
  191. package/dist/extensions/pi-subagents/src/extension/fanout-child.ts +5 -3
  192. package/dist/extensions/pi-subagents/src/extension/index.ts +83 -30
  193. package/dist/extensions/pi-subagents/src/extension/public-execution.ts +44 -15
  194. package/dist/extensions/pi-subagents/src/extension/rpc.ts +55 -19
  195. package/dist/extensions/pi-subagents/src/extension/schemas.ts +41 -15
  196. package/dist/extensions/pi-subagents/src/extension/tool-description.ts +28 -31
  197. package/dist/extensions/pi-subagents/src/inspectors/herdr/actions.ts +2 -1
  198. package/dist/extensions/pi-subagents/src/inspectors/herdr/inspector-runner.ts +2 -10
  199. package/dist/extensions/pi-subagents/src/inspectors/herdr/session-roots-codec.ts +42 -0
  200. package/dist/extensions/pi-subagents/src/integrations/herdr-status.ts +51 -3
  201. package/dist/extensions/pi-subagents/src/intercom/result-intercom.ts +2 -0
  202. package/dist/extensions/pi-subagents/src/profiles/profiles.ts +5 -6
  203. package/dist/extensions/pi-subagents/src/runs/background/active-async-capacity.ts +77 -10
  204. package/dist/extensions/pi-subagents/src/runs/background/async-execution.ts +176 -53
  205. package/dist/extensions/pi-subagents/src/runs/background/async-job-tracker.ts +19 -12
  206. package/dist/extensions/pi-subagents/src/runs/background/async-resume.ts +29 -27
  207. package/dist/extensions/pi-subagents/src/runs/background/async-retention.ts +20 -3
  208. package/dist/extensions/pi-subagents/src/runs/background/async-status-snapshot.ts +23 -261
  209. package/dist/extensions/pi-subagents/src/runs/background/async-status.ts +73 -7
  210. package/dist/extensions/pi-subagents/src/runs/background/chain-append.ts +8 -3
  211. package/dist/extensions/pi-subagents/src/runs/background/chain-root-attachment.ts +74 -8
  212. package/dist/extensions/pi-subagents/src/runs/background/fleet-view.ts +37 -21
  213. package/dist/extensions/pi-subagents/src/runs/background/inspect-rpc.ts +8 -8
  214. package/dist/extensions/pi-subagents/src/runs/background/notify.ts +184 -9
  215. package/dist/extensions/pi-subagents/src/runs/background/result-delivery-ownership.ts +45 -0
  216. package/dist/extensions/pi-subagents/src/runs/background/result-files.ts +28 -14
  217. package/dist/extensions/pi-subagents/src/runs/background/result-watcher.ts +38 -15
  218. package/dist/extensions/pi-subagents/src/runs/background/resume-guidance.ts +1 -1
  219. package/dist/extensions/pi-subagents/src/runs/background/retained-children.ts +1 -1
  220. package/dist/extensions/pi-subagents/src/runs/background/run-status.ts +50 -13
  221. package/dist/extensions/pi-subagents/src/runs/background/scheduled-runs.ts +93 -8
  222. package/dist/extensions/pi-subagents/src/runs/background/stale-run-reconciler.ts +10 -4
  223. package/dist/extensions/pi-subagents/src/runs/background/steering.ts +4 -14
  224. package/dist/extensions/pi-subagents/src/runs/background/subagent-runner.ts +549 -377
  225. package/dist/extensions/pi-subagents/src/runs/background/subagent-wait.ts +58 -10
  226. package/dist/extensions/pi-subagents/src/runs/background/terminal-run-index.ts +1 -1
  227. package/dist/extensions/pi-subagents/src/runs/background/wait-completions.ts +26 -1
  228. package/dist/extensions/pi-subagents/src/runs/background/wait-config.ts +23 -9
  229. package/dist/extensions/pi-subagents/src/runs/background/wait-tool.ts +9 -2
  230. package/dist/extensions/pi-subagents/src/runs/foreground/async-steering-action.ts +2 -2
  231. package/dist/extensions/pi-subagents/src/runs/foreground/execution.ts +204 -129
  232. package/dist/extensions/pi-subagents/src/runs/foreground/foreground-control.ts +9 -0
  233. package/dist/extensions/pi-subagents/src/runs/foreground/foreground-history.ts +23 -1
  234. package/dist/extensions/pi-subagents/src/runs/foreground/subagent-executor.ts +773 -311
  235. package/dist/extensions/pi-subagents/src/runs/foreground/workflow-detach-reconcile.ts +140 -115
  236. package/dist/extensions/pi-subagents/src/runs/shared/abort-recovery.ts +119 -0
  237. package/dist/extensions/pi-subagents/src/runs/shared/async-status-projection.ts +463 -0
  238. package/dist/extensions/pi-subagents/src/runs/shared/child-identity.ts +19 -4
  239. package/dist/extensions/pi-subagents/src/runs/shared/child-launch-plan.ts +151 -0
  240. package/dist/extensions/pi-subagents/src/runs/shared/child-protocol.ts +21 -7
  241. package/dist/extensions/pi-subagents/src/runs/shared/claude-code-adapter.ts +129 -0
  242. package/dist/extensions/pi-subagents/src/runs/shared/codex-exec-adapter.ts +129 -0
  243. package/dist/extensions/pi-subagents/src/runs/shared/completion-evidence.ts +89 -0
  244. package/dist/extensions/pi-subagents/src/runs/shared/completion-guard.ts +5 -4
  245. package/dist/extensions/pi-subagents/src/runs/shared/cursor-agent-adapter.ts +114 -0
  246. package/dist/extensions/pi-subagents/src/runs/shared/dynamic-fanout.ts +3 -3
  247. package/dist/extensions/pi-subagents/src/runs/shared/external-cli-contract.ts +167 -0
  248. package/dist/extensions/pi-subagents/src/runs/shared/external-cli-preflight.ts +122 -0
  249. package/dist/extensions/pi-subagents/src/runs/shared/external-cli-runner.ts +348 -55
  250. package/dist/extensions/pi-subagents/src/runs/shared/fast-mode-extension.ts +5 -5
  251. package/dist/extensions/pi-subagents/src/runs/shared/host-step-status.ts +230 -0
  252. package/dist/extensions/pi-subagents/src/runs/shared/lane-metadata.ts +105 -0
  253. package/dist/extensions/pi-subagents/src/runs/shared/launch-cwd.ts +16 -0
  254. package/dist/extensions/pi-subagents/src/runs/shared/long-running-guard.ts +2 -1
  255. package/dist/extensions/pi-subagents/src/runs/shared/mcp-config-sources.ts +422 -0
  256. package/dist/extensions/pi-subagents/src/runs/shared/mcp-direct-tool-allowlist.ts +221 -158
  257. package/dist/extensions/pi-subagents/src/runs/shared/mcp-direct-tool-grant.ts +197 -0
  258. package/dist/extensions/pi-subagents/src/runs/shared/model-exclusions.ts +69 -7
  259. package/dist/extensions/pi-subagents/src/runs/shared/model-fallback.ts +47 -6
  260. package/dist/extensions/pi-subagents/src/runs/shared/mutation-evidence.ts +7 -2
  261. package/dist/extensions/pi-subagents/src/runs/shared/nested-events.ts +9 -3
  262. package/dist/extensions/pi-subagents/src/runs/shared/nested-render.ts +9 -5
  263. package/dist/extensions/pi-subagents/src/runs/shared/parallel-handoff.ts +419 -7
  264. package/dist/extensions/pi-subagents/src/runs/shared/parallel-utils.ts +15 -1
  265. package/dist/extensions/pi-subagents/src/runs/shared/pi-args.ts +81 -8
  266. package/dist/extensions/pi-subagents/src/runs/shared/process-signal.ts +13 -0
  267. package/dist/extensions/pi-subagents/src/runs/shared/run-history.ts +21 -1
  268. package/dist/extensions/pi-subagents/src/runs/shared/single-output.ts +44 -4
  269. package/dist/extensions/pi-subagents/src/runs/shared/subagent-prompt-runtime.ts +98 -13
  270. package/dist/extensions/pi-subagents/src/runs/shared/subagent-startup-retry.ts +1 -1
  271. package/dist/extensions/pi-subagents/src/runs/shared/worktree-cleanup-plan.ts +847 -0
  272. package/dist/extensions/pi-subagents/src/runs/shared/worktree.ts +18 -0
  273. package/dist/extensions/pi-subagents/src/shared/child-session-name.ts +46 -0
  274. package/dist/extensions/pi-subagents/src/shared/extension-context.ts +24 -0
  275. package/dist/extensions/pi-subagents/src/shared/fork-context.ts +21 -0
  276. package/dist/extensions/pi-subagents/src/shared/formatters.ts +18 -3
  277. package/dist/extensions/pi-subagents/src/shared/launch-contract.ts +5 -1
  278. package/dist/extensions/pi-subagents/src/shared/pruned-fork.ts +450 -0
  279. package/dist/extensions/pi-subagents/src/shared/session-file-trust.ts +19 -0
  280. package/dist/extensions/pi-subagents/src/shared/session-tokens.ts +14 -3
  281. package/dist/extensions/pi-subagents/src/shared/settings.ts +19 -105
  282. package/dist/extensions/pi-subagents/src/shared/shortcuts.ts +17 -0
  283. package/dist/extensions/pi-subagents/src/shared/types.ts +359 -40
  284. package/dist/extensions/pi-subagents/src/shared/utils.ts +41 -84
  285. package/dist/extensions/pi-subagents/src/shared/workflow-child-permit.ts +116 -0
  286. package/dist/extensions/pi-subagents/src/slash/delegation-adapters.ts +1 -9
  287. package/dist/extensions/pi-subagents/src/slash/delegation-request.ts +0 -4
  288. package/dist/extensions/pi-subagents/src/slash/slash-bridge.ts +1 -2
  289. package/dist/extensions/pi-subagents/src/slash/slash-commands.ts +378 -88
  290. package/dist/extensions/pi-subagents/src/slash/slash-live-state.ts +22 -11
  291. package/dist/extensions/pi-subagents/src/slash/subagents-admin.ts +3 -0
  292. package/dist/extensions/pi-subagents/src/tui/fleet-status.ts +147 -79
  293. package/dist/extensions/pi-subagents/src/tui/fleet-transcript.ts +11 -5
  294. package/dist/extensions/pi-subagents/src/tui/fleet.ts +39 -18
  295. package/dist/extensions/pi-subagents/src/tui/render.ts +369 -46
  296. package/dist/extensions/pi-subagents/src/watchdog/turn-delta.ts +1 -1
  297. package/dist/extensions/pi-subagents/src/workflows/chat-progress.ts +6 -3
  298. package/dist/extensions/pi-subagents/src/workflows/host-command.ts +230 -0
  299. package/dist/extensions/pi-subagents/src/workflows/scripted-workflow.ts +740 -58
  300. package/dist/extensions/pi-subagents/src/workflows/workflow-child-summary.ts +121 -0
  301. package/dist/extensions/pi-subagents/src/workflows/workflow-preflight.ts +270 -0
  302. package/dist/extensions/pi-subagents/src/workflows/workflow-receipt.ts +195 -6
  303. package/dist/extensions/pi-subagents/src/workflows/workflow-settlement.ts +246 -0
  304. package/dist/extensions/pi-subagents/test/e2e/real-session-subagent.test.ts +2 -2
  305. package/dist/extensions/pi-subagents/test/integration/async-execution.test.ts +1114 -260
  306. package/dist/extensions/pi-subagents/test/integration/async-job-tracker.test.ts +34 -0
  307. package/dist/extensions/pi-subagents/test/integration/async-status.test.ts +150 -3
  308. package/dist/extensions/pi-subagents/test/integration/claude-code-smoke.test.ts +52 -0
  309. package/dist/extensions/pi-subagents/test/integration/claude-code-writer-smoke.test.ts +55 -0
  310. package/dist/extensions/pi-subagents/test/integration/codex-exec-smoke.test.ts +53 -0
  311. package/dist/extensions/pi-subagents/test/integration/codex-exec-writer-smoke.test.ts +57 -0
  312. package/dist/extensions/pi-subagents/test/integration/cursor-agent-smoke.test.ts +59 -0
  313. package/dist/extensions/pi-subagents/test/integration/cursor-agent-writer-smoke.test.ts +62 -0
  314. package/dist/extensions/pi-subagents/test/integration/error-handling.test.ts +57 -1
  315. package/dist/extensions/pi-subagents/test/integration/external-cli-runner.test.ts +1 -0
  316. package/dist/extensions/pi-subagents/test/integration/fork-context-execution.test.ts +20 -0
  317. package/dist/extensions/pi-subagents/test/integration/intercom-result-delivery.test.ts +4 -3
  318. package/dist/extensions/pi-subagents/test/integration/orca-progress-tabs.test.ts +2 -2
  319. package/dist/extensions/pi-subagents/test/integration/render-fork-badge.test.ts +54 -4
  320. package/dist/extensions/pi-subagents/test/integration/render-widget.test.ts +482 -4
  321. package/dist/extensions/pi-subagents/test/integration/result-watcher.test.ts +157 -9
  322. package/dist/extensions/pi-subagents/test/integration/session-tokens.test.ts +5 -5
  323. package/dist/extensions/pi-subagents/test/integration/single-execution.test.ts +1508 -291
  324. package/dist/extensions/pi-subagents/test/integration/slash-commands.test.ts +423 -1
  325. package/dist/extensions/pi-subagents/test/integration/template-resolution.test.ts +8 -0
  326. package/dist/extensions/pi-subagents/test/support/cursor-smoke-inputs.ts +51 -0
  327. package/dist/extensions/pi-subagents/test/support/helpers.ts +2 -0
  328. package/dist/extensions/pi-subagents/test/support/mock-pi.ts +5 -5
  329. package/dist/extensions/pi-subagents/test/support/real-session-runner.ts +6 -6
  330. package/dist/extensions/pi-subagents/test/unit/abort-recovery.test.ts +152 -0
  331. package/dist/extensions/pi-subagents/test/unit/active-async-capacity.test.ts +48 -0
  332. package/dist/extensions/pi-subagents/test/unit/agent-frontmatter.test.ts +184 -6
  333. package/dist/extensions/pi-subagents/test/unit/agent-management.test.ts +66 -6
  334. package/dist/extensions/pi-subagents/test/unit/agent-overrides.test.ts +77 -0
  335. package/dist/extensions/pi-subagents/test/unit/agent-refinements.test.ts +18 -0
  336. package/dist/extensions/pi-subagents/test/unit/anti-slop-oxlint.test.ts +86 -0
  337. package/dist/extensions/pi-subagents/test/unit/artifacts.test.ts +1 -1
  338. package/dist/extensions/pi-subagents/test/unit/async-execution.test.ts +101 -1
  339. package/dist/extensions/pi-subagents/test/unit/async-interrupt-action.test.ts +121 -0
  340. package/dist/extensions/pi-subagents/test/unit/async-recovery-descriptor.test.ts +28 -0
  341. package/dist/extensions/pi-subagents/test/unit/async-resume.test.ts +14 -7
  342. package/dist/extensions/pi-subagents/test/unit/async-retention.test.ts +53 -0
  343. package/dist/extensions/pi-subagents/test/unit/async-status-projection.test.ts +190 -0
  344. package/dist/extensions/pi-subagents/test/unit/async-status-snapshot.test.ts +42 -0
  345. package/dist/extensions/pi-subagents/test/unit/auto-drain.test.ts +8 -4
  346. package/dist/extensions/pi-subagents/test/unit/background-work.test.ts +23 -0
  347. package/dist/extensions/pi-subagents/test/unit/chain-root-attachment.test.ts +78 -1
  348. package/dist/extensions/pi-subagents/test/unit/child-launch-plan.test.ts +94 -0
  349. package/dist/extensions/pi-subagents/test/unit/child-protocol.test.ts +21 -0
  350. package/dist/extensions/pi-subagents/test/unit/child-session-name.test.ts +50 -0
  351. package/dist/extensions/pi-subagents/test/unit/claude-code-adapter.test.ts +245 -0
  352. package/dist/extensions/pi-subagents/test/unit/codex-exec-adapter.test.ts +192 -0
  353. package/dist/extensions/pi-subagents/test/unit/compaction-resume.test.ts +27 -0
  354. package/dist/extensions/pi-subagents/test/unit/completion-evidence.test.ts +150 -0
  355. package/dist/extensions/pi-subagents/test/unit/completion-guard.test.ts +21 -0
  356. package/dist/extensions/pi-subagents/test/unit/cursor-agent-adapter.test.ts +259 -0
  357. package/dist/extensions/pi-subagents/test/unit/cursor-smoke-inputs.test.ts +77 -0
  358. package/dist/extensions/pi-subagents/test/unit/delegation-api.test.ts +1 -4
  359. package/dist/extensions/pi-subagents/test/unit/doctor.test.ts +1 -1
  360. package/dist/extensions/pi-subagents/test/unit/extension-context.test.ts +27 -0
  361. package/dist/extensions/pi-subagents/test/unit/external-cli-runner.test.ts +223 -0
  362. package/dist/extensions/pi-subagents/test/unit/fast-mode-extension.test.ts +27 -0
  363. package/dist/extensions/pi-subagents/test/unit/fleet-status.test.ts +208 -2
  364. package/dist/extensions/pi-subagents/test/unit/fleet-transcript.test.ts +29 -1
  365. package/dist/extensions/pi-subagents/test/unit/fleet.test.ts +110 -1
  366. package/dist/extensions/pi-subagents/test/unit/foreground-control.test.ts +10 -0
  367. package/dist/extensions/pi-subagents/test/unit/formatters.test.ts +14 -0
  368. package/dist/extensions/pi-subagents/test/unit/herdr-inspector.test.ts +18 -9
  369. package/dist/extensions/pi-subagents/test/unit/herdr-session-roots-codec.test.ts +63 -0
  370. package/dist/extensions/pi-subagents/test/unit/herdr-shell-command.test.ts +2 -2
  371. package/dist/extensions/pi-subagents/test/unit/herdr-status-bridge.test.ts +132 -0
  372. package/dist/extensions/pi-subagents/test/unit/host-command.test.ts +156 -0
  373. package/dist/extensions/pi-subagents/test/unit/host-step-status.test.ts +98 -0
  374. package/dist/extensions/pi-subagents/test/unit/index-child-registration.test.ts +178 -26
  375. package/dist/extensions/pi-subagents/test/unit/index-segment.test.ts +2 -1
  376. package/dist/extensions/pi-subagents/test/unit/inspect-rpc.test.ts +21 -0
  377. package/dist/extensions/pi-subagents/test/unit/mcp-direct-tool-grant.test.ts +138 -0
  378. package/dist/extensions/pi-subagents/test/unit/mission-lifecycle.test.ts +1 -1
  379. package/dist/extensions/pi-subagents/test/unit/model-exclusions.test.ts +91 -0
  380. package/dist/extensions/pi-subagents/test/unit/model-fallback.test.ts +72 -7
  381. package/dist/extensions/pi-subagents/test/unit/mutation-evidence.test.ts +21 -0
  382. package/dist/extensions/pi-subagents/test/unit/nested-control.test.ts +1 -0
  383. package/dist/extensions/pi-subagents/test/unit/nested-events.test.ts +20 -2
  384. package/dist/extensions/pi-subagents/test/unit/notify.test.ts +129 -1
  385. package/dist/extensions/pi-subagents/test/unit/orca-progress-tabs.test.ts +2 -2
  386. package/dist/extensions/pi-subagents/test/unit/package-manifest.test.ts +4 -0
  387. package/dist/extensions/pi-subagents/test/unit/parallel-handoff.test.ts +358 -0
  388. package/dist/extensions/pi-subagents/test/unit/pi-args.test.ts +574 -21
  389. package/dist/extensions/pi-subagents/test/unit/pi-coding-agent-dir.test.ts +109 -1
  390. package/dist/extensions/pi-subagents/test/unit/preflight.test.ts +71 -1
  391. package/dist/extensions/pi-subagents/test/unit/profiles.test.ts +7 -7
  392. package/dist/extensions/pi-subagents/test/unit/pruned-fork.test.ts +214 -0
  393. package/dist/extensions/pi-subagents/test/unit/public-execution.test.ts +29 -8
  394. package/dist/extensions/pi-subagents/test/unit/render-helpers.test.ts +53 -0
  395. package/dist/extensions/pi-subagents/test/unit/result-delivery-ownership.test.ts +34 -0
  396. package/dist/extensions/pi-subagents/test/unit/result-files.test.ts +101 -3
  397. package/dist/extensions/pi-subagents/test/unit/retained-children.test.ts +1 -0
  398. package/dist/extensions/pi-subagents/test/unit/rpc.test.ts +10 -8
  399. package/dist/extensions/pi-subagents/test/unit/run-fanout-budget.test.ts +71 -1
  400. package/dist/extensions/pi-subagents/test/unit/run-status.test.ts +41 -7
  401. package/dist/extensions/pi-subagents/test/unit/runtime-agent-registration.test.ts +136 -2
  402. package/dist/extensions/pi-subagents/test/unit/scheduled-runs.test.ts +137 -0
  403. package/dist/extensions/pi-subagents/test/unit/schemas.test.ts +33 -12
  404. package/dist/extensions/pi-subagents/test/unit/scripted-workflow.test.ts +504 -1
  405. package/dist/extensions/pi-subagents/test/unit/single-output.test.ts +8 -0
  406. package/dist/extensions/pi-subagents/test/unit/skills-fallback.test.ts +1 -1
  407. package/dist/extensions/pi-subagents/test/unit/stale-run-reconciler.test.ts +12 -1
  408. package/dist/extensions/pi-subagents/test/unit/steering-action.test.ts +3 -5
  409. package/dist/extensions/pi-subagents/test/unit/steering.test.ts +5 -6
  410. package/dist/extensions/pi-subagents/test/unit/subagent-action-recovery.test.ts +0 -8
  411. package/dist/extensions/pi-subagents/test/unit/subagent-guide.test.ts +5 -0
  412. package/dist/extensions/pi-subagents/test/unit/subagent-prompt-runtime.test.ts +157 -2
  413. package/dist/extensions/pi-subagents/test/unit/subagent-startup-retry.test.ts +1 -1
  414. package/dist/extensions/pi-subagents/test/unit/subagent-wait.test.ts +63 -4
  415. package/dist/extensions/pi-subagents/test/unit/tool-description.test.ts +27 -23
  416. package/dist/extensions/pi-subagents/test/unit/wait-completions.test.ts +44 -0
  417. package/dist/extensions/pi-subagents/test/unit/wait-subscriptions.test.ts +1 -0
  418. package/dist/extensions/pi-subagents/test/unit/widget-nested-render.test.ts +7 -5
  419. package/dist/extensions/pi-subagents/test/unit/workflow-chat-progress.test.ts +106 -0
  420. package/dist/extensions/pi-subagents/test/unit/workflow-detach-reconcile.test.ts +484 -9
  421. package/dist/extensions/pi-subagents/test/unit/workflow-launch-params.test.ts +38 -4
  422. package/dist/extensions/pi-subagents/test/unit/workflow-preflight.test.ts +155 -0
  423. package/dist/extensions/pi-subagents/test/unit/workflow-receipt.test.ts +318 -0
  424. package/dist/extensions/pi-subagents/test/unit/workflow-resume-hint.test.ts +365 -0
  425. package/dist/extensions/pi-subagents/test/unit/worktree-cleanup-plan.test.ts +349 -0
  426. package/dist/extensions/pi-subagents/test/unit/worktree.test.ts +21 -0
  427. package/dist/extensions/pi-subagents/test/unit/writer-budget-guidance.test.ts +2 -7
  428. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/effect/index.ts +13 -0
  429. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/effect/rules/no-service-constructor-imports.ts +52 -0
  430. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/index.ts +41 -0
  431. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-chained-type-assertions.ts +77 -0
  432. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-conditional-empty-object-spread.ts +49 -0
  433. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-known-value-widening.ts +247 -0
  434. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-module-mocking.ts +91 -0
  435. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-object-parameters.ts +126 -0
  436. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-reflect-apply.ts +28 -0
  437. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-reflect-get.ts +28 -0
  438. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-runtime-typeof.ts +67 -0
  439. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-shape-in-symbol-names.ts +39 -0
  440. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-unknown-parameters.ts +83 -0
  441. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-unknown-returns.ts +115 -0
  442. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-unknown-type-aliases.ts +72 -0
  443. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-unsafe-dictionary-type.ts +134 -0
  444. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-widen-then-assert.ts +366 -0
  445. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/require-safety-comment-for-type-assertion.ts +62 -0
  446. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/shared/dictionary-types.ts +502 -0
  447. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/shared/lexical-type-parameters.ts +60 -0
  448. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/shared/reflect-method.ts +35 -0
  449. package/dist/extensions/pi-tool-display/tests/capabilities.test.ts +1 -1
  450. package/dist/extensions/pi-tool-display/tests/settings-inspector.test.ts +2 -2
  451. package/dist/extensions/pi-tool-display/tests/tool-overrides-mcp.test.ts +1 -1
  452. package/dist/modes/interactive/interactive-mode.js +7 -7
  453. package/dist/skills/workflow/SKILL.md +65 -50
  454. package/package.json +112 -110
  455. package/dist/extensions/enable-readonly-tools.test.ts +0 -63
  456. package/dist/extensions/enable-readonly-tools.ts +0 -24
  457. package/dist/extensions/model-prompt-injector/config.json +0 -16
  458. package/dist/extensions/model-prompt-injector/index.test.ts +0 -102
  459. package/dist/extensions/model-prompt-injector/index.ts +0 -148
  460. package/dist/extensions/pi-subagents/src/runs/shared/turn-budget.ts +0 -98
  461. package/dist/extensions/pi-subagents/test/unit/turn-budget.test.ts +0 -250
@@ -4,7 +4,7 @@ import os from "node:os";
4
4
  import path from "node:path";
5
5
  import { describe, it } from "node:test";
6
6
  import { Worker } from "node:worker_threads";
7
- import { formatWorkflowJsonPreview, previewSimpleWorkflowRun, runWorkflowScript, WorkflowScriptError } from "../../src/workflows/scripted-workflow.ts";
7
+ import { formatWorkflowJsonPreview, previewSimpleWorkflowRun, runWorkflowScript, validateWorkflowScript, WorkflowScriptError } from "../../src/workflows/scripted-workflow.ts";
8
8
 
9
9
  describe("scripted workflow runtime", () => {
10
10
  it("uses ordinary statement-body return semantics", async () => {
@@ -63,6 +63,139 @@ describe("scripted workflow runtime", () => {
63
63
  );
64
64
  });
65
65
 
66
+ it("validates workflow syntax and portable structure offline", () => {
67
+ const syntax = validateWorkflowScript(["const value = 1;", "return (;"].join("\n"));
68
+ assert.equal(syntax.ok, false);
69
+ assert.equal(syntax.errors[0]?.line, 2);
70
+ assert.ok((syntax.errors[0]?.column ?? 0) > 0);
71
+ assert.doesNotMatch(syntax.errors[0]?.message ?? "", /\(3:\d+\)$/);
72
+
73
+ const structural = validateWorkflowScript([
74
+ `const results = await runs.all([{ key: "bad key", agent: "worker" }, { key: "someChild", agent: "reviewer" }]);`,
75
+ `async function helper() { return "no"; }`,
76
+ `return results.someChild;`,
77
+ ].join("\n"));
78
+ assert.equal(structural.ok, false);
79
+ assert.ok(structural.errors.some((error) => error.message.includes("runs.all item key")));
80
+ assert.ok(structural.errors.some((error) => error.message.includes("nested async functions")));
81
+ assert.ok(structural.errors.some((error) => error.message.includes("ordered array")));
82
+
83
+ const invalidRunKey = validateWorkflowScript(`return runs.run("bad key", { agent: "worker" });`);
84
+ assert.equal(invalidRunKey.ok, false);
85
+ assert.ok(invalidRunKey.errors.some((error) => error.message.includes("runs.run key")));
86
+
87
+ for (const script of [
88
+ `return runs.run("single", { agent: "worker", task: undefined });`,
89
+ `return runs.all([{ key: "same", agent: "worker", task: undefined }]);`,
90
+ ]) {
91
+ const invalidParams = validateWorkflowScript(script);
92
+ assert.equal(invalidParams.ok, false);
93
+ assert.ok(invalidParams.errors.some((error) => error.message.includes("undefined is not JSON-representable")));
94
+ }
95
+ assert.deepEqual(validateWorkflowScript(`return runs.run("single", { agent: "worker", task: undefined, task: "real" });`), { ok: true, errors: [] });
96
+
97
+ assert.deepEqual(validateWorkflowScript(`return runs.all([{ ...{ key: "bad key" }, agent: "worker" }]);`), { ok: true, errors: [] });
98
+ assert.deepEqual(validateWorkflowScript(`return runs.run("same", { agent: selectedAgent });`), { ok: true, errors: [] });
99
+ });
100
+
101
+ it("validates runs.host shape offline without executing it", () => {
102
+ assert.deepEqual(validateWorkflowScript(`return runs.host("tests", { kind: "command", command: "npm test", timeoutMs: 1000, output: "reports/tests.log", role: "ci" });`), { ok: true, errors: [] });
103
+ for (const script of [
104
+ `return runs.host("tests", { command: "npm test", timeoutMs: 1000 });`,
105
+ `return runs.host("tests", { kind: "http", command: "npm test", timeoutMs: 1000 });`,
106
+ `return runs.host("tests", { kind: "command", command: "npm test" });`,
107
+ `return runs.host("tests", { kind: "command", command: "npm test", timeoutMs: 1000, output: "../tests.log" });`,
108
+ ]) assert.equal(validateWorkflowScript(script).ok, false);
109
+ });
110
+
111
+ it("runs an awaited host command through the host boundary", async () => {
112
+ const steps: string[] = [];
113
+ const result = await runWorkflowScript({
114
+ script: `return await runs.host("tests", { kind: "command", command: "npm test", timeoutMs: 1000, role: "ci", provider: "local" });`,
115
+ async host(key, params) {
116
+ assert.equal(params.kind, "command");
117
+ return { key, kind: "command", ok: true, state: "passed", exitCode: 0, stdout: "ok", stderr: "", outputPath: "tests.log", durationMs: 2 };
118
+ },
119
+ onHostStep(step) { steps.push(`${step.state}:${step.role ?? ""}`); },
120
+ async launch(key) { return { key, ok: true, output: "unused", artifactPaths: [] }; },
121
+ async status(key) { return { key, ok: true, output: "unused", artifactPaths: [] }; },
122
+ });
123
+ assert.equal((result.value as { state: string }).state, "passed");
124
+ assert.deepEqual(steps, ["running:ci", "done:ci"]);
125
+ assert.deepEqual(result.trace.map(({ operation, state }) => [operation, state]), [["host", "started"], ["host", "completed"]]);
126
+ });
127
+
128
+ it("fails the workflow when a host command fails", async () => {
129
+ await assert.rejects(runWorkflowScript({
130
+ script: `return await runs.host("tests", { kind: "command", command: "npm test", timeoutMs: 1000 });`,
131
+ async host(key) { return { key, kind: "command", ok: false, state: "failed", exitCode: 2, stdout: "", stderr: "bad", outputPath: "tests.log", durationMs: 2, error: "Command exited with code 2." }; },
132
+ async launch(key) { return { key, ok: true, output: "unused", artifactPaths: [] }; },
133
+ async status(key) { return { key, ok: true, output: "unused", artifactPaths: [] }; },
134
+ }), /Host command 'tests' failed/);
135
+ await assert.rejects(runWorkflowScript({
136
+ script: `return await runs.host("tests", { kind: "command", command: "npm test", timeoutMs: 1000 });`,
137
+ host() { throw new Error("host boundary failed"); },
138
+ async launch(key) { return { key, ok: true, output: "unused", artifactPaths: [] }; },
139
+ async status(key) { return { key, ok: true, output: "unused", artifactPaths: [] }; },
140
+ }), /host boundary failed/);
141
+ });
142
+
143
+ it("rejects an unawaited host command", async () => {
144
+ await assert.rejects(runWorkflowScript({
145
+ script: `runs.host("tests", { kind: "command", command: "npm test", timeoutMs: 1000 }); return "done";`,
146
+ async host(key) { return { key, kind: "command", ok: true, state: "passed", exitCode: 0, stdout: "", stderr: "", outputPath: "tests.log", durationMs: 1 }; },
147
+ async launch(key) { return { key, ok: true, output: "unused", artifactPaths: [] }; },
148
+ async status(key) { return { key, ok: true, output: "unused", artifactPaths: [] }; },
149
+ }), /unawaited runs\.host/);
150
+ });
151
+
152
+ it("bounds host command rows", async () => {
153
+ const calls = Array.from({ length: 33 }, (_, index) => `await runs.host("host-${index}", { kind: "command", command: "true", timeoutMs: 1000 });`).join("\n");
154
+ await assert.rejects(runWorkflowScript({
155
+ script: `${calls}\nreturn "done";`,
156
+ async host(key) { return { key, kind: "command", ok: true, state: "passed", exitCode: 0, stdout: "", stderr: "", outputPath: `${key}.log`, durationMs: 1 }; },
157
+ async launch(key) { return { key, ok: true, output: "unused", artifactPaths: [] }; },
158
+ async status(key) { return { key, ok: true, output: "unused", artifactPaths: [] }; },
159
+ }), /at most 32 runs\.host calls/);
160
+ });
161
+
162
+ it("keeps dynamic workflow keys silent during offline validation", () => {
163
+ const result = validateWorkflowScript([
164
+ `const prefix = "lane";`,
165
+ `const child = await runs.run(prefix + "-writer", { agent: selectedAgent, task: taskText });`,
166
+ `const results = await runs.all(items.map((item) => ({ key: item.key, agent: item.agent, task: item.task })));`,
167
+ `return { child: child.output, outputs: results.map((entry) => entry.output) };`,
168
+ ].join("\n"));
169
+ assert.deepEqual(result, { ok: true, errors: [] });
170
+ });
171
+
172
+ it("allows Array properties that match runs.all child keys", () => {
173
+ const valid = validateWorkflowScript([
174
+ `const mapResults = await runs.all([{ key: "map", agent: "reviewer", task: "Review" }]);`,
175
+ `const lengthResults = await runs.all([{ key: "length", agent: "reviewer", task: "Review" }]);`,
176
+ `return { outputs: mapResults.map((result) => result.output), count: lengthResults.length };`,
177
+ ].join("\n"));
178
+ assert.deepEqual(valid, { ok: true, errors: [] });
179
+
180
+ const keyed = validateWorkflowScript([
181
+ `const results = await runs.all([{ key: "someChild", agent: "reviewer", task: "Review" }]);`,
182
+ `return results.someChild;`,
183
+ ].join("\n"));
184
+ assert.equal(keyed.ok, false);
185
+ assert.ok(keyed.errors.some((error) => error.message.includes("'results.someChild' is keyed access")));
186
+ });
187
+
188
+ it("rejects statically non-JSON workflow boundary values", () => {
189
+ assert.deepEqual(validateWorkflowScript(`return void 0;`), { ok: true, errors: [] });
190
+ assert.deepEqual(validateWorkflowScript(`return undefined;`), { ok: true, errors: [] });
191
+ assert.deepEqual(validateWorkflowScript(`return [void 0, { value: void 0 }];`), { ok: true, errors: [] });
192
+ assert.deepEqual(validateWorkflowScript(`return [undefined, { value: undefined }];`), { ok: true, errors: [] });
193
+ const result = validateWorkflowScript(`emit(void 0); emit(undefined); state.set("void", void 0); state.set("undefined", undefined); return [1, , 2];`);
194
+ assert.equal(result.ok, false);
195
+ assert.equal(result.errors.filter((error) => error.message.includes("undefined is not JSON-representable")).length, 4);
196
+ assert.ok(result.errors.some((error) => error.message.includes("sparse arrays")));
197
+ });
198
+
66
199
  it("previews only simple explicit-return child scripts", () => {
67
200
  assert.deepEqual(previewSimpleWorkflowRun(`return runs.run('main', { agent: 'worker', task: 'Review' });`), { agent: "worker", task: "Review" });
68
201
  assert.deepEqual(previewSimpleWorkflowRun(`return runs.run("main", {"agent":"scout","task":"Scan"})`), { agent: "scout", task: "Scan" });
@@ -265,6 +398,320 @@ describe("scripted workflow runtime", () => {
265
398
  ]);
266
399
  });
267
400
 
401
+ it("caps concurrent workflow child launches across runs.all", async () => {
402
+ let active = 0;
403
+ let maxActive = 0;
404
+ const result = await runWorkflowScript({
405
+ script: `return await runs.all([
406
+ { key: "one", agent: "worker", task: "one" },
407
+ { key: "two", agent: "worker", task: "two" },
408
+ { key: "three", agent: "worker", task: "three" },
409
+ { key: "four", agent: "worker", task: "four" }
410
+ ]);`,
411
+ globalConcurrencyLimit: 2,
412
+ async launch(key) {
413
+ active += 1;
414
+ maxActive = Math.max(maxActive, active);
415
+ await new Promise((resolve) => setTimeout(resolve, 20));
416
+ active -= 1;
417
+ return { key, ok: true, output: key, artifactPaths: [] };
418
+ },
419
+ async status(key) { return { key, ok: true, output: "ok", artifactPaths: [] }; },
420
+ });
421
+
422
+ assert.equal(maxActive, 2);
423
+ assert.deepEqual((result.value as Array<{ key: string }>).map(({ key }) => key), ["one", "two", "three", "four"]);
424
+ });
425
+
426
+ it("runs parallel lane starts and sequential stages with retained resume", async () => {
427
+ const launches: Array<{ key: string; params: Record<string, unknown> }> = [];
428
+ let active = 0;
429
+ let maxActive = 0;
430
+ let betaWriterDone = false;
431
+ let alphaChallengeStartedBeforeBetaWriter = false;
432
+ const result = await runWorkflowScript({
433
+ script: `return await runs.lanes([
434
+ { key: "alpha", stages: [
435
+ { key: "writer", agent: "worker", task: "alpha write" },
436
+ { key: "challenge", resume: "previous", task: "alpha challenge" }
437
+ ] },
438
+ { key: "beta", stages: [
439
+ { key: "writer", agent: "worker", task: "beta write" },
440
+ { key: "review", agent: "reviewer", task: "beta review" }
441
+ ] }
442
+ ]);`,
443
+ timeoutMs: 2_000,
444
+ async launch(key, params) {
445
+ launches.push({ key, params });
446
+ active += 1;
447
+ maxActive = Math.max(maxActive, active);
448
+ await new Promise((resolve) => setTimeout(resolve, key === "beta.writer" ? 50 : 5));
449
+ if (key === "beta.writer") betaWriterDone = true;
450
+ if (key === "alpha.challenge") alphaChallengeStartedBeforeBetaWriter = !betaWriterDone;
451
+ active -= 1;
452
+ return {
453
+ key,
454
+ ok: true,
455
+ runId: "run-" + key,
456
+ output: key + " output",
457
+ outputReference: "/tmp/" + key + ".md",
458
+ artifactPaths: [],
459
+ results: [],
460
+ };
461
+ },
462
+ async status(key) { return { key, ok: true, output: "ok", artifactPaths: [] }; },
463
+ });
464
+
465
+ assert.equal(maxActive, 2, "first stages should be admitted together");
466
+ assert.equal(alphaChallengeStartedBeforeBetaWriter, true, "a settled lane should advance without waiting for slower siblings");
467
+ assert.deepEqual(launches.map(({ key }) => key), ["alpha.writer", "beta.writer", "alpha.challenge", "beta.review"]);
468
+ assert.deepEqual(launches.find(({ key }) => key === "alpha.challenge")?.params, { resume: "run-alpha.writer", task: "alpha challenge" });
469
+ for (const lane of ["alpha", "beta"]) {
470
+ const stageTrace = result.trace.filter((entry) => entry.operation === "run" && entry.key.startsWith(`${lane}.`));
471
+ assert.ok(stageTrace.length > 0);
472
+ assert.equal(stageTrace.every((entry) => entry.generatedLaneKey === lane), true);
473
+ }
474
+ assert.deepEqual(result.value, [
475
+ {
476
+ key: "alpha",
477
+ state: "complete",
478
+ stages: [
479
+ { key: "writer", runId: "run-alpha.writer", ok: true, state: "completed", outputReference: "/tmp/alpha.writer.md" },
480
+ { key: "challenge", runId: "run-alpha.challenge", ok: true, state: "completed", outputReference: "/tmp/alpha.challenge.md" },
481
+ ],
482
+ },
483
+ {
484
+ key: "beta",
485
+ state: "complete",
486
+ stages: [
487
+ { key: "writer", runId: "run-beta.writer", ok: true, state: "completed", outputReference: "/tmp/beta.writer.md" },
488
+ { key: "review", runId: "run-beta.review", ok: true, state: "completed", outputReference: "/tmp/beta.review.md" },
489
+ ],
490
+ },
491
+ ]);
492
+ });
493
+
494
+ it("marks only runs.lanes stages with generated lane provenance", async () => {
495
+ const result = await runWorkflowScript({
496
+ script: `const direct = await runs.run("audit.shadow", { agent: "worker", task: "direct" }); const lanes = await runs.lanes([{ key: "audit", stages: [{ key: "writer", agent: "worker", task: "generated" }] }]); return { direct: direct.output, lanes };`,
497
+ timeoutMs: 2_000,
498
+ async launch(key) { return { key, ok: true, runId: "run-" + key, output: key, artifactPaths: [], results: [] }; },
499
+ async status(key) { return { key, ok: true, output: "ok", artifactPaths: [] }; },
500
+ });
501
+ const directTrace = result.trace.filter((entry) => entry.operation === "run" && entry.key === "audit.shadow");
502
+ const generatedTrace = result.trace.filter((entry) => entry.operation === "run" && entry.key === "audit.writer");
503
+ assert.ok(directTrace.length > 0);
504
+ assert.equal(directTrace.every((entry) => entry.generatedLaneKey === undefined), true);
505
+ assert.ok(generatedTrace.length > 0);
506
+ assert.equal(generatedTrace.every((entry) => entry.generatedLaneKey === "audit"), true);
507
+ });
508
+
509
+ it("keeps a rejected first-stage result local to its lane", async () => {
510
+ const launches: string[] = [];
511
+ const result = await runWorkflowScript({
512
+ script: `return await runs.lanes([
513
+ { key: "broken", stages: [
514
+ { key: "writer", agent: "worker", task: "boundary failure" },
515
+ { key: "review", agent: "reviewer", task: "must be skipped" }
516
+ ] },
517
+ { key: "healthy", stages: [
518
+ { key: "writer", agent: "worker", task: "continue" },
519
+ { key: "review", agent: "reviewer", task: "continue review" }
520
+ ] }
521
+ ]);`,
522
+ timeoutMs: 2_000,
523
+ async launch(key) {
524
+ launches.push(key);
525
+ if (key === "broken.writer") {
526
+ const structuredOutput: Record<string, unknown> = {};
527
+ structuredOutput.self = structuredOutput;
528
+ return { key, ok: true, runId: "broken-run", output: "not persisted", structuredOutput, artifactPaths: [], results: [] };
529
+ }
530
+ return { key, ok: true, runId: "run-" + key, output: "done", artifactPaths: [], results: [] };
531
+ },
532
+ async status(key) { return { key, ok: true, output: "ok", artifactPaths: [] }; },
533
+ });
534
+
535
+ assert.deepEqual(launches, ["broken.writer", "healthy.writer", "healthy.review"]);
536
+ const board = result.value as Array<{ key: string; state: string; failedStage?: string; stages: Array<Record<string, unknown>> }>;
537
+ assert.equal(board.length, 2);
538
+ assert.deepEqual(board[0], {
539
+ key: "broken",
540
+ state: "blocked",
541
+ failedStage: "writer",
542
+ stages: [
543
+ { key: "writer", ok: false, state: "failed", error: board[0].stages[0].error },
544
+ { key: "review", state: "skipped" },
545
+ ],
546
+ });
547
+ assert.match(String(board[0].stages[0].error), /must contain only JSON data/);
548
+ assert.deepEqual(board[1], {
549
+ key: "healthy",
550
+ state: "complete",
551
+ stages: [
552
+ { key: "writer", runId: "run-healthy.writer", ok: true, state: "completed" },
553
+ { key: "review", runId: "run-healthy.review", ok: true, state: "completed" },
554
+ ],
555
+ });
556
+ assert.doesNotThrow(() => JSON.stringify(result));
557
+ assert.equal(result.children.find((child) => child.key === "broken.writer")?.ok, false);
558
+ assert.equal(result.children.find((child) => child.key === "broken.writer")?.structuredOutput, undefined);
559
+ assert.equal(result.children.find((child) => child.key === "healthy.writer")?.ok, true);
560
+ });
561
+
562
+ it("blocks one lane on an explicit structured verdict while siblings continue", async () => {
563
+ const launches: string[] = [];
564
+ const result = await runWorkflowScript({
565
+ script: `return await runs.lanes([
566
+ { key: "blocked", stages: [
567
+ { key: "review", agent: "reviewer", task: "block this lane" },
568
+ { key: "followup", agent: "worker", task: "must not run" }
569
+ ] },
570
+ { key: "healthy", stages: [
571
+ { key: "review", agent: "reviewer", task: "continue this lane" },
572
+ { key: "followup", agent: "worker", task: "continue followup" }
573
+ ] }
574
+ ]);`,
575
+ timeoutMs: 2_000,
576
+ async launch(key) {
577
+ launches.push(key);
578
+ return {
579
+ key,
580
+ ok: true,
581
+ runId: "run-" + key,
582
+ output: "done",
583
+ structuredOutput: key === "blocked.review" ? { verdict: "blocked" } : { verdict: "ready" },
584
+ artifactPaths: [],
585
+ results: [],
586
+ };
587
+ },
588
+ async status(key) { return { key, ok: true, output: "ok", artifactPaths: [] }; },
589
+ });
590
+
591
+ assert.deepEqual(launches, ["blocked.review", "healthy.review", "healthy.followup"]);
592
+ assert.deepEqual(result.value, [
593
+ {
594
+ key: "blocked",
595
+ state: "blocked",
596
+ failedStage: "review",
597
+ stages: [
598
+ { key: "review", runId: "run-blocked.review", ok: true, state: "blocked", verdict: "blocked", error: "Stage returned a blocked verdict." },
599
+ { key: "followup", state: "skipped" },
600
+ ],
601
+ },
602
+ {
603
+ key: "healthy",
604
+ state: "complete",
605
+ stages: [
606
+ { key: "review", runId: "run-healthy.review", ok: true, state: "completed", verdict: "ready" },
607
+ { key: "followup", runId: "run-healthy.followup", ok: true, state: "completed", verdict: "ready" },
608
+ ],
609
+ },
610
+ ]);
611
+ });
612
+
613
+ it("settles a failed lane without aborting later stages in sibling lanes", async () => {
614
+ const launches: string[] = [];
615
+ const result = await runWorkflowScript({
616
+ script: `return await runs.lanes([
617
+ { key: "fails", stages: [
618
+ { key: "writer", agent: "worker", task: "write" },
619
+ { key: "challenge", agent: "worker", task: "fail challenge" },
620
+ { key: "final", agent: "worker", task: "must be skipped" }
621
+ ] },
622
+ { key: "passes", stages: [
623
+ { key: "writer", agent: "worker", task: "write" },
624
+ { key: "challenge", agent: "worker", task: "pass challenge" }
625
+ ] }
626
+ ]);`,
627
+ timeoutMs: 2_000,
628
+ async launch(key) {
629
+ launches.push(key);
630
+ if (key === "fails.challenge") return { key, ok: false, runId: "failed-run", output: "challenge failed", error: "challenge failed", artifactPaths: [], results: [] };
631
+ return { key, ok: true, runId: "run-" + key, output: "done", artifactPaths: [], results: [] };
632
+ },
633
+ async status(key) { return { key, ok: true, output: "ok", artifactPaths: [] }; },
634
+ });
635
+
636
+ assert.deepEqual(launches, ["fails.writer", "passes.writer", "fails.challenge", "passes.challenge"]);
637
+ assert.deepEqual(result.value, [
638
+ {
639
+ key: "fails",
640
+ state: "blocked",
641
+ failedStage: "challenge",
642
+ stages: [
643
+ { key: "writer", runId: "run-fails.writer", ok: true, state: "completed" },
644
+ { key: "challenge", runId: "failed-run", ok: false, state: "failed", error: "challenge failed" },
645
+ { key: "final", state: "skipped" },
646
+ ],
647
+ },
648
+ {
649
+ key: "passes",
650
+ state: "complete",
651
+ stages: [
652
+ { key: "writer", runId: "run-passes.writer", ok: true, state: "completed" },
653
+ { key: "challenge", runId: "run-passes.challenge", ok: true, state: "completed" },
654
+ ],
655
+ },
656
+ ]);
657
+ });
658
+
659
+ it("gives actionable guidance for a retained stage-0 resume", async () => {
660
+ let launches = 0;
661
+ await assert.rejects(
662
+ runWorkflowScript({
663
+ script: `return runs.lanes([{ key: "lane", stages: [{ key: "writer", resume: "retained-run", task: "continue" }] }]);`,
664
+ timeoutMs: 2_000,
665
+ async launch(key) { launches += 1; return { key, ok: true, output: "unexpected", artifactPaths: [], results: [] }; },
666
+ async status(key) { return { key, ok: true, output: "ok", artifactPaths: [] }; },
667
+ }),
668
+ (error: unknown) => error instanceof WorkflowScriptError
669
+ && error.message.includes("runs.lanes lane 0 stage 0 cannot resume a retained run id in runs.lanes")
670
+ && error.message.includes("use runs.run(key, { resume: id }) outside lanes")
671
+ && error.message.includes('start the lane with an agent stage and use resume: "previous" later'),
672
+ );
673
+ assert.equal(launches, 0);
674
+ });
675
+
676
+ it("validates all lane stages before launching any child", async () => {
677
+ const malformedScripts = [
678
+ `return runs.lanes([{ key: "bad lane", stages: [{ key: "writer", agent: "worker", task: "write" }] }]);`,
679
+ `return runs.lanes([{ key: "lane", stages: [{ key: "writer", agent: "worker", task: "write" }, { key: "writer", agent: "worker", task: "again" }] }]);`,
680
+ `return runs.lanes([{ key: "lane", stages: [{ key: "writer", resume: "previous", task: "cannot start" }] }]);`,
681
+ `return runs.lanes([{ key: "lane", stages: [{ key: "writer", agent: "worker", task: "write" }, { key: "review", resume: "raw-run-id", task: "invalid" }] }]);`,
682
+ ];
683
+ for (const script of malformedScripts) {
684
+ let launches = 0;
685
+ await assert.rejects(
686
+ runWorkflowScript({
687
+ script,
688
+ timeoutMs: 2_000,
689
+ async launch(key) { launches += 1; return { key, ok: true, output: "unexpected", artifactPaths: [], results: [] }; },
690
+ async status(key) { return { key, ok: true, output: "ok", artifactPaths: [] }; },
691
+ }),
692
+ (error: unknown) => error instanceof WorkflowScriptError && /runs\.lanes/.test(error.message),
693
+ );
694
+ assert.equal(launches, 0, script);
695
+ }
696
+ });
697
+
698
+ it("rejects an invalid workflow concurrency limit before launching", async () => {
699
+ let launches = 0;
700
+ await assert.rejects(
701
+ runWorkflowScript({
702
+ script: `return runs.run("one", { agent: "worker", task: "one" });`,
703
+ globalConcurrencyLimit: 0,
704
+ async launch(key) {
705
+ launches += 1;
706
+ return { key, ok: true, output: key, artifactPaths: [] };
707
+ },
708
+ async status(key) { return { key, ok: true, output: "ok", artifactPaths: [] }; },
709
+ }),
710
+ /workflow script global concurrency limit must be a positive integer/,
711
+ );
712
+ assert.equal(launches, 0);
713
+ });
714
+
268
715
  it("returns runs.all launch errors without aborting successful siblings", async () => {
269
716
  const result = await runWorkflowScript({
270
717
  script: `
@@ -469,6 +916,23 @@ describe("scripted workflow runtime", () => {
469
916
  assert.deepEqual(result.value, [{ key: "review", output: "completed", values: [null] }]);
470
917
  });
471
918
 
919
+ it("reports completed child references when return serialization fails", async () => {
920
+ await assert.rejects(
921
+ runWorkflowScript({
922
+ script: `const child = await runs.run("writer", { agent: "worker", task: "write" }); return { child, invalid: new Map([["key", "value"]]) };`,
923
+ timeoutMs: 2_000,
924
+ async launch(key) { return { key, ok: true, runId: "run-writer", output: "saved", outputReference: "/tmp/writer-output.md", artifactPaths: ["/tmp/writer-artifact.md"] }; },
925
+ async status(key) { return { key, ok: true, output: "ok", artifactPaths: [] }; },
926
+ }),
927
+ (error: unknown) => error instanceof WorkflowScriptError
928
+ && error.message.includes("return.invalid must contain only plain JSON objects")
929
+ && error.message.includes("Child work completed before return serialization failed")
930
+ && error.message.includes("'writer' (runId=run-writer, outputReference=/tmp/writer-output.md, artifact=/tmp/writer-artifact.md)")
931
+ && error.message.includes("Return a plain projection")
932
+ && error.partial.children[0]?.runId === "run-writer",
933
+ );
934
+ });
935
+
472
936
  it("omits non-JSON child result metadata before returning reused runs.run results", async () => {
473
937
  let launches = 0;
474
938
  const result = await runWorkflowScript({
@@ -545,6 +1009,30 @@ describe("scripted workflow runtime", () => {
545
1009
  ]);
546
1010
  });
547
1011
 
1012
+ it("validates bounded lane metadata and passes it to child launch", async () => {
1013
+ const launches: Array<{ key: string; lane: unknown }> = [];
1014
+ await runWorkflowScript({
1015
+ script: `return runs.run("writer", { agent: "worker", task: "write", lane: { version: 1, key: "writer", mode: "mutation", sourceRef: "owner/repo#1621", claims: ["src/shared/types.ts"], outputPaths: ["reports/writer.md"] } });`,
1016
+ timeoutMs: 2_000,
1017
+ async launch(key, params) {
1018
+ launches.push({ key, lane: params.lane });
1019
+ return { key, ok: true, output: key, artifactPaths: [], results: [] };
1020
+ },
1021
+ async status(key) { return { key, ok: true, output: "ok", artifactPaths: [] }; },
1022
+ });
1023
+ assert.deepEqual(launches, [{ key: "writer", lane: { version: 1, key: "writer", mode: "mutation", sourceRef: "owner/repo#1621", claims: ["src/shared/types.ts"], outputPaths: ["reports/writer.md"] } }]);
1024
+
1025
+ await assert.rejects(
1026
+ runWorkflowScript({
1027
+ script: `return runs.run("writer", { agent: "worker", task: "write", lane: { version: 1, key: "other" } });`,
1028
+ timeoutMs: 2_000,
1029
+ async launch(key) { return { key, ok: true, output: "unexpected", artifactPaths: [] }; },
1030
+ async status(key) { return { key, ok: true, output: "ok", artifactPaths: [] }; },
1031
+ }),
1032
+ (error: unknown) => error instanceof WorkflowScriptError && /must match workflow key/.test(error.message),
1033
+ );
1034
+ });
1035
+
548
1036
  it("passes distinct namespaced bindings to parallel children", async () => {
549
1037
  const launches: Array<{ key: string; bindings: unknown }> = [];
550
1038
  await runWorkflowScript({
@@ -832,6 +1320,18 @@ describe("scripted workflow runtime", () => {
832
1320
  );
833
1321
  });
834
1322
 
1323
+ it("rejects an unawaited runs.lanes launch", async () => {
1324
+ await assert.rejects(
1325
+ runWorkflowScript({
1326
+ script: `runs.lanes([{ key: "lane", stages: [{ key: "writer", agent: "worker", task: "write" }] }]); return "done";`,
1327
+ timeoutMs: 2_000,
1328
+ async launch(key) { return { key, ok: true, output: "done", artifactPaths: [], results: [] }; },
1329
+ async status(key) { return { key, ok: true, output: "ok", artifactPaths: [] }; },
1330
+ }),
1331
+ (error: unknown) => error instanceof WorkflowScriptError && error.message.includes("unawaited runs.run launch(es): 'lane.writer'"),
1332
+ );
1333
+ });
1334
+
835
1335
  it("rejects an unawaited native Promise.all over runs.run", async () => {
836
1336
  await assert.rejects(
837
1337
  runWorkflowScript({
@@ -892,6 +1392,9 @@ describe("scripted workflow runtime", () => {
892
1392
  }),
893
1393
  (error: unknown) => error instanceof WorkflowScriptError
894
1394
  && error.message.includes("does not support nested async functions")
1395
+ && error.message.includes("validation failed before child launch; no children launched")
1396
+ && error.message.includes("Parallel plus sequential rewrite")
1397
+ && error.message.includes("const [aResult] = await Promise.all([a])")
895
1398
  && error.partial.children.length === 0,
896
1399
  );
897
1400
  assert.equal(launches, 0);
@@ -12,6 +12,7 @@ import {
12
12
  injectOutputPathSystemPrompt,
13
13
  injectSingleOutputInstruction,
14
14
  normalizeSingleOutputOverride,
15
+ requestedOutputPathFromTask,
15
16
  resolveSingleOutput,
16
17
  resolveSingleOutputPath,
17
18
  validateFileOnlyOutputMode,
@@ -98,6 +99,13 @@ describe("injectSingleOutputInstruction", () => {
98
99
  });
99
100
  });
100
101
 
102
+ describe("requestedOutputPathFromTask", () => {
103
+ it("extracts direct-write and runtime-persisted report paths", () => {
104
+ assert.equal(requestedOutputPathFromTask("Write your findings to exactly this path: /tmp/direct.md"), "/tmp/direct.md");
105
+ assert.equal(requestedOutputPathFromTask("The runtime will persist it to exactly this path: `/tmp/persisted.md`"), "/tmp/persisted.md");
106
+ });
107
+ });
108
+
101
109
  describe("injectOutputPathSystemPrompt", () => {
102
110
  it("adds the authoritative runtime output path to the system prompt", () => {
103
111
  const output = injectOutputPathSystemPrompt("Output format (`old.md`):", "/tmp/new.md");
@@ -686,7 +686,7 @@ Fallback global agent.
686
686
 
687
687
  assert.throws(
688
688
  () => resolveSkills(["missing-skill"], tempDir),
689
- /Failed to read skills settings file .+\.pi[\\/]settings\.json/,
689
+ /Failed to read skills settings file .+\.selesai[\\/]settings\.json/,
690
690
  );
691
691
  });
692
692
 
@@ -29,18 +29,20 @@ describe("async stale-run reconciliation", () => {
29
29
  assert.equal(checkPidLiveness(123, () => { throw new Error("boom"); }), "unknown");
30
30
  });
31
31
 
32
- it("marks a running async run failed when the runner pid is dead and no result exists", () => {
32
+ it("marks a dead runner failed and writes exactly one completion result", () => {
33
33
  const root = tempRoot("pi-stale-run-");
34
34
  try {
35
35
  const asyncDir = path.join(root, "run-dead");
36
36
  const resultsDir = path.join(root, "results");
37
37
  writeStatus(asyncDir, {
38
+ lifecycleArtifactVersion: 3,
38
39
  runId: "run-dead",
39
40
  sessionId: "session-current",
40
41
  completionOwnerId: "owner-current",
41
42
  mode: "single",
42
43
  state: "running",
43
44
  pid: 12345,
45
+ processTerminal: { version: 1, state: "pending", runId: "run-dead", runnerProcessInstanceId: "runner-dead" },
44
46
  startedAt: 1000,
45
47
  lastUpdate: 1000,
46
48
  currentStep: 0,
@@ -71,6 +73,15 @@ describe("async stale-run reconciliation", () => {
71
73
  assert.equal(resultJson.results[0].contextOverflow, true);
72
74
  assert.match(resultJson.summary, /process 12345 exited or disappeared/);
73
75
  assert.match(fs.readFileSync(path.join(asyncDir, "events.jsonl"), "utf-8"), /subagent\.run\.repaired_stale/);
76
+
77
+ const resultText = fs.readFileSync(path.join(resultsDir, "run-dead.json"), "utf-8");
78
+ const second = reconcileAsyncRun(asyncDir, {
79
+ resultsDir,
80
+ kill: () => { throw errno("ESRCH"); },
81
+ now: () => 3000,
82
+ });
83
+ assert.equal(second.repaired, false);
84
+ assert.equal(fs.readFileSync(path.join(resultsDir, "run-dead.json"), "utf-8"), resultText);
74
85
  } finally {
75
86
  fs.rmSync(root, { recursive: true, force: true });
76
87
  }
@@ -106,11 +106,11 @@ function recoveryDescriptor(runId: string): SteeringRecoveryDescriptor {
106
106
  agent: "worker-0",
107
107
  cwd: os.tmpdir(),
108
108
  systemPromptMode: "replace",
109
+ inheritGlobalContext: false,
109
110
  inheritProjectContext: false,
110
111
  inheritSkills: false,
111
112
  outputMode: "inline",
112
113
  absoluteDeadlineAt: Date.now() + 10_000,
113
- initialTurnBudget: { maxTurns: 10, graceTurns: 2 },
114
114
  initialToolBudget: { soft: 8, hard: 12, block: ["read"] },
115
115
  maxSubagentDepth: 2,
116
116
  share: false,
@@ -314,7 +314,6 @@ describe("acknowledged steering action", () => {
314
314
  ...routed,
315
315
  state: "paused",
316
316
  endedAt: Date.now(),
317
- turnBudget: { maxTurns: 10, graceTurns: 2, turnCount: 7, outcome: "within-budget" },
318
317
  toolBudget: { soft: 8, hard: 12, block: ["read"], toolCount: 9, outcome: "soft-reached" },
319
318
  steps: [{ ...routed.steps![0]!, status: "paused", sessionFile }],
320
319
  });
@@ -334,11 +333,10 @@ describe("acknowledged steering action", () => {
334
333
  assert.match(result.content[0]!.text, /Message: "correct course"/);
335
334
  assert.equal(result.details.steering?.replacementRunId, "replacement");
336
335
  assert.ok(result.details.steering?.targets[0]?.lateDeliveredAt);
337
- const limits = receivedLimits as { timeoutMs: number; absoluteDeadlineAt: number; turnBudget: unknown; toolBudget: unknown };
336
+ const limits = receivedLimits as { timeoutMs: number; absoluteDeadlineAt: number; toolBudget: unknown };
338
337
  assert.ok(limits.timeoutMs > 0 && limits.timeoutMs <= 10_000);
339
338
  assert.ok(limits.absoluteDeadlineAt >= Date.now());
340
- assert.deepEqual({ turnBudget: limits.turnBudget, toolBudget: limits.toolBudget }, {
341
- turnBudget: { maxTurns: 3, graceTurns: 2 },
339
+ assert.deepEqual({ toolBudget: limits.toolBudget }, {
342
340
  toolBudget: { hard: 3, block: ["read"] },
343
341
  });
344
342
  const persisted = JSON.parse(fs.readFileSync(path.join(asyncDir, "status.json"), "utf-8")) as AsyncStatus;