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
@@ -0,0 +1,768 @@
1
+ import { createHash, randomUUID } from 'node:crypto';
2
+ import { mkdirSync, chmodSync, realpathSync } from 'node:fs';
3
+ import { dirname, join, resolve } from 'node:path';
4
+ import { hostname } from 'node:os';
5
+ import { provenDeadByBootChange } from './boot-identity.js';
6
+ import { containerEmptiness } from './container-state.js';
7
+ import { sqliteRuntime } from './sqlite-runtime.js';
8
+ import { run } from './exec.js';
9
+ import { processMayBeAlive } from './process-liveness.js';
10
+ import { createCodexCodingProvider } from './coding-provider.js';
11
+ import { verifyCodingContext } from './coding-context.js';
12
+ export class CodingActionError extends Error {
13
+ delivery;
14
+ sessionId;
15
+ constructor(message, delivery, sessionId) {
16
+ super(message);
17
+ this.delivery = delivery;
18
+ this.sessionId = sessionId;
19
+ this.name = 'CodingActionError';
20
+ }
21
+ }
22
+ export class CodingStateConflictError extends CodingActionError {
23
+ constructor(id) { super('This session changed. Inspect its latest activity before acting again.', 'rejected', id); }
24
+ }
25
+ const object = (v) => typeof v === 'object' && v !== null && !Array.isArray(v) ? v : {};
26
+ const string = (v) => typeof v === 'string' ? v : '';
27
+ const now = () => new Date().toISOString();
28
+ const digest = (text) => createHash('sha256').update(text).digest('hex');
29
+ const keyPattern = /^[a-zA-Z0-9_-]{16,100}$/;
30
+ const CODING_INSTRUCTIONS = 'Work in this dedicated project checkout. Keep the conversation concise, inspect the result, and run focused checks for changed behavior. Do not publish, push, merge, deploy, or change other projects unless the user explicitly asks. Report unverified behavior honestly.';
31
+ const busy = new Set(['starting', 'working', 'needs-input', 'stopping']);
32
+ function actionError(error, fallback, sessionId) {
33
+ if (error instanceof CodingActionError)
34
+ return error;
35
+ const outcome = object(error)['outcomeUnknown'];
36
+ return new CodingActionError(error instanceof Error ? error.message : 'The coding action could not finish.', outcome === true ? 'unknown' : outcome === false ? 'rejected' : fallback, sessionId);
37
+ }
38
+ /** A native session catalog, separate from the unattended task/proof ledger. */
39
+ export class CodingWorkspace {
40
+ options;
41
+ db;
42
+ owner = randomUUID();
43
+ provider = null;
44
+ unsubscribe = null;
45
+ operations = new Set();
46
+ commands = new Set();
47
+ commandWaiters = new Set();
48
+ operationWaiters = new Set();
49
+ loaded = new Set();
50
+ closed = false;
51
+ closing = false;
52
+ closePromise = null;
53
+ recoveryRequired = false;
54
+ recovering = false;
55
+ persistenceFailed = false;
56
+ pendingItems = new Map();
57
+ flushTimer = null;
58
+ constructor(options) {
59
+ this.options = options;
60
+ mkdirSync(dirname(options.database), { recursive: true, mode: 0o700 });
61
+ this.db = new (sqliteRuntime().DatabaseSync)(options.database);
62
+ chmodSync(options.database, 0o600);
63
+ this.db.exec(`PRAGMA journal_mode=WAL; PRAGMA busy_timeout=3000;
64
+ CREATE TABLE IF NOT EXISTS coding_owner (singleton INTEGER PRIMARY KEY CHECK(singleton=1), token TEXT NOT NULL, pid INTEGER NOT NULL, native_pid INTEGER, clean INTEGER NOT NULL);
65
+ CREATE TABLE IF NOT EXISTS coding_session (id TEXT PRIMARY KEY, owner TEXT NOT NULL, generation INTEGER NOT NULL, repo TEXT NOT NULL, document TEXT NOT NULL, revision INTEGER NOT NULL DEFAULT 0);
66
+ CREATE TABLE IF NOT EXISTS coding_item (session TEXT NOT NULL, id TEXT NOT NULL, position INTEGER PRIMARY KEY AUTOINCREMENT, payload TEXT NOT NULL, UNIQUE(session,id));
67
+ CREATE TABLE IF NOT EXISTS coding_request (token TEXT PRIMARY KEY, session TEXT NOT NULL, rpc_id TEXT NOT NULL, payload TEXT NOT NULL, status TEXT NOT NULL);
68
+ CREATE TABLE IF NOT EXISTS coding_submission (session TEXT NOT NULL, key TEXT NOT NULL, digest TEXT NOT NULL, status TEXT NOT NULL, error TEXT, PRIMARY KEY(session,key));`);
69
+ this.db.exec('CREATE TABLE IF NOT EXISTS coding_custody (singleton INTEGER PRIMARY KEY CHECK(singleton=1), payload TEXT NOT NULL)');
70
+ this.db.exec('CREATE TABLE IF NOT EXISTS coding_command (owner TEXT NOT NULL, generation INTEGER NOT NULL, key TEXT NOT NULL, digest TEXT NOT NULL, status TEXT NOT NULL, session TEXT, message TEXT, PRIMARY KEY(owner,generation,key))');
71
+ this.db.exec("CREATE INDEX IF NOT EXISTS coding_native_thread ON coding_session(json_extract(document,'$.nativeThreadId'))");
72
+ this.db.exec('BEGIN IMMEDIATE');
73
+ try {
74
+ const prior = this.db.prepare('SELECT * FROM coding_owner WHERE singleton=1').get();
75
+ if (prior && prior['token'] !== '' && processMayBeAlive(Number(prior['pid']), false))
76
+ throw Error('Another Toolroll server owns the coding workspace. Use that server or stop it first.');
77
+ const clean = !prior || prior['clean'] === 1;
78
+ this.recoveryRequired = !clean;
79
+ this.db.prepare('INSERT OR REPLACE INTO coding_owner VALUES(1,?,?,?,?)').run(this.owner, process.pid, prior?.['native_pid'] ?? null, clean ? 1 : 0);
80
+ for (const row of this.db.prepare('SELECT document FROM coding_session').all()) {
81
+ const session = JSON.parse(String(row['document']));
82
+ if (session.status !== 'closed' && (!clean || busy.has(session.status)))
83
+ this.save({ ...session, status: clean ? 'interrupted' : 'uncertain', turnId: null, error: clean ? null : 'The previous server stopped without a verified process exit. Work is preserved; session recovery needs a process check before resuming.' });
84
+ }
85
+ this.db.prepare("UPDATE coding_request SET status='withdrawn' WHERE status='pending'").run();
86
+ this.db.exec('COMMIT');
87
+ }
88
+ catch (error) {
89
+ this.db.exec('ROLLBACK');
90
+ this.db.close();
91
+ throw error;
92
+ }
93
+ }
94
+ authorize(actor, repo) {
95
+ if (this.options.authorize && !this.options.authorize(actor, repo))
96
+ throw Error('Your access to this project changed. Sign in again.');
97
+ }
98
+ read(id) {
99
+ const row = this.db.prepare('SELECT document FROM coding_session WHERE id=?').get(id);
100
+ if (!row)
101
+ throw Error('Coding session unavailable.');
102
+ return JSON.parse(String(row['document']));
103
+ }
104
+ admit() {
105
+ if (this.closed || this.closing || this.persistenceFailed)
106
+ throw new CodingActionError('The coding server is closing or cannot save activity. Reopen it after recovery.', 'rejected');
107
+ if (this.options.admissionPaused?.())
108
+ throw new CodingActionError('Toolroll is preparing an update. Finish or stop current work; new coding turns can start after the update.', 'rejected');
109
+ }
110
+ releaseOperation(id) {
111
+ this.operations.delete(id);
112
+ if (!this.operations.size) {
113
+ for (const resolve of this.operationWaiters)
114
+ resolve();
115
+ this.operationWaiters.clear();
116
+ }
117
+ }
118
+ list(actor) {
119
+ return this.db.prepare('SELECT document FROM coding_session WHERE owner=? AND generation=? ORDER BY rowid DESC').all(actor.name, actor.generation).map(row => JSON.parse(String(row['document'])));
120
+ }
121
+ /** Inspect a fixed metadata window before loading admitted documents.
122
+ * A filtered window can be short without proving the entire list empty. */
123
+ listBounded(actor, options) {
124
+ const rows = options.repo === undefined
125
+ ? this.db.prepare('SELECT id,repo FROM coding_session WHERE owner=? AND generation=? ORDER BY rowid DESC LIMIT 101').all(actor.name, actor.generation)
126
+ : this.db.prepare('SELECT id,repo FROM coding_session WHERE owner=? AND generation=? AND repo=? ORDER BY rowid DESC LIMIT 101').all(actor.name, actor.generation, options.repo);
127
+ const window = rows.slice(0, 100);
128
+ const admitted = window.filter(row => options.authorized(String(row['repo'])));
129
+ const selected = admitted.slice(0, Math.min(100, Math.max(1, options.limit)));
130
+ return { sessions: selected.map(row => this.get(String(row['id']), actor)), truncated: rows.length > window.length || admitted.length > selected.length };
131
+ }
132
+ get(id, actor) {
133
+ const row = this.db.prepare('SELECT document FROM coding_session WHERE id=? AND owner=? AND generation=?').get(id, actor.name, actor.generation);
134
+ if (!row)
135
+ throw Error('This coding session is not available to your account.');
136
+ const session = JSON.parse(String(row['document']));
137
+ this.authorize(actor, session.repo);
138
+ return session;
139
+ }
140
+ snapshot(id, actor) {
141
+ const session = this.get(id, actor);
142
+ const items = this.db.prepare('SELECT payload FROM coding_item WHERE session=? ORDER BY position').all(id).map(row => JSON.parse(String(row['payload'])));
143
+ const requests = this.db.prepare("SELECT payload FROM coding_request WHERE session=? AND status='pending' ORDER BY rowid").all(id).map(row => JSON.parse(String(row['payload'])));
144
+ const revision = Number(this.db.prepare('SELECT revision FROM coding_session WHERE id=?').get(id)?.['revision']);
145
+ return { session, items, requests, revision };
146
+ }
147
+ /** Bounded transport read: query only the recent window plus one row
148
+ * for omission detection. The browser's full snapshot remains unchanged. */
149
+ snapshotBounded(id, actor) {
150
+ const session = this.get(id, actor);
151
+ const itemRows = this.db.prepare('SELECT payload FROM coding_item WHERE session=? ORDER BY position DESC LIMIT 101').all(id);
152
+ const requestRows = this.db.prepare("SELECT payload FROM coding_request WHERE session=? AND status='pending' ORDER BY rowid LIMIT 51").all(id);
153
+ const items = itemRows.slice(0, 100).reverse().map(row => JSON.parse(String(row['payload'])));
154
+ const requests = requestRows.slice(0, 50).map(row => JSON.parse(String(row['payload'])));
155
+ const revision = Number(this.db.prepare('SELECT revision FROM coding_session WHERE id=?').get(id)?.['revision']);
156
+ return { session, items, requests, revision, truncated: itemRows.length > items.length || requestRows.length > requests.length };
157
+ }
158
+ revision(id, actor) {
159
+ this.get(id, actor);
160
+ return Number(this.db.prepare('SELECT revision FROM coding_session WHERE id=?').get(id)?.['revision']);
161
+ }
162
+ assertExpected(id, actor, expected) {
163
+ const session = this.get(id, actor);
164
+ if (expected && (expected.revision !== this.revision(id, actor) || expected.nativeThreadId !== session.nativeThreadId || expected.turnId !== session.turnId))
165
+ throw new CodingStateConflictError(id);
166
+ }
167
+ /** Persist acceptance before calling the native owner. Repeating a key only
168
+ * reads its receipt; it never repeats a mutation, including after restart. */
169
+ async command(actor, key, request, check, perform) {
170
+ if (!keyPattern.test(key))
171
+ throw Error('This operation needs a valid request ID.');
172
+ check();
173
+ const fingerprint = digest(request), active = `${actor.name}:${actor.generation}:${key}`;
174
+ const row = this.db.prepare('SELECT * FROM coding_command WHERE owner=? AND generation=? AND key=?').get(actor.name, actor.generation, key);
175
+ if (row) {
176
+ if (row['digest'] !== fingerprint)
177
+ throw Error('This request ID was already used for a different operation.');
178
+ const status = String(row['status']);
179
+ return { status: status === 'pending' && !this.commands.has(active) ? 'uncertain' : status, sessionId: row['session'] === null ? null : String(row['session']), message: row['message'] === null ? null : String(row['message']) };
180
+ }
181
+ if (this.closed || this.closing || this.persistenceFailed)
182
+ throw Error('The coding server cannot save activity.');
183
+ this.db.prepare("INSERT INTO coding_command VALUES(?,?,?,?,'pending',NULL,NULL)").run(actor.name, actor.generation, key, fingerprint);
184
+ this.commands.add(active);
185
+ try {
186
+ const sessionId = await perform();
187
+ this.db.prepare("UPDATE coding_command SET status='accepted',session=?,message=NULL WHERE owner=? AND generation=? AND key=?").run(sessionId, actor.name, actor.generation, key);
188
+ return { status: 'accepted', sessionId, message: null };
189
+ }
190
+ catch (error) {
191
+ const failure = actionError(error, 'unknown');
192
+ const status = failure.delivery === 'rejected' ? 'rejected' : 'uncertain';
193
+ this.db.prepare('UPDATE coding_command SET status=?,session=?,message=? WHERE owner=? AND generation=? AND key=?').run(status, failure.sessionId ?? null, failure.message, actor.name, actor.generation, key);
194
+ return { status, sessionId: failure.sessionId ?? null, message: failure.message };
195
+ }
196
+ finally {
197
+ this.commands.delete(active);
198
+ if (!this.commands.size) {
199
+ for (const done of this.commandWaiters)
200
+ done();
201
+ this.commandWaiters.clear();
202
+ }
203
+ }
204
+ }
205
+ save(session) {
206
+ session.updatedAt = now();
207
+ this.db.prepare('INSERT INTO coding_session(id,owner,generation,repo,document) VALUES(?,?,?,?,?) ON CONFLICT(id) DO UPDATE SET document=excluded.document,revision=revision+1').run(session.id, session.owner, session.generation, session.repo, JSON.stringify(session));
208
+ }
209
+ byThread(thread) {
210
+ if (typeof thread !== 'string')
211
+ return null;
212
+ const row = this.db.prepare("SELECT document FROM coding_session WHERE json_extract(document,'$.nativeThreadId')=?").get(thread);
213
+ return row ? JSON.parse(String(row['document'])) : null;
214
+ }
215
+ putItem(session, item) {
216
+ // Re-reading unchanged native history must not invalidate the request
217
+ // that loaded it. New or changed history still requires a fresh look.
218
+ const saved = this.db.prepare('INSERT INTO coding_item(session,id,payload) VALUES(?,?,?) ON CONFLICT(session,id) DO UPDATE SET payload=excluded.payload WHERE coding_item.payload <> excluded.payload').run(session, item.id, JSON.stringify(item));
219
+ if (saved.changes)
220
+ this.db.prepare('UPDATE coding_session SET revision=revision+1 WHERE id=?').run(session);
221
+ if (item.type === 'userMessage' && item.clientId)
222
+ this.db.prepare("UPDATE coding_submission SET status='accepted',error=NULL WHERE session=? AND key=? AND digest=?").run(session, item.clientId, digest(item.text));
223
+ }
224
+ flush() {
225
+ if (this.flushTimer !== null)
226
+ clearTimeout(this.flushTimer);
227
+ this.flushTimer = null;
228
+ for (const { session, item } of this.pendingItems.values())
229
+ this.putItem(session, item);
230
+ this.pendingItems.clear();
231
+ }
232
+ item(raw) {
233
+ const item = object(raw), id = string(item['id']), type = string(item['type']);
234
+ if (!id || !type)
235
+ return null;
236
+ if (type === 'reasoning')
237
+ return null;
238
+ let text = string(item['text']);
239
+ if (type === 'userMessage')
240
+ text = (Array.isArray(item['content']) ? item['content'] : []).map(part => string(object(part)['text'])).filter(Boolean).join('\n');
241
+ if (type === 'commandExecution')
242
+ text = [string(item['command']), string(item['aggregatedOutput'])].filter(Boolean).join('\n\n');
243
+ if (type === 'fileChange')
244
+ text = (Array.isArray(item['changes']) ? item['changes'] : []).map(change => string(object(change)['path'])).join('\n');
245
+ if (!text)
246
+ text = string(item['summary']) || type.replace(/([a-z])([A-Z])/g, '$1 $2');
247
+ // Detailed command output stays in native history. The workspace is a bounded projection.
248
+ if (text.length > 100_000)
249
+ text = text.slice(0, 100_000) + '\n[More output is available in the native session.]';
250
+ const clientId = string(item['clientId']) || string(item['clientUserMessageId']);
251
+ return { id, type, text, status: string(item['status']) || null, ...(type === 'userMessage' && clientId ? { clientId } : {}) };
252
+ }
253
+ native() {
254
+ if (this.closed || this.closing || this.persistenceFailed)
255
+ throw Error('The coding server cannot save activity. Reopen the workspace after the server recovers.');
256
+ if (this.recoveryRequired)
257
+ throw Error('The previous agent process needs a recovery check before starting more coding work. Its saved process record has been preserved.');
258
+ if (this.provider === null) {
259
+ this.provider = (this.options.provider ?? (() => createCodexCodingProvider()))();
260
+ this.unsubscribe = this.provider.subscribe(event => this.event(event));
261
+ }
262
+ return this.provider;
263
+ }
264
+ async request(method, params) {
265
+ const provider = this.native();
266
+ this.db.prepare('UPDATE coding_owner SET clean=0 WHERE token=?').run(this.owner);
267
+ try {
268
+ const pending = provider.request(method, params);
269
+ this.db.prepare('UPDATE coding_owner SET native_pid=? WHERE token=?').run(provider.processId(), this.owner);
270
+ return await pending;
271
+ }
272
+ catch (error) {
273
+ if (error instanceof Error && /^thread [a-f0-9-]+ already has an active writer$/.test(error.message))
274
+ throw Error('This conversation is open in another Codex app. Close it there, then resume here. Your changes and draft are preserved.', { cause: error });
275
+ throw error;
276
+ }
277
+ finally {
278
+ this.db.prepare('UPDATE coding_owner SET native_pid=? WHERE token=?').run(provider.processId(), this.owner);
279
+ }
280
+ }
281
+ event(event) {
282
+ if (this.closed)
283
+ return;
284
+ this.db.prepare('UPDATE coding_owner SET native_pid=? WHERE token=?').run(this.provider?.processId() ?? null, this.owner);
285
+ if (event.kind === 'exit') {
286
+ this.flush();
287
+ this.loaded.clear();
288
+ for (const row of this.db.prepare('SELECT document FROM coding_session').all()) {
289
+ const session = JSON.parse(String(row['document']));
290
+ if (busy.has(session.status))
291
+ this.save({ ...session, status: 'uncertain', error: 'The agent connection closed. Work is preserved; delivery and process exit need to be checked before continuing.' });
292
+ }
293
+ this.db.prepare("UPDATE coding_request SET status='withdrawn' WHERE status='pending'").run();
294
+ return;
295
+ }
296
+ if (event.kind === 'custody') {
297
+ this.db.prepare('INSERT OR REPLACE INTO coding_custody VALUES(1,?)').run(JSON.stringify(event.custody));
298
+ return;
299
+ }
300
+ const p = event.params;
301
+ const session = this.byThread(p['threadId']);
302
+ if (event.kind === 'request') {
303
+ if (!session) {
304
+ this.native().reject(event.id, -32602, 'The requested coding session is not registered.');
305
+ return;
306
+ }
307
+ const kind = event.method === 'item/commandExecution/requestApproval' ? 'command' : event.method === 'item/fileChange/requestApproval' ? 'files' : event.method === 'item/tool/requestUserInput' ? 'questions' : null;
308
+ if (kind === null) {
309
+ this.native().reject(event.id, -32601, 'This interaction is not supported by the coding workspace.');
310
+ this.save({ ...session, error: `This agent requested an unsupported interaction: ${event.method}.` });
311
+ return;
312
+ }
313
+ const token = randomUUID();
314
+ const questions = (Array.isArray(p['questions']) ? p['questions'] : []).map(raw => {
315
+ const q = object(raw);
316
+ return { id: string(q['id']), header: string(q['header']), question: string(q['question']), options: (Array.isArray(q['options']) ? q['options'] : []).map(rawOption => { const o = object(rawOption); return { label: string(o['label']), description: string(o['description']) }; }) };
317
+ });
318
+ const request = { id: token, kind, method: event.method, title: kind === 'command' ? 'Allow this command?' : kind === 'files' ? 'Allow these file changes?' : 'The agent needs your answer', detail: kind === 'questions' ? '' : JSON.stringify(p, null, 2), questions };
319
+ this.db.prepare("INSERT INTO coding_request VALUES(?,?,?,?,'pending')").run(token, session.id, JSON.stringify(event.id), JSON.stringify(request));
320
+ this.save({ ...session, status: 'needs-input' });
321
+ return;
322
+ }
323
+ if (!session)
324
+ return;
325
+ if (event.method === 'serverRequest/resolved') {
326
+ this.db.prepare("UPDATE coding_request SET status='withdrawn' WHERE session=? AND rpc_id=? AND status='pending'").run(session.id, JSON.stringify(p['requestId']));
327
+ const pending = this.db.prepare("SELECT 1 FROM coding_request WHERE session=? AND status='pending'").get(session.id);
328
+ this.save({ ...session, status: session.status === 'needs-input' && !pending ? 'working' : session.status });
329
+ }
330
+ else if (event.method === 'turn/started')
331
+ this.save({ ...session, turnId: string(object(p['turn'])['id']) || session.turnId, status: 'working', error: null });
332
+ else if (event.method === 'turn/completed') {
333
+ this.flush();
334
+ const turn = object(p['turn']), status = string(turn['status']);
335
+ this.db.prepare("UPDATE coding_request SET status='withdrawn' WHERE session=? AND status='pending'").run(session.id);
336
+ this.save({ ...session, turnId: null, status: status === 'completed' ? 'ready' : status === 'interrupted' ? 'interrupted' : 'failed', error: status === 'failed' ? string(object(turn['error'])['message']) || 'The agent could not finish this turn.' : null });
337
+ }
338
+ else if (event.method === 'item/started' || event.method === 'item/completed') {
339
+ const item = this.item(p['item']);
340
+ if (item) {
341
+ this.pendingItems.delete(`${session.id}:${item.id}`);
342
+ this.putItem(session.id, item);
343
+ }
344
+ }
345
+ else if (event.method === 'item/agentMessage/delta') {
346
+ const id = string(p['itemId']), key = `${session.id}:${id}`;
347
+ const prior = this.pendingItems.get(key)?.item ?? (() => { const row = this.db.prepare('SELECT payload FROM coding_item WHERE session=? AND id=?').get(session.id, id); return row ? JSON.parse(String(row['payload'])) : { id, type: 'agentMessage', text: '', status: 'inProgress' }; })();
348
+ prior.text += string(p['delta']);
349
+ if (prior.text.length > 100_000)
350
+ prior.text = prior.text.slice(0, 100_000);
351
+ this.pendingItems.set(key, { session: session.id, item: prior });
352
+ if (this.flushTimer === null)
353
+ this.flushTimer = setTimeout(() => {
354
+ try {
355
+ this.flush();
356
+ }
357
+ catch {
358
+ this.persistenceFailed = true;
359
+ this.recoveryRequired = true;
360
+ void this.provider?.close().catch(() => { });
361
+ }
362
+ }, 50);
363
+ }
364
+ }
365
+ async git(repo, args) {
366
+ const result = await run('git', ['--no-optional-locks', '-c', 'core.hooksPath=/dev/null', ...args], { cwd: repo, timeoutMs: 60_000, maxBuffer: 1_048_576 });
367
+ if (result.code !== 0)
368
+ throw Error('Git could not complete this workspace operation. Check the repository and try again.');
369
+ return result.stdout;
370
+ }
371
+ async start(actor, input) {
372
+ this.validateMessage(input.prompt, input.requestId);
373
+ if (input.model !== null && !/^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$/.test(input.model))
374
+ throw new CodingActionError('Enter a valid model name or leave the model blank.', 'rejected');
375
+ const title = input.title.trim().slice(0, 160) || input.prompt.trim().split('\n')[0].slice(0, 100);
376
+ const id = digest(`${actor.name}:${actor.generation}:${input.requestId}`).slice(0, 32);
377
+ const previous = this.list(actor).find(s => s.id === id);
378
+ if (previous) {
379
+ this.authorize(actor, previous.repo);
380
+ const receipt = this.db.prepare('SELECT * FROM coding_submission WHERE session=? AND key=?').get(id, input.requestId);
381
+ if (previous.repo !== realpathSync(input.repo) || previous.title !== title || previous.model !== input.model || receipt?.['digest'] !== digest(input.prompt))
382
+ throw new CodingActionError('This submission ID was already used for a different request.', 'rejected', id);
383
+ if (receipt['status'] === 'accepted')
384
+ return previous;
385
+ throw this.receiptError(id, receipt);
386
+ }
387
+ if (this.operations.has(id))
388
+ throw new CodingActionError('This session is still starting. Check its delivery again shortly.', 'pending');
389
+ this.admit();
390
+ if (this.recoveryRequired || this.recovering)
391
+ throw new CodingActionError('The coding workspace needs recovery before another session can start. Existing work is preserved.', 'unknown');
392
+ this.operations.add(id);
393
+ let session = null;
394
+ let saved = false, nativeStartupAttempted = false;
395
+ try {
396
+ const repo = realpathSync(input.repo);
397
+ this.authorize(actor, repo);
398
+ const root = (await this.git(repo, ['rev-parse', '--show-toplevel'])).trim();
399
+ if (realpathSync(root) !== repo)
400
+ throw Error('Choose the root of a Git project.');
401
+ const base = (await this.git(repo, ['rev-parse', '--verify', 'HEAD^{commit}'])).trim();
402
+ const branch = `toolroll/code-${id}`, worktree = join(this.options.worktreeRoot, id);
403
+ session = { id, owner: actor.name, generation: actor.generation, repo, title, provider: 'codex', model: input.model, branch, base, worktree, nativeThreadId: null, turnId: null, status: 'starting', error: null, createdAt: now(), updatedAt: now(), initialRequest: { requestId: input.requestId, prompt: input.prompt } };
404
+ this.authorize(actor, repo);
405
+ const context = this.options.context?.({ repo, actor: actor.name, baseRevision: base, prompt: input.prompt });
406
+ if (context) {
407
+ verifyCodingContext(context, repo, base);
408
+ session.context = context;
409
+ }
410
+ this.save(session);
411
+ saved = true;
412
+ this.db.prepare("INSERT INTO coding_submission VALUES(?,?,?,'preparing',NULL)").run(id, input.requestId, digest(input.prompt));
413
+ mkdirSync(this.options.worktreeRoot, { recursive: true, mode: 0o700 });
414
+ await this.git(repo, ['worktree', 'add', '-b', branch, '--', worktree, base]);
415
+ this.authorize(actor, repo);
416
+ this.admit();
417
+ nativeStartupAttempted = true;
418
+ const result = object(await this.request('thread/start', { cwd: worktree, approvalPolicy: 'on-request', approvalsReviewer: 'user', sandbox: 'workspace-write', ...(input.model ? { model: input.model } : {}), developerInstructions: CODING_INSTRUCTIONS + (context?.text ?? ''), persistExtendedHistory: true }));
419
+ const thread = object(result['thread']), nativeThreadId = string(thread['id']);
420
+ if (!nativeThreadId)
421
+ throw Error('The agent did not return a session identity.');
422
+ session = { ...session, nativeThreadId, status: 'ready' };
423
+ this.save(session);
424
+ this.loaded.add(id);
425
+ await this.sendMessage(id, actor, input.prompt, input.requestId, true);
426
+ return this.get(id, actor);
427
+ }
428
+ catch (error) {
429
+ const accepted = saved && this.db.prepare("SELECT 1 FROM coding_submission WHERE session=? AND key=? AND status='accepted'").get(id, input.requestId);
430
+ const failure = accepted ? new CodingActionError(error instanceof Error ? error.message : 'The response could not be confirmed. Check the saved session.', 'unknown', id) : actionError(error, nativeStartupAttempted && !session?.nativeThreadId ? 'unknown' : 'rejected', saved ? id : undefined);
431
+ if (saved) {
432
+ this.db.prepare("UPDATE coding_submission SET status=?,error=? WHERE session=? AND key=? AND status!='accepted'").run(failure.delivery === 'rejected' ? 'failed' : 'uncertain', failure.message, id, input.requestId);
433
+ const current = this.read(id);
434
+ this.save({ ...current, status: accepted ? current.status : failure.delivery === 'unknown' || current.status === 'uncertain' ? 'uncertain' : 'failed', error: failure.message });
435
+ }
436
+ throw failure;
437
+ }
438
+ finally {
439
+ this.releaseOperation(id);
440
+ }
441
+ }
442
+ validateMessage(prompt, key) {
443
+ if (!prompt.trim() || Buffer.byteLength(prompt) > 64_000)
444
+ throw new CodingActionError('Write a request of up to 64 KB.', 'rejected');
445
+ if (!keyPattern.test(key))
446
+ throw new CodingActionError('This message needs a valid submission ID. Reload the page.', 'rejected');
447
+ }
448
+ receiptError(id, receipt) {
449
+ const status = receipt?.['status'];
450
+ if (status === 'failed')
451
+ return new CodingActionError(string(receipt?.['error']) || 'The agent rejected this request. Retry explicitly with a new message ID.', 'rejected', id);
452
+ if ((status === 'pending' || status === 'preparing') && this.operations.has(id))
453
+ return new CodingActionError('This request is still in progress. Check its delivery again shortly.', 'pending', id);
454
+ return new CodingActionError(string(receipt?.['error']) || 'Delivery has not been confirmed. Inspect the saved session before continuing; this request will not be sent again automatically.', 'unknown', id);
455
+ }
456
+ async load(session) {
457
+ if (session.status === 'uncertain')
458
+ throw Error(session.error || 'Resolve the uncertain delivery before continuing.');
459
+ if (this.loaded.has(session.id))
460
+ return;
461
+ if (!session.nativeThreadId)
462
+ throw Error('This session has no native conversation to resume. Start a new session.');
463
+ await this.verifyWorktree(session);
464
+ this.authorize({ name: session.owner, generation: session.generation }, session.repo);
465
+ if (session.context)
466
+ verifyCodingContext(session.context, session.repo, session.base);
467
+ const result = object(await this.request('thread/resume', { threadId: session.nativeThreadId, cwd: session.worktree, approvalPolicy: 'on-request', approvalsReviewer: 'user', sandbox: 'workspace-write', ...(session.model ? { model: session.model } : {}), developerInstructions: CODING_INSTRUCTIONS + (session.context?.text ?? ''), persistExtendedHistory: true }));
468
+ const thread = object(result['thread']);
469
+ if (thread['id'] !== session.nativeThreadId)
470
+ throw Error('The agent returned a different conversation. Continuity could not be verified.');
471
+ this.importHistory(session.id, thread);
472
+ this.authorize({ name: session.owner, generation: session.generation }, session.repo);
473
+ this.loaded.add(session.id);
474
+ }
475
+ importHistory(id, thread) {
476
+ for (const rawTurn of Array.isArray(thread['turns']) ? thread['turns'] : [])
477
+ for (const raw of Array.isArray(object(rawTurn)['items']) ? object(rawTurn)['items'] : []) {
478
+ const item = this.item(raw);
479
+ if (item)
480
+ this.putItem(id, item);
481
+ }
482
+ }
483
+ async verifyWorktree(session) {
484
+ const [branch, common, original] = await Promise.all([
485
+ this.git(session.worktree, ['symbolic-ref', '--short', 'HEAD']),
486
+ this.git(session.worktree, ['rev-parse', '--path-format=absolute', '--git-common-dir']),
487
+ this.git(session.repo, ['rev-parse', '--path-format=absolute', '--git-common-dir']),
488
+ ]);
489
+ if (branch.trim() !== session.branch || realpathSync(common.trim()) !== realpathSync(original.trim()))
490
+ throw Error('This checkout no longer matches the saved project and branch. Restore that checkout before resuming.');
491
+ await this.git(session.worktree, ['merge-base', '--is-ancestor', session.base, 'HEAD']);
492
+ }
493
+ priorProcessesGone() {
494
+ const row = this.db.prepare('SELECT payload FROM coding_custody WHERE singleton=1').get();
495
+ if (!row)
496
+ return false;
497
+ try {
498
+ const c = JSON.parse(String(row['payload']));
499
+ if (c.host !== hostname() || !Array.isArray(c.descendants) || typeof c.observationUnknown !== 'boolean')
500
+ return false;
501
+ if (provenDeadByBootChange(c))
502
+ return true;
503
+ if (c.container)
504
+ return containerEmptiness(c.container.backend, c.container.id, process.platform, c.container.identity) === 'empty';
505
+ if (c.observationUnknown || !Number.isSafeInteger(c.pid) || Number(c.pid) <= 0)
506
+ return false;
507
+ return !processMayBeAlive(Number(c.pid), c.group) && c.descendants.every(p => !processMayBeAlive(p.pid, p.group));
508
+ }
509
+ catch {
510
+ return false;
511
+ }
512
+ }
513
+ /** Reconnect the exact native conversation. An absent receipt never proves
514
+ * non-delivery, so an unmatched message requires an explicit review action. */
515
+ async recover(id, actor, expected) {
516
+ this.admit();
517
+ this.assertExpected(id, actor, expected);
518
+ const session = this.get(id, actor);
519
+ if (session.status !== 'uncertain' || this.operations.size || this.recovering || this.closed || this.closing || this.persistenceFailed)
520
+ throw new CodingActionError('Recovery is not available while another session operation is active.', 'rejected', id);
521
+ this.recovering = true;
522
+ this.operations.add(id);
523
+ try {
524
+ if (this.provider) {
525
+ const active = this.db.prepare('SELECT document FROM coding_session').all().some(row => busy.has(JSON.parse(String(row['document'])).status));
526
+ if (active)
527
+ throw new CodingActionError('Another coding turn is still active. Wait for it to finish or stop it before reconnecting the agent.', 'rejected', id);
528
+ await this.provider.close();
529
+ this.unsubscribe?.();
530
+ this.unsubscribe = null;
531
+ this.provider = null;
532
+ this.loaded.clear();
533
+ }
534
+ else if (this.recoveryRequired && !this.priorProcessesGone())
535
+ throw Error('The previous agent may still be running. Its process exit could not be verified; work remains preserved.');
536
+ this.recoveryRequired = false;
537
+ this.db.prepare('UPDATE coding_owner SET clean=1,native_pid=NULL WHERE token=?').run(this.owner);
538
+ await this.verifyWorktree(session);
539
+ this.authorize(actor, session.repo);
540
+ if (!session.nativeThreadId) {
541
+ this.save({ ...this.get(id, actor), turnId: null, status: 'uncertain', deliveryReviewRequired: true, error: 'Codex startup could not be confirmed. Its processes are stopped. Inspect Changes, then keep the work and close this session. The original request will not be resent.' });
542
+ return;
543
+ }
544
+ const result = object(await this.request('thread/read', { threadId: session.nativeThreadId, includeTurns: true }));
545
+ const thread = object(result['thread']);
546
+ if (thread['id'] !== session.nativeThreadId)
547
+ throw Error('The saved conversation could not be verified.');
548
+ this.importHistory(id, thread);
549
+ const unresolved = this.db.prepare("SELECT 1 FROM coding_submission WHERE session=? AND status IN ('preparing','pending','uncertain')").get(id);
550
+ this.save({ ...this.get(id, actor), turnId: null, status: unresolved ? 'uncertain' : 'interrupted', deliveryReviewRequired: Boolean(unresolved), error: unresolved ? 'The saved conversation is loaded, but the last message has no matching delivery receipt. Review the activity before continuing. It will not be resent.' : null });
551
+ }
552
+ finally {
553
+ this.recovering = false;
554
+ this.releaseOperation(id);
555
+ }
556
+ }
557
+ async continueSaved(id, actor) {
558
+ this.admit();
559
+ const session = this.get(id, actor);
560
+ if (!session.deliveryReviewRequired || session.status !== 'uncertain' || this.recoveryRequired || this.recovering)
561
+ throw Error('Check the saved conversation before continuing.');
562
+ this.db.prepare("UPDATE coding_submission SET status='not-retried',error=NULL WHERE session=? AND status IN ('preparing','pending','uncertain')").run(id);
563
+ if (!session.nativeThreadId) {
564
+ this.save({ ...session, status: 'closed', turnId: null, deliveryReviewRequired: false, error: 'Session closed without confirming startup. Your original request and checkout are preserved; nothing was resent.' });
565
+ return;
566
+ }
567
+ this.save({ ...session, status: 'interrupted', deliveryReviewRequired: false, error: null });
568
+ await this.resume(id, actor);
569
+ }
570
+ async send(id, actor, prompt, requestId, expected) {
571
+ return this.sendMessage(id, actor, prompt, requestId, false, expected);
572
+ }
573
+ async sendMessage(id, actor, prompt, requestId, initial, expected) {
574
+ this.validateMessage(prompt, requestId);
575
+ let session = this.get(id, actor);
576
+ const receipt = this.db.prepare('SELECT * FROM coding_submission WHERE session=? AND key=?').get(id, requestId);
577
+ if (receipt) {
578
+ if (receipt['digest'] !== digest(prompt))
579
+ throw new CodingActionError('This message ID was already used with different text.', 'rejected', id);
580
+ if (receipt['status'] === 'accepted')
581
+ return;
582
+ if (!(initial && receipt['status'] === 'preparing'))
583
+ throw this.receiptError(id, receipt);
584
+ }
585
+ this.assertExpected(id, actor, expected);
586
+ this.admit();
587
+ if ((!initial && this.operations.has(id)) || this.recovering)
588
+ throw new CodingActionError('A session operation is still in progress. Your draft is preserved.', 'rejected', id);
589
+ if (session.status === 'uncertain')
590
+ throw new CodingActionError(session.error || 'Resolve the uncertain delivery before continuing.', 'unknown', id);
591
+ if (session.status === 'closed')
592
+ throw new CodingActionError('This session is closed. Its request and checkout are preserved; start a new session to continue.', 'rejected', id);
593
+ if (session.status === 'stopping' || session.status === 'needs-input')
594
+ throw new CodingActionError(session.error || 'Finish the current action before sending another message.', 'rejected', id);
595
+ this.operations.add(id);
596
+ let transmissionAttempted = false;
597
+ try {
598
+ await this.load(session);
599
+ this.admit();
600
+ this.assertExpected(id, actor, expected);
601
+ session = this.get(id, actor);
602
+ const steering = session.status === 'working' && session.turnId !== null;
603
+ if (initial)
604
+ this.db.prepare("UPDATE coding_submission SET status='pending',error=NULL WHERE session=? AND key=? AND status='preparing'").run(id, requestId);
605
+ else
606
+ this.db.prepare("INSERT INTO coding_submission VALUES(?,?,?,'pending',NULL)").run(id, requestId, digest(prompt));
607
+ this.save({ ...session, status: 'working', error: null });
608
+ transmissionAttempted = true;
609
+ const result = object(await this.request(steering ? 'turn/steer' : 'turn/start', { threadId: session.nativeThreadId, input: [{ type: 'text', text: prompt }], clientUserMessageId: requestId, ...(steering ? { expectedTurnId: session.turnId } : {}) }));
610
+ this.db.prepare("UPDATE coding_submission SET status='accepted' WHERE session=? AND key=?").run(id, requestId);
611
+ const current = this.get(id, actor), turnId = string(result['turnId']) || string(object(result['turn'])['id']);
612
+ if (current.status === 'working' && turnId)
613
+ this.save({ ...current, turnId });
614
+ }
615
+ catch (error) {
616
+ if (error instanceof CodingStateConflictError)
617
+ throw error;
618
+ const accepted = this.db.prepare("SELECT 1 FROM coding_submission WHERE session=? AND key=? AND status='accepted'").get(id, requestId);
619
+ const failure = accepted ? new CodingActionError(error instanceof Error ? error.message : 'The response could not be confirmed. Check the saved session.', 'unknown', id) : actionError(error, transmissionAttempted ? 'unknown' : 'rejected', id);
620
+ const uncertain = failure.delivery !== 'rejected';
621
+ const message = uncertain && !accepted ? 'The connection closed before delivery was confirmed. Your message may have reached the agent; it will not be sent again automatically.' : failure.message;
622
+ this.db.prepare("UPDATE coding_submission SET status=?,error=? WHERE session=? AND key=? AND status!='accepted'").run(uncertain ? 'uncertain' : 'failed', message, id, requestId);
623
+ const current = this.read(id);
624
+ // A refused draft does not fail the saved turn. Unknown reconnects still
625
+ // require reconciliation even when this message never reached transport.
626
+ this.save({ ...current, status: accepted || (!transmissionAttempted && !uncertain) ? current.status : uncertain ? 'uncertain' : current.turnId ? current.status : 'failed', error: message });
627
+ throw new CodingActionError(message, failure.delivery, id);
628
+ }
629
+ finally {
630
+ if (!initial)
631
+ this.releaseOperation(id);
632
+ }
633
+ }
634
+ async resume(id, actor, expected) {
635
+ this.admit();
636
+ this.assertExpected(id, actor, expected);
637
+ const session = this.get(id, actor);
638
+ if (session.status === 'closed')
639
+ throw new CodingActionError('This session is closed. Its request and checkout are preserved; start a new session to continue.', 'rejected', id);
640
+ if (busy.has(session.status) || this.operations.has(id) || this.recovering)
641
+ throw new CodingActionError('The session is already active.', 'rejected', id);
642
+ this.operations.add(id);
643
+ try {
644
+ await this.load(session);
645
+ this.save({ ...this.get(id, actor), status: 'ready', error: null });
646
+ }
647
+ finally {
648
+ this.releaseOperation(id);
649
+ }
650
+ }
651
+ async stop(id, actor, expected) {
652
+ this.assertExpected(id, actor, expected);
653
+ const session = this.get(id, actor);
654
+ if (!session.nativeThreadId || !session.turnId || !busy.has(session.status))
655
+ throw new CodingActionError('There is no running turn to stop.', 'rejected', id);
656
+ for (const row of this.db.prepare("SELECT token,rpc_id,payload FROM coding_request WHERE session=? AND status='pending'").all(id)) {
657
+ const request = JSON.parse(String(row['payload']));
658
+ this.native().respond(JSON.parse(String(row['rpc_id'])), request.kind === 'questions' ? { answers: {} } : { decision: 'cancel' });
659
+ this.db.prepare("UPDATE coding_request SET status='withdrawn' WHERE token=?").run(String(row['token']));
660
+ }
661
+ this.save({ ...session, status: 'stopping' });
662
+ await this.request('turn/interrupt', { threadId: session.nativeThreadId, turnId: session.turnId });
663
+ }
664
+ answer(id, actor, token, decision, answers) {
665
+ const session = this.get(id, actor);
666
+ const row = this.db.prepare("SELECT * FROM coding_request WHERE session=? AND token=? AND status='pending'").get(id, token);
667
+ if (!row || session.status !== 'needs-input' || !this.loaded.has(id))
668
+ throw Error('This request is no longer waiting for an answer.');
669
+ const request = JSON.parse(String(row['payload']));
670
+ if (!['accept', 'decline', 'cancel'].includes(decision))
671
+ throw Error('Choose an available decision.');
672
+ const result = request.kind === 'questions' ? { answers: decision === 'accept' ? this.validateAnswers(request, answers) : {} } : { decision };
673
+ this.native().respond(JSON.parse(String(row['rpc_id'])), result);
674
+ this.db.prepare("UPDATE coding_request SET status='answered' WHERE token=?").run(token);
675
+ const remaining = this.db.prepare("SELECT 1 FROM coding_request WHERE session=? AND status='pending'").get(id);
676
+ this.save({ ...session, status: remaining ? 'needs-input' : 'working' });
677
+ }
678
+ validateAnswers(request, raw) {
679
+ const answers = object(raw), result = {};
680
+ for (const q of request.questions) {
681
+ const values = object(answers[q.id])['answers'];
682
+ if (!Array.isArray(values) || values.length < 1 || values.length > 10 || values.some(v => typeof v !== 'string' || v.length > 16_000))
683
+ throw Error('Answer each question before continuing.');
684
+ result[q.id] = { answers: values };
685
+ }
686
+ return result;
687
+ }
688
+ async changes(id, actor) {
689
+ const session = this.get(id, actor);
690
+ const [head, status, tracked, untracked] = await Promise.all([
691
+ this.git(session.worktree, ['rev-parse', 'HEAD']), this.git(session.worktree, ['status', '--short']),
692
+ run('git', ['--no-optional-locks', 'diff', '--no-ext-diff', '--no-textconv', session.base, '--'], { cwd: session.worktree, timeoutMs: 60_000, maxBuffer: 1_048_576 }),
693
+ this.git(session.worktree, ['ls-files', '--others', '--exclude-standard', '-z']),
694
+ ]);
695
+ let diff = tracked.stdout, truncated = tracked.code !== 0;
696
+ // git's no-index diff describes symlinks themselves; it does not follow a
697
+ // new link out of the checkout to display its target's contents.
698
+ const paths = untracked.split('\0').filter(Boolean);
699
+ for (let index = 0; index < paths.length; index++) {
700
+ if (index >= 100 || Buffer.byteLength(diff) >= 1_048_576) {
701
+ truncated = true;
702
+ break;
703
+ }
704
+ const part = await run('git', ['--no-optional-locks', 'diff', '--no-index', '--no-ext-diff', '--no-textconv', '--', '/dev/null', paths[index]], { cwd: session.worktree, timeoutMs: 60_000, maxBuffer: 131_072 });
705
+ diff += part.stdout;
706
+ if (part.code !== 0 && part.code !== 1)
707
+ truncated = true;
708
+ }
709
+ if (Buffer.byteLength(diff) > 1_048_576) {
710
+ diff = Buffer.from(diff).subarray(0, 1_048_576).toString('utf8');
711
+ truncated = true;
712
+ }
713
+ this.authorize(actor, session.repo);
714
+ return { head: head.trim(), status, diff, truncated };
715
+ }
716
+ close() {
717
+ return this.closePromise ??= this.closeOnce();
718
+ }
719
+ async closeOnce() {
720
+ if (this.closed)
721
+ return;
722
+ this.closing = true;
723
+ let persistenceError;
724
+ try {
725
+ this.flush();
726
+ }
727
+ catch (error) {
728
+ persistenceError = error;
729
+ this.persistenceFailed = true;
730
+ this.recoveryRequired = true;
731
+ }
732
+ let shutdownError;
733
+ try {
734
+ if (this.provider)
735
+ await this.provider.close();
736
+ }
737
+ catch (error) {
738
+ shutdownError = error;
739
+ }
740
+ if (this.operations.size)
741
+ await new Promise(resolve => this.operationWaiters.add(resolve));
742
+ if (this.commands.size)
743
+ await new Promise(resolve => this.commandWaiters.add(resolve));
744
+ // Saving the last delta must never be a prerequisite for stopping the
745
+ // owned agent. A failed save still fences installation and clean release.
746
+ if (persistenceError || this.persistenceFailed)
747
+ throw Error('Coding activity could not be saved. Agent cleanup was attempted; preserve the catalog and repair storage before restarting.', { cause: persistenceError });
748
+ if (shutdownError)
749
+ throw shutdownError;
750
+ // Closing this new server cannot establish the exit of a previous runtime.
751
+ if (this.recoveryRequired) {
752
+ this.db.prepare("UPDATE coding_owner SET token='',pid=0 WHERE token=?").run(this.owner);
753
+ this.closed = true;
754
+ this.db.close();
755
+ return;
756
+ }
757
+ for (const row of this.db.prepare('SELECT document FROM coding_session').all()) {
758
+ const session = JSON.parse(String(row['document']));
759
+ const uncertain = this.db.prepare("SELECT 1 FROM coding_submission WHERE session=? AND status IN ('preparing','pending','uncertain')").get(session.id);
760
+ if (!uncertain && (busy.has(session.status) || session.status === 'uncertain'))
761
+ this.save({ ...session, status: 'interrupted', turnId: null, error: null });
762
+ }
763
+ this.closed = true;
764
+ this.unsubscribe?.();
765
+ this.db.prepare("UPDATE coding_owner SET token='',pid=0,native_pid=NULL,clean=1 WHERE token=?").run(this.owner);
766
+ this.db.close();
767
+ }
768
+ }