@fitchmultz/pi-subagents 0.44.6 → 0.45.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (947) hide show
  1. package/CHANGELOG.md +265 -7
  2. package/LICENSE +1 -0
  3. package/README.md +58 -1483
  4. package/agents/context-builder.md +19 -1
  5. package/agents/debugger.md +10 -3
  6. package/agents/delegate.md +1 -0
  7. package/agents/fixer.md +4 -1
  8. package/agents/oracle.md +13 -2
  9. package/agents/planner.md +14 -3
  10. package/agents/researcher.md +11 -2
  11. package/agents/reviewer-claude.md +11 -2
  12. package/agents/reviewer-gpt.md +12 -2
  13. package/agents/reviewer-ponytail.md +11 -1
  14. package/agents/reviewer-security.md +13 -2
  15. package/agents/reviewer.md +12 -3
  16. package/agents/scout.md +12 -1
  17. package/agents/ui-designer.md +10 -3
  18. package/agents/watcher.md +1 -0
  19. package/agents/worker.md +8 -2
  20. package/agents/writer.md +6 -3
  21. package/dist/agents/agent-defaults.js +9 -0
  22. package/dist/agents/agent-definition.js +196 -0
  23. package/dist/agents/agent-management.js +64 -905
  24. package/dist/agents/agent-scope.js +4 -2
  25. package/dist/agents/agent-selection.js +10 -5
  26. package/dist/agents/agent-serializer.js +55 -67
  27. package/dist/agents/agents.js +75 -716
  28. package/dist/agents/builtin-overrides.js +218 -0
  29. package/dist/agents/chain-serializer.js +3 -367
  30. package/dist/agents/config-values.js +52 -0
  31. package/dist/agents/discovery-paths.js +91 -0
  32. package/dist/agents/frontmatter.js +15 -5
  33. package/dist/agents/identity.js +25 -8
  34. package/dist/agents/json-chain.js +254 -0
  35. package/dist/agents/management-config.js +332 -0
  36. package/dist/agents/management-format.js +231 -0
  37. package/dist/agents/management-persistence.js +287 -0
  38. package/dist/agents/management-targets.js +136 -0
  39. package/dist/agents/management-warnings.js +81 -0
  40. package/dist/agents/markdown-chain.js +210 -0
  41. package/dist/agents/package-agents.js +154 -0
  42. package/dist/agents/skill-files.js +157 -0
  43. package/dist/agents/skill-search-paths.js +246 -0
  44. package/dist/agents/skill-types.js +1 -0
  45. package/dist/agents/skills.js +54 -499
  46. package/dist/extension/acceptance-schema.js +55 -0
  47. package/dist/extension/build-info.js +1 -1
  48. package/dist/extension/chain-schema.js +196 -0
  49. package/dist/extension/child-runtime.js +237 -0
  50. package/dist/extension/compact-tools.js +122 -51
  51. package/dist/extension/config-schema.js +29 -0
  52. package/dist/extension/config.js +27 -11
  53. package/dist/extension/control-notice-schema.js +37 -0
  54. package/dist/extension/control-notices.js +13 -6
  55. package/dist/extension/delegation-bridges.js +43 -0
  56. package/dist/extension/doctor.js +48 -25
  57. package/dist/extension/everyday-schemas.js +207 -0
  58. package/dist/extension/fanout-child.js +18 -319
  59. package/dist/extension/index.js +8 -563
  60. package/dist/extension/message-renderers.js +225 -0
  61. package/dist/extension/nested-control-inbox.js +154 -0
  62. package/dist/extension/parent-runtime.js +339 -0
  63. package/dist/extension/primary-tool.js +69 -0
  64. package/dist/extension/result-snapshot.js +22 -0
  65. package/dist/extension/runtime-reload.js +62 -0
  66. package/dist/extension/runtime-storage.js +43 -0
  67. package/dist/extension/schema-overrides.js +75 -0
  68. package/dist/extension/schemas.js +265 -336
  69. package/dist/extension/tool-input.js +27 -12
  70. package/dist/extension/tool-result.js +19 -4
  71. package/dist/history/answer-texts.js +57 -0
  72. package/dist/history/canonical-result.js +76 -43
  73. package/dist/history/canonical-selection.js +40 -0
  74. package/dist/history/database-generation.js +82 -0
  75. package/dist/history/database.js +57 -33
  76. package/dist/history/entry-ingestion.js +61 -0
  77. package/dist/history/index.js +242 -97
  78. package/dist/history/ingest.js +168 -331
  79. package/dist/history/output-digests.js +40 -0
  80. package/dist/history/preview.js +210 -0
  81. package/dist/history/prune.js +33 -0
  82. package/dist/history/queries.js +60 -349
  83. package/dist/history/query-scope.js +190 -0
  84. package/dist/history/rows.js +77 -0
  85. package/dist/history/run-projection.js +137 -0
  86. package/dist/history/run-queries.js +153 -0
  87. package/dist/history/schema.js +31 -0
  88. package/dist/history/search-queries.js +163 -0
  89. package/dist/history/selected-record.js +108 -0
  90. package/dist/history/source-file.js +45 -0
  91. package/dist/history/source-watches.js +141 -0
  92. package/dist/history/store.js +164 -153
  93. package/dist/history/text-chunks.js +169 -0
  94. package/dist/history/text.js +35 -0
  95. package/dist/history/transcript-queries.js +177 -0
  96. package/dist/history/types.js +5 -1
  97. package/dist/history/values.js +36 -0
  98. package/dist/history/wire-inputs.js +122 -0
  99. package/dist/history/wire-models.js +38 -0
  100. package/dist/history/worker-runtime.js +441 -0
  101. package/dist/history/worker.js +66 -462
  102. package/dist/intercom/intercom-bridge.js +48 -20
  103. package/dist/intercom/live-intercom.js +80 -23
  104. package/dist/intercom/nested-result-tree.js +163 -0
  105. package/dist/intercom/result-delivery.js +40 -0
  106. package/dist/intercom/result-intercom.js +178 -261
  107. package/dist/pi-intercom/agent-dir.js +7 -3
  108. package/dist/pi-intercom/async.js +27 -0
  109. package/dist/pi-intercom/bridge-ask.js +57 -0
  110. package/dist/pi-intercom/bridge-config.js +79 -0
  111. package/dist/pi-intercom/bridge-http.js +86 -0
  112. package/dist/pi-intercom/bridge-protocol.js +106 -0
  113. package/dist/pi-intercom/bridge-routes.js +64 -0
  114. package/dist/pi-intercom/bridge-session.js +416 -0
  115. package/dist/pi-intercom/bridge.js +182 -603
  116. package/dist/pi-intercom/broker/broker.js +236 -244
  117. package/dist/pi-intercom/broker/client.js +189 -304
  118. package/dist/pi-intercom/broker/connection.js +165 -0
  119. package/dist/pi-intercom/broker/framing.js +5 -3
  120. package/dist/pi-intercom/broker/paths.js +4 -1
  121. package/dist/pi-intercom/broker/pid.js +53 -25
  122. package/dist/pi-intercom/broker/snapshot-reader.js +78 -0
  123. package/dist/pi-intercom/broker/snapshots.js +188 -0
  124. package/dist/pi-intercom/broker/spawn.js +36 -25
  125. package/dist/pi-intercom/config.js +85 -71
  126. package/dist/pi-intercom/connection.js +297 -0
  127. package/dist/pi-intercom/identity.js +58 -0
  128. package/dist/pi-intercom/inbound-delivery.js +356 -0
  129. package/dist/pi-intercom/inbound-journal.js +303 -0
  130. package/dist/pi-intercom/inbound-record.js +113 -0
  131. package/dist/pi-intercom/inbound.js +179 -0
  132. package/dist/pi-intercom/index.js +79 -2933
  133. package/dist/pi-intercom/inspection.js +62 -0
  134. package/dist/pi-intercom/interview.js +236 -0
  135. package/dist/pi-intercom/lifecycle.js +168 -0
  136. package/dist/pi-intercom/message-format.js +69 -0
  137. package/dist/pi-intercom/outbound.js +304 -0
  138. package/dist/pi-intercom/peer-awareness.js +66 -0
  139. package/dist/pi-intercom/reply-tracker.js +62 -43
  140. package/dist/pi-intercom/reply-wait.js +201 -0
  141. package/dist/pi-intercom/runtime-types.js +3 -0
  142. package/dist/pi-intercom/session-events.js +180 -0
  143. package/dist/pi-intercom/session-targets.js +42 -34
  144. package/dist/pi-intercom/subagent-events.js +238 -0
  145. package/dist/pi-intercom/subagent-live-events.js +129 -86
  146. package/dist/pi-intercom/supervisor.js +282 -0
  147. package/dist/pi-intercom/tool-arguments.js +74 -0
  148. package/dist/pi-intercom/tool-registration.js +95 -0
  149. package/dist/pi-intercom/tool-results.js +38 -0
  150. package/dist/pi-intercom/tool-schemas.js +82 -0
  151. package/dist/pi-intercom/tools.js +51 -0
  152. package/dist/pi-intercom/topic-actions.js +177 -0
  153. package/dist/pi-intercom/topics.js +177 -92
  154. package/dist/pi-intercom/transport.js +1 -0
  155. package/dist/pi-intercom/types.js +151 -114
  156. package/dist/pi-intercom/ui/compose-paste.js +93 -0
  157. package/dist/pi-intercom/ui/compose.js +121 -143
  158. package/dist/pi-intercom/ui/inline-message.js +59 -59
  159. package/dist/pi-intercom/ui/message-renderers.js +147 -0
  160. package/dist/pi-intercom/ui/overlay.js +181 -0
  161. package/dist/pi-intercom/ui/session-format.js +124 -0
  162. package/dist/pi-intercom/ui/session-list.js +76 -43
  163. package/dist/pi-intercom/ui/tool-renderers.js +110 -0
  164. package/dist/pi-intercom/ui/topic-view.js +88 -0
  165. package/dist/pi-intercom/validation.js +59 -0
  166. package/dist/runs/background/async-chain-plan.js +265 -0
  167. package/dist/runs/background/async-control.js +134 -58
  168. package/dist/runs/background/async-execution.js +136 -633
  169. package/dist/runs/background/async-job-admission.js +40 -0
  170. package/dist/runs/background/async-job-projection.js +169 -0
  171. package/dist/runs/background/async-job-tracker.js +297 -327
  172. package/dist/runs/background/async-launch.js +216 -0
  173. package/dist/runs/background/async-plan.js +116 -0
  174. package/dist/runs/background/async-result-file.js +47 -23
  175. package/dist/runs/background/async-resume.js +195 -253
  176. package/dist/runs/background/async-run-discovery.js +213 -0
  177. package/dist/runs/background/async-run-location.js +135 -0
  178. package/dist/runs/background/async-run-record.js +1 -0
  179. package/dist/runs/background/async-run-repair.js +223 -0
  180. package/dist/runs/background/async-run-summary.js +1 -0
  181. package/dist/runs/background/async-single-plan.js +105 -0
  182. package/dist/runs/background/async-status-projection.js +155 -0
  183. package/dist/runs/background/async-status-render.js +117 -0
  184. package/dist/runs/background/async-status-validation.js +181 -0
  185. package/dist/runs/background/async-status.js +3 -442
  186. package/dist/runs/background/async-value.js +21 -0
  187. package/dist/runs/background/completion-dedupe.js +36 -12
  188. package/dist/runs/background/completion-delivery.js +211 -205
  189. package/dist/runs/background/completion-receipts.js +237 -0
  190. package/dist/runs/background/legacy-status-acceptance.js +34 -0
  191. package/dist/runs/background/notify.js +96 -48
  192. package/dist/runs/background/parallel-groups.js +36 -19
  193. package/dist/runs/background/result-payload.js +178 -0
  194. package/dist/runs/background/result-watcher.js +348 -339
  195. package/dist/runs/background/run-id-resolver.js +53 -33
  196. package/dist/runs/background/run-schemas.js +189 -0
  197. package/dist/runs/background/run-status-contracts.js +14 -0
  198. package/dist/runs/background/run-status-render.js +270 -0
  199. package/dist/runs/background/run-status.js +231 -339
  200. package/dist/runs/background/runner-attempt-output.js +155 -0
  201. package/dist/runs/background/runner-attempt.js +263 -0
  202. package/dist/runs/background/runner-child-executor.js +166 -0
  203. package/dist/runs/background/runner-child-observer.js +192 -0
  204. package/dist/runs/background/runner-completion-summary.js +71 -0
  205. package/dist/runs/background/runner-completion.js +211 -0
  206. package/dist/runs/background/runner-config.js +1 -0
  207. package/dist/runs/background/runner-contract.js +1 -0
  208. package/dist/runs/background/runner-dynamic.js +121 -0
  209. package/dist/runs/background/runner-finalization.js +149 -0
  210. package/dist/runs/background/runner-graph.js +68 -0
  211. package/dist/runs/background/runner-initial-status.js +60 -0
  212. package/dist/runs/background/runner-lifecycle.js +142 -0
  213. package/dist/runs/background/runner-monitor.js +309 -0
  214. package/dist/runs/background/runner-parallel.js +204 -0
  215. package/dist/runs/background/runner-prepare-step.js +86 -0
  216. package/dist/runs/background/runner-reporting.js +153 -0
  217. package/dist/runs/background/runner-status.js +148 -0
  218. package/dist/runs/background/runner-step-output.js +92 -0
  219. package/dist/runs/background/runner-step-record.js +45 -0
  220. package/dist/runs/background/runner-step.js +112 -0
  221. package/dist/runs/background/runner-streaming.js +173 -0
  222. package/dist/runs/background/runner-workflow.js +213 -0
  223. package/dist/runs/background/stale-run-reconciler.js +155 -292
  224. package/dist/runs/background/subagent-runner-launcher.js +33 -13
  225. package/dist/runs/background/subagent-runner.js +51 -1842
  226. package/dist/runs/background/top-level-async.js +2 -1
  227. package/dist/runs/foreground/chain-clarify.js +293 -1017
  228. package/dist/runs/foreground/clarification-draft.js +87 -0
  229. package/dist/runs/foreground/clarify-contracts.js +1 -0
  230. package/dist/runs/foreground/clarify-invocation.js +129 -38
  231. package/dist/runs/foreground/clarify-text-editor.js +210 -0
  232. package/dist/runs/foreground/clarify-view.js +167 -0
  233. package/dist/runs/foreground/execution-input.js +226 -353
  234. package/dist/runs/foreground/execution-routing.js +33 -0
  235. package/dist/runs/foreground/execution-timeout.js +81 -0
  236. package/dist/runs/foreground/foreground-control.js +222 -763
  237. package/dist/runs/foreground/foreground-memory.js +99 -0
  238. package/dist/runs/foreground/invocation-context.js +55 -0
  239. package/dist/runs/foreground/invocation-execution.js +331 -0
  240. package/dist/runs/foreground/invocation-expansion.js +48 -0
  241. package/dist/runs/foreground/management-actions.js +295 -0
  242. package/dist/runs/foreground/nested-control.js +179 -0
  243. package/dist/runs/foreground/question-continuation.js +78 -0
  244. package/dist/runs/foreground/question-control.js +155 -50
  245. package/dist/runs/foreground/resume-target.js +221 -0
  246. package/dist/runs/foreground/revival-launch-policy.js +58 -0
  247. package/dist/runs/foreground/run-async-path.js +190 -178
  248. package/dist/runs/foreground/run-interrupt.js +164 -0
  249. package/dist/runs/foreground/run-nudge.js +278 -0
  250. package/dist/runs/foreground/saved-revival.js +251 -0
  251. package/dist/runs/foreground/subagent-executor.js +135 -424
  252. package/dist/runs/foreground/subagent-params.js +67 -30
  253. package/dist/runs/foreground/wait-observation.js +104 -0
  254. package/dist/runs/foreground/wait-registration.js +17 -0
  255. package/dist/runs/foreground/wait-run.js +283 -123
  256. package/dist/runs/shared/acceptance-contract.js +193 -99
  257. package/dist/runs/shared/acceptance-defaults.js +1 -0
  258. package/dist/runs/shared/acceptance-evaluation.js +201 -181
  259. package/dist/runs/shared/acceptance-finalization.js +256 -180
  260. package/dist/runs/shared/acceptance-outcome.js +53 -0
  261. package/dist/runs/shared/acceptance-reports.js +137 -72
  262. package/dist/runs/shared/acceptance-submission.js +167 -0
  263. package/dist/runs/shared/acceptance-verification.js +100 -0
  264. package/dist/runs/shared/chain-outputs.js +80 -52
  265. package/dist/runs/shared/child-attempt-types.js +1 -0
  266. package/dist/runs/shared/child-attempt.js +314 -667
  267. package/dist/runs/shared/child-boundaries.js +173 -0
  268. package/dist/runs/shared/child-context-filter.js +44 -0
  269. package/dist/runs/shared/child-event-observer.js +390 -0
  270. package/dist/runs/shared/child-execution-cwd.js +73 -43
  271. package/dist/runs/shared/child-execution-outcome.js +72 -0
  272. package/dist/runs/shared/child-invocation.js +54 -0
  273. package/dist/runs/shared/child-message-validation.js +215 -0
  274. package/dist/runs/shared/child-native-accounting.js +106 -0
  275. package/dist/runs/shared/child-native-observation.js +69 -0
  276. package/dist/runs/shared/child-observation-spool.js +164 -0
  277. package/dist/runs/shared/child-observations.js +157 -76
  278. package/dist/runs/shared/child-presence.js +3 -0
  279. package/dist/runs/shared/child-route-env.js +119 -0
  280. package/dist/runs/shared/child-stream-observer.js +221 -0
  281. package/dist/runs/shared/child-stream-projection.js +137 -0
  282. package/dist/runs/shared/child-structured-tool.js +27 -0
  283. package/dist/runs/shared/claude-code.js +121 -158
  284. package/dist/runs/shared/claude-model.js +42 -0
  285. package/dist/runs/shared/claude-session.js +83 -0
  286. package/dist/runs/shared/claude-types.js +1 -0
  287. package/dist/runs/shared/completion-guard.js +18 -18
  288. package/dist/runs/shared/control-event-policy.js +8 -0
  289. package/dist/runs/shared/control-notification-owner.js +13 -0
  290. package/dist/runs/shared/dynamic-fanout.js +188 -173
  291. package/dist/runs/shared/dynamic-item.js +102 -0
  292. package/dist/runs/shared/history-index.js +62 -36
  293. package/dist/runs/shared/legacy-async-recovery.js +114 -0
  294. package/dist/runs/shared/legacy-output-recovery.js +148 -0
  295. package/dist/runs/shared/mcp-config-schema.js +36 -0
  296. package/dist/runs/shared/mcp-direct-tool-allowlist.js +147 -116
  297. package/dist/runs/shared/model-fallback.js +97 -38
  298. package/dist/runs/shared/mutating-tool-guard.js +128 -88
  299. package/dist/runs/shared/native-finalization-types.js +1 -0
  300. package/dist/runs/shared/native-finalization.js +155 -76
  301. package/dist/runs/shared/native-usage.js +238 -98
  302. package/dist/runs/shared/nested-control-records.js +128 -0
  303. package/dist/runs/shared/nested-events.js +8 -700
  304. package/dist/runs/shared/nested-path.js +32 -28
  305. package/dist/runs/shared/nested-projection.js +155 -0
  306. package/dist/runs/shared/nested-protocol.js +18 -0
  307. package/dist/runs/shared/nested-registry.js +191 -0
  308. package/dist/runs/shared/nested-render.js +106 -58
  309. package/dist/runs/shared/nested-route.js +220 -0
  310. package/dist/runs/shared/nested-validation.js +216 -0
  311. package/dist/runs/shared/owned-child-evidence.js +125 -0
  312. package/dist/runs/shared/owned-run-list.js +146 -0
  313. package/dist/runs/shared/owned-run-observation.js +267 -0
  314. package/dist/runs/shared/owned-run-progress.js +109 -0
  315. package/dist/runs/shared/owned-run-read-state.js +1 -0
  316. package/dist/runs/shared/owned-run-results.js +202 -0
  317. package/dist/runs/shared/owned-run-status.js +180 -0
  318. package/dist/runs/shared/owned-run-view.js +51 -0
  319. package/dist/runs/shared/owned-workflow-details.js +17 -0
  320. package/dist/runs/shared/owner-output.js +43 -0
  321. package/dist/runs/shared/parallel-utils.js +36 -28
  322. package/dist/runs/shared/parent-receipts.js +90 -32
  323. package/dist/runs/shared/parent-usage.js +169 -57
  324. package/dist/runs/shared/pi-args.js +108 -201
  325. package/dist/runs/shared/pi-launch-input.js +1 -0
  326. package/dist/runs/shared/pi-session-launch.js +86 -0
  327. package/dist/runs/shared/pi-spawn.js +35 -21
  328. package/dist/runs/shared/run-history-queries.js +126 -35
  329. package/dist/runs/shared/run-history.js +101 -35
  330. package/dist/runs/shared/run-metadata-paths.js +14 -0
  331. package/dist/runs/shared/run-metadata.js +158 -0
  332. package/dist/runs/shared/run-persistence.js +131 -0
  333. package/dist/runs/shared/run-records.js +9 -679
  334. package/dist/runs/shared/run-restoration.js +338 -0
  335. package/dist/runs/shared/run-state-owner.js +26 -0
  336. package/dist/runs/shared/session-cwd-preload.js +27 -18
  337. package/dist/runs/shared/single-output.js +148 -70
  338. package/dist/runs/shared/stable-value.js +15 -0
  339. package/dist/runs/shared/streaming-text.js +23 -12
  340. package/dist/runs/shared/structured-output.js +25 -13
  341. package/dist/runs/shared/subagent-control.js +167 -100
  342. package/dist/runs/shared/subagent-prompt-runtime.js +27 -130
  343. package/dist/runs/shared/subagent-tool-loop-guard.js +81 -52
  344. package/dist/runs/shared/supervisor-question-state.js +145 -0
  345. package/dist/runs/shared/supervisor-questions.js +185 -254
  346. package/dist/runs/shared/workflow-graph.js +259 -167
  347. package/dist/runs/shared/workflow-policy.js +120 -48
  348. package/dist/runs/shared/worktree-contract.js +1 -0
  349. package/dist/runs/shared/worktree-hook.js +138 -0
  350. package/dist/runs/shared/worktree-location.js +47 -0
  351. package/dist/runs/shared/worktree-preservation.js +8 -0
  352. package/dist/runs/shared/worktree-process.js +107 -0
  353. package/dist/runs/shared/worktree.js +183 -322
  354. package/dist/shared/agent-context-policy.js +34 -44
  355. package/dist/shared/agent-dir.js +14 -0
  356. package/dist/shared/artifacts.js +30 -12
  357. package/dist/shared/atomic-json.js +4 -1
  358. package/dist/shared/chain-files.js +72 -0
  359. package/dist/shared/concurrency.js +18 -0
  360. package/dist/shared/content-preview.js +115 -0
  361. package/dist/shared/cooperative.js +5 -2
  362. package/dist/shared/execution-cwd.js +13 -5
  363. package/dist/shared/file-coalescer.js +2 -1
  364. package/dist/shared/fork-context.js +10 -12
  365. package/dist/shared/formatters.js +45 -19
  366. package/dist/shared/journal-frames.js +164 -0
  367. package/dist/shared/journal-projections.js +121 -0
  368. package/dist/shared/journal-reader.js +6 -407
  369. package/dist/shared/json-projection.js +191 -0
  370. package/dist/shared/lazy-tools.js +19 -8
  371. package/dist/shared/message-output.js +116 -0
  372. package/dist/shared/model-info.js +31 -14
  373. package/dist/shared/native-import.js +20 -0
  374. package/dist/shared/native-journal.js +140 -0
  375. package/dist/shared/native-session.js +5 -2
  376. package/dist/shared/native-tui.js +7 -2
  377. package/dist/shared/native-typebox.js +15 -3
  378. package/dist/shared/output-page.js +63 -0
  379. package/dist/shared/post-exit-stdio-guard.js +128 -88
  380. package/dist/shared/prompt-sections.js +2 -0
  381. package/dist/shared/session-entries.js +70 -35
  382. package/dist/shared/session-identity.js +5 -3
  383. package/dist/shared/settings.js +62 -192
  384. package/dist/shared/status-format.js +97 -37
  385. package/dist/shared/task-file-policy.js +24 -0
  386. package/dist/shared/temp-root.js +44 -15
  387. package/dist/shared/types/acceptance.js +1 -0
  388. package/dist/shared/types/async.js +1 -0
  389. package/dist/shared/types/config.js +1 -0
  390. package/dist/shared/types/details.js +1 -0
  391. package/dist/shared/types/history.js +1 -0
  392. package/dist/shared/types/inputs.js +1 -0
  393. package/dist/shared/types/intercom.js +1 -0
  394. package/dist/shared/types/launch.js +1 -0
  395. package/dist/shared/types/messages.js +1 -0
  396. package/dist/shared/types/nested.js +1 -0
  397. package/dist/shared/types/owned-runs.js +1 -0
  398. package/dist/shared/types/progress.js +1 -0
  399. package/dist/shared/types/questions.js +1 -0
  400. package/dist/shared/types/results.js +1 -0
  401. package/dist/shared/types/state.js +1 -0
  402. package/dist/shared/types/usage.js +1 -0
  403. package/dist/shared/types/workflow.js +1 -0
  404. package/dist/shared/types.js +75 -30
  405. package/dist/shared/unknown.js +52 -0
  406. package/dist/shared/utils.js +54 -260
  407. package/dist/slash/command-discovery.js +82 -0
  408. package/dist/slash/command-input.js +190 -0
  409. package/dist/slash/prompt-template-bridge.js +94 -270
  410. package/dist/slash/prompt-template-contract.js +77 -0
  411. package/dist/slash/prompt-template-results.js +125 -0
  412. package/dist/slash/request-protocol.js +68 -0
  413. package/dist/slash/result-delivery.js +237 -0
  414. package/dist/slash/saved-chain.js +78 -0
  415. package/dist/slash/slash-bridge.js +123 -89
  416. package/dist/slash/slash-commands.js +101 -572
  417. package/dist/slash/slash-live-state.js +68 -47
  418. package/dist/tui/action-hints.js +50 -22
  419. package/dist/tui/activity-lines.js +32 -0
  420. package/dist/tui/agent-browser.js +290 -0
  421. package/dist/tui/agent-controls.js +289 -0
  422. package/dist/tui/agent-conversation.js +444 -0
  423. package/dist/tui/agent-dock.js +171 -0
  424. package/dist/tui/agent-history.js +50 -183
  425. package/dist/tui/agent-model.js +62 -0
  426. package/dist/tui/agent-picker.js +262 -0
  427. package/dist/tui/agent-view.js +232 -1537
  428. package/dist/tui/compact-results.js +193 -0
  429. package/dist/tui/conversation-details.js +190 -0
  430. package/dist/tui/conversation-frame.js +135 -0
  431. package/dist/tui/conversation-history.js +201 -0
  432. package/dist/tui/conversation-menu.js +78 -0
  433. package/dist/tui/conversation-notices.js +79 -0
  434. package/dist/tui/conversation-selection.js +58 -0
  435. package/dist/tui/conversation-viewport.js +129 -0
  436. package/dist/tui/direction-message.js +27 -0
  437. package/dist/tui/display.js +282 -0
  438. package/dist/tui/editor-viewport.js +18 -0
  439. package/dist/tui/expanded-content.js +87 -0
  440. package/dist/tui/expanded-multi.js +170 -0
  441. package/dist/tui/expanded-single.js +65 -0
  442. package/dist/tui/history-cards.js +216 -0
  443. package/dist/tui/history-display.js +238 -0
  444. package/dist/tui/history-json.js +16 -0
  445. package/dist/tui/history-metadata.js +152 -0
  446. package/dist/tui/history-record.js +251 -0
  447. package/dist/tui/history-text.js +34 -0
  448. package/dist/tui/indexed-history.js +116 -0
  449. package/dist/tui/nested-widget.js +120 -0
  450. package/dist/tui/picker-page.js +59 -0
  451. package/dist/tui/render.js +58 -1501
  452. package/dist/tui/result-facts.js +64 -0
  453. package/dist/tui/task-observation.js +59 -0
  454. package/dist/tui/task-store.js +366 -0
  455. package/dist/tui/text-values.js +16 -0
  456. package/dist/tui/view-connections.js +47 -0
  457. package/dist/tui/view-model.js +94 -0
  458. package/dist/tui/view-persistence.js +51 -0
  459. package/dist/tui/view-ports.js +1 -0
  460. package/dist/tui/view-session.js +37 -0
  461. package/dist/tui/widget-details.js +144 -0
  462. package/dist/tui/widget-status.js +245 -0
  463. package/dist/tui/widget.js +145 -0
  464. package/dist/tui/workflow-labels.js +223 -0
  465. package/dist/tui/workflow-outcomes.js +124 -0
  466. package/docs/code-quality.md +38 -0
  467. package/docs/development.md +99 -0
  468. package/docs/intercom-bridge.md +23 -18
  469. package/docs/intercom.md +132 -106
  470. package/docs/performance.md +19 -17
  471. package/docs/reference.md +1472 -0
  472. package/generated/RunValidators.d.ts +28 -0
  473. package/generated/RunValidators.js +2 -0
  474. package/package.json +27 -6
  475. package/prompts/parallel-cleanup.md +3 -0
  476. package/prompts/parallel-context-build.md +3 -0
  477. package/prompts/parallel-handoff-plan.md +6 -0
  478. package/prompts/parallel-research.md +4 -0
  479. package/prompts/parallel-review.md +2 -0
  480. package/prompts/review-loop.md +2 -0
  481. package/scripts/build.mjs +129 -85
  482. package/scripts/check-compat.mjs +48 -11
  483. package/scripts/compat-host.mjs +35 -7
  484. package/scripts/generate-run-schemas.mjs +210 -0
  485. package/scripts/intercom-bridge-credentials.mjs +155 -28
  486. package/scripts/intercom-remote.mjs +175 -50
  487. package/scripts/local-install-smoke.mjs +47 -37
  488. package/scripts/native-package-smoke.mjs +135 -25
  489. package/scripts/package-smoke.mjs +360 -140
  490. package/scripts/prepare.mjs +54 -34
  491. package/scripts/real-pi-processes.mjs +261 -0
  492. package/scripts/real-pi-smoke.mjs +432 -351
  493. package/scripts/run-tests.mjs +29 -15
  494. package/skills/pi-intercom/SKILL.md +11 -11
  495. package/skills/pi-intercom/references/peer-sessions.md +7 -2
  496. package/skills/pi-subagents/SKILL.md +1 -1
  497. package/skills/pi-subagents/references/full-orchestration-guide.md +254 -112
  498. package/src/agents/agent-defaults.ts +13 -0
  499. package/src/agents/agent-definition.ts +253 -0
  500. package/src/agents/agent-management.ts +111 -808
  501. package/src/agents/agent-scope.ts +7 -3
  502. package/src/agents/agent-selection.ts +26 -16
  503. package/src/agents/agent-serializer.ts +84 -93
  504. package/src/agents/agents.ts +199 -906
  505. package/src/agents/builtin-overrides.ts +297 -0
  506. package/src/agents/chain-serializer.ts +3 -329
  507. package/src/agents/config-values.ts +57 -0
  508. package/src/agents/discovery-paths.ts +134 -0
  509. package/src/agents/frontmatter.ts +35 -17
  510. package/src/agents/identity.ts +49 -18
  511. package/src/agents/json-chain.ts +305 -0
  512. package/src/agents/management-config.ts +342 -0
  513. package/src/agents/management-format.ts +243 -0
  514. package/src/agents/management-persistence.ts +408 -0
  515. package/src/agents/management-targets.ts +260 -0
  516. package/src/agents/management-warnings.ts +115 -0
  517. package/src/agents/markdown-chain.ts +237 -0
  518. package/src/agents/package-agents.ts +231 -0
  519. package/src/agents/skill-files.ts +182 -0
  520. package/src/agents/skill-search-paths.ts +289 -0
  521. package/src/agents/skill-types.ts +31 -0
  522. package/src/agents/skills.ts +152 -610
  523. package/src/extension/acceptance-schema.ts +88 -0
  524. package/src/extension/chain-schema.ts +276 -0
  525. package/src/extension/child-runtime.ts +302 -0
  526. package/src/extension/compact-tools.ts +259 -102
  527. package/src/extension/config-schema.ts +59 -0
  528. package/src/extension/config.ts +39 -17
  529. package/src/extension/control-notice-schema.ts +55 -0
  530. package/src/extension/control-notices.ts +57 -33
  531. package/src/extension/delegation-bridges.ts +59 -0
  532. package/src/extension/doctor.ts +171 -135
  533. package/src/extension/everyday-schemas.ts +252 -0
  534. package/src/extension/fanout-child.ts +29 -308
  535. package/src/extension/index.ts +17 -642
  536. package/src/extension/message-renderers.ts +334 -0
  537. package/src/extension/nested-control-inbox.ts +196 -0
  538. package/src/extension/parent-runtime.ts +405 -0
  539. package/src/extension/primary-tool.ts +130 -0
  540. package/src/extension/result-snapshot.ts +35 -0
  541. package/src/extension/runtime-reload.ts +72 -0
  542. package/src/extension/runtime-storage.ts +49 -0
  543. package/src/extension/schema-overrides.ts +114 -0
  544. package/src/extension/schemas.ts +451 -418
  545. package/src/extension/tool-input.ts +40 -13
  546. package/src/extension/tool-result.ts +45 -14
  547. package/src/history/answer-texts.ts +74 -0
  548. package/src/history/canonical-result.ts +127 -51
  549. package/src/history/canonical-selection.ts +54 -0
  550. package/src/history/database-generation.ts +86 -0
  551. package/src/history/database.ts +86 -48
  552. package/src/history/entry-ingestion.ts +129 -0
  553. package/src/history/index.ts +426 -148
  554. package/src/history/ingest.ts +372 -342
  555. package/src/history/output-digests.ts +41 -0
  556. package/src/history/preview.ts +235 -0
  557. package/src/history/prune.ts +48 -0
  558. package/src/history/queries.ts +122 -256
  559. package/src/history/query-scope.ts +278 -0
  560. package/src/history/rows.ts +118 -0
  561. package/src/history/run-projection.ts +164 -0
  562. package/src/history/run-queries.ts +199 -0
  563. package/src/history/schema.ts +34 -0
  564. package/src/history/search-queries.ts +216 -0
  565. package/src/history/selected-record.ts +140 -0
  566. package/src/history/source-file.ts +51 -0
  567. package/src/history/source-watches.ts +156 -0
  568. package/src/history/store.ts +310 -197
  569. package/src/history/text-chunks.ts +181 -0
  570. package/src/history/text.ts +43 -0
  571. package/src/history/transcript-queries.ts +251 -0
  572. package/src/history/types.ts +27 -164
  573. package/src/history/values.ts +36 -0
  574. package/src/history/wire-inputs.ts +141 -0
  575. package/src/history/wire-models.ts +51 -0
  576. package/src/history/worker-runtime.ts +502 -0
  577. package/src/history/worker.ts +94 -336
  578. package/src/intercom/intercom-bridge.ts +136 -69
  579. package/src/intercom/live-intercom.ts +197 -92
  580. package/src/intercom/nested-result-tree.ts +213 -0
  581. package/src/intercom/result-delivery.ts +56 -0
  582. package/src/intercom/result-intercom.ts +364 -382
  583. package/src/pi-intercom/agent-dir.ts +9 -3
  584. package/src/pi-intercom/async.ts +33 -0
  585. package/src/pi-intercom/bridge-ask.ts +64 -0
  586. package/src/pi-intercom/bridge-config.ts +101 -0
  587. package/src/pi-intercom/bridge-http.ts +97 -0
  588. package/src/pi-intercom/bridge-protocol.ts +123 -0
  589. package/src/pi-intercom/bridge-routes.ts +90 -0
  590. package/src/pi-intercom/bridge-session.ts +506 -0
  591. package/src/pi-intercom/bridge.ts +258 -547
  592. package/src/pi-intercom/broker/broker.ts +326 -263
  593. package/src/pi-intercom/broker/client.ts +273 -354
  594. package/src/pi-intercom/broker/connection.ts +192 -0
  595. package/src/pi-intercom/broker/framing.ts +11 -6
  596. package/src/pi-intercom/broker/paths.ts +28 -25
  597. package/src/pi-intercom/broker/pid.ts +68 -24
  598. package/src/pi-intercom/broker/snapshot-reader.ts +91 -0
  599. package/src/pi-intercom/broker/snapshots.ts +241 -0
  600. package/src/pi-intercom/broker/spawn.ts +58 -34
  601. package/src/pi-intercom/config.ts +113 -108
  602. package/src/pi-intercom/connection.ts +346 -0
  603. package/src/pi-intercom/identity.ts +71 -0
  604. package/src/pi-intercom/inbound-delivery.ts +450 -0
  605. package/src/pi-intercom/inbound-journal.ts +350 -0
  606. package/src/pi-intercom/inbound-record.ts +141 -0
  607. package/src/pi-intercom/inbound.ts +237 -0
  608. package/src/pi-intercom/index.ts +86 -2923
  609. package/src/pi-intercom/inspection.ts +82 -0
  610. package/src/pi-intercom/interview.ts +295 -0
  611. package/src/pi-intercom/lifecycle.ts +183 -0
  612. package/src/pi-intercom/message-format.ts +77 -0
  613. package/src/pi-intercom/outbound.ts +418 -0
  614. package/src/pi-intercom/peer-awareness.ts +84 -0
  615. package/src/pi-intercom/reply-tracker.ts +121 -59
  616. package/src/pi-intercom/reply-wait.ts +247 -0
  617. package/src/pi-intercom/runtime-types.ts +97 -0
  618. package/src/pi-intercom/session-events.ts +247 -0
  619. package/src/pi-intercom/session-targets.ts +124 -72
  620. package/src/pi-intercom/subagent-events.ts +299 -0
  621. package/src/pi-intercom/subagent-live-events.ts +212 -109
  622. package/src/pi-intercom/supervisor.ts +362 -0
  623. package/src/pi-intercom/tool-arguments.ts +120 -0
  624. package/src/pi-intercom/tool-registration.ts +121 -0
  625. package/src/pi-intercom/tool-results.ts +54 -0
  626. package/src/pi-intercom/tool-schemas.ts +134 -0
  627. package/src/pi-intercom/tools.ts +80 -0
  628. package/src/pi-intercom/topic-actions.ts +243 -0
  629. package/src/pi-intercom/topics.ts +267 -121
  630. package/src/pi-intercom/transport.ts +14 -0
  631. package/src/pi-intercom/types.ts +288 -190
  632. package/src/pi-intercom/ui/compose-paste.ts +109 -0
  633. package/src/pi-intercom/ui/compose.ts +173 -157
  634. package/src/pi-intercom/ui/inline-message.ts +80 -62
  635. package/src/pi-intercom/ui/message-renderers.ts +182 -0
  636. package/src/pi-intercom/ui/overlay.ts +238 -0
  637. package/src/pi-intercom/ui/session-format.ts +146 -0
  638. package/src/pi-intercom/ui/session-list.ts +145 -62
  639. package/src/pi-intercom/ui/tool-renderers.ts +148 -0
  640. package/src/pi-intercom/ui/topic-view.ts +107 -0
  641. package/src/pi-intercom/validation.ts +69 -0
  642. package/src/runs/background/async-chain-plan.ts +451 -0
  643. package/src/runs/background/async-control.ts +201 -71
  644. package/src/runs/background/async-execution.ts +206 -829
  645. package/src/runs/background/async-job-admission.ts +54 -0
  646. package/src/runs/background/async-job-projection.ts +225 -0
  647. package/src/runs/background/async-job-tracker.ts +416 -360
  648. package/src/runs/background/async-launch.ts +319 -0
  649. package/src/runs/background/async-plan.ts +285 -0
  650. package/src/runs/background/async-result-file.ts +79 -37
  651. package/src/runs/background/async-resume.ts +338 -285
  652. package/src/runs/background/async-run-discovery.ts +270 -0
  653. package/src/runs/background/async-run-location.ts +194 -0
  654. package/src/runs/background/async-run-record.ts +14 -0
  655. package/src/runs/background/async-run-repair.ts +307 -0
  656. package/src/runs/background/async-run-summary.ts +71 -0
  657. package/src/runs/background/async-single-plan.ts +168 -0
  658. package/src/runs/background/async-status-projection.ts +188 -0
  659. package/src/runs/background/async-status-render.ts +164 -0
  660. package/src/runs/background/async-status-validation.ts +238 -0
  661. package/src/runs/background/async-status.ts +9 -481
  662. package/src/runs/background/async-value.ts +29 -0
  663. package/src/runs/background/completion-dedupe.ts +103 -54
  664. package/src/runs/background/completion-delivery.ts +313 -187
  665. package/src/runs/background/completion-receipts.ts +309 -0
  666. package/src/runs/background/legacy-status-acceptance.ts +38 -0
  667. package/src/runs/background/notify.ts +167 -106
  668. package/src/runs/background/parallel-groups.ts +95 -46
  669. package/src/runs/background/result-payload.ts +268 -0
  670. package/src/runs/background/result-watcher.ts +484 -387
  671. package/src/runs/background/run-id-resolver.ts +126 -57
  672. package/src/runs/background/run-schemas.ts +240 -0
  673. package/src/runs/background/run-status-contracts.ts +46 -0
  674. package/src/runs/background/run-status-render.ts +364 -0
  675. package/src/runs/background/run-status.ts +338 -356
  676. package/src/runs/background/runner-attempt-output.ts +281 -0
  677. package/src/runs/background/runner-attempt.ts +354 -0
  678. package/src/runs/background/runner-child-executor.ts +232 -0
  679. package/src/runs/background/runner-child-observer.ts +270 -0
  680. package/src/runs/background/runner-completion-summary.ts +106 -0
  681. package/src/runs/background/runner-completion.ts +274 -0
  682. package/src/runs/background/runner-config.ts +52 -0
  683. package/src/runs/background/runner-contract.ts +51 -0
  684. package/src/runs/background/runner-dynamic.ts +185 -0
  685. package/src/runs/background/runner-finalization.ts +203 -0
  686. package/src/runs/background/runner-graph.ts +91 -0
  687. package/src/runs/background/runner-initial-status.ts +72 -0
  688. package/src/runs/background/runner-lifecycle.ts +159 -0
  689. package/src/runs/background/runner-monitor.ts +399 -0
  690. package/src/runs/background/runner-parallel.ts +280 -0
  691. package/src/runs/background/runner-prepare-step.ts +128 -0
  692. package/src/runs/background/runner-reporting.ts +192 -0
  693. package/src/runs/background/runner-status.ts +220 -0
  694. package/src/runs/background/runner-step-output.ts +158 -0
  695. package/src/runs/background/runner-step-record.ts +76 -0
  696. package/src/runs/background/runner-step.ts +150 -0
  697. package/src/runs/background/runner-streaming.ts +220 -0
  698. package/src/runs/background/runner-workflow.ts +284 -0
  699. package/src/runs/background/stale-run-reconciler.ts +279 -336
  700. package/src/runs/background/subagent-runner-launcher.ts +67 -35
  701. package/src/runs/background/subagent-runner.ts +80 -2048
  702. package/src/runs/background/top-level-async.ts +9 -7
  703. package/src/runs/foreground/chain-clarify.ts +499 -1334
  704. package/src/runs/foreground/clarification-draft.ts +92 -0
  705. package/src/runs/foreground/clarify-contracts.ts +33 -0
  706. package/src/runs/foreground/clarify-invocation.ts +192 -65
  707. package/src/runs/foreground/clarify-text-editor.ts +229 -0
  708. package/src/runs/foreground/clarify-view.ts +222 -0
  709. package/src/runs/foreground/execution-input.ts +358 -424
  710. package/src/runs/foreground/execution-routing.ts +41 -0
  711. package/src/runs/foreground/execution-timeout.ts +92 -0
  712. package/src/runs/foreground/foreground-control.ts +333 -812
  713. package/src/runs/foreground/foreground-memory.ts +132 -0
  714. package/src/runs/foreground/invocation-context.ts +88 -0
  715. package/src/runs/foreground/invocation-execution.ts +465 -0
  716. package/src/runs/foreground/invocation-expansion.ts +58 -0
  717. package/src/runs/foreground/management-actions.ts +360 -0
  718. package/src/runs/foreground/nested-control.ts +248 -0
  719. package/src/runs/foreground/question-continuation.ts +112 -0
  720. package/src/runs/foreground/question-control.ts +297 -70
  721. package/src/runs/foreground/resume-target.ts +330 -0
  722. package/src/runs/foreground/revival-launch-policy.ts +112 -0
  723. package/src/runs/foreground/run-async-path.ts +307 -223
  724. package/src/runs/foreground/run-interrupt.ts +202 -0
  725. package/src/runs/foreground/run-nudge.ts +355 -0
  726. package/src/runs/foreground/saved-revival.ts +356 -0
  727. package/src/runs/foreground/subagent-executor.ts +195 -493
  728. package/src/runs/foreground/subagent-params.ts +314 -242
  729. package/src/runs/foreground/wait-observation.ts +160 -0
  730. package/src/runs/foreground/wait-registration.ts +18 -0
  731. package/src/runs/foreground/wait-run.ts +361 -119
  732. package/src/runs/shared/acceptance-contract.ts +410 -209
  733. package/src/runs/shared/acceptance-defaults.ts +1 -0
  734. package/src/runs/shared/acceptance-evaluation.ts +314 -240
  735. package/src/runs/shared/acceptance-finalization.ts +447 -305
  736. package/src/runs/shared/acceptance-outcome.ts +69 -0
  737. package/src/runs/shared/acceptance-reports.ts +220 -126
  738. package/src/runs/shared/acceptance-submission.ts +251 -0
  739. package/src/runs/shared/acceptance-verification.ts +118 -0
  740. package/src/runs/shared/acceptance.ts +25 -25
  741. package/src/runs/shared/chain-outputs.ts +190 -98
  742. package/src/runs/shared/child-attempt-types.ts +118 -0
  743. package/src/runs/shared/child-attempt.ts +372 -608
  744. package/src/runs/shared/child-boundaries.ts +217 -0
  745. package/src/runs/shared/child-context-filter.ts +53 -0
  746. package/src/runs/shared/child-event-observer.ts +496 -0
  747. package/src/runs/shared/child-execution-cwd.ts +139 -68
  748. package/src/runs/shared/child-execution-outcome.ts +103 -0
  749. package/src/runs/shared/child-invocation.ts +74 -0
  750. package/src/runs/shared/child-message-validation.ts +233 -0
  751. package/src/runs/shared/child-native-accounting.ts +154 -0
  752. package/src/runs/shared/child-native-observation.ts +79 -0
  753. package/src/runs/shared/child-observation-spool.ts +188 -0
  754. package/src/runs/shared/child-observations.ts +219 -89
  755. package/src/runs/shared/child-presence.ts +6 -0
  756. package/src/runs/shared/child-route-env.ts +142 -0
  757. package/src/runs/shared/child-stream-observer.ts +302 -0
  758. package/src/runs/shared/child-stream-projection.ts +160 -0
  759. package/src/runs/shared/child-structured-tool.ts +38 -0
  760. package/src/runs/shared/claude-code.ts +201 -228
  761. package/src/runs/shared/claude-model.ts +67 -0
  762. package/src/runs/shared/claude-session.ts +124 -0
  763. package/src/runs/shared/claude-types.ts +31 -0
  764. package/src/runs/shared/completion-guard.ts +35 -26
  765. package/src/runs/shared/control-event-policy.ts +15 -0
  766. package/src/runs/shared/control-notification-owner.ts +20 -0
  767. package/src/runs/shared/dynamic-fanout.ts +402 -238
  768. package/src/runs/shared/dynamic-item.ts +114 -0
  769. package/src/runs/shared/history-index.ts +112 -51
  770. package/src/runs/shared/legacy-async-recovery.ts +153 -0
  771. package/src/runs/shared/legacy-output-recovery.ts +214 -0
  772. package/src/runs/shared/mcp-config-schema.ts +46 -0
  773. package/src/runs/shared/mcp-direct-tool-allowlist.ts +396 -307
  774. package/src/runs/shared/model-fallback.ts +302 -180
  775. package/src/runs/shared/mutating-tool-guard.ts +259 -158
  776. package/src/runs/shared/native-finalization-types.ts +29 -0
  777. package/src/runs/shared/native-finalization.ts +233 -109
  778. package/src/runs/shared/native-usage.ts +379 -96
  779. package/src/runs/shared/nested-control-records.ts +178 -0
  780. package/src/runs/shared/nested-events.ts +45 -752
  781. package/src/runs/shared/nested-path.ts +47 -34
  782. package/src/runs/shared/nested-projection.ts +230 -0
  783. package/src/runs/shared/nested-protocol.ts +67 -0
  784. package/src/runs/shared/nested-registry.ts +273 -0
  785. package/src/runs/shared/nested-render.ts +198 -100
  786. package/src/runs/shared/nested-route.ts +292 -0
  787. package/src/runs/shared/nested-validation.ts +291 -0
  788. package/src/runs/shared/owned-child-evidence.ts +184 -0
  789. package/src/runs/shared/owned-run-list.ts +187 -0
  790. package/src/runs/shared/owned-run-observation.ts +368 -0
  791. package/src/runs/shared/owned-run-progress.ts +172 -0
  792. package/src/runs/shared/owned-run-read-state.ts +7 -0
  793. package/src/runs/shared/owned-run-results.ts +289 -0
  794. package/src/runs/shared/owned-run-status.ts +239 -0
  795. package/src/runs/shared/owned-run-view.ts +65 -0
  796. package/src/runs/shared/owned-workflow-details.ts +24 -0
  797. package/src/runs/shared/owner-output.ts +73 -0
  798. package/src/runs/shared/parallel-utils.ts +93 -129
  799. package/src/runs/shared/parent-receipts.ts +177 -50
  800. package/src/runs/shared/parent-usage.ts +314 -91
  801. package/src/runs/shared/pi-args.ts +241 -311
  802. package/src/runs/shared/pi-launch-input.ts +48 -0
  803. package/src/runs/shared/pi-session-launch.ts +94 -0
  804. package/src/runs/shared/pi-spawn.ts +57 -33
  805. package/src/runs/shared/run-history-queries.ts +189 -38
  806. package/src/runs/shared/run-history.ts +197 -76
  807. package/src/runs/shared/run-id-ambiguity.ts +7 -3
  808. package/src/runs/shared/run-metadata-paths.ts +17 -0
  809. package/src/runs/shared/run-metadata.ts +240 -0
  810. package/src/runs/shared/run-persistence.ts +181 -0
  811. package/src/runs/shared/run-records.ts +19 -607
  812. package/src/runs/shared/run-restoration.ts +455 -0
  813. package/src/runs/shared/run-state-owner.ts +31 -0
  814. package/src/runs/shared/session-cwd-preload.ts +39 -19
  815. package/src/runs/shared/single-output.ts +358 -205
  816. package/src/runs/shared/stable-value.ts +15 -0
  817. package/src/runs/shared/streaming-text.ts +39 -12
  818. package/src/runs/shared/structured-output.ts +76 -46
  819. package/src/runs/shared/subagent-control.ts +330 -197
  820. package/src/runs/shared/subagent-prompt-runtime.ts +92 -174
  821. package/src/runs/shared/subagent-tool-loop-guard.ts +118 -68
  822. package/src/runs/shared/supervisor-question-state.ts +221 -0
  823. package/src/runs/shared/supervisor-questions.ts +396 -401
  824. package/src/runs/shared/workflow-graph.ts +400 -198
  825. package/src/runs/shared/workflow-policy.ts +245 -110
  826. package/src/runs/shared/worktree-contract.ts +29 -0
  827. package/src/runs/shared/worktree-hook.ts +181 -0
  828. package/src/runs/shared/worktree-location.ts +61 -0
  829. package/src/runs/shared/worktree-preservation.ts +10 -0
  830. package/src/runs/shared/worktree-process.ts +132 -0
  831. package/src/runs/shared/worktree.ts +459 -587
  832. package/src/shared/agent-context-policy.ts +176 -145
  833. package/src/shared/agent-dir.ts +15 -0
  834. package/src/shared/artifacts.ts +99 -68
  835. package/src/shared/atomic-json.ts +14 -11
  836. package/src/shared/chain-files.ts +72 -0
  837. package/src/shared/concurrency.ts +27 -0
  838. package/src/shared/content-preview.ts +117 -0
  839. package/src/shared/cooperative.ts +17 -13
  840. package/src/shared/execution-cwd.ts +34 -9
  841. package/src/shared/file-coalescer.ts +24 -22
  842. package/src/shared/fork-context.ts +52 -48
  843. package/src/shared/formatters.ts +88 -47
  844. package/src/shared/journal-frames.ts +197 -0
  845. package/src/shared/journal-projections.ts +133 -0
  846. package/src/shared/journal-reader.ts +18 -333
  847. package/src/shared/json-projection.ts +224 -0
  848. package/src/shared/lazy-tools.ts +76 -27
  849. package/src/shared/message-output.ts +146 -0
  850. package/src/shared/model-info.ts +94 -58
  851. package/src/shared/native-import.ts +25 -0
  852. package/src/shared/native-journal.ts +172 -0
  853. package/src/shared/native-session.ts +8 -3
  854. package/src/shared/native-tui.ts +13 -3
  855. package/src/shared/native-typebox.ts +18 -3
  856. package/src/shared/output-page.ts +76 -0
  857. package/src/shared/post-exit-stdio-guard.ts +232 -172
  858. package/src/shared/prompt-sections.ts +13 -7
  859. package/src/shared/session-entries.ts +103 -44
  860. package/src/shared/session-identity.ts +17 -10
  861. package/src/shared/settings.ts +182 -367
  862. package/src/shared/status-format.ts +207 -98
  863. package/src/shared/stream-json.d.ts +10 -0
  864. package/src/shared/task-file-policy.ts +35 -0
  865. package/src/shared/temp-root.ts +122 -71
  866. package/src/shared/types/acceptance.ts +149 -0
  867. package/src/shared/types/async.ts +247 -0
  868. package/src/shared/types/config.ts +139 -0
  869. package/src/shared/types/details.ts +104 -0
  870. package/src/shared/types/history.ts +217 -0
  871. package/src/shared/types/inputs.ts +4 -0
  872. package/src/shared/types/intercom.ts +57 -0
  873. package/src/shared/types/launch.ts +39 -0
  874. package/src/shared/types/messages.ts +104 -0
  875. package/src/shared/types/nested.ts +153 -0
  876. package/src/shared/types/owned-runs.ts +148 -0
  877. package/src/shared/types/progress.ts +106 -0
  878. package/src/shared/types/questions.ts +103 -0
  879. package/src/shared/types/results.ts +119 -0
  880. package/src/shared/types/state.ts +56 -0
  881. package/src/shared/types/usage.ts +36 -0
  882. package/src/shared/types/workflow.ts +187 -0
  883. package/src/shared/types.ts +222 -1161
  884. package/src/shared/ui-mode.ts +1 -1
  885. package/src/shared/unknown.ts +59 -0
  886. package/src/shared/utils.ts +111 -305
  887. package/src/slash/command-discovery.ts +109 -0
  888. package/src/slash/command-input.ts +232 -0
  889. package/src/slash/prompt-template-bridge.ts +153 -400
  890. package/src/slash/prompt-template-contract.ts +152 -0
  891. package/src/slash/prompt-template-results.ts +189 -0
  892. package/src/slash/request-protocol.ts +87 -0
  893. package/src/slash/result-delivery.ts +312 -0
  894. package/src/slash/saved-chain.ts +115 -0
  895. package/src/slash/slash-bridge.ts +202 -156
  896. package/src/slash/slash-commands.ts +176 -598
  897. package/src/slash/slash-live-state.ts +287 -229
  898. package/src/tui/action-hints.ts +147 -61
  899. package/src/tui/activity-lines.ts +40 -0
  900. package/src/tui/agent-browser.ts +354 -0
  901. package/src/tui/agent-controls.ts +355 -0
  902. package/src/tui/agent-conversation.ts +514 -0
  903. package/src/tui/agent-dock.ts +250 -0
  904. package/src/tui/agent-history.ts +78 -203
  905. package/src/tui/agent-model.ts +78 -0
  906. package/src/tui/agent-picker.ts +325 -0
  907. package/src/tui/agent-view.ts +391 -1310
  908. package/src/tui/compact-results.ts +342 -0
  909. package/src/tui/conversation-details.ts +206 -0
  910. package/src/tui/conversation-frame.ts +184 -0
  911. package/src/tui/conversation-history.ts +246 -0
  912. package/src/tui/conversation-menu.ts +119 -0
  913. package/src/tui/conversation-notices.ts +116 -0
  914. package/src/tui/conversation-selection.ts +66 -0
  915. package/src/tui/conversation-viewport.ts +169 -0
  916. package/src/tui/direction-message.ts +35 -0
  917. package/src/tui/display.ts +409 -0
  918. package/src/tui/editor-viewport.ts +19 -0
  919. package/src/tui/expanded-content.ts +134 -0
  920. package/src/tui/expanded-multi.ts +264 -0
  921. package/src/tui/expanded-single.ts +92 -0
  922. package/src/tui/history-cards.ts +316 -0
  923. package/src/tui/history-display.ts +334 -0
  924. package/src/tui/history-json.ts +17 -0
  925. package/src/tui/history-metadata.ts +177 -0
  926. package/src/tui/history-record.ts +321 -0
  927. package/src/tui/history-text.ts +34 -0
  928. package/src/tui/indexed-history.ts +149 -0
  929. package/src/tui/nested-widget.ts +192 -0
  930. package/src/tui/picker-page.ts +80 -0
  931. package/src/tui/render.ts +100 -1546
  932. package/src/tui/result-facts.ts +99 -0
  933. package/src/tui/task-observation.ts +96 -0
  934. package/src/tui/task-store.ts +432 -0
  935. package/src/tui/text-values.ts +20 -0
  936. package/src/tui/view-connections.ts +64 -0
  937. package/src/tui/view-model.ts +181 -0
  938. package/src/tui/view-persistence.ts +57 -0
  939. package/src/tui/view-ports.ts +55 -0
  940. package/src/tui/view-session.ts +42 -0
  941. package/src/tui/widget-details.ts +220 -0
  942. package/src/tui/widget-status.ts +331 -0
  943. package/src/tui/widget.ts +208 -0
  944. package/src/tui/workflow-labels.ts +340 -0
  945. package/src/tui/workflow-outcomes.ts +150 -0
  946. package/tsconfig.build.json +11 -10
  947. package/tsconfig.json +30 -17
