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/serve.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { html, htmlString, jsonScript, textHtml } from "./html.js";
1
2
  import { browserAssetsAvailable, browserWorkspaceDocument } from './browser-shell.js';
2
3
  import { browserCrewFromIndex, browserCrewOf, browserNavigationOf, browserProjectsOf, needsYouLabelOf } from './browser-workspace.js';
3
4
  import { sharedActionPayload } from './chat-actions.js';
@@ -86,7 +87,7 @@ import { firstRunSteps, signInCommandFor } from "./first-run.js";
86
87
  import { installMethod } from "./install-method.js";
87
88
  import { createIntegrationMonitor } from "./integrations.js";
88
89
  import { logEvent } from "./log.js";
89
- import { MATE_MESSAGE_MAX_CHARS } from "./mate.js";
90
+ import { LEAD_MESSAGE_MAX_CHARS } from "./lead.js";
90
91
  import {} from "./model-catalog.js";
91
92
  import { modeTermsFromJson } from "./modes.js";
92
93
  import { envValue } from "./names.js";
@@ -111,11 +112,18 @@ import { isAlive as runnerAlive } from "./runner.js";
111
112
  import { freshIdentitySignIn } from "./scope.js";
112
113
  import { instrumentRequest } from "./server-telemetry.js";
113
114
  import { createChatHandlers } from './server/chat.js';
115
+ import { createLiveHandlers } from './server/live.js';
114
116
  import { createFlowsHandlers } from './server/flows.js';
115
117
  import { createPagesHandlers } from './server/pages.js';
116
118
  import { createPeopleHandlers } from './server/people-tokens.js';
117
119
  import { createSettingsHandlers } from './server/settings.js';
118
- import { BODY_CAP, chromeScript, decisionsFor, DEMO_BANNER, DEMO_BANNER_SHORT, escape, focusDocument, form, KBD_HELP, LEAD_BY_DEFAULT_FACT, loginHref, matchTaskPath, mateBrowserMessages, mateChatVersion, NO_PROJECT, NO_TOUCH_FRAGMENTS, NONCE_CAP, NONCE_TTL_MS, page, PersistentSessions, pinnedTheme, projectChatHref, QUEUE_VIEW, redactedPath, redirect, refuse, requestContext, respond, safeReturn, screen, SENSITIVE_INPUT, SESSION_ABSOLUTE_MS, SESSION_IDLE_MS, shell, SHUTDOWN_WAIT_MS, sidebarScript, SIGN_IN_LINK_MS, SIGN_IN_LINK_PATH, ssoStepUps, TASK_FORM_BODY_CAP, taskChatHref, teamProposalCardParts, wrongHostPage } from "./server/shared.js";
120
+ import { requestContext } from "./server/request-context.js";
121
+ import { chromeScript, DEMO_BANNER, DEMO_BANNER_SHORT, focusDocument, KBD_HELP, pinnedTheme, QUEUE_VIEW, refuse, screen, SENSITIVE_INPUT, shell, sidebarScript } from "./server/chrome.js";
122
+ import { BODY_CAP, form, loginHref, matchTaskPath, page, projectChatHref, redactedPath, redirect, respond, safeReturn, SHUTDOWN_WAIT_MS, taskChatHref } from "./server/http.js";
123
+ import { decisionsFor, LEAD_BY_DEFAULT_FACT, leadBrowserMessages, leadChatVersion, teamProposalCardParts } from "./server/render-chat.js";
124
+ import {} from "./server/render-pages.js";
125
+ import { ssoStepUps, wrongHostPage } from "./server/render-people.js";
126
+ import { NO_PROJECT, NO_TOUCH_FRAGMENTS, NONCE_CAP, NONCE_TTL_MS, PersistentSessions, SESSION_ABSOLUTE_MS, SESSION_IDLE_MS, SIGN_IN_LINK_MS, SIGN_IN_LINK_PATH } from "./server/session.js";
119
127
  import { createTasksHandlers } from './server/tasks.js';
120
128
  import { DEFAULT_GUARD_POLICY, passwordGuardOf, provenPasswordAccount, SourceBudget, withPasswordSource } from "./sign-in-guard.js";
121
129
  import { sourceKey } from "./source-key.js";
@@ -421,9 +429,8 @@ export function createDecisionServer(options) {
421
429
  };
422
430
  };
423
431
  const codingProjects = () => [...new Set([...managedRepos(), ...store.listProjects().map(project => project.path)])].filter(repo => rowVisible(liveCeiling(), repo));
424
- const teamStreams = new Set();
425
432
  /** Open chat streams (live replies), ended when the server closes. */
426
- const chatStreams = new Set();
433
+ let liveTurnStarted = () => { };
427
434
  /** Google sign-ins in progress (v89): each consent visit's state, for 10 minutes. */
428
435
  const googleVisits = new Map();
429
436
  /** One-click connections on their way: the service's sign-in page and back, 15 minutes at most. */
