toolroll 0.5.0

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 (626) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +1113 -0
  3. package/THIRD_PARTY_NOTICES.md +449 -0
  4. package/dist/accent-colors.d.ts +48 -0
  5. package/dist/accent-colors.js +122 -0
  6. package/dist/action-ledger.d.ts +26 -0
  7. package/dist/action-ledger.js +75 -0
  8. package/dist/agent-fence.d.ts +42 -0
  9. package/dist/agent-fence.js +183 -0
  10. package/dist/agentconfig.d.ts +216 -0
  11. package/dist/agentconfig.js +454 -0
  12. package/dist/api-tokens.d.ts +16 -0
  13. package/dist/api-tokens.js +27 -0
  14. package/dist/approval-policy.d.ts +69 -0
  15. package/dist/approval-policy.js +137 -0
  16. package/dist/approval-rules-ui.d.ts +25 -0
  17. package/dist/approval-rules-ui.js +39 -0
  18. package/dist/assignment-adapters.d.ts +180 -0
  19. package/dist/assignment-adapters.js +239 -0
  20. package/dist/assignment-brief.d.ts +71 -0
  21. package/dist/assignment-brief.js +129 -0
  22. package/dist/assignment-delivery.d.ts +56 -0
  23. package/dist/assignment-delivery.js +160 -0
  24. package/dist/assignment-presentation.d.ts +26 -0
  25. package/dist/assignment-presentation.js +80 -0
  26. package/dist/assignment-status.d.ts +62 -0
  27. package/dist/assignment-status.js +152 -0
  28. package/dist/assignment-ui.d.ts +71 -0
  29. package/dist/assignment-ui.js +103 -0
  30. package/dist/assignment.d.ts +222 -0
  31. package/dist/assignment.js +399 -0
  32. package/dist/attest.d.ts +56 -0
  33. package/dist/attest.js +153 -0
  34. package/dist/backend.d.ts +97 -0
  35. package/dist/backend.js +166 -0
  36. package/dist/backup-ui.d.ts +22 -0
  37. package/dist/backup-ui.js +59 -0
  38. package/dist/backup.d.ts +84 -0
  39. package/dist/backup.js +421 -0
  40. package/dist/beads.d.ts +34 -0
  41. package/dist/beads.js +135 -0
  42. package/dist/bin.d.ts +2 -0
  43. package/dist/bin.js +22 -0
  44. package/dist/board.d.ts +150 -0
  45. package/dist/board.js +210 -0
  46. package/dist/boot-identity.d.ts +63 -0
  47. package/dist/boot-identity.js +99 -0
  48. package/dist/browser/THIRD_PARTY_NOTICES.txt +2295 -0
  49. package/dist/browser/workspace.css +4 -0
  50. package/dist/browser/workspace.js +225 -0
  51. package/dist/browser-crew.d.ts +11 -0
  52. package/dist/browser-crew.js +50 -0
  53. package/dist/browser-shell.d.ts +8 -0
  54. package/dist/browser-shell.js +60 -0
  55. package/dist/browser-workspace.d.ts +780 -0
  56. package/dist/browser-workspace.js +47 -0
  57. package/dist/budget-alerts.d.ts +19 -0
  58. package/dist/budget-alerts.js +46 -0
  59. package/dist/builder.d.ts +370 -0
  60. package/dist/builder.js +3262 -0
  61. package/dist/capscan.d.ts +35 -0
  62. package/dist/capscan.js +113 -0
  63. package/dist/chat-acceptance.d.ts +71 -0
  64. package/dist/chat-acceptance.js +120 -0
  65. package/dist/chat-actions.d.ts +265 -0
  66. package/dist/chat-actions.js +1273 -0
  67. package/dist/chat-channel.d.ts +109 -0
  68. package/dist/chat-channel.js +652 -0
  69. package/dist/chat-continuity.d.ts +32 -0
  70. package/dist/chat-continuity.js +317 -0
  71. package/dist/chat-controls.d.ts +113 -0
  72. package/dist/chat-controls.js +53 -0
  73. package/dist/chat-delivery-state.d.ts +159 -0
  74. package/dist/chat-delivery-state.js +309 -0
  75. package/dist/chat-delivery.d.ts +38 -0
  76. package/dist/chat-delivery.js +693 -0
  77. package/dist/chat-display.d.ts +1 -0
  78. package/dist/chat-display.js +18 -0
  79. package/dist/chat-evidence.d.ts +123 -0
  80. package/dist/chat-evidence.js +207 -0
  81. package/dist/chat-flow.d.ts +44 -0
  82. package/dist/chat-flow.js +215 -0
  83. package/dist/chat-inbox.d.ts +57 -0
  84. package/dist/chat-inbox.js +123 -0
  85. package/dist/chat-polish.d.ts +16 -0
  86. package/dist/chat-polish.js +135 -0
  87. package/dist/chat-review.d.ts +46 -0
  88. package/dist/chat-review.js +98 -0
  89. package/dist/chat-rooms.d.ts +123 -0
  90. package/dist/chat-rooms.js +216 -0
  91. package/dist/chat-task-actions.d.ts +44 -0
  92. package/dist/chat-task-actions.js +79 -0
  93. package/dist/check-progress.d.ts +51 -0
  94. package/dist/check-progress.js +290 -0
  95. package/dist/child-database.d.ts +12 -0
  96. package/dist/child-database.js +30 -0
  97. package/dist/claim.d.ts +688 -0
  98. package/dist/claim.js +1740 -0
  99. package/dist/cli.d.ts +137 -0
  100. package/dist/cli.js +1461 -0
  101. package/dist/codex-limits.d.ts +15 -0
  102. package/dist/codex-limits.js +145 -0
  103. package/dist/coding-context.d.ts +55 -0
  104. package/dist/coding-context.js +92 -0
  105. package/dist/coding-handoff.d.ts +57 -0
  106. package/dist/coding-handoff.js +277 -0
  107. package/dist/coding-provider.d.ts +72 -0
  108. package/dist/coding-provider.js +424 -0
  109. package/dist/coding-shipping-ui.d.ts +4 -0
  110. package/dist/coding-shipping-ui.js +12 -0
  111. package/dist/coding-types.d.ts +62 -0
  112. package/dist/coding-types.js +1 -0
  113. package/dist/coding-ui.d.ts +17 -0
  114. package/dist/coding-ui.js +364 -0
  115. package/dist/coding-update.d.ts +15 -0
  116. package/dist/coding-update.js +149 -0
  117. package/dist/coding-workspace.d.ts +124 -0
  118. package/dist/coding-workspace.js +768 -0
  119. package/dist/container-state.d.ts +5 -0
  120. package/dist/container-state.js +62 -0
  121. package/dist/containment.d.ts +197 -0
  122. package/dist/containment.js +559 -0
  123. package/dist/contest.d.ts +318 -0
  124. package/dist/contest.js +753 -0
  125. package/dist/control-setup.d.ts +35 -0
  126. package/dist/control-setup.js +40 -0
  127. package/dist/control-ui.d.ts +29 -0
  128. package/dist/control-ui.js +53 -0
  129. package/dist/controller-service.d.ts +1 -0
  130. package/dist/controller-service.js +19 -0
  131. package/dist/controller-supervisor.d.ts +30 -0
  132. package/dist/controller-supervisor.js +104 -0
  133. package/dist/converse.d.ts +304 -0
  134. package/dist/converse.js +849 -0
  135. package/dist/coordinator-proposals.d.ts +29 -0
  136. package/dist/coordinator-proposals.js +137 -0
  137. package/dist/coordinator.d.ts +186 -0
  138. package/dist/coordinator.js +447 -0
  139. package/dist/credentials-ui.d.ts +25 -0
  140. package/dist/credentials-ui.js +48 -0
  141. package/dist/daemon.d.ts +209 -0
  142. package/dist/daemon.js +604 -0
  143. package/dist/decision.d.ts +120 -0
  144. package/dist/decision.js +388 -0
  145. package/dist/demo.d.ts +55 -0
  146. package/dist/demo.js +992 -0
  147. package/dist/desktop-access.d.ts +28 -0
  148. package/dist/desktop-access.js +88 -0
  149. package/dist/desktop-bundle.d.ts +16 -0
  150. package/dist/desktop-bundle.js +74 -0
  151. package/dist/desktop-host.d.ts +54 -0
  152. package/dist/desktop-host.js +508 -0
  153. package/dist/desktop-update-gate.d.ts +12 -0
  154. package/dist/desktop-update-gate.js +91 -0
  155. package/dist/desktop-update-recovery.d.ts +14 -0
  156. package/dist/desktop-update-recovery.js +243 -0
  157. package/dist/desktop-update.d.ts +110 -0
  158. package/dist/desktop-update.js +740 -0
  159. package/dist/discord-api.d.ts +20 -0
  160. package/dist/discord-api.js +138 -0
  161. package/dist/discord-chat.d.ts +16 -0
  162. package/dist/discord-chat.js +381 -0
  163. package/dist/discord-settings.d.ts +7 -0
  164. package/dist/discord-settings.js +75 -0
  165. package/dist/discord.d.ts +10 -0
  166. package/dist/discord.js +169 -0
  167. package/dist/discover.d.ts +75 -0
  168. package/dist/discover.js +150 -0
  169. package/dist/dispatch.d.ts +100 -0
  170. package/dist/dispatch.js +311 -0
  171. package/dist/dispose.d.ts +161 -0
  172. package/dist/dispose.js +803 -0
  173. package/dist/email-settings.d.ts +34 -0
  174. package/dist/email-settings.js +50 -0
  175. package/dist/envelope.d.ts +47 -0
  176. package/dist/envelope.js +105 -0
  177. package/dist/evidence-pack.d.ts +185 -0
  178. package/dist/evidence-pack.js +258 -0
  179. package/dist/evidence.d.ts +345 -0
  180. package/dist/evidence.js +782 -0
  181. package/dist/exec.d.ts +285 -0
  182. package/dist/exec.js +1841 -0
  183. package/dist/exhaustion.d.ts +85 -0
  184. package/dist/exhaustion.js +141 -0
  185. package/dist/export-ui.d.ts +4 -0
  186. package/dist/export-ui.js +17 -0
  187. package/dist/export.d.ts +48 -0
  188. package/dist/export.js +305 -0
  189. package/dist/flow-actions.d.ts +77 -0
  190. package/dist/flow-actions.js +203 -0
  191. package/dist/flow-code.d.ts +50 -0
  192. package/dist/flow-code.js +159 -0
  193. package/dist/flow-draft.d.ts +38 -0
  194. package/dist/flow-draft.js +80 -0
  195. package/dist/flow-engine.d.ts +66 -0
  196. package/dist/flow-engine.js +416 -0
  197. package/dist/flow-insights.d.ts +95 -0
  198. package/dist/flow-insights.js +149 -0
  199. package/dist/flow-live.d.ts +24 -0
  200. package/dist/flow-live.js +150 -0
  201. package/dist/flow-people.d.ts +24 -0
  202. package/dist/flow-people.js +91 -0
  203. package/dist/flow-replies.d.ts +48 -0
  204. package/dist/flow-replies.js +113 -0
  205. package/dist/flow-scripts.d.ts +38 -0
  206. package/dist/flow-scripts.js +73 -0
  207. package/dist/flow-secrets.d.ts +13 -0
  208. package/dist/flow-secrets.js +45 -0
  209. package/dist/flow-sort.d.ts +82 -0
  210. package/dist/flow-sort.js +153 -0
  211. package/dist/flow-steps.d.ts +40 -0
  212. package/dist/flow-steps.js +408 -0
  213. package/dist/flow-triggers.d.ts +244 -0
  214. package/dist/flow-triggers.js +959 -0
  215. package/dist/flows-ui.d.ts +20 -0
  216. package/dist/flows-ui.js +175 -0
  217. package/dist/flows.d.ts +229 -0
  218. package/dist/flows.js +968 -0
  219. package/dist/fonts.d.ts +19 -0
  220. package/dist/fonts.js +19 -0
  221. package/dist/gaps.d.ts +35 -0
  222. package/dist/gaps.js +101 -0
  223. package/dist/gate-failure.d.ts +40 -0
  224. package/dist/gate-failure.js +66 -0
  225. package/dist/git.d.ts +54 -0
  226. package/dist/git.js +94 -0
  227. package/dist/google-mail.d.ts +58 -0
  228. package/dist/google-mail.js +151 -0
  229. package/dist/grant.d.ts +136 -0
  230. package/dist/grant.js +238 -0
  231. package/dist/graph.d.ts +164 -0
  232. package/dist/graph.js +383 -0
  233. package/dist/guides.d.ts +22 -0
  234. package/dist/guides.js +363 -0
  235. package/dist/held.d.ts +150 -0
  236. package/dist/held.js +799 -0
  237. package/dist/invoke.d.ts +112 -0
  238. package/dist/invoke.js +780 -0
  239. package/dist/issues.d.ts +41 -0
  240. package/dist/issues.js +131 -0
  241. package/dist/job-object-helper.ps1 +252 -0
  242. package/dist/jsonl-discriminants.d.ts +11 -0
  243. package/dist/jsonl-discriminants.js +93 -0
  244. package/dist/keys.d.ts +104 -0
  245. package/dist/keys.js +229 -0
  246. package/dist/kits-ui.d.ts +17 -0
  247. package/dist/kits-ui.js +46 -0
  248. package/dist/kits.d.ts +86 -0
  249. package/dist/kits.js +215 -0
  250. package/dist/knowledge-cli.d.ts +101 -0
  251. package/dist/knowledge-cli.js +92 -0
  252. package/dist/knowledge-ui.d.ts +24 -0
  253. package/dist/knowledge-ui.js +40 -0
  254. package/dist/lead-context.d.ts +8 -0
  255. package/dist/lead-context.js +56 -0
  256. package/dist/lead-follow.d.ts +22 -0
  257. package/dist/lead-follow.js +167 -0
  258. package/dist/lead-status.d.ts +75 -0
  259. package/dist/lead-status.js +285 -0
  260. package/dist/ledger-chain.d.ts +46 -0
  261. package/dist/ledger-chain.js +187 -0
  262. package/dist/ledger-csv.d.ts +4 -0
  263. package/dist/ledger-csv.js +11 -0
  264. package/dist/ledger-view.d.ts +24 -0
  265. package/dist/ledger-view.js +58 -0
  266. package/dist/limits-ui.d.ts +26 -0
  267. package/dist/limits-ui.js +67 -0
  268. package/dist/link.d.ts +72 -0
  269. package/dist/link.js +217 -0
  270. package/dist/live.d.ts +96 -0
  271. package/dist/live.js +366 -0
  272. package/dist/liveness.d.ts +30 -0
  273. package/dist/liveness.js +42 -0
  274. package/dist/log.d.ts +7 -0
  275. package/dist/log.js +25 -0
  276. package/dist/mailbox.d.ts +57 -0
  277. package/dist/mailbox.js +150 -0
  278. package/dist/maintenance.d.ts +11 -0
  279. package/dist/maintenance.js +35 -0
  280. package/dist/mate-cli.d.ts +52 -0
  281. package/dist/mate-cli.js +345 -0
  282. package/dist/mate-contract.d.ts +10 -0
  283. package/dist/mate-contract.js +30 -0
  284. package/dist/mate-doors.d.ts +69 -0
  285. package/dist/mate-doors.js +548 -0
  286. package/dist/mate-progress.d.ts +29 -0
  287. package/dist/mate-progress.js +121 -0
  288. package/dist/mate-tools.d.ts +94 -0
  289. package/dist/mate-tools.js +1878 -0
  290. package/dist/mate.d.ts +92 -0
  291. package/dist/mate.js +435 -0
  292. package/dist/mcp-connect.d.ts +103 -0
  293. package/dist/mcp-connect.js +252 -0
  294. package/dist/mcp.d.ts +27 -0
  295. package/dist/mcp.js +651 -0
  296. package/dist/memory-cli.d.ts +466 -0
  297. package/dist/memory-cli.js +111 -0
  298. package/dist/memory-pass.d.ts +160 -0
  299. package/dist/memory-pass.js +409 -0
  300. package/dist/metrics.d.ts +3 -0
  301. package/dist/metrics.js +56 -0
  302. package/dist/mobile-viewport.d.ts +3 -0
  303. package/dist/mobile-viewport.js +37 -0
  304. package/dist/model-catalog.d.ts +118 -0
  305. package/dist/model-catalog.js +376 -0
  306. package/dist/models-cli.d.ts +102 -0
  307. package/dist/models-cli.js +66 -0
  308. package/dist/models-ui.d.ts +34 -0
  309. package/dist/models-ui.js +68 -0
  310. package/dist/modes.d.ts +80 -0
  311. package/dist/modes.js +182 -0
  312. package/dist/monitoring-settings.d.ts +44 -0
  313. package/dist/monitoring-settings.js +173 -0
  314. package/dist/monitoring-ui.d.ts +20 -0
  315. package/dist/monitoring-ui.js +51 -0
  316. package/dist/monitoring.d.ts +34 -0
  317. package/dist/monitoring.js +221 -0
  318. package/dist/names.d.ts +67 -0
  319. package/dist/names.js +102 -0
  320. package/dist/observations.d.ts +40 -0
  321. package/dist/observations.js +211 -0
  322. package/dist/oidc.d.ts +71 -0
  323. package/dist/oidc.js +182 -0
  324. package/dist/onboard.d.ts +108 -0
  325. package/dist/onboard.js +325 -0
  326. package/dist/openrouter-models.d.ts +30 -0
  327. package/dist/openrouter-models.js +120 -0
  328. package/dist/operate.d.ts +118 -0
  329. package/dist/operate.js +11321 -0
  330. package/dist/peek-cli.d.ts +97 -0
  331. package/dist/peek-cli.js +215 -0
  332. package/dist/peek.d.ts +134 -0
  333. package/dist/peek.js +578 -0
  334. package/dist/phase-routing.d.ts +307 -0
  335. package/dist/phase-routing.js +658 -0
  336. package/dist/plan-auto.d.ts +14 -0
  337. package/dist/plan-auto.js +92 -0
  338. package/dist/plan.d.ts +186 -0
  339. package/dist/plan.js +401 -0
  340. package/dist/planner-source.d.ts +251 -0
  341. package/dist/planner-source.js +460 -0
  342. package/dist/planner.d.ts +75 -0
  343. package/dist/planner.js +992 -0
  344. package/dist/policy-ui.d.ts +20 -0
  345. package/dist/policy-ui.js +41 -0
  346. package/dist/policy.d.ts +111 -0
  347. package/dist/policy.js +231 -0
  348. package/dist/prepared-evidence.d.ts +16 -0
  349. package/dist/prepared-evidence.js +96 -0
  350. package/dist/pricing.d.ts +45 -0
  351. package/dist/pricing.js +77 -0
  352. package/dist/principal.d.ts +42 -0
  353. package/dist/principal.js +82 -0
  354. package/dist/probe.d.ts +46 -0
  355. package/dist/probe.js +79 -0
  356. package/dist/process-custody.d.ts +17 -0
  357. package/dist/process-custody.js +91 -0
  358. package/dist/process-liveness.d.ts +10 -0
  359. package/dist/process-liveness.js +81 -0
  360. package/dist/process-recovery-anchor.d.ts +75 -0
  361. package/dist/process-recovery-anchor.js +188 -0
  362. package/dist/process-recovery-coalition.d.ts +25 -0
  363. package/dist/process-recovery-coalition.js +105 -0
  364. package/dist/process-recovery-eligibility.d.ts +178 -0
  365. package/dist/process-recovery-eligibility.js +170 -0
  366. package/dist/process-recovery-native.d.ts +172 -0
  367. package/dist/process-recovery-native.js +592 -0
  368. package/dist/process-recovery-provenance.d.ts +163 -0
  369. package/dist/process-recovery-provenance.js +292 -0
  370. package/dist/process-recovery-services.d.ts +56 -0
  371. package/dist/process-recovery-services.js +191 -0
  372. package/dist/process-recovery-settlement.d.ts +32 -0
  373. package/dist/process-recovery-settlement.js +132 -0
  374. package/dist/process-recovery.d.ts +41 -0
  375. package/dist/process-recovery.js +91 -0
  376. package/dist/process-tree.d.ts +41 -0
  377. package/dist/process-tree.js +264 -0
  378. package/dist/project-access.d.ts +14 -0
  379. package/dist/project-access.js +34 -0
  380. package/dist/project-cli.d.ts +18 -0
  381. package/dist/project-cli.js +104 -0
  382. package/dist/project-delete-ui.d.ts +22 -0
  383. package/dist/project-delete-ui.js +48 -0
  384. package/dist/project-delete.d.ts +55 -0
  385. package/dist/project-delete.js +413 -0
  386. package/dist/project-knowledge.d.ts +111 -0
  387. package/dist/project-knowledge.js +241 -0
  388. package/dist/project-learning.d.ts +100 -0
  389. package/dist/project-learning.js +438 -0
  390. package/dist/project-memory.d.ts +81 -0
  391. package/dist/project-memory.js +264 -0
  392. package/dist/project-skills.d.ts +152 -0
  393. package/dist/project-skills.js +660 -0
  394. package/dist/project-tools.d.ts +219 -0
  395. package/dist/project-tools.js +796 -0
  396. package/dist/project.d.ts +61 -0
  397. package/dist/project.js +125 -0
  398. package/dist/prompt.d.ts +22 -0
  399. package/dist/prompt.js +72 -0
  400. package/dist/proof.d.ts +513 -0
  401. package/dist/proof.js +1140 -0
  402. package/dist/proposal.d.ts +82 -0
  403. package/dist/proposal.js +210 -0
  404. package/dist/provider-connection.d.ts +22 -0
  405. package/dist/provider-connection.js +98 -0
  406. package/dist/provider-limits.d.ts +35 -0
  407. package/dist/provider-limits.js +95 -0
  408. package/dist/provider.d.ts +279 -0
  409. package/dist/provider.js +935 -0
  410. package/dist/publish.d.ts +107 -0
  411. package/dist/publish.js +650 -0
  412. package/dist/pulls.d.ts +118 -0
  413. package/dist/pulls.js +240 -0
  414. package/dist/push.d.ts +95 -0
  415. package/dist/push.js +353 -0
  416. package/dist/quality.d.ts +8 -0
  417. package/dist/quality.js +6 -0
  418. package/dist/recipe-ui.d.ts +18 -0
  419. package/dist/recipe-ui.js +152 -0
  420. package/dist/recipes.d.ts +81 -0
  421. package/dist/recipes.js +332 -0
  422. package/dist/remote.d.ts +67 -0
  423. package/dist/remote.js +122 -0
  424. package/dist/render.d.ts +64 -0
  425. package/dist/render.js +587 -0
  426. package/dist/report-summary.d.ts +13 -0
  427. package/dist/report-summary.js +15 -0
  428. package/dist/repos.d.ts +65 -0
  429. package/dist/repos.js +277 -0
  430. package/dist/repository-context-ui.d.ts +2 -0
  431. package/dist/repository-context-ui.js +10 -0
  432. package/dist/repository-context.d.ts +67 -0
  433. package/dist/repository-context.js +376 -0
  434. package/dist/restart-certification.d.ts +134 -0
  435. package/dist/restart-certification.js +226 -0
  436. package/dist/result-actions.d.ts +28 -0
  437. package/dist/result-actions.js +270 -0
  438. package/dist/result-completion.d.ts +18 -0
  439. package/dist/result-completion.js +66 -0
  440. package/dist/result-review.d.ts +213 -0
  441. package/dist/result-review.js +430 -0
  442. package/dist/retention-ui.d.ts +12 -0
  443. package/dist/retention-ui.js +39 -0
  444. package/dist/retention.d.ts +72 -0
  445. package/dist/retention.js +288 -0
  446. package/dist/review-context.d.ts +243 -0
  447. package/dist/review-context.js +1043 -0
  448. package/dist/review-evidence.d.ts +33 -0
  449. package/dist/review-evidence.js +56 -0
  450. package/dist/reviewer.d.ts +97 -0
  451. package/dist/reviewer.js +202 -0
  452. package/dist/routine.d.ts +228 -0
  453. package/dist/routine.js +764 -0
  454. package/dist/runner.d.ts +266 -0
  455. package/dist/runner.js +399 -0
  456. package/dist/scan.d.ts +32 -0
  457. package/dist/scan.js +92 -0
  458. package/dist/scope.d.ts +695 -0
  459. package/dist/scope.js +1176 -0
  460. package/dist/scout-report.d.ts +42 -0
  461. package/dist/scout-report.js +108 -0
  462. package/dist/scout.d.ts +69 -0
  463. package/dist/scout.js +351 -0
  464. package/dist/serve.d.ts +313 -0
  465. package/dist/serve.js +21776 -0
  466. package/dist/session-brief.d.ts +8 -0
  467. package/dist/session-brief.js +24 -0
  468. package/dist/session-cli.d.ts +13 -0
  469. package/dist/session-cli.js +284 -0
  470. package/dist/session-contract.d.ts +183 -0
  471. package/dist/session-contract.js +122 -0
  472. package/dist/session-http.d.ts +19 -0
  473. package/dist/session-http.js +154 -0
  474. package/dist/session-server.d.ts +12 -0
  475. package/dist/session-server.js +37 -0
  476. package/dist/session-service.d.ts +22 -0
  477. package/dist/session-service.js +157 -0
  478. package/dist/setup-guide.d.ts +39 -0
  479. package/dist/setup-guide.js +93 -0
  480. package/dist/sign-in-guard.d.ts +45 -0
  481. package/dist/sign-in-guard.js +74 -0
  482. package/dist/skills-ui.d.ts +11 -0
  483. package/dist/skills-ui.js +58 -0
  484. package/dist/skills.d.ts +97 -0
  485. package/dist/skills.js +276 -0
  486. package/dist/slack-api.d.ts +55 -0
  487. package/dist/slack-api.js +229 -0
  488. package/dist/slack-chat.d.ts +28 -0
  489. package/dist/slack-chat.js +413 -0
  490. package/dist/slack-settings.d.ts +7 -0
  491. package/dist/slack-settings.js +75 -0
  492. package/dist/slack-state.d.ts +13 -0
  493. package/dist/slack-state.js +9 -0
  494. package/dist/slack.d.ts +13 -0
  495. package/dist/slack.js +167 -0
  496. package/dist/spend-ui.d.ts +28 -0
  497. package/dist/spend-ui.js +95 -0
  498. package/dist/spend.d.ts +142 -0
  499. package/dist/spend.js +313 -0
  500. package/dist/sqlite-runtime.d.ts +3 -0
  501. package/dist/sqlite-runtime.js +6 -0
  502. package/dist/sso-settings.d.ts +33 -0
  503. package/dist/sso-settings.js +103 -0
  504. package/dist/sso-ui.d.ts +22 -0
  505. package/dist/sso-ui.js +39 -0
  506. package/dist/storage.d.ts +15 -0
  507. package/dist/storage.js +84 -0
  508. package/dist/store.d.ts +7754 -0
  509. package/dist/store.js +23279 -0
  510. package/dist/structured-output.d.ts +58 -0
  511. package/dist/structured-output.js +103 -0
  512. package/dist/style-asset.d.ts +7 -0
  513. package/dist/style-asset.js +41 -0
  514. package/dist/subscription-chat.d.ts +46 -0
  515. package/dist/subscription-chat.js +275 -0
  516. package/dist/summary.d.ts +33 -0
  517. package/dist/summary.js +71 -0
  518. package/dist/supervisor.mjs +297 -0
  519. package/dist/surface.d.ts +63 -0
  520. package/dist/surface.js +352 -0
  521. package/dist/sync.d.ts +94 -0
  522. package/dist/sync.js +279 -0
  523. package/dist/task-composer.d.ts +13 -0
  524. package/dist/task-composer.js +52 -0
  525. package/dist/task-control.d.ts +165 -0
  526. package/dist/task-control.js +214 -0
  527. package/dist/task-outcome-cli.d.ts +10 -0
  528. package/dist/task-outcome-cli.js +92 -0
  529. package/dist/task-text.d.ts +30 -0
  530. package/dist/task-text.js +41 -0
  531. package/dist/team-cli.d.ts +15 -0
  532. package/dist/team-cli.js +618 -0
  533. package/dist/team-contract.d.ts +92 -0
  534. package/dist/team-contract.js +1 -0
  535. package/dist/team-http.d.ts +17 -0
  536. package/dist/team-http.js +144 -0
  537. package/dist/team-leads.d.ts +81 -0
  538. package/dist/team-leads.js +565 -0
  539. package/dist/team-runtime.d.ts +27 -0
  540. package/dist/team-runtime.js +264 -0
  541. package/dist/team-ui.d.ts +3 -0
  542. package/dist/team-ui.js +9 -0
  543. package/dist/team-updates.d.ts +8 -0
  544. package/dist/team-updates.js +57 -0
  545. package/dist/teammate-admin.d.ts +53 -0
  546. package/dist/teammate-admin.js +111 -0
  547. package/dist/teammate-desk.d.ts +60 -0
  548. package/dist/teammate-desk.js +154 -0
  549. package/dist/teammate-memory.d.ts +64 -0
  550. package/dist/teammate-memory.js +196 -0
  551. package/dist/teammate-question.d.ts +59 -0
  552. package/dist/teammate-question.js +175 -0
  553. package/dist/teammate-tools.d.ts +108 -0
  554. package/dist/teammate-tools.js +271 -0
  555. package/dist/teammate-week.d.ts +68 -0
  556. package/dist/teammate-week.js +132 -0
  557. package/dist/teammate-work.d.ts +76 -0
  558. package/dist/teammate-work.js +280 -0
  559. package/dist/teammates-ui.d.ts +20 -0
  560. package/dist/teammates-ui.js +166 -0
  561. package/dist/teammates.d.ts +180 -0
  562. package/dist/teammates.js +253 -0
  563. package/dist/teams-api.d.ts +33 -0
  564. package/dist/teams-api.js +204 -0
  565. package/dist/teams-chat.d.ts +26 -0
  566. package/dist/teams-chat.js +215 -0
  567. package/dist/teams-settings.d.ts +12 -0
  568. package/dist/teams-settings.js +57 -0
  569. package/dist/teams.d.ts +21 -0
  570. package/dist/teams.js +124 -0
  571. package/dist/telegram-flow.d.ts +49 -0
  572. package/dist/telegram-flow.js +110 -0
  573. package/dist/telegram-mate.d.ts +137 -0
  574. package/dist/telegram-mate.js +707 -0
  575. package/dist/telegram-progress.d.ts +14 -0
  576. package/dist/telegram-progress.js +129 -0
  577. package/dist/telegram-settings.d.ts +9 -0
  578. package/dist/telegram-settings.js +36 -0
  579. package/dist/telegram-status.d.ts +75 -0
  580. package/dist/telegram-status.js +265 -0
  581. package/dist/telegram-team.d.ts +85 -0
  582. package/dist/telegram-team.js +359 -0
  583. package/dist/telegram.d.ts +230 -0
  584. package/dist/telegram.js +1931 -0
  585. package/dist/templates.d.ts +65 -0
  586. package/dist/templates.js +118 -0
  587. package/dist/tool-launcher.d.ts +1 -0
  588. package/dist/tool-launcher.js +101 -0
  589. package/dist/tools-ui.d.ts +30 -0
  590. package/dist/tools-ui.js +40 -0
  591. package/dist/transitions-recipes.d.ts +1 -0
  592. package/dist/transitions-recipes.js +203 -0
  593. package/dist/tree-proof.d.ts +34 -0
  594. package/dist/tree-proof.js +58 -0
  595. package/dist/verification-evidence.d.ts +44 -0
  596. package/dist/verification-evidence.js +238 -0
  597. package/dist/version.d.ts +2 -0
  598. package/dist/version.js +10 -0
  599. package/dist/webhooks.d.ts +91 -0
  600. package/dist/webhooks.js +283 -0
  601. package/dist/work-index.d.ts +68 -0
  602. package/dist/work-index.js +384 -0
  603. package/dist/work-summary.d.ts +57 -0
  604. package/dist/work-summary.js +116 -0
  605. package/dist/workspace-motion.d.ts +4 -0
  606. package/dist/workspace-motion.js +133 -0
  607. package/dist/workspace-revision.d.ts +19 -0
  608. package/dist/workspace-revision.js +92 -0
  609. package/dist/workspace-ui.d.ts +204 -0
  610. package/dist/workspace-ui.js +411 -0
  611. package/dist/worktree-notices.d.ts +13 -0
  612. package/dist/worktree-notices.js +17 -0
  613. package/dist/worktree.d.ts +249 -0
  614. package/dist/worktree.js +720 -0
  615. package/package.json +122 -0
  616. package/scripts/canary-assertions.mjs +42 -0
  617. package/scripts/crash-canary.mjs +253 -0
  618. package/scripts/fixtures/crash-process.mjs +74 -0
  619. package/scripts/fixtures/pilot-scenarios.mjs +85 -0
  620. package/scripts/fixtures/restart-service.mjs +27 -0
  621. package/scripts/launchd-certification.mjs +143 -0
  622. package/scripts/pilot.mjs +121 -0
  623. package/scripts/proof-preflight.mjs +140 -0
  624. package/scripts/provider-canary.mjs +379 -0
  625. package/scripts/recovery-canary.mjs +135 -0
  626. package/scripts/restart-certification.mjs +98 -0
