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
@@ -6,7 +6,7 @@ export declare const legacyAppSchema: (app: LegacyChatApp) => string;
6
6
  /** One app's 16 old tables, parents first. */
7
7
  export declare const legacyAppTables: (app: LegacyChatApp) => string[];
8
8
  /** Telegram's own tables (v1-v114) and the bridge's lease, kept verbatim. */
9
- export declare const LEGACY_TELEGRAM_SCHEMA = "\nCREATE TABLE IF NOT EXISTS telegram_flow_choice (\n token TEXT PRIMARY KEY,\n binding INTEGER NOT NULL REFERENCES telegram_binding(id) ON DELETE RESTRICT,\n card INTEGER NOT NULL REFERENCES flow_card(id),\n entry INTEGER NOT NULL,\n choice INTEGER NOT NULL,\n label TEXT NOT NULL,\n chat_id TEXT NOT NULL,\n message_id TEXT,\n created_at TEXT NOT NULL,\n expires_at TEXT NOT NULL,\n consumed_at TEXT\n);\nCREATE INDEX IF NOT EXISTS telegram_flow_choice_visit ON telegram_flow_choice (card, entry);\n\n-- v79: a Telegram message that showed one task (a /task status, a task\n-- picked from /tasks): a reply to it is about that task. Like chat_focus,\n-- a pointer and never an authority \u2014 the turn re-proves the task.\nCREATE TABLE IF NOT EXISTS telegram_task_message (\n binding INTEGER NOT NULL,\n chat_id TEXT NOT NULL,\n message_id TEXT NOT NULL,\n task_id TEXT NOT NULL,\n source_run INTEGER,\n created_at TEXT NOT NULL,\n PRIMARY KEY (binding, chat_id, message_id)\n);\n\n\n-- Away mode (v34, mate arc \u00A710): the Telegram bridge's digest cadence.\n-- One row. every_ms NULL = off (every fact pages as it lands); set, the\n-- bridge holds ROUTINE rows unclaimed until the window elapses and sends\n-- them as one message \u2014 decisions and attention-class facts still page\n-- singly. last_sent_at anchors the window; it never deletes a row.\nCREATE TABLE IF NOT EXISTS telegram_digest (\n id INTEGER PRIMARY KEY CHECK (id = 1),\n every_ms INTEGER,\n set_by TEXT,\n set_at TEXT,\n last_sent_at TEXT\n);\nINSERT OR IGNORE INTO telegram_digest (id, every_ms) VALUES (1, NULL);\n\n\n-- One Telegram chat speaking as one approver. Bindings are never deleted:\n-- revocation is a stamp, because \"who could answer as whom, when\" is an\n-- audit question a DELETE cannot answer. The partial unique index (v72) is\n-- the rule that one Telegram user has one live binding per bot: several\n-- teammates pair their own private chats with the same bot.\nCREATE TABLE IF NOT EXISTS telegram_binding (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n bot_id TEXT NOT NULL,\n chat_id TEXT NOT NULL,\n user_id TEXT NOT NULL,\n approver TEXT NOT NULL REFERENCES approver(name) ON DELETE RESTRICT,\n approver_generation INTEGER NOT NULL,\n paired_at TEXT NOT NULL,\n paired_by TEXT NOT NULL,\n pair_update_id INTEGER,\n revoked_at TEXT,\n revoked_by TEXT\n);\nCREATE UNIQUE INDEX IF NOT EXISTS telegram_binding_live_user\n ON telegram_binding (bot_id, user_id) WHERE revoked_at IS NULL;\n\n-- v72: a Telegram chat's place in the shared team conversations. A group\n-- follows exactly one team conversation (and a conversation has at most one\n-- group); a private chat may select one conversation to talk in instead of\n-- its personal assistant. The cursor is the last conversation message this\n-- chat received. Rows are revoked, never deleted.\nCREATE TABLE IF NOT EXISTS telegram_team_chat (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n bot_id TEXT NOT NULL,\n chat_id TEXT NOT NULL,\n binding INTEGER NOT NULL REFERENCES telegram_binding(id) ON DELETE RESTRICT,\n kind TEXT NOT NULL CHECK (kind IN ('group','private')),\n conversation TEXT NOT NULL REFERENCES team_conversation(id) ON DELETE RESTRICT,\n bound_by TEXT NOT NULL,\n bound_at TEXT NOT NULL,\n cursor INTEGER NOT NULL DEFAULT 0,\n revoked_at TEXT,\n revoked_by TEXT\n);\nCREATE UNIQUE INDEX IF NOT EXISTS telegram_team_chat_live\n ON telegram_team_chat (bot_id, chat_id) WHERE revoked_at IS NULL;\nCREATE UNIQUE INDEX IF NOT EXISTS telegram_team_chat_group\n ON telegram_team_chat (conversation) WHERE revoked_at IS NULL AND kind = 'group';\n\n\nCREATE TABLE IF NOT EXISTS telegram_outbound_message (\n binding INTEGER NOT NULL REFERENCES telegram_binding(id) ON DELETE RESTRICT,\n bot_id TEXT NOT NULL,\n chat_id TEXT NOT NULL,\n message_id TEXT NOT NULL,\n notification INTEGER NOT NULL REFERENCES notification(id) ON DELETE RESTRICT,\n destination TEXT NOT NULL,\n project TEXT,\n task_ref INTEGER REFERENCES task_ref(id),\n task_id TEXT,\n source_run INTEGER REFERENCES run(id),\n created_at TEXT NOT NULL,\n PRIMARY KEY (binding, chat_id, message_id, notification)\n);\nCREATE TABLE IF NOT EXISTS telegram_retry (\n bot_id TEXT PRIMARY KEY,\n next_attempt_at TEXT NOT NULL\n);\n\n-- One-time pairing codes, hashed like every other credential, consumed in\n-- one transaction with the binding they create.\nCREATE TABLE IF NOT EXISTS telegram_pairing (\n code_hash TEXT PRIMARY KEY,\n approver TEXT NOT NULL REFERENCES approver(name) ON DELETE RESTRICT,\n approver_generation INTEGER NOT NULL,\n created_at TEXT NOT NULL,\n created_by TEXT NOT NULL,\n expires_at TEXT NOT NULL,\n consumed_at TEXT,\n consumed_chat TEXT,\n consumed_user TEXT,\n consumed_update INTEGER\n);\n\n-- Every Telegram update this installation has applied, exactly once. The\n-- PRIMARY KEY is the idempotency: a replayed batch re-applies nothing.\nCREATE TABLE IF NOT EXISTS telegram_update (\n update_id INTEGER PRIMARY KEY,\n applied_at TEXT NOT NULL,\n result TEXT NOT NULL\n);\n\n-- Opaque one-tap actions. callback_data carries only the random token; what\n-- the tap MEANS \u2014 which binding, decision, option, and phase \u2014 lives here,\n-- where a stolen bot token cannot read or forge it. Confirm challenges are\n-- short-lived rows in the same table, consumed exactly once.\nCREATE TABLE IF NOT EXISTS telegram_action (\n token TEXT PRIMARY KEY,\n binding INTEGER NOT NULL REFERENCES telegram_binding(id) ON DELETE RESTRICT,\n decision INTEGER NOT NULL REFERENCES decision(id) ON DELETE CASCADE,\n option_id TEXT NOT NULL,\n phase TEXT NOT NULL CHECK (phase IN ('choose','confirm','cancel')),\n chat_id TEXT NOT NULL,\n message_id TEXT,\n created_at TEXT NOT NULL,\n expires_at TEXT,\n consumed_at TEXT\n);\nCREATE INDEX IF NOT EXISTS telegram_action_by_decision ON telegram_action (decision);\n\n-- v86: a flow card waiting at a \"Person decides\" zone reaches the decider on\n-- Telegram with Approve / Edit / Send back. Each button is one opaque token\n-- for one visit of one card (card, entry), placed on the message it rides so\n-- a tap on any other message proves itself stale. Edit and Send back ask\n-- for a reply to a prompt: telegram_flow_prompt names which prompt means\n-- what, so the reply becomes the new draft or the note.\nCREATE TABLE IF NOT EXISTS telegram_flow_action (\n token TEXT PRIMARY KEY,\n binding INTEGER NOT NULL REFERENCES telegram_binding(id) ON DELETE RESTRICT,\n card INTEGER NOT NULL REFERENCES flow_card(id),\n entry INTEGER NOT NULL,\n action TEXT NOT NULL CHECK (action IN ('approve','edit','send-back')),\n chat_id TEXT NOT NULL,\n message_id TEXT,\n created_at TEXT NOT NULL,\n expires_at TEXT NOT NULL,\n consumed_at TEXT\n);\nCREATE INDEX IF NOT EXISTS telegram_flow_action_visit ON telegram_flow_action (card, entry);\n-- v93: a teammate's question on Telegram. One button per option (choice)\n-- and one to reply in words (choice NULL), placed on the message it rides;\n-- a reply to the prompt the Reply button sends is the answer.\nCREATE TABLE IF NOT EXISTS telegram_question_action (\n token TEXT PRIMARY KEY,\n binding INTEGER NOT NULL REFERENCES telegram_binding(id) ON DELETE RESTRICT,\n question INTEGER NOT NULL REFERENCES teammate_question(id),\n choice TEXT,\n chat_id TEXT NOT NULL,\n message_id TEXT,\n created_at TEXT NOT NULL,\n expires_at TEXT NOT NULL,\n consumed_at TEXT\n);\nCREATE TABLE IF NOT EXISTS telegram_question_prompt (\n chat_id TEXT NOT NULL,\n message_id TEXT NOT NULL,\n binding INTEGER NOT NULL REFERENCES telegram_binding(id) ON DELETE RESTRICT,\n question INTEGER NOT NULL REFERENCES teammate_question(id),\n created_at TEXT NOT NULL,\n expires_at TEXT NOT NULL,\n consumed_at TEXT,\n PRIMARY KEY (chat_id, message_id)\n);\nCREATE TABLE IF NOT EXISTS telegram_flow_prompt (\n chat_id TEXT NOT NULL,\n message_id TEXT NOT NULL,\n binding INTEGER NOT NULL REFERENCES telegram_binding(id) ON DELETE RESTRICT,\n card INTEGER NOT NULL REFERENCES flow_card(id),\n entry INTEGER NOT NULL,\n mode TEXT NOT NULL CHECK (mode IN ('edit','send-back')),\n created_at TEXT NOT NULL,\n expires_at TEXT NOT NULL,\n consumed_at TEXT,\n PRIMARY KEY (chat_id, message_id)\n);\n\n-- Which outbound Telegram message carries which decision (v10). A free-text\n-- reply is routed through the EXACT message it replies to \u2014 never \"the\n-- latest decision\", never \"the only open one\" (Codex free-text review,\n-- finding 1). Losing the send/record race fails closed: an unrecorded\n-- message routes nothing.\nCREATE TABLE IF NOT EXISTS telegram_decision_message (\n binding INTEGER NOT NULL REFERENCES telegram_binding(id) ON DELETE CASCADE,\n chat_id TEXT NOT NULL,\n message_id TEXT NOT NULL,\n decision INTEGER NOT NULL REFERENCES decision(id) ON DELETE CASCADE,\n created_at TEXT NOT NULL,\n PRIMARY KEY (binding, chat_id, message_id)\n);\n\n-- The free-text draft (v10): an operator's note, held immutable and\n-- expiring until a TAP commits it with the choice. One live draft per\n-- (binding, decision); a newer valid reply SUPERSEDES, never edits.\nCREATE TABLE IF NOT EXISTS telegram_note_draft (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n binding INTEGER NOT NULL REFERENCES telegram_binding(id) ON DELETE CASCADE,\n decision INTEGER NOT NULL REFERENCES decision(id) ON DELETE CASCADE,\n update_id INTEGER NOT NULL,\n message_id TEXT NOT NULL,\n reply_to TEXT NOT NULL,\n note TEXT NOT NULL,\n state TEXT NOT NULL DEFAULT 'pending' CHECK (state IN ('pending','armed','superseded','consumed','discarded')),\n created_at TEXT NOT NULL,\n expires_at TEXT NOT NULL\n);\n\nCREATE UNIQUE INDEX IF NOT EXISTS telegram_note_draft_live\n ON telegram_note_draft (binding, decision) WHERE state IN ('pending','armed');\n\n-- v62: the durable inbound conversation queue. An ordinary paired message\n-- becomes a row in the SAME transaction that marks its update applied, so\n-- the poll cursor never moves past text nobody holds. The row carries the\n-- exact binding, sender, update, message and the request identity the\n-- shared mate engine receipts it under; the async model turn runs OUTSIDE\n-- any transaction under a short claim, and a replay or restart finds the\n-- receipt instead of dispatching the provider again.\nCREATE TABLE IF NOT EXISTS telegram_conversation (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n binding INTEGER NOT NULL REFERENCES telegram_binding(id) ON DELETE RESTRICT,\n bot_id TEXT NOT NULL,\n chat_id TEXT NOT NULL,\n user_id TEXT NOT NULL,\n approver TEXT NOT NULL,\n approver_generation INTEGER NOT NULL,\n update_id INTEGER NOT NULL UNIQUE,\n message_id TEXT NOT NULL,\n reply_to TEXT,\n request TEXT NOT NULL UNIQUE,\n text TEXT NOT NULL,\n context TEXT,\n task_id TEXT,\n source_run INTEGER,\n state TEXT NOT NULL CHECK (state IN ('queued','running','done','failed')),\n claim_owner TEXT,\n claim_expires_at TEXT,\n attempts INTEGER NOT NULL DEFAULT 0,\n next_attempt_at TEXT,\n session INTEGER,\n turn INTEGER,\n outcome TEXT,\n reply_message_id TEXT,\n created_at TEXT NOT NULL,\n started_at TEXT,\n finished_at TEXT\n);\nCREATE INDEX IF NOT EXISTS telegram_conversation_queue ON telegram_conversation (bot_id, state, id);\n\n-- v62: opaque one-tap tokens for the mate's proposal cards, the same shape\n-- as telegram_action for decisions: callback_data carries only the token,\n-- and what a tap MEANS (which binding, which proposal, which phase) lives\n-- here. Consumed exactly once; an irreversible answer arms a yes/cancel\n-- pair first, exactly as a decision button does.\nCREATE TABLE IF NOT EXISTS telegram_proposal_action (\n token TEXT PRIMARY KEY,\n binding INTEGER NOT NULL REFERENCES telegram_binding(id) ON DELETE RESTRICT,\n proposal INTEGER NOT NULL REFERENCES mate_proposal(id) ON DELETE CASCADE,\n phase TEXT NOT NULL CHECK (phase IN ('confirm','dismiss','yes','cancel')),\n chat_id TEXT NOT NULL,\n message_id TEXT,\n created_at TEXT NOT NULL,\n expires_at TEXT,\n consumed_at TEXT\n);\nCREATE INDEX IF NOT EXISTS telegram_proposal_action_by_proposal ON telegram_proposal_action (proposal);\n\n-- v63: the outbound half of a conversation, durable BEFORE any send. Once\n-- the engine's turn is answered (or recovered from its receipt), the exact\n-- reply text, split into Telegram-sized parts, and one card per pending\n-- proposal are written here in one transaction; the bridge then sends\n-- them in order under the row's claim, marking a part sent only when\n-- Telegram confirmed a message id. A crash, an outage, a rate limit or a\n-- restart resumes from the first unsent part \u2014 never another model call,\n-- proposal, task or revision. A card's buttons are minted with the part,\n-- so a resend carries the same tokens. A part whose network answer was\n-- lost is counted as uncertain: a resend may duplicate it, and the row\n-- says so instead of claiming an exactly-once Telegram cannot provide.\n-- v64: an 'image' part is one verified screenshot of one exact result,\n-- named by typed columns \u2014 task, run, artifact and the hash its record\n-- carried when the part was planned \u2014 never by JSON inside text or a\n-- keyboard, and never by bytes: every send re-reads the artifact from the\n-- evidence root and re-verifies it against these columns first. Its text\n-- is the short caption. A confirmed image message binds replies to that\n-- exact task and run, exactly as an outbox fact's message does.\nCREATE TABLE IF NOT EXISTS telegram_conversation_part (\n conversation INTEGER NOT NULL REFERENCES telegram_conversation(id) ON DELETE CASCADE,\n ordinal INTEGER NOT NULL,\n kind TEXT NOT NULL CHECK (kind IN ('reply','card','image')),\n text TEXT NOT NULL,\n reply_to TEXT,\n proposal INTEGER REFERENCES mate_proposal(id) ON DELETE SET NULL,\n keyboard_json TEXT,\n state TEXT NOT NULL CHECK (state IN ('pending','sent','dropped')),\n message_id TEXT,\n attempts INTEGER NOT NULL DEFAULT 0,\n uncertain INTEGER NOT NULL DEFAULT 0,\n next_attempt_at TEXT,\n last_error TEXT,\n created_at TEXT NOT NULL,\n sent_at TEXT,\n task_id TEXT,\n source_run INTEGER REFERENCES run(id),\n artifact INTEGER,\n sha256 TEXT,\n PRIMARY KEY (conversation, ordinal),\n CHECK ((state = 'sent') = (message_id IS NOT NULL)),\n CHECK ((state = 'sent') = (sent_at IS NOT NULL)),\n CHECK ((kind = 'image') = (task_id IS NOT NULL AND source_run IS NOT NULL AND artifact IS NOT NULL AND sha256 IS NOT NULL))\n);\n\n\n-- The bridge's poll lease and cursor, per bot. One live poller at a time;\n-- the cursor only ever moves forward, and only under a live generation.\nCREATE TABLE IF NOT EXISTS bridge_lease (\n bot_id TEXT PRIMARY KEY,\n owner TEXT NOT NULL,\n generation INTEGER NOT NULL,\n cursor INTEGER NOT NULL DEFAULT 0,\n expires_at TEXT NOT NULL,\n heartbeat_at TEXT NOT NULL,\n push_url TEXT,\n push_at TEXT,\n push_problem TEXT\n);\n-- v98: updates Telegram pushed to /hooks/telegram, kept until the bridge\n-- applies them (in update order, through the same door as polled ones).\nCREATE TABLE IF NOT EXISTS telegram_inbox (\n update_id INTEGER PRIMARY KEY,\n bot_id TEXT NOT NULL,\n payload TEXT NOT NULL,\n received_at TEXT NOT NULL\n);\n\n\nCREATE TABLE IF NOT EXISTS telegram_flow_confirm (\n token TEXT PRIMARY KEY,\n binding INTEGER NOT NULL,\n chat_id TEXT NOT NULL,\n message_id TEXT NOT NULL,\n card INTEGER NOT NULL,\n entry INTEGER NOT NULL,\n phase TEXT NOT NULL CHECK (phase IN ('yes','cancel')),\n created_at TEXT NOT NULL,\n expires_at TEXT NOT NULL,\n consumed_at TEXT\n);\nCREATE INDEX IF NOT EXISTS telegram_flow_confirm_visit ON telegram_flow_confirm (card, entry);\n";
9
+ export declare const LEGACY_TELEGRAM_SCHEMA = "\nCREATE TABLE IF NOT EXISTS telegram_flow_choice (\n token TEXT PRIMARY KEY,\n binding INTEGER NOT NULL REFERENCES telegram_binding(id) ON DELETE RESTRICT,\n card INTEGER NOT NULL REFERENCES flow_card(id),\n entry INTEGER NOT NULL,\n choice INTEGER NOT NULL,\n label TEXT NOT NULL,\n chat_id TEXT NOT NULL,\n message_id TEXT,\n created_at TEXT NOT NULL,\n expires_at TEXT NOT NULL,\n consumed_at TEXT\n);\nCREATE INDEX IF NOT EXISTS telegram_flow_choice_visit ON telegram_flow_choice (card, entry);\n\n-- v79: a Telegram message that showed one task (a /task status, a task\n-- picked from /tasks): a reply to it is about that task. Like chat_focus,\n-- a pointer and never an authority \u2014 the turn re-proves the task.\nCREATE TABLE IF NOT EXISTS telegram_task_message (\n binding INTEGER NOT NULL,\n chat_id TEXT NOT NULL,\n message_id TEXT NOT NULL,\n task_id TEXT NOT NULL,\n source_run INTEGER,\n created_at TEXT NOT NULL,\n PRIMARY KEY (binding, chat_id, message_id)\n);\n\n\n-- Away mode (v34, mate arc \u00A710): the Telegram bridge's digest cadence.\n-- One row. every_ms NULL = off (every fact pages as it lands); set, the\n-- bridge holds ROUTINE rows unclaimed until the window elapses and sends\n-- them as one message \u2014 decisions and attention-class facts still page\n-- singly. last_sent_at anchors the window; it never deletes a row.\nCREATE TABLE IF NOT EXISTS telegram_digest (\n id INTEGER PRIMARY KEY CHECK (id = 1),\n every_ms INTEGER,\n set_by TEXT,\n set_at TEXT,\n last_sent_at TEXT\n);\nINSERT OR IGNORE INTO telegram_digest (id, every_ms) VALUES (1, NULL);\n\n\n-- One Telegram chat speaking as one approver. Bindings are never deleted:\n-- revocation is a stamp, because \"who could answer as whom, when\" is an\n-- audit question a DELETE cannot answer. The partial unique index (v72) is\n-- the rule that one Telegram user has one live binding per bot: several\n-- teammates pair their own private chats with the same bot.\nCREATE TABLE IF NOT EXISTS telegram_binding (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n bot_id TEXT NOT NULL,\n chat_id TEXT NOT NULL,\n user_id TEXT NOT NULL,\n approver TEXT NOT NULL REFERENCES approver(name) ON DELETE RESTRICT,\n approver_generation INTEGER NOT NULL,\n paired_at TEXT NOT NULL,\n paired_by TEXT NOT NULL,\n pair_update_id INTEGER,\n revoked_at TEXT,\n revoked_by TEXT\n);\nCREATE UNIQUE INDEX IF NOT EXISTS telegram_binding_live_user\n ON telegram_binding (bot_id, user_id) WHERE revoked_at IS NULL;\n\n-- v72: a Telegram chat's place in the shared team conversations. A group\n-- follows exactly one team conversation (and a conversation has at most one\n-- group); a private chat may select one conversation to talk in instead of\n-- its personal assistant. The cursor is the last conversation message this\n-- chat received. Rows are revoked, never deleted.\nCREATE TABLE IF NOT EXISTS telegram_team_chat (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n bot_id TEXT NOT NULL,\n chat_id TEXT NOT NULL,\n binding INTEGER NOT NULL REFERENCES telegram_binding(id) ON DELETE RESTRICT,\n kind TEXT NOT NULL CHECK (kind IN ('group','private')),\n conversation TEXT NOT NULL REFERENCES team_conversation(id) ON DELETE RESTRICT,\n bound_by TEXT NOT NULL,\n bound_at TEXT NOT NULL,\n cursor INTEGER NOT NULL DEFAULT 0,\n revoked_at TEXT,\n revoked_by TEXT\n);\nCREATE UNIQUE INDEX IF NOT EXISTS telegram_team_chat_live\n ON telegram_team_chat (bot_id, chat_id) WHERE revoked_at IS NULL;\nCREATE UNIQUE INDEX IF NOT EXISTS telegram_team_chat_group\n ON telegram_team_chat (conversation) WHERE revoked_at IS NULL AND kind = 'group';\n\n\nCREATE TABLE IF NOT EXISTS telegram_outbound_message (\n binding INTEGER NOT NULL REFERENCES telegram_binding(id) ON DELETE RESTRICT,\n bot_id TEXT NOT NULL,\n chat_id TEXT NOT NULL,\n message_id TEXT NOT NULL,\n notification INTEGER NOT NULL REFERENCES notification(id) ON DELETE RESTRICT,\n destination TEXT NOT NULL,\n project TEXT,\n task_ref INTEGER REFERENCES task_ref(id),\n task_id TEXT,\n source_run INTEGER REFERENCES run(id),\n created_at TEXT NOT NULL,\n PRIMARY KEY (binding, chat_id, message_id, notification)\n);\nCREATE TABLE IF NOT EXISTS telegram_retry (\n bot_id TEXT PRIMARY KEY,\n next_attempt_at TEXT NOT NULL\n);\n\n-- One-time pairing codes, hashed like every other credential, consumed in\n-- one transaction with the binding they create.\nCREATE TABLE IF NOT EXISTS telegram_pairing (\n code_hash TEXT PRIMARY KEY,\n approver TEXT NOT NULL REFERENCES approver(name) ON DELETE RESTRICT,\n approver_generation INTEGER NOT NULL,\n created_at TEXT NOT NULL,\n created_by TEXT NOT NULL,\n expires_at TEXT NOT NULL,\n consumed_at TEXT,\n consumed_chat TEXT,\n consumed_user TEXT,\n consumed_update INTEGER\n);\n\n-- Every Telegram update this installation has applied, exactly once. The\n-- PRIMARY KEY is the idempotency: a replayed batch re-applies nothing.\nCREATE TABLE IF NOT EXISTS telegram_update (\n update_id INTEGER PRIMARY KEY,\n applied_at TEXT NOT NULL,\n result TEXT NOT NULL\n);\n\n-- Opaque one-tap actions. callback_data carries only the random token; what\n-- the tap MEANS \u2014 which binding, decision, option, and phase \u2014 lives here,\n-- where a stolen bot token cannot read or forge it. Confirm challenges are\n-- short-lived rows in the same table, consumed exactly once.\nCREATE TABLE IF NOT EXISTS telegram_action (\n token TEXT PRIMARY KEY,\n binding INTEGER NOT NULL REFERENCES telegram_binding(id) ON DELETE RESTRICT,\n decision INTEGER NOT NULL REFERENCES decision(id) ON DELETE CASCADE,\n option_id TEXT NOT NULL,\n phase TEXT NOT NULL CHECK (phase IN ('choose','confirm','cancel')),\n chat_id TEXT NOT NULL,\n message_id TEXT,\n created_at TEXT NOT NULL,\n expires_at TEXT,\n consumed_at TEXT\n);\nCREATE INDEX IF NOT EXISTS telegram_action_by_decision ON telegram_action (decision);\n\n-- v86: a flow card waiting at a \"Person decides\" zone reaches the decider on\n-- Telegram with Approve / Edit / Send back. Each button is one opaque token\n-- for one visit of one card (card, entry), placed on the message it rides so\n-- a tap on any other message proves itself stale. Edit and Send back ask\n-- for a reply to a prompt: telegram_flow_prompt names which prompt means\n-- what, so the reply becomes the new draft or the note.\nCREATE TABLE IF NOT EXISTS telegram_flow_action (\n token TEXT PRIMARY KEY,\n binding INTEGER NOT NULL REFERENCES telegram_binding(id) ON DELETE RESTRICT,\n card INTEGER NOT NULL REFERENCES flow_card(id),\n entry INTEGER NOT NULL,\n action TEXT NOT NULL CHECK (action IN ('approve','edit','send-back')),\n chat_id TEXT NOT NULL,\n message_id TEXT,\n created_at TEXT NOT NULL,\n expires_at TEXT NOT NULL,\n consumed_at TEXT\n);\nCREATE INDEX IF NOT EXISTS telegram_flow_action_visit ON telegram_flow_action (card, entry);\n-- v93: a subagent's question on Telegram. One button per option (choice)\n-- and one to reply in words (choice NULL), placed on the message it rides;\n-- a reply to the prompt the Reply button sends is the answer.\nCREATE TABLE IF NOT EXISTS telegram_question_action (\n token TEXT PRIMARY KEY,\n binding INTEGER NOT NULL REFERENCES telegram_binding(id) ON DELETE RESTRICT,\n question INTEGER NOT NULL REFERENCES subagent_question(id),\n choice TEXT,\n chat_id TEXT NOT NULL,\n message_id TEXT,\n created_at TEXT NOT NULL,\n expires_at TEXT NOT NULL,\n consumed_at TEXT\n);\nCREATE TABLE IF NOT EXISTS telegram_question_prompt (\n chat_id TEXT NOT NULL,\n message_id TEXT NOT NULL,\n binding INTEGER NOT NULL REFERENCES telegram_binding(id) ON DELETE RESTRICT,\n question INTEGER NOT NULL REFERENCES subagent_question(id),\n created_at TEXT NOT NULL,\n expires_at TEXT NOT NULL,\n consumed_at TEXT,\n PRIMARY KEY (chat_id, message_id)\n);\nCREATE TABLE IF NOT EXISTS telegram_flow_prompt (\n chat_id TEXT NOT NULL,\n message_id TEXT NOT NULL,\n binding INTEGER NOT NULL REFERENCES telegram_binding(id) ON DELETE RESTRICT,\n card INTEGER NOT NULL REFERENCES flow_card(id),\n entry INTEGER NOT NULL,\n mode TEXT NOT NULL CHECK (mode IN ('edit','send-back')),\n created_at TEXT NOT NULL,\n expires_at TEXT NOT NULL,\n consumed_at TEXT,\n PRIMARY KEY (chat_id, message_id)\n);\n\n-- Which outbound Telegram message carries which decision (v10). A free-text\n-- reply is routed through the EXACT message it replies to \u2014 never \"the\n-- latest decision\", never \"the only open one\" (Codex free-text review,\n-- finding 1). Losing the send/record race fails closed: an unrecorded\n-- message routes nothing.\nCREATE TABLE IF NOT EXISTS telegram_decision_message (\n binding INTEGER NOT NULL REFERENCES telegram_binding(id) ON DELETE CASCADE,\n chat_id TEXT NOT NULL,\n message_id TEXT NOT NULL,\n decision INTEGER NOT NULL REFERENCES decision(id) ON DELETE CASCADE,\n created_at TEXT NOT NULL,\n PRIMARY KEY (binding, chat_id, message_id)\n);\n\n-- The free-text draft (v10): an operator's note, held immutable and\n-- expiring until a TAP commits it with the choice. One live draft per\n-- (binding, decision); a newer valid reply SUPERSEDES, never edits.\nCREATE TABLE IF NOT EXISTS telegram_note_draft (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n binding INTEGER NOT NULL REFERENCES telegram_binding(id) ON DELETE CASCADE,\n decision INTEGER NOT NULL REFERENCES decision(id) ON DELETE CASCADE,\n update_id INTEGER NOT NULL,\n message_id TEXT NOT NULL,\n reply_to TEXT NOT NULL,\n note TEXT NOT NULL,\n state TEXT NOT NULL DEFAULT 'pending' CHECK (state IN ('pending','armed','superseded','consumed','discarded')),\n created_at TEXT NOT NULL,\n expires_at TEXT NOT NULL\n);\n\nCREATE UNIQUE INDEX IF NOT EXISTS telegram_note_draft_live\n ON telegram_note_draft (binding, decision) WHERE state IN ('pending','armed');\n\n-- v62: the durable inbound conversation queue. An ordinary paired message\n-- becomes a row in the SAME transaction that marks its update applied, so\n-- the poll cursor never moves past text nobody holds. The row carries the\n-- exact binding, sender, update, message and the request identity the\n-- shared mate engine receipts it under; the async model turn runs OUTSIDE\n-- any transaction under a short claim, and a replay or restart finds the\n-- receipt instead of dispatching the provider again.\nCREATE TABLE IF NOT EXISTS telegram_conversation (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n binding INTEGER NOT NULL REFERENCES telegram_binding(id) ON DELETE RESTRICT,\n bot_id TEXT NOT NULL,\n chat_id TEXT NOT NULL,\n user_id TEXT NOT NULL,\n approver TEXT NOT NULL,\n approver_generation INTEGER NOT NULL,\n update_id INTEGER NOT NULL UNIQUE,\n message_id TEXT NOT NULL,\n reply_to TEXT,\n request TEXT NOT NULL UNIQUE,\n text TEXT NOT NULL,\n context TEXT,\n task_id TEXT,\n source_run INTEGER,\n state TEXT NOT NULL CHECK (state IN ('queued','running','done','failed')),\n claim_owner TEXT,\n claim_expires_at TEXT,\n attempts INTEGER NOT NULL DEFAULT 0,\n next_attempt_at TEXT,\n session INTEGER,\n turn INTEGER,\n outcome TEXT,\n reply_message_id TEXT,\n created_at TEXT NOT NULL,\n started_at TEXT,\n finished_at TEXT\n);\nCREATE INDEX IF NOT EXISTS telegram_conversation_queue ON telegram_conversation (bot_id, state, id);\n\n-- v62: opaque one-tap tokens for the lead's proposal cards, the same shape\n-- as telegram_action for decisions: callback_data carries only the token,\n-- and what a tap MEANS (which binding, which proposal, which phase) lives\n-- here. Consumed exactly once; an irreversible answer arms a yes/cancel\n-- pair first, exactly as a decision button does.\nCREATE TABLE IF NOT EXISTS telegram_proposal_action (\n token TEXT PRIMARY KEY,\n binding INTEGER NOT NULL REFERENCES telegram_binding(id) ON DELETE RESTRICT,\n proposal INTEGER NOT NULL REFERENCES lead_proposal(id) ON DELETE CASCADE,\n phase TEXT NOT NULL CHECK (phase IN ('confirm','dismiss','yes','cancel')),\n chat_id TEXT NOT NULL,\n message_id TEXT,\n created_at TEXT NOT NULL,\n expires_at TEXT,\n consumed_at TEXT\n);\nCREATE INDEX IF NOT EXISTS telegram_proposal_action_by_proposal ON telegram_proposal_action (proposal);\n\n-- v63: the outbound half of a conversation, durable BEFORE any send. Once\n-- the engine's turn is answered (or recovered from its receipt), the exact\n-- reply text, split into Telegram-sized parts, and one card per pending\n-- proposal are written here in one transaction; the bridge then sends\n-- them in order under the row's claim, marking a part sent only when\n-- Telegram confirmed a message id. A crash, an outage, a rate limit or a\n-- restart resumes from the first unsent part \u2014 never another model call,\n-- proposal, task or revision. A card's buttons are minted with the part,\n-- so a resend carries the same tokens. A part whose network answer was\n-- lost is counted as uncertain: a resend may duplicate it, and the row\n-- says so instead of claiming an exactly-once Telegram cannot provide.\n-- v64: an 'image' part is one verified screenshot of one exact result,\n-- named by typed columns \u2014 task, run, artifact and the hash its record\n-- carried when the part was planned \u2014 never by JSON inside text or a\n-- keyboard, and never by bytes: every send re-reads the artifact from the\n-- evidence root and re-verifies it against these columns first. Its text\n-- is the short caption. A confirmed image message binds replies to that\n-- exact task and run, exactly as an outbox fact's message does.\nCREATE TABLE IF NOT EXISTS telegram_conversation_part (\n conversation INTEGER NOT NULL REFERENCES telegram_conversation(id) ON DELETE CASCADE,\n ordinal INTEGER NOT NULL,\n kind TEXT NOT NULL CHECK (kind IN ('reply','card','image')),\n text TEXT NOT NULL,\n reply_to TEXT,\n proposal INTEGER REFERENCES lead_proposal(id) ON DELETE SET NULL,\n keyboard_json TEXT,\n state TEXT NOT NULL CHECK (state IN ('pending','sent','dropped')),\n message_id TEXT,\n attempts INTEGER NOT NULL DEFAULT 0,\n uncertain INTEGER NOT NULL DEFAULT 0,\n next_attempt_at TEXT,\n last_error TEXT,\n created_at TEXT NOT NULL,\n sent_at TEXT,\n task_id TEXT,\n source_run INTEGER REFERENCES run(id),\n artifact INTEGER,\n sha256 TEXT,\n PRIMARY KEY (conversation, ordinal),\n CHECK ((state = 'sent') = (message_id IS NOT NULL)),\n CHECK ((state = 'sent') = (sent_at IS NOT NULL)),\n CHECK ((kind = 'image') = (task_id IS NOT NULL AND source_run IS NOT NULL AND artifact IS NOT NULL AND sha256 IS NOT NULL))\n);\n\n\n-- The bridge's poll lease and cursor, per bot. One live poller at a time;\n-- the cursor only ever moves forward, and only under a live generation.\nCREATE TABLE IF NOT EXISTS bridge_lease (\n bot_id TEXT PRIMARY KEY,\n owner TEXT NOT NULL,\n generation INTEGER NOT NULL,\n cursor INTEGER NOT NULL DEFAULT 0,\n expires_at TEXT NOT NULL,\n heartbeat_at TEXT NOT NULL,\n push_url TEXT,\n push_at TEXT,\n push_problem TEXT\n);\n-- v98: updates Telegram pushed to /hooks/telegram, kept until the bridge\n-- applies them (in update order, through the same door as polled ones).\nCREATE TABLE IF NOT EXISTS telegram_inbox (\n update_id INTEGER PRIMARY KEY,\n bot_id TEXT NOT NULL,\n payload TEXT NOT NULL,\n received_at TEXT NOT NULL\n);\n\n\nCREATE TABLE IF NOT EXISTS telegram_flow_confirm (\n token TEXT PRIMARY KEY,\n binding INTEGER NOT NULL,\n chat_id TEXT NOT NULL,\n message_id TEXT NOT NULL,\n card INTEGER NOT NULL,\n entry INTEGER NOT NULL,\n phase TEXT NOT NULL CHECK (phase IN ('yes','cancel')),\n created_at TEXT NOT NULL,\n expires_at TEXT NOT NULL,\n consumed_at TEXT\n);\nCREATE INDEX IF NOT EXISTS telegram_flow_confirm_visit ON telegram_flow_confirm (card, entry);\n";
10
10
  /** Telegram's 21 old tables and the bridge's lease, children first. */
