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,364 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ const escape = (value) => String(value ?? '').replace(/[&<>"']/g, c => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' }[c]));
3
+ const hidden = (values) => Object.entries(values).map(([name, value]) => `<input type="hidden" name="${escape(name)}" value="${escape(value)}">`).join('');
4
+ function linkedText(text) {
5
+ const pattern = /\[([^\]\n]+)\]\((https?:\/\/[^\s)]+)\)|\x60([^\x60\n]+)\x60|\*\*([^*\n]+)\*\*|(https?:\/\/[^\s<>]+)/g;
6
+ let end = 0;
7
+ let output = '';
8
+ for (const match of text.matchAll(pattern)) {
9
+ output += escape(text.slice(end, match.index));
10
+ if (match[3] !== undefined)
11
+ output += `<code>${escape(match[3])}</code>`;
12
+ else if (match[4] !== undefined)
13
+ output += `<strong>${escape(match[4])}</strong>`;
14
+ else {
15
+ const raw = match[2] ?? match[5];
16
+ const url = raw.replace(/[.,;!?]+$/, '');
17
+ output += `<a href="${escape(url)}" target="_blank" rel="noopener noreferrer">${escape(match[1] ?? url)}</a>${escape(raw.slice(url.length))}`;
18
+ }
19
+ end = match.index + match[0].length;
20
+ }
21
+ return output + escape(text.slice(end));
22
+ }
23
+ function friendlyCodingError(message) {
24
+ return /already has an active writer/i.test(message)
25
+ ? 'This conversation is open in another Codex app. Close it there, then resume here.'
26
+ : message;
27
+ }
28
+ function noticeHtml(message) {
29
+ const friendly = friendlyCodingError(message);
30
+ return escape(friendly) + (friendly === message ? '' : `<details><summary>Technical details</summary><pre>${escape(message)}</pre></details>`);
31
+ }
32
+ const sessionUrl = (id) => `/code/${encodeURIComponent(id)}`;
33
+ const active = (status) => ['starting', 'working', 'needs-input', 'stopping'].includes(status);
34
+ const stateLabel = (session, requests = []) => ({ starting: 'Starting Codex', ready: 'Ready', working: 'Working', 'needs-input': requests.some(r => r.kind !== 'questions') ? 'Permission needed' : 'Needs your answer', stopping: 'Stopping', interrupted: 'Stopped', failed: 'Stopped with an error', uncertain: 'Delivery not confirmed', closed: 'Session closed' }[session.status]);
35
+ function itemHtml(item) {
36
+ if (item.type === 'reasoning')
37
+ return '';
38
+ const role = ['user', 'userMessage'].includes(item.type) ? 'You' : ['agent', 'assistant', 'agentMessage'].includes(item.type) ? 'Codex' : null;
39
+ if (role)
40
+ return `<article class="coding-message${role === 'You' ? ' coding-user' : ''}" data-coding-item="${escape(item.id)}"><strong>${role}</strong><div class="coding-text">${linkedText(item.text)}</div></article>`;
41
+ const title = { commandExecution: 'Command', fileChange: 'File changes', mcpToolCall: 'Tool', reasoning: 'Reasoning', plan: 'Plan', error: 'Error' }[item.type] ?? 'Activity';
42
+ return `<details class="coding-tool" data-coding-item="${escape(item.id)}"><summary>${title}${item.status ? ` <span class="coding-meta">${escape(item.status)}</span>` : ''}</summary><pre>${escape(item.text)}</pre></details>`;
43
+ }
44
+ function requestHtml(request, session, csrf) {
45
+ const questions = request.kind === 'questions';
46
+ const body = questions ? request.questions.map((q, index) => `<label>${escape(q.question)}<input type="text" name="question:${escape(q.id)}"${q.options.length ? ` list="coding-options-${escape(request.id)}-${index}"` : ''} required autocomplete="off">${q.options.length ? `<datalist id="coding-options-${escape(request.id)}-${index}">${q.options.map(o => `<option value="${escape(o.label)}">${escape(o.description)}</option>`).join('')}</datalist>` : ''}</label>`).join('') : `<pre class="coding-permission-detail">${escape(request.detail)}</pre><label>Password to approve this request<input type="password" name="password" autocomplete="current-password" required></label>`;
47
+ return `<section class="coding-request" data-coding-request="${escape(request.id)}"><h3>${escape(request.title || (questions ? 'Your answer' : 'Allow extra access?'))}</h3><form method="post" action="${sessionUrl(session)}/answer" data-coding-form="answer">${hidden({ csrf, requestId: request.id })}${body}<div class="coding-actions"><button type="submit" name="decision" value="accept">${questions ? 'Send answer' : 'Approve access'}</button><button type="submit" name="decision" value="decline" formnovalidate class="coding-secondary">${questions ? 'Cancel question' : 'Decline'}</button></div></form></section>`;
48
+ }
49
+ function controlsHtml(session, csrf) {
50
+ if (active(session.status) && session.turnId)
51
+ return `<form method="post" action="${sessionUrl(session.id)}/stop" data-coding-form="stop">${hidden({ csrf })}<button class="coding-secondary"${session.status === 'stopping' ? ' disabled' : ''}>Stop</button></form>`;
52
+ if (session.status === 'uncertain')
53
+ return `<form method="post" action="${sessionUrl(session.id)}/${session.deliveryReviewRequired ? 'continue' : 'recover'}" data-coding-form="recover">${hidden({ csrf })}<button>${session.deliveryReviewRequired ? (session.nativeThreadId ? 'Continue with saved work' : 'Keep work and close session') : 'Check saved session'}</button></form>`;
54
+ if (!session.nativeThreadId && ['closed', 'failed', 'interrupted'].includes(session.status))
55
+ return '';
56
+ if (['failed', 'interrupted'].includes(session.status))
57
+ return `<form method="post" action="${sessionUrl(session.id)}/resume" data-coding-form="resume">${hidden({ csrf })}<button>Resume session</button></form>`;
58
+ return '';
59
+ }
60
+ export function codingWorkspaceHtml(input) {
61
+ const { selected, csrf } = input;
62
+ const selectedRepo = selected?.session.repo ?? input.project;
63
+ const projectName = (repo) => input.projects.find(p => p.path === repo)?.name ?? repo.split('/').filter(Boolean).at(-1) ?? repo;
64
+ const newUrl = `/code${selectedRepo ? `?project=${encodeURIComponent(selectedRepo)}` : ''}`;
65
+ const sessions = input.sessions.map(session => `<li><a href="${sessionUrl(session.id)}"${selected?.session.id === session.id ? ' aria-current="page"' : ''}><span>${escape(session.title)}</span><small>${escape(projectName(session.repo))} · <span data-coding-status-for="${escape(session.id)}">${escape(stateLabel(session))}</span></small></a></li>`).join('');
66
+ const sidebar = `<aside class="coding-sidebar"><div class="coding-sidebar-top"><h2>Sessions</h2>${selected ? `<a class="coding-link-button" href="${escape(newUrl)}">New session</a>` : ''}</div><nav class="coding-session-list" aria-label="Coding sessions"><ul>${sessions || '<li class="coding-meta">No sessions yet.</li>'}</ul></nav><details class="coding-mobile-sessions"><summary>Sessions${input.sessions.length ? ` (${input.sessions.length})` : ''}</summary><nav aria-label="Coding sessions"><ul>${sessions || '<li class="coding-meta">No sessions yet.</li>'}</ul></nav></details></aside>`;
67
+ const warning = !input.available ? '<p class="coding-notice">Codex is unavailable on this installation. Install and sign in to Codex on the host to start coding.</p>' : '';
68
+ const error = input.error ?? selected?.session.error ?? '';
69
+ let main;
70
+ if (!selected) {
71
+ const options = input.projects.map(p => `<option value="${escape(p.path)}"${selectedRepo === p.path ? ' selected' : ''}>${escape(p.name)}</option>`).join('');
72
+ main = `<header class="coding-header"><h1>Build with Codex</h1></header>${warning}${input.projects.length ? `<form method="post" action="/code/start" data-coding-form="start" class="coding-start">${hidden({ csrf, requestId: randomUUID() })}<label>Project<select name="repo" required>${options}</select></label><label>What would you like to change?<textarea name="prompt" rows="5" required maxlength="24000" placeholder="Describe the result you want…">${escape(input.draft ?? '')}</textarea></label><details class="coding-options"><summary>Session options</summary><label>Title <span class="coding-meta">Optional</span><input name="title" maxlength="160" autocomplete="off"></label><label>Model <span class="coding-meta">Optional; uses the installed default</span><input name="model" maxlength="120" placeholder="Installed default" autocomplete="off"></label></details><p class="coding-terms">Codex uses this installation’s login and edits a separate copy of this project. It can run commands and read files outside the project. Connected tools (MCP servers) keep their own access to this computer and connected services. Additional command and file access requires your approval.</p><label>Password to start<input type="password" name="password" autocomplete="current-password" required></label><button type="submit"${input.available ? '' : ' disabled'}>Start coding</button></form>` : '<div class="coding-empty"><p>Add a project to start coding.</p><a class="coding-link-button" href="/projects">Open projects</a></div>'}`;
73
+ }
74
+ else {
75
+ const s = selected.session;
76
+ const blocked = !s.nativeThreadId || !['ready', 'working', 'failed', 'interrupted'].includes(s.status);
77
+ const messages = selected.items.filter(item => item.type !== 'reasoning').map(itemHtml).join('');
78
+ main = `<header class="coding-header"><div><h1>${escape(s.title)}</h1><p class="coding-meta">${escape(projectName(s.repo))} · Codex${s.model ? ` · ${escape(s.model)}` : ''}</p></div><details class="coding-session-detail"><summary aria-label="Session details">Details</summary><dl><dt>Branch</dt><dd>${escape(s.branch)}</dd><dt>Worktree</dt><dd>${escape(s.worktree)}</dd><dt>Native session</dt><dd>${escape(s.nativeThreadId ?? 'Not established')}</dd>${s.initialRequest ? `<dt>Initial request</dt><dd class="coding-text">${escape(s.initialRequest.prompt)}</dd>` : ''}</dl></details></header><div class="coding-state-row"><p id="coding-state" role="status">${escape(stateLabel(s, selected.requests))}</p><div id="coding-controls">${controlsHtml(s, csrf)}</div></div><div id="coding-requests">${selected.requests.map(r => requestHtml(r, s.id, csrf)).join('')}</div><div id="coding-conversation" class="coding-conversation" tabindex="0" aria-label="Conversation"${s.status === 'closed' && !messages ? ' hidden' : ''}>${messages || '<p class="coding-meta coding-no-messages">No messages yet.</p>'}</div><button type="button" id="coding-latest" class="coding-secondary" hidden>Latest update ↓</button><form method="post" action="${sessionUrl(s.id)}/send" data-coding-form="send" class="coding-composer"${s.status === 'closed' ? ' hidden' : ''}>${hidden({ csrf, requestId: randomUUID() })}<label for="coding-prompt">Message Codex</label><textarea id="coding-prompt" name="prompt" rows="2" maxlength="24000" required placeholder="Describe a change or ask about the result…">${escape(input.draft ?? '')}</textarea><div class="coding-actions"><button type="submit"${blocked ? ' disabled' : ''}>${s.status === 'working' ? 'Send update' : 'Send message'}</button></div></form><details id="coding-changes" class="coding-changes"><summary>Changes</summary><div id="coding-change-content"><p class="coding-meta">Open to load the current changes.</p></div><button type="button" id="coding-refresh-changes" class="coding-secondary">Refresh changes</button><noscript><a href="${sessionUrl(s.id)}/changes">View changes</a></noscript><p id="coding-shipping"${['closed', 'uncertain'].includes(s.status) ? ' hidden' : ''}><a class="coding-link-button" href="${sessionUrl(s.id)}/ship">Review for shipping</a></p></details>`;
79
+ }
80
+ return `<section class="coding-workspace" data-coding-session="${escape(selected?.session.id ?? '')}" data-coding-owner="${escape(selected?.session.owner ?? input.owner ?? '')}" data-coding-project="${escape(selectedRepo ?? input.projects[0]?.path ?? '')}" data-coding-status="${escape(selected?.session.status ?? '')}" data-coding-native-thread="${Boolean(selected?.session.nativeThreadId)}" data-coding-revision="${selected?.revision ?? -1}" data-coding-csrf="${escape(csrf)}">${sidebar}<div class="coding-main"><div id="coding-error" class="coding-notice" role="alert"${error ? '' : ' hidden'}>${noticeHtml(error)}</div><p id="coding-connection" class="coding-meta" role="status" hidden></p>${main}</div></section>`;
81
+ }
82
+ export const CODING_CSS = `
83
+ .coding-workspace{display:grid;grid-template-columns:224px minmax(0,1fr);gap:24px;max-width:1220px;min-width:0;margin:0 auto;overflow-wrap:anywhere}
84
+ .coding-workspace *{box-sizing:border-box}.coding-workspace [hidden]{display:none!important}.coding-workspace h1{font-size:1.45rem;line-height:1.25;margin:0}.coding-workspace h2{font-size:1rem;margin:0}.coding-workspace h3{font-size:1rem;margin:0 0 12px}.coding-workspace p{margin:0 0 12px}.coding-main,.coding-sidebar{min-width:0}.coding-meta,.coding-workspace small{font-size:.83rem;color:var(--muted-foreground,#747981)}
85
+ .coding-sidebar-top,.coding-header,.coding-state-row,.coding-actions{display:flex;align-items:center;gap:12px;justify-content:space-between}.coding-sidebar-top{margin-bottom:12px}.coding-header{align-items:flex-start;margin-bottom:16px}.coding-header .coding-meta{margin:6px 0 0}.coding-session-detail{max-width:220px}.coding-session-detail summary{font-size:.82rem}.coding-session-detail dl{font-size:.82rem}.coding-session-detail dt{font-weight:600;margin-top:8px}.coding-session-detail dd{margin:2px 0 0}.coding-state-row{flex-wrap:wrap;padding:10px 0;border-bottom:1px solid var(--border,#353940);margin-bottom:12px}.coding-state-row p{margin:0;font-weight:600}.coding-state-row form{margin:0}
86
+ .coding-session-list{max-height:74vh;overflow:auto}.coding-sidebar ul{list-style:none;margin:0;padding:0;display:grid;gap:4px}.coding-sidebar nav a{display:block;padding:12px;border-radius:10px;text-decoration:none;color:inherit}.coding-sidebar nav a:hover,.coding-sidebar nav a[aria-current]{background:var(--muted,#22252a)}.coding-sidebar nav a[aria-current]{box-shadow:inset 3px 0 var(--primary,#90bfa7)}.coding-sidebar nav span,.coding-sidebar nav small{display:block}.coding-sidebar nav [data-coding-status-for]{display:inline}.coding-sidebar nav small{margin-top:5px}.coding-mobile-sessions{display:none}
87
+ .coding-workspace form:not(.coding-state-row form){display:grid;gap:12px;margin:0}.coding-workspace label{display:grid;gap:6px;font-size:.9rem;font-weight:500}.coding-workspace input,.coding-workspace textarea,.coding-workspace select{width:100%;min-width:0;max-width:100%;font-size:16px;border:1px solid var(--border,#353940);border-radius:8px;padding:11px 12px;background:var(--background,#14161a);color:var(--foreground,#f0f1f3)}.coding-workspace textarea{resize:vertical;line-height:1.5}.coding-workspace input:focus-visible,.coding-workspace textarea:focus-visible,.coding-workspace select:focus-visible,.coding-workspace button:focus-visible,.coding-workspace summary:focus-visible,.coding-workspace a:focus-visible{outline:2px solid var(--ring,#79acff);outline-offset:3px}.coding-workspace button,.coding-link-button{min-height:44px;padding:10px 15px;white-space:nowrap;border-radius:8px;max-width:100%;font-size:.9rem}.coding-workspace button{justify-self:start;cursor:pointer}.coding-workspace button:disabled{cursor:default;opacity:.55}.coding-workspace .coding-secondary{background:transparent;color:inherit;border:1px solid var(--border,#353940)}.coding-link-button{display:inline-flex;align-items:center;justify-content:center;text-decoration:none;color:inherit;border:1px solid var(--border,#353940)}.coding-workspace summary{cursor:pointer;min-height:44px;padding:12px 0}.coding-actions{justify-content:flex-start;flex-wrap:wrap}.coding-start{max-width:680px}.coding-options label+label{margin-top:12px}.coding-terms{font-size:.9rem;line-height:1.6;margin:0!important}.coding-empty{padding:28px 0}
88
+ .coding-notice{border:1px solid var(--destructive,#bd7674);border-radius:10px;padding:12px;line-height:1.5;margin-bottom:16px}.coding-conversation{max-height:min(58vh,640px);min-height:140px;overflow:auto;overscroll-behavior:contain;scrollbar-gutter:stable;padding:4px 10px 12px 0}.coding-message{padding:14px 0}.coding-message strong{display:block;font-size:.78rem;margin-bottom:7px;color:var(--muted-foreground,#747981)}.coding-user{margin:8px 0 8px 32px;padding:14px 16px;border-radius:12px;background:var(--muted,#22252a)}.coding-text{white-space:pre-wrap;line-height:1.6;overflow-wrap:anywhere}.coding-tool{border-bottom:1px solid var(--border,#353940);font-size:.88rem}.coding-workspace pre{white-space:pre-wrap;overflow-wrap:anywhere;max-width:100%;font-size:.8rem;line-height:1.5;margin:0 0 12px}.coding-tool summary{display:list-item}.coding-tool summary span{margin-left:6px}.coding-composer{border-top:1px solid var(--border,#353940);padding-top:14px;margin-top:8px!important}.coding-composer label{font-size:.83rem}.coding-request{border:1px solid var(--border,#353940);border-radius:12px;padding:16px;margin:12px 0}.coding-permission-detail{max-height:240px;overflow:auto}.coding-changes{margin-top:20px;border-top:1px solid var(--border,#353940)}.coding-changes pre{max-height:min(50dvh,480px);overflow:auto}.coding-change-meta{font-size:.8rem}.coding-workspace #coding-latest{margin:8px 0}.coding-workspace #coding-connection{line-height:1.4}
89
+ .coding-workspace .coding-start>button[type="submit"],.coding-workspace .coding-composer button[type="submit"]{background:var(--primary,#171717);color:var(--primary-foreground,#fff);border:1px solid var(--primary,#171717);font-weight:600;box-shadow:none}.coding-workspace .coding-start>button[type="submit"]:not(:disabled):hover,.coding-workspace .coding-composer button[type="submit"]:not(:disabled):hover{filter:brightness(.9)}
90
+ .coding-workspace:not([data-coding-session=""]) .coding-main{display:flex;flex-direction:column;min-height:calc(100dvh - 180px)}.coding-main>.coding-header,.coding-main>.coding-state-row,.coding-main>#coding-requests,.coding-main>.coding-composer,.coding-main>.coding-changes,.coding-main>.coding-notice,.coding-main>#coding-connection{flex-shrink:0}.coding-workspace:not([data-coding-session=""]) .coding-conversation{flex:1 1 120px;min-height:100px;max-height:min(58dvh,640px)}.coding-workspace .coding-tool{margin:4px 0;padding:0 10px;border-radius:8px}.coding-workspace .coding-session-detail{margin:0;padding:0 10px;flex-shrink:0}.coding-workspace .coding-changes{padding:0 12px;margin-top:12px}.coding-text strong{display:inline;font-size:inherit;color:inherit;margin:0;font-weight:600}.coding-composer textarea{min-height:72px;max-height:180px}
91
+ @media(max-width:760px){.coding-workspace{grid-template-columns:minmax(0,1fr);gap:16px}.coding-sidebar{border-bottom:1px solid var(--border,#353940);padding-bottom:12px}.coding-sidebar-top h2,.coding-session-list{display:none}.coding-sidebar-top{float:right;margin:0 0 8px 12px}.coding-mobile-sessions{display:block;max-width:100%;margin:0}.coding-mobile-sessions>nav{max-height:260px;overflow:auto;clear:both}.coding-mobile-sessions>summary{min-height:44px}.coding-header{display:block}.coding-session-detail{max-width:none;margin-top:4px}.coding-session-detail summary{padding:10px 0}.coding-conversation{max-height:52vh;min-height:100px;padding-right:4px}.coding-user{margin-left:16px}.coding-workspace h1{font-size:1.25rem}.coding-workspace button,.coding-link-button{padding:10px 12px}.coding-start{max-width:none}}
92
+ @media(max-width:760px){.coding-workspace:not([data-coding-session=""]) .coding-main{min-height:calc(100dvh - 245px - env(safe-area-inset-bottom,0px))}.coding-workspace:not([data-coding-session=""]) .coding-conversation{min-height:80px}.coding-header{display:flex;gap:12px}.coding-header>div{min-width:0;flex:1}.coding-header:has(.coding-session-detail[open]){flex-wrap:wrap}.coding-workspace .coding-session-detail{max-width:100px}.coding-workspace .coding-session-detail[open]{max-width:100%;flex-basis:100%}.coding-session-detail summary{white-space:nowrap}.coding-workspace .coding-mobile-sessions{padding:0 12px}.coding-composer{gap:8px!important}.coding-composer textarea{height:72px}.coding-state-row{padding:8px 0;margin-bottom:8px}}
93
+ @media(prefers-reduced-motion:reduce){.coding-workspace *{scroll-behavior:auto!important}}
94
+ `;
95
+ export function codingWorkspaceScript() {
96
+ return String.raw `(() => {
97
+ const root = document.querySelector('.coding-workspace');
98
+ if (!root || root.dataset.codingMounted) return;
99
+ root.dataset.codingMounted = 'true';
100
+ const session = root.dataset.codingSession;
101
+ const csrf = root.dataset.codingCsrf;
102
+ const base = session ? '/code/' + encodeURIComponent(session) : '/code';
103
+ const errorBox = root.querySelector('#coding-error');
104
+ const connection = root.querySelector('#coding-connection');
105
+ const composer = root.querySelector('[data-coding-form="send"], [data-coding-form="start"]');
106
+ const prompt = composer && composer.querySelector('[name="prompt"]');
107
+ const requestField = composer && composer.querySelector('[name="requestId"]');
108
+ let status = root.dataset.codingStatus;
109
+ let revision = Number(root.dataset.codingRevision);
110
+ let timer;
111
+ let polling = false;
112
+ let disposed = false;
113
+ let authLost = false;
114
+ let pendingSend = null;
115
+ let busy = false;
116
+ let changesBusy = false;
117
+ let hasChanges = null;
118
+ let followLatest = true;
119
+ let automaticScrollTop = null;
120
+ const jumpToLatest = () => {
121
+ const conversation = root.querySelector('#coding-conversation');
122
+ if (!conversation || conversation.hidden) return;
123
+ followLatest = true;
124
+ const lastReply = Array.from(conversation.querySelectorAll('.coding-message:not(.coding-user)')).at(-1);
125
+ if (status === 'ready' && lastReply) conversation.scrollTop += lastReply.getBoundingClientRect().top - conversation.getBoundingClientRect().top;
126
+ else conversation.scrollTop = conversation.scrollHeight;
127
+ automaticScrollTop = conversation.scrollTop;
128
+ root.querySelector('#coding-latest').hidden = true;
129
+ };
130
+ const e = value => String(value == null ? '' : value).replace(/[&<>"']/g, c => ({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[c]));
131
+ const linkedText = text => {
132
+ const pattern = /\[([^\]\n]+)\]\((https?:\/\/[^\s)]+)\)|\x60([^\x60\n]+)\x60|\*\*([^*\n]+)\*\*|(https?:\/\/[^\s<>]+)/g;
133
+ let end = 0; let output = '';
134
+ for (const match of text.matchAll(pattern)) { output += e(text.slice(end,match.index)); if (match[3] !== undefined) output += '<code>' + e(match[3]) + '</code>'; else if (match[4] !== undefined) output += '<strong>' + e(match[4]) + '</strong>'; else { const raw = match[2] || match[5]; const url = raw.replace(/[.,;!?]+$/, ''); output += '<a href="' + e(url) + '" target="_blank" rel="noopener noreferrer">' + e(match[1] || url) + '</a>' + e(raw.slice(url.length)); } end = match.index + match[0].length; }
135
+ return output + e(text.slice(end));
136
+ };
137
+ const h = values => Object.entries(values).map(([name,value]) => '<input type="hidden" name="' + e(name) + '" value="' + e(value) + '">').join('');
138
+ const isActive = value => ['starting','working','needs-input','stopping'].includes(value);
139
+ const draftKey = () => 'standing-orders:coding-draft:' + root.dataset.codingOwner + ':' + (session || 'new:' + (composer.querySelector('[name="repo"]')?.value || root.dataset.codingProject));
140
+ const storage = (operation, key, value) => { try { return sessionStorage[operation](key, value); } catch { return null; } };
141
+ const requestSignal = () => typeof AbortSignal.timeout === 'function' ? AbortSignal.timeout(30000) : undefined;
142
+ const saveDraft = () => {
143
+ if (!prompt || !root.dataset.codingOwner) return;
144
+ storage('setItem', draftKey(), JSON.stringify({text:prompt.value, requestId:requestField.value, pending:pendingSend, at:Date.now()}));
145
+ };
146
+ const restoreDraft = () => {
147
+ if (!prompt || !root.dataset.codingOwner) return;
148
+ try {
149
+ const saved = JSON.parse(storage('getItem',draftKey()) || 'null');
150
+ if (saved && typeof saved.text === 'string' && Date.now() - saved.at < 86400000) {
151
+ if (!prompt.value) prompt.value = saved.text;
152
+ if (/^[0-9a-f-]{36}$/i.test(saved.requestId || '')) requestField.value = saved.requestId;
153
+ if (saved.pending && typeof saved.pending.prompt === 'string' && saved.pending.requestId === requestField.value) {
154
+ pendingSend = saved.pending;
155
+ prompt.value = pendingSend.prompt;
156
+ prompt.readOnly = true;
157
+ }
158
+ }
159
+ } catch {}
160
+ };
161
+ const friendlyCodingError = ${friendlyCodingError.toString()};
162
+ const showError = (message, savedSession) => {
163
+ const friendly = friendlyCodingError(message || '');
164
+ errorBox.textContent = friendly; errorBox.hidden = !message;
165
+ if (friendly !== message && message) {
166
+ const details = document.createElement('details'), summary = document.createElement('summary'), raw = document.createElement('pre');
167
+ summary.textContent = 'Technical details'; raw.textContent = message; details.append(summary, raw); errorBox.append(details);
168
+ }
169
+ if (message && /^[a-f0-9]{32}$/.test(savedSession || '')) {
170
+ const link = document.createElement('a'); link.href = '/code/' + savedSession; link.textContent = 'View saved session'; errorBox.append(document.createTextNode(' '), link);
171
+ }
172
+ };
173
+ const showConnection = message => { connection.textContent = message || ''; connection.hidden = !message; };
174
+ const signedOut = () => {
175
+ authLost = true; saveDraft();
176
+ connection.replaceChildren(document.createTextNode('Sign in again to reconnect. Your draft is saved. '));
177
+ const link = document.createElement('a'); link.href = '/login?return=' + encodeURIComponent(location.pathname + location.search); link.textContent = 'Sign in'; connection.append(link); connection.hidden = false;
178
+ updateComposer();
179
+ };
180
+ let nativeThread = root.dataset.codingNativeThread === 'true';
181
+ const updateComposer = () => {
182
+ if (!composer) return;
183
+ composer.hidden = Boolean(session && status === 'closed');
184
+ const button = composer.querySelector('button[type="submit"]');
185
+ if (session) button.disabled = busy || authLost || !nativeThread || !['ready','working','failed','interrupted'].includes(status);
186
+ else if (busy || authLost) button.disabled = true;
187
+ button.textContent = pendingSend ? 'Retry send' : session ? status === 'working' ? 'Send update' : 'Send message' : 'Start coding';
188
+ if (pendingSend && !authLost && (!session || nativeThread) && !['uncertain','closed'].includes(status) && !busy) button.disabled = false;
189
+ prompt.readOnly = Boolean(pendingSend);
190
+ if (!session) composer.querySelectorAll('[name="repo"], [name="title"], [name="model"]').forEach(field => { if (field.tagName === 'SELECT') field.disabled = Boolean(pendingSend); else field.readOnly = Boolean(pendingSend); });
191
+ };
192
+ const updateShipping = () => {
193
+ const shipping = root.querySelector('#coding-shipping');
194
+ if (shipping) shipping.hidden = ['closed','uncertain'].includes(status) || hasChanges === false;
195
+ };
196
+ const requestHtml = request => {
197
+ const questions = request.kind === 'questions';
198
+ const body = questions ? request.questions.map((q,index) => '<label>' + e(q.question) + '<input type="text" name="question:' + e(q.id) + '"' + (q.options.length ? ' list="coding-options-' + e(request.id) + '-' + index + '"' : '') + ' required autocomplete="off">' + (q.options.length ? '<datalist id="coding-options-' + e(request.id) + '-' + index + '">' + q.options.map(o => '<option value="' + e(o.label) + '">' + e(o.description) + '</option>').join('') + '</datalist>' : '') + '</label>').join('') : '<pre class="coding-permission-detail">' + e(request.detail) + '</pre><label>Password to approve this request<input type="password" name="password" autocomplete="current-password" required></label>';
199
+ return '<section class="coding-request" data-coding-request="' + e(request.id) + '"><h3>' + e(request.title || (questions ? 'Your answer' : 'Allow extra access?')) + '</h3><form method="post" action="' + base + '/answer" data-coding-form="answer">' + h({csrf,requestId:request.id}) + body + '<div class="coding-actions"><button type="submit" name="decision" value="accept">' + (questions ? 'Send answer' : 'Approve access') + '</button><button type="submit" name="decision" value="decline" formnovalidate class="coding-secondary">' + (questions ? 'Cancel question' : 'Decline') + '</button></div></form></section>';
200
+ };
201
+ const render = snapshot => {
202
+ nativeThread = Boolean(snapshot.session.nativeThreadId);
203
+ if (!snapshot || !snapshot.session || snapshot.session.id !== session || !Array.isArray(snapshot.items) || !Array.isArray(snapshot.requests)) throw new Error('Could not read this session.');
204
+ status = snapshot.session.status;
205
+ root.dataset.codingStatus = status;
206
+ updateComposer(); updateShipping();
207
+ if (snapshot.revision === revision) return;
208
+ revision = snapshot.revision;
209
+ root.dataset.codingRevision = String(revision);
210
+ const labels = {starting:'Starting Codex',ready:'Ready',working:'Working','needs-input':snapshot.requests.some(r => r.kind !== 'questions') ? 'Permission needed' : 'Needs your answer',stopping:'Stopping',interrupted:'Stopped',failed:'Stopped with an error',uncertain:'Delivery not confirmed',closed:'Session closed'};
211
+ root.querySelector('#coding-state').textContent = labels[status] || 'Status unavailable';
212
+ root.querySelectorAll('[data-coding-status-for]').forEach(node => { if (node.dataset.codingStatusFor === session) node.textContent = labels[status] || 'Status unavailable'; });
213
+ if (snapshot.session.error) showError(snapshot.session.error);
214
+ else if (!pendingSend) showError('');
215
+ const controls = root.querySelector('#coding-controls');
216
+ const nextControls = isActive(status) && snapshot.session.turnId ? '<form method="post" action="' + base + '/stop" data-coding-form="stop">' + h({csrf}) + '<button class="coding-secondary"' + (status === 'stopping' ? ' disabled' : '') + '>Stop</button></form>' : status === 'uncertain' ? '<form method="post" action="' + base + '/' + (snapshot.session.deliveryReviewRequired ? 'continue' : 'recover') + '" data-coding-form="recover">' + h({csrf}) + '<button>' + (snapshot.session.deliveryReviewRequired ? (snapshot.session.nativeThreadId ? 'Continue with saved work' : 'Keep work and close session') : 'Check saved session') + '</button></form>' : !snapshot.session.nativeThreadId && ['closed','failed','interrupted'].includes(status) ? '' : ['failed','interrupted'].includes(status) ? '<form method="post" action="' + base + '/resume" data-coding-form="resume">' + h({csrf}) + '<button>Resume session</button></form>' : '';
217
+ const controlKey = status + ':' + (snapshot.session.turnId || '') + ':' + Boolean(snapshot.session.deliveryReviewRequired) + ':' + Boolean(snapshot.session.nativeThreadId);
218
+ if (controls.dataset.status !== controlKey) { controls.innerHTML = nextControls; controls.dataset.status = controlKey; }
219
+ const requests = root.querySelector('#coding-requests');
220
+ const live = new Set(snapshot.requests.map(request => request.id));
221
+ requests.querySelectorAll('[data-coding-request]').forEach(node => {
222
+ if (!live.has(node.dataset.codingRequest)) {
223
+ const focused = node.contains(document.activeElement); node.remove();
224
+ if (focused) { root.querySelector('#coding-state').tabIndex = -1; root.querySelector('#coding-state').focus(); showConnection('That request has been resolved.'); }
225
+ }
226
+ });
227
+ snapshot.requests.forEach(request => {
228
+ if (!Array.from(requests.children).some(node => node.dataset.codingRequest === request.id)) requests.insertAdjacentHTML('beforeend', requestHtml(request));
229
+ });
230
+ const conversation = root.querySelector('#coding-conversation');
231
+ const nearBottom = followLatest || conversation.scrollHeight - conversation.scrollTop - conversation.clientHeight < 80;
232
+ const items = new Set(snapshot.items.filter(item => item.type !== 'reasoning').map(item => item.id));
233
+ conversation.querySelectorAll('[data-coding-item]').forEach(node => { if (!items.has(node.dataset.codingItem)) node.remove(); });
234
+ conversation.hidden = status === 'closed' && items.size === 0;
235
+ if (items.size) conversation.querySelector('.coding-no-messages')?.remove();
236
+ else if (!conversation.querySelector('.coding-no-messages')) { const empty = document.createElement('p'); empty.className = 'coding-meta coding-no-messages'; empty.textContent = 'No messages yet.'; conversation.append(empty); }
237
+ snapshot.items.filter(item => item.type !== 'reasoning').forEach(item => {
238
+ const role = ['user','userMessage'].includes(item.type) ? 'You' : ['agent','assistant','agentMessage'].includes(item.type) ? 'Codex' : null;
239
+ let node = Array.from(conversation.children).find(child => child.dataset.codingItem === item.id);
240
+ if (!node) {
241
+ node = document.createElement(role ? 'article' : 'details'); node.dataset.codingItem = item.id;
242
+ node.className = role ? 'coding-message' + (role === 'You' ? ' coding-user' : '') : 'coding-tool';
243
+ if (role) { const who = document.createElement('strong'); who.textContent = role; node.append(who); const body = document.createElement('div'); body.className = 'coding-text'; node.append(body); }
244
+ else { node.append(document.createElement('summary'),document.createElement('pre')); }
245
+ conversation.append(node);
246
+ }
247
+ if (role) { const body = node.querySelector('.coding-text'); if (body && node.dataset.codingText !== item.text) { body.innerHTML = linkedText(item.text); node.dataset.codingText = item.text; } }
248
+ else {
249
+ const title = ({commandExecution:'Command',fileChange:'File changes',mcpToolCall:'Tool',reasoning:'Reasoning',plan:'Plan',error:'Error'})[item.type] || 'Activity';
250
+ node.querySelector('summary').textContent = title + (item.status ? ' · ' + item.status : '');
251
+ const text = node.querySelector('pre'); if (text.textContent !== item.text) text.textContent = item.text;
252
+ }
253
+ });
254
+ if (nearBottom) jumpToLatest();
255
+ root.querySelector('#coding-latest').hidden = conversation.hidden || nearBottom;
256
+ };
257
+ const poll = async () => {
258
+ if (!session || polling || disposed || authLost) return;
259
+ polling = true; clearTimeout(timer);
260
+ try {
261
+ const response = await fetch(base + '/state?revision=' + encodeURIComponent(String(revision)), {headers:{accept:'application/json'},credentials:'same-origin',cache:'no-store',signal:requestSignal()});
262
+ if (response.status === 401 || response.redirected) { signedOut(); return; }
263
+ if (!response.ok) throw new Error('Could not refresh the session.');
264
+ const snapshot = await response.json();
265
+ if (snapshot.unchanged !== true) render(snapshot);
266
+ if (!pendingSend) showConnection('');
267
+ } catch { if (!disposed) showConnection('Connection lost. Reconnecting… Your draft is saved.'); saveDraft(); }
268
+ finally { polling = false; if (!disposed && !authLost) timer = setTimeout(poll, isActive(status) ? 1000 : 5000); }
269
+ };
270
+ const loadChanges = async () => {
271
+ if (changesBusy || authLost) return;
272
+ changesBusy = true;
273
+ const area = root.querySelector('#coding-change-content');
274
+ const button = root.querySelector('#coding-refresh-changes'); button.disabled = true;
275
+ try {
276
+ const response = await fetch(base + '/changes', {headers:{accept:'application/json'},credentials:'same-origin',cache:'no-store',signal:requestSignal()});
277
+ if (response.status === 401 || response.redirected) { signedOut(); return; }
278
+ if (!response.ok) throw new Error('Could not load changes. Try Refresh changes.');
279
+ const changes = await response.json();
280
+ hasChanges = Boolean(changes.diff || changes.status); updateShipping();
281
+ area.replaceChildren();
282
+ const meta = document.createElement('p'); meta.className = 'coding-meta'; meta.textContent = changes.diff ? 'Changes since this session started.' : changes.status ? 'Changed files are listed below.' : 'No file changes.'; area.append(meta);
283
+ if (changes.status) {
284
+ const files = document.createElement('ul');
285
+ changes.status.split('\n').filter(line => line.trim()).forEach(line => { const file = document.createElement('li'); const code = line.slice(0,2); const label = code.includes('?') || code.includes('A') ? 'Added' : code.includes('D') ? 'Removed' : code.includes('R') ? 'Renamed' : 'Modified'; file.textContent = label + ': ' + line.slice(3); files.append(file); });
286
+ area.append(files);
287
+ }
288
+ if (changes.diff) { const pre = document.createElement('pre'); pre.tabIndex = 0; pre.setAttribute('aria-label','Current diff'); pre.textContent = changes.diff; area.append(pre); }
289
+ if (changes.truncated) { const note = document.createElement('p'); note.textContent = 'This diff is shortened. Inspect the complete changes in the worktree before shipping.'; area.append(note); }
290
+ } catch (error) { area.textContent = error.message || 'Could not load changes.'; }
291
+ finally { changesBusy = false; button.disabled = false; }
292
+ };
293
+ restoreDraft(); updateComposer();
294
+ if (pendingSend) showConnection('Delivery not confirmed. Retry the same message to check its receipt.');
295
+ prompt?.addEventListener('input',saveDraft);
296
+ composer?.querySelector('[name="repo"]')?.addEventListener('change',() => { prompt.value = ''; pendingSend = null; requestField.value = crypto.randomUUID(); restoreDraft(); updateComposer(); });
297
+ root.querySelector('#coding-latest')?.addEventListener('click',jumpToLatest);
298
+ root.querySelector('#coding-conversation')?.addEventListener('scroll',event => {
299
+ const conversation = event.target;
300
+ if (automaticScrollTop !== null && Math.abs(conversation.scrollTop - automaticScrollTop) < 1) { automaticScrollTop = null; return; }
301
+ automaticScrollTop = null;
302
+ followLatest = conversation.scrollHeight - conversation.scrollTop - conversation.clientHeight < 80;
303
+ if (followLatest) root.querySelector('#coding-latest').hidden = true;
304
+ });
305
+ root.querySelector('#coding-changes')?.addEventListener('toggle',event => { if (event.target.open) void loadChanges(); });
306
+ root.querySelector('#coding-refresh-changes')?.addEventListener('click',loadChanges);
307
+ root.addEventListener('submit', async event => {
308
+ const form = event.target;
309
+ if (!form.matches('[data-coding-form]')) return;
310
+ event.preventDefault();
311
+ if (form.dataset.busy || authLost) return;
312
+ const kind = form.dataset.codingForm;
313
+ if (kind === 'send' && status === 'closed') return;
314
+ if (kind === 'send' && status === 'uncertain') { showError('Delivery is not confirmed. Inspect the session before sending again.'); return; }
315
+ const submitter = event.submitter;
316
+ const decision = submitter?.value || 'accept';
317
+ if (!(submitter && submitter.formNoValidate) && !form.reportValidity()) return;
318
+ const data = new URLSearchParams();
319
+ new FormData(form).forEach((value,key) => { if (typeof value === 'string') data.append(key,value); });
320
+ if (kind === 'answer') {
321
+ data.set('decision',decision);
322
+ const answers = {};
323
+ form.querySelectorAll('[name^="question:"]').forEach(field => { answers[field.name.slice(9)] = {answers:[field.value]}; });
324
+ if (Object.keys(answers).length) data.set('answers',JSON.stringify(answers));
325
+ }
326
+ if (kind === 'send' || kind === 'start') {
327
+ if (pendingSend) Object.entries(pendingSend).forEach(([key,value]) => data.set(key,value));
328
+ else {
329
+ pendingSend = {prompt:data.get('prompt'),requestId:data.get('requestId')};
330
+ if (kind === 'start') ['repo','title','model'].forEach(key => pendingSend[key] = data.get(key) || '');
331
+ }
332
+ busy = true; saveDraft(); updateComposer();
333
+ if (kind === 'send') jumpToLatest();
334
+ }
335
+ form.dataset.busy = 'true';
336
+ const buttons = Array.from(form.querySelectorAll('button')); buttons.forEach(button => button.disabled = true);
337
+ try {
338
+ const response = await fetch(form.getAttribute('action'), {method:'POST',headers:{accept:'application/json','content-type':'application/x-www-form-urlencoded'},credentials:'same-origin',body:data.toString(),signal:requestSignal()});
339
+ if (response.status === 401 || response.redirected) { signedOut(); return; }
340
+ const result = await response.json();
341
+ if (!response.ok || !result.ok) {
342
+ if ((kind === 'send' || kind === 'start') && result.delivery === 'rejected') {
343
+ pendingSend = null; prompt.readOnly = false; requestField.value = crypto.randomUUID(); showConnection(''); saveDraft();
344
+ }
345
+ throw Object.assign(new Error(result.error || 'Could not complete this action.'), {sessionId:result.sessionId,delivery:result.delivery});
346
+ }
347
+ showError('');
348
+ if (kind === 'send' || kind === 'start') {
349
+ pendingSend = null; prompt.value = ''; prompt.readOnly = false; requestField.value = crypto.randomUUID(); storage('removeItem',draftKey());
350
+ if (kind === 'start' && result.id) { location.assign('/code/' + encodeURIComponent(result.id)); return; }
351
+ }
352
+ if (kind === 'recover' && form.getAttribute('action').endsWith('/continue')) { pendingSend = null; requestField.value = crypto.randomUUID(); saveDraft(); }
353
+ if (kind === 'answer') form.closest('[data-coding-request]')?.remove();
354
+ if (session) void poll();
355
+ } catch (error) { showError(error.message || 'Could not complete this action.', error.sessionId); if (pendingSend) showConnection(error.delivery === 'pending' ? 'The request is still in progress. Your draft is saved.' : 'Delivery not confirmed. Retry the same message to check its receipt.'); }
356
+ finally { form.querySelectorAll('[type="password"]').forEach(field => field.value = ''); delete form.dataset.busy; buttons.forEach(button => button.disabled = false); busy = false; saveDraft(); updateComposer(); }
357
+ });
358
+ window.addEventListener('pagehide',() => { disposed = true; clearTimeout(timer); saveDraft(); });
359
+ window.addEventListener('pageshow',event => { if (event.persisted) { disposed = false; void poll(); } });
360
+ document.addEventListener('visibilitychange',() => { if (!document.hidden) void poll(); });
361
+ if (session) requestAnimationFrame(jumpToLatest);
362
+ if (session) timer = setTimeout(poll, isActive(status) ? 1000 : 5000);
363
+ })();`;
364
+ }
@@ -0,0 +1,15 @@
1
+ import type { Database } from './store.js';
2
+ export declare function codingCatalogPath(orders: Database): string | null;
3
+ export declare function codingCatalogExists(file: string): boolean;
4
+ export declare function installCodingUpdateGate(orders: Database, id: string): void;
5
+ export declare function removeCodingUpdateGate(orders: Database, id: string): void;
6
+ export declare function activeCodingUpdateWork(orders: Database): {
7
+ coding: number;
8
+ codingDeliveries: number;
9
+ };
10
+ /** An idle provider may remain loaded during drain. The normal server stop
11
+ * must close it and establish native/tool exit before either app swap. */
12
+ export declare function assertCodingUpdateStopped(orders: Database): void;
13
+ /** Back up through SQLite so committed WAL records are included. This is a
14
+ * retained recovery artifact; updates never restore the live catalog. */
15
+ export declare function backupCodingCatalog(sourceFile: string, target: string, updateId: string): Promise<void>;
@@ -0,0 +1,149 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { chmodSync, closeSync, fsyncSync, lstatSync, openSync } from 'node:fs';
3
+ import { sqliteRuntime } from './sqlite-runtime.js';
4
+ const gatePrefix = 'so_coding_update_';
5
+ const pausedMessage = 'Toolroll is updating. Current work can finish; new coding work will resume after the update.';
6
+ export function codingCatalogPath(orders) {
7
+ const main = orders.prepare('PRAGMA database_list').all().find(row => row['name'] === 'main')?.['file'];
8
+ return typeof main === 'string' && main !== '' ? `${main}.coding.sqlite` : null;
9
+ }
10
+ export function codingCatalogExists(file) {
11
+ try {
12
+ lstatSync(file);
13
+ return true;
14
+ }
15
+ catch (error) {
16
+ if (error.code === 'ENOENT')
17
+ return false;
18
+ throw error;
19
+ }
20
+ }
21
+ function connect(file, readOnly) {
22
+ const stat = lstatSync(file);
23
+ if (!stat.isFile() || stat.isSymbolicLink())
24
+ throw Error('The coding catalog is linked or not a regular database. No app was replaced.');
25
+ const db = new (sqliteRuntime().DatabaseSync)(file, { readOnly });
26
+ try {
27
+ db.exec('PRAGMA busy_timeout=1000');
28
+ for (const table of ['coding_owner', 'coding_session', 'coding_item', 'coding_request', 'coding_submission']) {
29
+ if (!db.prepare("SELECT 1 FROM sqlite_master WHERE type='table' AND name=?").get(table))
30
+ throw Error('The coding catalog is incomplete. Preserve it and resolve its state before updating.');
31
+ }
32
+ if (!db.prepare('SELECT 1 FROM coding_owner WHERE singleton=1').get())
33
+ throw Error('The coding catalog has no process ownership record. Preserve it and resolve its state before updating.');
34
+ return db;
35
+ }
36
+ catch (error) {
37
+ db.close();
38
+ throw error;
39
+ }
40
+ }
41
+ function withCatalog(orders, absent, readOnly, action) {
42
+ const file = codingCatalogPath(orders);
43
+ // A legacy installation has no coding catalog. Never create one in the updater.
44
+ if (file === null || !codingCatalogExists(file))
45
+ return absent;
46
+ const db = connect(file, readOnly);
47
+ try {
48
+ return action(db);
49
+ }
50
+ finally {
51
+ db.close();
52
+ }
53
+ }
54
+ const gateRows = (db) => db.prepare("SELECT name,sql FROM sqlite_master WHERE type='trigger' AND name GLOB 'so_coding_update_*' ORDER BY name").all();
55
+ function gateStatements(id) {
56
+ if (!/^[a-f0-9-]{36}$/.test(id))
57
+ throw Error('Invalid coding update identity.');
58
+ const refusal = `SELECT RAISE(ABORT, '${pausedMessage} [${id}]');`;
59
+ return [
60
+ // Workspace saves are INSERT ... ON CONFLICT UPDATE, so existing-session
61
+ // events, approvals and Stop must still be allowed to settle during drain.
62
+ `CREATE TRIGGER ${gatePrefix}session BEFORE INSERT ON coding_session WHEN NOT EXISTS (SELECT 1 FROM coding_session WHERE id=NEW.id) BEGIN ${refusal} END`,
63
+ `CREATE TRIGGER ${gatePrefix}submission BEFORE INSERT ON coding_submission BEGIN ${refusal} END`,
64
+ ];
65
+ }
66
+ function changeGate(db, id, install) {
67
+ const wanted = gateStatements(id);
68
+ db.exec('BEGIN IMMEDIATE');
69
+ try {
70
+ const rows = gateRows(db);
71
+ if (rows.some(row => !wanted.includes(String(row['sql']))) || (rows.length > 0 && rows.length !== wanted.length))
72
+ throw Error('Another or unrecognized update owns coding admission. Its gate was preserved.');
73
+ if (install && rows.length === 0)
74
+ for (const statement of wanted)
75
+ db.exec(statement);
76
+ if (!install)
77
+ for (const row of rows)
78
+ db.exec(`DROP TRIGGER "${String(row['name'])}"`);
79
+ db.exec('COMMIT');
80
+ }
81
+ catch (error) {
82
+ db.exec('ROLLBACK');
83
+ throw error;
84
+ }
85
+ }
86
+ export function installCodingUpdateGate(orders, id) { withCatalog(orders, undefined, false, db => changeGate(db, id, true)); }
87
+ export function removeCodingUpdateGate(orders, id) { withCatalog(orders, undefined, false, db => changeGate(db, id, false)); }
88
+ export function activeCodingUpdateWork(orders) {
89
+ return withCatalog(orders, { coding: 0, codingDeliveries: 0 }, true, db => ({
90
+ coding: Number(db.prepare("SELECT count(*) n FROM coding_session WHERE json_extract(document,'$.status') IN ('starting','working','needs-input','stopping','uncertain') OR json_extract(document,'$.turnId') IS NOT NULL").get()?.['n']),
91
+ codingDeliveries: Number(db.prepare("SELECT count(*) n FROM coding_submission WHERE status IN ('preparing','pending','uncertain')").get()?.['n']),
92
+ }));
93
+ }
94
+ /** An idle provider may remain loaded during drain. The normal server stop
95
+ * must close it and establish native/tool exit before either app swap. */
96
+ export function assertCodingUpdateStopped(orders) {
97
+ withCatalog(orders, undefined, true, db => {
98
+ const owner = db.prepare('SELECT token,pid,native_pid,clean FROM coding_owner WHERE singleton=1').get();
99
+ if (!owner || owner['clean'] !== 1 || owner['token'] !== '' || owner['pid'] !== 0 || owner['native_pid'] !== null)
100
+ throw Error('The coding server has not verified agent and tool shutdown. No app was replaced.');
101
+ });
102
+ }
103
+ function snapshot(db) {
104
+ const data = {};
105
+ // Include every catalog table, including current/future custody records.
106
+ for (const row of db.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%' ORDER BY name").all()) {
107
+ const name = String(row['name']);
108
+ data[name] = db.prepare(`SELECT * FROM "${name.replaceAll('"', '""')}" ORDER BY rowid`).all();
109
+ }
110
+ return createHash('sha256').update(JSON.stringify(data)).digest('hex');
111
+ }
112
+ /** Back up through SQLite so committed WAL records are included. This is a
113
+ * retained recovery artifact; updates never restore the live catalog. */
114
+ export async function backupCodingCatalog(sourceFile, target, updateId) {
115
+ const reservation = connect(sourceFile, false);
116
+ try {
117
+ reservation.exec('BEGIN IMMEDIATE');
118
+ const source = connect(sourceFile, true);
119
+ let before;
120
+ try {
121
+ before = snapshot(source);
122
+ await sqliteRuntime().backup(source, target);
123
+ }
124
+ finally {
125
+ source.close();
126
+ }
127
+ chmodSync(target, 0o600);
128
+ const copied = connect(target, false);
129
+ try {
130
+ if (copied.prepare('PRAGMA integrity_check').get()?.['integrity_check'] !== 'ok' || copied.prepare('PRAGMA foreign_key_check').all().length !== 0 || snapshot(copied) !== before)
131
+ throw Error('Coding catalog backup verification failed. The installed app is unchanged.');
132
+ changeGate(copied, updateId, false);
133
+ }
134
+ finally {
135
+ copied.close();
136
+ }
137
+ reservation.exec('COMMIT');
138
+ const fd = openSync(target, 'r');
139
+ try {
140
+ fsyncSync(fd);
141
+ }
142
+ finally {
143
+ closeSync(fd);
144
+ }
145
+ }
146
+ finally {
147
+ reservation.close();
148
+ }
149
+ }