package/dist/invoke.js ADDED
@@ -0,0 +1,780 @@
1
+ /**
2
+ * The invocation gateway: the only door to any provider binary (§5).
3
+ *
4
+ * "Never let an LLM poll" is only enforceable if there is exactly one place
5
+ * that can start an LLM, and this is it. Every call verifies an open run
6
+ * record WHOSE RECORDED PROVIDER MATCHES the one about to spawn (a repair
7
+ * resumed on the wrong harness is a meaningless session id and a silent
8
+ * fresh spend — refused here, structurally), stamps `provider_started_at`
9
+ * the instant before the process spawns, and parses usage off the envelope
10
+ * of EVERY completed process — nonzero exits included, because a failed
11
+ * turn still spent money and a cost report that skips failures is a
12
+ * smaller number, not a truer one.
13
+ *
14
+ * The architecture test asserts the boundary by imports, not by string
15
+ * scan: only this module imports the registry's spawning surface
16
+ * (`adapterFor`), and only builder, planner, reviewer, and scout import
17
+ * `invokeAgent`.
18
+ */
19
+ import { adapterFor, auditOf } from "./provider.js";
20
+ import { readProviderKey, readAuthModeStrict, PROVIDER_KEY_ENV, OWN_KEY_ENV } from "./keys.js";
21
+ import { classifyTerminal } from "./exhaustion.js";
22
+ import { attestProvider } from "./attest.js";
23
+ import { run as runCommand, runOwnerTag, startClaudeHeldSession } from "./exec.js";
24
+ import { currentContainment } from "./containment.js";
25
+ import { witnessedRunner, recordProcessObservationFailure, preserveObservedProcesses } from "./process-custody.js";
26
+ import { underStopWatch } from "./task-control.js";
27
+ import { childDatabaseEnv, isolatedChildDatabase, removeChildDatabase as removeAgentDatabase } from "./child-database.js";
28
+ import { noToolsArgs, prepareRunTools } from "./project-tools.js";
29
+ import { realpathSync } from "node:fs";
30
+ import { agentFence, claudeFenceSettings, linuxFenceAvailable, macosFenceAvailable } from "./agent-fence.js";
31
+ import { billingOf, claudeBillingFrom, seenBilling } from "./spend.js";
32
+ /** The claude binary name — kept for the legacy quota rows and tests that
33
+ * describe history; new code carries a resolved AgentSpec instead. */
34
+ export const PROVIDER_BINARY = "claude";
35
+ /**
36
+ * A provider can run the repository's own CLI. In a self-hosting build that
37
+ * must never mean "open the database that launched me": a migration from the
38
+ * candidate checkout would change the live schema underneath the older
39
+ * supervisor, and even a read can contend with its watch transaction. Every
40
+ * provider process therefore sees a unique disposable Toolroll
41
+ * database while retaining its normal HOME/XDG environment for subscriptions
42
+ * and unrelated developer tools.
43
+ */
44
+ function isolatedAgentDatabase(runId) {
45
+ return isolatedChildDatabase(`agent-${runId}`);
46
+ }
47
+ /** Defend the durable registry even when a test or alternate transport calls
48
+ * the callback directly instead of passing through an in-tree JSONL runner. */
49
+ function transportSessionId(id) {
50
+ if (typeof id !== "string")
51
+ return null;
52
+ const normalized = id.trim();
53
+ return normalized === "" ? null : normalized;
54
+ }
55
+ /**
56
+ * Spend money, on the record. Throws — never refuses quietly — when the run
57
+ * is missing, already finished, or recorded against a different provider:
58
+ * a caller that reaches for a harness without a matching open run is a
59
+ * bug, not a case. Attestation and protocol refusals are VALUES (above),
60
+ * not throws — those are cases, and they must dispose cleanly.
61
+ */
62
+ export async function invokeAgent(store, runId, spec, invocation, options) {
63
+ const clock = options.clock ?? (() => new Date());
64
+ if (options.accumulateUsage && (invocation.phase !== "review" || invocation.resumeSession === null)) {
65
+ throw new Error("accumulated usage is only for evidence reads in an existing reviewer session");
66
+ }
67
+ const run = store.getRun(runId);
68
+ if (run === null || run.outcome !== null) {
69
+ throw new Error(`run ${runId} is not an open attempt — nothing spends without a run record that will outlive it`);
70
+ }
71
+ if (run.provider !== spec.provider) {
72
+ throw new Error(`run ${runId} was opened for ${run.provider} but ${spec.provider} is about to spawn — a session resumed across harnesses is not a session`);
73
+ }
74
+ // Session selection is durable authority, not an argv-only hint. A resumed
75
+ // turn must name exactly the canonical identity already recorded on THIS
76
+ // run before anything awaits or spawns. That makes a wrong child-row id a
77
+ // caller bug in the same class as a wrong provider, and prevents COALESCE's
78
+ // first-write rule from leaving row A while the process actually resumes B.
79
+ const recordedSessionId = transportSessionId(run.sessionId);
80
+ if (run.sessionId !== null && recordedSessionId === null) {
81
+ throw new Error(`run ${runId} carries an empty durable session identity — nothing resumes an identity the registry cannot name`);
82
+ }
83
+ const requestedResumeSessionId = invocation.resumeSession === null ? null : transportSessionId(invocation.resumeSession);
84
+ if (invocation.resumeSession !== null && requestedResumeSessionId === null) {
85
+ throw new Error(`run ${runId} was asked to resume an empty session identity`);
86
+ }
87
+ if (requestedResumeSessionId !== null && recordedSessionId !== requestedResumeSessionId) {
88
+ throw new Error(recordedSessionId === null
89
+ ? `run ${runId} has no durable session identity matching the requested resume ${requestedResumeSessionId}`
90
+ : `run ${runId} records session ${recordedSessionId} but was asked to resume ${requestedResumeSessionId}`);
91
+ }
92
+ const requestedStartSessionId = requestedResumeSessionId !== null || invocation.startSessionId === undefined
93
+ ? undefined
94
+ : transportSessionId(invocation.startSessionId);
95
+ if (requestedResumeSessionId === null && invocation.startSessionId !== undefined && requestedStartSessionId === null) {
96
+ throw new Error(`run ${runId} was asked to start under an empty session identity`);
97
+ }
98
+ const startSessionId = requestedResumeSessionId !== null ? undefined : (requestedStartSessionId ?? undefined);
99
+ if (recordedSessionId !== null && requestedResumeSessionId === null && startSessionId !== recordedSessionId) {
100
+ throw new Error(`run ${runId} already records session ${recordedSessionId} but this invocation neither resumes nor starts under it`);
101
+ }
102
+ const adapter = adapterFor(spec.provider);
103
+ const { startSessionId: _untrustedStartSessionId, ...invocationWithoutStart } = invocation;
104
+ const resolvedInvocation = {
105
+ ...invocationWithoutStart,
106
+ model: spec.model,
107
+ resumeSession: requestedResumeSessionId,
108
+ ...(startSessionId === undefined ? {} : { startSessionId }),
109
+ };
110
+ const argv = adapter.argv(resolvedInvocation);
111
+ const stdin = adapter.stdin?.(resolvedInvocation);
112
+ // Long-running phases pass idleTimeoutMs: the process has no absolute
113
+ // deadline and is stopped only after a full no-output interval. Bounded
114
+ // callers (notably repair) keep timeoutMs and its hard wall clock. A
115
+ // legacy caller that names neither retains the historic bounded default.
116
+ const hardTimeoutMs = options.timeoutMs === undefined ? undefined : adapter.clampTimeout(invocation.phase, options.timeoutMs);
117
+ const idleTimeoutMs = options.idleTimeoutMs === undefined ? undefined : adapter.clampTimeout(invocation.phase, options.idleTimeoutMs);
118
+ const defaultTimeoutMs = hardTimeoutMs === undefined && idleTimeoutMs === undefined
119
+ ? adapter.clampTimeout(invocation.phase, 30 * 60_000)
120
+ : undefined;
121
+ // Tier-2 attestation (A2/B3): the authoritative check, immediately
122
+ // before the spawn it authorizes. Tier-1 providers return null here and
123
+ // keep their road byte-identical.
124
+ const attested = await attestProvider(spec.provider, adapter.binary, options.versionProbe);
125
+ if (attested !== null && !attested.ok) {
126
+ if (attested.version !== null)
127
+ store.stampProviderVersion(runId, attested.version);
128
+ return {
129
+ kind: "refused",
130
+ reason: "provider-unattested",
131
+ providerVersion: attested.version,
132
+ diagnostic: attested.problem,
133
+ };
134
+ }
135
+ // Auth mode decides how the credential reaches the process (the operator
136
+ // keeps BOTH and prefers the subscription): in "api-key" mode the
137
+ // managed file wins, the ambient env is a real fallback (explicit
138
+ // re-supply survives resolveChildEnv's chat-key strip); in
139
+ // "subscription" mode the key is STRIPPED from the child so the CLI's
140
+ // own login is used — its key is retained, just not handed over.
141
+ // A CHAIN-BOUND run carries a PINNED mode sealed into its approved entry
142
+ // (E3d, review finding 5): the pin IS the authority — a per-provider
143
+ // mode-file flip between admission and spawn must never move the spend
144
+ // onto a credential the operator didn't approve for this entry. Every
145
+ // other run reads the operator's live setting, as always.
146
+ const { runner, clock: _clock, versionProbe: _probe, keyHome, timeoutMs: _hardTimeout, idleTimeoutMs: _idleTimeout, stdin: _callerStdin, accumulateUsage: _accumulateUsage, ...runOptions } = options;
147
+ // The live setting is read STRICTLY (atomic authority closure): a
148
+ // present mode file that says neither word is a value-shaped refusal
149
+ // before any stamp or process — never the subscription default a
150
+ // lenient read would coerce it to, which would spend on a credential
151
+ // the seal never proved. The same reader gates filing and the seal.
152
+ let authMode;
153
+ if (run.chainCycle != null && run.authMode != null) {
154
+ authMode = run.authMode;
155
+ }
156
+ else {
157
+ const strict = readAuthModeStrict(spec.provider, keyHome);
158
+ if (!strict.ok) {
159
+ return {
160
+ kind: "refused",
161
+ reason: "auth-mode",
162
+ providerVersion: attested === null ? null : attested.version,
163
+ diagnostic: strict.problem,
164
+ };
165
+ }
166
+ authMode = strict.mode;
167
+ }
168
+ const ownKeyEnv = OWN_KEY_ENV[spec.provider];
169
+ const managedKey = authMode === "api-key"
170
+ ? readProviderKey(spec.provider, keyHome) ?? (process.env[PROVIDER_KEY_ENV[spec.provider]] || null)
171
+ : null;
172
+ // A pinned api-key entry with NO key is REFUSED, not spawned (Codex E3d
173
+ // review, finding 1): with nothing to inject, the provider CLI would
174
+ // quietly fall back to its cached subscription login — spend on a
175
+ // credential the entry never approved. A value-shaped refusal before any
176
+ // stamp: no process, no spend, no strike.
177
+ if (run.chainCycle != null && authMode === "api-key" && managedKey === null) {
178
+ return {
179
+ kind: "refused",
180
+ reason: "chain-credential",
181
+ providerVersion: attested === null ? null : attested.version,
182
+ diagnostic: `the approved entry is pinned to a ${spec.provider} API key, and no managed or ambient key exists — add one on the keys screen`,
183
+ };
184
+ }
185
+ // Resume XOR mint, enforced at the GATEWAY (Codex gemini verify round 2,
186
+ // finding 3): a resume and a minted start id are mutually exclusive —
187
+ // geminiArgv silently prefers --resume, so a minted id that rides
188
+ // alongside a resume would be stamped and later enforced against an
189
+ // envelope that never carried it, a paid protocol refusal. When resuming,
190
+ // the minted id is dropped here, before it is ever stamped.
191
+ // The minted session identity (A5): stamped durably BEFORE the start
192
+ // stamp — intent precedes the process, and the envelope must later
193
+ // MATCH this id or the run fails typed.
194
+ if (startSessionId !== undefined) {
195
+ store.stampRun(runId, { sessionId: startSessionId });
196
+ }
197
+ // The stamp precedes the spawn, so a crash between the two leaves a run
198
+ // that claims spend which never happened — the honest direction. A spawn
199
+ // before the stamp would leave spend no record claims, which is the lie
200
+ // the invariant exists to rule out. For attested providers the probed
201
+ // version rides the SAME durable write (B2). A CHAIN-BOUND run's stamp is
202
+ // the PRE-SPAWN CUSTODY PROOF (Codex E3d review, finding 3): one
203
+ // transaction re-derives the approval, cycle, entry, pin, and (past the
204
+ // base) the live paid-fallback grant, and stamps ONLY if all still stand
205
+ // — a grant revoked between admission and this instant refuses here.
206
+ // The spawn leg of the runner gate (MCP spec v6, review finding 4):
207
+ // the tuple re-proven against LIVE rows AFTER every awaited step, in
208
+ // the same breath as the stamp — a takeover during the attestation
209
+ // await can no longer slip a stale process through.
210
+ // THE PLANNER'S ROUTE, RE-PROVED AT THE SPAWN (final authority closure):
211
+ // the provenance the planner was admitted under is held to the authority
212
+ // its task holds RIGHT NOW — the strict working projection of a filed
213
+ // scope (exact terms, resolved profile, whole chain, route parity, digest,
214
+ // live auth mode), or the bare word `legacy` on a task with no scope. A
215
+ // scope rewritten, corrupted, or unresolved between the claim and this
216
+ // instant invokes no provider: a value-shaped refusal, no spend.
217
+ if (run.role === "planner") {
218
+ const proved = store.proveRouteForSpawn(runId, clock());
219
+ if (!proved.ok) {
220
+ return {
221
+ kind: "refused",
222
+ reason: "route-authority",
223
+ providerVersion: attested === null ? null : attested.version,
224
+ diagnostic: `the planner's route authority lapsed before spawn — ${proved.problem}`,
225
+ };
226
+ }
227
+ }
228
+ if (!store.proveRunnerCustodyForSpawn(runId, clock())) {
229
+ return {
230
+ kind: "refused",
231
+ reason: "runner-custody",
232
+ providerVersion: attested === null ? null : attested.version,
233
+ diagnostic: "the run's runner custody lapsed before spawn — the lease, the runner, or its repo binding no longer stands",
234
+ };
235
+ }
236
+ // THE STOP FENCE at the spawn (v52): a stop recorded against this run
237
+ // — or the attempt it runs under — before this instant spawns nothing.
238
+ // A value-shaped refusal, no stamp, no process, no spend; the road's
239
+ // settlement seals the attempt as interrupted.
240
+ const stopBeforeSpawn = store.applicableStopFor(runId);
241
+ if (stopBeforeSpawn !== null) {
242
+ return {
243
+ kind: "refused",
244
+ reason: "stopped",
245
+ providerVersion: attested === null ? null : attested.version,
246
+ diagnostic: `stopped by ${stopBeforeSpawn.requestedBy} (run #${stopBeforeSpawn.run}) before the provider spawned — nothing was spent`,
247
+ };
248
+ }
249
+ // THE CONTAINMENT FENCE at the spawn (OS containment plan): a required
250
+ // native policy this runner cannot meet — macOS, an undelegated cgroup,
251
+ // a missing helper — spawns nothing. A value-shaped refusal in the
252
+ // policy's own words, no stamp, no process, no spend.
253
+ const containment = currentContainment();
254
+ if (containment.refusal !== null) {
255
+ return {
256
+ kind: "refused",
257
+ reason: "containment",
258
+ providerVersion: attested === null ? null : attested.version,
259
+ diagnostic: containment.refusal,
260
+ };
261
+ }
262
+ if (run.chainCycle != null) {
263
+ const custody = attested !== null
264
+ ? store.proveChainCustodyForSpawn(runId, clock(), attested.version)
265
+ : store.proveChainCustodyForSpawn(runId, clock());
266
+ if (!custody) {
267
+ return {
268
+ kind: "refused",
269
+ reason: "chain-custody",
270
+ providerVersion: attested === null ? null : attested.version,
271
+ diagnostic: "the run's chain custody lapsed before spawn — the approval, cycle, entry pin, or paid-fallback grant no longer stands",
272
+ };
273
+ }
274
+ }
275
+ else if (attested !== null)
276
+ store.stampProviderStart(runId, clock(), attested.version);
277
+ else
278
+ store.stampProviderStart(runId, clock());
279
+ const spawn = witnessedRunner(store, runId, clock, runner ?? adapter.defaultRunner);
280
+ let transportAnnouncedSessionId = null;
281
+ let transportSessionConflict = false;
282
+ // B3: the attested executable IS the spawned executable — one resolution.
283
+ // A MANAGED key reaches exactly its own provider's child environment
284
+ // (keys.ts): the foreign-credential strip already shed everybody
285
+ // else's, and the plane's own env never needed to carry it. A key
286
+ // already ambient in the environment keeps working; the managed file,
287
+ // being deliberate, wins.
288
+ const isolatedDb = isolatedAgentDatabase(runId);
289
+ // The project's tools (v80): exactly its MCP servers, and none of the
290
+ // operator's global or a repository's own. A review keeps its isolation.
291
+ const tools = invocation.phase === "review" ? null : runTools(store, runId, spec, options.keyHome, clock);
292
+ // The agent fence: Toolroll's own secrets, database and other runs'
293
+ // evidence stay out of reach, whatever the permission mode. A review is
294
+ // already confined to its sealed files.
295
+ const baseFence = invocation.phase === "review" ? [] : runFence(store, runId, options.keyHome);
296
+ const toolsDir = (spec.provider === "codex" || spec.provider === "openrouter") && baseFence.length > 0 ? tools?.privateDir : undefined;
297
+ const fence = toolsDir === undefined ? baseFence : [...baseFence, realpathSync(toolsDir)];
298
+ const launchArgv = tools === null ? argv : adapter.argv({ ...resolvedInvocation, toolArgv: tools.argv, fence });
299
+ const fenced = fenceLaunch(spec.provider, fence);
300
+ if (invocation.phase !== "review")
301
+ store.recordRunFence(runId, { method: fenced.method, paths: fence.length }, clock());
302
+ const billingSeen = { keySource: null, model: null, planWindows: false, answered: false };
303
+ // v105: a Codex run on its sign-in bills as Codex says it's signed in (a ChatGPT plan, or a saved key, or Bedrock),
304
+ // read right before it runs. Never with an injected runner: tests don't start a real Codex.
305
+ const codexLogin = spec.provider === "codex" && authMode === "subscription" && runner === undefined
306
+ ? await codexLoginBilling(attested !== null ? attested.executable : adapter.binary, [...(runOptions.omitEnv ?? []), ...adapter.extraOmitEnv, ...ownKeyEnv])
307
+ : null;
308
+ let result;
309
+ try {
310
+ // Under the stop watch (v52): the run's stop row is re-read while the
311
+ // provider runs, and a stop kills THIS run's process group through the
312
+ // handle registered under its owner tag — the exact-attempt road,
313
+ // never the global sweep. Settlement then reads the same row.
314
+ result = await underStopWatch(store, runId, () => spawn(attested !== null ? attested.executable : adapter.binary, launchArgv, {
315
+ ...(fenced.wrap.length === 0 ? {} : { fence: fenced.wrap }),
316
+ ...runOptions,
317
+ // v105: how the CLI really billed this turn (its key source, its plan's windows), read off its own stream.
318
+ onStreamEvent: event => {
319
+ if (event["type"] === "system" && event["subtype"] === "init") {
320
+ if (typeof event["apiKeySource"] === "string")
321
+ billingSeen.keySource = event["apiKeySource"].slice(0, 80);
322
+ if (typeof event["model"] === "string")
323
+ billingSeen.model = event["model"].slice(0, 200);
324
+ }
325
+ else if (event["type"] === "rate_limit_event") {
326
+ billingSeen.planWindows = true;
327
+ }
328
+ else if (event["type"] === "result" && event["is_error"] !== true && event["subtype"] === "success") {
329
+ billingSeen.answered = true;
330
+ }
331
+ runOptions.onStreamEvent?.(event);
332
+ },
333
+ owner: runOwnerTag(store, runId),
334
+ beforeSpawn: () => store.applicableStopFor(runId) === null && store.getRun(runId)?.outcome === null,
335
+ onSpawn: pid => {
336
+ runOptions.onSpawn?.(pid);
337
+ if (store.applicableStopFor(runId) !== null)
338
+ throw new Error("the attempt was stopped before spawn custody completed");
339
+ },
340
+ ...(stdin === undefined ? {} : { stdin }),
341
+ ...(hardTimeoutMs === undefined ? {} : { timeoutMs: hardTimeoutMs }),
342
+ ...(idleTimeoutMs === undefined ? {} : { idleTimeoutMs }),
343
+ ...(defaultTimeoutMs === undefined ? {} : { timeoutMs: defaultTimeoutMs }),
344
+ env: {
345
+ ...(runOptions.env ?? {}),
346
+ ...(tools?.env ?? {}),
347
+ ...(managedKey === null ? {} : { [PROVIDER_KEY_ENV[spec.provider]]: managedKey }),
348
+ // This key is deliberately last: no caller may point an agent back at
349
+ // the live control database through a generic RunOptions override.
350
+ ...childDatabaseEnv(isolatedDb.file),
351
+ },
352
+ omitEnv: [
353
+ ...(runOptions.omitEnv ?? []),
354
+ ...adapter.extraOmitEnv,
355
+ // Subscription mode: shed this provider's OWN key too, so an ambient
356
+ // one cannot force API billing over the login the operator prefers.
357
+ // Api-key mode: shed every OTHER own-key alias (finding 1) — gemini
358
+ // reads GOOGLE_API_KEY as well as GEMINI_API_KEY, and a stray alias
359
+ // must not override the ONE canonical key this mode injected.
360
+ ...(authMode === "subscription"
361
+ ? ownKeyEnv
362
+ : ownKeyEnv.filter(name => name !== PROVIDER_KEY_ENV[spec.provider])),
363
+ ],
364
+ // Providers run in their own process group (M6.12): the harness spawns
365
+ // shells and tools of its own, and both the timeout and the watch's
366
+ // hard stop must end the whole tree, not orphan the grandchildren.
367
+ processGroup: true,
368
+ // The session registry's crash guarantee (M6.9): the id is stamped the
369
+ // moment the stream announces it, first write wins — a daemon that
370
+ // dies mid-turn still knows which session to offer the successor.
371
+ onSessionId: id => {
372
+ const normalized = transportSessionId(id);
373
+ if (normalized === null)
374
+ return;
375
+ if (transportAnnouncedSessionId === null)
376
+ transportAnnouncedSessionId = normalized;
377
+ else if (transportAnnouncedSessionId !== normalized)
378
+ transportSessionConflict = true;
379
+ store.stampRun(runId, { sessionId: normalized });
380
+ },
381
+ }));
382
+ }
383
+ finally {
384
+ removeAgentDatabase(isolatedDb.dir);
385
+ tools?.cleanup();
386
+ }
387
+ // v105: the run's billing, fixed from evidence before its usage is priced: a key it was given is a key; otherwise
388
+ // what the CLI said (Claude's stream) or was last seen doing (Codex's account), and the setting only when neither.
389
+ const evidence = spec.provider === "claude" ? claudeBillingFrom(billingSeen)
390
+ // Codex signed in with ChatGPT is its plan, unless its plan is itself billed by use (seen by the console's probe).
391
+ : codexLogin === "subscription" ? seenBilling(store.handle, "codex") ?? "subscription" : codexLogin;
392
+ // What this computer's Claude does with no key from us (teammates and drafts bill the same way): learnt only from a
393
+ // run we gave no key that got as far as an answer.
394
+ if (spec.provider === "claude" && managedKey === null && authMode === "subscription" && billingSeen.answered && evidence !== null) {
395
+ store.recordProviderBilling("claude", evidence, new Date());
396
+ }
397
+ store.fixRunBilling(runId, authMode === "api-key" ? "api-key" : evidence ?? billingOf(spec.provider, store.handle), new Date());
398
+ const envelope = adapter.parse(result.stdout);
399
+ // The callback is the crash-safe early stamp; the parsed envelope is the
400
+ // authoritative completed-stream account. Stamp it as a fallback for an
401
+ // alternate transport. On fresh runs COALESCE makes callback/parser
402
+ // disagreement visible in the durable row; resumed runs need the separate
403
+ // callback latch because their row deliberately carries the input session.
404
+ if (envelope.sessionId !== null) {
405
+ store.stampRun(runId, { sessionId: envelope.sessionId });
406
+ }
407
+ const durableSessionId = store.getRun(runId)?.sessionId ?? null;
408
+ // The durable id, callback id, and retained envelope must all resolve to
409
+ // ONE identity. This deliberately includes resumed turns: a provider that
410
+ // returns a fork has not proved the same-session repair/warm-resume
411
+ // contract, and COALESCE must not disguise row A / process B as success.
412
+ const sessionIdentityProblem = transportSessionConflict
413
+ ? "the provider transport announced conflicting session ids"
414
+ : transportAnnouncedSessionId !== null && transportAnnouncedSessionId !== envelope.sessionId
415
+ ? "the retained provider envelope did not match the session identity announced through the transport callback"
416
+ : durableSessionId !== envelope.sessionId
417
+ ? envelope.sessionId === null
418
+ ? "the retained provider envelope did not confirm the durably recorded session id"
419
+ : requestedResumeSessionId !== null
420
+ ? "the provider returned a session id different from the exact identity recorded for resume"
421
+ : "the provider session id was different from the identity durably recorded when the stream initialized"
422
+ : null;
423
+ store.recordUsage(runId, {
424
+ ...(envelope.tokensIn === null ? {} : { tokensIn: envelope.tokensIn + (options.accumulateUsage ? run.tokensIn ?? 0 : 0) }),
425
+ ...(envelope.tokensOut === null ? {} : { tokensOut: envelope.tokensOut + (options.accumulateUsage ? run.tokensOut ?? 0 : 0) }),
426
+ ...(envelope.costUsd === null ? {} : { costUsd: envelope.costUsd + (options.accumulateUsage ? run.costUsd ?? 0 : 0) }),
427
+ ...(envelope.usageRaw === null ? {} : { usageJson: envelope.usageRaw }),
428
+ });
429
+ // The fallback taxonomy stamp (E2): classify HERE, where the evidence
430
+ // still exists — the structural terminal off this exact envelope, the
431
+ // AUTHORITATIVE version the gateway proved at spawn, and the auth mode
432
+ // that spawned it. `classifyTerminal` is fail-closed: with no
433
+ // fixture-backed recognizer for this (provider, version) — the state
434
+ // every build ships in — it can only ever return a non-eligible class,
435
+ // so this stamp authorizes nothing. It is the honest disposal record the
436
+ // dispatch's C8 gate later re-checks against `hasRecognizer` before it
437
+ // reads the class as anything more than history. Tier-1 providers prove
438
+ // no version here yet (attested === null), so they classify fail-closed
439
+ // until their exhaustion fixture — and the version proving it needs — is
440
+ // captured and reviewed.
441
+ const terminalClass = classifyTerminal({
442
+ provider: spec.provider,
443
+ version: attested === null ? null : attested.version,
444
+ authMode,
445
+ terminal: envelope.structuralTerminal,
446
+ });
447
+ store.stampTerminalClass(runId, authMode, terminalClass);
448
+ // Contradictory structural identity is a provider protocol failure, not
449
+ // a session selection problem. Usage above remains recorded because the
450
+ // process ran and spent, but no downstream handoff or repair may trust
451
+ // either id from this envelope.
452
+ if (envelope.protocolError !== null) {
453
+ return {
454
+ kind: "refused",
455
+ reason: "provider-protocol",
456
+ providerVersion: attested === null ? null : attested.version,
457
+ diagnostic: envelope.protocolError,
458
+ finalMessage: envelope.finalMessage,
459
+ };
460
+ }
461
+ // The minted-identity proof (A5): once the harness initialized, the id
462
+ // it announced must be the id the plane minted — anything else means
463
+ // the session on disk is not the session on record, and repair must
464
+ // never resume it. Usage above is already recorded: the spend happened.
465
+ if (startSessionId !== undefined &&
466
+ envelope.initObserved === true &&
467
+ envelope.sessionId !== startSessionId) {
468
+ return {
469
+ kind: "refused",
470
+ reason: "provider-protocol",
471
+ providerVersion: attested === null ? null : attested.version,
472
+ diagnostic: envelope.sessionId === null
473
+ ? "the harness initialized without announcing its session id"
474
+ : "the harness announced a session id different from the one it was started under",
475
+ finalMessage: envelope.finalMessage,
476
+ };
477
+ }
478
+ // An early transport announcement, a pre-existing resume identity, and
479
+ // the completed retained envelope must resolve to one canonical id. If
480
+ // either side is missing or different, no handoff or warm resume may use
481
+ // this run even though its spend remains on the record.
482
+ if (sessionIdentityProblem !== null) {
483
+ return {
484
+ kind: "refused",
485
+ reason: "provider-protocol",
486
+ providerVersion: attested === null ? null : attested.version,
487
+ diagnostic: sessionIdentityProblem,
488
+ finalMessage: envelope.finalMessage,
489
+ };
490
+ }
491
+ // A provider's explicit failure terminal always wins over its process exit
492
+ // status. Exit 0 only proves the wrapper closed cleanly; it cannot turn a
493
+ // failed model turn into a successful build.
494
+ if (result.code === 0 && envelope.structuralTerminal?.failed === true) {
495
+ return {
496
+ kind: "refused",
497
+ reason: "provider-protocol",
498
+ providerVersion: attested === null ? null : attested.version,
499
+ diagnostic: "the provider reported a failed terminal despite exiting with code 0",
500
+ finalMessage: envelope.finalMessage,
501
+ };
502
+ }
503
+ // The terminal contract (A4): when the audit requires it, a zero exit is
504
+ // believed ONLY with initialization observed and the structural success
505
+ // terminal present. A missing, truncated, or error-status terminal on
506
+ // exit 0 is the harness breaking its own protocol — never a completed
507
+ // build, and the handoff is never ingested.
508
+ if (auditOf(spec.provider).terminalContract === "required" &&
509
+ result.code === 0 &&
510
+ !(envelope.initObserved === true && envelope.promptConsumed === true)) {
511
+ return {
512
+ kind: "refused",
513
+ reason: "provider-protocol",
514
+ providerVersion: attested === null ? null : attested.version,
515
+ diagnostic: envelope.diagnostic ??
516
+ (envelope.initObserved === true
517
+ ? "the harness exited 0 without its success terminal — the stream ended mid-protocol"
518
+ : "the harness exited 0 without ever initializing"),
519
+ finalMessage: envelope.finalMessage,
520
+ };
521
+ }
522
+ return {
523
+ kind: "ran",
524
+ outcome: {
525
+ code: result.code,
526
+ stderr: result.stderr,
527
+ timedOut: result.timedOut,
528
+ notFound: result.notFound,
529
+ sessionId: envelope.sessionId,
530
+ finalMessage: envelope.finalMessage,
531
+ ending: envelope.ending ?? null,
532
+ usage: {
533
+ tokensIn: envelope.tokensIn,
534
+ tokensOut: envelope.tokensOut,
535
+ costUsd: envelope.costUsd,
536
+ },
537
+ // Gated on the run having NOTHING to show (Codex M5-M8 audit, C-8),
538
+ // where "nothing to show" is STRUCTURAL when the transport can say so
539
+ // (arc 1 finding 15): a retained error result carries diagnostic text
540
+ // in finalMessage, and that prose must not read as an agent's attempt.
541
+ // Transports without the consumption signal keep the historical
542
+ // no-final-message rule. A nonzero exit WITH a consumed prompt is a
543
+ // failed agent turn — the agent ran, spoke, and failed — and must be
544
+ // classified as that, never as the harness failing to come up.
545
+ initFailed: envelope.initObserved === false &&
546
+ (envelope.promptConsumed === null
547
+ ? envelope.finalMessage === null
548
+ : envelope.promptConsumed === false) &&
549
+ !result.timedOut &&
550
+ !result.notFound,
551
+ },
552
+ };
553
+ }
554
+ /**
555
+ * The HELD invocation gateway (Parity II Phase 2, spec v2 S0b): the only
556
+ * door to the held-session transport, symmetric with invokeAgent — same
557
+ * open-run verification, same provider-match rule (claude only in Phase
558
+ * 2: nothing else can hold), same stamp-before-spawn honesty. It returns
559
+ * the live handle rather than awaiting session end: ownership of the
560
+ * hold belongs to the coordinator, never to a promise chain that would
561
+ * stall the watch.
562
+ */
563
+ export async function invokeHeldAgent(store, runId, spec, argv, options) {
564
+ const clock = options.clock ?? (() => new Date());
565
+ const run = store.getRun(runId);
566
+ if (run === null || run.outcome !== null) {
567
+ throw new Error(`run ${runId} is not an open attempt — nothing spends without a run record that will outlive it`);
568
+ }
569
+ if (spec.provider !== "claude" || run.provider !== "claude") {
570
+ throw new Error(`run ${runId}: only claude can hold a session in Phase 2 — ${run.provider}/${spec.provider} cannot`);
571
+ }
572
+ const adapter = adapterFor("claude");
573
+ const { clock: _clock, starter, socketPath, cookie, graceMs, events, readyTimeoutMs, keyHome, ...runOptions } = options;
574
+ // The spawn leg of the runner gate (MCP spec v6) — held sessions are a
575
+ // provider spawn like any other; a lapsed custody throws, because the
576
+ // held road's contract is exceptions, not refusal values.
577
+ if (!store.proveRunnerCustodyForSpawn(runId, clock())) {
578
+ throw new Error(`run ${runId}: runner custody lapsed before the held spawn — the lease, the runner, or its repo binding no longer stands`);
579
+ }
580
+ // The held road reads the mode STRICTLY too (atomic authority closure),
581
+ // before the start stamp: a mode file that says neither word throws in
582
+ // its words — the held road's contract — and no spend is claimed.
583
+ const heldStrict = readAuthModeStrict("claude", keyHome);
584
+ if (!heldStrict.ok)
585
+ throw new Error(`run ${runId}: ${heldStrict.problem}`);
586
+ const heldMode = heldStrict.mode;
587
+ // The containment fence, same direction as the one-shot gateway: a
588
+ // required policy this runner cannot meet throws in its words before
589
+ // the start stamp, and no supervisor spawns.
590
+ const heldContainment = currentContainment();
591
+ if (heldContainment.refusal !== null)
592
+ throw new Error(`run ${runId}: ${heldContainment.refusal}`);
593
+ // The stamp precedes the spawn — same direction as the one-shot gateway.
594
+ store.stampProviderStart(runId, clock());
595
+ const heldKey = heldMode === "api-key" ? readProviderKey("claude", keyHome) ?? (process.env[PROVIDER_KEY_ENV.claude] || null) : null;
596
+ const heldSeen = { keySource: null, model: null, planWindows: false, decided: false };
597
+ // A session given a key bills the key from its first byte.
598
+ if (heldMode === "api-key")
599
+ store.fixRunBilling(runId, "api-key", clock());
600
+ const start = starter ?? startClaudeHeldSession;
601
+ const isolatedDb = isolatedAgentDatabase(runId);
602
+ // The project's tools (v80), as for every build: exactly its MCP servers;
603
+ // and the agent fence around Toolroll's own secrets.
604
+ const heldTools = runTools(store, runId, spec, keyHome, clock);
605
+ const heldFence = runFence(store, runId, keyHome);
606
+ argv = [...argv, ...heldTools.argv, ...(heldFence.length > 0 ? ["--settings", claudeFenceSettings(heldFence)] : [])];
607
+ const heldLaunch = fenceLaunch("claude", heldFence);
608
+ store.recordRunFence(runId, { method: heldLaunch.method, paths: heldFence.length }, clock());
609
+ let started;
610
+ let heldWitness;
611
+ let unknownTree = false;
612
+ let nativeHeld = false;
613
+ try {
614
+ started = await start(adapter.binary, argv, {
615
+ ...(heldLaunch.wrap.length === 0 ? {} : { fence: heldLaunch.wrap }),
616
+ ...runOptions,
617
+ beforeSpawn: () => {
618
+ if (store.applicableStopFor(runId) !== null)
619
+ return false;
620
+ heldWitness = store.reserveRunProcess(runId, clock(), false);
621
+ return true;
622
+ },
623
+ onSpawn: pid => {
624
+ store.recordRunProcess(runId, pid, clock(), false, heldWitness);
625
+ runOptions.onSpawn?.(pid);
626
+ },
627
+ onContainer: info => {
628
+ nativeHeld = true;
629
+ if (heldWitness !== undefined)
630
+ store.recordRunContainer(heldWitness, info.backend, info.id, info.identity);
631
+ runOptions.onContainer?.(info);
632
+ },
633
+ onContainerEmpty: () => {
634
+ if (heldWitness !== undefined)
635
+ store.markRunContainerEmpty(heldWitness, clock());
636
+ runOptions.onContainerEmpty?.();
637
+ },
638
+ onDescendant: (pid, group) => {
639
+ store.recordRunProcess(runId, pid, clock(), group);
640
+ runOptions.onDescendant?.(pid, group);
641
+ },
642
+ onDescendantWriteFailure: rows => runOptions.onDescendant === undefined
643
+ && preserveObservedProcesses(store, runId, clock(), rows),
644
+ onDescendantExit: (pid, group) => {
645
+ store.recordRunProcessExits(runId, clock(), { pid, group });
646
+ runOptions.onDescendantExit?.(pid, group);
647
+ },
648
+ onObservationFailure: failure => {
649
+ recordProcessObservationFailure(store, runId, clock(), failure);
650
+ runOptions.onObservationFailure?.(failure);
651
+ },
652
+ onUnknown: () => {
653
+ if (!nativeHeld && !unknownTree) {
654
+ store.reserveRunProcess(runId, clock());
655
+ unknownTree = true;
656
+ }
657
+ runOptions.onUnknown?.();
658
+ },
659
+ env: {
660
+ ...(runOptions.env ?? {}),
661
+ ...heldTools.env,
662
+ ...(heldKey === null ? {} : { [PROVIDER_KEY_ENV.claude]: heldKey }),
663
+ ...childDatabaseEnv(isolatedDb.file),
664
+ },
665
+ omitEnv: [
666
+ ...(runOptions.omitEnv ?? []),
667
+ ...adapter.extraOmitEnv,
668
+ ...(heldMode === "subscription" ? OWN_KEY_ENV.claude : []),
669
+ ],
670
+ socketPath,
671
+ cookie,
672
+ ...(graceMs === undefined ? {} : { graceMs }),
673
+ ...(readyTimeoutMs === undefined ? {} : { readyTimeoutMs }),
674
+ events: {
675
+ ...events,
676
+ // v105: the session's billing from its own stream. A sign-in can't change inside one process, and Claude reports
677
+ // its plan's windows on a process's first answer but later only when they move: so the first answer decides, and
678
+ // after it only a named key source or a cloud model can make the session a key.
679
+ onStreamEvent: event => {
680
+ if (event["type"] === "system" && event["subtype"] === "init") {
681
+ if (typeof event["apiKeySource"] === "string")
682
+ heldSeen.keySource = event["apiKeySource"].slice(0, 80);
683
+ if (typeof event["model"] === "string")
684
+ heldSeen.model = event["model"].slice(0, 200);
685
+ }
686
+ else if (event["type"] === "rate_limit_event") {
687
+ heldSeen.planWindows = true;
688
+ }
689
+ else if (event["type"] === "result" && event["is_error"] !== true && event["subtype"] === "success") {
690
+ // Only an answer decides: a failure before the first answer (a startup or sign-in error) says nothing yet.
691
+ const billing = heldMode === "api-key" ? "api-key"
692
+ : !heldSeen.decided ? claudeBillingFrom(heldSeen)
693
+ : claudeBillingFrom({ ...heldSeen, keySource: heldSeen.keySource === "none" ? null : heldSeen.keySource, planWindows: false }) === "api-key" ? "api-key" : null;
694
+ if (billing !== null) {
695
+ store.fixRunBilling(runId, billing, new Date());
696
+ if (!heldSeen.decided && heldMode === "subscription" && heldKey === null)
697
+ store.recordProviderBilling("claude", billing, new Date());
698
+ }
699
+ heldSeen.decided = true;
700
+ }
701
+ events?.onStreamEvent?.(event);
702
+ },
703
+ onSessionId: id => {
704
+ const normalized = transportSessionId(id);
705
+ if (normalized === null)
706
+ return;
707
+ store.stampRun(runId, { sessionId: normalized });
708
+ try {
709
+ events?.onSessionId?.(normalized);
710
+ }
711
+ catch {
712
+ // Observational.
713
+ }
714
+ },
715
+ },
716
+ });
717
+ }
718
+ catch (error) {
719
+ removeAgentDatabase(isolatedDb.dir);
720
+ heldTools.cleanup();
721
+ throw error;
722
+ }
723
+ if (!started.ok) {
724
+ removeAgentDatabase(isolatedDb.dir);
725
+ heldTools.cleanup();
726
+ }
727
+ else {
728
+ void started.handle.exited.then(() => { removeAgentDatabase(isolatedDb.dir); heldTools.cleanup(); }, () => { removeAgentDatabase(isolatedDb.dir); heldTools.cleanup(); });
729
+ }
730
+ return started;
731
+ }
732
+ /** The paths this run's agent may not reach: the state folder around the live database (but not its own worktree) and ~/.toolroll (or its older name). */
733
+ function runFence(store, runId, keyHome) {
734
+ try {
735
+ // A plane always has a database file; an in-memory store (a test) has nothing to fence.
736
+ const databaseFile = store.databaseFile();
737
+ if (databaseFile === null)
738
+ return [];
739
+ return agentFence({ databaseFile, worktree: store.getRun(runId)?.worktree ?? null, ...(keyHome === undefined ? {} : { home: keyHome }) });
740
+ }
741
+ catch {
742
+ return [];
743
+ }
744
+ }
745
+ /** How the fence reaches a provider: Codex through its own sandbox profile
746
+ * (already in its argv; wrapping it would nest sandboxes), Claude and
747
+ * Gemini inside the macOS sandbox the spawn road applies (`wrap`), Claude
748
+ * elsewhere through its own file-tool rules only. */
749
+ function fenceLaunch(provider, fence) {
750
+ if (fence.length === 0)
751
+ return { wrap: [], method: "none" };
752
+ if (provider === "codex" || provider === "openrouter")
753
+ return { wrap: [], method: "codex-profile" };
754
+ if (macosFenceAvailable())
755
+ return { wrap: fence, method: "macos-sandbox" };
756
+ if (linuxFenceAvailable())
757
+ return { wrap: fence, method: "linux-bubblewrap" };
758
+ return { wrap: [], method: provider === "claude" ? "claude-rules" : "none" };
759
+ }
760
+ /** One launch's tools, or — when they cannot be prepared — the same isolation with none, never the operator's global servers. */
761
+ function runTools(store, runId, spec, keyHome, clock) {
762
+ try {
763
+ return prepareRunTools(store, runId, spec.provider, { ...(keyHome === undefined ? {} : { home: keyHome }), now: clock(), includeModel: spec.model === null });
764
+ }
765
+ catch {
766
+ return noToolsArgs(spec.provider, spec.model === null);
767
+ }
768
+ }
769
+ /** How Codex says it's signed in (`codex login status`): a ChatGPT sign-in is its plan; a key or Bedrock is a key;
770
+ * null when it says neither (not signed in, or it didn't answer). */
771
+ async function codexLoginBilling(binary, omitEnv) {
772
+ try {
773
+ const status = await runCommand(binary, ["login", "status"], { timeoutMs: 15_000, omitEnv, processGroup: true });
774
+ const said = `${status.stdout}\n${status.stderr}`;
775
+ return /using chatgpt/i.test(said) ? "subscription" : /api key|bedrock/i.test(said) ? "api-key" : null;
776
+ }
777
+ catch {
778
+ return null;
779
+ }
780
+ }