11
11
  export declare const LEGACY_TELEGRAM_TABLES: readonly ["telegram_note_draft", "telegram_decision_message", "telegram_task_message", "telegram_outbound_message", "telegram_action", "telegram_proposal_action", "telegram_flow_action", "telegram_flow_confirm", "telegram_flow_choice", "telegram_flow_prompt", "telegram_question_action", "telegram_question_prompt", "telegram_conversation_part", "telegram_conversation", "telegram_team_chat", "telegram_pairing", "telegram_update", "telegram_inbox", "telegram_retry", "telegram_digest", "telegram_binding", "bridge_lease"];
12
12
  /** Whether this database already keeps its chats in the shared tables (v116 ran, or it was made at v116 or later). */
@@ -25,7 +25,7 @@ CREATE TABLE IF NOT EXISTS chat_event (
25
25
  kind TEXT NOT NULL CHECK(kind IN ('message','action','pair','notice')),
26
26
  channel TEXT NOT NULL, member TEXT NOT NULL, ts TEXT NOT NULL, thread TEXT NOT NULL,
27
27
  payload TEXT NOT NULL, created TEXT NOT NULL,
28
- session INTEGER REFERENCES mate_session(id), state TEXT NOT NULL DEFAULT 'queued' CHECK(state IN ('queued','done','dropped')),
28
+ session INTEGER REFERENCES lead_session(id), state TEXT NOT NULL DEFAULT 'queued' CHECK(state IN ('queued','done','dropped')),
29
29
  next_at TEXT, problem TEXT
30
30
  );
