toolroll 0.9.55 → 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 (200) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/README.md +24 -8
  3. package/dist/action-ledger.d.ts +2 -4
  4. package/dist/action-ledger.js +2 -10
  5. package/dist/approval-policy.d.ts +5 -5
  6. package/dist/approval-policy.js +4 -4
  7. package/dist/approval-rules-ui.js +2 -2
  8. package/dist/browser/workspace.js +5 -5
  9. package/dist/browser-workspace.d.ts +12 -12
  10. package/dist/budget-alerts.js +3 -3
  11. package/dist/chat-actions.d.ts +23 -18
  12. package/dist/chat-actions.js +110 -88
  13. package/dist/chat-ask.d.ts +2 -2
  14. package/dist/chat-ask.js +5 -5
  15. package/dist/chat-channel.d.ts +15 -15
  16. package/dist/chat-channel.js +20 -20
  17. package/dist/chat-controls.d.ts +0 -4
  18. package/dist/chat-controls.js +0 -1
  19. package/dist/chat-core.js +1 -1
  20. package/dist/chat-delivery-state.js +2 -2
  21. package/dist/chat-delivery.d.ts +2 -2
  22. package/dist/chat-delivery.js +27 -34
  23. package/dist/chat-flow.js +3 -3
  24. package/dist/chat-messages.d.ts +1 -1
  25. package/dist/chat-messages.js +4 -4
  26. package/dist/chat-migration.d.ts +1 -1
  27. package/dist/chat-migration.js +11 -11
  28. package/dist/chat-rooms.js +3 -3
  29. package/dist/chat-warmth.d.ts +2 -2
  30. package/dist/cli.d.ts +1 -1
  31. package/dist/cli.js +6 -7
  32. package/dist/contracts/chat-actions.d.ts +32 -27
  33. package/dist/contracts/chat-actions.js +29 -11
  34. package/dist/contracts/chat-content.d.ts +1 -1
  35. package/dist/contracts/chat-content.js +1 -1
  36. package/dist/contracts/chat-tables.js +4 -4
  37. package/dist/contracts/console-api.d.ts +6 -6
  38. package/dist/contracts/console-api.js +7 -7
  39. package/dist/contracts/flow-propose.d.ts +3 -3
  40. package/dist/contracts/flow.d.ts +132 -125
  41. package/dist/contracts/flow.js +27 -12
  42. package/dist/contracts/lead-context.d.ts +1 -1
  43. package/dist/contracts/lead-context.js +2 -2
  44. package/dist/contracts/lead-tools.d.ts +20 -18
  45. package/dist/contracts/lead-tools.js +15 -15
  46. package/dist/contracts/stage-output.d.ts +1 -1
  47. package/dist/contracts/stage-output.js +1 -1
  48. package/dist/contracts/store-json.d.ts +23 -22
  49. package/dist/contracts/store-json.js +17 -17
  50. package/dist/contracts/{teammate-turn.d.ts → subagent-turn.d.ts} +5 -5
  51. package/dist/contracts/{teammate-turn.js → subagent-turn.js} +7 -7
  52. package/dist/converse.d.ts +23 -23
  53. package/dist/converse.js +28 -28
  54. package/dist/coordinator-proposals.d.ts +2 -2
  55. package/dist/coordinator-proposals.js +1 -1
  56. package/dist/coordinator.js +1 -1
  57. package/dist/deprecations.d.ts +12 -0
  58. package/dist/deprecations.js +20 -0
  59. package/dist/desktop-update-gate.js +8 -4
  60. package/dist/desktop-update.js +6 -1
  61. package/dist/discord-chat.js +6 -6
  62. package/dist/dispatch.d.ts +1 -1
  63. package/dist/dispatch.js +1 -1
  64. package/dist/envelope.js +0 -1
  65. package/dist/export-ui.js +1 -1
  66. package/dist/export.js +7 -7
  67. package/dist/flow-engine.d.ts +2 -2
  68. package/dist/flow-engine.js +21 -21
  69. package/dist/flow-insights.d.ts +1 -1
  70. package/dist/flow-share.js +13 -8
  71. package/dist/flow-steps.d.ts +5 -5
  72. package/dist/flow-steps.js +19 -19
  73. package/dist/flow-triggers.d.ts +1 -1
  74. package/dist/flow-triggers.js +1 -1
  75. package/dist/flows-cli.js +1 -1
  76. package/dist/flows-ui.js +20 -20
  77. package/dist/flows.js +19 -19
  78. package/dist/guides.js +4 -3
  79. package/dist/invoke.js +1 -1
  80. package/dist/kits-ui.js +5 -5
  81. package/dist/kits.d.ts +12 -12
  82. package/dist/kits.js +31 -31
  83. package/dist/{mate-cli.d.ts → lead-cli.d.ts} +11 -11
  84. package/dist/{mate-cli.js → lead-cli.js} +38 -38
  85. package/dist/lead-commitments.js +4 -4
  86. package/dist/lead-context.d.ts +2 -2
  87. package/dist/lead-context.js +9 -9
  88. package/dist/{mate-contract.d.ts → lead-contract.d.ts} +3 -3
  89. package/dist/{mate-contract.js → lead-contract.js} +7 -6
  90. package/dist/{mate-doors.d.ts → lead-doors.d.ts} +6 -6
  91. package/dist/{mate-doors.js → lead-doors.js} +24 -24
  92. package/dist/lead-follow.d.ts +5 -5
  93. package/dist/lead-follow.js +9 -9
  94. package/dist/lead-people.d.ts +5 -5
  95. package/dist/lead-people.js +14 -14
  96. package/dist/{mate-progress.d.ts → lead-progress.d.ts} +9 -9
  97. package/dist/{mate-progress.js → lead-progress.js} +3 -3
  98. package/dist/{mate-tools.d.ts → lead-tools.d.ts} +19 -19
  99. package/dist/{mate-tools.js → lead-tools.js} +109 -101
  100. package/dist/{mate.d.ts → lead.d.ts} +27 -27
  101. package/dist/{mate.js → lead.js} +102 -102
  102. package/dist/limits-ui.d.ts +1 -1
  103. package/dist/limits-ui.js +1 -1
  104. package/dist/mcp-connect.d.ts +1 -1
  105. package/dist/mcp-connect.js +3 -3
  106. package/dist/mcp-core.js +1 -1
  107. package/dist/memory-pass.d.ts +2 -2
  108. package/dist/memory-pass.js +7 -7
  109. package/dist/notification-rows.d.ts +2 -2
  110. package/dist/operate.d.ts +3 -3
  111. package/dist/operate.js +35 -34
  112. package/dist/policy.d.ts +1 -1
  113. package/dist/principal.d.ts +1 -1
  114. package/dist/principal.js +2 -2
  115. package/dist/project-delete-ui.js +1 -1
  116. package/dist/project-delete.d.ts +1 -1
  117. package/dist/project-delete.js +26 -26
  118. package/dist/project-memory.js +1 -1
  119. package/dist/project-tools.d.ts +1 -1
  120. package/dist/project-tools.js +1 -1
  121. package/dist/recipe-ui.js +1 -1
  122. package/dist/retention.js +4 -4
  123. package/dist/scope.d.ts +2 -2
  124. package/dist/scout-report.d.ts +1 -1
  125. package/dist/scout-report.js +1 -1
  126. package/dist/serve.js +40 -45
  127. package/dist/server/chat.d.ts +4 -4
  128. package/dist/server/chat.js +97 -97
  129. package/dist/server/chrome.js +3 -3
  130. package/dist/server/flows.js +69 -62
  131. package/dist/server/guards.js +3 -3
  132. package/dist/server/http.d.ts +3 -3
  133. package/dist/server/http.js +1 -1
  134. package/dist/server/live.js +6 -6
  135. package/dist/server/people-tokens.js +1 -1
  136. package/dist/server/remote.js +12 -10
  137. package/dist/server/render-chat.d.ts +30 -30
  138. package/dist/server/render-chat.js +33 -33
  139. package/dist/server/render-pages.js +2 -2
  140. package/dist/server/render-settings.d.ts +7 -0
  141. package/dist/server/render-settings.js +3 -0
  142. package/dist/server/route-table.js +9 -7
  143. package/dist/server/runtime.d.ts +13 -13
  144. package/dist/server/settings.js +14 -13
  145. package/dist/server/tasks.js +6 -5
  146. package/dist/setup-guide.d.ts +2 -2
  147. package/dist/slack-chat.d.ts +2 -2
  148. package/dist/slack-chat.js +6 -6
  149. package/dist/spend-ui.d.ts +3 -3
  150. package/dist/spend-ui.js +9 -9
  151. package/dist/spend.d.ts +14 -14
  152. package/dist/spend.js +31 -31
  153. package/dist/store-baseline.d.ts +19 -0
  154. package/dist/store-baseline.js +2873 -0
  155. package/dist/store.d.ts +197 -249
  156. package/dist/store.js +747 -3084
  157. package/dist/subagent-admin.d.ts +53 -0
  158. package/dist/{teammate-admin.js → subagent-admin.js} +37 -37
  159. package/dist/{teammate-desk.d.ts → subagent-desk.d.ts} +22 -23
  160. package/dist/{teammate-desk.js → subagent-desk.js} +26 -35
  161. package/dist/{teammate-memory.d.ts → subagent-memory.d.ts} +17 -17
  162. package/dist/{teammate-memory.js → subagent-memory.js} +41 -41
  163. package/dist/{teammate-question.d.ts → subagent-question.d.ts} +6 -6
  164. package/dist/{teammate-question.js → subagent-question.js} +17 -17
  165. package/dist/{teammate-tools.d.ts → subagent-tools.d.ts} +18 -18
  166. package/dist/{teammate-tools.js → subagent-tools.js} +36 -36
  167. package/dist/{teammate-week.d.ts → subagent-week.d.ts} +10 -10
  168. package/dist/{teammate-week.js → subagent-week.js} +23 -23
  169. package/dist/{teammate-work.d.ts → subagent-work.d.ts} +21 -21
  170. package/dist/{teammate-work.js → subagent-work.js} +50 -50
  171. package/dist/subagents-ui.d.ts +22 -0
  172. package/dist/{teammates-ui.js → subagents-ui.js} +45 -45
  173. package/dist/{teammates.d.ts → subagents.d.ts} +11 -11
  174. package/dist/{teammates.js → subagents.js} +21 -21
  175. package/dist/subscription-chat.d.ts +13 -13
  176. package/dist/subscription-chat.js +19 -19
  177. package/dist/surface.d.ts +19 -2
  178. package/dist/surface.js +7 -31
  179. package/dist/team-cli.d.ts +1 -0
  180. package/dist/team-cli.js +14 -2
  181. package/dist/team-leads.js +13 -13
  182. package/dist/team-runtime.d.ts +2 -2
  183. package/dist/team-runtime.js +8 -8
  184. package/dist/teams-chat.js +6 -6
  185. package/dist/telegram-flow.d.ts +3 -3
  186. package/dist/telegram-flow.js +3 -3
  187. package/dist/{telegram-mate.d.ts → telegram-lead.d.ts} +5 -5
  188. package/dist/{telegram-mate.js → telegram-lead.js} +27 -27
  189. package/dist/telegram-status.js +1 -1
  190. package/dist/telegram-team.d.ts +1 -1
  191. package/dist/telegram-team.js +5 -5
  192. package/dist/telegram.d.ts +1 -1
  193. package/dist/telegram.js +9 -30
  194. package/dist/text-limits.d.ts +18 -18
  195. package/dist/text-limits.js +18 -18
  196. package/dist/toolroll-update.d.ts +8 -0
  197. package/dist/toolroll-update.js +45 -8
  198. package/package.json +1 -2
  199. package/dist/teammate-admin.d.ts +0 -53
  200. package/dist/teammates-ui.d.ts +0 -21
