@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,881 @@
1
+ /**
2
+ * Codex on the provider contract: one warm `codex app-server` per chat session.
3
+ *
4
+ * initialize → thread/start | thread/resume (startSession)
5
+ * turn/start → … notifications … → turn/completed (sendTurn, many per process)
6
+ * turn/interrupt (interruptTurn)
7
+ *
8
+ * The thread stays open between turns, so a follow-up costs one `turn/start`
9
+ * rather than a process launch and a thread resume. Native notifications and
10
+ * server requests are mapped onto the canonical runtime events; nothing above
11
+ * this file reads a Codex message.
12
+ *
13
+ * Ported from t3code (MIT, © T3 Tools Inc.):
14
+ * apps/server/src/provider/Layers/CodexSessionRuntime.ts (session lifecycle,
15
+ * thread open/resume, turn params, approvals) and CodexAdapter.ts
16
+ * (mapToRuntimeEvents, toCanonicalItemType, normalizeCodexTokenUsage).
17
+ * Protocol shapes follow `codex app-server generate-ts` (checked against
18
+ * codex-cli 0.153.4).
19
+ */
20
+ import { attachedImages } from "../attachments.js";
21
+ import { execFileSync, findExecutable } from "../../core/process.js";
22
+ import fs from "node:fs";
23
+ import { randomUUID } from "node:crypto";
24
+ import { guardNativeOutput } from "../../adapters/base.js";
25
+ import { VERSION } from "../../version.js";
26
+ import { EventHub } from "../adapter.js";
27
+ import { ProviderError } from "../errors.js";
28
+ import { launched, spawnHarness, stopHarness } from "../process.js";
29
+ import { codexDeveloperInstructions } from "./instructions.js";
30
+ import { CodexRpc } from "./rpc.js";
31
+ /** The CLI bundled inside the desktop app. */
32
+ const BUNDLED = [
33
+ // Codex ships inside the ChatGPT desktop app now, and stays current with it
34
+ "/Applications/ChatGPT.app/Contents/Resources/codex-cli/bin/codex",
35
+ `${process.env.HOME ?? ""}/Applications/ChatGPT.app/Contents/Resources/codex-cli/bin/codex`,
36
+ "/Applications/Codex.app/Contents/Resources/codex",
37
+ `${process.env.HOME ?? ""}/Applications/Codex.app/Contents/Resources/codex`,
38
+ ];
39
+ /** `codex --version` as numbers, cached per binary until it changes on disk. */
40
+ const versions = new Map();
41
+ export function codexVersion(bin) {
42
+ try {
43
+ const { mtimeMs } = fs.statSync(bin);
44
+ const hit = versions.get(bin);
45
+ if (hit && hit.mtimeMs === mtimeMs)
46
+ return hit.v;
47
+ const out = execFileSync(bin, ["--version"], { encoding: "utf8", timeout: 5_000, stdio: ["ignore", "pipe", "ignore"] });
48
+ const m = /(\d+)\.(\d+)\.(\d+)/.exec(out);
49
+ const v = m ? [Number(m[1]), Number(m[2]), Number(m[3])] : null;
50
+ versions.set(bin, { mtimeMs, v });
51
+ return v;
52
+ }
53
+ catch {
54
+ return null;
55
+ }
56
+ }
57
+ function onPath(name) {
58
+ return findExecutable(name);
59
+ }
60
+ const newer = (a, b) => {
61
+ if (!a)
62
+ return false;
63
+ if (!b)
64
+ return true;
65
+ for (let i = 0; i < 3; i++)
66
+ if (a[i] !== b[i])
67
+ return a[i] > b[i];
68
+ return false;
69
+ };
70
+ /**
71
+ * Where the codex CLI is: an explicit override (or LOOM_CODEX_BIN), else the
72
+ * newest of the app bundles and the one on PATH. Newest matters: OpenAI only
73
+ * serves its latest models to recent clients, and an old one is refused with
74
+ * "The 'X' model is not supported when using Codex with a ChatGPT account" —
75
+ * a Homebrew codex a few versions behind the ChatGPT app's can't run the model
76
+ * the app just set as your default.
77
+ */
78
+ export function codexBin(override) {
79
+ if (override)
80
+ return fs.existsSync(override) ? override : null;
81
+ const pinned = process.env.LOOM_CODEX_BIN;
82
+ if (pinned)
83
+ return fs.existsSync(pinned) ? pinned : null;
84
+ const found = [...new Set([...BUNDLED.filter(p => fs.existsSync(p)), onPath("codex")].filter((p) => Boolean(p)))];
85
+ if (!found.length)
86
+ return "codex";
87
+ if (found.length === 1)
88
+ return found[0];
89
+ let best = found[0], bestV = codexVersion(best);
90
+ for (const p of found.slice(1)) {
91
+ const v = codexVersion(p);
92
+ if (newer(v, bestV)) {
93
+ best = p;
94
+ bestV = v;
95
+ }
96
+ }
97
+ return best;
98
+ }
99
+ const SIGNED_OUT = /\b(?:not\s+(?:logged|signed)\s+in|not\s+authenticated|authentication\s+required|login\s+required|please\s+log\s+in)\b/i;
100
+ /** t3code's RECOVERABLE_THREAD_RESUME_ERROR_SNIPPETS. */
101
+ const MISSING_THREAD = /not found|missing thread|no such thread|unknown thread|does not exist|no rollout found/i;
102
+ export function codexFailure(message, stderr = "") {
103
+ if (SIGNED_OUT.test(`${message}\n${stderr}`))
104
+ return "codex not signed in — run `codex login` and try again";
105
+ if (PLAN_REFUSED.test(message))
106
+ return `${message} (OpenAI says this when the codex CLI is too old for the model, or the plan lacks it.) Loom switches this chat to one this Codex lists; update codex, or pick a model for this agent.`;
107
+ return message;
108
+ }
109
+ /** ChatGPT sign-in, a model this client or plan can't run. */
110
+ export const PLAN_REFUSED = /model is not supported when using Codex with a ChatGPT account/i;
111
+ /**
112
+ * The model to run instead of one this login can't use: the account's own
113
+ * default from `model/list` (Codex filters that list by sign-in and plan, as
114
+ * t3code relies on), else the first one listed. Null when the model is fine,
115
+ * or there's nothing to judge it against.
116
+ */
117
+ export function planFallback(model, list) {
118
+ if (!model || !list.length || list.some(m => m.id === model))
119
+ return null;
120
+ return (list.find(m => m.isDefault) ?? list[0]).id;
121
+ }
122
+ /**
123
+ * Sandbox and approval policy for a runtime mode, as Loom's permission modes
124
+ * have always meant them: full access asks nothing; auto-accept works inside
125
+ * the workspace sandbox and never asks; approval-required is read-only and
126
+ * every command or edit waits for a person. (t3code's auto-accept-edits uses
127
+ * `on-request`; Loom's "auto" promises it never stops to ask.)
128
+ */
129
+ export function codexThreadPolicy(mode, sandboxOverride) {
130
+ const sandbox = sandboxOverride ?? (mode === "full-access" ? "danger-full-access" : mode === "approval-required" ? "read-only" : "workspace-write");
131
+ return { sandbox, approvalPolicy: mode === "approval-required" ? "untrusted" : "never" };
132
+ }
133
+ /** t3code's toCanonicalItemType, on Codex item type names. */
134
+ export function codexItemType(raw) {
135
+ const type = String(raw ?? "").replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/[._/-]/g, " ").replace(/\s+/g, " ").trim().toLowerCase();
136
+ if (!type)
137
+ return "unknown";
138
+ if (type.includes("user"))
139
+ return "user_message";
140
+ if (type.includes("agent message") || type.includes("assistant"))
141
+ return "assistant_message";
142
+ if (type.includes("reasoning") || type.includes("thought"))
143
+ return "reasoning";
144
+ if (type.includes("plan") || type.includes("todo"))
145
+ return "plan";
146
+ if (type.includes("command"))
147
+ return "command_execution";
148
+ if (type.includes("file change") || type.includes("patch") || type.includes("edit"))
149
+ return "file_change";
150
+ if (type.includes("mcp"))
151
+ return "mcp_tool_call";
152
+ if (type.includes("dynamic tool"))
153
+ return "dynamic_tool_call";
154
+ if (type.includes("collab"))
155
+ return "collab_agent_tool_call";
156
+ if (type.includes("web search"))
157
+ return "web_search";
158
+ if (type.includes("image"))
159
+ return "image_view";
160
+ if (type.includes("compact"))
161
+ return "context_compaction";
162
+ if (type.includes("error"))
163
+ return "error";
164
+ return "unknown";
165
+ }
166
+ const REQUEST_TYPES = {
167
+ "item/commandExecution/requestApproval": "command_execution_approval",
168
+ "item/fileRead/requestApproval": "file_read_approval",
169
+ "item/fileChange/requestApproval": "file_change_approval",
170
+ applyPatchApproval: "apply_patch_approval",
171
+ execCommandApproval: "exec_command_approval",
172
+ "item/permissions/requestApproval": "permission_approval",
173
+ "mcpServer/elicitation/request": "mcp_elicitation_approval",
174
+ "item/tool/requestUserInput": "tool_user_input",
175
+ };
176
+ const DELTA_KINDS = {
177
+ "item/agentMessage/delta": "assistant_text",
178
+ "item/reasoning/textDelta": "reasoning_text",
179
+ "item/reasoning/summaryTextDelta": "reasoning_summary_text",
180
+ "item/commandExecution/outputDelta": "command_output",
181
+ "item/fileChange/outputDelta": "file_change_output",
182
+ };
183
+ const TURN_STATES = { completed: "completed", failed: "failed", interrupted: "interrupted", cancelled: "cancelled" };
184
+ const ITEM_STATUSES = new Set(["inProgress", "completed", "failed", "declined"]);
185
+ const usageOf = (u) => ({ input: Number(u?.inputTokens ?? 0), cached: Number(u?.cachedInputTokens ?? 0),
186
+ output: Number(u?.outputTokens ?? 0), reasoning: Number(u?.reasoningOutputTokens ?? 0) });
187
+ export class CodexProviderAdapter {
188
+ instanceId;
189
+ options;
190
+ provider = "codex";
191
+ capabilities = { sessionModelSwitch: "in-session", supportsConversationRollback: false, manualCompaction: true };
192
+ sessions = new Map();
193
+ hub = new EventHub();
194
+ constructor(instanceId, options = {}) {
195
+ this.instanceId = instanceId;
196
+ this.options = options;
197
+ }
198
+ onEvent(listener) { return this.hub.subscribe(listener); }
199
+ listSessions() { return [...this.sessions.values()].map(s => ({ ...s.info })); }
200
+ hasSession(threadId) { return this.sessions.has(threadId); }
201
+ emit(threadId, type, payload, extra = {}) {
202
+ this.hub.publish({ eventId: randomUUID(), provider: this.provider, instanceId: this.instanceId, threadId, createdAt: Date.now(),
203
+ ...(extra.turnId ? { turnId: extra.turnId } : {}), ...(extra.itemId ? { itemId: extra.itemId } : {}),
204
+ ...(extra.requestId ? { requestId: extra.requestId } : {}), type, payload });
205
+ }
206
+ // ---- sessions ------------------------------------------------------------
207
+ async startSession(input) {
208
+ const existing = this.sessions.get(input.threadId);
209
+ if (existing)
210
+ await this.stopSession(input.threadId);
211
+ const bin = codexBin(this.options.bin);
212
+ let proc;
213
+ const fail = (code, message, cause) => {
214
+ const stderr = proc?.stderr().trim();
215
+ return new ProviderError(code, "startSession", message, { provider: this.provider, instanceId: this.instanceId, threadId: input.threadId,
216
+ mayHaveStarted: false, cause, ...(stderr ? { stderr } : {}) });
217
+ };
218
+ if (!bin)
219
+ throw fail("transport", "codex CLI not found — install it or open Codex.app once");
220
+ const extraArgs = this.options.extraArgs ?? [];
221
+ if (!Array.isArray(extraArgs) || extraArgs.some(a => typeof a !== "string"))
222
+ throw new ProviderError("validation", "startSession", "extraArgs must be a list of strings", { provider: this.provider, instanceId: this.instanceId, threadId: input.threadId });
223
+ this.emit(input.threadId, "session.state.changed", { state: "starting" });
224
+ proc = spawnHarness(bin, ["app-server", ...extraArgs], { cwd: input.cwd });
225
+ try {
226
+ await launched(proc.child);
227
+ }
228
+ catch (error) {
229
+ throw fail("transport", `${bin}: ${error.message}`, error);
230
+ }
231
+ const now = Date.now();
232
+ const session = {
233
+ info: { provider: this.provider, instanceId: this.instanceId, threadId: input.threadId, status: "connecting",
234
+ runtimeMode: input.runtimeMode, cwd: input.cwd, createdAt: now, updatedAt: now,
235
+ ...(input.modelSelection?.model ? { model: input.modelSelection.model } : {}) },
236
+ proc, rpc: undefined, providerThreadId: "", pending: new Map(), inputs: new Map(), planMode: false, finished: new Set(),
237
+ baseline: null, turnUsage: null, compacting: false, stopping: false,
238
+ };
239
+ session.rpc = new CodexRpc(proc.child, {
240
+ notification: (method, params) => this.notification(session, method, params),
241
+ request: (method, params) => this.serverRequest(session, method, params),
242
+ });
243
+ // An oversized record is dropped, not imported: stop reading and end the session.
244
+ guardNativeOutput(proc.child, error => {
245
+ session.rpc.close(error);
246
+ session.failure = error.message;
247
+ if (!session.info.activeTurnId)
248
+ this.emit(input.threadId, "runtime.error", { message: error.message, class: "transport_error" });
249
+ void this.stopSession(input.threadId).catch(() => { });
250
+ });
251
+ void proc.closed.then(({ code }) => this.exited(session, code));
252
+ let reroute = null;
253
+ try {
254
+ await session.rpc.request("initialize", { clientInfo: { name: "loom", title: "Loom", version: VERSION },
255
+ capabilities: { experimentalApi: true, requestAttestation: false } });
256
+ session.rpc.notify("initialized");
257
+ const params = this.threadParams(input);
258
+ const cursor = typeof input.resumeCursor === "string" && input.resumeCursor ? input.resumeCursor : undefined;
259
+ let opened;
260
+ try {
261
+ opened = cursor
262
+ ? await session.rpc.request("thread/resume", { threadId: cursor, ...params, excludeTurns: true })
263
+ : await session.rpc.request("thread/start", params);
264
+ }
265
+ catch (error) {
266
+ const message = error.message;
267
+ if (cursor && MISSING_THREAD.test(message))
268
+ throw fail("session_missing", `codex thread ${cursor} could not be resumed: ${message}`, error);
269
+ throw fail("request", codexFailure(`codex could not open its thread: ${message}`, proc.stderr()), error);
270
+ }
271
+ const threadId = String(opened.thread?.id ?? cursor ?? "");
272
+ if (!threadId)
273
+ throw fail("request", "codex app-server returned no thread id");
274
+ session.providerThreadId = threadId;
275
+ session.info = { ...session.info, status: "ready", resumeCursor: threadId, updatedAt: Date.now(),
276
+ ...(typeof opened.model === "string" && opened.model ? { model: opened.model } : {}),
277
+ ...(typeof opened.cwd === "string" && opened.cwd ? { cwd: opened.cwd } : {}) };
278
+ // No model chosen in Loom: Codex falls back to ~/.codex/config.toml, which
279
+ // the Codex app may have set to a model this sign-in's plan doesn't have.
280
+ // Ask what the account can run and use its default instead of failing.
281
+ if (!input.modelSelection?.model) {
282
+ const to = planFallback(session.info.model, await this.accountModels(session.rpc));
283
+ if (to)
284
+ reroute = { fromModel: session.info.model, toModel: to };
285
+ if (to)
286
+ session.info = { ...session.info, model: to };
287
+ }
288
+ }
289
+ catch (error) {
290
+ session.stopping = true;
291
+ await stopHarness(proc).catch(() => { });
292
+ if (error instanceof ProviderError) {
293
+ const stderr = proc.stderr().trim();
294
+ if (stderr && !error.details.stderr)
295
+ error.details.stderr = stderr;
296
+ throw error;
297
+ }
298
+ throw fail("transport", codexFailure(`codex app-server failed to start: ${error.message}`, proc.stderr()), error);
299
+ }
300
+ this.sessions.set(input.threadId, session);
301
+ this.emit(input.threadId, "session.started", cursorPayload(input.resumeCursor));
302
+ this.emit(input.threadId, "thread.started", { providerThreadId: session.providerThreadId });
303
+ this.emit(input.threadId, "session.state.changed", { state: "ready" });
304
+ if (reroute)
305
+ this.emit(input.threadId, "model.rerouted", { ...reroute, reason: "this Codex doesn't list it for your sign-in — an older codex, or a model your plan lacks" });
306
+ return { ...session.info };
307
+ }
308
+ /** A turn was refused for the plan: the next one runs on a model the account has. */
309
+ async afterPlanRefusal(s) {
310
+ const from = s.info.model;
311
+ (s.refused ??= new Set()).add(from);
312
+ this.modelCache = null;
313
+ const list = (await this.accountModels(s.rpc)).filter(m => m.id !== from);
314
+ const to = (list.find(m => m.isDefault) ?? list[0])?.id;
315
+ if (!to)
316
+ return;
317
+ s.info = { ...s.info, model: to };
318
+ this.emit(s.info.threadId, "model.rerouted", { fromModel: from, toModel: to, reason: "this Codex doesn't list it for your sign-in — an older codex, or a model your plan lacks" });
319
+ }
320
+ /** What this sign-in can run (`model/list`), cached for ten minutes; empty when Codex can't say. */
321
+ modelCache = null;
322
+ async accountModels(rpc) {
323
+ if (this.modelCache && Date.now() - this.modelCache.at < 600_000)
324
+ return this.modelCache.list;
325
+ try {
326
+ // paged by cursor, as t3code and Agent Orchestrator read it
327
+ const data = [];
328
+ let cursor;
329
+ for (let page = 0; page < 10; page++) {
330
+ const res = await rpc.request("model/list", { limit: 100, ...(cursor ? { cursor } : {}) }, 10_000);
331
+ if (Array.isArray(res.data))
332
+ data.push(...res.data);
333
+ cursor = typeof res.nextCursor === "string" && res.nextCursor ? res.nextCursor : undefined;
334
+ if (!cursor)
335
+ break;
336
+ }
337
+ const list = data.filter(m => typeof m.id === "string" || typeof m.model === "string")
338
+ .map(m => ({ id: String(m.model ?? m.id), isDefault: m.isDefault === true }));
339
+ this.modelCache = { at: Date.now(), list };
340
+ return list;
341
+ }
342
+ catch {
343
+ return [];
344
+ }
345
+ }
346
+ /** Thread settings from the runtime mode, applied on start and resume. */
347
+ threadParams(input) {
348
+ const { sandbox, approvalPolicy } = codexThreadPolicy(input.runtimeMode, this.options.sandbox);
349
+ const mcp = this.options.mcpConfig?.();
350
+ return { cwd: input.cwd, sandbox, approvalPolicy, approvalsReviewer: "user",
351
+ ...(input.modelSelection?.model ? { model: input.modelSelection.model } : {}),
352
+ ...(mcp && Object.keys(mcp).length ? { config: mcp } : {}) };
353
+ }
354
+ session(threadId, operation) {
355
+ const s = this.sessions.get(threadId);
356
+ if (!s || s.stopping)
357
+ throw new ProviderError("not_found", operation, `no live codex session for chat "${threadId}"`, { provider: this.provider, instanceId: this.instanceId, threadId, mayHaveStarted: false });
358
+ return s;
359
+ }
360
+ async sendTurn(input) {
361
+ const s = this.session(input.threadId, "sendTurn");
362
+ if (s.info.activeTurnId)
363
+ throw new ProviderError("validation", "sendTurn", "a codex turn is already running in this chat", { provider: this.provider, instanceId: this.instanceId, threadId: input.threadId, mayHaveStarted: false });
364
+ // A model this plan refused earlier in the session gives way to the one it switched to.
365
+ if (s.rerouting) {
366
+ await s.rerouting;
367
+ s.rerouting = undefined;
368
+ }
369
+ const asked = input.modelSelection?.model;
370
+ const model = asked && !s.refused?.has(asked) ? asked : s.info.model;
371
+ // Notifications may arrive before turn/start's response. They must see
372
+ // the model this turn asked for, including a refusal that reroutes it.
373
+ if (model)
374
+ s.info = { ...s.info, model };
375
+ const effort = input.modelSelection?.effort;
376
+ s.baseline = null;
377
+ s.turnUsage = null;
378
+ // Plan mode is a collaboration mode (t3code's buildCodexCollaborationMode).
379
+ // It lasts until a turn says otherwise, so the first default turn after a
380
+ // plan turn carries the default mode; other turns carry none.
381
+ const mode = input.interactionMode === "plan" ? "plan" : s.planMode ? "default" : undefined;
382
+ const collaboration = mode ? { collaborationMode: { mode, settings: { model: model ?? "gpt-5-codex",
383
+ reasoning_effort: effort ?? "medium", developer_instructions: codexDeveloperInstructions(mode) } } } : {};
384
+ let response;
385
+ try {
386
+ response = await s.rpc.request("turn/start", { threadId: s.providerThreadId,
387
+ // attached pictures go in as localImage inputs, so Codex sees them rather than a path
388
+ input: [{ type: "text", text: input.input, text_elements: [] },
389
+ ...attachedImages(input.input, s.info.cwd).map((im) => ({ type: "localImage", path: im.abs }))],
390
+ ...(input.clientTurnId ? { clientUserMessageId: input.clientTurnId } : {}),
391
+ ...(model ? { model } : {}), ...(effort ? { effort } : {}), ...collaboration });
392
+ }
393
+ catch (error) {
394
+ // An error *response* is a refusal: the server did not start the turn.
395
+ // A dead process or a timeout is not — that outcome stays unknown.
396
+ const refused = error.code !== undefined;
397
+ throw new ProviderError(refused ? "request" : "transport", "sendTurn", codexFailure(refused ? `codex refused the turn: ${error.message}` : `${error.message}; native outcome is unknown`, s.proc.stderr()), { provider: this.provider, instanceId: this.instanceId, threadId: input.threadId, mayHaveStarted: !refused, cause: error,
398
+ ...(s.proc.stderr().trim() ? { stderr: s.proc.stderr().trim() } : {}) });
399
+ }
400
+ if (mode)
401
+ s.planMode = mode === "plan";
402
+ const turnId = String(response.turn?.id ?? "");
403
+ if (!turnId)
404
+ throw new ProviderError("request", "sendTurn", "codex app-server returned no turn id", { provider: this.provider, instanceId: this.instanceId, threadId: input.threadId, mayHaveStarted: true });
405
+ if (!s.finished.has(turnId))
406
+ s.info = { ...s.info, status: "running", activeTurnId: turnId, updatedAt: Date.now() };
407
+ return { threadId: input.threadId, turnId, resumeCursor: s.providerThreadId };
408
+ }
409
+ async interruptTurn(threadId, turnId) {
410
+ const s = this.sessions.get(threadId);
411
+ if (!s)
412
+ return;
413
+ // Settle open approvals and questions first (t3code: the interrupt must not
414
+ // queue behind a card nobody will answer).
415
+ this.settlePending(s, "cancel");
416
+ await s.rpc.flushServerResponses();
417
+ const target = turnId ?? s.info.activeTurnId;
418
+ if (!target || s.finished.has(target))
419
+ return;
420
+ await s.rpc.request("turn/interrupt", { threadId: s.providerThreadId, turnId: target }, 5000);
421
+ }
422
+ async respondToRequest(threadId, requestId, decision) {
423
+ const s = this.session(threadId, "respondToRequest");
424
+ const pending = s.pending.get(requestId);
425
+ if (!pending)
426
+ throw new ProviderError("not_found", "respondToRequest", `no open codex request "${requestId}"`, { provider: this.provider, threadId });
427
+ s.pending.delete(requestId);
428
+ pending.resolve(decision);
429
+ this.emit(threadId, "request.resolved", { requestType: pending.requestType, decision }, { requestId, ...(pending.turnId ? { turnId: pending.turnId } : {}) });
430
+ }
431
+ async respondToUserInput(threadId, requestId, answers) {
432
+ const s = this.session(threadId, "respondToUserInput");
433
+ const pending = s.inputs.get(requestId);
434
+ if (!pending)
435
+ throw new ProviderError("not_found", "respondToUserInput", `no open codex question "${requestId}"`, { provider: this.provider, threadId });
436
+ s.inputs.delete(requestId);
437
+ pending.resolve(answers);
438
+ // a secret answer reaches Codex, never the thread's log
439
+ const shown = Object.fromEntries(Object.entries(answers).map(([k, v]) => [k, pending.secret?.has(k) ? "\u2022\u2022\u2022\u2022" : v]));
440
+ this.emit(threadId, "user-input.resolved", { answers: shown }, { requestId, ...(pending.turnId ? { turnId: pending.turnId } : {}) });
441
+ }
442
+ async compact(threadId) {
443
+ const s = this.session(threadId, "compact");
444
+ await s.rpc.request("thread/compact/start", { threadId: s.providerThreadId });
445
+ }
446
+ async stopSession(threadId) {
447
+ const s = this.sessions.get(threadId);
448
+ if (!s)
449
+ return;
450
+ s.stopping = true;
451
+ this.settlePending(s, "cancel");
452
+ try {
453
+ await stopHarness(s.proc);
454
+ }
455
+ finally {
456
+ if (this.sessions.get(threadId) === s)
457
+ this.sessions.delete(threadId);
458
+ s.rpc.close(new Error("session stopped"));
459
+ if (s.info.activeTurnId) {
460
+ if (s.failure)
461
+ this.emit(threadId, "turn.aborted", { reason: `${s.failure}; native outcome is unknown` }, { turnId: s.info.activeTurnId });
462
+ else
463
+ this.emit(threadId, "turn.completed", { state: "interrupted", stopReason: "session stopped" }, { turnId: s.info.activeTurnId });
464
+ }
465
+ s.info = { ...s.info, status: "closed", updatedAt: Date.now() };
466
+ delete s.info.activeTurnId;
467
+ this.emit(threadId, "session.exited", s.failure ? { reason: s.failure, exitKind: "error", recoverable: true } : { reason: "session stopped", exitKind: "graceful" });
468
+ }
469
+ }
470
+ async stopAll() {
471
+ const results = await Promise.allSettled([...this.sessions.keys()].map(id => this.stopSession(id)));
472
+ const failed = results.find((r) => r.status === "rejected");
473
+ if (failed)
474
+ throw failed.reason;
475
+ }
476
+ /** The process went away on its own. */
477
+ exited(s, code) {
478
+ s.rpc.close(new Error(`codex app-server exited${code === null ? "" : ` ${code}`}`));
479
+ if (s.stopping)
480
+ return; // stopSession reports it
481
+ s.stopping = true;
482
+ this.settlePending(s, "cancel");
483
+ const threadId = s.info.threadId;
484
+ if (this.sessions.get(threadId) === s)
485
+ this.sessions.delete(threadId);
486
+ const message = codexFailure(`codex app-server exited${code === null ? "" : ` with code ${code}`}`, s.proc.stderr());
487
+ if (s.info.activeTurnId) {
488
+ const stderr = s.proc.stderr().trim();
489
+ this.emit(threadId, "turn.aborted", { reason: `${message} before the turn completed; native outcome is unknown`,
490
+ ...(stderr ? { detail: { stderr } } : {}) }, { turnId: s.info.activeTurnId });
491
+ delete s.info.activeTurnId;
492
+ }
493
+ s.info = { ...s.info, status: code === 0 ? "closed" : "error", updatedAt: Date.now() };
494
+ this.emit(threadId, "session.exited", { reason: message, exitKind: code === 0 ? "graceful" : "error", recoverable: true });
495
+ // The group may still hold tools the harness started.
496
+ void stopHarness(s.proc).catch(() => { });
497
+ }
498
+ settlePending(s, decision) {
499
+ for (const [requestId, input] of s.inputs) {
500
+ s.inputs.delete(requestId);
501
+ input.resolve({});
502
+ this.emit(s.info.threadId, "user-input.resolved", { answers: {} }, { requestId, ...(input.turnId ? { turnId: input.turnId } : {}) });
503
+ }
504
+ for (const [requestId, pending] of s.pending) {
505
+ s.pending.delete(requestId);
506
+ pending.resolve(decision);
507
+ this.emit(s.info.threadId, "request.resolved", { requestType: pending.requestType, decision }, { requestId, ...(pending.turnId ? { turnId: pending.turnId } : {}) });
508
+ }
509
+ }
510
+ // ---- native → canonical --------------------------------------------------
511
+ notification(s, method, params) {
512
+ // Sub-agent threads report their own items; this session's thread is the one.
513
+ if (s.providerThreadId && typeof params.threadId === "string" && params.threadId !== s.providerThreadId)
514
+ return;
515
+ const threadId = s.info.threadId;
516
+ const turnId = typeof params.turnId === "string" ? params.turnId : s.info.activeTurnId;
517
+ const at = { ...(turnId ? { turnId } : {}) };
518
+ const deltaKind = DELTA_KINDS[method];
519
+ if (deltaKind) {
520
+ const delta = typeof params.delta === "string" ? params.delta : "";
521
+ if (delta)
522
+ this.emit(threadId, "content.delta", { streamKind: deltaKind, delta,
523
+ ...(typeof params.contentIndex === "number" ? { contentIndex: params.contentIndex } : {}) }, { ...at, ...(typeof params.itemId === "string" ? { itemId: params.itemId } : {}) });
524
+ return;
525
+ }
526
+ switch (method) {
527
+ case "turn/started": {
528
+ const id = String(params.turn?.id ?? "");
529
+ if (!id)
530
+ return;
531
+ if (!s.finished.has(id))
532
+ s.info = { ...s.info, status: "running", activeTurnId: id, updatedAt: Date.now() };
533
+ this.emit(threadId, "turn.started", s.info.model ? { model: s.info.model } : {}, { turnId: id });
534
+ return;
535
+ }
536
+ case "turn/completed": {
537
+ const turn = (params.turn ?? {});
538
+ const id = String(turn.id ?? s.info.activeTurnId ?? "");
539
+ if (id)
540
+ s.finished.add(id);
541
+ if (s.info.activeTurnId === id || !id) {
542
+ delete s.info.activeTurnId;
543
+ s.info = { ...s.info, status: "ready", updatedAt: Date.now() };
544
+ }
545
+ this.settlePending(s, "cancel");
546
+ const error = (turn.error ?? null);
547
+ if (error?.message && PLAN_REFUSED.test(String(error.message)) && s.info.model)
548
+ s.rerouting = this.afterPlanRefusal(s).catch(() => { });
549
+ const u = s.turnUsage;
550
+ this.emit(threadId, "turn.completed", { state: TURN_STATES[String(turn.status)] ?? "failed",
551
+ ...(error?.message ? { errorMessage: codexFailure(String(error.message), s.proc.stderr()) } : {}),
552
+ ...(s.info.model ? { model: s.info.model } : {}),
553
+ // Codex's input already includes cached tokens, and its output already
554
+ // includes reasoning (totalTokens = input + output), as t3code reads it.
555
+ ...(u ? { tokenUsage: { usageStatus: "complete", inputTokens: u.input, outputTokens: u.output,
556
+ cachedInputTokens: u.cached, reasoningTokens: u.reasoning } } : {}) }, id ? { turnId: id } : {});
557
+ return;
558
+ }
559
+ case "item/started":
560
+ case "item/completed":
561
+ this.item(s, method === "item/started" ? "item.started" : "item.completed", (params.item ?? {}), at);
562
+ return;
563
+ case "item/plan/delta":
564
+ if (typeof params.delta === "string" && params.delta)
565
+ this.emit(threadId, "turn.proposed.delta", { delta: params.delta }, at);
566
+ return;
567
+ case "turn/plan/updated": {
568
+ const plan = Array.isArray(params.plan) ? params.plan : [];
569
+ this.emit(threadId, "turn.plan.updated", { ...(typeof params.explanation === "string" ? { explanation: params.explanation } : {}),
570
+ plan: plan.map(p => ({ step: String(p.step ?? "step"), status: p.status === "completed" || p.status === "inProgress" ? p.status : "pending" })) }, at);
571
+ return;
572
+ }
573
+ case "turn/diff/updated":
574
+ if (typeof params.diff === "string")
575
+ this.emit(threadId, "turn.diff.updated", { unifiedDiff: params.diff }, at);
576
+ return;
577
+ case "thread/tokenUsage/updated":
578
+ this.tokenUsage(s, (params.tokenUsage ?? {}), at);
579
+ return;
580
+ case "thread/compacted": // deprecated form; the contextCompaction item carries it on current versions
581
+ if (!s.compacting)
582
+ this.emit(threadId, "thread.state.changed", { state: "compacted", trigger: "auto",
583
+ ...(s.contextUsed ? { beforeTokens: s.contextUsed } : {}) }, at);
584
+ return;
585
+ case "account/rateLimits/updated":
586
+ this.rateLimits(s, (params.rateLimits ?? {}));
587
+ return;
588
+ case "model/rerouted":
589
+ if (typeof params.fromModel === "string" && typeof params.toModel === "string") {
590
+ s.info = { ...s.info, model: params.toModel };
591
+ this.emit(threadId, "model.rerouted", { fromModel: params.fromModel, toModel: params.toModel,
592
+ ...(typeof params.reason === "string" ? { reason: params.reason } : {}) }, at);
593
+ }
594
+ return;
595
+ case "configWarning":
596
+ case "deprecationNotice":
597
+ if (typeof params.summary === "string")
598
+ this.emit(threadId, "config.warning", { summary: params.summary,
599
+ ...(typeof params.details === "string" ? { details: params.details } : {}) }, at);
600
+ return;
601
+ case "error": {
602
+ // A retried stream error is a warning; a final failure also arrives as
603
+ // a failed turn/completed, which is what fails the turn.
604
+ const error = (params.error ?? {});
605
+ this.emit(threadId, "runtime.warning", { message: String(error.message ?? "codex error"),
606
+ ...(params.willRetry === true ? { retrying: true } : {}) }, at);
607
+ return;
608
+ }
609
+ default:
610
+ return;
611
+ }
612
+ }
613
+ item(s, type, item, at) {
614
+ const threadId = s.info.threadId;
615
+ const itemType = codexItemType(item.type);
616
+ const itemId = typeof item.id === "string" ? item.id : undefined;
617
+ const extra = { ...at, ...(itemId ? { itemId } : {}) };
618
+ if (itemType === "user_message")
619
+ return;
620
+ // An async question (t3code): the message asks, the turn ends, and the
621
+ // person's next message is the answer.
622
+ if (type === "item.completed" && itemType === "assistant_message" && item.delivery === "async" && Array.isArray(item.questions) && item.questions.length) {
623
+ this.emit(threadId, "item.completed", { itemType, status: "completed", ...describeItem(itemType, item) }, extra);
624
+ this.emit(threadId, "user-input.requested", { responseMode: "message", questions: item.questions.map((q, index) => ({
625
+ id: String(index), header: "Question", question: String(q.title ?? ""), allowCustomAnswer: true, multiSelect: false,
626
+ options: (Array.isArray(q.options) ? q.options : []).map(o => (o && typeof o === "object"
627
+ ? { label: String(o.label ?? o.value ?? ""), description: String(o.description ?? "") }
628
+ : { label: String(o), description: "" }))
629
+ })) }, { ...at, requestId: `codex-async:${threadId}:${itemId ?? randomUUID()}` });
630
+ return;
631
+ }
632
+ if (itemType === "plan") {
633
+ if (type === "item.completed" && typeof item.text === "string" && item.text.trim())
634
+ this.emit(threadId, "turn.proposed.completed", { planMarkdown: item.text }, at);
635
+ return;
636
+ }
637
+ if (itemType === "context_compaction" && type === "item.completed") {
638
+ s.compacting = false;
639
+ this.emit(threadId, "item.completed", { itemType, status: "completed" }, extra);
640
+ // Codex reports the size after compaction on its next token update.
641
+ this.emit(threadId, "thread.state.changed", { state: "compacted", trigger: "auto", ...(s.contextUsed ? { beforeTokens: s.contextUsed } : {}) }, at);
642
+ return;
643
+ }
644
+ if (itemType === "context_compaction")
645
+ s.compacting = true;
646
+ const status = ITEM_STATUSES.has(item.status) ? item.status
647
+ : type === "item.completed" ? "completed" : "inProgress";
648
+ const payload = { itemType, status, ...describeItem(itemType, item) };
649
+ this.emit(threadId, type, payload, extra);
650
+ }
651
+ tokenUsage(s, usage, at) {
652
+ const total = usageOf(usage.total), last = usageOf(usage.last);
653
+ const window = typeof usage.modelContextWindow === "number" ? usage.modelContextWindow : undefined;
654
+ // Turn usage: growth of the running total, seeded by the first response.
655
+ s.baseline ??= { total, first: last };
656
+ const b = s.baseline;
657
+ s.turnUsage = { input: total.input - b.total.input + b.first.input, cached: total.cached - b.total.cached + b.first.cached,
658
+ output: total.output - b.total.output + b.first.output, reasoning: total.reasoning - b.total.reasoning + b.first.reasoning };
659
+ if (s.turnUsage.input < 0)
660
+ s.turnUsage = last; // Codex reset its running total
661
+ // t3code's normalizeCodexTokenUsage: `last` is the newest response; the context now holds its total.
662
+ const used = Number(usage.last?.totalTokens ?? 0);
663
+ if (used <= 0)
664
+ return;
665
+ s.contextUsed = used;
666
+ const processed = Number(usage.total?.totalTokens ?? 0);
667
+ this.emit(s.info.threadId, "thread.token-usage.updated", { usage: { usedTokens: used, ...(window ? { maxTokens: window } : {}),
668
+ ...(processed > used ? { totalProcessedTokens: processed } : {}),
669
+ inputTokens: last.input, cachedInputTokens: last.cached, outputTokens: last.output, reasoningOutputTokens: last.reasoning,
670
+ compactsAutomatically: true } }, at);
671
+ }
672
+ rateLimits(s, snapshot) {
673
+ const windows = ["primary", "secondary"].flatMap(key => {
674
+ const w = snapshot[key];
675
+ if (!w || typeof w.usedPercent !== "number")
676
+ return [];
677
+ return [{ id: key, usedPercent: w.usedPercent, ...(typeof w.windowDurationMins === "number" ? { windowMinutes: w.windowDurationMins } : {}),
678
+ ...(typeof w.resetsAt === "number" ? { resetsAt: w.resetsAt * 1000 } : {}) }];
679
+ });
680
+ if (windows.length)
681
+ this.emit(s.info.threadId, "account.rate-limits.updated", { windows,
682
+ ...(typeof snapshot.rateLimitReachedType === "string" ? { reached: snapshot.rateLimitReachedType } : {}) });
683
+ }
684
+ /**
685
+ * Server requests. Approvals become `request.opened` and wait for
686
+ * respondToRequest (or the turn/session ending, which cancels them);
687
+ * anything Loom can't answer yet is declined rather than left waiting.
688
+ */
689
+ async serverRequest(s, method, params) {
690
+ switch (method) {
691
+ case "item/commandExecution/requestApproval":
692
+ case "item/fileChange/requestApproval":
693
+ case "item/fileRead/requestApproval": {
694
+ const decision = await this.ask(s, method, params);
695
+ return { decision };
696
+ }
697
+ case "execCommandApproval":
698
+ case "applyPatchApproval": {
699
+ const decision = await this.ask(s, method, params);
700
+ return { decision: decision === "accept" ? "approved" : decision === "acceptForSession" ? "approved_for_session"
701
+ : { denied: { rejection: "Denied in Loom." } } };
702
+ }
703
+ case "item/permissions/requestApproval": {
704
+ const decision = await this.ask(s, method, params);
705
+ // Approving grants the requested profile; an empty grant withholds it.
706
+ return { permissions: decision === "accept" || decision === "acceptForSession" ? params.permissions ?? {} : {},
707
+ scope: decision === "acceptForSession" ? "session" : "turn" };
708
+ }
709
+ case "mcpServer/elicitation/request": {
710
+ // An MCP server asking for structured input: not answerable from Loom yet,
711
+ // so it's declined — but out loud, so a tool that fails next isn't a mystery.
712
+ const who = typeof params.serverName === "string" ? params.serverName : "an MCP server";
713
+ const what = typeof params.message === "string" ? `: ${params.message.slice(0, 200)}` : "";
714
+ this.emit(s.info.threadId, "runtime.warning", { message: `${who} asked for input${what} — Loom declined it (MCP forms aren't supported yet)` }, typeof params.turnId === "string" ? { turnId: params.turnId } : {});
715
+ return { action: "decline", content: null, _meta: null };
716
+ }
717
+ case "item/tool/requestUserInput":
718
+ return { answers: toCodexAnswers(await this.askUser(s, params)) };
719
+ default:
720
+ throw new Error(`Loom does not handle ${method}`);
721
+ }
722
+ }
723
+ /** A structured question (`request_user_input`): waits for respondToUserInput. */
724
+ askUser(s, params) {
725
+ const questions = toUserInputQuestions(params.questions);
726
+ if (s.stopping || !questions.length)
727
+ return Promise.resolve({});
728
+ const requestId = randomUUID();
729
+ const turnId = typeof params.turnId === "string" ? params.turnId : s.info.activeTurnId;
730
+ const secret = new Set(questions.filter(q => q.secret).map(q => q.id));
731
+ return new Promise(resolve => {
732
+ s.inputs.set(requestId, { resolve, ...(turnId ? { turnId } : {}), ...(secret.size ? { secret } : {}) });
733
+ this.emit(s.info.threadId, "user-input.requested", { questions }, { requestId, ...(turnId ? { turnId } : {}),
734
+ ...(typeof params.itemId === "string" ? { itemId: params.itemId } : {}) });
735
+ });
736
+ }
737
+ ask(s, method, params) {
738
+ if (s.stopping)
739
+ return Promise.resolve("cancel");
740
+ const requestType = REQUEST_TYPES[method] ?? "unknown";
741
+ const requestId = randomUUID();
742
+ const turnId = typeof params.turnId === "string" ? params.turnId : s.info.activeTurnId;
743
+ return new Promise(resolve => {
744
+ s.pending.set(requestId, { method, requestType, params, resolve, ...(turnId ? { turnId } : {}) });
745
+ this.emit(s.info.threadId, "request.opened", { requestType, ...describeRequest(method, params),
746
+ args: { ...requestArgs(method, params) }, options: APPROVAL_OPTIONS }, { requestId, ...(turnId ? { turnId } : {}),
747
+ ...(typeof params.itemId === "string" ? { itemId: params.itemId } : {}) });
748
+ });
749
+ }
750
+ }
751
+ /** What a person may answer to a Codex approval. */
752
+ const APPROVAL_OPTIONS = [
753
+ { decision: "accept", label: "Allow" },
754
+ { decision: "acceptForSession", label: "Allow for this session" },
755
+ { decision: "decline", label: "Deny" },
756
+ ];
757
+ /** t3code's toUserInputQuestions: a question needs an id, header, prompt and options. */
758
+ function toUserInputQuestions(raw) {
759
+ if (!Array.isArray(raw))
760
+ return [];
761
+ return raw.flatMap(q => {
762
+ const text = (v) => (typeof v === "string" ? v.trim() : "");
763
+ const options = (Array.isArray(q.options) ? q.options : [])
764
+ .map(o => ({ label: text(o.label), description: text(o.description) })).filter(o => o.label);
765
+ if (!text(q.id) || !text(q.question))
766
+ return [];
767
+ return [{ id: text(q.id), header: text(q.header) || "Question", question: text(q.question), options,
768
+ ...(q.isOther === true || !options.length ? { allowCustomAnswer: true } : {}), multiSelect: false,
769
+ ...(q.isSecret === true ? { secret: true, allowCustomAnswer: true } : {}) }];
770
+ });
771
+ }
772
+ /** Loom answers → Codex's `{ [questionId]: { answers: string[] } }` (t3code's toCodexUserInputAnswer). */
773
+ function toCodexAnswers(answers) {
774
+ const out = {};
775
+ for (const [id, value] of Object.entries(answers)) {
776
+ if (typeof value === "string")
777
+ out[id] = { answers: [value] };
778
+ else if (Array.isArray(value))
779
+ out[id] = { answers: value.filter((v) => typeof v === "string") };
780
+ else if (value && typeof value === "object" && Array.isArray(value.answers))
781
+ out[id] = { answers: value.answers.map(String) };
782
+ }
783
+ return out;
784
+ }
785
+ const cursorPayload = (cursor) => (cursor !== undefined ? { resume: cursor } : {});
786
+ /** Title, detail and normalized data for a Codex item. */
787
+ function describeItem(itemType, item) {
788
+ switch (itemType) {
789
+ case "assistant_message":
790
+ return typeof item.text === "string" ? { detail: item.text } : {};
791
+ case "reasoning": {
792
+ const parts = [...(item.summary ?? []), ...(item.content ?? [])].map(String);
793
+ const text = parts.join("\n").trim();
794
+ return text ? { detail: text } : {};
795
+ }
796
+ case "command_execution": {
797
+ const command = String(item.command ?? "");
798
+ return { detail: command, data: { command, ...(typeof item.cwd === "string" ? { cwd: item.cwd } : {}),
799
+ exitCode: typeof item.exitCode === "number" ? item.exitCode : null,
800
+ ...(typeof item.aggregatedOutput === "string" ? { output: item.aggregatedOutput } : {}) } };
801
+ }
802
+ case "file_change": {
803
+ const changes = (item.changes ?? [])
804
+ .filter(c => c.path).map(c => ({ path: String(c.path), kind: c.kind?.type ?? "update", ...(typeof c.diff === "string" && c.diff ? { diff: c.diff } : {}) }));
805
+ return { data: { changes } };
806
+ }
807
+ case "mcp_tool_call": {
808
+ const { output, images } = mcpResult(item.result);
809
+ const err = item.error && typeof item.error === "object" ? String(item.error.message ?? "") : typeof item.error === "string" ? item.error : "";
810
+ const server = typeof item.server === "string" ? item.server : "";
811
+ return { detail: `${server ? server + " · " : "mcp: "}${String(item.tool ?? "")}`, data: { tool: String(item.tool ?? "mcp"), ...(server ? { server } : {}),
812
+ ...(item.arguments && typeof item.arguments === "object" ? { input: item.arguments } : {}),
813
+ ...(output ? { output } : {}), ...(err ? { error: err } : {}), ...(images.length ? { images } : {}) } };
814
+ }
815
+ case "dynamic_tool_call":
816
+ case "collab_agent_tool_call": {
817
+ const { output, images } = mcpResult({ content: item.contentItems });
818
+ const prompt = typeof item.prompt === "string" ? item.prompt : "";
819
+ return { detail: prompt ? `${String(item.tool ?? itemType)}: ${prompt.replace(/\s+/g, " ").slice(0, 140)}` : String(item.tool ?? itemType),
820
+ data: { tool: String(item.tool ?? itemType), ...(item.arguments && typeof item.arguments === "object" ? { input: item.arguments } : {}),
821
+ ...(output ? { output } : {}), ...(images.length ? { images } : {}) } };
822
+ }
823
+ case "web_search":
824
+ return { detail: `search: ${String(item.query ?? "")}`.slice(0, 160), data: { tool: "web_search", ...(item.query ? { input: { query: String(item.query) } } : {}) } };
825
+ case "image_view":
826
+ return { detail: typeof item.path === "string" ? `view ${item.path}` : "view image",
827
+ data: { tool: "image_view", ...(typeof item.path === "string" ? { images: [{ path: item.path }] } : {}) } };
828
+ case "error":
829
+ return typeof item.message === "string" ? { detail: item.message } : {};
830
+ default:
831
+ return {};
832
+ }
833
+ }
834
+ /** An MCP result's text and images (content blocks: {type:"text"|"image", text | data+mimeType}). */
835
+ function mcpResult(result) {
836
+ const content = result && typeof result === "object" ? result.content : undefined;
837
+ if (!Array.isArray(content))
838
+ return { output: typeof result === "string" ? result : "", images: [] };
839
+ const blocks = content;
840
+ return {
841
+ output: blocks.map(b => (b.type === "text" && typeof b.text === "string" ? b.text : "")).filter(Boolean).join("\n"),
842
+ images: blocks.flatMap(b => (b.type === "image" && typeof b.data === "string" ? [{ data: b.data, mime: String(b.mimeType ?? b.mime_type ?? "image/png") }] : [])),
843
+ };
844
+ }
845
+ function describeRequest(method, params) {
846
+ const text = (v) => (typeof v === "string" && v.trim() ? v.trim() : undefined);
847
+ switch (method) {
848
+ case "item/commandExecution/requestApproval": {
849
+ const d = text(params.command) ?? text(params.reason);
850
+ return d ? { detail: `shell: ${d}`.slice(0, 200) } : {};
851
+ }
852
+ case "execCommandApproval": {
853
+ const command = Array.isArray(params.command) ? params.command.join(" ") : text(params.command);
854
+ return command ? { detail: `shell: ${command}`.slice(0, 200) } : {};
855
+ }
856
+ case "item/fileChange/requestApproval":
857
+ case "applyPatchApproval":
858
+ // The reason rides in the card's input; the summary says what is asked.
859
+ return { detail: "apply file changes" };
860
+ case "item/permissions/requestApproval":
861
+ return { detail: text(params.reason) ?? "grant additional permissions" };
862
+ default:
863
+ return {};
864
+ }
865
+ }
866
+ /** What the approval card shows as the tool's input. */
867
+ function requestArgs(method, params) {
868
+ switch (method) {
869
+ case "item/commandExecution/requestApproval":
870
+ return { command: params.command ?? null, cwd: params.cwd ?? null, reason: params.reason ?? null };
871
+ case "execCommandApproval":
872
+ return { command: Array.isArray(params.command) ? params.command.join(" ") : params.command ?? null };
873
+ case "item/fileChange/requestApproval":
874
+ return { reason: params.reason ?? null, grantRoot: params.grantRoot ?? null };
875
+ case "applyPatchApproval":
876
+ return { reason: params.reason ?? null };
877
+ default:
878
+ return { ...params };
879
+ }
880
+ }
881
+ //# sourceMappingURL=adapter.js.map