31
31
  CREATE INDEX IF NOT EXISTS chat_pending_event ON chat_event(installation,state,next_at);
@@ -38,7 +38,7 @@ CREATE TABLE IF NOT EXISTS chat_part (
38
38
  );
39
39
  CREATE TABLE IF NOT EXISTS chat_action (
40
40
  token TEXT PRIMARY KEY, part INTEGER NOT NULL REFERENCES chat_part(id),
41
- proposal INTEGER NOT NULL REFERENCES mate_proposal(id), phase TEXT NOT NULL CHECK(phase IN ('confirm','dismiss','yes','cancel')),
41
+ proposal INTEGER NOT NULL REFERENCES lead_proposal(id), phase TEXT NOT NULL CHECK(phase IN ('confirm','dismiss','yes','cancel')),
42
42
  expires TEXT NOT NULL, consumed TEXT
43
43
  );
44
44
  CREATE TABLE IF NOT EXISTS chat_progress (
@@ -105,19 +105,19 @@ CREATE TABLE IF NOT EXISTS chat_flow_note (
105
105
  );
106
106
  CREATE INDEX IF NOT EXISTS chat_flow_note_visit ON chat_flow_note(card, entry);
107
107
  `;
108
- /** v93: a teammate's question in the chat app. One button per option (choice)
108
+ /** v93: a subagent's question in the chat app. One button per option (choice)
109
109
  * and one to answer in words (choice NULL); that one opens a prompt the
110
110
  * person's next message in their DM answers. Never in CHAT_TABLES: an older
111
111
  * database has no such tables until this schema creates them. */
112
112
  const LEGACY_APP_QUESTION_SCHEMA = `
113
113
  CREATE TABLE IF NOT EXISTS chat_question_action (
114
114
  token TEXT PRIMARY KEY, part INTEGER NOT NULL REFERENCES chat_part(id),
115
- question INTEGER NOT NULL REFERENCES teammate_question(id), choice TEXT, expires TEXT NOT NULL, consumed TEXT
115
+ question INTEGER NOT NULL REFERENCES subagent_question(id), choice TEXT, expires TEXT NOT NULL, consumed TEXT
116
116
  );
117
117
  CREATE INDEX IF NOT EXISTS chat_question_action_question ON chat_question_action(question);
118
118
  CREATE TABLE IF NOT EXISTS chat_question_prompt (
119
119
  id INTEGER PRIMARY KEY AUTOINCREMENT, binding INTEGER NOT NULL REFERENCES chat_binding(id),
120
- question INTEGER NOT NULL REFERENCES teammate_question(id), created TEXT NOT NULL, expires TEXT NOT NULL, consumed TEXT
120
+ question INTEGER NOT NULL REFERENCES subagent_question(id), created TEXT NOT NULL, expires TEXT NOT NULL, consumed TEXT
121
121
  );
122
122
  CREATE INDEX IF NOT EXISTS chat_question_prompt_open ON chat_question_prompt(binding, consumed);
123
123
  `;
@@ -306,13 +306,13 @@ CREATE TABLE IF NOT EXISTS telegram_flow_action (
306
306
  consumed_at TEXT
307
307
  );
308
308
  CREATE INDEX IF NOT EXISTS telegram_flow_action_visit ON telegram_flow_action (card, entry);
309
- -- v93: a teammate's question on Telegram. One button per option (choice)
309
+ -- v93: a subagent's question on Telegram. One button per option (choice)
310
310
  -- and one to reply in words (choice NULL), placed on the message it rides;
311
311
  -- a reply to the prompt the Reply button sends is the answer.
312
312
  CREATE TABLE IF NOT EXISTS telegram_question_action (
313
313
  token TEXT PRIMARY KEY,
314
314
  binding INTEGER NOT NULL REFERENCES telegram_binding(id) ON DELETE RESTRICT,
315
- question INTEGER NOT NULL REFERENCES teammate_question(id),
315
+ question INTEGER NOT NULL REFERENCES subagent_question(id),
316
316
  choice TEXT,
317
317
  chat_id TEXT NOT NULL,
318
318
  message_id TEXT,
@@ -324,7 +324,7 @@ CREATE TABLE IF NOT EXISTS telegram_question_prompt (
324
324
  chat_id TEXT NOT NULL,
325
325
  message_id TEXT NOT NULL,
326
326
  binding INTEGER NOT NULL REFERENCES telegram_binding(id) ON DELETE RESTRICT,
327
- question INTEGER NOT NULL REFERENCES teammate_question(id),
327
+ question INTEGER NOT NULL REFERENCES subagent_question(id),
328
328
  created_at TEXT NOT NULL,
329
329
  expires_at TEXT NOT NULL,
330
330
  consumed_at TEXT,
@@ -414,7 +414,7 @@ CREATE TABLE IF NOT EXISTS telegram_conversation (
414
414
  );
415
415
  CREATE INDEX IF NOT EXISTS telegram_conversation_queue ON telegram_conversation (bot_id, state, id);
416
416
 
417
- -- v62: opaque one-tap tokens for the mate's proposal cards, the same shape
417
+ -- v62: opaque one-tap tokens for the lead's proposal cards, the same shape
418
418
  -- as telegram_action for decisions: callback_data carries only the token,
419
419
  -- and what a tap MEANS (which binding, which proposal, which phase) lives
420
420
  -- here. Consumed exactly once; an irreversible answer arms a yes/cancel
@@ -422,7 +422,7 @@ CREATE INDEX IF NOT EXISTS telegram_conversation_queue ON telegram_conversation
422
422
  CREATE TABLE IF NOT EXISTS telegram_proposal_action (
423
423
  token TEXT PRIMARY KEY,
424
424
  binding INTEGER NOT NULL REFERENCES telegram_binding(id) ON DELETE RESTRICT,
425
- proposal INTEGER NOT NULL REFERENCES mate_proposal(id) ON DELETE CASCADE,
425
+ proposal INTEGER NOT NULL REFERENCES lead_proposal(id) ON DELETE CASCADE,
426
426
  phase TEXT NOT NULL CHECK (phase IN ('confirm','dismiss','yes','cancel')),
427
427
  chat_id TEXT NOT NULL,
428
428
  message_id TEXT,
@@ -456,7 +456,7 @@ CREATE TABLE IF NOT EXISTS telegram_conversation_part (
456
456
  kind TEXT NOT NULL CHECK (kind IN ('reply','card','image')),
457
457
  text TEXT NOT NULL,
458
458
  reply_to TEXT,
459
- proposal INTEGER REFERENCES mate_proposal(id) ON DELETE SET NULL,
459
+ proposal INTEGER REFERENCES lead_proposal(id) ON DELETE SET NULL,
460
460
  keyboard_json TEXT,
461
461
  state TEXT NOT NULL CHECK (state IN ('pending','sent','dropped')),
462
462
  message_id TEXT,
@@ -1,6 +1,6 @@
1
1
  import { TeamLeads } from "./team-leads.js";
2
2
  import { tooLongText } from "./chat-channel.js";
3
- import { MATE_MESSAGE_MAX_CHARS } from "./mate.js";
3
+ import { LEAD_MESSAGE_MAX_CHARS } from "./lead.js";
4
4
  import { phoneText } from "./telegram-status.js";
5
5
  /** `/team` lists, `/team <n>` chooses, `/team off` (or `private`) leaves.
6
6
  * The bare word works too, because Slack and Discord keep unregistered
@@ -130,7 +130,7 @@ export function applyRoomInbound(input) {
130
130
  report.ignored++;
131
131
  return true;
132
132
  }
133
- if (trimmed.length > MATE_MESSAGE_MAX_CHARS) {
133
+ if (trimmed.length > LEAD_MESSAGE_MAX_CHARS) {
134
134
  say(tooLongText(trimmed.length));
135
135
  report.chatRefused = (report.chatRefused ?? 0) + 1;
136
136
  return true;
@@ -192,7 +192,7 @@ export function roomGrantAllowed(store, domain, backend, room) {
192
192
  export function teamDomain(store, projects) { return domainFor(store, projects); }
193
193
  /** The conversation messages a room has not received yet, oldest first. */
194
194
  export function roomMessagesAfter(store, thread, cursor, limit = 20) {
195
- return store.handle.prepare(`SELECT m.id, m.role, m.text, m.turn, q.author, q.request_id, q.status FROM mate_message m
195
+ return store.handle.prepare(`SELECT m.id, m.role, m.text, m.turn, q.author, q.request_id, q.status FROM lead_message m
196
196
  LEFT JOIN team_message q ON q.message = m.id WHERE m.thread = ? AND m.id > ? ORDER BY m.id LIMIT ?`).all(thread, cursor, limit)
197
197
  .map(row => ({ id: Number(row["id"]), role: row["role"] === "assistant" ? "assistant" : "operator", text: String(row["text"]), turn: row["turn"] === null ? null : Number(row["turn"]),
198
198
  author: row["author"] === null || row["author"] === undefined ? null : String(row["author"]), requestId: row["request_id"] === null || row["request_id"] === undefined ? null : String(row["request_id"]), status: row["status"] === null || row["status"] === undefined ? null : String(row["status"]) }));
@@ -1,7 +1,7 @@
1
1
  /** Small warm touches while the lead works on a chat app: once a turn will take more than a few seconds (its first tool
2
2
  * step starts), a 👍 on the owner's message and the app's typing indicator until the turn ends. Best effort only: an
3
3
  * app or permission that does not allow it is skipped silently, and nothing here can fail, delay or change the turn. */
4
- import type { MateProgress } from "./mate-progress.js";
4
+ import type { LeadProgress } from "./lead-progress.js";
5
5
  export declare const WARM_EMOJI = "\uD83D\uDC4D";
6
6
  /** Telegram and Teams show typing for about five seconds, Discord for ten: refreshed inside the shorter window. */
7
7
  export declare const TYPING_REFRESH_MS = 4000;
@@ -12,7 +12,7 @@ export type WarmHooks = {
12
12
  typing?: () => Promise<unknown>;
13
13
  };
14
14
  export type WarmTurn = {
15
- onProgress: (event: MateProgress) => void;
15
+ onProgress: (event: LeadProgress) => void;
16
16
  stop: () => void;
17
17
  };
18
18
  /** The turn's progress listener: on the first tool step, react once and keep typing shown until `stop`. */
package/dist/cli.d.ts CHANGED
@@ -41,7 +41,7 @@ export type ParseResult = {
41
41
  error: string;
42
42
  };
43
43
  type Write = (line: string) => void;
44
- export declare const HELP = "toolroll \u2014 a control plane for unattended coding agents\n\nUsage\n toolroll [path...] report what is in flight\n toolroll pulls report what is waiting on a person\n toolroll graph report which work graph is already here\n toolroll repos list connected repositories, and how to adjust\n toolroll repos add <path> add a project; it is in the console at once (no path: the repo you are in)\n toolroll repos remove <path>\n take it out of the console; its tasks and results stay\n toolroll repos add-from-github <owner/name> --root <dir>\n preview, then clone and connect (--yes)\n toolroll link put `toolroll` on your PATH\n toolroll unlink take it off again\n toolroll update update to the latest release: verified, drained, undoable\n (preview first; --yes, --now, --at HH:MM, --rollback)\n toolroll release <branch> release Toolroll itself: gate, your approval, deploy, merge, tag, publish\n (resumable: rerun to continue; --help)\n toolroll contract the machine contract: envelope version + capabilities\n (--commands dumps the declared command guide)\n toolroll skills install --claude-code [--dir <path>]\n install this binary's guides for Claude Code (preview first)\n toolroll skills install teach a repo's agents this queue exists (preview first)\n toolroll skills list the guides this exact binary serves\n toolroll skills get <name> print one guide (version-matched, never stale)\n toolroll demo a seeded throwaway sandbox \u2014 see it working in 90 seconds\n toolroll up app + builder for every saved project \u2014 the normal start\n toolroll onboard make your agent Toolroll's lead: add this repo, install its skill (--yes), offer starter flows (--starter)\n toolroll status running, queued, ready results, release check and plan windows\n toolroll health recent server latency, write waits and live streams\n toolroll integrations which integrations work, and what to do about the ones that don't (--json)\n toolroll session native coding sessions through the running service\n toolroll connect save a private connection to your central service\n toolroll lead named leads on the connected service\n toolroll lead token mint your lead agent's credential: its own work pings nobody\n toolroll lead say \"<text>\" (the lead) one short message in its person's chat\n toolroll conversation shared and private conversations on that service\n toolroll chat --lead <id> --conversation <id> central chat (use --local for local chat)\n\nOperating the queue \u2014 `toolroll task` prints the whole surface,\nand any queue command + --help prints it too\n toolroll approver add <name>\n mint the credential that lets a person say yes\n toolroll ready what could be dispatched right now\n toolroll check-progress <run> current or final approved check progress\n toolroll task wait <id> [--timeout <seconds>]\n wait for this attempt to finish or need a person\n toolroll task add <title> queue work\n toolroll task scope <id> --goal <text>\n state what success is; approval binds to it\n toolroll task approve <id>\n the yes \u2014 nothing builds without one\n toolroll claim <id> --runner <name>\n toolroll heartbeat <lease> / worker release <lease> / reap\n toolroll tick --runner <name> --token <t> --repo <path>\n one unattended pass over the ready set\n toolroll flows list | show <id> | create | edit | trigger | script | card | archive\n set up and inspect flows (writes preview until --yes)\n toolroll chat --as <you> --token <t>\n talk to the mate: one conversation across every\n project; it proposes, you confirm (--say \"\u2026\" for one turn)\n toolroll serve --repo <path> advanced: run only the console\n (--editor vscode with --runner: file links\n open in VS Code on the device you browse from)\n toolroll watch / daemon install advanced: run only the builder\n toolroll reconcile recover what the last stretch left behind\n\nWith nothing connected it reports everything it can find below the working\ndirectory. Once you connect repositories it reports those instead.\n\nOptions\n --all report everything discoverable, ignoring connected repos\n --depth <n> directory levels to descend (default: 6)\n --hidden include dot-directories, which are skipped by default\n --dirty also read each working tree for uncommitted files\n --local branches only: no network, no pull requests, no issues\n --json emit a machine-readable envelope instead of a report\n -o <file> also write that envelope to a file (requires --json)\n -h, --help show this\n\nNothing is written, installed, or configured. Every repository is read\nthrough git with --no-optional-locks, so a scan never contends for the\nindex lock with an editor you have open.\n\n--dirty is off by default because reading a working tree is unbounded\nwork: on a large or cloud-synced repo `git status` can take minutes,\nwhere listing branches takes milliseconds.\n\nlink shows what it would do and changes nothing; add --yes to apply it,\nor --to <dir> to choose where. It never uses sudo, never leaves your\nhome directory, and never touches a file it did not create.";
44
+ export declare const HELP = "toolroll \u2014 a control plane for unattended coding agents\n\nUsage\n toolroll [path...] report what is in flight\n toolroll pulls report what is waiting on a person\n toolroll graph report which work graph is already here\n toolroll repos list connected repositories, and how to adjust\n toolroll repos add <path> add a project; it is in the console at once (no path: the repo you are in)\n toolroll repos remove <path>\n take it out of the console; its tasks and results stay\n toolroll repos add-from-github <owner/name> --root <dir>\n preview, then clone and connect (--yes)\n toolroll link put `toolroll` on your PATH\n toolroll unlink take it off again\n toolroll update update to the latest release: verified, drained, undoable\n (preview first; --yes, --now, --at HH:MM, --rollback)\n toolroll release <branch> release Toolroll itself: gate, your approval, deploy, merge, tag, publish\n (resumable: rerun to continue; --help)\n toolroll contract the machine contract: envelope version + capabilities\n (--commands dumps the declared command guide)\n toolroll skills install --claude-code [--dir <path>]\n install this binary's guides for Claude Code (preview first)\n toolroll skills install teach a repo's agents this queue exists (preview first)\n toolroll skills list the guides this exact binary serves\n toolroll skills get <name> print one guide (version-matched, never stale)\n toolroll demo a seeded throwaway sandbox \u2014 see it working in 90 seconds\n toolroll up app + builder for every saved project \u2014 the normal start\n toolroll onboard make your agent Toolroll's lead: add this repo, install its skill (--yes), offer starter flows (--starter)\n toolroll status running, queued, ready results, release check and plan windows\n toolroll health recent server latency, write waits and live streams\n toolroll integrations which integrations work, and what to do about the ones that don't (--json)\n toolroll lead token mint your lead agent's credential: its own work pings nobody\n toolroll lead say \"<text>\" (the lead) one short message in its person's chat\n\nOperating the queue \u2014 `toolroll task` prints the whole surface,\nand any queue command + --help prints it too\n toolroll approver add <name>\n mint the credential that lets a person say yes\n toolroll ready what could be dispatched right now\n toolroll check-progress <run> current or final approved check progress\n toolroll task wait <id> [--timeout <seconds>]\n wait for this attempt to finish or need a person\n toolroll task add <title> queue work\n toolroll task scope <id> --goal <text>\n state what success is; approval binds to it\n toolroll task approve <id>\n the yes \u2014 nothing builds without one\n toolroll claim <id> --runner <name>\n toolroll heartbeat <lease> / worker release <lease> / reap\n toolroll tick --runner <name> --token <t> --repo <path>\n one unattended pass over the ready set\n toolroll flows list | show <id> | create | edit | trigger | script | card | archive\n set up and inspect flows (writes preview until --yes)\n toolroll chat --as <you> --token <t>\n talk to your lead: one conversation across every\n project; it proposes, you confirm (--say \"\u2026\" for one turn)\n toolroll serve --repo <path> advanced: run only the console\n (--editor vscode with --runner: file links\n open in VS Code on the device you browse from)\n toolroll watch / daemon install advanced: run only the builder\n toolroll reconcile recover what the last stretch left behind\n\nWith nothing connected it reports everything it can find below the working\ndirectory. Once you connect repositories it reports those instead.\n\nOptions\n --all report everything discoverable, ignoring connected repos\n --depth <n> directory levels to descend (default: 6)\n --hidden include dot-directories, which are skipped by default\n --dirty also read each working tree for uncommitted files\n --local branches only: no network, no pull requests, no issues\n --json emit a machine-readable envelope instead of a report\n -o <file> also write that envelope to a file (requires --json)\n -h, --help show this\n\nNothing is written, installed, or configured. Every repository is read\nthrough git with --no-optional-locks, so a scan never contends for the\nindex lock with an editor you have open.\n\n--dirty is off by default because reading a working tree is unbounded\nwork: on a large or cloud-synced repo `git status` can take minutes,\nwhere listing branches takes milliseconds.\n\nlink shows what it would do and changes nothing; add --yes to apply it,\nor --to <dir> to choose where. It never uses sudo, never leaves your\nhome directory, and never touches a file it did not create.";
45
45
  export declare function parseArgs(argv: readonly string[]): ParseResult;
46
46
  /** The commands that operate the queue rather than report on the world. */
47
47
  /** Every verb the CLI routes OUTSIDE runOperate. Exported (with
package/dist/cli.js CHANGED
@@ -27,6 +27,7 @@ import { run as execRun } from "./exec.js";
27
27
  import { COMMAND_GUIDE, SURFACE_NOTES, SURFACE_SCHEMA_VERSION } from "./surface.js";
28
28
  import { runSessionCommand, SESSION_CLI_ACTIONS } from "./session-cli.js";
29
29
  import { maybeRunTeamCommand, TEAM_CLI_ACTIONS } from "./team-cli.js";
30
+ import { deprecationWarning } from "./deprecations.js";
30
31
  import { maybeRunRemoteCommand } from "./remote-exec.js";
31
32
  import { runHealthCommand } from "./health-cli.js";
32
33
  import { discover, inspectAll } from "./discover.js";
@@ -73,13 +74,8 @@ Usage
73
74
  toolroll status running, queued, ready results, release check and plan windows
74
75
  toolroll health recent server latency, write waits and live streams
75
76
  toolroll integrations which integrations work, and what to do about the ones that don't (--json)
76
- toolroll session native coding sessions through the running service
77
- toolroll connect save a private connection to your central service
78
- toolroll lead named leads on the connected service
79
77
  toolroll lead token mint your lead agent's credential: its own work pings nobody
80
78
  toolroll lead say "<text>" (the lead) one short message in its person's chat
81
- toolroll conversation shared and private conversations on that service
82
- toolroll chat --lead <id> --conversation <id> central chat (use --local for local chat)
83
79
 
84
80
  Operating the queue — \`toolroll task\` prints the whole surface,
85
81
  and any queue command + --help prints it too
@@ -101,7 +97,7 @@ and any queue command + --help prints it too
101
97
  toolroll flows list | show <id> | create | edit | trigger | script | card | archive
102
98
  set up and inspect flows (writes preview until --yes)
103
99
  toolroll chat --as <you> --token <t>
104
- talk to the mate: one conversation across every
100
+ talk to your lead: one conversation across every
105
101
  project; it proposes, you confirm (--say "…" for one turn)
106
102
  toolroll serve --repo <path> advanced: run only the console
107
103
  (--editor vscode with --runner: file links
@@ -425,8 +421,11 @@ async function dispatch(argv, write, mainOptions) {
425
421
  }
426
422
  if (first === "contract")
427
423
  return runContractCommand(rest, write);
428
- if (first === "session")
424
+ if (first === "session") {
425
+ // D5: native coding sessions are deprecated. They still run this release; each use says so once, on stderr.
426
+ (mainOptions.session?.stderr ?? (line => process.stderr.write(`${line}\n`)))(deprecationWarning(`session ${rest.find(arg => !arg.startsWith("-")) ?? ""}`.trim(), "session"));
429
427
  return runSessionCommand(rest, write, mainOptions.session);
428
+ }
430
429
  if (first === "demo")
431
430
  return runDemoCommand(rest, write);
432
431
  if (first === "skills")
@@ -146,35 +146,40 @@ export declare const chatActionRequestSchemas: {
146
146
  readonly flow_trigger_remove: z.ZodObject<{
147
147
  trigger: z.ZodInt;
148
148
  }, z.core.$strict>;
149
- readonly teammate_create: z.ZodObject<{
149
+ readonly subagent_create: z.ZodObject<{
150
150
  repo: z.ZodString;
151
151
  template: z.ZodOptional<z.ZodUnknown>;
152
152
  name: z.ZodOptional<z.ZodUnknown>;
153
153
  soul: z.ZodOptional<z.ZodUnknown>;
154
154
  }, z.core.$strict>;
155
- readonly teammate_soul: z.ZodObject<{
156
- teammate: z.ZodInt;
155
+ readonly subagent_soul: z.ZodObject<{
156
+ subagent: z.ZodInt;
157
157
  soul: z.ZodString;
158
158
  }, z.core.$strict>;
159
- readonly teammate_state: z.ZodObject<{
160
- teammate: z.ZodInt;
159
+ readonly subagent_state: z.ZodObject<{
160
+ subagent: z.ZodInt;
161
161
  state: z.ZodEnum<{
162
162
  active: "active";
163
163
  paused: "paused";
164
164
  removed: "removed";
165
165
  }>;
166
166
  }, z.core.$strict>;
167
- readonly teammate_note: z.ZodObject<{
168
- teammate: z.ZodInt;
167
+ readonly subagent_note: z.ZodObject<{
168
+ subagent: z.ZodInt;
169
169
  note: z.ZodString;
170
170
  }, z.core.$strict>;
171
- readonly teammate_answer: z.ZodObject<{
171
+ /** D5: the lead delegates — "ask Rosa to …" — as a card on the subagent's desk. */
172
+ readonly subagent_ask: z.ZodObject<{
173
+ subagent: z.ZodInt;
174
+ text: z.ZodString;
175
+ }, z.core.$strict>;
176
+ readonly subagent_answer: z.ZodObject<{
172
177
  question: z.ZodInt;
173
178
  choice: z.ZodOptional<z.ZodUnknown>;
174
179
  text: z.ZodOptional<z.ZodUnknown>;
175
180
  }, z.core.$strict>;
176
- readonly teammate_tools: z.ZodObject<{
177
- teammate: z.ZodInt;
181
+ readonly subagent_tools: z.ZodObject<{
182
+ subagent: z.ZodInt;
178
183
  tool: z.ZodString;
179
184
  change: z.ZodEnum<{
180
185
  grant: "grant";
@@ -187,8 +192,8 @@ export declare const chatActionRequestSchemas: {
187
192
  limitOver: z.ZodOptional<z.ZodUnknown>;
188
193
  undoWith: z.ZodOptional<z.ZodUnknown>;
189
194
  }, z.core.$strict>;
190
- readonly teammate_memory: z.ZodObject<{
191
- teammate: z.ZodInt;
195
+ readonly subagent_memory: z.ZodObject<{
196
+ subagent: z.ZodInt;
192
197
  memory: z.ZodInt;
193
198
  change: z.ZodEnum<{
194
199
  edit: "edit";
@@ -196,8 +201,8 @@ export declare const chatActionRequestSchemas: {
196
201
  }>;
197
202
  text: z.ZodOptional<z.ZodUnknown>;
198
203
  }, z.core.$strict>;
199
- readonly teammate_routine: z.ZodObject<{
200
- teammate: z.ZodInt;
204
+ readonly subagent_routine: z.ZodObject<{
205
+ subagent: z.ZodInt;
201
206
  change: z.ZodEnum<{
202
207
  add: "add";
203
208
  remove: "remove";
@@ -206,8 +211,8 @@ export declare const chatActionRequestSchemas: {
206
211
  schedule: z.ZodOptional<z.ZodUnknown>;
207
212
  text: z.ZodOptional<z.ZodUnknown>;
208
213
  }, z.core.$strict>;
209
- readonly teammate_undo: z.ZodObject<{
210
- teammate: z.ZodInt;
214
+ readonly subagent_undo: z.ZodObject<{
215
+ subagent: z.ZodInt;
211
216
  call: z.ZodInt;
212
217
  }, z.core.$strict>;
213
218
  readonly kit_setup: z.ZodObject<{
@@ -294,17 +299,18 @@ export declare const sharedActionSchema: z.ZodObject<{
294
299
  skill_import: "skill_import";
295
300
  skill_restore: "skill_restore";
296
301
  skill_test: "skill_test";
302
+ subagent_answer: "subagent_answer";
303
+ subagent_ask: "subagent_ask";
304
+ subagent_create: "subagent_create";
305
+ subagent_memory: "subagent_memory";
306
+ subagent_note: "subagent_note";
307
+ subagent_routine: "subagent_routine";
308
+ subagent_soul: "subagent_soul";
309
+ subagent_state: "subagent_state";
310
+ subagent_tools: "subagent_tools";
311
+ subagent_undo: "subagent_undo";
297
312
  task_cancel: "task_cancel";
298
313
  task_resume: "task_resume";
299
- teammate_answer: "teammate_answer";
300
- teammate_create: "teammate_create";
301
- teammate_memory: "teammate_memory";
302
- teammate_note: "teammate_note";
303
- teammate_routine: "teammate_routine";
304
- teammate_soul: "teammate_soul";
305
- teammate_state: "teammate_state";
306
- teammate_tools: "teammate_tools";
307
- teammate_undo: "teammate_undo";
308
314
  tool_add: "tool_add";
309
315
  tool_remove: "tool_remove";
310
316
  }>;
@@ -324,5 +330,4 @@ export declare function upgradeUnversionedSharedAction(body: Record<string, unkn
324
330
  export declare const SHARED_ACTION_UPGRADES: {
325
331
  readonly 0: typeof upgradeUnversionedSharedAction;
326
332
  };
327
- /** Legacy readers checked only the envelope. Confirmation still prepares the request again before acting. */
328
- export declare function readSharedAction(input: unknown): ContractResult<SharedAction>;
333
+ export declare function readSharedAction(given: unknown): ContractResult<SharedAction>;
@@ -57,18 +57,20 @@ export const chatActionRequestSchemas = {
57
57
  flow_trigger_pause: z.strictObject({ trigger: id }),
58
58
  flow_trigger_resume: z.strictObject({ trigger: id }),
59
59
  flow_trigger_remove: z.strictObject({ trigger: id }),
60
- teammate_create: z.strictObject({ repo: words, template: z.unknown().optional(), name: z.unknown().optional(), soul: z.unknown().optional() }),
61
- teammate_soul: z.strictObject({ teammate: id, soul: words }),
62
- teammate_state: z.strictObject({ teammate: id, state: z.enum(["active", "paused", "removed"]) }),
63
- teammate_note: z.strictObject({ teammate: id, note: words }),
64
- teammate_answer: z.strictObject({ question: id, choice: z.unknown().optional(), text: z.unknown().optional() }),
65
- teammate_tools: z.strictObject({
66
- teammate: id, tool: words, change: z.enum(["grant", "revoke", "rule"]), action: z.unknown().optional(), use: z.unknown().optional(),
60
+ subagent_create: z.strictObject({ repo: words, template: z.unknown().optional(), name: z.unknown().optional(), soul: z.unknown().optional() }),
61
+ subagent_soul: z.strictObject({ subagent: id, soul: words }),
62
+ subagent_state: z.strictObject({ subagent: id, state: z.enum(["active", "paused", "removed"]) }),
63
+ subagent_note: z.strictObject({ subagent: id, note: words }),
64
+ /** D5: the lead delegates — "ask Rosa to …" — as a card on the subagent's desk. */
65
+ subagent_ask: z.strictObject({ subagent: id, text: words }),
66
+ subagent_answer: z.strictObject({ question: id, choice: z.unknown().optional(), text: z.unknown().optional() }),
67
+ subagent_tools: z.strictObject({
68
+ subagent: id, tool: words, change: z.enum(["grant", "revoke", "rule"]), action: z.unknown().optional(), use: z.unknown().optional(),
67
69
  limitField: z.unknown().optional(), limitOver: z.unknown().optional(), undoWith: z.unknown().optional(),
68
70
  }),
69
- teammate_memory: z.strictObject({ teammate: id, memory: id, change: z.enum(["edit", "forget"]), text: z.unknown().optional() }),
70
- teammate_routine: z.strictObject({ teammate: id, change: z.enum(["add", "remove"]), routine: z.unknown().optional(), schedule: z.unknown().optional(), text: z.unknown().optional() }),
71
- teammate_undo: z.strictObject({ teammate: id, call: id }),
71
+ subagent_memory: z.strictObject({ subagent: id, memory: id, change: z.enum(["edit", "forget"]), text: z.unknown().optional() }),
72
+ subagent_routine: z.strictObject({ subagent: id, change: z.enum(["add", "remove"]), routine: z.unknown().optional(), schedule: z.unknown().optional(), text: z.unknown().optional() }),
73
+ subagent_undo: z.strictObject({ subagent: id, call: id }),
72
74
  kit_setup: z.strictObject({ repo: words, kit: words }),
73
75
  decision_record: z.strictObject({ repo: words, claim: words, why: words, supersedes: id.optional(), source: words.optional() }),
74
76
  decision_retire: z.strictObject({ repo: words, decision: id, reason: words }),
@@ -120,7 +122,23 @@ const under = (prefix, issues) => issues.map(issue => {
120
122
  return { ...issue, path, line: `${path}${issue.line.slice(issue.path.length)}` };
121
123
  });
122
124
  /** Legacy readers checked only the envelope. Confirmation still prepares the request again before acting. */
123
- export function readSharedAction(input) {
125
+ /**
126
+ * D5 (schema v117): a subagent action was a "teammate_…" one, naming its "teammate", before. A saved card reads in today's
127
+ * words, so a conversation's history still shows it; its stamp was made over the old words, so confirming it asks for
128
+ * a fresh proposal, as any card drafted before a change does.
129
+ */
130
+ function legacySubagentAction(input) {
131
+ if (input === null || typeof input !== "object" || Array.isArray(input))
132
+ return input;
133
+ const row = input;
134
+ if (typeof row["operation"] !== "string" || !row["operation"].startsWith("teammate_"))
135
+ return input;
136
+ const renamed = (value) => value === null || typeof value !== "object" || Array.isArray(value) ? value
137
+ : Object.fromEntries(Object.entries(value).map(([key, one]) => [key === "teammate" ? "subagent" : key, one]));
138
+ return { ...row, operation: `subagent_${row["operation"].slice("teammate_".length)}`, request: renamed(row["request"]), ...(Object.hasOwn(row, "state") ? { state: renamed(row["state"]) } : {}) };
139
+ }
140
+ export function readSharedAction(given) {
141
+ const input = legacySubagentAction(given);
124
142
  const read = readVersioned(sharedActionSchema, input, SHARED_ACTION_UPGRADES);
125
143
  if (!read.ok)
126
144
  return read;
@@ -67,7 +67,7 @@ export declare const chatContentShape: {
67
67
  entry: z.ZodInt;
68
68
  held: z.ZodString;
69
69
  }, z.core.$strict>>;
70
- /** A teammate's question's buttons ride this part (v93): each option, then one to answer in words (choice null). */
70
+ /** A subagent's question's buttons ride this part (v93): each option, then one to answer in words (choice null). */
71
71
  question: z.ZodOptional<z.ZodObject<{
72
72
  id: z.ZodInt;
73
73
  choices: z.ZodArray<z.ZodObject<{
@@ -37,7 +37,7 @@ export const chatContentShape = {
37
37
  choose: z.strictObject({ card: id, entry: z.int(), options: z.array(z.strictObject({ choice: z.int(), label: z.string() })) }).optional(),
38
38
  /** "Use this as your note?" Yes / No about the person's message `held` (an event id), for one choice's visit. */
39
39
  note: z.strictObject({ card: id, entry: z.int(), held: z.string() }).optional(),
40
- /** A teammate's question's buttons ride this part (v93): each option, then one to answer in words (choice null). */
40
+ /** A subagent's question's buttons ride this part (v93): each option, then one to answer in words (choice null). */
41
41
  question: z.strictObject({ id, choices: z.array(z.strictObject({ choice: z.string().nullable(), label: z.string() })) }).optional(),
42
42
  /** The lead's question to its owner rides this part: its options, then "Something else". */
43
43
  ask: z.strictObject({ turn: id, options: z.array(z.string()) }).optional(),
@@ -55,7 +55,7 @@ CREATE INDEX IF NOT EXISTS chat_part_message ON chat_part(provider, message);
55
55
  -- A button rides its planned part, or (Telegram) the binding, chat and message it was placed on.
56
56
  CREATE TABLE IF NOT EXISTS chat_action (
57
57
  ${PROVIDER}, token TEXT NOT NULL, part INTEGER, binding INTEGER, chat TEXT, message TEXT,
58
- proposal INTEGER REFERENCES mate_proposal(id) ON DELETE CASCADE,
58
+ proposal INTEGER REFERENCES lead_proposal(id) ON DELETE CASCADE,
59
59
  decision INTEGER REFERENCES decision(id) ON DELETE CASCADE, option_id TEXT, note_digest TEXT,
60
60
  phase TEXT NOT NULL CHECK (phase IN ('confirm','dismiss','yes','cancel','choose')),
61
61
  created TEXT, expires TEXT, consumed TEXT,
@@ -135,17 +135,17 @@ CREATE TABLE IF NOT EXISTS chat_flow_note (
135
135
  FOREIGN KEY (provider, part) REFERENCES chat_part(provider, id)
136
136
  );
137
137
  CREATE INDEX IF NOT EXISTS chat_flow_note_visit ON chat_flow_note(card, entry);
138
- -- A teammate's question: one button per option and one to answer in words (choice NULL).
138
+ -- A subagent's question: one button per option and one to answer in words (choice NULL).
139
139
  CREATE TABLE IF NOT EXISTS chat_question_action (
140
140
  ${PROVIDER}, token TEXT NOT NULL, part INTEGER, binding INTEGER, chat TEXT, message TEXT,
141
- question INTEGER NOT NULL REFERENCES teammate_question(id), choice TEXT, created TEXT, expires TEXT NOT NULL, consumed TEXT,
141
+ question INTEGER NOT NULL REFERENCES subagent_question(id), choice TEXT, created TEXT, expires TEXT NOT NULL, consumed TEXT,
142
142
  PRIMARY KEY (provider, token),
143
143
  FOREIGN KEY (provider, part) REFERENCES chat_part(provider, id)
144
144
  );
145
145
  CREATE INDEX IF NOT EXISTS chat_question_action_question ON chat_question_action(question);
146
146
  CREATE TABLE IF NOT EXISTS chat_question_prompt (
147
147
  ${PROVIDER}, id INTEGER NOT NULL, binding INTEGER NOT NULL, chat TEXT, message TEXT,
148
- question INTEGER NOT NULL REFERENCES teammate_question(id), created TEXT NOT NULL, expires TEXT NOT NULL, consumed TEXT,
148
+ question INTEGER NOT NULL REFERENCES subagent_question(id), created TEXT NOT NULL, expires TEXT NOT NULL, consumed TEXT,
149
149
  PRIMARY KEY (provider, id)
150
150
  );
151
151
  CREATE INDEX IF NOT EXISTS chat_question_prompt_open ON chat_question_prompt(provider, binding, consumed);
@@ -71,7 +71,7 @@ export declare const CONSOLE_FORMS: {
71
71
  readonly skillsRevise: FormContract<"feedback" | "nonce" | "repo" | "run">;
72
72
  readonly skillsChange: FormContract<"action" | "content" | "files" | "identity" | "method" | "nonce" | "repo" | "restore" | "revision" | "sample" | "sha" | "url">;
73
73
  readonly kit: FormContract<"repo">;
74
- readonly teammates: FormContract<"choice" | "dailyTurns" | "id" | "manager" | "model" | "name" | "note" | "op" | "repo" | "schedule" | "soul" | "state" | "template" | "text" | "tool" | `field.${string}` | `over.${string}` | `undo.${string}` | `use.${string}`>;
74
+ readonly subagents: FormContract<"choice" | "dailyTurns" | "id" | "manager" | "model" | "name" | "note" | "op" | "repo" | "schedule" | "soul" | "state" | "template" | "text" | "tool" | `field.${string}` | `over.${string}` | `undo.${string}` | `use.${string}`>;
75
75
  readonly galleryUse: FormContract<string>;
76
76
  readonly flows: FormContract<"about" | "address" | "answers" | "approve" | "body" | "choice" | "confirm" | "decision" | "definition" | "description" | "document" | "draft" | "entry" | "file" | "key" | "label" | "language" | "name" | "note" | "owner" | "password" | "previewed" | "remove" | "repo" | "revision" | "secret" | "stage" | "template" | "timeoutMinutes" | "title" | "trigger" | "url" | "value" | "watching" | `param.${string}`>;
77
77
  readonly spendBudget: FormContract<"action" | "password" | "stop" | "target" | "usd">;
@@ -140,11 +140,11 @@ export declare const CONSOLE_FORMS: {
140
140
  readonly leadAbout: FormContract<"about">;
141
141
  readonly leadPromiseCancel: FormContract<"promise">;
142
142
  readonly chatConfig: FormContract<"daily-turns" | "forget-key" | "key" | "model" | "off" | "provider" | "return" | "token" | "weekly-usd">;
143
- readonly mateMint: FormContract<"ceiling-usd" | "follow" | "return" | "token">;
144
- readonly mateFollow: FormContract<"enabled" | "return">;
145
- readonly mateEnd: FormContract<"return">;
146
- readonly mateStop: FormContract<"return" | "turn">;
147
- readonly mateProposal: FormContract<"confirm" | "nonce" | "return" | "token">;
143
+ readonly leadMint: FormContract<"ceiling-usd" | "follow" | "return" | "token">;
144
+ readonly leadFollow: FormContract<"enabled" | "return">;
145
+ readonly leadEnd: FormContract<"return">;
146
+ readonly leadStop: FormContract<"return" | "turn">;
147
+ readonly leadProposal: FormContract<"confirm" | "nonce" | "return" | "token">;
148
148
  readonly coordinatorProposal: FormContract<"confirm" | "return">;
149
149
  readonly chat: FormContract<"message" | "mode" | "project" | "request" | "request-session" | "result" | "task" | "token">;
150
150
  readonly chatFile: FormContract<"token">;
@@ -61,7 +61,7 @@ export function readForm(body, contract, report = toStderr) {
61
61
  // ---- every console form ------------------------------------------------------------------------------------------
62
62
  //
63
63
  // One per route (or per verb, where one route takes several bodies). A family whose sub-routes share one dispatch —
64
- // `/code/*`, `/flows/*`, the teammate pages, Slack/Teams/Discord — has one contract naming every field its handlers
64
+ // `/code/*`, `/flows/*`, the subagent pages, Slack/Teams/Discord — has one contract naming every field its handlers
65
65
  // read. Every body also passes through `mutationGuardForm` (the shared CSRF and duplicate check, unchanged) and
66
66
  // `ledgerTargetForm` (the project a request is recorded against).
67
67
  export const CONSOLE_FORMS = {
@@ -80,7 +80,7 @@ export const CONSOLE_FORMS = {
80
80
  skillsRevise: formContract("POST /settings/skills/revise", ["run", "repo", "feedback", "nonce"]),
81
81
  skillsChange: formContract("POST /settings/skills/import, /settings/skills/change", ["repo", "identity", "revision", "method", "content", "files", "url", "action", "sha", "sample", "nonce", "restore"]),
82
82
  kit: formContract("POST /kits/<kit>/<act>", ["repo"]),
83
- teammates: formContract("POST /teammates/new, /teammates/<id>/<part>, /teammates/questions/<id>/answer", ["choice", "text", "repo", "template", "name", "soul", "op", "id", "schedule", "tool", "state", "note", "model", "dailyTurns", "manager"], { prefixes: ["use.", "undo.", "field.", "over."] }),
83
+ subagents: formContract("POST /settings/lead/subagents/new, /settings/lead/subagents/<id>/<part>, /settings/lead/subagents/questions/<id>/answer", ["choice", "text", "repo", "template", "name", "soul", "op", "id", "schedule", "tool", "state", "note", "model", "dailyTurns", "manager"], { prefixes: ["use.", "undo.", "field.", "over."] }),
84
84
  galleryUse: formContract("POST /flows/new/<template>", ["repo", "send-result", "name", "intent", "previewed"], { dynamic: true }),
85
85
  flows: formContract("POST /flows/new, /flows/example, /flows/import, /flows/<id>/<act>, /flows/<id>/cards/<card>/<act>, /flows/<id>/triggers/<trigger>/<act>", ["repo", "document", "url", "confirm", "previewed", "template", "name", "answers", "password", "secret", "trigger", "approve", "remove", "about", "body", "timeoutMinutes", "language", "file", "address", "key", "definition", "owner", "revision", "value", "title", "description", "stage", "decision", "note", "draft", "choice", "entry", "label", "watching"], { prefixes: ["param."] }),
86
86
  spendBudget: formContract("POST /spend/budget", ["password", "target", "action", "usd", "stop"]),
@@ -149,11 +149,11 @@ export const CONSOLE_FORMS = {
149
149
  leadAbout: formContract("POST /settings/lead/about", ["about"]),
150
150
  leadPromiseCancel: formContract("POST /settings/lead/promise/cancel", ["promise"]),
151
151
  chatConfig: formContract("POST /chat/config", ["return", "token", "off", "forget-key", "provider", "model", "weekly-usd", "daily-turns", "key"]),
152
- mateMint: formContract("POST /chat/mate/mint", ["return", "ceiling-usd", "token", "follow"]),
153
- mateFollow: formContract("POST /chat/mate/follow", ["enabled", "return"]),
154
- mateEnd: formContract("POST /chat/mate/end", ["return"]),
155
- mateStop: formContract("POST /chat/mate/stop", ["return", "turn"]),
156
- mateProposal: formContract("POST /chat/proposal/<id>/confirm|dismiss", ["return", "nonce", "confirm", "token"]),
152
+ leadMint: formContract("POST /chat/mate/mint", ["return", "ceiling-usd", "token", "follow"]),
153
+ leadFollow: formContract("POST /chat/mate/follow", ["enabled", "return"]),
154
+ leadEnd: formContract("POST /chat/mate/end", ["return"]),
155
+ leadStop: formContract("POST /chat/mate/stop", ["return", "turn"]),
156
+ leadProposal: formContract("POST /chat/proposal/<id>/confirm|dismiss", ["return", "nonce", "confirm", "token"]),
157
157
  coordinatorProposal: formContract("POST /proposals/<id>/confirm|dismiss", ["return", "confirm"]),
158
158
  chat: formContract("POST /chat", ["task", "project", "result", "mode", "request", "request-session", "message", "token"]),
159
159
  chatFile: formContract("POST /chat/file/<id>", ["token"]),