toolroll 0.9.54 → 0.9.56

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 (350) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/README.md +29 -12
  3. package/dist/action-ledger.d.ts +2 -4
  4. package/dist/action-ledger.js +2 -10
  5. package/dist/agent-onboard.js +4 -1
  6. package/dist/approval-policy.d.ts +5 -5
  7. package/dist/approval-policy.js +4 -4
  8. package/dist/approval-rules-ui.d.ts +3 -2
  9. package/dist/approval-rules-ui.js +17 -22
  10. package/dist/assignment-ui.d.ts +3 -2
  11. package/dist/assignment-ui.js +4 -11
  12. package/dist/backup-ui.d.ts +2 -1
  13. package/dist/backup-ui.js +16 -27
  14. package/dist/brand-mark.d.ts +3 -2
  15. package/dist/brand-mark.js +4 -4
  16. package/dist/browser/workspace.css +1 -1
  17. package/dist/browser/workspace.js +25 -25
  18. package/dist/browser-shell.d.ts +2 -1
  19. package/dist/browser-shell.js +9 -9
  20. package/dist/browser-workspace.d.ts +14 -15
  21. package/dist/browser-workspace.js +0 -3
  22. package/dist/budget-alerts.js +3 -3
  23. package/dist/chat-actions.d.ts +23 -18
  24. package/dist/chat-actions.js +110 -88
  25. package/dist/chat-ask.d.ts +2 -2
  26. package/dist/chat-ask.js +5 -5
  27. package/dist/chat-channel.d.ts +15 -15
  28. package/dist/chat-channel.js +20 -20
  29. package/dist/chat-controls.d.ts +0 -4
  30. package/dist/chat-controls.js +0 -1
  31. package/dist/chat-core.js +1 -1
  32. package/dist/chat-delivery-state.js +2 -2
  33. package/dist/chat-delivery.d.ts +2 -2
  34. package/dist/chat-delivery.js +27 -34
  35. package/dist/chat-flow.js +3 -3
  36. package/dist/chat-messages.d.ts +1 -1
  37. package/dist/chat-messages.js +4 -4
  38. package/dist/chat-migration.d.ts +1 -1
  39. package/dist/chat-migration.js +11 -11
  40. package/dist/chat-polish.d.ts +5 -4
  41. package/dist/chat-polish.js +9 -10
  42. package/dist/chat-rooms.js +3 -3
  43. package/dist/chat-warmth.d.ts +2 -2
  44. package/dist/check-levels-ui.d.ts +2 -1
  45. package/dist/check-levels-ui.js +26 -53
  46. package/dist/cli.d.ts +3 -1
  47. package/dist/cli.js +15 -10
  48. package/dist/coding-shipping-ui.d.ts +2 -1
  49. package/dist/coding-shipping-ui.js +6 -6
  50. package/dist/coding-ui.d.ts +3 -1
  51. package/dist/coding-ui.js +30 -29
  52. package/dist/contracts/chat-actions.d.ts +32 -27
  53. package/dist/contracts/chat-actions.js +29 -11
  54. package/dist/contracts/chat-content.d.ts +1 -1
  55. package/dist/contracts/chat-content.js +1 -1
  56. package/dist/contracts/chat-tables.js +4 -4
  57. package/dist/contracts/cli.d.ts +1 -3
  58. package/dist/contracts/console-api.d.ts +7 -7
  59. package/dist/contracts/console-api.js +13 -12
  60. package/dist/contracts/flow-propose.d.ts +3 -3
  61. package/dist/contracts/flow.d.ts +132 -125
  62. package/dist/contracts/flow.js +27 -12
  63. package/dist/contracts/health.d.ts +1 -3
  64. package/dist/contracts/lead-context.d.ts +1 -1
  65. package/dist/contracts/lead-context.js +2 -2
  66. package/dist/contracts/lead-tools.d.ts +20 -18
  67. package/dist/contracts/lead-tools.js +15 -15
  68. package/dist/contracts/stage-output.d.ts +1 -1
  69. package/dist/contracts/stage-output.js +1 -1
  70. package/dist/contracts/store-json.d.ts +23 -22
  71. package/dist/contracts/store-json.js +17 -17
  72. package/dist/contracts/{teammate-turn.d.ts → subagent-turn.d.ts} +5 -5
  73. package/dist/contracts/{teammate-turn.js → subagent-turn.js} +7 -7
  74. package/dist/control-ui.d.ts +5 -4
  75. package/dist/control-ui.js +9 -34
  76. package/dist/converse.d.ts +23 -23
  77. package/dist/converse.js +28 -28
  78. package/dist/coordinator-proposals.d.ts +2 -2
  79. package/dist/coordinator-proposals.js +1 -1
  80. package/dist/coordinator.js +1 -1
  81. package/dist/credentials-ui.d.ts +5 -4
  82. package/dist/credentials-ui.js +23 -24
  83. package/dist/demo-chat.d.ts +3 -3
  84. package/dist/demo-chat.js +29 -76
  85. package/dist/deprecations.d.ts +12 -0
  86. package/dist/deprecations.js +20 -0
  87. package/dist/desktop-update-gate.js +8 -4
  88. package/dist/desktop-update.js +6 -1
  89. package/dist/discord-chat.js +6 -6
  90. package/dist/discord-settings.d.ts +3 -2
  91. package/dist/discord-settings.js +17 -42
  92. package/dist/dispatch.d.ts +1 -1
  93. package/dist/dispatch.js +1 -1
  94. package/dist/envelope.js +10 -1
  95. package/dist/evidence-pack.d.ts +2 -1
  96. package/dist/evidence-pack.js +45 -49
  97. package/dist/export-ui.d.ts +7 -2
  98. package/dist/export-ui.js +4 -10
  99. package/dist/export.js +7 -7
  100. package/dist/flow-engine.d.ts +2 -2
  101. package/dist/flow-engine.js +21 -21
  102. package/dist/flow-gallery-ui.d.ts +4 -3
  103. package/dist/flow-gallery-ui.js +28 -43
  104. package/dist/flow-insights.d.ts +1 -1
  105. package/dist/flow-share.js +13 -8
  106. package/dist/flow-starters-ui.d.ts +2 -1
  107. package/dist/flow-starters-ui.js +12 -15
  108. package/dist/flow-steps.d.ts +5 -5
  109. package/dist/flow-steps.js +19 -19
  110. package/dist/flow-triggers.d.ts +2 -1
  111. package/dist/flow-triggers.js +17 -15
  112. package/dist/flows-cli.js +1 -1
  113. package/dist/flows-ui.d.ts +4 -3
  114. package/dist/flows-ui.js +38 -49
  115. package/dist/flows.js +21 -20
  116. package/dist/guides.js +112 -6
  117. package/dist/html.d.ts +76 -0
  118. package/dist/html.js +173 -0
  119. package/dist/integrations-ui.d.ts +2 -1
  120. package/dist/integrations-ui.js +19 -23
  121. package/dist/invoke.js +1 -1
  122. package/dist/kits-ui.d.ts +5 -4
  123. package/dist/kits-ui.js +22 -24
  124. package/dist/kits.d.ts +12 -12
  125. package/dist/kits.js +31 -31
  126. package/dist/knowledge-ui.d.ts +6 -5
  127. package/dist/knowledge-ui.js +26 -25
  128. package/dist/{mate-cli.d.ts → lead-cli.d.ts} +11 -11
  129. package/dist/{mate-cli.js → lead-cli.js} +38 -38
  130. package/dist/lead-commitments.js +4 -4
  131. package/dist/lead-context.d.ts +12 -2
  132. package/dist/lead-context.js +15 -17
  133. package/dist/{mate-contract.d.ts → lead-contract.d.ts} +3 -3
  134. package/dist/{mate-contract.js → lead-contract.js} +7 -6
  135. package/dist/{mate-doors.d.ts → lead-doors.d.ts} +6 -6
  136. package/dist/{mate-doors.js → lead-doors.js} +24 -24
  137. package/dist/lead-follow.d.ts +5 -5
  138. package/dist/lead-follow.js +9 -9
  139. package/dist/lead-people.d.ts +5 -5
  140. package/dist/lead-people.js +14 -14
  141. package/dist/{mate-progress.d.ts → lead-progress.d.ts} +9 -9
  142. package/dist/{mate-progress.js → lead-progress.js} +3 -3
  143. package/dist/{mate-tools.d.ts → lead-tools.d.ts} +19 -19
  144. package/dist/{mate-tools.js → lead-tools.js} +109 -101
  145. package/dist/{mate.d.ts → lead.d.ts} +27 -27
  146. package/dist/{mate.js → lead.js} +102 -102
  147. package/dist/ledger-view.d.ts +2 -1
  148. package/dist/ledger-view.js +27 -29
  149. package/dist/limits-ui.d.ts +3 -2
  150. package/dist/limits-ui.js +8 -8
  151. package/dist/live-bus.d.ts +50 -8
  152. package/dist/live-bus.js +133 -50
  153. package/dist/mcp-connect.d.ts +1 -1
  154. package/dist/mcp-connect.js +3 -3
  155. package/dist/mcp-core.js +1 -1
  156. package/dist/mcp-oauth.js +35 -36
  157. package/dist/memory-pass.d.ts +2 -2
  158. package/dist/memory-pass.js +7 -7
  159. package/dist/models-ui.d.ts +2 -1
  160. package/dist/models-ui.js +25 -35
  161. package/dist/monitoring-ui.d.ts +4 -3
  162. package/dist/monitoring-ui.js +13 -26
  163. package/dist/notification-rows.d.ts +2 -2
  164. package/dist/openrouter-models.d.ts +2 -1
  165. package/dist/openrouter-models.js +5 -9
  166. package/dist/operate.d.ts +3 -3
  167. package/dist/operate.js +46 -41
  168. package/dist/people-audit-ui.d.ts +2 -1
  169. package/dist/people-audit-ui.js +13 -13
  170. package/dist/policy-ui.d.ts +3 -2
  171. package/dist/policy-ui.js +17 -23
  172. package/dist/policy.d.ts +1 -1
  173. package/dist/principal.d.ts +1 -1
  174. package/dist/principal.js +2 -2
  175. package/dist/project-delete-ui.d.ts +4 -3
  176. package/dist/project-delete-ui.js +16 -28
  177. package/dist/project-delete.d.ts +1 -1
  178. package/dist/project-delete.js +26 -26
  179. package/dist/project-memory.js +1 -1
  180. package/dist/project-tools.d.ts +1 -1
  181. package/dist/project-tools.js +1 -1
  182. package/dist/pull-request-ui.d.ts +2 -1
  183. package/dist/pull-request-ui.js +17 -32
  184. package/dist/recipe-ui.d.ts +8 -7
  185. package/dist/recipe-ui.js +39 -32
  186. package/dist/release-adapters.d.ts +60 -0
  187. package/dist/release-adapters.js +435 -0
  188. package/dist/release-cli.d.ts +24 -0
  189. package/dist/release-cli.js +181 -0
  190. package/dist/release-gate.d.ts +80 -0
  191. package/dist/release-gate.js +150 -0
  192. package/dist/release-links.d.ts +29 -0
  193. package/dist/release-links.js +102 -0
  194. package/dist/release.d.ts +331 -0
  195. package/dist/release.js +550 -0
  196. package/dist/reply-shape.d.ts +2 -1
  197. package/dist/reply-shape.js +5 -5
  198. package/dist/repository-context-ui.d.ts +2 -1
  199. package/dist/repository-context-ui.js +2 -7
  200. package/dist/request-budget-ui.d.ts +2 -1
  201. package/dist/request-budget-ui.js +16 -16
  202. package/dist/result-review.d.ts +2 -3
  203. package/dist/result-review.js +3 -9
  204. package/dist/retention-ui.d.ts +7 -1
  205. package/dist/retention-ui.js +7 -11
  206. package/dist/retention.js +4 -4
  207. package/dist/scope.d.ts +2 -2
  208. package/dist/scout-report.d.ts +1 -1
  209. package/dist/scout-report.js +1 -1
  210. package/dist/serve.d.ts +11 -2
  211. package/dist/serve.js +105 -99
  212. package/dist/server/chat.d.ts +7 -10
  213. package/dist/server/chat.js +984 -1021
  214. package/dist/server/chrome.d.ts +291 -0
  215. package/dist/server/chrome.js +747 -0
  216. package/dist/server/console.css +2819 -0
  217. package/dist/server/flows.d.ts +2 -6
  218. package/dist/server/flows.js +854 -773
  219. package/dist/server/guards.d.ts +1 -1
  220. package/dist/server/guards.js +8 -5
  221. package/dist/server/handler-context.d.ts +1 -1
  222. package/dist/server/handler-context.js +1 -1
  223. package/dist/server/handler-registry.d.ts +8 -6
  224. package/dist/server/handler-registry.js +14 -2
  225. package/dist/server/http.d.ts +274 -0
  226. package/dist/server/http.js +239 -0
  227. package/dist/server/live.d.ts +34 -0
  228. package/dist/server/live.js +309 -0
  229. package/dist/server/pages.d.ts +2 -6
  230. package/dist/server/pages.js +808 -789
  231. package/dist/server/people-tokens.d.ts +1 -6
  232. package/dist/server/people-tokens.js +581 -573
  233. package/dist/server/remote.d.ts +4 -7
  234. package/dist/server/remote.js +39 -70
  235. package/dist/server/render-chat.d.ts +402 -0
  236. package/dist/server/render-chat.js +927 -0
  237. package/dist/server/render-pages.d.ts +535 -0
  238. package/dist/server/render-pages.js +1277 -0
  239. package/dist/server/render-people.d.ts +32 -0
  240. package/dist/server/render-people.js +121 -0
  241. package/dist/server/render-results.d.ts +744 -0
  242. package/dist/server/render-results.js +2237 -0
  243. package/dist/server/render-settings.d.ts +154 -0
  244. package/dist/server/render-settings.js +580 -0
  245. package/dist/server/render-tasks.d.ts +632 -0
  246. package/dist/server/render-tasks.js +2345 -0
  247. package/dist/server/request-context.d.ts +46 -0
  248. package/dist/server/request-context.js +17 -0
  249. package/dist/server/route-table.d.ts +25 -3
  250. package/dist/server/route-table.js +167 -77
  251. package/dist/server/runtime.d.ts +28 -20
  252. package/dist/server/runtime.js +7 -1
  253. package/dist/server/session.d.ts +138 -0
  254. package/dist/server/session.js +94 -0
  255. package/dist/server/settings.d.ts +1 -5
  256. package/dist/server/settings.js +2186 -2121
  257. package/dist/server/task-views.d.ts +11 -4
  258. package/dist/server/task-views.js +26 -22
  259. package/dist/server/tasks.d.ts +9 -9
  260. package/dist/server/tasks.js +1489 -1420
  261. package/dist/server-telemetry.d.ts +4 -3
  262. package/dist/server-telemetry.js +4 -8
  263. package/dist/setup-guide.d.ts +2 -2
  264. package/dist/skills-ui.d.ts +4 -3
  265. package/dist/skills-ui.js +14 -17
  266. package/dist/skills.d.ts +1 -1
  267. package/dist/skills.js +2 -1
  268. package/dist/slack-chat.d.ts +2 -2
  269. package/dist/slack-chat.js +6 -6
  270. package/dist/slack-settings.d.ts +3 -2
  271. package/dist/slack-settings.js +17 -42
  272. package/dist/spend-ui.d.ts +5 -4
  273. package/dist/spend-ui.js +17 -40
  274. package/dist/spend.d.ts +14 -14
  275. package/dist/spend.js +31 -31
  276. package/dist/sso-ui.d.ts +3 -2
  277. package/dist/sso-ui.js +26 -27
  278. package/dist/storage-ui.d.ts +2 -1
  279. package/dist/storage-ui.js +14 -33
  280. package/dist/store-baseline.d.ts +19 -0
  281. package/dist/store-baseline.js +2873 -0
  282. package/dist/store.d.ts +197 -249
  283. package/dist/store.js +747 -3084
  284. package/dist/subagent-admin.d.ts +53 -0
  285. package/dist/{teammate-admin.js → subagent-admin.js} +37 -37
  286. package/dist/{teammate-desk.d.ts → subagent-desk.d.ts} +22 -23
  287. package/dist/{teammate-desk.js → subagent-desk.js} +26 -35
  288. package/dist/{teammate-memory.d.ts → subagent-memory.d.ts} +17 -17
  289. package/dist/{teammate-memory.js → subagent-memory.js} +41 -41
  290. package/dist/{teammate-question.d.ts → subagent-question.d.ts} +6 -6
  291. package/dist/{teammate-question.js → subagent-question.js} +17 -17
  292. package/dist/{teammate-tools.d.ts → subagent-tools.d.ts} +18 -18
  293. package/dist/{teammate-tools.js → subagent-tools.js} +36 -36
  294. package/dist/{teammate-week.d.ts → subagent-week.d.ts} +10 -10
  295. package/dist/{teammate-week.js → subagent-week.js} +23 -23
  296. package/dist/{teammate-work.d.ts → subagent-work.d.ts} +21 -21
  297. package/dist/{teammate-work.js → subagent-work.js} +50 -50
  298. package/dist/subagents-ui.d.ts +22 -0
  299. package/dist/subagents-ui.js +137 -0
  300. package/dist/{teammates.d.ts → subagents.d.ts} +11 -11
  301. package/dist/{teammates.js → subagents.js} +21 -21
  302. package/dist/subscription-chat.d.ts +13 -13
  303. package/dist/subscription-chat.js +19 -19
  304. package/dist/surface.d.ts +19 -2
  305. package/dist/surface.js +20 -33
  306. package/dist/task-composer.d.ts +2 -1
  307. package/dist/task-composer.js +7 -7
  308. package/dist/task-status.d.ts +4 -3
  309. package/dist/task-status.js +15 -16
  310. package/dist/team-cli.d.ts +1 -0
  311. package/dist/team-cli.js +14 -2
  312. package/dist/team-http.d.ts +5 -9
  313. package/dist/team-http.js +5 -63
  314. package/dist/team-leads.js +13 -13
  315. package/dist/team-runtime.d.ts +2 -2
  316. package/dist/team-runtime.js +8 -8
  317. package/dist/team-ui.d.ts +2 -1
  318. package/dist/team-ui.js +2 -5
  319. package/dist/teams-chat.js +6 -6
  320. package/dist/teams-settings.d.ts +3 -2
  321. package/dist/teams-settings.js +20 -31
  322. package/dist/telegram-flow.d.ts +3 -3
  323. package/dist/telegram-flow.js +3 -3
  324. package/dist/{telegram-mate.d.ts → telegram-lead.d.ts} +5 -5
  325. package/dist/{telegram-mate.js → telegram-lead.js} +27 -27
  326. package/dist/telegram-settings.d.ts +3 -2
  327. package/dist/telegram-settings.js +14 -25
  328. package/dist/telegram-status.js +1 -1
  329. package/dist/telegram-team.d.ts +1 -1
  330. package/dist/telegram-team.js +5 -5
  331. package/dist/telegram.d.ts +1 -1
  332. package/dist/telegram.js +9 -30
  333. package/dist/text-limits.d.ts +18 -18
  334. package/dist/text-limits.js +18 -18
  335. package/dist/toolroll-update-ui.d.ts +3 -2
  336. package/dist/toolroll-update-ui.js +35 -39
  337. package/dist/toolroll-update.d.ts +8 -0
  338. package/dist/toolroll-update.js +45 -8
  339. package/dist/tools-ui.d.ts +5 -2
  340. package/dist/tools-ui.js +31 -31
  341. package/dist/when-html.d.ts +11 -3
  342. package/dist/when-html.js +5 -6
  343. package/dist/workspace-ui.d.ts +4 -2
  344. package/dist/workspace-ui.js +10 -10
  345. package/package.json +1 -2
  346. package/dist/server/shared.d.ts +0 -3151
  347. package/dist/server/shared.js +0 -12001
  348. package/dist/teammate-admin.d.ts +0 -53
  349. package/dist/teammates-ui.d.ts +0 -20
  350. package/dist/teammates-ui.js +0 -166
