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
package/dist/chat-ask.js CHANGED
@@ -1,8 +1,8 @@
1
1
  import { chatHash, partContent, savedChatPart } from "./chat-delivery-state.js";
2
- import { MATE_ASK_OTHER } from "./store.js";
2
+ import { LEAD_ASK_OTHER } from "./store.js";
3
3
  /** The question as text with its options, where no buttons are drawn. */
4
4
  export function chatAskText(ask) {
5
- return [ask.question, ...ask.options.map(one => `• ${one}`), `• ${MATE_ASK_OTHER}: say it in your own words`].join("\n");
5
+ return [ask.question, ...ask.options.map(one => `• ${one}`), `• ${LEAD_ASK_OTHER}: say it in your own words`].join("\n");
6
6
  }
7
7
  /** The live buttons on one part, in the order they were minted: the options, then "Something else". None when the part
8
8
  * is not in its owner's own chat with Toolroll (a room), where a tap could not be their message. */
@@ -16,7 +16,7 @@ export function chatAskButtons(state, part, now) {
16
16
  const options = content.ask?.options ?? [];
17
17
  return state.prepare("SELECT token,choice FROM chat_ask_action WHERE provider=:provider AND part=? AND consumed IS NULL AND expires>? ORDER BY rowid").all(part, now.toISOString())
18
18
  .map(one => {
19
- const label = one.choice === null ? MATE_ASK_OTHER : options[Number(one.choice)] ?? null;
19
+ const label = one.choice === null ? LEAD_ASK_OTHER : options[Number(one.choice)] ?? null;
20
20
  return label === null ? null : { token: String(one.token), label: label.slice(0, 75), words: one.choice === null };
21
21
  })
22
22
  .filter((one) => one !== null);
@@ -51,12 +51,12 @@ export function applyChatAskTap(options, event, binding, token, now) {
51
51
  }
52
52
  const turn = Number(action["turn"]), part = Number(action["part"]);
53
53
  // Only the person the lead asked answers it: anyone else's tap changes nothing and says nothing.
54
- const asker = store.getMateTurn(turn)?.approver;
54
+ const asker = store.getLeadTurn(turn)?.approver;
55
55
  if (asker !== undefined && asker !== binding.approver) {
56
56
  state.finish(event.id);
57
57
  return true;
58
58
  }
59
- const found = asker === undefined ? { state: "expired" } : store.mateAskState(turn, now);
59
+ const found = asker === undefined ? { state: "expired" } : store.leadAskState(turn, now);
60
60
  const expired = found.state === "expired" || String(action["expires"]) <= now.toISOString();
61
61
  // A question already settled was shown so once (or is being): a later tap says nothing more.
62
62
  if (action["consumed"] !== null) {
@@ -1,12 +1,12 @@
1
- import type { DoorOutcome } from "./mate-doors.js";
1
+ import type { DoorOutcome } from "./lead-doors.js";
2
2
  import { type VerifiedApprover } from "./principal.js";
3
- import type { ChatConfig, MateProposal, MateSession, MateThread, Store, SubscriptionChatProviderId } from "./store.js";
3
+ import type { ChatConfig, LeadProposal, LeadSession, LeadThread, Store, SubscriptionChatProviderId } from "./store.js";
4
4
  export declare function channelRepos(store: Store, approver: string, registry: readonly string[]): string[];
5
- export type ResolvedMate = {
5
+ export type ResolvedLead = {
6
6
  ok: true;
7
7
  who: VerifiedApprover;
8
- session: MateSession;
9
- thread: MateThread;
8
+ session: LeadSession;
9
+ thread: LeadThread;
10
10
  config: ChatConfig & {
11
11
  provider: SubscriptionChatProviderId;
12
12
  };
@@ -25,10 +25,10 @@ export type ResolvedMate = {
25
25
  * always gets an answer — except while a turn is still running there,
26
26
  * which is "busy": the message waits its turn instead of ending it.
27
27
  */
28
- export declare function resolveChannelMate(store: Store, binding: {
28
+ export declare function resolveChannelLead(store: Store, binding: {
29
29
  approver: string;
30
30
  approverGeneration: number;
31
- }, repos: readonly string[], now: Date): ResolvedMate;
31
+ }, repos: readonly string[], now: Date): ResolvedLead;
32
32
  /** A phone exchange about one task (a reply to that task's or result's
33
33
  * message) stays in the lead conversation, and is kept in the task's own
34
34
  * chat as well, so the task shows everything asked of it wherever it was
@@ -65,10 +65,10 @@ export declare function taskInCeiling(store: Store, taskId: string, repos: reado
65
65
  export type ChatChannelName = "telegram" | "slack" | "discord" | "teams";
66
66
  /** The card text once its challenge is armed: an irreversible answer or a
67
67
  * challenge action, in the same words on every channel. */
68
- export declare function armedCardText(proposal: MateProposal, previewText: string): string;
68
+ export declare function armedCardText(proposal: LeadProposal, previewText: string): string;
69
69
  /** The "yes" button's label for an armed card. */
70
- export declare function armedYesLabel(proposal: MateProposal): string;
71
- export declare function proposalLink(store: Store, proposal: MateProposal, repos: readonly string[], channel?: ChatChannelName | null): PhoneLink | null;
70
+ export declare function armedYesLabel(proposal: LeadProposal): string;
71
+ export declare function proposalLink(store: Store, proposal: LeadProposal, repos: readonly string[], channel?: ChatChannelName | null): PhoneLink | null;
72
72
  /**
73
73
  * The link a CONFIRMED card carries: the exact recorded task the outcome
74
74
  * names, where its remaining step lives — the approval control while the
@@ -76,17 +76,17 @@ export declare function proposalLink(store: Store, proposal: MateProposal, repos
76
76
  * automatic approval, a staged resume). Kinds whose outcome is complete
77
77
  * carry none.
78
78
  */
79
- export declare function confirmedLink(store: Store, outcome: DoorOutcome, proposal: MateProposal, repos: readonly string[]): PhoneLink | null;
79
+ export declare function confirmedLink(store: Store, outcome: DoorOutcome, proposal: LeadProposal, repos: readonly string[]): PhoneLink | null;
80
80
  /** The card: plain words for what confirming does, the exact terms it binds to, and whether a button belongs on it. */
81
- export declare function proposalPreview(store: Store, proposal: MateProposal, repos: readonly string[], channel?: ChatChannelName | null): {
81
+ export declare function proposalPreview(store: Store, proposal: LeadProposal, repos: readonly string[], channel?: ChatChannelName | null): {
82
82
  text: string;
83
83
  buttons: boolean;
84
84
  };
85
85
  /** The card after it resolves, from the recorded outcome — the same words every surface shows. */
86
- export declare function proposalOutcomeText(proposal: MateProposal): string;
86
+ export declare function proposalOutcomeText(proposal: LeadProposal): string;
87
87
  export type ParitySupport = "direct" | "handoff" | "missing";
88
88
  /**
89
- * One row per mate tool: how the paired phone reaches it. `direct` means
89
+ * One row per lead tool: how the paired phone reaches it. `direct` means
90
90
  * the same engine tool or the same confirm door runs from Telegram;
91
91
  * `handoff` means the phone shows where the existing authenticated
92
92
  * control lives and does nothing itself; `missing` means no phone path
@@ -108,4 +108,4 @@ export declare function handoffCardText(text: string, note: string | null): stri
108
108
  * the console on the computer when no link exists). A scope approved under
109
109
  * a signed mode asks for nothing more. A staged resume says what remains.
110
110
  */
111
- export declare function confirmedCardText(store: Store, outcome: DoorOutcome, proposal: MateProposal, note: string | null): string;
111
+ export declare function confirmedCardText(store: Store, outcome: DoorOutcome, proposal: LeadProposal, note: string | null): string;
@@ -3,8 +3,8 @@ import { FLOW_HREF } from "./flow-engine.js";
3
3
  import { createHash } from "node:crypto";
4
4
  import { sharedActionPayload, sharedActionNeedsReview, sharedActionReviewPath, sharedActionAllowsChallenge, CHAT_ACTIONS, } from "./chat-actions.js";
5
5
  import { isDirectChatProvider, subscriptionCredentialKey } from "./converse.js";
6
- import { MATE_MESSAGE_MAX_CHARS } from "./mate.js";
7
- import { MATE_TOOL_SCHEMAS } from "./mate-tools.js";
6
+ import { LEAD_MESSAGE_MAX_CHARS } from "./lead.js";
7
+ import { LEAD_TOOL_SCHEMAS } from "./lead-tools.js";
8
8
  import { verifyApproverStanding } from "./principal.js";
9
9
  import { canonicalProject } from "./project.js";
10
10
  import { phoneText, projectLabel } from "./telegram-status.js";
@@ -32,7 +32,7 @@ export function channelRepos(store, approver, registry) {
32
32
  * always gets an answer — except while a turn is still running there,
33
33
  * which is "busy": the message waits its turn instead of ending it.
34
34
  */
35
- export function resolveChannelMate(store, binding, repos, now) {
35
+ export function resolveChannelLead(store, binding, repos, now) {
36
36
  const config = store.getChatConfig();
37
37
  if (config === null) {
38
38
  return {
@@ -60,21 +60,21 @@ export function resolveChannelMate(store, binding, repos, now) {
60
60
  return { ok: false, reason: "unpaired", said: null };
61
61
  const who = verified.who;
62
62
  const credentialKey = subscriptionCredentialKey(config.provider);
63
- let session = store.activeMateSession(who.name);
64
- const liveThread = store.liveMateThreadFor(who.name);
63
+ let session = store.activeLeadSession(who.name);
64
+ const liveThread = store.liveLeadThreadFor(who.name);
65
65
  const sessionMismatch = session !== null &&
66
66
  (session.approverGeneration !== who.generation ||
67
67
  session.credentialKey !== credentialKey ||
68
68
  session.ceilingDigest !== who.ceilingDigest);
69
69
  const threadMismatch = liveThread !== null && liveThread.ceilingDigest !== who.ceilingDigest;
70
70
  // Replacing either would end a turn still running on the computer.
71
- if ((sessionMismatch || threadMismatch) && store.liveMateTurnFor(who.name) !== null)
71
+ if ((sessionMismatch || threadMismatch) && store.liveLeadTurnFor(who.name) !== null)
72
72
  return { ok: false, reason: "busy", said: null };
73
73
  if (session === null || sessionMismatch) {
74
74
  const termsDigest = createHash("sha256")
75
75
  .update(`0\n${who.ceilingDigest}`)
76
76
  .digest("hex");
77
- const id = store.mintMateSession({
77
+ const id = store.mintLeadSession({
78
78
  approver: who.name,
79
79
  approverGeneration: who.generation,
80
80
  credentialKey,
@@ -82,13 +82,13 @@ export function resolveChannelMate(store, binding, repos, now) {
82
82
  ceilingDigest: who.ceilingDigest,
83
83
  termsDigest,
84
84
  }, now);
85
- session = store.getMateSession(id);
85
+ session = store.getLeadSession(id);
86
86
  if (session === null)
87
87
  return { ok: false, reason: "no-session", said: couldNotAnswerText("a new chat session couldn't be started") };
88
88
  }
89
- // openMateThread closes a thread kept under another ceiling.
89
+ // openLeadThread closes a thread kept under another ceiling.
90
90
  const thread = threadMismatch || liveThread === null
91
- ? store.openMateThread(who.name, who.ceilingDigest, now).thread
91
+ ? store.openLeadThread(who.name, who.ceilingDigest, now).thread
92
92
  : liveThread;
93
93
  return {
94
94
  ok: true,
@@ -107,9 +107,9 @@ export function mirrorToTaskChat(store, who, taskId, surface, message, reply, no
107
107
  const root = store.taskFamilyOf(taskId, who.repos, false)?.root.id ?? taskId;
108
108
  if (!taskInCeiling(store, root, who.repos))
109
109
  return;
110
- const thread = store.openMateThread(who.name, who.ceilingDigest, now, { kind: "task", key: root }).thread;
111
- store.appendMateMessage({ thread: thread.id, turn: null, role: "operator", text: `From ${surface}: ${message}` }, now);
112
- store.appendMateMessage({ thread: thread.id, turn: null, role: "assistant", text: reply }, now);
110
+ const thread = store.openLeadThread(who.name, who.ceilingDigest, now, { kind: "task", key: root }).thread;
111
+ store.appendLeadMessage({ thread: thread.id, turn: null, role: "operator", text: `From ${surface}: ${message}` }, now);
112
+ store.appendLeadMessage({ thread: thread.id, turn: null, role: "assistant", text: reply }, now);
113
113
  }
114
114
  catch {
115
115
  // The task chat is a copy; the phone's own conversation already has it.
@@ -120,7 +120,7 @@ export function couldNotAnswerText(reason) {
120
120
  return `I couldn't answer that just now: ${reason.replace(/\.$/, "")}. Ask again, or open the console.`;
121
121
  }
122
122
  export function tooLongText(length) {
123
- return `That message is ${length.toLocaleString("en-US")} characters; chat takes up to ${MATE_MESSAGE_MAX_CHARS.toLocaleString("en-US")}. Nothing was sent to the assistant. Send it in shorter parts, or say which part matters most.`;
123
+ return `That message is ${length.toLocaleString("en-US")} characters; chat takes up to ${LEAD_MESSAGE_MAX_CHARS.toLocaleString("en-US")}. Nothing was sent to the assistant. Send it in shorter parts, or say which part matters most.`;
124
124
  }
125
125
  /** A reply to a message about several tasks is never guessed: it names them and says how to choose. */
126
126
  export function whichTaskText(titles) {
@@ -459,7 +459,7 @@ export function proposalOutcomeText(proposal) {
459
459
  }
460
460
  }
461
461
  /**
462
- * One row per mate tool: how the paired phone reaches it. `direct` means
462
+ * One row per lead tool: how the paired phone reaches it. `direct` means
463
463
  * the same engine tool or the same confirm door runs from Telegram;
464
464
  * `handoff` means the phone shows where the existing authenticated
465
465
  * control lives and does nothing itself; `missing` means no phone path
@@ -518,14 +518,14 @@ export const CHAT_ACTION_PARITY = {
518
518
  get_project_tools: { support: "direct", how: "Read during a turn: a project's tools, the common tools list and servers found on the computer. Adding one is a secure-review card; secrets are set only on the console's Tools page.", gap: null },
519
519
  get_task_conversation: { support: "direct", how: "Read during a turn: what the person and the lead said in one task's own chat, including what was confirmed there.", gap: null },
520
520
  get_flows: { support: "direct", how: "Read during a turn: a person's flows, each one's steps in order and its cards — where each card is, what it waits on, whether it needs them, its owner and its discussion.", gap: null },
521
- get_teammates: { support: "direct", how: "Read during a turn: the project's AI teammates, their soul files, the zones they work, what they did today and the questions they're waiting on.", gap: null },
522
- propose_teammate: { support: "direct", how: "Adds a teammate from a template or a soul file, changes one section of its soul file (or the whole short file), pauses, resumes or removes it, passes it a note, or answers its question for the person asked, through the shared confirm door.", gap: "Soul files longer than one message are edited on the console's teammate page; the phone changes one section at a time." },
521
+ get_subagents: { support: "direct", how: "Read during a turn: the project's subagents, their soul files, the zones they work, what they did today and the questions they're waiting on.", gap: null },
522
+ propose_subagent: { support: "direct", how: "Asks a subagent to do something for you (the lead delegates; its answer comes back here), adds one from a template or a soul file, changes one section of its soul file (or the whole short file), pauses, resumes or removes it, passes it a note, or answers its question for the person asked, through the shared confirm door.", gap: "Soul files longer than one message are edited on the console's subagent page; the phone changes one section at a time." },
523
523
  get_flow_insights: { support: "direct", how: "Read during a turn: where each flow's cards pass, fail or are sent back, how long they wait, how its scripts did, and a run's log.", gap: null },
524
524
  propose_flow: { support: "direct", how: "Creates or changes a flow, adds, moves, approves, sends back or cancels its cards, comments on them (@name pings that person), sets their owner, follows them, saves the project's scripts, and adds, pauses or removes its triggers, through the shared confirm door; a long drawing opens the secure review. Work a card files is an ordinary task under the usual approvals.", gap: "The flow canvas itself is on the console; the phone confirms cards but draws nothing. Webhook addresses and the Linear key are set on the console's Triggers panel." },
525
525
  commit_to: { support: "direct", how: "Records what the lead promised to follow up on (a task, attempt, check or time; 7 days at most). The follow pass says one line when it is met, in the shared conversation and on the chat the promise was made on.", gap: null },
526
526
  release_commitment: { support: "direct", how: "Stops following up on one of the person's open promises, with the reason.", gap: null },
527
527
  remember: { support: "direct", how: "Proposes a correction or lasting preference as a decision, project instruction or about-you card, through the shared confirm door; an about-you card shows the line it replaces.", gap: null },
528
- get_person: { support: "direct", how: "Read during a turn: one person, AI teammate or team chat in full, with their open tasks.", gap: null },
528
+ get_person: { support: "direct", how: "Read during a turn: one person, subagent or team chat in full, with their open tasks.", gap: null },
529
529
  get_integrations: { support: "direct", how: "Read during a turn: which chat apps, email, GitHub, project tools and monitoring are Connected, Not set up or Broken, as Settings → Integrations shows them.", gap: null },
530
530
  get_capabilities: { support: "direct", how: "Read during a turn: which agents are signed in, which workers are online, which tools and skills work, which integrations are connected and what checks the project runs, each with its next step and settings link.", gap: null },
531
531
  ask_owner: { support: "direct", how: "One question with 2-4 options and Something else, drawn as buttons under the reply; the tapped option is sent as the person's next message.", gap: null },
@@ -568,7 +568,7 @@ export const CHAT_ACTION_PARITY = {
568
568
  },
569
569
  propose_task: {
570
570
  support: "direct",
571
- how: "Card with Confirm/Dismiss through confirmMateProposal (filed as a mate proposal, via telegram). The confirmed card links Review & start for the filed task while its scope waits; under a signed automatic mode it says the scope is approved and links the task.",
571
+ how: "Card with Confirm/Dismiss through confirmLeadProposal (filed as a lead proposal, via telegram). The confirmed card links Review & start for the filed task while its scope waits; under a signed automatic mode it says the scope is approved and links the task.",
572
572
  gap: "Under manual approval the password step happens in the console, reached from the card's button.",
573
573
  },
574
574
  propose_scope: {
@@ -634,7 +634,7 @@ export const CHAT_ACTION_PARITY = {
634
634
  };
635
635
  /** Every tool the model can call, mapped; a new tool must name its phone road. */
636
636
  export function parityGaps() {
637
- return MATE_TOOL_SCHEMAS.map((tool) => tool.name).filter((name) => CHAT_ACTION_PARITY[name] === undefined);
637
+ return LEAD_TOOL_SCHEMAS.map((tool) => tool.name).filter((name) => CHAT_ACTION_PARITY[name] === undefined);
638
638
  }
639
639
  export function handoffCardText(text, note) {
640
640
  return note === null ? text : `${text}\n\n${note}`;
@@ -38,10 +38,6 @@ export declare const CHAT_CONTROLS: {
38
38
  readonly label: "Manage projects";
39
39
  readonly href: "/projects";
40
40
  };
41
- readonly code: {
42
- readonly label: "Open coding workspace";
43
- readonly href: "/code";
44
- };
45
41
  readonly flows: {
46
42
  readonly label: "Open flows";
47
43
  readonly href: "/flows";
@@ -11,7 +11,6 @@ export const CHAT_CONTROLS = {
11
11
  acceptance: { label: "Inspect result", target: "task" },
12
12
  publish: { label: "Review publication", target: "task" },
13
13
  projects: { label: "Manage projects", href: "/projects" },
14
- code: { label: "Open coding workspace", href: "/code" },
15
14
  flows: { label: "Open flows", href: "/flows" },
16
15
  recipes: { label: "Browse workflows", href: "/recipes" },
17
16
  workers: { label: "Manage workers", href: "/fleet" },
package/dist/chat-core.js CHANGED
@@ -183,7 +183,7 @@ export class ChatCore {
183
183
  const thread = this.db.prepare("SELECT thread FROM team_conversation WHERE id = ?").get(args.conversation);
184
184
  if (thread === undefined)
185
185
  throw new Error("no such team conversation");
186
- const cursor = Number(this.db.prepare("SELECT COALESCE(MAX(id), 0) AS n FROM mate_message WHERE thread = ?").get(Number(thread["thread"]))?.["n"] ?? 0);
186
+ const cursor = Number(this.db.prepare("SELECT COALESCE(MAX(id), 0) AS n FROM lead_message WHERE thread = ?").get(Number(thread["thread"]))?.["n"] ?? 0);
187
187
  this.prepare("INSERT INTO chat_room (provider, id, installation, chat, kind, conversation, binding, bound_by, bound, cursor) VALUES (:provider, ?, ?, ?, ?, ?, ?, ?, ?, ?)")
188
188
  .run(this.nextId("chat_room"), args.installation, args.chat, args.kind, args.conversation, args.binding, args.by, stamp, cursor);
189
189
  return { ok: true, room: this.room(args.installation, args.chat) };
@@ -1,6 +1,6 @@
1
1
  /** Durable chat receipts on the shared chat core: what each app's chats sent us and what we plan to send them. */
2
2
  import { randomBytes } from "node:crypto";
3
- import { MATE_ASK_TTL_MS } from "./store.js";
3
+ import { LEAD_ASK_TTL_MS } from "./store.js";
4
4
  import { ChatCore, chatHash } from "./chat-core.js";
5
5
  import { ChatMessages } from "./chat-messages.js";
6
6
  import { readChatPart, savedChatEventBody, savedChatPart } from "./contracts/chat-content.js";
@@ -80,7 +80,7 @@ export class ChatState extends ChatMessages {
80
80
  if (part.ask)
81
81
  for (const choice of [...part.ask.options.map((_, index) => index), null])
82
82
  this.prepare("INSERT INTO chat_ask_action (provider, token, part, turn, choice, expires) VALUES (:provider, ?, ?, ?, ?, ?)")
83
- .run(token(), id, part.ask.turn, choice, new Date(now.getTime() + MATE_ASK_TTL_MS).toISOString());
83
+ .run(token(), id, part.ask.turn, choice, new Date(now.getTime() + LEAD_ASK_TTL_MS).toISOString());
84
84
  if (part.choose)
85
85
  for (const one of part.choose.options)
86
86
  this.prepare("INSERT INTO chat_flow_choice (provider, token, part, card, entry, choice, label, created, expires) VALUES (:provider, ?, ?, ?, ?, ?, ?, ?, ?)")
@@ -1,7 +1,7 @@
1
1
  import { type WarmHooks } from "./chat-warmth.js";
2
2
  import { ChatState, type ChatBinding, type ChatContent, type ChatEvent, type ChatIdentity } from "./chat-delivery-state.js";
3
3
  import { type Store } from "./store.js";
4
- import type { SubscriptionMateRunner } from "./subscription-chat.js";
4
+ import type { SubscriptionLeadRunner } from "./subscription-chat.js";
5
5
  import { type ChatMerge } from "./chat-decide.js";
6
6
  export declare const chatObject: (v: unknown) => Record<string, unknown>;
7
7
  export type ChatDeliveryOptions = {
@@ -15,7 +15,7 @@ export type ChatDeliveryOptions = {
15
15
  evidenceRoot: string;
16
16
  current: () => boolean;
17
17
  origin: () => string | null;
18
- subscriptionRunner?: SubscriptionMateRunner;
18
+ subscriptionRunner?: SubscriptionLeadRunner;
19
19
  canNotify?: () => boolean;
20
20
  clock?: () => Date;
21
21
  partSize?: number;
@@ -1,11 +1,11 @@
1
1
  /** Shared saved replies, explicit confirmations and progress for private chat transports. */
2
- import { answerChatQuestionPrompt, applyChatQuestionTap, questionParts } from "./teammate-question.js";
2
+ import { answerChatQuestionPrompt, applyChatQuestionTap, questionParts } from "./subagent-question.js";
3
3
  import { applyChatAskTap, chatAskText } from "./chat-ask.js";
4
- import { channelRepos, resolveChannelMate, proposalPreview, proposalOutcomeText, confirmedLink, confirmedCardText, replyContextFor, mirrorToTaskChat, focusContextFor, taskInCeiling, tooLongText, } from "./chat-channel.js";
5
- import { MATE_MESSAGE_MAX_CHARS, mateFailureText, runMateTurn } from "./mate.js";
4
+ import { channelRepos, resolveChannelLead, proposalPreview, proposalOutcomeText, confirmedLink, confirmedCardText, replyContextFor, mirrorToTaskChat, focusContextFor, taskInCeiling, tooLongText, } from "./chat-channel.js";
5
+ import { LEAD_MESSAGE_MAX_CHARS, leadFailureText, runLeadTurn } from "./lead.js";
6
6
  import { shapeReplyParts } from "./reply-shape.js";
7
7
  import { warmTurn } from "./chat-warmth.js";
8
- import { confirmMateProposal, dismissMateProposal, } from "./mate-doors.js";
8
+ import { confirmLeadProposal, dismissLeadProposal, } from "./lead-doors.js";
9
9
  import { ceilingDigestOf, verifyApproverStanding } from "./principal.js";
10
10
  import { ChatState, ChatDeliveryError, chatHash, savedChatPart, } from "./chat-delivery-state.js";
11
11
  import { readChatActionBody, readChatMessageBody, readChatPairBody, readChatPart } from "./contracts/chat-content.js";
@@ -23,7 +23,6 @@ import { phoneText, PHONE_HELP, phoneCommand, phoneStatus, phoneTaskView, phoneT
23
23
  import { applyRoomInbound, conversationRow, roomCardApprover, roomCommand, roomGrantAllowed, roomMessagesAfter, roomMessageText, teamDomain } from "./chat-rooms.js";
24
24
  import { isTelegramProgressNotification, proposalTaskOf } from "./store.js";
25
25
  import { isShotsKind, resultShotsFor } from "./result-shots.js";
26
- import { messageTeammate } from "./teammate-desk.js";
27
26
  import { CHAT_APP_NAMES } from "./flow-triggers.js";
28
27
  import { answersPrompt, applyDecideFeedback, applyDecideTap, decideFallbackLink, decideOffer, decideTargetOf, hasLiveDecideTokens, linksFor, mergedText, mintDecideButtons, offerFingerprint, openPromptFor, recordChatMerge } from "./chat-decide.js";
29
28
  import { mergePullRequest } from "./pull-request-flow.js";
@@ -198,7 +197,7 @@ export async function processChatEvent(options) {
198
197
  return true;
199
198
  }
200
199
  const text = String(input.text ?? "");
201
- // A teammate's question asked for this person's next message (v93): it is the answer.
200
+ // A subagent's question asked for this person's next message (v93): it is the answer.
202
201
  if (answerChatQuestionPrompt({ store, state, label: options.label }, event, binding, { text, ...(typeof input.originalLength === "number" ? { originalLength: input.originalLength } : {}) }, nowOf(options)))
203
202
  return true;
204
203
  // A flow decision asked for this person's next message (Edit, Send back): it is the draft or the note.
@@ -217,14 +216,8 @@ export async function processChatEvent(options) {
217
216
  // A "Person chooses" notice's thread reply is the note; the first other message after it is asked about (chat-flow.ts).
218
217
  if (answerChoiceMessage({ store, state, label: options.label }, event, binding, { text, ...(typeof input.originalLength === "number" ? { originalLength: input.originalLength } : {}), lead: input.lead === true }, nowOf(options), repos))
219
218
  return true;
220
- // A message to a teammate by name (v96), in someone's own chat with Toolroll: a card on its desk.
221
- if (event.channel === binding.channel) {
222
- const handed = messageTeammate(store, { who: binding.approver, repos, via: CHAT_APP_NAMES[state.provider] ?? "Chat" }, text, nowOf(options));
223
- if (handed !== null) {
224
- state.plan(event.id, [{ text: handed.said, ...(handed.link === undefined ? {} : { link: handed.link }) }], nowOf(options));
225
- return true;
226
- }
227
- }
219
+ // D5: a message naming a subagent ("@rosa …") is the lead's to read like any other: it asks the subagent for its
220
+ // person with a card they confirm (propose_subagent ask), never by the transport passing it on unseen.
228
221
  // The task this message's turn is about, once chosen (kept on the event,
229
222
  // so a reply planned after a restart names the same task).
230
223
  let about = typeof object(input.about).task === "string" ? { task: String(object(input.about).task), run: typeof object(input.about).run === "number" ? Number(object(input.about).run) : null } : null;
@@ -280,7 +273,7 @@ export async function processChatEvent(options) {
280
273
  return true;
281
274
  }
282
275
  }
283
- if (text.length > MATE_MESSAGE_MAX_CHARS) {
276
+ if (text.length > LEAD_MESSAGE_MAX_CHARS) {
284
277
  state.plan(event.id, [
285
278
  {
286
279
  text: tooLongText(input.originalLength || text.length),
@@ -291,7 +284,7 @@ export async function processChatEvent(options) {
291
284
  const request = chatHash(`${options.state.provider}:${binding.id}:${event.id}`).slice(0, 32);
292
285
  let receipt = event.session === null
293
286
  ? null
294
- : store.mateRequestReceipt(event.session, request);
287
+ : store.leadRequestReceipt(event.session, request);
295
288
  if (!receipt) {
296
289
  if (new Date(event.created).getTime() + 600_000 < now.getTime()) {
297
290
  state.plan(event.id, [
@@ -301,7 +294,7 @@ export async function processChatEvent(options) {
301
294
  ], now);
302
295
  return true;
303
296
  }
304
- const resolved = resolveChannelMate(store, { approver: binding.approver, approverGeneration: binding.generation }, repos, now);
297
+ const resolved = resolveChannelLead(store, { approver: binding.approver, approverGeneration: binding.generation }, repos, now);
305
298
  if (!resolved.ok && resolved.reason === "busy") {
306
299
  state.defer(event.id, "Assistant is answering another message", new Date(now.getTime() + 5000));
307
300
  return true;
@@ -332,7 +325,7 @@ export async function processChatEvent(options) {
332
325
  .map((c) => {
333
326
  if (c.task || c.proposal === undefined)
334
327
  return c;
335
- const about = proposalTaskOf(store.getMateProposal(c.proposal));
328
+ const about = proposalTaskOf(store.getLeadProposal(c.proposal));
336
329
  return about === null ? c : { ...c, task: about.task, ...(about.run === null ? {} : { run: about.run }) };
337
330
  });
338
331
  const targets = [
@@ -363,7 +356,7 @@ export async function processChatEvent(options) {
363
356
  // Only ever the owner's own message in their own chat: never the bot's, never a room.
364
357
  const warm = warmTurn(options.warm !== undefined && event.kind === "message" && event.member !== identity.bot && event.member === binding.member && event.channel === binding.channel
365
358
  ? options.warm(event, binding) : {});
366
- const outcome = await runMateTurn({
359
+ const outcome = await runLeadTurn({
367
360
  store,
368
361
  who: resolved.who,
369
362
  session: resolved.session,
@@ -407,7 +400,7 @@ export async function processChatEvent(options) {
407
400
  state.defer(event.id, "Assistant is answering another message", new Date(nowOf(options).getTime() + 5000));
408
401
  return true;
409
402
  }
410
- receipt = store.mateRequestReceipt(resolved.session.id, request);
403
+ receipt = store.leadRequestReceipt(resolved.session.id, request);
411
404
  if (!receipt) {
412
405
  state.plan(event.id, [
413
406
  {
@@ -419,8 +412,8 @@ export async function processChatEvent(options) {
419
412
  return true;
420
413
  }
421
414
  }
422
- store.sweepStaleMateTurns(nowOf(options));
423
- const turn = store.getMateTurn(receipt.turn);
415
+ store.sweepStaleLeadTurns(nowOf(options));
416
+ const turn = store.getLeadTurn(receipt.turn);
424
417
  if (turn?.state === "running" || turn?.state === "queued") {
425
418
  state.defer(event.id, "Reply is still running", new Date(nowOf(options).getTime() + 5000));
426
419
  return true;
@@ -428,19 +421,19 @@ export async function processChatEvent(options) {
428
421
  if (turn?.state !== "answered") {
429
422
  state.plan(event.id, [
430
423
  {
431
- text: mateFailureText(turn?.failureReason ?? null),
424
+ text: leadFailureText(turn?.failureReason ?? null),
432
425
  },
433
426
  ], nowOf(options));
434
427
  return true;
435
428
  }
436
- const session = store.getMateSession(turn.session);
429
+ const session = store.getLeadSession(turn.session);
437
430
  if (!session) {
438
431
  state.finish(event.id, true);
439
432
  return true;
440
433
  }
441
434
  await channelAccess(options, binding, session.ceilingDigest);
442
435
  const reply = store
443
- .listMateMessages(turn.thread, 200)
436
+ .listLeadMessages(turn.thread, 200)
444
437
  .find((message) => message.turn === turn.id && message.role === "assistant")?.text ?? "The reply is no longer in the saved thread.";
445
438
  // The lead's answer about one task names it: a reply to it stays on that task.
446
439
  // Split as written, then shape each part: no cut ever lands inside a link, code or a bold anchor.
@@ -448,11 +441,11 @@ export async function processChatEvent(options) {
448
441
  const parts = (shapedParts.length > 0 ? shapedParts : splitChatText("")).map((text) => (about === null ? { text, voice: true } : { text, voice: true, task: about.task, ...(about.run === null ? {} : { run: about.run }) }));
449
442
  // The lead's question to its owner: its options as buttons, then "Something else", in their own chat (any thread);
450
443
  // in a room a tap is not their message, so the question and its options go out as text.
451
- const ask = store.mateAsk(turn.id);
444
+ const ask = store.leadAsk(turn.id);
452
445
  if (ask !== null)
453
446
  parts.push({ ...(event.channel === binding.channel ? { text: phoneText(ask.question, 1000), ask: { turn: ask.turn, options: ask.options } } : { text: phoneText(chatAskText(ask), 1000) }),
454
447
  ...(about === null ? {} : { task: about.task, ...(about.run === null ? {} : { run: about.run }) }) });
455
- for (const image of store.listMateTurnEvidence(turn.id))
448
+ for (const image of store.listLeadTurnEvidence(turn.id))
456
449
  parts.push({
457
450
  text: image.caption,
458
451
  image: {
@@ -465,7 +458,7 @@ export async function processChatEvent(options) {
465
458
  run: image.run,
466
459
  });
467
460
  for (const proposal of store
468
- .listMateProposals(turn.thread, ["pending"])
461
+ .listLeadProposals(turn.thread, ["pending"])
469
462
  .filter((p) => p.turn === turn.id))
470
463
  parts.push({ text: "", proposal: proposal.id });
471
464
  state.plan(event.id, parts, nowOf(options));
@@ -565,7 +558,7 @@ export function applyChatAction(options, event, binding, repos) {
565
558
  // A flow decision's button (v88) is answered by the flow's own door.
566
559
  if (applyChatFlowTap({ store, state, label: options.label }, event, binding, token, repos, now))
567
560
  return;
568
- // A teammate's question's button (v93) is answered by the question's own door.
561
+ // A subagent's question's button (v93) is answered by the question's own door.
569
562
  if (applyChatQuestionTap({ store, state, label: options.label }, event, binding, token, now))
570
563
  return;
571
564
  // The lead's question to its owner: the tapped option becomes their next message.
@@ -592,7 +585,7 @@ export function applyChatAction(options, event, binding, repos) {
592
585
  ], now);
593
586
  return;
594
587
  }
595
- const proposal = store.getMateProposal(action.proposal);
588
+ const proposal = store.getLeadProposal(action.proposal);
596
589
  const verified = verifyApproverStanding(store, binding.approver, binding.generation, repos);
597
590
  if (!proposal || !verified.ok) {
598
591
  state.finish(event.id, true);
@@ -614,7 +607,7 @@ export function applyChatAction(options, event, binding, repos) {
614
607
  state.tokens(action.part, proposal.id, ["confirm", "dismiss"], now);
615
608
  }
616
609
  else if (phase === "dismiss") {
617
- const dismissed = dismissMateProposal(store, verified.who, proposal.id, now);
610
+ const dismissed = dismissLeadProposal(store, verified.who, proposal.id, now);
618
611
  content = {
619
612
  text: dismissed
620
613
  ? "Dismissed."
@@ -629,7 +622,7 @@ export function applyChatAction(options, event, binding, repos) {
629
622
  state.tokens(action.part, proposal.id, ["yes", "cancel"], now);
630
623
  }
631
624
  else {
632
- const outcome = confirmMateProposal(store, verified.who, proposal.id, now, {
625
+ const outcome = confirmLeadProposal(store, verified.who, proposal.id, now, {
633
626
  via: options.state.provider,
634
627
  evidenceRoot: options.evidenceRoot,
635
628
  confirm: phase === "yes",
@@ -716,7 +709,7 @@ export async function planRoomMessages(options) {
716
709
  parts.push({ text, channel: room.chat });
717
710
  }
718
711
  if (message.role === "assistant" && message.turn !== null)
719
- for (const proposal of store.listMateProposals(row.thread, ["pending"]).filter(one => one.turn === message.turn))
712
+ for (const proposal of store.listLeadProposals(row.thread, ["pending"]).filter(one => one.turn === message.turn))
720
713
  parts.push({ text: "", proposal: proposal.id, channel: room.chat });
721
714
  const id = chatHash(`${state.provider}:room:${room.id}:${message.id}`);
722
715
  const target = carrier;
@@ -998,7 +991,7 @@ function offerNotice(options, notification, binding, registry) {
998
991
  state.plan(id, flowSendParts(store, notification, state.provider), now);
999
992
  }
1000
993
  else if (notification.kind === "flow-card" && questionParts(store, notification, binding) !== null) {
1001
- // A teammate's question (v93): its options and "Answer in words" on the notice, for the person it asks.
994
+ // A subagent's question (v93): its options and "Answer in words" on the notice, for the person it asks.
1002
995
  state.enqueue({ id, installation: identity.installation, binding: binding.id, kind: "notice", channel: binding.channel, member: binding.member,
1003
996
  ts: "", thread: "", payload: {}, created: now.toISOString() });
1004
997
  state.plan(id, questionParts(store, notification, binding), now);
package/dist/chat-flow.js CHANGED
@@ -5,7 +5,7 @@ import { chooseFlowCard, flowChoiceAt, flowSendPaths, flowSendTail, FLOW_CHOOSE_
5
5
  import { cleanFlowMessage, fitFlowMessage } from "./flow-items.js";
6
6
  import { renderReply } from "./reply-shape.js";
7
7
  import { deciderOf, replyTarget } from "./flows.js";
8
- import { MATE_MESSAGE_MAX_CHARS } from "./mate.js";
8
+ import { LEAD_MESSAGE_MAX_CHARS } from "./lead.js";
9
9
  import { verifyApproverStanding } from "./principal.js";
10
10
  import { chatHash, partContent, savedChatPart } from "./chat-delivery-state.js";
11
11
  import { readHeldWords } from "./contracts/chat-content.js";
@@ -235,7 +235,7 @@ function replyAsNote(options, binding, said, cutShort, visit, repos, now) {
235
235
  if (said === "")
236
236
  return { ok: false, said: "Say what you'd change." };
237
237
  if (cutShort || said.length > LIMITS.note)
238
- return { ok: false, said: `That's too long to take from here. Keep it to ${Math.min(LIMITS.note, MATE_MESSAGE_MAX_CHARS).toLocaleString("en-US")} characters, or reply in Toolroll.` };
238
+ return { ok: false, said: `That's too long to take from here. Keep it to ${Math.min(LIMITS.note, LEAD_MESSAGE_MAX_CHARS).toLocaleString("en-US")} characters, or reply in Toolroll.` };
239
239
  if (!verifyApproverStanding(store, binding.approver, binding.generation, repos).ok)
240
240
  return null;
241
241
  const chosen = chooseFlowCard(store, { card: visit.card, entry: visit.entry, choice: null, note: said, actor: binding.approver, where: options.label, repos }, now);
@@ -390,7 +390,7 @@ export function answerChatFlowPrompt(options, event, binding, input, repos, now)
390
390
  // An edit is a draft (DRAFT_LIMIT); a send-back is a note (LIMITS.note). Over it is refused with the limit, never cut.
391
391
  const cutShort = (input.originalLength ?? 0) > input.text.length;
392
392
  const most = mode === "edit" ? DRAFT_LIMIT : LIMITS.note;
393
- const limit = cutShort ? Math.min(MATE_MESSAGE_MAX_CHARS, most) : most;
393
+ const limit = cutShort ? Math.min(LEAD_MESSAGE_MAX_CHARS, most) : most;
394
394
  if (cutShort || said.length > most) {
395
395
  say(`That's too long to take from here. Keep it to ${limit.toLocaleString("en-US")} characters, or ${mode === "edit" ? "change it" : "send it back"} in Toolroll.`);
396
396
  return true;
@@ -181,7 +181,7 @@ export declare class ChatMessages extends ChatCore {
181
181
  /**
182
182
  * Claim the oldest message that still needs a turn: queued, or running
183
183
  * under a claim that lapsed (a crash mid-turn), and not deferred past now.
184
- * One at a time per bot — the mate runs one turn per approver anyway.
184
+ * One at a time per bot — the lead runs one turn per approver anyway.
185
185
  */
186
186
  claimMessage(botId: string, owner: string, ttlMs: number, now: Date, only?: "turns" | "replies"): TelegramConversation | null;
187
187
  /** Extend a held claim; false means it lapsed and somebody else may hold the row. */
@@ -394,7 +394,7 @@ export class ChatMessages extends ChatCore {
394
394
  /**
395
395
  * Claim the oldest message that still needs a turn: queued, or running
396
396
  * under a claim that lapsed (a crash mid-turn), and not deferred past now.
397
- * One at a time per bot — the mate runs one turn per approver anyway.
397
+ * One at a time per bot — the lead runs one turn per approver anyway.
398
398
  */
399
399
  claimMessage(botId, owner, ttlMs, now, only) {
400
400
  return this.store.transact(() => {
@@ -525,7 +525,7 @@ export class ChatMessages extends ChatCore {
525
525
  }
526
526
  replyParts(conversation) {
527
527
  return this
528
- .prepare(`SELECT p.*, EXISTS (SELECT 1 FROM mate_proposal mp WHERE mp.id = json_extract(p.payload, '$.proposal')) AS proposal_live
528
+ .prepare(`SELECT p.*, EXISTS (SELECT 1 FROM lead_proposal mp WHERE mp.id = json_extract(p.payload, '$.proposal')) AS proposal_live
529
529
  FROM chat_part p WHERE p.provider = :provider AND p.event = ? ORDER BY p.ordinal`)
530
530
  .all(`m${conversation}`).map(row => readTelegramConversationPart(row, conversation));
531
531
  }
@@ -596,7 +596,7 @@ export class ChatMessages extends ChatCore {
596
596
  }));
597
597
  // Every sent part of this bot's replies on that message, oldest first: images, the lead's replies and its cards.
598
598
  const parts = chat
599
- .prepare(`SELECT p.*, c.payload AS asked, EXISTS (SELECT 1 FROM mate_proposal mp WHERE mp.id = json_extract(p.payload, '$.proposal')) AS proposal_live
599
+ .prepare(`SELECT p.*, c.payload AS asked, EXISTS (SELECT 1 FROM lead_proposal mp WHERE mp.id = json_extract(p.payload, '$.proposal')) AS proposal_live
600
600
  FROM chat_part p JOIN chat_event c ON c.provider = p.provider AND c.id = p.event
601
601
  WHERE p.provider = :provider AND c.kind = 'message' AND c.binding = ? AND c.channel = ? AND p.message = ? AND p.state = 'sent'
602
602
  ORDER BY CAST(substr(p.event, 2) AS INTEGER), p.ordinal`)
@@ -610,7 +610,7 @@ export class ChatMessages extends ChatCore {
610
610
  // The lead's own messages: a reply from a turn that was about one task,
611
611
  // and a card, which names its task (or, once it filed one, the new task).
612
612
  const lead = parts.filter(one => one.part.kind !== "image").map(({ part, asked }) => part.kind === "card"
613
- ? proposalTaskOf(part.proposal === null ? null : this.store.getMateProposal(part.proposal))
613
+ ? proposalTaskOf(part.proposal === null ? null : this.store.getLeadProposal(part.proposal))
614
614
  : asked.ok && asked.value.about !== undefined ? { task: asked.value.about.task, run: asked.value.about.run } : null);
615
615
  const shown = chat
616
616
  .prepare("SELECT task_id, run FROM chat_message_ref WHERE provider = :provider AND kind = 'task' AND binding = ? AND chat = ? AND message = ?")