@@ -1,4 +1,5 @@
1
1
  import { handlersOf } from './handler-registry.js';
2
+ import { DEPRECATED_PAGE } from "../deprecations.js";
2
3
  /** chat handlers, moved without changing their route bodies. */
3
4
  import { randomBytes } from "node:crypto";
4
5
  import { chmodSync, rmSync as rmFileSync, writeFileSync as writeFsFileSync } from "node:fs";
@@ -18,8 +19,8 @@ import { leadBriefHtml } from '../lead-context.js';
18
19
  import { configureLeadFollow, leadFollowStatus } from '../lead-follow.js';
19
20
  import { leadActivity } from "../lead-voice.js";
20
21
  import { limitsView } from "../limits-ui.js";
21
- import { confirmCoordinatorProposal, confirmMateProposal, dismissCoordinatorProposal, dismissMateProposal } from "../mate-doors.js";
22
- import { MATE_MESSAGE_MAX_CHARS, runMateTurn } from "../mate.js";
22
+ import { confirmCoordinatorProposal, confirmLeadProposal, dismissCoordinatorProposal, dismissLeadProposal } from "../lead-doors.js";
23
+ import { LEAD_MESSAGE_MAX_CHARS, runLeadTurn } from "../lead.js";
23
24
  import { livePin, modelOptions } from "../model-catalog.js";
24
25
  import { verifyApproverByPassword, verifyApproverStanding } from "../principal.js";
25
26
  import { projectName } from "../project.js";
@@ -37,12 +38,12 @@ import { html, htmlString, postForm } from "../html.js";
37
38
  import { requestContext } from "./request-context.js";
38
39
  import { refuse, screen } from "./chrome.js";
39
40
  import { chatResultHref, chatReturnWithLatest, chatReturnWithSaid, projectChatHref, redirect, respond, safeChatReturn, safeReturn, taskChatHref, taskHref } from "./http.js";
40
- import { chatAckPage, chatPage, coordinatorProposalsSection, decisionsFor, mateAfterComposerHtml, mateChatVersion, mateMintCard, matePage, mateThreadHtml, PHONE_CARD_FACT, TASK_COMPOSER_MODES, taskChatLiveRegion, teamProposalCardParts } from "./render-chat.js";
41
+ import { chatAckPage, chatPage, coordinatorProposalsSection, decisionsFor, leadAfterComposerHtml, leadChatVersion, leadMintCard, leadPage, leadThreadHtml, PHONE_CARD_FACT, TASK_COMPOSER_MODES, taskChatLiveRegion, teamProposalCardParts } from "./render-chat.js";
41
42
  import {} from "./render-pages.js";
42
43
  import { completionForm, homePhaseWords, owedAcceptanceOf, resultPanelHtml } from "./render-results.js";
43
44
  import { browserCaller, oneLineUa } from "./session.js";
