@fitchmultz/pi-subagents 0.0.0-stage → 0.44.4

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 (342) hide show
  1. package/CHANGELOG.md +1645 -0
  2. package/LICENSE +21 -0
  3. package/README.md +1529 -2
  4. package/agents/context-builder.md +97 -0
  5. package/agents/debugger.md +51 -0
  6. package/agents/delegate.md +13 -0
  7. package/agents/fixer.md +37 -0
  8. package/agents/oracle.md +71 -0
  9. package/agents/planner.md +72 -0
  10. package/agents/researcher.md +51 -0
  11. package/agents/reviewer-claude.md +69 -0
  12. package/agents/reviewer-gpt.md +68 -0
  13. package/agents/reviewer-ponytail.md +82 -0
  14. package/agents/reviewer-security.md +81 -0
  15. package/agents/reviewer.md +69 -0
  16. package/agents/scout.md +66 -0
  17. package/agents/ui-designer.md +51 -0
  18. package/agents/watcher.md +26 -0
  19. package/agents/worker.md +46 -0
  20. package/agents/writer.md +36 -0
  21. package/dist/agents/agent-management.js +923 -0
  22. package/dist/agents/agent-scope.js +7 -0
  23. package/dist/agents/agent-selection.js +20 -0
  24. package/dist/agents/agent-serializer.js +100 -0
  25. package/dist/agents/agents.js +752 -0
  26. package/dist/agents/chain-serializer.js +367 -0
  27. package/dist/agents/frontmatter.js +21 -0
  28. package/dist/agents/identity.js +29 -0
  29. package/dist/agents/skills.js +570 -0
  30. package/dist/extension/build-info.js +1 -0
  31. package/dist/extension/compact-tools.js +112 -0
  32. package/dist/extension/config.js +22 -0
  33. package/dist/extension/control-notices.js +29 -0
  34. package/dist/extension/doctor.js +143 -0
  35. package/dist/extension/fanout-child.js +326 -0
  36. package/dist/extension/index.js +577 -0
  37. package/dist/extension/schemas.js +416 -0
  38. package/dist/extension/tool-input.js +20 -0
  39. package/dist/extension/tool-result.js +14 -0
  40. package/dist/history/canonical-result.js +81 -0
  41. package/dist/history/database.js +72 -0
  42. package/dist/history/index.js +194 -0
  43. package/dist/history/ingest.js +447 -0
  44. package/dist/history/queries.js +350 -0
  45. package/dist/history/store.js +216 -0
  46. package/dist/history/types.js +4 -0
  47. package/dist/history/worker.js +461 -0
  48. package/dist/intercom/intercom-bridge.js +112 -0
  49. package/dist/intercom/live-intercom.js +94 -0
  50. package/dist/intercom/result-intercom.js +373 -0
  51. package/dist/pi-intercom/agent-dir.js +10 -0
  52. package/dist/pi-intercom/broker/broker.js +426 -0
  53. package/dist/pi-intercom/broker/client.js +492 -0
  54. package/dist/pi-intercom/broker/framing.js +65 -0
  55. package/dist/pi-intercom/broker/paths.js +42 -0
  56. package/dist/pi-intercom/broker/pid.js +66 -0
  57. package/dist/pi-intercom/broker/spawn.js +221 -0
  58. package/dist/pi-intercom/config.js +102 -0
  59. package/dist/pi-intercom/index.js +2942 -0
  60. package/dist/pi-intercom/reply-tracker.js +182 -0
  61. package/dist/pi-intercom/session-targets.js +128 -0
  62. package/dist/pi-intercom/subagent-live-events.js +132 -0
  63. package/dist/pi-intercom/topics.js +157 -0
  64. package/dist/pi-intercom/types.js +163 -0
  65. package/dist/pi-intercom/ui/compose.js +269 -0
  66. package/dist/pi-intercom/ui/inline-message.js +80 -0
  67. package/dist/pi-intercom/ui/session-list.js +124 -0
  68. package/dist/runs/background/async-control.js +95 -0
  69. package/dist/runs/background/async-execution.js +676 -0
  70. package/dist/runs/background/async-job-tracker.js +383 -0
  71. package/dist/runs/background/async-result-file.js +58 -0
  72. package/dist/runs/background/async-resume.js +305 -0
  73. package/dist/runs/background/async-status.js +442 -0
  74. package/dist/runs/background/completion-dedupe.js +69 -0
  75. package/dist/runs/background/completion-delivery.js +245 -0
  76. package/dist/runs/background/notify.js +71 -0
  77. package/dist/runs/background/parallel-groups.js +55 -0
  78. package/dist/runs/background/result-watcher.js +426 -0
  79. package/dist/runs/background/run-id-resolver.js +64 -0
  80. package/dist/runs/background/run-status.js +382 -0
  81. package/dist/runs/background/stale-run-reconciler.js +335 -0
  82. package/dist/runs/background/subagent-runner-launcher.js +55 -0
  83. package/dist/runs/background/subagent-runner.js +1872 -0
  84. package/dist/runs/background/top-level-async.js +5 -0
  85. package/dist/runs/foreground/chain-clarify.js +1152 -0
  86. package/dist/runs/foreground/clarify-invocation.js +72 -0
  87. package/dist/runs/foreground/execution-input.js +423 -0
  88. package/dist/runs/foreground/foreground-control.js +817 -0
  89. package/dist/runs/foreground/question-control.js +77 -0
  90. package/dist/runs/foreground/run-async-path.js +205 -0
  91. package/dist/runs/foreground/subagent-executor.js +442 -0
  92. package/dist/runs/foreground/subagent-params.js +165 -0
  93. package/dist/runs/foreground/wait-run.js +139 -0
  94. package/dist/runs/shared/acceptance-contract.js +253 -0
  95. package/dist/runs/shared/acceptance-evaluation.js +256 -0
  96. package/dist/runs/shared/acceptance-finalization.js +280 -0
  97. package/dist/runs/shared/acceptance-reports.js +147 -0
  98. package/dist/runs/shared/acceptance.js +4 -0
  99. package/dist/runs/shared/chain-outputs.js +110 -0
  100. package/dist/runs/shared/child-attempt.js +693 -0
  101. package/dist/runs/shared/child-execution-cwd.js +96 -0
  102. package/dist/runs/shared/child-observations.js +114 -0
  103. package/dist/runs/shared/claude-code.js +199 -0
  104. package/dist/runs/shared/completion-guard.js +31 -0
  105. package/dist/runs/shared/dynamic-fanout.js +274 -0
  106. package/dist/runs/shared/history-index.js +72 -0
  107. package/dist/runs/shared/mcp-direct-tool-allowlist.js +334 -0
  108. package/dist/runs/shared/model-fallback.js +185 -0
  109. package/dist/runs/shared/mutating-tool-guard.js +170 -0
  110. package/dist/runs/shared/native-finalization.js +101 -0
  111. package/dist/runs/shared/native-usage.js +153 -0
  112. package/dist/runs/shared/nested-events.js +700 -0
  113. package/dist/runs/shared/nested-path.js +48 -0
  114. package/dist/runs/shared/nested-render.js +108 -0
  115. package/dist/runs/shared/parallel-utils.js +61 -0
  116. package/dist/runs/shared/parent-receipts.js +68 -0
  117. package/dist/runs/shared/parent-usage.js +121 -0
  118. package/dist/runs/shared/pi-args.js +310 -0
  119. package/dist/runs/shared/pi-spawn.js +52 -0
  120. package/dist/runs/shared/run-history-queries.js +45 -0
  121. package/dist/runs/shared/run-history.js +103 -0
  122. package/dist/runs/shared/run-id-ambiguity.js +4 -0
  123. package/dist/runs/shared/run-records.js +679 -0
  124. package/dist/runs/shared/session-cwd-preload.js +33 -0
  125. package/dist/runs/shared/single-output.js +222 -0
  126. package/dist/runs/shared/streaming-text.js +15 -0
  127. package/dist/runs/shared/structured-output.js +55 -0
  128. package/dist/runs/shared/subagent-control.js +179 -0
  129. package/dist/runs/shared/subagent-prompt-runtime.js +197 -0
  130. package/dist/runs/shared/subagent-tool-loop-guard.js +66 -0
  131. package/dist/runs/shared/supervisor-questions.js +320 -0
  132. package/dist/runs/shared/workflow-graph.js +214 -0
  133. package/dist/runs/shared/workflow-policy.js +84 -0
  134. package/dist/runs/shared/worktree.js +525 -0
  135. package/dist/shared/agent-context-policy.js +126 -0
  136. package/dist/shared/artifacts.js +96 -0
  137. package/dist/shared/atomic-json.js +13 -0
  138. package/dist/shared/cooperative.js +20 -0
  139. package/dist/shared/execution-cwd.js +14 -0
  140. package/dist/shared/file-coalescer.js +21 -0
  141. package/dist/shared/fork-context.js +49 -0
  142. package/dist/shared/formatters.js +85 -0
  143. package/dist/shared/journal-reader.js +407 -0
  144. package/dist/shared/lazy-tools.js +32 -0
  145. package/dist/shared/model-info.js +68 -0
  146. package/dist/shared/native-session.js +15 -0
  147. package/dist/shared/native-tui.js +18 -0
  148. package/dist/shared/native-typebox.js +20 -0
  149. package/dist/shared/post-exit-stdio-guard.js +197 -0
  150. package/dist/shared/prompt-sections.js +8 -0
  151. package/dist/shared/session-entries.js +52 -0
  152. package/dist/shared/session-identity.js +14 -0
  153. package/dist/shared/settings.js +254 -0
  154. package/dist/shared/status-format.js +111 -0
  155. package/dist/shared/temp-root.js +114 -0
  156. package/dist/shared/types.js +207 -0
  157. package/dist/shared/ui-mode.js +3 -0
  158. package/dist/shared/utils.js +334 -0
  159. package/dist/slash/prompt-template-bridge.js +298 -0
  160. package/dist/slash/slash-bridge.js +116 -0
  161. package/dist/slash/slash-commands.js +607 -0
  162. package/dist/slash/slash-live-state.js +246 -0
  163. package/dist/tui/action-hints.js +75 -0
  164. package/dist/tui/agent-history.js +197 -0
  165. package/dist/tui/agent-view.js +1656 -0
  166. package/dist/tui/render.js +1515 -0
  167. package/docs/intercom.md +579 -0
  168. package/docs/performance.md +121 -0
  169. package/package.json +101 -4
  170. package/prompts/gather-context-and-clarify.md +14 -0
  171. package/prompts/parallel-cleanup.md +62 -0
  172. package/prompts/parallel-context-build.md +56 -0
  173. package/prompts/parallel-handoff-plan.md +62 -0
  174. package/prompts/parallel-research.md +51 -0
  175. package/prompts/parallel-review.md +59 -0
  176. package/prompts/review-loop.md +42 -0
  177. package/scripts/build.mjs +134 -0
  178. package/scripts/check-compat.mjs +48 -0
  179. package/scripts/compat-host.mjs +19 -0
  180. package/scripts/linux-smoke.sh +51 -0
  181. package/scripts/local-install-smoke.mjs +66 -0
  182. package/scripts/native-package-smoke.mjs +82 -0
  183. package/scripts/package-smoke.mjs +205 -0
  184. package/scripts/prepare.mjs +67 -0
  185. package/scripts/real-pi-smoke.mjs +415 -0
  186. package/scripts/run-tests.mjs +149 -0
  187. package/skills/pi-intercom/SKILL.md +113 -0
  188. package/skills/pi-intercom/evals/evals.json +29 -0
  189. package/skills/pi-intercom/evals/trigger-evals.json +47 -0
  190. package/skills/pi-intercom/references/peer-sessions.md +69 -0
  191. package/skills/pi-subagents/SKILL.md +76 -0
  192. package/skills/pi-subagents/evals/evals.json +29 -0
  193. package/skills/pi-subagents/evals/trigger-evals.json +42 -0
  194. package/skills/pi-subagents/references/full-orchestration-guide.md +863 -0
  195. package/src/agents/agent-management.ts +813 -0
  196. package/src/agents/agent-scope.ts +7 -0
  197. package/src/agents/agent-selection.ts +23 -0
  198. package/src/agents/agent-serializer.ts +99 -0
  199. package/src/agents/agents.ts +916 -0
  200. package/src/agents/chain-serializer.ts +329 -0
  201. package/src/agents/frontmatter.ts +21 -0
  202. package/src/agents/identity.ts +30 -0
  203. package/src/agents/skills.ts +637 -0
  204. package/src/extension/build-info.ts +2 -0
  205. package/src/extension/compact-tools.ts +114 -0
  206. package/src/extension/config.ts +23 -0
  207. package/src/extension/control-notices.ts +47 -0
  208. package/src/extension/doctor.ts +171 -0
  209. package/src/extension/fanout-child.ts +312 -0
  210. package/src/extension/index.ts +649 -0
  211. package/src/extension/schemas.ts +444 -0
  212. package/src/extension/tool-input.ts +19 -0
  213. package/src/extension/tool-result.ts +16 -0
  214. package/src/history/canonical-result.ts +62 -0
  215. package/src/history/database.ts +59 -0
  216. package/src/history/index.ts +152 -0
  217. package/src/history/ingest.ts +332 -0
  218. package/src/history/queries.ts +241 -0
  219. package/src/history/store.ts +203 -0
  220. package/src/history/types.ts +166 -0
  221. package/src/history/worker.ts +317 -0
  222. package/src/intercom/intercom-bridge.ts +123 -0
  223. package/src/intercom/live-intercom.ts +104 -0
  224. package/src/intercom/result-intercom.ts +429 -0
  225. package/src/pi-intercom/agent-dir.ts +9 -0
  226. package/src/pi-intercom/broker/broker.ts +463 -0
  227. package/src/pi-intercom/broker/client.ts +558 -0
  228. package/src/pi-intercom/broker/framing.ts +77 -0
  229. package/src/pi-intercom/broker/paths.ts +47 -0
  230. package/src/pi-intercom/broker/pid.ts +59 -0
  231. package/src/pi-intercom/broker/spawn.ts +248 -0
  232. package/src/pi-intercom/config.ts +142 -0
  233. package/src/pi-intercom/index.ts +2929 -0
  234. package/src/pi-intercom/reply-tracker.ts +211 -0
  235. package/src/pi-intercom/session-targets.ts +172 -0
  236. package/src/pi-intercom/subagent-live-events.ts +133 -0
  237. package/src/pi-intercom/topics.ts +125 -0
  238. package/src/pi-intercom/types.ts +271 -0
  239. package/src/pi-intercom/ui/compose.ts +289 -0
  240. package/src/pi-intercom/ui/inline-message.ts +92 -0
  241. package/src/pi-intercom/ui/session-list.ts +139 -0
  242. package/src/runs/background/async-control.ts +89 -0
  243. package/src/runs/background/async-execution.ts +850 -0
  244. package/src/runs/background/async-job-tracker.ts +385 -0
  245. package/src/runs/background/async-result-file.ts +62 -0
  246. package/src/runs/background/async-resume.ts +333 -0
  247. package/src/runs/background/async-status.ts +481 -0
  248. package/src/runs/background/completion-dedupe.ts +79 -0
  249. package/src/runs/background/completion-delivery.ts +195 -0
  250. package/src/runs/background/notify.ts +123 -0
  251. package/src/runs/background/parallel-groups.ts +53 -0
  252. package/src/runs/background/result-watcher.ts +423 -0
  253. package/src/runs/background/run-id-resolver.ts +69 -0
  254. package/src/runs/background/run-status.ts +384 -0
  255. package/src/runs/background/stale-run-reconciler.ts +369 -0
  256. package/src/runs/background/subagent-runner-launcher.ts +56 -0
  257. package/src/runs/background/subagent-runner.ts +2053 -0
  258. package/src/runs/background/top-level-async.ts +13 -0
  259. package/src/runs/foreground/chain-clarify.ts +1340 -0
  260. package/src/runs/foreground/clarify-invocation.ts +77 -0
  261. package/src/runs/foreground/execution-input.ts +467 -0
  262. package/src/runs/foreground/foreground-control.ts +821 -0
  263. package/src/runs/foreground/question-control.ts +70 -0
  264. package/src/runs/foreground/run-async-path.ts +243 -0
  265. package/src/runs/foreground/subagent-executor.ts +501 -0
  266. package/src/runs/foreground/subagent-params.ts +320 -0
  267. package/src/runs/foreground/wait-run.ts +121 -0
  268. package/src/runs/shared/acceptance-contract.ts +257 -0
  269. package/src/runs/shared/acceptance-evaluation.ts +275 -0
  270. package/src/runs/shared/acceptance-finalization.ts +353 -0
  271. package/src/runs/shared/acceptance-reports.ts +152 -0
  272. package/src/runs/shared/acceptance.ts +33 -0
  273. package/src/runs/shared/chain-outputs.ts +114 -0
  274. package/src/runs/shared/child-attempt.ts +623 -0
  275. package/src/runs/shared/child-execution-cwd.ts +91 -0
  276. package/src/runs/shared/child-observations.ts +99 -0
  277. package/src/runs/shared/claude-code.ts +243 -0
  278. package/src/runs/shared/completion-guard.ts +35 -0
  279. package/src/runs/shared/dynamic-fanout.ts +295 -0
  280. package/src/runs/shared/history-index.ts +69 -0
  281. package/src/runs/shared/mcp-direct-tool-allowlist.ts +376 -0
  282. package/src/runs/shared/model-fallback.ts +219 -0
  283. package/src/runs/shared/mutating-tool-guard.ts +194 -0
  284. package/src/runs/shared/native-finalization.ts +128 -0
  285. package/src/runs/shared/native-usage.ts +113 -0
  286. package/src/runs/shared/nested-events.ts +752 -0
  287. package/src/runs/shared/nested-path.ts +52 -0
  288. package/src/runs/shared/nested-render.ts +119 -0
  289. package/src/runs/shared/parallel-utils.ts +153 -0
  290. package/src/runs/shared/parent-receipts.ts +59 -0
  291. package/src/runs/shared/parent-usage.ts +121 -0
  292. package/src/runs/shared/pi-args.ts +350 -0
  293. package/src/runs/shared/pi-spawn.ts +52 -0
  294. package/src/runs/shared/run-history-queries.ts +44 -0
  295. package/src/runs/shared/run-history.ts +93 -0
  296. package/src/runs/shared/run-id-ambiguity.ts +4 -0
  297. package/src/runs/shared/run-records.ts +607 -0
  298. package/src/runs/shared/session-cwd-preload.ts +27 -0
  299. package/src/runs/shared/single-output.ts +267 -0
  300. package/src/runs/shared/streaming-text.ts +18 -0
  301. package/src/runs/shared/structured-output.ts +68 -0
  302. package/src/runs/shared/subagent-control.ts +219 -0
  303. package/src/runs/shared/subagent-prompt-runtime.ts +201 -0
  304. package/src/runs/shared/subagent-tool-loop-guard.ts +89 -0
  305. package/src/runs/shared/supervisor-questions.ts +372 -0
  306. package/src/runs/shared/workflow-graph.ts +220 -0
  307. package/src/runs/shared/workflow-policy.ts +125 -0
  308. package/src/runs/shared/worktree.ts +635 -0
  309. package/src/shared/agent-context-policy.ts +184 -0
  310. package/src/shared/artifacts.ts +95 -0
  311. package/src/shared/atomic-json.ts +16 -0
  312. package/src/shared/cooperative.ts +21 -0
  313. package/src/shared/execution-cwd.ts +14 -0
  314. package/src/shared/file-coalescer.ts +29 -0
  315. package/src/shared/fork-context.ts +68 -0
  316. package/src/shared/formatters.ts +84 -0
  317. package/src/shared/journal-reader.ts +333 -0
  318. package/src/shared/lazy-tools.ts +36 -0
  319. package/src/shared/model-info.ts +81 -0
  320. package/src/shared/native-session.ts +8 -0
  321. package/src/shared/native-tui.ts +10 -0
  322. package/src/shared/native-typebox.ts +14 -0
  323. package/src/shared/post-exit-stdio-guard.ts +204 -0
  324. package/src/shared/prompt-sections.ts +10 -0
  325. package/src/shared/session-entries.ts +49 -0
  326. package/src/shared/session-identity.ts +18 -0
  327. package/src/shared/settings.ts +384 -0
  328. package/src/shared/status-format.ts +123 -0
  329. package/src/shared/temp-root.ts +107 -0
  330. package/src/shared/types.ts +1259 -0
  331. package/src/shared/ui-mode.ts +5 -0
  332. package/src/shared/utils.ts +342 -0
  333. package/src/slash/prompt-template-bridge.ts +408 -0
  334. package/src/slash/slash-bridge.ts +178 -0
  335. package/src/slash/slash-commands.ts +617 -0
  336. package/src/slash/slash-live-state.ts +292 -0
  337. package/src/tui/action-hints.ts +69 -0
  338. package/src/tui/agent-history.ts +210 -0
  339. package/src/tui/agent-view.ts +1323 -0
  340. package/src/tui/render.ts +1550 -0
  341. package/tsconfig.build.json +12 -0
  342. package/tsconfig.json +19 -0
