@loompad/cli 0.1.0 → 1.1.2

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 (688) hide show
  1. package/ARCHITECTURE.md +96 -11
  2. package/CHANGELOG.md +1054 -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 +4646 -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/release-notes/v1.1.2.md +21 -0
  674. package/docs/teams-architecture.md +697 -0
  675. package/docs/teams.md +171 -0
  676. package/docs/windows.md +85 -0
  677. package/package.json +22 -9
  678. package/dist/adapters/claude-code.d.ts +0 -26
  679. package/dist/adapters/claude-code.js +0 -209
  680. package/dist/adapters/claude-code.js.map +0 -1
  681. package/dist/adapters/codex.d.ts +0 -51
  682. package/dist/adapters/codex.js +0 -291
  683. package/dist/adapters/codex.js.map +0 -1
  684. package/docs/img/board-linear.png +0 -0
  685. package/docs/img/board-projects.png +0 -0
  686. package/docs/img/brain.png +0 -0
  687. package/docs/img/settings.png +0 -0
  688. package/docs/img/workspace.png +0 -0
@@ -0,0 +1,790 @@
1
+ /**
2
+ * Claude Code on the provider contract: one warm Agent SDK query per chat
3
+ * session, fed by a streaming-input prompt queue.
4
+ *
5
+ * query({ prompt: queue, options }) → initializationResult() (startSession)
6
+ * queue.push(user message) → … stream … → result (sendTurn, many per process)
7
+ * query.setModel(model) (model switch, in-session)
8
+ *
9
+ * The CLI stays up between turns, so a follow-up is one user message on an
10
+ * open stream rather than a process launch and a session resume. SDK messages
11
+ * are mapped onto the canonical runtime events; nothing above this file reads
12
+ * one.
13
+ *
14
+ * Ported from t3code (MIT, © T3 Tools Inc.):
15
+ * apps/server/src/provider/Layers/ClaudeAdapter.ts — startSession (prompt
16
+ * queue, query options, session id chosen up front), sendTurn (setModel, the
17
+ * user message carries the turn id), interruptTurn (a hard session boundary:
18
+ * the query is closed, because interrupt() can acknowledge while background
19
+ * tasks keep the CLI alive), canUseTool, and the stream/result handling.
20
+ * Loom spawns the CLI itself (spawnClaudeCodeProcess) so each session owns a
21
+ * process group, and stopping it proves every tool it started is gone.
22
+ */
23
+ import { attachedImages } from "../attachments.js";
24
+ import fs from "node:fs";
25
+ import path from "node:path";
26
+ import os from "node:os";
27
+ import { randomUUID } from "node:crypto";
28
+ /**
29
+ * The Agent SDK is ~30 MB resident once loaded. Every daemon imports this
30
+ * adapter (and continuity's harness probe imports claudeBin from it), but most
31
+ * never start a Claude session — so the SDK loads on the first one, not at boot.
32
+ */
33
+ let sdk = null;
34
+ const loadSdk = () => (sdk ??= import("@anthropic-ai/claude-agent-sdk").catch((error) => { sdk = null; throw error; }));
35
+ import { guardNativeOutput } from "../../adapters/base.js";
36
+ import { EventHub } from "../adapter.js";
37
+ import { ProviderError } from "../errors.js";
38
+ import { spawnHarness, stopHarness } from "../process.js";
39
+ /**
40
+ * Where `claude` lives when it isn't on PATH. The installer puts it in
41
+ * ~/.local/bin, which a daemon started from the desktop app often doesn't
42
+ * have on PATH. PATH is still the last word.
43
+ */
44
+ export function claudeBin(override) {
45
+ if (override)
46
+ return fs.existsSync(override) ? override : null;
47
+ const home = process.env.HOME ?? os.homedir();
48
+ const candidates = process.platform === "win32" ? [path.join(home, ".local", "bin", "claude.exe")] : [`${home}/.local/bin/claude`, "/opt/homebrew/bin/claude", "/usr/local/bin/claude", `${home}/.bun/bin/claude`, `${home}/.volta/bin/claude`];
49
+ for (const p of candidates) {
50
+ if (p && fs.existsSync(p))
51
+ return p;
52
+ }
53
+ return "claude";
54
+ }
55
+ const MISSING_SESSION = /No conversation found|session .* (?:not found|does not exist)/i;
56
+ const WINDOW_MINUTES = { five_hour: 300, seven_day: 10_080, seven_day_opus: 10_080, seven_day_sonnet: 10_080 };
57
+ const EDIT_TOOLS = new Set(["Edit", "Write", "MultiEdit", "NotebookEdit"]);
58
+ /** What a person may answer to a Claude tool approval. */
59
+ const APPROVAL_OPTIONS = [
60
+ { decision: "accept", label: "Allow" },
61
+ { decision: "acceptForSession", label: "Allow for this session" },
62
+ { decision: "decline", label: "Deny" },
63
+ ];
64
+ /** `["--a", "1", "--b"]` → `{ a: "1", b: null }`, the SDK's extraArgs shape. */
65
+ export function extraArgsRecord(args) {
66
+ if (args === undefined)
67
+ return undefined;
68
+ if (!Array.isArray(args) || args.some(a => typeof a !== "string"))
69
+ throw new Error("extraArgs must be a list of strings");
70
+ if (!args.length)
71
+ return undefined;
72
+ const out = {};
73
+ for (let i = 0; i < args.length; i++) {
74
+ const arg = args[i];
75
+ if (!arg.startsWith("--"))
76
+ continue;
77
+ const eq = arg.indexOf("=");
78
+ if (eq > 0) {
79
+ out[arg.slice(2, eq)] = arg.slice(eq + 1);
80
+ continue;
81
+ }
82
+ const next = args[i + 1];
83
+ out[arg.slice(2)] = next !== undefined && !next.startsWith("--") ? (i++, next) : null;
84
+ }
85
+ return out;
86
+ }
87
+ /** t3code's classifyToolItemType, for the tools Claude Code ships. */
88
+ export function claudeToolItemType(name) {
89
+ if (name === "Bash" || name === "BashOutput")
90
+ return "command_execution";
91
+ if (EDIT_TOOLS.has(name))
92
+ return "file_change";
93
+ if (name.startsWith("mcp__"))
94
+ return "mcp_tool_call";
95
+ if (name === "WebSearch" || name === "WebFetch")
96
+ return "web_search";
97
+ if (name === "Task" || name === "Agent")
98
+ return "collab_agent_tool_call";
99
+ return "dynamic_tool_call";
100
+ }
101
+ function requestTypeFor(name) {
102
+ const t = claudeToolItemType(name);
103
+ if (t === "command_execution")
104
+ return "command_execution_approval";
105
+ if (t === "file_change")
106
+ return "file_change_approval";
107
+ if (name === "Read" || name === "Glob" || name === "Grep" || name === "LS")
108
+ return "file_read_approval";
109
+ return "unknown";
110
+ }
111
+ export function summarizeToolInput(name, input) {
112
+ if (name === "TodoWrite" && Array.isArray(input.todos)) {
113
+ const todos = input.todos;
114
+ return `TodoWrite: ${todos.filter(t => t.status === "completed").length}/${todos.length} done`;
115
+ }
116
+ const interesting = input.file_path ?? input.notebook_path ?? input.command ?? input.query ?? input.pattern ?? input.url ?? input.description ?? input.prompt ?? "";
117
+ return `${name}: ${String(interesting).replace(/\s+/g, " ").slice(0, 160)}`;
118
+ }
119
+ /** Tokens now in context from a main-thread response's usage: its last
120
+ * server-side iteration when present, input (with cache) plus output. */
121
+ export function activeTokens(value) {
122
+ if (!value || typeof value !== "object")
123
+ return 0;
124
+ const usage = value;
125
+ const iterations = Array.isArray(usage.iterations) ? usage.iterations : [];
126
+ const u = (iterations.length ? iterations[iterations.length - 1] : usage);
127
+ const n = (k) => (typeof u[k] === "number" && Number.isFinite(u[k]) ? Math.max(0, u[k]) : 0);
128
+ if (n("total_tokens") > 0)
129
+ return n("total_tokens");
130
+ return n("input_tokens") + n("cache_read_input_tokens") + n("cache_creation_input_tokens") + n("output_tokens");
131
+ }
132
+ /** An unbounded async queue: the streaming-input prompt. */
133
+ class PromptQueue {
134
+ items = [];
135
+ waiting = null;
136
+ closed = false;
137
+ push(message) {
138
+ if (this.closed)
139
+ throw new Error("prompt queue is closed");
140
+ if (this.waiting) {
141
+ const w = this.waiting;
142
+ this.waiting = null;
143
+ w({ value: message, done: false });
144
+ }
145
+ else
146
+ this.items.push(message);
147
+ }
148
+ close() {
149
+ this.closed = true;
150
+ if (this.waiting) {
151
+ const w = this.waiting;
152
+ this.waiting = null;
153
+ w({ value: undefined, done: true });
154
+ }
155
+ }
156
+ [Symbol.asyncIterator]() {
157
+ return {
158
+ next: () => {
159
+ const item = this.items.shift();
160
+ if (item)
161
+ return Promise.resolve({ value: item, done: false });
162
+ if (this.closed)
163
+ return Promise.resolve({ value: undefined, done: true });
164
+ return new Promise(resolve => { this.waiting = resolve; });
165
+ },
166
+ return: () => { this.close(); return Promise.resolve({ value: undefined, done: true }); },
167
+ };
168
+ }
169
+ }
170
+ export class ClaudeProviderAdapter {
171
+ instanceId;
172
+ options;
173
+ provider = "claude-code";
174
+ capabilities = { sessionModelSwitch: "in-session", supportsConversationRollback: false, manualCompaction: false };
175
+ sessions = new Map();
176
+ hub = new EventHub();
177
+ constructor(instanceId, options = {}) {
178
+ this.instanceId = instanceId;
179
+ this.options = options;
180
+ }
181
+ onEvent(listener) { return this.hub.subscribe(listener); }
182
+ listSessions() { return [...this.sessions.values()].map(s => ({ ...s.info })); }
183
+ hasSession(threadId) { return this.sessions.has(threadId); }
184
+ emit(threadId, type, payload, extra = {}) {
185
+ this.hub.publish({ eventId: randomUUID(), provider: this.provider, instanceId: this.instanceId, threadId, createdAt: Date.now(),
186
+ ...(extra.turnId ? { turnId: extra.turnId } : {}), ...(extra.itemId ? { itemId: extra.itemId } : {}),
187
+ ...(extra.requestId ? { requestId: extra.requestId } : {}), type, payload });
188
+ }
189
+ // ---- sessions ------------------------------------------------------------
190
+ async startSession(input) {
191
+ if (this.sessions.has(input.threadId))
192
+ await this.stopSession(input.threadId);
193
+ const fail = (code, message, cause) => new ProviderError(code, "startSession", message, { provider: this.provider, instanceId: this.instanceId, threadId: input.threadId, mayHaveStarted: false, cause });
194
+ const bin = claudeBin(this.options.bin);
195
+ if (!bin)
196
+ throw fail("transport", "claude CLI not found — install Claude Code or set its path");
197
+ let extraArgs;
198
+ try {
199
+ extraArgs = extraArgsRecord(this.options.extraArgs);
200
+ }
201
+ catch (error) {
202
+ throw fail("validation", error.message);
203
+ }
204
+ const resume = typeof input.resumeCursor === "string" && input.resumeCursor ? input.resumeCursor : undefined;
205
+ const sessionId = resume ?? randomUUID();
206
+ const prompts = new PromptQueue();
207
+ const model = input.modelSelection?.model;
208
+ const mcpServers = this.options.mcpServers?.();
209
+ const now = Date.now();
210
+ const session = {
211
+ info: { provider: this.provider, instanceId: this.instanceId, threadId: input.threadId, status: "connecting",
212
+ runtimeMode: input.runtimeMode, cwd: input.cwd, resumeCursor: sessionId, createdAt: now, updatedAt: now, ...(model ? { model } : {}) },
213
+ sessionId, prompts, proc: null, turn: null, tools: new Map(), pending: new Map(), inputs: new Map(), planMode: false, compactions: 0, stopping: false,
214
+ };
215
+ const options = {
216
+ cwd: input.cwd,
217
+ pathToClaudeCodeExecutable: bin,
218
+ // Claude Code's own system prompt and the user's/project's settings, as
219
+ // an interactive `claude` would load them.
220
+ systemPrompt: { type: "preset", preset: "claude_code" },
221
+ settingSources: ["user", "project", "local"],
222
+ includePartialMessages: true,
223
+ ...this.permissions(session, input.runtimeMode),
224
+ ...(resume ? { resume } : { sessionId }),
225
+ ...(model ? { model } : {}),
226
+ ...(input.modelSelection?.effort ? { effort: input.modelSelection.effort } : {}),
227
+ ...(mcpServers && Object.keys(mcpServers).length ? { mcpServers } : {}),
228
+ ...(extraArgs ? { extraArgs } : {}),
229
+ spawnClaudeCodeProcess: spawnOptions => {
230
+ const proc = spawnHarness(spawnOptions.command, spawnOptions.args, { cwd: spawnOptions.cwd ?? input.cwd,
231
+ env: spawnOptions.env });
232
+ session.proc = proc;
233
+ guardNativeOutput(proc.child, error => {
234
+ session.failure = error.message;
235
+ if (!session.turn)
236
+ this.emit(input.threadId, "runtime.error", { message: error.message, class: "transport_error" });
237
+ void this.stopSession(input.threadId).catch(() => { });
238
+ });
239
+ const onAbort = () => { void stopHarness(proc, 0).catch(() => { }); };
240
+ if (spawnOptions.signal.aborted)
241
+ onAbort();
242
+ else
243
+ spawnOptions.signal.addEventListener("abort", onAbort, { once: true });
244
+ return proc.child;
245
+ },
246
+ };
247
+ this.emit(input.threadId, "session.state.changed", { state: "starting" });
248
+ let q;
249
+ try {
250
+ q = (await loadSdk()).query({ prompt: prompts, options });
251
+ }
252
+ catch (error) {
253
+ throw fail("transport", `claude could not start: ${error.message}`, error);
254
+ }
255
+ session.query = q;
256
+ session.done = this.consume(session);
257
+ const timeoutMs = this.options.startTimeoutMs ?? 60_000;
258
+ try {
259
+ // A resume of a session the CLI doesn't have fails here, before any
260
+ // prompt is read: "No conversation found with session ID".
261
+ await Promise.race([q.initializationResult(),
262
+ new Promise((_, reject) => setTimeout(() => reject(new Error(`claude did not initialize within ${Math.round(timeoutMs / 1000)}s`)), timeoutMs).unref())]);
263
+ }
264
+ catch (error) {
265
+ session.stopping = true;
266
+ prompts.close();
267
+ try {
268
+ q.close();
269
+ }
270
+ catch { /* already closed */ }
271
+ if (session.proc)
272
+ await stopHarness(session.proc, 0).catch(() => { });
273
+ const stderr = session.proc?.stderr().trim() ?? "";
274
+ if (resume && MISSING_SESSION.test(`${error.message}\n${stderr}`))
275
+ throw fail("session_missing", `claude session ${resume} could not be resumed`, error);
276
+ const failure = fail("transport", `claude could not start: ${error.message}`.slice(0, 500), error);
277
+ if (stderr)
278
+ failure.details.stderr = stderr;
279
+ throw failure;
280
+ }
281
+ session.info = { ...session.info, status: "ready", updatedAt: Date.now() };
282
+ this.sessions.set(input.threadId, session);
283
+ this.emit(input.threadId, "session.started", resume ? { resume } : {});
284
+ this.emit(input.threadId, "thread.started", { providerThreadId: sessionId });
285
+ this.emit(input.threadId, "session.state.changed", { state: "ready" });
286
+ return { ...session.info };
287
+ }
288
+ /** Permission mode and approval callback from the runtime mode. */
289
+ /**
290
+ * Permission mode from the runtime mode, and a `canUseTool` in every mode (as
291
+ * t3code has it): questions (AskUserQuestion) and plans (ExitPlanMode) reach
292
+ * Loom whatever the mode. Other tools the CLI asks about are allowed in full
293
+ * access, put to a person in approval-required, and denied in auto — Loom's
294
+ * auto never stops to ask; a tool runs there only if Claude's settings allow it.
295
+ */
296
+ permissions(s, mode) {
297
+ const permissionMode = this.options.permissionMode
298
+ ?? (mode === "full-access" ? "bypassPermissions" : mode === "auto-accept-edits" ? "acceptEdits"
299
+ // Approval-required needs someone to ask; with nobody, it runs read-only (plan), never "allow".
300
+ : this.options.canAsk && !this.options.canAsk() ? "plan" : "default");
301
+ s.basePermissionMode = permissionMode;
302
+ const canUseTool = (tool, toolInput, { signal, toolUseID, suggestions }) => this.canUseTool(s, tool, toolInput, signal, toolUseID, suggestions);
303
+ return { permissionMode, canUseTool, ...(permissionMode === "bypassPermissions" ? { allowDangerouslySkipPermissions: true } : {}) };
304
+ }
305
+ async canUseTool(s, tool, toolInput, signal, toolUseId, suggestions) {
306
+ if (s.stopping)
307
+ return { behavior: "deny", message: "Session stopped." };
308
+ if (tool === "AskUserQuestion")
309
+ return this.askUser(s, toolInput, signal, toolUseId);
310
+ if (tool === "ExitPlanMode") {
311
+ // t3code: the client captures the plan; the model stops and waits.
312
+ const plan = typeof toolInput.plan === "string" ? toolInput.plan.trim() : "";
313
+ if (plan)
314
+ this.emit(s.info.threadId, "turn.proposed.completed", { planMarkdown: plan }, s.turn ? { turnId: s.turn.id } : {});
315
+ return { behavior: "deny", message: "The client captured your proposed plan. Stop here and wait for the user's feedback or implementation request in a later turn." };
316
+ }
317
+ const base = s.basePermissionMode;
318
+ if (base === "bypassPermissions")
319
+ return { behavior: "allow", updatedInput: toolInput };
320
+ if (base === "acceptEdits" || base === "plan" || base === "dontAsk")
321
+ return { behavior: "deny", message: `${tool} is not allowed in this permission mode. Loom's Auto mode never stops to ask; allow the tool in Claude's settings or switch this agent to Always ask.` };
322
+ const requestId = randomUUID();
323
+ const requestType = requestTypeFor(tool);
324
+ const turnId = s.turn?.id;
325
+ const decision = await new Promise(resolve => {
326
+ s.pending.set(requestId, { requestType, resolve, ...(turnId ? { turnId } : {}), ...(toolUseId ? { toolUseId } : {}) });
327
+ const onAbort = () => this.resolvePending(s, requestId, "cancel");
328
+ signal.addEventListener("abort", onAbort, { once: true });
329
+ this.emit(s.info.threadId, "request.opened", { requestType, detail: summarizeToolInput(tool, toolInput),
330
+ args: { tool, input: toolInput }, options: APPROVAL_OPTIONS }, { requestId, ...(turnId ? { turnId } : {}), ...(toolUseId ? { itemId: toolUseId } : {}) });
331
+ if (signal.aborted)
332
+ onAbort();
333
+ });
334
+ if (decision === "accept")
335
+ return { behavior: "allow", updatedInput: toolInput };
336
+ // t3code's toSessionPermissionUpdates: the CLI's own suggestions, scoped to this session.
337
+ if (decision === "acceptForSession")
338
+ return { behavior: "allow", updatedInput: toolInput,
339
+ updatedPermissions: suggestions?.length ? suggestions.map(u => ({ ...u, destination: "session" }))
340
+ : [{ type: "addRules", rules: [{ toolName: tool }], behavior: "allow", destination: "session" }] };
341
+ if (toolUseId) {
342
+ const t = s.tools.get(toolUseId);
343
+ if (t)
344
+ t.declined = true;
345
+ }
346
+ return { behavior: "deny", message: decision === "cancel" ? "User cancelled tool execution." : "Denied in Loom." };
347
+ }
348
+ /**
349
+ * AskUserQuestion → `user-input.requested`, and the answers back as the
350
+ * tool's input (t3code's handleAskUserQuestion). A question's id is its
351
+ * text: the SDK looks answers up by question text.
352
+ */
353
+ async askUser(s, toolInput, signal, toolUseId) {
354
+ const raw = Array.isArray(toolInput.questions) ? toolInput.questions : [];
355
+ const questions = raw.map((q, i) => ({
356
+ id: typeof q.question === "string" && q.question ? q.question : `q-${i}`,
357
+ header: typeof q.header === "string" ? q.header : `Question ${i + 1}`,
358
+ question: typeof q.question === "string" ? q.question : "",
359
+ options: (Array.isArray(q.options) ? q.options : [])
360
+ .map(o => ({ label: typeof o.label === "string" ? o.label : "", description: typeof o.description === "string" ? o.description : "" })),
361
+ multiSelect: q.multiSelect === true,
362
+ allowCustomAnswer: true,
363
+ }));
364
+ const requestId = randomUUID();
365
+ const turnId = s.turn?.id;
366
+ let aborted = false;
367
+ const answers = await new Promise(resolve => {
368
+ s.inputs.set(requestId, { resolve, ...(turnId ? { turnId } : {}) });
369
+ const onAbort = () => { aborted = true; this.resolveInput(s, requestId, {}); };
370
+ signal.addEventListener("abort", onAbort, { once: true });
371
+ this.emit(s.info.threadId, "user-input.requested", { questions }, { requestId, ...(turnId ? { turnId } : {}), ...(toolUseId ? { itemId: toolUseId } : {}) });
372
+ if (signal.aborted)
373
+ onAbort();
374
+ });
375
+ if (aborted || s.stopping || !Object.keys(answers).length)
376
+ return { behavior: "deny", message: "User cancelled tool execution." };
377
+ const flat = Object.fromEntries(Object.entries(answers).map(([k, v]) => [k, Array.isArray(v) ? v.map(String).join(", ") : String(v)]));
378
+ return { behavior: "allow", updatedInput: { questions: toolInput.questions, answers: flat } };
379
+ }
380
+ resolveInput(s, requestId, answers) {
381
+ const pending = s.inputs.get(requestId);
382
+ if (!pending)
383
+ return false;
384
+ s.inputs.delete(requestId);
385
+ pending.resolve(answers);
386
+ this.emit(s.info.threadId, "user-input.resolved", { answers }, { requestId, ...(pending.turnId ? { turnId: pending.turnId } : {}) });
387
+ return true;
388
+ }
389
+ resolvePending(s, requestId, decision) {
390
+ const pending = s.pending.get(requestId);
391
+ if (!pending)
392
+ return false;
393
+ s.pending.delete(requestId);
394
+ pending.resolve(decision);
395
+ this.emit(s.info.threadId, "request.resolved", { requestType: pending.requestType, decision }, { requestId, ...(pending.turnId ? { turnId: pending.turnId } : {}) });
396
+ return true;
397
+ }
398
+ session(threadId, operation) {
399
+ const s = this.sessions.get(threadId);
400
+ if (!s || s.stopping)
401
+ throw new ProviderError("not_found", operation, `no live claude session for chat "${threadId}"`, { provider: this.provider, instanceId: this.instanceId, threadId, mayHaveStarted: false });
402
+ return s;
403
+ }
404
+ async sendTurn(input) {
405
+ const s = this.session(input.threadId, "sendTurn");
406
+ const refuse = (message, cause) => new ProviderError("request", "sendTurn", message, { provider: this.provider, instanceId: this.instanceId, threadId: input.threadId, mayHaveStarted: false, cause });
407
+ if (s.turn)
408
+ throw new ProviderError("validation", "sendTurn", "a claude turn is already running in this chat", { provider: this.provider, instanceId: this.instanceId, threadId: input.threadId, mayHaveStarted: false });
409
+ const model = input.modelSelection?.model;
410
+ if (model && model !== s.info.model) {
411
+ try {
412
+ await s.query.setModel(model);
413
+ }
414
+ catch (error) {
415
+ throw refuse(`claude could not switch to model ${model}: ${error.message}`, error);
416
+ }
417
+ s.info = { ...s.info, model };
418
+ }
419
+ // Plan mode is Claude's own permission mode, switched per turn (t3code).
420
+ const wantPlan = input.interactionMode === "plan";
421
+ if (wantPlan !== s.planMode) {
422
+ try {
423
+ await s.query.setPermissionMode(wantPlan ? "plan" : s.basePermissionMode ?? "default");
424
+ }
425
+ catch (error) {
426
+ throw refuse(`claude could not ${wantPlan ? "enter" : "leave"} plan mode: ${error.message}`, error);
427
+ }
428
+ s.planMode = wantPlan;
429
+ }
430
+ const turnId = randomUUID();
431
+ s.turn = { id: turnId, ...(s.info.model ? { model: s.info.model } : {}) };
432
+ s.info = { ...s.info, status: "running", activeTurnId: turnId, updatedAt: Date.now() };
433
+ this.emit(input.threadId, "turn.started", s.info.model ? { model: s.info.model } : {}, { turnId });
434
+ try {
435
+ // attached pictures go in as image blocks, so Claude sees them rather than a path it may not open
436
+ const pictures = attachedImages(input.input, s.info.cwd).map((im) => ({
437
+ type: "image", source: { type: "base64", media_type: im.mime, data: fs.readFileSync(im.abs).toString("base64") }
438
+ }));
439
+ s.prompts.push({ type: "user", message: { role: "user", content: [{ type: "text", text: input.input }, ...pictures] },
440
+ parent_tool_use_id: null, uuid: turnId });
441
+ }
442
+ catch (error) {
443
+ s.turn = null;
444
+ delete s.info.activeTurnId;
445
+ this.emit(input.threadId, "turn.aborted", { reason: "the claude session closed before the turn was sent" }, { turnId });
446
+ throw refuse("the claude session closed before the turn was sent", error);
447
+ }
448
+ return { threadId: input.threadId, turnId, resumeCursor: s.sessionId };
449
+ }
450
+ /** A hard boundary, as t3code has it: close the query and its process group. The next turn resumes. */
451
+ async interruptTurn(threadId) {
452
+ if (!this.sessions.has(threadId))
453
+ return;
454
+ await this.stopSession(threadId);
455
+ }
456
+ async respondToRequest(threadId, requestId, decision) {
457
+ const s = this.session(threadId, "respondToRequest");
458
+ if (!this.resolvePending(s, requestId, decision))
459
+ throw new ProviderError("not_found", "respondToRequest", `no open claude request "${requestId}"`, { provider: this.provider, threadId });
460
+ }
461
+ async respondToUserInput(threadId, requestId, answers) {
462
+ const s = this.session(threadId, "respondToUserInput");
463
+ if (!this.resolveInput(s, requestId, answers))
464
+ throw new ProviderError("not_found", "respondToUserInput", `no open claude question "${requestId}"`, { provider: this.provider, threadId });
465
+ }
466
+ async stopSession(threadId) {
467
+ const s = this.sessions.get(threadId);
468
+ if (!s)
469
+ return;
470
+ if (!s.stopping) {
471
+ s.stopping = true;
472
+ for (const id of [...s.pending.keys()])
473
+ this.resolvePending(s, id, "cancel");
474
+ for (const id of [...s.inputs.keys()])
475
+ this.resolveInput(s, id, {});
476
+ s.prompts.close();
477
+ try {
478
+ s.query.close();
479
+ }
480
+ catch { /* already closed */ }
481
+ }
482
+ try {
483
+ if (s.proc)
484
+ await stopHarness(s.proc, 0);
485
+ await Promise.race([s.done, new Promise(resolve => setTimeout(resolve, 3000).unref())]);
486
+ }
487
+ finally {
488
+ if (s.failure)
489
+ this.closed(s, s.failure, "error", "aborted");
490
+ else
491
+ this.closed(s, "session stopped", "graceful", "interrupted");
492
+ }
493
+ }
494
+ async stopAll() {
495
+ const results = await Promise.allSettled([...this.sessions.keys()].map(id => this.stopSession(id)));
496
+ const failed = results.find((r) => r.status === "rejected");
497
+ if (failed)
498
+ throw failed.reason;
499
+ }
500
+ /** The session is over: end its turn and report the exit (once). */
501
+ closed(s, reason, exitKind, turnEnd, stderr = "") {
502
+ const threadId = s.info.threadId;
503
+ if (this.sessions.get(threadId) !== s)
504
+ return;
505
+ this.sessions.delete(threadId);
506
+ if (s.turn) {
507
+ const turnId = s.turn.id;
508
+ s.turn = null;
509
+ if (turnEnd === "interrupted")
510
+ this.emit(threadId, "turn.completed", { state: "interrupted", stopReason: reason }, { turnId });
511
+ else
512
+ this.emit(threadId, "turn.aborted", { reason: `${reason} before the turn completed; native outcome is unknown`,
513
+ ...(stderr ? { detail: { stderr } } : {}) }, { turnId });
514
+ }
515
+ delete s.info.activeTurnId;
516
+ s.info = { ...s.info, status: exitKind === "error" ? "error" : "closed", updatedAt: Date.now() };
517
+ this.emit(threadId, "session.exited", { reason, exitKind, recoverable: true });
518
+ }
519
+ /** Read the SDK stream until the CLI goes away. */
520
+ async consume(s) {
521
+ let failure = null;
522
+ try {
523
+ for await (const msg of s.query) {
524
+ try {
525
+ this.handle(s, msg);
526
+ }
527
+ catch (error) {
528
+ failure = `claude event handling failed: ${error.message}`;
529
+ break;
530
+ }
531
+ }
532
+ }
533
+ catch (error) {
534
+ failure = error.message;
535
+ }
536
+ if (s.stopping)
537
+ return;
538
+ s.stopping = true;
539
+ for (const id of [...s.pending.keys()])
540
+ this.resolvePending(s, id, "cancel");
541
+ for (const id of [...s.inputs.keys()])
542
+ this.resolveInput(s, id, {});
543
+ s.prompts.close();
544
+ try {
545
+ s.query.close();
546
+ }
547
+ catch { /* already closed */ }
548
+ if (s.proc)
549
+ await stopHarness(s.proc, 0).catch(() => { });
550
+ const stderr = s.proc?.stderr().trim() ?? "";
551
+ const reason = `claude exited${failure ? `: ${failure}`.slice(0, 300) : ""}${stderr ? ` — ${stderr.slice(-300)}` : ""}`;
552
+ this.closed(s, reason, failure ? "error" : "graceful", "aborted", stderr);
553
+ }
554
+ // ---- SDK → canonical -----------------------------------------------------
555
+ handle(s, msg) {
556
+ const threadId = s.info.threadId;
557
+ const turnId = s.turn?.id;
558
+ const at = turnId ? { turnId } : {};
559
+ switch (msg.type) {
560
+ case "system":
561
+ if (msg.subtype === "init") {
562
+ if (typeof msg.model === "string" && msg.model)
563
+ s.info = { ...s.info, model: msg.model };
564
+ if (msg.session_id && msg.session_id !== s.sessionId) {
565
+ s.sessionId = msg.session_id;
566
+ s.info = { ...s.info, resumeCursor: msg.session_id };
567
+ this.emit(threadId, "thread.started", { providerThreadId: msg.session_id });
568
+ }
569
+ }
570
+ else if (msg.subtype === "status") {
571
+ if (msg.status === "compacting")
572
+ this.emit(threadId, "item.started", { itemType: "context_compaction", status: "inProgress" }, { ...at, itemId: `compaction:${++s.compactions}` });
573
+ if (msg.compact_result === "failed")
574
+ this.emit(threadId, "runtime.warning", { message: `compaction failed${msg.compact_error ? `: ${msg.compact_error}` : ""}` }, at);
575
+ }
576
+ else if (msg.subtype === "compact_boundary") {
577
+ // Native compaction: the session now holds a summary, not its history.
578
+ const meta = msg.compact_metadata;
579
+ this.emit(threadId, "thread.state.changed", { state: "compacted", trigger: meta.trigger === "manual" ? "manual" : "auto",
580
+ ...(typeof meta.pre_tokens === "number" ? { beforeTokens: meta.pre_tokens } : {}),
581
+ ...(typeof meta.post_tokens === "number" ? { afterTokens: meta.post_tokens } : {}) }, at);
582
+ if (typeof meta.post_tokens === "number" && meta.post_tokens > 0) {
583
+ s.used = meta.post_tokens;
584
+ this.contextUsage(s, at);
585
+ }
586
+ }
587
+ return;
588
+ case "stream_event": {
589
+ if (msg.parent_tool_use_id)
590
+ return; // a sub-agent's narration is not this chat's
591
+ const event = msg.event;
592
+ if (event.type === "message_start") {
593
+ if (s.turn && event.message?.id)
594
+ s.turn.streamingMessageId = event.message.id;
595
+ return;
596
+ }
597
+ if (event.type !== "content_block_delta" || !s.turn)
598
+ return;
599
+ const d = event.delta;
600
+ const text = d?.type === "text_delta" ? d.text : d?.type === "thinking_delta" ? d.thinking : undefined;
601
+ if (!text)
602
+ return;
603
+ const itemId = s.turn.streamingMessageId !== undefined && event.index !== undefined ? `${s.turn.streamingMessageId}:${event.index}` : undefined;
604
+ this.emit(threadId, "content.delta", { streamKind: d?.type === "text_delta" ? "assistant_text" : "reasoning_text", delta: text,
605
+ ...(event.index !== undefined ? { contentIndex: event.index } : {}) }, { ...at, ...(itemId ? { itemId } : {}) });
606
+ return;
607
+ }
608
+ case "assistant": {
609
+ const main = msg.parent_tool_use_id === null;
610
+ const message = msg.message;
611
+ if (main) {
612
+ if (typeof message.model === "string" && message.model && s.turn)
613
+ s.turn.model = message.model;
614
+ const used = activeTokens(message.usage);
615
+ if (used > 0 && used !== s.used) {
616
+ s.used = used;
617
+ this.contextUsage(s, at);
618
+ }
619
+ if (msg.error === "authentication_failed")
620
+ this.emit(threadId, "runtime.error", { message: "claude not signed in — run `claude` and /login, then try again", class: "permission_error" }, at);
621
+ }
622
+ (message.content ?? []).forEach((block, index) => {
623
+ const itemId = message.id ? `${message.id}:${index}` : undefined;
624
+ const extra = { ...at, ...(itemId ? { itemId } : {}) };
625
+ if (block.type === "text" && typeof block.text === "string" && main) {
626
+ this.emit(threadId, "item.completed", { itemType: "assistant_message", status: "completed", detail: block.text }, extra);
627
+ }
628
+ else if (block.type === "thinking" && typeof block.thinking === "string" && main) {
629
+ this.emit(threadId, "item.completed", { itemType: "reasoning", status: "completed", detail: block.thinking }, extra);
630
+ }
631
+ else if (block.type === "tool_use" && typeof block.id === "string") {
632
+ const name = String(block.name ?? "tool");
633
+ const toolInput = (block.input ?? {});
634
+ let itemType = claudeToolItemType(name);
635
+ // A write outside the session's directory (plan mode's own plan file
636
+ // under ~/.claude/plans) is not a change to the project.
637
+ const target = toolInput.file_path ?? toolInput.notebook_path;
638
+ if (itemType === "file_change" && typeof target === "string" && outside(s.info.cwd, target))
639
+ itemType = "dynamic_tool_call";
640
+ const tool = { name, input: toolInput, itemType };
641
+ s.tools.set(block.id, tool);
642
+ this.emit(threadId, "item.started", { itemType: tool.itemType, status: "inProgress", ...describeTool(tool),
643
+ ...(main ? {} : { parentToolUseId: msg.parent_tool_use_id }) }, { ...at, itemId: block.id });
644
+ // A todo list is the agent's plan: shown as a checklist that updates in place.
645
+ if (name === "TodoWrite" && main && Array.isArray(toolInput.todos)) {
646
+ const plan = toolInput.todos.map(t => ({ step: String(t.content ?? t.activeForm ?? ""),
647
+ status: t.status === "completed" ? "completed" : t.status === "in_progress" ? "inProgress" : "pending" })).filter(t => t.step);
648
+ if (plan.length)
649
+ this.emit(threadId, "turn.plan.updated", { plan }, at);
650
+ }
651
+ }
652
+ });
653
+ return;
654
+ }
655
+ case "user": {
656
+ const content = msg.message.content;
657
+ if (!Array.isArray(content))
658
+ return;
659
+ for (const block of content) {
660
+ if (block.type !== "tool_result" || typeof block.tool_use_id !== "string")
661
+ continue;
662
+ const tool = s.tools.get(block.tool_use_id);
663
+ if (!tool)
664
+ continue;
665
+ s.tools.delete(block.tool_use_id);
666
+ const output = toolResultText(block.content);
667
+ const images = toolResultImages(block.content);
668
+ const status = tool.declined ? "declined" : block.is_error === true ? "failed" : "completed";
669
+ this.emit(threadId, "item.completed", { itemType: tool.itemType, status, ...describeTool(tool, output, images, block.is_error === true),
670
+ ...(msg.parent_tool_use_id ? { parentToolUseId: msg.parent_tool_use_id } : {}) }, { ...at, itemId: block.tool_use_id });
671
+ }
672
+ return;
673
+ }
674
+ case "result": {
675
+ const windows = Object.values(msg.modelUsage ?? {}).map(u => u.contextWindow).filter(w => w > 0);
676
+ if (windows.length)
677
+ s.contextWindow = Math.max(...windows);
678
+ this.contextUsage(s, at);
679
+ const turn = s.turn;
680
+ if (!turn)
681
+ return; // a result for no turn of ours (a background task)
682
+ s.turn = null;
683
+ // A tool with no result by the end of the turn finished without saying how.
684
+ for (const [id, tool] of s.tools)
685
+ this.emit(threadId, "item.completed", { itemType: tool.itemType, ...describeTool(tool) }, { turnId: turn.id, itemId: id });
686
+ s.tools.clear();
687
+ delete s.info.activeTurnId;
688
+ s.info = { ...s.info, status: "ready", updatedAt: Date.now() };
689
+ const errors = msg.subtype === "success" ? [] : msg.errors ?? [];
690
+ const interrupted = !msg.is_error ? false : errors.some(e => /interrupt/i.test(e));
691
+ const errorMessage = msg.subtype === "success" ? (msg.is_error ? msg.result || "unknown error" : undefined) : errors.join("; ") || msg.subtype;
692
+ // Cache reads and creations count as input, so tokens aren't lost.
693
+ const usage = msg.usage;
694
+ this.emit(threadId, "turn.completed", {
695
+ state: !msg.is_error ? "completed" : interrupted ? "interrupted" : "failed",
696
+ ...(msg.is_error && !interrupted && errorMessage ? { errorMessage } : {}),
697
+ ...(msg.total_cost_usd !== undefined ? { totalCostUsd: msg.total_cost_usd } : {}),
698
+ ...(turn.model ? { model: turn.model } : {}),
699
+ tokenUsage: { usageStatus: "complete", inputTokens: (usage.input_tokens ?? 0) + (usage.cache_read_input_tokens ?? 0) + (usage.cache_creation_input_tokens ?? 0),
700
+ outputTokens: usage.output_tokens ?? 0, ...(usage.cache_read_input_tokens !== undefined ? { cachedInputTokens: usage.cache_read_input_tokens } : {}),
701
+ ...(usage.cache_creation_input_tokens !== undefined ? { cacheCreationTokens: usage.cache_creation_input_tokens } : {}) },
702
+ }, { turnId: turn.id });
703
+ return;
704
+ }
705
+ case "rate_limit_event": {
706
+ const info = msg.rate_limit_info;
707
+ // Overage the account has provisioned keeps the turn running.
708
+ const blocked = info.status === "rejected" && !(info.overageStatus === "allowed" || info.overageStatus === "allowed_warning"
709
+ || info.isUsingOverage === true || info.overageInUse === true);
710
+ if (typeof info.utilization === "number" && info.rateLimitType) {
711
+ this.emit(threadId, "account.rate-limits.updated", { windows: [{ id: info.rateLimitType, usedPercent: Math.round(info.utilization * 1000) / 10,
712
+ ...(WINDOW_MINUTES[info.rateLimitType] ? { windowMinutes: WINDOW_MINUTES[info.rateLimitType] } : {}),
713
+ ...(typeof info.resetsAt === "number" ? { resetsAt: info.resetsAt * 1000 } : {}) }], ...(blocked ? { reached: info.rateLimitType } : {}) }, at);
714
+ }
715
+ // A rejected window parks the turn inside the CLI: no result arrives
716
+ // until it resets, so say why the turn is waiting.
717
+ if (blocked)
718
+ this.emit(threadId, "runtime.warning", { message: `Claude usage limit reached${info.rateLimitType ? ` (${info.rateLimitType})` : ""}${typeof info.resetsAt === "number" ? ` — resets ${new Date(info.resetsAt * 1000).toLocaleString()}` : ""}` }, at);
719
+ return;
720
+ }
721
+ default:
722
+ return;
723
+ }
724
+ }
725
+ contextUsage(s, at) {
726
+ if (s.used === undefined)
727
+ return;
728
+ const max = s.contextWindow;
729
+ this.emit(s.info.threadId, "thread.token-usage.updated", { usage: { usedTokens: max ? Math.min(s.used, max) : s.used,
730
+ ...(max ? { maxTokens: max } : {}), compactsAutomatically: true } }, at);
731
+ }
732
+ }
733
+ /** Title, detail and normalized data for a Claude tool call. */
734
+ function describeTool(tool, output, images, failed = false) {
735
+ const detail = summarizeToolInput(tool.name, tool.input);
736
+ switch (tool.itemType) {
737
+ case "command_execution": {
738
+ const command = String(tool.input.command ?? "");
739
+ return { detail: command, data: { command, ...(output !== undefined ? { output } : {}) } };
740
+ }
741
+ case "mcp_tool_call": {
742
+ // mcp__<server>__<tool>
743
+ const [, server = "", ...rest] = tool.name.split("__");
744
+ return { title: tool.name, detail: `${server} · ${rest.join("__") || tool.name}`, data: { tool: rest.join("__") || tool.name, server, input: tool.input,
745
+ ...(output !== undefined ? (failed ? { error: output } : { output }) : {}), ...(images?.length ? { images } : {}) } };
746
+ }
747
+ case "file_change": {
748
+ const p = tool.input.file_path ?? tool.input.notebook_path;
749
+ return { title: tool.name, detail, data: { changes: p ? [{ path: String(p), kind: tool.name === "Write" ? "add_or_update" : "update" }] : [] } };
750
+ }
751
+ default:
752
+ return { title: tool.name, detail, data: { tool: tool.name, input: tool.input,
753
+ ...(output !== undefined ? (failed ? { error: output } : { output }) : {}), ...(images?.length ? { images } : {}) } };
754
+ }
755
+ }
756
+ /** Image blocks in a tool result (Read on a PNG, an MCP screenshot): base64 for ingestion to file. */
757
+ function toolResultImages(content) {
758
+ if (!Array.isArray(content))
759
+ return [];
760
+ return content.flatMap(c => {
761
+ const src = c && c.type === "image" ? c.source : undefined;
762
+ return src && src.type === "base64" && typeof src.data === "string" ? [{ data: src.data, mime: String(src.media_type ?? "image/png") }] : [];
763
+ });
764
+ }
765
+ /** Is an absolute path outside `dir`? Symlinks resolved (macOS /tmp is /private/tmp). */
766
+ function outside(dir, file) {
767
+ if (!path.isAbsolute(file))
768
+ return false;
769
+ const real = (p) => { try {
770
+ return fs.realpathSync(p);
771
+ }
772
+ catch {
773
+ try {
774
+ return path.join(fs.realpathSync(path.dirname(p)), path.basename(p));
775
+ }
776
+ catch {
777
+ return p;
778
+ }
779
+ } };
780
+ const rel = path.relative(real(dir), real(file));
781
+ return rel.startsWith("..") || path.isAbsolute(rel);
782
+ }
783
+ function toolResultText(content) {
784
+ if (typeof content === "string")
785
+ return content;
786
+ if (!Array.isArray(content))
787
+ return undefined;
788
+ return content.map(c => (c && typeof c === "object" && typeof c.text === "string" ? c.text : "")).join("");
789
+ }
790
+ //# sourceMappingURL=adapter.js.map