@agimon-ai/doompi-team 0.0.1-alpha.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 (520) hide show
  1. package/LICENSE +25 -0
  2. package/README.md +46 -0
  3. package/dist/_virtual/_@oxc-project_runtime@0.127.0/helpers/decorate.cjs +1 -0
  4. package/dist/_virtual/_@oxc-project_runtime@0.127.0/helpers/decorate.mjs +1 -0
  5. package/dist/_virtual/_@oxc-project_runtime@0.127.0/helpers/decorateMetadata.cjs +1 -0
  6. package/dist/_virtual/_@oxc-project_runtime@0.127.0/helpers/decorateMetadata.mjs +1 -0
  7. package/dist/_virtual/_@oxc-project_runtime@0.127.0/helpers/decorateParam.cjs +1 -0
  8. package/dist/_virtual/_@oxc-project_runtime@0.127.0/helpers/decorateParam.mjs +1 -0
  9. package/dist/_virtual/_rolldown/runtime.cjs +1 -0
  10. package/dist/_virtual/_rolldown/runtime.mjs +1 -0
  11. package/dist/agents/discovery.cjs +2 -0
  12. package/dist/agents/discovery.cjs.map +1 -0
  13. package/dist/agents/discovery.mjs +2 -0
  14. package/dist/agents/discovery.mjs.map +1 -0
  15. package/dist/agents/frontmatter.cjs +10 -0
  16. package/dist/agents/frontmatter.cjs.map +1 -0
  17. package/dist/agents/frontmatter.mjs +10 -0
  18. package/dist/agents/frontmatter.mjs.map +1 -0
  19. package/dist/agents/loader.cjs +2 -0
  20. package/dist/agents/loader.cjs.map +1 -0
  21. package/dist/agents/loader.mjs +2 -0
  22. package/dist/agents/loader.mjs.map +1 -0
  23. package/dist/agents/memory.cjs +3 -0
  24. package/dist/agents/memory.cjs.map +1 -0
  25. package/dist/agents/memory.mjs +3 -0
  26. package/dist/agents/memory.mjs.map +1 -0
  27. package/dist/agents/projectRoot.cjs +2 -0
  28. package/dist/agents/projectRoot.cjs.map +1 -0
  29. package/dist/agents/projectRoot.mjs +2 -0
  30. package/dist/agents/projectRoot.mjs.map +1 -0
  31. package/dist/agents/settings.cjs +2 -0
  32. package/dist/agents/settings.cjs.map +1 -0
  33. package/dist/agents/settings.mjs +2 -0
  34. package/dist/agents/settings.mjs.map +1 -0
  35. package/dist/agents/skills.cjs +0 -0
  36. package/dist/agents/skills.cjs.map +1 -0
  37. package/dist/agents/skills.mjs +5 -0
  38. package/dist/agents/skills.mjs.map +1 -0
  39. package/dist/agents/types.d.cts +121 -0
  40. package/dist/agents/types.d.cts.map +1 -0
  41. package/dist/agents/types.d.mts +121 -0
  42. package/dist/agents/types.d.mts.map +1 -0
  43. package/dist/api/backgroundWork.cjs +1 -0
  44. package/dist/api/backgroundWork.d.cts +2 -0
  45. package/dist/api/backgroundWork.d.mts +2 -0
  46. package/dist/api/backgroundWork.mjs +1 -0
  47. package/dist/api/capabilityCeiling.cjs +1 -0
  48. package/dist/api/capabilityCeiling.d.cts +3 -0
  49. package/dist/api/capabilityCeiling.d.mts +3 -0
  50. package/dist/api/capabilityCeiling.mjs +2 -0
  51. package/dist/api/capabilityCeiling.mjs.map +1 -0
  52. package/dist/api/delegation.cjs +1 -0
  53. package/dist/api/delegation.d.cts +2 -0
  54. package/dist/api/delegation.d.mts +2 -0
  55. package/dist/api/delegation.mjs +1 -0
  56. package/dist/api/teamSnapshot.cjs +1 -0
  57. package/dist/api/teamSnapshot.d.cts +2 -0
  58. package/dist/api/teamSnapshot.d.mts +2 -0
  59. package/dist/api/teamSnapshot.mjs +2 -0
  60. package/dist/api/teamSnapshot.mjs.map +1 -0
  61. package/dist/container/bindOrRebind.cjs +2 -0
  62. package/dist/container/bindOrRebind.cjs.map +1 -0
  63. package/dist/container/bindOrRebind.mjs +2 -0
  64. package/dist/container/bindOrRebind.mjs.map +1 -0
  65. package/dist/container/globalBridge.cjs +2 -0
  66. package/dist/container/globalBridge.cjs.map +1 -0
  67. package/dist/container/globalBridge.d.cts +19 -0
  68. package/dist/container/globalBridge.d.cts.map +1 -0
  69. package/dist/container/globalBridge.d.mts +19 -0
  70. package/dist/container/globalBridge.d.mts.map +1 -0
  71. package/dist/container/globalBridge.mjs +2 -0
  72. package/dist/container/globalBridge.mjs.map +1 -0
  73. package/dist/container/index.cjs +2 -0
  74. package/dist/container/index.cjs.map +1 -0
  75. package/dist/container/index.d.cts +38 -0
  76. package/dist/container/index.d.cts.map +1 -0
  77. package/dist/container/index.d.mts +38 -0
  78. package/dist/container/index.d.mts.map +1 -0
  79. package/dist/container/index.mjs +2 -0
  80. package/dist/container/index.mjs.map +1 -0
  81. package/dist/container/modules/agents.cjs +2 -0
  82. package/dist/container/modules/agents.cjs.map +1 -0
  83. package/dist/container/modules/agents.d.cts +1 -0
  84. package/dist/container/modules/agents.d.mts +1 -0
  85. package/dist/container/modules/agents.mjs +2 -0
  86. package/dist/container/modules/agents.mjs.map +1 -0
  87. package/dist/container/modules/core.cjs +2 -0
  88. package/dist/container/modules/core.cjs.map +1 -0
  89. package/dist/container/modules/core.d.cts +1 -0
  90. package/dist/container/modules/core.d.mts +1 -0
  91. package/dist/container/modules/core.mjs +2 -0
  92. package/dist/container/modules/core.mjs.map +1 -0
  93. package/dist/container/modules/extensions.cjs +2 -0
  94. package/dist/container/modules/extensions.cjs.map +1 -0
  95. package/dist/container/modules/extensions.mjs +2 -0
  96. package/dist/container/modules/extensions.mjs.map +1 -0
  97. package/dist/container/modules/intercom.cjs +2 -0
  98. package/dist/container/modules/intercom.cjs.map +1 -0
  99. package/dist/container/modules/intercom.mjs +2 -0
  100. package/dist/container/modules/intercom.mjs.map +1 -0
  101. package/dist/container/modules/runs.cjs +2 -0
  102. package/dist/container/modules/runs.cjs.map +1 -0
  103. package/dist/container/modules/runs.mjs +2 -0
  104. package/dist/container/modules/runs.mjs.map +1 -0
  105. package/dist/container/types.cjs +2 -0
  106. package/dist/container/types.cjs.map +1 -0
  107. package/dist/container/types.d.cts +45 -0
  108. package/dist/container/types.d.cts.map +1 -0
  109. package/dist/container/types.d.mts +45 -0
  110. package/dist/container/types.d.mts.map +1 -0
  111. package/dist/container/types.mjs +2 -0
  112. package/dist/container/types.mjs.map +1 -0
  113. package/dist/env.cjs +2 -0
  114. package/dist/env.cjs.map +1 -0
  115. package/dist/env.d.cts +65 -0
  116. package/dist/env.d.cts.map +1 -0
  117. package/dist/env.d.mts +65 -0
  118. package/dist/env.d.mts.map +1 -0
  119. package/dist/env.mjs +2 -0
  120. package/dist/env.mjs.map +1 -0
  121. package/dist/extension.cjs +2 -0
  122. package/dist/extension.cjs.map +1 -0
  123. package/dist/extension.mjs +2 -0
  124. package/dist/extension.mjs.map +1 -0
  125. package/dist/extensions/config.cjs +2 -0
  126. package/dist/extensions/config.cjs.map +1 -0
  127. package/dist/extensions/config.mjs +2 -0
  128. package/dist/extensions/config.mjs.map +1 -0
  129. package/dist/extensions/delegationBridge.cjs +2 -0
  130. package/dist/extensions/delegationBridge.cjs.map +1 -0
  131. package/dist/extensions/delegationBridge.mjs +2 -0
  132. package/dist/extensions/delegationBridge.mjs.map +1 -0
  133. package/dist/extensions/doom.cjs +2 -0
  134. package/dist/extensions/doom.cjs.map +1 -0
  135. package/dist/extensions/doom.d.cts +8 -0
  136. package/dist/extensions/doom.d.cts.map +1 -0
  137. package/dist/extensions/doom.d.mts +8 -0
  138. package/dist/extensions/doom.d.mts.map +1 -0
  139. package/dist/extensions/doom.mjs +2 -0
  140. package/dist/extensions/doom.mjs.map +1 -0
  141. package/dist/extensions/fablePlanBridge.cjs +2 -0
  142. package/dist/extensions/fablePlanBridge.cjs.map +1 -0
  143. package/dist/extensions/fablePlanBridge.mjs +2 -0
  144. package/dist/extensions/fablePlanBridge.mjs.map +1 -0
  145. package/dist/extensions/managementActions.cjs +2 -0
  146. package/dist/extensions/managementActions.cjs.map +1 -0
  147. package/dist/extensions/managementActions.mjs +2 -0
  148. package/dist/extensions/managementActions.mjs.map +1 -0
  149. package/dist/extensions/operationJournal.cjs +2 -0
  150. package/dist/extensions/operationJournal.cjs.map +1 -0
  151. package/dist/extensions/operationJournal.mjs +2 -0
  152. package/dist/extensions/operationJournal.mjs.map +1 -0
  153. package/dist/extensions/orchestratorPrompt.cjs +35 -0
  154. package/dist/extensions/orchestratorPrompt.cjs.map +1 -0
  155. package/dist/extensions/orchestratorPrompt.mjs +35 -0
  156. package/dist/extensions/orchestratorPrompt.mjs.map +1 -0
  157. package/dist/extensions/pi.cjs +2 -0
  158. package/dist/extensions/pi.cjs.map +1 -0
  159. package/dist/extensions/pi.d.cts +2 -0
  160. package/dist/extensions/pi.d.mts +2 -0
  161. package/dist/extensions/pi.mjs +2 -0
  162. package/dist/extensions/pi.mjs.map +1 -0
  163. package/dist/extensions/schemas.cjs +1 -0
  164. package/dist/extensions/schemas.mjs +2 -0
  165. package/dist/extensions/schemas.mjs.map +1 -0
  166. package/dist/extensions/spawnPlan.cjs +3 -0
  167. package/dist/extensions/spawnPlan.cjs.map +1 -0
  168. package/dist/extensions/spawnPlan.mjs +3 -0
  169. package/dist/extensions/spawnPlan.mjs.map +1 -0
  170. package/dist/extensions/standard.cjs +2 -0
  171. package/dist/extensions/standard.cjs.map +1 -0
  172. package/dist/extensions/standard.d.cts +8 -0
  173. package/dist/extensions/standard.d.cts.map +1 -0
  174. package/dist/extensions/standard.d.mts +8 -0
  175. package/dist/extensions/standard.d.mts.map +1 -0
  176. package/dist/extensions/standard.mjs +2 -0
  177. package/dist/extensions/standard.mjs.map +1 -0
  178. package/dist/extensions/statusViews.cjs +7 -0
  179. package/dist/extensions/statusViews.cjs.map +1 -0
  180. package/dist/extensions/statusViews.mjs +7 -0
  181. package/dist/extensions/statusViews.mjs.map +1 -0
  182. package/dist/extensions/subagentPromptRuntime.cjs +18 -0
  183. package/dist/extensions/subagentPromptRuntime.cjs.map +1 -0
  184. package/dist/extensions/subagentPromptRuntime.d.cts +58 -0
  185. package/dist/extensions/subagentPromptRuntime.d.cts.map +1 -0
  186. package/dist/extensions/subagentPromptRuntime.d.mts +58 -0
  187. package/dist/extensions/subagentPromptRuntime.d.mts.map +1 -0
  188. package/dist/extensions/subagentPromptRuntime.mjs +18 -0
  189. package/dist/extensions/subagentPromptRuntime.mjs.map +1 -0
  190. package/dist/extensions/subagentPromptRuntimeEntry.cjs +2 -0
  191. package/dist/extensions/subagentPromptRuntimeEntry.cjs.map +1 -0
  192. package/dist/extensions/subagentPromptRuntimeEntry.d.cts +2 -0
  193. package/dist/extensions/subagentPromptRuntimeEntry.d.mts +2 -0
  194. package/dist/extensions/subagentPromptRuntimeEntry.mjs +2 -0
  195. package/dist/extensions/subagentPromptRuntimeEntry.mjs.map +1 -0
  196. package/dist/extensions/subagentTool.cjs +7 -0
  197. package/dist/extensions/subagentTool.cjs.map +1 -0
  198. package/dist/extensions/subagentTool.mjs +7 -0
  199. package/dist/extensions/subagentTool.mjs.map +1 -0
  200. package/dist/extensions/toolDescription.cjs +2 -0
  201. package/dist/extensions/toolDescription.cjs.map +1 -0
  202. package/dist/extensions/toolDescription.mjs +2 -0
  203. package/dist/extensions/toolDescription.mjs.map +1 -0
  204. package/dist/index.cjs +2 -0
  205. package/dist/index.cjs.map +1 -0
  206. package/dist/index.d.cts +34 -0
  207. package/dist/index.d.cts.map +1 -0
  208. package/dist/index.d.mts +34 -0
  209. package/dist/index.d.mts.map +1 -0
  210. package/dist/index.mjs +2 -0
  211. package/dist/index.mjs.map +1 -0
  212. package/dist/intercom/intercomBridge.cjs +2 -0
  213. package/dist/intercom/intercomBridge.cjs.map +1 -0
  214. package/dist/intercom/intercomBridge.mjs +2 -0
  215. package/dist/intercom/intercomBridge.mjs.map +1 -0
  216. package/dist/intercom/nativeTeamChannel.cjs +5 -0
  217. package/dist/intercom/nativeTeamChannel.cjs.map +1 -0
  218. package/dist/intercom/nativeTeamChannel.d.cts +22 -0
  219. package/dist/intercom/nativeTeamChannel.d.cts.map +1 -0
  220. package/dist/intercom/nativeTeamChannel.d.mts +22 -0
  221. package/dist/intercom/nativeTeamChannel.d.mts.map +1 -0
  222. package/dist/intercom/nativeTeamChannel.mjs +5 -0
  223. package/dist/intercom/nativeTeamChannel.mjs.map +1 -0
  224. package/dist/intercom/supervisorControlChannel.cjs +2 -0
  225. package/dist/intercom/supervisorControlChannel.cjs.map +1 -0
  226. package/dist/intercom/supervisorControlChannel.mjs +2 -0
  227. package/dist/intercom/supervisorControlChannel.mjs.map +1 -0
  228. package/dist/protocol/backgroundWork.cjs +2 -0
  229. package/dist/protocol/backgroundWork.cjs.map +1 -0
  230. package/dist/protocol/backgroundWork.d.cts +52 -0
  231. package/dist/protocol/backgroundWork.d.cts.map +1 -0
  232. package/dist/protocol/backgroundWork.d.mts +52 -0
  233. package/dist/protocol/backgroundWork.d.mts.map +1 -0
  234. package/dist/protocol/backgroundWork.mjs +2 -0
  235. package/dist/protocol/backgroundWork.mjs.map +1 -0
  236. package/dist/protocol/capabilityCeiling.cjs +2 -0
  237. package/dist/protocol/capabilityCeiling.cjs.map +1 -0
  238. package/dist/protocol/capabilityCeiling.d.cts +51 -0
  239. package/dist/protocol/capabilityCeiling.d.cts.map +1 -0
  240. package/dist/protocol/capabilityCeiling.d.mts +51 -0
  241. package/dist/protocol/capabilityCeiling.d.mts.map +1 -0
  242. package/dist/protocol/capabilityCeiling.mjs +2 -0
  243. package/dist/protocol/capabilityCeiling.mjs.map +1 -0
  244. package/dist/protocol/delegation.cjs +2 -0
  245. package/dist/protocol/delegation.cjs.map +1 -0
  246. package/dist/protocol/delegation.d.cts +269 -0
  247. package/dist/protocol/delegation.d.cts.map +1 -0
  248. package/dist/protocol/delegation.d.mts +269 -0
  249. package/dist/protocol/delegation.d.mts.map +1 -0
  250. package/dist/protocol/delegation.mjs +2 -0
  251. package/dist/protocol/delegation.mjs.map +1 -0
  252. package/dist/runs/background/asyncExecution.cjs +2 -0
  253. package/dist/runs/background/asyncExecution.cjs.map +1 -0
  254. package/dist/runs/background/asyncExecution.mjs +2 -0
  255. package/dist/runs/background/asyncExecution.mjs.map +1 -0
  256. package/dist/runs/background/asyncJobTracker.cjs +2 -0
  257. package/dist/runs/background/asyncJobTracker.cjs.map +1 -0
  258. package/dist/runs/background/asyncJobTracker.mjs +2 -0
  259. package/dist/runs/background/asyncJobTracker.mjs.map +1 -0
  260. package/dist/runs/background/cliRunnerEntry.cjs +2 -0
  261. package/dist/runs/background/cliRunnerEntry.cjs.map +1 -0
  262. package/dist/runs/background/cliRunnerEntry.d.cts +1 -0
  263. package/dist/runs/background/cliRunnerEntry.d.mts +1 -0
  264. package/dist/runs/background/cliRunnerEntry.mjs +2 -0
  265. package/dist/runs/background/cliRunnerEntry.mjs.map +1 -0
  266. package/dist/runs/background/completionBatcher.cjs +2 -0
  267. package/dist/runs/background/completionBatcher.cjs.map +1 -0
  268. package/dist/runs/background/completionBatcher.mjs +2 -0
  269. package/dist/runs/background/completionBatcher.mjs.map +1 -0
  270. package/dist/runs/background/notify.cjs +4 -0
  271. package/dist/runs/background/notify.cjs.map +1 -0
  272. package/dist/runs/background/notify.mjs +4 -0
  273. package/dist/runs/background/notify.mjs.map +1 -0
  274. package/dist/runs/background/parentWatchdog.cjs +2 -0
  275. package/dist/runs/background/parentWatchdog.cjs.map +1 -0
  276. package/dist/runs/background/parentWatchdog.mjs +2 -0
  277. package/dist/runs/background/parentWatchdog.mjs.map +1 -0
  278. package/dist/runs/background/processTerminal.cjs +2 -0
  279. package/dist/runs/background/processTerminal.cjs.map +1 -0
  280. package/dist/runs/background/processTerminal.mjs +2 -0
  281. package/dist/runs/background/processTerminal.mjs.map +1 -0
  282. package/dist/runs/background/resultWatcher.cjs +2 -0
  283. package/dist/runs/background/resultWatcher.cjs.map +1 -0
  284. package/dist/runs/background/resultWatcher.mjs +2 -0
  285. package/dist/runs/background/resultWatcher.mjs.map +1 -0
  286. package/dist/runs/background/runIdResolver.cjs +2 -0
  287. package/dist/runs/background/runIdResolver.cjs.map +1 -0
  288. package/dist/runs/background/runIdResolver.mjs +2 -0
  289. package/dist/runs/background/runIdResolver.mjs.map +1 -0
  290. package/dist/runs/background/runnerBootstrap.cjs +2 -0
  291. package/dist/runs/background/runnerBootstrap.cjs.map +1 -0
  292. package/dist/runs/background/runnerBootstrap.mjs +2 -0
  293. package/dist/runs/background/runnerBootstrap.mjs.map +1 -0
  294. package/dist/runs/background/runnerExecution.cjs +2 -0
  295. package/dist/runs/background/runnerExecution.cjs.map +1 -0
  296. package/dist/runs/background/runnerExecution.mjs +2 -0
  297. package/dist/runs/background/runnerExecution.mjs.map +1 -0
  298. package/dist/runs/background/runnerReporting.cjs +3 -0
  299. package/dist/runs/background/runnerReporting.cjs.map +1 -0
  300. package/dist/runs/background/runnerReporting.mjs +3 -0
  301. package/dist/runs/background/runnerReporting.mjs.map +1 -0
  302. package/dist/runs/background/runnerTeamMembership.cjs +2 -0
  303. package/dist/runs/background/runnerTeamMembership.cjs.map +1 -0
  304. package/dist/runs/background/runnerTeamMembership.mjs +2 -0
  305. package/dist/runs/background/runnerTeamMembership.mjs.map +1 -0
  306. package/dist/runs/background/spawnHandshake.cjs +2 -0
  307. package/dist/runs/background/spawnHandshake.cjs.map +1 -0
  308. package/dist/runs/background/spawnHandshake.mjs +2 -0
  309. package/dist/runs/background/spawnHandshake.mjs.map +1 -0
  310. package/dist/runs/background/staleRunReconciler.cjs +2 -0
  311. package/dist/runs/background/staleRunReconciler.cjs.map +1 -0
  312. package/dist/runs/background/staleRunReconciler.mjs +2 -0
  313. package/dist/runs/background/staleRunReconciler.mjs.map +1 -0
  314. package/dist/runs/background/statusReader.cjs +2 -0
  315. package/dist/runs/background/statusReader.cjs.map +1 -0
  316. package/dist/runs/background/statusReader.mjs +2 -0
  317. package/dist/runs/background/statusReader.mjs.map +1 -0
  318. package/dist/runs/background/statusWriter.cjs +2 -0
  319. package/dist/runs/background/statusWriter.cjs.map +1 -0
  320. package/dist/runs/background/statusWriter.mjs +2 -0
  321. package/dist/runs/background/statusWriter.mjs.map +1 -0
  322. package/dist/runs/background/subagentWait.cjs +2 -0
  323. package/dist/runs/background/subagentWait.cjs.map +1 -0
  324. package/dist/runs/background/subagentWait.mjs +2 -0
  325. package/dist/runs/background/subagentWait.mjs.map +1 -0
  326. package/dist/runs/background/terminalPersistence.cjs +2 -0
  327. package/dist/runs/background/terminalPersistence.cjs.map +1 -0
  328. package/dist/runs/background/terminalPersistence.mjs +2 -0
  329. package/dist/runs/background/terminalPersistence.mjs.map +1 -0
  330. package/dist/runs/registry/processGroup.cjs +2 -0
  331. package/dist/runs/registry/processGroup.cjs.map +1 -0
  332. package/dist/runs/registry/processGroup.mjs +2 -0
  333. package/dist/runs/registry/processGroup.mjs.map +1 -0
  334. package/dist/runs/registry/runRegistry.cjs +2 -0
  335. package/dist/runs/registry/runRegistry.cjs.map +1 -0
  336. package/dist/runs/registry/runRegistry.mjs +2 -0
  337. package/dist/runs/registry/runRegistry.mjs.map +1 -0
  338. package/dist/runs/registry/sessionLifecycle.cjs +2 -0
  339. package/dist/runs/registry/sessionLifecycle.cjs.map +1 -0
  340. package/dist/runs/registry/sessionLifecycle.mjs +2 -0
  341. package/dist/runs/registry/sessionLifecycle.mjs.map +1 -0
  342. package/dist/runs/registry/suspendedRuns.cjs +4 -0
  343. package/dist/runs/registry/suspendedRuns.cjs.map +1 -0
  344. package/dist/runs/registry/suspendedRuns.mjs +4 -0
  345. package/dist/runs/registry/suspendedRuns.mjs.map +1 -0
  346. package/dist/runs/sdkRunnerEntry.cjs +2 -0
  347. package/dist/runs/sdkRunnerEntry.cjs.map +1 -0
  348. package/dist/runs/sdkRunnerEntry.d.cts +1 -0
  349. package/dist/runs/sdkRunnerEntry.d.mts +1 -0
  350. package/dist/runs/sdkRunnerEntry.mjs +2 -0
  351. package/dist/runs/sdkRunnerEntry.mjs.map +1 -0
  352. package/dist/runs/shared/claudeFableProfile.cjs +4 -0
  353. package/dist/runs/shared/claudeFableProfile.cjs.map +1 -0
  354. package/dist/runs/shared/claudeFableProfile.mjs +4 -0
  355. package/dist/runs/shared/claudeFableProfile.mjs.map +1 -0
  356. package/dist/runs/shared/depthGuard.cjs +2 -0
  357. package/dist/runs/shared/depthGuard.cjs.map +1 -0
  358. package/dist/runs/shared/depthGuard.mjs +2 -0
  359. package/dist/runs/shared/depthGuard.mjs.map +1 -0
  360. package/dist/runs/shared/mcpDirectToolAllowlist.cjs +2 -0
  361. package/dist/runs/shared/mcpDirectToolAllowlist.cjs.map +1 -0
  362. package/dist/runs/shared/mcpDirectToolAllowlist.mjs +2 -0
  363. package/dist/runs/shared/mcpDirectToolAllowlist.mjs.map +1 -0
  364. package/dist/runs/shared/modelFallback.cjs +2 -0
  365. package/dist/runs/shared/modelFallback.cjs.map +1 -0
  366. package/dist/runs/shared/modelFallback.mjs +2 -0
  367. package/dist/runs/shared/modelFallback.mjs.map +1 -0
  368. package/dist/runs/shared/modelScope.cjs +2 -0
  369. package/dist/runs/shared/modelScope.cjs.map +1 -0
  370. package/dist/runs/shared/modelScope.mjs +2 -0
  371. package/dist/runs/shared/modelScope.mjs.map +1 -0
  372. package/dist/runs/shared/nestedPath.cjs +2 -0
  373. package/dist/runs/shared/nestedPath.cjs.map +1 -0
  374. package/dist/runs/shared/nestedPath.mjs +2 -0
  375. package/dist/runs/shared/nestedPath.mjs.map +1 -0
  376. package/dist/runs/shared/piArgs.cjs +2 -0
  377. package/dist/runs/shared/piArgs.cjs.map +1 -0
  378. package/dist/runs/shared/piArgs.mjs +2 -0
  379. package/dist/runs/shared/piArgs.mjs.map +1 -0
  380. package/dist/runs/shared/piSpawn.cjs +2 -0
  381. package/dist/runs/shared/piSpawn.cjs.map +1 -0
  382. package/dist/runs/shared/piSpawn.mjs +2 -0
  383. package/dist/runs/shared/piSpawn.mjs.map +1 -0
  384. package/dist/runs/shared/runWithConcurrency.cjs +2 -0
  385. package/dist/runs/shared/runWithConcurrency.cjs.map +1 -0
  386. package/dist/runs/shared/runWithConcurrency.mjs +2 -0
  387. package/dist/runs/shared/runWithConcurrency.mjs.map +1 -0
  388. package/dist/runs/shared/runtimeRegistry.cjs +2 -0
  389. package/dist/runs/shared/runtimeRegistry.cjs.map +1 -0
  390. package/dist/runs/shared/runtimeRegistry.mjs +2 -0
  391. package/dist/runs/shared/runtimeRegistry.mjs.map +1 -0
  392. package/dist/runs/shared/structuredOutput.cjs +2 -0
  393. package/dist/runs/shared/structuredOutput.cjs.map +1 -0
  394. package/dist/runs/shared/structuredOutput.mjs +2 -0
  395. package/dist/runs/shared/structuredOutput.mjs.map +1 -0
  396. package/dist/runs/shared/toolAvailability.cjs +2 -0
  397. package/dist/runs/shared/toolAvailability.cjs.map +1 -0
  398. package/dist/runs/shared/toolAvailability.mjs +2 -0
  399. package/dist/runs/shared/toolAvailability.mjs.map +1 -0
  400. package/dist/runs/shared/toolBudget.cjs +2 -0
  401. package/dist/runs/shared/toolBudget.cjs.map +1 -0
  402. package/dist/runs/shared/toolBudget.mjs +2 -0
  403. package/dist/runs/shared/toolBudget.mjs.map +1 -0
  404. package/dist/shared/artifacts.cjs +2 -0
  405. package/dist/shared/artifacts.cjs.map +1 -0
  406. package/dist/shared/artifacts.mjs +2 -0
  407. package/dist/shared/artifacts.mjs.map +1 -0
  408. package/dist/shared/atomicJson.cjs +2 -0
  409. package/dist/shared/atomicJson.cjs.map +1 -0
  410. package/dist/shared/atomicJson.mjs +2 -0
  411. package/dist/shared/atomicJson.mjs.map +1 -0
  412. package/dist/shared/boundedKeySet.cjs +2 -0
  413. package/dist/shared/boundedKeySet.cjs.map +1 -0
  414. package/dist/shared/boundedKeySet.mjs +2 -0
  415. package/dist/shared/boundedKeySet.mjs.map +1 -0
  416. package/dist/shared/childTranscript.cjs +5 -0
  417. package/dist/shared/childTranscript.cjs.map +1 -0
  418. package/dist/shared/childTranscript.mjs +5 -0
  419. package/dist/shared/childTranscript.mjs.map +1 -0
  420. package/dist/shared/configDir.cjs +2 -0
  421. package/dist/shared/configDir.cjs.map +1 -0
  422. package/dist/shared/configDir.mjs +2 -0
  423. package/dist/shared/configDir.mjs.map +1 -0
  424. package/dist/shared/errors.cjs +2 -0
  425. package/dist/shared/errors.cjs.map +1 -0
  426. package/dist/shared/errors.mjs +2 -0
  427. package/dist/shared/errors.mjs.map +1 -0
  428. package/dist/shared/fileSystemRetry.cjs +2 -0
  429. package/dist/shared/fileSystemRetry.cjs.map +1 -0
  430. package/dist/shared/fileSystemRetry.mjs +2 -0
  431. package/dist/shared/fileSystemRetry.mjs.map +1 -0
  432. package/dist/shared/formatters.cjs +2 -0
  433. package/dist/shared/formatters.cjs.map +1 -0
  434. package/dist/shared/formatters.mjs +2 -0
  435. package/dist/shared/formatters.mjs.map +1 -0
  436. package/dist/shared/lruCache.cjs +2 -0
  437. package/dist/shared/lruCache.cjs.map +1 -0
  438. package/dist/shared/lruCache.mjs +2 -0
  439. package/dist/shared/lruCache.mjs.map +1 -0
  440. package/dist/shared/modelInfo.cjs +2 -0
  441. package/dist/shared/modelInfo.cjs.map +1 -0
  442. package/dist/shared/modelInfo.mjs +2 -0
  443. package/dist/shared/modelInfo.mjs.map +1 -0
  444. package/dist/shared/modelResolution.cjs +2 -0
  445. package/dist/shared/modelResolution.cjs.map +1 -0
  446. package/dist/shared/modelResolution.mjs +2 -0
  447. package/dist/shared/modelResolution.mjs.map +1 -0
  448. package/dist/shared/paths.cjs +2 -0
  449. package/dist/shared/paths.cjs.map +1 -0
  450. package/dist/shared/paths.mjs +2 -0
  451. package/dist/shared/paths.mjs.map +1 -0
  452. package/dist/shared/pollScheduler.cjs +2 -0
  453. package/dist/shared/pollScheduler.cjs.map +1 -0
  454. package/dist/shared/pollScheduler.mjs +2 -0
  455. package/dist/shared/pollScheduler.mjs.map +1 -0
  456. package/dist/shared/scopeOwner.cjs +2 -0
  457. package/dist/shared/scopeOwner.cjs.map +1 -0
  458. package/dist/shared/scopeOwner.mjs +2 -0
  459. package/dist/shared/scopeOwner.mjs.map +1 -0
  460. package/dist/shared/versioned.cjs +2 -0
  461. package/dist/shared/versioned.cjs.map +1 -0
  462. package/dist/shared/versioned.mjs +2 -0
  463. package/dist/shared/versioned.mjs.map +1 -0
  464. package/dist/slash/chainExpression.cjs +2 -0
  465. package/dist/slash/chainExpression.cjs.map +1 -0
  466. package/dist/slash/chainExpression.mjs +2 -0
  467. package/dist/slash/chainExpression.mjs.map +1 -0
  468. package/dist/slash/doctor.cjs +5 -0
  469. package/dist/slash/doctor.cjs.map +1 -0
  470. package/dist/slash/doctor.mjs +5 -0
  471. package/dist/slash/doctor.mjs.map +1 -0
  472. package/dist/slash/slashCommands.cjs +4 -0
  473. package/dist/slash/slashCommands.cjs.map +1 -0
  474. package/dist/slash/slashCommands.mjs +4 -0
  475. package/dist/slash/slashCommands.mjs.map +1 -0
  476. package/dist/slash/spawnRequestMapping.cjs +2 -0
  477. package/dist/slash/spawnRequestMapping.cjs.map +1 -0
  478. package/dist/slash/spawnRequestMapping.mjs +2 -0
  479. package/dist/slash/spawnRequestMapping.mjs.map +1 -0
  480. package/dist/slash/subagentLaunch.cjs +2 -0
  481. package/dist/slash/subagentLaunch.cjs.map +1 -0
  482. package/dist/slash/subagentLaunch.mjs +2 -0
  483. package/dist/slash/subagentLaunch.mjs.map +1 -0
  484. package/dist/tui/agentCatalog.cjs +2 -0
  485. package/dist/tui/agentCatalog.cjs.map +1 -0
  486. package/dist/tui/agentCatalog.mjs +2 -0
  487. package/dist/tui/agentCatalog.mjs.map +1 -0
  488. package/dist/tui/agentResourceProjection.cjs +2 -0
  489. package/dist/tui/agentResourceProjection.cjs.map +1 -0
  490. package/dist/tui/agentResourceProjection.mjs +2 -0
  491. package/dist/tui/agentResourceProjection.mjs.map +1 -0
  492. package/dist/tui/completionNotice.cjs +5 -0
  493. package/dist/tui/completionNotice.cjs.map +1 -0
  494. package/dist/tui/completionNotice.mjs +5 -0
  495. package/dist/tui/completionNotice.mjs.map +1 -0
  496. package/dist/tui/fleet.cjs +3 -0
  497. package/dist/tui/fleet.cjs.map +1 -0
  498. package/dist/tui/fleet.mjs +3 -0
  499. package/dist/tui/fleet.mjs.map +1 -0
  500. package/dist/tui/fleetAgentView.cjs +2 -0
  501. package/dist/tui/fleetAgentView.cjs.map +1 -0
  502. package/dist/tui/fleetAgentView.mjs +2 -0
  503. package/dist/tui/fleetAgentView.mjs.map +1 -0
  504. package/dist/tui/fleetStatus.cjs +2 -0
  505. package/dist/tui/fleetStatus.cjs.map +1 -0
  506. package/dist/tui/fleetStatus.mjs +2 -0
  507. package/dist/tui/fleetStatus.mjs.map +1 -0
  508. package/dist/tui/fleetTranscript.cjs +5 -0
  509. package/dist/tui/fleetTranscript.cjs.map +1 -0
  510. package/dist/tui/fleetTranscript.mjs +5 -0
  511. package/dist/tui/fleetTranscript.mjs.map +1 -0
  512. package/dist/tui/register.cjs +2 -0
  513. package/dist/tui/register.cjs.map +1 -0
  514. package/dist/tui/register.mjs +2 -0
  515. package/dist/tui/register.mjs.map +1 -0
  516. package/dist/types.d.cts +59 -0
  517. package/dist/types.d.cts.map +1 -0
  518. package/dist/types.d.mts +59 -0
  519. package/dist/types.d.mts.map +1 -0
  520. package/package.json +86 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"asyncExecution.cjs","names":["path","currentRunsDir","fs","readNativeTeamRootFromEnvironment","CoalescedStatusWriter","SpawnHandshake","STATUS_FILE_NAME","buildPiArgs","SUBAGENT_RUN_ID_ENV","currentRunConfigPath","getArtifactPaths","getArtifactsDir","stepSteerInboxDir","steerCapabilityPath","steerAcksDir","sessionScopeEnvironment","requireCurrentSessionScope","nativeTeamRootEnvironment","isPiRuntime","CLAUDE_FABLE_PROFILE","prepareClaudeFableLaunch","currentResultsDir","resolveRuntimeLaunch","TERMINAL_ASYNC_JOB_STATES","DOOM_TEAM_TYPES"],"sources":["../../../src/runs/background/asyncExecution.ts"],"sourcesContent":["/**\n * The async-only spawn executor: launches exactly one subagent child process\n * and returns a run handle immediately, without waiting for the run to\n * finish.\n *\n * SCOPE - PARENT SIDE ONLY, SINGLE SPAWN:\n * `doom-pi-subagents/src/runs/background/asyncExecution.ts` is 1698 lines\n * doing far more than spawning: chain/parallel-group orchestration (building\n * `AgentConfig[]` sequences, dynamic fan-out expansion, workflow-graph\n * snapshots), session revival (a 3-way ready/ack/proceed handshake distinct\n * from the plain started-signal this module uses), and the foreground\n * (synchronous, blocking) code path. None of that is ported here:\n * - Chain/fan-out ORCHESTRATION (deciding how many children to spawn, in\n * what order, from what step list) is `port-steering`'s concurrent work on\n * the resolved-step and workflow-graph types. This module is the\n * PRIMITIVE that orchestration calls once per child - `childIndex` and\n * `fanout` arrive as inputs, already decided by the caller, not derived\n * here (see the child-index section below for why that is safe)\n * - Session revival (`revivalLease`, the 3-way ready/ack/proceed dance at\n * predecessor lines 498-509) is a materially different operation - resuming\n * an existing transcript, not spawning a fresh child - and is not ported.\n * `session-lease.ts` already exists in this package for whoever builds that\n * path later\n * - The foreground (synchronous) branch is dropped entirely, per the async-\n * only requirement below; there is no `async` parameter, only this\n *\n * WHAT THIS DOES NOT BUILD (compose, don't reimplement):\n * `CoalescedStatusWriter`, `TerminalPersistenceService`, `AsyncJobTracker`,\n * `SpawnHandshake`, `ResultWatcher`, `CompletionNotifier` all already exist\n * in this directory and are used here, not reimplemented. In particular, the\n * predecessor's process-exit tracking is reduced to the child handle's exact\n * exit signal: an abnormal exit marks that run failed immediately, while the\n * crash-marker path (`TerminalPersistenceService` +\n * `ProcessTerminalInspector`) remains the recovery path after parent loss.\n * `trackChild(pid)` on the caller's own already-active\n * `TerminalPersistenceService` is what makes a spawned child get killed if\n * the run that spawned it dies, which is the same guarantee the predecessor\n * used its own mechanism for.\n *\n * SDK RUNNER BOUNDARY:\n * `sdkRunnerEntry.ts` is the detached child process target. It reads the\n * launch config, creates the child through Pi's SDK, and loads\n * `subagent-prompt-runtime-entry` as an explicit child extension. That\n * extension owns the handshake, status, steering, and terminal result\n * lifecycle through the existing runner services. The real Pi CLI remains\n * the parent host used by system tests and DoomPi, not the child launcher.\n *\n * WHY SpawnHandshake, NOT `Atomics.wait`:\n * The predecessor blocks the parent's event loop for up to 10 seconds per\n * spawn (`asyncExecution.ts:376-380`, `Atomics.wait` on a shared buffer).\n * That codepath only actually ran for session revival there (a fresh spawn\n * had no `startupPath` and returned immediately) - but this package has no\n * synchronous/foreground path left to hide the cost behind, so a busy-wait\n * would block every single async spawn, not just revivals. `SpawnHandshake`\n * (already in this directory) replaces it: a real `fs.watch` on the\n * handshake file's directory, with a short poll as the safety net, and it\n * never blocks the event loop.\n *\n * LAUNCH CONFIG FILE LIFECYCLE (fix):\n * The predecessor writes the launch config with `fs.writeFileSync(cfgPath,\n * ...)` - no `mode` option, so it lands at the umask default - and never\n * unlinks it on any path, successful or failed\n * (`asyncExecution.ts:491-495`). Both are fixed here: `writeLaunchConfig`\n * writes 0600 (`writePrivateAtomicJson`, already in this package), and\n * `removeLaunchConfig` runs on every exit path - success (once the handshake\n * confirms the child has read it), and every failure (no pid, spawn error,\n * handshake failure or timeout) - so nothing is ever left behind holding the\n * full task text and resolved secrets/tokens at world-readable-adjacent\n * permissions.\n *\n * CHILD INDEX (fix - `DeliverableGuard` had no way to get one):\n * `DeliverableGuard.deliverNudge` requires an explicit `nudgeTarget` of\n * `{kind:'child', index}` for a fan-out run specifically so it cannot\n * broadcast a nudge to every sibling over one child's missing deliverable\n * (see that module's header). Nothing in this package maps a team member id\n * to a fan-out child index (verified: neither `nativeTeamChannel.ts` nor\n * `parallel-groups.ts` does this). This module needs no such mapping either\n * - `childIndex` is simply which spawn call this is, a fact the caller (a\n * single spawn, or a fan-out loop calling this once per child) already knows\n * without looking anything up, exactly like a flat index into\n * `status.steps[]` is decided by whoever builds that array. `nudgeTarget` is\n * derived from `childIndex`/`fanout` and persisted onto the run's own\n * `status.json` (`nudgeTarget` field) for whatever later evaluates\n * `DeliverableGuardInput` to read back, rather than re-derived at that point.\n *\n * DELIVERABLE EXPECTATION (fix - `DeliverableGuard` had no policy):\n * Derived here, at the one point this module already has the task text:\n * `classifyTaskMutationIntent` (used AS-IS, per its own header's warning\n * against widening its vocabulary for a third caller) distinguishes\n * `'implementation'` intent from `'read-only'`/`'unknown'`. Implementation\n * intent gets a real expectation (`{kind:'summary'}` by default - the\n * minimum a run can be asked to prove it did something, when no more\n * specific expectation like a structured-output schema or artifact path was\n * supplied); read-only or unknown intent gets none at all. This is\n * deliberately NOT `{kind:'summary'}` unconditionally: a guard that flags\n * every research run for an empty summary gets ignored, and an ignored guard\n * misses the run that actually needed it.\n *\n * TEAM MEMBERSHIP ENV FORWARDING (fix - a spawned child had no way to join\n * the team):\n * If THIS process (the one calling `spawn()`) is itself part of a native\n * team - `readNativeTeamRootFromEnvironment()` resolves a root context off\n * its own `process.env` - the same root vars\n * (`nativeTeamRootEnvironment(root)`, already in `nativeTeamChannel.ts`,\n * not reimplemented here) are folded into the child's env alongside\n * whatever `buildPiArgs` produced. Only the ROOT vars travel this way, never\n * a member id/token: those are per-member secrets the CHILD mints for\n * itself when it registers (see `runnerBootstrap.ts` and\n * `runnerTeamMembership.ts`), not something safe to hand down from the\n * parent's own identity. When this process has no team root (a standalone\n * spawn outside any team), nothing is forwarded and the child simply has\n * none to register against.\n */\n\nimport { spawn } from 'node:child_process';\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { createHarnessSession, getHarnessState } from '@agimon-ai/doompi-config';\nimport type { InlineAgent } from '@agimon-ai/doompi-extension-contracts/subagent-tool';\nimport { inject, injectable } from 'inversify';\n\nimport { getArtifactPaths, getArtifactsDir } from '../../shared/artifacts.ts';\nimport { writePrivateAtomicJson } from '../../shared/atomicJson.ts';\nimport {\n currentResultsDir,\n currentRunConfigPath,\n currentRunsDir,\n requireCurrentSessionScope,\n sessionScopeEnvironment,\n} from '../../shared/paths.ts';\nimport { DOOM_TEAM_TYPES } from '../../container/types.ts';\nimport { PI_RUNTIME_NAME, SUBAGENT_RUN_ID_ENV } from '../../env.ts';\nimport {\n nativeTeamRootEnvironment,\n readNativeTeamRootFromEnvironment,\n type TeamRootContext,\n} from '../../intercom/nativeTeamChannel.ts';\nimport type { ActivityState, ArtifactDirPreference } from '../../types.ts';\nimport { type IAsyncJobTracker, TERMINAL_ASYNC_JOB_STATES } from './asyncJobTracker.ts';\nimport {\n buildPiArgs,\n type BuildPiArgsInput,\n type BuildPiArgsResult,\n type PiSdkLaunchSettings,\n} from '../shared/piArgs.ts';\nimport { isPiRuntime, resolveRuntimeLaunch, type RuntimeTable } from '../shared/runtimeRegistry.ts';\nimport {\n CLAUDE_FABLE_PROFILE,\n cleanupClaudeFableLaunch,\n prepareClaudeFableLaunch,\n type ClaudeFableLaunch,\n} from '../shared/claudeFableProfile.ts';\nimport { registerRun, releaseRun } from '../registry/runRegistry.ts';\nimport { SpawnHandshake, type ISpawnHandshake } from './spawnHandshake.ts';\nimport {\n CoalescedStatusWriter,\n type ICoalescedStatusWriter,\n STATUS_FILE_NAME,\n type StatusWithRecentEntries,\n} from './statusWriter.ts';\nimport type { ITerminalPersistenceService } from './terminalPersistence.ts';\nimport { steerAcksDir, steerCapabilityPath, stepSteerInboxDir } from '../../intercom/supervisorControlChannel.ts';\n\n/** Leaf name of the started-signal file `SpawnHandshake` watches for, under `currentRunsDir()/<runId>/`. Exported so the child-side runner can compose the same convention rather than duplicate it. */\nexport const HANDSHAKE_FILE_NAME = 'handshake.json';\nexport const RUNNER_STDOUT_FILE_NAME = 'runner.stdout.log';\nexport const RUNNER_STDERR_FILE_NAME = 'runner.stderr.log';\nexport const FABLE_PROFILE_RESULT_FILE_NAME = 'fable-profile-result.json';\n/**\n * The effective system prompt this run launched with, kept for the fleet\n * inspector's agent view.\n *\n * A SIDECAR RATHER THAN A FIELD ON status.json: `CoalescedStatusWriter`\n * rewrites the whole status file on every coalesced flush, and a system prompt\n * is kilobytes of text that never changes after spawn. Inlining it would put\n * that payload through every status write for the life of the run. This file\n * is written once, next to the status it belongs to.\n *\n * Written 0600 for the same reason `piArgs.ts` writes its prompt file that\n * way: a resolved prompt can carry inherited project context.\n */\nexport const SYSTEM_PROMPT_FILE_NAME = 'system-prompt.md';\nconst SYSTEM_PROMPT_FILE_MODE = 0o600;\nconst DEFAULT_HANDSHAKE_TIMEOUT_MS = 10_000;\n\n/**\n * `state`/`steps`/`parallelGroups`/`chainStepCount`/`workflowGraph`/\n * `pendingAppends` are declared as optional-and-widened, rather than a\n * runner-owned status type, so this SAME status shape is what\n * `chain-append.ts`'s `applyChainAppendRequest` (which expects\n * `AppendableChainStatus`, an object with exactly these optional fields) can\n * mutate directly - one status type for the whole spawn-through-finalize\n * lifecycle, not a parent-side one and a child-side one that have to be kept\n * in sync by hand.\n */\nexport interface AsyncRunStatus extends StatusWithRecentEntries {\n /** Version 1 is emitted by current writers; absent is accepted only by the compatibility reader. */\n version?: 1;\n runId: string;\n operationId?: string;\n agent: string;\n /** Original task text required to restore a suspended run. */\n task?: string;\n cwd?: string;\n /** Resolved model selected for this run, when one was requested. */\n model?: string;\n /** One-shot agent definition required to restore an inline agent. */\n inlineAgent?: InlineAgent;\n state: 'queued' | 'running' | 'complete' | 'completed' | 'failed' | 'paused' | 'stopped';\n startedAt: number;\n lastUpdate: number;\n endedAt?: number;\n /** This run's position among its fan-out siblings, when it has any. Absent for a standalone run. */\n fanoutIndex?: number;\n /** Bridge-owned runs are consumed internally and never rendered as generic completion chat. */\n internal?: boolean;\n /** Which runtime executes this run. Absent means the default in-process `pi` path. */\n runtime?: string;\n activityState?: ActivityState;\n attentionReason?: string;\n error?: string;\n /**\n * Set once, at finalize, by `RunnerReporting.mutateTerminalStatus` - the\n * SAME value it puts in the terminal result file, truncated to a bounded\n * length (see that module's header for why). Lets a caller (chain mode,\n * in particular) learn a completed run's own summary through the status\n * file it already reads to learn the run finished, rather than needing a\n * second read of a claimed, single-consumer result file.\n */\n summary?: string;\n /**\n * This run's own Pi session transcript path, written during child bootstrap\n * and retained by `RunnerReporting.mutateTerminalStatus` at finalize. See\n * that module's header for where the value comes from and why it is bounded\n * by omission, not truncation. Absent for a run that either never reported\n * a session file (headless, no persisted transcript) or reported one too\n * long to trust. This is what `action='resume'` reads to know which\n * transcript a later revival should reopen.\n */\n sessionFile?: string;\n /** Child-owned Pi SDK event transcript consumed by the fleet detail pane. */\n transcriptPath?: string;\n /**\n * Sidecar holding this run's effective system prompt, and the resolved tool\n * plan it launched with. Recorded at spawn purely so the fleet inspector can\n * show what an agent was actually told: the launch config that carried them\n * is deleted as soon as the child handshakes, and the agent definition on\n * disk can change while a run is still going.\n *\n * All absent for a non-`pi` runtime, which has no SDK launch settings, and\n * for any run started before this was recorded.\n */\n systemPromptPath?: string;\n systemPromptMode?: 'append' | 'replace';\n /** Effective tool allowlist. Absent means \"whatever the child defaults to\". */\n tools?: string[];\n excludeTools?: string[];\n noTools?: boolean;\n /** Cumulative tokens observed from finalized child messages. */\n tokens?: number;\n /** Most recently started child tool. */\n currentTool?: string;\n /** Number of child tool executions observed so far. */\n toolCount?: number;\n}\n\n/** The parent-written contract a child-side runner reads via `currentRunConfigPath(runId)`. */\nexport interface LaunchConfig {\n runId: string;\n agent: string;\n task: string;\n cwd: string;\n childIndex: number;\n handshakePath: string;\n sdk: PiSdkLaunchSettings;\n transcriptPath?: string;\n /** Retained during the SDK cutover for compatibility diagnostics only. */\n args: string[];\n env: Record<string, string | undefined>;\n}\n\nexport interface AsyncSubagentSpawnInput {\n runId: string;\n operationId?: string;\n agent: string;\n /** One-shot agent definition retained only for explicit suspension restore. */\n inlineAgent?: InlineAgent;\n task: string;\n cwd: string;\n /** Persisted parent transcript used to place child artifacts beside the parent session. */\n parentSessionFile?: string;\n /** This spawn's position among its siblings. 0 for a standalone (non-fan-out) run. */\n childIndex: number;\n /** Whether this spawn is one of several children under a fan-out parent. */\n fanout: boolean;\n /** Everything `buildPiArgs` needs, minus what this spawner supplies itself (`runId`/`task`/`childIndex`). */\n piArgs: Omit<BuildPiArgsInput, 'runId' | 'task' | 'childIndex'>;\n /** False disables the child event transcript. Defaults to enabled. */\n artifacts?: boolean;\n /** Artifact root policy inherited from the parent Doom Team config. */\n artifactDir?: ArtifactDirPreference;\n handshakeTimeoutMs?: number;\n /**\n * Which runtime executes this run. Defaults to the in-process Pi SDK path.\n * Anything else is an external CLI resolved through `runtimeRegistry.ts`.\n */\n runtime?: string;\n /** Fixed trusted external profile. Only Doom Team internal bridges may set this. */\n externalProfile?: string;\n /** Prevent sensitive task text from being persisted in status.json. */\n sensitiveTask?: boolean;\n /** Suppress the generic completion notifier for an internal bridge run. */\n internal?: boolean;\n /** Runtimes available to this spawn, already merged with the shipped defaults. */\n runtimes?: RuntimeTable;\n}\n\nexport interface AsyncSubagentSpawnResult {\n runId: string;\n pid: number;\n}\n\nexport interface IAsyncSubagentSpawner {\n /** Spawn one child and return once it has confirmed it started. Throws on any failure to reach that point. */\n spawn(input: AsyncSubagentSpawnInput): Promise<AsyncSubagentSpawnResult>;\n}\n\n/** `currentRunsDir()/<runId>`. Exported so the child-side runner resolves the same directory, not a parallel one. */\nexport function runDirFor(runId: string): string {\n return path.join(currentRunsDir(), runId);\n}\n\n/** Exported so the child-side runner writes its ready/error signal at the exact path this parent half waits on. */\nexport function handshakePathFor(runId: string): string {\n return path.join(runDirFor(runId), HANDSHAKE_FILE_NAME);\n}\n\nexport function fableProfileResultPathFor(runId: string): string {\n return path.join(runDirFor(runId), FABLE_PROFILE_RESULT_FILE_NAME);\n}\n\n/** Where `spawn()` records this run's effective system prompt. See `SYSTEM_PROMPT_FILE_NAME`. */\nexport function systemPromptPathFor(runId: string): string {\n return path.join(runDirFor(runId), SYSTEM_PROMPT_FILE_NAME);\n}\n\n/**\n * Locate a child entry point on disk, built or source.\n *\n * The child is a separate process loading this by path, so the path has to be\n * the one that exists now: `.mjs` under `dist` when this bundle is built, the\n * original source file when it is not. `relativeDir` is where the entry sits\n * relative to `src/` - the SDK runner is one level up from this module, the\n * CLI runner is beside it.\n */\nfunction resolveRunnerEntry(relativeDir: string, stem: string): string {\n const moduleDirectory = path.dirname(fileURLToPath(import.meta.url));\n const packageRoot = path.resolve(moduleDirectory, '../..');\n const builtCandidate = path.join(packageRoot, relativeDir, `${stem}.mjs`);\n if (fs.existsSync(builtCandidate)) return builtCandidate;\n const sourceCandidate = path.join(packageRoot, relativeDir, `${stem}.ts`);\n if (fs.existsSync(sourceCandidate)) return sourceCandidate;\n throw new Error(`Doom Team runner '${stem}' is unavailable at '${builtCandidate}' or '${sourceCandidate}'.`);\n}\n\nfunction resolveSdkRunnerEntry(): string {\n return resolveRunnerEntry('runs', 'sdkRunnerEntry');\n}\n\nfunction resolveCliRunnerEntry(): string {\n return resolveRunnerEntry('runs/background', 'cliRunnerEntry');\n}\n\n@injectable()\nexport class AsyncSubagentSpawner implements IAsyncSubagentSpawner {\n constructor(\n @inject(DOOM_TEAM_TYPES.AsyncJobTracker) private readonly jobTracker: IAsyncJobTracker,\n @inject(DOOM_TEAM_TYPES.TerminalPersistenceService)\n private readonly terminalPersistence: ITerminalPersistenceService,\n ) {}\n\n /**\n * Seam for tests, as a protected member rather than a constructor\n * argument: inversify cannot resolve a primitive. See `pollScheduler.ts`\n * for the same pattern.\n */\n protected readonly defaultHandshakeTimeoutMs: number = DEFAULT_HANDSHAKE_TIMEOUT_MS;\n\n protected now(): number {\n return Date.now();\n }\n\n /**\n * This process's own team root context, read fresh per spawn. Protected\n * so a test can supply one without writing real team files to disk - see\n * the module header's TEAM MEMBERSHIP ENV FORWARDING section for why this\n * is read from `process.env` rather than accepted as a constructor\n * argument (inversify cannot resolve a primitive either way).\n */\n protected readCurrentTeamRoot(): TeamRootContext | undefined {\n return readNativeTeamRootFromEnvironment();\n }\n\n /**\n * A fresh writer per spawn, deliberately never the container's shared\n * singleton - the same reasoning as `StaleRunReconciler.createStatusWriter`\n * (see that class's header): the singleton may simultaneously be the live\n * write path for a run THIS process is itself running, and bootstrapping\n * someone else's (the spawned child's) initial status is a different\n * writer's job.\n */\n protected createStatusWriter(): ICoalescedStatusWriter<AsyncRunStatus> {\n return new CoalescedStatusWriter<AsyncRunStatus>();\n }\n\n /**\n * A fresh handshake wait per spawn. `SpawnHandshake` is bound transient in\n * the container for exactly this reason (each `waitForHandshake()` owns\n * its own timers and watcher for exactly one spawn); resolving it via\n * constructor injection would hand every spawn the same, already-used\n * instance, so this seam constructs one directly instead.\n */\n protected createSpawnHandshake(): ISpawnHandshake {\n return new SpawnHandshake();\n }\n\n protected writeLaunchConfig(filePath: string, config: object): void {\n writePrivateAtomicJson(filePath, config);\n }\n\n protected removeLaunchConfig(filePath: string): void {\n fs.rmSync(filePath, { force: true });\n }\n\n protected readRunState(runId: string): string | undefined {\n try {\n const parsed = JSON.parse(fs.readFileSync(path.join(runDirFor(runId), STATUS_FILE_NAME), 'utf8')) as {\n state?: unknown;\n };\n return typeof parsed.state === 'string' ? parsed.state : undefined;\n } catch {\n return undefined;\n }\n }\n\n /**\n * Resolves the child's CLI args/env. Protected so a test can bypass\n * `buildPiArgs`'s own real filesystem resolution (it locates a runtime\n * extension script on disk via `resolvePiLaunchToolPlan`, which is\n * environment-dependent and not something a unit test for THIS module's\n * orchestration logic should depend on).\n */\n protected buildLaunchArgs(input: BuildPiArgsInput): BuildPiArgsResult {\n return buildPiArgs(input);\n }\n\n /**\n * The actual `child_process.spawn`. Detached and unref'd immediately. Output\n * goes to per-run files so a startup failure is diagnosable without keeping\n * parent-owned pipes alive.\n */\n protected spawnChild(\n command: string,\n args: string[],\n options: { cwd: string; env: NodeJS.ProcessEnv },\n ): {\n pid: number | undefined;\n onError: (handler: (error: Error) => void) => void;\n onExit: (handler: (code: number | null, signal: NodeJS.Signals | null) => void) => void;\n } {\n const runId = options.env[SUBAGENT_RUN_ID_ENV];\n let stdoutFd: number | undefined;\n let stderrFd: number | undefined;\n if (runId) {\n const runDir = runDirFor(runId);\n fs.mkdirSync(runDir, { recursive: true });\n stdoutFd = fs.openSync(path.join(runDir, RUNNER_STDOUT_FILE_NAME), 'a', 0o600);\n try {\n stderrFd = fs.openSync(path.join(runDir, RUNNER_STDERR_FILE_NAME), 'a', 0o600);\n } catch (error) {\n fs.closeSync(stdoutFd);\n throw new Error(`Could not open runner stderr for '${runId}'.`, { cause: error });\n }\n }\n let child: ReturnType<typeof spawn>;\n try {\n child = spawn(command, args, {\n cwd: options.cwd,\n detached: true,\n stdio: ['ignore', stdoutFd ?? 'ignore', stderrFd ?? 'ignore'],\n windowsHide: true,\n env: options.env,\n });\n } finally {\n if (stdoutFd !== undefined) fs.closeSync(stdoutFd);\n if (stderrFd !== undefined) fs.closeSync(stderrFd);\n }\n child.unref();\n return {\n pid: child.pid,\n onError: (handler) => child.on('error', handler),\n onExit: (handler) => child.on('exit', handler),\n };\n }\n\n async spawn(input: AsyncSubagentSpawnInput): Promise<AsyncSubagentSpawnResult> {\n const { runId, agent, task, cwd } = input;\n const fanoutIndex = input.fanout ? input.childIndex : undefined;\n const handshakePath = handshakePathFor(runId);\n const configPath = currentRunConfigPath(runId);\n const transcriptPath =\n input.artifacts === false\n ? undefined\n : getArtifactPaths(\n getArtifactsDir(input.parentSessionFile ?? null, cwd, input.artifactDir ?? 'session'),\n runId,\n agent,\n ).transcriptPath;\n\n const runDir = runDirFor(runId);\n const steerIndex = input.childIndex ?? 0;\n const teamRoot = this.readCurrentTeamRoot();\n const {\n args,\n env: builtEnv,\n sdk,\n } = this.buildLaunchArgs({\n ...input.piArgs,\n runId,\n task,\n childIndex: input.childIndex,\n steerInboxDir: stepSteerInboxDir(runDir, steerIndex),\n steerCapabilityPath: steerCapabilityPath(runDir, steerIndex),\n steerAckDir: steerAcksDir(runDir, steerIndex),\n teamToolEnabled: Boolean(teamRoot),\n });\n // The child gets its own harness state, in this run's directory, which this\n // package owns. Sharing the parent's file would let a detached child rewrite\n // a session it does not own, and pointing at the parent's directory would\n // lose the file the moment the parent cleaned up its own session: a child\n // is detached and can outlive it. Pi 0.84 SDK launches consume the resolved\n // extension list directly, so preserve that same list in the child harness\n // snapshot instead of projecting the parent's stale childExtensions value.\n createHarnessSession(\n { ...getHarnessState(), childExtensions: sdk.extensions },\n { directory: runDir, environment: builtEnv, unclaimed: true },\n );\n const env = {\n ...process.env,\n ...builtEnv,\n // The child resolves the same filesystem tree from this, independently of\n // whether it also joins a team.\n ...sessionScopeEnvironment(requireCurrentSessionScope()),\n ...(teamRoot ? nativeTeamRootEnvironment(teamRoot) : {}),\n };\n\n // Recorded before open() so the status never advertises a sidecar that is\n // not on disk yet. A failure here is not worth failing a spawn over: the\n // prompt is an inspection aid, so the run proceeds without it.\n let systemPromptPath: string | undefined;\n if (isPiRuntime(input.runtime) && sdk.systemPrompt) {\n try {\n fs.mkdirSync(runDir, { recursive: true });\n const target = systemPromptPathFor(runId);\n fs.writeFileSync(target, sdk.systemPrompt, { mode: SYSTEM_PROMPT_FILE_MODE });\n systemPromptPath = target;\n } catch {\n systemPromptPath = undefined;\n }\n }\n\n const now = this.now();\n const statusWriter = this.createStatusWriter();\n statusWriter.open(runId, {\n version: 1,\n runId,\n ...(input.operationId ? { operationId: input.operationId } : {}),\n agent,\n ...(input.sensitiveTask ? {} : { task }),\n cwd,\n ...(input.piArgs.model ? { model: input.piArgs.model } : {}),\n ...(input.inlineAgent ? { inlineAgent: input.inlineAgent } : {}),\n state: 'queued',\n startedAt: now,\n lastUpdate: now,\n ...(fanoutIndex === undefined ? {} : { fanoutIndex }),\n ...(input.internal ? { internal: true } : {}),\n ...(isPiRuntime(input.runtime) ? {} : { runtime: input.runtime }),\n ...(transcriptPath ? { transcriptPath } : {}),\n ...(systemPromptPath ? { systemPromptPath } : {}),\n ...(systemPromptPath && sdk.systemPromptMode ? { systemPromptMode: sdk.systemPromptMode } : {}),\n ...(sdk.tools ? { tools: sdk.tools } : {}),\n ...(sdk.excludeTools?.length ? { excludeTools: sdk.excludeTools } : {}),\n ...(sdk.noTools ? { noTools: true } : {}),\n });\n\n if (isPiRuntime(input.runtime)) {\n this.writeLaunchConfig(configPath, {\n runId,\n agent,\n task,\n cwd,\n childIndex: input.childIndex,\n handshakePath,\n sdk,\n ...(transcriptPath ? { transcriptPath } : {}),\n args,\n env,\n });\n }\n\n let fableLaunch: ClaudeFableLaunch | undefined;\n const fail = (message: string): never => {\n this.removeLaunchConfig(configPath);\n if (fableLaunch) cleanupClaudeFableLaunch(fableLaunch);\n fs.rmSync(fableProfileResultPathFor(runId), { force: true });\n statusWriter.updateSync((status) => {\n status.state = 'failed';\n status.error = message;\n status.lastUpdate = this.now();\n });\n throw new Error(message);\n };\n\n // One branch, and only one: `pi` runs in-process through the SDK runner and\n // gets the team channel, steering and structured output that go with it.\n // Every other runtime is an external CLI, spawned through the generic\n // runner, which deliberately knows nothing about the vendor.\n const usePi = isPiRuntime(input.runtime);\n if (!usePi) {\n const ceiling = input.piArgs.capabilityCeiling;\n if (ceiling && (!input.externalProfile || !ceiling.allowedExternalProfiles.includes(input.externalProfile))) {\n return fail(\n `External profile '${input.externalProfile ?? 'none'}' is denied by the active capability ceiling.`,\n );\n }\n if (input.externalProfile !== undefined && input.externalProfile !== CLAUDE_FABLE_PROFILE) {\n return fail(`External profile '${input.externalProfile}' is not trusted.`);\n }\n if (input.externalProfile === CLAUDE_FABLE_PROFILE) {\n if (input.runtime !== 'claude' || input.piArgs.model !== 'fable') {\n return fail('The Fable profile requires runtime claude and model fable.');\n }\n if (input.inlineAgent || input.piArgs.sessionFile || input.piArgs.parentSessionId) {\n return fail('The Fable profile requires a fresh isolated context.');\n }\n fableLaunch = prepareClaudeFableLaunch({\n runId,\n prompt: task,\n repositoryCwd: cwd,\n privateRoot: runDir,\n });\n this.writeLaunchConfig(configPath, {\n runId,\n ...(input.operationId ? { operationId: input.operationId } : {}),\n agent,\n runtime: 'claude',\n command: fableLaunch.command,\n args: fableLaunch.args,\n cwd: fableLaunch.cwd,\n env: fableLaunch.env,\n profile: fableLaunch.profile,\n stdinPath: fableLaunch.stdinPath,\n cleanupPaths: fableLaunch.cleanupPaths,\n profileResultPath: fableProfileResultPathFor(runId),\n internal: input.internal === true,\n handshakePath,\n resultPath: path.join(currentResultsDir(), `${runId}.json`),\n });\n } else {\n this.writeLaunchConfig(configPath, {\n runId,\n ...(input.operationId ? { operationId: input.operationId } : {}),\n agent,\n ...resolveRuntimeLaunch(input.runtime as string, input.runtimes ?? {}, {\n prompt: task,\n model: input.piArgs.model,\n cwd,\n }),\n cwd,\n env: builtEnv as Record<string, string>,\n handshakePath,\n resultPath: path.join(currentResultsDir(), `${runId}.json`),\n });\n }\n }\n\n const { pid, onError, onExit } = this.spawnChild(\n process.execPath,\n [usePi ? resolveSdkRunnerEntry() : resolveCliRunnerEntry()],\n { cwd, env: env as NodeJS.ProcessEnv },\n );\n if (pid === undefined) {\n return fail(`Async spawn for '${agent}' (run '${runId}') did not produce a pid.`);\n }\n let spawnError: Error | undefined;\n onError((error) => {\n spawnError = error;\n });\n\n // Recorded before the handshake: a parent that dies between spawn and\n // handshake must still leave a record a later sweep can act on.\n registerRun(requireCurrentSessionScope(), {\n runId,\n pid,\n agent,\n runtime: input.runtime ?? PI_RUNTIME_NAME,\n startedAt: now,\n });\n this.terminalPersistence.trackChild(pid);\n onExit((code, signal) => {\n this.terminalPersistence.untrackChild(pid);\n releaseRun(requireCurrentSessionScope(), runId);\n if (code === 0 && signal === null) return;\n statusWriter.updateSync((status) => {\n if (status.state && TERMINAL_ASYNC_JOB_STATES.has(status.state)) return;\n status.state = 'failed';\n status.error = `SDK runner process ${pid} exited unexpectedly (${signal ?? `code ${code ?? 'unknown'}`}).`;\n status.lastUpdate = this.now();\n status.endedAt = this.now();\n });\n this.jobTracker.track(runId);\n });\n\n const handshake = this.createSpawnHandshake();\n const outcome = await handshake.waitForHandshake({\n path: handshakePath,\n timeoutMs: input.handshakeTimeoutMs ?? this.defaultHandshakeTimeoutMs,\n }).promise;\n\n if (outcome.status !== 'signalled') {\n this.terminalPersistence.untrackChild(pid);\n const message =\n outcome.status === 'failed'\n ? outcome.error\n : (spawnError?.message ?? `Timed out waiting for '${agent}' (run '${runId}') to start.`);\n return fail(message);\n }\n\n // The child has confirmed it read the launch config; the parent's copy\n // is no longer needed (see the module header's LAUNCH CONFIG FILE\n // LIFECYCLE section).\n this.removeLaunchConfig(configPath);\n const observedState = this.readRunState(runId);\n if (!observedState || !TERMINAL_ASYNC_JOB_STATES.has(observedState)) {\n statusWriter.updateSync((status) => {\n status.state = 'running';\n status.lastUpdate = this.now();\n });\n }\n this.jobTracker.track(runId);\n\n return { runId, pid };\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import{__esmMin as e}from"../../_virtual/_rolldown/runtime.mjs";import{init_atomicJson as t,writePrivateAtomicJson as n}from"../../shared/atomicJson.mjs";import{__decorate as r,init_decorate as i}from"../../_virtual/_@oxc-project_runtime@0.127.0/helpers/decorate.mjs";import{SUBAGENT_RUN_ID_ENV as a,init_env as o}from"../../env.mjs";import{DOOM_TEAM_TYPES as s,init_types as c}from"../../container/types.mjs";import{__decorateMetadata as l,init_decorateMetadata as u}from"../../_virtual/_@oxc-project_runtime@0.127.0/helpers/decorateMetadata.mjs";import{__decorateParam as d,init_decorateParam as f}from"../../_virtual/_@oxc-project_runtime@0.127.0/helpers/decorateParam.mjs";import{init_supervisorControlChannel as p,steerAcksDir as ee,steerCapabilityPath as te,stepSteerInboxDir as ne}from"../../intercom/supervisorControlChannel.mjs";import{currentResultsDir as m,currentRunConfigPath as re,currentRunsDir as h,init_paths as g,requireCurrentSessionScope as _,sessionScopeEnvironment as ie}from"../../shared/paths.mjs";import{CoalescedStatusWriter as v,STATUS_FILE_NAME as y,init_statusWriter as b}from"./statusWriter.mjs";import{init_runtimeRegistry as x,isPiRuntime as S,resolveRuntimeLaunch as C}from"../shared/runtimeRegistry.mjs";import{TERMINAL_ASYNC_JOB_STATES as w,init_asyncJobTracker as T}from"./asyncJobTracker.mjs";import{init_nativeTeamChannel as E,nativeTeamRootEnvironment as ae,readNativeTeamRootFromEnvironment as D}from"../../intercom/nativeTeamChannel.mjs";import{getArtifactPaths as O,getArtifactsDir as oe,init_artifacts as k}from"../../shared/artifacts.mjs";import{buildPiArgs as A,init_piArgs as j}from"../shared/piArgs.mjs";import{CLAUDE_FABLE_PROFILE as M,cleanupClaudeFableLaunch as N,init_claudeFableProfile as P,prepareClaudeFableLaunch as se}from"../shared/claudeFableProfile.mjs";import{init_runRegistry as F,registerRun as ce,releaseRun as le}from"../registry/runRegistry.mjs";import{SpawnHandshake as ue,init_spawnHandshake as de}from"./spawnHandshake.mjs";import*as I from"node:fs";import*as L from"node:path";import{createHarnessSession as fe,getHarnessState as R}from"@agimon-ai/doompi-config";import{inject as z,injectable as B}from"inversify";import{spawn as pe}from"node:child_process";import{fileURLToPath as me}from"node:url";function V(e){return L.join(h(),e)}function H(e){return L.join(V(e),G)}function U(e){return L.join(V(e),J)}function he(e){return L.join(V(e),Y)}function W(e,t){let n=L.dirname(me(import.meta.url)),r=L.resolve(n,`../..`),i=L.join(r,e,`${t}.mjs`);if(I.existsSync(i))return i;let a=L.join(r,e,`${t}.ts`);if(I.existsSync(a))return a;throw Error(`Doom Team runner '${t}' is unavailable at '${i}' or '${a}'.`)}function ge(){return W(`runs`,`sdkRunnerEntry`)}function _e(){return W(`runs/background`,`cliRunnerEntry`)}var G,K,q,J,Y,X,Z,Q,$=e((()=>{k(),t(),g(),c(),o(),E(),T(),j(),x(),P(),F(),de(),b(),p(),u(),f(),i(),G=`handshake.json`,K=`runner.stdout.log`,q=`runner.stderr.log`,J=`fable-profile-result.json`,Y=`system-prompt.md`,X=384,Z=1e4,Q=class{constructor(e,t){this.jobTracker=e,this.terminalPersistence=t}defaultHandshakeTimeoutMs=Z;now(){return Date.now()}readCurrentTeamRoot(){return D()}createStatusWriter(){return new v}createSpawnHandshake(){return new ue}writeLaunchConfig(e,t){n(e,t)}removeLaunchConfig(e){I.rmSync(e,{force:!0})}readRunState(e){try{let t=JSON.parse(I.readFileSync(L.join(V(e),y),`utf8`));return typeof t.state==`string`?t.state:void 0}catch{return}}buildLaunchArgs(e){return A(e)}spawnChild(e,t,n){let r=n.env[a],i,o;if(r){let e=V(r);I.mkdirSync(e,{recursive:!0}),i=I.openSync(L.join(e,K),`a`,384);try{o=I.openSync(L.join(e,q),`a`,384)}catch(e){throw I.closeSync(i),Error(`Could not open runner stderr for '${r}'.`,{cause:e})}}let s;try{s=pe(e,t,{cwd:n.cwd,detached:!0,stdio:[`ignore`,i??`ignore`,o??`ignore`],windowsHide:!0,env:n.env})}finally{i!==void 0&&I.closeSync(i),o!==void 0&&I.closeSync(o)}return s.unref(),{pid:s.pid,onError:e=>s.on(`error`,e),onExit:e=>s.on(`exit`,e)}}async spawn(e){let{runId:t,agent:n,task:r,cwd:i}=e,a=e.fanout?e.childIndex:void 0,o=H(t),s=re(t),c=e.artifacts===!1?void 0:O(oe(e.parentSessionFile??null,i,e.artifactDir??`session`),t,n).transcriptPath,l=V(t),u=e.childIndex??0,d=this.readCurrentTeamRoot(),{args:f,env:p,sdk:h}=this.buildLaunchArgs({...e.piArgs,runId:t,task:r,childIndex:e.childIndex,steerInboxDir:ne(l,u),steerCapabilityPath:te(l,u),steerAckDir:ee(l,u),teamToolEnabled:!!d});fe({...R(),childExtensions:h.extensions},{directory:l,environment:p,unclaimed:!0});let g={...process.env,...p,...ie(_()),...d?ae(d):{}},v;if(S(e.runtime)&&h.systemPrompt)try{I.mkdirSync(l,{recursive:!0});let e=he(t);I.writeFileSync(e,h.systemPrompt,{mode:X}),v=e}catch{v=void 0}let y=this.now(),b=this.createStatusWriter();b.open(t,{version:1,runId:t,...e.operationId?{operationId:e.operationId}:{},agent:n,...e.sensitiveTask?{}:{task:r},cwd:i,...e.piArgs.model?{model:e.piArgs.model}:{},...e.inlineAgent?{inlineAgent:e.inlineAgent}:{},state:`queued`,startedAt:y,lastUpdate:y,...a===void 0?{}:{fanoutIndex:a},...e.internal?{internal:!0}:{},...S(e.runtime)?{}:{runtime:e.runtime},...c?{transcriptPath:c}:{},...v?{systemPromptPath:v}:{},...v&&h.systemPromptMode?{systemPromptMode:h.systemPromptMode}:{},...h.tools?{tools:h.tools}:{},...h.excludeTools?.length?{excludeTools:h.excludeTools}:{},...h.noTools?{noTools:!0}:{}}),S(e.runtime)&&this.writeLaunchConfig(s,{runId:t,agent:n,task:r,cwd:i,childIndex:e.childIndex,handshakePath:o,sdk:h,...c?{transcriptPath:c}:{},args:f,env:g});let x,T=e=>{throw this.removeLaunchConfig(s),x&&N(x),I.rmSync(U(t),{force:!0}),b.updateSync(t=>{t.state=`failed`,t.error=e,t.lastUpdate=this.now()}),Error(e)},E=S(e.runtime);if(!E){let a=e.piArgs.capabilityCeiling;if(a&&(!e.externalProfile||!a.allowedExternalProfiles.includes(e.externalProfile)))return T(`External profile '${e.externalProfile??`none`}' is denied by the active capability ceiling.`);if(e.externalProfile!==void 0&&e.externalProfile!==M)return T(`External profile '${e.externalProfile}' is not trusted.`);if(e.externalProfile===M){if(e.runtime!==`claude`||e.piArgs.model!==`fable`)return T(`The Fable profile requires runtime claude and model fable.`);if(e.inlineAgent||e.piArgs.sessionFile||e.piArgs.parentSessionId)return T(`The Fable profile requires a fresh isolated context.`);x=se({runId:t,prompt:r,repositoryCwd:i,privateRoot:l}),this.writeLaunchConfig(s,{runId:t,...e.operationId?{operationId:e.operationId}:{},agent:n,runtime:`claude`,command:x.command,args:x.args,cwd:x.cwd,env:x.env,profile:x.profile,stdinPath:x.stdinPath,cleanupPaths:x.cleanupPaths,profileResultPath:U(t),internal:e.internal===!0,handshakePath:o,resultPath:L.join(m(),`${t}.json`)})}else this.writeLaunchConfig(s,{runId:t,...e.operationId?{operationId:e.operationId}:{},agent:n,...C(e.runtime,e.runtimes??{},{prompt:r,model:e.piArgs.model,cwd:i}),cwd:i,env:p,handshakePath:o,resultPath:L.join(m(),`${t}.json`)})}let{pid:D,onError:k,onExit:A}=this.spawnChild(process.execPath,[E?ge():_e()],{cwd:i,env:g});if(D===void 0)return T(`Async spawn for '${n}' (run '${t}') did not produce a pid.`);let j;k(e=>{j=e}),ce(_(),{runId:t,pid:D,agent:n,runtime:e.runtime??`pi`,startedAt:y}),this.terminalPersistence.trackChild(D),A((e,n)=>{this.terminalPersistence.untrackChild(D),le(_(),t),!(e===0&&n===null)&&(b.updateSync(t=>{t.state&&w.has(t.state)||(t.state=`failed`,t.error=`SDK runner process ${D} exited unexpectedly (${n??`code ${e??`unknown`}`}).`,t.lastUpdate=this.now(),t.endedAt=this.now())}),this.jobTracker.track(t))});let P=await this.createSpawnHandshake().waitForHandshake({path:o,timeoutMs:e.handshakeTimeoutMs??this.defaultHandshakeTimeoutMs}).promise;if(P.status!==`signalled`)return this.terminalPersistence.untrackChild(D),T(P.status===`failed`?P.error:j?.message??`Timed out waiting for '${n}' (run '${t}') to start.`);this.removeLaunchConfig(s);let F=this.readRunState(t);return(!F||!w.has(F))&&b.updateSync(e=>{e.state=`running`,e.lastUpdate=this.now()}),this.jobTracker.track(t),{runId:t,pid:D}}},Q=r([B(),d(0,z(s.AsyncJobTracker)),d(1,z(s.TerminalPersistenceService)),l(`design:paramtypes`,[Object,Object])],Q)}));$();export{Q as AsyncSubagentSpawner,U as fableProfileResultPathFor,H as handshakePathFor,$ as init_asyncExecution,V as runDirFor};
2
+ //# sourceMappingURL=asyncExecution.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"asyncExecution.mjs","names":[],"sources":["../../../src/runs/background/asyncExecution.ts"],"sourcesContent":["/**\n * The async-only spawn executor: launches exactly one subagent child process\n * and returns a run handle immediately, without waiting for the run to\n * finish.\n *\n * SCOPE - PARENT SIDE ONLY, SINGLE SPAWN:\n * `doom-pi-subagents/src/runs/background/asyncExecution.ts` is 1698 lines\n * doing far more than spawning: chain/parallel-group orchestration (building\n * `AgentConfig[]` sequences, dynamic fan-out expansion, workflow-graph\n * snapshots), session revival (a 3-way ready/ack/proceed handshake distinct\n * from the plain started-signal this module uses), and the foreground\n * (synchronous, blocking) code path. None of that is ported here:\n * - Chain/fan-out ORCHESTRATION (deciding how many children to spawn, in\n * what order, from what step list) is `port-steering`'s concurrent work on\n * the resolved-step and workflow-graph types. This module is the\n * PRIMITIVE that orchestration calls once per child - `childIndex` and\n * `fanout` arrive as inputs, already decided by the caller, not derived\n * here (see the child-index section below for why that is safe)\n * - Session revival (`revivalLease`, the 3-way ready/ack/proceed dance at\n * predecessor lines 498-509) is a materially different operation - resuming\n * an existing transcript, not spawning a fresh child - and is not ported.\n * `session-lease.ts` already exists in this package for whoever builds that\n * path later\n * - The foreground (synchronous) branch is dropped entirely, per the async-\n * only requirement below; there is no `async` parameter, only this\n *\n * WHAT THIS DOES NOT BUILD (compose, don't reimplement):\n * `CoalescedStatusWriter`, `TerminalPersistenceService`, `AsyncJobTracker`,\n * `SpawnHandshake`, `ResultWatcher`, `CompletionNotifier` all already exist\n * in this directory and are used here, not reimplemented. In particular, the\n * predecessor's process-exit tracking is reduced to the child handle's exact\n * exit signal: an abnormal exit marks that run failed immediately, while the\n * crash-marker path (`TerminalPersistenceService` +\n * `ProcessTerminalInspector`) remains the recovery path after parent loss.\n * `trackChild(pid)` on the caller's own already-active\n * `TerminalPersistenceService` is what makes a spawned child get killed if\n * the run that spawned it dies, which is the same guarantee the predecessor\n * used its own mechanism for.\n *\n * SDK RUNNER BOUNDARY:\n * `sdkRunnerEntry.ts` is the detached child process target. It reads the\n * launch config, creates the child through Pi's SDK, and loads\n * `subagent-prompt-runtime-entry` as an explicit child extension. That\n * extension owns the handshake, status, steering, and terminal result\n * lifecycle through the existing runner services. The real Pi CLI remains\n * the parent host used by system tests and DoomPi, not the child launcher.\n *\n * WHY SpawnHandshake, NOT `Atomics.wait`:\n * The predecessor blocks the parent's event loop for up to 10 seconds per\n * spawn (`asyncExecution.ts:376-380`, `Atomics.wait` on a shared buffer).\n * That codepath only actually ran for session revival there (a fresh spawn\n * had no `startupPath` and returned immediately) - but this package has no\n * synchronous/foreground path left to hide the cost behind, so a busy-wait\n * would block every single async spawn, not just revivals. `SpawnHandshake`\n * (already in this directory) replaces it: a real `fs.watch` on the\n * handshake file's directory, with a short poll as the safety net, and it\n * never blocks the event loop.\n *\n * LAUNCH CONFIG FILE LIFECYCLE (fix):\n * The predecessor writes the launch config with `fs.writeFileSync(cfgPath,\n * ...)` - no `mode` option, so it lands at the umask default - and never\n * unlinks it on any path, successful or failed\n * (`asyncExecution.ts:491-495`). Both are fixed here: `writeLaunchConfig`\n * writes 0600 (`writePrivateAtomicJson`, already in this package), and\n * `removeLaunchConfig` runs on every exit path - success (once the handshake\n * confirms the child has read it), and every failure (no pid, spawn error,\n * handshake failure or timeout) - so nothing is ever left behind holding the\n * full task text and resolved secrets/tokens at world-readable-adjacent\n * permissions.\n *\n * CHILD INDEX (fix - `DeliverableGuard` had no way to get one):\n * `DeliverableGuard.deliverNudge` requires an explicit `nudgeTarget` of\n * `{kind:'child', index}` for a fan-out run specifically so it cannot\n * broadcast a nudge to every sibling over one child's missing deliverable\n * (see that module's header). Nothing in this package maps a team member id\n * to a fan-out child index (verified: neither `nativeTeamChannel.ts` nor\n * `parallel-groups.ts` does this). This module needs no such mapping either\n * - `childIndex` is simply which spawn call this is, a fact the caller (a\n * single spawn, or a fan-out loop calling this once per child) already knows\n * without looking anything up, exactly like a flat index into\n * `status.steps[]` is decided by whoever builds that array. `nudgeTarget` is\n * derived from `childIndex`/`fanout` and persisted onto the run's own\n * `status.json` (`nudgeTarget` field) for whatever later evaluates\n * `DeliverableGuardInput` to read back, rather than re-derived at that point.\n *\n * DELIVERABLE EXPECTATION (fix - `DeliverableGuard` had no policy):\n * Derived here, at the one point this module already has the task text:\n * `classifyTaskMutationIntent` (used AS-IS, per its own header's warning\n * against widening its vocabulary for a third caller) distinguishes\n * `'implementation'` intent from `'read-only'`/`'unknown'`. Implementation\n * intent gets a real expectation (`{kind:'summary'}` by default - the\n * minimum a run can be asked to prove it did something, when no more\n * specific expectation like a structured-output schema or artifact path was\n * supplied); read-only or unknown intent gets none at all. This is\n * deliberately NOT `{kind:'summary'}` unconditionally: a guard that flags\n * every research run for an empty summary gets ignored, and an ignored guard\n * misses the run that actually needed it.\n *\n * TEAM MEMBERSHIP ENV FORWARDING (fix - a spawned child had no way to join\n * the team):\n * If THIS process (the one calling `spawn()`) is itself part of a native\n * team - `readNativeTeamRootFromEnvironment()` resolves a root context off\n * its own `process.env` - the same root vars\n * (`nativeTeamRootEnvironment(root)`, already in `nativeTeamChannel.ts`,\n * not reimplemented here) are folded into the child's env alongside\n * whatever `buildPiArgs` produced. Only the ROOT vars travel this way, never\n * a member id/token: those are per-member secrets the CHILD mints for\n * itself when it registers (see `runnerBootstrap.ts` and\n * `runnerTeamMembership.ts`), not something safe to hand down from the\n * parent's own identity. When this process has no team root (a standalone\n * spawn outside any team), nothing is forwarded and the child simply has\n * none to register against.\n */\n\nimport { spawn } from 'node:child_process';\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { createHarnessSession, getHarnessState } from '@agimon-ai/doompi-config';\nimport type { InlineAgent } from '@agimon-ai/doompi-extension-contracts/subagent-tool';\nimport { inject, injectable } from 'inversify';\n\nimport { getArtifactPaths, getArtifactsDir } from '../../shared/artifacts.ts';\nimport { writePrivateAtomicJson } from '../../shared/atomicJson.ts';\nimport {\n currentResultsDir,\n currentRunConfigPath,\n currentRunsDir,\n requireCurrentSessionScope,\n sessionScopeEnvironment,\n} from '../../shared/paths.ts';\nimport { DOOM_TEAM_TYPES } from '../../container/types.ts';\nimport { PI_RUNTIME_NAME, SUBAGENT_RUN_ID_ENV } from '../../env.ts';\nimport {\n nativeTeamRootEnvironment,\n readNativeTeamRootFromEnvironment,\n type TeamRootContext,\n} from '../../intercom/nativeTeamChannel.ts';\nimport type { ActivityState, ArtifactDirPreference } from '../../types.ts';\nimport { type IAsyncJobTracker, TERMINAL_ASYNC_JOB_STATES } from './asyncJobTracker.ts';\nimport {\n buildPiArgs,\n type BuildPiArgsInput,\n type BuildPiArgsResult,\n type PiSdkLaunchSettings,\n} from '../shared/piArgs.ts';\nimport { isPiRuntime, resolveRuntimeLaunch, type RuntimeTable } from '../shared/runtimeRegistry.ts';\nimport {\n CLAUDE_FABLE_PROFILE,\n cleanupClaudeFableLaunch,\n prepareClaudeFableLaunch,\n type ClaudeFableLaunch,\n} from '../shared/claudeFableProfile.ts';\nimport { registerRun, releaseRun } from '../registry/runRegistry.ts';\nimport { SpawnHandshake, type ISpawnHandshake } from './spawnHandshake.ts';\nimport {\n CoalescedStatusWriter,\n type ICoalescedStatusWriter,\n STATUS_FILE_NAME,\n type StatusWithRecentEntries,\n} from './statusWriter.ts';\nimport type { ITerminalPersistenceService } from './terminalPersistence.ts';\nimport { steerAcksDir, steerCapabilityPath, stepSteerInboxDir } from '../../intercom/supervisorControlChannel.ts';\n\n/** Leaf name of the started-signal file `SpawnHandshake` watches for, under `currentRunsDir()/<runId>/`. Exported so the child-side runner can compose the same convention rather than duplicate it. */\nexport const HANDSHAKE_FILE_NAME = 'handshake.json';\nexport const RUNNER_STDOUT_FILE_NAME = 'runner.stdout.log';\nexport const RUNNER_STDERR_FILE_NAME = 'runner.stderr.log';\nexport const FABLE_PROFILE_RESULT_FILE_NAME = 'fable-profile-result.json';\n/**\n * The effective system prompt this run launched with, kept for the fleet\n * inspector's agent view.\n *\n * A SIDECAR RATHER THAN A FIELD ON status.json: `CoalescedStatusWriter`\n * rewrites the whole status file on every coalesced flush, and a system prompt\n * is kilobytes of text that never changes after spawn. Inlining it would put\n * that payload through every status write for the life of the run. This file\n * is written once, next to the status it belongs to.\n *\n * Written 0600 for the same reason `piArgs.ts` writes its prompt file that\n * way: a resolved prompt can carry inherited project context.\n */\nexport const SYSTEM_PROMPT_FILE_NAME = 'system-prompt.md';\nconst SYSTEM_PROMPT_FILE_MODE = 0o600;\nconst DEFAULT_HANDSHAKE_TIMEOUT_MS = 10_000;\n\n/**\n * `state`/`steps`/`parallelGroups`/`chainStepCount`/`workflowGraph`/\n * `pendingAppends` are declared as optional-and-widened, rather than a\n * runner-owned status type, so this SAME status shape is what\n * `chain-append.ts`'s `applyChainAppendRequest` (which expects\n * `AppendableChainStatus`, an object with exactly these optional fields) can\n * mutate directly - one status type for the whole spawn-through-finalize\n * lifecycle, not a parent-side one and a child-side one that have to be kept\n * in sync by hand.\n */\nexport interface AsyncRunStatus extends StatusWithRecentEntries {\n /** Version 1 is emitted by current writers; absent is accepted only by the compatibility reader. */\n version?: 1;\n runId: string;\n operationId?: string;\n agent: string;\n /** Original task text required to restore a suspended run. */\n task?: string;\n cwd?: string;\n /** Resolved model selected for this run, when one was requested. */\n model?: string;\n /** One-shot agent definition required to restore an inline agent. */\n inlineAgent?: InlineAgent;\n state: 'queued' | 'running' | 'complete' | 'completed' | 'failed' | 'paused' | 'stopped';\n startedAt: number;\n lastUpdate: number;\n endedAt?: number;\n /** This run's position among its fan-out siblings, when it has any. Absent for a standalone run. */\n fanoutIndex?: number;\n /** Bridge-owned runs are consumed internally and never rendered as generic completion chat. */\n internal?: boolean;\n /** Which runtime executes this run. Absent means the default in-process `pi` path. */\n runtime?: string;\n activityState?: ActivityState;\n attentionReason?: string;\n error?: string;\n /**\n * Set once, at finalize, by `RunnerReporting.mutateTerminalStatus` - the\n * SAME value it puts in the terminal result file, truncated to a bounded\n * length (see that module's header for why). Lets a caller (chain mode,\n * in particular) learn a completed run's own summary through the status\n * file it already reads to learn the run finished, rather than needing a\n * second read of a claimed, single-consumer result file.\n */\n summary?: string;\n /**\n * This run's own Pi session transcript path, written during child bootstrap\n * and retained by `RunnerReporting.mutateTerminalStatus` at finalize. See\n * that module's header for where the value comes from and why it is bounded\n * by omission, not truncation. Absent for a run that either never reported\n * a session file (headless, no persisted transcript) or reported one too\n * long to trust. This is what `action='resume'` reads to know which\n * transcript a later revival should reopen.\n */\n sessionFile?: string;\n /** Child-owned Pi SDK event transcript consumed by the fleet detail pane. */\n transcriptPath?: string;\n /**\n * Sidecar holding this run's effective system prompt, and the resolved tool\n * plan it launched with. Recorded at spawn purely so the fleet inspector can\n * show what an agent was actually told: the launch config that carried them\n * is deleted as soon as the child handshakes, and the agent definition on\n * disk can change while a run is still going.\n *\n * All absent for a non-`pi` runtime, which has no SDK launch settings, and\n * for any run started before this was recorded.\n */\n systemPromptPath?: string;\n systemPromptMode?: 'append' | 'replace';\n /** Effective tool allowlist. Absent means \"whatever the child defaults to\". */\n tools?: string[];\n excludeTools?: string[];\n noTools?: boolean;\n /** Cumulative tokens observed from finalized child messages. */\n tokens?: number;\n /** Most recently started child tool. */\n currentTool?: string;\n /** Number of child tool executions observed so far. */\n toolCount?: number;\n}\n\n/** The parent-written contract a child-side runner reads via `currentRunConfigPath(runId)`. */\nexport interface LaunchConfig {\n runId: string;\n agent: string;\n task: string;\n cwd: string;\n childIndex: number;\n handshakePath: string;\n sdk: PiSdkLaunchSettings;\n transcriptPath?: string;\n /** Retained during the SDK cutover for compatibility diagnostics only. */\n args: string[];\n env: Record<string, string | undefined>;\n}\n\nexport interface AsyncSubagentSpawnInput {\n runId: string;\n operationId?: string;\n agent: string;\n /** One-shot agent definition retained only for explicit suspension restore. */\n inlineAgent?: InlineAgent;\n task: string;\n cwd: string;\n /** Persisted parent transcript used to place child artifacts beside the parent session. */\n parentSessionFile?: string;\n /** This spawn's position among its siblings. 0 for a standalone (non-fan-out) run. */\n childIndex: number;\n /** Whether this spawn is one of several children under a fan-out parent. */\n fanout: boolean;\n /** Everything `buildPiArgs` needs, minus what this spawner supplies itself (`runId`/`task`/`childIndex`). */\n piArgs: Omit<BuildPiArgsInput, 'runId' | 'task' | 'childIndex'>;\n /** False disables the child event transcript. Defaults to enabled. */\n artifacts?: boolean;\n /** Artifact root policy inherited from the parent Doom Team config. */\n artifactDir?: ArtifactDirPreference;\n handshakeTimeoutMs?: number;\n /**\n * Which runtime executes this run. Defaults to the in-process Pi SDK path.\n * Anything else is an external CLI resolved through `runtimeRegistry.ts`.\n */\n runtime?: string;\n /** Fixed trusted external profile. Only Doom Team internal bridges may set this. */\n externalProfile?: string;\n /** Prevent sensitive task text from being persisted in status.json. */\n sensitiveTask?: boolean;\n /** Suppress the generic completion notifier for an internal bridge run. */\n internal?: boolean;\n /** Runtimes available to this spawn, already merged with the shipped defaults. */\n runtimes?: RuntimeTable;\n}\n\nexport interface AsyncSubagentSpawnResult {\n runId: string;\n pid: number;\n}\n\nexport interface IAsyncSubagentSpawner {\n /** Spawn one child and return once it has confirmed it started. Throws on any failure to reach that point. */\n spawn(input: AsyncSubagentSpawnInput): Promise<AsyncSubagentSpawnResult>;\n}\n\n/** `currentRunsDir()/<runId>`. Exported so the child-side runner resolves the same directory, not a parallel one. */\nexport function runDirFor(runId: string): string {\n return path.join(currentRunsDir(), runId);\n}\n\n/** Exported so the child-side runner writes its ready/error signal at the exact path this parent half waits on. */\nexport function handshakePathFor(runId: string): string {\n return path.join(runDirFor(runId), HANDSHAKE_FILE_NAME);\n}\n\nexport function fableProfileResultPathFor(runId: string): string {\n return path.join(runDirFor(runId), FABLE_PROFILE_RESULT_FILE_NAME);\n}\n\n/** Where `spawn()` records this run's effective system prompt. See `SYSTEM_PROMPT_FILE_NAME`. */\nexport function systemPromptPathFor(runId: string): string {\n return path.join(runDirFor(runId), SYSTEM_PROMPT_FILE_NAME);\n}\n\n/**\n * Locate a child entry point on disk, built or source.\n *\n * The child is a separate process loading this by path, so the path has to be\n * the one that exists now: `.mjs` under `dist` when this bundle is built, the\n * original source file when it is not. `relativeDir` is where the entry sits\n * relative to `src/` - the SDK runner is one level up from this module, the\n * CLI runner is beside it.\n */\nfunction resolveRunnerEntry(relativeDir: string, stem: string): string {\n const moduleDirectory = path.dirname(fileURLToPath(import.meta.url));\n const packageRoot = path.resolve(moduleDirectory, '../..');\n const builtCandidate = path.join(packageRoot, relativeDir, `${stem}.mjs`);\n if (fs.existsSync(builtCandidate)) return builtCandidate;\n const sourceCandidate = path.join(packageRoot, relativeDir, `${stem}.ts`);\n if (fs.existsSync(sourceCandidate)) return sourceCandidate;\n throw new Error(`Doom Team runner '${stem}' is unavailable at '${builtCandidate}' or '${sourceCandidate}'.`);\n}\n\nfunction resolveSdkRunnerEntry(): string {\n return resolveRunnerEntry('runs', 'sdkRunnerEntry');\n}\n\nfunction resolveCliRunnerEntry(): string {\n return resolveRunnerEntry('runs/background', 'cliRunnerEntry');\n}\n\n@injectable()\nexport class AsyncSubagentSpawner implements IAsyncSubagentSpawner {\n constructor(\n @inject(DOOM_TEAM_TYPES.AsyncJobTracker) private readonly jobTracker: IAsyncJobTracker,\n @inject(DOOM_TEAM_TYPES.TerminalPersistenceService)\n private readonly terminalPersistence: ITerminalPersistenceService,\n ) {}\n\n /**\n * Seam for tests, as a protected member rather than a constructor\n * argument: inversify cannot resolve a primitive. See `pollScheduler.ts`\n * for the same pattern.\n */\n protected readonly defaultHandshakeTimeoutMs: number = DEFAULT_HANDSHAKE_TIMEOUT_MS;\n\n protected now(): number {\n return Date.now();\n }\n\n /**\n * This process's own team root context, read fresh per spawn. Protected\n * so a test can supply one without writing real team files to disk - see\n * the module header's TEAM MEMBERSHIP ENV FORWARDING section for why this\n * is read from `process.env` rather than accepted as a constructor\n * argument (inversify cannot resolve a primitive either way).\n */\n protected readCurrentTeamRoot(): TeamRootContext | undefined {\n return readNativeTeamRootFromEnvironment();\n }\n\n /**\n * A fresh writer per spawn, deliberately never the container's shared\n * singleton - the same reasoning as `StaleRunReconciler.createStatusWriter`\n * (see that class's header): the singleton may simultaneously be the live\n * write path for a run THIS process is itself running, and bootstrapping\n * someone else's (the spawned child's) initial status is a different\n * writer's job.\n */\n protected createStatusWriter(): ICoalescedStatusWriter<AsyncRunStatus> {\n return new CoalescedStatusWriter<AsyncRunStatus>();\n }\n\n /**\n * A fresh handshake wait per spawn. `SpawnHandshake` is bound transient in\n * the container for exactly this reason (each `waitForHandshake()` owns\n * its own timers and watcher for exactly one spawn); resolving it via\n * constructor injection would hand every spawn the same, already-used\n * instance, so this seam constructs one directly instead.\n */\n protected createSpawnHandshake(): ISpawnHandshake {\n return new SpawnHandshake();\n }\n\n protected writeLaunchConfig(filePath: string, config: object): void {\n writePrivateAtomicJson(filePath, config);\n }\n\n protected removeLaunchConfig(filePath: string): void {\n fs.rmSync(filePath, { force: true });\n }\n\n protected readRunState(runId: string): string | undefined {\n try {\n const parsed = JSON.parse(fs.readFileSync(path.join(runDirFor(runId), STATUS_FILE_NAME), 'utf8')) as {\n state?: unknown;\n };\n return typeof parsed.state === 'string' ? parsed.state : undefined;\n } catch {\n return undefined;\n }\n }\n\n /**\n * Resolves the child's CLI args/env. Protected so a test can bypass\n * `buildPiArgs`'s own real filesystem resolution (it locates a runtime\n * extension script on disk via `resolvePiLaunchToolPlan`, which is\n * environment-dependent and not something a unit test for THIS module's\n * orchestration logic should depend on).\n */\n protected buildLaunchArgs(input: BuildPiArgsInput): BuildPiArgsResult {\n return buildPiArgs(input);\n }\n\n /**\n * The actual `child_process.spawn`. Detached and unref'd immediately. Output\n * goes to per-run files so a startup failure is diagnosable without keeping\n * parent-owned pipes alive.\n */\n protected spawnChild(\n command: string,\n args: string[],\n options: { cwd: string; env: NodeJS.ProcessEnv },\n ): {\n pid: number | undefined;\n onError: (handler: (error: Error) => void) => void;\n onExit: (handler: (code: number | null, signal: NodeJS.Signals | null) => void) => void;\n } {\n const runId = options.env[SUBAGENT_RUN_ID_ENV];\n let stdoutFd: number | undefined;\n let stderrFd: number | undefined;\n if (runId) {\n const runDir = runDirFor(runId);\n fs.mkdirSync(runDir, { recursive: true });\n stdoutFd = fs.openSync(path.join(runDir, RUNNER_STDOUT_FILE_NAME), 'a', 0o600);\n try {\n stderrFd = fs.openSync(path.join(runDir, RUNNER_STDERR_FILE_NAME), 'a', 0o600);\n } catch (error) {\n fs.closeSync(stdoutFd);\n throw new Error(`Could not open runner stderr for '${runId}'.`, { cause: error });\n }\n }\n let child: ReturnType<typeof spawn>;\n try {\n child = spawn(command, args, {\n cwd: options.cwd,\n detached: true,\n stdio: ['ignore', stdoutFd ?? 'ignore', stderrFd ?? 'ignore'],\n windowsHide: true,\n env: options.env,\n });\n } finally {\n if (stdoutFd !== undefined) fs.closeSync(stdoutFd);\n if (stderrFd !== undefined) fs.closeSync(stderrFd);\n }\n child.unref();\n return {\n pid: child.pid,\n onError: (handler) => child.on('error', handler),\n onExit: (handler) => child.on('exit', handler),\n };\n }\n\n async spawn(input: AsyncSubagentSpawnInput): Promise<AsyncSubagentSpawnResult> {\n const { runId, agent, task, cwd } = input;\n const fanoutIndex = input.fanout ? input.childIndex : undefined;\n const handshakePath = handshakePathFor(runId);\n const configPath = currentRunConfigPath(runId);\n const transcriptPath =\n input.artifacts === false\n ? undefined\n : getArtifactPaths(\n getArtifactsDir(input.parentSessionFile ?? null, cwd, input.artifactDir ?? 'session'),\n runId,\n agent,\n ).transcriptPath;\n\n const runDir = runDirFor(runId);\n const steerIndex = input.childIndex ?? 0;\n const teamRoot = this.readCurrentTeamRoot();\n const {\n args,\n env: builtEnv,\n sdk,\n } = this.buildLaunchArgs({\n ...input.piArgs,\n runId,\n task,\n childIndex: input.childIndex,\n steerInboxDir: stepSteerInboxDir(runDir, steerIndex),\n steerCapabilityPath: steerCapabilityPath(runDir, steerIndex),\n steerAckDir: steerAcksDir(runDir, steerIndex),\n teamToolEnabled: Boolean(teamRoot),\n });\n // The child gets its own harness state, in this run's directory, which this\n // package owns. Sharing the parent's file would let a detached child rewrite\n // a session it does not own, and pointing at the parent's directory would\n // lose the file the moment the parent cleaned up its own session: a child\n // is detached and can outlive it. Pi 0.84 SDK launches consume the resolved\n // extension list directly, so preserve that same list in the child harness\n // snapshot instead of projecting the parent's stale childExtensions value.\n createHarnessSession(\n { ...getHarnessState(), childExtensions: sdk.extensions },\n { directory: runDir, environment: builtEnv, unclaimed: true },\n );\n const env = {\n ...process.env,\n ...builtEnv,\n // The child resolves the same filesystem tree from this, independently of\n // whether it also joins a team.\n ...sessionScopeEnvironment(requireCurrentSessionScope()),\n ...(teamRoot ? nativeTeamRootEnvironment(teamRoot) : {}),\n };\n\n // Recorded before open() so the status never advertises a sidecar that is\n // not on disk yet. A failure here is not worth failing a spawn over: the\n // prompt is an inspection aid, so the run proceeds without it.\n let systemPromptPath: string | undefined;\n if (isPiRuntime(input.runtime) && sdk.systemPrompt) {\n try {\n fs.mkdirSync(runDir, { recursive: true });\n const target = systemPromptPathFor(runId);\n fs.writeFileSync(target, sdk.systemPrompt, { mode: SYSTEM_PROMPT_FILE_MODE });\n systemPromptPath = target;\n } catch {\n systemPromptPath = undefined;\n }\n }\n\n const now = this.now();\n const statusWriter = this.createStatusWriter();\n statusWriter.open(runId, {\n version: 1,\n runId,\n ...(input.operationId ? { operationId: input.operationId } : {}),\n agent,\n ...(input.sensitiveTask ? {} : { task }),\n cwd,\n ...(input.piArgs.model ? { model: input.piArgs.model } : {}),\n ...(input.inlineAgent ? { inlineAgent: input.inlineAgent } : {}),\n state: 'queued',\n startedAt: now,\n lastUpdate: now,\n ...(fanoutIndex === undefined ? {} : { fanoutIndex }),\n ...(input.internal ? { internal: true } : {}),\n ...(isPiRuntime(input.runtime) ? {} : { runtime: input.runtime }),\n ...(transcriptPath ? { transcriptPath } : {}),\n ...(systemPromptPath ? { systemPromptPath } : {}),\n ...(systemPromptPath && sdk.systemPromptMode ? { systemPromptMode: sdk.systemPromptMode } : {}),\n ...(sdk.tools ? { tools: sdk.tools } : {}),\n ...(sdk.excludeTools?.length ? { excludeTools: sdk.excludeTools } : {}),\n ...(sdk.noTools ? { noTools: true } : {}),\n });\n\n if (isPiRuntime(input.runtime)) {\n this.writeLaunchConfig(configPath, {\n runId,\n agent,\n task,\n cwd,\n childIndex: input.childIndex,\n handshakePath,\n sdk,\n ...(transcriptPath ? { transcriptPath } : {}),\n args,\n env,\n });\n }\n\n let fableLaunch: ClaudeFableLaunch | undefined;\n const fail = (message: string): never => {\n this.removeLaunchConfig(configPath);\n if (fableLaunch) cleanupClaudeFableLaunch(fableLaunch);\n fs.rmSync(fableProfileResultPathFor(runId), { force: true });\n statusWriter.updateSync((status) => {\n status.state = 'failed';\n status.error = message;\n status.lastUpdate = this.now();\n });\n throw new Error(message);\n };\n\n // One branch, and only one: `pi` runs in-process through the SDK runner and\n // gets the team channel, steering and structured output that go with it.\n // Every other runtime is an external CLI, spawned through the generic\n // runner, which deliberately knows nothing about the vendor.\n const usePi = isPiRuntime(input.runtime);\n if (!usePi) {\n const ceiling = input.piArgs.capabilityCeiling;\n if (ceiling && (!input.externalProfile || !ceiling.allowedExternalProfiles.includes(input.externalProfile))) {\n return fail(\n `External profile '${input.externalProfile ?? 'none'}' is denied by the active capability ceiling.`,\n );\n }\n if (input.externalProfile !== undefined && input.externalProfile !== CLAUDE_FABLE_PROFILE) {\n return fail(`External profile '${input.externalProfile}' is not trusted.`);\n }\n if (input.externalProfile === CLAUDE_FABLE_PROFILE) {\n if (input.runtime !== 'claude' || input.piArgs.model !== 'fable') {\n return fail('The Fable profile requires runtime claude and model fable.');\n }\n if (input.inlineAgent || input.piArgs.sessionFile || input.piArgs.parentSessionId) {\n return fail('The Fable profile requires a fresh isolated context.');\n }\n fableLaunch = prepareClaudeFableLaunch({\n runId,\n prompt: task,\n repositoryCwd: cwd,\n privateRoot: runDir,\n });\n this.writeLaunchConfig(configPath, {\n runId,\n ...(input.operationId ? { operationId: input.operationId } : {}),\n agent,\n runtime: 'claude',\n command: fableLaunch.command,\n args: fableLaunch.args,\n cwd: fableLaunch.cwd,\n env: fableLaunch.env,\n profile: fableLaunch.profile,\n stdinPath: fableLaunch.stdinPath,\n cleanupPaths: fableLaunch.cleanupPaths,\n profileResultPath: fableProfileResultPathFor(runId),\n internal: input.internal === true,\n handshakePath,\n resultPath: path.join(currentResultsDir(), `${runId}.json`),\n });\n } else {\n this.writeLaunchConfig(configPath, {\n runId,\n ...(input.operationId ? { operationId: input.operationId } : {}),\n agent,\n ...resolveRuntimeLaunch(input.runtime as string, input.runtimes ?? {}, {\n prompt: task,\n model: input.piArgs.model,\n cwd,\n }),\n cwd,\n env: builtEnv as Record<string, string>,\n handshakePath,\n resultPath: path.join(currentResultsDir(), `${runId}.json`),\n });\n }\n }\n\n const { pid, onError, onExit } = this.spawnChild(\n process.execPath,\n [usePi ? resolveSdkRunnerEntry() : resolveCliRunnerEntry()],\n { cwd, env: env as NodeJS.ProcessEnv },\n );\n if (pid === undefined) {\n return fail(`Async spawn for '${agent}' (run '${runId}') did not produce a pid.`);\n }\n let spawnError: Error | undefined;\n onError((error) => {\n spawnError = error;\n });\n\n // Recorded before the handshake: a parent that dies between spawn and\n // handshake must still leave a record a later sweep can act on.\n registerRun(requireCurrentSessionScope(), {\n runId,\n pid,\n agent,\n runtime: input.runtime ?? PI_RUNTIME_NAME,\n startedAt: now,\n });\n this.terminalPersistence.trackChild(pid);\n onExit((code, signal) => {\n this.terminalPersistence.untrackChild(pid);\n releaseRun(requireCurrentSessionScope(), runId);\n if (code === 0 && signal === null) return;\n statusWriter.updateSync((status) => {\n if (status.state && TERMINAL_ASYNC_JOB_STATES.has(status.state)) return;\n status.state = 'failed';\n status.error = `SDK runner process ${pid} exited unexpectedly (${signal ?? `code ${code ?? 'unknown'}`}).`;\n status.lastUpdate = this.now();\n status.endedAt = this.now();\n });\n this.jobTracker.track(runId);\n });\n\n const handshake = this.createSpawnHandshake();\n const outcome = await handshake.waitForHandshake({\n path: handshakePath,\n timeoutMs: input.handshakeTimeoutMs ?? this.defaultHandshakeTimeoutMs,\n }).promise;\n\n if (outcome.status !== 'signalled') {\n this.terminalPersistence.untrackChild(pid);\n const message =\n outcome.status === 'failed'\n ? outcome.error\n : (spawnError?.message ?? `Timed out waiting for '${agent}' (run '${runId}') to start.`);\n return fail(message);\n }\n\n // The child has confirmed it read the launch config; the parent's copy\n // is no longer needed (see the module header's LAUNCH CONFIG FILE\n // LIFECYCLE section).\n this.removeLaunchConfig(configPath);\n const observedState = this.readRunState(runId);\n if (!observedState || !TERMINAL_ASYNC_JOB_STATES.has(observedState)) {\n statusWriter.updateSync((status) => {\n status.state = 'running';\n status.lastUpdate = this.now();\n });\n }\n this.jobTracker.track(runId);\n\n return { runId, pid };\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ const e=require(`../../_virtual/_rolldown/runtime.cjs`),t=require(`../../_virtual/_@oxc-project_runtime@0.127.0/helpers/decorate.cjs`),n=require(`../../container/types.cjs`),r=require(`../../_virtual/_@oxc-project_runtime@0.127.0/helpers/decorateMetadata.cjs`),i=require(`../../_virtual/_@oxc-project_runtime@0.127.0/helpers/decorateParam.cjs`),a=require(`../../shared/paths.cjs`),o=require(`./statusWriter.cjs`),s=require(`./statusReader.cjs`);let c=require(`node:fs`);c=e.__toESM(c,1);let l=require(`node:path`);l=e.__toESM(l,1);let u=require(`inversify`);const d=new Set([`complete`,`completed`,`failed`,`paused`,`stopped`]);function f(e,t){let n=e.list().map(e=>e.runId).filter(e=>e===t||e.startsWith(t)),r=n.find(e=>e===t);if(r)return r;if(n.length===1)return n[0];throw n.length===0?Error(`No current-session run found for '${t}'.`):Error(`Multiple current-session runs match '${t}'. Use a longer id.`)}const p=`__unscoped__`;function m(e){return l.join(a.currentRunsDir(),e,o.STATUS_FILE_NAME)}function h(e){return typeof e==`object`&&e&&`code`in e?e.code:void 0}function g(e){return h(e)===`ENOENT`}const _=new Set([`starting`,`working`,`tool`,`waiting_for_reply`,`needs_attention`,`finalizing`,`active_long_running`]);function v(e){return typeof e==`string`&&_.has(e)?e:void 0}let y=class{constructor(e){this.scheduler=e}pollIntervalMs=250;retentionMs=1e4;now(){return Date.now()}readFile(e){try{return c.readFileSync(e,`utf-8`)}catch(e){if(g(e))return;throw e}}sessions=new Map;unregisterPoll;session(e){let t=this.sessions.get(e);return t||(t={jobs:new Map,terminalAt:new Map},this.sessions.set(e,t)),t}forSession(e){if(!e.trim())throw Error(`Pi session identity is required to track subagent runs.`);return{track:t=>this.trackInSession(e,t),untrack:t=>this.untrackInSession(e,t),list:()=>this.listInSession(e),get:t=>this.getInSession(e,t),reset:()=>this.resetSession(e)}}readStatus(e){let t=this.readFile(m(e));if(t===void 0)return;let n=s.parseAsyncRunStatus(t,m(e));if(n.kind!==`ok`)return;let r=n.status;return{agent:r.agent,status:r.state,startedAt:r.startedAt,updatedAt:r.lastUpdate,error:r.error,activityState:v(r.activityState),attentionReason:r.attentionReason,runtime:r.runtime,tokens:r.tokens,currentTool:r.currentTool,toolCount:r.toolCount}}refresh(e,t){let n=this.session(e),r=n.jobs.get(t),i=this.readStatus(t);if(i===void 0)return r||n.jobs.set(t,{runId:t,status:void 0}),!1;let a={runId:t,...i};n.jobs.set(t,a);let o=r?.status!==void 0&&d.has(r.status),s=a.status!==void 0&&d.has(a.status);return s&&!o&&n.terminalAt.set(t,this.now()),s||n.terminalAt.delete(t),!r||r.status!==a.status||r.updatedAt!==a.updatedAt||r.tokens!==a.tokens||r.currentTool!==a.currentTool||r.toolCount!==a.toolCount}run(){let e=!1,t=this.now();for(let[n,r]of this.sessions){for(let t of r.jobs.keys())this.refresh(n,t)&&(e=!0);for(let[n,i]of r.terminalAt)t-i<=this.retentionMs||(r.jobs.delete(n),r.terminalAt.delete(n),e=!0);r.jobs.size===0&&this.sessions.delete(n)}return e}track(e){this.trackInSession(p,e)}untrack(e){this.untrackInSession(p,e)}list(){return this.listInSession(p)}get(e){return this.getInSession(p,e)}reset(){this.resetSession(p)}trackInSession(e,t){this.refresh(e,t)}untrackInSession(e,t){let n=this.sessions.get(e);n?.jobs.delete(t),n?.terminalAt.delete(t),n?.jobs.size===0&&this.sessions.delete(e)}listInSession(e){return[...this.sessions.get(e)?.jobs.values()??[]]}getInSession(e,t){return this.sessions.get(e)?.jobs.get(t)}resetSession(e){this.sessions.delete(e)}start(){this.stop(),this.unregisterPoll=this.scheduler.register({id:`async-job-tracker`,intervalMs:this.pollIntervalMs,run:()=>this.run()})}stop(){this.unregisterPoll?.(),this.unregisterPoll=void 0,this.sessions.clear()}};y=t.__decorate([(0,u.injectable)(),i.__decorateParam(0,(0,u.inject)(n.DOOM_TEAM_TYPES.PollScheduler)),r.__decorateMetadata(`design:paramtypes`,[Object])],y),Object.defineProperty(exports,`AsyncJobTracker`,{enumerable:!0,get:function(){return y}}),exports.TERMINAL_ASYNC_JOB_STATES=d,exports.resolveTrackedRunId=f;
2
+ //# sourceMappingURL=asyncJobTracker.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"asyncJobTracker.cjs","names":["path","currentRunsDir","STATUS_FILE_NAME","fs","parseAsyncRunStatus","DOOM_TEAM_TYPES"],"sources":["../../../src/runs/background/asyncJobTracker.ts"],"sourcesContent":["/**\n * Live, in-memory registry of the async runs this session currently cares\n * about, refreshed from `status.json` on a `PollScheduler` tick.\n *\n * WHY THIS IS SMALLER THAN THE PREDECESSOR:\n * `doom-pi-subagents/src/runs/background/asyncJobTracker.ts` is 590 lines\n * doing four different jobs: this registry, TUI widget rendering\n * (`renderWidget`/`widgetRenderKey`/`ctx.ui.requestRender`), forwarding\n * control events and steering notices by byte-range-tailing each run's\n * `events.jsonl`, and projecting nested/parallel-group run trees. Only the\n * registry is ported. The other three have no consumer in this package yet:\n * TUI rendering is `G8` (slash commands + TUI), and nested/chain/parallel run\n * modes are `G7`+ (this package is currently single-run async-only - see\n * `spawnHandshake.ts`'s header). Porting rendering or event-tailing logic\n * with nothing to render to or forward to would be untested, unreachable\n * code; it is deferred to whichever later port actually needs it, not\n * dropped silently - this note is where to look for it.\n *\n * DESIGN PATTERNS:\n * - The in-memory map is a pure CACHE, never authoritative on its own:\n * `status.json` (owned by `CoalescedStatusWriter` elsewhere) is the only\n * source of truth. `track()` reads it synchronously immediately, so a\n * caller that already knows a run's id sees *something* right away rather\n * than waiting out the first poll\n * - Restart survivability follows from the point above rather than a second\n * persistence layer: this package has not built session-scoped run\n * ownership yet (which ids \"belong\" to a restarted session is not\n * something this module can determine on its own - see the AVOID entry),\n * so recovery is the caller re-`track()`-ing whatever ids it still cares\n * about from ITS OWN durable record. The very next poll tick repopulates\n * that job's status straight from disk. There is no separate store to keep\n * in sync, and therefore no window where memory could diverge from disk\n * that a restart would not immediately close\n * - Registered with `PollScheduler`, not a private timer: this polls\n * potentially many jobs' status files on an unbounded, periodic cadence\n * with no low-latency requirement - the exact shape `PollScheduler` exists\n * for. Contrast `completionBatcher.ts`/`spawnHandshake.ts`, which own\n * their own timers because they are short-lived, single-shot, and need\n * sub-200ms responsiveness `PollScheduler`'s floor cannot give them; this\n * registry needs neither\n * - Retention-then-eviction happens on the SAME poll tick as the status\n * refresh, rather than the predecessor's per-job `setTimeout`\n * (`scheduleCleanup`): a terminal job aged past `retentionMs` is simply\n * dropped the next time `run()` walks the map. One clock, not one timer per\n * job\n *\n * AVOID:\n * - Scanning `currentRunsDir()` to guess which run ids \"belong\" to this session on\n * startup. This package has no session-scoped run ownership concept yet;\n * inventing one here to answer \"what should I restore\" would be a guess,\n * not a fact this module has standing to assert. Restoration is the\n * caller's job (see the design note above)\n * - Growing `terminalStates` independently of `staleRunReconciler.ts`'s own\n * (unexported) copy; both must treat the same state strings as terminal,\n * or a job could be evicted here while the reconciler still thinks it is\n * in flight, or vice versa\n *\n * `activityState`/`attentionReason` (added for `subagentWait.ts`):\n * `DeliverableGuard.evaluate()` computes `activityState: 'needs_attention'`\n * and a `reason` (e.g. `'missing-deliverable'`), but returns it rather than\n * writing it anywhere - it has no standing to touch `status.json` itself (see\n * that module's header). Something upstream of this tracker - the run's own\n * runner main loop, not yet built - is responsible for folding that result\n * into `status.json` through `CoalescedStatusWriter`. This tracker's job is\n * only to read whatever ends up there, the same as every other field: it does\n * not compute attention, detect it, or decide when a run needs it.\n */\n\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport { inject, injectable } from 'inversify';\n\nimport type { ActivityState } from '../../types.ts';\nimport { currentRunsDir } from '../../shared/paths.ts';\nimport { DOOM_TEAM_TYPES } from '../../container/types.ts';\nimport type { IPollScheduler } from '../../shared/pollScheduler.ts';\nimport { parseAsyncRunStatus } from './statusReader.ts';\nimport { STATUS_FILE_NAME } from './statusWriter.ts';\n\n/**\n * States that mean \"already finished\"; matches `staleRunReconciler.ts`'s\n * own (unexported) set. Exported so a caller outside this module (the slash\n * layer's poll-until-terminal helper) can recognize the same terminal set\n * `TrackedAsyncJob.status` uses, rather than declaring a third copy of this\n * literal.\n */\nexport const TERMINAL_ASYNC_JOB_STATES = new Set(['complete', 'completed', 'failed', 'paused', 'stopped']);\n\n/** How long a terminal job stays queryable before eviction. */\nconst DEFAULT_RETENTION_MS = 10_000;\n/** How often tracked jobs' status is refreshed from disk. */\nconst DEFAULT_POLL_INTERVAL_MS = 250;\n\nconst POLL_SUBSCRIBER_ID = 'async-job-tracker';\n\n/**\n * The minimum shape this module reads. A run's actual `status.json` carries\n * far more (this package has no shared status type yet - see\n * `staleRunReconciler.ts`'s `ReconcilableStatus` for the same caveat);\n * everything else is simply not read here.\n */\nexport interface TrackedAsyncJob {\n runId: string;\n /** Named agent executing this run. Absent until the first readable status. */\n agent?: string;\n /** `status.json`'s `state` field. `undefined` until the first successful read. */\n status: string | undefined;\n startedAt?: number;\n updatedAt?: number;\n error?: string;\n /** `status.json`'s `activityState` field, e.g. `'needs_attention'`. See the module doc. */\n activityState?: ActivityState;\n /** `status.json`'s `reason` field, present alongside `activityState` (e.g. `'missing-deliverable'`). */\n attentionReason?: string;\n /** Which runtime is executing this run. Absent means the default `pi` path. */\n runtime?: string;\n tokens?: number;\n currentTool?: string;\n toolCount?: number;\n}\n\nexport interface ITrackedAsyncJobs {\n /**\n * Start tracking `runId`: reads its current status synchronously (so a\n * caller sees something immediately) and adds it to the poll rotation.\n * Calling this again for an id already tracked simply re-reads it now,\n * the same idempotent-refresh idiom as `CoalescedStatusWriter.open()`.\n */\n track(runId: string): void;\n /** Stop tracking `runId` immediately, without waiting out its retention window. */\n untrack(runId: string): void;\n /** Every currently tracked job (in-flight, or terminal within its retention window). */\n list(): TrackedAsyncJob[];\n get(runId: string): TrackedAsyncJob | undefined;\n /** Drop every tracked job. */\n reset(): void;\n}\n\nexport interface IAsyncJobTracker extends ITrackedAsyncJobs {\n /** Return the isolated run collection owned by one Pi session context. */\n forSession(sessionId: string): ITrackedAsyncJobs;\n /** Register with `PollScheduler`. Call once; a second call is a reset, same as `ResultWatcher.start()`. */\n start(): void;\n /** Unregister from `PollScheduler` and drop every tracked job. */\n stop(): void;\n}\n\n/** Resolve an exact id or unique prefix only inside one Pi session's run collection. */\nexport function resolveTrackedRunId(jobs: ITrackedAsyncJobs, id: string): string {\n const matches = jobs\n .list()\n .map((job) => job.runId)\n .filter((runId) => runId === id || runId.startsWith(id));\n const exact = matches.find((runId) => runId === id);\n if (exact) return exact;\n if (matches.length === 1) return matches[0]!;\n if (matches.length === 0) throw new Error(`No current-session run found for '${id}'.`);\n throw new Error(`Multiple current-session runs match '${id}'. Use a longer id.`);\n}\n\ninterface SessionJobs {\n jobs: Map<string, TrackedAsyncJob>;\n terminalAt: Map<string, number>;\n}\n\nconst UNSCOPED_SESSION = '__unscoped__';\n\nfunction statusPathFor(runId: string): string {\n return path.join(currentRunsDir(), runId, STATUS_FILE_NAME);\n}\n\nfunction errorCode(error: unknown): string | undefined {\n return typeof error === 'object' && error !== null && 'code' in error\n ? (error as NodeJS.ErrnoException).code\n : undefined;\n}\n\nfunction isNotFound(error: unknown): boolean {\n return errorCode(error) === 'ENOENT';\n}\n\ninterface ReadStatusResult {\n agent: string;\n status: string | undefined;\n startedAt?: number;\n updatedAt?: number;\n error?: string;\n activityState?: ActivityState;\n attentionReason?: string;\n runtime?: string;\n tokens?: number;\n currentTool?: string;\n toolCount?: number;\n}\n\nconst VALID_ACTIVITY_STATES: ReadonlySet<string> = new Set<ActivityState>([\n 'starting',\n 'working',\n 'tool',\n 'waiting_for_reply',\n 'needs_attention',\n 'finalizing',\n 'active_long_running',\n]);\n\nfunction asActivityState(value: unknown): ActivityState | undefined {\n return typeof value === 'string' && VALID_ACTIVITY_STATES.has(value) ? (value as ActivityState) : undefined;\n}\n\n@injectable()\nexport class AsyncJobTracker implements IAsyncJobTracker {\n constructor(@inject(DOOM_TEAM_TYPES.PollScheduler) private readonly scheduler: IPollScheduler) {}\n\n /**\n * Seams for tests, as protected members rather than constructor arguments:\n * inversify cannot resolve a primitive. See `pollScheduler.ts` and\n * `agents/discovery.ts` for the same pattern.\n */\n protected readonly pollIntervalMs: number = DEFAULT_POLL_INTERVAL_MS;\n protected readonly retentionMs: number = DEFAULT_RETENTION_MS;\n\n protected now(): number {\n return Date.now();\n }\n\n protected readFile(filePath: string): string | undefined {\n try {\n return fs.readFileSync(filePath, 'utf-8');\n } catch (error) {\n if (isNotFound(error)) return undefined;\n throw error;\n }\n }\n\n private readonly sessions = new Map<string, SessionJobs>();\n private unregisterPoll: (() => void) | undefined;\n\n private session(sessionId: string): SessionJobs {\n let session = this.sessions.get(sessionId);\n if (!session) {\n session = { jobs: new Map(), terminalAt: new Map() };\n this.sessions.set(sessionId, session);\n }\n return session;\n }\n\n forSession(sessionId: string): ITrackedAsyncJobs {\n if (!sessionId.trim()) throw new Error('Pi session identity is required to track subagent runs.');\n return {\n track: (runId) => this.trackInSession(sessionId, runId),\n untrack: (runId) => this.untrackInSession(sessionId, runId),\n list: () => this.listInSession(sessionId),\n get: (runId) => this.getInSession(sessionId, runId),\n reset: () => this.resetSession(sessionId),\n };\n }\n\n private readStatus(runId: string): ReadStatusResult | undefined {\n const raw = this.readFile(statusPathFor(runId));\n if (raw === undefined) return undefined;\n const result = parseAsyncRunStatus(raw, statusPathFor(runId));\n if (result.kind !== 'ok') return undefined;\n const status = result.status;\n return {\n agent: status.agent,\n status: status.state,\n startedAt: status.startedAt,\n updatedAt: status.lastUpdate,\n error: status.error,\n activityState: asActivityState(status.activityState),\n attentionReason: status.attentionReason,\n runtime: status.runtime,\n tokens: status.tokens,\n currentTool: status.currentTool,\n toolCount: status.toolCount,\n };\n }\n\n private refresh(sessionId: string, runId: string): boolean {\n const session = this.session(sessionId);\n const existing = session.jobs.get(runId);\n const read = this.readStatus(runId);\n if (read === undefined) {\n // Not readable yet (the run has not started) or a transient torn/\n // mid-write read (see readStatus's own comment): neither is evidence\n // to overwrite whatever was already known, so an existing entry is\n // left exactly as it was rather than blanked out for one tick.\n if (existing) return false;\n session.jobs.set(runId, { runId, status: undefined });\n return false;\n }\n\n const job: TrackedAsyncJob = { runId, ...read };\n session.jobs.set(runId, job);\n\n const wasTerminal = existing?.status !== undefined && TERMINAL_ASYNC_JOB_STATES.has(existing.status);\n const isTerminal = job.status !== undefined && TERMINAL_ASYNC_JOB_STATES.has(job.status);\n if (isTerminal && !wasTerminal) session.terminalAt.set(runId, this.now());\n if (!isTerminal) session.terminalAt.delete(runId);\n\n return (\n !existing ||\n existing.status !== job.status ||\n existing.updatedAt !== job.updatedAt ||\n existing.tokens !== job.tokens ||\n existing.currentTool !== job.currentTool ||\n existing.toolCount !== job.toolCount\n );\n }\n\n /** One scheduler tick: refresh every tracked job's status, then evict anything past its retention window. */\n protected run(): boolean {\n let changed = false;\n const now = this.now();\n for (const [sessionId, session] of this.sessions) {\n for (const runId of session.jobs.keys()) {\n if (this.refresh(sessionId, runId)) changed = true;\n }\n for (const [runId, terminatedAt] of session.terminalAt) {\n if (now - terminatedAt <= this.retentionMs) continue;\n session.jobs.delete(runId);\n session.terminalAt.delete(runId);\n changed = true;\n }\n if (session.jobs.size === 0) this.sessions.delete(sessionId);\n }\n\n return changed;\n }\n\n track(runId: string): void {\n this.trackInSession(UNSCOPED_SESSION, runId);\n }\n\n untrack(runId: string): void {\n this.untrackInSession(UNSCOPED_SESSION, runId);\n }\n\n list(): TrackedAsyncJob[] {\n return this.listInSession(UNSCOPED_SESSION);\n }\n\n get(runId: string): TrackedAsyncJob | undefined {\n return this.getInSession(UNSCOPED_SESSION, runId);\n }\n\n reset(): void {\n this.resetSession(UNSCOPED_SESSION);\n }\n\n private trackInSession(sessionId: string, runId: string): void {\n this.refresh(sessionId, runId);\n }\n\n private untrackInSession(sessionId: string, runId: string): void {\n const session = this.sessions.get(sessionId);\n session?.jobs.delete(runId);\n session?.terminalAt.delete(runId);\n if (session?.jobs.size === 0) this.sessions.delete(sessionId);\n }\n\n private listInSession(sessionId: string): TrackedAsyncJob[] {\n return [...(this.sessions.get(sessionId)?.jobs.values() ?? [])];\n }\n\n private getInSession(sessionId: string, runId: string): TrackedAsyncJob | undefined {\n return this.sessions.get(sessionId)?.jobs.get(runId);\n }\n\n private resetSession(sessionId: string): void {\n this.sessions.delete(sessionId);\n }\n\n start(): void {\n this.stop();\n this.unregisterPoll = this.scheduler.register({\n id: POLL_SUBSCRIBER_ID,\n intervalMs: this.pollIntervalMs,\n run: () => this.run(),\n });\n }\n\n stop(): void {\n this.unregisterPoll?.();\n this.unregisterPoll = undefined;\n this.sessions.clear();\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import{__esmMin as e}from"../../_virtual/_rolldown/runtime.mjs";import{__decorate as t,init_decorate as n}from"../../_virtual/_@oxc-project_runtime@0.127.0/helpers/decorate.mjs";import{DOOM_TEAM_TYPES as r,init_types as i}from"../../container/types.mjs";import{__decorateMetadata as a,init_decorateMetadata as o}from"../../_virtual/_@oxc-project_runtime@0.127.0/helpers/decorateMetadata.mjs";import{__decorateParam as s,init_decorateParam as c}from"../../_virtual/_@oxc-project_runtime@0.127.0/helpers/decorateParam.mjs";import{currentRunsDir as l,init_paths as u}from"../../shared/paths.mjs";import{STATUS_FILE_NAME as d,init_statusWriter as f}from"./statusWriter.mjs";import{init_statusReader as p,parseAsyncRunStatus as m}from"./statusReader.mjs";import*as h from"node:fs";import*as g from"node:path";import{inject as _,injectable as v}from"inversify";function y(e,t){let n=e.list().map(e=>e.runId).filter(e=>e===t||e.startsWith(t)),r=n.find(e=>e===t);if(r)return r;if(n.length===1)return n[0];throw n.length===0?Error(`No current-session run found for '${t}'.`):Error(`Multiple current-session runs match '${t}'. Use a longer id.`)}function b(e){return g.join(l(),e,d)}function x(e){return typeof e==`object`&&e&&`code`in e?e.code:void 0}function S(e){return x(e)===`ENOENT`}function C(e){return typeof e==`string`&&k.has(e)?e:void 0}var w,T,E,D,O,k,A,j=e((()=>{u(),i(),p(),f(),o(),c(),n(),w=new Set([`complete`,`completed`,`failed`,`paused`,`stopped`]),T=1e4,E=250,D=`async-job-tracker`,O=`__unscoped__`,k=new Set([`starting`,`working`,`tool`,`waiting_for_reply`,`needs_attention`,`finalizing`,`active_long_running`]),A=class{constructor(e){this.scheduler=e}pollIntervalMs=E;retentionMs=T;now(){return Date.now()}readFile(e){try{return h.readFileSync(e,`utf-8`)}catch(e){if(S(e))return;throw e}}sessions=new Map;unregisterPoll;session(e){let t=this.sessions.get(e);return t||(t={jobs:new Map,terminalAt:new Map},this.sessions.set(e,t)),t}forSession(e){if(!e.trim())throw Error(`Pi session identity is required to track subagent runs.`);return{track:t=>this.trackInSession(e,t),untrack:t=>this.untrackInSession(e,t),list:()=>this.listInSession(e),get:t=>this.getInSession(e,t),reset:()=>this.resetSession(e)}}readStatus(e){let t=this.readFile(b(e));if(t===void 0)return;let n=m(t,b(e));if(n.kind!==`ok`)return;let r=n.status;return{agent:r.agent,status:r.state,startedAt:r.startedAt,updatedAt:r.lastUpdate,error:r.error,activityState:C(r.activityState),attentionReason:r.attentionReason,runtime:r.runtime,tokens:r.tokens,currentTool:r.currentTool,toolCount:r.toolCount}}refresh(e,t){let n=this.session(e),r=n.jobs.get(t),i=this.readStatus(t);if(i===void 0)return r||n.jobs.set(t,{runId:t,status:void 0}),!1;let a={runId:t,...i};n.jobs.set(t,a);let o=r?.status!==void 0&&w.has(r.status),s=a.status!==void 0&&w.has(a.status);return s&&!o&&n.terminalAt.set(t,this.now()),s||n.terminalAt.delete(t),!r||r.status!==a.status||r.updatedAt!==a.updatedAt||r.tokens!==a.tokens||r.currentTool!==a.currentTool||r.toolCount!==a.toolCount}run(){let e=!1,t=this.now();for(let[n,r]of this.sessions){for(let t of r.jobs.keys())this.refresh(n,t)&&(e=!0);for(let[n,i]of r.terminalAt)t-i<=this.retentionMs||(r.jobs.delete(n),r.terminalAt.delete(n),e=!0);r.jobs.size===0&&this.sessions.delete(n)}return e}track(e){this.trackInSession(O,e)}untrack(e){this.untrackInSession(O,e)}list(){return this.listInSession(O)}get(e){return this.getInSession(O,e)}reset(){this.resetSession(O)}trackInSession(e,t){this.refresh(e,t)}untrackInSession(e,t){let n=this.sessions.get(e);n?.jobs.delete(t),n?.terminalAt.delete(t),n?.jobs.size===0&&this.sessions.delete(e)}listInSession(e){return[...this.sessions.get(e)?.jobs.values()??[]]}getInSession(e,t){return this.sessions.get(e)?.jobs.get(t)}resetSession(e){this.sessions.delete(e)}start(){this.stop(),this.unregisterPoll=this.scheduler.register({id:D,intervalMs:this.pollIntervalMs,run:()=>this.run()})}stop(){this.unregisterPoll?.(),this.unregisterPoll=void 0,this.sessions.clear()}},A=t([v(),s(0,_(r.PollScheduler)),a(`design:paramtypes`,[Object])],A)}));j();export{A as AsyncJobTracker,w as TERMINAL_ASYNC_JOB_STATES,j as init_asyncJobTracker,y as resolveTrackedRunId};
2
+ //# sourceMappingURL=asyncJobTracker.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"asyncJobTracker.mjs","names":[],"sources":["../../../src/runs/background/asyncJobTracker.ts"],"sourcesContent":["/**\n * Live, in-memory registry of the async runs this session currently cares\n * about, refreshed from `status.json` on a `PollScheduler` tick.\n *\n * WHY THIS IS SMALLER THAN THE PREDECESSOR:\n * `doom-pi-subagents/src/runs/background/asyncJobTracker.ts` is 590 lines\n * doing four different jobs: this registry, TUI widget rendering\n * (`renderWidget`/`widgetRenderKey`/`ctx.ui.requestRender`), forwarding\n * control events and steering notices by byte-range-tailing each run's\n * `events.jsonl`, and projecting nested/parallel-group run trees. Only the\n * registry is ported. The other three have no consumer in this package yet:\n * TUI rendering is `G8` (slash commands + TUI), and nested/chain/parallel run\n * modes are `G7`+ (this package is currently single-run async-only - see\n * `spawnHandshake.ts`'s header). Porting rendering or event-tailing logic\n * with nothing to render to or forward to would be untested, unreachable\n * code; it is deferred to whichever later port actually needs it, not\n * dropped silently - this note is where to look for it.\n *\n * DESIGN PATTERNS:\n * - The in-memory map is a pure CACHE, never authoritative on its own:\n * `status.json` (owned by `CoalescedStatusWriter` elsewhere) is the only\n * source of truth. `track()` reads it synchronously immediately, so a\n * caller that already knows a run's id sees *something* right away rather\n * than waiting out the first poll\n * - Restart survivability follows from the point above rather than a second\n * persistence layer: this package has not built session-scoped run\n * ownership yet (which ids \"belong\" to a restarted session is not\n * something this module can determine on its own - see the AVOID entry),\n * so recovery is the caller re-`track()`-ing whatever ids it still cares\n * about from ITS OWN durable record. The very next poll tick repopulates\n * that job's status straight from disk. There is no separate store to keep\n * in sync, and therefore no window where memory could diverge from disk\n * that a restart would not immediately close\n * - Registered with `PollScheduler`, not a private timer: this polls\n * potentially many jobs' status files on an unbounded, periodic cadence\n * with no low-latency requirement - the exact shape `PollScheduler` exists\n * for. Contrast `completionBatcher.ts`/`spawnHandshake.ts`, which own\n * their own timers because they are short-lived, single-shot, and need\n * sub-200ms responsiveness `PollScheduler`'s floor cannot give them; this\n * registry needs neither\n * - Retention-then-eviction happens on the SAME poll tick as the status\n * refresh, rather than the predecessor's per-job `setTimeout`\n * (`scheduleCleanup`): a terminal job aged past `retentionMs` is simply\n * dropped the next time `run()` walks the map. One clock, not one timer per\n * job\n *\n * AVOID:\n * - Scanning `currentRunsDir()` to guess which run ids \"belong\" to this session on\n * startup. This package has no session-scoped run ownership concept yet;\n * inventing one here to answer \"what should I restore\" would be a guess,\n * not a fact this module has standing to assert. Restoration is the\n * caller's job (see the design note above)\n * - Growing `terminalStates` independently of `staleRunReconciler.ts`'s own\n * (unexported) copy; both must treat the same state strings as terminal,\n * or a job could be evicted here while the reconciler still thinks it is\n * in flight, or vice versa\n *\n * `activityState`/`attentionReason` (added for `subagentWait.ts`):\n * `DeliverableGuard.evaluate()` computes `activityState: 'needs_attention'`\n * and a `reason` (e.g. `'missing-deliverable'`), but returns it rather than\n * writing it anywhere - it has no standing to touch `status.json` itself (see\n * that module's header). Something upstream of this tracker - the run's own\n * runner main loop, not yet built - is responsible for folding that result\n * into `status.json` through `CoalescedStatusWriter`. This tracker's job is\n * only to read whatever ends up there, the same as every other field: it does\n * not compute attention, detect it, or decide when a run needs it.\n */\n\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport { inject, injectable } from 'inversify';\n\nimport type { ActivityState } from '../../types.ts';\nimport { currentRunsDir } from '../../shared/paths.ts';\nimport { DOOM_TEAM_TYPES } from '../../container/types.ts';\nimport type { IPollScheduler } from '../../shared/pollScheduler.ts';\nimport { parseAsyncRunStatus } from './statusReader.ts';\nimport { STATUS_FILE_NAME } from './statusWriter.ts';\n\n/**\n * States that mean \"already finished\"; matches `staleRunReconciler.ts`'s\n * own (unexported) set. Exported so a caller outside this module (the slash\n * layer's poll-until-terminal helper) can recognize the same terminal set\n * `TrackedAsyncJob.status` uses, rather than declaring a third copy of this\n * literal.\n */\nexport const TERMINAL_ASYNC_JOB_STATES = new Set(['complete', 'completed', 'failed', 'paused', 'stopped']);\n\n/** How long a terminal job stays queryable before eviction. */\nconst DEFAULT_RETENTION_MS = 10_000;\n/** How often tracked jobs' status is refreshed from disk. */\nconst DEFAULT_POLL_INTERVAL_MS = 250;\n\nconst POLL_SUBSCRIBER_ID = 'async-job-tracker';\n\n/**\n * The minimum shape this module reads. A run's actual `status.json` carries\n * far more (this package has no shared status type yet - see\n * `staleRunReconciler.ts`'s `ReconcilableStatus` for the same caveat);\n * everything else is simply not read here.\n */\nexport interface TrackedAsyncJob {\n runId: string;\n /** Named agent executing this run. Absent until the first readable status. */\n agent?: string;\n /** `status.json`'s `state` field. `undefined` until the first successful read. */\n status: string | undefined;\n startedAt?: number;\n updatedAt?: number;\n error?: string;\n /** `status.json`'s `activityState` field, e.g. `'needs_attention'`. See the module doc. */\n activityState?: ActivityState;\n /** `status.json`'s `reason` field, present alongside `activityState` (e.g. `'missing-deliverable'`). */\n attentionReason?: string;\n /** Which runtime is executing this run. Absent means the default `pi` path. */\n runtime?: string;\n tokens?: number;\n currentTool?: string;\n toolCount?: number;\n}\n\nexport interface ITrackedAsyncJobs {\n /**\n * Start tracking `runId`: reads its current status synchronously (so a\n * caller sees something immediately) and adds it to the poll rotation.\n * Calling this again for an id already tracked simply re-reads it now,\n * the same idempotent-refresh idiom as `CoalescedStatusWriter.open()`.\n */\n track(runId: string): void;\n /** Stop tracking `runId` immediately, without waiting out its retention window. */\n untrack(runId: string): void;\n /** Every currently tracked job (in-flight, or terminal within its retention window). */\n list(): TrackedAsyncJob[];\n get(runId: string): TrackedAsyncJob | undefined;\n /** Drop every tracked job. */\n reset(): void;\n}\n\nexport interface IAsyncJobTracker extends ITrackedAsyncJobs {\n /** Return the isolated run collection owned by one Pi session context. */\n forSession(sessionId: string): ITrackedAsyncJobs;\n /** Register with `PollScheduler`. Call once; a second call is a reset, same as `ResultWatcher.start()`. */\n start(): void;\n /** Unregister from `PollScheduler` and drop every tracked job. */\n stop(): void;\n}\n\n/** Resolve an exact id or unique prefix only inside one Pi session's run collection. */\nexport function resolveTrackedRunId(jobs: ITrackedAsyncJobs, id: string): string {\n const matches = jobs\n .list()\n .map((job) => job.runId)\n .filter((runId) => runId === id || runId.startsWith(id));\n const exact = matches.find((runId) => runId === id);\n if (exact) return exact;\n if (matches.length === 1) return matches[0]!;\n if (matches.length === 0) throw new Error(`No current-session run found for '${id}'.`);\n throw new Error(`Multiple current-session runs match '${id}'. Use a longer id.`);\n}\n\ninterface SessionJobs {\n jobs: Map<string, TrackedAsyncJob>;\n terminalAt: Map<string, number>;\n}\n\nconst UNSCOPED_SESSION = '__unscoped__';\n\nfunction statusPathFor(runId: string): string {\n return path.join(currentRunsDir(), runId, STATUS_FILE_NAME);\n}\n\nfunction errorCode(error: unknown): string | undefined {\n return typeof error === 'object' && error !== null && 'code' in error\n ? (error as NodeJS.ErrnoException).code\n : undefined;\n}\n\nfunction isNotFound(error: unknown): boolean {\n return errorCode(error) === 'ENOENT';\n}\n\ninterface ReadStatusResult {\n agent: string;\n status: string | undefined;\n startedAt?: number;\n updatedAt?: number;\n error?: string;\n activityState?: ActivityState;\n attentionReason?: string;\n runtime?: string;\n tokens?: number;\n currentTool?: string;\n toolCount?: number;\n}\n\nconst VALID_ACTIVITY_STATES: ReadonlySet<string> = new Set<ActivityState>([\n 'starting',\n 'working',\n 'tool',\n 'waiting_for_reply',\n 'needs_attention',\n 'finalizing',\n 'active_long_running',\n]);\n\nfunction asActivityState(value: unknown): ActivityState | undefined {\n return typeof value === 'string' && VALID_ACTIVITY_STATES.has(value) ? (value as ActivityState) : undefined;\n}\n\n@injectable()\nexport class AsyncJobTracker implements IAsyncJobTracker {\n constructor(@inject(DOOM_TEAM_TYPES.PollScheduler) private readonly scheduler: IPollScheduler) {}\n\n /**\n * Seams for tests, as protected members rather than constructor arguments:\n * inversify cannot resolve a primitive. See `pollScheduler.ts` and\n * `agents/discovery.ts` for the same pattern.\n */\n protected readonly pollIntervalMs: number = DEFAULT_POLL_INTERVAL_MS;\n protected readonly retentionMs: number = DEFAULT_RETENTION_MS;\n\n protected now(): number {\n return Date.now();\n }\n\n protected readFile(filePath: string): string | undefined {\n try {\n return fs.readFileSync(filePath, 'utf-8');\n } catch (error) {\n if (isNotFound(error)) return undefined;\n throw error;\n }\n }\n\n private readonly sessions = new Map<string, SessionJobs>();\n private unregisterPoll: (() => void) | undefined;\n\n private session(sessionId: string): SessionJobs {\n let session = this.sessions.get(sessionId);\n if (!session) {\n session = { jobs: new Map(), terminalAt: new Map() };\n this.sessions.set(sessionId, session);\n }\n return session;\n }\n\n forSession(sessionId: string): ITrackedAsyncJobs {\n if (!sessionId.trim()) throw new Error('Pi session identity is required to track subagent runs.');\n return {\n track: (runId) => this.trackInSession(sessionId, runId),\n untrack: (runId) => this.untrackInSession(sessionId, runId),\n list: () => this.listInSession(sessionId),\n get: (runId) => this.getInSession(sessionId, runId),\n reset: () => this.resetSession(sessionId),\n };\n }\n\n private readStatus(runId: string): ReadStatusResult | undefined {\n const raw = this.readFile(statusPathFor(runId));\n if (raw === undefined) return undefined;\n const result = parseAsyncRunStatus(raw, statusPathFor(runId));\n if (result.kind !== 'ok') return undefined;\n const status = result.status;\n return {\n agent: status.agent,\n status: status.state,\n startedAt: status.startedAt,\n updatedAt: status.lastUpdate,\n error: status.error,\n activityState: asActivityState(status.activityState),\n attentionReason: status.attentionReason,\n runtime: status.runtime,\n tokens: status.tokens,\n currentTool: status.currentTool,\n toolCount: status.toolCount,\n };\n }\n\n private refresh(sessionId: string, runId: string): boolean {\n const session = this.session(sessionId);\n const existing = session.jobs.get(runId);\n const read = this.readStatus(runId);\n if (read === undefined) {\n // Not readable yet (the run has not started) or a transient torn/\n // mid-write read (see readStatus's own comment): neither is evidence\n // to overwrite whatever was already known, so an existing entry is\n // left exactly as it was rather than blanked out for one tick.\n if (existing) return false;\n session.jobs.set(runId, { runId, status: undefined });\n return false;\n }\n\n const job: TrackedAsyncJob = { runId, ...read };\n session.jobs.set(runId, job);\n\n const wasTerminal = existing?.status !== undefined && TERMINAL_ASYNC_JOB_STATES.has(existing.status);\n const isTerminal = job.status !== undefined && TERMINAL_ASYNC_JOB_STATES.has(job.status);\n if (isTerminal && !wasTerminal) session.terminalAt.set(runId, this.now());\n if (!isTerminal) session.terminalAt.delete(runId);\n\n return (\n !existing ||\n existing.status !== job.status ||\n existing.updatedAt !== job.updatedAt ||\n existing.tokens !== job.tokens ||\n existing.currentTool !== job.currentTool ||\n existing.toolCount !== job.toolCount\n );\n }\n\n /** One scheduler tick: refresh every tracked job's status, then evict anything past its retention window. */\n protected run(): boolean {\n let changed = false;\n const now = this.now();\n for (const [sessionId, session] of this.sessions) {\n for (const runId of session.jobs.keys()) {\n if (this.refresh(sessionId, runId)) changed = true;\n }\n for (const [runId, terminatedAt] of session.terminalAt) {\n if (now - terminatedAt <= this.retentionMs) continue;\n session.jobs.delete(runId);\n session.terminalAt.delete(runId);\n changed = true;\n }\n if (session.jobs.size === 0) this.sessions.delete(sessionId);\n }\n\n return changed;\n }\n\n track(runId: string): void {\n this.trackInSession(UNSCOPED_SESSION, runId);\n }\n\n untrack(runId: string): void {\n this.untrackInSession(UNSCOPED_SESSION, runId);\n }\n\n list(): TrackedAsyncJob[] {\n return this.listInSession(UNSCOPED_SESSION);\n }\n\n get(runId: string): TrackedAsyncJob | undefined {\n return this.getInSession(UNSCOPED_SESSION, runId);\n }\n\n reset(): void {\n this.resetSession(UNSCOPED_SESSION);\n }\n\n private trackInSession(sessionId: string, runId: string): void {\n this.refresh(sessionId, runId);\n }\n\n private untrackInSession(sessionId: string, runId: string): void {\n const session = this.sessions.get(sessionId);\n session?.jobs.delete(runId);\n session?.terminalAt.delete(runId);\n if (session?.jobs.size === 0) this.sessions.delete(sessionId);\n }\n\n private listInSession(sessionId: string): TrackedAsyncJob[] {\n return [...(this.sessions.get(sessionId)?.jobs.values() ?? [])];\n }\n\n private getInSession(sessionId: string, runId: string): TrackedAsyncJob | undefined {\n return this.sessions.get(sessionId)?.jobs.get(runId);\n }\n\n private resetSession(sessionId: string): void {\n this.sessions.delete(sessionId);\n }\n\n start(): void {\n this.stop();\n this.unregisterPoll = this.scheduler.register({\n id: POLL_SUBSCRIBER_ID,\n intervalMs: this.pollIntervalMs,\n run: () => this.run(),\n });\n }\n\n stop(): void {\n this.unregisterPoll?.();\n this.unregisterPoll = undefined;\n this.sessions.clear();\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ const e=require(`../../_virtual/_rolldown/runtime.cjs`),t=require(`../../shared/atomicJson.cjs`),n=require(`../../env.cjs`),r=require(`../../shared/paths.cjs`),i=require(`./statusWriter.cjs`),a=require(`./statusReader.cjs`),o=require(`../shared/claudeFableProfile.cjs`),s=require(`./parentWatchdog.cjs`);let c=require(`node:fs`);c=e.__toESM(c,1);let l=require(`@agimon-ai/doompi-telemetry`),u=require(`node:child_process`),d=require(`@agimon-ai/doompi-extension-contracts/child-process`);const f=`Parent process was lost.`,p=`SIGKILL`;function m(e){return e instanceof Error?e.message:String(e)}function h(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function g(e){let t=JSON.parse(c.readFileSync(r.currentRunConfigPath(e),`utf8`));if(!h(t)||t.runId!==e||typeof t.command!=`string`)throw Error(`CLI launch config for run '${e}' is invalid.`);return t}function _(e,t,n){let r=e+t;for(;Buffer.byteLength(r,`utf8`)>n&&r.length>0;){let e=Buffer.byteLength(r,`utf8`)-n;r=r.slice(Math.max(1,Math.ceil(e/2)))}return r}let v;async function y(e,t,n){let r=v;v=void 0,r&&(n===void 0?await r.recordEvent(e,t):await r.recordError(e,n,t),await r.flush(),await r.shutdown())}async function b(){let e=Date.now();v=(0,l.createDoomTelemetry)({serviceName:`doom-team-cli-child`,packageName:`@agimon-ai/doompi-team`,env:process.env,enableLogs:!0,enableTraces:!0});let h=process.env[n.SUBAGENT_RUN_ID_ENV]?.trim();if(!h)throw Error(`${n.SUBAGENT_RUN_ID_ENV} is required.`);if(!r.adoptSessionScopeFromEnv())throw Error(`${d.SUBAGENT_ROOT_SESSION_ENV} is required: a child cannot resolve its session scope without it.`);let b;try{b=g(h)}catch(e){throw b?.handshakePath&&!c.existsSync(b.handshakePath)&&t.writeAtomicJson(b.handshakePath,{state:`error`,error:m(e)}),e}let x=new i.CoalescedStatusWriter,S=Date.now();await v?.recordEvent(`doom_team.child_started`,{runtime:b.runtime,"agent.name":b.agent,outcome:`started`}),x.open(h,a.readAsyncRunStatus(h)??{version:1,runId:h,...b.operationId?{operationId:b.operationId}:{},agent:b.agent,cwd:b.cwd,runtime:b.runtime,state:`queued`,startedAt:S,lastUpdate:S});let C=b.profile===o.CLAUDE_FABLE_PROFILE;if(b.profile!==void 0&&!C)throw Error(`Untrusted external profile.`);if(C&&(!b.stdinPath||!b.profileResultPath))throw Error(`Fable profile launch config is incomplete.`);let w=C?c.openSync(b.stdinPath,`r`):void 0,T;try{T=(0,u.spawn)(b.command,b.args,{cwd:b.cwd,env:C?b.env:{...process.env,...b.env},stdio:[w??`ignore`,`pipe`,`pipe`],detached:!0,windowsHide:!0})}catch(e){throw C&&(o.cleanupClaudeFableLaunch({cleanupPaths:b.cleanupPaths??[]}),b.profileResultPath&&c.rmSync(b.profileResultPath,{force:!0})),e}finally{w!==void 0&&c.closeSync(w)}let E=e=>{C&&(o.cleanupClaudeFableLaunch({cleanupPaths:b.cleanupPaths??[]}),e&&b.profileResultPath&&c.rmSync(b.profileResultPath,{force:!0}))},D,O=e=>{if(D=e,T.pid)try{process.kill(-T.pid,e)}catch(e){process.stderr.write(`[doom-team cli runner] Could not signal vendor process: ${m(e)}\n`)}};process.once(`SIGTERM`,O),process.once(`SIGINT`,O);try{await new Promise((e,t)=>{T.once(`spawn`,e),T.once(`error`,t)}),t.writeAtomicJson(b.handshakePath,{state:`ready`}),x.updateSync(e=>{e.state=`running`,e.activityState=`working`,e.lastUpdate=Date.now()})}catch(n){let r=m(n);t.writeAtomicJson(b.handshakePath,{state:`error`,error:r}),x.updateSync(e=>{e.state=`failed`,e.error=r,e.lastUpdate=Date.now(),e.endedAt=Date.now()}),t.writeAtomicJson(b.resultPath,{runId:h,...b.internal?{internal:!0}:{},...b.operationId?{operationId:b.operationId}:{},agent:b.agent,runtime:b.runtime,success:!1,summary:C?`Fable Claude launch failed.`:r}),x.close(),E(!0),await y(`doom_team.child_failed`,{runtime:b.runtime,"agent.name":b.agent,duration_ms:Date.now()-e,outcome:`failed`},n),process.exit(1)}let k=s.startParentWatchdog({onParentLost:()=>{O(p);let n=Date.now();x.updateSync(e=>{e.state=`stopped`,e.error=f,e.lastUpdate=n,e.endedAt=n}),t.writeAtomicJson(b.resultPath,{runId:h,...b.internal?{internal:!0}:{},...b.operationId?{operationId:b.operationId}:{},agent:b.agent,runtime:b.runtime,success:!1,summary:f,signal:p}),x.close(),E(!0),y(`doom_team.child_finished`,{runtime:b.runtime,"agent.name":b.agent,duration_ms:Date.now()-e,outcome:`parent_lost`}).finally(()=>process.exit(0))}}),A=``,j=``;T.stdout?.on(`data`,e=>{A=_(A,e.toString(`utf8`),65536)}),T.stderr?.on(`data`,e=>{j=_(j,e.toString(`utf8`),8192)});let M=await new Promise(e=>{T.on(`error`,t=>e({code:null,signal:null,error:t})),T.on(`close`,(t,n)=>e({code:t,signal:n}))});k();let N=D!==void 0,P;if(C&&!N&&M.error===void 0&&M.code===0)try{let e=o.parseClaudeFableOutput(A);t.writePrivateAtomicJson(b.profileResultPath,{text:e.text,outputBytes:e.outputBytes})}catch(e){P=e instanceof Error?e:Error(String(e))}let F=!N&&M.error===void 0&&M.code===0&&P===void 0,I=C?P?.message??`Fable Claude launch failed.`:j.trim()||`${b.runtime} exited with ${M.signal??`code ${M.code??`unknown`}`}.`,L=N?`Stopped by ${D}.`:F?C?`Fable profile completed.`:A.trim():M.error?.message??I,R=Date.now();x.updateSync(e=>{e.state=N?`stopped`:F?`completed`:`failed`,e.activityState=`finalizing`,e.summary=L,F||(e.error=L),e.lastUpdate=R,e.endedAt=R}),t.writeAtomicJson(b.resultPath,{runId:h,...b.internal?{internal:!0}:{},...b.operationId?{operationId:b.operationId}:{},agent:b.agent,runtime:b.runtime,success:F,summary:L,...M.code===null?{}:{exitCode:M.code},...M.signal?{signal:M.signal}:{}}),x.close(),E(!F),await y(`doom_team.child_finished`,{runtime:b.runtime,"agent.name":b.agent,duration_ms:Date.now()-e,"runner.stdout_bytes":Buffer.byteLength(A,`utf8`),"runner.stderr_bytes":Buffer.byteLength(j,`utf8`),"runner.exit_code":M.code??0,outcome:N?`stopped`:F?`completed`:`failed`}),process.exit(0)}b().catch(async e=>{await y(`doom_team.child_failed`,{outcome:`failed`},e),process.stderr.write(`[doom-team cli runner] ${m(e)}\n`),process.exitCode=1});
2
+ //# sourceMappingURL=cliRunnerEntry.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cliRunnerEntry.cjs","names":["fs","currentRunConfigPath","SUBAGENT_RUN_ID_ENV","adoptSessionScopeFromEnv","SUBAGENT_ROOT_SESSION_ENV","CoalescedStatusWriter","readAsyncRunStatus","CLAUDE_FABLE_PROFILE","startParentWatchdog","parseClaudeFableOutput"],"sources":["../../../src/runs/background/cliRunnerEntry.ts"],"sourcesContent":["/**\n * The detached child process for an external agent CLI.\n *\n * Counterpart to `sdkRunnerEntry.ts`, which does the same job for the\n * in-process Pi runtime. Both are spawned by `AsyncSubagentSpawner`, both write\n * the same `status.json` and the same terminal result, and both signal the same\n * handshake - so `AsyncJobTracker`, `ResultWatcher`, the fleet view and\n * folded `subagent` wait/status handling cannot tell them apart, which is the point.\n *\n * WHAT AN EXTERNAL CHILD DOES NOT GET, STATED PLAINLY:\n * No intercom, no steering, no structured output. Those\n * are delivered by `subagentPromptRuntime.ts`, which Pi loads in-process into\n * a Pi child; an arbitrary CLI has nowhere to load it. Layer tool exclusions\n * are therefore best effort for external runtimes. An external child is a\n * one-shot worker: it receives a prompt and returns output. Stop and interrupt\n * still work, because they are process-group signals rather than cooperative\n * file requests.\n *\n * WHY THE OUTPUT IS RECORDED, NOT PARSED:\n * A generic runner cannot know what a given CLI's output means. Trailing stdout\n * is the result; anything more specific would either be wrong for a CLI nobody\n * tested or lock the config to the two that were.\n *\n * AVOID:\n * - Vendor-specific parsing here. If a runtime needs it, that is a different\n * module and a deliberate decision\n */\n\nimport { spawn } from 'node:child_process';\nimport * as fs from 'node:fs';\n\nimport { createDoomTelemetry, type DoomTelemetry } from '@agimon-ai/doompi-telemetry';\nimport { SUBAGENT_ROOT_SESSION_ENV, SUBAGENT_RUN_ID_ENV } from '../../env.ts';\nimport { adoptSessionScopeFromEnv, currentRunConfigPath } from '../../shared/paths.ts';\nimport { writeAtomicJson, writePrivateAtomicJson } from '../../shared/atomicJson.ts';\nimport type { AsyncRunStatus } from './asyncExecution.ts';\nimport { startParentWatchdog } from './parentWatchdog.ts';\nimport type { CliLaunchConfig } from './cliLaunchConfig.ts';\nimport { readAsyncRunStatus } from './statusReader.ts';\nimport { CoalescedStatusWriter } from './statusWriter.ts';\nimport {\n CLAUDE_FABLE_PROFILE,\n cleanupClaudeFableLaunch,\n parseClaudeFableOutput,\n} from '../shared/claudeFableProfile.ts';\n\n/** How much trailing stdout is kept as the run's result. */\nconst MAX_RESULT_BYTES = 64 * 1024;\n/** How much stderr is kept for a failure message. */\nconst MAX_STDERR_BYTES = 8 * 1024;\nconst PARENT_LOST_MESSAGE = 'Parent process was lost.';\nconst FORCE_KILL_SIGNAL: NodeJS.Signals = 'SIGKILL';\n\nfunction errorMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction readLaunchConfig(runId: string): CliLaunchConfig {\n const parsed: unknown = JSON.parse(fs.readFileSync(currentRunConfigPath(runId), 'utf8'));\n if (!isRecord(parsed) || parsed.runId !== runId || typeof parsed.command !== 'string') {\n throw new Error(`CLI launch config for run '${runId}' is invalid.`);\n }\n return parsed as unknown as CliLaunchConfig;\n}\n\n/** Keep only a UTF-8-safe trailing value within `limit` bytes, so a chatty CLI cannot exhaust memory. */\nfunction appendBounded(buffer: string, chunk: string, limit: number): string {\n let combined = buffer + chunk;\n while (Buffer.byteLength(combined, 'utf8') > limit && combined.length > 0) {\n const excess = Buffer.byteLength(combined, 'utf8') - limit;\n combined = combined.slice(Math.max(1, Math.ceil(excess / 2)));\n }\n return combined;\n}\n\nlet childTelemetry: DoomTelemetry | undefined;\n\nasync function finishTelemetry(event: string, attributes: Record<string, unknown>, error?: unknown): Promise<void> {\n const telemetry = childTelemetry;\n childTelemetry = undefined;\n if (!telemetry) return;\n if (error === undefined) await telemetry.recordEvent(event, attributes);\n else await telemetry.recordError(event, error, attributes);\n await telemetry.flush();\n await telemetry.shutdown();\n}\n\nasync function run(): Promise<void> {\n const startedAt = Date.now();\n childTelemetry = createDoomTelemetry({\n serviceName: 'doom-team-cli-child',\n packageName: '@agimon-ai/doompi-team',\n env: process.env,\n enableLogs: true,\n enableTraces: true,\n });\n const runId = process.env[SUBAGENT_RUN_ID_ENV]?.trim();\n if (!runId) throw new Error(`${SUBAGENT_RUN_ID_ENV} is required.`);\n if (!adoptSessionScopeFromEnv()) {\n throw new Error(`${SUBAGENT_ROOT_SESSION_ENV} is required: a child cannot resolve its session scope without it.`);\n }\n\n let config: CliLaunchConfig | undefined;\n try {\n config = readLaunchConfig(runId);\n // The vendor process emits its own spawn event before the ready handshake\n // is written below. Reading the config alone is not sufficient evidence\n // that the configured executable can start.\n } catch (error) {\n if (config?.handshakePath && !fs.existsSync(config.handshakePath)) {\n writeAtomicJson(config.handshakePath, { state: 'error', error: errorMessage(error) });\n }\n throw error;\n }\n\n const statusWriter = new CoalescedStatusWriter<AsyncRunStatus>();\n const now = Date.now();\n await childTelemetry?.recordEvent('doom_team.child_started', {\n runtime: config.runtime,\n 'agent.name': config.agent,\n outcome: 'started',\n });\n statusWriter.open(\n runId,\n readAsyncRunStatus(runId) ?? {\n version: 1,\n runId,\n ...(config.operationId ? { operationId: config.operationId } : {}),\n agent: config.agent,\n cwd: config.cwd,\n runtime: config.runtime,\n state: 'queued',\n startedAt: now,\n lastUpdate: now,\n },\n );\n\n const profileLaunch = config.profile === CLAUDE_FABLE_PROFILE;\n if (config.profile !== undefined && !profileLaunch) throw new Error('Untrusted external profile.');\n if (profileLaunch && (!config.stdinPath || !config.profileResultPath)) {\n throw new Error('Fable profile launch config is incomplete.');\n }\n const stdinFd = profileLaunch ? fs.openSync(config.stdinPath!, 'r') : undefined;\n let child: ReturnType<typeof spawn>;\n try {\n child = spawn(config.command, config.args, {\n cwd: config.cwd,\n env: profileLaunch ? config.env : { ...process.env, ...config.env },\n stdio: [stdinFd ?? 'ignore', 'pipe', 'pipe'],\n // Its own group, so the watchdog below can take the CLI and anything it\n // spawned down together.\n detached: true,\n windowsHide: true,\n });\n } catch (error) {\n if (profileLaunch) {\n cleanupClaudeFableLaunch({ cleanupPaths: config.cleanupPaths ?? [] });\n if (config.profileResultPath) fs.rmSync(config.profileResultPath, { force: true });\n }\n throw error;\n } finally {\n if (stdinFd !== undefined) fs.closeSync(stdinFd);\n }\n const cleanupProfile = (removeResult: boolean): void => {\n if (!profileLaunch) return;\n cleanupClaudeFableLaunch({ cleanupPaths: config.cleanupPaths ?? [] });\n if (removeResult && config.profileResultPath) fs.rmSync(config.profileResultPath, { force: true });\n };\n\n let requestedSignal: NodeJS.Signals | undefined;\n const stopVendor = (signal: NodeJS.Signals): void => {\n requestedSignal = signal;\n if (!child.pid) return;\n try {\n process.kill(-child.pid, signal);\n } catch (error) {\n process.stderr.write(`[doom-team cli runner] Could not signal vendor process: ${errorMessage(error)}\\n`);\n }\n };\n process.once('SIGTERM', stopVendor);\n process.once('SIGINT', stopVendor);\n\n try {\n await new Promise<void>((resolve, reject) => {\n child.once('spawn', resolve);\n child.once('error', reject);\n });\n writeAtomicJson(config.handshakePath, { state: 'ready' });\n statusWriter.updateSync((status) => {\n status.state = 'running';\n status.activityState = 'working';\n status.lastUpdate = Date.now();\n });\n } catch (error) {\n const message = errorMessage(error);\n writeAtomicJson(config.handshakePath, { state: 'error', error: message });\n statusWriter.updateSync((status) => {\n status.state = 'failed';\n status.error = message;\n status.lastUpdate = Date.now();\n status.endedAt = Date.now();\n });\n writeAtomicJson(config.resultPath, {\n runId,\n ...(config.internal ? { internal: true } : {}),\n ...(config.operationId ? { operationId: config.operationId } : {}),\n agent: config.agent,\n runtime: config.runtime,\n success: false,\n summary: profileLaunch ? 'Fable Claude launch failed.' : message,\n });\n statusWriter.close();\n cleanupProfile(true);\n await finishTelemetry(\n 'doom_team.child_failed',\n { runtime: config.runtime, 'agent.name': config.agent, duration_ms: Date.now() - startedAt, outcome: 'failed' },\n error,\n );\n process.exit(1);\n }\n\n // A SIGKILLed parent cannot signal us, so terminate the vendor tree and\n // persist a terminal record before leaving when the watchdog can observe it.\n const stopWatchdog = startParentWatchdog({\n onParentLost: () => {\n stopVendor(FORCE_KILL_SIGNAL);\n const endedAt = Date.now();\n statusWriter.updateSync((status) => {\n status.state = 'stopped';\n status.error = PARENT_LOST_MESSAGE;\n status.lastUpdate = endedAt;\n status.endedAt = endedAt;\n });\n writeAtomicJson(config.resultPath, {\n runId,\n ...(config.internal ? { internal: true } : {}),\n ...(config.operationId ? { operationId: config.operationId } : {}),\n agent: config.agent,\n runtime: config.runtime,\n success: false,\n summary: PARENT_LOST_MESSAGE,\n signal: FORCE_KILL_SIGNAL,\n });\n statusWriter.close();\n cleanupProfile(true);\n void finishTelemetry('doom_team.child_finished', {\n runtime: config.runtime,\n 'agent.name': config.agent,\n duration_ms: Date.now() - startedAt,\n outcome: 'parent_lost',\n }).finally(() => process.exit(0));\n },\n });\n\n let stdout = '';\n let stderr = '';\n child.stdout?.on('data', (chunk: Buffer) => {\n stdout = appendBounded(stdout, chunk.toString('utf8'), MAX_RESULT_BYTES);\n });\n child.stderr?.on('data', (chunk: Buffer) => {\n stderr = appendBounded(stderr, chunk.toString('utf8'), MAX_STDERR_BYTES);\n });\n\n const exit = await new Promise<{ code: number | null; signal: NodeJS.Signals | null; error?: Error }>((resolve) => {\n child.on('error', (error) => resolve({ code: null, signal: null, error }));\n child.on('close', (code, signal) => resolve({ code, signal }));\n });\n\n stopWatchdog();\n\n const stopped = requestedSignal !== undefined;\n let profileError: Error | undefined;\n if (profileLaunch && !stopped && exit.error === undefined && exit.code === 0) {\n try {\n const parsed = parseClaudeFableOutput(stdout);\n writePrivateAtomicJson(config.profileResultPath!, { text: parsed.text, outputBytes: parsed.outputBytes });\n } catch (error) {\n profileError = error instanceof Error ? error : new Error(String(error));\n }\n }\n const success = !stopped && exit.error === undefined && exit.code === 0 && profileError === undefined;\n const failureDetail = profileLaunch\n ? (profileError?.message ?? 'Fable Claude launch failed.')\n : stderr.trim() || `${config.runtime} exited with ${exit.signal ?? `code ${exit.code ?? 'unknown'}`}.`;\n const summary = stopped\n ? `Stopped by ${requestedSignal}.`\n : success\n ? profileLaunch\n ? 'Fable profile completed.'\n : stdout.trim()\n : (exit.error?.message ?? failureDetail);\n const endedAt = Date.now();\n\n statusWriter.updateSync((status) => {\n status.state = stopped ? 'stopped' : success ? 'completed' : 'failed';\n status.activityState = 'finalizing';\n status.summary = summary;\n if (!success) status.error = summary;\n status.lastUpdate = endedAt;\n status.endedAt = endedAt;\n });\n writeAtomicJson(config.resultPath, {\n runId,\n ...(config.internal ? { internal: true } : {}),\n ...(config.operationId ? { operationId: config.operationId } : {}),\n agent: config.agent,\n runtime: config.runtime,\n success,\n summary,\n ...(exit.code === null ? {} : { exitCode: exit.code }),\n ...(exit.signal ? { signal: exit.signal } : {}),\n });\n\n statusWriter.close();\n cleanupProfile(!success);\n await finishTelemetry('doom_team.child_finished', {\n runtime: config.runtime,\n 'agent.name': config.agent,\n duration_ms: Date.now() - startedAt,\n 'runner.stdout_bytes': Buffer.byteLength(stdout, 'utf8'),\n 'runner.stderr_bytes': Buffer.byteLength(stderr, 'utf8'),\n 'runner.exit_code': exit.code ?? 0,\n outcome: stopped ? 'stopped' : success ? 'completed' : 'failed',\n });\n // The runner exit code reports transport health. Task success or failure is\n // encoded durably in status and result data before this process exits.\n process.exit(0);\n}\n\nvoid run().catch(async (error: unknown) => {\n await finishTelemetry('doom_team.child_failed', { outcome: 'failed' }, error);\n // Keep stdout reserved for vendor output. Stderr is captured in the run's\n // runner log and makes a transport failure diagnosable by the parent.\n process.stderr.write(`[doom-team cli runner] ${errorMessage(error)}\\n`);\n process.exitCode = 1;\n});\n"],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export { };
@@ -0,0 +1 @@
1
+ export { };
@@ -0,0 +1,2 @@
1
+ import{init_atomicJson as e,writeAtomicJson as t,writePrivateAtomicJson as n}from"../../shared/atomicJson.mjs";import{SUBAGENT_ROOT_SESSION_ENV as r,SUBAGENT_RUN_ID_ENV as i,init_env as a}from"../../env.mjs";import{adoptSessionScopeFromEnv as o,currentRunConfigPath as s,init_paths as c}from"../../shared/paths.mjs";import{CoalescedStatusWriter as l,init_statusWriter as u}from"./statusWriter.mjs";import{init_statusReader as d,readAsyncRunStatus as f}from"./statusReader.mjs";import{CLAUDE_FABLE_PROFILE as p,cleanupClaudeFableLaunch as m,init_claudeFableProfile as h,parseClaudeFableOutput as g}from"../shared/claudeFableProfile.mjs";import{startParentWatchdog as _}from"./parentWatchdog.mjs";import*as v from"node:fs";import{createDoomTelemetry as y}from"@agimon-ai/doompi-telemetry";import{spawn as b}from"node:child_process";a(),c(),e(),d(),u(),h();const x=`Parent process was lost.`,S=`SIGKILL`;function C(e){return e instanceof Error?e.message:String(e)}function w(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function T(e){let t=JSON.parse(v.readFileSync(s(e),`utf8`));if(!w(t)||t.runId!==e||typeof t.command!=`string`)throw Error(`CLI launch config for run '${e}' is invalid.`);return t}function E(e,t,n){let r=e+t;for(;Buffer.byteLength(r,`utf8`)>n&&r.length>0;){let e=Buffer.byteLength(r,`utf8`)-n;r=r.slice(Math.max(1,Math.ceil(e/2)))}return r}let D;async function O(e,t,n){let r=D;D=void 0,r&&(n===void 0?await r.recordEvent(e,t):await r.recordError(e,n,t),await r.flush(),await r.shutdown())}async function k(){let e=Date.now();D=y({serviceName:`doom-team-cli-child`,packageName:`@agimon-ai/doompi-team`,env:process.env,enableLogs:!0,enableTraces:!0});let a=process.env[i]?.trim();if(!a)throw Error(`${i} is required.`);if(!o())throw Error(`${r} is required: a child cannot resolve its session scope without it.`);let s;try{s=T(a)}catch(e){throw s?.handshakePath&&!v.existsSync(s.handshakePath)&&t(s.handshakePath,{state:`error`,error:C(e)}),e}let c=new l,u=Date.now();await D?.recordEvent(`doom_team.child_started`,{runtime:s.runtime,"agent.name":s.agent,outcome:`started`}),c.open(a,f(a)??{version:1,runId:a,...s.operationId?{operationId:s.operationId}:{},agent:s.agent,cwd:s.cwd,runtime:s.runtime,state:`queued`,startedAt:u,lastUpdate:u});let d=s.profile===p;if(s.profile!==void 0&&!d)throw Error(`Untrusted external profile.`);if(d&&(!s.stdinPath||!s.profileResultPath))throw Error(`Fable profile launch config is incomplete.`);let h=d?v.openSync(s.stdinPath,`r`):void 0,w;try{w=b(s.command,s.args,{cwd:s.cwd,env:d?s.env:{...process.env,...s.env},stdio:[h??`ignore`,`pipe`,`pipe`],detached:!0,windowsHide:!0})}catch(e){throw d&&(m({cleanupPaths:s.cleanupPaths??[]}),s.profileResultPath&&v.rmSync(s.profileResultPath,{force:!0})),e}finally{h!==void 0&&v.closeSync(h)}let k=e=>{d&&(m({cleanupPaths:s.cleanupPaths??[]}),e&&s.profileResultPath&&v.rmSync(s.profileResultPath,{force:!0}))},A,j=e=>{if(A=e,w.pid)try{process.kill(-w.pid,e)}catch(e){process.stderr.write(`[doom-team cli runner] Could not signal vendor process: ${C(e)}\n`)}};process.once(`SIGTERM`,j),process.once(`SIGINT`,j);try{await new Promise((e,t)=>{w.once(`spawn`,e),w.once(`error`,t)}),t(s.handshakePath,{state:`ready`}),c.updateSync(e=>{e.state=`running`,e.activityState=`working`,e.lastUpdate=Date.now()})}catch(n){let r=C(n);t(s.handshakePath,{state:`error`,error:r}),c.updateSync(e=>{e.state=`failed`,e.error=r,e.lastUpdate=Date.now(),e.endedAt=Date.now()}),t(s.resultPath,{runId:a,...s.internal?{internal:!0}:{},...s.operationId?{operationId:s.operationId}:{},agent:s.agent,runtime:s.runtime,success:!1,summary:d?`Fable Claude launch failed.`:r}),c.close(),k(!0),await O(`doom_team.child_failed`,{runtime:s.runtime,"agent.name":s.agent,duration_ms:Date.now()-e,outcome:`failed`},n),process.exit(1)}let M=_({onParentLost:()=>{j(S);let n=Date.now();c.updateSync(e=>{e.state=`stopped`,e.error=x,e.lastUpdate=n,e.endedAt=n}),t(s.resultPath,{runId:a,...s.internal?{internal:!0}:{},...s.operationId?{operationId:s.operationId}:{},agent:s.agent,runtime:s.runtime,success:!1,summary:x,signal:S}),c.close(),k(!0),O(`doom_team.child_finished`,{runtime:s.runtime,"agent.name":s.agent,duration_ms:Date.now()-e,outcome:`parent_lost`}).finally(()=>process.exit(0))}}),N=``,P=``;w.stdout?.on(`data`,e=>{N=E(N,e.toString(`utf8`),65536)}),w.stderr?.on(`data`,e=>{P=E(P,e.toString(`utf8`),8192)});let F=await new Promise(e=>{w.on(`error`,t=>e({code:null,signal:null,error:t})),w.on(`close`,(t,n)=>e({code:t,signal:n}))});M();let I=A!==void 0,L;if(d&&!I&&F.error===void 0&&F.code===0)try{let e=g(N);n(s.profileResultPath,{text:e.text,outputBytes:e.outputBytes})}catch(e){L=e instanceof Error?e:Error(String(e))}let R=!I&&F.error===void 0&&F.code===0&&L===void 0,z=d?L?.message??`Fable Claude launch failed.`:P.trim()||`${s.runtime} exited with ${F.signal??`code ${F.code??`unknown`}`}.`,B=I?`Stopped by ${A}.`:R?d?`Fable profile completed.`:N.trim():F.error?.message??z,V=Date.now();c.updateSync(e=>{e.state=I?`stopped`:R?`completed`:`failed`,e.activityState=`finalizing`,e.summary=B,R||(e.error=B),e.lastUpdate=V,e.endedAt=V}),t(s.resultPath,{runId:a,...s.internal?{internal:!0}:{},...s.operationId?{operationId:s.operationId}:{},agent:s.agent,runtime:s.runtime,success:R,summary:B,...F.code===null?{}:{exitCode:F.code},...F.signal?{signal:F.signal}:{}}),c.close(),k(!R),await O(`doom_team.child_finished`,{runtime:s.runtime,"agent.name":s.agent,duration_ms:Date.now()-e,"runner.stdout_bytes":Buffer.byteLength(N,`utf8`),"runner.stderr_bytes":Buffer.byteLength(P,`utf8`),"runner.exit_code":F.code??0,outcome:I?`stopped`:R?`completed`:`failed`}),process.exit(0)}k().catch(async e=>{await O(`doom_team.child_failed`,{outcome:`failed`},e),process.stderr.write(`[doom-team cli runner] ${C(e)}\n`),process.exitCode=1});export{};
2
+ //# sourceMappingURL=cliRunnerEntry.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cliRunnerEntry.mjs","names":[],"sources":["../../../src/runs/background/cliRunnerEntry.ts"],"sourcesContent":["/**\n * The detached child process for an external agent CLI.\n *\n * Counterpart to `sdkRunnerEntry.ts`, which does the same job for the\n * in-process Pi runtime. Both are spawned by `AsyncSubagentSpawner`, both write\n * the same `status.json` and the same terminal result, and both signal the same\n * handshake - so `AsyncJobTracker`, `ResultWatcher`, the fleet view and\n * folded `subagent` wait/status handling cannot tell them apart, which is the point.\n *\n * WHAT AN EXTERNAL CHILD DOES NOT GET, STATED PLAINLY:\n * No intercom, no steering, no structured output. Those\n * are delivered by `subagentPromptRuntime.ts`, which Pi loads in-process into\n * a Pi child; an arbitrary CLI has nowhere to load it. Layer tool exclusions\n * are therefore best effort for external runtimes. An external child is a\n * one-shot worker: it receives a prompt and returns output. Stop and interrupt\n * still work, because they are process-group signals rather than cooperative\n * file requests.\n *\n * WHY THE OUTPUT IS RECORDED, NOT PARSED:\n * A generic runner cannot know what a given CLI's output means. Trailing stdout\n * is the result; anything more specific would either be wrong for a CLI nobody\n * tested or lock the config to the two that were.\n *\n * AVOID:\n * - Vendor-specific parsing here. If a runtime needs it, that is a different\n * module and a deliberate decision\n */\n\nimport { spawn } from 'node:child_process';\nimport * as fs from 'node:fs';\n\nimport { createDoomTelemetry, type DoomTelemetry } from '@agimon-ai/doompi-telemetry';\nimport { SUBAGENT_ROOT_SESSION_ENV, SUBAGENT_RUN_ID_ENV } from '../../env.ts';\nimport { adoptSessionScopeFromEnv, currentRunConfigPath } from '../../shared/paths.ts';\nimport { writeAtomicJson, writePrivateAtomicJson } from '../../shared/atomicJson.ts';\nimport type { AsyncRunStatus } from './asyncExecution.ts';\nimport { startParentWatchdog } from './parentWatchdog.ts';\nimport type { CliLaunchConfig } from './cliLaunchConfig.ts';\nimport { readAsyncRunStatus } from './statusReader.ts';\nimport { CoalescedStatusWriter } from './statusWriter.ts';\nimport {\n CLAUDE_FABLE_PROFILE,\n cleanupClaudeFableLaunch,\n parseClaudeFableOutput,\n} from '../shared/claudeFableProfile.ts';\n\n/** How much trailing stdout is kept as the run's result. */\nconst MAX_RESULT_BYTES = 64 * 1024;\n/** How much stderr is kept for a failure message. */\nconst MAX_STDERR_BYTES = 8 * 1024;\nconst PARENT_LOST_MESSAGE = 'Parent process was lost.';\nconst FORCE_KILL_SIGNAL: NodeJS.Signals = 'SIGKILL';\n\nfunction errorMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction readLaunchConfig(runId: string): CliLaunchConfig {\n const parsed: unknown = JSON.parse(fs.readFileSync(currentRunConfigPath(runId), 'utf8'));\n if (!isRecord(parsed) || parsed.runId !== runId || typeof parsed.command !== 'string') {\n throw new Error(`CLI launch config for run '${runId}' is invalid.`);\n }\n return parsed as unknown as CliLaunchConfig;\n}\n\n/** Keep only a UTF-8-safe trailing value within `limit` bytes, so a chatty CLI cannot exhaust memory. */\nfunction appendBounded(buffer: string, chunk: string, limit: number): string {\n let combined = buffer + chunk;\n while (Buffer.byteLength(combined, 'utf8') > limit && combined.length > 0) {\n const excess = Buffer.byteLength(combined, 'utf8') - limit;\n combined = combined.slice(Math.max(1, Math.ceil(excess / 2)));\n }\n return combined;\n}\n\nlet childTelemetry: DoomTelemetry | undefined;\n\nasync function finishTelemetry(event: string, attributes: Record<string, unknown>, error?: unknown): Promise<void> {\n const telemetry = childTelemetry;\n childTelemetry = undefined;\n if (!telemetry) return;\n if (error === undefined) await telemetry.recordEvent(event, attributes);\n else await telemetry.recordError(event, error, attributes);\n await telemetry.flush();\n await telemetry.shutdown();\n}\n\nasync function run(): Promise<void> {\n const startedAt = Date.now();\n childTelemetry = createDoomTelemetry({\n serviceName: 'doom-team-cli-child',\n packageName: '@agimon-ai/doompi-team',\n env: process.env,\n enableLogs: true,\n enableTraces: true,\n });\n const runId = process.env[SUBAGENT_RUN_ID_ENV]?.trim();\n if (!runId) throw new Error(`${SUBAGENT_RUN_ID_ENV} is required.`);\n if (!adoptSessionScopeFromEnv()) {\n throw new Error(`${SUBAGENT_ROOT_SESSION_ENV} is required: a child cannot resolve its session scope without it.`);\n }\n\n let config: CliLaunchConfig | undefined;\n try {\n config = readLaunchConfig(runId);\n // The vendor process emits its own spawn event before the ready handshake\n // is written below. Reading the config alone is not sufficient evidence\n // that the configured executable can start.\n } catch (error) {\n if (config?.handshakePath && !fs.existsSync(config.handshakePath)) {\n writeAtomicJson(config.handshakePath, { state: 'error', error: errorMessage(error) });\n }\n throw error;\n }\n\n const statusWriter = new CoalescedStatusWriter<AsyncRunStatus>();\n const now = Date.now();\n await childTelemetry?.recordEvent('doom_team.child_started', {\n runtime: config.runtime,\n 'agent.name': config.agent,\n outcome: 'started',\n });\n statusWriter.open(\n runId,\n readAsyncRunStatus(runId) ?? {\n version: 1,\n runId,\n ...(config.operationId ? { operationId: config.operationId } : {}),\n agent: config.agent,\n cwd: config.cwd,\n runtime: config.runtime,\n state: 'queued',\n startedAt: now,\n lastUpdate: now,\n },\n );\n\n const profileLaunch = config.profile === CLAUDE_FABLE_PROFILE;\n if (config.profile !== undefined && !profileLaunch) throw new Error('Untrusted external profile.');\n if (profileLaunch && (!config.stdinPath || !config.profileResultPath)) {\n throw new Error('Fable profile launch config is incomplete.');\n }\n const stdinFd = profileLaunch ? fs.openSync(config.stdinPath!, 'r') : undefined;\n let child: ReturnType<typeof spawn>;\n try {\n child = spawn(config.command, config.args, {\n cwd: config.cwd,\n env: profileLaunch ? config.env : { ...process.env, ...config.env },\n stdio: [stdinFd ?? 'ignore', 'pipe', 'pipe'],\n // Its own group, so the watchdog below can take the CLI and anything it\n // spawned down together.\n detached: true,\n windowsHide: true,\n });\n } catch (error) {\n if (profileLaunch) {\n cleanupClaudeFableLaunch({ cleanupPaths: config.cleanupPaths ?? [] });\n if (config.profileResultPath) fs.rmSync(config.profileResultPath, { force: true });\n }\n throw error;\n } finally {\n if (stdinFd !== undefined) fs.closeSync(stdinFd);\n }\n const cleanupProfile = (removeResult: boolean): void => {\n if (!profileLaunch) return;\n cleanupClaudeFableLaunch({ cleanupPaths: config.cleanupPaths ?? [] });\n if (removeResult && config.profileResultPath) fs.rmSync(config.profileResultPath, { force: true });\n };\n\n let requestedSignal: NodeJS.Signals | undefined;\n const stopVendor = (signal: NodeJS.Signals): void => {\n requestedSignal = signal;\n if (!child.pid) return;\n try {\n process.kill(-child.pid, signal);\n } catch (error) {\n process.stderr.write(`[doom-team cli runner] Could not signal vendor process: ${errorMessage(error)}\\n`);\n }\n };\n process.once('SIGTERM', stopVendor);\n process.once('SIGINT', stopVendor);\n\n try {\n await new Promise<void>((resolve, reject) => {\n child.once('spawn', resolve);\n child.once('error', reject);\n });\n writeAtomicJson(config.handshakePath, { state: 'ready' });\n statusWriter.updateSync((status) => {\n status.state = 'running';\n status.activityState = 'working';\n status.lastUpdate = Date.now();\n });\n } catch (error) {\n const message = errorMessage(error);\n writeAtomicJson(config.handshakePath, { state: 'error', error: message });\n statusWriter.updateSync((status) => {\n status.state = 'failed';\n status.error = message;\n status.lastUpdate = Date.now();\n status.endedAt = Date.now();\n });\n writeAtomicJson(config.resultPath, {\n runId,\n ...(config.internal ? { internal: true } : {}),\n ...(config.operationId ? { operationId: config.operationId } : {}),\n agent: config.agent,\n runtime: config.runtime,\n success: false,\n summary: profileLaunch ? 'Fable Claude launch failed.' : message,\n });\n statusWriter.close();\n cleanupProfile(true);\n await finishTelemetry(\n 'doom_team.child_failed',\n { runtime: config.runtime, 'agent.name': config.agent, duration_ms: Date.now() - startedAt, outcome: 'failed' },\n error,\n );\n process.exit(1);\n }\n\n // A SIGKILLed parent cannot signal us, so terminate the vendor tree and\n // persist a terminal record before leaving when the watchdog can observe it.\n const stopWatchdog = startParentWatchdog({\n onParentLost: () => {\n stopVendor(FORCE_KILL_SIGNAL);\n const endedAt = Date.now();\n statusWriter.updateSync((status) => {\n status.state = 'stopped';\n status.error = PARENT_LOST_MESSAGE;\n status.lastUpdate = endedAt;\n status.endedAt = endedAt;\n });\n writeAtomicJson(config.resultPath, {\n runId,\n ...(config.internal ? { internal: true } : {}),\n ...(config.operationId ? { operationId: config.operationId } : {}),\n agent: config.agent,\n runtime: config.runtime,\n success: false,\n summary: PARENT_LOST_MESSAGE,\n signal: FORCE_KILL_SIGNAL,\n });\n statusWriter.close();\n cleanupProfile(true);\n void finishTelemetry('doom_team.child_finished', {\n runtime: config.runtime,\n 'agent.name': config.agent,\n duration_ms: Date.now() - startedAt,\n outcome: 'parent_lost',\n }).finally(() => process.exit(0));\n },\n });\n\n let stdout = '';\n let stderr = '';\n child.stdout?.on('data', (chunk: Buffer) => {\n stdout = appendBounded(stdout, chunk.toString('utf8'), MAX_RESULT_BYTES);\n });\n child.stderr?.on('data', (chunk: Buffer) => {\n stderr = appendBounded(stderr, chunk.toString('utf8'), MAX_STDERR_BYTES);\n });\n\n const exit = await new Promise<{ code: number | null; signal: NodeJS.Signals | null; error?: Error }>((resolve) => {\n child.on('error', (error) => resolve({ code: null, signal: null, error }));\n child.on('close', (code, signal) => resolve({ code, signal }));\n });\n\n stopWatchdog();\n\n const stopped = requestedSignal !== undefined;\n let profileError: Error | undefined;\n if (profileLaunch && !stopped && exit.error === undefined && exit.code === 0) {\n try {\n const parsed = parseClaudeFableOutput(stdout);\n writePrivateAtomicJson(config.profileResultPath!, { text: parsed.text, outputBytes: parsed.outputBytes });\n } catch (error) {\n profileError = error instanceof Error ? error : new Error(String(error));\n }\n }\n const success = !stopped && exit.error === undefined && exit.code === 0 && profileError === undefined;\n const failureDetail = profileLaunch\n ? (profileError?.message ?? 'Fable Claude launch failed.')\n : stderr.trim() || `${config.runtime} exited with ${exit.signal ?? `code ${exit.code ?? 'unknown'}`}.`;\n const summary = stopped\n ? `Stopped by ${requestedSignal}.`\n : success\n ? profileLaunch\n ? 'Fable profile completed.'\n : stdout.trim()\n : (exit.error?.message ?? failureDetail);\n const endedAt = Date.now();\n\n statusWriter.updateSync((status) => {\n status.state = stopped ? 'stopped' : success ? 'completed' : 'failed';\n status.activityState = 'finalizing';\n status.summary = summary;\n if (!success) status.error = summary;\n status.lastUpdate = endedAt;\n status.endedAt = endedAt;\n });\n writeAtomicJson(config.resultPath, {\n runId,\n ...(config.internal ? { internal: true } : {}),\n ...(config.operationId ? { operationId: config.operationId } : {}),\n agent: config.agent,\n runtime: config.runtime,\n success,\n summary,\n ...(exit.code === null ? {} : { exitCode: exit.code }),\n ...(exit.signal ? { signal: exit.signal } : {}),\n });\n\n statusWriter.close();\n cleanupProfile(!success);\n await finishTelemetry('doom_team.child_finished', {\n runtime: config.runtime,\n 'agent.name': config.agent,\n duration_ms: Date.now() - startedAt,\n 'runner.stdout_bytes': Buffer.byteLength(stdout, 'utf8'),\n 'runner.stderr_bytes': Buffer.byteLength(stderr, 'utf8'),\n 'runner.exit_code': exit.code ?? 0,\n outcome: stopped ? 'stopped' : success ? 'completed' : 'failed',\n });\n // The runner exit code reports transport health. Task success or failure is\n // encoded durably in status and result data before this process exits.\n process.exit(0);\n}\n\nvoid run().catch(async (error: unknown) => {\n await finishTelemetry('doom_team.child_failed', { outcome: 'failed' }, error);\n // Keep stdout reserved for vendor output. Stderr is captured in the run's\n // runner log and makes a transport failure diagnosable by the parent.\n process.stderr.write(`[doom-team cli runner] ${errorMessage(error)}\\n`);\n process.exitCode = 1;\n});\n"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ const e={enabled:!0,debounceMs:150,maxWaitMs:1e3,stragglerDebounceMs:75,stragglerMaxWaitMs:400,stragglerWindowMs:2e3};function t(e){if(!(typeof e!=`number`||!Number.isFinite(e)||!Number.isInteger(e)||e<1))return e}function n(n){return{enabled:typeof n?.enabled==`boolean`?n.enabled:e.enabled,debounceMs:t(n?.debounceMs)??e.debounceMs,maxWaitMs:t(n?.maxWaitMs)??e.maxWaitMs,stragglerDebounceMs:t(n?.stragglerDebounceMs)??e.stragglerDebounceMs,stragglerMaxWaitMs:t(n?.stragglerMaxWaitMs)??e.stragglerMaxWaitMs,stragglerWindowMs:t(n?.stragglerWindowMs)??e.stragglerWindowMs}}const r={setTimeout:(e,t)=>setTimeout(e,t),clearTimeout:e=>clearTimeout(e)};function i(e){let t=e.timers??r,n=e.now??Date.now,i=e.config;if(!i.enabled)return{push(t){e.emit([t])},flush(){},dispose(){return[]}};let a=[],o,s,c=!1,l,u=()=>{o!==void 0&&(t.clearTimeout(o),o=void 0),s!==void 0&&(t.clearTimeout(s),s=void 0)},d=()=>{if(u(),a.length===0)return;let t=a;a=[],l=n(),e.emit(t)};return{push(e){a.length===0&&(c=l!==void 0&&n()-l<i.stragglerWindowMs),a.push(e),o!==void 0&&t.clearTimeout(o);let r=c?i.stragglerDebounceMs:i.debounceMs;if(o=t.setTimeout(d,r),o.unref?.(),s===void 0){let e=c?i.stragglerMaxWaitMs:i.maxWaitMs;s=t.setTimeout(d,e),s.unref?.()}},flush:d,dispose(){u();let e=a;return a=[],e}}}exports.createCompletionBatcher=i,exports.resolveCompletionBatchConfig=n;
2
+ //# sourceMappingURL=completionBatcher.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"completionBatcher.cjs","names":[],"sources":["../../../src/runs/background/completionBatcher.ts"],"sourcesContent":["/**\n * Debounced grouping for successful completion notifications.\n *\n * WHY THIS EXISTS:\n * A fan-out run's children tend to finish within milliseconds of each other.\n * Notifying once per child turns a five-way parallel run into five separate\n * interruptions for the operator instead of one. This holds a *successful*\n * completion briefly so siblings finishing in the same short window arrive as\n * a single grouped message.\n *\n * DESIGN PATTERNS (ported as-is from\n * `doom-pi-subagents/src/runs/background/completionBatcher.ts` - this\n * module's logic was not one of the flagged predecessor bugs):\n * - A hard `maxWaitMs` cap, measured from the first item in the group, means\n * a debounce that keeps getting reset by new arrivals cannot hold a group\n * open forever\n * - After a group emits, arrivals within `stragglerWindowMs` join a shorter\n * \"straggler\" group (`stragglerDebounceMs`/`stragglerMaxWaitMs`): a single\n * slow sibling still gets grouped with whoever else trails in soon after,\n * rather than waiting out the full debounce alone\n * - `push()` is the only entry point that arms timers; `flush()` and\n * `dispose()` both go through the same `clearTimers`/emit path, so there is\n * one place a leak could be introduced\n * - Disabled mode (`config.enabled === false`) degrades to \"emit immediately,\n * one item at a time\" rather than a special code path elsewhere: every\n * caller of `push()` behaves the same regardless of whether batching is on\n *\n * WHY THIS OWNS ITS OWN TIMERS RATHER THAN REGISTERING WITH `PollScheduler`:\n * Same reasoning as `spawnHandshake.ts`'s documented exception, and for the\n * same underlying reason: `PollScheduler`'s floor is 200ms, and this\n * batcher's default `debounceMs` is 150ms - below the floor before backoff\n * even starts growing it. Coupling a sub-200ms debounce to a shared tick that\n * is explicitly designed to back off toward 5s while idle would make the\n * debounce it exists to provide unreliable. A completion batcher is also\n * short-lived and per-group (bounded by `maxWaitMs`, at most ~1s), the same\n * shape as a handshake wait, not a long-lived background subscriber.\n *\n * AVOID:\n * - Calling `push()` for a failed or paused completion; this batcher has no\n * opinion on what should bypass it; that decision belongs to the caller\n * (`notify.ts`), which flushes and emits those immediately instead\n */\n\nexport interface CompletionBatchConfig {\n enabled?: boolean;\n /** Idle window after each arrival; resets on every new item. */\n debounceMs?: number;\n /** Hard cap measured from the first item in a group. */\n maxWaitMs?: number;\n /** Shorter idle window for straggler groups. */\n stragglerDebounceMs?: number;\n /** Shorter hard cap for straggler groups. */\n stragglerMaxWaitMs?: number;\n /** Arrivals within this window after an emit join a straggler group. */\n stragglerWindowMs?: number;\n}\n\nexport interface ResolvedCompletionBatchConfig {\n enabled: boolean;\n debounceMs: number;\n maxWaitMs: number;\n stragglerDebounceMs: number;\n stragglerMaxWaitMs: number;\n stragglerWindowMs: number;\n}\n\nexport const DEFAULT_COMPLETION_BATCH_CONFIG: ResolvedCompletionBatchConfig = {\n enabled: true,\n debounceMs: 150,\n maxWaitMs: 1000,\n stragglerDebounceMs: 75,\n stragglerMaxWaitMs: 400,\n stragglerWindowMs: 2000,\n};\n\nfunction parsePositiveInt(value: unknown): number | undefined {\n if (typeof value !== 'number' || !Number.isFinite(value) || !Number.isInteger(value) || value < 1) return undefined;\n return value;\n}\n\nexport function resolveCompletionBatchConfig(override?: CompletionBatchConfig): ResolvedCompletionBatchConfig {\n return {\n enabled: typeof override?.enabled === 'boolean' ? override.enabled : DEFAULT_COMPLETION_BATCH_CONFIG.enabled,\n debounceMs: parsePositiveInt(override?.debounceMs) ?? DEFAULT_COMPLETION_BATCH_CONFIG.debounceMs,\n maxWaitMs: parsePositiveInt(override?.maxWaitMs) ?? DEFAULT_COMPLETION_BATCH_CONFIG.maxWaitMs,\n stragglerDebounceMs:\n parsePositiveInt(override?.stragglerDebounceMs) ?? DEFAULT_COMPLETION_BATCH_CONFIG.stragglerDebounceMs,\n stragglerMaxWaitMs:\n parsePositiveInt(override?.stragglerMaxWaitMs) ?? DEFAULT_COMPLETION_BATCH_CONFIG.stragglerMaxWaitMs,\n stragglerWindowMs:\n parsePositiveInt(override?.stragglerWindowMs) ?? DEFAULT_COMPLETION_BATCH_CONFIG.stragglerWindowMs,\n };\n}\n\ntype TimerHandle = ReturnType<typeof setTimeout>;\n\ninterface TimerApi {\n setTimeout(handler: () => void, delayMs: number): TimerHandle;\n clearTimeout(handle: TimerHandle): void;\n}\n\nconst defaultTimers: TimerApi = {\n setTimeout: (handler, delayMs) => setTimeout(handler, delayMs),\n clearTimeout: (handle) => clearTimeout(handle),\n};\n\nexport interface CompletionBatcherOptions<T> {\n config: ResolvedCompletionBatchConfig;\n emit: (items: T[]) => void;\n timers?: TimerApi;\n now?: () => number;\n}\n\nexport interface CompletionBatcher<T> {\n /** Add a batchable item. Emits immediately, alone, when batching is disabled. */\n push(item: T): void;\n /** Emit any held items immediately as a single group. No-op when nothing is held. */\n flush(): void;\n /** Clear timers and return items that were never emitted, without emitting them. */\n dispose(): T[];\n}\n\n/**\n * Create a completion batcher. Single-use per registration: it holds at most\n * one open group at a time. `flush()` forces emission (used by the caller to\n * drain a group before an immediate, unbatched emit for a failure/pause);\n * `dispose()` tears down timers for reload/shutdown without emitting.\n */\nexport function createCompletionBatcher<T>(options: CompletionBatcherOptions<T>): CompletionBatcher<T> {\n const timers = options.timers ?? defaultTimers;\n const now = options.now ?? Date.now;\n const config = options.config;\n\n if (!config.enabled) {\n return {\n push(item: T) {\n options.emit([item]);\n },\n flush() {\n /* nothing is ever held when disabled */\n },\n dispose() {\n return [];\n },\n };\n }\n\n let pending: T[] = [];\n let debounceTimer: TimerHandle | undefined;\n let maxWaitTimer: TimerHandle | undefined;\n let straggler = false;\n let lastEmitAt: number | undefined;\n\n const clearTimers = (): void => {\n if (debounceTimer !== undefined) {\n timers.clearTimeout(debounceTimer);\n debounceTimer = undefined;\n }\n if (maxWaitTimer !== undefined) {\n timers.clearTimeout(maxWaitTimer);\n maxWaitTimer = undefined;\n }\n };\n\n const emitGroup = (): void => {\n clearTimers();\n if (pending.length === 0) return;\n const items = pending;\n pending = [];\n lastEmitAt = now();\n options.emit(items);\n };\n\n return {\n push(item: T) {\n if (pending.length === 0) {\n straggler = lastEmitAt !== undefined && now() - lastEmitAt < config.stragglerWindowMs;\n }\n pending.push(item);\n\n if (debounceTimer !== undefined) timers.clearTimeout(debounceTimer);\n const debounceDelay = straggler ? config.stragglerDebounceMs : config.debounceMs;\n debounceTimer = timers.setTimeout(emitGroup, debounceDelay);\n debounceTimer.unref?.();\n\n if (maxWaitTimer === undefined) {\n const maxWaitDelay = straggler ? config.stragglerMaxWaitMs : config.maxWaitMs;\n maxWaitTimer = timers.setTimeout(emitGroup, maxWaitDelay);\n maxWaitTimer.unref?.();\n }\n },\n flush: emitGroup,\n dispose() {\n clearTimers();\n const abandoned = pending;\n pending = [];\n return abandoned;\n },\n };\n}\n"],"mappings":"AAkEA,MAAa,EAAiE,CAC5E,QAAS,GACT,WAAY,IACZ,UAAW,IACX,oBAAqB,GACrB,mBAAoB,IACpB,kBAAmB,IACpB,CAED,SAAS,EAAiB,EAAoC,CACxD,YAAO,GAAU,UAAY,CAAC,OAAO,SAAS,EAAM,EAAI,CAAC,OAAO,UAAU,EAAM,EAAI,EAAQ,GAChG,OAAO,EAGT,SAAgB,EAA6B,EAAiE,CAC5G,MAAO,CACL,QAAS,OAAO,GAAU,SAAY,UAAY,EAAS,QAAU,EAAgC,QACrG,WAAY,EAAiB,GAAU,WAAW,EAAI,EAAgC,WACtF,UAAW,EAAiB,GAAU,UAAU,EAAI,EAAgC,UACpF,oBACE,EAAiB,GAAU,oBAAoB,EAAI,EAAgC,oBACrF,mBACE,EAAiB,GAAU,mBAAmB,EAAI,EAAgC,mBACpF,kBACE,EAAiB,GAAU,kBAAkB,EAAI,EAAgC,kBACpF,CAUH,MAAM,EAA0B,CAC9B,YAAa,EAAS,IAAY,WAAW,EAAS,EAAQ,CAC9D,aAAe,GAAW,aAAa,EAAO,CAC/C,CAwBD,SAAgB,EAA2B,EAA4D,CACrG,IAAM,EAAS,EAAQ,QAAU,EAC3B,EAAM,EAAQ,KAAO,KAAK,IAC1B,EAAS,EAAQ,OAEvB,GAAI,CAAC,EAAO,QACV,MAAO,CACL,KAAK,EAAS,CACZ,EAAQ,KAAK,CAAC,EAAK,CAAC,EAEtB,OAAQ,GAGR,SAAU,CACR,MAAO,EAAE,EAEZ,CAGH,IAAI,EAAe,EAAE,CACjB,EACA,EACA,EAAY,GACZ,EAEE,MAA0B,CAC1B,IAAkB,IAAA,KACpB,EAAO,aAAa,EAAc,CAClC,EAAgB,IAAA,IAEd,IAAiB,IAAA,KACnB,EAAO,aAAa,EAAa,CACjC,EAAe,IAAA,KAIb,MAAwB,CAE5B,GADA,GAAa,CACT,EAAQ,SAAW,EAAG,OAC1B,IAAM,EAAQ,EACd,EAAU,EAAE,CACZ,EAAa,GAAK,CAClB,EAAQ,KAAK,EAAM,EAGrB,MAAO,CACL,KAAK,EAAS,CACR,EAAQ,SAAW,IACrB,EAAY,IAAe,IAAA,IAAa,GAAK,CAAG,EAAa,EAAO,mBAEtE,EAAQ,KAAK,EAAK,CAEd,IAAkB,IAAA,IAAW,EAAO,aAAa,EAAc,CACnE,IAAM,EAAgB,EAAY,EAAO,oBAAsB,EAAO,WAItE,GAHA,EAAgB,EAAO,WAAW,EAAW,EAAc,CAC3D,EAAc,SAAS,CAEnB,IAAiB,IAAA,GAAW,CAC9B,IAAM,EAAe,EAAY,EAAO,mBAAqB,EAAO,UACpE,EAAe,EAAO,WAAW,EAAW,EAAa,CACzD,EAAa,SAAS,GAG1B,MAAO,EACP,SAAU,CACR,GAAa,CACb,IAAM,EAAY,EAElB,MADA,GAAU,EAAE,CACL,GAEV"}
@@ -0,0 +1,2 @@
1
+ import{__esmMin as e}from"../../_virtual/_rolldown/runtime.mjs";function t(e){if(!(typeof e!=`number`||!Number.isFinite(e)||!Number.isInteger(e)||e<1))return e}function n(e){return{enabled:typeof e?.enabled==`boolean`?e.enabled:i.enabled,debounceMs:t(e?.debounceMs)??i.debounceMs,maxWaitMs:t(e?.maxWaitMs)??i.maxWaitMs,stragglerDebounceMs:t(e?.stragglerDebounceMs)??i.stragglerDebounceMs,stragglerMaxWaitMs:t(e?.stragglerMaxWaitMs)??i.stragglerMaxWaitMs,stragglerWindowMs:t(e?.stragglerWindowMs)??i.stragglerWindowMs}}function r(e){let t=e.timers??a,n=e.now??Date.now,r=e.config;if(!r.enabled)return{push(t){e.emit([t])},flush(){},dispose(){return[]}};let i=[],o,s,c=!1,l,u=()=>{o!==void 0&&(t.clearTimeout(o),o=void 0),s!==void 0&&(t.clearTimeout(s),s=void 0)},d=()=>{if(u(),i.length===0)return;let t=i;i=[],l=n(),e.emit(t)};return{push(e){i.length===0&&(c=l!==void 0&&n()-l<r.stragglerWindowMs),i.push(e),o!==void 0&&t.clearTimeout(o);let a=c?r.stragglerDebounceMs:r.debounceMs;if(o=t.setTimeout(d,a),o.unref?.(),s===void 0){let e=c?r.stragglerMaxWaitMs:r.maxWaitMs;s=t.setTimeout(d,e),s.unref?.()}},flush:d,dispose(){u();let e=i;return i=[],e}}}var i,a,o=e((()=>{i={enabled:!0,debounceMs:150,maxWaitMs:1e3,stragglerDebounceMs:75,stragglerMaxWaitMs:400,stragglerWindowMs:2e3},a={setTimeout:(e,t)=>setTimeout(e,t),clearTimeout:e=>clearTimeout(e)}}));o();export{r as createCompletionBatcher,o as init_completionBatcher,n as resolveCompletionBatchConfig};
2
+ //# sourceMappingURL=completionBatcher.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"completionBatcher.mjs","names":[],"sources":["../../../src/runs/background/completionBatcher.ts"],"sourcesContent":["/**\n * Debounced grouping for successful completion notifications.\n *\n * WHY THIS EXISTS:\n * A fan-out run's children tend to finish within milliseconds of each other.\n * Notifying once per child turns a five-way parallel run into five separate\n * interruptions for the operator instead of one. This holds a *successful*\n * completion briefly so siblings finishing in the same short window arrive as\n * a single grouped message.\n *\n * DESIGN PATTERNS (ported as-is from\n * `doom-pi-subagents/src/runs/background/completionBatcher.ts` - this\n * module's logic was not one of the flagged predecessor bugs):\n * - A hard `maxWaitMs` cap, measured from the first item in the group, means\n * a debounce that keeps getting reset by new arrivals cannot hold a group\n * open forever\n * - After a group emits, arrivals within `stragglerWindowMs` join a shorter\n * \"straggler\" group (`stragglerDebounceMs`/`stragglerMaxWaitMs`): a single\n * slow sibling still gets grouped with whoever else trails in soon after,\n * rather than waiting out the full debounce alone\n * - `push()` is the only entry point that arms timers; `flush()` and\n * `dispose()` both go through the same `clearTimers`/emit path, so there is\n * one place a leak could be introduced\n * - Disabled mode (`config.enabled === false`) degrades to \"emit immediately,\n * one item at a time\" rather than a special code path elsewhere: every\n * caller of `push()` behaves the same regardless of whether batching is on\n *\n * WHY THIS OWNS ITS OWN TIMERS RATHER THAN REGISTERING WITH `PollScheduler`:\n * Same reasoning as `spawnHandshake.ts`'s documented exception, and for the\n * same underlying reason: `PollScheduler`'s floor is 200ms, and this\n * batcher's default `debounceMs` is 150ms - below the floor before backoff\n * even starts growing it. Coupling a sub-200ms debounce to a shared tick that\n * is explicitly designed to back off toward 5s while idle would make the\n * debounce it exists to provide unreliable. A completion batcher is also\n * short-lived and per-group (bounded by `maxWaitMs`, at most ~1s), the same\n * shape as a handshake wait, not a long-lived background subscriber.\n *\n * AVOID:\n * - Calling `push()` for a failed or paused completion; this batcher has no\n * opinion on what should bypass it; that decision belongs to the caller\n * (`notify.ts`), which flushes and emits those immediately instead\n */\n\nexport interface CompletionBatchConfig {\n enabled?: boolean;\n /** Idle window after each arrival; resets on every new item. */\n debounceMs?: number;\n /** Hard cap measured from the first item in a group. */\n maxWaitMs?: number;\n /** Shorter idle window for straggler groups. */\n stragglerDebounceMs?: number;\n /** Shorter hard cap for straggler groups. */\n stragglerMaxWaitMs?: number;\n /** Arrivals within this window after an emit join a straggler group. */\n stragglerWindowMs?: number;\n}\n\nexport interface ResolvedCompletionBatchConfig {\n enabled: boolean;\n debounceMs: number;\n maxWaitMs: number;\n stragglerDebounceMs: number;\n stragglerMaxWaitMs: number;\n stragglerWindowMs: number;\n}\n\nexport const DEFAULT_COMPLETION_BATCH_CONFIG: ResolvedCompletionBatchConfig = {\n enabled: true,\n debounceMs: 150,\n maxWaitMs: 1000,\n stragglerDebounceMs: 75,\n stragglerMaxWaitMs: 400,\n stragglerWindowMs: 2000,\n};\n\nfunction parsePositiveInt(value: unknown): number | undefined {\n if (typeof value !== 'number' || !Number.isFinite(value) || !Number.isInteger(value) || value < 1) return undefined;\n return value;\n}\n\nexport function resolveCompletionBatchConfig(override?: CompletionBatchConfig): ResolvedCompletionBatchConfig {\n return {\n enabled: typeof override?.enabled === 'boolean' ? override.enabled : DEFAULT_COMPLETION_BATCH_CONFIG.enabled,\n debounceMs: parsePositiveInt(override?.debounceMs) ?? DEFAULT_COMPLETION_BATCH_CONFIG.debounceMs,\n maxWaitMs: parsePositiveInt(override?.maxWaitMs) ?? DEFAULT_COMPLETION_BATCH_CONFIG.maxWaitMs,\n stragglerDebounceMs:\n parsePositiveInt(override?.stragglerDebounceMs) ?? DEFAULT_COMPLETION_BATCH_CONFIG.stragglerDebounceMs,\n stragglerMaxWaitMs:\n parsePositiveInt(override?.stragglerMaxWaitMs) ?? DEFAULT_COMPLETION_BATCH_CONFIG.stragglerMaxWaitMs,\n stragglerWindowMs:\n parsePositiveInt(override?.stragglerWindowMs) ?? DEFAULT_COMPLETION_BATCH_CONFIG.stragglerWindowMs,\n };\n}\n\ntype TimerHandle = ReturnType<typeof setTimeout>;\n\ninterface TimerApi {\n setTimeout(handler: () => void, delayMs: number): TimerHandle;\n clearTimeout(handle: TimerHandle): void;\n}\n\nconst defaultTimers: TimerApi = {\n setTimeout: (handler, delayMs) => setTimeout(handler, delayMs),\n clearTimeout: (handle) => clearTimeout(handle),\n};\n\nexport interface CompletionBatcherOptions<T> {\n config: ResolvedCompletionBatchConfig;\n emit: (items: T[]) => void;\n timers?: TimerApi;\n now?: () => number;\n}\n\nexport interface CompletionBatcher<T> {\n /** Add a batchable item. Emits immediately, alone, when batching is disabled. */\n push(item: T): void;\n /** Emit any held items immediately as a single group. No-op when nothing is held. */\n flush(): void;\n /** Clear timers and return items that were never emitted, without emitting them. */\n dispose(): T[];\n}\n\n/**\n * Create a completion batcher. Single-use per registration: it holds at most\n * one open group at a time. `flush()` forces emission (used by the caller to\n * drain a group before an immediate, unbatched emit for a failure/pause);\n * `dispose()` tears down timers for reload/shutdown without emitting.\n */\nexport function createCompletionBatcher<T>(options: CompletionBatcherOptions<T>): CompletionBatcher<T> {\n const timers = options.timers ?? defaultTimers;\n const now = options.now ?? Date.now;\n const config = options.config;\n\n if (!config.enabled) {\n return {\n push(item: T) {\n options.emit([item]);\n },\n flush() {\n /* nothing is ever held when disabled */\n },\n dispose() {\n return [];\n },\n };\n }\n\n let pending: T[] = [];\n let debounceTimer: TimerHandle | undefined;\n let maxWaitTimer: TimerHandle | undefined;\n let straggler = false;\n let lastEmitAt: number | undefined;\n\n const clearTimers = (): void => {\n if (debounceTimer !== undefined) {\n timers.clearTimeout(debounceTimer);\n debounceTimer = undefined;\n }\n if (maxWaitTimer !== undefined) {\n timers.clearTimeout(maxWaitTimer);\n maxWaitTimer = undefined;\n }\n };\n\n const emitGroup = (): void => {\n clearTimers();\n if (pending.length === 0) return;\n const items = pending;\n pending = [];\n lastEmitAt = now();\n options.emit(items);\n };\n\n return {\n push(item: T) {\n if (pending.length === 0) {\n straggler = lastEmitAt !== undefined && now() - lastEmitAt < config.stragglerWindowMs;\n }\n pending.push(item);\n\n if (debounceTimer !== undefined) timers.clearTimeout(debounceTimer);\n const debounceDelay = straggler ? config.stragglerDebounceMs : config.debounceMs;\n debounceTimer = timers.setTimeout(emitGroup, debounceDelay);\n debounceTimer.unref?.();\n\n if (maxWaitTimer === undefined) {\n const maxWaitDelay = straggler ? config.stragglerMaxWaitMs : config.maxWaitMs;\n maxWaitTimer = timers.setTimeout(emitGroup, maxWaitDelay);\n maxWaitTimer.unref?.();\n }\n },\n flush: emitGroup,\n dispose() {\n clearTimers();\n const abandoned = pending;\n pending = [];\n return abandoned;\n },\n };\n}\n"],"mappings":"gEA2EA,SAAS,EAAiB,EAAoC,CACxD,YAAO,GAAU,UAAY,CAAC,OAAO,SAAS,EAAM,EAAI,CAAC,OAAO,UAAU,EAAM,EAAI,EAAQ,GAChG,OAAO,EAGT,SAAgB,EAA6B,EAAiE,CAC5G,MAAO,CACL,QAAS,OAAO,GAAU,SAAY,UAAY,EAAS,QAAU,EAAgC,QACrG,WAAY,EAAiB,GAAU,WAAW,EAAI,EAAgC,WACtF,UAAW,EAAiB,GAAU,UAAU,EAAI,EAAgC,UACpF,oBACE,EAAiB,GAAU,oBAAoB,EAAI,EAAgC,oBACrF,mBACE,EAAiB,GAAU,mBAAmB,EAAI,EAAgC,mBACpF,kBACE,EAAiB,GAAU,kBAAkB,EAAI,EAAgC,kBACpF,CAqCH,SAAgB,EAA2B,EAA4D,CACrG,IAAM,EAAS,EAAQ,QAAU,EAC3B,EAAM,EAAQ,KAAO,KAAK,IAC1B,EAAS,EAAQ,OAEvB,GAAI,CAAC,EAAO,QACV,MAAO,CACL,KAAK,EAAS,CACZ,EAAQ,KAAK,CAAC,EAAK,CAAC,EAEtB,OAAQ,GAGR,SAAU,CACR,MAAO,EAAE,EAEZ,CAGH,IAAI,EAAe,EAAE,CACjB,EACA,EACA,EAAY,GACZ,EAEE,MAA0B,CAC1B,IAAkB,IAAA,KACpB,EAAO,aAAa,EAAc,CAClC,EAAgB,IAAA,IAEd,IAAiB,IAAA,KACnB,EAAO,aAAa,EAAa,CACjC,EAAe,IAAA,KAIb,MAAwB,CAE5B,GADA,GAAa,CACT,EAAQ,SAAW,EAAG,OAC1B,IAAM,EAAQ,EACd,EAAU,EAAE,CACZ,EAAa,GAAK,CAClB,EAAQ,KAAK,EAAM,EAGrB,MAAO,CACL,KAAK,EAAS,CACR,EAAQ,SAAW,IACrB,EAAY,IAAe,IAAA,IAAa,GAAK,CAAG,EAAa,EAAO,mBAEtE,EAAQ,KAAK,EAAK,CAEd,IAAkB,IAAA,IAAW,EAAO,aAAa,EAAc,CACnE,IAAM,EAAgB,EAAY,EAAO,oBAAsB,EAAO,WAItE,GAHA,EAAgB,EAAO,WAAW,EAAW,EAAc,CAC3D,EAAc,SAAS,CAEnB,IAAiB,IAAA,GAAW,CAC9B,IAAM,EAAe,EAAY,EAAO,mBAAqB,EAAO,UACpE,EAAe,EAAO,WAAW,EAAW,EAAa,CACzD,EAAa,SAAS,GAG1B,MAAO,EACP,SAAU,CACR,GAAa,CACb,IAAM,EAAY,EAElB,MADA,GAAU,EAAE,CACL,GAEV,mBApIU,EAAiE,CAC5E,QAAS,GACT,WAAY,IACZ,UAAW,IACX,oBAAqB,GACrB,mBAAoB,IACpB,kBAAmB,IACpB,CA4BK,EAA0B,CAC9B,YAAa,EAAS,IAAY,WAAW,EAAS,EAAQ,CAC9D,aAAe,GAAW,aAAa,EAAO,CAC/C"}
@@ -0,0 +1,4 @@
1
+ require(`../../_virtual/_rolldown/runtime.cjs`);const e=require(`../../_virtual/_@oxc-project_runtime@0.127.0/helpers/decorate.cjs`),t=require(`./completionBatcher.cjs`);let n=require(`inversify`);const r=`(no output)`,i=`unknown`;function a(e){let t=e.resultPreview.trim().length>0;return e.status===`completed`?t?`Read this result now and incorporate, reject, or mark it irrelevant before replying to the user.`:`No summary was produced. Call { action: "status", id: "${e.runId}", view: "transcript" } before replying.`:e.status===`paused`?`Run is paused and resumable. Inspect it with { action: "status", id: "${e.runId}" }, then decide whether to resume or abandon it.`:`Review this failure and tell the user whether it affects the current work. Full output: { action: "status", id: "${e.runId}", view: "transcript" }.`}function o(e){if(e.sessionValue)return e.sessionLabel?`${e.sessionLabel}: ${e.sessionValue}`:e.sessionValue}function s(e){let t=o(e);return[`Background task ${e.status}: **${e.agent}**${e.taskInfo??``}`,`run id: ${e.runId}`,``,e.resultPreview.trim()?e.resultPreview:r,e.handoffPath?``:void 0,e.handoffPath?`Parallel handoff: ${e.handoffPath}`:void 0,t?``:void 0,t,``,a(e)].filter(e=>e!==void 0).join(`
2
+ `)}function c(e){let t=[`Background tasks completed (${e.length}): ${e.map(e=>`**${e.agent}**${e.taskInfo??``}`).join(`, `)}`,``];for(let n=0;n<e.length;n++){let i=e[n];if(!i)continue;let s=o(i);t.push(`${n+1}. ${i.agent}${i.taskInfo??``} - run id: ${i.runId}`),t.push(i.resultPreview.trim()?i.resultPreview:r),i.handoffPath&&t.push(`Parallel handoff: ${i.handoffPath}`),s&&t.push(s),t.push(a(i)),t.push(``)}return t.join(`
3
+ `).trimEnd()}function l(e){return typeof e==`string`?e:void 0}function u(e){return typeof e==`number`&&Number.isFinite(e)?e:void 0}function d(e){let t=l(e.sessionId)?.trim();if(t)return`session:${t}`;let n=l(e.cwd)?.trim();return n?`cwd:${n}`:i}function f(e){let t=l(e.agent)??i,n=l(e.summary)??``,r=typeof e.success==`boolean`?e.success:void 0,a=l(e.state),o=r===!1&&(a===`paused`||n.startsWith(`Paused after interrupt.`))?`paused`:r===!1?`failed`:`completed`,s=u(e.taskIndex),c=u(e.totalTasks),d=s!==void 0&&c!==void 0?` (${s+1}/${c})`:void 0,f=l((typeof e.parallelHandoff==`object`&&e.parallelHandoff!==null?e.parallelHandoff:void 0)?.path),p=l(e.shareUrl),m=l(e.shareError),h=l(e.sessionFile),g=p?{label:`Session`,value:p}:m?{label:`Session share error`,value:m}:h?{label:`Session file`,value:h}:void 0;return{agent:t,runId:e.runId,status:o,resultPreview:n,...d?{taskInfo:d}:{},...u(e.durationMs)===void 0?{}:{durationMs:u(e.durationMs)},...f?{handoffPath:f}:{},...g?{sessionLabel:g.label,sessionValue:g.value}:{}}}const p=`subagent-notify`;let m=class{batchConfig=void 0;batchers=new Map;disposed=!1;host;attachHost(e){this.host=e}sendMessage(e,t){if(!this.host)return!1;try{return this.host.sendMessage({customType:p,content:e,display:!0,details:t.details},{triggerTurn:t.triggerTurn,deliverAs:`steer`}),!0}catch{return!1}}async deliver(e){if(e.internal===!0)return!0;if(this.disposed)return!1;let t=f(e),n=e.triggerTurn!==!1;return new Promise(r=>{let i={details:t,triggerTurn:n,resolve:r},a=this.getBatcher(e);if(t.status!==`completed`){a.flush(),this.emit([i]);return}a.push(i)})}dispose(){if(!this.disposed){this.disposed=!0;for(let e of this.batchers.values())for(let t of e.dispose())t.resolve(!1);this.batchers.clear()}}getBatcher(e){let n=d(e),r=this.batchers.get(n);return r||(r=t.createCompletionBatcher({config:t.resolveCompletionBatchConfig(this.batchConfig),emit:e=>this.emit(e)}),this.batchers.set(n,r)),r}emit(e){if(e.length===0)return;let t=e.map(e=>e.details),n=t.length===1?s(t[0]):c(t),r=this.sendMessage(n,{triggerTurn:e.some(e=>e.triggerTurn),details:t});for(let t of e)t.resolve(r)}};m=e.__decorate([(0,n.injectable)()],m),Object.defineProperty(exports,`CompletionNotifier`,{enumerable:!0,get:function(){return m}}),exports.SUBAGENT_NOTIFY_MESSAGE_TYPE=p;
4
+ //# sourceMappingURL=notify.cjs.map