@loompad/cli 0.1.0 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (687) hide show
  1. package/ARCHITECTURE.md +96 -11
  2. package/CHANGELOG.md +1042 -0
  3. package/README.md +1473 -44
  4. package/THIRD_PARTY_NOTICES.md +275 -0
  5. package/dist/adapters/antigravity-cli.d.ts +62 -0
  6. package/dist/adapters/antigravity-cli.js +258 -0
  7. package/dist/adapters/antigravity-cli.js.map +1 -0
  8. package/dist/adapters/base.d.ts +61 -9
  9. package/dist/adapters/base.js +266 -15
  10. package/dist/adapters/base.js.map +1 -1
  11. package/dist/adapters/bridges/antigravity.d.ts +9 -12
  12. package/dist/adapters/bridges/antigravity.js +9 -12
  13. package/dist/adapters/bridges/antigravity.js.map +1 -1
  14. package/dist/adapters/echo.d.ts +1 -0
  15. package/dist/adapters/echo.js +59 -3
  16. package/dist/adapters/echo.js.map +1 -1
  17. package/dist/adapters/grok.d.ts +7 -0
  18. package/dist/adapters/grok.js +20 -10
  19. package/dist/adapters/grok.js.map +1 -1
  20. package/dist/adapters/index.d.ts +5 -1
  21. package/dist/adapters/index.js +53 -7
  22. package/dist/adapters/index.js.map +1 -1
  23. package/dist/adapters/model.d.ts +137 -0
  24. package/dist/adapters/model.js +527 -0
  25. package/dist/adapters/model.js.map +1 -0
  26. package/dist/adapters/opencode.d.ts +101 -1
  27. package/dist/adapters/opencode.js +874 -98
  28. package/dist/adapters/opencode.js.map +1 -1
  29. package/dist/cli/doctor.d.ts +32 -0
  30. package/dist/cli/doctor.js +89 -4
  31. package/dist/cli/doctor.js.map +1 -1
  32. package/dist/cli/index.js +2651 -87
  33. package/dist/cli/index.js.map +1 -1
  34. package/dist/cli/tui-model.js +3 -1
  35. package/dist/cli/tui-model.js.map +1 -1
  36. package/dist/cli/tui.js +44 -1
  37. package/dist/cli/tui.js.map +1 -1
  38. package/dist/cli/ui.d.ts +2 -0
  39. package/dist/cli/ui.js +13 -0
  40. package/dist/cli/ui.js.map +1 -1
  41. package/dist/core/ades.d.ts +32 -16
  42. package/dist/core/ades.js +57 -20
  43. package/dist/core/ades.js.map +1 -1
  44. package/dist/core/agent-state.d.ts +14 -0
  45. package/dist/core/agent-state.js +32 -0
  46. package/dist/core/agent-state.js.map +1 -0
  47. package/dist/core/approvals.d.ts +33 -0
  48. package/dist/core/approvals.js +29 -0
  49. package/dist/core/approvals.js.map +1 -0
  50. package/dist/core/backup.d.ts +19 -0
  51. package/dist/core/backup.js +86 -0
  52. package/dist/core/backup.js.map +1 -0
  53. package/dist/core/baton.d.ts +2 -2
  54. package/dist/core/baton.js.map +1 -1
  55. package/dist/core/brain-extract.d.ts +17 -0
  56. package/dist/core/brain-extract.js +28 -2
  57. package/dist/core/brain-extract.js.map +1 -1
  58. package/dist/core/brain-index.d.ts +71 -0
  59. package/dist/core/brain-index.js +204 -5
  60. package/dist/core/brain-index.js.map +1 -1
  61. package/dist/core/brain.d.ts +58 -2
  62. package/dist/core/brain.js +79 -1
  63. package/dist/core/brain.js.map +1 -1
  64. package/dist/core/chat-import.d.ts +71 -0
  65. package/dist/core/chat-import.js +419 -0
  66. package/dist/core/chat-import.js.map +1 -0
  67. package/dist/core/checkpoint.d.ts +107 -0
  68. package/dist/core/checkpoint.js +259 -0
  69. package/dist/core/checkpoint.js.map +1 -0
  70. package/dist/core/claude-cli.js +1 -1
  71. package/dist/core/claude-cli.js.map +1 -1
  72. package/dist/core/continuity/artifacts.d.ts +14 -0
  73. package/dist/core/continuity/artifacts.js +118 -0
  74. package/dist/core/continuity/artifacts.js.map +1 -0
  75. package/dist/core/continuity/capabilities.d.ts +65 -0
  76. package/dist/core/continuity/capabilities.js +129 -0
  77. package/dist/core/continuity/capabilities.js.map +1 -0
  78. package/dist/core/continuity/contracts.d.ts +352 -0
  79. package/dist/core/continuity/contracts.js +117 -0
  80. package/dist/core/continuity/contracts.js.map +1 -0
  81. package/dist/core/continuity/engine.d.ts +55 -0
  82. package/dist/core/continuity/engine.js +504 -0
  83. package/dist/core/continuity/engine.js.map +1 -0
  84. package/dist/core/continuity/store.d.ts +73 -0
  85. package/dist/core/continuity/store.js +438 -0
  86. package/dist/core/continuity/store.js.map +1 -0
  87. package/dist/core/conversations.d.ts +19 -0
  88. package/dist/core/conversations.js +87 -0
  89. package/dist/core/conversations.js.map +1 -0
  90. package/dist/core/crew-coordination.d.ts +6 -0
  91. package/dist/core/crew-coordination.js +24 -0
  92. package/dist/core/crew-coordination.js.map +1 -0
  93. package/dist/core/crew-policy.d.ts +18 -0
  94. package/dist/core/crew-policy.js +40 -0
  95. package/dist/core/crew-policy.js.map +1 -0
  96. package/dist/core/crew-protocol.d.ts +64 -0
  97. package/dist/core/crew-protocol.js +146 -0
  98. package/dist/core/crew-protocol.js.map +1 -0
  99. package/dist/core/crew-requests.d.ts +14 -0
  100. package/dist/core/crew-requests.js +22 -0
  101. package/dist/core/crew-requests.js.map +1 -0
  102. package/dist/core/crew.d.ts +395 -0
  103. package/dist/core/crew.js +1857 -0
  104. package/dist/core/crew.js.map +1 -0
  105. package/dist/core/digest.d.ts +40 -0
  106. package/dist/core/digest.js +94 -0
  107. package/dist/core/digest.js.map +1 -0
  108. package/dist/core/distill.js +3 -5
  109. package/dist/core/distill.js.map +1 -1
  110. package/dist/core/eventlog.d.ts +27 -25
  111. package/dist/core/eventlog.js +97 -150
  112. package/dist/core/eventlog.js.map +1 -1
  113. package/dist/core/events/contracts.d.ts +51 -0
  114. package/dist/core/events/contracts.js +2 -0
  115. package/dist/core/events/contracts.js.map +1 -0
  116. package/dist/core/events/jsonl-store.d.ts +23 -0
  117. package/dist/core/events/jsonl-store.js +90 -0
  118. package/dist/core/events/jsonl-store.js.map +1 -0
  119. package/dist/core/events/sqlite-store.d.ts +37 -0
  120. package/dist/core/events/sqlite-store.js +215 -0
  121. package/dist/core/events/sqlite-store.js.map +1 -0
  122. package/dist/core/forecast.d.ts +44 -0
  123. package/dist/core/forecast.js +46 -0
  124. package/dist/core/forecast.js.map +1 -0
  125. package/dist/core/free-pool.d.ts +65 -0
  126. package/dist/core/free-pool.js +171 -0
  127. package/dist/core/free-pool.js.map +1 -0
  128. package/dist/core/git.d.ts +30 -0
  129. package/dist/core/git.js +50 -2
  130. package/dist/core/git.js.map +1 -1
  131. package/dist/core/github-events.d.ts +53 -0
  132. package/dist/core/github-events.js +227 -0
  133. package/dist/core/github-events.js.map +1 -0
  134. package/dist/core/goal-lanes.d.ts +53 -0
  135. package/dist/core/goal-lanes.js +99 -0
  136. package/dist/core/goal-lanes.js.map +1 -0
  137. package/dist/core/hosted.d.ts +27 -0
  138. package/dist/core/hosted.js +50 -0
  139. package/dist/core/hosted.js.map +1 -0
  140. package/dist/core/http-server.d.ts +5 -0
  141. package/dist/core/http-server.js +8 -0
  142. package/dist/core/http-server.js.map +1 -0
  143. package/dist/core/invite-link.d.ts +31 -0
  144. package/dist/core/invite-link.js +45 -0
  145. package/dist/core/invite-link.js.map +1 -0
  146. package/dist/core/loops.d.ts +37 -0
  147. package/dist/core/loops.js +81 -0
  148. package/dist/core/loops.js.map +1 -0
  149. package/dist/core/mcp-catalog.d.ts +182 -0
  150. package/dist/core/mcp-catalog.js +431 -0
  151. package/dist/core/mcp-catalog.js.map +1 -0
  152. package/dist/core/mcp.d.ts +87 -0
  153. package/dist/core/mcp.js +181 -0
  154. package/dist/core/mcp.js.map +1 -0
  155. package/dist/core/memory.d.ts +1 -0
  156. package/dist/core/memory.js +5 -2
  157. package/dist/core/memory.js.map +1 -1
  158. package/dist/core/model-tools.d.ts +97 -0
  159. package/dist/core/model-tools.js +388 -0
  160. package/dist/core/model-tools.js.map +1 -0
  161. package/dist/core/notify.js +19 -1
  162. package/dist/core/notify.js.map +1 -1
  163. package/dist/core/orchestra.d.ts +635 -0
  164. package/dist/core/orchestra.js +2120 -0
  165. package/dist/core/orchestra.js.map +1 -0
  166. package/dist/core/permissions.d.ts +56 -0
  167. package/dist/core/permissions.js +122 -0
  168. package/dist/core/permissions.js.map +1 -0
  169. package/dist/core/preview-proxy.d.ts +70 -0
  170. package/dist/core/preview-proxy.js +563 -0
  171. package/dist/core/preview-proxy.js.map +1 -0
  172. package/dist/core/preview-shot.d.ts +46 -0
  173. package/dist/core/preview-shot.js +109 -0
  174. package/dist/core/preview-shot.js.map +1 -0
  175. package/dist/core/process.d.ts +36 -0
  176. package/dist/core/process.js +168 -0
  177. package/dist/core/process.js.map +1 -0
  178. package/dist/core/prompt-queue.d.ts +136 -0
  179. package/dist/core/prompt-queue.js +269 -0
  180. package/dist/core/prompt-queue.js.map +1 -0
  181. package/dist/core/prompts.d.ts +53 -0
  182. package/dist/core/prompts.js +127 -0
  183. package/dist/core/prompts.js.map +1 -0
  184. package/dist/core/proof.d.ts +82 -0
  185. package/dist/core/proof.js +216 -0
  186. package/dist/core/proof.js.map +1 -0
  187. package/dist/core/provider-text.d.ts +23 -0
  188. package/dist/core/provider-text.js +66 -0
  189. package/dist/core/provider-text.js.map +1 -0
  190. package/dist/core/providers.d.ts +152 -0
  191. package/dist/core/providers.js +343 -0
  192. package/dist/core/providers.js.map +1 -0
  193. package/dist/core/quota.d.ts +33 -0
  194. package/dist/core/quota.js +74 -0
  195. package/dist/core/quota.js.map +1 -0
  196. package/dist/core/recipes.d.ts +50 -0
  197. package/dist/core/recipes.js +99 -0
  198. package/dist/core/recipes.js.map +1 -0
  199. package/dist/core/registry.d.ts +54 -0
  200. package/dist/core/registry.js +62 -5
  201. package/dist/core/registry.js.map +1 -1
  202. package/dist/core/relay-client.d.ts +67 -0
  203. package/dist/core/relay-client.js +184 -0
  204. package/dist/core/relay-client.js.map +1 -0
  205. package/dist/core/relay-protocol.d.ts +140 -0
  206. package/dist/core/relay-protocol.js +189 -0
  207. package/dist/core/relay-protocol.js.map +1 -0
  208. package/dist/core/replay.d.ts +18 -0
  209. package/dist/core/replay.js +191 -0
  210. package/dist/core/replay.js.map +1 -0
  211. package/dist/core/review-triage.d.ts +41 -0
  212. package/dist/core/review-triage.js +124 -0
  213. package/dist/core/review-triage.js.map +1 -0
  214. package/dist/core/router.js +2 -2
  215. package/dist/core/router.js.map +1 -1
  216. package/dist/core/routes.d.ts +39 -3
  217. package/dist/core/routes.js +137 -4
  218. package/dist/core/routes.js.map +1 -1
  219. package/dist/core/run-command.d.ts +14 -0
  220. package/dist/core/run-command.js +45 -0
  221. package/dist/core/run-command.js.map +1 -0
  222. package/dist/core/runner-setup.d.ts +36 -0
  223. package/dist/core/runner-setup.js +114 -0
  224. package/dist/core/runner-setup.js.map +1 -0
  225. package/dist/core/search.js +0 -0
  226. package/dist/core/search.js.map +1 -1
  227. package/dist/core/semantic.d.ts +92 -0
  228. package/dist/core/semantic.js +227 -0
  229. package/dist/core/semantic.js.map +1 -0
  230. package/dist/core/servers.d.ts +115 -0
  231. package/dist/core/servers.js +305 -0
  232. package/dist/core/servers.js.map +1 -0
  233. package/dist/core/setup.js +64 -6
  234. package/dist/core/setup.js.map +1 -1
  235. package/dist/core/shield.d.ts +17 -0
  236. package/dist/core/shield.js +81 -0
  237. package/dist/core/shield.js.map +1 -0
  238. package/dist/core/shift.d.ts +69 -0
  239. package/dist/core/shift.js +120 -0
  240. package/dist/core/shift.js.map +1 -0
  241. package/dist/core/skill-install.d.ts +125 -0
  242. package/dist/core/skill-install.js +328 -0
  243. package/dist/core/skill-install.js.map +1 -0
  244. package/dist/core/skills.d.ts +116 -0
  245. package/dist/core/skills.js +223 -0
  246. package/dist/core/skills.js.map +1 -0
  247. package/dist/core/step-conditions.d.ts +65 -0
  248. package/dist/core/step-conditions.js +102 -0
  249. package/dist/core/step-conditions.js.map +1 -0
  250. package/dist/core/subagents.d.ts +45 -0
  251. package/dist/core/subagents.js +90 -0
  252. package/dist/core/subagents.js.map +1 -0
  253. package/dist/core/team-canon.d.ts +42 -0
  254. package/dist/core/team-canon.js +121 -0
  255. package/dist/core/team-canon.js.map +1 -0
  256. package/dist/core/team-crypto.d.ts +97 -0
  257. package/dist/core/team-crypto.js +175 -0
  258. package/dist/core/team-crypto.js.map +1 -0
  259. package/dist/core/team-hub.d.ts +514 -0
  260. package/dist/core/team-hub.js +964 -0
  261. package/dist/core/team-hub.js.map +1 -0
  262. package/dist/core/team-landing.d.ts +239 -0
  263. package/dist/core/team-landing.js +504 -0
  264. package/dist/core/team-landing.js.map +1 -0
  265. package/dist/core/team-leases.d.ts +44 -0
  266. package/dist/core/team-leases.js +145 -0
  267. package/dist/core/team-leases.js.map +1 -0
  268. package/dist/core/team-memory.d.ts +42 -0
  269. package/dist/core/team-memory.js +84 -0
  270. package/dist/core/team-memory.js.map +1 -0
  271. package/dist/core/team-policy.d.ts +85 -0
  272. package/dist/core/team-policy.js +191 -0
  273. package/dist/core/team-policy.js.map +1 -0
  274. package/dist/core/thread-md.d.ts +6 -0
  275. package/dist/core/thread-md.js +42 -0
  276. package/dist/core/thread-md.js.map +1 -0
  277. package/dist/core/turn-stats.d.ts +61 -0
  278. package/dist/core/turn-stats.js +124 -0
  279. package/dist/core/turn-stats.js.map +1 -0
  280. package/dist/core/updater.d.ts +73 -0
  281. package/dist/core/updater.js +153 -0
  282. package/dist/core/updater.js.map +1 -0
  283. package/dist/core/usage.d.ts +75 -0
  284. package/dist/core/usage.js +163 -0
  285. package/dist/core/usage.js.map +1 -0
  286. package/dist/core/windows-jobs.d.ts +15 -0
  287. package/dist/core/windows-jobs.js +170 -0
  288. package/dist/core/windows-jobs.js.map +1 -0
  289. package/dist/core/worktree-merge.d.ts +57 -0
  290. package/dist/core/worktree-merge.js +123 -0
  291. package/dist/core/worktree-merge.js.map +1 -0
  292. package/dist/core/worktree.d.ts +5 -0
  293. package/dist/core/worktree.js +45 -5
  294. package/dist/core/worktree.js.map +1 -1
  295. package/dist/core/worktrees.d.ts +6 -0
  296. package/dist/core/worktrees.js +21 -0
  297. package/dist/core/worktrees.js.map +1 -0
  298. package/dist/daemon/app-page.d.ts +0 -17
  299. package/dist/daemon/app-page.js +10 -6346
  300. package/dist/daemon/app-page.js.map +1 -1
  301. package/dist/daemon/auth.d.ts +24 -3
  302. package/dist/daemon/auth.js +66 -7
  303. package/dist/daemon/auth.js.map +1 -1
  304. package/dist/daemon/board.d.ts +7 -0
  305. package/dist/daemon/board.js +12 -1
  306. package/dist/daemon/board.js.map +1 -1
  307. package/dist/daemon/brand-icons.d.ts +6 -1
  308. package/dist/daemon/brand-icons.js +11 -3
  309. package/dist/daemon/brand-icons.js.map +1 -1
  310. package/dist/daemon/client.d.ts +665 -1
  311. package/dist/daemon/client.js +381 -3
  312. package/dist/daemon/client.js.map +1 -1
  313. package/dist/daemon/delivery.d.ts +34 -0
  314. package/dist/daemon/delivery.js +63 -0
  315. package/dist/daemon/delivery.js.map +1 -0
  316. package/dist/daemon/deploys.d.ts +62 -0
  317. package/dist/daemon/deploys.js +159 -0
  318. package/dist/daemon/deploys.js.map +1 -0
  319. package/dist/daemon/landing.d.ts +192 -0
  320. package/dist/daemon/landing.js +992 -0
  321. package/dist/daemon/landing.js.map +1 -0
  322. package/dist/daemon/onboard.d.ts +150 -0
  323. package/dist/daemon/onboard.js +390 -0
  324. package/dist/daemon/onboard.js.map +1 -0
  325. package/dist/daemon/proof.d.ts +70 -0
  326. package/dist/daemon/proof.js +172 -0
  327. package/dist/daemon/proof.js.map +1 -0
  328. package/dist/daemon/push.d.ts +19 -1
  329. package/dist/daemon/push.js +90 -3
  330. package/dist/daemon/push.js.map +1 -1
  331. package/dist/daemon/relay.d.ts +82 -0
  332. package/dist/daemon/relay.js +351 -0
  333. package/dist/daemon/relay.js.map +1 -0
  334. package/dist/daemon/routes/agent-discovery.d.ts +4 -0
  335. package/dist/daemon/routes/agent-discovery.js +29 -0
  336. package/dist/daemon/routes/agent-discovery.js.map +1 -0
  337. package/dist/daemon/routes/agent-settings.d.ts +4 -0
  338. package/dist/daemon/routes/agent-settings.js +94 -0
  339. package/dist/daemon/routes/agent-settings.js.map +1 -0
  340. package/dist/daemon/routes/agents.d.ts +4 -0
  341. package/dist/daemon/routes/agents.js +105 -0
  342. package/dist/daemon/routes/agents.js.map +1 -0
  343. package/dist/daemon/routes/alerts.d.ts +4 -0
  344. package/dist/daemon/routes/alerts.js +137 -0
  345. package/dist/daemon/routes/alerts.js.map +1 -0
  346. package/dist/daemon/routes/approvals.d.ts +4 -0
  347. package/dist/daemon/routes/approvals.js +40 -0
  348. package/dist/daemon/routes/approvals.js.map +1 -0
  349. package/dist/daemon/routes/assets.d.ts +3 -0
  350. package/dist/daemon/routes/assets.js +64 -0
  351. package/dist/daemon/routes/assets.js.map +1 -0
  352. package/dist/daemon/routes/board.d.ts +4 -0
  353. package/dist/daemon/routes/board.js +147 -0
  354. package/dist/daemon/routes/board.js.map +1 -0
  355. package/dist/daemon/routes/brain.d.ts +4 -0
  356. package/dist/daemon/routes/brain.js +270 -0
  357. package/dist/daemon/routes/brain.js.map +1 -0
  358. package/dist/daemon/routes/chats.d.ts +4 -0
  359. package/dist/daemon/routes/chats.js +249 -0
  360. package/dist/daemon/routes/chats.js.map +1 -0
  361. package/dist/daemon/routes/clients.d.ts +4 -0
  362. package/dist/daemon/routes/clients.js +49 -0
  363. package/dist/daemon/routes/clients.js.map +1 -0
  364. package/dist/daemon/routes/cloud.d.ts +4 -0
  365. package/dist/daemon/routes/cloud.js +46 -0
  366. package/dist/daemon/routes/cloud.js.map +1 -0
  367. package/dist/daemon/routes/config.d.ts +4 -0
  368. package/dist/daemon/routes/config.js +35 -0
  369. package/dist/daemon/routes/config.js.map +1 -0
  370. package/dist/daemon/routes/context.d.ts +51 -0
  371. package/dist/daemon/routes/context.js +2 -0
  372. package/dist/daemon/routes/context.js.map +1 -0
  373. package/dist/daemon/routes/crews.d.ts +4 -0
  374. package/dist/daemon/routes/crews.js +149 -0
  375. package/dist/daemon/routes/crews.js.map +1 -0
  376. package/dist/daemon/routes/files.d.ts +3 -0
  377. package/dist/daemon/routes/files.js +253 -0
  378. package/dist/daemon/routes/files.js.map +1 -0
  379. package/dist/daemon/routes/git.d.ts +4 -0
  380. package/dist/daemon/routes/git.js +96 -0
  381. package/dist/daemon/routes/git.js.map +1 -0
  382. package/dist/daemon/routes/history.d.ts +4 -0
  383. package/dist/daemon/routes/history.js +101 -0
  384. package/dist/daemon/routes/history.js.map +1 -0
  385. package/dist/daemon/routes/integrations-project.d.ts +3 -0
  386. package/dist/daemon/routes/integrations-project.js +103 -0
  387. package/dist/daemon/routes/integrations-project.js.map +1 -0
  388. package/dist/daemon/routes/integrations.d.ts +3 -0
  389. package/dist/daemon/routes/integrations.js +111 -0
  390. package/dist/daemon/routes/integrations.js.map +1 -0
  391. package/dist/daemon/routes/logs.d.ts +3 -0
  392. package/dist/daemon/routes/logs.js +40 -0
  393. package/dist/daemon/routes/logs.js.map +1 -0
  394. package/dist/daemon/routes/messages.d.ts +4 -0
  395. package/dist/daemon/routes/messages.js +60 -0
  396. package/dist/daemon/routes/messages.js.map +1 -0
  397. package/dist/daemon/routes/observability.d.ts +4 -0
  398. package/dist/daemon/routes/observability.js +247 -0
  399. package/dist/daemon/routes/observability.js.map +1 -0
  400. package/dist/daemon/routes/onboard.d.ts +12 -0
  401. package/dist/daemon/routes/onboard.js +67 -0
  402. package/dist/daemon/routes/onboard.js.map +1 -0
  403. package/dist/daemon/routes/orchestra.d.ts +4 -0
  404. package/dist/daemon/routes/orchestra.js +110 -0
  405. package/dist/daemon/routes/orchestra.js.map +1 -0
  406. package/dist/daemon/routes/pairing.d.ts +4 -0
  407. package/dist/daemon/routes/pairing.js +155 -0
  408. package/dist/daemon/routes/pairing.js.map +1 -0
  409. package/dist/daemon/routes/preview.d.ts +24 -0
  410. package/dist/daemon/routes/preview.js +122 -0
  411. package/dist/daemon/routes/preview.js.map +1 -0
  412. package/dist/daemon/routes/project-actions.d.ts +4 -0
  413. package/dist/daemon/routes/project-actions.js +53 -0
  414. package/dist/daemon/routes/project-actions.js.map +1 -0
  415. package/dist/daemon/routes/project-team.d.ts +4 -0
  416. package/dist/daemon/routes/project-team.js +201 -0
  417. package/dist/daemon/routes/project-team.js.map +1 -0
  418. package/dist/daemon/routes/projects.d.ts +14 -0
  419. package/dist/daemon/routes/projects.js +138 -0
  420. package/dist/daemon/routes/projects.js.map +1 -0
  421. package/dist/daemon/routes/prompts.d.ts +4 -0
  422. package/dist/daemon/routes/prompts.js +44 -0
  423. package/dist/daemon/routes/prompts.js.map +1 -0
  424. package/dist/daemon/routes/proof.d.ts +4 -0
  425. package/dist/daemon/routes/proof.js +22 -0
  426. package/dist/daemon/routes/proof.js.map +1 -0
  427. package/dist/daemon/routes/providers.d.ts +3 -0
  428. package/dist/daemon/routes/providers.js +60 -0
  429. package/dist/daemon/routes/providers.js.map +1 -0
  430. package/dist/daemon/routes/public.d.ts +4 -0
  431. package/dist/daemon/routes/public.js +64 -0
  432. package/dist/daemon/routes/public.js.map +1 -0
  433. package/dist/daemon/routes/queue.d.ts +4 -0
  434. package/dist/daemon/routes/queue.js +140 -0
  435. package/dist/daemon/routes/queue.js.map +1 -0
  436. package/dist/daemon/routes/routing.d.ts +4 -0
  437. package/dist/daemon/routes/routing.js +61 -0
  438. package/dist/daemon/routes/routing.js.map +1 -0
  439. package/dist/daemon/routes/runtime-handler.d.ts +2 -0
  440. package/dist/daemon/routes/runtime-handler.js +63 -0
  441. package/dist/daemon/routes/runtime-handler.js.map +1 -0
  442. package/dist/daemon/routes/search.d.ts +4 -0
  443. package/dist/daemon/routes/search.js +17 -0
  444. package/dist/daemon/routes/search.js.map +1 -0
  445. package/dist/daemon/routes/servers.d.ts +4 -0
  446. package/dist/daemon/routes/servers.js +133 -0
  447. package/dist/daemon/routes/servers.js.map +1 -0
  448. package/dist/daemon/routes/setup.d.ts +3 -0
  449. package/dist/daemon/routes/setup.js +50 -0
  450. package/dist/daemon/routes/setup.js.map +1 -0
  451. package/dist/daemon/routes/shift.d.ts +4 -0
  452. package/dist/daemon/routes/shift.js +30 -0
  453. package/dist/daemon/routes/shift.js.map +1 -0
  454. package/dist/daemon/routes/specs.d.ts +4 -0
  455. package/dist/daemon/routes/specs.js +57 -0
  456. package/dist/daemon/routes/specs.js.map +1 -0
  457. package/dist/daemon/routes/task-delivery.d.ts +4 -0
  458. package/dist/daemon/routes/task-delivery.js +38 -0
  459. package/dist/daemon/routes/task-delivery.js.map +1 -0
  460. package/dist/daemon/routes/team.d.ts +4 -0
  461. package/dist/daemon/routes/team.js +193 -0
  462. package/dist/daemon/routes/team.js.map +1 -0
  463. package/dist/daemon/routes/terminals.d.ts +4 -0
  464. package/dist/daemon/routes/terminals.js +87 -0
  465. package/dist/daemon/routes/terminals.js.map +1 -0
  466. package/dist/daemon/routes/tools.d.ts +4 -0
  467. package/dist/daemon/routes/tools.js +180 -0
  468. package/dist/daemon/routes/tools.js.map +1 -0
  469. package/dist/daemon/routes/updates.d.ts +4 -0
  470. package/dist/daemon/routes/updates.js +89 -0
  471. package/dist/daemon/routes/updates.js.map +1 -0
  472. package/dist/daemon/routes/voice.d.ts +4 -0
  473. package/dist/daemon/routes/voice.js +53 -0
  474. package/dist/daemon/routes/voice.js.map +1 -0
  475. package/dist/daemon/runner.d.ts +186 -0
  476. package/dist/daemon/runner.js +612 -0
  477. package/dist/daemon/runner.js.map +1 -0
  478. package/dist/daemon/runtime/accounting.d.ts +130 -0
  479. package/dist/daemon/runtime/accounting.js +303 -0
  480. package/dist/daemon/runtime/accounting.js.map +1 -0
  481. package/dist/daemon/runtime/agents.d.ts +16 -0
  482. package/dist/daemon/runtime/agents.js +71 -0
  483. package/dist/daemon/runtime/agents.js.map +1 -0
  484. package/dist/daemon/runtime/briefings.d.ts +134 -0
  485. package/dist/daemon/runtime/briefings.js +329 -0
  486. package/dist/daemon/runtime/briefings.js.map +1 -0
  487. package/dist/daemon/runtime/native-usage.d.ts +21 -0
  488. package/dist/daemon/runtime/native-usage.js +71 -0
  489. package/dist/daemon/runtime/native-usage.js.map +1 -0
  490. package/dist/daemon/runtime/queue.d.ts +94 -0
  491. package/dist/daemon/runtime/queue.js +222 -0
  492. package/dist/daemon/runtime/queue.js.map +1 -0
  493. package/dist/daemon/runtime/relay.d.ts +93 -0
  494. package/dist/daemon/runtime/relay.js +240 -0
  495. package/dist/daemon/runtime/relay.js.map +1 -0
  496. package/dist/daemon/runtime/turns.d.ts +202 -0
  497. package/dist/daemon/runtime/turns.js +663 -0
  498. package/dist/daemon/runtime/turns.js.map +1 -0
  499. package/dist/daemon/runtime-support.d.ts +76 -0
  500. package/dist/daemon/runtime-support.js +134 -0
  501. package/dist/daemon/runtime-support.js.map +1 -0
  502. package/dist/daemon/runtime.d.ts +739 -70
  503. package/dist/daemon/runtime.js +2751 -470
  504. package/dist/daemon/runtime.js.map +1 -1
  505. package/dist/daemon/server.d.ts +82 -55
  506. package/dist/daemon/server.js +674 -1435
  507. package/dist/daemon/server.js.map +1 -1
  508. package/dist/daemon/shift.d.ts +70 -0
  509. package/dist/daemon/shift.js +170 -0
  510. package/dist/daemon/shift.js.map +1 -0
  511. package/dist/daemon/specs.d.ts +92 -0
  512. package/dist/daemon/specs.js +285 -0
  513. package/dist/daemon/specs.js.map +1 -0
  514. package/dist/daemon/system.d.ts +208 -0
  515. package/dist/daemon/system.js +557 -0
  516. package/dist/daemon/system.js.map +1 -0
  517. package/dist/daemon/tasks.js +1 -1
  518. package/dist/daemon/team-brain.d.ts +136 -0
  519. package/dist/daemon/team-brain.js +659 -0
  520. package/dist/daemon/team-brain.js.map +1 -0
  521. package/dist/daemon/team-coordinator.d.ts +78 -0
  522. package/dist/daemon/team-coordinator.js +408 -0
  523. package/dist/daemon/team-coordinator.js.map +1 -0
  524. package/dist/daemon/team.d.ts +339 -0
  525. package/dist/daemon/team.js +1433 -0
  526. package/dist/daemon/team.js.map +1 -0
  527. package/dist/daemon/terminals.d.ts +22 -3
  528. package/dist/daemon/terminals.js +236 -16
  529. package/dist/daemon/terminals.js.map +1 -1
  530. package/dist/hub/client.d.ts +120 -0
  531. package/dist/hub/client.js +228 -0
  532. package/dist/hub/client.js.map +1 -0
  533. package/dist/hub/server.d.ts +32 -0
  534. package/dist/hub/server.js +180 -0
  535. package/dist/hub/server.js.map +1 -0
  536. package/dist/hub/supabase-client.d.ts +220 -0
  537. package/dist/hub/supabase-client.js +843 -0
  538. package/dist/hub/supabase-client.js.map +1 -0
  539. package/dist/observability/ask.d.ts +91 -0
  540. package/dist/observability/ask.js +179 -0
  541. package/dist/observability/ask.js.map +1 -0
  542. package/dist/observability/clickhouse.d.ts +33 -0
  543. package/dist/observability/clickhouse.js +48 -0
  544. package/dist/observability/clickhouse.js.map +1 -0
  545. package/dist/observability/decisions.d.ts +176 -0
  546. package/dist/observability/decisions.js +377 -0
  547. package/dist/observability/decisions.js.map +1 -0
  548. package/dist/observability/index.d.ts +89 -0
  549. package/dist/observability/index.js +348 -0
  550. package/dist/observability/index.js.map +1 -0
  551. package/dist/observability/insights.d.ts +151 -0
  552. package/dist/observability/insights.js +319 -0
  553. package/dist/observability/insights.js.map +1 -0
  554. package/dist/observability/logs-query.d.ts +82 -0
  555. package/dist/observability/logs-query.js +102 -0
  556. package/dist/observability/logs-query.js.map +1 -0
  557. package/dist/observability/logs.d.ts +72 -0
  558. package/dist/observability/logs.js +295 -0
  559. package/dist/observability/logs.js.map +1 -0
  560. package/dist/observability/metrics.d.ts +86 -0
  561. package/dist/observability/metrics.js +303 -0
  562. package/dist/observability/metrics.js.map +1 -0
  563. package/dist/observability/otlp.d.ts +127 -0
  564. package/dist/observability/otlp.js +242 -0
  565. package/dist/observability/otlp.js.map +1 -0
  566. package/dist/observability/snapshots.d.ts +43 -0
  567. package/dist/observability/snapshots.js +129 -0
  568. package/dist/observability/snapshots.js.map +1 -0
  569. package/dist/observability/triage.d.ts +49 -0
  570. package/dist/observability/triage.js +284 -0
  571. package/dist/observability/triage.js.map +1 -0
  572. package/dist/providers/adapter.d.ts +50 -0
  573. package/dist/providers/adapter.js +38 -0
  574. package/dist/providers/adapter.js.map +1 -0
  575. package/dist/providers/agent.d.ts +110 -0
  576. package/dist/providers/agent.js +487 -0
  577. package/dist/providers/agent.js.map +1 -0
  578. package/dist/providers/approvals.d.ts +24 -0
  579. package/dist/providers/approvals.js +78 -0
  580. package/dist/providers/approvals.js.map +1 -0
  581. package/dist/providers/attachments.d.ts +20 -0
  582. package/dist/providers/attachments.js +51 -0
  583. package/dist/providers/attachments.js.map +1 -0
  584. package/dist/providers/claude/adapter.d.ts +99 -0
  585. package/dist/providers/claude/adapter.js +790 -0
  586. package/dist/providers/claude/adapter.js.map +1 -0
  587. package/dist/providers/codex/adapter.d.ts +113 -0
  588. package/dist/providers/codex/adapter.js +881 -0
  589. package/dist/providers/codex/adapter.js.map +1 -0
  590. package/dist/providers/codex/instructions.d.ts +11 -0
  591. package/dist/providers/codex/instructions.js +154 -0
  592. package/dist/providers/codex/instructions.js.map +1 -0
  593. package/dist/providers/codex/rpc.d.ts +36 -0
  594. package/dist/providers/codex/rpc.js +104 -0
  595. package/dist/providers/codex/rpc.js.map +1 -0
  596. package/dist/providers/contracts.d.ts +339 -0
  597. package/dist/providers/contracts.js +19 -0
  598. package/dist/providers/contracts.js.map +1 -0
  599. package/dist/providers/directory.d.ts +70 -0
  600. package/dist/providers/directory.js +95 -0
  601. package/dist/providers/directory.js.map +1 -0
  602. package/dist/providers/errors.d.ts +47 -0
  603. package/dist/providers/errors.js +24 -0
  604. package/dist/providers/errors.js.map +1 -0
  605. package/dist/providers/index.d.ts +18 -0
  606. package/dist/providers/index.js +19 -0
  607. package/dist/providers/index.js.map +1 -0
  608. package/dist/providers/ingestion.d.ts +80 -0
  609. package/dist/providers/ingestion.js +330 -0
  610. package/dist/providers/ingestion.js.map +1 -0
  611. package/dist/providers/live.d.ts +22 -0
  612. package/dist/providers/live.js +56 -0
  613. package/dist/providers/live.js.map +1 -0
  614. package/dist/providers/process.d.ts +29 -0
  615. package/dist/providers/process.js +71 -0
  616. package/dist/providers/process.js.map +1 -0
  617. package/dist/providers/reaper.d.ts +33 -0
  618. package/dist/providers/reaper.js +65 -0
  619. package/dist/providers/reaper.js.map +1 -0
  620. package/dist/providers/service.d.ts +87 -0
  621. package/dist/providers/service.js +256 -0
  622. package/dist/providers/service.js.map +1 -0
  623. package/dist/sdk.d.ts +1 -1
  624. package/dist/sdk.js +1 -1
  625. package/dist/sdk.js.map +1 -1
  626. package/dist/types.d.ts +385 -6
  627. package/dist/types.js +1 -0
  628. package/dist/types.js.map +1 -1
  629. package/dist/version.d.ts +7 -0
  630. package/dist/version.js +16 -0
  631. package/dist/version.js.map +1 -0
  632. package/dist/web/app.css +4643 -0
  633. package/dist/web/app.js +22640 -0
  634. package/dist/web/app.js.map +7 -0
  635. package/dist/web/shell.html +39 -0
  636. package/docs/README.md +21 -0
  637. package/docs/SETUP.md +10 -0
  638. package/docs/adapters.md +21 -8
  639. package/docs/brain-continuity.md +186 -0
  640. package/docs/cloud.md +108 -0
  641. package/docs/crews.md +62 -0
  642. package/docs/design-system.md +31 -2
  643. package/docs/integration-notes.md +128 -20
  644. package/docs/loompad.md +105 -0
  645. package/docs/observability.md +220 -0
  646. package/docs/play-store.md +123 -0
  647. package/docs/proposals/agent-teams.md +249 -0
  648. package/docs/proposals/brain-continuity-implementation.md +468 -0
  649. package/docs/proposals/brain-session-continuity.md +623 -0
  650. package/docs/proposals/t3code-port.md +81 -0
  651. package/docs/proposals/t3code-reference.md +95 -0
  652. package/docs/refactoring/AGENT-TEAMS-PROGRESS.md +88 -0
  653. package/docs/refactoring/BOUNDARIES.md +120 -0
  654. package/docs/refactoring/BRAIN-AUDIT.md +176 -0
  655. package/docs/refactoring/BRAIN-NOTES.md +390 -0
  656. package/docs/refactoring/BRAIN-TODO.md +214 -0
  657. package/docs/refactoring/NOTES.md +129 -0
  658. package/docs/refactoring/T3-PORT-NOTES.md +165 -0
  659. package/docs/refactoring/T3-PORT-TODO.md +123 -0
  660. package/docs/refactoring/TODO.md +46 -0
  661. package/docs/refactoring/brain-benchmark-2026-09-28.json +48 -0
  662. package/docs/refactoring/brain-benchmark-audit-2026-09-28.json +48 -0
  663. package/docs/release-notes/v0.2.0.md +35 -0
  664. package/docs/release-notes/v0.2.1.md +59 -0
  665. package/docs/release-notes/v0.2.3.md +77 -0
  666. package/docs/release-notes/v0.2.4.md +128 -0
  667. package/docs/release-notes/v0.2.5.md +153 -0
  668. package/docs/release-notes/v0.2.6.md +62 -0
  669. package/docs/release-notes/v1.0.0.md +120 -0
  670. package/docs/release-notes/v1.0.1.md +77 -0
  671. package/docs/release-notes/v1.1.0.md +71 -0
  672. package/docs/release-notes/v1.1.1.md +38 -0
  673. package/docs/teams-architecture.md +697 -0
  674. package/docs/teams.md +171 -0
  675. package/docs/windows.md +85 -0
  676. package/package.json +22 -9
  677. package/dist/adapters/claude-code.d.ts +0 -26
  678. package/dist/adapters/claude-code.js +0 -209
  679. package/dist/adapters/claude-code.js.map +0 -1
  680. package/dist/adapters/codex.d.ts +0 -51
  681. package/dist/adapters/codex.js +0 -291
  682. package/dist/adapters/codex.js.map +0 -1
  683. package/docs/img/board-linear.png +0 -0
  684. package/docs/img/board-projects.png +0 -0
  685. package/docs/img/brain.png +0 -0
  686. package/docs/img/settings.png +0 -0
  687. package/docs/img/workspace.png +0 -0