@@ -0,0 +1,817 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { formatRunIdAmbiguity } from "../shared/run-id-ambiguity.js";
3
+ import { resolveRootSessionId } from "../../shared/session-identity.js";
4
+ import { writeAsyncControlRequest, writeAsyncInterruptRequest } from "../background/async-control.js";
5
+ import { getRunMetadataDir, listSupervisorQuestions, questionProcessAlive, readQuestionContract, readRunJson, recordQuestionDelivery, saveQuestionOwner } from "../shared/supervisor-questions.js";
6
+ import * as fs from "node:fs";
7
+ import * as path from "node:path";
8
+ import {} from "../../agents/agents.js";
9
+ import { providerQualifiedModelId, toModelInfo } from "../../shared/model-info.js";
10
+ import { normalizeSkillInput } from "../../agents/skills.js";
11
+ import { resolveExecutionAgentScope } from "../../agents/agent-scope.js";
12
+ import { executeAsyncSingle, formatAsyncStartedMessage } from "../background/async-execution.js";
13
+ import { resolveConfiguredChildProjectTrustPolicy } from "../shared/pi-args.js";
14
+ import { requestChildExecutionCwd } from "../shared/child-execution-cwd.js";
15
+ import { resolveCurrentSessionId } from "../../shared/session-identity.js";
16
+ import { resolveIntercomBridge, resolveIntercomSessionTarget, resolveOrchestratorIntercomTarget, resolveSubagentIntercomTarget } from "../../intercom/intercom-bridge.js";
17
+ import { resolveControlConfig } from "../shared/subagent-control.js";
18
+ import { readStatus } from "../../shared/utils.js";
19
+ import { deliverSubagentIntercomMessageEvent } from "../../intercom/result-intercom.js";
20
+ import { sendLiveSubagentMessage } from "../../intercom/live-intercom.js";
21
+ import { buildRevivedAsyncTask, resolveAsyncResumeTarget } from "../background/async-resume.js";
22
+ import { readNestedControlResults, resolveInheritedNestedRouteFromEnv, resolveNestedAsyncDir, resolveNestedParentAddressFromEnv, writeNestedControlRequest } from "../shared/nested-events.js";
23
+ import { inspectSubagentStatus } from "../background/run-status.js";
24
+ import { resolveSubagentRunId } from "../background/run-id-resolver.js";
25
+ import { buildManagementControl, formatRunAction } from "../../shared/status-format.js";
26
+ import { acceptanceInputFromResolved } from "../shared/acceptance.js";
27
+ import { ownedRunStatusResult, ownedRunView, rememberOwnedRun, resolveOwnedRun } from "../shared/run-records.js";
28
+ import { ASYNC_DIR, getAsyncConfigPath, RUNNER_ERROR_LOG_FILE, checkSubagentDepth, resolveCurrentMaxSubagentDepth, } from "../../shared/types.js";
29
+ import {} from "./subagent-params.js";
30
+ export { writeAsyncInterruptRequest } from "../background/async-control.js";
31
+ export const MUTATING_MANAGEMENT_ACTIONS = new Set(["create", "update", "delete"]);
32
+ export function resolveRequestedCwd(runtimeCwd, requestedCwd) {
33
+ return requestedCwd ? path.resolve(runtimeCwd, requestedCwd) : runtimeCwd;
34
+ }
35
+ export function nestedResolutionScopeForExecutor(deps) {
36
+ if (deps.allowMutatingManagementActions !== false)
37
+ return undefined;
38
+ const route = resolveInheritedNestedRouteFromEnv();
39
+ const address = route ? resolveNestedParentAddressFromEnv() : undefined;
40
+ return {
41
+ routes: route ? [route] : [],
42
+ ...(address ? { descendantOf: { parentRunId: address.parentRunId, ...(address.parentStepIndex !== undefined ? { parentStepIndex: address.parentStepIndex } : {}) } } : {}),
43
+ };
44
+ }
45
+ const LATEST_FOREGROUND_ALIASES = new Set(["last", "latest"]);
46
+ export function resolveRememberedForegroundRun(requested, state) {
47
+ if (!requested || !state.foregroundRuns?.size)
48
+ return undefined;
49
+ const normalized = requested.trim();
50
+ if (!normalized)
51
+ return undefined;
52
+ if (LATEST_FOREGROUND_ALIASES.has(normalized)) {
53
+ return [...state.foregroundRuns.values()].sort((left, right) => right.updatedAt - left.updatedAt)[0];
54
+ }
55
+ const direct = state.foregroundRuns.get(normalized);
56
+ const matches = direct ? [direct] : [...state.foregroundRuns.values()].filter((run) => run.runId.startsWith(normalized));
57
+ if (matches.length === 0)
58
+ return undefined;
59
+ if (matches.length > 1)
60
+ throw new Error(formatRunIdAmbiguity("foreground", normalized, matches.map((run) => run.runId)));
61
+ return matches[0];
62
+ }
63
+ function foregroundResumeGuidance(run, childSafe) {
64
+ const childWithSession = run.children.find((child) => child.sessionFile && child.status !== "detached");
65
+ if (!childWithSession && run.children.some((child) => child.status === "detached"))
66
+ return `Completion unconfirmed. Check ${formatRunAction("questions", run.runId, {}, childSafe)} before continuing.`;
67
+ if (run.children.length === 1 && childWithSession)
68
+ return `Continue: ${formatRunAction("resume", run.runId, { message: "..." }, childSafe)}`;
69
+ if (!childWithSession)
70
+ return "Revive: unavailable; no child session file was persisted.";
71
+ return `Continue child: ${formatRunAction("resume", run.runId, { index: childWithSession.index, message: "..." }, childSafe)}`;
72
+ }
73
+ function compactStatusText(value, maxLength = 240) {
74
+ const normalized = value.replace(/\s+/g, " ").trim();
75
+ return normalized.length > maxLength ? `${normalized.slice(0, maxLength - 1)}…` : normalized;
76
+ }
77
+ function foregroundResultChildren(run) {
78
+ return run.children.map((child) => ({ child, finalOutput: child.summary }));
79
+ }
80
+ function rememberedForegroundState(children) {
81
+ if (children.some(({ child }) => child.status === "failed" || child.status === "timed-out"))
82
+ return "failed";
83
+ if (children.some(({ child }) => child.status === "blocked"))
84
+ return "blocked";
85
+ if (children.some(({ child }) => child.status === "paused"))
86
+ return "paused";
87
+ return children.some(({ child }) => child.status === "detached") ? "unknown" : "completed";
88
+ }
89
+ export function rememberedForegroundStatusResult(run, childSafe = false) {
90
+ const children = foregroundResultChildren(run);
91
+ const childState = rememberedForegroundState(children);
92
+ const state = run.error ? "failed" : childState === "completed" && run.pausedReason ? "paused" : childState;
93
+ const resumable = children.find(({ child }) => child.sessionFile && child.status !== "detached")?.child;
94
+ const lines = [
95
+ `Run: ${run.runId}`,
96
+ "State: remembered foreground",
97
+ `Outcome: ${state}`,
98
+ ...(run.error ? [`Error: ${run.error}`] : state === "paused" && run.pausedReason ? [run.pausedReason] : []),
99
+ `Mode: ${run.mode}`,
100
+ `Updated: ${new Date(run.updatedAt).toISOString()}`,
101
+ `Launch cwd: ${run.cwd}`,
102
+ "Children:",
103
+ ...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)}` : ""}`),
104
+ foregroundResumeGuidance(run, childSafe),
105
+ `Status: ${formatRunAction("status", run.runId, {}, childSafe)}`,
106
+ ];
107
+ return {
108
+ content: [{ type: "text", text: lines.join("\n") }],
109
+ details: {
110
+ mode: "management", results: [],
111
+ managementControl: buildManagementControl({ state, runId: run.runId, index: resumable?.index, canResume: Boolean(resumable) }),
112
+ },
113
+ };
114
+ }
115
+ function resolveForegroundResumeTarget(params, state) {
116
+ const requested = (params.id ?? params.runId)?.trim();
117
+ const run = resolveRememberedForegroundRun(requested, state);
118
+ if (!run)
119
+ return undefined;
120
+ if (run.children.length > 1 && params.index === undefined)
121
+ throw new Error(`Foreground run '${run.runId}' has ${run.children.length} children. Provide index to choose one.`);
122
+ const index = params.index ?? 0;
123
+ if (!Number.isInteger(index))
124
+ throw new Error(`Foreground run '${run.runId}' index must be an integer.`);
125
+ if (index < 0 || index >= run.children.length)
126
+ throw new Error(`Foreground run '${run.runId}' has ${run.children.length} children. Index ${index} is out of range.`);
127
+ const child = run.children[index];
128
+ if (child.status === "detached")
129
+ 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.`);
130
+ if (!child.sessionFile)
131
+ throw new Error(`Foreground run '${run.runId}' child ${index} does not have a persisted session file to resume from.`);
132
+ if (path.extname(child.sessionFile) !== ".jsonl")
133
+ throw new Error(`Foreground run '${run.runId}' child ${index} session file must be a .jsonl file: ${child.sessionFile}`);
134
+ const sessionFile = path.resolve(child.sessionFile);
135
+ if (!fs.existsSync(sessionFile))
136
+ throw new Error(`Foreground run '${run.runId}' child ${index} session file does not exist: ${child.sessionFile}`);
137
+ 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 };
138
+ }
139
+ function isAsyncRunNotFound(error) {
140
+ return error instanceof Error && error.message.startsWith("Async run not found.");
141
+ }
142
+ function isResumeAmbiguity(error) {
143
+ return error instanceof Error && /Ambiguous .*run id prefix/.test(error.message);
144
+ }
145
+ function resumeTargetExact(target, requested) {
146
+ return target?.runId === requested;
147
+ }
148
+ function escapeRegExp(value) {
149
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
150
+ }
151
+ function isExactResumeError(error, source, requested) {
152
+ if (!(error instanceof Error) || !requested)
153
+ return false;
154
+ return new RegExp(`\\b${source} run '${escapeRegExp(requested)}'`, "i").test(error.message);
155
+ }
156
+ function resolveResumeTarget(params, state) {
157
+ const requested = (params.id ?? params.runId)?.trim() ?? "";
158
+ let foregroundTarget;
159
+ let foregroundError;
160
+ let asyncTarget;
161
+ let asyncError;
162
+ try {
163
+ const target = resolveForegroundResumeTarget(params, state);
164
+ if (target)
165
+ foregroundTarget = { kind: "revive", source: "foreground", ...target };
166
+ }
167
+ catch (error) {
168
+ foregroundError = error;
169
+ }
170
+ try {
171
+ asyncTarget = { source: "async", ...resolveAsyncResumeTarget(params) };
172
+ }
173
+ catch (error) {
174
+ asyncError = error;
175
+ }
176
+ if (foregroundTarget && asyncTarget) {
177
+ const foregroundExact = resumeTargetExact(foregroundTarget, requested);
178
+ const asyncExact = resumeTargetExact(asyncTarget, requested);
179
+ if (foregroundExact && !asyncExact)
180
+ return foregroundTarget;
181
+ if (asyncExact && !foregroundExact)
182
+ return asyncTarget;
183
+ throw new Error(`Resume id '${requested}' is ambiguous between foreground run '${foregroundTarget.runId}' and async run '${asyncTarget.runId}'. Provide a full run id.`);
184
+ }
185
+ if (foregroundTarget) {
186
+ if (isExactResumeError(asyncError, "async", requested))
187
+ throw asyncError;
188
+ if (isResumeAmbiguity(asyncError) && !resumeTargetExact(foregroundTarget, requested))
189
+ throw asyncError;
190
+ return foregroundTarget;
191
+ }
192
+ if (asyncTarget) {
193
+ if (isExactResumeError(foregroundError, "foreground", requested))
194
+ throw foregroundError;
195
+ if (isResumeAmbiguity(foregroundError) && !resumeTargetExact(asyncTarget, requested))
196
+ throw foregroundError;
197
+ return asyncTarget;
198
+ }
199
+ if (foregroundError && !isAsyncRunNotFound(asyncError))
200
+ throw foregroundError;
201
+ if (foregroundError)
202
+ throw foregroundError;
203
+ if (asyncError)
204
+ throw asyncError;
205
+ throw new Error("Run not found. Provide id or runId.");
206
+ }
207
+ function getAsyncInterruptTarget(state, runId) {
208
+ if (runId) {
209
+ const direct = state.asyncJobs.get(runId);
210
+ if (direct)
211
+ return { asyncId: direct.asyncId, asyncDir: direct.asyncDir };
212
+ const owned = resolveOwnedRun(state, runId);
213
+ return owned?.asyncDir ? { asyncId: owned.runId, asyncDir: owned.asyncDir } : undefined;
214
+ }
215
+ let newest;
216
+ for (const job of state.asyncJobs.values()) {
217
+ if (job.status !== "running")
218
+ continue;
219
+ if (!newest || (job.updatedAt ?? 0) > newest.updatedAt) {
220
+ newest = { asyncId: job.asyncId, asyncDir: job.asyncDir, updatedAt: job.updatedAt ?? 0 };
221
+ }
222
+ }
223
+ for (const run of state.ownedRuns?.values() ?? []) {
224
+ if (!run.asyncDir || (newest && run.startedAt <= newest.updatedAt))
225
+ continue;
226
+ const status = readStatus(run.asyncDir);
227
+ if (status ? status.state !== "running" && status.state !== "queued" : !run.pid || !questionProcessAlive({ pid: run.pid }))
228
+ continue;
229
+ newest = { asyncId: run.runId, asyncDir: run.asyncDir, updatedAt: run.startedAt };
230
+ }
231
+ return newest ? { asyncId: newest.asyncId, asyncDir: newest.asyncDir } : undefined;
232
+ }
233
+ export function extendAsyncTimeoutResult(state, runId, extendMs) {
234
+ const target = getAsyncInterruptTarget(state, runId);
235
+ const status = target && readStatus(target.asyncDir);
236
+ const launch = target && readRunJson(path.join(target.asyncDir, "launch.json"));
237
+ const extendable = status ? status.runtimeVersion === 2 && status.state === "running" && !status.timedOut && status.timeoutAt
238
+ : launch?.runtimeVersion === 2 && launch.timeoutMs;
239
+ if (!target || !extendable)
240
+ return {
241
+ content: [{ type: "text", text: "No live run with an extendable timeout was found. No extension was requested." }], isError: true, details: { mode: "management", results: [] },
242
+ };
243
+ try {
244
+ writeAsyncControlRequest(target.asyncDir, target.asyncId, "extend", undefined, extendMs);
245
+ return { content: [{ type: "text", text: `Requested ${extendMs}ms more for run ${target.asyncId}. Inspect status for the owner's updated deadline.` }],
246
+ details: { mode: "management", results: [], managementControl: buildManagementControl({ state: "live", runId: target.asyncId, canInterrupt: true, canExtend: true }) } };
247
+ }
248
+ catch (error) {
249
+ return { content: [{ type: "text", text: error instanceof Error ? error.message : String(error) }], isError: true, details: { mode: "management", results: [] } };
250
+ }
251
+ }
252
+ export function interruptAsyncRun(state, runId, index) {
253
+ const target = getAsyncInterruptTarget(state, runId);
254
+ if (!target)
255
+ return null;
256
+ const status = readStatus(target.asyncDir);
257
+ if (!status || status.runId !== target.asyncId || status.state !== "running") {
258
+ return {
259
+ content: [{ type: "text", text: `No running async run with a matching control channel was found for '${runId ?? "current"}'.` }],
260
+ isError: true,
261
+ details: { mode: "management", results: [] },
262
+ };
263
+ }
264
+ if (index !== undefined && !status.indexedControl && (status.steps?.length ?? 0) > 1)
265
+ return {
266
+ 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: [] },
267
+ };
268
+ if (index !== undefined && (!Number.isSafeInteger(index) || index < 0 || status.steps?.[index]?.status !== "running")) {
269
+ 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: [] } };
270
+ }
271
+ try {
272
+ writeAsyncInterruptRequest(target.asyncDir, target.asyncId, index);
273
+ const tracked = state.asyncJobs.get(target.asyncId);
274
+ if (tracked) {
275
+ tracked.activityState = undefined;
276
+ tracked.updatedAt = Date.now();
277
+ }
278
+ return {
279
+ content: [{ type: "text", text: `Interrupt requested for async run ${target.asyncId}${index !== undefined ? ` child ${index} only` : ""}. Agent and command exit are not yet confirmed.` }],
280
+ details: { mode: "management", results: [], managementControl: buildManagementControl({ state: "live", runId: target.asyncId }) },
281
+ };
282
+ }
283
+ catch (error) {
284
+ const message = error instanceof Error ? error.message : String(error);
285
+ return {
286
+ content: [{ type: "text", text: `Failed to interrupt async run ${target.asyncId}: ${message}` }],
287
+ isError: true,
288
+ details: { mode: "management", results: [] },
289
+ };
290
+ }
291
+ }
292
+ function nestedRunSessionFile(run) {
293
+ return run.sessionFile ?? (run.steps?.length === 1 ? run.steps[0]?.sessionFile : undefined);
294
+ }
295
+ function nestedRunAgent(run) {
296
+ return run.agent ?? run.agents?.[0] ?? (run.steps?.length === 1 ? run.steps[0]?.agent : undefined);
297
+ }
298
+ function pathWithin(base, candidate) {
299
+ const resolvedBase = path.resolve(base);
300
+ const resolvedCandidate = path.resolve(candidate);
301
+ return resolvedCandidate === resolvedBase || resolvedCandidate.startsWith(`${resolvedBase}${path.sep}`);
302
+ }
303
+ function validateNestedSessionFile(run, trustedSessionRoots) {
304
+ const sessionFile = nestedRunSessionFile(run);
305
+ if (!sessionFile)
306
+ throw new Error(`Nested run '${run.id}' does not have a persisted session file to resume from.`);
307
+ if (path.extname(sessionFile) !== ".jsonl")
308
+ throw new Error(`Nested run '${run.id}' session file must be a .jsonl file: ${sessionFile}`);
309
+ const resolved = path.resolve(sessionFile);
310
+ if (!path.isAbsolute(sessionFile))
311
+ throw new Error(`Nested run '${run.id}' session file must be absolute: ${sessionFile}`);
312
+ if (!fs.existsSync(resolved))
313
+ throw new Error(`Nested run '${run.id}' session file does not exist: ${sessionFile}`);
314
+ const stat = fs.lstatSync(resolved);
315
+ if (!stat.isFile() || stat.isSymbolicLink())
316
+ throw new Error(`Nested run '${run.id}' session file is not a regular file: ${sessionFile}`);
317
+ const realSessionFile = fs.realpathSync(resolved);
318
+ const trustedRoots = trustedSessionRoots
319
+ .filter((root) => fs.existsSync(root))
320
+ .map((root) => fs.realpathSync(root));
321
+ if (!trustedRoots.some((root) => pathWithin(root, realSessionFile))) {
322
+ throw new Error(`Nested run '${run.id}' session file is outside trusted nested session roots: ${sessionFile}`);
323
+ }
324
+ if (!realSessionFile.split(path.sep).includes(run.id)) {
325
+ throw new Error(`Nested run '${run.id}' session file is not under that nested run's session directory: ${sessionFile}`);
326
+ }
327
+ return realSessionFile;
328
+ }
329
+ function resolveNestedResumeTarget(match, trustedSessionRoots) {
330
+ const run = match.match.run;
331
+ if (run.state === "running" || run.state === "queued")
332
+ throw new Error(`Nested run '${run.id}' is live; route the follow-up to the owner process instead.`);
333
+ const agent = nestedRunAgent(run);
334
+ if (!agent)
335
+ throw new Error(`Could not determine child agent for nested run '${run.id}'.`);
336
+ const state = run.state === "complete" || run.state === "failed" || run.state === "blocked" || run.state === "paused" ? run.state : "failed";
337
+ const asyncDir = resolveNestedAsyncDir(match.match.rootRunId, run);
338
+ const effectiveAcceptance = asyncDir ? readStatus(asyncDir)?.steps?.[0]?.acceptance?.effectiveAcceptance : undefined;
339
+ return {
340
+ kind: "revive",
341
+ source: "nested",
342
+ runId: run.id,
343
+ state,
344
+ agent,
345
+ index: 0,
346
+ intercomTarget: resolveSubagentIntercomTarget(run.id, agent, 0),
347
+ cwd: asyncDir ? path.dirname(asyncDir) : undefined,
348
+ sessionFile: validateNestedSessionFile(run, trustedSessionRoots),
349
+ effectiveAcceptance,
350
+ };
351
+ }
352
+ async function waitForNestedControlResult(target, requestId, timeoutMs = 1_000) {
353
+ const deadline = Date.now() + timeoutMs;
354
+ while (Date.now() < deadline) {
355
+ const result = readNestedControlResults(target.match.route).find((candidate) => candidate.requestId === requestId && candidate.targetRunId === target.match.run.id);
356
+ if (result)
357
+ return result;
358
+ await new Promise((resolve) => setTimeout(resolve, 50));
359
+ }
360
+ return undefined;
361
+ }
362
+ async function sendNestedControlRequest(target, action, message, index) {
363
+ const requestId = randomUUID();
364
+ const targetChildIndex = target.match.run.path?.[0]?.stepIndex ?? target.match.run.parentStepIndex;
365
+ writeNestedControlRequest(target.match.route, {
366
+ ts: Date.now(),
367
+ requestId,
368
+ targetRunId: target.match.run.id,
369
+ ...(targetChildIndex !== undefined ? { targetChildIndex } : {}),
370
+ action,
371
+ ...(index !== undefined ? { index } : {}),
372
+ ...(message ? { message } : {}),
373
+ });
374
+ return waitForNestedControlResult(target, requestId);
375
+ }
376
+ function directNestedAsyncInterrupt(target, index) {
377
+ const run = target.match.run;
378
+ const asyncDir = resolveNestedAsyncDir(target.match.rootRunId, run);
379
+ if (!asyncDir)
380
+ return undefined;
381
+ const status = readStatus(asyncDir);
382
+ if (!status || status.runId !== run.id || status.state !== "running")
383
+ return undefined;
384
+ if (index !== undefined && !status.indexedControl && (status.steps?.length ?? 0) > 1)
385
+ return undefined;
386
+ if (index !== undefined && status.steps?.[index]?.status !== "running")
387
+ return { content: [{ type: "text", text: `No running nested child at index ${index}. No siblings were stopped.` }], isError: true, details: { mode: "management", results: [] } };
388
+ try {
389
+ writeAsyncInterruptRequest(asyncDir, run.id, index);
390
+ 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 }) } };
391
+ }
392
+ catch (error) {
393
+ const message = error instanceof Error ? error.message : String(error);
394
+ return { content: [{ type: "text", text: `Failed to interrupt nested async run ${run.id}: ${message}` }], isError: true, details: { mode: "management", results: [] } };
395
+ }
396
+ }
397
+ export async function interruptNestedRun(target, index) {
398
+ const run = target.match.run;
399
+ if (run.state === "complete")
400
+ return { content: [{ type: "text", text: `Nested run ${run.id} is already complete and cannot be interrupted.` }], isError: true, details: { mode: "management", results: [] } };
401
+ if (run.state === "failed")
402
+ return { content: [{ type: "text", text: `Nested run ${run.id} has failed and cannot be interrupted.` }], isError: true, details: { mode: "management", results: [] } };
403
+ if (run.state === "paused")
404
+ return { content: [{ type: "text", text: `Nested run ${run.id} is already paused.` }], isError: true, details: { mode: "management", results: [] } };
405
+ if (index !== undefined && run.indexedControl !== true)
406
+ return directNestedAsyncInterrupt(target, index) ?? {
407
+ 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: [] },
408
+ };
409
+ const result = await sendNestedControlRequest(target, "interrupt", undefined, index);
410
+ if (result?.ok)
411
+ return {
412
+ content: [{ type: "text", text: result.message }],
413
+ details: {
414
+ mode: "management", results: [],
415
+ managementControl: buildManagementControl({ state: "live", runId: run.id, intercomTarget: run.intercomTarget ?? run.leafIntercomTarget, canResume: true, canInterrupt: true }),
416
+ },
417
+ };
418
+ const direct = directNestedAsyncInterrupt(target, index);
419
+ if (direct)
420
+ return direct;
421
+ if (result)
422
+ return { content: [{ type: "text", text: result.message }], isError: true, details: { mode: "management", results: [] } };
423
+ 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: [] } };
424
+ }
425
+ const LIVE_ACCEPTANCE_OVERRIDE_NOTICE = "Acceptance override applies only to revive and was not applied to this live delivery.";
426
+ export function liveLaunchOverrideNotice(params) {
427
+ if (params.acceptance !== undefined)
428
+ return LIVE_ACCEPTANCE_OVERRIDE_NOTICE;
429
+ 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)
430
+ ? "Launch overrides change a newly started continuation, not this live child. No launch settings were changed."
431
+ : undefined;
432
+ }
433
+ async function resumeLiveNestedRun(input) {
434
+ const run = input.target.match.run;
435
+ const result = await sendNestedControlRequest(input.target, "resume", input.message, input.index);
436
+ if (result?.ok) {
437
+ return { content: [{ type: "text", text: [result.message, input.acceptanceOverrideSupplied ? LIVE_ACCEPTANCE_OVERRIDE_NOTICE : undefined].filter(Boolean).join("\n") }], details: { mode: "management", results: [] } };
438
+ }
439
+ const asyncDir = input.index !== undefined ? resolveNestedAsyncDir(input.target.match.rootRunId, run) : undefined;
440
+ const selected = asyncDir ? readStatus(asyncDir)?.steps?.[input.index] : undefined;
441
+ const directTarget = input.index === undefined ? run.leafIntercomTarget ?? run.intercomTarget
442
+ : selected?.status === "running" ? resolveSubagentIntercomTarget(run.id, selected.agent, input.index) : undefined;
443
+ if (directTarget) {
444
+ const delivered = await deliverSubagentIntercomMessageEvent(input.pi.events, directTarget, `Follow-up for nested run ${run.id}:\n\n${input.message}`, 500, { source: "nested-resume-fallback", runId: run.id, agent: run.agent, index: input.index ?? run.currentStep });
445
+ if (delivered) {
446
+ 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: [] } };
447
+ }
448
+ }
449
+ if (result)
450
+ return { content: [{ type: "text", text: result.message }], isError: true, details: { mode: "management", results: [] } };
451
+ 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: [] } };
452
+ }
453
+ function terminalNudgeResult(runId, deps) {
454
+ const owned = resolveOwnedRun(deps.state, runId);
455
+ if (owned) {
456
+ const result = ownedRunStatusResult(owned, deps.state, undefined, { childSafe: Boolean(nestedResolutionScopeForExecutor(deps)) });
457
+ const state = result.details.run.state;
458
+ if (state === "live")
459
+ return undefined;
460
+ 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")}` }] };
461
+ }
462
+ const remembered = deps.state.foregroundRuns?.get(runId);
463
+ const status = remembered
464
+ ? rememberedForegroundStatusResult(remembered, Boolean(nestedResolutionScopeForExecutor(deps)))
465
+ : inspectSubagentStatus({ id: runId }, { state: deps.state, nested: nestedResolutionScopeForExecutor(deps) });
466
+ const state = status.details.managementControl?.state;
467
+ if (status.isError || !state || state === "live" || state === "unknown")
468
+ return undefined;
469
+ return {
470
+ ...status,
471
+ 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")}` }],
472
+ };
473
+ }
474
+ export async function nudgeSubagentRun(input) {
475
+ const message = input.params.message?.trim() || "What are you blocked on? Reply with the smallest next step, or state the exact decision you need.";
476
+ const requestedId = input.params.id ?? input.params.runId;
477
+ let runId;
478
+ let agent;
479
+ let index;
480
+ let target;
481
+ let resolvedRunId;
482
+ try {
483
+ const owned = input.params.dir ? undefined : resolveOwnedRun(input.deps.state, requestedId ?? getAsyncInterruptTarget(input.deps.state, undefined)?.asyncId ?? "latest");
484
+ if (owned) {
485
+ resolvedRunId = owned.runId;
486
+ const terminal = terminalNudgeResult(owned.runId, input.deps);
487
+ if (terminal)
488
+ return terminal;
489
+ const view = ownedRunView(owned, input.deps.state);
490
+ const children = view.children.filter((child) => child.state === "live" && (input.params.index === undefined || input.params.index === child.index));
491
+ if (children.length !== 1)
492
+ throw new Error(`Run '${owned.runId}' has ${children.length} matching live children. Provide index to choose one.`);
493
+ const child = children[0];
494
+ if (child.activity?.status === "pending")
495
+ throw new Error(`Child ${child.index} is waiting to start in run ${owned.runId}. No message was sent and no continuation was started.`);
496
+ runId = owned.runId;
497
+ agent = child.agent;
498
+ index = child.index;
499
+ target = resolveSubagentIntercomTarget(runId, agent, index);
500
+ }
501
+ else {
502
+ const resolved = requestedId ? resolveSubagentRunId(requestedId, { state: input.deps.state, nested: nestedResolutionScopeForExecutor(input.deps) }) : undefined;
503
+ const remembered = !resolved && requestedId ? resolveRememberedForegroundRun(requestedId, input.deps.state) : undefined;
504
+ resolvedRunId = resolved?.id ?? remembered?.runId;
505
+ const terminal = resolvedRunId ? terminalNudgeResult(resolvedRunId, input.deps) : undefined;
506
+ if (terminal)
507
+ return terminal;
508
+ if (resolved?.kind === "nested") {
509
+ const run = resolved.match.run;
510
+ 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";
511
+ const intercomTarget = run.intercomTarget ?? run.leafIntercomTarget;
512
+ const childSafe = Boolean(nestedResolutionScopeForExecutor(input.deps));
513
+ const valid = [formatRunAction("status", run.id, {}, childSafe)];
514
+ if (state === "live" || run.sessionFile)
515
+ valid.push(formatRunAction("resume", run.id, { message: "..." }, childSafe));
516
+ if (state === "live")
517
+ valid.push(formatRunAction("interrupt", run.id, {}, childSafe));
518
+ return {
519
+ content: [{ type: "text", text: `Nested run ${run.id} cannot be nudged. Valid actions: ${valid.join(" or ")}${intercomTarget ? `. Intercom target: ${intercomTarget}` : "."}` }],
520
+ isError: true,
521
+ 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." } }) },
522
+ };
523
+ }
524
+ if (remembered)
525
+ return rememberedForegroundStatusResult(remembered, Boolean(nestedResolutionScopeForExecutor(input.deps)));
526
+ 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 });
527
+ if (asyncTarget.kind !== "live") {
528
+ return terminalNudgeResult(asyncTarget.runId, input.deps)
529
+ ?? inspectSubagentStatus({ id: asyncTarget.runId }, { state: input.deps.state, nested: nestedResolutionScopeForExecutor(input.deps) });
530
+ }
531
+ runId = asyncTarget.runId;
532
+ agent = asyncTarget.agent;
533
+ index = asyncTarget.index;
534
+ target = asyncTarget.intercomTarget;
535
+ }
536
+ }
537
+ catch (error) {
538
+ const terminal = resolvedRunId ? terminalNudgeResult(resolvedRunId, input.deps) : undefined;
539
+ if (terminal)
540
+ return terminal;
541
+ const text = error instanceof Error ? error.message : String(error);
542
+ return { content: [{ type: "text", text }], isError: true, details: { mode: "management", results: [] } };
543
+ }
544
+ const result = await sendLiveSubagentMessage(input.deps.pi.events, {
545
+ to: target,
546
+ message: `Nudge for subagent run ${runId} (${agent}${index !== undefined ? ` step ${index + 1}` : ""}):\n\n${message}`,
547
+ delivery: "steer",
548
+ extra: { source: "subagent-nudge", runId, agent, index },
549
+ });
550
+ if (!result.delivered) {
551
+ const terminal = terminalNudgeResult(runId, input.deps);
552
+ if (terminal)
553
+ return terminal;
554
+ return { content: [{ type: "text", text: [`Nudge was not delivered.`, `Run: ${runId}`, `Intercom target: ${target}`, result.reason ? `Reason: ${result.reason}` : undefined].filter((line) => Boolean(line)).join("\n") }], isError: true, details: { mode: "management", results: [] } };
555
+ }
556
+ return {
557
+ content: [{ type: "text", text: [`Nudge delivered to live subagent.`, `Run: ${runId}`, `Agent: ${agent}`, `Intercom target: ${target}`].join("\n") }],
558
+ details: { mode: "management", results: [], managementControl: buildManagementControl({ state: "live", runId, index, intercomTarget: target, canNudge: true, canResume: true, canInterrupt: true }) },
559
+ };
560
+ }
561
+ export async function resumeAsyncRun(input) {
562
+ const followUp = (input.params.message ?? input.params.task ?? "").trim();
563
+ if (!followUp) {
564
+ return {
565
+ content: [{ type: "text", text: "action='resume' requires message." }],
566
+ isError: true,
567
+ details: { mode: "management", results: [] },
568
+ };
569
+ }
570
+ let target;
571
+ const parentSessionFile = input.ctx.sessionManager.getSessionFile() ?? null;
572
+ const nudgeContinuation = (runId, sessionFile) => {
573
+ if (!sessionFile)
574
+ return;
575
+ for (const candidate of input.deps.state.ownedRuns?.values() ?? []) {
576
+ if (candidate.runId === runId)
577
+ continue;
578
+ if (!candidate.children.some((child) => child.sessionFile === sessionFile)
579
+ && input.deps.state.asyncJobs.get(candidate.runId)?.sessionFile !== sessionFile)
580
+ continue;
581
+ const active = ownedRunView(candidate, input.deps.state, { readConfiguration: false }).children.find((entry) => entry.sessionFile === sessionFile && entry.state === "live");
582
+ if (!active)
583
+ continue;
584
+ return nudgeSubagentRun({ params: { ...input.params, dir: undefined, id: candidate.runId, index: active.index, message: followUp }, deps: input.deps, ctx: input.ctx }).then((result) => {
585
+ const notice = liveLaunchOverrideNotice(input.params);
586
+ if (notice)
587
+ result.content.push({ type: "text", text: notice });
588
+ return result;
589
+ });
590
+ }
591
+ };
592
+ try {
593
+ const requestedId = input.params.id ?? input.params.runId;
594
+ const pendingQuestions = requestedId && !input.params.dir
595
+ ? listSupervisorQuestions(input.ctx.sessionManager.getSessionId(), requestedId).filter((question) => question.state === "awaiting_input" || question.state === "answer_pending")
596
+ : [];
597
+ if (pendingQuestions.length)
598
+ return continueQuestionSession(input, pendingQuestions);
599
+ const owned = requestedId && !input.params.dir ? resolveOwnedRun(input.deps.state, requestedId) : undefined;
600
+ if (owned) {
601
+ const view = ownedRunView(owned, input.deps.state);
602
+ if (view.children.length > 1 && input.params.index === undefined)
603
+ throw new Error(`Run '${owned.runId}' has ${view.children.length} children. Provide index to choose one.`);
604
+ const child = view.children.find((entry) => entry.index === (input.params.index ?? 0));
605
+ if (!child)
606
+ throw new Error(`Run '${owned.runId}' has no child at index ${input.params.index ?? 0}.`);
607
+ if (child.state === "live") {
608
+ const result = await nudgeSubagentRun({ params: { ...input.params, id: owned.runId, message: followUp }, deps: input.deps, ctx: input.ctx });
609
+ const notice = liveLaunchOverrideNotice(input.params);
610
+ if (notice)
611
+ result.content.push({ type: "text", text: notice });
612
+ return result;
613
+ }
614
+ const continuation = nudgeContinuation(owned.runId, child.sessionFile);
615
+ if (continuation)
616
+ return await continuation;
617
+ const contract = readQuestionContract(owned.runId, child.index);
618
+ return reviveSavedSubagent(input, {
619
+ ...contract, runId: owned.runId, agent: child.agent, index: child.index, source: owned.source,
620
+ cwd: child.launch?.cwd ?? owned.cwd, sessionFile: child.sessionFile,
621
+ effectiveAcceptance: contract?.effectiveAcceptance ?? child.result?.acceptance?.effectiveAcceptance,
622
+ model: child.result?.model,
623
+ });
624
+ }
625
+ const resolved = requestedId ? resolveSubagentRunId(requestedId, { state: input.deps.state, nested: nestedResolutionScopeForExecutor(input.deps) }) : undefined;
626
+ if (resolved?.kind === "nested") {
627
+ if (resolved.match.run.state === "running" || resolved.match.run.state === "queued") {
628
+ return resumeLiveNestedRun({ target: resolved, message: followUp, index: input.params.index, acceptanceOverrideSupplied: input.params.acceptance !== undefined, pi: input.deps.pi, childSafe: Boolean(nestedResolutionScopeForExecutor(input.deps)) });
629
+ }
630
+ const trustedSessionRoots = [
631
+ ...(input.deps.config.defaultSessionDir ? [path.resolve(input.deps.expandTilde(input.deps.config.defaultSessionDir))] : []),
632
+ ...(parentSessionFile ? [input.deps.getSubagentSessionRoot(parentSessionFile)] : []),
633
+ ];
634
+ target = resolveNestedResumeTarget(resolved, trustedSessionRoots);
635
+ }
636
+ else {
637
+ target = resolveResumeTarget(input.params, input.deps.state);
638
+ }
639
+ }
640
+ catch (error) {
641
+ const message = error instanceof Error ? error.message : String(error);
642
+ const requestedId = input.params.id ?? input.params.runId;
643
+ if (requestedId && !input.params.dir && (isAsyncRunNotFound(error) || message.includes("is detached for intercom coordination"))) {
644
+ try {
645
+ const questions = listSupervisorQuestions(input.ctx.sessionManager.getSessionId(), requestedId);
646
+ if (questions.length)
647
+ return continueQuestionSession(input, questions);
648
+ }
649
+ catch (questionError) {
650
+ return { content: [{ type: "text", text: questionError instanceof Error ? questionError.message : String(questionError) }], isError: true, details: { mode: "management", results: [] } };
651
+ }
652
+ }
653
+ return { content: [{ type: "text", text: message }], isError: true, details: { mode: "management", results: [] } };
654
+ }
655
+ if (target.kind === "live") {
656
+ const delivered = await deliverSubagentIntercomMessageEvent(input.deps.pi.events, target.intercomTarget, `Follow-up for async run ${target.runId} (${target.agent}):\n\n${followUp}`, 500, { source: "async-resume", runId: target.runId, agent: target.agent, index: target.index });
657
+ if (delivered) {
658
+ return {
659
+ 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") }],
660
+ details: { mode: "management", results: [], managementControl: buildManagementControl({ state: "live", runId: target.runId, index: target.index, intercomTarget: target.intercomTarget, canNudge: true, canResume: true, canInterrupt: true }) },
661
+ };
662
+ }
663
+ return {
664
+ 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") }],
665
+ isError: true,
666
+ details: { mode: "management", results: [] },
667
+ };
668
+ }
669
+ return nudgeContinuation(target.runId, target.sessionFile) ?? reviveSavedSubagent(input, target);
670
+ }
671
+ function continueQuestionSession(input, questions) {
672
+ if (input.params.index === undefined && new Set(questions.map((question) => question.index)).size > 1)
673
+ throw new Error("Provide index to choose which child question to continue.");
674
+ const question = questions.findLast((entry) => input.params.index === undefined || entry.index === input.params.index);
675
+ if (!question)
676
+ throw new Error("No saved question for that child index.");
677
+ if (questionProcessAlive(question))
678
+ throw new Error("The question's child is still alive. Answer the question or stop it before continuing.");
679
+ if (question.delivery?.kind === "revive")
680
+ 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)
692
+ recordQuestionDelivery(question, { kind: "revive", runId: result.details.asyncId, deliveredAt: Date.now() });
693
+ return result;
694
+ }
695
+ export function reviveSavedSubagent(input, target, runId = randomUUID()) {
696
+ const followUp = (input.params.message ?? input.params.task ?? "").trim();
697
+ const parentSessionFile = input.ctx.sessionManager.getSessionFile() ?? null;
698
+ if (!target.sessionFile || path.extname(target.sessionFile) !== ".jsonl" || !fs.existsSync(target.sessionFile)) {
699
+ 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: [] } };
700
+ }
701
+ const { blocked, depth, maxDepth } = checkSubagentDepth(input.deps.config.maxSubagentDepth);
702
+ if (blocked) {
703
+ return {
704
+ content: [{ type: "text", text: `Nested subagent resume blocked (depth=${depth}, max=${maxDepth}). Complete the follow-up directly instead.` }],
705
+ isError: true,
706
+ details: { mode: "management", results: [] },
707
+ };
708
+ }
709
+ input.deps.state.currentSessionId = resolveCurrentSessionId(input.ctx.sessionManager);
710
+ const contract = readQuestionContract(target.runId, target.index) ?? target;
711
+ const savedLaunch = input.params.agent === undefined && contract.launch ? { ...contract.launch, ...contract.effectiveConfiguration } : undefined;
712
+ const generatedOutputFilename = input.params.output === undefined ? contract.launch?.generatedOutputFilename : undefined;
713
+ const effectiveCwd = input.params.cwd ?? savedLaunch?.cwd ?? target.cwd ?? input.requestCwd;
714
+ const scope = resolveExecutionAgentScope(input.params.agentScope);
715
+ if (!savedLaunch && !input.params.agent) {
716
+ 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: [] } };
717
+ }
718
+ const discoveredAgents = savedLaunch ? [savedLaunch.agent] : input.deps.discoverAgents(effectiveCwd, scope, { projectTrusted: input.ctx.isProjectTrusted() }).agents;
719
+ const fallbackTarget = resolveIntercomSessionTarget(input.deps.pi.getSessionName(), input.ctx.sessionManager.getSessionId());
720
+ const orchestratorTarget = resolveOrchestratorIntercomTarget(input.deps.pi.events, fallbackTarget);
721
+ const intercomBridge = resolveIntercomBridge(orchestratorTarget);
722
+ const inheritedModel = providerQualifiedModelId(input.ctx.model?.provider, input.ctx.model?.id);
723
+ const agents = savedLaunch ? discoveredAgents : discoveredAgents.map((agent) => agent.model || !inheritedModel ? agent : { ...agent, model: inheritedModel });
724
+ const selectedAgent = input.params.agent ?? target.agent;
725
+ const profile = agents.find((agent) => agent.name === selectedAgent);
726
+ const agentConfig = profile && savedLaunch ? { ...profile, thinking: savedLaunch.thinking ?? profile.thinking, maxExecutionTimeMs: savedLaunch.maxExecutionTimeMs, maxTokens: savedLaunch.maxTokens } : profile;
727
+ if (!agentConfig) {
728
+ return {
729
+ content: [{ type: "text", text: `Unknown agent for resume: ${selectedAgent}` }],
730
+ isError: true,
731
+ details: { mode: "management", results: [] },
732
+ };
733
+ }
734
+ saveQuestionOwner(runId, input.ctx.sessionManager.getSessionId());
735
+ const prior = input.deps.state.ownedRuns?.get(target.runId);
736
+ rememberOwnedRun(input.deps.state, {
737
+ runId, ownerSessionId: input.ctx.sessionManager.getSessionId(), rootRunId: prior?.rootRunId ?? target.runId,
738
+ predecessorRunId: target.runId, predecessorIndex: target.index,
739
+ source: "async", mode: "single", cwd: effectiveCwd, task: followUp, startedAt: Date.now(),
740
+ children: [{ agent: selectedAgent, index: 0, task: followUp, label: prior?.children.find((child) => child.index === target.index)?.label, sessionFile: target.sessionFile }],
741
+ });
742
+ const modelOverride = input.params.model;
743
+ const skill = normalizeSkillInput(input.params.skill);
744
+ const availableModels = input.ctx.modelRegistry.getAvailable().map(toModelInfo);
745
+ const launchInput = {
746
+ agent: selectedAgent,
747
+ task: buildRevivedAsyncTask(target, followUp, input.params.messageOrigin),
748
+ agentConfig,
749
+ ctx: {
750
+ pi: input.deps.pi,
751
+ cwd: input.requestCwd,
752
+ currentSessionId: input.deps.state.currentSessionId,
753
+ rootSessionId: resolveRootSessionId(input.ctx.sessionManager),
754
+ currentModelProvider: input.ctx.model?.provider,
755
+ projectTrusted: input.ctx.isProjectTrusted(),
756
+ },
757
+ cwd: effectiveCwd,
758
+ maxOutput: input.params.maxOutput ?? savedLaunch?.maxOutput,
759
+ artifactsDir: input.params.artifacts === false || (input.params.artifacts === undefined && savedLaunch?.artifacts === false) ? undefined : savedLaunch?.artifactsDir ?? input.deps.tempArtifactsDir,
760
+ shareEnabled: input.params.share ?? savedLaunch?.share ?? false,
761
+ sessionRoot: input.deps.getSubagentSessionRoot(parentSessionFile),
762
+ sessionFile: target.sessionFile,
763
+ maxSubagentDepth: savedLaunch?.maxSubagentDepth ?? resolveCurrentMaxSubagentDepth(input.deps.config.maxSubagentDepth),
764
+ worktreeSetupHook: input.deps.config.worktreeSetupHook,
765
+ worktreeSetupHookTimeoutMs: input.deps.config.worktreeSetupHookTimeoutMs,
766
+ controlConfig: resolveControlConfig(savedLaunch?.controlConfig ?? input.deps.config.control, input.params.control),
767
+ controlIntercomTarget: intercomBridge.orchestratorTarget,
768
+ childIntercomTarget: (agent, index) => resolveSubagentIntercomTarget(runId, agent, index),
769
+ availableModels, savedLaunch, modelOverride,
770
+ skills: skill,
771
+ acceptance: input.params.acceptance ?? acceptanceInputFromResolved(contract.effectiveAcceptance ?? savedLaunch?.effectiveAcceptance),
772
+ output: input.params.output ?? (generatedOutputFilename ? true : savedLaunch?.output ?? contract.output),
773
+ generatedOutputFilename,
774
+ outputMode: input.params.outputMode ?? savedLaunch?.outputMode ?? contract.outputMode,
775
+ outputSchema: input.params.outputSchema ?? savedLaunch?.outputSchema ?? contract.outputSchema,
776
+ projectTrust: savedLaunch?.projectTrust ?? resolveConfiguredChildProjectTrustPolicy(input.deps.config.projectTrust),
777
+ };
778
+ const undoCwdRequest = input.params.cwd !== undefined ? requestChildExecutionCwd(target.sessionFile, effectiveCwd) : undefined;
779
+ let result;
780
+ try {
781
+ result = executeAsyncSingle(runId, launchInput);
782
+ }
783
+ catch (error) {
784
+ undoCwdRequest?.();
785
+ throw error;
786
+ }
787
+ if (result.isError)
788
+ undoCwdRequest?.();
789
+ const owned = input.deps.state.ownedRuns?.get(runId);
790
+ if (owned)
791
+ 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") } : {}) });
792
+ if (result.isError)
793
+ return result;
794
+ const revivedId = result.details.asyncId ?? runId;
795
+ const revivedTarget = resolveSubagentIntercomTarget(revivedId, selectedAgent, 0);
796
+ const sourceLabel = target.source;
797
+ const lines = [
798
+ `Revived ${sourceLabel} subagent from ${target.runId}.`,
799
+ `Run mapping: ${target.runId} -> ${revivedId}`,
800
+ `Revived run: ${revivedId}`,
801
+ `Agent: ${selectedAgent}`,
802
+ `Configuration: ${savedLaunch ? "saved effective launch" : "explicitly selected current profile"}${modelOverride ? `; model ${modelOverride}` : ""}`,
803
+ `Root: ${prior?.rootRunId ?? target.runId}`,
804
+ `Session: ${target.sessionFile}`,
805
+ result.details.asyncDir ? `Async dir: ${result.details.asyncDir}` : undefined,
806
+ revivedTarget ? `Intercom target: ${revivedTarget} (if registered)` : undefined,
807
+ `Prior pending-reply context for ${target.runId} is invalid; use the revived run and target only.`,
808
+ `Status if needed: ${formatRunAction("status", revivedId, {}, Boolean(nestedResolutionScopeForExecutor(input.deps)))}`,
809
+ ].filter((line) => Boolean(line));
810
+ return {
811
+ content: [{ type: "text", text: formatAsyncStartedMessage(lines.join("\n"), Boolean(nestedResolutionScopeForExecutor(input.deps))) }],
812
+ details: {
813
+ ...result.details,
814
+ managementControl: buildManagementControl({ state: "live", runId: revivedId, index: 0, intercomTarget: revivedTarget, canInterrupt: true, revivedFromRunId: target.runId }),
815
+ },
816
+ };
817
+ }