44
45
  export function createChatHandlers(runtime) {
45
- const { firstRunStepsNow, managedRepos, leadWords, store, options, phoneSetup, visible, liveTurns, mateSaid, evidenceRoot, clock, sessions, CHAT_CANDIDATE_TTL_MS, chatFetcher, CHAT_CANDIDATES_PER_APPROVER, chatCeilingDigest, workAccess, familyOf, firstTasks, matePrincipal, taskChatFocus, chatScopeOf, mateConversationRows, demoLeadHere, sendScreen, chromeFor, teamBrowserReply, team, teamChatProvider, runVisible, runIsLive, resultDetailOf, pullRequestTargetOf, chatEnablement, startMateConversation, projectFamilyPeek, needsYouBadge, liveRefreshSeconds, chatKeyFor, chatCatalog, providerHome, mintApprovalNonce, checkLocalAgents, agentSignInCommand, bustBadge, authenticateApprover, revisionDestination, armTaskResume, consumeApprovalNonce } = runtime;
46
+ const { firstRunStepsNow, managedRepos, leadWords, store, options, phoneSetup, visible, liveTurns, leadSaid, evidenceRoot, clock, sessions, CHAT_CANDIDATE_TTL_MS, chatFetcher, CHAT_CANDIDATES_PER_APPROVER, chatCeilingDigest, workAccess, familyOf, firstTasks, leadPrincipal, taskChatFocus, chatScopeOf, leadConversationRows, demoLeadHere, sendScreen, chromeFor, teamBrowserReply, team, teamChatProvider, runVisible, runIsLive, resultDetailOf, pullRequestTargetOf, chatEnablement, startLeadConversation, projectFamilyPeek, needsYouBadge, liveRefreshSeconds, chatKeyFor, chatCatalog, providerHome, mintApprovalNonce, checkLocalAgents, agentSignInCommand, bustBadge, authenticateApprover, revisionDestination, armTaskResume, consumeApprovalNonce } = runtime;
46
47
  // The read-only refresh (package 2): the same JSON status as before —
47
48
  // session binding, the send receipt, the live turn — plus a version
48
49
  // of the DISPLAYED facts and, only when the caller's version differs,
@@ -52,14 +53,14 @@ export function createChatHandlers(runtime) {
52
53
  async function mateStatus(ctx) {
53
54
  const { url, response, now } = ctx;
54
55
  const who = browserCaller(ctx.who);
55
- const session = store.activeMateSession(who.name);
56
- const principal = matePrincipal(who);
56
+ const session = store.activeLeadSession(who.name);
57
+ const principal = leadPrincipal(who);
57
58
  if (session === null || principal === null || session.ceilingDigest !== principal.ceilingDigest || session.approverGeneration !== principal.generation) {
58
59
  return respond(response, 200, "application/json", JSON.stringify({ session: null }));
59
60
  }
60
- store.sweepStaleMateTurns(now);
61
+ store.sweepStaleLeadTurns(now);
61
62
  const request = url.searchParams.get("request") ?? "";
62
- const receipt = /^[a-f0-9]{32}$/.test(request) ? store.mateRequestReceipt(session.id, request) : null;
63
+ const receipt = /^[a-f0-9]{32}$/.test(request) ? store.leadRequestReceipt(session.id, request) : null;
63
64
  const requestedTask = url.searchParams.get("task");
64
65
  const focusTask = requestedTask === null || requestedTask === "" ? null : taskChatFocus(requestedTask, now, who, { mintNonce: false });
65
66
  if (requestedTask !== null && requestedTask !== "" && focusTask === null) {
@@ -70,15 +71,15 @@ export function createChatHandlers(runtime) {
70
71
  const chatProject = focusTask !== null ? null : chatProjectOf(url.searchParams.getAll("project").filter(one => one !== ""));
71
72
  if (chatProject === undefined)
72
73
  return respond(response, 200, "application/json", JSON.stringify({ session: session.id, unavailable: true, received: receipt !== null }));
73
- const rows = mateConversationRows(who, principal, focusTask, now, chatProject);
74
- const version = mateChatVersion({ ...rows, focusTask });
74
+ const rows = leadConversationRows(who, principal, focusTask, now, chatProject);
75
+ const version = leadChatVersion({ ...rows, focusTask });
75
76
  const known = url.searchParams.get("version") ?? "";
76
77
  const csrf = who.session.csrf;
77
78
  const fragments = known === version
78
79
  ? null
79
80
  : {
80
- thread: htmlString(mateThreadHtml({ ...rows, focusTask, csrf, now, problem: takeMateNote(csrf, session.id), chatProject })),
81
- after: htmlString(mateAfterComposerHtml({ messages: rows.messages, pending: rows.pending, focusTask })),
81
+ thread: htmlString(leadThreadHtml({ ...rows, focusTask, csrf, now, problem: takeLeadNote(csrf, session.id), chatProject })),
82
+ after: htmlString(leadAfterComposerHtml({ messages: rows.messages, pending: rows.pending, focusTask })),
82
83
  live: focusTask === null ? null : htmlString(taskChatLiveRegion(focusTask, csrf, true, rows.pending !== null)),
83
84
  };
84
85
  return respond(response, 200, "application/json", JSON.stringify({
@@ -118,7 +119,8 @@ export function createChatHandlers(runtime) {
118
119
  // THIS session's memory; a bearer caller has nowhere to keep them.
119
120
  if (who.via !== "cookie")
120
121
  return refuse(response, who, 403, "chat is a browser surface — it keeps your drafts in the session");
121
- if (url.searchParams.get('private') !== '1' && !url.searchParams.has('task') && !url.searchParams.has('result') && !url.searchParams.has('project')) {
122
+ const teamAsked = url.searchParams.get('team') === '1' || url.searchParams.has('conversation') || url.searchParams.has('lead') || url.searchParams.has('proposal');
123
+ if (teamAsked && url.searchParams.get('private') !== '1' && !url.searchParams.has('task') && !url.searchParams.has('result') && !url.searchParams.has('project')) {
122
124
  const reply = teamBrowserReply(await team.execute({ name: who.name, generation: who.session.generation }, {
123
125
  operation: url.searchParams.has('conversation') ? 'show' : 'list',
124
126
  args: { ...(url.searchParams.has('conversation') ? { conversationId: url.searchParams.get('conversation') } : {}),
@@ -128,7 +130,7 @@ export function createChatHandlers(runtime) {
128
130
  return refuse(response, who, 403, reply.message, '/chat?private=1');
129
131
  if (url.searchParams.has('proposal')) {
130
132
  const id = Number(url.searchParams.get('proposal')), selected = reply.snapshot.selected;
131
- const proposal = Number.isSafeInteger(id) && id > 0 ? store.getMateProposal(id) : null;
133
+ const proposal = Number.isSafeInteger(id) && id > 0 ? store.getLeadProposal(id) : null;
132
134
  if (!selected || !proposal || proposal.thread !== selected.threadId)
133
135
  return refuse(response, who, 404, 'This proposal is unavailable.', '/chat');
134
136
  const back = '/chat?conversation=' + encodeURIComponent(selected.id);
@@ -136,15 +138,13 @@ export function createChatHandlers(runtime) {
136
138
  const card = teamProposalCardParts(store, { name: who.name, generation: who.session.generation }, proposal, reply.snapshot, who.session.csrf, decision, clock(), teamChatProvider).html;
137
139
  return sendScreen(response, 200, screen('Review action', html `<p><a href="${back}">Back to conversation</a></p>${card}`, { chrome: chromeFor(null, 'chat', undefined, 'all') }));
138
140
  }
139
- if (reply.snapshot.leads.length > 0 || url.searchParams.has('conversation') || url.searchParams.get('team') === '1') {
140
- const said = url.searchParams.get('said')?.slice(0, 1_000);
141
- return sendScreen(response, 200, screen('Chat', html `${said ? html `<p role="alert">${said}</p>` : ''}${teamWorkspaceHtml(reply.snapshot)}`, {
142
- chrome: chromeFor(null, 'chat', undefined, 'all'), workspace: { team: reply.snapshot, ...(said ? { notices: [said] } : {}) },
143
- }));
144
- }
141
+ const said = url.searchParams.get('said')?.slice(0, 1_000);
142
+ return sendScreen(response, 200, screen('Chat', html `<p class="problem" role="status" data-deprecated="team">${DEPRECATED_PAGE.team}</p>${said ? html `<p role="alert">${said}</p>` : ''}${teamWorkspaceHtml(reply.snapshot)}`, {
143
+ chrome: chromeFor(null, 'chat', undefined, 'all'), workspace: { team: reply.snapshot, notices: [DEPRECATED_PAGE.team, ...(said ? [said] : [])] },
144
+ }));
145
145
  }
146
146
  store.sweepStaleChatTurns(now);
147
- store.sweepStaleMateTurns(now);
147
+ store.sweepStaleLeadTurns(now);
148
148
  store.sweepCoordinatorProposals(now);
149
149
  sweepChatDrafts(Date.now());
150
150
  const requestedTask = url.searchParams.get("task");
@@ -233,8 +233,8 @@ export function createChatHandlers(runtime) {
233
233
  // The mate (mate arc §5): while a mate session is live, /chat IS the
234
234
  // thread — the same rows the CLI reads. Without one, fleet chat as
235
235
  // before, plus the card that mints a session.
236
- let mateSession = enabled.ok && who.role === "approver" ? store.activeMateSession(who.name) : null;
237
- const principal = enabled.ok && who.role === "approver" ? matePrincipal(who) : null;
236
+ let leadSession = enabled.ok && who.role === "approver" ? store.activeLeadSession(who.name) : null;
237
+ const principal = enabled.ok && who.role === "approver" ? leadPrincipal(who) : null;
238
238
  // Chat opens ready to talk (2026-09-23): an approver in good standing
239
239
  // needs no second password to start their own conversation. The one
240
240
  // exception to "a GET writes nothing": the strict same-site session
@@ -243,14 +243,14 @@ export function createChatHandlers(runtime) {
243
243
  // Membership chat only: it spends no dollars. Direct-API chat keeps a
244
244
  // one-tap Start with its visible spending limit (no password).
245
245
  const settingsView = url.searchParams.get("settings") === "1";
246
- if (!settingsView && enabled.ok && enabled.billing === "subscription" && principal !== null && (mateSession === null || mateSession.ceilingDigest !== principal.ceilingDigest) && !requestContext.getStore()?.workspaceRead) {
247
- startMateConversation(who, principal, enabled, 0, false, now);
248
- mateSession = store.activeMateSession(who.name);
246
+ if (!settingsView && enabled.ok && enabled.billing === "subscription" && principal !== null && (leadSession === null || leadSession.ceilingDigest !== principal.ceilingDigest) && !requestContext.getStore()?.workspaceRead) {
247
+ startLeadConversation(who, principal, enabled, 0, false, now);
248
+ leadSession = store.activeLeadSession(who.name);
249
249
  }
250
250
  // A session under another ceiling is not continuable from here; a GET
251
251
  // writes nothing (slice-2 review, finding 7) — the mint card below
252
252
  // starts a new conversation, and minting ends the old session.
253
- const ceilingStale = enabled.ok && mateSession !== null && principal !== null && mateSession.ceilingDigest !== principal.ceilingDigest;
253
+ const ceilingStale = enabled.ok && leadSession !== null && principal !== null && leadSession.ceilingDigest !== principal.ceilingDigest;
254
254
  const catchUp = leadBriefHtml(assignmentCatchUp(store, now, { principal: "operator", repos }, { limit: 6 }, evidenceRoot));
255
255
  const chatProjects = repos.map((repo, index) => {
256
256
  let peek = null;
@@ -284,12 +284,12 @@ export function createChatHandlers(runtime) {
284
284
  const withFirstRun = (shown) => firstRun === undefined && phone === undefined && home === null ? shown
285
285
  : { ...shown, ...(home === null ? {} : { refreshSeconds: liveRefreshSeconds() }),
286
286
  workspace: { ...shown.workspace, ...(firstRun === undefined ? {} : { firstRun }), ...(phone === undefined ? {} : { phone }), ...(home === null ? {} : { home }) } };
287
- if (enabled.ok && mateSession !== null && principal !== null && !ceilingStale) {
287
+ if (enabled.ok && leadSession !== null && principal !== null && !ceilingStale) {
288
288
  {
289
- const said = takeMateNote(who.session.csrf, mateSession.id);
290
- return sendScreen(response, 200, withFirstRun(matePage(chromeFor(null, "chat", undefined, "all"), {
291
- session: mateSession,
292
- ...mateConversationRows(who, principal, focusTask, now, chatProject),
289
+ const said = takeLeadNote(who.session.csrf, leadSession.id);
290
+ return sendScreen(response, 200, withFirstRun(leadPage(chromeFor(null, "chat", undefined, "all"), {
291
+ session: leadSession,
292
+ ...leadConversationRows(who, principal, focusTask, now, chatProject),
293
293
  chatProject,
294
294
  latched,
295
295
  config: enabled.config,
@@ -338,9 +338,9 @@ export function createChatHandlers(runtime) {
338
338
  problem: url.searchParams.get("said") ??
339
339
  focusProblem ??
340
340
  (ceilingStale ? "Project access changed. Your tasks and results are saved. Start a conversation with the current projects to continue." : null) ??
341
- takeMateNote(who.session.csrf, null),
341
+ takeLeadNote(who.session.csrf, null),
342
342
  resultPanel,
343
- ...(enabled.ok && who.role === "approver" ? { mateMint: mateMintCard(enabled, focusTask === null ? "/chat" : taskChatHref(focusTask.id)) } : {}),
343
+ ...(enabled.ok && who.role === "approver" ? { leadMint: leadMintCard(enabled, focusTask === null ? "/chat" : taskChatHref(focusTask.id)) } : {}),
344
344
  ...(who.role === "approver" ? { coordinatorProposals: coordinatorProposalsSection(coordinatorRows, decisionsFor(store, coordinatorRows), now, true, focusTask === null ? null : taskChatHref(focusTask.id)) } : {}),
345
345
  })));
346
346
  }
@@ -361,7 +361,7 @@ export function createChatHandlers(runtime) {
361
361
  const { url, response } = ctx;
362
362
  const who = browserCaller(ctx.who);
363
363
  const proposalId = Number(url.pathname.split("/")[3]);
364
- const reviewProposal = store.getMateProposal(proposalId);
364
+ const reviewProposal = store.getLeadProposal(proposalId);
365
365
  const sharedConversation = reviewProposal ? store.handle.prepare('SELECT id FROM team_conversation WHERE thread=?').get(reviewProposal.thread) : null;
366
366
  let principal = null;
367
367
  if (sharedConversation) {
@@ -375,12 +375,12 @@ export function createChatHandlers(runtime) {
375
375
  }
376
376
  }
377
377
  else
378
- principal = matePrincipal(who);
378
+ principal = leadPrincipal(who);
379
379
  if (principal === null)
380
380
  return refuse(response, who, 403, 'Your access changed. Sign in again.', '/chat');
381
381
  try {
382
- const id = proposalId, saved = store.getMateProposal(id), savedAction = saved?.kind === 'action' ? sharedActionPayload(saved.payload) : null;
383
- if (saved && savedAction && saved.state !== 'pending' && (sharedConversation !== null || store.getMateThread(saved.thread)?.approver === principal.name) && principal.repos.includes(savedAction.repo) && store.accountCanAccess(principal.name, savedAction.repo)) {
382
+ const id = proposalId, saved = store.getLeadProposal(id), savedAction = saved?.kind === 'action' ? sharedActionPayload(saved.payload) : null;
383
+ if (saved && savedAction && saved.state !== 'pending' && (sharedConversation !== null || store.getLeadThread(saved.thread)?.approver === principal.name) && principal.repos.includes(savedAction.repo) && store.accountCanAccess(principal.name, savedAction.repo)) {
384
384
  const task = typeof saved.outcome?.['taskId'] === 'string' ? saved.outcome['taskId'] : typeof savedAction.request['task'] === 'string' ? savedAction.request['task'] : null;
385
385
  const destination = task ? taskHref(task) : `/settings/${savedAction.operation.startsWith('skill_') ? 'skills' : 'knowledge'}?repo=${encodeURIComponent(savedAction.repo)}`;
386
386
  const said = typeof saved.outcome?.['said'] === 'string' ? saved.outcome['said'] : saved.state === 'dismissed' ? 'Action dismissed.' : saved.state === 'expired' ? 'This proposal expired. Ask for a fresh proposal.' : 'This action has no completed outcome yet.';
@@ -459,7 +459,7 @@ export function createChatHandlers(runtime) {
459
459
  : verb === "change" ? lead.change(id, note, now)
460
460
  : verb === "revise" ? lead.requestChanges(id, note, now)
461
461
  : lead.complete(id, now, (taskId, runId) => {
462
- const principal = matePrincipal(who);
462
+ const principal = leadPrincipal(who);
463
463
  if (principal === null)
464
464
  return { ok: false, message: "Your access changed. Sign in again." };
465
465
  const current = assignmentOf(store, taskId, now, { principal: "operator", repos: principal.repos }, evidenceRoot);
@@ -613,11 +613,11 @@ export function createChatHandlers(runtime) {
613
613
  return redirect(response, back);
614
614
  }
615
615
  // ---- the mate (mate arc §5) ------------------------------------------
616
- async function mateMint(ctx) {
616
+ async function leadMint(ctx) {
617
617
  const { who, response, now, posted } = ctx;
618
- const body = readForm(posted, CONSOLE_FORMS.mateMint);
618
+ const body = readForm(posted, CONSOLE_FORMS.leadMint);
619
619
  if (who.via !== "cookie")
620
- return refuse(response, who, 403, "the mate is a browser surface");
620
+ return refuse(response, who, 403, "the lead is a browser surface");
621
621
  const back = safeChatReturn(body.get("return"));
622
622
  const enabled = chatEnablement();
623
623
  if (!enabled.ok)
@@ -636,60 +636,60 @@ export function createChatHandlers(runtime) {
636
636
  const verified = token === "" ? verifyApproverStanding(store, who.name, who.session.generation, managedRepos()) : verifyApproverByPassword(store, who.name, token, managedRepos());
637
637
  if (!verified.ok)
638
638
  return redirect(response, chatReturnWithSaid(back, token === "" ? "Your access changed. Sign in again to start chat." : "That password did not match."));
639
- startMateConversation(who, verified.who, enabled, ceilingUsd, body.get("follow") === "yes", now);
639
+ startLeadConversation(who, verified.who, enabled, ceilingUsd, body.get("follow") === "yes", now);
640
640
  return redirect(response, back);
641
641
  }
642
- async function mateFollow(ctx) {
642
+ async function leadFollow(ctx) {
643
643
  const { who, response, now, posted } = ctx;
644
- const body = readForm(posted, CONSOLE_FORMS.mateFollow);
644
+ const body = readForm(posted, CONSOLE_FORMS.leadFollow);
645
645
  if (who.via !== "cookie")
646
646
  return refuse(response, who, 403, "Open the conversation to change automatic updates.");
647
- const principal = matePrincipal(who), session = store.activeMateSession(who.name), thread = store.liveMateThreadFor(who.name);
647
+ const principal = leadPrincipal(who), session = store.activeLeadSession(who.name), thread = store.liveLeadThreadFor(who.name);
648
648
  if (!principal || !session || !thread || !configureLeadFollow(store, principal, session, thread, body.get("enabled") === "yes", now))
649
649
  return refuse(response, who, 409, "Conversation access changed. Start chat again.", "/chat");
650
650
  return redirect(response, safeChatReturn(body.get("return")));
651
651
  }
652
- async function mateEnd(ctx) {
652
+ async function leadEnd(ctx) {
653
653
  const { who, response, now, posted } = ctx;
654
- const body = readForm(posted, CONSOLE_FORMS.mateEnd);
654
+ const body = readForm(posted, CONSOLE_FORMS.leadEnd);
655
655
  if (who.via !== "cookie")
656
- return refuse(response, who, 403, "the mate is a browser surface");
656
+ return refuse(response, who, 403, "the lead is a browser surface");
657
657
  const back = safeChatReturn(body.get("return"));
658
658
  // Ending spend and forgetting the thread takes no password: any
659
659
  // approver may revoke (§1), and the thread is theirs to drop (ruling 11).
660
- store.failLiveMateTurnsFor(who.name, "ended", now);
661
- store.endMateSessionsFor(who.name, who.name, now);
662
- store.closeMateThreadsFor(who.name, now);
663
- mateSaid.delete(who.session.csrf);
660
+ store.failLiveLeadTurnsFor(who.name, "ended", now);
661
+ store.endLeadSessionsFor(who.name, who.name, now);
662
+ store.closeLeadThreadsFor(who.name, now);
663
+ leadSaid.delete(who.session.csrf);
664
664
  return redirect(response, back);
665
665
  }
666
- async function mateStop(ctx) {
666
+ async function leadStop(ctx) {
667
667
  const { who, response, now, posted } = ctx;
668
- const body = readForm(posted, CONSOLE_FORMS.mateStop);
668
+ const body = readForm(posted, CONSOLE_FORMS.leadStop);
669
669
  if (who.via !== "cookie")
670
- return refuse(response, who, 403, "the mate is a browser surface");
670
+ return refuse(response, who, 403, "the lead is a browser surface");
671
671
  const back = safeChatReturn(body.get("return"));
672
672
  const wanted = Number(body.get("turn") ?? "");
673
- const live = store.liveMateTurnFor(who.name);
673
+ const live = store.liveLeadTurnFor(who.name);
674
674
  if (!Number.isInteger(wanted) || live === null || live.id !== wanted) {
675
- noteMate(who.session.csrf, null, "that turn has already finished");
675
+ noteLead(who.session.csrf, null, "that turn has already finished");
676
676
  return redirect(response, chatReturnWithLatest(back));
677
677
  }
678
678
  // A stopped direct-API turn is conservatively charged its reserved
679
679
  // worst case: dispatch may already have happened. Membership turns
680
680
  // reserve zero. The conversation itself stays live.
681
- store.failLiveMateTurnsFor(who.name, "stopped", now);
682
- noteMate(who.session.csrf, live.id, "stopped — the conversation is still open");
681
+ store.failLiveLeadTurnsFor(who.name, "stopped", now);
682
+ noteLead(who.session.csrf, live.id, "stopped — the conversation is still open");
683
683
  return redirect(response, chatReturnWithLatest(back));
684
684
  }
685
685
  const chatProposalAct = (op) => async (ctx) => {
686
686
  const { url, who, request, response, now, posted } = ctx;
687
- const body = readForm(posted, CONSOLE_FORMS.mateProposal);
687
+ const body = readForm(posted, CONSOLE_FORMS.leadProposal);
688
688
  if (who.via !== "cookie")
689
- return refuse(response, who, 403, "the mate is a browser surface");
689
+ return refuse(response, who, 403, "the lead is a browser surface");
690
690
  const back = safeChatReturn(body.get("return"));
691
691
  const id = Number(url.pathname.split("/")[3]);
692
- const proposalRow = store.getMateProposal(id);
692
+ const proposalRow = store.getLeadProposal(id);
693
693
  const shared = proposalRow && store.handle.prepare('SELECT id FROM team_conversation WHERE thread=?').get(proposalRow.thread);
694
694
  const proposalTaskHref = (taskId) => taskChatHref(taskId) + (shared ? '&conversation=' + encodeURIComponent(String(shared['id'])) : '');
695
695
  let principal = null;
@@ -704,7 +704,7 @@ export function createChatHandlers(runtime) {
704
704
  }
705
705
  }
706
706
  else
707
- principal = matePrincipal(who);
707
+ principal = leadPrincipal(who);
708
708
  // Confirm in place (chat cards): the same door and checks, answered in
709
709
  // JSON so the conversation stays where it is; the card's refreshed
710
710
  // state carries the result. The secure review screen stays a form.
@@ -713,14 +713,14 @@ export function createChatHandlers(runtime) {
713
713
  if (principal === null)
714
714
  return cardJson ? cardAnswer(403, false, "Your approver standing changed. Sign in again.") : refuse(response, who, 403, "your approver standing changed — sign in again", back);
715
715
  if (op === "dismiss") {
716
- const dismissed = dismissMateProposal(store, principal, id, now);
716
+ const dismissed = dismissLeadProposal(store, principal, id, now);
717
717
  if (cardJson)
718
718
  return cardAnswer(dismissed ? 200 : 409, dismissed, dismissed ? "Dismissed." : "That card was already acted on.");
719
719
  if (!dismissed)
720
- noteMate(who.session.csrf, null, "that proposal was already acted on");
720
+ noteLead(who.session.csrf, null, "that proposal was already acted on");
721
721
  return redirect(response, chatReturnWithLatest(back));
722
722
  }
723
- const outcome = confirmMateProposal(store, principal, id, now, { chatProvider: teamChatProvider, confirm: body.get("confirm") === "yes", via: "web", evidenceRoot, ...(body.has("nonce") ? { actionReview: { nonce: body.get("nonce") ?? "", password: body.get("token") ?? "" } } : {}) });
723
+ const outcome = confirmLeadProposal(store, principal, id, now, { chatProvider: teamChatProvider, confirm: body.get("confirm") === "yes", via: "web", evidenceRoot, ...(body.has("nonce") ? { actionReview: { nonce: body.get("nonce") ?? "", password: body.get("token") ?? "" } } : {}) });
724
724
  if (cardJson) {
725
725
  if (!outcome.ok)
726
726
  return cardAnswer(outcome.reason === "standing" ? 403 : outcome.reason === "not-yours" ? 404 : 409, false, outcome.said);
@@ -737,7 +737,7 @@ export function createChatHandlers(runtime) {
737
737
  if (!outcome.ok && shared)
738
738
  return redirect(response, chatReturnWithSaid(back, outcome.said));
739
739
  if (!outcome.ok && outcome.reason === "needs-confirm")
740
- noteMate(who.session.csrf, null, outcome.said);
740
+ noteLead(who.session.csrf, null, outcome.said);
741
741
  // A confirmed task proposal leads to the task it actually created
742
742
  // (package 2): the lens over this same conversation, whose journey
743
743
  // and plan are the next step. The id is the door's recorded
@@ -748,7 +748,7 @@ export function createChatHandlers(runtime) {
748
748
  return redirect(response, `${proposalTaskHref(outcome.taskId)}#task-chat-live`);
749
749
  }
750
750
  if (outcome.ok && outcome.kind === "review" && outcome.taskId !== null) {
751
- const proposal = store.getMateProposal(id);
751
+ const proposal = store.getLeadProposal(id);
752
752
  // A revision confirmed here returns to its receipt: the newest
753
753
  // reply, whose confirmed card names the revision and links to the
754
754
  // plan. Landing at the top left that message under the phone's
@@ -758,7 +758,7 @@ export function createChatHandlers(runtime) {
758
758
  : `${proposalTaskHref(outcome.taskId)}&result=${Number(proposal.payload["run"])}#request-changes`);
759
759
  }
760
760
  if (outcome.ok && outcome.kind === "task_action" && outcome.taskId !== null) {
761
- const proposal = store.getMateProposal(id);
761
+ const proposal = store.getLeadProposal(id);
762
762
  if (proposal.payload["operation"] === "resume") {
763
763
  return armTaskResume(response, who, outcome.taskId, String(proposal.payload["run"]), "chat", now);
764
764
  }
@@ -773,7 +773,7 @@ export function createChatHandlers(runtime) {
773
773
  const body = readForm(posted, CONSOLE_FORMS.coordinatorProposal);
774
774
  if (who.via !== "cookie")
775
775
  return refuse(response, who, 403, "proposals are confirmed from the browser or the CLI");
776
- const principal = matePrincipal(who);
776
+ const principal = leadPrincipal(who);
777
777
  if (principal === null)
778
778
  return refuse(response, who, 403, "your approver standing changed — sign in again", "/chat");
779
779
  const id = Number(url.pathname.split("/")[2]);
@@ -835,34 +835,34 @@ export function createChatHandlers(runtime) {
835
835
  // A live mate session: the message is a mate turn — no password, the
836
836
  // session's ceremony already covered it (§1); the engine refuses on
837
837
  // its own terms and the thread shows why.
838
- const mateSession = who.role === "approver" ? store.activeMateSession(who.name) : null;
839
- if (mateSession !== null) {
838
+ const leadSession = who.role === "approver" ? store.activeLeadSession(who.name) : null;
839
+ if (leadSession !== null) {
840
840
  const requestId = body.get("request");
841
- if (requestId !== null && (body.getAll("request").length !== 1 || body.getAll("request-session").length !== 1 || body.get("request-session") !== String(mateSession.id))) {
841
+ if (requestId !== null && (body.getAll("request").length !== 1 || body.getAll("request-session").length !== 1 || body.get("request-session") !== String(leadSession.id))) {
842
842
  return said(409, "This conversation changed. Reload it before sending your message.");
843
843
  }
844
- const principal = matePrincipal(who);
844
+ const principal = leadPrincipal(who);
845
845
  if (principal === null)
846
846
  return wantsJson ? said(403, "your approver standing changed — sign in again") : refuse(response, who, 403, "your approver standing changed — sign in again", back);
847
847
  const message = (body.get("message") ?? "").trim();
848
- if (message === "" || message.length > MATE_MESSAGE_MAX_CHARS) {
849
- return said(400, `a message is 1 to ${MATE_MESSAGE_MAX_CHARS} characters`, mateSession.id);
848
+ if (message === "" || message.length > LEAD_MESSAGE_MAX_CHARS) {
849
+ return said(400, `a message is 1 to ${LEAD_MESSAGE_MAX_CHARS} characters`, leadSession.id);
850
850
  }
851
- const opened = store.openMateThread(who.name, principal.ceilingDigest, now, chatScopeOf(focusTask, chatProject));
851
+ const opened = store.openLeadThread(who.name, principal.ceilingDigest, now, chatScopeOf(focusTask, chatProject));
852
852
  const onProgress = beginLiveTurn(opened.thread.id);
853
- void runMateTurn({ store, who: principal, session: mateSession, thread: opened.thread, config: enabled.config, key: enabled.key, message, onProgress, channel: "console", ...(requestId === null ? {} : { requestId }), ...(focusTask === null && chatProject !== null ? { context: `Current project: ${projectName(chatProject)} (${chatProject}). Keep this conversation about that project unless the operator explicitly asks to broaden it; use it as the repo for project tools.` } : {}), ...(focusTask === null ? {} : { context: `Current task: ${focusTask.id}. Read it with get_task before answering or proposing changes. Read its currentExecution next and bind new actions to that exact execution. Never replace the target of a prior proposal with a newer revision. Keep this turn about that task unless the operator explicitly asks to broaden it.${resultContext}${modeContext}` }), fetcher: chatFetcher, ...(options.subscriptionChatRunner === undefined ? {} : { subscriptionRunner: options.subscriptionChatRunner }), clock, evidenceRoot })
853
+ void runLeadTurn({ store, who: principal, session: leadSession, thread: opened.thread, config: enabled.config, key: enabled.key, message, onProgress, channel: "console", ...(requestId === null ? {} : { requestId }), ...(focusTask === null && chatProject !== null ? { context: `Current project: ${projectName(chatProject)} (${chatProject}). Keep this conversation about that project unless the operator explicitly asks to broaden it; use it as the repo for project tools.` } : {}), ...(focusTask === null ? {} : { context: `Current task: ${focusTask.id}. Read it with get_task before answering or proposing changes. Read its currentExecution next and bind new actions to that exact execution. Never replace the target of a prior proposal with a newer revision. Keep this turn about that task unless the operator explicitly asks to broaden it.${resultContext}${modeContext}` }), fetcher: chatFetcher, ...(options.subscriptionChatRunner === undefined ? {} : { subscriptionRunner: options.subscriptionChatRunner }), clock, evidenceRoot })
854
854
  .then(outcome => {
855
855
  // A turn that saved its outcome in the thread (one stopped at its deadline) is said there, once.
856
856
  if (!outcome.ok && !("saved" in outcome && outcome.saved === true))
857
- noteMate(who.session.csrf, "turn" in outcome ? outcome.turn : null, outcome.message);
857
+ noteLead(who.session.csrf, "turn" in outcome ? outcome.turn : null, outcome.message);
858
858
  endLiveTurn(opened.thread.id, outcome.ok);
859
859
  })
860
- .catch(() => { noteMate(who.session.csrf, null, "the turn failed unexpectedly"); endLiveTurn(opened.thread.id, false); });
860
+ .catch(() => { noteLead(who.session.csrf, null, "the turn failed unexpectedly"); endLiveTurn(opened.thread.id, false); });
861
861
  // Accepted for the engine, not received: the receipt is written by
862
862
  // the turn's own admission, and the status poll is where the page
863
863
  // learns of it — the same road as after a native send.
864
864
  if (wantsJson)
865
- return respond(response, 202, "application/json", JSON.stringify({ ok: true, session: mateSession.id, task: focusTask?.id ?? "", ...(chatProject === null ? {} : { project: chatProject }), request: requestId }));
865
+ return respond(response, 202, "application/json", JSON.stringify({ ok: true, session: leadSession.id, task: focusTask?.id ?? "", ...(chatProject === null ? {} : { project: chatProject }), request: requestId }));
866
866
  return redirect(response, chatReturnWithLatest(back));
867
867
  }
868
868
  if (wantsJson)
@@ -1089,22 +1089,22 @@ export function createChatHandlers(runtime) {
1089
1089
  liveTurns.delete(thread); }, 60_000);
1090
1090
  live.expiry.unref();
1091
1091
  }
1092
- function noteMate(csrf, turn, message) {
1093
- if (mateSaid.size >= 500) {
1094
- const oldest = mateSaid.keys().next().value;
1092
+ function noteLead(csrf, turn, message) {
1093
+ if (leadSaid.size >= 500) {
1094
+ const oldest = leadSaid.keys().next().value;
1095
1095
  if (oldest !== undefined)
1096
- mateSaid.delete(oldest);
1096
+ leadSaid.delete(oldest);
1097
1097
  }
1098
- mateSaid.set(csrf, { turn, message });
1098
+ leadSaid.set(csrf, { turn, message });
1099
1099
  }
1100
- function takeMateNote(csrf, liveSession) {
1101
- const noted = mateSaid.get(csrf);
1100
+ function takeLeadNote(csrf, liveSession) {
1101
+ const noted = leadSaid.get(csrf);
1102
1102
  if (noted === undefined)
1103
1103
  return null;
1104
- mateSaid.delete(csrf);
1104
+ leadSaid.delete(csrf);
1105
1105
  if (noted.turn === null)
1106
1106
  return noted.message;
1107
- const turn = store.getMateTurn(noted.turn);
1107
+ const turn = store.getLeadTurn(noted.turn);
1108
1108
  return turn !== null && turn.session === liveSession ? noted.message : null;
1109
1109
  }
1110
1110
  /** A finished demo exchange's result, read back from the evidence its run actually stored. */
@@ -1312,7 +1312,7 @@ export function createChatHandlers(runtime) {
1312
1312
  return href === null ? null : { label: one.primaryAction.label, href };
1313
1313
  })(),
1314
1314
  }));
1315
- const windows = who.via === "cookie" && store.isInstanceOperator(who.name) ? limitsView(store.providerLimits(), [], { project: projectName, teammate: id => `Teammate ${id}` }, now) : null;
1315
+ const windows = who.via === "cookie" && store.isInstanceOperator(who.name) ? limitsView(store.providerLimits(), [], { project: projectName, subagent: id => `Subagent ${id}` }, now) : null;
1316
1316
  // One line: what this person's lead is doing now and when it last acted.
1317
1317
  const activity = leadActivity(store, who.name);
1318
1318
  const leadTask = activity?.taskId == null ? null : store.lookupRef(activity.taskId);
@@ -1382,10 +1382,10 @@ export function createChatHandlers(runtime) {
1382
1382
  "push.remove": pushRemove,
1383
1383
  "onboarding.phone-dismiss": phoneDismiss,
1384
1384
  "chat.config": chatConfig,
1385
- "chat.mate-mint": mateMint,
1386
- "chat.mate-follow": mateFollow,
1387
- "chat.mate-end": mateEnd,
1388
- "chat.mate-stop": mateStop,
1385
+ "chat.mate-mint": leadMint,
1386
+ "chat.mate-follow": leadFollow,
1387
+ "chat.mate-end": leadEnd,
1388
+ "chat.mate-stop": leadStop,
1389
1389
  "chat.proposal.confirm": chatProposalAct("confirm"),
1390
1390
  "chat.proposal.dismiss": chatProposalAct("dismiss"),
1391
1391
  "coordinator.proposal.confirm": coordinatorProposalAct("confirm"),
@@ -1394,5 +1394,5 @@ export function createChatHandlers(runtime) {
1394
1394
  "chat.file": chatFile,
1395
1395
  "chat.ack-send": chatAckSend,
1396
1396
  });
1397
- return { registrations, chatFirstRun, phoneCard, chatProjectOf, beginLiveTurn, endLiveTurn, noteMate, takeMateNote, demoResultView, storeChatKey, forgetChatKey, sweepChatDrafts, runChatTurn, chatHomeOf, firstTasksFor, approverCandidateCount, evictOldestCandidate };
1397
+ return { registrations, chatFirstRun, phoneCard, chatProjectOf, beginLiveTurn, endLiveTurn, noteLead, takeLeadNote, demoResultView, storeChatKey, forgetChatKey, sweepChatDrafts, runChatTurn, chatHomeOf, firstTasksFor, approverCandidateCount, evictOldestCandidate };
1398
1398
  }
@@ -41,7 +41,7 @@ import {} from "../store.js";
41
41
  import { styleAsset } from "../style-asset.js";
42
42
  import { spendLine } from "../summary.js";
43
43
  import { TASK_STATUS_CSS } from "../task-status.js";
44
- import { TEAMMATE_CSS } from "../teammates-ui.js";
44
+ import { SUBAGENT_CSS } from "../subagents-ui.js";
45
45
  import { UPDATES_CSS } from "../toolroll-update-ui.js";
46
46
  import { TOOLS_CSS } from "../tools-ui.js";
47
47
  import { TRANSITIONS_CSS } from "../transitions-recipes.js";
@@ -144,7 +144,7 @@ export const INBOX_TABS_CSS = '.inbox-tabs{display:inline-flex;gap:2px;max-width
144
144
  '.inbox-ask{margin:18px 0 0}.inbox-ask>h2{display:flex;align-items:baseline;gap:8px;margin:0 0 4px;font-size:15px;font-weight:600}.inbox-ask>h2 .count{font:500 12px var(--so-mono,ui-monospace,monospace);font-variant-numeric:tabular-nums;color:var(--so-muted)}.inbox-ask h3{font-size:13px;font-weight:600;margin:12px 0 4px}' +
145
145
  '.inbox-unread{display:none;position:absolute;top:4px;right:3px;width:6px;height:6px;border-radius:50%;background:var(--so-signal)}' +
146
146
  '@media (max-width:760px){.inbox-tabs{display:flex;width:100%}.inbox-tabs a{flex:1;justify-content:center;min-height:44px;padding:0 6px}.inbox-unread{display:block}}';
147
- export const WORKSPACE_STYLE = styleAsset(STYLE + BRAND_MARK_CSS + INBOX_TABS_CSS + APPROVAL_RULES_CSS + SPEND_CSS + RETENTION_CSS + STORAGE_CSS + UPDATES_CSS + LIMITS_CSS + MONITORING_CSS + INTEGRATIONS_CSS + BACKUP_CSS + EXPORT_CSS + PROJECT_DELETE_CSS + POLICY_CSS + EVIDENCE_PACK_CSS + THEME_CONTROLS_CSS + CODING_CSS + CODING_SHIPPING_CSS + RECIPE_CSS + SKILLS_CSS + TOOLS_CSS + FLOWS_CSS + TEAMMATE_CSS + KITS_CSS + STARTERS_CSS + GALLERY_CSS + SSO_CSS + CREDENTIALS_CSS + REQUEST_LIMITS_CSS + PEOPLE_AUDIT_CSS + KNOWLEDGE_CSS + MODELS_CSS + CHAT_POLISH_CSS + TRANSITIONS_CSS + WORKSPACE_MOTION_CSS + ASSIGNMENT_CSS + TASK_STATUS_CSS + LEAD_CONTEXT_CSS + PULL_REQUEST_SETTINGS_CSS + CHECK_SETTINGS_CSS + DISCLOSURE_CSS + '.learning{min-width:0;overflow-wrap:anywhere}.learning .card{min-width:0}.learning code,.learning blockquote,.learning pre{white-space:pre-wrap;overflow-wrap:anywhere;word-break:break-word}.learning button,.learning summary,.learning .button-link{min-height:44px}.learning button{white-space:nowrap}.learning summary{padding:12px 0;cursor:pointer}.learning form{margin:12px 0}.learning select{max-width:100%}.learning blockquote{margin:8px 0}.learning ul{padding-left:20px}');
147
+ export const WORKSPACE_STYLE = styleAsset(STYLE + BRAND_MARK_CSS + INBOX_TABS_CSS + APPROVAL_RULES_CSS + SPEND_CSS + RETENTION_CSS + STORAGE_CSS + UPDATES_CSS + LIMITS_CSS + MONITORING_CSS + INTEGRATIONS_CSS + BACKUP_CSS + EXPORT_CSS + PROJECT_DELETE_CSS + POLICY_CSS + EVIDENCE_PACK_CSS + THEME_CONTROLS_CSS + CODING_CSS + CODING_SHIPPING_CSS + RECIPE_CSS + SKILLS_CSS + TOOLS_CSS + FLOWS_CSS + SUBAGENT_CSS + KITS_CSS + STARTERS_CSS + GALLERY_CSS + SSO_CSS + CREDENTIALS_CSS + REQUEST_LIMITS_CSS + PEOPLE_AUDIT_CSS + KNOWLEDGE_CSS + MODELS_CSS + CHAT_POLISH_CSS + TRANSITIONS_CSS + WORKSPACE_MOTION_CSS + ASSIGNMENT_CSS + TASK_STATUS_CSS + LEAD_CONTEXT_CSS + PULL_REQUEST_SETTINGS_CSS + CHECK_SETTINGS_CSS + DISCLOSURE_CSS + '.learning{min-width:0;overflow-wrap:anywhere}.learning .card{min-width:0}.learning code,.learning blockquote,.learning pre{white-space:pre-wrap;overflow-wrap:anywhere;word-break:break-word}.learning button,.learning summary,.learning .button-link{min-height:44px}.learning button{white-space:nowrap}.learning summary{padding:12px 0;cursor:pointer}.learning form{margin:12px 0}.learning select{max-width:100%}.learning blockquote{margin:8px 0}.learning ul{padding-left:20px}');
148
148
  /**
149
149
  * A server-rendered region that stays current: when the page's live stream
150
150
  * says something changed (GET /live, live-bus.ts), fetch this page's own
@@ -675,7 +675,7 @@ export const NAV_ICONS = {
675
675
  */
676
676
  export function workToolRows(scoped = false, _offersChat = false, offersCode = false) {
677
677
  const rows = [
678
- ...(offersCode ? [{ key: "code", href: "/code", label: "Coding sessions", hint: "Direct coding sessions and their saved results" }] : []),
678
+ // D5: coding sessions are deprecated; /code still opens this release, but nothing links to it.
679
679
  { key: "inbox", href: "/inbox", label: "Inbox", hint: "questions, approvals, and repairs to act on" },
680
680
  { key: "board", href: "/board", label: "Board", hint: "lanes by state, with the order view" },
681
681
  { key: "tasks", href: "/tasks", label: "Task list", hint: "everything, filterable by state" },