@@ -0,0 +1,663 @@
1
+ import { NotHolderError } from "../../core/baton.js";
2
+ import { triage } from "../../core/review-triage.js";
3
+ import * as checkpoints from "../../core/checkpoint.js";
4
+ import { push as gitPush, stageAndCommitFiles } from "../../core/git.js";
5
+ import { logbook } from "../../core/logbook.js";
6
+ import { writeMcpSession } from "../../core/mcp.js";
7
+ import { NO_CHANGES } from "../../core/step-conditions.js";
8
+ import { agentAllowed } from "../../core/team-policy.js";
9
+ import { diffSinceSnapshot, porcelainStatus } from "../../core/worktree.js";
10
+ import { MAIN_CHAT, isAdapter } from "../../types.js";
11
+ import { planModeBriefing } from '../runtime-support.js';
12
+ import { ProviderAgent } from "../../providers/agent.js";
13
+ import { randomUUID } from "node:crypto";
14
+ import { ContinuityError, NativeDispatchRejected } from "../../core/continuity/contracts.js";
15
+ import { isNativeKind } from "../../core/continuity/capabilities.js";
16
+ /** How long you asked the answer to be, this once ("" for the usual). */
17
+ function lengthLine(length) {
18
+ return length === "brief"
19
+ ? "Keep this reply brief: the answer first, a few sentences at most, no preamble."
20
+ : length === "detailed"
21
+ ? "Give a detailed reply this time: explain your reasoning and the trade-offs, with examples where they help."
22
+ : "";
23
+ }
24
+ /** Owns turns state for exactly one open project. */
25
+ export class RuntimeTurns {
26
+ host;
27
+ constructor(host) {
28
+ this.host = host;
29
+ }
30
+ /**
31
+ * Which conversation each agent's current turn belongs to. Set when a turn
32
+ * starts and left in place afterwards — an agent's trailing events (a late
33
+ * run_complete, a diff) still belong to the chat that prompted them.
34
+ */
35
+ turnChat = new Map();
36
+ // A turn's cost lands on a `turn_cost` status just before its `run_complete`
37
+ // (the CLI reports it mid-stream). We hold it here so the completed turn — and
38
+ // therefore its exported gen_ai span — carries the real cost, not just tokens.
39
+ pendingCost = new Map();
40
+ // Turn text accumulated per agent (from its message events) so we can extract
41
+ // structured decisions once the turn completes. Reset after each run_complete.
42
+ turnText = new Map();
43
+ /** Pre-turn porcelain snapshots, for per-prompt diff attribution. */
44
+ preTurnTree = new Map();
45
+ /** The checkpoint taken before each agent's current turn (#101). */
46
+ turnCheckpoint = new Map();
47
+ /**
48
+ * The diff of each agent's most recent turn, as a promise.
49
+ *
50
+ * A route's step conditions ("run the reviewer if more than 200 lines
51
+ * changed") are decided the moment the turn completes, which is before the
52
+ * diff has finished being computed. Keeping the promise lets the route wait
53
+ * for the real numbers instead of reading the previous turn's.
54
+ */
55
+ lastTurnDiff = new Map();
56
+ /** App-owned diff/commit writes finish before the next native writer starts. */
57
+ postTurn = new Map();
58
+ // -------------------------------------------------------------------------
59
+ // Stale sessions
60
+ // -------------------------------------------------------------------------
61
+ /**
62
+ * When each adapter's current turn started. Set at dispatch, cleared when its
63
+ * run_complete / error / interrupted lands. An entry much older than any
64
+ * plausible turn is a hung session: the process is alive enough to hold
65
+ * `busy` and dead enough to never finish, which blocks every dispatch with
66
+ * "is busy" until someone notices.
67
+ */
68
+ busySince = new Map();
69
+ preparing = new Map();
70
+ /** Native requests between capture and settlement (or queueing). */
71
+ inFlight = new Set();
72
+ /**
73
+ * Write down what the files are, before a turn changes them (#101).
74
+ *
75
+ * Announced in the log so the thread can offer "put it back" on the turn
76
+ * that follows it, and so the list survives a daemon restart with the
77
+ * labels intact. Failing is not an error: a checkpoint is a courtesy, and
78
+ * a project that isn't a git repo simply doesn't get one. What it must
79
+ * never do is stop the turn.
80
+ */
81
+ async checkpointBefore(agentId, prompt) {
82
+ try {
83
+ const label = prompt.replace(/\s+/g, " ").trim().slice(0, 120) || `a turn by ${agentId}`;
84
+ const cp = await checkpoints.capture(this.host.agentDir(agentId), label);
85
+ if (!cp || this.host.closed)
86
+ return;
87
+ // Carried onto this turn's turn_diff, so the card that shows what
88
+ // changed also knows the point to put it back to. Working it out in the
89
+ // client by "the checkpoint nearest above this card" would be right
90
+ // until the day two turns interleave.
91
+ this.turnCheckpoint.set(agentId, cp.id);
92
+ this.host.log.append({
93
+ kind: "checkpoint",
94
+ agentId,
95
+ payload: { id: cp.id, label: cp.label, at: cp.at, dirty: cp.dirty, branch: cp.branch, reason: "before_turn" },
96
+ });
97
+ }
98
+ catch {
99
+ /* never the reason a turn doesn't run */
100
+ }
101
+ }
102
+ /** Every point this project's files can be put back to, newest first. */
103
+ checkpoints() {
104
+ return checkpoints.list(this.host.info.dir);
105
+ }
106
+ /**
107
+ * Put the files back, and say what moved.
108
+ *
109
+ * Refused while an agent is mid-turn: rewinding the tree under a running
110
+ * agent gives it a working directory that contradicts everything it has
111
+ * read this turn, and the damage lands in whatever it writes next.
112
+ */
113
+ async rewind(id) {
114
+ if (this.host.continuity?.store.activeReceipts().length)
115
+ throw new ContinuityError("recovery_required", "finish or reconcile native writers before rewinding files");
116
+ const busy = [...this.busySince.keys()];
117
+ if (busy.length) {
118
+ throw new Error(`${busy.join(", ")} ${busy.length === 1 ? "is" : "are"} mid-turn — stop the turn first, or the rewind lands underneath it`);
119
+ }
120
+ const out = await checkpoints.restore(this.host.info.dir, id);
121
+ this.host.log.append({
122
+ kind: "checkpoint",
123
+ payload: {
124
+ id: out.restored.id,
125
+ label: out.restored.label,
126
+ at: Date.now(),
127
+ reason: "rewound",
128
+ files: out.changed.length,
129
+ undo: out.undo.id,
130
+ },
131
+ });
132
+ return out;
133
+ }
134
+ /** What an agent's last turn changed — for route step conditions. */
135
+ async turnFacts(agentId) {
136
+ const diff = await (this.lastTurnDiff.get(agentId) ?? Promise.resolve(null));
137
+ if (!diff)
138
+ return NO_CHANGES;
139
+ return { files: diff.files.map((f) => f.path), added: diff.added, removed: diff.removed };
140
+ }
141
+ /** After a turn: log which files that prompt changed (turn_diff), then learn. */
142
+ captureTurnDiff(agentId) {
143
+ this.postTurn.delete(agentId);
144
+ const before = this.preTurnTree.get(agentId);
145
+ if (before === undefined) {
146
+ // No snapshot (e.g. a turn with no pre-tree) — still worth reading.
147
+ // The previous turn's diff goes with it: a route asking what this turn
148
+ // changed must not be handed the last one's numbers.
149
+ this.lastTurnDiff.delete(agentId);
150
+ this.host.extractMemory(agentId, []);
151
+ return;
152
+ }
153
+ this.preTurnTree.delete(agentId);
154
+ const checkpoint = this.turnCheckpoint.get(agentId);
155
+ this.turnCheckpoint.delete(agentId);
156
+ // The chat the turn ran in, read now: by the time the diff is computed the
157
+ // agent may already be on its next turn somewhere else. Without it, every
158
+ // "changed N files" card landed in Main whatever thread asked.
159
+ const turnChat = this.turnChat.get(agentId);
160
+ const pending = diffSinceSnapshot(this.host.agentDir(agentId), before).catch(() => null);
161
+ this.lastTurnDiff.set(agentId, pending);
162
+ const finalized = pending
163
+ .then(async (diff) => {
164
+ if (this.host.closed)
165
+ return;
166
+ if (diff) {
167
+ this.host.log.append({
168
+ kind: "turn_diff",
169
+ ...(turnChat && turnChat !== MAIN_CHAT ? { chat: turnChat } : {}),
170
+ agentId,
171
+ payload: {
172
+ files: diff.files,
173
+ added: diff.added,
174
+ removed: diff.removed,
175
+ patch: diff.patch,
176
+ truncated: diff.truncated,
177
+ // what to read first: the files ranked by what a reviewer would want to see (core/review-triage.ts)
178
+ triage: triage(diff.patch, diff.files).files.slice(0, 40).map(({ path, level, reasons }) => ({ path, level, reasons: reasons.slice(0, 3) })),
179
+ // The point these changes can be put back to, when there is one
180
+ // — a project that isn't a git repo has no checkpoint to offer.
181
+ ...(checkpoint ? { checkpoint } : {}),
182
+ },
183
+ });
184
+ await this.commitTurn(agentId, diff.files.map((f) => f.path));
185
+ }
186
+ // Learn from the turn once we know which files it touched — the files
187
+ // sharpen candidate retrieval. Runs after the diff so recentTurnFiles
188
+ // isn't needed; the files are right here.
189
+ this.host.extractMemory(agentId, (diff?.files ?? []).map((f) => f.path));
190
+ })
191
+ .catch(() => this.host.extractMemory(agentId, []));
192
+ this.postTurn.set(agentId, finalized);
193
+ }
194
+ /**
195
+ * Opt-in: commit a turn's changes as they land, with the agent as co-author.
196
+ *
197
+ * git blame on a fleet's work answered "who wrote this" with whoever ran the
198
+ * daemon. With `git.commitPerTurn` on, each turn's changes become one commit —
199
+ * subject from the prompt that caused them, `Co-Authored-By: <agent> via
200
+ * Loom` so both git log and GitHub attribute the work.
201
+ *
202
+ * Off by default and per-project on purpose: committing is a policy, not a
203
+ * mechanic, and half-done turns land too. Only the files THIS turn touched
204
+ * are staged, so two agents finishing close together each commit their own
205
+ * work rather than whoever finishes second swallowing both.
206
+ */
207
+ async commitTurn(agentId, files) {
208
+ const delivery = this.host.config.git?.delivery ?? "none";
209
+ if ((!this.host.config.git?.commitPerTurn && delivery === "none") || !files.length)
210
+ return;
211
+ try {
212
+ const events = this.host.log.list({ limit: 60 });
213
+ const prompt = [...events].reverse().find((e) => e.kind === "message" && !e.agentId)?.payload.text ?? "";
214
+ const subject = String(prompt).split("\n")[0].slice(0, 68) || `work by ${agentId}`;
215
+ const cfg = this.host.config.agents.find((a) => a.id === agentId);
216
+ const message = `${subject}\n\n` +
217
+ `Turn by ${agentId}${cfg ? ` (${cfg.kind})` : ""} in Loom.\n` +
218
+ `Co-Authored-By: ${agentId} <${agentId}@loom.local>`;
219
+ await stageAndCommitFiles(this.host.agentDir(agentId), files, message);
220
+ this.host.log.append({
221
+ kind: "status",
222
+ agentId,
223
+ payload: { state: "turn_committed", files: files.length, subject },
224
+ });
225
+ // "push" delivers each committed turn; "pr" leaves pushing to the
226
+ // orchestra's branch (a PR per turn is the flood teams complain about).
227
+ if (delivery === "push") {
228
+ const pushed = await gitPush(this.host.agentDir(agentId));
229
+ this.host.log.append({ kind: "status", agentId, payload: { state: "turn_pushed", branch: pushed.branch } });
230
+ }
231
+ }
232
+ catch (err) {
233
+ // A commit that can't happen (not a repo, hooks failed, nothing staged
234
+ // after filters) is a Console line, never a failed turn.
235
+ logbook.warn("git", `turn commit skipped for ${agentId}`, err instanceof Error ? err.message : String(err), this.host.info.id);
236
+ }
237
+ }
238
+ async sendMessage(text, agentId, opts = {}) {
239
+ if (this.host.continuity)
240
+ return this.sendContinuity(text, agentId, opts);
241
+ const source = opts.source ?? "user";
242
+ const chat = opts.chat ?? MAIN_CHAT;
243
+ // A thread that named an agent answers with that agent, whoever holds the
244
+ // baton — which is what lets two threads talk to two agents at once. An
245
+ // explicit target still wins: you asked for that one.
246
+ const bound = this.host.chatBinding(chat);
247
+ let target = agentId ?? bound.agentId ?? this.host.validHolder() ?? this.host.defaultAdapterId();
248
+ const agent = this.host.agent(target);
249
+ if (!isAdapter(agent)) {
250
+ throw new Error(`agent "${target}" is a bridge (read-only) — it cannot take turns`);
251
+ }
252
+ // Before anything is committed — the baton, the message in the thread, the
253
+ // process — check the agent can afford the turn. Refusing after the message
254
+ // is logged would leave a prompt in the conversation that nothing answers.
255
+ this.host.enforceQuarantine(target);
256
+ this.host.enforceBudget(target);
257
+ const kind = this.host.config.agents.find((a) => a.id === target)?.kind ?? "";
258
+ if (this.host.teamPolicy && !agentAllowed(this.host.teamPolicy, kind)) {
259
+ throw new Error(`team policy doesn't allow ${kind} on this repo (loom.team.json)`);
260
+ }
261
+ // The baton is the write lock for work that touches the repository. A
262
+ // thread pinned to an agent doesn't need it to answer a question, and
263
+ // taking it would stop the agent that IS working — so a pinned thread
264
+ // leaves it alone, and the thread that isn't pinned behaves as it always
265
+ // has.
266
+ const pinned = !agentId && bound.agentId === target;
267
+ if (!pinned) {
268
+ const holder = this.host.validHolder();
269
+ if (holder === null) {
270
+ this.host.baton.acquire(target);
271
+ }
272
+ else if (holder !== target) {
273
+ throw new NotHolderError(target, holder);
274
+ }
275
+ }
276
+ // The agent is mid-turn: queue the prompt, in order, and run it when the
277
+ // turn ends. It used to go straight to the adapter, which threw "busy" into
278
+ // an error event — the prompt was lost while the send had said 200.
279
+ // Answering while the agent is still busy is still answering.
280
+ if (source === "user")
281
+ this.host.releaseQuestionHold(target);
282
+ // A turn blocked on a question the agent asked through its tool won't end
283
+ // until that question is answered — so a message queued behind it would
284
+ // wait forever. What you type in that chat IS the answer: it goes to the
285
+ // question, and the turn carries on.
286
+ const open = source === "user" && !opts.fromQueue && this.busySince.has(target) ? this.host.openQuestion(target) : undefined;
287
+ if (open && open.chat === chat) {
288
+ this.host.log.append({ kind: "message", chat, payload: { text, author: "user", answers: open.requestId } });
289
+ await this.host.answerQuestion(target, chat, open.requestId, Object.fromEntries((open.ids.length ? open.ids : ["0"]).map((id) => [id, text])));
290
+ return { agentId: target, answered: true };
291
+ }
292
+ // It shows in the queue, editable, and enters the thread when it's sent.
293
+ if (!opts.fromQueue && this.busySince.has(target)) {
294
+ const item = this.host.queue.add({
295
+ text,
296
+ target: { kind: "agent", agentId: target },
297
+ chat,
298
+ source,
299
+ ...(opts.plan ? { plan: true } : {}),
300
+ ...(opts.length ? { length: opts.length } : {}),
301
+ });
302
+ return { agentId: target, queued: this.host.queue.length, queueId: item.id };
303
+ }
304
+ // A user reply to a paused route's question resumes the route — and to an
305
+ // agent's own question, the queue it was holding.
306
+ if (source === "user") {
307
+ this.host.routes.onUserMessage(target);
308
+ this.host.releaseQuestionHold(target);
309
+ }
310
+ // everything this turn produces belongs to the chat you sent from
311
+ this.turnChat.set(target, chat);
312
+ this.busySince.set(target, Date.now()); // the stale-session clock starts
313
+ this.host.log.append({
314
+ kind: "message",
315
+ chat,
316
+ payload: { text, author: source === "route" ? "loom" : "user", ...(opts.fromQueue ? { fromQueue: true } : {}) },
317
+ });
318
+ let mcp = null;
319
+ try {
320
+ await this.host.ensureStarted(target);
321
+ if (!this.host.isCurrentAgent(agent))
322
+ throw new Error(`agent "${target}" is no longer active`);
323
+ const pendingBriefing = this.host.consumePendingBriefing(target);
324
+ // Prepend the enabled skills so every turn carries them, alongside any
325
+ // one-shot handoff briefing. Empty when no skills are on.
326
+ // A provider agent plans in its own plan mode; the plan it proposes is
327
+ // saved under plans/ by the runtime. Other agents get Loom's briefing.
328
+ const nativePlan = Boolean(opts.plan) && agent instanceof ProviderAgent;
329
+ const briefing = [this.host.agentInstructions(target), this.host.activeSkillsBlock(), pendingBriefing, opts.plan && !nativePlan ? planModeBriefing(text) : "", lengthLine(opts.length)]
330
+ .filter(Boolean)
331
+ .join("\n")
332
+ .trim() || undefined;
333
+ // The project's configured MCP servers, rendered to a temp config file the
334
+ // adapter hands to its CLI. Null when nothing is configured — or when this
335
+ // adapter's CLI has no flag for it, because an "MCP attached" note on a
336
+ // turn that dropped the config would be the same lie in a new place.
337
+ mcp = agent.capabilities.mcp ? writeMcpSession(this.host.healthyMcps()) : null;
338
+ // The thread's model, only ever to an adapter that can act on it — see
339
+ // bindable(), which is where a model that couldn't be honoured is refused.
340
+ const perTurnModel = bound.agentId === target ? bound.model : undefined;
341
+ const input = {
342
+ text,
343
+ chat,
344
+ ...(nativePlan ? { interactionMode: "plan" } : {}),
345
+ ...(briefing ? { briefing } : {}),
346
+ ...(perTurnModel ? { model: perTurnModel } : {}),
347
+ ...(mcp ? { mcp: { configPath: mcp.configPath, servers: mcp.servers } } : {}),
348
+ };
349
+ if (mcp) {
350
+ this.host.log.append({
351
+ kind: "status",
352
+ agentId: target,
353
+ payload: { state: "mcp_attached", servers: mcp.servers.map((s) => s.name) },
354
+ });
355
+ }
356
+ // Snapshot the tree so this prompt's changes can be attributed to it.
357
+ this.preTurnTree.set(target, await porcelainStatus(this.host.agentDir(target)));
358
+ // …and a checkpoint you can actually go back to. The porcelain snapshot
359
+ // above only says *which* paths changed; this holds their content, so
360
+ // "undo what that turn did" is a click rather than a re-typing (#101).
361
+ await this.checkpointBefore(target, text);
362
+ // Fire-and-notify: the turn runs in the background; progress streams
363
+ // into the log and completion lands as run_complete.
364
+ if (!this.host.isCurrentAgent(agent))
365
+ throw new Error(`agent "${target}" is no longer active`);
366
+ void Promise.resolve()
367
+ .then(() => {
368
+ if (!this.host.isCurrentAgent(agent))
369
+ throw new Error(`agent "${target}" is no longer active`);
370
+ return agent.send(input);
371
+ })
372
+ .catch((error) => this.host.dispatchFailed(agent, chat, error))
373
+ // The config file exists for exactly this turn. Cleaned up whether the
374
+ // turn succeeded, failed or was interrupted — a temp file per turn that
375
+ // nothing removes is a slow leak of the project's server URLs.
376
+ .finally(() => mcp?.cleanup());
377
+ return { agentId: target };
378
+ }
379
+ catch (error) {
380
+ mcp?.cleanup();
381
+ this.host.dispatchFailed(agent, chat, error);
382
+ throw error;
383
+ }
384
+ }
385
+ async sendContinuity(text, agentId, opts) {
386
+ const brain = this.host.continuity;
387
+ const chat = opts.chat ?? MAIN_CHAT, source = opts.source ?? "user";
388
+ if (!this.host.chatExists(chat))
389
+ throw new ContinuityError("invalid", "conversation is missing or deleted; create a chat before dispatching");
390
+ const bound = this.host.chatBinding(chat);
391
+ const target = agentId ?? bound.agentId ?? this.host.validHolder() ?? this.host.defaultAdapterId();
392
+ const agent = this.host.agent(target);
393
+ const cfg = this.host.config.agents.find(a => a.id === target);
394
+ if (!isAdapter(agent) || !isNativeKind(cfg.kind))
395
+ throw new ContinuityError("unsupported", "native continuity supports Codex, Claude Code and OpenCode; bridges and model agents use the legacy workflow");
396
+ if (/^\[(?:image|file)\]\s/m.test(text))
397
+ throw new ContinuityError("unsupported", "attachment continuity is not verified for these CLI protocols; use an ordinary workspace file reference or the legacy attachment workflow");
398
+ if (Array.isArray(cfg.options?.extraArgs) && cfg.options.extraArgs.length)
399
+ throw new ContinuityError("unsupported", "arbitrary CLI arguments have unverified session/context semantics; remove extraArgs for native continuity");
400
+ this.host.enforceQuarantine(target);
401
+ this.host.enforceBudget(target);
402
+ if (this.host.teamPolicy && !agentAllowed(this.host.teamPolicy, cfg.kind))
403
+ throw new Error(`team policy doesn't allow ${cfg.kind}`);
404
+ // Every check that can refuse the turn runs before the request enters the
405
+ // conversation: a refused request must not become history.
406
+ const health = await this.host.harness(target);
407
+ if (!health.available)
408
+ throw new ContinuityError("unsupported", health.error ?? `${cfg.kind} CLI is not reachable`);
409
+ if (this.busySince.size && (!opts.requestId || !brain.store.request(opts.requestId)))
410
+ this.host.queue.assertCanAdd({ text });
411
+ const captured = brain.capture({ id: opts.requestId ?? randomUUID(), conversationId: chat, agentInstanceId: target,
412
+ text, source, model: opts.capturedModel !== undefined ? opts.capturedModel : bound.agentId === target ? bound.model ?? null : null,
413
+ plan: Boolean(opts.plan), targetAddedTokens: 6000 });
414
+ const request = captured.request;
415
+ const previous = brain.store.receipts(request.id).at(-1);
416
+ const status = (continuityStatus) => ({ agentId: target, requestId: request.id, continuityStatus,
417
+ ...(previous ? { receiptId: previous.id, packetId: previous.packetId } : {}) });
418
+ // A retried ID is a no-op only while that request is live or was submitted.
419
+ // An unsent one (refused, overflowed, failed before launch) runs again.
420
+ if (!captured.created && !opts.fromQueue &&
421
+ (this.inFlight.has(request.id) || this.host.queue.snapshot().items.some(i => i.continuity?.requestId === request.id)))
422
+ return status(this.inFlight.has(request.id) ? "preparing" : "queued");
423
+ if (previous && (previous.status === "accepted" || previous.status === "submitting" || previous.status === "outcome_unknown"))
424
+ return status(previous.status);
425
+ // Default sequential foreground execution. A pinned chat does not bypass a
426
+ // workspace's writer lock. The queued target/model remain those captured now.
427
+ if (this.busySince.size) {
428
+ if (opts.fromQueue)
429
+ throw new ContinuityError("conflict", "another foreground turn is preparing or running");
430
+ const item = this.host.queue.add({ text, target: { kind: "agent", agentId: target }, chat, source,
431
+ ...(opts.plan ? { plan: true } : {}), ...(opts.length ? { length: opts.length } : {}), continuity: { requestId: request.id, model: request.model } });
432
+ return { agentId: target, queued: this.host.queue.length, queueId: item.id, requestId: request.id };
433
+ }
434
+ this.busySince.set(target, Date.now());
435
+ this.turnChat.set(target, chat);
436
+ this.inFlight.add(request.id);
437
+ const preparation = new AbortController();
438
+ this.preparing.set(target, preparation);
439
+ const assertPrepared = () => {
440
+ if (preparation.signal.aborted || this.host.closed || !this.host.isCurrentAgent(agent))
441
+ throw new ContinuityError("conflict", "native dispatch preparation was cancelled or replaced");
442
+ };
443
+ let mcp = null, runId;
444
+ try {
445
+ const options = structuredClone(this.host.nativeOptions(target));
446
+ const holder = this.host.validHolder();
447
+ if (holder && holder !== target)
448
+ await this.host.handoff(target, { source });
449
+ else if (!holder)
450
+ this.host.baton.acquire(target);
451
+ await this.host.ensureStarted(target);
452
+ assertPrepared();
453
+ if (!this.host.isCurrentAgent(agent))
454
+ throw new Error("native target was replaced before dispatch");
455
+ // Snapshot/checkpoint is complete before the frozen context is observed.
456
+ this.preTurnTree.set(target, await porcelainStatus(this.host.agentDir(target)));
457
+ await this.checkpointBefore(target, text);
458
+ assertPrepared();
459
+ const nativePlan = Boolean(opts.plan) && agent instanceof ProviderAgent;
460
+ const supplement = [this.host.agentInstructions(target), this.host.activeSkillsBlock(), opts.plan && !nativePlan ? planModeBriefing(text) : "", lengthLine(opts.length)].filter(Boolean).join("\n");
461
+ let prepared = await brain.prepare({ ...request, targetAddedTokens: opts.contextTarget ?? request.targetAddedTokens }, cfg.kind, this.host.agentDir(target), options, supplement);
462
+ assertPrepared();
463
+ const finishOverflow = () => {
464
+ this.preparing.delete(target);
465
+ this.busySince.delete(target);
466
+ this.inFlight.delete(request.id);
467
+ mcp?.cleanup();
468
+ this.host.kickQueue();
469
+ return { agentId: target, requestId: request.id, receiptId: prepared.receipt.id,
470
+ packetId: prepared.packet.id, continuityStatus: "overflow" };
471
+ };
472
+ if (prepared.packet.budget.overflow === "mandatory")
473
+ return finishOverflow();
474
+ if (!this.host.isCurrentAgent(agent))
475
+ throw new Error("native target was replaced before submission");
476
+ mcp = agent.capabilities.mcp ? writeMcpSession(this.host.healthyMcps()) : null;
477
+ let continuity;
478
+ // Reassembly is safe only before submission intent exists. Never retry a
479
+ // submitted action automatically, even when its acknowledgement is lost.
480
+ for (let attempt = 0;; attempt++) {
481
+ try {
482
+ continuity = await brain.submit(prepared, preparation.signal);
483
+ break;
484
+ }
485
+ catch (error) {
486
+ assertPrepared();
487
+ if (!(error instanceof ContinuityError) || error.code !== "stale" || attempt >= 2)
488
+ throw error;
489
+ prepared = await brain.prepare({ ...request, targetAddedTokens: opts.contextTarget ?? request.targetAddedTokens }, cfg.kind, this.host.agentDir(target), options, supplement);
490
+ assertPrepared();
491
+ if (prepared.packet.budget.overflow === "mandatory")
492
+ return finishOverflow();
493
+ }
494
+ }
495
+ runId = continuity.runId;
496
+ this.preparing.delete(target);
497
+ const input = { text, chat, continuity, ...(nativePlan ? { interactionMode: "plan" } : {}),
498
+ ...(request.model ? { model: request.model } : {}), ...(mcp ? { mcp: { configPath: mcp.configPath, servers: mcp.servers } } : {}) };
499
+ // Never consume a legacy handoff briefing into the new packet path.
500
+ this.host.pendingBriefings.delete(target);
501
+ if (source === "user") {
502
+ this.host.routes.onUserMessage(target);
503
+ this.host.releaseQuestionHold(target);
504
+ }
505
+ void Promise.resolve().then(() => {
506
+ if (preparation.signal.aborted || this.host.closed || !this.host.isCurrentAgent(agent))
507
+ throw new NativeDispatchRejected("dispatch cancelled before native process launch");
508
+ return agent.send(input);
509
+ }).then(() => {
510
+ if (!this.host.closed)
511
+ brain.settled(continuity.runId);
512
+ }, error => {
513
+ if (!this.host.closed)
514
+ brain.settled(continuity.runId, error);
515
+ this.host.dispatchFailed(agent, chat, error);
516
+ }).finally(async () => {
517
+ mcp?.cleanup();
518
+ await this.postTurn.get(target);
519
+ this.postTurn.delete(target);
520
+ // Native error events may arrive before the process exits. Only this
521
+ // settlement releases the runtime's foreground preparation barrier.
522
+ this.inFlight.delete(request.id);
523
+ if (!this.host.closed && this.host.isCurrentAgent(agent)) {
524
+ this.busySince.delete(target);
525
+ this.host.kickQueue();
526
+ }
527
+ });
528
+ return { agentId: target, requestId: request.id, receiptId: prepared.receipt.id,
529
+ packetId: prepared.packet.id, continuityStatus: "submitting" };
530
+ }
531
+ catch (error) {
532
+ this.preparing.delete(target);
533
+ this.inFlight.delete(request.id);
534
+ mcp?.cleanup();
535
+ if (runId)
536
+ brain.settled(runId, error);
537
+ this.host.dispatchFailed(agent, chat, error);
538
+ throw error;
539
+ }
540
+ }
541
+ /** Adapters that look hung: busy far longer than any plausible turn. */
542
+ staleSessions(now = Date.now()) {
543
+ const out = [];
544
+ for (const [agentId, since] of this.busySince) {
545
+ const live = this.host.agents.get(agentId);
546
+ if (!live || !isAdapter(live) || !live.busy())
547
+ continue;
548
+ const busyMs = now - since;
549
+ if (busyMs >= this.host.staleTurnMs)
550
+ out.push({ agentId, busyMs });
551
+ }
552
+ return out;
553
+ }
554
+ /**
555
+ * Explicit baton pass: interrupt the current holder if mid-turn, project
556
+ * the log into the target's namespaced memory, arm the one-shot briefing.
557
+ * A *manual* handoff cancels any active route — the human outranks it.
558
+ */
559
+ /**
560
+ * Re-run a failed turn on a different agent.
561
+ *
562
+ * When a turn failed, the only recovery was retyping the prompt at someone
563
+ * else — and the someone else started cold, not knowing an attempt had been
564
+ * made. This finds the failed turn's prompt, hands the baton to the chosen
565
+ * agent, and re-sends the same text with the failure attached as context, so
566
+ * the second agent knows what was tried and what it died of.
567
+ *
568
+ * The failure context rides the one-shot handoff briefing rather than the
569
+ * message text, so the thread shows the same clean prompt twice rather than
570
+ * a prompt wearing a stack trace.
571
+ */
572
+ async retryTurn(toAgentId) {
573
+ const events = this.host.log.list({ limit: 200 });
574
+ // The last error, and the last user message before it: that pairing is the
575
+ // failed turn. Route-authored messages count too — a route step that died
576
+ // is exactly what you retry somewhere else.
577
+ let errorAt = -1;
578
+ for (let i = events.length - 1; i >= 0; i--) {
579
+ if (events[i].kind === "error") {
580
+ errorAt = i;
581
+ break;
582
+ }
583
+ }
584
+ if (errorAt === -1)
585
+ throw new Error("no failed turn to retry");
586
+ const err = events[errorAt];
587
+ let prompt;
588
+ for (let i = errorAt - 1; i >= 0; i--) {
589
+ const e = events[i];
590
+ if (e.kind === "message" && !e.agentId) {
591
+ prompt = String(e.payload.text ?? "");
592
+ break;
593
+ }
594
+ }
595
+ if (!prompt?.trim())
596
+ throw new Error("could not find the prompt that failed");
597
+ await this.host.handoff(toAgentId, { source: "user" });
598
+ const failedAgent = err.agentId ?? "the previous agent";
599
+ const failure = String(err.payload.message ?? "unknown error").slice(0, 500);
600
+ const prior = this.host.pendingBriefings.get(toAgentId);
601
+ this.host.pendingBriefings.set(toAgentId, [
602
+ prior,
603
+ `[Loom retry] "${failedAgent}" attempted this and failed with: ${failure}`,
604
+ "Do not repeat the failing approach without addressing the failure.",
605
+ ]
606
+ .filter(Boolean)
607
+ .join("\n"));
608
+ await this.sendMessage(prompt, toAgentId, { chat: err.chat ?? MAIN_CHAT });
609
+ return { agentId: toAgentId, retried: prompt };
610
+ }
611
+ async interrupt(opts = {}) {
612
+ // Stop in one chat stops that chat's turn — not whatever the baton holder
613
+ // is doing in another thread.
614
+ if (opts.chat) {
615
+ for (const [id, chat] of this.turnChat) {
616
+ if ((chat ?? "main") !== opts.chat)
617
+ continue;
618
+ const a = this.host.agent(id);
619
+ if (isAdapter(a) && a.busy()) {
620
+ if ((opts.source ?? "user") === "user")
621
+ this.host.routes.onManualInterrupt();
622
+ await a.interrupt();
623
+ return { interrupted: id };
624
+ }
625
+ }
626
+ const pendingHere = [...this.preparing.entries()].find(([id]) => (this.turnChat.get(id) ?? "main") === opts.chat);
627
+ if (pendingHere) {
628
+ pendingHere[1].abort();
629
+ return { interrupted: pendingHere[0] };
630
+ }
631
+ return { interrupted: null };
632
+ }
633
+ if ((opts.source ?? "user") === "user")
634
+ this.host.routes.onManualInterrupt();
635
+ const pending = this.preparing.entries().next().value;
636
+ if (pending) {
637
+ pending[1].abort();
638
+ if ((opts.source ?? "user") === "user" && this.host.queue.length)
639
+ this.host.queue.setPaused(true, "you pressed Stop during dispatch preparation");
640
+ return { interrupted: pending[0] };
641
+ }
642
+ const holder = this.host.validHolder();
643
+ if (!holder)
644
+ return { interrupted: null };
645
+ const agent = this.host.agent(holder);
646
+ // Stop means stop: what's queued doesn't start after it — it waits,
647
+ // paused, for you to resume, edit or clear it.
648
+ if ((opts.source ?? "user") === "user" && this.host.queue.length && !this.host.queue.paused) {
649
+ this.host.queue.setPaused(true, "you pressed Stop — resume to run what's queued");
650
+ this.host.log.append({ kind: "status", agentId: holder, payload: { state: "queue_paused", waiting: this.host.queue.length } });
651
+ }
652
+ if (isAdapter(agent) && agent.busy()) {
653
+ await agent.interrupt();
654
+ return { interrupted: holder };
655
+ }
656
+ return { interrupted: null };
657
+ }
658
+ /** The chat an agent's current (or last) turn belongs to, if any. */
659
+ chatOf(agentId) {
660
+ return this.turnChat.get(agentId);
661
+ }
662
+ }
663
+ //# sourceMappingURL=turns.js.map