@@ -1,821 +1,342 @@
1
- import { randomUUID } from "node:crypto";
2
- import { formatRunIdAmbiguity } from "../shared/run-id-ambiguity.ts";
3
- import { resolveRootSessionId } from "../../shared/session-identity.ts";
4
- import { writeAsyncControlRequest, writeAsyncInterruptRequest } from "../background/async-control.ts";
5
- import { getRunMetadataDir, listSupervisorQuestions, questionProcessAlive, readQuestionContract, readRunJson, recordQuestionDelivery, saveQuestionOwner, type SupervisorQuestionView, type SupervisorRunContract } from "../shared/supervisor-questions.ts";
6
- import * as fs from "node:fs";
7
1
  import * as path from "node:path";
8
- import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
9
- import { type AgentScope } from "../../agents/agents.ts";
10
- import { providerQualifiedModelId, toModelInfo } from "../../shared/model-info.ts";
11
- import { normalizeSkillInput } from "../../agents/skills.ts";
12
- import { resolveExecutionAgentScope } from "../../agents/agent-scope.ts";
13
- import { executeAsyncSingle, formatAsyncStartedMessage } from "../background/async-execution.ts";
14
- import { resolveConfiguredChildProjectTrustPolicy } from "../shared/pi-args.ts";
15
- import { requestChildExecutionCwd } from "../shared/child-execution-cwd.ts";
16
- import { resolveCurrentSessionId } from "../../shared/session-identity.ts";
17
- import { resolveIntercomBridge, resolveIntercomSessionTarget, resolveOrchestratorIntercomTarget, resolveSubagentIntercomTarget } from "../../intercom/intercom-bridge.ts";
18
- import { resolveControlConfig } from "../shared/subagent-control.ts";
19
- import { readStatus } from "../../shared/utils.ts";
2
+ import { errorMessage } from "../../shared/unknown.ts";
3
+ import { listSupervisorQuestions, readQuestionContract } from "../shared/supervisor-questions.ts";
20
4
  import { deliverSubagentIntercomMessageEvent } from "../../intercom/result-intercom.ts";
