@loompad/cli 0.1.0 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (687) hide show
  1. package/ARCHITECTURE.md +96 -11
  2. package/CHANGELOG.md +1042 -0
  3. package/README.md +1473 -44
  4. package/THIRD_PARTY_NOTICES.md +275 -0
  5. package/dist/adapters/antigravity-cli.d.ts +62 -0
  6. package/dist/adapters/antigravity-cli.js +258 -0
  7. package/dist/adapters/antigravity-cli.js.map +1 -0
  8. package/dist/adapters/base.d.ts +61 -9
  9. package/dist/adapters/base.js +266 -15
  10. package/dist/adapters/base.js.map +1 -1
  11. package/dist/adapters/bridges/antigravity.d.ts +9 -12
  12. package/dist/adapters/bridges/antigravity.js +9 -12
  13. package/dist/adapters/bridges/antigravity.js.map +1 -1
  14. package/dist/adapters/echo.d.ts +1 -0
  15. package/dist/adapters/echo.js +59 -3
  16. package/dist/adapters/echo.js.map +1 -1
  17. package/dist/adapters/grok.d.ts +7 -0
  18. package/dist/adapters/grok.js +20 -10
  19. package/dist/adapters/grok.js.map +1 -1
  20. package/dist/adapters/index.d.ts +5 -1
  21. package/dist/adapters/index.js +53 -7
  22. package/dist/adapters/index.js.map +1 -1
  23. package/dist/adapters/model.d.ts +137 -0
  24. package/dist/adapters/model.js +527 -0
  25. package/dist/adapters/model.js.map +1 -0
  26. package/dist/adapters/opencode.d.ts +101 -1
  27. package/dist/adapters/opencode.js +874 -98
  28. package/dist/adapters/opencode.js.map +1 -1
  29. package/dist/cli/doctor.d.ts +32 -0
  30. package/dist/cli/doctor.js +89 -4
  31. package/dist/cli/doctor.js.map +1 -1
  32. package/dist/cli/index.js +2651 -87
  33. package/dist/cli/index.js.map +1 -1
  34. package/dist/cli/tui-model.js +3 -1
  35. package/dist/cli/tui-model.js.map +1 -1
  36. package/dist/cli/tui.js +44 -1
  37. package/dist/cli/tui.js.map +1 -1
  38. package/dist/cli/ui.d.ts +2 -0
  39. package/dist/cli/ui.js +13 -0
  40. package/dist/cli/ui.js.map +1 -1
  41. package/dist/core/ades.d.ts +32 -16
  42. package/dist/core/ades.js +57 -20
  43. package/dist/core/ades.js.map +1 -1
  44. package/dist/core/agent-state.d.ts +14 -0
  45. package/dist/core/agent-state.js +32 -0
  46. package/dist/core/agent-state.js.map +1 -0
  47. package/dist/core/approvals.d.ts +33 -0
  48. package/dist/core/approvals.js +29 -0
  49. package/dist/core/approvals.js.map +1 -0
  50. package/dist/core/backup.d.ts +19 -0
  51. package/dist/core/backup.js +86 -0
  52. package/dist/core/backup.js.map +1 -0
  53. package/dist/core/baton.d.ts +2 -2
  54. package/dist/core/baton.js.map +1 -1
  55. package/dist/core/brain-extract.d.ts +17 -0
  56. package/dist/core/brain-extract.js +28 -2
  57. package/dist/core/brain-extract.js.map +1 -1
  58. package/dist/core/brain-index.d.ts +71 -0
  59. package/dist/core/brain-index.js +204 -5
  60. package/dist/core/brain-index.js.map +1 -1
  61. package/dist/core/brain.d.ts +58 -2
  62. package/dist/core/brain.js +79 -1
  63. package/dist/core/brain.js.map +1 -1
  64. package/dist/core/chat-import.d.ts +71 -0
  65. package/dist/core/chat-import.js +419 -0
  66. package/dist/core/chat-import.js.map +1 -0
  67. package/dist/core/checkpoint.d.ts +107 -0
  68. package/dist/core/checkpoint.js +259 -0
  69. package/dist/core/checkpoint.js.map +1 -0
  70. package/dist/core/claude-cli.js +1 -1
  71. package/dist/core/claude-cli.js.map +1 -1
  72. package/dist/core/continuity/artifacts.d.ts +14 -0
  73. package/dist/core/continuity/artifacts.js +118 -0
  74. package/dist/core/continuity/artifacts.js.map +1 -0
  75. package/dist/core/continuity/capabilities.d.ts +65 -0
  76. package/dist/core/continuity/capabilities.js +129 -0
  77. package/dist/core/continuity/capabilities.js.map +1 -0
  78. package/dist/core/continuity/contracts.d.ts +352 -0
  79. package/dist/core/continuity/contracts.js +117 -0
  80. package/dist/core/continuity/contracts.js.map +1 -0
  81. package/dist/core/continuity/engine.d.ts +55 -0
  82. package/dist/core/continuity/engine.js +504 -0
  83. package/dist/core/continuity/engine.js.map +1 -0
  84. package/dist/core/continuity/store.d.ts +73 -0
  85. package/dist/core/continuity/store.js +438 -0
  86. package/dist/core/continuity/store.js.map +1 -0
  87. package/dist/core/conversations.d.ts +19 -0
  88. package/dist/core/conversations.js +87 -0
  89. package/dist/core/conversations.js.map +1 -0
  90. package/dist/core/crew-coordination.d.ts +6 -0
  91. package/dist/core/crew-coordination.js +24 -0
  92. package/dist/core/crew-coordination.js.map +1 -0
  93. package/dist/core/crew-policy.d.ts +18 -0
  94. package/dist/core/crew-policy.js +40 -0
  95. package/dist/core/crew-policy.js.map +1 -0
  96. package/dist/core/crew-protocol.d.ts +64 -0
  97. package/dist/core/crew-protocol.js +146 -0
  98. package/dist/core/crew-protocol.js.map +1 -0
  99. package/dist/core/crew-requests.d.ts +14 -0
  100. package/dist/core/crew-requests.js +22 -0
  101. package/dist/core/crew-requests.js.map +1 -0
  102. package/dist/core/crew.d.ts +395 -0
  103. package/dist/core/crew.js +1857 -0
  104. package/dist/core/crew.js.map +1 -0
  105. package/dist/core/digest.d.ts +40 -0
  106. package/dist/core/digest.js +94 -0
  107. package/dist/core/digest.js.map +1 -0
  108. package/dist/core/distill.js +3 -5
  109. package/dist/core/distill.js.map +1 -1
  110. package/dist/core/eventlog.d.ts +27 -25
  111. package/dist/core/eventlog.js +97 -150
  112. package/dist/core/eventlog.js.map +1 -1
  113. package/dist/core/events/contracts.d.ts +51 -0
  114. package/dist/core/events/contracts.js +2 -0
  115. package/dist/core/events/contracts.js.map +1 -0
  116. package/dist/core/events/jsonl-store.d.ts +23 -0
  117. package/dist/core/events/jsonl-store.js +90 -0
  118. package/dist/core/events/jsonl-store.js.map +1 -0
  119. package/dist/core/events/sqlite-store.d.ts +37 -0
  120. package/dist/core/events/sqlite-store.js +215 -0
  121. package/dist/core/events/sqlite-store.js.map +1 -0
  122. package/dist/core/forecast.d.ts +44 -0
  123. package/dist/core/forecast.js +46 -0
  124. package/dist/core/forecast.js.map +1 -0
  125. package/dist/core/free-pool.d.ts +65 -0
  126. package/dist/core/free-pool.js +171 -0
  127. package/dist/core/free-pool.js.map +1 -0
  128. package/dist/core/git.d.ts +30 -0
  129. package/dist/core/git.js +50 -2
  130. package/dist/core/git.js.map +1 -1
  131. package/dist/core/github-events.d.ts +53 -0
  132. package/dist/core/github-events.js +227 -0
  133. package/dist/core/github-events.js.map +1 -0
  134. package/dist/core/goal-lanes.d.ts +53 -0
  135. package/dist/core/goal-lanes.js +99 -0
  136. package/dist/core/goal-lanes.js.map +1 -0
  137. package/dist/core/hosted.d.ts +27 -0
  138. package/dist/core/hosted.js +50 -0
  139. package/dist/core/hosted.js.map +1 -0
  140. package/dist/core/http-server.d.ts +5 -0
  141. package/dist/core/http-server.js +8 -0
  142. package/dist/core/http-server.js.map +1 -0
  143. package/dist/core/invite-link.d.ts +31 -0
  144. package/dist/core/invite-link.js +45 -0
  145. package/dist/core/invite-link.js.map +1 -0
  146. package/dist/core/loops.d.ts +37 -0
  147. package/dist/core/loops.js +81 -0
  148. package/dist/core/loops.js.map +1 -0
  149. package/dist/core/mcp-catalog.d.ts +182 -0
  150. package/dist/core/mcp-catalog.js +431 -0
  151. package/dist/core/mcp-catalog.js.map +1 -0
  152. package/dist/core/mcp.d.ts +87 -0
  153. package/dist/core/mcp.js +181 -0
  154. package/dist/core/mcp.js.map +1 -0
  155. package/dist/core/memory.d.ts +1 -0
  156. package/dist/core/memory.js +5 -2
  157. package/dist/core/memory.js.map +1 -1
  158. package/dist/core/model-tools.d.ts +97 -0
  159. package/dist/core/model-tools.js +388 -0
  160. package/dist/core/model-tools.js.map +1 -0
  161. package/dist/core/notify.js +19 -1
  162. package/dist/core/notify.js.map +1 -1
  163. package/dist/core/orchestra.d.ts +635 -0
  164. package/dist/core/orchestra.js +2120 -0
  165. package/dist/core/orchestra.js.map +1 -0
  166. package/dist/core/permissions.d.ts +56 -0
  167. package/dist/core/permissions.js +122 -0
  168. package/dist/core/permissions.js.map +1 -0
  169. package/dist/core/preview-proxy.d.ts +70 -0
  170. package/dist/core/preview-proxy.js +563 -0
  171. package/dist/core/preview-proxy.js.map +1 -0
  172. package/dist/core/preview-shot.d.ts +46 -0
  173. package/dist/core/preview-shot.js +109 -0
  174. package/dist/core/preview-shot.js.map +1 -0
  175. package/dist/core/process.d.ts +36 -0
  176. package/dist/core/process.js +168 -0
  177. package/dist/core/process.js.map +1 -0
  178. package/dist/core/prompt-queue.d.ts +136 -0
  179. package/dist/core/prompt-queue.js +269 -0
  180. package/dist/core/prompt-queue.js.map +1 -0
  181. package/dist/core/prompts.d.ts +53 -0
  182. package/dist/core/prompts.js +127 -0
  183. package/dist/core/prompts.js.map +1 -0
  184. package/dist/core/proof.d.ts +82 -0
  185. package/dist/core/proof.js +216 -0
  186. package/dist/core/proof.js.map +1 -0
  187. package/dist/core/provider-text.d.ts +23 -0
  188. package/dist/core/provider-text.js +66 -0
  189. package/dist/core/provider-text.js.map +1 -0
  190. package/dist/core/providers.d.ts +152 -0
  191. package/dist/core/providers.js +343 -0
  192. package/dist/core/providers.js.map +1 -0
  193. package/dist/core/quota.d.ts +33 -0
  194. package/dist/core/quota.js +74 -0
  195. package/dist/core/quota.js.map +1 -0
  196. package/dist/core/recipes.d.ts +50 -0
  197. package/dist/core/recipes.js +99 -0
  198. package/dist/core/recipes.js.map +1 -0
  199. package/dist/core/registry.d.ts +54 -0
  200. package/dist/core/registry.js +62 -5
  201. package/dist/core/registry.js.map +1 -1
  202. package/dist/core/relay-client.d.ts +67 -0
  203. package/dist/core/relay-client.js +184 -0
  204. package/dist/core/relay-client.js.map +1 -0
  205. package/dist/core/relay-protocol.d.ts +140 -0
  206. package/dist/core/relay-protocol.js +189 -0
  207. package/dist/core/relay-protocol.js.map +1 -0
  208. package/dist/core/replay.d.ts +18 -0
  209. package/dist/core/replay.js +191 -0
  210. package/dist/core/replay.js.map +1 -0
  211. package/dist/core/review-triage.d.ts +41 -0
  212. package/dist/core/review-triage.js +124 -0
  213. package/dist/core/review-triage.js.map +1 -0
  214. package/dist/core/router.js +2 -2
  215. package/dist/core/router.js.map +1 -1
  216. package/dist/core/routes.d.ts +39 -3
  217. package/dist/core/routes.js +137 -4
  218. package/dist/core/routes.js.map +1 -1
  219. package/dist/core/run-command.d.ts +14 -0
  220. package/dist/core/run-command.js +45 -0
  221. package/dist/core/run-command.js.map +1 -0
  222. package/dist/core/runner-setup.d.ts +36 -0
  223. package/dist/core/runner-setup.js +114 -0
  224. package/dist/core/runner-setup.js.map +1 -0
  225. package/dist/core/search.js +0 -0
  226. package/dist/core/search.js.map +1 -1
  227. package/dist/core/semantic.d.ts +92 -0
  228. package/dist/core/semantic.js +227 -0
  229. package/dist/core/semantic.js.map +1 -0
  230. package/dist/core/servers.d.ts +115 -0
  231. package/dist/core/servers.js +305 -0
  232. package/dist/core/servers.js.map +1 -0
  233. package/dist/core/setup.js +64 -6
  234. package/dist/core/setup.js.map +1 -1
  235. package/dist/core/shield.d.ts +17 -0
  236. package/dist/core/shield.js +81 -0
  237. package/dist/core/shield.js.map +1 -0
  238. package/dist/core/shift.d.ts +69 -0
  239. package/dist/core/shift.js +120 -0
  240. package/dist/core/shift.js.map +1 -0
  241. package/dist/core/skill-install.d.ts +125 -0
  242. package/dist/core/skill-install.js +328 -0
  243. package/dist/core/skill-install.js.map +1 -0
  244. package/dist/core/skills.d.ts +116 -0
  245. package/dist/core/skills.js +223 -0
  246. package/dist/core/skills.js.map +1 -0
  247. package/dist/core/step-conditions.d.ts +65 -0
  248. package/dist/core/step-conditions.js +102 -0
  249. package/dist/core/step-conditions.js.map +1 -0
  250. package/dist/core/subagents.d.ts +45 -0
  251. package/dist/core/subagents.js +90 -0
  252. package/dist/core/subagents.js.map +1 -0
  253. package/dist/core/team-canon.d.ts +42 -0
  254. package/dist/core/team-canon.js +121 -0
  255. package/dist/core/team-canon.js.map +1 -0
  256. package/dist/core/team-crypto.d.ts +97 -0
  257. package/dist/core/team-crypto.js +175 -0
  258. package/dist/core/team-crypto.js.map +1 -0
  259. package/dist/core/team-hub.d.ts +514 -0
  260. package/dist/core/team-hub.js +964 -0
  261. package/dist/core/team-hub.js.map +1 -0
  262. package/dist/core/team-landing.d.ts +239 -0
  263. package/dist/core/team-landing.js +504 -0
  264. package/dist/core/team-landing.js.map +1 -0
  265. package/dist/core/team-leases.d.ts +44 -0
  266. package/dist/core/team-leases.js +145 -0
  267. package/dist/core/team-leases.js.map +1 -0
  268. package/dist/core/team-memory.d.ts +42 -0
  269. package/dist/core/team-memory.js +84 -0
  270. package/dist/core/team-memory.js.map +1 -0
  271. package/dist/core/team-policy.d.ts +85 -0
  272. package/dist/core/team-policy.js +191 -0
  273. package/dist/core/team-policy.js.map +1 -0
  274. package/dist/core/thread-md.d.ts +6 -0
  275. package/dist/core/thread-md.js +42 -0
  276. package/dist/core/thread-md.js.map +1 -0
  277. package/dist/core/turn-stats.d.ts +61 -0
  278. package/dist/core/turn-stats.js +124 -0
  279. package/dist/core/turn-stats.js.map +1 -0
  280. package/dist/core/updater.d.ts +73 -0
  281. package/dist/core/updater.js +153 -0
  282. package/dist/core/updater.js.map +1 -0
  283. package/dist/core/usage.d.ts +75 -0
  284. package/dist/core/usage.js +163 -0
  285. package/dist/core/usage.js.map +1 -0
  286. package/dist/core/windows-jobs.d.ts +15 -0
  287. package/dist/core/windows-jobs.js +170 -0
  288. package/dist/core/windows-jobs.js.map +1 -0
  289. package/dist/core/worktree-merge.d.ts +57 -0
  290. package/dist/core/worktree-merge.js +123 -0
  291. package/dist/core/worktree-merge.js.map +1 -0
  292. package/dist/core/worktree.d.ts +5 -0
  293. package/dist/core/worktree.js +45 -5
  294. package/dist/core/worktree.js.map +1 -1
  295. package/dist/core/worktrees.d.ts +6 -0
  296. package/dist/core/worktrees.js +21 -0
  297. package/dist/core/worktrees.js.map +1 -0
  298. package/dist/daemon/app-page.d.ts +0 -17
  299. package/dist/daemon/app-page.js +10 -6346
  300. package/dist/daemon/app-page.js.map +1 -1
  301. package/dist/daemon/auth.d.ts +24 -3
  302. package/dist/daemon/auth.js +66 -7
  303. package/dist/daemon/auth.js.map +1 -1
  304. package/dist/daemon/board.d.ts +7 -0
  305. package/dist/daemon/board.js +12 -1
  306. package/dist/daemon/board.js.map +1 -1
  307. package/dist/daemon/brand-icons.d.ts +6 -1
  308. package/dist/daemon/brand-icons.js +11 -3
  309. package/dist/daemon/brand-icons.js.map +1 -1
  310. package/dist/daemon/client.d.ts +665 -1
  311. package/dist/daemon/client.js +381 -3
  312. package/dist/daemon/client.js.map +1 -1
  313. package/dist/daemon/delivery.d.ts +34 -0
  314. package/dist/daemon/delivery.js +63 -0
  315. package/dist/daemon/delivery.js.map +1 -0
  316. package/dist/daemon/deploys.d.ts +62 -0
  317. package/dist/daemon/deploys.js +159 -0
  318. package/dist/daemon/deploys.js.map +1 -0
  319. package/dist/daemon/landing.d.ts +192 -0
  320. package/dist/daemon/landing.js +992 -0
  321. package/dist/daemon/landing.js.map +1 -0
  322. package/dist/daemon/onboard.d.ts +150 -0
  323. package/dist/daemon/onboard.js +390 -0
  324. package/dist/daemon/onboard.js.map +1 -0
  325. package/dist/daemon/proof.d.ts +70 -0
  326. package/dist/daemon/proof.js +172 -0
  327. package/dist/daemon/proof.js.map +1 -0
  328. package/dist/daemon/push.d.ts +19 -1
  329. package/dist/daemon/push.js +90 -3
  330. package/dist/daemon/push.js.map +1 -1
  331. package/dist/daemon/relay.d.ts +82 -0
  332. package/dist/daemon/relay.js +351 -0
  333. package/dist/daemon/relay.js.map +1 -0
  334. package/dist/daemon/routes/agent-discovery.d.ts +4 -0
  335. package/dist/daemon/routes/agent-discovery.js +29 -0
  336. package/dist/daemon/routes/agent-discovery.js.map +1 -0
  337. package/dist/daemon/routes/agent-settings.d.ts +4 -0
  338. package/dist/daemon/routes/agent-settings.js +94 -0
  339. package/dist/daemon/routes/agent-settings.js.map +1 -0
  340. package/dist/daemon/routes/agents.d.ts +4 -0
  341. package/dist/daemon/routes/agents.js +105 -0
  342. package/dist/daemon/routes/agents.js.map +1 -0
  343. package/dist/daemon/routes/alerts.d.ts +4 -0
  344. package/dist/daemon/routes/alerts.js +137 -0
  345. package/dist/daemon/routes/alerts.js.map +1 -0
  346. package/dist/daemon/routes/approvals.d.ts +4 -0
  347. package/dist/daemon/routes/approvals.js +40 -0
  348. package/dist/daemon/routes/approvals.js.map +1 -0
  349. package/dist/daemon/routes/assets.d.ts +3 -0
  350. package/dist/daemon/routes/assets.js +64 -0
  351. package/dist/daemon/routes/assets.js.map +1 -0
  352. package/dist/daemon/routes/board.d.ts +4 -0
  353. package/dist/daemon/routes/board.js +147 -0
  354. package/dist/daemon/routes/board.js.map +1 -0
  355. package/dist/daemon/routes/brain.d.ts +4 -0
  356. package/dist/daemon/routes/brain.js +270 -0
  357. package/dist/daemon/routes/brain.js.map +1 -0
  358. package/dist/daemon/routes/chats.d.ts +4 -0
  359. package/dist/daemon/routes/chats.js +249 -0
  360. package/dist/daemon/routes/chats.js.map +1 -0
  361. package/dist/daemon/routes/clients.d.ts +4 -0
  362. package/dist/daemon/routes/clients.js +49 -0
  363. package/dist/daemon/routes/clients.js.map +1 -0
  364. package/dist/daemon/routes/cloud.d.ts +4 -0
  365. package/dist/daemon/routes/cloud.js +46 -0
  366. package/dist/daemon/routes/cloud.js.map +1 -0
  367. package/dist/daemon/routes/config.d.ts +4 -0
  368. package/dist/daemon/routes/config.js +35 -0
  369. package/dist/daemon/routes/config.js.map +1 -0
  370. package/dist/daemon/routes/context.d.ts +51 -0
  371. package/dist/daemon/routes/context.js +2 -0
  372. package/dist/daemon/routes/context.js.map +1 -0
  373. package/dist/daemon/routes/crews.d.ts +4 -0
  374. package/dist/daemon/routes/crews.js +149 -0
  375. package/dist/daemon/routes/crews.js.map +1 -0
  376. package/dist/daemon/routes/files.d.ts +3 -0
  377. package/dist/daemon/routes/files.js +253 -0
  378. package/dist/daemon/routes/files.js.map +1 -0
  379. package/dist/daemon/routes/git.d.ts +4 -0
  380. package/dist/daemon/routes/git.js +96 -0
  381. package/dist/daemon/routes/git.js.map +1 -0
  382. package/dist/daemon/routes/history.d.ts +4 -0
  383. package/dist/daemon/routes/history.js +101 -0
  384. package/dist/daemon/routes/history.js.map +1 -0
  385. package/dist/daemon/routes/integrations-project.d.ts +3 -0
  386. package/dist/daemon/routes/integrations-project.js +103 -0
  387. package/dist/daemon/routes/integrations-project.js.map +1 -0
  388. package/dist/daemon/routes/integrations.d.ts +3 -0
  389. package/dist/daemon/routes/integrations.js +111 -0
  390. package/dist/daemon/routes/integrations.js.map +1 -0
  391. package/dist/daemon/routes/logs.d.ts +3 -0
  392. package/dist/daemon/routes/logs.js +40 -0
  393. package/dist/daemon/routes/logs.js.map +1 -0
  394. package/dist/daemon/routes/messages.d.ts +4 -0
  395. package/dist/daemon/routes/messages.js +60 -0
  396. package/dist/daemon/routes/messages.js.map +1 -0
  397. package/dist/daemon/routes/observability.d.ts +4 -0
  398. package/dist/daemon/routes/observability.js +247 -0
  399. package/dist/daemon/routes/observability.js.map +1 -0
  400. package/dist/daemon/routes/onboard.d.ts +12 -0
  401. package/dist/daemon/routes/onboard.js +67 -0
  402. package/dist/daemon/routes/onboard.js.map +1 -0
  403. package/dist/daemon/routes/orchestra.d.ts +4 -0
  404. package/dist/daemon/routes/orchestra.js +110 -0
  405. package/dist/daemon/routes/orchestra.js.map +1 -0
  406. package/dist/daemon/routes/pairing.d.ts +4 -0
  407. package/dist/daemon/routes/pairing.js +155 -0
  408. package/dist/daemon/routes/pairing.js.map +1 -0
  409. package/dist/daemon/routes/preview.d.ts +24 -0
  410. package/dist/daemon/routes/preview.js +122 -0
  411. package/dist/daemon/routes/preview.js.map +1 -0
  412. package/dist/daemon/routes/project-actions.d.ts +4 -0
  413. package/dist/daemon/routes/project-actions.js +53 -0
  414. package/dist/daemon/routes/project-actions.js.map +1 -0
  415. package/dist/daemon/routes/project-team.d.ts +4 -0
  416. package/dist/daemon/routes/project-team.js +201 -0
  417. package/dist/daemon/routes/project-team.js.map +1 -0
  418. package/dist/daemon/routes/projects.d.ts +14 -0
  419. package/dist/daemon/routes/projects.js +138 -0
  420. package/dist/daemon/routes/projects.js.map +1 -0
  421. package/dist/daemon/routes/prompts.d.ts +4 -0
  422. package/dist/daemon/routes/prompts.js +44 -0
  423. package/dist/daemon/routes/prompts.js.map +1 -0
  424. package/dist/daemon/routes/proof.d.ts +4 -0
  425. package/dist/daemon/routes/proof.js +22 -0
  426. package/dist/daemon/routes/proof.js.map +1 -0
  427. package/dist/daemon/routes/providers.d.ts +3 -0
  428. package/dist/daemon/routes/providers.js +60 -0
  429. package/dist/daemon/routes/providers.js.map +1 -0
  430. package/dist/daemon/routes/public.d.ts +4 -0
  431. package/dist/daemon/routes/public.js +64 -0
  432. package/dist/daemon/routes/public.js.map +1 -0
  433. package/dist/daemon/routes/queue.d.ts +4 -0
  434. package/dist/daemon/routes/queue.js +140 -0
  435. package/dist/daemon/routes/queue.js.map +1 -0
  436. package/dist/daemon/routes/routing.d.ts +4 -0
  437. package/dist/daemon/routes/routing.js +61 -0
  438. package/dist/daemon/routes/routing.js.map +1 -0
  439. package/dist/daemon/routes/runtime-handler.d.ts +2 -0
  440. package/dist/daemon/routes/runtime-handler.js +63 -0
  441. package/dist/daemon/routes/runtime-handler.js.map +1 -0
  442. package/dist/daemon/routes/search.d.ts +4 -0
  443. package/dist/daemon/routes/search.js +17 -0
  444. package/dist/daemon/routes/search.js.map +1 -0
  445. package/dist/daemon/routes/servers.d.ts +4 -0
  446. package/dist/daemon/routes/servers.js +133 -0
  447. package/dist/daemon/routes/servers.js.map +1 -0
  448. package/dist/daemon/routes/setup.d.ts +3 -0
  449. package/dist/daemon/routes/setup.js +50 -0
  450. package/dist/daemon/routes/setup.js.map +1 -0
  451. package/dist/daemon/routes/shift.d.ts +4 -0
  452. package/dist/daemon/routes/shift.js +30 -0
  453. package/dist/daemon/routes/shift.js.map +1 -0
  454. package/dist/daemon/routes/specs.d.ts +4 -0
  455. package/dist/daemon/routes/specs.js +57 -0
  456. package/dist/daemon/routes/specs.js.map +1 -0
  457. package/dist/daemon/routes/task-delivery.d.ts +4 -0
  458. package/dist/daemon/routes/task-delivery.js +38 -0
  459. package/dist/daemon/routes/task-delivery.js.map +1 -0
  460. package/dist/daemon/routes/team.d.ts +4 -0
  461. package/dist/daemon/routes/team.js +193 -0
  462. package/dist/daemon/routes/team.js.map +1 -0
  463. package/dist/daemon/routes/terminals.d.ts +4 -0
  464. package/dist/daemon/routes/terminals.js +87 -0
  465. package/dist/daemon/routes/terminals.js.map +1 -0
  466. package/dist/daemon/routes/tools.d.ts +4 -0
  467. package/dist/daemon/routes/tools.js +180 -0
  468. package/dist/daemon/routes/tools.js.map +1 -0
  469. package/dist/daemon/routes/updates.d.ts +4 -0
  470. package/dist/daemon/routes/updates.js +89 -0
  471. package/dist/daemon/routes/updates.js.map +1 -0
  472. package/dist/daemon/routes/voice.d.ts +4 -0
  473. package/dist/daemon/routes/voice.js +53 -0
  474. package/dist/daemon/routes/voice.js.map +1 -0
  475. package/dist/daemon/runner.d.ts +186 -0
  476. package/dist/daemon/runner.js +612 -0
  477. package/dist/daemon/runner.js.map +1 -0
  478. package/dist/daemon/runtime/accounting.d.ts +130 -0
  479. package/dist/daemon/runtime/accounting.js +303 -0
  480. package/dist/daemon/runtime/accounting.js.map +1 -0
  481. package/dist/daemon/runtime/agents.d.ts +16 -0
  482. package/dist/daemon/runtime/agents.js +71 -0
  483. package/dist/daemon/runtime/agents.js.map +1 -0
  484. package/dist/daemon/runtime/briefings.d.ts +134 -0
  485. package/dist/daemon/runtime/briefings.js +329 -0
  486. package/dist/daemon/runtime/briefings.js.map +1 -0
  487. package/dist/daemon/runtime/native-usage.d.ts +21 -0
  488. package/dist/daemon/runtime/native-usage.js +71 -0
  489. package/dist/daemon/runtime/native-usage.js.map +1 -0
  490. package/dist/daemon/runtime/queue.d.ts +94 -0
  491. package/dist/daemon/runtime/queue.js +222 -0
  492. package/dist/daemon/runtime/queue.js.map +1 -0
  493. package/dist/daemon/runtime/relay.d.ts +93 -0
  494. package/dist/daemon/runtime/relay.js +240 -0
  495. package/dist/daemon/runtime/relay.js.map +1 -0
  496. package/dist/daemon/runtime/turns.d.ts +202 -0
  497. package/dist/daemon/runtime/turns.js +663 -0
  498. package/dist/daemon/runtime/turns.js.map +1 -0
  499. package/dist/daemon/runtime-support.d.ts +76 -0
  500. package/dist/daemon/runtime-support.js +134 -0
  501. package/dist/daemon/runtime-support.js.map +1 -0
  502. package/dist/daemon/runtime.d.ts +739 -70
  503. package/dist/daemon/runtime.js +2751 -470
  504. package/dist/daemon/runtime.js.map +1 -1
  505. package/dist/daemon/server.d.ts +82 -55
  506. package/dist/daemon/server.js +674 -1435
  507. package/dist/daemon/server.js.map +1 -1
  508. package/dist/daemon/shift.d.ts +70 -0
  509. package/dist/daemon/shift.js +170 -0
  510. package/dist/daemon/shift.js.map +1 -0
  511. package/dist/daemon/specs.d.ts +92 -0
  512. package/dist/daemon/specs.js +285 -0
  513. package/dist/daemon/specs.js.map +1 -0
  514. package/dist/daemon/system.d.ts +208 -0
  515. package/dist/daemon/system.js +557 -0
  516. package/dist/daemon/system.js.map +1 -0
  517. package/dist/daemon/tasks.js +1 -1
  518. package/dist/daemon/team-brain.d.ts +136 -0
  519. package/dist/daemon/team-brain.js +659 -0
  520. package/dist/daemon/team-brain.js.map +1 -0
  521. package/dist/daemon/team-coordinator.d.ts +78 -0
  522. package/dist/daemon/team-coordinator.js +408 -0
  523. package/dist/daemon/team-coordinator.js.map +1 -0
  524. package/dist/daemon/team.d.ts +339 -0
  525. package/dist/daemon/team.js +1433 -0
  526. package/dist/daemon/team.js.map +1 -0
  527. package/dist/daemon/terminals.d.ts +22 -3
  528. package/dist/daemon/terminals.js +236 -16
  529. package/dist/daemon/terminals.js.map +1 -1
  530. package/dist/hub/client.d.ts +120 -0
  531. package/dist/hub/client.js +228 -0
  532. package/dist/hub/client.js.map +1 -0
  533. package/dist/hub/server.d.ts +32 -0
  534. package/dist/hub/server.js +180 -0
  535. package/dist/hub/server.js.map +1 -0
  536. package/dist/hub/supabase-client.d.ts +220 -0
  537. package/dist/hub/supabase-client.js +843 -0
  538. package/dist/hub/supabase-client.js.map +1 -0
  539. package/dist/observability/ask.d.ts +91 -0
  540. package/dist/observability/ask.js +179 -0
  541. package/dist/observability/ask.js.map +1 -0
  542. package/dist/observability/clickhouse.d.ts +33 -0
  543. package/dist/observability/clickhouse.js +48 -0
  544. package/dist/observability/clickhouse.js.map +1 -0
  545. package/dist/observability/decisions.d.ts +176 -0
  546. package/dist/observability/decisions.js +377 -0
  547. package/dist/observability/decisions.js.map +1 -0
  548. package/dist/observability/index.d.ts +89 -0
  549. package/dist/observability/index.js +348 -0
  550. package/dist/observability/index.js.map +1 -0
  551. package/dist/observability/insights.d.ts +151 -0
  552. package/dist/observability/insights.js +319 -0
  553. package/dist/observability/insights.js.map +1 -0
  554. package/dist/observability/logs-query.d.ts +82 -0
  555. package/dist/observability/logs-query.js +102 -0
  556. package/dist/observability/logs-query.js.map +1 -0
  557. package/dist/observability/logs.d.ts +72 -0
  558. package/dist/observability/logs.js +295 -0
  559. package/dist/observability/logs.js.map +1 -0
  560. package/dist/observability/metrics.d.ts +86 -0
  561. package/dist/observability/metrics.js +303 -0
  562. package/dist/observability/metrics.js.map +1 -0
  563. package/dist/observability/otlp.d.ts +127 -0
  564. package/dist/observability/otlp.js +242 -0
  565. package/dist/observability/otlp.js.map +1 -0
  566. package/dist/observability/snapshots.d.ts +43 -0
  567. package/dist/observability/snapshots.js +129 -0
  568. package/dist/observability/snapshots.js.map +1 -0
  569. package/dist/observability/triage.d.ts +49 -0
  570. package/dist/observability/triage.js +284 -0
  571. package/dist/observability/triage.js.map +1 -0
  572. package/dist/providers/adapter.d.ts +50 -0
  573. package/dist/providers/adapter.js +38 -0
  574. package/dist/providers/adapter.js.map +1 -0
  575. package/dist/providers/agent.d.ts +110 -0
  576. package/dist/providers/agent.js +487 -0
  577. package/dist/providers/agent.js.map +1 -0
  578. package/dist/providers/approvals.d.ts +24 -0
  579. package/dist/providers/approvals.js +78 -0
  580. package/dist/providers/approvals.js.map +1 -0
  581. package/dist/providers/attachments.d.ts +20 -0
  582. package/dist/providers/attachments.js +51 -0
  583. package/dist/providers/attachments.js.map +1 -0
  584. package/dist/providers/claude/adapter.d.ts +99 -0
  585. package/dist/providers/claude/adapter.js +790 -0
  586. package/dist/providers/claude/adapter.js.map +1 -0
  587. package/dist/providers/codex/adapter.d.ts +113 -0
  588. package/dist/providers/codex/adapter.js +881 -0
  589. package/dist/providers/codex/adapter.js.map +1 -0
  590. package/dist/providers/codex/instructions.d.ts +11 -0
  591. package/dist/providers/codex/instructions.js +154 -0
  592. package/dist/providers/codex/instructions.js.map +1 -0
  593. package/dist/providers/codex/rpc.d.ts +36 -0
  594. package/dist/providers/codex/rpc.js +104 -0
  595. package/dist/providers/codex/rpc.js.map +1 -0
  596. package/dist/providers/contracts.d.ts +339 -0
  597. package/dist/providers/contracts.js +19 -0
  598. package/dist/providers/contracts.js.map +1 -0
  599. package/dist/providers/directory.d.ts +70 -0
  600. package/dist/providers/directory.js +95 -0
  601. package/dist/providers/directory.js.map +1 -0
  602. package/dist/providers/errors.d.ts +47 -0
  603. package/dist/providers/errors.js +24 -0
  604. package/dist/providers/errors.js.map +1 -0
  605. package/dist/providers/index.d.ts +18 -0
  606. package/dist/providers/index.js +19 -0
  607. package/dist/providers/index.js.map +1 -0
  608. package/dist/providers/ingestion.d.ts +80 -0
  609. package/dist/providers/ingestion.js +330 -0
  610. package/dist/providers/ingestion.js.map +1 -0
  611. package/dist/providers/live.d.ts +22 -0
  612. package/dist/providers/live.js +56 -0
  613. package/dist/providers/live.js.map +1 -0
  614. package/dist/providers/process.d.ts +29 -0
  615. package/dist/providers/process.js +71 -0
  616. package/dist/providers/process.js.map +1 -0
  617. package/dist/providers/reaper.d.ts +33 -0
  618. package/dist/providers/reaper.js +65 -0
  619. package/dist/providers/reaper.js.map +1 -0
  620. package/dist/providers/service.d.ts +87 -0
  621. package/dist/providers/service.js +256 -0
  622. package/dist/providers/service.js.map +1 -0
  623. package/dist/sdk.d.ts +1 -1
  624. package/dist/sdk.js +1 -1
  625. package/dist/sdk.js.map +1 -1
  626. package/dist/types.d.ts +385 -6
  627. package/dist/types.js +1 -0
  628. package/dist/types.js.map +1 -1
  629. package/dist/version.d.ts +7 -0
  630. package/dist/version.js +16 -0
  631. package/dist/version.js.map +1 -0
  632. package/dist/web/app.css +4643 -0
  633. package/dist/web/app.js +22640 -0
  634. package/dist/web/app.js.map +7 -0
  635. package/dist/web/shell.html +39 -0
  636. package/docs/README.md +21 -0
  637. package/docs/SETUP.md +10 -0
  638. package/docs/adapters.md +21 -8
  639. package/docs/brain-continuity.md +186 -0
  640. package/docs/cloud.md +108 -0
  641. package/docs/crews.md +62 -0
  642. package/docs/design-system.md +31 -2
  643. package/docs/integration-notes.md +128 -20
  644. package/docs/loompad.md +105 -0
  645. package/docs/observability.md +220 -0
  646. package/docs/play-store.md +123 -0
  647. package/docs/proposals/agent-teams.md +249 -0
  648. package/docs/proposals/brain-continuity-implementation.md +468 -0
  649. package/docs/proposals/brain-session-continuity.md +623 -0
  650. package/docs/proposals/t3code-port.md +81 -0
  651. package/docs/proposals/t3code-reference.md +95 -0
  652. package/docs/refactoring/AGENT-TEAMS-PROGRESS.md +88 -0
  653. package/docs/refactoring/BOUNDARIES.md +120 -0
  654. package/docs/refactoring/BRAIN-AUDIT.md +176 -0
  655. package/docs/refactoring/BRAIN-NOTES.md +390 -0
  656. package/docs/refactoring/BRAIN-TODO.md +214 -0
  657. package/docs/refactoring/NOTES.md +129 -0
  658. package/docs/refactoring/T3-PORT-NOTES.md +165 -0
  659. package/docs/refactoring/T3-PORT-TODO.md +123 -0
  660. package/docs/refactoring/TODO.md +46 -0
  661. package/docs/refactoring/brain-benchmark-2026-09-28.json +48 -0
  662. package/docs/refactoring/brain-benchmark-audit-2026-09-28.json +48 -0
  663. package/docs/release-notes/v0.2.0.md +35 -0
  664. package/docs/release-notes/v0.2.1.md +59 -0
  665. package/docs/release-notes/v0.2.3.md +77 -0
  666. package/docs/release-notes/v0.2.4.md +128 -0
  667. package/docs/release-notes/v0.2.5.md +153 -0
  668. package/docs/release-notes/v0.2.6.md +62 -0
  669. package/docs/release-notes/v1.0.0.md +120 -0
  670. package/docs/release-notes/v1.0.1.md +77 -0
  671. package/docs/release-notes/v1.1.0.md +71 -0
  672. package/docs/release-notes/v1.1.1.md +38 -0
  673. package/docs/teams-architecture.md +697 -0
  674. package/docs/teams.md +171 -0
  675. package/docs/windows.md +85 -0
  676. package/package.json +22 -9
  677. package/dist/adapters/claude-code.d.ts +0 -26
  678. package/dist/adapters/claude-code.js +0 -209
  679. package/dist/adapters/claude-code.js.map +0 -1
  680. package/dist/adapters/codex.d.ts +0 -51
  681. package/dist/adapters/codex.js +0 -291
  682. package/dist/adapters/codex.js.map +0 -1
  683. package/docs/img/board-linear.png +0 -0
  684. package/docs/img/board-projects.png +0 -0
  685. package/docs/img/brain.png +0 -0
  686. package/docs/img/settings.png +0 -0
  687. package/docs/img/workspace.png +0 -0