@@ -25,7 +25,7 @@ CREATE TABLE IF NOT EXISTS chat_event (
25
25
  kind TEXT NOT NULL CHECK(kind IN ('message','action','pair','notice')),
26
26
  channel TEXT NOT NULL, member TEXT NOT NULL, ts TEXT NOT NULL, thread TEXT NOT NULL,
27
27
  payload TEXT NOT NULL, created TEXT NOT NULL,
28
- session INTEGER REFERENCES mate_session(id), state TEXT NOT NULL DEFAULT 'queued' CHECK(state IN ('queued','done','dropped')),
28
+ session INTEGER REFERENCES lead_session(id), state TEXT NOT NULL DEFAULT 'queued' CHECK(state IN ('queued','done','dropped')),
29
29
  next_at TEXT, problem TEXT
30
30
  );
31
31
  CREATE INDEX IF NOT EXISTS chat_pending_event ON chat_event(installation,state,next_at);
@@ -38,7 +38,7 @@ CREATE TABLE IF NOT EXISTS chat_part (
38
38
  );
39
39
  CREATE TABLE IF NOT EXISTS chat_action (
40
40
  token TEXT PRIMARY KEY, part INTEGER NOT NULL REFERENCES chat_part(id),
41
- proposal INTEGER NOT NULL REFERENCES mate_proposal(id), phase TEXT NOT NULL CHECK(phase IN ('confirm','dismiss','yes','cancel')),
41
+ proposal INTEGER NOT NULL REFERENCES lead_proposal(id), phase TEXT NOT NULL CHECK(phase IN ('confirm','dismiss','yes','cancel')),
42
42
  expires TEXT NOT NULL, consumed TEXT
43
43
  );
