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
package/dist/surface.js CHANGED
@@ -19,13 +19,11 @@ import { FLOWS_DESCRIPTORS } from "./flows-cli.js";
19
19
  * CANNOT prove — that a given flag is read by a given handler — is why
20
20
  * SURFACE_NOTES.flags says "intended".
21
21
  */
22
- import { SESSION_DESCRIPTORS } from './session-contract.js';
23
- import { sessionCliFlags } from './session-cli.js';
24
22
  import { commandEntries } from './contracts/cli.js';
25
23
  /** The guide's limits, stated machine-readably in every dump. */
26
24
  export const SURFACE_NOTES = {
27
- authority: "documentation is not permission; session descriptors also drive parser and request validation; legacy entries remain advisory",
28
- flags: "session flags are exact per operation; legacy flags are intended per command and may use a global parser vocabulary",
25
+ authority: "documentation is not permission; legacy entries remain advisory",
26
+ flags: "flags are intended per command and may use a global parser vocabulary",
29
27
  reasons: "curated, not exhaustive — the runtime `reason` field is the truth; ignore tokens you do not recognize",
30
28
  database: "local database commands wait up to 15 seconds for another writer; an exhausted wait returns database-busy and names the selected file",
31
29
  };
@@ -66,22 +64,9 @@ const GUIDE_ROWS = [
66
64
  { invocation: "graph", synopsis: "report which work graph is already here", audience: "agent", agentMayInvoke: true, mutation: "none", flags: [jsonFlag] },
67
65
  { invocation: "repos", synopsis: "list connected repositories", audience: "agent", agentMayInvoke: true, mutation: "none", flags: [jsonFlag] },
68
66
  operator("proposals", "what coordinators proposed over the MCP gateway — list, confirm <id> [--yes], dismiss <id>; confirming runs the plane's own door under your password"),
69
- operator("chat", "with a saved profile, --lead <id> --conversation <id> opens central chat; --say sends one message, --follow only reads updates, --authorize --terms-digest enables the displayed paid terms; --local uses the existing local coordinator, where --follow enables crew updates and --no-follow pauses them; proposals keep their approval requirements"),
70
- operator('connect', 'save an authenticated central HTTPS profile with --as and --token-stdin or --token-file; --local-login explicitly uses the existing private local sign-in; secrets never belong in arguments or URLs'),
71
- operatorRead('lead list', 'list central leads available to the signed-in account; --profile selects a saved connection'),
72
- operator('lead create', 'create a central lead with --name, --instructions and repeatable --project paths admitted by the server'),
73
- operator('lead update', 'change --name, --instructions or --status for --lead at --revision; projects remain fixed'),
74
- operator('lead member', 'set --account membership on --lead with --role, --active true|false and the current --revision'),
75
- operator('lead transfer', 'move ownership of --task to --lead at --revision; existing execution authority remains unchanged'),
76
- operatorRead('conversation list', 'list admitted conversations, optionally within --lead; attachment never starts model work'),
77
- operator('conversation create', 'create a conversation with --lead, --title, --visibility private|team and repeatable --project paths'),
78
- operatorRead('conversation show', 'read --conversation messages and saved work; no model call or read-marker mutation'),
79
- operator('conversation member', 'set --account membership on --conversation with --role, --active true|false and the current --revision; lead membership must already permit the role'),
80
- operator('conversation edit', 'edit your queued --message in --conversation at --revision using --text; running messages cannot be edited'),
81
- operator('conversation withdraw', 'withdraw your queued --message in --conversation at --revision; an active turn requires stop'),
82
- operator('conversation read', 'explicitly record your read position at --message within --conversation'),
83
- operator('conversation follow', 'enable or pause automatic lead summaries with --conversation and --enabled true|false, within the existing paid chat authorization; passive chat --follow only reads updates'),
84
- operator('conversation stop', 'request stopping the exact --message in --conversation; retain uncertain work until its owner settles it'),
67
+ // D5: the central team service (connect, the central lead verbs, conversation …) and native coding sessions are
68
+ // deprecated: they still run this release, with a warning, but are no longer declared here.
69
+ operator("chat", "talk to your lead: one conversation across every project; --say sends one message; --follow enables crew updates and --no-follow pauses them; proposals keep their approval requirements"),
85
70
  { invocation: "repos add", synopsis: "add a project: a Git repository, in the console at once; the lead adds with its token", audience: "agent", agentMayInvoke: true, mutation: "identity-idempotent",
86
71
  positionals: [{ name: "path", required: false, meaning: "the repository (default: the one you are in)" }],
87
72
  flags: [jsonFlag, { name: "token", takesValue: true, meaning: "the lead token (or TOOLROLL_LEAD_TOKEN)" }],
@@ -114,13 +99,18 @@ const GUIDE_ROWS = [
114
99
  operator("link", "put toolroll on PATH"),
115
100
  operator("unlink", "take it off PATH"),
116
101
  operator("update", "update to the latest release: verified provenance, drained work, backup, rehearsal, health check, automatic restore; --rollback undoes it"),
102
+ { invocation: "release", synopsis: "release Toolroll itself from a branch: gate the exact commit, wait for the owner's approval, check, complete, deploy, merge, tag, publish, Homebrew; rerun to resume",
103
+ audience: "operator", agentMayInvoke: false, mutation: "unkeyed",
104
+ positionals: [{ name: "branch", required: true, meaning: "the pushed branch with an open pull request" }],
105
+ flags: [jsonFlag,
106
+ { name: "repo", takesValue: true, meaning: "the gate checkout the release check runs in (default: this checkout)" },
107
+ { name: "new", takesValue: false, meaning: "release a new commit on the branch, setting the earlier unfinished release aside" },
108
+ { name: "limit", takesValue: true, meaning: "<step>=<minutes>: one waiting step's time limit" },
109
+ { name: "tap", takesValue: true, meaning: "the Homebrew tap, owner/name" },
110
+ { name: "release-gate", takesValue: true, meaning: "<id>: show why an update pause left before a deployment's swap is safe to lift" },
111
+ { name: "yes", takesValue: false, meaning: "with --release-gate: lift that one pause once it is proved" }],
112
+ notableReasons: ["usage", "ambiguous", "drift", "timeout", "check-failed", "ci-failed", "tree-mismatch", "conflict", "mixed-runtime", "gate-held", "after-swap"] },
117
113
  operator("demo", "a seeded throwaway sandbox"),
118
- { invocation: 'session capabilities', synopsis: 'Show executable session schemas and operator authority requirements; no credentials needed', audience: 'agent', agentMayInvoke: true, mutation: 'none', flags: [jsonFlag] },
119
- ...SESSION_DESCRIPTORS.map(spec => ({
120
- invocation: `session ${spec.operation}`, synopsis: spec.synopsis, audience: spec.audience, agentMayInvoke: spec.agentMayInvoke,
121
- mutation: spec.mutation ? 'keyed' : 'none', inputSchema: spec.inputSchema,
122
- flags: Object.entries(sessionCliFlags(spec)).map(([name, arity]) => ({ name, takesValue: arity === 'value', meaning: name === 'key' ? 'keep the same key when reconciling; inspect after unknown delivery, never blind-retry' : `see session ${spec.operation} --help` })),
123
- })),
124
114
  ...KNOWLEDGE_DESCRIPTORS.map(spec => ({ invocation: `knowledge ${spec.action}`, synopsis: spec.synopsis, audience: "audience" in spec ? spec.audience : "agent", agentMayInvoke: !("audience" in spec), mutation: spec.mutation, flags: spec.flags, ...(spec.takesQuery ? { positionals: [{ name: "query", required: true, meaning: "search text or source file for impact" }] } : {}) })),
125
115
  ...MEMORY_DESCRIPTORS.map(spec => ({ invocation: `memory ${spec.action}`, synopsis: spec.synopsis, audience: "agent", agentMayInvoke: true, mutation: spec.mutation, flags: spec.flags, ...(spec.takesQuery ? { positionals: [{ name: "query", required: true, meaning: "search text, a decision id, or the decision sentence" }] } : {}) })),
126
116
  // Flow writes carry an approver's credential: an agent runs them only as the person asked, after showing the preview.
@@ -133,7 +123,7 @@ const GUIDE_ROWS = [
133
123
  { invocation: "integrations", synopsis: "every integration as Connected, Not set up or Broken, with its last success and error, what uses it and what to do; --saved skips new checks; never a secret", audience: "agent", agentMayInvoke: true, mutation: "none",
134
124
  flags: [jsonFlag, dbFlag, { name: "saved", takesValue: false, meaning: "show the last checks without checking again" }] },
135
125
  { invocation: "ready", synopsis: "what could be dispatched right now (rows carry reservedFor)", audience: "agent", agentMayInvoke: true, mutation: "none", flags: [jsonFlag, dbFlag] },
136
- { invocation: "brief", synopsis: "with a saved central profile, --lead <id> --conversation <id> reads messages, proposals and saved work; --request-id inspects your saved receipt without resending; --local uses local DB catch-up, where --history selects the older operational report", audience: "agent", agentMayInvoke: true, mutation: "none", flags: [jsonFlag, dbFlag] },
126
+ { invocation: "brief", synopsis: "the lead's catch-up from the local database; --history selects the older operational report", audience: "agent", agentMayInvoke: true, mutation: "none", flags: [jsonFlag, dbFlag] },
137
127
  { invocation: "gaps", synopsis: "requirement gaps blocking dispatch", audience: "agent", agentMayInvoke: true, mutation: "none", flags: [jsonFlag, dbFlag, repoFlag] },
138
128
  { invocation: "grants", synopsis: "list authority grants", audience: "agent", agentMayInvoke: true, mutation: "none", flags: [jsonFlag, dbFlag] },
139
129
  { invocation: "audit", synopsis: "what people did on this server with their API tokens, newest first: when, who, which token, from the CLI (api) or an agent (mcp), the command or tool, the project and the outcome (ok, refused, error); an approver sees everyone in their projects, anyone else only themselves",
@@ -315,7 +305,7 @@ const GUIDE_ROWS = [
315
305
  { invocation: "heartbeat", synopsis: "still working; extends the lease", audience: "agent", agentMayInvoke: true, mutation: "identity-idempotent",
316
306
  positionals: [{ name: "lease", required: true, meaning: "the lease id" }],
317
307
  flags: [jsonFlag, dbFlag], notableReasons: ["not-leased", "fenced"] },
318
- { invocation: "release", synopsis: "done with it; fenced if superseded", audience: "agent", agentMayInvoke: true, mutation: "identity-idempotent",
308
+ { invocation: "worker release", synopsis: "done with it; fenced if superseded", audience: "agent", agentMayInvoke: true, mutation: "identity-idempotent",
319
309
  positionals: [{ name: "lease", required: true, meaning: "the lease id" }],
320
310
  flags: [jsonFlag, dbFlag], notableReasons: ["not-leased", "fenced"] },
321
311
  { invocation: "reap", synopsis: "release every lease that ran out", audience: "agent", agentMayInvoke: true, mutation: "identity-idempotent", flags: [jsonFlag, dbFlag] },
@@ -428,15 +418,12 @@ const GUIDE_ROWS = [
428
418
  const REMOTE_NO = [
429
419
  // Answered by the local CLI, or a client of a central server itself.
430
420
  "", "pulls", "graph", "repos", "repos add", "repos remove", "repos add-from-github", "contract", "skills list", "skills get", "skills install",
431
- "link", "unlink", "update", "demo", "chat", "connect", "lead list", "lead create", "lead update", "lead member", "lead transfer",
432
- "conversation list", "conversation create", "conversation show", "conversation member", "conversation edit", "conversation withdraw",
433
- "conversation read", "conversation follow", "conversation stop",
434
- "session capabilities", "session list", "session show", "session changes", "session start", "session send", "session stop", "session resume", "session recover",
421
+ "link", "unlink", "update", "release", "demo", "chat",
435
422
  // This machine's checkout, saved profile, credentials and catch-up.
436
423
  "project show", "project use", "brief", "lead token", "lead say", "health",
437
424
  "assignment show", "assignment updates", "assignment claim", "assignment check", "assignment brief", "assignment inbox", "assignment ack",
438
425
  // Workers, the loop and the services that run here.
439
- "claim", "heartbeat", "release", "reap", "tick", "build", "reconcile",
426
+ "claim", "heartbeat", "worker release", "reap", "tick", "build", "reconcile",
440
427
  "runner register", "runner retire", "runner bind", "runner capacity", "coordinator mint", "coordinator revoke", "mcp", "outbox deliver", "peek",
441
428
  "serve", "serve check-public", "watch", "up", "onboard", "daemon", "bridge", "setup show", "setup clear",
442
429
  // Provider keys and model tools on this machine.
@@ -1,5 +1,6 @@
1
1
  /** UI choices translate into the same digest-bound schedules as the CLI. */
2
2
  import { type Schedule } from "./flow-schedule.js";
3
+ import { type Html } from "./html.js";
3
4
  export declare function composerSchedule(fields: URLSearchParams): {
4
5
  ok: true;
5
6
  schedule: string | null;
@@ -9,5 +10,5 @@ export declare function composerSchedule(fields: URLSearchParams): {
9
10
  };
10
11
  export declare function scheduleFields(schedule: Schedule): Record<string, string>;
11
12
  /** A normal form works without JavaScript; enhancement only folds unused fields. */
12
- export declare function scheduleEditorHtml(raw: string | null, submitted?: URLSearchParams): string;
13
+ export declare function scheduleEditorHtml(raw: string | null, submitted?: URLSearchParams): Html;
13
14
  export declare function scheduleEditorScript(): string;
@@ -1,5 +1,6 @@
1
1
  /** UI choices translate into the same digest-bound schedules as the CLI. */
2
2
  import { parseSchedule, scheduleText, validTimezone } from "./flow-schedule.js";
3
+ import { html, joinHtml } from "./html.js";
3
4
  export function composerSchedule(fields) {
4
5
  const repeat = fields.get("repeat") ?? "once";
5
6
  if (repeat === "once")
@@ -29,18 +30,17 @@ export function scheduleFields(schedule) {
29
30
  }
30
31
  return { repeat: schedule.kind, time: schedule.hhmm, timezone: schedule.timezone ?? "UTC", ...(schedule.kind === "weekly" ? { weekday: String(schedule.day) } : {}) };
31
32
  }
32
- const escape = (text) => text.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
33
33
  /** A normal form works without JavaScript; enhancement only folds unused fields. */
34
34
  export function scheduleEditorHtml(raw, submitted) {
35
35
  const parsed = raw === null ? null : parseSchedule(raw);
36
36
  const fields = submitted?.has("repeat") ? Object.fromEntries(submitted) : parsed === null ? {} : scheduleFields(parsed);
37
37
  const repeat = fields.repeat ?? "daily";
38
- const value = (name, fallback) => escape(fields[name] ?? fallback);
39
- return `<fieldset data-schedule-editor${raw === null && submitted === undefined ? ' data-detect-timezone="true"' : ""}><legend>When to run</legend>` +
40
- `<label>Repeat<select name="repeat">${[["daily", "Daily"], ["weekly", "Weekly"], ["custom", "Custom interval"]].map(([id, label]) => `<option value="${id}"${repeat === id ? " selected" : ""}>${label}</option>`).join("")}</select></label>` +
41
- `<div data-schedule-kind="weekly"><label>Day<select name="weekday">${["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"].map((day, index) => `<option value="${index}"${String(index) === (fields.weekday ?? "1") ? " selected" : ""}>${day}</option>`).join("")}</select></label></div>` +
42
- `<div data-schedule-kind="calendar"><label>Time<input type="time" name="time" value="${value("time", "09:00")}"></label><label>Timezone<input type="text" name="timezone" value="${value("timezone", "UTC")}" autocomplete="off" placeholder="America/Los_Angeles"></label><p class="meta">Daily and weekly times follow this timezone. A skipped clock time skips that occurrence; a repeated time runs once.</p></div>` +
43
- `<div data-schedule-kind="custom"><label>Every<input type="number" name="interval" min="1" value="${value("interval", "60")}"></label><label>Unit<select name="interval-unit">${["minutes", "hours", "days"].map(unit => `<option${unit === (fields["interval-unit"] ?? "minutes") ? " selected" : ""}>${unit}</option>`).join("")}</select></label><p class="meta">Intervals range from 5 minutes to 7 days.</p></div></fieldset>`;
38
+ const value = (name, fallback) => fields[name] ?? fallback;
39
+ return joinHtml([html `<fieldset data-schedule-editor${raw === null && submitted === undefined ? html ` data-detect-timezone="true"` : ""}><legend>When to run</legend>`,
40
+ html `<label>Repeat<select name="repeat">${[["daily", "Daily"], ["weekly", "Weekly"], ["custom", "Custom interval"]].map(([id, label]) => html `<option value="${id}"${repeat === id ? html ` selected` : ""}>${label}</option>`)}</select></label>`,
41
+ html `<div data-schedule-kind="weekly"><label>Day<select name="weekday">${["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"].map((day, index) => html `<option value="${index}"${String(index) === (fields.weekday ?? "1") ? html ` selected` : ""}>${day}</option>`)}</select></label></div>`,
42
+ html `<div data-schedule-kind="calendar"><label>Time<input type="time" name="time" value="${value("time", "09:00")}"></label><label>Timezone<input type="text" name="timezone" value="${value("timezone", "UTC")}" autocomplete="off" placeholder="America/Los_Angeles"></label><p class="meta">Daily and weekly times follow this timezone. A skipped clock time skips that occurrence; a repeated time runs once.</p></div>`,
43
+ html `<div data-schedule-kind="custom"><label>Every<input type="number" name="interval" min="1" value="${value("interval", "60")}"></label><label>Unit<select name="interval-unit">${["minutes", "hours", "days"].map(unit => html `<option${unit === (fields["interval-unit"] ?? "minutes") ? html ` selected` : ""}>${unit}</option>`)}</select></label><p class="meta">Intervals range from 5 minutes to 7 days.</p></div></fieldset>`]);
44
44
  }
45
45
  export function scheduleEditorScript() {
46
46
  return `(function(){document.querySelectorAll('[data-schedule-editor]').forEach(function(root){
@@ -1,3 +1,4 @@
1
+ import { type Html } from "./html.js";
1
2
  import type { AssignmentSnapshot } from "./assignment.js";
2
3
  import { type NeedAction, type NeedContext, type NeedKey, type WaitKey } from "./needs-you.js";
3
4
  export type { NeedKey, WaitKey } from "./needs-you.js";
@@ -200,9 +201,9 @@ export declare function workToneOf(headline: Headline): "attention" | "problem"
200
201
  export declare function statusDetailLines(status: TaskStatus): string[];
201
202
  /** The headline's emoji for chat cards (colour is never the only signal: the words follow). */
202
203
  export declare function headlineEmoji(headline: Headline): string;
203
- export declare const statusIconSvg: (mark: DetailMark) => string;
204
+ export declare const statusIconSvg: (mark: DetailMark) => Html;
204
205
  /** The detail rows as server HTML: quiet rows, colour only on the icon. */
205
- export declare function statusDetailsHtml(status: TaskStatus): string;
206
+ export declare function statusDetailsHtml(status: TaskStatus): Html;
206
207
  /** The status card's fold of exact reasons: "More", never a third thing called Details. */
207
208
  export declare const STATUS_MORE = "More";
208
209
  /** The technical reasons, one tap away. */
@@ -210,7 +211,7 @@ export declare function statusWhyHtml(status: TaskStatus, extra?: readonly strin
210
211
  token: string;
211
212
  label: string;
212
213
  detail: string;
213
- }[]): string;
214
+ }[]): Html;
214
215
  /** Shared CSS for the server-rendered status: neutral rows, coloured icons. */
215
216
  export declare const TASK_STATUS_CSS = ".status-headline{display:flex;align-items:center;gap:.6rem;margin:0;font-size:1.125rem;font-weight:600;color:var(--foreground)}.status-headline i{width:.625rem;height:.625rem;border-radius:999px;flex-shrink:0;background:var(--muted-foreground)}[data-headline-tone=live] .status-headline i{background:var(--so-info,#0d74ce)}[data-headline-tone=attention] .status-headline i{background:var(--so-attention,var(--attention))}[data-headline-tone=ready] .status-headline i{background:transparent;box-shadow:inset 0 0 0 2px var(--so-success,#218358)}[data-headline-tone=success] .status-headline i{background:var(--so-success,#218358)}[data-headline-tone=danger] .status-headline i{background:var(--so-danger,#c4320a)}.status-sentence{margin:.35rem 0 0;color:var(--muted-foreground)}.status-details{list-style:none;margin:.75rem 0 0;padding:.25rem 0 0;border-top:1px solid var(--so-line,var(--border))}.status-detail{display:grid;grid-template-columns:14px 7.5rem minmax(0,1fr);align-items:center;gap:.6rem;padding:.4rem 0;font-size:.8125rem;color:var(--foreground)}.status-detail .status-icon{align-self:center;color:var(--muted-foreground)}.status-detail--ok .status-icon{color:var(--so-success,#218358)}.status-detail--running .status-icon{color:var(--so-info,#0d74ce)}.status-detail--note .status-icon{color:var(--so-warning,#ab6400)}.status-detail--failed .status-icon{color:var(--so-danger,#c4320a)}.status-detail-label{color:var(--muted-foreground)}.status-detail-text{display:flex;flex-wrap:wrap;align-items:baseline;gap:0 .6rem;min-width:0;overflow-wrap:anywhere}.status-detail-text a{color:inherit}.status-detail-act{font-weight:600;white-space:nowrap}.status-detail--note .status-detail-act{color:var(--so-warning,#ab6400)}@media(max-width:480px){.status-detail{grid-template-columns:14px 6.5rem minmax(0,1fr);min-height:2.75rem;padding:.25rem 0}.status-detail-text a{display:inline-block;padding:.75rem 0;margin:-.75rem 0}}.status-why{border:0;padding:0;margin:.25rem 0 0;background:transparent;box-shadow:none}.status-why>summary{cursor:pointer;min-height:2.75rem;display:list-item;align-content:center;font-size:.8125rem;color:var(--muted-foreground)}.status-why p{margin:.25rem 0 .5rem}.receipt-note{display:flex;align-items:center;gap:.45rem;font-size:.8125rem;color:var(--foreground)}.receipt-note .status-icon{color:var(--so-warning,#ab6400)}";
216
217
  /** A dispatch diagnosis as its stage, for the phone and CLI reads that hold
@@ -11,6 +11,7 @@
11
11
  * output) is an amber note on its own row with one action; the exact
12
12
  * technical reason stays one tap away in `why`. */
13
13
  import { DEFAULT_LEAD_NAME } from "./lead-identity.js";
14
+ import { html } from "./html.js";
14
15
  import { CODE_NEED, NEEDS, needSentence, waitSentence } from "./needs-you.js";
15
16
  /** A cancelled task with a successor reads "Replaced by <id>" everywhere, never "Cancelled". */
16
17
  export const replacedWords = (successor) => `Replaced by ${successor}`;
@@ -398,24 +399,23 @@ export function statusDetailLines(status) {
398
399
  export function headlineEmoji(headline) {
399
400
  return { Queued: "🕓", Planning: "📝", "Needs you": "👋", Waiting: "⏸", Building: "⏳", "Ready for review": "✅", Complete: "✅", Failed: "❌", Stopped: "⏹" }[headline];
400
401
  }
401
- const escapeHtml = (value) => value.replace(/[&<>"']/g, char => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&#39;" })[char]);
402
402
  const ICON_PATHS = {
403
- ok: `<path d="M20 6 9 17l-5-5"/>`,
404
- running: `<circle cx="12" cy="12" r="4"/>`,
405
- none: `<circle cx="12" cy="12" r="3"/>`,
406
- note: `<path d="M12 8v5"/><path d="M12 16.5h.01"/><path d="M10.3 3.9 2.4 18a2 2 0 0 0 1.7 3h15.8a2 2 0 0 0 1.7-3L13.7 3.9a2 2 0 0 0-3.4 0z"/>`,
407
- failed: `<path d="M18 6 6 18M6 6l12 12"/>`,
403
+ ok: html `<path d="M20 6 9 17l-5-5"/>`,
404
+ running: html `<circle cx="12" cy="12" r="4"/>`,
405
+ none: html `<circle cx="12" cy="12" r="3"/>`,
406
+ note: html `<path d="M12 8v5"/><path d="M12 16.5h.01"/><path d="M10.3 3.9 2.4 18a2 2 0 0 0 1.7 3h15.8a2 2 0 0 0 1.7-3L13.7 3.9a2 2 0 0 0-3.4 0z"/>`,
407
+ failed: html `<path d="M18 6 6 18M6 6l12 12"/>`,
408
408
  };
409
- export const statusIconSvg = (mark) => `<svg class="status-icon status-icon--${mark}" viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2.25" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">${ICON_PATHS[mark]}</svg>`;
409
+ export const statusIconSvg = (mark) => html `<svg class="status-icon status-icon--${mark}" viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2.25" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">${ICON_PATHS[mark]}</svg>`;
410
410
  /** The detail rows as server HTML: quiet rows, colour only on the icon. */
411
411
  export function statusDetailsHtml(status) {
412
412
  if (status.details.length === 0)
413
- return "";
414
- return `<ul class="status-details" aria-label="Status">` + status.details.map(one => {
415
- const text = one.href === null ? escapeHtml(one.text) : `<a href="${escapeHtml(one.href)}">${escapeHtml(one.text)}</a>`;
416
- const action = one.action === null ? "" : one.action.href === null ? `<span class="status-detail-act">${escapeHtml(one.action.label)}</span>` : `<a class="status-detail-act" href="${escapeHtml(one.action.href)}">${escapeHtml(one.action.label)}</a>`;
417
- return `<li class="status-detail status-detail--${one.mark}" data-status-detail="${one.key}" data-mark="${one.mark}">${statusIconSvg(one.mark)}<span class="status-detail-label">${escapeHtml(one.label)}</span><span class="status-detail-text"><span>${text}</span>${action}</span></li>`;
418
- }).join("") + `</ul>`;
413
+ return html ``;
414
+ return html `<ul class="status-details" aria-label="Status">${status.details.map(one => {
415
+ const text = one.href === null ? one.text : html `<a href="${one.href}">${one.text}</a>`;
416
+ const action = one.action === null ? "" : one.action.href === null ? html `<span class="status-detail-act">${one.action.label}</span>` : html `<a class="status-detail-act" href="${one.action.href}">${one.action.label}</a>`;
417
+ return html `<li class="status-detail status-detail--${one.mark}" data-status-detail="${one.key}" data-mark="${one.mark}">${statusIconSvg(one.mark)}<span class="status-detail-label">${one.label}</span><span class="status-detail-text"><span>${text}</span>${action}</span></li>`;
418
+ })}</ul>`;
419
419
  }
420
420
  /** The status card's fold of exact reasons: "More", never a third thing called Details. */
421
421
  export const STATUS_MORE = "More";
@@ -423,9 +423,8 @@ export const STATUS_MORE = "More";
423
423
  export function statusWhyHtml(status, extra = [], diagnostics = []) {
424
424
  const lines = [...new Set([...status.details.flatMap(one => one.why === null ? [] : [`${one.label}: ${one.why}`]), ...status.why, ...extra])];
425
425
  if (lines.length === 0 && diagnostics.length === 0)
426
- return "";
427
- return `<details class="status-why"><summary>${STATUS_MORE}</summary>${lines.map(one => `<p class="meta">${escapeHtml(one)}</p>`).join("")}` +
428
- diagnostics.map(one => `<p class="meta" data-work-diagnostic="${escapeHtml(one.token)}">${escapeHtml(one.label)} · ${escapeHtml(one.detail)}</p>`).join("") + `</details>`;
426
+ return html ``;
427
+ return html `<details class="status-why"><summary>${STATUS_MORE}</summary>${lines.map(one => html `<p class="meta">${one}</p>`)}${diagnostics.map(one => html `<p class="meta" data-work-diagnostic="${one.token}">${one.label} · ${one.detail}</p>`)}</details>`;
429
428
  }
430
429
  /** Shared CSS for the server-rendered status: neutral rows, coloured icons. */
431
430
  export const TASK_STATUS_CSS = `.status-headline{display:flex;align-items:center;gap:.6rem;margin:0;font-size:1.125rem;font-weight:600;color:var(--foreground)}.status-headline i{width:.625rem;height:.625rem;border-radius:999px;flex-shrink:0;background:var(--muted-foreground)}[data-headline-tone=live] .status-headline i{background:var(--so-info,#0d74ce)}[data-headline-tone=attention] .status-headline i{background:var(--so-attention,var(--attention))}[data-headline-tone=ready] .status-headline i{background:transparent;box-shadow:inset 0 0 0 2px var(--so-success,#218358)}[data-headline-tone=success] .status-headline i{background:var(--so-success,#218358)}[data-headline-tone=danger] .status-headline i{background:var(--so-danger,#c4320a)}.status-sentence{margin:.35rem 0 0;color:var(--muted-foreground)}.status-details{list-style:none;margin:.75rem 0 0;padding:.25rem 0 0;border-top:1px solid var(--so-line,var(--border))}.status-detail{display:grid;grid-template-columns:14px 7.5rem minmax(0,1fr);align-items:center;gap:.6rem;padding:.4rem 0;font-size:.8125rem;color:var(--foreground)}.status-detail .status-icon{align-self:center;color:var(--muted-foreground)}.status-detail--ok .status-icon{color:var(--so-success,#218358)}.status-detail--running .status-icon{color:var(--so-info,#0d74ce)}.status-detail--note .status-icon{color:var(--so-warning,#ab6400)}.status-detail--failed .status-icon{color:var(--so-danger,#c4320a)}.status-detail-label{color:var(--muted-foreground)}.status-detail-text{display:flex;flex-wrap:wrap;align-items:baseline;gap:0 .6rem;min-width:0;overflow-wrap:anywhere}.status-detail-text a{color:inherit}.status-detail-act{font-weight:600;white-space:nowrap}.status-detail--note .status-detail-act{color:var(--so-warning,#ab6400)}@media(max-width:480px){.status-detail{grid-template-columns:14px 6.5rem minmax(0,1fr);min-height:2.75rem;padding:.25rem 0}.status-detail-text a{display:inline-block;padding:.75rem 0;margin:-.75rem 0}}.status-why{border:0;padding:0;margin:.25rem 0 0;background:transparent;box-shadow:none}.status-why>summary{cursor:pointer;min-height:2.75rem;display:list-item;align-content:center;font-size:.8125rem;color:var(--muted-foreground)}.status-why p{margin:.25rem 0 .5rem}.receipt-note{display:flex;align-items:center;gap:.45rem;font-size:.8125rem;color:var(--foreground)}.receipt-note .status-icon{color:var(--so-warning,#ab6400)}`;
@@ -10,6 +10,7 @@ export type TeamCliOptions = {
10
10
  signal?: AbortSignal;
11
11
  sleep?: (milliseconds: number, signal: AbortSignal) => Promise<void>;
12
12
  };
13
+ /** D5: deprecated (deprecations.ts). Each still runs this release, hidden from help and the contract, with one warning on stderr. */
13
14
  export declare const TEAM_CLI_ACTIONS: readonly ['connect', 'lead list', 'lead create', 'lead update', 'lead member', 'lead transfer', 'conversation list', 'conversation create', 'conversation show', 'conversation member', 'conversation edit', 'conversation withdraw', 'conversation read', 'conversation follow', 'conversation stop'];
14
15
  export declare class UsageError extends Error {
15
16
  }
package/dist/team-cli.js CHANGED
@@ -9,6 +9,10 @@ import { sessionServiceOrigin } from './session-cli.js';
9
9
  import { databasePath } from './store.js';
10
10
  import { configBase, namedPath } from './names.js';
11
11
  import { parseApiToken } from './api-tokens.js';
12
+ import { deprecationWarning } from './deprecations.js';
13
+ /** The central service's own lead verbs; every other `lead …` (token, say, or none) is the local lead's. */
14
+ const CENTRAL_LEAD_ACTIONS = ['list', 'create', 'update', 'member', 'transfer'];
15
+ /** D5: deprecated (deprecations.ts). Each still runs this release, hidden from help and the contract, with one warning on stderr. */
12
16
  export const TEAM_CLI_ACTIONS = ['connect', 'lead list', 'lead create', 'lead update', 'lead member', 'lead transfer', 'conversation list', 'conversation create', 'conversation show', 'conversation member', 'conversation edit', 'conversation withdraw', 'conversation read', 'conversation follow', 'conversation stop'];
13
17
  const HELP = `toolroll connect <HTTPS-origin> --as <account> --token-stdin
14
18
  Pipe your API token (so_…, from Settings → API tokens) or use --token-file <private-file>;
@@ -335,8 +339,9 @@ function render(reply, listing) {
335
339
  /** null means preserve the existing local command path. Nothing here opens a database. */
336
340
  export async function maybeRunTeamCommand(argv, write, options = {}) {
337
341
  const command = argv[0];
338
- // `lead token` is local: the credential a lead agent acts with on this computer.
339
- if (command === 'lead' && (argv[1] === 'token' || argv[1] === 'say'))
342
+ // `lead token` and `lead say` are local: the credential a lead agent acts with on this computer, and its voice. A bare
343
+ // `lead` is the local lead's too ("lead" means only the lead); only the central service's verbs go on from here.
344
+ if (command === 'lead' && (argv[1] === 'token' || argv[1] === 'say' || !argv.some(arg => arg === '--profile' || CENTRAL_LEAD_ACTIONS.includes(arg))))
340
345
  return null;
341
346
  if (!['connect', 'lead', 'conversation', 'chat', 'brief'].includes(command ?? '') && !argv.includes('--profile'))
342
347
  return null;
@@ -357,6 +362,11 @@ export async function maybeRunTeamCommand(argv, write, options = {}) {
357
362
  return null;
358
363
  }
359
364
  const explicit = ['connect', 'lead', 'conversation'].includes(command) || argv.some(arg => ['--profile', '--lead', '--conversation', '--authorize', '--terms-digest', '--request-id'].includes(arg));
365
+ // D5: the central team service is deprecated. It still runs this release; each use says so once, on stderr.
366
+ const verb = command === 'lead' || command === 'conversation' ? `${command} ${argv.slice(1).find(arg => !arg.startsWith('--')) ?? ''}`.trim() : command === 'connect' ? command : `${command} --conversation`;
367
+ const warn = () => stderr(deprecationWarning(verb, 'team'));
368
+ if (explicit)
369
+ warn();
360
370
  if (explicit && (argv.includes('--help') || ['lead', 'conversation'].includes(command) && argv.length === 1)) {
361
371
  write(HELP);
362
372
  return 0;
@@ -364,6 +374,8 @@ export async function maybeRunTeamCommand(argv, write, options = {}) {
364
374
  const saved = profiles(options);
365
375
  if (!explicit && !saved)
366
376
  return null;
377
+ if (!explicit)
378
+ warn();
367
379
  const parsed = parse(argv), { flags, action } = parsed;
368
380
  if (flags.help || ((command === 'lead' || command === 'conversation') && !action)) {
369
381
  write(HELP);
@@ -2,8 +2,8 @@ import type { IncomingMessage, ServerResponse } from 'node:http';
2
2
  import { type TeamActor, type TeamExecute } from './team-contract.js';
3
3
  import { type Admission } from './request-budget.js';
4
4
  export declare const TEAM_REQUEST_BYTES: number;
5
- /** How long a password bearer's proof stands on a long /api/team/events stream before its password is checked again.
6
- * The account, its revocation and its credential generation are still rechecked on every tick. */
5
+ /** How long a password bearer's proof stands on a long live team stream (GET /live?room=team) before its password is
6
+ * checked again. The account, its revocation and its credential generation are still rechecked on every signal. */
7
7
  export declare const TEAM_PASSWORD_REVERIFY_MS: number;
8
8
  /** Account role belongs to HTTP authentication; API tokens carry their own scope. */
9
9
  export type TeamHttpActor = TeamActor & {
@@ -15,15 +15,11 @@ export type TeamHttpOptions = {
15
15
  revalidate: (request: IncomingMessage, actor: TeamActor) => boolean;
16
16
  authorizeMutation: (request: IncomingMessage, actor: TeamActor) => boolean;
17
17
  execute: TeamExecute;
18
- /** Current, authorized event cursor. null means the audience is no longer available. */
19
- cursor?: (actor: TeamActor, conversationId?: string) => number | null;
20
- /** Source admission before credential verification. Streams are charged once when opened. */
18
+ /** Source admission before credential verification. */
21
19
  admit?: (request: IncomingMessage) => Admission;
22
20
  /** Proved account/token admission, before reading the body or executing any operation. */
23
21
  admitAuthenticated?: (request: IncomingMessage, actor: TeamActor) => Admission;
24
- streamIntervalMs?: number;
25
- streams?: Set<ServerResponse>;
26
22
  };
27
- /** Authenticated browser and remote CLI share one operation boundary. Streams
28
- * are refresh hints only: no model calls, admissions, or completion writes. */
23
+ /** Authenticated browser and remote CLI share one operation boundary. Live
24
+ * updates come from GET /live?room=team (server/live.ts). */
29
25
  export declare function handleTeamHttp(request: IncomingMessage, response: ServerResponse, options: TeamHttpOptions): Promise<boolean>;
package/dist/team-http.js CHANGED
@@ -2,8 +2,8 @@ import { adapterPolicy } from "./server/route-policy.js";
2
2
  import { TEAM_OPERATIONS, TEAM_MUTATIONS, teamScopeAllows } from './team-contract.js';
3
3
  import { limitWords } from './request-budget.js';
4
4
  export const TEAM_REQUEST_BYTES = 64 * 1024;
5
- /** How long a password bearer's proof stands on a long /api/team/events stream before its password is checked again.
6
- * The account, its revocation and its credential generation are still rechecked on every tick. */
5
+ /** How long a password bearer's proof stands on a long live team stream (GET /live?room=team) before its password is
6
+ * checked again. The account, its revocation and its credential generation are still rechecked on every signal. */
7
7
  export const TEAM_PASSWORD_REVERIFY_MS = 5 * 60_000;
8
8
  const failure = (code, message) => ({ version: 1, ok: false, code, message });
9
9
  function send(response, status, value, headers = {}) {
@@ -33,11 +33,11 @@ function valid(value) {
33
33
  !!row.args && typeof row.args === 'object' && !Array.isArray(row.args) &&
34
34
  !['actor', 'generation', 'authenticatedActor'].some(key => key in row.args);
35
35
  }
36
- /** Authenticated browser and remote CLI share one operation boundary. Streams
37
- * are refresh hints only: no model calls, admissions, or completion writes. */
36
+ /** Authenticated browser and remote CLI share one operation boundary. Live
37
+ * updates come from GET /live?room=team (server/live.ts). */
38
38
  export async function handleTeamHttp(request, response, options) {
39
39
  const url = new URL(request.url ?? '/', 'http://standing-orders.local');
40
- if (url.pathname !== '/api/team' && url.pathname !== '/api/team/events')
40
+ if (url.pathname !== '/api/team')
41
41
  return false;
42
42
  const reject = (status, code, message) => { send(response, status, failure(code, message)); request.resume(); return true; };
43
43
  if ([...url.searchParams.keys()].some(key => /token|password|credential|authorization/i.test(key)))
@@ -71,64 +71,6 @@ export async function handleTeamHttp(request, response, options) {
71
71
  if (!adapterPolicy(policyPrincipal()).ok)
72
72
  return reject(403, 'read-only', 'Your token reads only. Use an act token for this.');
73
73
  const conversationId = url.searchParams.get('conversation') ?? undefined;
74
- if (url.pathname === '/api/team/events') {
75
- if (request.method !== 'GET')
76
- return reject(405, 'method-not-allowed', 'Use GET for conversation updates.');
77
- let initial;
78
- try {
79
- initial = await options.execute(actor, { operation: conversationId ? 'show' : 'list', args: conversationId ? { conversationId } : {} });
80
- }
81
- catch {
82
- return reject(503, 'unavailable', 'Conversation updates are unavailable.');
83
- }
84
- if (!initial.ok || !initial.snapshot) {
85
- send(response, 403, initial);
86
- return true;
87
- }
88
- response.writeHead(200, { 'content-type': 'text/event-stream', 'cache-control': 'no-store', 'x-content-type-options': 'nosniff', 'x-accel-buffering': 'no' });
89
- options.streams?.add(response);
90
- let cursor = initial.snapshot.cursor;
91
- response.write(`id: ${cursor}\nevent: change\ndata: {"cursor":${cursor}}\n\n`);
92
- let active = false;
93
- const close = () => { clearInterval(timer); options.streams?.delete(response); response.end(); };
94
- const revoked = () => { response.write('event: revoked\ndata: {}\n\n'); close(); };
95
- const tick = async () => {
96
- if (active || response.destroyed || response.writableEnded)
97
- return;
98
- active = true;
99
- try {
100
- if (!options.revalidate(request, actor))
101
- return revoked();
102
- let next;
103
- if (options.cursor)
104
- next = options.cursor(actor, conversationId);
105
- else {
106
- const reply = await options.execute(actor, { operation: conversationId ? 'show' : 'list', args: conversationId ? { conversationId } : {} });
107
- next = reply.ok ? reply.snapshot?.cursor ?? null : null;
108
- }
109
- if (next === null)
110
- return revoked();
111
- if (next !== cursor) {
112
- cursor = next;
113
- // A slow client receives a fresh scoped snapshot on reconnect, never an unbounded replay buffer.
114
- if (!response.write(`id: ${cursor}\nevent: change\ndata: {"cursor":${cursor}}\n\n`))
115
- close();
116
- }
117
- }
118
- catch {
119
- response.write('event: unavailable\ndata: {}\n\n');
120
- close();
121
- }
122
- finally {
123
- active = false;
124
- }
125
- };
126
- const timer = setInterval(() => { void tick(); }, options.streamIntervalMs ?? 1_000);
127
- timer.unref();
128
- request.once('close', close);
129
- response.once('close', close);
130
- return true;
131
- }
132
74
  let input;
133
75
  if (request.method === 'GET')
134
76
  input = { operation: conversationId ? 'show' : 'list', args: conversationId ? { conversationId } : {} };
@@ -185,7 +185,7 @@ export class TeamLeads {
185
185
  }
186
186
  const selected = conversationId ? this.access(actor, conversationId).conversation : conversations[0] ?? null;
187
187
  const access = selected ? this.access(actor, selected.id) : null;
188
- const messages = selected ? this.db.prepare(`SELECT * FROM (SELECT m.*,q.author,q.request_id,q.status,q.revision AS message_revision,q.error FROM mate_message m LEFT JOIN team_message q ON q.message=m.id WHERE m.thread=? ORDER BY m.id DESC LIMIT 201) ORDER BY id`).all(selected.threadId).map(r => ({ id: Number(r['id']), author: r['role'] === 'assistant' ? access.lead.name : String(r['author'] ?? selected.createdBy), role: String(r['role']), text: String(r['text']), status: (r['status'] ?? 'answered'), revision: Number(r['message_revision'] ?? 1), createdAt: String(r['created_at']), requestId: r['request_id'] == null ? null : String(r['request_id']), turnId: r['turn'] == null ? null : Number(r['turn']), error: r['error'] == null ? null : String(r['error']) })) : [];
188
+ const messages = selected ? this.db.prepare(`SELECT * FROM (SELECT m.*,q.author,q.request_id,q.status,q.revision AS message_revision,q.error FROM lead_message m LEFT JOIN team_message q ON q.message=m.id WHERE m.thread=? ORDER BY m.id DESC LIMIT 201) ORDER BY id`).all(selected.threadId).map(r => ({ id: Number(r['id']), author: r['role'] === 'assistant' ? access.lead.name : String(r['author'] ?? selected.createdBy), role: String(r['role']), text: String(r['text']), status: (r['status'] ?? 'answered'), revision: Number(r['message_revision'] ?? 1), createdAt: String(r['created_at']), requestId: r['request_id'] == null ? null : String(r['request_id']), turnId: r['turn'] == null ? null : Number(r['turn']), error: r['error'] == null ? null : String(r['error']) })) : [];
189
189
  const participants = selected ? this.db.prepare('SELECT account,role,active FROM team_participant WHERE conversation=? ORDER BY account').all(selected.id).map(r => ({ account: String(r['account']), role: String(r['role']), active: Number(r['active']) === 1 })) : [];
190
190
  const canManage = account.role === 'approver' && (access ? access.role === 'manager' : leadId ? this.leadAccess(actor, leadId).role === 'manager' : false);
191
191
  return { leads, conversations, selected, participants, messages: messages.slice(-200), canManage, canCreateLead: account.role === 'approver' && this.enrolled().some(p => projectAccessAllows(account.projects, p)), canSend: account.role === 'approver' && access !== null && rank(access.role) >= 1 && access.lead.status === 'active', cursor: this.cursor(actor, selected?.id) ?? 0, truncated: messages.length > 200, projects: this.enrolled().filter(p => projectAccessAllows(account.projects, p)), accounts: canManage ? this.store.accountFacts().filter(a => a.revokedAt === null && (selected?.projects ?? []).every(p => projectAccessAllows(a.projects, p))).map(a => a.name) : [] };
@@ -345,14 +345,14 @@ export class TeamLeads {
345
345
  return ok('Message already saved.', { messageId: Number(seen['message']), status: String(seen['status']) });
346
346
  }
347
347
  const revisions = this.memberRevisions(conversationId, actor.name);
348
- const messageId = this.store.appendMateMessage({ thread: conversation.threadId, turn: null, role: 'operator', text }, now);
348
+ const messageId = this.store.appendLeadMessage({ thread: conversation.threadId, turn: null, role: 'operator', text }, now);
349
349
  this.db.prepare("INSERT INTO team_message(message,conversation,author,author_generation,lead_member_revision,participant_revision,request_id,payload_hash,status) VALUES(?,?,?,?,?,?,?,?,'queued')").run(messageId, conversationId, actor.name, actor.generation, revisions.lead, revisions.participant, requestId, digest);
350
350
  this.event(lead.id, conversationId, 'message-queued', actor.name, now);
351
351
  return ok('Message saved.', { messageId, status: 'queued' });
352
352
  }
353
353
  if (request.operation === 'read') {
354
354
  const id = integer(a['messageId'], 'Message');
355
- if (id > 0 && !this.db.prepare('SELECT 1 FROM mate_message WHERE id=? AND thread=?').get(id, conversation.threadId))
355
+ if (id > 0 && !this.db.prepare('SELECT 1 FROM lead_message WHERE id=? AND thread=?').get(id, conversation.threadId))
356
356
  refuse('not-found', 'Message not available.');
357
357
  this.db.prepare('INSERT INTO team_read(conversation,account,message) VALUES(?,?,?) ON CONFLICT(conversation,account) DO UPDATE SET message=MAX(team_read.message,excluded.message)').run(conversationId, actor.name, id);
358
358
  return ok('Read position saved.');
@@ -374,9 +374,9 @@ export class TeamLeads {
374
374
  if (message['status'] === 'running') {
375
375
  this.db.prepare('UPDATE team_message SET stop_requested=1 WHERE message=?').run(id);
376
376
  if (message['turn_id'] !== null) {
377
- const turn = this.store.getMateTurn(Number(message['turn_id']));
377
+ const turn = this.store.getLeadTurn(Number(message['turn_id']));
378
378
  if (turn)
379
- this.store.finalizeMateTurn(turn.id, turn.generation, { state: 'failed', settledMicrousd: turn.reservedMicrousd, unknownSpend: true, tokensIn: 0, tokensOut: 0, failureReason: 'stopped' }, now);
379
+ this.store.finalizeLeadTurn(turn.id, turn.generation, { state: 'failed', settledMicrousd: turn.reservedMicrousd, unknownSpend: true, tokensIn: 0, tokensOut: 0, failureReason: 'stopped' }, now);
380
380
  }
381
381
  }
382
382
  else if (message['status'] === 'queued' || message['status'] === 'uncertain')
@@ -393,7 +393,7 @@ export class TeamLeads {
393
393
  refuse('conflict', 'This message changed or has started. Send a follow-up instead.');
394
394
  if (request.operation === 'edit') {
395
395
  const text = messageText(a['text']);
396
- this.db.prepare('UPDATE mate_message SET text=? WHERE id=?').run(text, id);
396
+ this.db.prepare('UPDATE lead_message SET text=? WHERE id=?').run(text, id);
397
397
  this.db.prepare('UPDATE team_message SET revision=revision+1 WHERE message=?').run(id);
398
398
  }
399
399
  else
@@ -404,9 +404,9 @@ export class TeamLeads {
404
404
  refuse('unsupported', 'This action is not available here.');
405
405
  }
406
406
  revokeMemberSessions(leadId, conversationId, account, now) {
407
- this.db.prepare(`UPDATE mate_session SET ended_at=?,ended_by='membership-changed' WHERE approver=? AND ended_at IS NULL
407
+ this.db.prepare(`UPDATE lead_session SET ended_at=?,ended_by='membership-changed' WHERE approver=? AND ended_at IS NULL
408
408
  AND EXISTS(SELECT 1 FROM team_mate_session ms JOIN team_conversation c ON c.thread=ms.thread
409
- WHERE ms.session=mate_session.id AND c.lead=? AND (? IS NULL OR c.id=?))`).run(now.toISOString(), account, leadId, conversationId, conversationId);
409
+ WHERE ms.session=lead_session.id AND c.lead=? AND (? IS NULL OR c.id=?))`).run(now.toISOString(), account, leadId, conversationId, conversationId);
410
410
  }
411
411
  memberRevisions(conversationId, account) {
412
412
  const row = this.db.prepare(`SELECT lm.revision AS lr,p.revision AS pr FROM team_conversation c JOIN team_lead_member lm ON lm.lead=c.lead AND lm.account=? AND lm.active=1 JOIN team_participant p ON p.conversation=c.id AND p.account=lm.account AND p.active=1 WHERE c.id=?`).get(account, conversationId);
@@ -427,7 +427,7 @@ export class TeamLeads {
427
427
  claimNext(runner, now = new Date(), eligible) {
428
428
  str(runner, 'Runner', 180);
429
429
  return this.store.transact(() => {
430
- const candidates = this.db.prepare(`SELECT q.*,m.text,c.lead,c.thread FROM team_message q JOIN mate_message m ON m.id=q.message JOIN team_conversation c ON c.id=q.conversation JOIN team_lead l ON l.id=c.lead AND l.status='active'
430
+ const candidates = this.db.prepare(`SELECT q.*,m.text,c.lead,c.thread FROM team_message q JOIN lead_message m ON m.id=q.message JOIN team_conversation c ON c.id=q.conversation JOIN team_lead l ON l.id=c.lead AND l.status='active'
431
431
  WHERE q.status='queued' AND NOT EXISTS(SELECT 1 FROM team_message live WHERE live.conversation=q.conversation AND live.status IN ('running','uncertain'))
432
432
  AND NOT EXISTS(SELECT 1 FROM team_message earlier WHERE earlier.conversation=q.conversation AND earlier.status='queued' AND earlier.message<q.message)
433
433
  AND (SELECT COUNT(*) FROM team_message running JOIN team_conversation other ON other.id=running.conversation WHERE running.status='running' AND other.lead=c.lead)<2
@@ -453,7 +453,7 @@ export class TeamLeads {
453
453
  current(claim) { const row = this.claimed(claim); return row !== undefined && Number(row['stop_requested']) === 0 && this.authorStillAllowed(row); }
454
454
  bindTurn(claim, turnId) {
455
455
  return this.store.transact(() => { if (!this.current(claim))
456
- return false; const turn = this.store.getMateTurn(turnId); if (!turn || turn.thread !== claim.threadId || turn.approver !== claim.actor.name)
456
+ return false; const turn = this.store.getLeadTurn(turnId); if (!turn || turn.thread !== claim.threadId || turn.approver !== claim.actor.name)
457
457
  return false; const changed = this.db.prepare('UPDATE team_message SET turn_id=? WHERE message=? AND turn_id IS NULL').run(turnId, claim.messageId); return Number(changed.changes) === 1; });
458
458
  }
459
459
  finish(claim, result, now = new Date()) {
@@ -467,7 +467,7 @@ export class TeamLeads {
467
467
  return false;
468
468
  this.db.prepare('UPDATE team_message SET status=?,error=?,turn_id=COALESCE(turn_id,?),revision=revision+1 WHERE message=?').run(status, result.error ?? (!allowed ? 'Access changed while this message was running.' : stopped ? 'Stopped.' : null), result.turnId ?? null, claim.messageId);
469
469
  if (allowed && !stopped && result.text)
470
- this.store.appendMateMessage({ thread: claim.threadId, turn: result.turnId ?? (row['turn_id'] === null ? null : Number(row['turn_id'])), role: 'assistant', text: result.text }, now);
470
+ this.store.appendLeadMessage({ thread: claim.threadId, turn: result.turnId ?? (row['turn_id'] === null ? null : Number(row['turn_id'])), role: 'assistant', text: result.text }, now);
471
471
  this.event(claim.leadId, claim.conversationId, 'message-finished', claim.actor.name, now);
472
472
  return true;
473
473
  });
@@ -478,7 +478,7 @@ export class TeamLeads {
478
478
  const row = this.claimed(claim);
479
479
  if (!row || row['turn_id'] !== null || !this.current(claim))
480
480
  return false;
481
- if (this.db.prepare('SELECT turn FROM mate_message WHERE id=?').get(claim.messageId)?.['turn'] !== null)
481
+ if (this.db.prepare('SELECT turn FROM lead_message WHERE id=?').get(claim.messageId)?.['turn'] !== null)
482
482
  return false;
483
483
  const changed = this.db.prepare("UPDATE team_message SET status='queued',generation=generation+1,revision=revision+1,runner=NULL,claimed_at=NULL,error=? WHERE message=? AND status='running' AND turn_id IS NULL AND generation=?").run(error, claim.messageId, claim.generation);
484
484
  if (Number(changed.changes) !== 1)
@@ -520,7 +520,7 @@ export class TeamLeads {
520
520
  return this.store.transact(() => {
521
521
  const rows = this.db.prepare("SELECT q.*,c.lead FROM team_message q JOIN team_conversation c ON c.id=q.conversation WHERE runner=? AND status='running'").all(runner);
522
522
  for (const row of rows) {
523
- const turn = row['turn_id'] === null ? null : this.store.getMateTurn(Number(row['turn_id']));
523
+ const turn = row['turn_id'] === null ? null : this.store.getLeadTurn(Number(row['turn_id']));
524
524
  const status = turn?.state === 'answered' ? 'answered' : turn?.state === 'failed' && !this.deliveryUncertain(turn.id) ? 'failed' : 'uncertain';
525
525
  const error = status === 'uncertain' ? 'Delivery unconfirmed after the previous worker stopped. Inspect activity before continuing.' : status === 'failed' ? savedFailure(turn?.failureReason) : null;
526
526
  this.db.prepare('UPDATE team_message SET status=?,generation=generation+1,revision=revision+1,error=? WHERE message=?').run(status, error, Number(row['message']));
@@ -2,14 +2,14 @@ import type { Store } from './store.js';
2
2
  import type { WorkspaceRevision } from './workspace-revision.js';
3
3
  import { TeamLeads } from './team-leads.js';
4
4
  import { type TeamActor, type TeamExecute } from './team-contract.js';
5
- import { type MateTurnInput } from './mate.js';
5
+ import { type LeadTurnInput } from './lead.js';
6
6
  import { type TeamChatProviderResolver } from './team-chat-authorization.js';
7
7
  export type TeamRuntimeOptions = {
8
8
  store: Store;
9
9
  repos: () => readonly string[];
10
10
  evidenceRoot: string;
11
11
  provider: TeamChatProviderResolver;
12
- subscriptionRunner?: MateTurnInput['subscriptionRunner'];
12
+ subscriptionRunner?: LeadTurnInput['subscriptionRunner'];
13
13
  fetcher?: typeof fetch;
14
14
  clock?: () => Date;
15
15
  capacity?: number;