@@ -0,0 +1,612 @@
1
+ /**
2
+ * Runner — a member's always-on Loom daemon that takes goals from the team hub
3
+ * (Loom Teams, Phase 5; docs/teams-architecture.md D67–D78).
4
+ *
5
+ * A runner is a normal Loom daemon in runner mode. It registers one of its
6
+ * owner's devices as a runner (D74) and claims jobs from the hub (D71):
7
+ * start a new goal on this machine
8
+ * continue a goal moved here mid-run (its branches on refs/loom/run/<id>/*)
9
+ * fix make the owner's goal PR green while they're away (D69)
10
+ * return hand a goal back to the owner's laptop (D76 "Bring back")
11
+ * land the owner clicked Land on a goal that lives here
12
+ *
13
+ * Each goal gets a fresh clone as its own project (D70). The runner process
14
+ * runs with a scrubbed environment; with Docker, the claimed job runs in a
15
+ * container instead (`loom runner exec`). A job stays claimed, heartbeating a
16
+ * progress snapshot, until its goal lands, fails or moves on (D76).
17
+ */
18
+ import { execFile } from "../core/process.js";
19
+ import fs from "node:fs";
20
+ import os from "node:os";
21
+ import path from "node:path";
22
+ import { writeProjectConfig, loomHome } from "../core/registry.js";
23
+ import { logbook } from "../core/logbook.js";
24
+ import { isTerminal, taskSummary } from "../core/orchestra.js";
25
+ import { openFromTeam, sealForTeam } from "../core/team-crypto.js";
26
+ import { loadPolicy } from "../core/team-policy.js";
27
+ export const DEFAULT_RUNNER = { enabled: false, shared: false, capacity: 1, isolation: "auto", kinds: [] };
28
+ export function runnerConfigPath() {
29
+ return path.join(loomHome(), "runner.json");
30
+ }
31
+ export function readRunnerConfig() {
32
+ try {
33
+ return { ...DEFAULT_RUNNER, ...JSON.parse(fs.readFileSync(runnerConfigPath(), "utf8")) };
34
+ }
35
+ catch {
36
+ return { ...DEFAULT_RUNNER };
37
+ }
38
+ }
39
+ export function writeRunnerConfig(c) {
40
+ fs.mkdirSync(loomHome(), { recursive: true });
41
+ fs.writeFileSync(runnerConfigPath(), JSON.stringify(c, null, 2), { mode: 0o600 });
42
+ }
43
+ /** The runner's GitHub token lives beside its config, 0600, and never goes through the hub (D77). */
44
+ export function runnerTokenPath() {
45
+ return path.join(loomHome(), "runner-token");
46
+ }
47
+ export function readRunnerToken() {
48
+ try {
49
+ return fs.readFileSync(runnerTokenPath(), "utf8").trim() || null;
50
+ }
51
+ catch {
52
+ return null;
53
+ }
54
+ }
55
+ export function writeRunnerToken(token) {
56
+ fs.mkdirSync(loomHome(), { recursive: true });
57
+ fs.writeFileSync(runnerTokenPath(), token.trim() + "\n", { mode: 0o600 });
58
+ }
59
+ // ── the environment agents inherit (D70) ──
60
+ /** Variables agents need: model API keys, GitHub for pushing, Loom's own. */
61
+ const KEEP = /^(ANTHROPIC_API_KEY|OPENAI_API_KEY|GEMINI_API_KEY|GOOGLE_API_KEY|XAI_API_KEY|OPENROUTER_API_KEY|GH_TOKEN|GITHUB_TOKEN|LOOM_[A-Z0-9_]+)$/;
62
+ const SECRETISH = /(SECRET|PASSWORD|PASSWD|PRIVATE|TOKEN|_KEY$|^KEY$|API_KEY|CREDENTIAL|DATABASE_URL|_DSN$|SESSION|COOKIE|AWS_|AZURE_|GCP_|GOOGLE_APPLICATION)/i;
63
+ /**
64
+ * What stays in a runner's environment: everything that isn't secret-shaped,
65
+ * plus the agent keys and GitHub token it's meant to have. A box someone also
66
+ * uses for production work doesn't hand its secrets to agents.
67
+ */
68
+ export function scrubEnv(env) {
69
+ const out = {};
70
+ const removed = [];
71
+ for (const [k, v] of Object.entries(env)) {
72
+ if (KEEP.test(k) || !SECRETISH.test(k))
73
+ out[k] = v;
74
+ else
75
+ removed.push(k);
76
+ }
77
+ return { env: out, removed: removed.sort() };
78
+ }
79
+ /** The container a job runs in when Docker is available (D70). Agent logins mount read-only. */
80
+ export function dockerCommand(opts) {
81
+ const home = opts.home ?? os.homedir();
82
+ const mounts = [".claude", ".codex", ".config/opencode", ".gemini", ".grok"]
83
+ .filter((d) => fs.existsSync(path.join(home, d)))
84
+ .flatMap((d) => ["-v", `${path.join(home, d)}:/home/loom/${d}:ro`]);
85
+ return [
86
+ "run", "--rm", "--name", `loom-job-${opts.jobId}`,
87
+ "-v", `${opts.jobHome}:/loom`,
88
+ ...mounts,
89
+ "-e", "LOOM_HOME=/loom",
90
+ ...["ANTHROPIC_API_KEY", "OPENAI_API_KEY", "GEMINI_API_KEY", "XAI_API_KEY", "GH_TOKEN"].flatMap((k) => ["-e", k]),
91
+ opts.image,
92
+ "loom", "runner", "exec", "--job", opts.jobId, "--team", opts.teamId,
93
+ ];
94
+ }
95
+ export function progressOf(run, fallbackGoal = "") {
96
+ return {
97
+ runId: run?.id ?? null,
98
+ goal: (run?.goal ?? fallbackGoal).split("\n")[0].slice(0, 200),
99
+ status: run?.status ?? "preparing",
100
+ tasks: run ? run.tasks.map(taskSummary) : [],
101
+ costUsd: run ? Math.round(run.costUsd * 100) / 100 : 0,
102
+ landing: run?.landing ?? null,
103
+ ...(run?.question ? { question: run.question.slice(0, 300) } : {}),
104
+ at: Date.now(),
105
+ };
106
+ }
107
+ export function crewProgress(id, name, goal, runId) {
108
+ return { runId, goal: (goal?.text ?? "").split("\n")[0].slice(0, 200), status: goal?.status ?? "preparing",
109
+ tasks: goal?.cards.map(card => ({ id: card.id, title: card.title, status: card.stage, worker: card.builder })) ?? [],
110
+ costUsd: goal?.costUsd ?? 0, landing: null, crew: { id, name, ...(goal?.pr ? { pr: goal.pr.url } : {}) },
111
+ ...(goal?.question ? { question: goal.question.text.slice(0, 300) } : {}), at: Date.now() };
112
+ }
113
+ const TICK_MS = 15_000;
114
+ const BEAT_MS = 60_000;
115
+ /** A goal of the owner's needing a CI fix, owner offline this long: the runner takes it (D69). */
116
+ const OWNER_AWAY_MS = 15 * 60_000;
117
+ function git(args, cwd) {
118
+ return new Promise((resolve, reject) => {
119
+ execFile("git", args, { cwd, timeout: 300_000, maxBuffer: 16 * 1024 * 1024 }, (err, out, errOut) => err ? reject(new Error(String(errOut || err.message).trim())) : resolve(String(out)));
120
+ });
121
+ }
122
+ export class Runner {
123
+ deps;
124
+ active = new Map();
125
+ timer = null;
126
+ claiming = false;
127
+ registered = null;
128
+ autoFixed = new Set();
129
+ lastError = null;
130
+ constructor(deps) {
131
+ this.deps = deps;
132
+ }
133
+ get running() {
134
+ return Boolean(this.timer);
135
+ }
136
+ async start() {
137
+ if (this.timer)
138
+ return;
139
+ if (this.deps.exec)
140
+ return this.execOne(this.deps.exec);
141
+ await this.register();
142
+ this.timer = setInterval(() => void this.tick().catch((e) => this.fail(e)), TICK_MS);
143
+ this.timer.unref?.();
144
+ void this.tick().catch((e) => this.fail(e));
145
+ }
146
+ async stop() {
147
+ if (this.timer)
148
+ clearInterval(this.timer);
149
+ this.timer = null;
150
+ }
151
+ fail(e) {
152
+ this.lastError = e instanceof Error ? e.message : String(e);
153
+ logbook.warn("runner", "runner tick failed", this.lastError);
154
+ }
155
+ async register() {
156
+ const hub = this.deps.hub();
157
+ const device = this.deps.deviceId();
158
+ if (!hub || !device)
159
+ return null;
160
+ const c = this.deps.config();
161
+ this.registered = await hub.registerRunner({ deviceId: device, kinds: await this.deps.kinds(), shared: c.shared, capacity: c.capacity });
162
+ return this.registered;
163
+ }
164
+ /** A hub event: a job appeared, changed, or was cancelled. */
165
+ onJob(teamId, job) {
166
+ const mine = this.active.get(job.id);
167
+ if (mine && job.state === "cancelled")
168
+ void this.cancel(mine);
169
+ if (job.state === "queued")
170
+ void this.tick().catch((e) => this.fail(e));
171
+ void teamId;
172
+ }
173
+ /** Claim what we can, beat for what we hold, finish what's done, look for goals to rescue. */
174
+ async tick() {
175
+ const hub = this.deps.hub();
176
+ const device = this.deps.deviceId();
177
+ if (!hub || !device)
178
+ return;
179
+ if (!this.registered)
180
+ await this.register();
181
+ for (const a of [...this.active.values()])
182
+ await this.checkDone(a).catch((e) => this.fail(e));
183
+ for (const a of this.active.values()) {
184
+ if (Date.now() - a.lastBeat > BEAT_MS)
185
+ await this.beat(a).catch(() => { });
186
+ }
187
+ await this.rescueOwnGoals().catch((e) => this.fail(e));
188
+ if (this.claiming)
189
+ return;
190
+ this.claiming = true;
191
+ try {
192
+ const cap = this.deps.config().capacity;
193
+ for (const teamId of this.deps.teams()) {
194
+ for (;;) {
195
+ const job = await hub.claimJob(teamId, device, this.goalCount() >= cap);
196
+ if (!job)
197
+ break;
198
+ await this.execute(teamId, job);
199
+ }
200
+ }
201
+ }
202
+ finally {
203
+ this.claiming = false;
204
+ }
205
+ }
206
+ /** Goals being worked on (return/land jobs are instant and don't count). */
207
+ goalCount() {
208
+ return [...this.active.values()].filter((a) => a.job.kind === "start" || a.job.kind === "continue" || a.job.kind === "fix").length;
209
+ }
210
+ /** Inside a container: the dispatcher claimed this job; run it here, then exit (D70). */
211
+ async execOne(x) {
212
+ const hub = this.deps.hub();
213
+ if (!hub)
214
+ throw new Error("runner exec: not signed in");
215
+ const job = (await hub.jobs(x.teamId)).find((j) => j.id === x.jobId);
216
+ if (!job || job.state !== "claimed")
217
+ throw new Error(`runner exec: job ${x.jobId} isn't claimed`);
218
+ await this.execute(x.teamId, job, { inline: true });
219
+ this.timer = setInterval(() => {
220
+ void this.tick().catch((e) => this.fail(e));
221
+ if (!this.active.has(x.jobId)) {
222
+ if (this.timer)
223
+ clearInterval(this.timer);
224
+ this.timer = null;
225
+ x.done(true);
226
+ }
227
+ }, TICK_MS);
228
+ }
229
+ dockerOk = null;
230
+ /** D70: Docker per goal when it's running (auto), always (docker), or never (inline). */
231
+ async useDocker() {
232
+ const mode = this.deps.config().isolation;
233
+ if (mode === "inline")
234
+ return false;
235
+ if (!this.dockerOk || Date.now() - this.dockerOk.at > 5 * 60_000) {
236
+ const ok = await new Promise((resolve) => execFile("docker", ["info", "--format", "{{.ServerVersion}}"], { timeout: 10_000 }, (err) => resolve(!err)));
237
+ this.dockerOk = { ok, at: Date.now() };
238
+ }
239
+ if (mode === "docker" && !this.dockerOk.ok)
240
+ throw new Error("isolation is docker, but Docker isn't running on this runner");
241
+ return this.dockerOk.ok;
242
+ }
243
+ /** Hand a claimed goal to its own container; it heartbeats and finishes the job itself. */
244
+ async dispatchDocker(teamId, job) {
245
+ const jobHome = path.join(loomHome(), "runner", "jobs", job.id);
246
+ fs.mkdirSync(jobHome, { recursive: true, mode: 0o700 });
247
+ fs.copyFileSync(this.deps.statePath(), path.join(jobHome, "team.json"));
248
+ if (readRunnerToken())
249
+ fs.copyFileSync(runnerTokenPath(), path.join(jobHome, "runner-token"));
250
+ fs.writeFileSync(path.join(jobHome, "runner.json"), JSON.stringify({ ...this.deps.config(), enabled: false, isolation: "inline" }));
251
+ const args = dockerCommand({ jobId: job.id, teamId, image: this.deps.config().image ?? "loom-runner", jobHome });
252
+ const a = { job, teamId, payload: {}, rt: null, dir: jobHome, runId: null, lastBeat: Date.now() };
253
+ this.active.set(job.id, a);
254
+ execFile("docker", args, { maxBuffer: 64 * 1024 * 1024 }, (err) => {
255
+ this.active.delete(job.id);
256
+ fs.rmSync(jobHome, { recursive: true, force: true });
257
+ if (err) {
258
+ const hub = this.deps.hub();
259
+ const device = this.deps.deviceId();
260
+ if (hub && device)
261
+ void hub.finishJob(teamId, job.id, device, { state: "failed", error: `container: ${err.message.slice(0, 300)}` }).catch(() => { });
262
+ }
263
+ });
264
+ }
265
+ /** Run one claimed job. */
266
+ async execute(teamId, job, opts = {}) {
267
+ const payload = (openFromTeam(this.deps.keys(teamId), job.sealed) ?? {});
268
+ const a = { job, teamId, payload, rt: null, dir: null, runId: null, lastBeat: 0 };
269
+ try {
270
+ if (job.kind === "return" || job.kind === "land")
271
+ return await this.instant(a);
272
+ if (!opts.inline && (await this.useDocker()))
273
+ return await this.dispatchDocker(teamId, job);
274
+ this.active.set(job.id, a);
275
+ await this.beat(a);
276
+ await this.prepare(a);
277
+ const rt = a.rt;
278
+ // Someone else's goal: only when the repo's reviewed policy allows shared runners (D68).
279
+ if (job.userId !== this.deps.userId()) {
280
+ const policy = await loadPolicy(rt.info.dir);
281
+ if (!policy.runners.shared)
282
+ throw new Error(`${job.repo}'s loom.team.json doesn't allow shared runners (runners.shared)`);
283
+ }
284
+ if (payload.crew) {
285
+ if (job.kind !== "start" || !payload.goal)
286
+ throw new Error("runner crews need a start job and goal");
287
+ // Only map the sender's roster IDs to locally installed kinds. Sender
288
+ // executable paths, options, hooks and permission settings are ignored.
289
+ const roster = rt.config.agents;
290
+ const teammates = payload.crew.teammates.map(seat => {
291
+ const kind = payload.crew.agents.find(agent => agent.id === seat.agent)?.kind;
292
+ const agent = roster.find(agent => agent.kind === kind);
293
+ if (!agent)
294
+ throw new Error(`runner has no installed ${kind ?? seat.agent} agent`);
295
+ return { id: seat.id, role: seat.role, agent: agent.id, ...(seat.charter ? { charter: seat.charter } : {}) };
296
+ });
297
+ const crew = rt.crews.create({ name: payload.crew.name, teammates, planApproval: true, play: payload.crew.play,
298
+ maxParallel: payload.crew.maxParallel, budgetUsd: payload.crew.budgetUsd });
299
+ a.crewId = crew.id;
300
+ const goal = await rt.crews.goal(crew.id, payload.goal, payload.crew.play);
301
+ a.runId = rt.crews.coordination(crew.id).id;
302
+ a.unsub = rt.log.onEvent(event => {
303
+ if (event.kind === "crew" && event.payload.crewId === crew.id)
304
+ void this.beat(a).then(() => this.checkDone(a)).catch(error => this.fail(error));
305
+ });
306
+ void goal;
307
+ await this.beat(a);
308
+ return;
309
+ }
310
+ let run;
311
+ if (job.kind === "continue") {
312
+ if (!a.payload.record)
313
+ throw new Error("a continue job needs the run record");
314
+ run = await rt.orchestra.importRun(a.payload.record, { from: a.payload.from ?? job.github });
315
+ }
316
+ else if (job.kind === "fix") {
317
+ if (!a.payload.pr || !a.payload.branch)
318
+ throw new Error("a fix job needs the PR and its branch");
319
+ run = await rt.orchestra.start({
320
+ goal: a.payload.goal ?? `Make PR #${a.payload.pr} green: its required checks are failing. Find the cause in \`gh pr checks ${a.payload.pr}\` / \`gh run view <id> --log-failed\`, fix it with the smallest change, and finish.`,
321
+ ...(a.payload.orchestrator ? { orchestrator: a.payload.orchestrator } : {}),
322
+ ...(a.payload.workers?.length ? { workers: a.payload.workers } : {}),
323
+ from: { branch: a.payload.branch, pr: a.payload.pr, url: a.payload.url ?? "", owner: job.github, ...(a.payload.ownerRunId ? { ownerRunId: a.payload.ownerRunId } : {}) },
324
+ });
325
+ }
326
+ else {
327
+ if (!a.payload.goal)
328
+ throw new Error("a start job needs a goal");
329
+ run = await rt.orchestra.start({
330
+ goal: a.payload.goal,
331
+ ...(a.payload.orchestrator ? { orchestrator: a.payload.orchestrator } : {}),
332
+ ...(a.payload.workers?.length ? { workers: a.payload.workers } : {}),
333
+ ...(a.payload.plan ? { plan: true } : {}),
334
+ });
335
+ }
336
+ a.runId = run.id;
337
+ let debounce = null;
338
+ // a heartbeat the hub didn't take (it was slow, the network blinked) is sent again
339
+ // shortly — otherwise the job's progress (its PR, its status) waits for the next
340
+ // event or the minute's beat, and the team sees a goal stuck short of where it is
341
+ const report = (delayMs) => {
342
+ if (debounce)
343
+ return;
344
+ debounce = setTimeout(() => {
345
+ debounce = null;
346
+ void this.beat(a).then(() => this.checkDone(a)).catch(() => { if (this.active.has(a.job.id))
347
+ report(5_000); });
348
+ }, delayMs);
349
+ debounce.unref?.();
350
+ };
351
+ a.unsub = rt.log.onEvent((e) => {
352
+ if (e.kind !== "orchestra" || e.payload.runId !== run.id)
353
+ return;
354
+ report(1000);
355
+ });
356
+ await this.beat(a);
357
+ }
358
+ catch (e) {
359
+ await this.finish(a, "failed", undefined, e instanceof Error ? e.message : String(e));
360
+ }
361
+ }
362
+ /** return and land: act on a goal this runner holds, answer at once. */
363
+ async instant(a) {
364
+ const hub = this.deps.hub();
365
+ const device = this.deps.deviceId();
366
+ const holder = [...this.active.values()].find((x) => x.runId && x.runId === a.payload.runId);
367
+ try {
368
+ if (!holder?.rt || !holder.runId)
369
+ throw new Error(`this runner doesn't hold goal ${a.payload.runId ?? "?"}`);
370
+ if (holder.job.userId !== a.job.userId)
371
+ throw new Error("only the goal's owner can control this runner job");
372
+ if (a.payload.crewAction) {
373
+ const action = a.payload.crewAction;
374
+ if (!holder.crewId || action.jobId !== holder.job.id)
375
+ throw new Error("crew job does not match this held goal");
376
+ const crews = holder.rt.crews, id = holder.crewId;
377
+ if (action.action === "approve")
378
+ crews.approve(id);
379
+ else if (action.action === "say")
380
+ await crews.say(id, action.text ?? "", action.to);
381
+ else if (action.action === "pause")
382
+ crews.pause(id);
383
+ else if (action.action === "resume")
384
+ crews.resume(id);
385
+ else if (action.action === "stop")
386
+ await crews.stop(id);
387
+ else if (action.action === "pick")
388
+ await crews.pick(id, action.to ?? "");
389
+ else
390
+ throw new Error("unsupported crew control");
391
+ await hub.finishJob(a.teamId, a.job.id, device, { state: "done" });
392
+ await this.beat(holder);
393
+ return;
394
+ }
395
+ if (holder.crewId)
396
+ throw new Error("crew PRs require human review and merge; use a crew control action");
397
+ if (a.job.kind === "land") {
398
+ const l = await this.deps.landing(holder.rt).land(holder.runId);
399
+ await hub.finishJob(a.teamId, a.job.id, device, { state: "done", result: this.seal(a.teamId, { landing: l }) });
400
+ await this.beat(holder);
401
+ return;
402
+ }
403
+ const { record } = await holder.rt.orchestra.moveOut(holder.runId, a.payload.from ?? `${a.job.github}'s laptop`);
404
+ await hub.finishJob(a.teamId, a.job.id, device, { state: "done", result: this.seal(a.teamId, { record }) });
405
+ await this.finish(holder, "done", { moved: true, to: a.payload.from ?? "the owner's laptop" });
406
+ }
407
+ catch (e) {
408
+ await hub.finishJob(a.teamId, a.job.id, device, { state: "failed", error: e instanceof Error ? e.message : String(e) }).catch(() => { });
409
+ }
410
+ }
411
+ /** A fresh clone of the job's repo, opened as its own project (D70). */
412
+ async prepare(a) {
413
+ const base = path.join(loomHome(), "runner", "work", a.job.id);
414
+ const name = a.job.repo.split("/")[1];
415
+ const dir = path.join(base, name);
416
+ fs.mkdirSync(base, { recursive: true });
417
+ const url = this.deps.cloneUrl?.(a.job.repo) ?? `https://github.com/${a.job.repo}.git`;
418
+ await git(["clone", "-q", url, dir], base);
419
+ await git(["config", "loom.repo", a.job.repo], dir);
420
+ await git(["config", "user.name", `${this.deps.github() ?? "loom"} (runner)`], dir);
421
+ await git(["config", "user.email", `${this.deps.github() ?? "loom"}@users.noreply.github.com`], dir);
422
+ await git(["remote", "set-head", "origin", "--auto"], dir).catch(() => { });
423
+ if (readRunnerToken()) {
424
+ // push with the runner's own fine-grained token (D77), never baked into the remote URL
425
+ await git(["config", "credential.helper", `!f() { echo username=x-access-token; echo "password=$(cat '${runnerTokenPath()}')"; }; f`], dir);
426
+ }
427
+ const kinds = await this.deps.kinds();
428
+ const c = this.deps.config();
429
+ writeProjectConfig(dir, {
430
+ name: `${name} · runner`,
431
+ agents: kinds.map((k, i) => ({ id: k, kind: k, role: i === 0 ? "orchestrator" : "worker" })),
432
+ brain: { extractor: "off", ...(a.payload.nativeCrew ? { continuity: true } : {}) },
433
+ git: { delivery: "pr" },
434
+ ...{ runner: { job: a.job.id, owner: a.job.github, label: c.label ?? os.hostname() } },
435
+ });
436
+ a.dir = base;
437
+ a.rt = await this.deps.openProject(dir, `${name} (runner · ${a.job.github})`);
438
+ a.rt.runnerMode = true;
439
+ await this.deps.share(a.rt, a.teamId);
440
+ a.rt.teamPolicy = await loadPolicy(dir);
441
+ }
442
+ seal(teamId, value) {
443
+ const keys = this.deps.keys(teamId);
444
+ return sealForTeam(keys[keys.length - 1], value);
445
+ }
446
+ /** Heartbeat the job with a progress snapshot. */
447
+ async beat(a) {
448
+ const hub = this.deps.hub();
449
+ const device = this.deps.deviceId();
450
+ if (!hub || !device)
451
+ return;
452
+ const run = a.runId ? a.rt?.orchestra.get(a.runId) : undefined;
453
+ const crew = a.crewId ? a.rt?.crews.get(a.crewId) : undefined;
454
+ const progress = crew ? crewProgress(crew.id, crew.name, crew.state.goal, a.runId) : progressOf(run, a.payload.goal ?? a.payload.record?.goal);
455
+ await hub.heartbeatJob(a.teamId, a.job.id, device, this.seal(a.teamId, progress));
456
+ a.lastBeat = Date.now();
457
+ }
458
+ /** D76: a job is done when its goal lands, fails, or moves on. */
459
+ async checkDone(a) {
460
+ if (!a.rt || !a.runId || a.finishing)
461
+ return;
462
+ if (a.crewId) {
463
+ const crew = a.rt.crews.get(a.crewId), goal = crew.state.goal;
464
+ if (!goal || crew.busy)
465
+ return;
466
+ if (["failed", "stopped"].includes(goal.status))
467
+ return this.finish(a, "failed", crewProgress(crew.id, crew.name, goal, a.runId), goal.error ?? goal.status);
468
+ if (goal.status !== "completed")
469
+ return;
470
+ a.finishing = true;
471
+ if (goal.recovery || goal.deliveryError)
472
+ return this.finish(a, "failed", undefined, goal.recovery ?? goal.deliveryError);
473
+ if (!goal.pr) {
474
+ try {
475
+ await a.rt.crews.deliver(crew.id);
476
+ }
477
+ catch (error) {
478
+ return this.finish(a, "failed", undefined, error.message);
479
+ }
480
+ }
481
+ return this.finish(a, "done", crewProgress(crew.id, crew.name, goal, a.runId));
482
+ }
483
+ const run = a.rt.orchestra.get(a.runId);
484
+ if (!run)
485
+ return;
486
+ const l = run.landing;
487
+ if (run.status === "moved")
488
+ return this.finish(a, "done", { moved: true });
489
+ if (run.status === "failed" || run.status === "aborted")
490
+ return this.finish(a, "failed", progressOf(run), run.error ?? run.status);
491
+ if (!isTerminal(run.status))
492
+ return;
493
+ if (run.from && l?.returned)
494
+ return this.finish(a, "done", progressOf(run)); // a fix, handed back
495
+ if (!l && run.delivered && !run.delivered.prUrl)
496
+ return this.finish(a, "done", progressOf(run)); // delivered without a PR
497
+ if (!l && run.deliveryError)
498
+ return this.finish(a, "failed", progressOf(run), run.deliveryError);
499
+ if (l && (l.state === "merged" || l.state === "closed"))
500
+ return this.finish(a, "done", progressOf(run));
501
+ }
502
+ async finish(a, state, result, error) {
503
+ a.finishing = true;
504
+ const hub = this.deps.hub();
505
+ const device = this.deps.deviceId();
506
+ this.active.delete(a.job.id);
507
+ a.unsub?.();
508
+ if (hub && device) {
509
+ await hub
510
+ .finishJob(a.teamId, a.job.id, device, { state, ...(result ? { result: this.seal(a.teamId, result) } : {}), ...(error ? { error } : {}) })
511
+ .catch((e) => logbook.warn("runner", `couldn't finish job ${a.job.id}`, String(e)));
512
+ }
513
+ await this.cleanup(a);
514
+ }
515
+ /** The goal is over here: its branches are pushed; the clone goes (D70). */
516
+ async cleanup(a) {
517
+ if (a.crewId && a.rt) {
518
+ const goal = a.rt.crews.get(a.crewId).state.goal;
519
+ if (goal && (!goal.pr || goal.status !== "completed" || a.payload.nativeCrew)) {
520
+ // Failed/uncertain turns may leave uncommitted edits and card refs.
521
+ // Keep their whole repository and native bindings for inspection.
522
+ if (!goal.pr)
523
+ await git(["push", "-q", "origin", `${goal.branch}:refs/loom/crew/${goal.id}/main`], goal.dir).catch(() => { });
524
+ await this.deps.closeProject(a.rt);
525
+ return;
526
+ }
527
+ await a.rt.crews.remove(a.crewId);
528
+ }
529
+ const run = a.runId ? a.rt?.orchestra.get(a.runId) : undefined;
530
+ if (run && a.rt && run.status === "failed" && !run.delivered) {
531
+ // a failed goal's work isn't thrown away: keep it on its hidden ref for whoever looks next
532
+ await git(["push", "-q", "-f", "origin", `${run.branch}:refs/loom/run/${run.id}/main`], run.dir).catch(() => { });
533
+ }
534
+ if (a.rt)
535
+ await this.deps.closeProject(a.rt).catch(() => { });
536
+ // Windows can retain a child's working-directory handle briefly after
537
+ // shutdown. Retry transient locks without blocking the daemon's event loop.
538
+ if (a.dir)
539
+ await fs.promises.rm(a.dir, { recursive: true, force: true, maxRetries: 10, retryDelay: 100 });
540
+ }
541
+ async cancel(a) {
542
+ if (a.rt && a.crewId)
543
+ await a.rt.crews.stop(a.crewId);
544
+ else if (a.rt && a.runId)
545
+ await a.rt.orchestra.abort(a.runId, "the job was cancelled").catch(() => { });
546
+ this.active.delete(a.job.id);
547
+ a.unsub?.();
548
+ await this.cleanup(a);
549
+ }
550
+ /**
551
+ * D69: the owner's goal needs a CI fix and the owner has been away 15+
552
+ * minutes — ask for it as a job for this runner, like an Adopt.
553
+ */
554
+ async rescueOwnGoals() {
555
+ const hub = this.deps.hub();
556
+ const me = this.deps.github();
557
+ const device = this.deps.deviceId();
558
+ if (!hub || !me || !device)
559
+ return;
560
+ for (const teamId of this.deps.teams()) {
561
+ const feed = this.deps.feed(teamId);
562
+ const presence = this.deps.presence(teamId);
563
+ const lastSeen = Math.max(0, ...presence.filter((p) => p.github === me && p.deviceId !== device).map((p) => p.ts));
564
+ for (const e of feed) {
565
+ if (e.type !== "goal_needs_someone" || e.github !== me || e.deviceId === device)
566
+ continue;
567
+ const pr = Number(e.meta.pr);
568
+ const key = `${teamId}:${e.repo}#${pr}`;
569
+ if (!pr || this.autoFixed.has(key))
570
+ continue;
571
+ const later = feed.filter((x) => x.id > e.id && Number(x.meta.pr ?? x.meta.number) === pr);
572
+ if (later.some((x) => ["goal_adopted", "goal_returned", "goal_landed", "pr_merged", "pr_closed"].includes(x.type)))
573
+ continue;
574
+ if (Date.now() - Math.max(lastSeen, e.ts) < (this.deps.awayMs ?? OWNER_AWAY_MS))
575
+ continue;
576
+ this.autoFixed.add(key);
577
+ await hub.createJob(teamId, {
578
+ repo: e.repo,
579
+ kind: "fix",
580
+ target: device,
581
+ deviceId: device,
582
+ sealed: this.seal(teamId, {
583
+ pr,
584
+ url: String(e.meta.url ?? ""),
585
+ branch: String(e.meta.branch ?? ""),
586
+ ownerRunId: String(e.meta.runId ?? ""),
587
+ from: "your runner (you were away)",
588
+ }),
589
+ });
590
+ logbook.info("runner", `taking PR #${pr} while ${me} is away`);
591
+ }
592
+ }
593
+ }
594
+ status() {
595
+ return {
596
+ running: this.running,
597
+ registered: this.registered,
598
+ config: this.deps.config(),
599
+ token: Boolean(readRunnerToken()),
600
+ active: [...this.active.values()].map((a) => ({
601
+ jobId: a.job.id,
602
+ kind: a.job.kind,
603
+ repo: a.job.repo,
604
+ owner: a.job.github,
605
+ runId: a.runId,
606
+ project: a.rt?.info.id ?? null,
607
+ })),
608
+ lastError: this.lastError,
609
+ };
610
+ }
611
+ }
612
+ //# sourceMappingURL=runner.js.map