21
- import { sendLiveSubagentMessage } from "../../intercom/live-intercom.ts";
22
- import { buildRevivedAsyncTask, resolveAsyncResumeTarget } from "../background/async-resume.ts";
23
- import { readNestedControlResults, resolveInheritedNestedRouteFromEnv, resolveNestedAsyncDir, resolveNestedParentAddressFromEnv, writeNestedControlRequest, type NestedRunResolutionScope } from "../shared/nested-events.ts";
24
- import { inspectSubagentStatus } from "../background/run-status.ts";
25
- import { resolveSubagentRunId, type ResolvedSubagentRunId } from "../background/run-id-resolver.ts";
5
+ import { resolveSubagentRunId } from "../background/run-id-resolver.ts";
26
6
  import { buildManagementControl, formatRunAction } from "../../shared/status-format.ts";
27
- import { acceptanceInputFromResolved } from "../shared/acceptance.ts";
28
- import { ownedRunStatusResult, ownedRunView, rememberOwnedRun, resolveOwnedRun } from "../shared/run-records.ts";
7
+ import { ownedRunView, resolveOwnedRun } from "../shared/run-records.ts";
8
+ import type { SubagentExecutionResult, OwnedRun, OwnedRunView } from "../../shared/types.ts";
9
+ import type { ExecutorReadDeps, SubagentParamsLike } from "./subagent-params.ts";
10
+ import { nestedResolutionScopeForExecutor } from "./execution-routing.ts";
29
11
  import {
30
- ASYNC_DIR,
31
- getAsyncConfigPath,
32
- RUNNER_ERROR_LOG_FILE,
33
- type SubagentExecutionResult,
34
- type ForegroundResumeRun,
35
- type NestedRunSummary,
36
- type ResolvedAcceptanceConfig,
37
- type SubagentState,
38
- checkSubagentDepth,
39
- resolveCurrentMaxSubagentDepth,
40
- } from "../../shared/types.ts";
41
-
42
- import {
43
- type ExecutorDeps,
44
- type SubagentParamsLike,
45
- } from "./subagent-params.ts";
12
+ resolveResumeTarget,
13
+ resolveNestedResumeTarget,
14
+ type ResumeSourceTarget,
15
+ } from "./resume-target.ts";
16
+ import { LIVE_ACCEPTANCE_OVERRIDE_NOTICE, resumeLiveNestedRun } from "./nested-control.ts";
17
+ import { nudgeSubagentRun } from "./run-nudge.ts";
18
+ import { reviveSavedSubagent, type RevivalInput, type RevivalTarget } from "./saved-revival.ts";
19
+ import { continueQuestionSession } from "./question-continuation.ts";
46
20
 