@@ -446,7 +453,7 @@ export function createDecisionServer(options) {
446
453
  if (!snapshot?.selected || !snapshot.proposals)
447
454
  return reply;
448
455
  return { ...reply, snapshot: { ...snapshot, proposals: snapshot.proposals.map(summary => {
449
- const proposal = store.getMateProposal(summary.id);
456
+ const proposal = store.getLeadProposal(summary.id);
450
457
  if (!proposal || proposal.thread !== snapshot.selected.threadId)
451
458
  return summary;
452
459
  const decision = proposal.kind === 'answer' && typeof proposal.payload['decision'] === 'number' ? store.getDecision(proposal.payload['decision']) : null;
@@ -625,12 +632,13 @@ export function createDecisionServer(options) {
625
632
  }
626
633
  if (edgeRoute === null && isEdgeAddress(url.pathname))
627
634
  return refuseEdge(request, response, url.pathname, wrongMethodEdge);
628
- if (edgeRoute !== null && edgeRoute.domain !== 'people')
635
+ // Live rooms share the browser identity/context below; their adapter carries token limits into each room.
636
+ if (edgeRoute !== null && edgeRoute.domain !== 'people' && edgeRoute.domain !== 'live')
629
637
  return dispatchEdge(edgeRoute, { url, who: null, request, response, method });
630
638
  // Passive polls authenticate without extending the browser session.
631
639
  const fragmentName = url.searchParams.get("fragment");
632
640
  const workspaceRead = method === "GET" && url.searchParams.get('format') === 'workspace';
633
- const touch = !(method === "GET" && (workspaceRead || (fragmentName !== null && NO_TOUCH_FRAGMENTS.has(fragmentName)) || /^\/code\/[a-f0-9]{32}\/state$/.test(url.pathname)));
641
+ const touch = !(method === "GET" && (edgeRoute?.domain === 'live' || workspaceRead || (fragmentName !== null && NO_TOUCH_FRAGMENTS.has(fragmentName)) || /^\/code\/[a-f0-9]{32}\/state$/.test(url.pathname)));
634
642
  const refuseBudget = (admitted) => {
635
643
  if (admitted.ok)
636
644
  return false;
@@ -644,7 +652,7 @@ export function createDecisionServer(options) {
644
652
  if (refuseBudget(admitPasswordSource(request)))
645
653
  return;
646
654
  const refusedToken = {};
647
- const who = identify(request, touch, false, refusedToken);
655
+ const who = identify(request, touch, edgeRoute?.domain === 'live', refusedToken);
648
656
  if (refusedToken.principal !== undefined) {
649
657
  if (refuseBudget(requestBudget.admit(refusedToken.principal.tokenId, "api")))
650
658
  return;
@@ -685,8 +693,9 @@ export function createDecisionServer(options) {
685
693
  // An empty password stands for a fresh identity-provider sign-in, for this person only (v100).
686
694
  if (method === "GET" || method === "POST")
687
695
  return freshIdentitySignIn.run({ actor: requestFacts.sso?.fresh === true ? who.name : null }, () => provenPasswordAccount.run(passwordProof, () => requestContext.run(requestFacts, async () => {
688
- const taskTextForm = url.pathname === "/tasks/add" || /^\/t\/[^/]+\/scope$/.test(url.pathname);
689
- const body = method === "POST" ? await form(request, url.pathname === "/settings/skills/import" ? 2 * 1024 * 1024 : url.pathname === "/flows/import" ? 1024 * 1024 : taskTextForm ? TASK_FORM_BODY_CAP : BODY_CAP) : null;
696
+ if (edgeRoute?.domain === 'live')
697
+ return dispatchEdge(edgeRoute, { url, who, request, response, method });
698
+ const body = method === "POST" ? await form(request, (matchedRoute?.stage === "console" ? matchedRoute.bodyCap : undefined) ?? BODY_CAP) : null;
690
699
  const target = actionTarget(url, who, request, body === null ? null : readForm(body, CONSOLE_FORMS.ledgerTarget));
691
700
  const execute = async () => {
692
701
  // THE ROUTE TABLE (server/route-table.ts): nothing reaches a handler unless a row declares this method and
@@ -717,7 +726,7 @@ export function createDecisionServer(options) {
717
726
  // Authorize every read before considering a conditional response.
718
727
  // Task/result opening and receipt reconciliation always re-read work.
719
728
  const facts = requestContext.getStore();
720
- if (facts?.workspaceRead && who.via === 'cookie' && url.pathname === '/chat' &&
729
+ if (facts?.workspaceRead && who.via === 'cookie' && route.id === 'chat.page' &&
721
730
  !url.searchParams.has('task') && !url.searchParams.has('result') && !url.searchParams.has('request')) {
722
731
  const key = createHash('sha256').update(JSON.stringify([workspaceIncarnation, who.name,
723
732
  who.session.generation, who.session.csrf, who.session.project, who.session.projectRevision,
@@ -771,10 +780,10 @@ export function createDecisionServer(options) {
771
780
  }
772
781
  const shared = /^\/chat\/(?:action\/([0-9]{1,15})|proposal\/([0-9]{1,15})\/(?:confirm|dismiss))$/.exec(url.pathname);
773
782
  if (shared) {
774
- const proposal = store.getMateProposal(Number(shared[1] ?? shared[2]));
783
+ const proposal = store.getLeadProposal(Number(shared[1] ?? shared[2]));
775
784
  const action = proposal?.kind === 'action' ? sharedActionPayload(proposal.payload) : null;
776
785
  const room = proposal ? store.handle.prepare('SELECT id FROM team_conversation WHERE thread=?').get(proposal.thread) : null;
777
- let admitted = proposal && store.getMateThread(proposal.thread)?.approver === who.name;
786
+ let admitted = proposal && store.getLeadThread(proposal.thread)?.approver === who.name;
778
787
  if (room && who.via === 'cookie') {
779
788
  try {
780
789
  team.domain.access({ name: who.name, generation: who.session.generation }, String(room['id']), 'contributor');
@@ -819,7 +828,7 @@ export function createDecisionServer(options) {
819
828
  // A project link is a read context, not a session-changing operation.
820
829
  // Prove it against both admission and the known project catalog before
821
830
  // collection queries; unknown/foreign paths reveal no work.
822
- if ((url.pathname === '/work' || url.pathname === '/system') && url.searchParams.has('project')) {
831
+ if ((route.id === 'work' || route.id === 'system') && url.searchParams.has('project')) {
823
832
  const wanted = url.searchParams.get('project') ?? '';
824
833
  if (url.searchParams.getAll('project').length !== 1 || !visible(wanted) ||
825
834
  ![...managedRepos(), ...store.listProjects().map(one => one.path)].includes(wanted)) {
@@ -836,7 +845,7 @@ export function createDecisionServer(options) {
836
845
  // viewing a result never changes the session's selected project, and
837
846
  // the page's project switch keeps showing the person's own choice.
838
847
  const chosenProject = project;
839
- if (url.pathname === "/review" && url.searchParams.has("result")) {
848
+ if (route.id === "review" && url.searchParams.has("result")) {
840
849
  const wanted = url.searchParams.get("result") ?? "";
841
850
  const ref = wanted.length > 0 && wanted.length <= 64 && !hasForbiddenControls(wanted) ? store.lookupRef(wanted) : null;
842
851
  const namedProject = url.searchParams.get("project");
@@ -867,8 +876,8 @@ export function createDecisionServer(options) {
867
876
  // and the all-projects board carry their own read context.
868
877
  const needsProject = who.via === "cookie" && project === null && !unscopedMode &&
869
878
  route.needsProject &&
870
- !(url.pathname === "/review" && url.searchParams.has("result")) &&
871
- !(url.pathname === "/board" && url.searchParams.get("scope") === "all");
879
+ !(route.id === "review" && url.searchParams.has("result")) &&
880
+ !(route.id === "board" && url.searchParams.get("scope") === "all");
872
881
  if (needsProject)
873
882
  return redirect(response, `/projects?return=${encodeURIComponent(safeReturn(url.pathname + url.search))}`);
874
883
  return dispatchConsole(route, { url, who, request, response, route, now, project, chosenProject, posted: new URLSearchParams() });
@@ -972,36 +981,36 @@ export function createDecisionServer(options) {
972
981
  // ---- fleet chat (v13) ----------------------------------------------------
973
982
  const chatFetcher = options.chatFetcher ?? fetch;
974
983
  const chatEnv = options.chatEnv ?? process.env;
975
- /** The mate's last non-answer per BROWSER session (keyed by its csrf —
984
+ /** The lead's last non-answer per BROWSER session (keyed by its csrf —
976
985
  * never by name, so two browsers on one account do not read each other's
977
986
  * notes), bound to the mate turn it came from so a note from a session
978
987
  * that has since ended never shows; bounded; read once. */
979
- const mateSaid = new Map();
988
+ const leadSaid = new Map();
980
989
  /** The unified conversation's rows for one reader (package 2): the SAME
981
990
  * rows the page renders and the status poll's fragments re-render, so a
982
991
  * live update can never show a card the page would not. A task lens
983
- * keeps only that task's coordinator cards; the mate's own thread is one
992
+ * keeps only that task's coordinator cards; the lead's own thread is one
984
993
  * thread regardless of lens. */
985
- function mateConversationRows(who, principal, focusTask, now, chatProject = null) {
994
+ function leadConversationRows(who, principal, focusTask, now, chatProject = null) {
986
995
  const allCoordinatorRows = store.listCoordinatorProposals({ repos: managedRepos(), states: ["pending", "confirmed", "refused"], limit: 30 });
987
996
  const coordinatorProposals = focusTask !== null ? allCoordinatorRows.filter(one => focusTask.family.versions.some(version => version.id === one.payload["task"]))
988
997
  : chatProject !== null ? allCoordinatorRows.filter(one => one.repo === chatProject) : allCoordinatorRows;
989
- const opened = store.openMateThread(who.name, principal.ceilingDigest, now, chatScopeOf(focusTask, chatProject));
990
- const proposals = store.listMateProposals(opened.thread.id);
998
+ const opened = store.openLeadThread(who.name, principal.ceilingDigest, now, chatScopeOf(focusTask, chatProject));
999
+ const proposals = store.listLeadProposals(opened.thread.id);
991
1000
  // One reply runs at a time per person; this thread shows it only when
992
1001
  // the reply is its own.
993
- const live = store.liveMateTurnFor(who.name);
994
- const messages = store.listMateMessages(opened.thread.id, 40);
1002
+ const live = store.liveLeadTurnFor(who.name);
1003
+ const messages = store.listLeadMessages(opened.thread.id, 40);
995
1004
  // The lead's question with buttons, while its reply is the last word in the thread.
996
1005
  const last = messages.at(-1);
997
- const ask = last?.role === "assistant" && last.turn !== null && store.getMateTurn(last.turn)?.approver === who.name ? store.mateAskOpen(last.turn, now) : null;
1006
+ const ask = last?.role === "assistant" && last.turn !== null && store.getLeadTurn(last.turn)?.approver === who.name ? store.leadAskOpen(last.turn, now) : null;
998
1007
  // Answered questions stay readable above the answer; only their buttons go.
999
- const asks = new Map(messages.flatMap(one => { const asked = one.role === "assistant" && one.turn !== null ? store.mateAsk(one.turn) : null; return asked === null ? [] : [[asked.turn, asked]]; }));
1008
+ const asks = new Map(messages.flatMap(one => { const asked = one.role === "assistant" && one.turn !== null ? store.leadAsk(one.turn) : null; return asked === null ? [] : [[asked.turn, asked]]; }));
1000
1009
  // The thread a ceiling change replaced (ruling 9): read after the open, display only.
1001
1010
  const replaced = store.replacedLeadThread(opened.thread);
1002
- const previousProposals = replaced === null ? [] : store.listMateProposals(replaced.id);
1011
+ const previousProposals = replaced === null ? [] : store.listLeadProposals(replaced.id);
1003
1012
  return {
1004
- previous: replaced === null ? null : { messages: store.listMateMessages(replaced.id, 40), proposals: previousProposals, decisions: decisionsFor(store, previousProposals) },
1013
+ previous: replaced === null ? null : { messages: store.listLeadMessages(replaced.id, 40), proposals: previousProposals, decisions: decisionsFor(store, previousProposals) },
1005
1014
  messages,
1006
1015
  ask,
1007
1016
  asks,
@@ -1009,7 +1018,7 @@ export function createDecisionServer(options) {
1009
1018
  decisions: decisionsFor(store, [...proposals, ...coordinatorProposals]),
1010
1019
  coordinatorProposals,
1011
1020
  pending: live !== null && live.thread === opened.thread.id ? live : null,
1012
- recent: store.recentMateTurns(who.name, 5),
1021
+ recent: store.recentLeadTurns(who.name, 5),
1013
1022
  };
1014
1023
  }
1015
1024
  /** The thread a chat surface speaks in (v77): the task's own thread, the
@@ -1026,21 +1035,21 @@ export function createDecisionServer(options) {
1026
1035
  if (who.via !== "cookie" || who.role !== "approver" || (focusTask === null && chatProject === null))
1027
1036
  return null;
1028
1037
  const enabled = chatEnablement();
1029
- const principal = enabled.ok ? matePrincipal(who) : null;
1038
+ const principal = enabled.ok ? leadPrincipal(who) : null;
1030
1039
  if (!enabled.ok || principal === null)
1031
1040
  return null;
1032
- let session = store.activeMateSession(who.name);
1041
+ let session = store.activeLeadSession(who.name);
1033
1042
  if ((session === null || session.ceilingDigest !== principal.ceilingDigest) && enabled.billing === "subscription" && !requestContext.getStore()?.workspaceRead) {
1034
- startMateConversation(who, principal, enabled, 0, false, now);
1035
- session = store.activeMateSession(who.name);
1043
+ startLeadConversation(who, principal, enabled, 0, false, now);
1044
+ session = store.activeLeadSession(who.name);
1036
1045
  }
1037
1046
  if (session === null || session.ceilingDigest !== principal.ceilingDigest || session.approverGeneration !== principal.generation)
1038
1047
  return null;
1039
- const rows = mateConversationRows(who, principal, focusTask, now, chatProject);
1048
+ const rows = leadConversationRows(who, principal, focusTask, now, chatProject);
1040
1049
  return {
1041
- sessionId: session.id, user: session.approver, version: mateChatVersion({ ...rows, focusTask }),
1042
- messages: mateBrowserMessages(rows, who.session.csrf, back, { task: focusTask?.id ?? null, project: chatProject }),
1043
- pendingTurnId: rows.pending?.id ?? null, requestId: randomBytes(16).toString("hex"), maxChars: MATE_MESSAGE_MAX_CHARS,
1050
+ sessionId: session.id, user: session.approver, version: leadChatVersion({ ...rows, focusTask }),
1051
+ messages: leadBrowserMessages(rows, back, { task: focusTask?.id ?? null, project: chatProject }),
1052
+ pendingTurnId: rows.pending?.id ?? null, requestId: randomBytes(16).toString("hex"), maxChars: LEAD_MESSAGE_MAX_CHARS,
1044
1053
  taskId: focusTask?.id ?? null, resultRunId, project: focusTask === null ? chatProject : null,
1045
1054
  };
1046
1055
  }
@@ -1049,17 +1058,17 @@ export function createDecisionServer(options) {
1049
1058
  /** Start the person's lead conversation: the session every later turn
1050
1059
  * debits (a membership spends nothing; direct API gets a per-conversation
1051
1060
  * ceiling) and its thread. Starting ends any older session. */
1052
- function startMateConversation(who, principal, enabled, ceilingUsd, follow, now) {
1061
+ function startLeadConversation(who, principal, enabled, ceilingUsd, follow, now) {
1053
1062
  const ceilingMicrousd = enabled.billing === "subscription" ? 0 : Math.round(ceilingUsd * 1_000_000);
1054
1063
  const termsDigest = createHash("sha256").update(`${ceilingMicrousd}\n${principal.ceilingDigest}`).digest("hex");
1055
- const sessionId = store.mintMateSession({ approver: who.name, approverGeneration: principal.generation, credentialKey: enabled.credentialKey, ceilingMicrousd, ceilingDigest: principal.ceilingDigest, termsDigest }, now);
1056
- const thread = store.openMateThread(who.name, principal.ceilingDigest, now).thread;
1064
+ const sessionId = store.mintLeadSession({ approver: who.name, approverGeneration: principal.generation, credentialKey: enabled.credentialKey, ceilingMicrousd, ceilingDigest: principal.ceilingDigest, termsDigest }, now);
1065
+ const thread = store.openLeadThread(who.name, principal.ceilingDigest, now).thread;
1057
1066
  if (follow)
1058
- configureLeadFollow(store, principal, store.getMateSession(sessionId), thread, true, now);
1059
- mateSaid.delete(who.session.csrf);
1067
+ configureLeadFollow(store, principal, store.getLeadSession(sessionId), thread, true, now);
1068
+ leadSaid.delete(who.session.csrf);
1060
1069
  return sessionId;
1061
1070
  }
1062
- function matePrincipal(who) {
1071
+ function leadPrincipal(who) {
1063
1072
  const verified = verifyApproverStanding(store, who.name, who.session.generation, managedRepos());
1064
1073
  return verified.ok ? verified.who : null;
1065
1074
  }
@@ -1187,9 +1196,7 @@ export function createDecisionServer(options) {
1187
1196
  }
1188
1197
  const saturated = open.length > 200;
1189
1198
  const safeEntries = restricted() ? entries.filter(one => ["/work", "/work?view=needs-you", "/", "/board", "/flows", "/done", "/review", "/tasks", "/runs", "/projects", "/ledger"].includes(one.href) || one.href.startsWith("/t/")) : entries;
1190
- const json = JSON.stringify(saturated ? [...safeEntries, { label: "… more in the task list", href: "/tasks" }] : safeEntries)
1191
- .replace(/</g, "\\u003c");
1192
- return `<script type="application/json" id="palette-index">${json}</script>`;
1199
+ return jsonScript(saturated ? [...safeEntries, { label: "… more in the task list", href: "/tasks" }] : safeEntries, { id: "palette-index" });
1193
1200
  }
1194
1201
  /**
1195
1202
  * The palette cache (arc 4, finding 6), and its honest contract: a
@@ -1230,15 +1237,10 @@ export function createDecisionServer(options) {
1230
1237
  if (s.workspace?.view?.kind === "flow")
1231
1238
  s = { ...s, workspace: { ...s.workspace, view: { ...s.workspace.view, stepUp: { label: ssoFacts.label, fresh: ssoFacts.fresh, confirmHref: `/login/sso?reauth=1&return=${encodeURIComponent(back)}` } } } };
1232
1239
  }
1233
- const teamEntry = requestContext.getStore()?.returnTo;
1234
- if (teamEntry?.startsWith('/chat') && !s.workspace?.team && !teamEntry.includes('task=') && !teamEntry.includes('proposal=') && demoLeadHere() === null) {
1235
- s.body = '<p class="team-entry"><a href="/chat?team=1">Open team chat</a></p>' + s.body;
1236
- if (s.workspace?.conversation)
1237
- s.workspace.controlsHtml = '<p><a href="/chat?team=1">Open team chat</a></p>' + (s.workspace.controlsHtml ?? '');
1238
- }
1240
+ // D5: team chat (the central team service) is deprecated; /chat?team=1 still opens this release, but nothing links to it.
1239
1241
  const sensitive = s.forceSensitive === true ||
1240
- SENSITIVE_INPUT.test(s.body) ||
1241
- (s.chrome?.listPane !== undefined && SENSITIVE_INPUT.test(s.chrome.listPane));
1242
+ SENSITIVE_INPUT.test(htmlString(s.body)) ||
1243
+ (s.chrome?.listPane !== undefined && SENSITIVE_INPUT.test(htmlString(s.chrome.listPane)));
1242
1244
  const requestFacts = requestContext.getStore();
1243
1245
  if (requestFacts?.browser && s.chrome && !s.forceSensitive && (requestFacts.workspaceRead || browserAssetsAvailable())) {
1244
1246
  const path = new URL(requestFacts.returnTo, 'http://standing-orders.local');
@@ -1248,7 +1250,7 @@ export function createDecisionServer(options) {
1248
1250
  path.searchParams.set('format', 'workspace');
1249
1251
  // The legacy list pane (every task or build as links) duplicates the
1250
1252
  // Crew panel and Tasks page; inside the workspace the page stands alone.
1251
- const pageHtml = s.body;
1253
+ const pageHtml = htmlString(s.body);
1252
1254
  const extras = s.workspace ?? {};
1253
1255
  const notices = [...(extras.notices ?? [])];
1254
1256
  if (s.chrome.modeBanner)
@@ -1289,7 +1291,7 @@ export function createDecisionServer(options) {
1289
1291
  try {
1290
1292
  const actor = requestFacts.actor ?? '';
1291
1293
  if (actor !== '') {
1292
- chats = store.listMateThreads(actor, 16).flatMap((thread) => {
1294
+ chats = store.listLeadThreads(actor, 16).flatMap((thread) => {
1293
1295
  if (thread.scope.kind === 'project') {
1294
1296
  const repo = thread.scope.key;
1295
1297
  return visible(repo) && managedRepos().includes(repo) ? [{ kind: 'project', title: projectName(repo), href: projectChatHref(repo), at: thread.lastMessageAt }] : [];
@@ -1312,12 +1314,12 @@ export function createDecisionServer(options) {
1312
1314
  ...(requestFacts.actor ? { leadName: leadNameOf(store, requestFacts.actor) } : {}),
1313
1315
  refreshUrl: path.pathname + path.search,
1314
1316
  receipt: conversation !== null && request && REQUEST_TOKEN.test(request)
1315
- ? { request, received: store.mateRequestReceipt(conversation.sessionId, request) !== null } : null,
1317
+ ? { request, received: store.leadRequestReceipt(conversation.sessionId, request) !== null } : null,
1316
1318
  projects: browserProjectsOf(s.chrome.projects ?? []), ...crew,
1317
1319
  conversation, ...(extras.team ? { team: extras.team } : {}), focus: extras.focus ?? null, result: extras.result ?? null,
1318
- catchUpHtml: extras.catchUpHtml ?? '', controlsHtml: extras.controlsHtml ?? '', notices, view: extras.view ?? null,
1320
+ catchUpHtml: extras.catchUpHtml === undefined ? '' : htmlString(extras.catchUpHtml), controlsHtml: extras.controlsHtml === undefined ? '' : htmlString(extras.controlsHtml), notices, view: extras.view ?? null,
1319
1321
  ...(s.chrome.demo ? { demo: { text: DEMO_BANNER, short: DEMO_BANNER_SHORT } } : {}),
1320
- pageHtml: extras.pageHtml === undefined ? (conversation === null ? pageHtml : null) : extras.pageHtml,
1322
+ pageHtml: extras.pageHtml === undefined ? (conversation === null ? pageHtml : null) : extras.pageHtml === null ? null : htmlString(extras.pageHtml),
1321
1323
  navigation: [...browserNavigationOf(currentPath, s.chrome.project, needsYou, tasksProject, s.chrome.inboxLabel), { label: 'Workspace tools', href: '/menu', active: path.pathname === '/menu' }],
1322
1324
  chats,
1323
1325
  ...(s.refreshSeconds === undefined ? {} : { refreshSeconds: Math.max(5, Math.floor(s.refreshSeconds)) }),
@@ -1355,9 +1357,9 @@ export function createDecisionServer(options) {
1355
1357
  const script = functional + (chromeLayer ? chromeScript() + WORKSPACE_MOTION_SCRIPT : sensitiveChrome);
1356
1358
  const nonce = script === "" ? undefined : randomBytes(16).toString("base64");
1357
1359
  const body = chromeLayer
1358
- ? `${s.body}\n${paletteTagCached(s.chrome?.project ?? null)}\n${KBD_HELP}`
1360
+ ? html `${s.body}\n${paletteTagCached(s.chrome?.project ?? null)}\n${KBD_HELP}`
1359
1361
  : s.body;
1360
- const html = shell(s.title, body, {
1362
+ const document = shell(s.title, body, {
1361
1363
  ...(s.chrome === undefined ? {} : { chrome: s.chrome }),
1362
1364
  ...(sensitive ? { sensitive: true } : {}),
1363
1365
  ...(s.chrome !== undefined ? { sidebarToggle: true } : {}),
@@ -1367,7 +1369,7 @@ export function createDecisionServer(options) {
1367
1369
  // Pages that ship the chrome layer keep connect-src (v28 granted it for
1368
1370
  // the since-removed watched-session beat), beside pages whose own
1369
1371
  // functional script polls.
1370
- return page(response, status, html, nonce, s.functional?.fetches === true || chromeLayer || sensitiveChrome !== "");
1372
+ return page(response, status, document, nonce, s.functional?.fetches === true || chromeLayer || sensitiveChrome !== "");
1371
1373
  }
1372
1374
  /** The needs-you count every surface wears, read from this request's own
1373
1375
  * Work counts (one query per request, never a cached earlier one), so a
@@ -1513,41 +1515,30 @@ export function createDecisionServer(options) {
1513
1515
  if (peekCache.size <= PEEK_CACHE_ENTRIES && peekCacheBytes <= PEEK_CACHE_BYTES)
1514
1516
  break;
1515
1517
  peekCache.delete(key);
1516
- peekCacheBytes -= Buffer.byteLength(entry.fragment);
1518
+ peekCacheBytes -= entry.bytes;
1517
1519
  }
1518
1520
  };
1519
1521
  /** One typed sentence inside the region — plain words, no raw errors.
1520
1522
  * `final` marks conditions that cannot heal for this run (finished,
1521
1523
  * superseded, wrong machine): the region poller reads the marker and
1522
1524
  * stops, instead of refetching a dead build every beat forever. */
1523
- const peekSay = (message, final = false) => `<p class="meta"${final ? " data-region-stop" : ""}>${escape(message)}</p>`;
1525
+ const peekSay = (message, final = false) => html `<p class="meta"${final ? html ` data-region-stop` : ""}>${message}</p>`;
1524
1526
  /** The sanitize pipeline (finding 30/35): normalize → mask → escape. */
1525
1527
  const peekName = (path) => {
1526
1528
  const normalized = path.replace(/[\u0000-\u001f\u007f]/g, "");
1527
1529
  const masked = scanForSecrets(normalized).length > 0 ? "[redacted: a credential-shaped name]" : normalized;
1528
- return escape(masked);
1530
+ return textHtml(masked);
1529
1531
  };
1530
1532
  async function handlePost(route, url, who, request, response, posted) {
1531
- const early = () => dispatchConsole(route, { url, who, request, response, route, now: clock(), project: projectOf(who, request) ?? null, chosenProject: projectOf(who, request) ?? null, posted });
1533
+ // Every POST passes the shared mutation guard. Caller, scope, project (including a limited account's posted repo)
1534
+ // and role were admitted by the route table.
1532
1535
  const denied = authorizeMutation(request, who, readForm(posted, CONSOLE_FORMS.mutationGuard));
1533
1536
  if (denied !== null)
1534
1537
  return refuse(response, who, denied.status, denied.message);
1535
- if (["chat.demo", "provider.resume", "settings.updates-dismiss", "settings.updates-checks", "code.act"].includes(route.id))
1536
- return early();
1537
- if (restricted()) {
1538
- const repos = readForm(posted, CONSOLE_FORMS.projectCeiling).getAll("repo");
1539
- if (repos.length > 1 || repos.some(repo => repo.trim() !== "" && !visible(repo.trim())))
1540
- return refuse(response, who, 403, "That project is outside your access.", "/projects");
1541
- }
1542
- // THE CENTRAL VIEWER GATE (modes chain, D2/E2): consequential POSTs
1543
- // require ACTIVE approver standing — cookie and bearer alike — with
1544
- // an EXACT allowlist of session-local acts, never a prefix. /login,
1545
- // /logout and /join answer before this handler.
1546
- if (who.role === "viewer") {
1547
- if (route.viewer !== true) {
1548
- return refuse(response, who, 403, "your login can watch, not act — ask an approver to upgrade you");
1549
- }
1550
- }
1538
+ // Standing can change after a token was minted: a viewer's act stays refused, cookie and bearer alike, except
1539
+ // the row's session-local acts.
1540
+ if (who.role === "viewer" && !route.viewer)
1541
+ return refuse(response, who, 403, "your login can watch, not act — ask an approver to upgrade you");
1551
1542
  const now = clock();
1552
1543
  // Any accepted mutation may change what the inbox owes; the badge
1553
1544
  // re-counts within five seconds either way, this just makes it exact.
@@ -1568,7 +1559,7 @@ export function createDecisionServer(options) {
1568
1559
  });
1569
1560
  });
1570
1561
  // Keep native process custody until shutdown has verified agent/tool exit.
1571
- // Coding custody does not depend on teammates or the lead's follow pass, so
1562
+ // Coding custody does not depend on subagents or the lead's follow pass, so
1572
1563
  // it closes at once and never waits behind a model turn: a stop must release
1573
1564
  // the owner record well inside the service's exit window (Oct 2: deploys
1574
1565
  // over 0.9.11 found it still held by a killed process).
@@ -1588,14 +1579,9 @@ export function createDecisionServer(options) {
1588
1579
  requestBudget.flush();
1589
1580
  }
1590
1581
  catch { /* saved again on the close event when it can be */ }
1591
- for (const stream of teamStreams)
1592
- stream.end();
1593
- teamStreams.clear();
1594
- for (const stream of chatStreams)
1595
- stream.end();
1596
- chatStreams.clear();
1597
1582
  flowRooms.close();
1598
1583
  taskRooms.close();
1584
+ liveHandlers.close();
1599
1585
  detachReads();
1600
1586
  liveFollower.close();
1601
1587
  void Promise.all([closeCoding(), bounded(team.close()), bounded(leadMaintenance?.stop()), bounded(reads?.close())]).then(() => closeServer(callback)).catch(error => {
@@ -1654,18 +1640,18 @@ export function createDecisionServer(options) {
1654
1640
  get flowRooms() { return flowRooms; },
1655
1641
  get providerHome() { return providerHome; },
1656
1642
  get toolHome() { return toolHome; },
1657
- get matePrincipal() { return matePrincipal; },
1643
+ get leadPrincipal() { return leadPrincipal; },
1658
1644
  get chatScopeOf() { return chatScopeOf; },
1659
1645
  get liveTurns() { return liveTurns; },
1660
- get chatStreams() { return chatStreams; },
1661
- get mateConversationRows() { return mateConversationRows; },
1646
+ get liveTurnStarted() { return liveTurnStarted; },
1647
+ get leadConversationRows() { return leadConversationRows; },
1662
1648
  get demoLeadHere() { return demoLeadHere; },
1663
1649
  get teamBrowserReply() { return teamBrowserReply; },
1664
1650
  get team() { return team; },
1665
1651
  get teamChatProvider() { return teamChatProvider; },
1666
1652
  get pullRequestTargetOf() { return pullRequestTargetOf; },
1667
1653
  get chatEnablement() { return chatEnablement; },
1668
- get startMateConversation() { return startMateConversation; },
1654
+ get startLeadConversation() { return startLeadConversation; },
1669
1655
  get projectFamilyPeek() { return projectFamilyPeek; },
1670
1656
  get needsYouBadge() { return needsYouBadge; },
1671
1657
  get liveRefreshSeconds() { return liveRefreshSeconds; },
@@ -1698,7 +1684,7 @@ export function createDecisionServer(options) {
1698
1684
  get deletedRepos() { return deletedRepos; },
1699
1685
  get lookupSession() { return lookupSession; },
1700
1686
  get authorizeMutation() { return authorizeMutation; },
1701
- get mateSaid() { return mateSaid; },
1687
+ get leadSaid() { return leadSaid; },
1702
1688
  get armTaskResume() { return armTaskResume; },
1703
1689
  get chatFetcher() { return chatFetcher; },
1704
1690
  get chatCeilingDigest() { return chatCeilingDigest; },
@@ -1763,7 +1749,6 @@ export function createDecisionServer(options) {
1763
1749
  get allowedHost() { return allowedHost; },
1764
1750
  get team() { return team; },
1765
1751
  get teamBrowserReply() { return teamBrowserReply; },
1766
- get teamStreams() { return teamStreams; },
1767
1752
  get requestBudget() { return requestBudget; },
1768
1753
  get options() { return options; },
1769
1754
  get coding() { return coding; },
@@ -1781,13 +1766,26 @@ export function createDecisionServer(options) {
1781
1766
  get teamsSourceBudget() { return teamsSourceBudget; },
1782
1767
  get teamsTenantBudget() { return teamsTenantBudget; },
1783
1768
  };
1769
+ const liveHandlers = createLiveHandlers(handlerRuntime, { bus: liveBus, workspaceRevision, chatProjectOf: chatHandlers.chatProjectOf });
1770
+ liveTurnStarted = liveHandlers.turnStarted;
1784
1771
  const remoteHandlers = createRemoteHandlers(remoteRuntime);
1785
1772
  const registry = createHandlerRegistry([
1786
1773
  ...tasksHandlers.registrations, ...flowsHandlers.registrations, ...chatHandlers.registrations,
1787
1774
  ...settingsHandlers.registrations, ...peopleHandlers.registrations, ...pagesHandlers.registrations,
1788
- ...remoteHandlers.registrations,
1775
+ ...remoteHandlers.registrations, ...liveHandlers.registrations,
1789
1776
  ]);
1790
- function dispatchConsole(route, ctx) { return registry.console(route, ctx); }
1777
+ /** The row's role, after admission and before its one handler. */
1778
+ function dispatchConsole(route, ctx) {
1779
+ const { who, response } = ctx;
1780
+ const held = route.role === "any" || ((!route.roleBrowser || who.via === "cookie") &&
1781
+ (route.role === "approver" ? who.role === "approver" : store.isInstanceOperator(who.name)));
1782
+ if (!held) {
1783
+ ctx.request.resume();
1784
+ const refusal = route.roleRefusal;
1785
+ return Promise.resolve("message" in refusal ? refuse(response, who, 403, refusal.message, refusal.back) : respond(response, refusal.status, refusal.type, refusal.body));
1786
+ }
1787
+ return registry.console(route, ctx);
1788
+ }
1791
1789
  async function dispatchEdge(route, ctx) {
1792
1790
  if (route.callers.length === 1 && route.callers[0] === 'cookie' && ctx.request.headers.authorization !== undefined) {
1793
1791
  return respond(ctx.response, 403, 'text/plain; charset=utf-8', 'This address needs a browser sign-in.');
@@ -1801,4 +1799,12 @@ export function createDecisionServer(options) {
1801
1799
  }
1802
1800
  return Object.assign(server, { mintSignInLink, closeCoding });
1803
1801
  }
1804
- export { decisionAnswerScript, diffFileAnchor, earlierVersionsWords, editorFileHref, inboxFingerprints, needActionOf, orderChangedFiles, PAGE_CSS, parseInboxTab, pinnedTheme, plainConclusionOf, queueScript, rankReviewQueue, redactedPath, reviewFilePriority, reviewPriorityOf, revisionLineageWords, runFactsFragment, SENSITIVE_INPUT, settingsGroups, settingsTiles, SHUTDOWN_WAIT_MS, SIGN_IN_LINK_MS, SIGN_IN_LINK_PATH, ssoStepUps, TASK_COMPOSER_MODES, withinSignedTouches, wrongHostPage } from "./server/shared.js";
1802
+ export { PAGE_CSS, pinnedTheme, SENSITIVE_INPUT } from "./server/chrome.js";
1803
+ export { redactedPath, SHUTDOWN_WAIT_MS } from "./server/http.js";
1804
+ export { TASK_COMPOSER_MODES } from "./server/render-chat.js";
1805
+ export { inboxFingerprints, parseInboxTab, queueScript } from "./server/render-pages.js";
1806
+ export { ssoStepUps, wrongHostPage } from "./server/render-people.js";
1807
+ export { diffFileAnchor, editorFileHref, needActionOf, orderChangedFiles, plainConclusionOf, rankReviewQueue, reviewFilePriority, reviewPriorityOf, runFactsFragment, withinSignedTouches } from "./server/render-results.js";
1808
+ export { settingsGroups, settingsTiles } from "./server/render-settings.js";
1809
+ export { decisionAnswerScript, earlierVersionsWords, revisionLineageWords } from "./server/render-tasks.js";
1810
+ export { SIGN_IN_LINK_MS, SIGN_IN_LINK_PATH } from "./server/session.js";
@@ -1,26 +1,23 @@
1
- import type { Registration } from './handler-registry.js';
2
1
  import { type BrowserFirstRun, type BrowserHome, type BrowserPhoneCard } from '../browser-workspace.js';
3
2
  import { type DemoResultView } from "../demo-chat.js";
4
3
  import type { DemoExchange } from "../demo.js";
5
4
  import { type FirstTaskSuggestion } from "../first-run.js";
6
- import type { MateProgress } from "../mate-progress.js";
5
+ import type { LeadProgress } from "../lead-progress.js";
7
6
  import type { DirectChatProviderId } from "../store.js";
8
- import type { HandlerContext } from './handler-context.js';
9
7
  import type { ServerRuntime } from './runtime.js';
10
- import { type ChatEnablement, type Session, type Who } from "./shared.js";
8
+ import { type ChatEnablement } from "./render-chat.js";
9
+ import { type Session, type Who } from "./session.js";
11
10
  export declare function createChatHandlers(runtime: ServerRuntime): {
12
- registrations: Registration[];
13
- get: (ctx: HandlerContext) => Promise<void>;
14
- post: (ctx: HandlerContext) => Promise<void>;
11
+ registrations: import("./handler-registry.js").Registration[];
15
12
  chatFirstRun: (now: Date, project: string | null) => BrowserFirstRun | undefined;
16
13
  phoneCard: (who: Who & {
17
14
  via: "cookie";
18
15
  }, now: Date) => BrowserPhoneCard | undefined;
19
16
  chatProjectOf: (values: string[]) => string | null | undefined;
20
- beginLiveTurn: (thread: number) => (event: MateProgress) => void;
17
+ beginLiveTurn: (thread: number) => (event: LeadProgress) => void;
21
18
  endLiveTurn: (thread: number, ok: boolean) => void;
22
- noteMate: (csrf: string, turn: number | null, message: string) => void;
23
- takeMateNote: (csrf: string, liveSession: number | null) => string | null;
19
+ noteLead: (csrf: string, turn: number | null, message: string) => void;
20
+ takeLeadNote: (csrf: string, liveSession: number | null) => string | null;
24
21
  demoResultView: (exchange: DemoExchange) => DemoResultView | null;
25
22
  storeChatKey: (provider: DirectChatProviderId, key: string) => {
26
23
  ok: true;