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.
- package/CHANGELOG.md +10 -0
- package/README.md +24 -8
- package/dist/action-ledger.d.ts +2 -4
- package/dist/action-ledger.js +2 -10
- package/dist/approval-policy.d.ts +5 -5
- package/dist/approval-policy.js +4 -4
- package/dist/approval-rules-ui.js +2 -2
- package/dist/browser/workspace.js +5 -5
- package/dist/browser-workspace.d.ts +12 -12
- package/dist/budget-alerts.js +3 -3
- package/dist/chat-actions.d.ts +23 -18
- package/dist/chat-actions.js +110 -88
- package/dist/chat-ask.d.ts +2 -2
- package/dist/chat-ask.js +5 -5
- package/dist/chat-channel.d.ts +15 -15
- package/dist/chat-channel.js +20 -20
- package/dist/chat-controls.d.ts +0 -4
- package/dist/chat-controls.js +0 -1
- package/dist/chat-core.js +1 -1
- package/dist/chat-delivery-state.js +2 -2
- package/dist/chat-delivery.d.ts +2 -2
- package/dist/chat-delivery.js +27 -34
- package/dist/chat-flow.js +3 -3
- package/dist/chat-messages.d.ts +1 -1
- package/dist/chat-messages.js +4 -4
- package/dist/chat-migration.d.ts +1 -1
- package/dist/chat-migration.js +11 -11
- package/dist/chat-rooms.js +3 -3
- package/dist/chat-warmth.d.ts +2 -2
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +6 -7
- package/dist/contracts/chat-actions.d.ts +32 -27
- package/dist/contracts/chat-actions.js +29 -11
- package/dist/contracts/chat-content.d.ts +1 -1
- package/dist/contracts/chat-content.js +1 -1
- package/dist/contracts/chat-tables.js +4 -4
- package/dist/contracts/console-api.d.ts +6 -6
- package/dist/contracts/console-api.js +7 -7
- package/dist/contracts/flow-propose.d.ts +3 -3
- package/dist/contracts/flow.d.ts +132 -125
- package/dist/contracts/flow.js +27 -12
- package/dist/contracts/lead-context.d.ts +1 -1
- package/dist/contracts/lead-context.js +2 -2
- package/dist/contracts/lead-tools.d.ts +20 -18
- package/dist/contracts/lead-tools.js +15 -15
- package/dist/contracts/stage-output.d.ts +1 -1
- package/dist/contracts/stage-output.js +1 -1
- package/dist/contracts/store-json.d.ts +23 -22
- package/dist/contracts/store-json.js +17 -17
- package/dist/contracts/{teammate-turn.d.ts → subagent-turn.d.ts} +5 -5
- package/dist/contracts/{teammate-turn.js → subagent-turn.js} +7 -7
- package/dist/converse.d.ts +23 -23
- package/dist/converse.js +28 -28
- package/dist/coordinator-proposals.d.ts +2 -2
- package/dist/coordinator-proposals.js +1 -1
- package/dist/coordinator.js +1 -1
- package/dist/deprecations.d.ts +12 -0
- package/dist/deprecations.js +20 -0
- package/dist/desktop-update-gate.js +8 -4
- package/dist/desktop-update.js +6 -1
- package/dist/discord-chat.js +6 -6
- package/dist/dispatch.d.ts +1 -1
- package/dist/dispatch.js +1 -1
- package/dist/envelope.js +0 -1
- package/dist/export-ui.js +1 -1
- package/dist/export.js +7 -7
- package/dist/flow-engine.d.ts +2 -2
- package/dist/flow-engine.js +21 -21
- package/dist/flow-insights.d.ts +1 -1
- package/dist/flow-share.js +13 -8
- package/dist/flow-steps.d.ts +5 -5
- package/dist/flow-steps.js +19 -19
- package/dist/flow-triggers.d.ts +1 -1
- package/dist/flow-triggers.js +1 -1
- package/dist/flows-cli.js +1 -1
- package/dist/flows-ui.js +20 -20
- package/dist/flows.js +19 -19
- package/dist/guides.js +4 -3
- package/dist/invoke.js +1 -1
- package/dist/kits-ui.js +5 -5
- package/dist/kits.d.ts +12 -12
- package/dist/kits.js +31 -31
- package/dist/{mate-cli.d.ts → lead-cli.d.ts} +11 -11
- package/dist/{mate-cli.js → lead-cli.js} +38 -38
- package/dist/lead-commitments.js +4 -4
- package/dist/lead-context.d.ts +2 -2
- package/dist/lead-context.js +9 -9
- package/dist/{mate-contract.d.ts → lead-contract.d.ts} +3 -3
- package/dist/{mate-contract.js → lead-contract.js} +7 -6
- package/dist/{mate-doors.d.ts → lead-doors.d.ts} +6 -6
- package/dist/{mate-doors.js → lead-doors.js} +24 -24
- package/dist/lead-follow.d.ts +5 -5
- package/dist/lead-follow.js +9 -9
- package/dist/lead-people.d.ts +5 -5
- package/dist/lead-people.js +14 -14
- package/dist/{mate-progress.d.ts → lead-progress.d.ts} +9 -9
- package/dist/{mate-progress.js → lead-progress.js} +3 -3
- package/dist/{mate-tools.d.ts → lead-tools.d.ts} +19 -19
- package/dist/{mate-tools.js → lead-tools.js} +109 -101
- package/dist/{mate.d.ts → lead.d.ts} +27 -27
- package/dist/{mate.js → lead.js} +102 -102
- package/dist/limits-ui.d.ts +1 -1
- package/dist/limits-ui.js +1 -1
- package/dist/mcp-connect.d.ts +1 -1
- package/dist/mcp-connect.js +3 -3
- package/dist/mcp-core.js +1 -1
- package/dist/memory-pass.d.ts +2 -2
- package/dist/memory-pass.js +7 -7
- package/dist/notification-rows.d.ts +2 -2
- package/dist/operate.d.ts +3 -3
- package/dist/operate.js +35 -34
- package/dist/policy.d.ts +1 -1
- package/dist/principal.d.ts +1 -1
- package/dist/principal.js +2 -2
- package/dist/project-delete-ui.js +1 -1
- package/dist/project-delete.d.ts +1 -1
- package/dist/project-delete.js +26 -26
- package/dist/project-memory.js +1 -1
- package/dist/project-tools.d.ts +1 -1
- package/dist/project-tools.js +1 -1
- package/dist/recipe-ui.js +1 -1
- package/dist/retention.js +4 -4
- package/dist/scope.d.ts +2 -2
- package/dist/scout-report.d.ts +1 -1
- package/dist/scout-report.js +1 -1
- package/dist/serve.js +40 -45
- package/dist/server/chat.d.ts +4 -4
- package/dist/server/chat.js +97 -97
- package/dist/server/chrome.js +3 -3
- package/dist/server/flows.js +69 -62
- package/dist/server/guards.js +3 -3
- package/dist/server/http.d.ts +3 -3
- package/dist/server/http.js +1 -1
- package/dist/server/live.js +6 -6
- package/dist/server/people-tokens.js +1 -1
- package/dist/server/remote.js +12 -10
- package/dist/server/render-chat.d.ts +30 -30
- package/dist/server/render-chat.js +33 -33
- package/dist/server/render-pages.js +2 -2
- package/dist/server/render-settings.d.ts +7 -0
- package/dist/server/render-settings.js +3 -0
- package/dist/server/route-table.js +9 -7
- package/dist/server/runtime.d.ts +13 -13
- package/dist/server/settings.js +14 -13
- package/dist/server/tasks.js +6 -5
- package/dist/setup-guide.d.ts +2 -2
- package/dist/slack-chat.d.ts +2 -2
- package/dist/slack-chat.js +6 -6
- package/dist/spend-ui.d.ts +3 -3
- package/dist/spend-ui.js +9 -9
- package/dist/spend.d.ts +14 -14
- package/dist/spend.js +31 -31
- package/dist/store-baseline.d.ts +19 -0
- package/dist/store-baseline.js +2873 -0
- package/dist/store.d.ts +197 -249
- package/dist/store.js +747 -3084
- package/dist/subagent-admin.d.ts +53 -0
- package/dist/{teammate-admin.js → subagent-admin.js} +37 -37
- package/dist/{teammate-desk.d.ts → subagent-desk.d.ts} +22 -23
- package/dist/{teammate-desk.js → subagent-desk.js} +26 -35
- package/dist/{teammate-memory.d.ts → subagent-memory.d.ts} +17 -17
- package/dist/{teammate-memory.js → subagent-memory.js} +41 -41
- package/dist/{teammate-question.d.ts → subagent-question.d.ts} +6 -6
- package/dist/{teammate-question.js → subagent-question.js} +17 -17
- package/dist/{teammate-tools.d.ts → subagent-tools.d.ts} +18 -18
- package/dist/{teammate-tools.js → subagent-tools.js} +36 -36
- package/dist/{teammate-week.d.ts → subagent-week.d.ts} +10 -10
- package/dist/{teammate-week.js → subagent-week.js} +23 -23
- package/dist/{teammate-work.d.ts → subagent-work.d.ts} +21 -21
- package/dist/{teammate-work.js → subagent-work.js} +50 -50
- package/dist/subagents-ui.d.ts +22 -0
- package/dist/{teammates-ui.js → subagents-ui.js} +45 -45
- package/dist/{teammates.d.ts → subagents.d.ts} +11 -11
- package/dist/{teammates.js → subagents.js} +21 -21
- package/dist/subscription-chat.d.ts +13 -13
- package/dist/subscription-chat.js +19 -19
- package/dist/surface.d.ts +19 -2
- package/dist/surface.js +7 -31
- package/dist/team-cli.d.ts +1 -0
- package/dist/team-cli.js +14 -2
- package/dist/team-leads.js +13 -13
- package/dist/team-runtime.d.ts +2 -2
- package/dist/team-runtime.js +8 -8
- package/dist/teams-chat.js +6 -6
- package/dist/telegram-flow.d.ts +3 -3
- package/dist/telegram-flow.js +3 -3
- package/dist/{telegram-mate.d.ts → telegram-lead.d.ts} +5 -5
- package/dist/{telegram-mate.js → telegram-lead.js} +27 -27
- package/dist/telegram-status.js +1 -1
- package/dist/telegram-team.d.ts +1 -1
- package/dist/telegram-team.js +5 -5
- package/dist/telegram.d.ts +1 -1
- package/dist/telegram.js +9 -30
- package/dist/text-limits.d.ts +18 -18
- package/dist/text-limits.js +18 -18
- package/dist/toolroll-update.d.ts +8 -0
- package/dist/toolroll-update.js +45 -8
- package/package.json +1 -2
- package/dist/teammate-admin.d.ts +0 -53
- package/dist/teammates-ui.d.ts +0 -21
package/dist/chat-migration.d.ts
CHANGED
|
@@ -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). */
|
package/dist/chat-migration.js
CHANGED
|
@@ -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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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,
|
package/dist/chat-rooms.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { TeamLeads } from "./team-leads.js";
|
|
2
2
|
import { tooLongText } from "./chat-channel.js";
|
|
3
|
-
import {
|
|
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 >
|
|
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
|
|
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"]) }));
|
package/dist/chat-warmth.d.ts
CHANGED
|
@@ -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 {
|
|
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:
|
|
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
|
|
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
|
|
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
|
|
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
|
|
156
|
-
|
|
155
|
+
readonly subagent_soul: z.ZodObject<{
|
|
156
|
+
subagent: z.ZodInt;
|
|
157
157
|
soul: z.ZodString;
|
|
158
158
|
}, z.core.$strict>;
|
|
159
|
-
readonly
|
|
160
|
-
|
|
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
|
|
168
|
-
|
|
167
|
+
readonly subagent_note: z.ZodObject<{
|
|
168
|
+
subagent: z.ZodInt;
|
|
169
169
|
note: z.ZodString;
|
|
170
170
|
}, z.core.$strict>;
|
|
171
|
-
|
|
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
|
|
177
|
-
|
|
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
|
|
191
|
-
|
|
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
|
|
200
|
-
|
|
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
|
|
210
|
-
|
|
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
|
-
|
|
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
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
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
|
-
|
|
70
|
-
|
|
71
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
144
|
-
readonly
|
|
145
|
-
readonly
|
|
146
|
-
readonly
|
|
147
|
-
readonly
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
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"]),
|