47
21
  export { writeAsyncInterruptRequest } from "../background/async-control.ts";
48
- export const MUTATING_MANAGEMENT_ACTIONS = new Set(["create", "update", "delete"]);
49
-
50
- export function resolveRequestedCwd(runtimeCwd: string, requestedCwd: string | undefined): string {
51
- return requestedCwd ? path.resolve(runtimeCwd, requestedCwd) : runtimeCwd;
52
- }
53
-
54
- export function nestedResolutionScopeForExecutor(deps: ExecutorDeps): NestedRunResolutionScope | undefined {
55
- if (deps.allowMutatingManagementActions !== false) return undefined;
56
- const route = resolveInheritedNestedRouteFromEnv();
57
- const address = route ? resolveNestedParentAddressFromEnv() : undefined;
58
- return {
59
- routes: route ? [route] : [],
60
- ...(address ? { descendantOf: { parentRunId: address.parentRunId, ...(address.parentStepIndex !== undefined ? { parentStepIndex: address.parentStepIndex } : {}) } } : {}),
61
- };
62
- }
63
-
64
- const LATEST_FOREGROUND_ALIASES = new Set(["last", "latest"]);
65
-
66
- export function resolveRememberedForegroundRun(requested: string | undefined, state: SubagentState): ForegroundResumeRun | undefined {
67
- if (!requested || !state.foregroundRuns?.size) return undefined;
68
- const normalized = requested.trim();
69
- if (!normalized) return undefined;
70
- if (LATEST_FOREGROUND_ALIASES.has(normalized)) {
71
- return [...state.foregroundRuns.values()].sort((left, right) => right.updatedAt - left.updatedAt)[0];
72
- }
73
- const direct = state.foregroundRuns.get(normalized);
74
- const matches = direct ? [direct] : [...state.foregroundRuns.values()].filter((run) => run.runId.startsWith(normalized));
75
- if (matches.length === 0) return undefined;
76
- if (matches.length > 1) throw new Error(formatRunIdAmbiguity("foreground", normalized, matches.map((run) => run.runId)));
77
- return matches[0]!;
78
- }
79
-
80
- function foregroundResumeGuidance(run: ForegroundResumeRun, childSafe: boolean): string {
81
- const childWithSession = run.children.find((child) => child.sessionFile && child.status !== "detached");
82
- if (!childWithSession && run.children.some((child) => child.status === "detached")) return `Completion unconfirmed. Check ${formatRunAction("questions", run.runId, {}, childSafe)} before continuing.`;
83
- if (run.children.length === 1 && childWithSession) return `Continue: ${formatRunAction("resume", run.runId, { message: "..." }, childSafe)}`;
84
- if (!childWithSession) return "Revive: unavailable; no child session file was persisted.";
85
- return `Continue child: ${formatRunAction("resume", run.runId, { index: childWithSession.index, message: "..." }, childSafe)}`;
86
- }
87
-
88
- function compactStatusText(value: string, maxLength = 240): string {
89
- const normalized = value.replace(/\s+/g, " ").trim();
90
- return normalized.length > maxLength ? `${normalized.slice(0, maxLength - 1)}…` : normalized;
91
- }
92
-
93
- function foregroundResultChildren(run: ForegroundResumeRun) {
94
- return run.children.map((child) => ({ child, finalOutput: child.summary }));
95
- }
96
-
97
- function rememberedForegroundState(children: ReturnType<typeof foregroundResultChildren>): "completed" | "paused" | "blocked" | "failed" | "unknown" {
98
- if (children.some(({ child }) => child.status === "failed" || child.status === "timed-out")) return "failed";
99
- if (children.some(({ child }) => child.status === "blocked")) return "blocked";
100
- if (children.some(({ child }) => child.status === "paused")) return "paused";
101
- return children.some(({ child }) => child.status === "detached") ? "unknown" : "completed";
102
- }
103
-
104
- export function rememberedForegroundStatusResult(run: ForegroundResumeRun, childSafe = false): SubagentExecutionResult {
105
- const children = foregroundResultChildren(run);
106
- const childState = rememberedForegroundState(children);
107
- const state = run.error ? "failed" : childState === "completed" && run.pausedReason ? "paused" : childState;
108
- const resumable = children.find(({ child }) => child.sessionFile && child.status !== "detached")?.child;
109
- const lines = [
110
- `Run: ${run.runId}`,
111
- "State: remembered foreground",
112
- `Outcome: ${state}`,
113
- ...(run.error ? [`Error: ${run.error}`] : state === "paused" && run.pausedReason ? [run.pausedReason] : []),
114
- `Mode: ${run.mode}`,
115
- `Updated: ${new Date(run.updatedAt).toISOString()}`,
116
- `Launch cwd: ${run.cwd}`,
117
- "Children:",
118
- ...children.map(({ child, finalOutput }) => ` ${child.index + 1}. ${child.agent} ${child.status}${child.sessionFile ? `, session: ${child.sessionFile}` : ""}${child.artifactPath ? `, artifact: ${child.artifactPath}` : ""}${finalOutput ? `, final: ${compactStatusText(finalOutput)}` : ""}`),
119
- foregroundResumeGuidance(run, childSafe),
120
- `Status: ${formatRunAction("status", run.runId, {}, childSafe)}`,
121
- ];
122
- return {
123
- content: [{ type: "text", text: lines.join("\n") }],
124
- details: {
125
- mode: "management", results: [],
126
- managementControl: buildManagementControl({ state, runId: run.runId, index: resumable?.index, canResume: Boolean(resumable) }),
127
- },
128
- };
129
- }
130
-
131
- function resolveForegroundResumeTarget(params: SubagentParamsLike, state: SubagentState): { runId: string; mode: "single" | "parallel" | "chain"; state: "complete"; agent: string; index: number; intercomTarget: string; cwd: string; sessionFile: string; effectiveAcceptance?: ResolvedAcceptanceConfig } | undefined {
132
- const requested = (params.id ?? params.runId)?.trim();
133
- const run = resolveRememberedForegroundRun(requested, state);
134
- if (!run) return undefined;
135
- if (run.children.length > 1 && params.index === undefined) throw new Error(`Foreground run '${run.runId}' has ${run.children.length} children. Provide index to choose one.`);
136
- const index = params.index ?? 0;
137
- if (!Number.isInteger(index)) throw new Error(`Foreground run '${run.runId}' index must be an integer.`);
138
- if (index < 0 || index >= run.children.length) throw new Error(`Foreground run '${run.runId}' has ${run.children.length} children. Index ${index} is out of range.`);
139
- const child = run.children[index]!;
140
- if (child.status === "detached") throw new Error(`Foreground run '${run.runId}' child ${index} is detached for intercom coordination and cannot be revived safely from the remembered foreground state. Reply to the supervisor request first; after the child exits, start a fresh follow-up if needed.`);
141
- if (!child.sessionFile) throw new Error(`Foreground run '${run.runId}' child ${index} does not have a persisted session file to resume from.`);
142
- if (path.extname(child.sessionFile) !== ".jsonl") throw new Error(`Foreground run '${run.runId}' child ${index} session file must be a .jsonl file: ${child.sessionFile}`);
143
- const sessionFile = path.resolve(child.sessionFile);
144
- if (!fs.existsSync(sessionFile)) throw new Error(`Foreground run '${run.runId}' child ${index} session file does not exist: ${child.sessionFile}`);
145
- return { runId: run.runId, mode: run.mode, state: "complete", agent: child.agent, index, intercomTarget: resolveSubagentIntercomTarget(run.runId, child.agent, index), cwd: run.cwd, sessionFile, effectiveAcceptance: child.effectiveAcceptance };
146
- }
147
-
148
- type AsyncResumeSourceTarget = ReturnType<typeof resolveAsyncResumeTarget> & { source: "async" };
149
- type ForegroundResumeSourceTarget = NonNullable<ReturnType<typeof resolveForegroundResumeTarget>> & { kind: "revive"; source: "foreground" };
150
- type NestedResumeSourceTarget = {
151
- kind: "revive";
152
- source: "nested";
153
- runId: string;
154
- state: "complete" | "failed" | "blocked" | "paused";
155
- agent: string;
156
- index: number;
157
- intercomTarget: string;
158
- cwd?: string;
159
- sessionFile: string;
160
- effectiveAcceptance?: ResolvedAcceptanceConfig;
161
- };
162
- type ResumeSourceTarget = AsyncResumeSourceTarget | ForegroundResumeSourceTarget | NestedResumeSourceTarget;
163
-
164
- function isAsyncRunNotFound(error: unknown): boolean {
165
- return error instanceof Error && error.message.startsWith("Async run not found.");
166
- }
167
-
168
- function isResumeAmbiguity(error: unknown): boolean {
169
- return error instanceof Error && /Ambiguous .*run id prefix/.test(error.message);
170
- }
171
-
172
- function resumeTargetExact(target: { runId: string } | undefined, requested: string): boolean {
173
- return target?.runId === requested;
174
- }
175
-
176
- function escapeRegExp(value: string): string {
177
- return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
178
- }
179
-
180
- function isExactResumeError(error: unknown, source: "async" | "foreground", requested: string): boolean {
181
- if (!(error instanceof Error) || !requested) return false;
182
- return new RegExp(`\\b${source} run '${escapeRegExp(requested)}'`, "i").test(error.message);
183
- }
184
-
185
- function resolveResumeTarget(params: SubagentParamsLike, state: SubagentState): ResumeSourceTarget {
186
- const requested = (params.id ?? params.runId)?.trim() ?? "";
187
- let foregroundTarget: ForegroundResumeSourceTarget | undefined;
188
- let foregroundError: unknown;
189
- let asyncTarget: AsyncResumeSourceTarget | undefined;
190
- let asyncError: unknown;
191
-
192
- try {
193
- const target = resolveForegroundResumeTarget(params, state);
194
- if (target) foregroundTarget = { kind: "revive", source: "foreground", ...target };
195
- } catch (error) {
196
- foregroundError = error;
197
- }
198
- try {
199
- asyncTarget = { source: "async", ...resolveAsyncResumeTarget(params) };
200
- } catch (error) {
201
- asyncError = error;
202
- }
203
-
204
- if (foregroundTarget && asyncTarget) {
205
- const foregroundExact = resumeTargetExact(foregroundTarget, requested);
206
- const asyncExact = resumeTargetExact(asyncTarget, requested);
207
- if (foregroundExact && !asyncExact) return foregroundTarget;
208
- if (asyncExact && !foregroundExact) return asyncTarget;
209
- throw new Error(`Resume id '${requested}' is ambiguous between foreground run '${foregroundTarget.runId}' and async run '${asyncTarget.runId}'. Provide a full run id.`);
210
- }
211
- if (foregroundTarget) {
212
- if (isExactResumeError(asyncError, "async", requested)) throw asyncError;
213
- if (isResumeAmbiguity(asyncError) && !resumeTargetExact(foregroundTarget, requested)) throw asyncError;
214
- return foregroundTarget;
215
- }
216
- if (asyncTarget) {
217
- if (isExactResumeError(foregroundError, "foreground", requested)) throw foregroundError;
218
- if (isResumeAmbiguity(foregroundError) && !resumeTargetExact(asyncTarget, requested)) throw foregroundError;
219
- return asyncTarget;
220
- }
221
- if (foregroundError && !isAsyncRunNotFound(asyncError)) throw foregroundError;
222
- if (foregroundError) throw foregroundError;
223
- if (asyncError) throw asyncError;
224
- throw new Error("Run not found. Provide id or runId.");
225
- }
226
-
227
- function getAsyncInterruptTarget(state: SubagentState, runId: string | undefined): { asyncId: string; asyncDir: string } | undefined {
228
- if (runId) {
229
- const direct = state.asyncJobs.get(runId);
230
- if (direct) return { asyncId: direct.asyncId, asyncDir: direct.asyncDir };
231
- const owned = resolveOwnedRun(state, runId);
232
- return owned?.asyncDir ? { asyncId: owned.runId, asyncDir: owned.asyncDir } : undefined;
233
- }
234
- let newest: { asyncId: string; asyncDir: string; updatedAt: number } | undefined;
235
- for (const job of state.asyncJobs.values()) {
236
- if (job.status !== "running") continue;
237
- if (!newest || (job.updatedAt ?? 0) > newest.updatedAt) {
238
- newest = { asyncId: job.asyncId, asyncDir: job.asyncDir, updatedAt: job.updatedAt ?? 0 };
239
- }
240
- }
241
- for (const run of state.ownedRuns?.values() ?? []) {
242
- if (!run.asyncDir || (newest && run.startedAt <= newest.updatedAt)) continue;
243
- const status = readStatus(run.asyncDir);
244
- if (status ? status.state !== "running" && status.state !== "queued" : !run.pid || !questionProcessAlive({ pid: run.pid })) continue;
245
- newest = { asyncId: run.runId, asyncDir: run.asyncDir, updatedAt: run.startedAt };
246
- }
247
- return newest ? { asyncId: newest.asyncId, asyncDir: newest.asyncDir } : undefined;
248
- }
249
-
250
- export function extendAsyncTimeoutResult(state: SubagentState, runId: string | undefined, extendMs: number): SubagentExecutionResult {
251
- const target = getAsyncInterruptTarget(state, runId);
252
- const status = target && readStatus(target.asyncDir);
253
- const launch = target && readRunJson<{ runtimeVersion?: number; timeoutMs?: number }>(path.join(target.asyncDir, "launch.json"));
254
- const extendable = status ? status.runtimeVersion === 2 && status.state === "running" && !status.timedOut && status.timeoutAt
255
- : launch?.runtimeVersion === 2 && launch.timeoutMs;
256
- if (!target || !extendable) return {
257
- content: [{ type: "text", text: "No live run with an extendable timeout was found. No extension was requested." }], isError: true, details: { mode: "management", results: [] },
258
- };
259
- try {
260
- writeAsyncControlRequest(target.asyncDir, target.asyncId, "extend", undefined, extendMs);
261
- return { content: [{ type: "text", text: `Requested ${extendMs}ms more for run ${target.asyncId}. Inspect status for the owner's updated deadline.` }],
262
- details: { mode: "management", results: [], managementControl: buildManagementControl({ state: "live", runId: target.asyncId, canInterrupt: true, canExtend: true }) } };
263
- } catch (error) {
264
- return { content: [{ type: "text", text: error instanceof Error ? error.message : String(error) }], isError: true, details: { mode: "management", results: [] } };
265
- }
266
- }
267
-
268
- export function interruptAsyncRun(state: SubagentState, runId: string | undefined, index?: number): SubagentExecutionResult | null {
269
- const target = getAsyncInterruptTarget(state, runId);
270
- if (!target) return null;
271
- const status = readStatus(target.asyncDir);
272
- if (!status || status.runId !== target.asyncId || status.state !== "running") {
273
- return {
274
- content: [{ type: "text", text: `No running async run with a matching control channel was found for '${runId ?? "current"}'.` }],
275
- isError: true,
276
- details: { mode: "management", results: [] },
277
- };
278
- }
279
- if (index !== undefined && !status.indexedControl && (status.steps?.length ?? 0) > 1) return {
280
- content: [{ type: "text", text: "This older runner does not support selected-child stop. No stop was sent; whole-run stop remains an explicit separate action." }], isError: true, details: { mode: "management", results: [] },
281
- };
282
- if (index !== undefined && (!Number.isSafeInteger(index) || index < 0 || status.steps?.[index]?.status !== "running")) {
283
- return { content: [{ type: "text", text: `Async run ${target.asyncId} has no running child at index ${index}. No siblings were stopped.` }], isError: true, details: { mode: "management", results: [] } };
284
- }
285
- try {
286
- writeAsyncInterruptRequest(target.asyncDir, target.asyncId, index);
287
- const tracked = state.asyncJobs.get(target.asyncId);
288
- if (tracked) {
289
- tracked.activityState = undefined;
290
- tracked.updatedAt = Date.now();
291
- }
292
- return {
293
- content: [{ type: "text", text: `Interrupt requested for async run ${target.asyncId}${index !== undefined ? ` child ${index} only` : ""}. Agent and command exit are not yet confirmed.` }],
294
- details: { mode: "management", results: [], managementControl: buildManagementControl({ state: "live", runId: target.asyncId }) },
295
- };
296
- } catch (error) {
297
- const message = error instanceof Error ? error.message : String(error);
298
- return {
299
- content: [{ type: "text", text: `Failed to interrupt async run ${target.asyncId}: ${message}` }],
300
- isError: true,
301
- details: { mode: "management", results: [] },
302
- };
303
- }
22
+ export {
23
+ MUTATING_MANAGEMENT_ACTIONS,
24
+ resolveRequestedCwd,
25
+ nestedResolutionScopeForExecutor,
26
+ } from "./execution-routing.ts";
27
+ export {
28
+ rememberedForegroundStatusResult,
29
+ resolveRememberedForegroundRun,
30
+ } from "./foreground-memory.ts";
31
+ export { extendAsyncTimeoutResult, interruptAsyncRun } from "./run-interrupt.ts";
32
+ export { interruptNestedRun } from "./nested-control.ts";
33
+ export { nudgeSubagentRun } from "./run-nudge.ts";
34
+ export { reviveSavedSubagent } from "./saved-revival.ts";
35
+
36
+ type ReadResumeInput = Readonly<Omit<RevivalInput, "deps">> & { readonly deps: ExecutorReadDeps };
37
+ type Resolution =
38
+ | { readonly kind: "result"; readonly result: SubagentExecutionResult }
39
+ | { readonly kind: "target"; readonly target: ResumeSourceTarget };
40
+ function failure(text: string): SubagentExecutionResult {
41
+ return {
42
+ content: [{ type: "text", text }],
43
+ isError: true,
44
+ details: { mode: "management", results: [] },
45
+ };
46
+ }
47
+ function requestedOwnedId(params: SubagentParamsLike): string | undefined {
48
+ const id = params.id ?? params.runId;
49
+ return id !== undefined && id.length > 0 && (params.dir === undefined || params.dir.length === 0)
50
+ ? id
51
+ : undefined;
304
52
  }
305
-
306
- function nestedRunSessionFile(run: NestedRunSummary): string | undefined {
307
- return run.sessionFile ?? (run.steps?.length === 1 ? run.steps[0]?.sessionFile : undefined);
308
- }
309
-
310
- function nestedRunAgent(run: NestedRunSummary): string | undefined {
311
- return run.agent ?? run.agents?.[0] ?? (run.steps?.length === 1 ? run.steps[0]?.agent : undefined);
312
- }
313
-
314
- function pathWithin(base: string, candidate: string): boolean {
315
- const resolvedBase = path.resolve(base);
316
- const resolvedCandidate = path.resolve(candidate);
317
- return resolvedCandidate === resolvedBase || resolvedCandidate.startsWith(`${resolvedBase}${path.sep}`);
318
- }
319
-
320
- function validateNestedSessionFile(run: NestedRunSummary, trustedSessionRoots: string[]): string {
321
- const sessionFile = nestedRunSessionFile(run);
322
- if (!sessionFile) throw new Error(`Nested run '${run.id}' does not have a persisted session file to resume from.`);
323
- if (path.extname(sessionFile) !== ".jsonl") throw new Error(`Nested run '${run.id}' session file must be a .jsonl file: ${sessionFile}`);
324
- const resolved = path.resolve(sessionFile);
325
- if (!path.isAbsolute(sessionFile)) throw new Error(`Nested run '${run.id}' session file must be absolute: ${sessionFile}`);
326
- if (!fs.existsSync(resolved)) throw new Error(`Nested run '${run.id}' session file does not exist: ${sessionFile}`);
327
- const stat = fs.lstatSync(resolved);
328
- if (!stat.isFile() || stat.isSymbolicLink()) throw new Error(`Nested run '${run.id}' session file is not a regular file: ${sessionFile}`);
329
- const realSessionFile = fs.realpathSync(resolved);
330
- const trustedRoots = trustedSessionRoots
331
- .filter((root) => fs.existsSync(root))
332
- .map((root) => fs.realpathSync(root));
333
- if (!trustedRoots.some((root) => pathWithin(root, realSessionFile))) {
334
- throw new Error(`Nested run '${run.id}' session file is outside trusted nested session roots: ${sessionFile}`);
335
- }
336
- if (!realSessionFile.split(path.sep).includes(run.id)) {
337
- throw new Error(`Nested run '${run.id}' session file is not under that nested run's session directory: ${sessionFile}`);
338
- }
339
- return realSessionFile;
340
- }
341
-
342
- function resolveNestedResumeTarget(match: ResolvedSubagentRunId & { kind: "nested" }, trustedSessionRoots: string[]): NestedResumeSourceTarget {
343
- const run = match.match.run;
344
- if (run.state === "running" || run.state === "queued") throw new Error(`Nested run '${run.id}' is live; route the follow-up to the owner process instead.`);
345
- const agent = nestedRunAgent(run);
346
- if (!agent) throw new Error(`Could not determine child agent for nested run '${run.id}'.`);
347
- const state = run.state === "complete" || run.state === "failed" || run.state === "blocked" || run.state === "paused" ? run.state : "failed";
348
- const asyncDir = resolveNestedAsyncDir(match.match.rootRunId, run);
349
- const effectiveAcceptance = asyncDir ? readStatus(asyncDir)?.steps?.[0]?.acceptance?.effectiveAcceptance : undefined;
350
- return {
351
- kind: "revive",
352
- source: "nested",
353
- runId: run.id,
354
- state,
355
- agent,
356
- index: 0,
357
- intercomTarget: resolveSubagentIntercomTarget(run.id, agent, 0),
358
- cwd: asyncDir ? path.dirname(asyncDir) : undefined,
359
- sessionFile: validateNestedSessionFile(run, trustedSessionRoots),
360
- effectiveAcceptance,
361
- };
362
- }
363
-
364
- async function waitForNestedControlResult(target: ResolvedSubagentRunId & { kind: "nested" }, requestId: string, timeoutMs = 1_000) {
365
- const deadline = Date.now() + timeoutMs;
366
- while (Date.now() < deadline) {
367
- const result = readNestedControlResults(target.match.route).find((candidate) => candidate.requestId === requestId && candidate.targetRunId === target.match.run.id);
368
- if (result) return result;
369
- await new Promise((resolve) => setTimeout(resolve, 50));
370
- }
371
- return undefined;
372
- }
373
-
374
- async function sendNestedControlRequest(target: ResolvedSubagentRunId & { kind: "nested" }, action: "interrupt" | "resume", message?: string, index?: number) {
375
- const requestId = randomUUID();
376
- const targetChildIndex = target.match.run.path?.[0]?.stepIndex ?? target.match.run.parentStepIndex;
377
- writeNestedControlRequest(target.match.route, {
378
- ts: Date.now(),
379
- requestId,
380
- targetRunId: target.match.run.id,
381
- ...(targetChildIndex !== undefined ? { targetChildIndex } : {}),
382
- action,
383
- ...(index !== undefined ? { index } : {}),
384
- ...(message ? { message } : {}),
385
- });
386
- return waitForNestedControlResult(target, requestId);
387
- }
388
-
389
- function directNestedAsyncInterrupt(target: ResolvedSubagentRunId & { kind: "nested" }, index?: number): SubagentExecutionResult | undefined {
390
- const run = target.match.run;
391
- const asyncDir = resolveNestedAsyncDir(target.match.rootRunId, run);
392
- if (!asyncDir) return undefined;
393
- const status = readStatus(asyncDir);
394
- if (!status || status.runId !== run.id || status.state !== "running") return undefined;
395
- if (index !== undefined && !status.indexedControl && (status.steps?.length ?? 0) > 1) return undefined;
396
- if (index !== undefined && status.steps?.[index]?.status !== "running") return { content: [{ type: "text", text: `No running nested child at index ${index}. No siblings were stopped.` }], isError: true, details: { mode: "management", results: [] } };
397
- try {
398
- writeAsyncInterruptRequest(asyncDir, run.id, index);
399
- return { content: [{ type: "text", text: `Interrupt requested for nested async run ${run.id}.` }], details: { mode: "management", results: [], managementControl: buildManagementControl({ state: "live", runId: run.id, intercomTarget: run.intercomTarget ?? run.leafIntercomTarget, canInterrupt: true }) } };
400
- } catch (error) {
401
- const message = error instanceof Error ? error.message : String(error);
402
- return { content: [{ type: "text", text: `Failed to interrupt nested async run ${run.id}: ${message}` }], isError: true, details: { mode: "management", results: [] } };
403
- }
404
- }
405
-
406
- export async function interruptNestedRun(target: ResolvedSubagentRunId & { kind: "nested" }, index?: number): Promise<SubagentExecutionResult> {
407
- const run = target.match.run;
408
- if (run.state === "complete") return { content: [{ type: "text", text: `Nested run ${run.id} is already complete and cannot be interrupted.` }], isError: true, details: { mode: "management", results: [] } };
409
- if (run.state === "failed") return { content: [{ type: "text", text: `Nested run ${run.id} has failed and cannot be interrupted.` }], isError: true, details: { mode: "management", results: [] } };
410
- if (run.state === "paused") return { content: [{ type: "text", text: `Nested run ${run.id} is already paused.` }], isError: true, details: { mode: "management", results: [] } };
411
- if (index !== undefined && run.indexedControl !== true) return directNestedAsyncInterrupt(target, index) ?? {
412
- content: [{ type: "text", text: "This nested owner does not advertise selected-child stop. No stop was sent; inspect the child or explicitly stop the whole run." }], isError: true, details: { mode: "management", results: [] },
413
- };
414
- const result = await sendNestedControlRequest(target, "interrupt", undefined, index);
415
- if (result?.ok) return {
416
- content: [{ type: "text", text: result.message }],
417
- details: {
418
- mode: "management", results: [],
419
- managementControl: buildManagementControl({ state: "live", runId: run.id, intercomTarget: run.intercomTarget ?? run.leafIntercomTarget, canResume: true, canInterrupt: true }),
420
- },
421
- };
422
- const direct = directNestedAsyncInterrupt(target, index);
423
- if (direct) return direct;
424
- if (result) return { content: [{ type: "text", text: result.message }], isError: true, details: { mode: "management", results: [] } };
425
- return { content: [{ type: "text", text: `Nested run ${run.id} owner is not reachable and no safe direct async interrupt fallback is available.` }], isError: true, details: { mode: "management", results: [] } };
426
- }
427
-
428
- const LIVE_ACCEPTANCE_OVERRIDE_NOTICE = "Acceptance override applies only to revive and was not applied to this live delivery.";
429
-
430
53
  export function liveLaunchOverrideNotice(params: SubagentParamsLike): string | undefined {
431
- if (params.acceptance !== undefined) return LIVE_ACCEPTANCE_OVERRIDE_NOTICE;
432
- return [params.agent, params.model, params.cwd, params.output, params.outputMode, params.outputSchema, params.skill, params.maxOutput, params.control, params.artifacts, params.share].some((value) => value !== undefined)
433
- ? "Launch overrides change a newly started continuation, not this live child. No launch settings were changed."
434
- : undefined;
435
- }
436
-
437
- async function resumeLiveNestedRun(input: { target: ResolvedSubagentRunId & { kind: "nested" }; message: string; index?: number; acceptanceOverrideSupplied: boolean; pi: ExtensionAPI; childSafe: boolean }): Promise<SubagentExecutionResult> {
438
- const run = input.target.match.run;
439
- const result = await sendNestedControlRequest(input.target, "resume", input.message, input.index);
440
- if (result?.ok) {
441
- return { content: [{ type: "text", text: [result.message, input.acceptanceOverrideSupplied ? LIVE_ACCEPTANCE_OVERRIDE_NOTICE : undefined].filter(Boolean).join("\n") }], details: { mode: "management", results: [] } };
442
- }
443
- const asyncDir = input.index !== undefined ? resolveNestedAsyncDir(input.target.match.rootRunId, run) : undefined;
444
- const selected = asyncDir ? readStatus(asyncDir)?.steps?.[input.index!] : undefined;
445
- const directTarget = input.index === undefined ? run.leafIntercomTarget ?? run.intercomTarget
446
- : selected?.status === "running" ? resolveSubagentIntercomTarget(run.id, selected.agent, input.index) : undefined;
447
- if (directTarget) {
448
- const delivered = await deliverSubagentIntercomMessageEvent(
449
- input.pi.events,
450
- directTarget,
451
- `Follow-up for nested run ${run.id}:\n\n${input.message}`,
452
- 500,
453
- { source: "nested-resume-fallback", runId: run.id, agent: run.agent, index: input.index ?? run.currentStep },
454
- );
455
- if (delivered) {
456
- return { content: [{ type: "text", text: [`Delivered follow-up directly to live nested run ${run.id}.`, input.acceptanceOverrideSupplied ? LIVE_ACCEPTANCE_OVERRIDE_NOTICE : undefined].filter(Boolean).join("\n") }], details: { mode: "management", results: [] } };
457
- }
458
- }
459
- if (result) return { content: [{ type: "text", text: result.message }], isError: true, details: { mode: "management", results: [] } };
460
- return { content: [{ type: "text", text: `Nested run ${run.id} appears live but its owner route is not reachable. Wait for completion, then retry ${formatRunAction("resume", run.id, { message: "..." }, input.childSafe)}.` }], isError: true, details: { mode: "management", results: [] } };
461
- }
462
-
463
- function terminalNudgeResult(runId: string, deps: ExecutorDeps): SubagentExecutionResult | undefined {
464
- const owned = resolveOwnedRun(deps.state, runId);
465
- if (owned) {
466
- const result = ownedRunStatusResult(owned, deps.state, undefined, { childSafe: Boolean(nestedResolutionScopeForExecutor(deps)) });
467
- const state = result.details.run!.state;
468
- if (state === "live") return undefined;
469
- return { ...result, content: [{ type: "text", text: `Nudge not sent: ${state === "unknown" ? "completion is unconfirmed" : `run is already ${state}`}. No child was restarted.\n\n${result.content.map((part) => part.type === "text" ? part.text : "").join("\n")}` }] };
470
- }
471
- const remembered = deps.state.foregroundRuns?.get(runId);
472
- const status = remembered
473
- ? rememberedForegroundStatusResult(remembered, Boolean(nestedResolutionScopeForExecutor(deps)))
474
- : inspectSubagentStatus({ id: runId }, { state: deps.state, nested: nestedResolutionScopeForExecutor(deps) });
475
- const state = status.details.managementControl?.state;
476
- if (status.isError || !state || state === "live" || state === "unknown") return undefined;
477
- return {
478
- ...status,
479
- content: [{ type: "text", text: `Nudge not sent: run is already ${state}. No child was restarted.\n\n${status.content.filter((part) => part.type === "text").map((part) => part.text).join("\n")}` }],
480
- };
481
- }
482
-
483
- export async function nudgeSubagentRun(input: {
484
- params: SubagentParamsLike;
485
- deps: ExecutorDeps;
486
- ctx?: ExtensionContext;
487
- }): Promise<SubagentExecutionResult> {
488
- const message = input.params.message?.trim() || "What are you blocked on? Reply with the smallest next step, or state the exact decision you need.";
489
- const requestedId = input.params.id ?? input.params.runId;
490
- let runId: string;
491
- let agent: string;
492
- let index: number;
493
- let target: string;
494
- let resolvedRunId: string | undefined;
495
-
496
- try {
497
- const owned = input.params.dir ? undefined : resolveOwnedRun(input.deps.state, requestedId ?? getAsyncInterruptTarget(input.deps.state, undefined)?.asyncId ?? "latest");
498
- if (owned) {
499
- resolvedRunId = owned.runId;
500
- const terminal = terminalNudgeResult(owned.runId, input.deps);
501
- if (terminal) return terminal;
502
- const view = ownedRunView(owned, input.deps.state);
503
- const children = view.children.filter((child) => child.state === "live" && (input.params.index === undefined || input.params.index === child.index));
504
- if (children.length !== 1) throw new Error(`Run '${owned.runId}' has ${children.length} matching live children. Provide index to choose one.`);
505
- const child = children[0]!;
506
- if (child.activity?.status === "pending") throw new Error(`Child ${child.index} is waiting to start in run ${owned.runId}. No message was sent and no continuation was started.`);
507
- runId = owned.runId; agent = child.agent; index = child.index;
508
- target = resolveSubagentIntercomTarget(runId, agent, index);
509
- } else {
510
- const resolved = requestedId ? resolveSubagentRunId(requestedId, { state: input.deps.state, nested: nestedResolutionScopeForExecutor(input.deps) }) : undefined;
511
- const remembered = !resolved && requestedId ? resolveRememberedForegroundRun(requestedId, input.deps.state) : undefined;
512
- resolvedRunId = resolved?.id ?? remembered?.runId;
513
- const terminal = resolvedRunId ? terminalNudgeResult(resolvedRunId, input.deps) : undefined;
514
- if (terminal) return terminal;
515
- if (resolved?.kind === "nested") {
516
- const run = resolved.match.run;
517
- const state = run.state === "running" || run.state === "queued" ? "live" : run.state === "complete" ? "completed" : run.state === "paused" || run.state === "blocked" || run.state === "failed" ? run.state : "unknown";
518
- const intercomTarget = run.intercomTarget ?? run.leafIntercomTarget;
519
- const childSafe = Boolean(nestedResolutionScopeForExecutor(input.deps));
520
- const valid = [formatRunAction("status", run.id, {}, childSafe)];
521
- if (state === "live" || run.sessionFile) valid.push(formatRunAction("resume", run.id, { message: "..." }, childSafe));
522
- if (state === "live") valid.push(formatRunAction("interrupt", run.id, {}, childSafe));
523
- return {
524
- content: [{ type: "text", text: `Nested run ${run.id} cannot be nudged. Valid actions: ${valid.join(" or ")}${intercomTarget ? `. Intercom target: ${intercomTarget}` : "."}` }],
525
- isError: true,
526
- details: { mode: "management", results: [], managementControl: buildManagementControl({ state, runId: run.id, intercomTarget, canNudge: false, canResume: state === "live" || Boolean(run.sessionFile), canInterrupt: state === "live", unavailableActions: { nudge: "Nested runs do not support nudge; use an advertised exact action or intercom target." } }) },
527
- };
528
- }
529
- if (remembered) return rememberedForegroundStatusResult(remembered, Boolean(nestedResolutionScopeForExecutor(input.deps)));
530
- const asyncTarget = resolveAsyncResumeTarget({ id: input.params.id ?? (!requestedId ? getAsyncInterruptTarget(input.deps.state, undefined)?.asyncId : undefined), runId: input.params.runId, dir: input.params.dir, index: input.params.index });
531
- if (asyncTarget.kind !== "live") {
532
- return terminalNudgeResult(asyncTarget.runId, input.deps)
533
- ?? inspectSubagentStatus({ id: asyncTarget.runId }, { state: input.deps.state, nested: nestedResolutionScopeForExecutor(input.deps) });
534
- }
535
- runId = asyncTarget.runId;
536
- agent = asyncTarget.agent;
537
- index = asyncTarget.index;
538
- target = asyncTarget.intercomTarget;
539
- }
540
- } catch (error) {
541
- const terminal = resolvedRunId ? terminalNudgeResult(resolvedRunId, input.deps) : undefined;
542
- if (terminal) return terminal;
543
- const text = error instanceof Error ? error.message : String(error);
544
- return { content: [{ type: "text", text }], isError: true, details: { mode: "management", results: [] } };
545
- }
546
-
547
- const result = await sendLiveSubagentMessage(input.deps.pi.events, {
548
- to: target,
549
- message: `Nudge for subagent run ${runId} (${agent}${index !== undefined ? ` step ${index + 1}` : ""}):\n\n${message}`,
550
- delivery: "steer",
551
- extra: { source: "subagent-nudge", runId, agent, index },
552
- });
553
- if (!result.delivered) {
554
- const terminal = terminalNudgeResult(runId, input.deps);
555
- if (terminal) return terminal;
556
- return { content: [{ type: "text", text: [`Nudge was not delivered.`, `Run: ${runId}`, `Intercom target: ${target}`, result.reason ? `Reason: ${result.reason}` : undefined].filter((line): line is string => Boolean(line)).join("\n") }], isError: true, details: { mode: "management", results: [] } };
557
- }
558
- return {
559
- content: [{ type: "text", text: [`Nudge delivered to live subagent.`, `Run: ${runId}`, `Agent: ${agent}`, `Intercom target: ${target}`].join("\n") }],
560
- details: { mode: "management", results: [], managementControl: buildManagementControl({ state: "live", runId, index, intercomTarget: target, canNudge: true, canResume: true, canInterrupt: true }) },
561
- };
562
- }
563
-
564
- export async function resumeAsyncRun(input: {
565
- params: SubagentParamsLike;
566
- requestCwd: string;
567
- ctx: ExtensionContext;
568
- deps: ExecutorDeps;
569
- }): Promise<SubagentExecutionResult> {
570
- const followUp = (input.params.message ?? input.params.task ?? "").trim();
571
- if (!followUp) {
572
- return {
573
- content: [{ type: "text", text: "action='resume' requires message." }],
574
- isError: true,
575
- details: { mode: "management", results: [] },
576
- };
577
- }
578
-
579
- let target: ResumeSourceTarget;
580
- const parentSessionFile = input.ctx.sessionManager.getSessionFile() ?? null;
581
- const nudgeContinuation = (runId: string, sessionFile?: string) => {
582
- if (!sessionFile) return;
583
- for (const candidate of input.deps.state.ownedRuns?.values() ?? []) {
584
- if (candidate.runId === runId) continue;
585
- if (!candidate.children.some((child) => child.sessionFile === sessionFile)
586
- && input.deps.state.asyncJobs.get(candidate.runId)?.sessionFile !== sessionFile) continue;
587
- const active = ownedRunView(candidate, input.deps.state, { readConfiguration: false }).children.find((entry) => entry.sessionFile === sessionFile && entry.state === "live");
588
- if (!active) continue;
589
- return nudgeSubagentRun({ params: { ...input.params, dir: undefined, id: candidate.runId, index: active.index, message: followUp }, deps: input.deps, ctx: input.ctx }).then((result) => {
590
- const notice = liveLaunchOverrideNotice(input.params);
591
- if (notice) result.content.push({ type: "text", text: notice });
592
- return result;
593
- });
594
- }
595
- };
596
- try {
597
- const requestedId = input.params.id ?? input.params.runId;
598
- const pendingQuestions = requestedId && !input.params.dir
599
- ? listSupervisorQuestions(input.ctx.sessionManager.getSessionId(), requestedId).filter((question) => question.state === "awaiting_input" || question.state === "answer_pending")
600
- : [];
601
- if (pendingQuestions.length) return continueQuestionSession(input, pendingQuestions);
602
- const owned = requestedId && !input.params.dir ? resolveOwnedRun(input.deps.state, requestedId) : undefined;
603
- if (owned) {
604
- const view = ownedRunView(owned, input.deps.state);
605
- if (view.children.length > 1 && input.params.index === undefined) throw new Error(`Run '${owned.runId}' has ${view.children.length} children. Provide index to choose one.`);
606
- const child = view.children.find((entry) => entry.index === (input.params.index ?? 0));
607
- if (!child) throw new Error(`Run '${owned.runId}' has no child at index ${input.params.index ?? 0}.`);
608
- if (child.state === "live") {
609
- const result = await nudgeSubagentRun({ params: { ...input.params, id: owned.runId, message: followUp }, deps: input.deps, ctx: input.ctx });
610
- const notice = liveLaunchOverrideNotice(input.params);
611
- if (notice) result.content.push({ type: "text", text: notice });
612
- return result;
613
- }
614
- const continuation = nudgeContinuation(owned.runId, child.sessionFile);
615
- if (continuation) return await continuation;
616
- const contract = readQuestionContract(owned.runId, child.index);
617
- return reviveSavedSubagent(input, {
618
- ...contract, runId: owned.runId, agent: child.agent, index: child.index, source: owned.source,
619
- cwd: child.launch?.cwd ?? owned.cwd, sessionFile: child.sessionFile,
620
- effectiveAcceptance: contract?.effectiveAcceptance ?? child.result?.acceptance?.effectiveAcceptance,
621
- model: child.result?.model,
622
- });
623
- }
624
- const resolved = requestedId ? resolveSubagentRunId(requestedId, { state: input.deps.state, nested: nestedResolutionScopeForExecutor(input.deps) }) : undefined;
625
- if (resolved?.kind === "nested") {
626
- if (resolved.match.run.state === "running" || resolved.match.run.state === "queued") {
627
- return resumeLiveNestedRun({ target: resolved, message: followUp, index: input.params.index, acceptanceOverrideSupplied: input.params.acceptance !== undefined, pi: input.deps.pi, childSafe: Boolean(nestedResolutionScopeForExecutor(input.deps)) });
628
- }
629
- const trustedSessionRoots = [
630
- ...(input.deps.config.defaultSessionDir ? [path.resolve(input.deps.expandTilde(input.deps.config.defaultSessionDir))] : []),
631
- ...(parentSessionFile ? [input.deps.getSubagentSessionRoot(parentSessionFile)] : []),
632
- ];
633
- target = resolveNestedResumeTarget(resolved, trustedSessionRoots);
634
- } else {
635
- target = resolveResumeTarget(input.params, input.deps.state);
636
- }
637
- } catch (error) {
638
- const message = error instanceof Error ? error.message : String(error);
639
- const requestedId = input.params.id ?? input.params.runId;
640
- if (requestedId && !input.params.dir && (isAsyncRunNotFound(error) || message.includes("is detached for intercom coordination"))) {
641
- try {
642
- const questions = listSupervisorQuestions(input.ctx.sessionManager.getSessionId(), requestedId);
643
- if (questions.length) return continueQuestionSession(input, questions);
644
- } catch (questionError) {
645
- return { content: [{ type: "text", text: questionError instanceof Error ? questionError.message : String(questionError) }], isError: true, details: { mode: "management", results: [] } };
646
- }
647
- }
648
- return { content: [{ type: "text", text: message }], isError: true, details: { mode: "management", results: [] } };
649
- }
650
-
651
- if (target.kind === "live") {
652
- const delivered = await deliverSubagentIntercomMessageEvent(
653
- input.deps.pi.events,
654
- target.intercomTarget,
655
- `Follow-up for async run ${target.runId} (${target.agent}):\n\n${followUp}`,
656
- 500,
657
- { source: "async-resume", runId: target.runId, agent: target.agent, index: target.index },
658
- );
659
- if (delivered) {
660
- return {
661
- content: [{ type: "text", text: [`Delivered follow-up to live async child.`, `Run: ${target.runId}`, `Intercom target: ${target.intercomTarget}`, input.params.acceptance !== undefined ? LIVE_ACCEPTANCE_OVERRIDE_NOTICE : undefined].filter(Boolean).join("\n") }],
662
- details: { mode: "management", results: [], managementControl: buildManagementControl({ state: "live", runId: target.runId, index: target.index, intercomTarget: target.intercomTarget, canNudge: true, canResume: true, canInterrupt: true }) },
663
- };
664
- }
665
- return {
666
- content: [{ type: "text", text: [`Async child appears live but its intercom target is not registered.`, `Run: ${target.runId}`, `Intercom target: ${target.intercomTarget}`, `Wait for completion, then retry ${formatRunAction("resume", target.runId, { message: "..." }, Boolean(nestedResolutionScopeForExecutor(input.deps)))}.`].join("\n") }],
667
- isError: true,
668
- details: { mode: "management", results: [] },
669
- };
670
- }
671
-
672
- return nudgeContinuation(target.runId, target.sessionFile) ?? reviveSavedSubagent(input, target);
673
- }
674
-
675
- function continueQuestionSession(input: Parameters<typeof reviveSavedSubagent>[0], questions: SupervisorQuestionView[]): SubagentExecutionResult {
676
- if (input.params.index === undefined && new Set(questions.map((question) => question.index)).size > 1) throw new Error("Provide index to choose which child question to continue.");
677
- const question = questions.findLast((entry) => input.params.index === undefined || entry.index === input.params.index);
678
- if (!question) throw new Error("No saved question for that child index.");
679
- if (questionProcessAlive(question)) throw new Error("The question's child is still alive. Answer the question or stop it before continuing.");
680
- if (question.delivery?.kind === "revive") throw new Error(`Use continue with the answer's continuation run ${question.delivery.runId}.`);
681
- const revival = question.revival;
682
- const prior = revival ? resolveSubagentRunId(revival.runId, { state: input.deps.state, nested: nestedResolutionScopeForExecutor(input.deps) }) : undefined;
683
- const priorExists = prior?.kind === "async"
684
- ? prior.location.resultPath || (prior.location.asyncDir && fs.existsSync(path.join(prior.location.asyncDir, "status.json")))
685
- : Boolean(prior);
686
- if (revival && (questionProcessAlive(revival) || priorExists || fs.existsSync(getAsyncConfigPath(revival.runId)) || fs.existsSync(path.join(getRunMetadataDir(revival.runId), "launch.json")) || fs.existsSync(path.join(ASYNC_DIR, revival.runId, RUNNER_ERROR_LOG_FILE)))) {
687
- throw new Error(`Continuation ${revival.runId} may already have launched. Inspect or continue that run; the original question was not restarted.`);
688
- }
689
- const message = [input.params.message ?? input.params.task, question.answer ? `Saved supervisor answer:\n${question.answer.message}\n\nOriginal question:\n${question.message}` : undefined].filter(Boolean).join("\n\n");
690
- const result = reviveSavedSubagent({ ...input, params: { ...input.params, message } }, { ...question, source: "question" });
691
- if (!result.isError && question.answer && !question.delivery) recordQuestionDelivery(question, { kind: "revive", runId: result.details.asyncId!, deliveredAt: Date.now() });
692
- return result;
693
- }
694
-
695
- export function reviveSavedSubagent(input: {
696
- params: SubagentParamsLike;
697
- requestCwd: string;
698
- ctx: ExtensionContext;
699
- deps: ExecutorDeps;
700
- }, target: Pick<ResumeSourceTarget, "runId" | "agent" | "index" | "cwd" | "sessionFile" | "effectiveAcceptance"> & SupervisorRunContract & { source: string; model?: string }, runId: string = randomUUID()): SubagentExecutionResult {
701
- const followUp = (input.params.message ?? input.params.task ?? "").trim();
702
- const parentSessionFile = input.ctx.sessionManager.getSessionFile() ?? null;
703
- if (!target.sessionFile || path.extname(target.sessionFile) !== ".jsonl" || !fs.existsSync(target.sessionFile)) {
704
- return { content: [{ type: "text", text: `Saved child session file is unavailable: ${target.sessionFile ?? "none"}. No child was started; any saved answer remains pending.` }], isError: true, details: { mode: "management", results: [] } };
705
- }
706
- const { blocked, depth, maxDepth } = checkSubagentDepth(input.deps.config.maxSubagentDepth);
707
- if (blocked) {
708
- return {
709
- content: [{ type: "text", text: `Nested subagent resume blocked (depth=${depth}, max=${maxDepth}). Complete the follow-up directly instead.` }],
710
- isError: true,
711
- details: { mode: "management", results: [] },
712
- };
713
- }
714
-
715
- input.deps.state.currentSessionId = resolveCurrentSessionId(input.ctx.sessionManager);
716
- const contract = readQuestionContract(target.runId, target.index) ?? target;
717
- const savedLaunch = input.params.agent === undefined && contract.launch ? { ...contract.launch, ...contract.effectiveConfiguration } : undefined;
718
- const generatedOutputFilename = input.params.output === undefined ? contract.launch?.generatedOutputFilename : undefined;
719
- const effectiveCwd = input.params.cwd ?? savedLaunch?.cwd ?? target.cwd ?? input.requestCwd;
720
- const scope: AgentScope = resolveExecutionAgentScope(input.params.agentScope);
721
- if (!savedLaunch && !input.params.agent) {
722
- return { content: [{ type: "text", text: `Run '${target.runId}' predates saved launch configuration. Its session and known result remain available, but the original profile cannot be reconstructed safely. Continue with agent: '${target.agent}' to explicitly use that profile's current configuration, including model and thinking; known output and acceptance are retained unless overridden.` }], isError: true, details: { mode: "management", results: [] } };
723
- }
724
- const discoveredAgents = savedLaunch ? [savedLaunch.agent] : input.deps.discoverAgents(effectiveCwd, scope, { projectTrusted: input.ctx.isProjectTrusted() }).agents;
725
- const fallbackTarget = resolveIntercomSessionTarget(input.deps.pi.getSessionName(), input.ctx.sessionManager.getSessionId());
726
- const orchestratorTarget = resolveOrchestratorIntercomTarget(input.deps.pi.events, fallbackTarget);
727
- const intercomBridge = resolveIntercomBridge(orchestratorTarget);
728
- const inheritedModel = providerQualifiedModelId(input.ctx.model?.provider, input.ctx.model?.id);
729
- const agents = savedLaunch ? discoveredAgents : discoveredAgents.map((agent) => agent.model || !inheritedModel ? agent : { ...agent, model: inheritedModel });
730
- const selectedAgent = input.params.agent ?? target.agent;
731
- const profile = agents.find((agent) => agent.name === selectedAgent);
732
- const agentConfig = profile && savedLaunch ? { ...profile, thinking: savedLaunch.thinking ?? profile.thinking, maxExecutionTimeMs: savedLaunch.maxExecutionTimeMs, maxTokens: savedLaunch.maxTokens } : profile;
733
- if (!agentConfig) {
734
- return {
735
- content: [{ type: "text", text: `Unknown agent for resume: ${selectedAgent}` }],
736
- isError: true,
737
- details: { mode: "management", results: [] },
738
- };
739
- }
740
-
741
- saveQuestionOwner(runId, input.ctx.sessionManager.getSessionId());
742
- const prior = input.deps.state.ownedRuns?.get(target.runId);
743
- rememberOwnedRun(input.deps.state, {
744
- runId, ownerSessionId: input.ctx.sessionManager.getSessionId(), rootRunId: prior?.rootRunId ?? target.runId,
745
- predecessorRunId: target.runId, predecessorIndex: target.index,
746
- source: "async", mode: "single", cwd: effectiveCwd, task: followUp, startedAt: Date.now(),
747
- children: [{ agent: selectedAgent, index: 0, task: followUp, label: prior?.children.find((child) => child.index === target.index)?.label, sessionFile: target.sessionFile }],
748
- });
749
- const modelOverride = input.params.model;
750
- const skill = normalizeSkillInput(input.params.skill);
751
- const availableModels = input.ctx.modelRegistry.getAvailable().map(toModelInfo);
752
- const launchInput: Parameters<typeof executeAsyncSingle>[1] = {
753
- agent: selectedAgent,
754
- task: buildRevivedAsyncTask(target, followUp, input.params.messageOrigin),
755
- agentConfig,
756
- ctx: {
757
- pi: input.deps.pi,
758
- cwd: input.requestCwd,
759
- currentSessionId: input.deps.state.currentSessionId,
760
- rootSessionId: resolveRootSessionId(input.ctx.sessionManager),
761
- currentModelProvider: input.ctx.model?.provider,
762
- projectTrusted: input.ctx.isProjectTrusted(),
763
- },
764
- cwd: effectiveCwd,
765
- maxOutput: input.params.maxOutput ?? savedLaunch?.maxOutput,
766
- artifactsDir: input.params.artifacts === false || (input.params.artifacts === undefined && savedLaunch?.artifacts === false) ? undefined : savedLaunch?.artifactsDir ?? input.deps.tempArtifactsDir,
767
- shareEnabled: input.params.share ?? savedLaunch?.share ?? false,
768
- sessionRoot: input.deps.getSubagentSessionRoot(parentSessionFile),
769
- sessionFile: target.sessionFile,
770
- maxSubagentDepth: savedLaunch?.maxSubagentDepth ?? resolveCurrentMaxSubagentDepth(input.deps.config.maxSubagentDepth),
771
- worktreeSetupHook: input.deps.config.worktreeSetupHook,
772
- worktreeSetupHookTimeoutMs: input.deps.config.worktreeSetupHookTimeoutMs,
773
- controlConfig: resolveControlConfig(savedLaunch?.controlConfig ?? input.deps.config.control, input.params.control),
774
- controlIntercomTarget: intercomBridge.orchestratorTarget,
775
- childIntercomTarget: (agent, index) => resolveSubagentIntercomTarget(runId, agent, index),
776
- availableModels, savedLaunch, modelOverride,
777
- skills: skill,
778
- acceptance: input.params.acceptance ?? acceptanceInputFromResolved(contract.effectiveAcceptance ?? savedLaunch?.effectiveAcceptance),
779
- output: input.params.output ?? (generatedOutputFilename ? true : savedLaunch?.output ?? contract.output),
780
- generatedOutputFilename,
781
- outputMode: input.params.outputMode ?? savedLaunch?.outputMode ?? contract.outputMode,
782
- outputSchema: input.params.outputSchema ?? savedLaunch?.outputSchema ?? contract.outputSchema,
783
- projectTrust: savedLaunch?.projectTrust ?? resolveConfiguredChildProjectTrustPolicy(input.deps.config.projectTrust),
784
- };
785
- const undoCwdRequest = input.params.cwd !== undefined ? requestChildExecutionCwd(target.sessionFile, effectiveCwd) : undefined;
786
- let result: ReturnType<typeof executeAsyncSingle>;
787
- try {
788
- result = executeAsyncSingle(runId, launchInput);
789
- } catch (error) {
790
- undoCwdRequest?.();
791
- throw error;
792
- }
793
- if (result.isError) undoCwdRequest?.();
794
- const owned = input.deps.state.ownedRuns?.get(runId);
795
- if (owned) rememberOwnedRun(input.deps.state, { ...owned, asyncDir: result.details.asyncDir, pid: result.details.asyncPid ?? input.deps.state.asyncJobs.get(runId)?.pid, ...(result.isError ? { error: result.content.map((part) => part.text).join("\n") } : {}) });
796
- if (result.isError) return result;
797
-
798
- const revivedId = result.details.asyncId ?? runId;
799
- const revivedTarget = resolveSubagentIntercomTarget(revivedId, selectedAgent, 0);
800
- const sourceLabel = target.source;
801
- const lines = [
802
- `Revived ${sourceLabel} subagent from ${target.runId}.`,
803
- `Run mapping: ${target.runId} -> ${revivedId}`,
804
- `Revived run: ${revivedId}`,
805
- `Agent: ${selectedAgent}`,
806
- `Configuration: ${savedLaunch ? "saved effective launch" : "explicitly selected current profile"}${modelOverride ? `; model ${modelOverride}` : ""}`,
807
- `Root: ${prior?.rootRunId ?? target.runId}`,
808
- `Session: ${target.sessionFile}`,
809
- result.details.asyncDir ? `Async dir: ${result.details.asyncDir}` : undefined,
810
- revivedTarget ? `Intercom target: ${revivedTarget} (if registered)` : undefined,
811
- `Prior pending-reply context for ${target.runId} is invalid; use the revived run and target only.`,
812
- `Status if needed: ${formatRunAction("status", revivedId, {}, Boolean(nestedResolutionScopeForExecutor(input.deps)))}`,
813
- ].filter((line): line is string => Boolean(line));
814
- return {
815
- content: [{ type: "text", text: formatAsyncStartedMessage(lines.join("\n"), Boolean(nestedResolutionScopeForExecutor(input.deps))) }],
816
- details: {
817
- ...result.details,
818
- managementControl: buildManagementControl({ state: "live", runId: revivedId, index: 0, intercomTarget: revivedTarget, canInterrupt: true, revivedFromRunId: target.runId }),
819
- },
820
- };
54
+ if (params.acceptance !== undefined) {
55
+ return LIVE_ACCEPTANCE_OVERRIDE_NOTICE;
56
+ }
57
+ return [
58
+ params.agent,
59
+ params.model,
60
+ params.cwd,
61
+ params.output,
62
+ params.outputMode,
63
+ params.outputSchema,
64
+ params.skill,
65
+ params.maxOutput,
66
+ params.control,
67
+ params.artifacts,
68
+ params.share,
69
+ ].some((value) => value !== undefined)
70
+ ? "Launch overrides change a newly started continuation, not this live child. No launch settings were changed."
71
+ : undefined;
72
+ }
73
+ function withLiveNotice(
74
+ result: SubagentExecutionResult,
75
+ params: SubagentParamsLike,
76
+ ): SubagentExecutionResult {
77
+ const notice = liveLaunchOverrideNotice(params);
78
+ if (notice !== undefined) {
79
+ result.content.push({ type: "text", text: notice });
80
+ }
81
+ return result;
82
+ }
83
+ function normalizedFollowUp(params: SubagentParamsLike): string {
84
+ return (params.message ?? params.task ?? "").trim();
85
+ }
86
+ function usesSession(candidate: OwnedRun, deps: ExecutorReadDeps, sessionFile: string): boolean {
87
+ return (
88
+ candidate.children.some((child) => child.sessionFile === sessionFile) ||
89
+ deps.state.asyncJobs.get(candidate.runId)?.sessionFile === sessionFile
90
+ );
91
+ }
92
+ function nudgeContinuation(
93
+ input: ReadResumeInput,
94
+ runId: string,
95
+ sessionFile: string | undefined,
96
+ ): Promise<SubagentExecutionResult> | undefined {
97
+ if (sessionFile === undefined || sessionFile.length === 0) {
98
+ return;
99
+ }
100
+ for (const candidate of input.deps.state.ownedRuns?.values() ?? []) {
101
+ if (candidate.runId === runId) {
102
+ continue;
103
+ }
104
+ if (!usesSession(candidate, input.deps, sessionFile)) {
105
+ continue;
106
+ }
107
+ const active = ownedRunView(candidate, input.deps.state, {
108
+ readConfiguration: false,
109
+ }).children.find((child) => child.sessionFile === sessionFile && child.state === "live");
110
+ if (!active) {
111
+ continue;
112
+ }
113
+ return nudgeSubagentRun({
114
+ params: {
115
+ ...input.params,
116
+ dir: undefined,
117
+ id: candidate.runId,
118
+ index: active.index,
119
+ message: normalizedFollowUp(input.params),
120
+ },
121
+ deps: input.deps,
122
+ ctx: input.ctx,
123
+ }).then((result) => withLiveNotice(result, input.params));
124
+ }
125
+ return;
126
+ }
127
+ async function resumeOwned(
128
+ // Owned continuation may persist a new run; deps retains the actual session mutation owner.
129
+ input: RevivalInput,
130
+ owned: OwnedRun,
131
+ ): Promise<SubagentExecutionResult> {
132
+ const view = ownedRunView(owned, input.deps.state);
133
+ if (view.children.length > 1 && input.params.index === undefined) {
134
+ throw new Error(
135
+ `Run '${owned.runId}' has ${view.children.length} children. Provide index to choose one.`,
136
+ );
137
+ }
138
+ const child = view.children.find((entry) => entry.index === (input.params.index ?? 0));
139
+ if (!child) {
140
+ throw new Error(`Run '${owned.runId}' has no child at index ${input.params.index ?? 0}.`);
141
+ }
142
+ if (child.state === "live") {
143
+ return withLiveNotice(
144
+ await nudgeSubagentRun({
145
+ params: {
146
+ ...input.params,
147
+ id: owned.runId,
148
+ message: normalizedFollowUp(input.params),
149
+ },
150
+ deps: input.deps,
151
+ ctx: input.ctx,
152
+ }),
153
+ input.params,
154
+ );
155
+ }
156
+ const liveContinuation = nudgeContinuation(input, owned.runId, child.sessionFile);
157
+ if (liveContinuation) {
158
+ return await liveContinuation;
159
+ }
160
+ return reviveSavedSubagent(input, ownedRevivalTarget(owned, child));
161
+ }
162
+ function ownedRevivalTarget(
163
+ owned: OwnedRun,
164
+ child: OwnedRunView["children"][number],
165
+ ): RevivalTarget {
166
+ const contract = readQuestionContract(owned.runId, child.index);
167
+ return {
168
+ ...contract,
169
+ runId: owned.runId,
170
+ agent: child.agent,
171
+ index: child.index,
172
+ source: owned.source,
173
+ cwd: child.launch?.cwd ?? owned.cwd,
174
+ sessionFile: child.sessionFile,
175
+ effectiveAcceptance:
176
+ contract?.effectiveAcceptance ?? child.result?.acceptance?.effectiveAcceptance,
177
+ model: child.result?.model,
178
+ };
179
+ }
180
+ function trustedSessionRoots(
181
+ input: ReadResumeInput,
182
+ parentSessionFile: string | null,
183
+ ): readonly string[] {
184
+ const roots: string[] = [];
185
+ const configured = input.deps.config.defaultSessionDir;
186
+ if (configured !== undefined && configured.length > 0) {
187
+ roots.push(path.resolve(input.deps.expandTilde(configured)));
188
+ }
189
+ if (parentSessionFile !== null && parentSessionFile.length > 0) {
190
+ roots.push(input.deps.getSubagentSessionRoot(parentSessionFile));
191
+ }
192
+ return roots;
193
+ }
194
+ function resolveRequestedRun(
195
+ params: SubagentParamsLike,
196
+ deps: ExecutorReadDeps,
197
+ ): ReturnType<typeof resolveSubagentRunId> {
198
+ const id = params.id ?? params.runId;
199
+ if (id === undefined || id.length === 0) {
200
+ return;
201
+ }
202
+ return resolveSubagentRunId(id, {
203
+ state: deps.state,
204
+ nested: nestedResolutionScopeForExecutor(deps),
205
+ });
206
+ }
207
+ async function resolveContinuation(
208
+ // Resolution includes question/owned revival, whose durable updates belong to this session owner.
209
+ input: RevivalInput,
210
+ parentSessionFile: string | null,
211
+ ): Promise<Resolution> {
212
+ const requested = requestedOwnedId(input.params);
213
+ const questions =
214
+ requested !== undefined
215
+ ? listSupervisorQuestions(input.ctx.sessionManager.getSessionId(), requested).filter(
216
+ (question) => question.state === "awaiting_input" || question.state === "answer_pending",
217
+ )
218
+ : [];
219
+ if (questions.length > 0) {
220
+ return { kind: "result", result: continueQuestionSession(input, questions) };
221
+ }
222
+ const owned = requested !== undefined ? resolveOwnedRun(input.deps.state, requested) : undefined;
223
+ if (owned) {
224
+ return { kind: "result", result: await resumeOwned(input, owned) };
225
+ }
226
+ const resolved = resolveRequestedRun(input.params, input.deps);
227
+ if (resolved?.kind !== "nested") {
228
+ return { kind: "target", target: resolveResumeTarget(input.params, input.deps.state) };
229
+ }
230
+ if (resolved.match.run.state === "running" || resolved.match.run.state === "queued") {
231
+ return {
232
+ kind: "result",
233
+ result: await resumeLiveNestedRun({
234
+ target: resolved,
235
+ message: normalizedFollowUp(input.params),
236
+ index: input.params.index,
237
+ acceptanceOverrideSupplied: input.params.acceptance !== undefined,
238
+ pi: input.deps.pi,
239
+ childSafe: nestedResolutionScopeForExecutor(input.deps) !== undefined,
240
+ }),
241
+ };
242
+ }
243
+ return {
244
+ kind: "target",
245
+ target: resolveNestedResumeTarget(resolved, trustedSessionRoots(input, parentSessionFile)),
246
+ };
247
+ }
248
+ function lookupFailure(
249
+ // Recovery may revive a saved question; the real dependency state remains owned, not frozen.
250
+ input: RevivalInput,
251
+ error: unknown,
252
+ ): SubagentExecutionResult {
253
+ const message = errorMessage(error),
254
+ requested = requestedOwnedId(input.params);
255
+ const recoverable =
256
+ (error instanceof Error && error.message.startsWith("Async run not found.")) ||
257
+ message.includes("is detached for intercom coordination");
258
+ if (requested !== undefined && recoverable) {
259
+ try {
260
+ const questions = listSupervisorQuestions(input.ctx.sessionManager.getSessionId(), requested);
261
+ if (questions.length > 0) {
262
+ return continueQuestionSession(input, questions);
263
+ }
264
+ } catch (questionError) {
265
+ return failure(errorMessage(questionError));
266
+ }
267
+ }
268
+ return failure(message);
269
+ }
270
+ async function deliverLiveContinuation(
271
+ input: ReadResumeInput,
272
+ target: Extract<ResumeSourceTarget, { readonly kind: "live" }>,
273
+ ): Promise<SubagentExecutionResult> {
274
+ const followUp = normalizedFollowUp(input.params);
275
+ const delivered = await deliverSubagentIntercomMessageEvent(
276
+ input.deps.pi.events,
277
+ target.intercomTarget,
278
+ `Follow-up for async run ${target.runId} (${target.agent}):\n\n${followUp}`,
279
+ 500,
280
+ { source: "async-resume", runId: target.runId, agent: target.agent, index: target.index },
281
+ );
282
+ if (!delivered) {
283
+ return failure(
284
+ [
285
+ "Async child appears live but its intercom target is not registered.",
286
+ `Run: ${target.runId}`,
287
+ `Intercom target: ${target.intercomTarget}`,
288
+ `Wait for completion, then retry ${formatRunAction("resume", target.runId, { message: "..." }, nestedResolutionScopeForExecutor(input.deps) !== undefined)}.`,
289
+ ].join("\n"),
290
+ );
291
+ }
292
+ const lines = [
293
+ "Delivered follow-up to live async child.",
294
+ `Run: ${target.runId}`,
295
+ `Intercom target: ${target.intercomTarget}`,
296
+ ];
297
+ if (input.params.acceptance !== undefined) {
298
+ lines.push(LIVE_ACCEPTANCE_OVERRIDE_NOTICE);
299
+ }
300
+ return {
301
+ content: [{ type: "text", text: lines.join("\n") }],
302
+ details: {
303
+ mode: "management",
304
+ results: [],
305
+ managementControl: buildManagementControl({
306
+ state: "live",
307
+ runId: target.runId,
308
+ index: target.index,
309
+ intercomTarget: target.intercomTarget,
310
+ canNudge: true,
311
+ canResume: true,
312
+ canInterrupt: true,
313
+ }),
314
+ },
315
+ };
316
+ }
317
+ /** Pending question, owned/live continuation, then saved session revival—in that order. */
318
+ export async function resumeAsyncRun(
319
+ // Resume owns durable question delivery and newly launched continuation state through its helpers.
320
+ input: RevivalInput,
321
+ ): Promise<SubagentExecutionResult> {
322
+ if (normalizedFollowUp(input.params).length === 0) {
323
+ return failure("action='resume' requires message.");
324
+ }
325
+ const parentSessionFile = input.ctx.sessionManager.getSessionFile() ?? null;
326
+ let resolution: Resolution;
327
+ try {
328
+ resolution = await resolveContinuation(input, parentSessionFile);
329
+ } catch (error) {
330
+ return lookupFailure(input, error);
331
+ }
332
+ if (resolution.kind === "result") {
333
+ return resolution.result;
334
+ }
335
+ const { target } = resolution;
336
+ if (target.kind === "live") {
337
+ return deliverLiveContinuation(input, target);
338
+ }
339
+ return (
340
+ nudgeContinuation(input, target.runId, target.sessionFile) ?? reviveSavedSubagent(input, target)
341
+ );
821
342
  }