44
44
  CREATE TABLE IF NOT EXISTS chat_progress (
@@ -105,19 +105,19 @@ CREATE TABLE IF NOT EXISTS chat_flow_note (
105
105
  );
106
106
  CREATE INDEX IF NOT EXISTS chat_flow_note_visit ON chat_flow_note(card, entry);
107
107
  `;
108
- /** v93: a teammate's question in the chat app. One button per option (choice)
108
+ /** v93: a subagent's question in the chat app. One button per option (choice)
109
109
  * and one to answer in words (choice NULL); that one opens a prompt the
110
110
  * person's next message in their DM answers. Never in CHAT_TABLES: an older
111
111
  * database has no such tables until this schema creates them. */
112
112
  const LEGACY_APP_QUESTION_SCHEMA = `
113
113
  CREATE TABLE IF NOT EXISTS chat_question_action (
114
114
  token TEXT PRIMARY KEY, part INTEGER NOT NULL REFERENCES chat_part(id),
115
- question INTEGER NOT NULL REFERENCES teammate_question(id), choice TEXT, expires TEXT NOT NULL, consumed TEXT
115
+ question INTEGER NOT NULL REFERENCES subagent_question(id), choice TEXT, expires TEXT NOT NULL, consumed TEXT
116
116
  );
117
117
  CREATE INDEX IF NOT EXISTS chat_question_action_question ON chat_question_action(question);
118
118
  CREATE TABLE IF NOT EXISTS chat_question_prompt (
119
119
  id INTEGER PRIMARY KEY AUTOINCREMENT, binding INTEGER NOT NULL REFERENCES chat_binding(id),
120
- question INTEGER NOT NULL REFERENCES teammate_question(id), created TEXT NOT NULL, expires TEXT NOT NULL, consumed TEXT
120
+ question INTEGER NOT NULL REFERENCES subagent_question(id), created TEXT NOT NULL, expires TEXT NOT NULL, consumed TEXT
121
121
  );
122
122
  CREATE INDEX IF NOT EXISTS chat_question_prompt_open ON chat_question_prompt(binding, consumed);
123
123
  `;
@@ -306,13 +306,13 @@ CREATE TABLE IF NOT EXISTS telegram_flow_action (
306
306
  consumed_at TEXT
307
307
  );
308
308
  CREATE INDEX IF NOT EXISTS telegram_flow_action_visit ON telegram_flow_action (card, entry);
309
- -- v93: a teammate's question on Telegram. One button per option (choice)
309
+ -- v93: a subagent's question on Telegram. One button per option (choice)
310
310
  -- and one to reply in words (choice NULL), placed on the message it rides;
311
311
  -- a reply to the prompt the Reply button sends is the answer.
312
312
  CREATE TABLE IF NOT EXISTS telegram_question_action (
313
313
  token TEXT PRIMARY KEY,
314
314
  binding INTEGER NOT NULL REFERENCES telegram_binding(id) ON DELETE RESTRICT,
315
- question INTEGER NOT NULL REFERENCES teammate_question(id),
315
+ question INTEGER NOT NULL REFERENCES subagent_question(id),
316
316
  choice TEXT,
317
317
  chat_id TEXT NOT NULL,
318
318
  message_id TEXT,
@@ -324,7 +324,7 @@ CREATE TABLE IF NOT EXISTS telegram_question_prompt (
324
324
  chat_id TEXT NOT NULL,
325
325
  message_id TEXT NOT NULL,
326
326
  binding INTEGER NOT NULL REFERENCES telegram_binding(id) ON DELETE RESTRICT,
327
- question INTEGER NOT NULL REFERENCES teammate_question(id),
327
+ question INTEGER NOT NULL REFERENCES subagent_question(id),
328
328
  created_at TEXT NOT NULL,
329
329
  expires_at TEXT NOT NULL,
330
330
  consumed_at TEXT,
@@ -414,7 +414,7 @@ CREATE TABLE IF NOT EXISTS telegram_conversation (
414
414
  );
415
415
  CREATE INDEX IF NOT EXISTS telegram_conversation_queue ON telegram_conversation (bot_id, state, id);
416
416
 
417
- -- v62: opaque one-tap tokens for the mate's proposal cards, the same shape
417
+ -- v62: opaque one-tap tokens for the lead's proposal cards, the same shape
418
418
  -- as telegram_action for decisions: callback_data carries only the token,
419
419
  -- and what a tap MEANS (which binding, which proposal, which phase) lives
420
420
  -- here. Consumed exactly once; an irreversible answer arms a yes/cancel
@@ -422,7 +422,7 @@ CREATE INDEX IF NOT EXISTS telegram_conversation_queue ON telegram_conversation
422
422
  CREATE TABLE IF NOT EXISTS telegram_proposal_action (
423
423
  token TEXT PRIMARY KEY,
424
424
  binding INTEGER NOT NULL REFERENCES telegram_binding(id) ON DELETE RESTRICT,
425
- proposal INTEGER NOT NULL REFERENCES mate_proposal(id) ON DELETE CASCADE,
425
+ proposal INTEGER NOT NULL REFERENCES lead_proposal(id) ON DELETE CASCADE,
426
426
  phase TEXT NOT NULL CHECK (phase IN ('confirm','dismiss','yes','cancel')),
427
427
  chat_id TEXT NOT NULL,
428
428
  message_id TEXT,
@@ -456,7 +456,7 @@ CREATE TABLE IF NOT EXISTS telegram_conversation_part (
456
456
  kind TEXT NOT NULL CHECK (kind IN ('reply','card','image')),
457
457
  text TEXT NOT NULL,
458
458
  reply_to TEXT,
459
- proposal INTEGER REFERENCES mate_proposal(id) ON DELETE SET NULL,
459
+ proposal INTEGER REFERENCES lead_proposal(id) ON DELETE SET NULL,
460
460
  keyboard_json TEXT,
461
461
  state TEXT NOT NULL CHECK (state IN ('pending','sent','dropped')),
462
462
  message_id TEXT,
@@ -1,16 +1,17 @@
1
+ import { type Html } from './html.js';
1
2
  import type { ChatSnapshot } from './store.js';
2
3
  /** Presentation only. These are recorded completions, not a claim that
3
4
  * work happened since the user last visited, or that every result passed. */
4
5
  export declare function completedWorkHtml(snapshot: ChatSnapshot, projectLabels: readonly string[], assignments?: Readonly<Record<string, {
5
6
  state: string;
6
- }>>): string;
7
+ }>>): Html;
7
8
  /** Keep activity truthful: the turn's recorded step count is not a
8
9
  * percentage or an invented tool phase. Billing detail stays available. */
9
10
  export declare function chatWorkingHtml(input: {
10
11
  details: string;
11
- stopForm?: string;
12
+ stopForm?: Html;
12
13
  keyed?: boolean;
13
- }): string;
14
+ }): Html;
14
15
  /** Recorded activity is inspectable, not a row of performance badges. */
15
- export declare function chatActivityDetailsHtml(activity: string | null): string;
16
+ export declare function chatActivityDetailsHtml(activity: string | null): Html;
16
17
  export declare const CHAT_POLISH_CSS = "\n /* Quiet workspace surfaces: color signals state, not an AI aesthetic. */\n body { background: var(--background); }\n .chat-workspace .chat-overview, .chat-workspace .chat-plan, .chat-workspace .result-panel,\n .chat-workspace .decide-card { background: var(--card); box-shadow: none; }\n .chat-workspace .result-panel { border-radius: .75rem; }\n .chat-workspace .chat-empty::before { content: none; }\n .chat-workspace .thread .msg.mate::before { background: var(--muted); box-shadow: none; border: 0; }\n .chat-workspace .thread .msg.op { box-shadow: none; border: 0; }\n .chat-workspace .completion-receipt { background: var(--card); box-shadow: none; }\n .chat-workspace .completion-receipt::after { content: none; }\n .chat-workspace .thread { min-height: 0; }\n .chat-workspace .chat-turn-history { margin: 1rem 0; border: 0; border-top: 1px solid var(--border); border-radius: 0; background: transparent; box-shadow: none; padding: 0; }\n .chat-turn-history > summary { display: flex; align-items: center; gap: .75rem; min-height: 44px; cursor: pointer; font-size: .875rem; }\n .chat-turn-history > summary .meta { margin-left: auto; }\n .chat-turn-history .row { display: flex; flex-wrap: wrap; gap: .4rem; }\n .chat-turn-history .row .right { float: none; margin-left: auto; }\n .chat-workspace .task-live-summary { margin-bottom: .75rem; padding-bottom: .25rem; }\n .chat-workspace .task-journey > details, .chat-workspace .result-feedback-history,\n .chat-workspace .chat-limits, .chat-workspace .task-chat-agents {\n border: 0; border-radius: 0; background: transparent; box-shadow: none; padding: 0; margin: .25rem 0;\n }\n .chat-workspace .task-chat-agents > summary { min-height: 44px; cursor: pointer; font-size: .8125rem; color: var(--muted-foreground); }\n .chat-workspace .task-chat-agents p { margin: 0 0 .5rem; }\n .chat-workspace .decide-options .decide-option { border: 0; border-top: 1px solid var(--border); border-radius: 0; background: transparent; padding: .75rem 0; }\n .chat-workspace .chat-plan { padding: 1rem; margin-top: .5rem; }\n .chat-workspace .result-knowledge { margin-bottom: .5rem; }\n .chat-workspace .result-panel [data-result-view] > details { margin-block: 0; padding-block: 0; }\n .chat-workspace .result-panel [data-result-view] > details > summary { min-height: 44px; display: flex; align-items: center; }\n .chat-workspace .result-feedback-history > summary { min-height: 44px; }\n .chat-activity-details { border: 0; background: transparent; box-shadow: none; border-radius: 0; padding: 0; margin: 0; }\n .chat-activity-details > summary { min-height: 44px; display: flex; align-items: center; gap: .4rem; cursor: pointer; color: var(--muted-foreground); font-size: .75rem; }\n .chat-activity-details > summary::after { content: '\u2304'; }\n .chat-activity-details[open] > summary::after { transform: rotate(180deg); }\n .chat-activity-details .chat-activity { gap: .25rem .75rem; }\n .chat-activity-details .chat-activity span { border: 0; padding: 0; border-radius: 0; font: inherit; font-size: .75rem; }\n .chat-workspace .chat-message-foot { justify-content: flex-start; align-items: baseline; margin-top: .25rem; }\n .chat-activity-row { border: 0; background: transparent; box-shadow: none; padding: .5rem 0; min-height: 44px; gap: .65rem; }\n .chat-activity-row details { flex: 1; min-width: 0; border: 0; background: transparent; padding: 0; margin: 0; box-shadow: none; }\n .chat-activity-row summary { min-height: 44px; display: flex; align-items: center; cursor: pointer; width: fit-content; font-size: .875rem; font-weight: 550; }\n .chat-activity-row summary::after { content: '\u2304'; margin-left: .65rem; color: var(--muted-foreground); }\n .chat-activity-row details[open] summary::after { transform: rotate(180deg); }\n .chat-activity-row .meta { padding-bottom: .5rem; overflow-wrap: anywhere; }\n .activity-dot { width: 6px; height: 6px; border-radius: 50%; background: currentColor; flex: none; }\n .task-live-summary { border-bottom: 1px solid var(--border); margin-bottom: 1rem; padding-bottom: .5rem; }\n .task-live-summary .task-journey, .task-live-summary .task-control { border: 0; border-radius: 0; box-shadow: none; background: transparent; margin: 0; padding: .65rem 0; }\n .task-live-summary .task-journey h2 { font-size: 1rem; }\n .task-live-summary .agents-strip { margin: .4rem 0; }\n .chat-decisions .chat-section-head { margin-bottom: .5rem; }\n .chat-decisions h2 { font-size: .875rem; font-weight: 550; color: var(--muted-foreground); }\n .decision-context summary { cursor: pointer; min-height: 44px; display: flex; align-items: center; font-size: .8125rem; color: var(--muted-foreground); }\n .decision-context { border: 0; padding: 0; margin: 0; background: transparent; box-shadow: none; }\n .decide-options { display: grid; gap: .5rem; }\n .decide-options .decide-option { margin: 0; padding: .65rem .75rem; border: 1px solid var(--border); border-radius: .75rem; background: var(--background); }\n .decide-options .decide-option .meta { display: block; margin-top: .3rem; overflow-wrap: anywhere; }\n .decide-options .decide-option button { min-height: 44px; max-width: 100%; white-space: normal; text-align: left; }\n .chat-completed { margin-top: 1rem; }\n .chat-completed h3 { font-size: .8125rem; font-weight: 550; margin: 0 0 .25rem; color: var(--muted-foreground); }\n .chat-completed-action { flex: none; font-size: .75rem; white-space: nowrap; color: var(--muted-foreground); }\n .chat-overview .chat-overview-stat { box-shadow: none; border: 0; background: transparent; border-radius: 0; }\n .chat-overview .chat-overview-stat + .chat-overview-stat { border-left: 1px solid var(--border); }\n .result-panel .result-request { border-top: 1px solid var(--border); padding-top: 1rem; }\n .result-panel .result-head h2 { color: var(--foreground); }\n .result-panel .result-request h3 { color: var(--foreground); font-size: .875rem; text-transform: none; letter-spacing: 0; }\n .result-panel .result-feedback-actions { display: flex; justify-content: flex-end; align-items: center; flex-wrap: wrap; gap: .5rem; }\n .result-panel .result-feedback-actions > [data-request-changes] { order: 2; background: var(--foreground); color: var(--background); border-color: var(--foreground); box-shadow: none; }\n .result-panel .result-feedback-actions > button { width: auto; min-height: 44px; white-space: nowrap; }\n .result-panel .result-feedback-actions > .quiet { background: transparent; border-color: transparent; box-shadow: none; }\n .result-panel .result-feedback-actions > button:disabled { opacity: .45; cursor: default; }\n .result-panel .result-feedback-link { display: inline-flex; align-items: center; min-height: 44px; font-size: .8125rem; text-underline-offset: 3px; }\n .result-panel .result-pin > summary, .result-panel .result-notes > summary, .result-panel .result-details > summary { min-height: 44px; }\n .result-panel .diff-comment-form textarea { min-height: 80px; margin: 0; font-size: 1rem; }\n .result-panel .result-attention { background: transparent; border: 0; border-radius: 0; box-shadow: none; padding: 0; margin: .75rem 0; color: var(--foreground); }\n .result-panel .result-attention ul { margin: 0; padding-left: 1.15rem; }\n .result-panel .result-attention ul:has(> li:only-child) { list-style: none; padding-left: 0; }\n .result-panel .result-knowledge { border: 0; border-top: 1px solid var(--border); background: transparent; box-shadow: none; border-radius: 0; padding: .25rem 0; }\n .result-panel .revision-from-comments { border: 0; background: transparent; box-shadow: none; padding: .5rem 0; }\n .result-panel .result-pin summary { font-size: .8125rem; min-height: 44px; display: flex; align-items: center; cursor: pointer; }\n .result-panel .diff-comment-limit { font-size: .75rem; }\n .chat-main .chat-plan { box-shadow: none; }\n .proposal-review-notes { margin: .5rem 0; padding-left: 1.15rem; overflow-wrap: anywhere; }\n .proposal-review-notes li + li { margin-top: .5rem; }\n .proposal-actions .button-link, .proposal-actions button { min-height: 44px; white-space: nowrap; }\n .chat-stop-confirm > summary { min-height: 44px; display: flex; align-items: center; cursor: pointer; }\n .task-chat-title-line h1, .task-main-title, .resume-target, .resume-ceremony, .proposal-task_action, .chat-stop-confirm { overflow-wrap: anywhere; }\n .chat-main .chat-task-back { display: none; }\n .knowledge-library .knowledge-intro { padding: .5rem 0 1.25rem; margin-bottom: 1.5rem; border-bottom: 1px solid var(--border); }\n .knowledge-library .knowledge-instructions { line-height: 1.65; margin-bottom: .5rem; }\n .knowledge-library .knowledge-editor summary { width: fit-content; font-size: .875rem; font-weight: 550; }\n .knowledge-library .knowledge-editor, .knowledge-library > details { border: 0; border-bottom: 1px solid var(--border); border-radius: 0; box-shadow: none; background: transparent; padding: 0; }\n .knowledge-library .knowledge-editor { border-bottom: 0; }\n .knowledge-library .card { border: 0; border-bottom: 1px solid var(--border); border-radius: 0; padding: 0 0 .5rem; margin: 0; box-shadow: none; background: transparent; }\n .knowledge-library .card[open] { padding-bottom: 1rem; }\n .knowledge-library .card > summary { padding: .85rem 0; }\n @media (prefers-reduced-motion: no-preference) {\n .chat-activity-row .activity-dot { animation: quiet-activity 1.8s ease-in-out infinite; }\n .chat-completed .chat-overview-item { transition: background-color 140ms ease; }\n @keyframes quiet-activity { 50% { opacity: .35; } }\n }\n @media (max-width: 600px) {\n .chat-workspace .result-panel { background: transparent; border-radius: 0; }\n .chat-completed .chat-overview-item { align-items: flex-start; flex-wrap: wrap; gap: .35rem; }\n .chat-completed .chat-overview-copy { flex-basis: 65%; }\n .task-live-summary .task-journey { gap: .5rem; }\n }\n";
@@ -1,4 +1,4 @@
1
- const escape = (value) => value.replace(/[&<>"']/g, char => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' }[char]));
1
+ import { html } from './html.js';
2
2
  /** Presentation only. These are recorded completions, not a claim that
3
3
  * work happened since the user last visited, or that every result passed. */
4
4
  export function completedWorkHtml(snapshot, projectLabels, assignments) {
@@ -7,23 +7,22 @@ export function completedWorkHtml(snapshot, projectLabels, assignments) {
7
7
  && !task.historyProblem && !task.otherActive?.length
8
8
  && task.dispatch?.condition !== 'waiting' : assignments[task.id]?.state === 'complete').slice(0, 3);
9
9
  if (!completed.length)
10
- return '';
11
- return `<section class="chat-completed" aria-label="Completed work"><h3>Completed work</h3>` + completed.map(task => {
12
- return `<a class="chat-overview-item" href="/t/${encodeURIComponent(task.rootId ?? task.id)}"><span class="chat-overview-copy"><strong>${escape(task.title)}</strong><span>${escape(projectLabels[task.repoIndex] ?? `Project ${task.repoIndex + 1}`)}</span></span><span class="chat-completed-action">Open task <span aria-hidden="true">→</span></span></a>`;
13
- }).join('') + '</section>';
10
+ return html ``;
11
+ return html `<section class="chat-completed" aria-label="Completed work"><h3>Completed work</h3>${completed.map(task => {
12
+ return html `<a class="chat-overview-item" href="/t/${encodeURIComponent(task.rootId ?? task.id)}"><span class="chat-overview-copy"><strong>${task.title}</strong><span>${projectLabels[task.repoIndex] ?? `Project ${task.repoIndex + 1}`}</span></span><span class="chat-completed-action">Open task <span aria-hidden="true">→</span></span></a>`;
13
+ })}</section>`;
14
14
  }
15
15
  /** Keep activity truthful: the turn's recorded step count is not a
16
16
  * percentage or an invented tool phase. Billing detail stays available. */
17
17
  export function chatWorkingHtml(input) {
18
- return `<div class="chat-thinking chat-activity-row" id="latest" role="status"${input.keyed ? ' data-key="pending"' : ''}>` +
19
- `<span class="activity-dot" aria-hidden="true"></span><details><summary>Working<span class="activity-ellipsis" aria-hidden="true">…</span></summary><p class="meta">${escape(input.details)}</p></details>` +
20
- (input.stopForm ?? '') + '</div>';
18
+ const activity = html `<span class="activity-dot" aria-hidden="true"></span><details><summary>Working<span class="activity-ellipsis" aria-hidden="true">…</span></summary><p class="meta">${input.details}</p></details>`;
19
+ return html `<div class="chat-thinking chat-activity-row" id="latest" role="status"${input.keyed ? html ` data-key="pending"` : ''}>${activity}${input.stopForm}</div>`;
21
20
  }
22
21
  /** Recorded activity is inspectable, not a row of performance badges. */
23
22
  export function chatActivityDetailsHtml(activity) {
24
23
  if (!activity)
25
- return '';
26
- return `<details class="chat-activity-details"><summary>Activity</summary><div class="chat-activity" aria-label="work performed">${activity.split(' · ').map(one => `<span>${escape(one)}</span>`).join('')}</div></details>`;
24
+ return html ``;
25
+ return html `<details class="chat-activity-details"><summary>Activity</summary><div class="chat-activity" aria-label="work performed">${activity.split(' · ').map(one => html `<span>${one}</span>`)}</div></details>`;
27
26
  }
28
27
  // Local primitives adapted from the researched interaction patterns. No
29
28
  // imported kit, animation runtime, simulated progress, or changed authority.
@@ -1,6 +1,6 @@
1
1
  import { TeamLeads } from "./team-leads.js";
2
2
  import { tooLongText } from "./chat-channel.js";
3
- import { MATE_MESSAGE_MAX_CHARS } from "./mate.js";
3
+ import { LEAD_MESSAGE_MAX_CHARS } from "./lead.js";
4
4
  import { phoneText } from "./telegram-status.js";
5
5
  /** `/team` lists, `/team <n>` chooses, `/team off` (or `private`) leaves.
6
6
  * The bare word works too, because Slack and Discord keep unregistered
@@ -130,7 +130,7 @@ export function applyRoomInbound(input) {
130
130
  report.ignored++;
131
131
  return true;
132
132
  }
133
- if (trimmed.length > MATE_MESSAGE_MAX_CHARS) {
133
+ if (trimmed.length > LEAD_MESSAGE_MAX_CHARS) {
134
134
  say(tooLongText(trimmed.length));
135
135
  report.chatRefused = (report.chatRefused ?? 0) + 1;
136
136
  return true;
@@ -192,7 +192,7 @@ export function roomGrantAllowed(store, domain, backend, room) {
192
192
  export function teamDomain(store, projects) { return domainFor(store, projects); }
193
193
  /** The conversation messages a room has not received yet, oldest first. */
194
194
  export function roomMessagesAfter(store, thread, cursor, limit = 20) {
195
- return store.handle.prepare(`SELECT m.id, m.role, m.text, m.turn, q.author, q.request_id, q.status FROM mate_message m
195
+ return store.handle.prepare(`SELECT m.id, m.role, m.text, m.turn, q.author, q.request_id, q.status FROM lead_message m
196
196
  LEFT JOIN team_message q ON q.message = m.id WHERE m.thread = ? AND m.id > ? ORDER BY m.id LIMIT ?`).all(thread, cursor, limit)
197
197
  .map(row => ({ id: Number(row["id"]), role: row["role"] === "assistant" ? "assistant" : "operator", text: String(row["text"]), turn: row["turn"] === null ? null : Number(row["turn"]),
198
198
  author: row["author"] === null || row["author"] === undefined ? null : String(row["author"]), requestId: row["request_id"] === null || row["request_id"] === undefined ? null : String(row["request_id"]), status: row["status"] === null || row["status"] === undefined ? null : String(row["status"]) }));
@@ -1,7 +1,7 @@
1
1
  /** Small warm touches while the lead works on a chat app: once a turn will take more than a few seconds (its first tool
2
2
  * step starts), a 👍 on the owner's message and the app's typing indicator until the turn ends. Best effort only: an
3
3
  * app or permission that does not allow it is skipped silently, and nothing here can fail, delay or change the turn. */
4
- import type { MateProgress } from "./mate-progress.js";
4
+ import type { LeadProgress } from "./lead-progress.js";
5
5
  export declare const WARM_EMOJI = "\uD83D\uDC4D";
6
6
  /** Telegram and Teams show typing for about five seconds, Discord for ten: refreshed inside the shorter window. */
7
7
  export declare const TYPING_REFRESH_MS = 4000;
@@ -12,7 +12,7 @@ export type WarmHooks = {
12
12
  typing?: () => Promise<unknown>;
13
13
  };
14
14
  export type WarmTurn = {
15
- onProgress: (event: MateProgress) => void;
15
+ onProgress: (event: LeadProgress) => void;
16
16
  stop: () => void;
17
17
  };
18
18
  /** The turn's progress listener: on the first tool step, react once and keep typing shown until `stop`. */
@@ -2,6 +2,7 @@
2
2
  * words. One choice (Quick, Full, Off) and the quick command beside the full
3
3
  * one. Every change takes the password and lands in the ledger; approving a
4
4
  * command restates exactly what will run, unattended, before the yes. */
5
+ import { type Html } from "./html.js";
5
6
  import { type CheckLevel } from "./check-levels.js";
6
7
  import type { VerifyCommand } from "./store.js";
7
8
  export type CheckSettingsView = {
@@ -26,5 +27,5 @@ export type CheckSettingsView = {
26
27
  said: string | null;
27
28
  problem: string | null;
28
29
  };
29
- export declare function checkSettingsHtml(view: CheckSettingsView): string;
30
+ export declare function checkSettingsHtml(view: CheckSettingsView): Html;
30
31
  export declare const CHECK_SETTINGS_CSS = "\n .check-settings { display: grid; gap: .6rem; max-width: 40rem; }\n .check-settings p, .check-settings h2 { margin: 0; }\n .check-settings h2 { font-size: 1rem; }\n .check-settings fieldset { border: 0; margin: 0; padding: 0; display: grid; gap: .25rem; }\n .check-settings legend { font-weight: 600; margin-bottom: .25rem; }\n .check-settings label { display: grid; gap: .25rem; }\n .check-settings .check-choice { display: flex; align-items: flex-start; gap: .6rem; min-height: 44px; padding: .35rem 0; cursor: pointer; }\n .check-settings .check-choice > span { display: grid; gap: .1rem; }\n .check-settings .check-choice input { margin-top: .2rem; }\n .check-settings input[type=password], .check-settings input[type=number], .check-settings textarea { min-height: 44px; box-sizing: border-box; width: 100%; }\n .check-settings button { min-height: 44px; justify-self: start; }\n .check-settings .check-command { overflow-wrap: anywhere; }\n .check-terms { margin: 0; padding-left: 1.1rem; display: grid; gap: .25rem; }\n @media (max-width: 480px) { .check-settings button { justify-self: stretch; } }\n .follow-ups .follow-up-list { margin: .4rem 0; padding-left: 1.1rem; display: grid; gap: .25rem; }\n .follow-ups .follow-up-acts, .follow-ups .follow-up-act { display: flex; flex-wrap: wrap; gap: .5rem; margin: .5rem 0 0; }\n .follow-ups .follow-up-act { margin: 0; }\n .follow-ups button { min-height: 44px; }\n @media (max-width: 480px) { .follow-ups .follow-up-acts, .follow-ups .follow-up-act { display: grid; } .follow-ups .follow-up-act button { width: 100%; } }\n";
@@ -2,51 +2,33 @@
2
2
  * words. One choice (Quick, Full, Off) and the quick command beside the full
3
3
  * one. Every change takes the password and lands in the ledger; approving a
4
4
  * command restates exactly what will run, unattended, before the yes. */
5
+ import { html, postForm } from "./html.js";
5
6
  import { CHECK_LEVELS, CHECK_LEVEL_HINTS, CHECK_LEVEL_WORDS } from "./check-levels.js";
6
7
  import { BATCH_HINT } from "./batch-policy.js";
7
- const escape = (text) => text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
8
8
  export function checkSettingsHtml(view) {
9
- const hidden = (act) => `<input type="hidden" name="csrf" value="${escape(view.csrf)}"><input type="hidden" name="repo" value="${escape(view.repo)}"><input type="hidden" name="act" value="${act}">`;
10
- const password = `<label>Password<input type="password" name="password" autocomplete="current-password" required></label>`;
9
+ const post = (act, body, attrs) => postForm("/settings/checks", body, { attrs, hidden: { repo: view.repo, act } });
10
+ const password = html `<label>Password<input type="password" name="password" autocomplete="current-password" required></label>`;
11
11
  const notes = [
12
- view.said === null ? "" : `<p class="notice" role="status">${escape(view.said)}</p>`,
13
- view.problem === null ? "" : `<p class="problem" role="alert">${escape(view.problem)}</p>`,
14
- ].join("");
15
- const head = `<p class="meta">${escape(view.name)} · <span class="mono">${escape(view.repo)}</span></p>${notes}`;
16
- const command = (one) => `<p class="mono check-command">${escape(one.command)}</p><p class="meta">Up to ${Math.round(one.timeoutMs / 1000)}s · approved by ${escape(one.approvedBy)}</p>`;
12
+ view.said === null ? "" : html `<p class="notice" role="status">${view.said}</p>`,
13
+ view.problem === null ? "" : html `<p class="problem" role="alert">${view.problem}</p>`,
14
+ ];
15
+ const head = html `<p class="meta">${view.name} · <span class="mono">${view.repo}</span></p>${notes}`;
16
+ const command = (one) => html `<p class="mono check-command">${one.command}</p><p class="meta">Up to ${Math.round(one.timeoutMs / 1000)}s · approved by ${one.approvedBy}</p>`;
17
17
  // Quick with no quick command runs the full check; say so where the choice is made.
18
18
  const fallsBack = view.level === "quick" && view.quick === null;
19
- const current = `<p><strong>${CHECK_LEVEL_WORDS[view.level]}</strong> · ${escape(CHECK_LEVEL_HINTS[view.level])}</p>` +
20
- (fallsBack ? `<p class="meta">No quick command yet, so builds run the full check.</p>` : "") +
21
- (view.level !== "full" && view.full !== null ? `<p class="meta">The full check still runs when a pull request opens, and Merge waits for it.</p>` : "");
19
+ const current = html `<p><strong>${CHECK_LEVEL_WORDS[view.level]}</strong> · ${CHECK_LEVEL_HINTS[view.level]}</p>${fallsBack ? html `<p class="meta">No quick command yet, so builds run the full check.</p>` : ""}${view.level !== "full" && view.full !== null ? html `<p class="meta">The full check still runs when a pull request opens, and Merge waits for it.</p>` : ""}`;
22
20
  if (!view.canChange) {
23
- return head + `<section class="card check-settings">${current}</section>` + batchHtml(view, "") + reviewHtml(view, "") +
24
- `<section class="card check-settings"><h2>Quick check</h2>${view.quick === null ? `<p class="meta">None yet.</p>` : command(view.quick)}</section>` +
25
- `<section class="card check-settings"><h2>Full check</h2>${view.full === null ? `<p class="meta">None yet.</p>` : command(view.full)}</section>`;
21
+ return html `${head}<section class="card check-settings">${current}</section>${batchHtml(view, null)}${reviewHtml(view, null)}<section class="card check-settings"><h2>Quick check</h2>${view.quick === null ? html `<p class="meta">None yet.</p>` : command(view.quick)}</section><section class="card check-settings"><h2>Full check</h2>${view.full === null ? html `<p class="meta">None yet.</p>` : command(view.full)}</section>`;
26
22
  }
27
- const choices = CHECK_LEVELS.map(level => `<label class="check-choice"><input type="radio" name="level" value="${level}"${level === view.level ? " checked" : ""}>` +
28
- `<span><strong>${CHECK_LEVEL_WORDS[level]}</strong><span class="meta">${escape(CHECK_LEVEL_HINTS[level])}</span></span></label>`).join("");
29
- const levelForm = `<form method="post" action="/settings/checks" class="card check-settings">${hidden("level")}` +
30
- `<fieldset><legend>After each build</legend>${choices}</fieldset>` +
31
- (fallsBack ? `<p class="meta">No quick command yet, so Quick runs the full check.</p>` : "") +
32
- `<p class="meta">A task can choose its own checks when it's filed. With Quick or Off, the full check runs when a pull request opens, and Merge waits for it unless you merge anyway.</p>` +
33
- `${password}<button type="submit">Save</button></form>`;
34
- const quickTerms = `<ul class="check-terms"><li>Runs unattended right after every Quick build, in that build's checkout.</li><li>Plain environment: no credentials.</li><li>A failure shows on the result; nothing is blocked.</li></ul>`;
35
- const quickForm = (value, submit) => `<form method="post" action="/settings/checks" class="check-settings">${hidden("quick")}` +
36
- `<label>Command<textarea name="command" rows="2" maxlength="2000" spellcheck="false" class="mono" required>${escape(value)}</textarea></label>` +
37
- `<label>Time limit (seconds)<input type="number" name="timeout" min="1" max="3600" value="${view.quick === null ? 180 : Math.round(view.quick.timeoutMs / 1000)}" required></label>` +
38
- `${quickTerms}${password}<button type="submit">${submit}</button></form>`;
23
+ const choices = CHECK_LEVELS.map(level => html `<label class="check-choice"><input type="radio" name="level" value="${level}"${level === view.level ? html ` checked` : ""}><span><strong>${CHECK_LEVEL_WORDS[level]}</strong><span class="meta">${CHECK_LEVEL_HINTS[level]}</span></span></label>`);
24
+ const levelForm = post("level", html `<fieldset><legend>After each build</legend>${choices}</fieldset>${fallsBack ? html `<p class="meta">No quick command yet, so Quick runs the full check.</p>` : ""}<p class="meta">A task can choose its own checks when it's filed. With Quick or Off, the full check runs when a pull request opens, and Merge waits for it unless you merge anyway.</p>${password}<button type="submit">Save</button>`, { class: "card check-settings" });
25
+ const quickTerms = html `<ul class="check-terms"><li>Runs unattended right after every Quick build, in that build's checkout.</li><li>Plain environment: no credentials.</li><li>A failure shows on the result; nothing is blocked.</li></ul>`;
26
+ const quickForm = (value, submit) => post("quick", html `<label>Command<textarea name="command" rows="2" maxlength="2000" spellcheck="false" class="mono" required>${value}</textarea></label><label>Time limit (seconds)<input type="number" name="timeout" min="1" max="3600" value="${view.quick === null ? 180 : Math.round(view.quick.timeoutMs / 1000)}" required></label>${quickTerms}${password}<button type="submit">${submit}</button>`, { class: "check-settings" });
39
27
  const quick = view.quick === null
40
- ? `<section class="card check-settings"><h2>Quick check</h2>` +
41
- (view.suggestion === null ? `<p class="meta">Typecheck and the tests near the change, ideally under 2 minutes.</p>` : `<p class="meta">Suggested from the project's scripts. Edit it before approving.</p>`) +
42
- quickForm(view.suggestion ?? "", "Approve quick check") + `</section>`
43
- : `<section class="card check-settings"><h2>Quick check</h2>${command(view.quick)}` +
44
- `<details class="settings-more"><summary>Change quick check</summary>${quickForm(view.quick.command, "Approve change")}</details>` +
45
- `<details class="settings-more"><summary>Remove quick check</summary><form method="post" action="/settings/checks" class="check-settings">${hidden("quick-clear")}` +
46
- `<p class="meta">Quick builds run the full check instead.</p>${password}<button type="submit" class="danger">Remove</button></form></details></section>`;
47
- const full = `<details class="settings-more"><summary>Full check</summary><section class="card check-settings">` +
48
- (view.full === null ? `<p class="meta">None yet. Approve one with <span class="mono">toolroll verify set --repo … --command "…"</span>.</p>` : command(view.full)) + `</section></details>`;
49
- return head + levelForm + batchHtml(view, password) + reviewHtml(view, password) + quick + full;
28
+ ? html `<section class="card check-settings"><h2>Quick check</h2>${view.suggestion === null ? html `<p class="meta">Typecheck and the tests near the change, ideally under 2 minutes.</p>` : html `<p class="meta">Suggested from the project's scripts. Edit it before approving.</p>`}${quickForm(view.suggestion ?? "", "Approve quick check")}</section>`
29
+ : html `<section class="card check-settings"><h2>Quick check</h2>${command(view.quick)}<details class="settings-more"><summary>Change quick check</summary>${quickForm(view.quick.command, "Approve change")}</details><details class="settings-more"><summary>Remove quick check</summary>${post("quick-clear", html `<p class="meta">Quick builds run the full check instead.</p>${password}<button type="submit" class="danger">Remove</button>`, { class: "check-settings" })}</details></section>`;
30
+ const full = html `<details class="settings-more"><summary>Full check</summary><section class="card check-settings">${view.full === null ? html `<p class="meta">None yet. Approve one with <span class="mono">toolroll verify set --repo … --command "…"</span>.</p>` : command(view.full)}</section></details>`;
31
+ return html `${head}${levelForm}${batchHtml(view, password)}${reviewHtml(view, password)}${quick}${full}`;
50
32
  }
51
33
  /** Batch checks: one state and one sentence; turning it on restates exactly what happens, behind a disclosure with the password. */
52
34
  function batchHtml(view, password) {
@@ -54,17 +36,11 @@ function batchHtml(view, password) {
54
36
  if (batch === undefined)
55
37
  return "";
56
38
  const act = batch.on ? "Turn off" : "Turn on";
57
- const terms = batch.on ? `<p class="meta">Each result runs its own full check again. Results already waiting are checked now.</p>`
58
- : `<ul class="check-terms"><li>When results finish within 10 minutes of each other, they're merged in a temporary checkout and the full check runs once.</li>` +
59
- `<li>If it fails, the batch is split until the result that breaks it is found. Results that conflict are checked on their own.</li>` +
60
- `<li>Nothing is merged into a real branch. Each result still lands on its own.</li></ul>`;
61
- const change = password === "" ? ""
62
- : `<details class="settings-more"><summary>${act}</summary><form method="post" action="/settings/checks" class="check-settings">` +
63
- `<input type="hidden" name="csrf" value="${escape(view.csrf)}"><input type="hidden" name="repo" value="${escape(view.repo)}"><input type="hidden" name="act" value="batch">` +
64
- `<input type="hidden" name="on" value="${batch.on ? "0" : "1"}">${terms}${password}<button type="submit">${act}</button></form></details>`;
65
- return `<section class="card check-settings" data-batch-checks="${batch.on ? "on" : "off"}"><h2>Batch checks</h2>` +
66
- `<p><strong>${batch.on ? "On" : "Off"}</strong> · ${escape(BATCH_HINT)}</p>` +
67
- (batch.on && view.level !== "full" ? `<p class="meta">Only Full checks are batched.</p>` : "") + `${change}</section>`;
39
+ const terms = batch.on ? html `<p class="meta">Each result runs its own full check again. Results already waiting are checked now.</p>`
40
+ : html `<ul class="check-terms"><li>When results finish within 10 minutes of each other, they're merged in a temporary checkout and the full check runs once.</li><li>If it fails, the batch is split until the result that breaks it is found. Results that conflict are checked on their own.</li><li>Nothing is merged into a real branch. Each result still lands on its own.</li></ul>`;
41
+ const change = password === null ? ""
42
+ : html `<details class="settings-more"><summary>${act}</summary>${postForm("/settings/checks", html `${terms}${password}<button type="submit">${act}</button>`, { attrs: { class: "check-settings" }, hidden: { repo: view.repo, act: "batch", on: batch.on ? "0" : "1" } })}</details>`;
43
+ return html `<section class="card check-settings" data-batch-checks="${batch.on ? "on" : "off"}"><h2>Batch checks</h2><p><strong>${batch.on ? "On" : "Off"}</strong> · ${BATCH_HINT}</p>${batch.on && view.level !== "full" ? html `<p class="meta">Only Full checks are batched.</p>` : ""}${change}</section>`;
68
44
  }
69
45
  /** Automatic review: one state, one sentence, and its one change behind a disclosure (the password). */
70
46
  function reviewHtml(view, password) {
@@ -73,12 +49,9 @@ function reviewHtml(view, password) {
73
49
  return "";
74
50
  const state = review.on ? (review.source === "hands-off" ? "On while hands-off lasts" : "On") : "Off";
75
51
  const act = review.on ? "Turn off" : "Turn on";
76
- const change = password === "" ? ""
77
- : `<details class="settings-more"><summary>${act}</summary><form method="post" action="/settings/checks" class="check-settings">` +
78
- `<input type="hidden" name="csrf" value="${escape(view.csrf)}"><input type="hidden" name="repo" value="${escape(view.repo)}"><input type="hidden" name="act" value="review">` +
79
- `<input type="hidden" name="on" value="${review.on ? "0" : "1"}">${password}<button type="submit">${act}</button></form></details>`;
80
- return `<section class="card check-settings" data-review-switch="${review.on ? "on" : "off"}"><h2>Automatic review</h2>` +
81
- `<p><strong>${state}</strong> · One read-only review after each build passes its checks. Only high findings send it back, once.</p>${change}</section>`;
52
+ const change = password === null ? ""
53
+ : html `<details class="settings-more"><summary>${act}</summary>${postForm("/settings/checks", html `${password}<button type="submit">${act}</button>`, { attrs: { class: "check-settings" }, hidden: { repo: view.repo, act: "review", on: review.on ? "0" : "1" } })}</details>`;
54
+ return html `<section class="card check-settings" data-review-switch="${review.on ? "on" : "off"}"><h2>Automatic review</h2><p><strong>${state}</strong> · One read-only review after each build passes its checks. Only high findings send it back, once.</p>${change}</section>`;
82
55
  }
83
56
  export const CHECK_SETTINGS_CSS = `
84
57
  .check-settings { display: grid; gap: .6rem; max-width: 40rem; }
package/dist/cli.d.ts CHANGED
@@ -15,6 +15,7 @@ import { type SessionCliOptions } from "./session-cli.js";
15
15
  import { type TeamCliOptions } from "./team-cli.js";
16
16
  import { type RemoteExecOptions } from "./remote-exec.js";
17
17
  import { type UpdateCliDeps } from "./toolroll-update-cli.js";
18
+ import { type ReleaseCliDeps } from "./release-cli.js";
18
19
  import { type OperateOptions } from "./operate.js";
19
20
  export type CliOptions = {
20
21
  roots: string[];
@@ -40,7 +41,7 @@ export type ParseResult = {
40
41
  error: string;
41
42
  };
42
43
  type Write = (line: string) => void;
43
- export declare const HELP = "toolroll \u2014 a control plane for unattended coding agents\n\nUsage\n toolroll [path...] report what is in flight\n toolroll pulls report what is waiting on a person\n toolroll graph report which work graph is already here\n toolroll repos list connected repositories, and how to adjust\n toolroll repos add <path> add a project; it is in the console at once (no path: the repo you are in)\n toolroll repos remove <path>\n take it out of the console; its tasks and results stay\n toolroll repos add-from-github <owner/name> --root <dir>\n preview, then clone and connect (--yes)\n toolroll link put `toolroll` on your PATH\n toolroll unlink take it off again\n toolroll update update to the latest release: verified, drained, undoable\n (preview first; --yes, --now, --at HH:MM, --rollback)\n toolroll contract the machine contract: envelope version + capabilities\n (--commands dumps the declared command guide)\n toolroll skills install --claude-code [--dir <path>]\n install this binary's guides for Claude Code (preview first)\n toolroll skills install teach a repo's agents this queue exists (preview first)\n toolroll skills list the guides this exact binary serves\n toolroll skills get <name> print one guide (version-matched, never stale)\n toolroll demo a seeded throwaway sandbox \u2014 see it working in 90 seconds\n toolroll up app + builder for every saved project \u2014 the normal start\n toolroll onboard make your agent Toolroll's lead: add this repo, install its skill (--yes), offer starter flows (--starter)\n toolroll status running, queued, ready results, release check and plan windows\n toolroll health recent server latency, write waits and live streams\n toolroll integrations which integrations work, and what to do about the ones that don't (--json)\n toolroll session native coding sessions through the running service\n toolroll connect save a private connection to your central service\n toolroll lead named leads on the connected service\n toolroll lead token mint your lead agent's credential: its own work pings nobody\n toolroll lead say \"<text>\" (the lead) one short message in its person's chat\n toolroll conversation shared and private conversations on that service\n toolroll chat --lead <id> --conversation <id> central chat (use --local for local chat)\n\nOperating the queue \u2014 `toolroll task` prints the whole surface,\nand any queue command + --help prints it too\n toolroll approver add <name>\n mint the credential that lets a person say yes\n toolroll ready what could be dispatched right now\n toolroll check-progress <run> current or final approved check progress\n toolroll task wait <id> [--timeout <seconds>]\n wait for this attempt to finish or need a person\n toolroll task add <title> queue work\n toolroll task scope <id> --goal <text>\n state what success is; approval binds to it\n toolroll task approve <id>\n the yes \u2014 nothing builds without one\n toolroll claim <id> --runner <name>\n toolroll heartbeat <lease> / release <lease> / reap\n toolroll tick --runner <name> --token <t> --repo <path>\n one unattended pass over the ready set\n toolroll flows list | show <id> | create | edit | trigger | script | card | archive\n set up and inspect flows (writes preview until --yes)\n toolroll chat --as <you> --token <t>\n talk to the mate: one conversation across every\n project; it proposes, you confirm (--say \"\u2026\" for one turn)\n toolroll serve --repo <path> advanced: run only the console\n (--editor vscode with --runner: file links\n open in VS Code on the device you browse from)\n toolroll watch / daemon install advanced: run only the builder\n toolroll reconcile recover what the last stretch left behind\n\nWith nothing connected it reports everything it can find below the working\ndirectory. Once you connect repositories it reports those instead.\n\nOptions\n --all report everything discoverable, ignoring connected repos\n --depth <n> directory levels to descend (default: 6)\n --hidden include dot-directories, which are skipped by default\n --dirty also read each working tree for uncommitted files\n --local branches only: no network, no pull requests, no issues\n --json emit a machine-readable envelope instead of a report\n -o <file> also write that envelope to a file (requires --json)\n -h, --help show this\n\nNothing is written, installed, or configured. Every repository is read\nthrough git with --no-optional-locks, so a scan never contends for the\nindex lock with an editor you have open.\n\n--dirty is off by default because reading a working tree is unbounded\nwork: on a large or cloud-synced repo `git status` can take minutes,\nwhere listing branches takes milliseconds.\n\nlink shows what it would do and changes nothing; add --yes to apply it,\nor --to <dir> to choose where. It never uses sudo, never leaves your\nhome directory, and never touches a file it did not create.";
44
+ export declare const HELP = "toolroll \u2014 a control plane for unattended coding agents\n\nUsage\n toolroll [path...] report what is in flight\n toolroll pulls report what is waiting on a person\n toolroll graph report which work graph is already here\n toolroll repos list connected repositories, and how to adjust\n toolroll repos add <path> add a project; it is in the console at once (no path: the repo you are in)\n toolroll repos remove <path>\n take it out of the console; its tasks and results stay\n toolroll repos add-from-github <owner/name> --root <dir>\n preview, then clone and connect (--yes)\n toolroll link put `toolroll` on your PATH\n toolroll unlink take it off again\n toolroll update update to the latest release: verified, drained, undoable\n (preview first; --yes, --now, --at HH:MM, --rollback)\n toolroll release <branch> release Toolroll itself: gate, your approval, deploy, merge, tag, publish\n (resumable: rerun to continue; --help)\n toolroll contract the machine contract: envelope version + capabilities\n (--commands dumps the declared command guide)\n toolroll skills install --claude-code [--dir <path>]\n install this binary's guides for Claude Code (preview first)\n toolroll skills install teach a repo's agents this queue exists (preview first)\n toolroll skills list the guides this exact binary serves\n toolroll skills get <name> print one guide (version-matched, never stale)\n toolroll demo a seeded throwaway sandbox \u2014 see it working in 90 seconds\n toolroll up app + builder for every saved project \u2014 the normal start\n toolroll onboard make your agent Toolroll's lead: add this repo, install its skill (--yes), offer starter flows (--starter)\n toolroll status running, queued, ready results, release check and plan windows\n toolroll health recent server latency, write waits and live streams\n toolroll integrations which integrations work, and what to do about the ones that don't (--json)\n toolroll lead token mint your lead agent's credential: its own work pings nobody\n toolroll lead say \"<text>\" (the lead) one short message in its person's chat\n\nOperating the queue \u2014 `toolroll task` prints the whole surface,\nand any queue command + --help prints it too\n toolroll approver add <name>\n mint the credential that lets a person say yes\n toolroll ready what could be dispatched right now\n toolroll check-progress <run> current or final approved check progress\n toolroll task wait <id> [--timeout <seconds>]\n wait for this attempt to finish or need a person\n toolroll task add <title> queue work\n toolroll task scope <id> --goal <text>\n state what success is; approval binds to it\n toolroll task approve <id>\n the yes \u2014 nothing builds without one\n toolroll claim <id> --runner <name>\n toolroll heartbeat <lease> / worker release <lease> / reap\n toolroll tick --runner <name> --token <t> --repo <path>\n one unattended pass over the ready set\n toolroll flows list | show <id> | create | edit | trigger | script | card | archive\n set up and inspect flows (writes preview until --yes)\n toolroll chat --as <you> --token <t>\n talk to your lead: one conversation across every\n project; it proposes, you confirm (--say \"\u2026\" for one turn)\n toolroll serve --repo <path> advanced: run only the console\n (--editor vscode with --runner: file links\n open in VS Code on the device you browse from)\n toolroll watch / daemon install advanced: run only the builder\n toolroll reconcile recover what the last stretch left behind\n\nWith nothing connected it reports everything it can find below the working\ndirectory. Once you connect repositories it reports those instead.\n\nOptions\n --all report everything discoverable, ignoring connected repos\n --depth <n> directory levels to descend (default: 6)\n --hidden include dot-directories, which are skipped by default\n --dirty also read each working tree for uncommitted files\n --local branches only: no network, no pull requests, no issues\n --json emit a machine-readable envelope instead of a report\n -o <file> also write that envelope to a file (requires --json)\n -h, --help show this\n\nNothing is written, installed, or configured. Every repository is read\nthrough git with --no-optional-locks, so a scan never contends for the\nindex lock with an editor you have open.\n\n--dirty is off by default because reading a working tree is unbounded\nwork: on a large or cloud-synced repo `git status` can take minutes,\nwhere listing branches takes milliseconds.\n\nlink shows what it would do and changes nothing; add --yes to apply it,\nor --to <dir> to choose where. It never uses sudo, never leaves your\nhome directory, and never touches a file it did not create.";
44
45
  export declare function parseArgs(argv: readonly string[]): ParseResult;
45
46
  /** The commands that operate the queue rather than report on the world. */
46
47
  /** Every verb the CLI routes OUTSIDE runOperate. Exported (with
@@ -65,6 +66,7 @@ export type MainOptions = {
65
66
  clone?: typeof cloneGithubRepo;
66
67
  };
67
68
  update?: UpdateCliDeps;
69
+ release?: ReleaseCliDeps;
68
70
  };
69
71
  /**
70
72
  * Strip `-o <file>` / `--output <file>` before dispatch. The flag belongs to
package/dist/cli.js CHANGED
@@ -27,6 +27,7 @@ import { run as execRun } from "./exec.js";
27
27
  import { COMMAND_GUIDE, SURFACE_NOTES, SURFACE_SCHEMA_VERSION } from "./surface.js";
28
28
  import { runSessionCommand, SESSION_CLI_ACTIONS } from "./session-cli.js";
29
29
  import { maybeRunTeamCommand, TEAM_CLI_ACTIONS } from "./team-cli.js";
30
+ import { deprecationWarning } from "./deprecations.js";
30
31
  import { maybeRunRemoteCommand } from "./remote-exec.js";
31
32
  import { runHealthCommand } from "./health-cli.js";
32
33
  import { discover, inspectAll } from "./discover.js";
@@ -35,6 +36,7 @@ import { detectGraphs, chooseBackend, setupOptions, LABELS as GRAPH_LABELS, } fr
35
36
  import { openStore, openStoreReadOnly, databasePath } from "./store.js";
36
37
  import { PACKAGE_VERSION } from "./version.js";
37
38
  import { runUpdateCommand } from "./toolroll-update-cli.js";
39
+ import { runReleaseCommand } from "./release-cli.js";
38
40
  import { runOperate, OPERATE_HELP } from "./operate.js";
39
41
  import { renderReport, renderPulls, renderGraph } from "./render.js";
40
42
  import { readRemote } from "./remote.js";
@@ -57,6 +59,8 @@ Usage
57
59
  toolroll unlink take it off again
58
60
  toolroll update update to the latest release: verified, drained, undoable
59
61
  (preview first; --yes, --now, --at HH:MM, --rollback)
62
+ toolroll release <branch> release Toolroll itself: gate, your approval, deploy, merge, tag, publish
63
+ (resumable: rerun to continue; --help)
60
64
  toolroll contract the machine contract: envelope version + capabilities
61
65
  (--commands dumps the declared command guide)
62
66
  toolroll skills install --claude-code [--dir <path>]
@@ -70,13 +74,8 @@ Usage
70
74
  toolroll status running, queued, ready results, release check and plan windows
71
75
  toolroll health recent server latency, write waits and live streams
72
76
  toolroll integrations which integrations work, and what to do about the ones that don't (--json)
73
- toolroll session native coding sessions through the running service
74
- toolroll connect save a private connection to your central service
75
- toolroll lead named leads on the connected service
76
77
  toolroll lead token mint your lead agent's credential: its own work pings nobody
77
78
  toolroll lead say "<text>" (the lead) one short message in its person's chat
78
- toolroll conversation shared and private conversations on that service
79
- toolroll chat --lead <id> --conversation <id> central chat (use --local for local chat)
80
79
 
81
80
  Operating the queue — \`toolroll task\` prints the whole surface,
82
81
  and any queue command + --help prints it too
@@ -92,13 +91,13 @@ and any queue command + --help prints it too
92
91
  toolroll task approve <id>
93
92
  the yes — nothing builds without one
94
93
  toolroll claim <id> --runner <name>
95
- toolroll heartbeat <lease> / release <lease> / reap
94
+ toolroll heartbeat <lease> / worker release <lease> / reap
96
95
  toolroll tick --runner <name> --token <t> --repo <path>
97
96
  one unattended pass over the ready set
98
97
  toolroll flows list | show <id> | create | edit | trigger | script | card | archive
99
98
  set up and inspect flows (writes preview until --yes)
100
99
  toolroll chat --as <you> --token <t>
101
- talk to the mate: one conversation across every
100
+ talk to your lead: one conversation across every
102
101
  project; it proposes, you confirm (--say "…" for one turn)
103
102
  toolroll serve --repo <path> advanced: run only the console
104
103
  (--editor vscode with --runner: file links
@@ -196,7 +195,7 @@ export function parseArgs(argv) {
196
195
  * which answers as `scan`. */
197
196
  export const TOP_LEVEL_COMMANDS = [
198
197
  "", "pulls", "graph", "repos", "repos add", "repos remove", "repos add-from-github",
199
- "link", "unlink", "update", "contract", "skills list", "skills get", "skills install", "demo",
198
+ "link", "unlink", "update", "release", "contract", "skills list", "skills get", "skills install", "demo",
200
199
  ...SESSION_CLI_ACTIONS.map(action => `session ${action}`),
201
200
  ...TEAM_CLI_ACTIONS,
202
201
  ];
@@ -230,7 +229,7 @@ export const OPERATE_COMMANDS = new Set([
230
229
  "models",
231
230
  "claim",
232
231
  "heartbeat",
233
- "release",
232
+ "worker",
234
233
  "reap",
235
234
  "enroll",
236
235
  "grants",
@@ -417,10 +416,16 @@ async function dispatch(argv, write, mainOptions) {
417
416
  }
418
417
  if (first === "update")
419
418
  return runUpdateCommand(rest, write, mainOptions.update);
419
+ if (first === "release") {
420
+ return runReleaseCommand(rest, write, { releaseLease: lease => runOperate("worker", ["release", ...lease], write, mainOptions.operate ?? {}), ...mainOptions.release });
421
+ }
420
422
  if (first === "contract")
421
423
  return runContractCommand(rest, write);
422
- if (first === "session")
424
+ if (first === "session") {
425
+ // D5: native coding sessions are deprecated. They still run this release; each use says so once, on stderr.
426
+ (mainOptions.session?.stderr ?? (line => process.stderr.write(`${line}\n`)))(deprecationWarning(`session ${rest.find(arg => !arg.startsWith("-")) ?? ""}`.trim(), "session"));
423
427
  return runSessionCommand(rest, write, mainOptions.session);
428
+ }
424
429
  if (first === "demo")
425
430
  return runDemoCommand(rest, write);
426
431
  if (first === "skills")
@@ -1,4 +1,5 @@
1
1
  import type { CodingHandoffPreview } from './coding-handoff.js';
2
+ import { type Html } from './html.js';
2
3
  /** A reviewable handoff. The next screen retains the existing approval terms. */
3
- export declare function codingShippingHtml(preview: CodingHandoffPreview, csrf: string, values?: URLSearchParams, error?: string): string;
4
+ export declare function codingShippingHtml(preview: CodingHandoffPreview, values?: URLSearchParams, error?: string): Html;
4
5
  export declare const CODING_SHIPPING_CSS = ".coding-workspace.coding-handoff{display:block;max-width:780px}.coding-handoff h1{margin-bottom:12px}.coding-handoff>p{margin-top:12px}.coding-workspace .coding-check{display:flex;align-items:center;gap:10px;min-height:44px}.coding-workspace .coding-check input{width:20px;height:20px;flex:0 0 20px}";
@@ -1,12 +1,12 @@
1
- const escape = (value) => value.replace(/[&<>"']/g, c => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' }[c]));
1
+ import { html, postForm } from './html.js';
2
2
  /** A reviewable handoff. The next screen retains the existing approval terms. */
3
- export function codingShippingHtml(preview, csrf, values, error) {
4
- const hidden = (name, value) => `<input type="hidden" name="${name}" value="${escape(value)}">`;
3
+ export function codingShippingHtml(preview, values, error) {
5
4
  const goal = values?.get('goal') ?? preview.title;
6
5
  const criteria = values?.get('acceptance') ?? '';
7
6
  const notice = error?.startsWith('Review screenshots are not ready.')
8
- ? `<p class="coding-notice" role="alert">Screenshots needed. Ask Codex to capture and commit them, then try again.</p><details><summary>Technical details</summary><p>${escape(error)}</p></details>`
9
- : error ? `<p class="coding-notice" role="alert">${escape(error)}</p>` : '';
10
- return `<section class="coding-workspace coding-handoff" data-coding-session=""><h1>Review for shipping</h1><p>${escape(preview.title)}</p>${notice}<form method="post" action="/code/${escape(preview.sessionId)}/ship" class="coding-start">${hidden('csrf', csrf)}${hidden('base', preview.base)}${hidden('candidate', preview.candidate)}${hidden('title', preview.title)}<label>Outcome<textarea name="goal" rows="4" required maxlength="16000">${escape(goal)}</textarea></label><label>Checks for review<textarea name="acceptance" rows="4" required maxlength="16000" placeholder="One observable result per line">${escape(criteria)}</textarea></label><label class="coding-check"><input type="checkbox" name="visual" value="yes"${values?.get('visual') === 'yes' ? ' checked' : ''}>Include desktop and phone screenshots</label><details><summary>${preview.changedPaths.length} changed files · commit ${escape(preview.candidate.slice(0, 8))}</summary><ul>${preview.changedPaths.map(path => `<li><code>${escape(path)}</code></li>`).join('')}</ul><p class="coding-meta">Review covers the complete change from ${escape(preview.base)} to ${escape(preview.candidate)}.</p></details><p class="coding-terms">Verification and publication use this project’s existing approval process.</p><button type="submit">Create review task</button></form><p><a href="/code/${escape(preview.sessionId)}">Back to coding</a></p></section>`;
7
+ ? html `<p class="coding-notice" role="alert">Screenshots needed. Ask Codex to capture and commit them, then try again.</p><details><summary>Technical details</summary><p>${error}</p></details>`
8
+ : error ? html `<p class="coding-notice" role="alert">${error}</p>` : '';
9
+ const form = postForm(`/code/${preview.sessionId}/ship`, html `<label>Outcome<textarea name="goal" rows="4" required maxlength="16000">${goal}</textarea></label><label>Checks for review<textarea name="acceptance" rows="4" required maxlength="16000" placeholder="One observable result per line">${criteria}</textarea></label><label class="coding-check"><input type="checkbox" name="visual" value="yes"${values?.get('visual') === 'yes' ? html ` checked` : ''}>Include desktop and phone screenshots</label><details><summary>${preview.changedPaths.length} changed files · commit ${preview.candidate.slice(0, 8)}</summary><ul>${preview.changedPaths.map(path => html `<li><code>${path}</code></li>`)}</ul><p class="coding-meta">Review covers the complete change from ${preview.base} to ${preview.candidate}.</p></details><p class="coding-terms">Verification and publication use this project’s existing approval process.</p><button type="submit">Create review task</button>`, { attrs: { class: 'coding-start' }, hidden: { base: preview.base, candidate: preview.candidate, title: preview.title } });
10
+ return html `<section class="coding-workspace coding-handoff" data-coding-session=""><h1>Review for shipping</h1><p>${preview.title}</p>${notice}${form}<p><a href="/code/${preview.sessionId}">Back to coding</a></p></section>`;
11
11
  }
12
12
  export const CODING_SHIPPING_CSS = `.coding-workspace.coding-handoff{display:block;max-width:780px}.coding-handoff h1{margin-bottom:12px}.coding-handoff>p{margin-top:12px}.coding-workspace .coding-check{display:flex;align-items:center;gap:10px;min-height:44px}.coding-workspace .coding-check input{width:20px;height:20px;flex:0 0 20px}`;
@@ -1,4 +1,5 @@
1
1
  import type { CodingSession, CodingSnapshot } from './coding-types.js';
2
+ import { type Html } from './html.js';
2
3
  export declare function codingWorkspaceHtml(input: {
3
4
  projects: {
4
5
  path: string;
@@ -6,12 +7,13 @@ export declare function codingWorkspaceHtml(input: {
6
7
  }[];
7
8
  sessions: CodingSession[];
8
9
  selected: CodingSnapshot | null;
10
+ /** The page script posts with it (data-coding-csrf); the forms get theirs from postForm. */
9
11
  csrf: string;
10
12
  project: string | null;
11
13
  error?: string;
12
14
  draft?: string;
13
15
  available: boolean;
14
16
  owner?: string;
15
- }): string;
17
+ }): Html;
16
18
  export declare const CODING_CSS = "\n.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}\n.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)}\n.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}\n.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}\n.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}\n.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}\n.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)}\n.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}\n@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}}\n@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}}\n@media(prefers-reduced-motion:reduce){.coding-workspace *{scroll-behavior:auto!important}}\n";
17
19
  export declare function codingWorkspaceScript(): string;