@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
package/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # Loom
2
2
 
3
3
  [![ci](https://github.com/nickthelegend/loom/actions/workflows/ci.yml/badge.svg)](https://github.com/nickthelegend/loom/actions/workflows/ci.yml)
4
- [![npm](https://img.shields.io/npm/v/threadloom)](https://www.npmjs.com/package/threadloom)
4
+ [![npm](https://img.shields.io/npm/v/@loompad/cli)](https://www.npmjs.com/package/@loompad/cli)
5
5
  [![license](https://img.shields.io/badge/license-MIT-blue)](LICENSE)
6
6
 
7
7
  **The shared-memory layer for AI dev environments.** Every coding agent — Claude Code,
@@ -9,13 +9,25 @@ OpenCode, Antigravity, Codex, … — keeps its own brain in its own files. Loom
9
9
  **one brain**: connect your ADEs, and their memory, decisions, and context become a
10
10
  single shared thread that flows from one agent to the next.
11
11
 
12
- Today that means **Claude Code, Codex, OpenCode and Grok Code** as full agents, each
13
- verified against a real version, plus **Antigravity and Kiro** driven through their
14
- own windows —
12
+ Today that means **Claude Code, Codex, OpenCode, Grok Code and Antigravity** as full
13
+ agents, each verified against a real version, plus **Kiro** driven through its own
14
+ window —
15
15
  see [Supported agents](#supported-agents) for exactly how far each one goes, and
16
16
  [How memory actually reaches a model](#how-memory-actually-reaches-a-model) for the
17
17
  part most tools gloss over.
18
18
 
19
+ An agent doesn't have to be a CLI any more. A **[`model` agent](#agents-that-are-models)**
20
+ is an HTTP endpoint — any OpenAI-compatible provider, a model you pick from a list,
21
+ nothing to install — which means the cheap work can go somewhere cheap, and on a
22
+ provider's free tier it costs nothing at all. Pin one to a
23
+ **[thread](#threads-and-who-answers-in-them)** and two conversations run on two models
24
+ at once; **[ask several](#asking-several-models-at-once)** and one prompt fans out to
25
+ all of them.
26
+
27
+ And because a fleet you can't see is a fleet you can't trust, every turn, handoff and
28
+ route step is exported as **OpenTelemetry** — traces, metrics and logs — and read back
29
+ into an in-app **[Observatory](#observability)**.
30
+
19
31
  Loom is **not** another IDE. It's the thin layer *between* your agents — the continuity
20
32
  and memory they don't share on their own.
21
33
 
@@ -39,6 +51,244 @@ and memory they don't share on their own.
39
51
  <em>One thread over every agent — projects and chats on the left, the shared conversation in the middle, the Explorer on the right, and a composer you switch agents from without leaving the box.</em>
40
52
  </p>
41
53
 
54
+ ## One link — a teammate, their agents, your repo
55
+
56
+ Click **Invite** in a project, or run `loom invite`. Loom makes sure you're on a
57
+ team that shares this repo (it creates the team and shares the repo if you
58
+ haven't) and gives you one https link to send.
59
+
60
+ Your teammate clicks it. If they already have Loom, it opens in their Loom and
61
+ one **Join** does the rest. If they don't, the page shows one command that
62
+ installs Loom and joins:
63
+
64
+ 1. **Sign in** with GitHub.
65
+ 2. **Join** the team, and keep the team key the link carries.
66
+ 3. **Get the repo.** Loom uses a clone they already have, or clones it. For a
67
+ private repo, *your* Loom adds them as a collaborator the moment they join,
68
+ and *theirs* accepts GitHub's invitation.
69
+ 4. **Open it with their agents** — whichever of Codex, OpenCode, Claude Code
70
+ and the rest are installed on their machine.
71
+ 5. **Share** it with the team, so you see each other's live agents, goals and
72
+ files.
73
+ 6. **Set up your crews**, filled from their own agents.
74
+
75
+ ```bash
76
+ loom invite # in a project: the link, a QR, and a message to paste
77
+ loom join '<link>' # the same thing from a terminal
78
+ ```
79
+
80
+ The invite is the part of the link after `#`. Browsers never send that part to
81
+ a server, including the join page's. A link works once and expires in a day.
82
+ It carries the team key, so send it the way you'd send a password.
83
+
84
+ ## Quota Relay — a limit doesn't stop the work
85
+
86
+ Hit Claude's 5-hour window halfway through a task, and the work normally
87
+ stops: Claude Code parks the turn until the reset, and Codex ends it with
88
+ "try again at 3:04 PM". Loom recognises the limit and responds:
89
+
90
+ - **It hands the work on.** It stops the parked turn and passes the baton to
91
+ the next agent, with a briefing: "claude ran out of quota; its changes are in
92
+ the working tree, continue from them". It re-sends the same request in the
93
+ same chat.
94
+ - **It routes new prompts.** While an agent is out, anything sent to it goes
95
+ to the agent standing in.
96
+ - **It gives the chat back.** When the window resets, the baton returns.
97
+ - **It waits if it has to.** If no other agent is free, the request is queued
98
+ for the moment the limit lifts.
99
+
100
+ The thread records each step ("claude hit its usage limit — codex carries on;
101
+ claude is back around 19:00"), and your phone gets a push. By default only
102
+ your other CLI agents take over, never a metered API model. Set the order
103
+ with `relay.to`. `relay.auto: false` only reports the limit, and
104
+ `relay.handBack: false` keeps the stand-in. Limits are read from what Claude
105
+ and Codex report about their usage windows, and from the error text any CLI
106
+ gives ("usage limit", "rate limited", "quota exceeded", with "try again at…"
107
+ or "resets in…").
108
+
109
+ ## Proof — is "done" true?
110
+
111
+ Agents say "all tests pass" when three of fourteen ran, or when none did. When
112
+ an agent's reply claims the tests, build, typecheck or lint pass, Loom checks
113
+ the claim three ways:
114
+
115
+ - **What it said.** The claims in its final message, including any numbers
116
+ ("all 14 tests pass").
117
+ - **What it ran.** The commands from that turn and their exit codes. A claim
118
+ with no test run behind it, or with a failed one, doesn't hold.
119
+ - **A clean re-run.** Loom runs your tests again in a fresh worktree containing
120
+ exactly the agent's uncommitted changes and new files. Dependency folders
121
+ are linked, not reinstalled. Your working tree is never touched.
122
+
123
+ The verdict goes into the thread as a card: **Verified**, **Didn't hold** (with
124
+ the reason, such as "said 14 pass; 11 did"), or **Unverified**. If a "done"
125
+ doesn't hold, your phone gets a push. Every turn card also has **Check it**,
126
+ to verify any turn on demand.
127
+
128
+ ```bash
129
+ loom proof # check the newest turn (exits 1 if it didn't hold)
130
+ loom proof --test "npm run test:unit"
131
+ ```
132
+
133
+ The test command is detected from the project (`npm test`, `cargo test`,
134
+ `go test ./...`, `pytest`, `make test`). Override it with `proof.testCommand`
135
+ in `.loom/config.json`; `proof.auto: "off"` turns off automatic checks.
136
+ Pass/fail counts are read from vitest, jest, node:test, mocha, pytest, cargo,
137
+ go test, rspec and phpunit output.
138
+
139
+ ## Crews — agents with roles, on one goal
140
+
141
+ A crew is a few of your agents with jobs: a **lead** who plans, **builders**, a
142
+ **reviewer**, a **tester**. You give the crew a goal. The Lead turns it into
143
+ cards, and you approve the plan. Each card is then built, reviewed (changes go
144
+ back to the same builder), and tested, in the goal's own worktree and branch.
145
+ When it's done, you apply it.
146
+
147
+ ```bash
148
+ loom crew create ship # templates: ship · fix · research · solo
149
+ loom crew goal "Add rate limiting to the public API"
150
+ loom crew approve # or: loom crew say "smaller cards, please"
151
+ loom crew # cards by stage; questions a teammate asked you
152
+ loom crew apply # merge the goal's branch into yours
153
+ ```
154
+
155
+ The **Crew** tab shows the same thing. Every commit carries `Loom-Crew`,
156
+ `Loom-Goal`, `Loom-Card` and `Loom-Teammate` trailers. Phase 1 works one card
157
+ at a time; [docs/proposals/agent-teams.md](docs/proposals/agent-teams.md) has
158
+ the rest of the plan.
159
+
160
+ ## Orchestra — one conductor, many agents, in parallel
161
+
162
+ Give Loom a goal and let one agent run the others. The **orchestrator** can be any
163
+ agent: Claude Code, Codex (ChatGPT), Antigravity, Grok or OpenCode.
164
+
165
+ 1. It reads the repository and splits the goal into a **task graph**.
166
+ 2. Loom runs every ready task **at the same time** on the **worker** agents the
167
+ plan names. Each task gets its own thread and its own git worktree.
168
+ 3. Loom merges each finished task into one integration branch.
169
+ 4. The orchestrator reviews the results. It can accept them, send follow-ups,
170
+ spawn more tasks, or finish.
171
+
172
+ ```bash
173
+ loom orchestrate "add OAuth login with tests" -o claude-code -w codex,antigravity,claude-code -p 6
174
+ ```
175
+
176
+ ```
177
+ ┌──────────── orchestrator (any agent) ────────────┐
178
+ goal ──▶ plan │ t1 → codex t2 → antigravity t3 → codex │ ◀── review, follow up, done
179
+ └──────┬──────────────────┬────────────────┬──────┘
180
+ ▼ ▼ ▼ (after t1)
181
+ worktree t1 worktree t2 worktree t3
182
+ └──────── merged into loom/orchestra/<run>/main ──▶ apply to your branch
183
+ ```
184
+
185
+ - **Any agent can orchestrate.** The orchestrator never runs shell commands. It
186
+ answers in a small JSON block that Loom parses, so an agent in a sandbox or with
187
+ no tools at all can still lead.
188
+ - **Many workers, mixed or the same kind.** Three Codex tasks and two Antigravity
189
+ tasks run side by side as separate CLI sessions, capped by `--parallel` (1–12).
190
+ - **Real dependencies.** A task with `dependsOn` starts only after those tasks are
191
+ merged, and it branches from a tip that already contains their work.
192
+ - **Nothing touches your branch until you say so.** All work lands on
193
+ `loom/orchestra/<run>/main`. You merge it with `loom orchestra:apply <run>`, or
194
+ with the **Apply** button in the app. Merge conflicts go back to the
195
+ orchestrator, which can ask the worker to resolve them.
196
+ - **You're in the loop.** When the orchestrator needs a decision it asks, and the
197
+ run waits for your reply (`loom orchestra:reply <run> "…"`, or the app on your
198
+ phone). You can steer a run mid-flight the same way.
199
+ - **Everything is visible.** Every step is an `orchestra` event. The web app,
200
+ desktop app, phone and CLI all watch the same run live, and each task's stream
201
+ sits in its own thread. The project's budgets, quarantine and shared brain apply
202
+ to every worker.
203
+
204
+ - **Race instead of plan.** `--race` (or the Race switch in Orchestrate) gives
205
+ every worker the whole goal in its own worktree, with no orchestrator. The
206
+ entrants finish side by side, each with its diff, and you keep one:
207
+ **Pick this one** in the app, or `loom orchestra:apply <run> --task race-<agent>`.
208
+ - **A restart doesn't lose the run.** If Loom stops mid-run, the run says it
209
+ was interrupted. **Resume** (or `loom orchestra:resume <run>`) picks up the
210
+ tasks that were running and briefs the orchestrator again.
211
+
212
+ Under the hood: [`src/core/orchestra.ts`](src/core/orchestra.ts).
213
+ `node scripts/verify-orchestra.mjs` runs a real orchestra against your installed
214
+ CLIs and checks the result on disk.
215
+
216
+ ## Codex & GPT‑5.6
217
+
218
+ Loom is built around orchestrating **OpenAI Codex** as a first‑class agent — and in this
219
+ build every Codex turn runs **GPT‑5.6**: Codex's model, pinned in Loom's per‑project agent
220
+ config (`.loom/config.json` → `codex → model: "gpt-5.6-terra"`) at high reasoning effort.
221
+
222
+ > The slug is `gpt-5.6-terra`, not a bare `gpt-5.6` — that is the id Codex actually
223
+ > accepts, and asking for `gpt-5.6` is a 400 (`not supported when using Codex with a
224
+ > ChatGPT account`). Loom's model picker asks the CLI what it supports
225
+ > (`codex debug models`) rather than shipping a list that goes stale, which is exactly
226
+ > how this got caught.
227
+
228
+ - **Codex holds the baton like any other agent.** The adapter
229
+ ([`src/providers/codex/adapter.ts`](src/providers/codex/adapter.ts)) keeps a warm
230
+ `codex app-server` (JSON-RPC) per chat: it opens a thread, streams Codex's items
231
+ (`agentMessage` / `commandExecution` / `fileChange`), its context usage and compaction,
232
+ and **keeps the same thread across turns** (resuming it after a restart) so Codex keeps
233
+ its own context between handoffs.
234
+ - **Codex reads and writes the shared brain.** Before a Codex turn, Loom projects the
235
+ unified memory (imported ADE memory + decisions + the thread) into its briefing; its
236
+ replies and memory writes land back in the one shared store. So a handoff
237
+ *Claude Code → Codex* carries the full context, and the next agent inherits what Codex
238
+ learned. *(Verified end‑to‑end: Codex recalled a value another agent set one turn
239
+ earlier, and its turns emit `run_complete` + `memory_add`.)*
240
+ - **Voice, on real hardware.** The **LoomPad** is a physical ESP32‑S3 macropad, and the
241
+ loop is: press the **Codex** key to hand Codex the baton, hold the mic and speak, hear
242
+ the reply spoken back through the pad. It lives in this repo under
243
+ [`hardware/orchestrator-pad/`](hardware/orchestrator-pad/README.md) — parametric CAD,
244
+ printable STLs, the wiring diagram, the Arduino firmware and the voice backend — with
245
+ the daemon-side proxy endpoints (`/api/loompad/health`, `/api/loompad/connect`) and the
246
+ status pill that reads them. See [Hardware](#hardware) to print and wire one.
247
+ - **Codex as a dev agent, too.** Because Codex is a full agent, you can hand it real work
248
+ inside Loom — `loom route ship "…"` routes *plan → Codex executes → review*, the brain
249
+ flowing hop to hop.
250
+
251
+ In short: **GPT‑5.6, via Codex, is one of the interchangeable minds Loom keeps in sync** —
252
+ start a thread in Claude Code, hand it to Codex mid‑task, and it picks up with the whole
253
+ shared context intact.
254
+
255
+ ## Observability
256
+
257
+ Loom exports its agent activity as **all three OpenTelemetry signals**, and then reads
258
+ them back into an in-app **Observatory** so you never have to leave to answer "what is
259
+ the fleet doing, and what did it cost".
260
+
261
+ **Backend-agnostic.** Loom speaks OTLP/HTTP and nothing else — no vendor SDK, no
262
+ vendor-specific code path. Point `OTEL_EXPORTER_OTLP_ENDPOINT` at a Collector, Grafana,
263
+ Jaeger, SigNoz, Honeycomb, anything with a `/v1/traces` endpoint. An ingestion key
264
+ rides in through the standard `OTEL_EXPORTER_OTLP_HEADERS`, so a new destination is a
265
+ config change and never a code change.
266
+
267
+ | Signal | What Loom puts in it |
268
+ |---|---|
269
+ | **Traces** | every turn as `gen_ai.agent.turn` (model, tokens, cost, duration), tool calls, baton handoffs, route steps, memory folds — one trace per turn |
270
+ | **Metrics** | tokens, turn duration, turns, cost, active agents, handoffs — GenAI semantic conventions |
271
+ | **Logs** | every message, tool call, edit, decision and error, correlated to the span it happened inside |
272
+
273
+ The Observatory tab renders it back: a live fleet canvas, per-agent **burn rate** and
274
+ budgets, **Span Replay** with a trace waterfall, a **Decisions** explorer, a **Logs**
275
+ view, **Time-Travel Replay** that refolds the whole run from the event log, and
276
+ **Ask** — a question about your fleet, answered from its own telemetry.
277
+
278
+ **Self-heal** closes the loop: point your monitoring's webhook at
279
+ `POST /api/webhooks/alerts` (the Alertmanager shape SigNoz, Prometheus and Grafana all
280
+ send) and a firing alert takes the named agent out of rotation until it resolves. That
281
+ is the part a dashboard can't show you — it knows the alert fired, only Loom knows the
282
+ fleet reacted.
283
+
284
+ Two rules run through all of it: **nothing is estimated**, and **absent beats invented**.
285
+ Tokens and cost are what each agent's own CLI reported; an adapter that reports tokens
286
+ but no dollar figure (codex) produces no cost datapoint rather than a zero, because
287
+ zeros are indistinguishable from real cheap turns once summed.
288
+
289
+ Off with `DO_NOT_TRACK=1`. Full details, every variable, and how to verify it's flowing:
290
+ **[docs/observability.md](docs/observability.md)**.
291
+
42
292
  ## Why
43
293
 
44
294
  Every coding agent keeps its own brain. Claude Code's memory can't be read by
@@ -52,7 +302,7 @@ agents' **memory together** so work *continues* across them instead of forking.
52
302
  - **One brain across every ADE** — Loom imports each agent's native memory
53
303
  (`CLAUDE.md`, `AGENTS.md`, …) into a unified store, merges it with your decisions and
54
304
  the shared thread, and hands the whole thing to whoever picks up next. `loom memory`.
55
- - **The baton** — exactly one agent works at a time; passing it *carries the context*
305
+ - **The baton** — exactly one agent may *write* at a time; passing it *carries the context*
56
306
  (interrupt-safe, memory projected, briefing armed). Not isolation — continuation.
57
307
  - **Routes** — let Loom drive the chain: `loom route ship "add dark mode"` runs
58
308
  plan → execute → review as one command, the brain flowing hop to hop; or `loom route
@@ -66,8 +316,34 @@ agents' **memory together** so work *continues* across them instead of forking.
66
316
 
67
317
  Requires **Node ≥ 22.5** (Loom's event log uses the built-in `node:sqlite`).
68
318
 
319
+ On **Windows**, install Node.js and Git for Windows, then use PowerShell or
320
+ Command Prompt. Bash and WSL are not required. From a source checkout:
321
+
322
+ ```powershell
323
+ git clone https://github.com/nickthelegend/loom.git
324
+ cd loom
325
+ npm.cmd ci
326
+ npm.cmd link
327
+ loom.cmd doctor
328
+ ```
329
+
330
+ For a managed source installation, run `scripts/install.ps1` in PowerShell
331
+ (it clones into `%USERPROFILE%\.loom-src`, builds, and links the CLI). Use
332
+ `loom.cmd` when PowerShell's execution policy blocks npm's `.ps1` shim.
333
+ Install agents separately: `npm.cmd i -g @openai/codex opencode-ai`, then
334
+ sign in to the agents you use. Claude Code's native Windows installer is also
335
+ detected under `%USERPROFILE%\.local\bin`.
336
+
337
+ Normal chat, shared memory, routes, Orchestra worktrees and crews use the
338
+ standard runtime on Windows. The optional **native continuity** mode
339
+ (`brain.continuity: true`) needs verified process containment. On Windows,
340
+ every harness runs inside a Job Object, and Loom checks the containment
341
+ works on this machine before it offers the mode; if the check fails, the
342
+ agent says why and stays on the standard runtime. Leave it off for Orchestra
343
+ and crews, which also run outside native continuity on other OSes.
344
+
69
345
  ```bash
70
- npm install -g threadloom # → `loom` on your PATH
346
+ npm install -g @loompad/cli # → `loom` on your PATH
71
347
  ```
72
348
 
73
349
  Other paths:
@@ -92,13 +368,118 @@ loom doctor # checks node, agents, tailscale, daemon, and your project
92
368
 
93
369
  Surfaces, all talking to the same daemon:
94
370
  - **TUI / CLI** — `loom` (default), `loom chat`, `loom send`, …
95
- - **Desktop app** — [`desktop/`](desktop/README.md): `cd desktop && npm install && npm
96
- start` opens a native Electron window (starts the daemon and pairs itself) on the
97
- workspace below.
98
- - **Phone app** — [`app/`](app/README.md): `cd app && npx expo install && npx expo start`,
99
- scan with Expo Go (voice input, per-prompt diffs, push).
371
+ - **Desktop app (Loom Desktop)** — prebuilt for
372
+ [**macOS**, **Linux**, and **Windows**](https://loompad.tech/#download)
373
+ (`.dmg` · `.AppImage` · `.exe`; the macOS dmg is ad-hoc signed, so right-click → **Open**
374
+ the first time), or build from [`desktop/`](desktop/README.md): `cd desktop && npm
375
+ install && npm start`. Either way it opens a native window that starts the daemon and
376
+ pairs itself.
377
+ - **Phone app (LoomPad)** — install the newest
378
+ [Android APK](https://loompad.tech/download/android) (allow unknown sources; Google
379
+ Play is coming), open **Loom**, and **Scan QR code** from the desktop's *Connect a phone*.
380
+ Voice input, per-prompt diffs, push. Or build from source
381
+ ([`app/`](app/README.md)): `cd app && npx expo install && npx expo start`.
100
382
  - **Web app** — no install; `loom pair` → open the link. Same workspace in the browser.
101
383
 
384
+ ## Fan out, retry, reap
385
+
386
+ Three moves the fleet learned for when one turn isn't the right shape:
387
+
388
+ - **Sub-agents** — a turn can fan a subtask out to a child that runs alongside
389
+ it and **never holds the baton**: `loom spawn "audit the tests" --agent codex`.
390
+ The child gets a narrowed briefing (its task, the project's rules, retrieval
391
+ scoped to its own work — not the parent's thread), reports back as
392
+ `subtask_done`, and everything it learns lands in the one brain. Capped at
393
+ four per project, because each child is a real CLI process.
394
+ - **Retry on another agent** — when a turn fails, `loom retry <agentId>`
395
+ re-runs the failed prompt on the agent you choose, with the failure attached
396
+ as a briefing instead of pasted into the message: the thread shows a clean
397
+ prompt twice, and the second agent is told what was tried and what it died of.
398
+ - **Reap a hung session** — a CLI that hangs holds `busy` forever and blocks
399
+ every dispatch. `loom stale` names what's hung (busy past ten minutes);
400
+ `loom reap <agentId>` interrupts, stops, respawns from config, and releases
401
+ the baton if the corpse held it.
402
+
403
+ ## Native context continuity (opt-in)
404
+
405
+ The new deterministic Brain path supports sequential switching between checked
406
+ Codex/Claude CLI profiles with scoped native sessions, protected user context,
407
+ SQLite search, source-backed checkpoints and delivery diagnostics. It requires no
408
+ embedding model or inference package. Enable **Native context continuity** in
409
+ Settings → Preferences; the legacy workflow remains the default.
410
+
411
+ Unknown CLI versions, OpenCode/bridges, attachments and parallel execution are
412
+ gated in this mode. Overflow saves a reviewable request instead of silently dropping
413
+ user intent. See [native continuity](docs/brain-continuity.md),
414
+ [current architecture](ARCHITECTURE.md) and
415
+ [remaining verification gates](docs/refactoring/BRAIN-TODO.md).
416
+
417
+ ## Several sessions, one brain
418
+
419
+ `loom agents:add claude-code` a second time doesn't error — it adds
420
+ `claude-code-2`: its own session, its own context window, **the same project
421
+ brain**. Name them for their jobs (`--as reviewer --role reviewer`) and pick
422
+ per message. Memory import dedupes by file and units are project-scoped, so
423
+ what one session learns the others retrieve. One baton still rules them all:
424
+ two sessions of a kind never means two writers at once.
425
+
426
+ ## The Browser tab
427
+
428
+ Agents write Playwright specs constantly and could never watch them run. The
429
+ **Browser** tab (next to Console in the dock) lists the project's specs
430
+ (`*.spec.*`, `*.e2e.*` — never inside `node_modules`), runs one on click with
431
+ the reporter streaming line by line, records pass/fail in the Console, and on
432
+ failure stages the reporter's own words in the composer to hand back to
433
+ whichever agent should fix it. Playwright stays the project's dependency (`npx
434
+ --no-install`) — a project without it is told so plainly.
435
+
436
+ **Your dev servers live there too.** Declare them under `servers` in
437
+ `.loom/config.json` (Loom suggests them from `package.json` and waits for you
438
+ to accept) and the rail shows each one's real state: **running** means a port
439
+ answered, **starting** means the process is up but nothing is listening yet,
440
+ and **crashed** carries the exit code it died with. Start, stop and restart
441
+ from the row; click the row to point the preview at it; open its output under
442
+ the page, which is where the reason for a blank frame usually is. A server
443
+ that dies while an agent works against it says so in the thread, and every
444
+ server Loom started is stopped when the daemon goes.
445
+
446
+ From the terminal: `loom servers`, `loom servers start|stop|restart <name>`,
447
+ `loom servers logs <name> -n 100`.
448
+
449
+ **The page can talk back.** Click a server and Loom previews it through its
450
+ own proxy, which forwards everything (including the socket hot reload rides on)
451
+ and injects one small script. The **Page** pane then shows what that page
452
+ logged and what it fetched, live — and a click puts any line into the composer
453
+ as context, which is the whole point: the loop used to be *see the error,
454
+ select it, retype it*. **Pick** turns the cursor into an element picker: click
455
+ the thing that's wrong and its selector, text, size, markup and computed
456
+ styles (fonts, colours, spacing, layout) land in the composer, along with a
457
+ picture of just that element, so any agent can see what you mean. The picture
458
+ is taken at your width and colour scheme with the project's own Playwright.
459
+ Nothing about the page is rewritten; the script only reports.
460
+
461
+ **The preview itself** emulates a width (Fit / 375 / 768 / 1280, remembered per
462
+ project, scaled down when the dock is narrower), reloads itself when an agent
463
+ changes files — a toggle, on by default — and has a camera button that puts a
464
+ picture of what you're looking at into the composer. The page is another
465
+ origin, so the browser won't let it photograph itself: the daemon takes the
466
+ shot with the project's own Playwright, and a project without Playwright is
467
+ told exactly that rather than handed an empty file.
468
+
469
+ **Dark mode, on the page being previewed** — Auto / ☀ / ☾, independent of
470
+ Loom's own theme and remembered per project. No API lets one document set
471
+ another's `prefers-color-scheme`; Loom isn't outside the page, though — the
472
+ proxy served it *and* its stylesheets — so the bridge re-points the page's own
473
+ `@media (prefers-color-scheme: …)` rules, sets the UA colour-scheme, and makes
474
+ `matchMedia` answer the way you asked (firing `change`, so an app that themes
475
+ itself in JavaScript re-renders). Every rewritten rule remembers the media text
476
+ it came with, so **Auto is the page exactly as it shipped**; stylesheets Loom
477
+ doesn't serve can't be read and aren't claimed. A page you typed into the
478
+ address bar rather than previewing through Loom says so instead of offering a
479
+ switch that does nothing — and the screenshot drives a real browser, where the
480
+ scheme is honoured either way. Both conditions ride along with what you send:
481
+ *Screenshot taken at 375×812, dark mode*, and an element pick names them too.
482
+
102
483
  ## The workspace
103
484
 
104
485
  On a wide screen the web app (and the desktop shell around it) is a full workspace for
@@ -198,7 +579,7 @@ form — the same bet the agent adapters make by shelling out to the CLIs you al
198
579
 
199
580
  ```bash
200
581
  cd your-project
201
- loom init # detects installed agents (claude, opencode), assigns roles
582
+ loom init # detects the agents installed on this machine, assigns roles
202
583
  loom # opens the TUI — a tabbed workspace (Thread · Board · Brain · Diff)
203
584
  ```
204
585
 
@@ -267,7 +648,11 @@ What happens per hop: interrupt-safe **handoff** → shared-memory **projection*
267
648
  - the agent asks a question → the route **pauses** (`waiting_human`), you get a
268
649
  notification, `loom route --status` and the board show the question; you answer in
269
650
  the shared thread (`loom send "…"`) and the route **resumes by itself**;
270
- - an agent errors or a step times out (45 min default) → the route fails loudly;
651
+ - an agent errors or a step times out (45 min default) → the route fails loudly —
652
+ unless the step carries an [`onFail`](#routes-that-loop-and-steps-that-skip), which
653
+ sends it back to an earlier step instead;
654
+ - a step carrying a [condition](#routes-that-loop-and-steps-that-skip) the previous turn
655
+ didn't meet (`reviewer?lines>200`) is **skipped**, with the numbers that decided it;
271
656
  - **you always outrank the route**: any manual `handoff`/`interrupt` cancels it, and
272
657
  `loom route --abort` stops it and interrupts the in-flight turn.
273
658
 
@@ -284,13 +669,15 @@ step can carry its own focus:
284
669
  "api-only": [
285
670
  { "step": "planner", "instruction": "design the endpoint contract only" },
286
671
  { "step": "executor", "instruction": "only touch src/api — no schema changes" },
287
- "reviewer"
672
+ { "step": "reviewer", "when": "lines>200", "onFail": "executor" }
288
673
  ]
289
674
  }
290
675
  ```
291
676
 
292
677
  Per-step instructions are appended to the role guidance for exactly that step — the
293
- next hop never sees them. `loom init` seeds a `ship` route automatically when it
678
+ next hop never sees them. `when` runs a step only if the previous turn met a condition
679
+ Loom measured, and `onFail` sends a failing step back to an earlier one; both are
680
+ [explained here](#routes-that-loop-and-steps-that-skip). `loom init` seeds a `ship` route automatically when it
294
681
  detects at least two roles.
295
682
 
296
683
  ## Commands
@@ -305,29 +692,94 @@ detects at least two roles.
305
692
  | `loom route <spec> "<task>"` | Run a pipeline (name, or `a,b,c` ids/roles); `--status` / `--abort` / `--detach` |
306
693
  | `loom routes` | List named pipelines defined for this project |
307
694
  | `loom interrupt` | Stop the current holder's turn (cancels an active route) |
695
+ | `loom update [--check] [--yes]` | Is there a newer Loom — and install it, in the commands it prints first |
696
+ | `loom servers [start\|stop\|restart\|logs] [name]` | The project's dev servers: real state, start/stop, and their output |
697
+ | `loom queue` | What's lined up behind the running turn — position, who takes it, why it's waiting |
698
+ | `loom queue add "<text>" [--to <agent\|orchestrate\|auto>] [--plan]` | Line a prompt up; it goes when nothing is in its way |
699
+ | `loom queue edit / to / move / rm / clear` | Rewrite one, send it to someone else, reorder it, drop it, empty the queue |
700
+ | `loom queue pause` / `loom queue resume` | Hold the queue where it is · let it run again |
701
+ | `loom queue at <when> "<text>"` / `after landed:<run>\|green:<run>` | Queue a prompt for a time, or for a goal landing / going green |
702
+ | `loom queue save <name>` / `run <name>` / `recipes` | Keep a queue as a recipe and replay it on any project |
703
+ | `loom digest [--since <hours>]` | What happened in this project while you were away |
308
704
  | `loom decision <text>` | Record a decision into shared memory |
309
705
  | `loom memory [import]` | The unified brain — one memory across every connected ADE |
310
706
  | `loom log [-f]` | Show (or follow) the project event log |
311
707
  | `loom costs` | Project spend: total + per-agent turns, $ and agent time |
312
- | `loom agents` / `loom projects` / `loom status` | Who's who, board of projects, daemon health |
708
+ | `loom agents` / `loom projects` / `loom status` | Agent roster, project board, daemon health |
709
+ | `loom providers` | Where model agents send their turns, and whether each has a key |
710
+ | `loom providers:set <id> [--key --base-url --header]` | Point Loom at a provider — the key goes to `~/.loom/providers.json` (0600) or the env, never a project |
711
+ | `loom providers:rm <id>` | Forget a provider's key and settings |
712
+ | `loom models [agentId] [--provider --free --refresh]` | With an agent: the models it reports. Without: what every configured provider has right now |
713
+ | `loom ask "<text>" [--models a,b,c \| --free --limit n]` | One prompt to several models at once, a thread each, side by side |
313
714
  | `loom up [--tailnet] [--restart]` / `loom down` / `loom daemon` | Daemon lifecycle (`--tailnet` binds to your Tailscale IP) |
314
715
  | `loom pair` | QR deep link that pairs a phone (single-use token) |
315
716
  | `loom clients [--revoke <id>] [--ping]` | Paired devices: list, revoke, or send a test push |
316
- | `loom doctor` | Diagnose env, daemon, binding, and project config — with fixes |
717
+ | `loom doctor [--json] [--fix]` | Diagnose env, daemon, binding, and project config — `--fix` repairs what has exactly one safe repair |
718
+ | `loom orchestrate "<goal>"` | One orchestrator plans, many workers build in parallel worktrees (`-o`, `-w`, `-p`, `--no-watch`) |
719
+ | `loom orchestrate --plan "<goal>"` | Plan mode: the plan and each task's spec land as markdown under `plans/<run>/` for any agent to pick up |
720
+ | `loom orchestra [run] [--watch]` | Orchestra runs in this project, or one run's task graph live |
721
+ | `loom orchestra:reply / :apply / :abort / :cleanup <run>` | Answer or steer the orchestrator · merge the run into your branch (a race: `--task <entrant>`) · stop it · remove its worktrees |
722
+ | `loom orchestrate --race "<goal>" -w a,b` | Race: each agent takes the whole goal in its own worktree, and you keep the one you like |
723
+ | `loom orchestra:resume <run>` | Carry on a run Loom stopped by restarting |
724
+ | `loom stats` | Today across every project: turns, how many failed, the time and the spend |
725
+ | `loom export [--chat <id\|title>] [--out file]` | A chat as Markdown: prompts, replies, tool summaries and turn footers |
726
+ | `loom backup [--out file]` | Everything Loom knows in one archive: `~/.loom` and every project's `.loom` (logs copied as consistent snapshots) |
727
+ | `loom completion zsh\|bash` | Shell completion: `loom completion zsh >> ~/.zshrc` |
728
+ | `loom hub [--host --port --secret]` | Run a self-hosted Team Hub for your team |
729
+ | `loom invite` / `loom join <link>` | One link: a teammate joins the team and gets this repo, their agents and your crews |
730
+ | `loom crew [status\|create\|goal\|say\|approve\|stop\|resume\|apply\|diff\|remove]` | Crews: agents with roles (lead, builders, reviewer, tester) working one goal |
731
+ | `loom team [status\|signin\|create\|invite\|join\|share\|unshare\|brain\|landing\|doctor\|adopt\|deploys\|release-notes\|webhook\|remove\|leave]` | Loom Teams: see teammates' live agents and goals; the shared team brain; landing goal PRs; GitHub webhooks; membership and key rotation |
732
+ | `loom land [runId]` | Land a goal's PR: fresh main in, fast tests, push, merge when GitHub's rules pass |
733
+ | `loom runner [status\|pair\|join\|start\|stop\|token\|doctor\|install\|revoke\|goal\|move\|back\|jobs]` | Runners: your always-on Loom that takes goals while you're away |
734
+ | `loom cloud [status\|enable\|disable\|rotate]` | Loom Cloud relay: reach this daemon from any network, end-to-end encrypted |
735
+ | `loom spawn "<task>"` | Fan a subtask out to a child agent — the parent keeps the baton |
736
+ | `loom subtasks` | Subtasks running right now |
737
+ | `loom agents:add <kind> [--as name] [--role r]` | Add an agent session — repeat for a second session of the same kind, same brain |
738
+ | `loom agents:add model --model <id> [--tools --write --run "npm test"]` | An agent that *is* a model: no CLI, and what it may read, write or run |
739
+ | `loom agents:rm <id>` / `loom agents:available` | Remove a session · what this machine can drive and how many are here |
740
+ | `loom watch [--all] [--json]` | Tail a project's events live — turns, handoffs, memory, subtasks |
741
+ | `loom retry <agentId>` | Re-run the last failed turn on a different agent, failure attached |
742
+ | `loom stale` / `loom reap <agentId>` | Sessions that look hung · respawn one fresh (baton released) |
743
+ | `loom budgets` / `loom budget <id> [usd]` | Daily USD caps: measured spend vs cap · set or clear one |
744
+ | `loom brain:search "<query>" [--explain]` | Retrieval exactly as a briefing sees it — the score, and the arithmetic behind it |
745
+ | `loom brain:forget <memoryId>` | Drop one unit from the project's memory |
746
+ | `loom brain:export [file]` / `brain:import <file>` | The project's memory as a portable file — import dedupes |
747
+ | `loom brain:conflicts` | Units that likely contradict each other, with the signal that tripped each |
748
+ | `loom snapshot [file]` / `loom restore <file>` | Checkpoint brain+board+config · bring one back (brain merges) |
749
+ | `loom rewind [checkpoint]` | Points the working tree can go back to · put the files back to one |
750
+ | `loom routes:save <name> <steps...>` / `routes:rm` | Define a named pipeline, validated against the roster · remove one |
751
+ | `loom task "<title>"` / `loom tasks` | Put a card on the board (`--agent`, `--blocked-by`) · list yours |
752
+ | `loom specs` / `loom specs:run <file>` | The project's Playwright specs · run one through the daemon |
753
+ | `loom mcp:health` | Each MCP server's live state from the background poll |
754
+ | `loom costs --series [days]` | The spend ledger day by day, per agent |
755
+ | `loom open` / `loom version` | Open this project in the web app · what's installed |
756
+ | `loom rename <name>` / `loom find <query>` | Rename the project (id never moves) · search the thread |
317
757
 
318
758
  ## Supported agents
319
759
 
320
760
  | Agent | Tier | Transport | Status |
321
761
  |---|---|---|---|
322
- | Claude Code | adapter (full-duplex) | headless CLI, `stream-json`, `--resume`, briefing via `--append-system-prompt` | ✅ verified against 2.1.83 |
323
- | Codex | adapter (full-duplex) | `codex exec --json` (JSONL), `exec resume <thread>`; found on PATH **or inside Codex.app** | ✅ verified against codex-cli 0.142.4 |
324
- | OpenCode | adapter (full-duplex) | `opencode serve` HTTP + SSE (`/prompt`, `/interrupt`, `/event`) | ✅ verified against 1.17.20 |
762
+ | Claude Code | adapter (full-duplex) | Claude Agent SDK driving the installed `claude` (stream-json control protocol), resume by session, briefing appended to Claude Code's system prompt | ✅ verified against 2.1.278 with SDK 0.3.283 (2026-09-28) |
763
+ | Codex | adapter (full-duplex) | `codex app-server` JSON-RPC, `thread/resume`; found on PATH **or inside Codex.app** | ✅ verified against codex-cli 0.153.4 (2026-09-28) |
764
+ | OpenCode | adapter (full-duplex) | `opencode serve` HTTP + SSE (`/prompt`, `/interrupt`, `/event`) | ✅ verified against 1.17.20; re-verified 1.18.31 (2026-09-18) |
325
765
  | Grok Code | adapter (full-duplex) | `grok -p --output-format json`, `-r <session>` | 🔶 verified against 0.2.54 — **answers only, no tool or edit events** (see below) |
766
+ | Antigravity | adapter (full-duplex) | `agy -p` headless, `--conversation <id>` to resume | ✅ verified against agy 1.1.6; re-verified 1.2.6 (2026-09-18) |
767
+ | Cursor | — | `cursor-agent --print` | 🔜 coming soon |
768
+ | **Model (API)** | adapter (full-duplex) | `POST /v1/chat/completions`, streamed — any OpenAI-compatible provider | ✅ no CLI to install; tools are read-only unless you allow more (see [below](#agents-that-are-models)) |
326
769
  | Echo | adapter (demo/tests) | in-process | ✅ |
327
- | Antigravity | **bridge** (driveable) | Chromium debug port — types into the real chat panel and reads the panel back | 🔶 mechanism verified; its selectors are not (see below) |
328
- | Kiro | **bridge** (driveable) | same, via the same driver | 🔶 mechanism verified; its selectors are not |
770
+ | Kiro | **bridge** (driveable) | Chromium debug port — types into the real chat panel and reads the panel back | 🔶 mechanism verified; its selectors are not (see below) |
771
+
772
+ **Context and limits.** For Claude Code and Codex, the composer shows the chosen
773
+ agent's context in use against its model's window, shows **compacting…** while the
774
+ harness compacts (with a "compacted its context · 180k → 12k tokens" line in the
775
+ thread when it's done), and calls out a usage-limit window once it passes 80%. Hover
776
+ for every window and when it resets.
329
777
 
330
- Three of those need their asterisks spelled out, because the table row is
778
+ > The Claude Agent SDK package carries a platform build of Claude Code as an
779
+ > optional dependency (about 215 MB installed on macOS arm64). Loom never runs it: it always points
780
+ > the SDK at your own `claude`. Installing with `--omit=optional` skips it.
781
+
782
+ Four of those need their asterisks spelled out, because the table row is
331
783
  shorter than the truth:
332
784
 
333
785
  **Codex reports tokens, never money.** Its `turn.completed` carries
@@ -344,15 +796,24 @@ the tree would put guesses in the event log dressed as facts. Its permission
344
796
  mode also defaults to `bypassPermissions`, because headless with no TTY to ask,
345
797
  every other mode ends the turn `Cancelled` having written nothing.
346
798
 
347
- **Antigravity and Kiro are driven, not routed.** Both are Electron apps with no
799
+ **Antigravity used to be a bridge, and isn't any more.** Loom drove the IDE's
800
+ chat panel over the DevTools protocol: it needed the app open with a debugging
801
+ port, it could only watch, and it could never hold the baton. `agy` does the
802
+ whole turn headless, so Antigravity is a full agent and the GUI stopped being a
803
+ dependency. It reports no dollar cost — the CLI hands us none — so its turns
804
+ show a model and a duration and honestly nothing else. The CDP bridge is still
805
+ in the tree and still builds, so projects that name it still open; it just isn't
806
+ offered any more.
807
+
808
+ **Kiro is driven, not routed.** It's an Electron app with no
348
809
  API; Loom connects to the debugging port, finds the chat box, types through the
349
810
  input pipeline and reads back what the panel gained — the approach
350
811
  [antigravity_phone_chat](https://github.com/krishnakanthb13/antigravity_phone_chat)
351
812
  takes, and for the same reason: never touch the provider APIs, drive the app
352
- that's already signed in. Launch them with
813
+ that's already signed in. Launch it with
353
814
  `--remote-debugging-port=9222` first.
354
815
 
355
- The driver refuses more than it accepts, on purpose. Both apps are VS Code
816
+ The driver refuses more than it accepts, on purpose. Kiro is VS Code
356
817
  family and Monaco — the editor holding your source file — is a
357
818
  `contenteditable`. Anything under `.monaco-editor` is never a candidate, a
358
819
  candidate must be labelled like a chat box, and zero-or-several matches is a
@@ -360,18 +821,159 @@ refusal that names the fix (`options.selectors.composer`). Typing a prompt into
360
821
  your code and pressing Enter is not a mistake an error message repairs.
361
822
 
362
823
  What's verified is the mechanism, against a real Chromium. What is **not**
363
- verified is either app's actual chat DOM: Antigravity shows a sign-in screen and
364
- Kiro shows no chat panel until you open one, so there was no composer to read
365
- the selectors from. Reachable and driveable are separate questions, and Loom
366
- answers both — a signed-out Antigravity replies to CDP cheerfully and reports
824
+ verified is Kiro's actual chat DOM: it shows no chat panel until you open one,
825
+ so there was no composer to read the selectors from. Reachable and driveable are
826
+ separate questions, and Loom answers both — an app with no chat panel open
827
+ replies to CDP cheerfully and reports
367
828
  `driveable: false — no chat box on screen`.
368
829
 
369
- **Adapters** implement the full contract (send / stream / injectMemory / interrupt /
370
- diff) and may hold the baton. **Bridges** only observe and receive shared-memory
830
+ **Adapters** run a turn to completion headless and may hold the baton. **Bridges** only observe and receive shared-memory
371
831
  projections — they never hold the write lock. That's a design decision, not a gap: GUI
372
832
  agents without a stable API can't be trusted with interrupt-safe writes. See
373
833
  [docs/integration-notes.md](docs/integration-notes.md) for the verified surfaces.
374
834
 
835
+ ## Agents that are models
836
+
837
+ Every other agent in Loom wraps a CLI. A `model` agent is an HTTP endpoint:
838
+
839
+ ```sh
840
+ loom providers:set openrouter --key sk-… # or $OPENROUTER_API_KEY
841
+ loom models --free # what costs nothing today
842
+ loom agents:add model --as cheap --role reviewer \
843
+ --model "google/gemma-4-31b-it:free" --tools
844
+ ```
845
+
846
+ It streams into the thread like any other agent, shows reasoning as reasoning,
847
+ reports its token counts, and holds the baton. Out of the box it is a thinker
848
+ rather than an editor — planning, reviewing, summarising, answering, routing,
849
+ which is most of what a fleet does between edits and what free quota is very
850
+ happy to pay for — and it becomes an editor only when you say so, below.
851
+
852
+ **It can read the project, if you let it.** `--tools` adds `read_file`,
853
+ `list_files` and `search` — read-only, inside the project, with `.git` and
854
+ `.loom` off limits and every path proven contained before anything opens it.
855
+ Each call shows in the thread, and the loop is bounded at eight hops.
856
+
857
+ **And write, if you say so.** With `--tools` already on, `--write` adds
858
+ `write_file` and `--run "npm test"` adds `run` with that allow-list. (Either
859
+ without `--tools` is refused — they're the writing half of it, not a
860
+ replacement for it.) Every write and every command is a card you
861
+ allow or deny first:
862
+
863
+ > **write_file** — write src/app.ts: 40 lines (replacing 12) — fix the port
864
+ > &nbsp;&nbsp;[Allow] [Deny]
865
+
866
+ It asks in `auto` as well as `ask`, which is stricter than the CLI mapping and
867
+ on purpose: a CLI in your roster is one you installed and signed into, and a
868
+ model agent is a name you picked off a provider's list an hour ago. `bypass`
869
+ is the only mode that doesn't ask. With nobody to ask, the answer is no.
870
+
871
+ The allow-list is a prefix match on whole commands and there is no shell — so
872
+ `npm test` permits `npm test --watch` and refuses `npm testify`,
873
+ `npm test; rm -rf ~`, backticks, pipes and redirection — and a tool is only
874
+ offered to the model when it can actually be used.
875
+
876
+ **Providers Loom knows by name** — and anything else that speaks
877
+ `/v1/chat/completions` works with a base URL:
878
+
879
+ | provider | base URL | free tier |
880
+ |---|---|---|
881
+ | `openrouter` | `https://openrouter.ai/api` | models whose id ends `:free` |
882
+ | `agentrouter` | `https://agentrouter.org` | all of it — but see the note below |
883
+ | `groq` | `https://api.groq.com/openai` | — |
884
+ | `openai` | `https://api.openai.com` | — |
885
+ | `ollama` | `http://127.0.0.1:11434` | local, no key |
886
+ | *anything* | `loom providers:set mine --base-url …` | whatever you tell it |
887
+
888
+ **Keys are never project config.** `.loom/config.json` names a provider;
889
+ the key lives in the environment or in `~/.loom/providers.json` (mode 0600),
890
+ and nothing — no route, no log, no listing — hands it back. You get the last
891
+ four characters, which is enough to tell two keys apart.
892
+
893
+ **A fallback chain, for when the free pool is dry:**
894
+
895
+ ```json
896
+ { "id": "cheap", "kind": "model", "role": "reviewer",
897
+ "options": { "provider": "openrouter",
898
+ "model": "google/gemma-4-31b-it:free",
899
+ "fallbacks": ["qwen/qwen3.8-27b:free"],
900
+ "tools": true } }
901
+ ```
902
+
903
+ **Refusals are read, not guessed at.** A dry free pool (402) or a rate limit
904
+ (429) moves to the next model in `fallbacks` and says so once; a model name
905
+ that doesn't exist (503) stops, because falling back would hide the typo. And
906
+ a provider that rejects *Loom* rather than your key says exactly that —
907
+ AgentRouter, for one, only accepts clients it recognises, which is a thing to
908
+ ask them about rather than a key to go and regenerate.
909
+
910
+ ## Threads, and who answers in them
911
+
912
+ A thread can name its own agent. It then answers there whatever the baton is
913
+ doing elsewhere, which is what lets two threads talk to two agents at once —
914
+ one planning on a free model, one editing with Claude Code, neither waiting for
915
+ the other.
916
+
917
+ Pinning does **not** take the baton. The baton is the write lock for work that
918
+ touches the repository; a conversation doesn't need it, and taking it would
919
+ stop whatever is actually working. The main thread still follows the baton,
920
+ because that's what makes it the main thread.
921
+
922
+ A thread may pin a **model** as well, for agents that can change model per turn
923
+ (the `model` kind). Pinning one to an agent whose model is baked into a spawned
924
+ process is refused at the point of pinning — a setting that silently does
925
+ nothing is worse than an error.
926
+
927
+ ## Asking several models at once
928
+
929
+ ```sh
930
+ loom ask --free --limit 3 "is this migration reversible?"
931
+ loom ask --models "openrouter/qwen/qwen3.8-27b:free,ollama/llama3" "review this approach"
932
+ ```
933
+
934
+ In the app it's **More → Ask several models…** in the composer: tick the
935
+ models (free ones are listed first), and what's in the box goes to each.
936
+
937
+ One prompt, one thread per model, all at the same time, each thread named
938
+ after the model that answered in it. With free quota this costs what asking
939
+ one model costs, and picking the good answer takes a person ten seconds.
940
+
941
+ The agents that run are transient — five asks don't leave five agents in the
942
+ roster — and one model failing leaves its error in its own thread while the
943
+ others carry on.
944
+
945
+ ## The brain, sharpened
946
+
947
+ Five retrieval behaviours worth knowing, all inspectable with `explain`:
948
+
949
+ - **Recency decay** — a unit's match decays toward a floor (7-day half-life,
950
+ never below 0.55): an equal match, fresher, wins; a strong old constraint
951
+ still beats a weak new fact. Age tips ties, it never erases history.
952
+ - **A fuzzy channel** — hashed character-trigram vectors catch typos and
953
+ morphology (`sqllite databse` finds `sqlite database`; `deploying` finds
954
+ `deployment`). Honestly **not** synonymy: that needs a model, which is the
955
+ next one.
956
+ - **A semantic channel, opt-in** (`brain.semantic`, or Settings → Brain) —
957
+ all-MiniLM-L6-v2 embeddings, so *how does login work* reaches a note about
958
+ Supabase JWKS. Measured on `test/brain-recall.test.ts`, recall@5 goes
959
+ **0.50 → 1.00** on queries that share no word with the memory they want,
960
+ **0.80 → 1.00** across word forms, and stays **1.00** on the literal ones —
961
+ it costs nothing where the lexical channels already worked. It is **not** a
962
+ dependency: the 23MB model needs a ~470MB ONNX runtime, so Loom asks you to
963
+ install it (`npm i -g @huggingface/transformers`) and works exactly as
964
+ before if you don't. Scored relative to the best hit in the query, like
965
+ BM25, because this model's absolute similarities don't mean enough to
966
+ threshold.
967
+ - **Contradiction flags** — `loom brain:conflicts` scans same-kind units for
968
+ negation pairs (`always`/`never` on one topic) and same-topic-divergent
969
+ decisions. Every flag names the signal that tripped it, so you can judge the
970
+ judge. Resolution stays human.
971
+ - **Portability** — `loom brain:export` writes what the project knows to a
972
+ plain file; `brain:import` brings one in, deduped by hash, so the same file
973
+ twice reports *known* rather than minting duplicates. `loom snapshot` /
974
+ `restore` widen that to brain+board+config — restore **merges** the brain,
975
+ because what the project learned since the snapshot should survive it.
976
+
375
977
  ## How memory actually reaches a model
376
978
 
377
979
  Worth being precise about, because this is the whole premise and it has a soft edge.
@@ -384,15 +986,15 @@ Getting it into the model's context is a different problem, and it depends on th
384
986
 
385
987
  | Agent | How the brain arrives | Strength |
386
988
  |---|---|---|
387
- | Claude Code | briefing via `--append-system-prompt` — the model *always* sees a summary (recent decisions + messages) plus a pointer to `.loom/memory/claude-code.md`, which it can Read | **strong** — the summary is guaranteed; the full file is one tool-call away |
989
+ | Claude Code | briefing appended to Claude Code's system prompt — the model *always* sees a summary (recent decisions + messages) plus a pointer to `.loom/memory/claude-code.md`, which it can Read | **strong** — the summary is guaranteed; the full file is one tool-call away |
388
990
  | Grok Code | the briefing rides in `--rules`, Grok's real system-prompt channel, so `-p` stays your clean prompt | **strong** — `--rules` is a genuine system channel, not text in the turn |
389
- | Codex | no `--append-system-prompt` on `codex exec`, so the briefing rides in front of your prompt — **framed** as an unmissable `LOOM SESSION MEMORY — authoritative, read first` block | **reliable** — one prompt either way, but framed so it can't be mistaken for chatter |
991
+ | Codex | no per-turn system channel in `codex app-server`, so the briefing rides in front of your prompt — **framed** as an unmissable `LOOM SESSION MEMORY — authoritative, read first` block | **reliable** — one prompt either way, but framed so it can't be mistaken for chatter |
390
992
  | OpenCode | no per-prompt system field on `/prompt`, so the same **framed** block is prepended to your prompt | **reliable** — delivered as an authoritative block, not loose text |
391
993
  | Antigravity, Kiro | nothing tells them the file exists — Loom types into their chat box, which is not a system prompt | **none** — a human has to open it |
392
994
 
393
995
  So: the **summary always lands**; the **full brain is an invitation**. An agent that
394
996
  ignores the pointer works from the summary alone. If you need something remembered for
395
- certain, put it in a decision (`loom decide`) — decisions ride in the briefing itself.
997
+ certain, put it in a decision (`loom decision`) — decisions ride in the briefing itself.
396
998
  There's an opt-in eval (`LOOM_TEST_REAL=1`) that checks a real model actually *uses* an
397
999
  injected brief, and declines rather than invents when the brief is silent.
398
1000
 
@@ -408,9 +1010,10 @@ convention, a fact, a failure — reconciled on write (add / update / forget, ne
408
1010
  growing blob), the approach [mem0](https://github.com/mem0ai/mem0) pioneered, adapted to
409
1011
  Loom's event log. Every unit's evidence is verified against the turn before it's kept, so
410
1012
  the brain doesn't remember things that were never said. Retrieval is hybrid too — exact
411
- entity matches (file paths, symbols, error codes) unioned with BM25 over the text, no
412
- embedding model to ship — with failures and constraints biased to the top of the brief,
413
- because getting burned twice is worse than missing a detail.
1013
+ entity matches (file paths, symbols, error codes) unioned with BM25 over the text and a
1014
+ trigram channel for typos and word forms, and [a real embedding
1015
+ channel](#the-brain-sharpened) if you turn one on — with failures and constraints biased
1016
+ to the top of the brief, because getting burned twice is worse than missing a detail.
414
1017
 
415
1018
  The brain is the **project's**, not each agent's: a fact one agent learns is scoped to the
416
1019
  chat, not walled off to whoever happened to learn it, so it reaches whichever agent takes
@@ -447,7 +1050,7 @@ the extractor off per project in Settings.
447
1050
  - **Unified memory ("multiple memory in one")** — each connected ADE keeps its own
448
1051
  native memory (`CLAUDE.md`, `AGENTS.md`, …). Loom imports them all into one brain
449
1052
  (`memory_import` events, content-hash deduped), merges them with the project's
450
- decisions and shared thread, and projects the union into whoever holds the baton.
1053
+ decisions and shared thread, and projects the union into whoever is taking the turn.
451
1054
  Connect a new agent → its knowledge joins the brain, and everything the others learned
452
1055
  flows into it. `loom memory` shows the merged brain; it refreshes on open and on every
453
1056
  handoff. This is the seam an isolation-first tool (separate worktrees) can't own.
@@ -502,8 +1105,762 @@ phone buzzes when an agent **needs input**, when a **route completes or fails**,
502
1105
  when a solo turn finishes — route hops are deliberately silent (a 5-step pipeline
503
1106
  buzzes once, not five times). Verify with `loom clients --ping`.
504
1107
 
1108
+ ## Permissions: bypass, auto, or always ask
1109
+
1110
+ Every agent CLI has its own idea of "may I?". Loom gives you one dropdown with
1111
+ three words and maps them onto each CLI. Every cell below was checked against the
1112
+ real CLI with `node scripts/verify-permissions.mjs`, not read off `--help`.
1113
+
1114
+ | | Bypass | Auto | Always ask |
1115
+ |---|---|---|---|
1116
+ | Claude Code | `bypassPermissions` | `acceptEdits` *(default)* | **real approvals**: each tool call waits for your Allow / Deny in Loom, on desktop or phone |
1117
+ | Codex | no sandbox | `workspace-write` *(default)* | **real approvals**: a `read-only` sandbox, and each command or edit waits for your Allow / Deny in Loom |
1118
+ | Antigravity | `--dangerously-skip-permissions` *(default)* | ✗ unavailable: agy 1.2.6 headless writes into its own scratch folder, not your project | `--mode plan` |
1119
+ | Grok | `bypassPermissions` *(default)* | `auto` | `plan` |
1120
+ | OpenCode | allow-all | its defaults *(default)* | ✗ unavailable: opencode 1.18.31 ignores read-only settings on its headless API |
1121
+
1122
+ - "Unavailable" cells are shown disabled with the reason, and the API refuses them.
1123
+ - **Always ask** is answered inside the daemon: Claude Code's permission prompts
1124
+ arrive through the Agent SDK's `canUseTool`, and Codex's through app-server
1125
+ approval requests. The request appears in the thread as an approval card; nothing
1126
+ happens until you answer, and a card closes by itself if the turn ends first.
1127
+ - `node scripts/verify-approvals.mjs` proves it against the real CLI: the file does
1128
+ not exist until you press Allow.
1129
+
1130
+ ## Plan mode
1131
+
1132
+ Turn on **Plan** in the composer and agents write plans, not code.
1133
+
1134
+ - **In a chat:** the agent investigates, then writes
1135
+ `plans/<date>-<slug>.md` (Goal, Context, Approach, self-contained Tasks, Risks,
1136
+ Verification) and stops.
1137
+ - **In an orchestra** (`loom orchestrate --plan "<goal>"`):
1138
+ - the orchestrator's plan becomes `plans/<run>/PLAN.md` plus one spec file per
1139
+ task, committed to the run's branch *before* any worker starts;
1140
+ - each worker is pointed at its own spec;
1141
+ - when the run ends, statuses and results are written back.
1142
+
1143
+ Every file has front matter (`id`, `agent`, `depends_on`, `status`) and is written
1144
+ so another agent, or a teammate, can pick up any task with no other context.
1145
+
1146
+ ## Git delivery: commit, push, or open a PR
1147
+
1148
+ One per-project setting decides what happens to finished work. It's the toggle
1149
+ next to GitHub in the status bar, or `git.delivery` in `.loom/config.json`.
1150
+
1151
+ | | Chat turns | A finished orchestra |
1152
+ |---|---|---|
1153
+ | **No commit** *(default)* | left uncommitted | waits on its branch until you Apply |
1154
+ | **Commit only** | committed, agent as co-author | merged into your branch |
1155
+ | **Commit & push** | committed and pushed | merged and pushed |
1156
+ | **Commit & open PR** | committed | its branch pushed, and a PR opened with `gh`: summary, task table, PLAN.md |
1157
+
1158
+ A failed push never un-finishes a run. The error is shown, and **Retry delivery**
1159
+ is one click.
1160
+
1161
+ ## Rewind: put the work back the way it was
1162
+
1163
+ An agent turn can touch forty files, and until now the honest answer to *undo
1164
+ that* was "read the diff and retype it". So **Loom writes down what the files
1165
+ were before every turn**, and the turn's Update card offers **Rewind**.
1166
+
1167
+ It is a working tree, not a history. Rewinding restores what changed, removes
1168
+ what was created, brings back what was deleted — and does not move HEAD, does
1169
+ not touch your commits, and does not edit the thread. What happened still
1170
+ happened; the files just don't have to live with it.
1171
+
1172
+ **Three things it will never touch.** Checkpoints are taken with `git add -A`,
1173
+ so `.gitignore` applies at capture: your `node_modules`, your build output and
1174
+ your `.env` are never read, never stored and never restored. `.loom/` is
1175
+ ignored too, which is why a rewind cannot eat the event log.
1176
+
1177
+ **It is itself rewindable.** A rewind saves what it is about to replace before
1178
+ it replaces it, and hands you that checkpoint back — in the thread as *Undo the
1179
+ rewind*, and on the command line as the id to pass next. A one-way undo is a
1180
+ trap, and the one time it matters is the time someone rewinds past work they
1181
+ meant to keep.
1182
+
1183
+ **It refuses while an agent is mid-turn**, naming who. Replacing the tree under
1184
+ a running agent gives it a working directory that contradicts everything it has
1185
+ read, and the damage lands in whatever it writes next.
1186
+
1187
+ ```sh
1188
+ loom rewind # the points you can go back to
1189
+ loom rewind cm8x2k1p # go back to one (asks first)
1190
+ ```
1191
+
1192
+ Checkpoints live on hidden refs under `refs/loom/checkpoints/`, so no branch
1193
+ listing, no stash and no `git log` ever shows them. The newest 60 are kept.
1194
+
1195
+ ## Review Triage: what to read first
1196
+
1197
+ An agent hands back twelve changed files and you have five minutes. Most are
1198
+ fine to skim; one deleted a test, one touched the auth middleware, one added a
1199
+ dependency. Every turn card now ranks its files by what a reviewer would want
1200
+ to see first and puts the top ones up front, each with its reason:
1201
+
1202
+ > **Review first** `session.ts` touches auth or security code · `ci.yml` changes CI, build or deploy
1203
+
1204
+ A chip opens the diff on that file, and its header carries the reason. What it
1205
+ flags, most serious first:
1206
+
1207
+ - an added secret key or hard-coded credential
1208
+ - a deleted test file, a skipped or focused test, or test assertions removed
1209
+ - a database migration or schema change
1210
+ - auth, session, token, permission or other security code
1211
+ - CI, build and deploy files; new or changed dependencies; project config
1212
+ - added risky calls: `eval`, shell commands, `rm -rf` or `git push --force`,
1213
+ raw HTML injection, TLS checks turned off, a silenced type or lint check
1214
+ - a large source change in a turn that touched no test
1215
+
1216
+ Lockfiles and generated files sink to the bottom. Triage reads only what the
1217
+ diff shows and never guesses why: it says "a test was removed", not "the agent
1218
+ cheated". The phone's turn card lists the same files.
1219
+
1220
+ ## Opening a PR from a card
1221
+
1222
+ With `git.branchPerTask` on, dragging a card to **Working** cuts its branch.
1223
+ When it reaches **Review**, the card offers **Open PR** — which first shows
1224
+ what would be pushed: the branch, the commits `main` doesn't have, the files
1225
+ they touch, and the exact `git push` and `gh pr create` it will run. Asking
1226
+ pushes nothing. Publishing happens on the button and nowhere else.
1227
+
1228
+ ## While you were away
1229
+
1230
+ Come back to a project you haven't looked at in a while and Loom says what
1231
+ happened: goals started and finished, what landed, what failed, what a server
1232
+ did, and what is waiting on you — newest first, each line clicking through to
1233
+ the moment it came from. It says what happened and never guesses at why; a
1234
+ line without an event behind it doesn't get written.
1235
+
1236
+ The mark is per device, because "when did you last look" is a fact about this
1237
+ window. From the terminal: `loom digest --since 8`.
1238
+
1239
+ ## Night Shift: run the list while you sleep
1240
+
1241
+ Line up work before bed and wake up to a report you can trust. Each task runs
1242
+ in its own chat, one after another, through the queue. While the shift runs:
1243
+
1244
+ - **Proof** checks every "done" against what ran and a clean re-run of the tests.
1245
+ - **Loop watch** nudges an agent going in circles (on by default during a shift).
1246
+ - **Quota Relay** hands the work on if an agent runs out.
1247
+ - **A question doesn't hold up the line.** It stays open in its chat, and the
1248
+ next task goes.
1249
+
1250
+ When the last task settles, one report card lands in the thread and on your
1251
+ phone: each task marked **Verified**, **Done**, **Didn't hold**, **Failed**,
1252
+ **Needs you** or **Not started**, with what decided it, the files it changed,
1253
+ and what it cost. Click a task to open its chat.
1254
+
1255
+ ```bash
1256
+ loom shift "fix the flaky date test" "add a --json flag to loom costs" --to codex --until 7:00 --max 5
1257
+ loom shift # how it's going, or the last report
1258
+ loom shift --stop # the running task finishes; nothing new starts
1259
+ ```
1260
+
1261
+ In the desktop app, type the tasks in the composer, one per line, and choose
1262
+ **⋯ → Night shift…** to pick the agent, a stop-by time and a budget. On the
1263
+ phone, type them and tap 🌙. `--until` and `--max` stop *new* tasks from
1264
+ starting; the one running always finishes. A shift survives a daemon restart.
1265
+ A task the restart cut off is reported as failed, and the rest carry on.
1266
+
1267
+ ## Scorecards: which agent can you trust with this?
1268
+
1269
+ "Finished without an error" isn't the same as "did what it said". Proof, the
1270
+ loop watch and the shield record, per agent, whether its work held up, and
1271
+ the Observatory's leaderboard (and the phone's) has a **Held up** column:
1272
+
1273
+ - how many of its checked "done"s were verified, out of all it claimed (for
1274
+ example `9/10`, green above 90%)
1275
+ - the times the loop watch caught it going in circles (↻) and the shield
1276
+ caught a command that can't be taken back (⛔)
1277
+ - **cost per verified result**: its spend over the "done"s that held, which
1278
+ is what a result you can trust costs from that agent
1279
+
1280
+ ```bash
1281
+ loom scorecard # every agent's record in this project
1282
+ loom scorecard --days 7
1283
+ ```
1284
+
1285
+ The same numbers are on every agent in `/api/projects/:id/insights/turns` as
1286
+ `trust`.
1287
+
1288
+ ## Replay: a run you can send anyone
1289
+
1290
+ Export any chat as one self-contained HTML page:
1291
+ - what you asked and what the agents said;
1292
+ - every command they ran, with its output folded away;
1293
+ - every diff, with Review Triage's "read first";
1294
+ - what Loom checked along the way: Proof's verdicts, the loop watch, the
1295
+ shield, a Night Shift's report.
1296
+
1297
+ Across the top is a one-line summary: who worked, how many turns, how long,
1298
+ files changed, checks verified, cost. Send it to a teammate, attach it to a
1299
+ PR, or post it. It opens in any browser, offline, with nothing to install.
1300
+
1301
+ ```bash
1302
+ loom replay # the main chat → <project>-main.replay.html
1303
+ loom replay --chat "night" --open
1304
+ ```
1305
+
1306
+ In the desktop app it's **⋯ → Export as replay**, beside Export as
1307
+ Markdown. The page runs no scripts and loads nothing: every value is
1308
+ escaped, and a Content-Security-Policy forbids the rest. It does contain the
1309
+ code and output from the chat, so share it the way you'd share the repo.
1310
+
1311
+ ## Blast-Radius Shield: what can't be taken back
1312
+
1313
+ Rewind puts the files back. It can't un-push a force-push, restore a dropped
1314
+ table, or un-publish a package. The shield watches every command an agent
1315
+ runs, and when one can't be taken back it says so at once, with a red card
1316
+ in its chat and a push to your phone, naming the command and why it matters:
1317
+
1318
+ - **git:** force-push (not `--force-with-lease`), `reset --hard`,
1319
+ `clean -fd`, discarding every change, `branch -D`, history rewrites
1320
+ - **files:** a recursive delete outside the project (`/`, your home, `..`,
1321
+ an absolute path elsewhere). Inside the project is fine.
1322
+ - **data:** `DROP TABLE`, `TRUNCATE`, a `DELETE` with no `WHERE`, database resets
1323
+ - **infrastructure and publishing:** `terraform destroy`, `kubectl delete`,
1324
+ `npm publish`, `docker system prune -a`
1325
+ - **the machine:** `sudo`, piping a download into a shell, `chmod 777`,
1326
+ writing to a disk device
1327
+
1328
+ Most CLIs report a command after it has run, so the shield can't veto that
1329
+ first command. What it can do is stop the turn before it does more:
1330
+
1331
+ ```json
1332
+ { "shield": { "auto": "stop" } }
1333
+ ```
1334
+
1335
+ `"notify"` (the default) tells you and offers **Stop the turn**. `"stop"`
1336
+ stops it immediately, and is the default during a Night Shift, when nobody
1337
+ is watching. `"off"` turns the shield off.
1338
+
1339
+ ## Loop watch: an agent going in circles
1340
+
1341
+ The expensive failure isn't the error. It's the agent that hits the same red
1342
+ test eight times while you're away, burning tokens on fixes that don't work.
1343
+ Loom counts what each agent runs within a turn, and when the same command has
1344
+ failed three times (or the exact same call has been made five times) it says
1345
+ so: a card in the thread with the command, its last output, and three ways
1346
+ out, plus a push to your phone under Questions.
1347
+
1348
+ - **Stop**: end the turn.
1349
+ - **Nudge it**: stop the turn and tell the agent to say what's actually
1350
+ failing and try a different approach.
1351
+ - **Hand it off**: give the original request to another agent, told what the
1352
+ first one kept getting wrong. The changes stay in the working tree.
1353
+
1354
+ ```json
1355
+ { "watchdog": { "auto": "nudge" } }
1356
+ ```
1357
+
1358
+ `"notify"` (the default) only tells you. `"nudge"` also nudges on its own,
1359
+ once; if the agent is still going in circles after that, it's yours to decide.
1360
+ `"off"` turns the watch off. A person speaking in the chat starts every count
1361
+ over.
1362
+
1363
+ ## Fleet: what every agent is doing
1364
+
1365
+ The **Fleet** tab (and the phone's Fleet screen) shows every agent in every open
1366
+ project, updated live:
1367
+ - its thread;
1368
+ - whether it's mid-turn, and for how long;
1369
+ - its permission mode;
1370
+ - the last thing it did: the command it ran, the file it edited, or what it said.
1371
+
1372
+ Orchestra tasks are listed too, each with a link to its thread.
1373
+ `GET /api/activity` serves the same data.
1374
+
1375
+ ## Updating
1376
+
1377
+ Loom tells you when a newer version is published — a quiet pill in the status
1378
+ bar, never a banner — and **Settings → Updates** has the button.
1379
+
1380
+ It says what it will do before it does it, because that depends on how you
1381
+ installed it: a **git checkout** pulls, installs and rebuilds; a **global npm
1382
+ install** reinstalls itself; anything else is pointed at the release rather
1383
+ than having a command guessed at and run over it. A checkout with uncommitted
1384
+ changes is refused — that tree is yours. The commands are shown in full, their
1385
+ output streams while they run, and the daemon restarts itself on the new build
1386
+ with the page reconnecting to it.
1387
+
1388
+ From the terminal: `loom update --check` to look, `loom update` to do it.
1389
+
1390
+ **The desktop app** has its own, under **Help → Check for Updates…**, because
1391
+ an installed application is not a checkout:
1392
+
1393
+ - **Windows** and the **Linux AppImage** update themselves — it asks before
1394
+ downloading, installs on quit, and restarts only when you press Restart.
1395
+ - **macOS** finds the build for your architecture, downloads it, and
1396
+ **verifies its SHA-256** against the checksums the release publishes (a file
1397
+ that doesn't match is deleted, not opened), then opens the disk image for
1398
+ you to drag across. The drag stays yours: macOS only lets an app signed by a
1399
+ certificate it already trusts replace itself, and this build is ad-hoc
1400
+ signed. Automating the *checking* is the part worth having — the checksums
1401
+ have shipped since 0.2.0 and nobody compares them by hand.
1402
+ - A **`.deb`** is left to the package manager that owns it — and so is a
1403
+ **Homebrew** install: `brew install --cask` means `brew upgrade --cask`, and
1404
+ Loom says so rather than downloading over a copy brew is tracking. It also
1405
+ offers to **run that command in Loom's own terminal**, the one in the dock,
1406
+ so you watch the upgrade happen instead of it happening inside the app:
1407
+
1408
+ > Loom Desktop 0.2.4 was installed with Homebrew
1409
+ > &nbsp;&nbsp;`brew upgrade --cask loom-desktop`
1410
+ > &nbsp;&nbsp;[Run it in Loom] [Copy command] [Close]
1411
+
1412
+ It replaces the app in place, so restart Loom when it finishes. An upgrade
1413
+ that happens invisibly inside an app is an upgrade nobody can check.
1414
+
1415
+ **On a Mac, Homebrew is the easy way in:**
1416
+
1417
+ ```sh
1418
+ brew tap nickthelegend/loom https://github.com/nickthelegend/loom
1419
+ brew install --cask loom-desktop
1420
+ brew upgrade --cask loom-desktop
1421
+ ```
1422
+
1423
+ The cask lives in this repo because the official tap requires every cask to be
1424
+ signed and notarized by Apple, and Loom's macOS build is ad-hoc signed. **There
1425
+ is no free Developer ID for open-source projects** — Apple's fee waiver covers
1426
+ nonprofits, schools and government entities, and a free Apple ID cannot sign
1427
+ for distribution at all — so the honest options are to buy one, to be one of
1428
+ those entities, or to ship unsigned and say so.
1429
+
1430
+ Installing with Homebrew does **not** get past Gatekeeper: brew applies the
1431
+ quarantine attribute to cask downloads deliberately, and removed the flag that
1432
+ used to skip it. The first launch asks, exactly as it does for a dmg you
1433
+ downloaded yourself — right-click, Open, Open, once. Some taps strip the
1434
+ attribute in a postflight; this one doesn't, because quarantine is the
1435
+ protection that exists *because* the app is unsigned. What the cask buys is a
1436
+ one-line install and a real update path owned by a package manager you already
1437
+ trust.
1438
+
1439
+ ## More than one goal, when they can't collide
1440
+
1441
+ One orchestra goal at a time is the default, and for good reason: two goals
1442
+ editing the same files is a merge nobody can explain. Set
1443
+ `maxConcurrentGoals` in `.loom/config.json` and Loom will start a second one —
1444
+ but only when the paths can't collide. A goal's scope is the `touches` its
1445
+ plan declares; a goal that hasn't said what it touches counts as touching
1446
+ everything, and waits. Anything overlapping queues as it always did, with the
1447
+ overlap named: *waiting for "rework billing" — both touch src/billing/\*\* and
1448
+ src/billing/tax.ts*.
1449
+
1450
+ ## What a goal may spend
1451
+
1452
+ `loom orchestrate --max-usd 2 "…"` stops that goal at $2: running work
1453
+ finishes its turn, nothing new starts, and the goal goes to **needs you** with
1454
+ what it spent. Replying allows one more budget's worth. A project default
1455
+ lives in `.loom/config.json` as `budgets.perGoalUsd`, and there's a word in the
1456
+ thread at 80% — hearing about a budget for the first time when the goal halts
1457
+ is a bad way to learn it exists.
1458
+
1459
+ ## Before you send: the forecast
1460
+
1461
+ The composer shows what a turn on the chosen agent usually costs and takes:
1462
+ `~$0.12 · 1m 40s`. The numbers come from that agent's own recent turns in
1463
+ this project, not from a price list. "Typical" is the median of its last 30
1464
+ turns, and the tooltip shows the 80th percentile, so one marathon turn doesn't
1465
+ skew it. An agent on a subscription reports no spend, so it gets a time-only
1466
+ forecast. If the agent has a daily budget, the chip turns amber when one more
1467
+ long turn could cross it. The same numbers are on every agent in
1468
+ `/api/projects/:id` as `forecast`.
1469
+
1470
+ ## The prompt queue — line up the next ones
1471
+
1472
+ Type while an agent is mid-turn, or while a goal is still running, and the
1473
+ prompt waits above the composer instead of being refused or lost. It stays
1474
+ yours until it's sent:
1475
+
1476
+ - **Queue as many as you like.** They run strictly one at a time, in order.
1477
+ - **Send each one where you want it:** one agent by name, the **orchestrator**
1478
+ (a whole new goal, started when the running one finishes), or **Auto** to let
1479
+ the router pick. Change a waiting prompt's target from the list on the item.
1480
+ - **Edit, reorder, drop.** Click a queued prompt to edit it (⌘⏎ saves, Esc
1481
+ cancels), move it up or down, or remove it — all before it runs.
1482
+ - **Pause and resume** the whole queue. **Stop** pauses it too: stopping a turn
1483
+ never silently discards what you lined up behind it.
1484
+ - **A question holds the queue.** If the agent stops to ask you something, the
1485
+ next queued prompt would answer a question you never read — so the queue
1486
+ waits and says who asked. Your reply goes out at once and lifts the hold; a
1487
+ pause you set yourself stays until you resume it.
1488
+ - A prompt enters the conversation when it is actually sent, not when it's
1489
+ queued, so the thread stays an honest record of what the agent was asked.
1490
+ - **Reorder by dragging** (the arrows stay, for anyone who can't drag).
1491
+ - **Queue for later.** A prompt can wait for a time, for a goal to *land*
1492
+ (not merely finish), for that goal's checks to go green, or for the project
1493
+ to be quiet for a while: `loom queue at 03:00 "…"`,
1494
+ `loom queue after landed:<runId> "…"`, `loom queue after green:<runId> "…"`.
1495
+ The row says what it's waiting for, and one click releases it.
1496
+ - **Save a queue as a recipe** and run it anywhere: `loom queue save ship`,
1497
+ `loom queue run ship`, `loom queue recipes`. Steps remember who they go to by
1498
+ **role**, so a recipe travels between projects; anything a project can't
1499
+ resolve becomes an Auto prompt rather than a refusal.
1500
+
1501
+ It survives a daemon restart (reloaded paused, so an hour-old queue doesn't
1502
+ start itself), and lives in the project's `.loom/queue.json`. Over the API:
1503
+ `GET/POST /api/projects/:id/queue`, `PATCH|DELETE .../queue/:itemId` (text,
1504
+ target, or `to` for its position), `POST .../queue/pause`, `DELETE .../queue`.
1505
+
1506
+ ## Prompt manager
1507
+
1508
+ A clipboard manager for prompts, built into the chat bar (⌘⇧V):
1509
+ - **Saved** prompts: pinned first, then most used.
1510
+ - **Recent:** every prompt you actually sent (chats, plans, orchestra goals),
1511
+ de-duplicated and searchable.
1512
+
1513
+ Everything is stored in `~/.loom/prompts.json` and never leaves the machine.
1514
+
1515
+ ## Teams — see each other's agents
1516
+
1517
+ Five people, each with their own agents, on one repo. **Phases 1 to 6 are built.** Phase 1:
1518
+ teammates see each other's live agents and goals, and a team feed collects goals,
1519
+ plans, PRs and CI in one place. Content is end-to-end encrypted to the team, so
1520
+ the hub can't read goal titles.
1521
+
1522
+ ```bash
1523
+ loom hub --host 0.0.0.0 --secret <join-secret> # one teammate runs a hub (or use hosted Loom Teams)
1524
+ loom team signin http://<hub>:7430 --secret <join-secret>
1525
+ loom team create "Acme"
1526
+ loom team invite # one-time link; it carries the team key, so send it like a password
1527
+ loom team join '<link>' # a teammate
1528
+ loom team share # in a project: its repo now shows up for the team
1529
+ loom team # who's doing what, right now
1530
+ ```
1531
+
1532
+ - **What teammates see:** agents, state, branch, the files each task declared
1533
+ it will touch, and goal and task titles. They never see your prompts or your
1534
+ agents' transcripts.
1535
+ - **Sharing is opt-in per project.** A clone whose `origin` matches a team repo
1536
+ is shared automatically, and `loom team unshare` opts it out.
1537
+ - **Removing a member rotates the team key.** The new key is sealed to every
1538
+ remaining device, so the person who left can't read anything new.
1539
+ - **Worker commits carry `Loom-Goal`, `Loom-Task`, `Loom-Agent` and `Loom-Member`
1540
+ trailers.** `git log` alone tells you which goal, task and agent wrote a line.
1541
+
1542
+ **Phase 2 (stop colliding) is built too:**
1543
+ - **Declaring touches.** On a team project, every orchestra task declares the files
1544
+ it will `touch`, and Loom takes a lease on them.
1545
+ - **Overlap needs a decision.** If a teammate's goal already holds some of those
1546
+ files, the task waits until the orchestrator decides: wait for their PR, narrow
1547
+ its scope, or proceed with a reason the team can see.
1548
+ - **Hard zones.** Areas listed in `loom.team.json` (migrations, lockfiles) take one
1549
+ goal at a time. Other tasks queue and start on their own when the area frees up.
1550
+ - **Conflict prediction.** Work in progress goes to hidden `refs/loom/wip/*` refs,
1551
+ and `git merge-tree` predicts merge conflicts before anyone opens a PR. Both
1552
+ people involved hear about it.
1553
+ - **Team policy.** `loom.team.json` on your default branch sets a permission
1554
+ ceiling, the allowed agents, protected branches and concurrency caps. Changing it
1555
+ takes a reviewed PR.
1556
+
1557
+ ```jsonc
1558
+ // loom.team.json
1559
+ { "hardZones": ["db/migrations/**", "package-lock.json"],
1560
+ "permissions": { "ceiling": "auto", "bypassRequiresPlan": true },
1561
+ "delivery": { "protected": ["main"] },
1562
+ "orchestra": { "maxParallelPerMember": 6, "teamMaxConcurrentAgents": 20 } }
1563
+ ```
1564
+
1565
+ **Phase 3 (one brain) is built too:**
1566
+ - **What one agent learns, the team's agents know.** Durable memories
1567
+ (constraints, failures, decisions, conventions, facts) from a shared project
1568
+ reach teammates, encrypted to the team. `task` notes, memories you mark
1569
+ private, and anything learned while reading outside content (web pages, issue
1570
+ and PR text) stay on your machine.
1571
+ - **Every briefed line says how sure to be.** Briefings rank team canon first,
1572
+ then memories confirmed by two or more teammates, then your own, then a single
1573
+ teammate's proposal, and label each one. When two people's agents learn the
1574
+ same thing, it becomes one memory with two confirmations.
1575
+ - **Canon lives in `AGENTS.md`, reviewed like code.** "Propose as canon" adds a
1576
+ memory to a Loom-managed section of `AGENTS.md` on one rolling `loom/canon` PR,
1577
+ and adds `@AGENTS.md` to `CLAUDE.md`. Once merged, every agent reads it, Loom or
1578
+ not. Edit or delete a line in any PR to change canon.
1579
+ - **An inbox for what needs a human:** contradictions between teammates' memories,
1580
+ near-duplicates, corrections waiting on a decision, untrusted memories, and
1581
+ memories ready for canon. Resolving keeps the loser, linked to the winner.
1582
+ - **Live team context.** Worker briefings also say which teammates hold leases on
1583
+ the same paths, which open PRs change them, which checks are failing, and where
1584
+ a conflict is predicted.
1585
+
1586
+ ```bash
1587
+ loom team brain # the team's memories and your inbox
1588
+ loom team brain promote <id> # propose as canon (opens or updates the loom/canon PR)
1589
+ loom team brain resolve <keep> <drop> "why"
1590
+ loom team brain trust <id> # an untrusted memory is fine: share it
1591
+ ```
1592
+
1593
+ **Phase 4 (land safely) is built too:**
1594
+ - **CI comes back to the owner.** Your daemon watches your goal PRs. A failing
1595
+ required check is rerun once; if it passes, it's labelled flaky and no agent
1596
+ touches it. A real failure's log goes to the goal's orchestrator, which fixes
1597
+ it and pushes to the same PR — at most 2 attempts, then your phone buzzes.
1598
+ - **A second opinion from another vendor.** An agent from a different vendor
1599
+ than the goal's authors reviews each goal PR (on open and after each fix) and
1600
+ posts a `loom/review` check that fails only on high-severity findings. It
1601
+ comments; it never approves.
1602
+ - **Land.** One click (or `loom land`) merges fresh main into the goal, runs the
1603
+ team's fast tests, pushes, and asks GitHub to merge when approvals and checks
1604
+ pass. A conflict gets one agent attempt; a conflict in a hard zone goes to you.
1605
+ - **Stacks, Adopt, budgets.** Big goals can land as a stack of PRs
1606
+ (`"delivery": {"stack": "auto"}`). When your goal is stuck and you're offline,
1607
+ a teammate can adopt it, get it green on your branch, and hand it back. Goals
1608
+ and members can have budgets, and the team view rolls up cost per member, per
1609
+ goal and per landed PR.
1610
+ - **Doctor.** `loom team doctor` checks your repo for merge-queue traps
1611
+ (workflows that don't run on `merge_group`, no required checks) and offers a
1612
+ fix PR. It never changes repo settings.
1613
+
1614
+ ```bash
1615
+ loom team landing # your goal PRs, and teammates' goals that need someone
1616
+ loom land # land the goal PR
1617
+ loom team adopt 123 # take a stuck teammate goal and make it green
1618
+ loom team doctor # merge-queue setup check (add "fix" to open the PR)
1619
+ ```
1620
+
1621
+ ```jsonc
1622
+ // loom.team.json — Phase 4 additions
1623
+ { "landing": { "fastTest": "npm test -- --changed", "timeoutMin": 10, "autoFixAttempts": 2 },
1624
+ "review": { "enabled": true, "maxRuns": 3 },
1625
+ "delivery": { "protected": ["main"], "stack": "auto" },
1626
+ "budgets": { "perGoalUsd": 15, "perMemberDailyUsd": 60 } }
1627
+ ```
1628
+
1629
+ **Hosted hub.** `loom team signin` with no URL signs in to hosted Loom Teams with
1630
+ your GitHub account; `loom team signin http://host:7430` still uses a
1631
+ self-hosted `loom hub`.
1632
+
1633
+ **Phase 5 (runners) is built too:**
1634
+ - **Your goals keep going when your laptop sleeps.** A runner is your own
1635
+ always-on Loom (a VPS, a home server, a container) paired to your account. Start
1636
+ a goal on it, or move a running goal there — from the desktop or your phone —
1637
+ and bring it back whenever you like. Its code never touches Loom's servers.
1638
+ - **Its own trust tier.** Each goal gets a fresh clone (a fresh container when
1639
+ Docker runs), agents get a scrubbed environment with only their logins and a
1640
+ fine-grained GitHub token, and `loom.team.json` sets how far they may go.
1641
+ - **CI fixes while you're away.** If your goal needs a fix and you've been offline
1642
+ 15 minutes, your runner takes it.
1643
+ - **Shared runners** can take teammates' goals when the repo's policy allows it.
1644
+ - **Deploys and release notes.** Deploy results show in the team feed, a failed
1645
+ deploy that contains your goal buzzes your phone, and
1646
+ `loom team release-notes --since v1.2.0` writes the notes.
1647
+
1648
+ ```bash
1649
+ loom runner pair # on your laptop: a one-time link
1650
+ loom runner join '<link>' # on the always-on box (then: loom runner install)
1651
+ loom runner token github_pat_… # a fine-grained token: contents + pull requests
1652
+ loom runner goal "Add rate limiting" # start a goal there
1653
+ loom runner move <runId> # move a running goal; `loom runner back <runId>` to return it
1654
+ ```
1655
+
1656
+ Or with Docker: `docker build -f Dockerfile.runner -t loom-runner .`
1657
+
1658
+ **Phase 6 (land in turn, hear it now) is built too:**
1659
+ - **A landing train when there's no merge queue.** Land queues the goal in its
1660
+ lanes and merges it on its turn: fresh main in, fast tests, push, green on that
1661
+ commit, then `gh pr merge --squash`. The next goal in the lane lands on top of
1662
+ it. A goal waiting shows **queued**; a red check on its turn hands the lane to
1663
+ the next goal, and it rejoins when green. Repos with a merge queue keep
1664
+ Phase 4's path.
1665
+ - **Lanes.** Path scopes in `loom.team.json`: goals in different lanes land side
1666
+ by side.
1667
+ - **GitHub webhooks, no App needed.** `loom team webhook --install` points a repo
1668
+ webhook at your hub (self-hosted, or the hosted hub's Edge Function). Checks,
1669
+ merges, reviews and deploys reach the team feed as they happen, signed and
1670
+ verified, and your daemon acts on your PR's CI result at once. Polling keeps
1671
+ running; nothing is posted twice.
1672
+
1673
+ ```jsonc
1674
+ // loom.team.json — Phase 6 addition
1675
+ { "landing": { "lanes": { "web": ["web/**"], "api": ["api/**", "db/**"] } } }
1676
+ ```
1677
+
1678
+ ```bash
1679
+ loom team webhook --install # a repo webhook to the team hub (prints the URL and secret)
1680
+ ```
1681
+
1682
+ **How to use it, step by step: [docs/teams.md](docs/teams.md).** The researched
1683
+ architecture (84 decisions, six phases, all built) is in
1684
+ **[docs/teams-architecture.md](docs/teams-architecture.md)**. Every doc is indexed
1685
+ in [docs/README.md](docs/README.md); the code map is [src/README.md](src/README.md).
1686
+
1687
+ ## Loom Cloud — your agents from any network
1688
+
1689
+ Tailscale is great when your phone is on your tailnet. **Loom Cloud** reaches your
1690
+ computer from anywhere (mobile data, a café, another country) with nothing to
1691
+ install on the computer's network.
1692
+
1693
+ ```bash
1694
+ loom cloud enable --url https://<ref>.supabase.co --key <anon key>
1695
+ loom pair # the QR now works from any network
1696
+ ```
1697
+
1698
+ - **End-to-end encrypted.** The daemon and the phone meet on a Supabase Realtime
1699
+ channel. Every message is XChaCha20-Poly1305 with a key that exists only in the
1700
+ pairing QR. Supabase relays ciphertext it cannot read or forge. `loom cloud rotate`
1701
+ mints a new key and locks out every phone that paired with the old one.
1702
+ - **Same auth as the LAN.** Each relayed request becomes a real call against the
1703
+ daemon, carrying the phone's own paired token. Scopes, admin-only routes and
1704
+ revocation behave identically. The local admin bootstrap is never reachable
1705
+ through the relay.
1706
+ - **Direct when it can, cloud when it must.** The phone tries the LAN or tailnet
1707
+ address first and falls back to the relay. It reconnects on its own when you
1708
+ come back to the app.
1709
+ - **Bring your own Supabase project.** Free tier is plenty. Loom never runs a
1710
+ server of its own.
1711
+
1712
+ Setup, including Google sign-in and the country-only usage stats, takes about 10 minutes:
1713
+ [docs/cloud.md](docs/cloud.md). Protocol: [`src/core/relay-protocol.ts`](src/core/relay-protocol.ts).
1714
+ Daemon side: [`src/daemon/relay.ts`](src/daemon/relay.ts).
1715
+
1716
+ ## Hardware
1717
+
1718
+ <div align="center">
1719
+ <img src="hardware/orchestrator-pad/docs/images/hero.png" alt="The LoomPad — a 3D-printed macropad with one key per agent and a voice bar" width="720">
1720
+ </div>
1721
+
1722
+ The **LoomPad** is the pad on your desk: one key per agent, a bar you hold to
1723
+ talk, and the answer spoken back through its own speaker. Press a key and it
1724
+ locks that agent in Loom — a real baton handoff, visible in the thread like any
1725
+ other. It talks to the daemon over Wi‑Fi, with USB‑HID as a fallback.
1726
+
1727
+ It is all here, MIT, in [`hardware/orchestrator-pad/`](hardware/orchestrator-pad/README.md):
1728
+ parametric CAD, printable STLs, the wiring diagram, the Arduino firmware and the
1729
+ voice backend. Nothing to buy a licence for and nothing to reverse‑engineer.
1730
+
1731
+ ### Print it
1732
+
1733
+ Six parts, all watertight, none needing supports. 0.4 mm nozzle, 0.2 mm layers,
1734
+ PETG or PLA — it prints on a bed‑slinger in an evening.
1735
+
1736
+ | Part | Download | Orientation |
1737
+ |---|---|---|
1738
+ | Tray | [`tray.stl`](hardware/orchestrator-pad/exports/tray.stl) | flat on the bed |
1739
+ | Switch deck | [`switch-deck.stl`](hardware/orchestrator-pad/exports/switch-deck.stl) | flat, sits between tray and plate |
1740
+ | Plate | [`plate.stl`](hardware/orchestrator-pad/exports/plate.stl) | top face down |
1741
+ | Keycaps (all 14) | [`caps-all.stl`](hardware/orchestrator-pad/exports/caps-all.stl) | as oriented — slow the outer walls for crisp glyphs |
1742
+ | Legends | [`legends-all.stl`](hardware/orchestrator-pad/exports/legends-all.stl) | print in the contrast colour |
1743
+ | Knob | [`knob.stl`](hardware/orchestrator-pad/exports/knob.stl) | upright |
1744
+
1745
+ Look before you print: [`orchestrator-pad-assembled.glb`](hardware/orchestrator-pad/exports/orchestrator-pad-assembled.glb)
1746
+ and [`orchestrator-pad-exploded.glb`](hardware/orchestrator-pad/exports/orchestrator-pad-exploded.glb)
1747
+ open in any GLB viewer. Sizes, triangle counts and watertightness are recorded in
1748
+ [`MANIFEST.json`](hardware/orchestrator-pad/exports/MANIFEST.json).
1749
+
1750
+ The enclosure is code, not a binary — `numpy` and `shapely`, no CSG kernel — so
1751
+ you can change the layout and regenerate every STL:
1752
+
1753
+ ```bash
1754
+ cd hardware/orchestrator-pad
1755
+ python -m venv .venv && .venv/bin/pip install numpy shapely
1756
+ cd cad && ../.venv/bin/python assembly.py
1757
+ ```
1758
+
1759
+ The agents printed on the keycaps are just a table in `cad/partlib.py`. Add your
1760
+ own and reprint the caps.
1761
+
1762
+ ### Wire it
1763
+
1764
+ <div align="center">
1765
+ <img src="hardware/orchestrator-pad/docs/images/circuit.png" alt="Wiring diagram — 4x4 key matrix, INMP441 microphone and MAX98357A amplifier on an ESP32-S3" width="920">
1766
+ </div>
1767
+
1768
+ An **ESP32‑S3‑DevKitC‑1** (N16R8), a 4×4 key matrix on pull‑ups with no diodes,
1769
+ an **INMP441** microphone and a **MAX98357A** amplifier on I2S, and the onboard
1770
+ WS2812 as a status light in the locked agent's colour. Every pin is in
1771
+ [`config.h`](hardware/orchestrator-pad/firmware/orchestrator_pad/config.h), and
1772
+ the full table is in the
1773
+ [hardware README](hardware/orchestrator-pad/README.md#wire-it).
1774
+
1775
+ ### Flash it
1776
+
1777
+ The sketch is
1778
+ [`orchestrator_pad.ino`](hardware/orchestrator-pad/firmware/orchestrator_pad/orchestrator_pad.ino).
1779
+ You never reflash to change networks — on first boot the pad raises its own
1780
+ access point, **`LoomPad-Setup`**, and a captive page asks for your Wi‑Fi, the
1781
+ backend URL and a pad token, then keeps them in NVS. It checks the daemon's
1782
+ `/health` and speaks "connected" through the amp so you know it is up without
1783
+ opening a terminal.
1784
+
1785
+ ```
1786
+ hold 🎤 ──► the pad streams mic audio ──► the daemon does speech-to-text
1787
+ release ──► { agent: "claude-code", prompt: "..." }
1788
+ press ➤ ──► the daemon routes the job to that agent's session
1789
+ ✓ / ✕ ──► answer the agent's next approval prompt from the pad
1790
+ ```
1791
+
1792
+ The pad stays deliberately dumb: a small JSON protocol over WebSocket, and the
1793
+ daemon owns speech‑to‑text, session routing and CLI orchestration. Point it at
1794
+ your own stack by implementing one message handler.
1795
+
1796
+ ## Git policies (all opt-in)
1797
+
1798
+ Four flags in `.loom/config.json → git`, off by default because committing is
1799
+ a policy, not a mechanic (a fifth key, `delivery`, is
1800
+ [its own setting](#git-delivery-commit-push-or-open-a-pr)):
1801
+
1802
+ - **`commitPerTurn`** — each turn's changes become one commit as they land:
1803
+ subject from the prompt, `Co-Authored-By: <agent>` so blame answers "who
1804
+ wrote this" with the agent, staged by exactly the files that turn touched —
1805
+ a bystander's uncommitted mess is never swallowed.
1806
+ - **`worktreePerAgent`** — each adapter works in a sibling checkout on branch
1807
+ `agent/<id>`; parallel edits can't collide in the filesystem. Pair with
1808
+ `commitPerTurn` so the branches actually carry the work; `git merge
1809
+ agent/<id>` is always available.
1810
+ - **`mergeOnHandoff`** — with worktrees on, the baton carries the work: handing
1811
+ from A to B merges `agent/A` into B's checkout, so B starts from what A
1812
+ finished instead of from before A began. It refuses more than it does, and
1813
+ says which: uncommitted work on either side isn't merged (A's isn't on the
1814
+ branch yet; merging onto B's is a state nobody can unpick), and a merge
1815
+ already in progress is never stacked on. A conflict is left in the tree —
1816
+ those files are the thing to resolve — and is named in the incoming
1817
+ briefing, in the handoff event, and stops a route rather than prompting an
1818
+ agent on top of conflict markers.
1819
+ - **`branchPerTask`** — dragging a board card to Working checks out
1820
+ `task/<id>-<slug>` (id first, so retitles don't orphan it; idempotent on
1821
+ re-drag). Reaching Review logs the exact `gh pr create` command instead of
1822
+ running it — pushing publishes, and that stays a human act.
1823
+
1824
+ ## Routes that loop, and steps that skip
1825
+
1826
+ A step can carry `onFail` naming an **earlier** step: when it errors, the route
1827
+ re-enters there instead of failing — "review fails → back to execute", as data.
1828
+ Backward-only (a forward jump would skip work), budgeted at three re-entries
1829
+ (a pipeline that never converges should say so, not orbit), and visible in the
1830
+ thread as `route_step {loopedFrom, loop}`. Define and keep pipelines with
1831
+ `loom routes:save <name> planner executor reviewer` — validated against the
1832
+ roster at save, stored in the project config so they travel with the repo.
1833
+
1834
+ A step can also carry a **condition on the previous turn**, and runs only when
1835
+ it holds:
1836
+
1837
+ ```bash
1838
+ loom route 'planner,executor,reviewer?lines>200' "tighten the tax rules"
1839
+ ```
1840
+
1841
+ Conditions read the turn's own diff and nothing else — `changed>10`,
1842
+ `changed<3`, `lines>200`, `lines<20`, `touched:src/db/**`, `!touched:docs/**` —
1843
+ so a route can send a big change to the reviewer and let a small one go
1844
+ straight on. That bar is deliberate: a condition must be something the daemon
1845
+ *measured*, never a judgement about whether the work was any good; judgement
1846
+ stays with the LLM router. Anything else is refused when the route is defined,
1847
+ not silently never matched at run time, and the first step can't be
1848
+ conditional because there's no turn before it. A skipped step says so in the
1849
+ thread with the numbers that decided it — *step 3/3 → reviewer skipped — needs
1850
+ more than 200 lines changed, the turn changed 1 file, 4 lines* — and skipping
1851
+ is not failing: the route completes. In `.loom/config.json` the same thing is
1852
+ `{ "step": "reviewer", "when": "lines>200" }`.
1853
+
505
1854
  ## Security model
506
1855
 
1856
+ **Per-project tokens.** `POST /api/pair/new {"projects": ["<id>"]}` mints a
1857
+ pairing token whose claimed client can touch exactly those projects: everything
1858
+ else 403s (by id or name — the wall resolves names first), the project list
1859
+ filters to its world, and the event/terminal/log streams skip out-of-scope
1860
+ frames. The admin who mints chooses the scope; the device that claims cannot
1861
+ widen it. Clients paired before scoping existed keep the run of the place.
1862
+
1863
+
507
1864
  - The daemon binds to `127.0.0.1` by default, or your **Tailscale interface** with
508
1865
  `--tailnet` — never `0.0.0.0` on its own. **Connect-a-phone** can *add* a listener on a
509
1866
  specific LAN/tailnet IP (never `0.0.0.0`, never an arbitrary host — the target is
@@ -540,7 +1897,7 @@ buzzes once, not five times). Verify with `loom clients --ping`.
540
1897
  Add an agent in ~40 lines — implement the contract, register the kind:
541
1898
 
542
1899
  ```ts
543
- import { AdapterBase, registerAgentKind, type SendInput } from "threadloom/sdk";
1900
+ import { AdapterBase, registerAgentKind, type SendInput } from "@loompad/cli/sdk";
544
1901
 
545
1902
  class MyAgentAdapter extends AdapterBase {
546
1903
  async available() { return true; }
@@ -582,15 +1939,61 @@ with its why: [ARCHITECTURE.md](ARCHITECTURE.md).
582
1939
  }
583
1940
  ```
584
1941
 
585
- Roles: `planner` · `executor` · `reviewer` · `general`. Claude Code options:
586
- `permissionMode` (default `acceptEdits`), `model`. OpenCode options:
1942
+ Roles are free text — `planner` · `executor` · `reviewer` · `general` are the ones
1943
+ Loom suggests and routes understand, and your project may invent others. Claude Code
1944
+ options: `permissionMode` (default `acceptEdits`), `model`. OpenCode options:
587
1945
  `model` (`"providerID/modelID"`, e.g. `"opencode/minimax-m2.5"` — **set this**: headless
588
1946
  sessions don't inherit your TUI default), `agent`, `baseUrl` to reuse a running server.
1947
+ A `model` agent's options are [over here](#agents-that-are-models).
1948
+
1949
+ **Everything else the file can hold**, each explained where it belongs — nothing is
1950
+ required, and the defaults are the behaviour Loom had before the key existed:
1951
+
1952
+ | key | what it turns on |
1953
+ |---|---|
1954
+ | [`git.commitPerTurn` · `worktreePerAgent` · `mergeOnHandoff` · `branchPerTask`](#git-policies-all-opt-in) | committing, isolation, carrying work across the baton, branch per card |
1955
+ | [`git.delivery`](#git-delivery-commit-push-or-open-a-pr) | what happens to finished work: nothing, commit, push, or a PR |
1956
+ | [`brain.extractor` · `brain.model`](#the-brain-sharpened) | who reads finished turns for memory |
1957
+ | [`brain.semantic`](#the-brain-sharpened) | the embedding channel — the one that follows a synonym |
1958
+ | [`budgets.perGoalUsd`](#what-a-goal-may-spend) | what one orchestra goal may spend |
1959
+ | [`maxConcurrentGoals`](#more-than-one-goal-when-they-cant-collide) | goals side by side, when their paths can't collide |
1960
+ | [`safety.snapshotBeforeRoutes`](#routing--multi-hop-pipelines) | checkpoint brain+board+config before a fleet runs unattended |
1961
+ | [`servers`](#the-browser-tab) | the dev servers Loom may run and preview |
1962
+ | [`mcps`](#odds-and-ends-that-pull-their-weight) | MCP servers offered to the agents whose CLI takes them |
1963
+ | [`skills`](#odds-and-ends-that-pull-their-weight) | which `SKILL.md` skills ride along in every briefing |
1964
+ | [`team`](#teams--see-each-others-agents) | which team this project publishes to |
1965
+
1966
+ Keys are **never** in here: a provider's API key lives in `~/.loom/providers.json`
1967
+ (mode 0600) or the environment, because this file is committed.
1968
+
1969
+ ## Odds and ends that pull their weight
1970
+
1971
+ - **MCP health** — the daemon polls each project's HTTP MCP servers; a server
1972
+ that stops answering is named in the Console and **withheld from turns**
1973
+ (an absent tool the agent never saw beats a present tool that throws) until
1974
+ it answers again. `loom mcp:health`.
1975
+ - **Terminal scrollback survives restarts** — the pty dies with the daemon
1976
+ (physics), the text doesn't (persisted, seeded back under a divider that
1977
+ says the shell is new). A deliberately closed terminal is forgotten — only
1978
+ daemon death preserves.
1979
+ - **Console search + scope filter** — find the error from *grok* among six
1980
+ agents' noise; the scope menu is built from the records themselves.
1981
+ - **Review comments** — click a line in any diff, type, Enter; comments stage
1982
+ as one `file:line — comment` message in the composer, going to whichever
1983
+ agent you pick. No side channel: baton, budgets and the thread all apply.
1984
+ - **Hold-to-talk** — the composer mic records while held and transcribes via
1985
+ `LOOM_STT_CMD` (whisper.cpp or anything that takes `{file}` and prints
1986
+ text). No cloud STT: no-keys is the contract. Voice fills the box; you still
1987
+ press send.
1988
+ - **Skill authoring** — `POST /api/projects/:id/skills/author` scaffolds
1989
+ `skills/<id>/SKILL.md`, validated by the same parser the roster reads with.
1990
+ - **The spend ledger** — `loom costs --series 14`: one line per day, per-agent
1991
+ split inline, from the same events the budgets enforce against.
589
1992
 
590
1993
  ## Development
591
1994
 
592
1995
  ```bash
593
- npm test # 179 tests: unit + full HTTP/WS end-to-end
1996
+ npm test # the whole suite (~1,300): unit + full HTTP/WS end-to-end
594
1997
  npm run build # tsc → dist/
595
1998
  npm run dev # run the CLI from source (tsx)
596
1999
  ```
@@ -606,6 +2009,7 @@ npm run dev # run the CLI from source (tsx)
606
2009
  | `LOOM_NO_NOTIFY` | `1` silences desktop notifications. |
607
2010
  | `LOOM_NO_PUSH` | `1` silences phone push. |
608
2011
  | `LOOM_ROUTE_STEP_TIMEOUT_MS` | Per-hop route timeout. Default 45 min. |
2012
+ | `OTEL_EXPORTER_OTLP_ENDPOINT` | Where telemetry goes. Default `http://localhost:4318`. `DO_NOT_TRACK=1` turns it all off. The rest of the observability variables — the store to read back from, self-heal, trace deep links — are in [docs/observability.md](docs/observability.md). |
609
2013
 
610
2014
  Going the other way, Loom **sets `LOOM_TERMINAL=1`** inside every terminal it opens, so
611
2015
  your shell profile can tell it's running in Loom's pane. (`LOOM_EXPO_PUSH_URL` and
@@ -613,7 +2017,8 @@ your shell profile can tell it's running in Loom's pane. (`LOOM_EXPO_PUSH_URL` a
613
2017
 
614
2018
  ## Roadmap
615
2019
 
616
- - Tasks beyond GitHub — GitLab and Linear sit disabled in the provider row today.
2020
+ - Tasks beyond GitHub and Linear — the board's source row is GitHub / Projects / Linear
2021
+ today. GitLab is not in it at all; only its brand mark is in the icon set.
617
2022
  - More adapters/bridges via the SDK — contributions welcome.
618
2023
 
619
2024
  ## Design
@@ -625,6 +2030,30 @@ Adapted from the [Orca](https://github.com/stablyai/orca) design system (MIT,
625
2030
  © Lovecast Inc.); the Geist typeface is © Vercel under the SIL Open Font
626
2031
  License 1.1. Tokens and rules: [docs/design-system.md](docs/design-system.md).
627
2032
 
2033
+ The thread is a reading column: replies stream in as they're written (Claude
2034
+ Code, OpenCode, model agents, and every orchestra worker in its task thread),
2035
+ each under a byline with the agent, model and time; tool use folds into one
2036
+ activity line; a turn ends on a footer with its duration and cost; and an
2037
+ orchestrator's plan is a card, not JSON.
2038
+
2039
+ ## Credits
2040
+
2041
+ Loom stands on good open-source work. Full license texts are in
2042
+ [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md).
2043
+
2044
+ - **[Agent Orchestrator](https://github.com/Untrivial-ai/agent-orchestrator)**
2045
+ (Apache-2.0, © Untrivial). Loom adapted its orchestrator/worker model, its
2046
+ "coordinate, never implement" orchestrator rules, its worker rules and its
2047
+ worktree-per-worker isolation. Loom rebuilt the orchestration around a parsed
2048
+ action protocol and a dependency graph, so any agent can orchestrate any mix
2049
+ of workers.
2050
+ - **[T3 Code](https://github.com/pingdotgg/t3code)** (MIT, © T3 Tools Inc.). Loom
2051
+ adapted its mobile connection patterns: the pairing secret in the URL fragment,
2052
+ resuming a stream from a known sequence, and a reconnect supervisor that forces
2053
+ a fresh connection after the app was backgrounded.
2054
+ - **[Orca](https://github.com/stablyai/orca)** (MIT). The design system (see
2055
+ [Design](#design)).
2056
+
628
2057
  ## License
629
2058
 
630
2059
  MIT © Nivesh Gajengi