switchroom 0.14.27 → 0.14.29

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 (28) hide show
  1. package/dist/cli/switchroom.js +20 -4
  2. package/dist/host-control/main.js +2 -2
  3. package/package.json +1 -1
  4. package/telegram-plugin/bridge/bridge.ts +15 -0
  5. package/telegram-plugin/card-format.ts +7 -4
  6. package/telegram-plugin/dist/bridge/bridge.js +18 -0
  7. package/telegram-plugin/dist/gateway/gateway.js +2151 -1729
  8. package/telegram-plugin/dist/server.js +18 -0
  9. package/telegram-plugin/gateway/gateway.ts +464 -12
  10. package/telegram-plugin/history.ts +16 -4
  11. package/telegram-plugin/permission-title.ts +48 -0
  12. package/telegram-plugin/registry/subagents-schema.ts +35 -0
  13. package/telegram-plugin/registry/subagents.test.ts +78 -0
  14. package/telegram-plugin/secret-detect/patterns.ts +8 -0
  15. package/telegram-plugin/secret-detect/redact.ts +76 -0
  16. package/telegram-plugin/session-tail.ts +15 -0
  17. package/telegram-plugin/subagent-watcher.ts +19 -1
  18. package/telegram-plugin/tests/card-format.test.ts +16 -0
  19. package/telegram-plugin/tests/gateway-outbound-redact.test.ts +80 -0
  20. package/telegram-plugin/tests/gateway-request-secret.test.ts +78 -0
  21. package/telegram-plugin/tests/history.test.ts +59 -0
  22. package/telegram-plugin/tests/permission-title.test.ts +68 -0
  23. package/telegram-plugin/tests/permission-verdict-resume-guard.test.ts +35 -0
  24. package/telegram-plugin/tests/secret-detect-sanctum.test.ts +115 -0
  25. package/telegram-plugin/tests/session-tail.test.ts +43 -0
  26. package/telegram-plugin/tests/worker-activity-feed.test.ts +15 -0
  27. package/telegram-plugin/uat/scenarios/jtbd-request-secret-dm.test.ts +101 -0
  28. package/telegram-plugin/worker-activity-feed.ts +5 -2
@@ -6557,10 +6557,10 @@ function stripMarkdown(s) {
6557
6557
  out = out.replace(/__(.+?)__/g, "$1");
6558
6558
  out = out.replace(/\*(.+?)\*/g, "$1");
6559
6559
  out = out.replace(/(?<![A-Za-z0-9])_(.+?)_(?![A-Za-z0-9])/g, "$1");
6560
- out = out.replace(/^\s{0,3}#{1,6}\s+/, "");
6561
- out = out.replace(/^\s{0,3}>\s?/, "");
6562
- out = out.replace(/^\s{0,3}[-*+]\s+/, "");
6563
- out = out.replace(/^\s{0,3}\d+[.)]\s+/, "");
6560
+ out = out.replace(/^\s{0,3}#{1,6}\s+/gm, "");
6561
+ out = out.replace(/^\s{0,3}>\s?/gm, "");
6562
+ out = out.replace(/^\s{0,3}[-*+]\s+/gm, "");
6563
+ out = out.replace(/^\s{0,3}\d+[.)]\s+/gm, "");
6564
6564
  out = out.replace(/\*\*/g, "");
6565
6565
  return out.trim();
6566
6566
  }
@@ -24866,1269 +24866,380 @@ var init_loader = __esm(() => {
24866
24866
  };
24867
24867
  });
24868
24868
 
24869
- // history.ts
24870
- var exports_history = {};
24871
- __export(exports_history, {
24872
- recordReaction: () => recordReaction,
24873
- recordOutbound: () => recordOutbound,
24874
- recordInbound: () => recordInbound,
24875
- recordEdit: () => recordEdit,
24876
- query: () => query,
24877
- pruneMessagesOlderThanDays: () => pruneMessagesOlderThanDays,
24878
- lookupMessageRoleAndText: () => lookupMessageRoleAndText,
24879
- initHistory: () => initHistory,
24880
- getRecentOutboundCount: () => getRecentOutboundCount,
24881
- getLatestInboundMessageId: () => getLatestInboundMessageId,
24882
- deleteFromHistory: () => deleteFromHistory,
24883
- checkpointWal: () => checkpointWal,
24884
- _resetForTests: () => _resetForTests
24885
- });
24886
- import { chmodSync as chmodSync2, mkdirSync as mkdirSync9 } from "fs";
24887
- import { join as join11 } from "path";
24888
- function loadDatabaseClass() {
24889
- if (DatabaseClass != null)
24890
- return DatabaseClass;
24891
- try {
24892
- const metaRequire = import.meta.require;
24893
- if (typeof metaRequire !== "function") {
24894
- throw new Error("import.meta.require not available \u2014 Bun runtime required");
24895
- }
24896
- const mod = metaRequire("bun:sqlite");
24897
- if (!mod.Database)
24898
- throw new Error("bun:sqlite did not export Database");
24899
- DatabaseClass = mod.Database;
24900
- return DatabaseClass;
24901
- } catch (err) {
24902
- throw new Error(`history.ts requires Bun runtime (bun:sqlite). Caller: ${err.message}`);
24903
- }
24904
- }
24905
- function initHistory(stateDir, retentionDays = 30) {
24906
- if (db != null)
24907
- return;
24908
- const Database = loadDatabaseClass();
24909
- mkdirSync9(stateDir, { recursive: true, mode: 448 });
24910
- const path = join11(stateDir, "history.db");
24911
- db = new Database(path, { create: true });
24912
- db.exec("PRAGMA journal_mode = WAL");
24913
- db.exec("PRAGMA synchronous = NORMAL");
24914
- db.exec(`
24915
- CREATE TABLE IF NOT EXISTS messages (
24916
- chat_id TEXT NOT NULL,
24917
- thread_id INTEGER,
24918
- message_id INTEGER NOT NULL,
24919
- role TEXT NOT NULL,
24920
- user TEXT,
24921
- user_id TEXT,
24922
- ts INTEGER NOT NULL,
24923
- text TEXT NOT NULL,
24924
- attachment_kind TEXT,
24925
- group_id INTEGER,
24926
- reply_to_message_id INTEGER,
24927
- reply_to_text TEXT,
24928
- PRIMARY KEY (chat_id, thread_id, message_id)
24929
- )
24930
- `);
24931
- db.exec(`
24932
- CREATE INDEX IF NOT EXISTS idx_messages_recent
24933
- ON messages (chat_id, thread_id, ts DESC)
24934
- `);
24935
- for (const column of ["reply_to_message_id INTEGER", "reply_to_text TEXT", "user_reaction TEXT"]) {
24936
- try {
24937
- db.exec(`ALTER TABLE messages ADD COLUMN ${column}`);
24938
- } catch (err) {
24939
- const msg = err instanceof Error ? err.message : String(err);
24940
- if (!/duplicate column name/i.test(msg))
24941
- throw err;
24869
+ // secret-detect/patterns.ts
24870
+ var ANCHORED_PATTERNS, STRUCTURED_PATTERNS, ALL_PATTERNS;
24871
+ var init_patterns = __esm(() => {
24872
+ ANCHORED_PATTERNS = [
24873
+ { rule_id: "anthropic_api_key", regex: /\b(sk-ant-[A-Za-z0-9_-]{8,})\b/g, captureIndex: 1, slugHint: "anthropic_api_key" },
24874
+ { rule_id: "anthropic_oauth_code", regex: /(?:^|\s)([A-Za-z0-9_-]{20,}#[A-Za-z0-9_-]{20,})(?=\s|$)/gm, captureIndex: 1, slugHint: "anthropic_oauth_code" },
24875
+ { rule_id: "openai_api_key", regex: /\b(sk-[A-Za-z0-9_-]{20,})\b/g, captureIndex: 1, slugHint: "openai_api_key" },
24876
+ { rule_id: "github_pat_classic", regex: /\b(ghp_[A-Za-z0-9]{20,})\b/g, captureIndex: 1, slugHint: "github_pat" },
24877
+ { rule_id: "github_pat_fine_grained", regex: /\b(github_pat_[A-Za-z0-9_]{20,})\b/g, captureIndex: 1, slugHint: "github_pat" },
24878
+ { rule_id: "slack_token", regex: /\b(xox[baprs]-[A-Za-z0-9-]{10,})\b/g, captureIndex: 1, slugHint: "slack_token" },
24879
+ { rule_id: "slack_app_token", regex: /\b(xapp-[A-Za-z0-9-]{10,})\b/g, captureIndex: 1, slugHint: "slack_app_token" },
24880
+ { rule_id: "groq_api_key", regex: /\b(gsk_[A-Za-z0-9_-]{10,})\b/g, captureIndex: 1, slugHint: "groq_api_key" },
24881
+ { rule_id: "google_api_key", regex: /\b(AIza[0-9A-Za-z\-_]{20,})\b/g, captureIndex: 1, slugHint: "google_api_key" },
24882
+ { rule_id: "perplexity_api_key", regex: /\b(pplx-[A-Za-z0-9_-]{10,})\b/g, captureIndex: 1, slugHint: "perplexity_api_key" },
24883
+ { rule_id: "npm_token", regex: /\b(npm_[A-Za-z0-9]{10,})\b/g, captureIndex: 1, slugHint: "npm_token" },
24884
+ { rule_id: "telegram_bot_token_prefixed", regex: /\bbot(\d{6,}:[A-Za-z0-9_-]{20,})\b/g, captureIndex: 1, slugHint: "telegram_bot_token" },
24885
+ { rule_id: "telegram_bot_token", regex: /\b(\d{6,}:[A-Za-z0-9_-]{20,})\b/g, captureIndex: 1, slugHint: "telegram_bot_token" },
24886
+ { rule_id: "laravel_sanctum_token", regex: /\b(\d+\|[A-Za-z0-9]{40,})\b/g, captureIndex: 1, slugHint: "api_token" },
24887
+ { rule_id: "aws_access_key", regex: /\b(AKIA[0-9A-Z]{16})\b/g, captureIndex: 1, slugHint: "aws_access_key" },
24888
+ { rule_id: "jwt", regex: /\b(eyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,})\b/g, captureIndex: 1, slugHint: "jwt" }
24889
+ ];
24890
+ STRUCTURED_PATTERNS = [
24891
+ {
24892
+ rule_id: "env_key_value",
24893
+ regex: /\b([A-Z0-9_]*(?:KEY|TOKEN|SECRET|PASSWORD|PASSWD))\b\s*[=:]\s*(["']?)([^\s"'\\]+)\2/g,
24894
+ captureIndex: 3,
24895
+ slugHint: "env"
24896
+ },
24897
+ {
24898
+ rule_id: "json_secret_field",
24899
+ regex: /"(?:apiKey|token|secret|password|passwd|accessToken|refreshToken)"\s*:\s*"([^"]+)"/g,
24900
+ captureIndex: 1,
24901
+ slugHint: "json_secret"
24902
+ },
24903
+ {
24904
+ rule_id: "cli_flag",
24905
+ regex: /--(?:api[-_]?key|hook[-_]?token|token|secret|password|passwd)\s+(["']?)([^\s"']+)\1/g,
24906
+ captureIndex: 2,
24907
+ slugHint: "cli_flag"
24908
+ },
24909
+ {
24910
+ rule_id: "bearer_auth_header",
24911
+ regex: /Authorization\s*[:=]\s*Bearer\s+([A-Za-z0-9._\-+=]+)/g,
24912
+ captureIndex: 1,
24913
+ slugHint: "bearer_token"
24914
+ },
24915
+ {
24916
+ rule_id: "bearer_loose",
24917
+ regex: /\bBearer\s+([A-Za-z0-9._\-+=]{18,})\b/g,
24918
+ captureIndex: 1,
24919
+ slugHint: "bearer_token"
24920
+ },
24921
+ {
24922
+ rule_id: "pem_private_key",
24923
+ regex: /-----BEGIN [A-Z ]*PRIVATE KEY-----[\s\S]+?-----END [A-Z ]*PRIVATE KEY-----/g,
24924
+ captureIndex: 0,
24925
+ slugHint: "pem_private_key"
24942
24926
  }
24927
+ ];
24928
+ ALL_PATTERNS = [...ANCHORED_PATTERNS, ...STRUCTURED_PATTERNS];
24929
+ });
24930
+
24931
+ // secret-detect/entropy.ts
24932
+ function shannonEntropy(s) {
24933
+ if (s.length === 0)
24934
+ return 0;
24935
+ const counts = new Map;
24936
+ for (const ch of s) {
24937
+ counts.set(ch, (counts.get(ch) ?? 0) + 1);
24943
24938
  }
24944
- try {
24945
- chmodSync2(path, 384);
24946
- } catch {}
24947
- if (retentionDays > 0) {
24948
- const cutoff = Math.floor(Date.now() / 1000) - retentionDays * 86400;
24949
- db.prepare("DELETE FROM messages WHERE ts < ?").run(cutoff);
24939
+ let h = 0;
24940
+ const len = s.length;
24941
+ for (const c of counts.values()) {
24942
+ const p = c / len;
24943
+ h -= p * Math.log2(p);
24950
24944
  }
24945
+ return h;
24951
24946
  }
24952
- function _resetForTests() {
24953
- if (db != null) {
24954
- db.close();
24955
- db = null;
24947
+
24948
+ // secret-detect/kv-scanner.ts
24949
+ function scanKeyValue(text) {
24950
+ const hits = [];
24951
+ KV_RE.lastIndex = 0;
24952
+ let m;
24953
+ while ((m = KV_RE.exec(text)) !== null) {
24954
+ const [, keyName, value] = m;
24955
+ if (!value)
24956
+ continue;
24957
+ const h = shannonEntropy(value);
24958
+ if (h < KV_ENTROPY_THRESHOLD)
24959
+ continue;
24960
+ const valueOffsetInMatch = m[0].indexOf(value, keyName.length);
24961
+ if (valueOffsetInMatch < 0)
24962
+ continue;
24963
+ const start = m.index + valueOffsetInMatch;
24964
+ const end = start + value.length;
24965
+ hits.push({
24966
+ rule_id: "kv_entropy",
24967
+ start,
24968
+ end,
24969
+ matched_text: value,
24970
+ key_name: keyName,
24971
+ confidence: "ambiguous"
24972
+ });
24956
24973
  }
24974
+ return hits;
24957
24975
  }
24958
- function checkpointWal() {
24959
- if (db == null)
24960
- return false;
24961
- try {
24962
- db.prepare("PRAGMA wal_checkpoint(TRUNCATE)").run();
24963
- return true;
24964
- } catch {
24965
- return false;
24976
+ var KV_RE, KV_ENTROPY_THRESHOLD = 4;
24977
+ var init_kv_scanner = __esm(() => {
24978
+ KV_RE = /\b([A-Za-z_][A-Za-z0-9_-]*(?:password|passwd|token|secret|key|api[_-]?key))\s*[:=]\s*["']?([^\s"'\\]{8,})["']?/gi;
24979
+ });
24980
+
24981
+ // secret-detect/chunker.ts
24982
+ function chunk(text) {
24983
+ if (text.length <= CHUNK_THRESHOLD) {
24984
+ return [{ offset: 0, text }];
24966
24985
  }
24967
- }
24968
- function pruneMessagesOlderThanDays(retentionDays, nowSec, batchLimit = 5000) {
24969
- if (db == null)
24970
- return 0;
24971
- if (retentionDays <= 0)
24972
- return 0;
24973
- const cutoffSec = (nowSec ?? Math.floor(Date.now() / 1000)) - retentionDays * 86400;
24974
- const stmt = db.prepare(`
24975
- DELETE FROM messages
24976
- WHERE rowid IN (
24977
- SELECT rowid FROM messages WHERE ts < ? LIMIT ?
24978
- )
24979
- `);
24980
- let total = 0;
24981
- for (let i = 0;i < 1000; i++) {
24982
- const result = stmt.run(cutoffSec, batchLimit);
24983
- const n = result.changes ?? 0;
24984
- total += n;
24985
- if (n === 0)
24986
+ const out = [];
24987
+ let offset = 0;
24988
+ while (offset < text.length) {
24989
+ const end = Math.min(offset + WINDOW_SIZE, text.length);
24990
+ out.push({ offset, text: text.slice(offset, end) });
24991
+ if (end >= text.length)
24986
24992
  break;
24993
+ offset = end - OVERLAP;
24987
24994
  }
24988
- return total;
24989
- }
24990
- function requireDb() {
24991
- if (db == null) {
24992
- throw new Error("history: initHistory() must be called before any record/query operation");
24993
- }
24994
- return db;
24995
- }
24996
- function recordInbound(args) {
24997
- if (args.message_id == null)
24998
- return;
24999
- const stmt = requireDb().prepare(`
25000
- INSERT OR REPLACE INTO messages
25001
- (chat_id, thread_id, message_id, role, user, user_id, ts, text, attachment_kind, group_id, reply_to_message_id, reply_to_text)
25002
- VALUES (?, ?, ?, 'user', ?, ?, ?, ?, ?, NULL, ?, ?)
25003
- `);
25004
- stmt.run(args.chat_id, args.thread_id ?? null, args.message_id, args.user ?? null, args.user_id ?? null, args.ts, args.text, args.attachment_kind ?? null, args.reply_to_message_id ?? null, args.reply_to_text ?? null);
25005
- }
25006
- function recordOutbound(args) {
25007
- if (args.message_ids.length === 0)
25008
- return;
25009
- const ts = args.ts ?? Math.floor(Date.now() / 1000);
25010
- const groupId = args.message_ids[0];
25011
- const stmt = requireDb().prepare(`
25012
- INSERT OR REPLACE INTO messages
25013
- (chat_id, thread_id, message_id, role, user, user_id, ts, text, attachment_kind, group_id)
25014
- VALUES (?, ?, ?, 'assistant', NULL, NULL, ?, ?, ?, ?)
25015
- `);
25016
- const tx = requireDb().transaction((rows2) => {
25017
- for (const [msgId, text, attachKind] of rows2) {
25018
- stmt.run(args.chat_id, args.thread_id ?? null, msgId, ts, text, attachKind, groupId);
25019
- }
25020
- });
25021
- const rows = args.message_ids.map((id, i) => [
25022
- id,
25023
- args.texts[i] ?? "",
25024
- args.attachment_kinds?.[i] ?? null
25025
- ]);
25026
- tx(rows);
24995
+ return out;
25027
24996
  }
25028
- function recordEdit(args) {
25029
- requireDb().prepare(`
25030
- UPDATE messages
25031
- SET text = ?
25032
- WHERE chat_id = ? AND message_id = ?
25033
- `).run(args.text, args.chat_id, args.message_id);
24997
+ var CHUNK_THRESHOLD, WINDOW_SIZE, OVERLAP = 1024;
24998
+ var init_chunker = __esm(() => {
24999
+ CHUNK_THRESHOLD = 32 * 1024;
25000
+ WINDOW_SIZE = 16 * 1024;
25001
+ });
25002
+
25003
+ // secret-detect/suppressor.ts
25004
+ function isSuppressed(text, start, end) {
25005
+ const left = Math.max(0, start - WINDOW);
25006
+ const right = Math.min(text.length, end + WINDOW);
25007
+ const context = text.slice(left, start) + text.slice(end, right);
25008
+ return MARKER_RE.test(context);
25034
25009
  }
25035
- function recordReaction(args) {
25036
- requireDb().prepare(`
25037
- UPDATE messages
25038
- SET user_reaction = ?
25039
- WHERE chat_id = ? AND message_id = ?
25040
- `).run(args.emoji, args.chat_id, args.message_id);
25010
+ var MARKERS, WINDOW = 40, MARKER_RE;
25011
+ var init_suppressor = __esm(() => {
25012
+ MARKERS = ["test", "mock", "example", "fixture", "dummy"];
25013
+ MARKER_RE = new RegExp(`\\b(?:${MARKERS.join("|")})\\b`, "i");
25014
+ });
25015
+
25016
+ // secret-detect/slug.ts
25017
+ function sanitizeKeyName(raw) {
25018
+ const up = raw.toUpperCase();
25019
+ const cleaned = up.replace(/[^A-Z0-9_]+/g, "_").replace(/_+/g, "_").replace(/^_+|_+$/g, "");
25020
+ return cleaned.length > 0 ? cleaned : "SECRET";
25041
25021
  }
25042
- function deleteFromHistory(args) {
25043
- requireDb().prepare(`
25044
- DELETE FROM messages
25045
- WHERE chat_id = ? AND message_id = ?
25046
- `).run(args.chat_id, args.message_id);
25022
+ function datePart(now = new Date) {
25023
+ const y = now.getUTCFullYear();
25024
+ const m = String(now.getUTCMonth() + 1).padStart(2, "0");
25025
+ const d = String(now.getUTCDate()).padStart(2, "0");
25026
+ return `${y}${m}${d}`;
25047
25027
  }
25048
- function getLatestInboundMessageId(chatId, threadId) {
25049
- const params = [chatId];
25050
- let sql = "SELECT message_id FROM messages WHERE chat_id = ? AND role = 'user'";
25051
- if (threadId !== undefined) {
25052
- if (threadId === null) {
25053
- sql += " AND thread_id IS NULL";
25054
- } else {
25055
- sql += " AND thread_id = ?";
25056
- params.push(threadId);
25057
- }
25028
+ function deriveSlug(inputs, existing) {
25029
+ let base;
25030
+ if (inputs.key_name && inputs.key_name.trim().length > 0) {
25031
+ base = sanitizeKeyName(inputs.key_name);
25032
+ } else {
25033
+ base = `${inputs.rule_id}_${datePart(inputs.now)}`;
25058
25034
  }
25059
- sql += " ORDER BY ts DESC, message_id DESC LIMIT 1";
25060
- const row = requireDb().prepare(sql).get(...params);
25061
- return row?.message_id ?? null;
25062
- }
25063
- function lookupMessageRoleAndText(chatId, messageId) {
25064
- const row = requireDb().prepare(`SELECT role, text FROM messages WHERE chat_id = ? AND message_id = ? LIMIT 1`).get(chatId, messageId);
25065
- if (!row)
25066
- return null;
25067
- return { role: row.role, text: row.text ?? "" };
25068
- }
25069
- function getRecentOutboundCount(chatId, withinSeconds) {
25070
- const cutoff = Math.floor(Date.now() / 1000) - withinSeconds;
25071
- const row = requireDb().prepare("SELECT COUNT(*) as cnt FROM messages WHERE chat_id = ? AND role = ? AND ts >= ?").get(chatId, "assistant", cutoff);
25072
- return row?.cnt ?? 0;
25035
+ if (!existing.has(base))
25036
+ return base;
25037
+ let n = 2;
25038
+ while (existing.has(`${base}_${n}`))
25039
+ n++;
25040
+ return `${base}_${n}`;
25073
25041
  }
25074
- function query(opts) {
25075
- const limit = Math.min(MAX_LIMIT, Math.max(1, opts.limit ?? DEFAULT_LIMIT));
25076
- const params = [opts.chat_id];
25077
- let sql = "SELECT * FROM messages WHERE chat_id = ?";
25078
- if (opts.thread_id !== undefined) {
25079
- if (opts.thread_id === null) {
25080
- sql += " AND thread_id IS NULL";
25081
- } else {
25082
- sql += " AND thread_id = ?";
25083
- params.push(opts.thread_id);
25084
- }
25085
- }
25086
- if (opts.before_message_id != null) {
25087
- sql += " AND message_id < ?";
25088
- params.push(opts.before_message_id);
25042
+
25043
+ // secret-detect/mask.ts
25044
+ function maskToken(s) {
25045
+ if (s.length >= 18) {
25046
+ return `${s.slice(0, 6)}...${s.slice(-4)}`;
25089
25047
  }
25090
- sql += " ORDER BY ts DESC, message_id DESC LIMIT ?";
25091
- params.push(limit);
25092
- const rows = requireDb().prepare(sql).all(...params);
25093
- rows.reverse();
25094
- return rows;
25048
+ return "***";
25095
25049
  }
25096
- var DatabaseClass = null, DEFAULT_LIMIT = 10, MAX_LIMIT = 50, db = null;
25097
- var init_history = () => {};
25098
25050
 
25099
- // quota-check.ts
25100
- import { readFileSync as readFileSync8, existsSync as existsSync12 } from "fs";
25101
- import { join as join12 } from "path";
25102
- function readOauthToken(claudeConfigDir) {
25103
- const tokenFile = join12(claudeConfigDir, ".oauth-token");
25104
- if (!existsSync12(tokenFile))
25105
- return null;
25051
+ // secret-detect/url-redact.ts
25052
+ function redactUrls(text) {
25053
+ return text.replace(URL_RE, (m) => {
25054
+ const redacted = redactOne(m);
25055
+ return redacted ?? m;
25056
+ });
25057
+ }
25058
+ function redactOne(raw) {
25059
+ let u;
25106
25060
  try {
25107
- const raw = readFileSync8(tokenFile, "utf-8").trim();
25108
- return raw.length > 0 ? raw : null;
25061
+ u = new URL(raw);
25109
25062
  } catch {
25110
25063
  return null;
25111
25064
  }
25112
- }
25113
- function parseFloatHeader(headers, name) {
25114
- const v = headers.get(name);
25115
- if (v == null || v.trim().length === 0)
25116
- return null;
25117
- const n = Number(v);
25118
- return Number.isFinite(n) ? n : null;
25119
- }
25120
- function parseEpochHeader(headers, name) {
25121
- const v = headers.get(name);
25122
- if (v == null)
25123
- return null;
25124
- const n = Number(v);
25125
- if (!Number.isFinite(n) || n <= 0)
25126
- return null;
25127
- return new Date(n * 1000);
25128
- }
25129
- function parseQuotaHeaders(headers) {
25130
- const fiveHour = parseFloatHeader(headers, "anthropic-ratelimit-unified-5h-utilization");
25131
- const sevenDay = parseFloatHeader(headers, "anthropic-ratelimit-unified-7d-utilization");
25132
- if (fiveHour == null && sevenDay == null) {
25133
- return {
25134
- ok: false,
25135
- reason: "no unified rate-limit headers in response (API token, not OAuth?)"
25136
- };
25137
- }
25138
- return {
25139
- ok: true,
25140
- data: {
25141
- fiveHourUtilizationPct: (fiveHour ?? 0) * 100,
25142
- sevenDayUtilizationPct: (sevenDay ?? 0) * 100,
25143
- fiveHourResetAt: parseEpochHeader(headers, "anthropic-ratelimit-unified-5h-reset"),
25144
- sevenDayResetAt: parseEpochHeader(headers, "anthropic-ratelimit-unified-7d-reset"),
25145
- representativeClaim: headers.get("anthropic-ratelimit-unified-representative-claim"),
25146
- overageStatus: headers.get("anthropic-ratelimit-unified-overage-status"),
25147
- overageDisabledReason: headers.get("anthropic-ratelimit-unified-overage-disabled-reason")
25065
+ let changed = false;
25066
+ if (u.username || u.password) {
25067
+ u.username = "***";
25068
+ u.password = "";
25069
+ changed = true;
25070
+ }
25071
+ for (const [key] of u.searchParams) {
25072
+ if (SENSITIVE_PARAMS.has(key.toLowerCase())) {
25073
+ u.searchParams.set(key, "***");
25074
+ changed = true;
25148
25075
  }
25149
- };
25150
- }
25151
- async function fetchQuota(opts) {
25152
- let token;
25153
- if (opts.accessToken && opts.claudeConfigDir) {
25154
- return {
25155
- ok: false,
25156
- reason: "pass only one of `accessToken` or `claudeConfigDir`, not both"
25157
- };
25158
- }
25159
- if (opts.accessToken) {
25160
- token = opts.accessToken.trim().length > 0 ? opts.accessToken : null;
25161
- } else if (opts.claudeConfigDir) {
25162
- token = readOauthToken(opts.claudeConfigDir);
25163
- } else {
25164
- return {
25165
- ok: false,
25166
- reason: "fetchQuota requires `accessToken` or `claudeConfigDir`"
25167
- };
25168
25076
  }
25169
- if (!token) {
25170
- return { ok: false, reason: "no OAuth token at .oauth-token" };
25077
+ return changed ? u.toString() : null;
25078
+ }
25079
+ var SENSITIVE_PARAMS, URL_RE;
25080
+ var init_url_redact = __esm(() => {
25081
+ SENSITIVE_PARAMS = new Set([
25082
+ "token",
25083
+ "key",
25084
+ "api_key",
25085
+ "apikey",
25086
+ "secret",
25087
+ "access_token",
25088
+ "password",
25089
+ "pass",
25090
+ "auth",
25091
+ "client_secret",
25092
+ "refresh_token",
25093
+ "signature"
25094
+ ]);
25095
+ URL_RE = /\b(?:https?|wss?|ftp):\/\/[^\s<>"']+/gi;
25096
+ });
25097
+
25098
+ // ../node_modules/.bun/boundary@2.0.0/node_modules/boundary/lib/index.js
25099
+ var require_lib = __commonJS((exports2) => {
25100
+ Object.defineProperty(exports2, "__esModule", { value: true });
25101
+ exports2.binarySearch = exports2.upperBound = exports2.lowerBound = exports2.compare = undefined;
25102
+ function compare(v1, v2) {
25103
+ return v1 < v2;
25171
25104
  }
25172
- const controller = new AbortController;
25173
- const timeout = setTimeout(() => controller.abort(), opts.timeoutMs ?? 1e4);
25174
- const fetchFn = opts.fetchImpl ?? fetch;
25175
- let resp;
25176
- try {
25177
- resp = await fetchFn("https://api.anthropic.com/v1/messages", {
25178
- method: "POST",
25179
- headers: {
25180
- "anthropic-version": "2023-06-01",
25181
- "anthropic-beta": OAUTH_BETA,
25182
- authorization: `Bearer ${token}`,
25183
- "x-app": "cli",
25184
- "user-agent": DEFAULT_USER_AGENT,
25185
- "content-type": "application/json"
25186
- },
25187
- body: JSON.stringify({
25188
- model: opts.model ?? DEFAULT_PROBE_MODEL,
25189
- max_tokens: 1,
25190
- messages: [{ role: "user", content: "hi" }]
25191
- }),
25192
- signal: controller.signal
25193
- });
25194
- } catch (err) {
25195
- const msg = err?.message ?? String(err);
25196
- return { ok: false, reason: `request failed: ${msg}` };
25197
- } finally {
25198
- clearTimeout(timeout);
25105
+ exports2.compare = compare;
25106
+ function upperBound(array, value, comp = compare) {
25107
+ let len = array.length;
25108
+ let i = 0;
25109
+ while (len) {
25110
+ let diff = len >>> 1;
25111
+ let cursor = i + diff;
25112
+ if (comp(value, array[cursor])) {
25113
+ len = diff;
25114
+ } else {
25115
+ i = cursor + 1;
25116
+ len -= diff + 1;
25117
+ }
25118
+ }
25119
+ return i;
25199
25120
  }
25200
- if (resp.status === 401 || resp.status === 403) {
25201
- return { ok: false, reason: `auth rejected (HTTP ${resp.status})` };
25121
+ exports2.upperBound = upperBound;
25122
+ function lowerBound(array, value, comp = compare) {
25123
+ let len = array.length;
25124
+ let i = 0;
25125
+ while (len) {
25126
+ let diff = len >>> 1;
25127
+ let cursor = i + diff;
25128
+ if (comp(array[cursor], value)) {
25129
+ i = cursor + 1;
25130
+ len -= diff + 1;
25131
+ } else {
25132
+ len = diff;
25133
+ }
25134
+ }
25135
+ return i;
25202
25136
  }
25203
- const parsed = parseQuotaHeaders(resp.headers);
25204
- if (!parsed.ok && resp.status >= 400) {
25205
- return { ok: false, reason: `HTTP ${resp.status}, ${parsed.reason}` };
25137
+ exports2.lowerBound = lowerBound;
25138
+ function binarySearch(array, value, comp = compare) {
25139
+ let cursor = lowerBound(array, value, comp);
25140
+ return cursor !== array.length && !comp(value, array[cursor]);
25206
25141
  }
25207
- return parsed;
25208
- }
25209
- function formatQuotaLine(q) {
25210
- const fmt = (n) => `${Math.round(n)}%`;
25211
- return `${fmt(q.fiveHourUtilizationPct)} / 5h \u00b7 ${fmt(q.sevenDayUtilizationPct)} / 7d`;
25212
- }
25213
- function formatResetRelative(target, now = new Date) {
25214
- if (!target)
25215
- return "\u2014";
25216
- const deltaMs = target.getTime() - now.getTime();
25217
- if (deltaMs <= 0)
25218
- return "resets now";
25219
- const totalMin = Math.round(deltaMs / 60000);
25220
- if (totalMin < 60)
25221
- return `resets in ${totalMin}m`;
25222
- const hours = Math.floor(totalMin / 60);
25223
- const mins = totalMin % 60;
25224
- if (hours < 24)
25225
- return mins > 0 ? `resets in ${hours}h ${mins}m` : `resets in ${hours}h`;
25226
- const days = Math.floor(hours / 24);
25227
- const remH = hours % 24;
25228
- return remH > 0 ? `resets in ${days}d ${remH}h` : `resets in ${days}d`;
25229
- }
25230
- var OAUTH_BETA = "oauth-2025-04-20", DEFAULT_USER_AGENT = "claude-cli/1.0.0 (external, cli)", DEFAULT_PROBE_MODEL = "claude-haiku-4-5-20251001";
25231
- var init_quota_check = () => {};
25142
+ exports2.binarySearch = binarySearch;
25143
+ });
25232
25144
 
25233
- // ../src/vault/broker/peercred-ffi.ts
25234
- function getPeerCred(fd) {
25235
- if (process.platform !== "linux")
25236
- return null;
25237
- try {
25238
- const ffi = __require("bun:ffi");
25239
- const { dlopen, FFIType, ptr } = ffi;
25240
- const SOL_SOCKET = 1;
25241
- const SO_PEERCRED = 17;
25242
- const UCRED_SIZE = 12;
25243
- const cache = getPeerCred;
25244
- const lib = cache._lib ?? (() => {
25245
- const candidates = ["libc.so.6", "libc.so"];
25246
- const symbolSpec = {
25247
- getsockopt: {
25248
- args: [FFIType.i32, FFIType.i32, FFIType.i32, FFIType.ptr, FFIType.ptr],
25249
- returns: FFIType.i32
25250
- }
25251
- };
25252
- const errors3 = [];
25253
- for (const name of candidates) {
25254
- try {
25255
- const opened = dlopen(name, symbolSpec);
25256
- cache._lib = opened;
25257
- return opened;
25258
- } catch (e) {
25259
- errors3.push(`${name}: ${e instanceof Error ? e.message : String(e)}`);
25145
+ // ../node_modules/.bun/structured-source@4.0.0/node_modules/structured-source/lib/structured-source.js
25146
+ var require_structured_source = __commonJS((exports2) => {
25147
+ Object.defineProperty(exports2, "__esModule", { value: true });
25148
+ exports2.StructuredSource = undefined;
25149
+ var boundary_1 = require_lib();
25150
+
25151
+ class StructuredSource {
25152
+ constructor(source) {
25153
+ this.indice = [0];
25154
+ let regexp = /[\r\n\u2028\u2029]/g;
25155
+ const length = source.length;
25156
+ regexp.lastIndex = 0;
25157
+ while (true) {
25158
+ let result = regexp.exec(source);
25159
+ if (!result) {
25160
+ break;
25161
+ }
25162
+ let index = result.index;
25163
+ if (source.charCodeAt(index) === 13 && source.charCodeAt(index + 1) === 10) {
25164
+ index += 1;
25165
+ }
25166
+ let nextIndex = index + 1;
25167
+ if (length < nextIndex) {
25168
+ break;
25260
25169
  }
25170
+ this.indice.push(nextIndex);
25171
+ regexp.lastIndex = nextIndex;
25261
25172
  }
25262
- process.stderr.write(`[vault-broker] peercred-ffi: dlopen failed for all libc candidates ` + `(${errors3.join("; ")}); falling back to ss-parsing.
25263
- `);
25264
- throw new Error("no libc candidate could be opened");
25265
- })();
25266
- const credBuf = new ArrayBuffer(UCRED_SIZE);
25267
- const lenBuf = new Uint32Array(1);
25268
- lenBuf[0] = UCRED_SIZE;
25269
- const rc = lib.symbols.getsockopt(fd, SOL_SOCKET, SO_PEERCRED, ptr(credBuf), ptr(lenBuf.buffer));
25270
- if (rc !== 0)
25271
- return null;
25272
- if (lenBuf[0] !== UCRED_SIZE)
25273
- return null;
25274
- const view = new DataView(credBuf);
25275
- return {
25276
- pid: view.getInt32(0, true),
25277
- uid: view.getInt32(4, true),
25278
- gid: view.getInt32(8, true)
25279
- };
25280
- } catch {
25281
- return null;
25282
- }
25283
- }
25284
-
25285
- // ../src/vault/broker/peercred.ts
25286
- function isReservedAgentName(name) {
25287
- return RESERVED_AGENT_NAMES.has(name);
25288
- }
25289
- function unlockSocketFor(dataSocketPath) {
25290
- if (dataSocketPath.endsWith("/sock")) {
25291
- return dataSocketPath.slice(0, -"/sock".length) + "/unlock";
25173
+ }
25174
+ get line() {
25175
+ return this.indice.length;
25176
+ }
25177
+ locationToRange(loc) {
25178
+ return [this.positionToIndex(loc.start), this.positionToIndex(loc.end)];
25179
+ }
25180
+ rangeToLocation(range) {
25181
+ return {
25182
+ start: this.indexToPosition(range[0]),
25183
+ end: this.indexToPosition(range[1])
25184
+ };
25185
+ }
25186
+ positionToIndex(pos) {
25187
+ let start = this.indice[pos.line - 1];
25188
+ return start + pos.column;
25189
+ }
25190
+ indexToPosition(index) {
25191
+ const startLine = (0, boundary_1.upperBound)(this.indice, index);
25192
+ return {
25193
+ line: startLine,
25194
+ column: index - this.indice[startLine - 1]
25195
+ };
25196
+ }
25292
25197
  }
25293
- return dataSocketPath.replace(/\.sock$/, ".unlock.sock");
25294
- }
25295
- var RESERVED_AGENT_NAMES;
25296
- var init_peercred = __esm(() => {
25297
- RESERVED_AGENT_NAMES = new Set(["operator", "hostd"]);
25198
+ exports2.StructuredSource = StructuredSource;
25199
+ });
25200
+ // ../node_modules/.bun/@secretlint+core@12.2.0/node_modules/@secretlint/core/module/SecretLintSourceCodeImpl.js
25201
+ var import_structured_source;
25202
+ var init_SecretLintSourceCodeImpl = __esm(() => {
25203
+ import_structured_source = __toESM(require_structured_source(), 1);
25298
25204
  });
25299
25205
 
25300
- // ../src/vault/broker/protocol.ts
25301
- function encodeRequest2(req) {
25302
- const json = JSON.stringify(req);
25303
- if (Buffer.byteLength(json, "utf8") > MAX_FRAME_BYTES2) {
25304
- throw new Error(`Request frame too large (${Buffer.byteLength(json, "utf8")} bytes; max ${MAX_FRAME_BYTES2})`);
25206
+ // ../node_modules/.bun/@secretlint+core@12.2.0/node_modules/@secretlint/core/module/helper/promise-event-emitter.js
25207
+ class EventEmitter {
25208
+ #listeners = new Map;
25209
+ on(type, listener) {
25210
+ const prevSet = this.#listeners.get(type);
25211
+ const listenerSet = prevSet ?? new Set;
25212
+ listenerSet?.add(listener);
25213
+ this.#listeners.set(type, listenerSet);
25305
25214
  }
25306
- return json + `
25307
- `;
25308
- }
25309
- function decodeResponse2(line) {
25310
- if (Buffer.byteLength(line, "utf8") > MAX_FRAME_BYTES2) {
25311
- throw new RangeError(`Response frame too large (${Buffer.byteLength(line, "utf8")} bytes; max ${MAX_FRAME_BYTES2})`);
25215
+ emit(type, ...args) {
25216
+ const listenerSet = this.#listeners.get(type);
25217
+ if (!listenerSet) {
25218
+ return;
25219
+ }
25220
+ for (const listenerSetElement of listenerSet) {
25221
+ listenerSetElement(...args);
25222
+ }
25312
25223
  }
25313
- const obj = JSON.parse(line);
25314
- return ResponseSchema2.parse(obj);
25315
- }
25316
- var MAX_FRAME_BYTES2, AgentNameSchema, GetRequestSchema, PutRequestSchema, ListRequestSchema, MintGrantRequestSchema, ListGrantsRequestSchema, RevokeGrantRequestSchema, StatusRequestSchema, LockRequestSchema, PreflightAccessRequestSchema, OkPreflightAccessResponseSchema, ApprovalRequestRequestSchema, ApprovalLookupRequestSchema, ApprovalConsumeRequestSchema, ApprovalRevokeRequestSchema, ApprovalListRequestSchema, ApprovalDecisionModeSchema, ApprovalRecordRequestSchema, ApprovalConsumeRecordRequestSchema, RequestSchema2, VaultEntrySchema, ErrorCode, OkEntryResponseSchema, OkKeysResponseSchema, BrokerStatus, OkStatusResponseSchema, OkLockResponseSchema, OkPutResponseSchema, OkMintGrantResponseSchema, GrantMetaSchema, OkListGrantsResponseSchema, OkRevokeGrantResponseSchema, OkApprovalRequestResponseSchema, ApprovalDecisionMetaSchema, OkApprovalLookupResponseSchema, OkApprovalConsumeResponseSchema, OkApprovalRevokeResponseSchema, OkApprovalListResponseSchema, OkApprovalRecordResponseSchema, OkApprovalConsumeRecordResponseSchema, ErrorResponseSchema2, ResponseSchema2;
25317
- var init_protocol2 = __esm(() => {
25318
- init_zod();
25319
- init_peercred();
25320
- MAX_FRAME_BYTES2 = 64 * 1024;
25321
- AgentNameSchema = exports_external.string().min(1).max(64, "agent name max 64 chars").regex(/^[a-zA-Z0-9][a-zA-Z0-9_-]*$/, "agent name must be kebab-case ASCII (alnum + _- only, first char alnum)").refine((s) => !isReservedAgentName(s), {
25322
- message: "agent name is reserved"
25323
- });
25324
- GetRequestSchema = exports_external.object({
25325
- v: exports_external.literal(1),
25326
- op: exports_external.literal("get"),
25327
- key: exports_external.string().min(1),
25328
- filename: exports_external.string().optional(),
25329
- token: exports_external.string().optional()
25330
- });
25331
- PutRequestSchema = exports_external.object({
25332
- v: exports_external.literal(1),
25333
- op: exports_external.literal("put"),
25334
- key: exports_external.string().min(1),
25335
- entry: exports_external.union([
25336
- exports_external.object({ kind: exports_external.literal("string"), value: exports_external.string() }),
25337
- exports_external.object({ kind: exports_external.literal("binary"), value: exports_external.string() })
25338
- ]),
25339
- token: exports_external.string().optional(),
25340
- passphrase: exports_external.string().optional(),
25341
- attest_via_posture: exports_external.boolean().optional()
25342
- });
25343
- ListRequestSchema = exports_external.object({
25344
- v: exports_external.literal(1),
25345
- op: exports_external.literal("list"),
25346
- token: exports_external.string().optional()
25347
- });
25348
- MintGrantRequestSchema = exports_external.object({
25349
- v: exports_external.literal(1),
25350
- op: exports_external.literal("mint_grant"),
25351
- agent: AgentNameSchema,
25352
- keys: exports_external.array(exports_external.string().min(1)),
25353
- ttl_seconds: exports_external.number().int().positive().nullable(),
25354
- description: exports_external.string().optional(),
25355
- write_keys: exports_external.array(exports_external.string().min(1)).optional(),
25356
- passphrase: exports_external.string().optional(),
25357
- attest_via_posture: exports_external.boolean().optional()
25358
- });
25359
- ListGrantsRequestSchema = exports_external.object({
25360
- v: exports_external.literal(1),
25361
- op: exports_external.literal("list_grants"),
25362
- agent: AgentNameSchema.optional(),
25363
- passphrase: exports_external.string().optional(),
25364
- attest_via_posture: exports_external.boolean().optional()
25365
- });
25366
- RevokeGrantRequestSchema = exports_external.object({
25367
- v: exports_external.literal(1),
25368
- op: exports_external.literal("revoke_grant"),
25369
- id: exports_external.string().min(1)
25370
- });
25371
- StatusRequestSchema = exports_external.object({
25372
- v: exports_external.literal(1),
25373
- op: exports_external.literal("status")
25374
- });
25375
- LockRequestSchema = exports_external.object({
25376
- v: exports_external.literal(1),
25377
- op: exports_external.literal("lock")
25378
- });
25379
- PreflightAccessRequestSchema = exports_external.object({
25380
- v: exports_external.literal(1),
25381
- op: exports_external.literal("preflight_access"),
25382
- agent: AgentNameSchema,
25383
- keys: exports_external.array(exports_external.string().min(1)).min(1).max(128)
25384
- });
25385
- OkPreflightAccessResponseSchema = exports_external.object({
25386
- ok: exports_external.literal(true),
25387
- op: exports_external.literal("preflight_access"),
25388
- results: exports_external.array(exports_external.object({
25389
- key: exports_external.string(),
25390
- exists: exports_external.boolean(),
25391
- acl_ok: exports_external.boolean(),
25392
- acl_reason: exports_external.string().optional(),
25393
- scope_ok: exports_external.boolean(),
25394
- scope_reason: exports_external.string().optional()
25395
- }))
25396
- });
25397
- ApprovalRequestRequestSchema = exports_external.object({
25398
- v: exports_external.literal(1),
25399
- op: exports_external.literal("approval_request"),
25400
- agent_unit: exports_external.string().min(1),
25401
- scope: exports_external.string().min(1),
25402
- action: exports_external.string().min(1),
25403
- approver_set: exports_external.array(exports_external.string()),
25404
- why: exports_external.string().optional(),
25405
- ttl_ms: exports_external.number().int().positive().optional()
25406
- });
25407
- ApprovalLookupRequestSchema = exports_external.object({
25408
- v: exports_external.literal(1),
25409
- op: exports_external.literal("approval_lookup"),
25410
- agent_unit: exports_external.string().min(1),
25411
- scope: exports_external.string().min(1),
25412
- action: exports_external.string().min(1),
25413
- current_approver_set: exports_external.array(exports_external.string())
25414
- });
25415
- ApprovalConsumeRequestSchema = exports_external.object({
25416
- v: exports_external.literal(1),
25417
- op: exports_external.literal("approval_consume"),
25418
- request_id: exports_external.string().regex(/^[0-9a-f]{32}$/)
25419
- });
25420
- ApprovalRevokeRequestSchema = exports_external.object({
25421
- v: exports_external.literal(1),
25422
- op: exports_external.literal("approval_revoke"),
25423
- decision_id: exports_external.string().min(1),
25424
- actor: exports_external.string().min(1),
25425
- reason: exports_external.string().optional()
25426
- });
25427
- ApprovalListRequestSchema = exports_external.object({
25428
- v: exports_external.literal(1),
25429
- op: exports_external.literal("approval_list"),
25430
- agent_unit: exports_external.string().optional()
25431
- });
25432
- ApprovalDecisionModeSchema = exports_external.enum([
25433
- "allow_once",
25434
- "allow_always",
25435
- "allow_ttl",
25436
- "deny",
25437
- "deny_perm"
25438
- ]);
25439
- ApprovalRecordRequestSchema = exports_external.object({
25440
- v: exports_external.literal(1),
25441
- op: exports_external.literal("approval_record"),
25442
- request_id: exports_external.string().regex(/^[0-9a-f]{32}$/),
25443
- decision: ApprovalDecisionModeSchema,
25444
- approver_set: exports_external.array(exports_external.string()),
25445
- granted_by_user_id: exports_external.number().int(),
25446
- ttl_ms: exports_external.number().int().positive().nullable().optional()
25447
- });
25448
- ApprovalConsumeRecordRequestSchema = exports_external.object({
25449
- v: exports_external.literal(1),
25450
- op: exports_external.literal("approval_consume_record"),
25451
- request_id: exports_external.string().regex(/^[0-9a-f]{32}$/),
25452
- decision: ApprovalDecisionModeSchema,
25453
- approver_set: exports_external.array(exports_external.string()),
25454
- granted_by_user_id: exports_external.number().int(),
25455
- ttl_ms: exports_external.number().int().positive().nullable().optional()
25456
- });
25457
- RequestSchema2 = exports_external.discriminatedUnion("op", [
25458
- GetRequestSchema,
25459
- PutRequestSchema,
25460
- ListRequestSchema,
25461
- StatusRequestSchema,
25462
- LockRequestSchema,
25463
- PreflightAccessRequestSchema,
25464
- MintGrantRequestSchema,
25465
- ListGrantsRequestSchema,
25466
- RevokeGrantRequestSchema,
25467
- ApprovalRequestRequestSchema,
25468
- ApprovalLookupRequestSchema,
25469
- ApprovalConsumeRequestSchema,
25470
- ApprovalRevokeRequestSchema,
25471
- ApprovalListRequestSchema,
25472
- ApprovalRecordRequestSchema,
25473
- ApprovalConsumeRecordRequestSchema
25474
- ]);
25475
- VaultEntrySchema = exports_external.union([
25476
- exports_external.object({ kind: exports_external.literal("string"), value: exports_external.string() }),
25477
- exports_external.object({ kind: exports_external.literal("binary"), value: exports_external.string() }),
25478
- exports_external.object({
25479
- kind: exports_external.literal("files"),
25480
- files: exports_external.record(exports_external.string(), exports_external.object({
25481
- encoding: exports_external.enum(["utf8", "base64"]),
25482
- value: exports_external.string()
25483
- }))
25484
- })
25485
- ]);
25486
- ErrorCode = exports_external.enum([
25487
- "LOCKED",
25488
- "DENIED",
25489
- "UNKNOWN_KEY",
25490
- "BAD_REQUEST",
25491
- "INTERNAL"
25492
- ]);
25493
- OkEntryResponseSchema = exports_external.object({
25494
- ok: exports_external.literal(true),
25495
- entry: VaultEntrySchema
25496
- });
25497
- OkKeysResponseSchema = exports_external.object({
25498
- ok: exports_external.literal(true),
25499
- keys: exports_external.array(exports_external.string())
25500
- });
25501
- BrokerStatus = exports_external.object({
25502
- unlocked: exports_external.boolean(),
25503
- keyCount: exports_external.number().int().nonnegative(),
25504
- uptimeSec: exports_external.number().nonnegative()
25505
- });
25506
- OkStatusResponseSchema = exports_external.object({
25507
- ok: exports_external.literal(true),
25508
- status: BrokerStatus
25509
- });
25510
- OkLockResponseSchema = exports_external.object({
25511
- ok: exports_external.literal(true),
25512
- locked: exports_external.literal(true)
25513
- });
25514
- OkPutResponseSchema = exports_external.object({
25515
- ok: exports_external.literal(true),
25516
- put: exports_external.literal(true),
25517
- key: exports_external.string()
25518
- });
25519
- OkMintGrantResponseSchema = exports_external.object({
25520
- ok: exports_external.literal(true),
25521
- token: exports_external.string(),
25522
- id: exports_external.string(),
25523
- expires_at: exports_external.number().nullable()
25524
- });
25525
- GrantMetaSchema = exports_external.object({
25526
- id: exports_external.string(),
25527
- agent_slug: exports_external.string(),
25528
- key_allow: exports_external.array(exports_external.string()),
25529
- write_allow: exports_external.array(exports_external.string()).default([]),
25530
- expires_at: exports_external.number().nullable(),
25531
- created_at: exports_external.number(),
25532
- description: exports_external.string().nullable()
25533
- });
25534
- OkListGrantsResponseSchema = exports_external.object({
25535
- ok: exports_external.literal(true),
25536
- grants: exports_external.array(GrantMetaSchema)
25537
- });
25538
- OkRevokeGrantResponseSchema = exports_external.object({
25539
- ok: exports_external.literal(true),
25540
- revoked: exports_external.boolean()
25541
- });
25542
- OkApprovalRequestResponseSchema = exports_external.discriminatedUnion("state", [
25543
- exports_external.object({
25544
- ok: exports_external.literal(true),
25545
- kind: exports_external.literal("approval_request"),
25546
- state: exports_external.literal("pending"),
25547
- request_id: exports_external.string(),
25548
- expires_at: exports_external.number()
25549
- }),
25550
- exports_external.object({
25551
- ok: exports_external.literal(true),
25552
- kind: exports_external.literal("approval_request"),
25553
- state: exports_external.literal("rate_limited"),
25554
- retry_after_ms: exports_external.number()
25555
- })
25556
- ]);
25557
- ApprovalDecisionMetaSchema = exports_external.object({
25558
- id: exports_external.string(),
25559
- agent_unit: exports_external.string(),
25560
- scope: exports_external.string(),
25561
- action: exports_external.string(),
25562
- decision: ApprovalDecisionModeSchema,
25563
- granted_at: exports_external.number(),
25564
- granted_by_user_id: exports_external.number(),
25565
- ttl_expires_at: exports_external.number().nullable(),
25566
- last_used_at: exports_external.number().nullable(),
25567
- revoked_at: exports_external.number().nullable(),
25568
- revoke_reason: exports_external.string().nullable()
25569
- });
25570
- OkApprovalLookupResponseSchema = exports_external.object({
25571
- ok: exports_external.literal(true),
25572
- state: exports_external.enum(["granted", "denied", "pending", "expired", "drift_revoked", "no_decision"]),
25573
- decision: ApprovalDecisionMetaSchema.nullable().optional()
25574
- });
25575
- OkApprovalConsumeResponseSchema = exports_external.object({
25576
- ok: exports_external.literal(true),
25577
- consumed: exports_external.boolean(),
25578
- agent_unit: exports_external.string().optional(),
25579
- scope: exports_external.string().optional(),
25580
- action: exports_external.string().optional(),
25581
- why: exports_external.string().nullable().optional()
25582
- });
25583
- OkApprovalRevokeResponseSchema = exports_external.object({
25584
- ok: exports_external.literal(true),
25585
- revoked: exports_external.boolean()
25586
- });
25587
- OkApprovalListResponseSchema = exports_external.object({
25588
- ok: exports_external.literal(true),
25589
- decisions: exports_external.array(ApprovalDecisionMetaSchema)
25590
- });
25591
- OkApprovalRecordResponseSchema = exports_external.object({
25592
- ok: exports_external.literal(true),
25593
- decision_id: exports_external.string()
25594
- });
25595
- OkApprovalConsumeRecordResponseSchema = exports_external.object({
25596
- ok: exports_external.literal(true),
25597
- consumed: exports_external.boolean(),
25598
- decision_id: exports_external.string().optional(),
25599
- agent_unit: exports_external.string().optional(),
25600
- scope: exports_external.string().optional(),
25601
- action: exports_external.string().optional(),
25602
- why: exports_external.string().nullable().optional()
25603
- });
25604
- ErrorResponseSchema2 = exports_external.object({
25605
- ok: exports_external.literal(false),
25606
- code: ErrorCode,
25607
- msg: exports_external.string()
25608
- });
25609
- ResponseSchema2 = exports_external.union([
25610
- OkEntryResponseSchema,
25611
- OkKeysResponseSchema,
25612
- OkStatusResponseSchema,
25613
- OkLockResponseSchema,
25614
- OkPreflightAccessResponseSchema,
25615
- OkPutResponseSchema,
25616
- OkMintGrantResponseSchema,
25617
- OkListGrantsResponseSchema,
25618
- OkRevokeGrantResponseSchema,
25619
- OkApprovalRequestResponseSchema,
25620
- OkApprovalLookupResponseSchema,
25621
- OkApprovalConsumeResponseSchema,
25622
- OkApprovalRevokeResponseSchema,
25623
- OkApprovalListResponseSchema,
25624
- OkApprovalRecordResponseSchema,
25625
- OkApprovalConsumeRecordResponseSchema,
25626
- ErrorResponseSchema2
25627
- ]);
25628
- });
25629
-
25630
- // ../src/runtime-mode.ts
25631
- function isDockerRuntime() {
25632
- return process.env.SWITCHROOM_RUNTIME === "docker";
25633
- }
25634
-
25635
- // ../src/vault/broker/client.ts
25636
- import * as net3 from "node:net";
25637
- import * as fs from "node:fs";
25638
- import { homedir as homedir7 } from "node:os";
25639
- import { join as join15 } from "node:path";
25640
- function defaultBrokerSocketPath() {
25641
- if (fs.existsSync(OPERATOR_SOCKET_PATH))
25642
- return OPERATOR_SOCKET_PATH;
25643
- if (isDockerRuntime())
25644
- return OPERATOR_SOCKET_PATH;
25645
- return LEGACY_SOCKET_PATH;
25646
- }
25647
- function vaultTokenFilePath(agentSlug) {
25648
- return join15(homedir7(), ".switchroom", "agents", agentSlug, ".vault-token");
25649
- }
25650
- function readVaultTokenFile(agentSlug) {
25651
- const filePath = vaultTokenFilePath(agentSlug);
25652
- try {
25653
- const stat = fs.statSync(filePath);
25654
- const mode = stat.mode & 511;
25655
- if ((mode & 63) !== 0) {
25656
- process.stderr.write(`[vault-broker] Refusing to read ${filePath} with mode ${mode.toString(8).padStart(3, "0")} ` + `(must be 0600). Delete the file and re-mint with 'switchroom vault grant mint <agent>'. ` + `Falling through to peercred ACL.
25657
- `);
25658
- return null;
25224
+ off(type, listener) {
25225
+ const listenerSet = this.#listeners.get(type);
25226
+ if (!listenerSet) {
25227
+ return;
25659
25228
  }
25660
- const raw = fs.readFileSync(filePath, "utf8");
25661
- const token = raw.split(`
25662
- `)[0].trim();
25663
- return token.length > 0 ? token : null;
25664
- } catch (err) {
25665
- const code = err.code;
25666
- if (code === "ENOENT") {
25667
- return null;
25229
+ for (const listenerSetElement of listenerSet) {
25230
+ if (listenerSetElement === listener) {
25231
+ listenerSet.delete(listener);
25232
+ }
25668
25233
  }
25669
- const reason = code === "EACCES" ? "permission denied" : err instanceof Error ? err.message : String(err);
25670
- process.stderr.write(`[vault-broker] Warning: could not read token file ${filePath}: ${reason}. ` + `Falling through to peercred ACL.
25671
- `);
25672
- return null;
25673
- }
25674
- }
25675
- function resolveBrokerSocketPath(opts) {
25676
- if (opts?.socket)
25677
- return opts.socket;
25678
- const env = process.env.SWITCHROOM_VAULT_BROKER_SOCK;
25679
- if (env)
25680
- return env;
25681
- if (opts?.vaultBrokerSocket)
25682
- return opts.vaultBrokerSocket;
25683
- return defaultBrokerSocketPath();
25684
- }
25685
- async function rpcRaw(req, opts) {
25686
- return rpc(req, opts);
25687
- }
25688
- async function rpc(req, opts) {
25689
- const socketPath = resolveBrokerSocketPath(opts);
25690
- const timeoutMs = opts?.timeoutMs ?? DEFAULT_TIMEOUT_MS3;
25691
- return new Promise((resolve5) => {
25692
- let settled = false;
25693
- const settle = (val) => {
25694
- if (settled)
25695
- return;
25696
- settled = true;
25697
- resolve5(val);
25698
- };
25699
- const client3 = new net3.Socket;
25700
- const timer3 = setTimeout(() => {
25701
- client3.destroy();
25702
- settle({ kind: "unreachable", msg: `broker did not respond within ${timeoutMs}ms` });
25703
- }, timeoutMs);
25704
- client3.on("error", (err) => {
25705
- clearTimeout(timer3);
25706
- const code = err.code ?? "ERR";
25707
- let msg;
25708
- if (code === "ENOENT")
25709
- msg = "broker socket not found (is the daemon running?)";
25710
- else if (code === "ECONNREFUSED")
25711
- msg = "broker socket exists but refused connection";
25712
- else if (code === "EACCES")
25713
- msg = "broker socket access denied (wrong UID?)";
25714
- else
25715
- msg = `broker connection failed: ${err.message}`;
25716
- settle({ kind: "unreachable", msg });
25717
- });
25718
- let buffer = "";
25719
- client3.on("data", (chunk) => {
25720
- buffer += chunk.toString("utf8");
25721
- const newlineIdx = buffer.indexOf(`
25722
- `);
25723
- if (newlineIdx !== -1) {
25724
- const line = buffer.slice(0, newlineIdx).trimEnd();
25725
- clearTimeout(timer3);
25726
- client3.destroy();
25727
- try {
25728
- const resp = decodeResponse2(line);
25729
- settle({ kind: "response", resp });
25730
- } catch (err) {
25731
- settle({
25732
- kind: "unreachable",
25733
- msg: `unparseable broker response: ${err instanceof Error ? err.message : String(err)}`
25734
- });
25735
- }
25736
- }
25737
- });
25738
- client3.on("connect", () => {
25739
- try {
25740
- client3.write(encodeRequest2(req));
25741
- } catch (err) {
25742
- clearTimeout(timer3);
25743
- client3.destroy();
25744
- settle({
25745
- kind: "unreachable",
25746
- msg: `failed to send request: ${err instanceof Error ? err.message : String(err)}`
25747
- });
25748
- }
25749
- });
25750
- client3.connect({ path: socketPath });
25751
- });
25752
- }
25753
- async function getViaBrokerStructured(key, opts) {
25754
- const token = opts?.token;
25755
- const result = await rpc({ v: 1, op: "get", key, ...token ? { token } : {} }, opts);
25756
- if (result.kind === "unreachable") {
25757
- return { kind: "unreachable", msg: result.msg };
25758
- }
25759
- const resp = result.resp;
25760
- if (resp.ok && "entry" in resp) {
25761
- return { kind: "ok", entry: resp.entry };
25762
- }
25763
- if (!resp.ok) {
25764
- if (resp.code === "UNKNOWN_KEY") {
25765
- return { kind: "not_found", code: resp.code, msg: resp.msg };
25766
- }
25767
- return { kind: "denied", code: resp.code, msg: resp.msg };
25768
- }
25769
- return { kind: "unreachable", msg: "unexpected broker response shape" };
25770
- }
25771
- async function putViaBroker(key, entry, opts) {
25772
- const token = opts?.token;
25773
- const passphrase = opts?.passphrase;
25774
- const attestViaPosture = opts?.attest_via_posture === true;
25775
- const result = await rpc({
25776
- v: 1,
25777
- op: "put",
25778
- key,
25779
- entry,
25780
- ...token ? { token } : {},
25781
- ...passphrase ? { passphrase } : {},
25782
- ...attestViaPosture ? { attest_via_posture: true } : {}
25783
- }, opts);
25784
- if (result.kind === "unreachable") {
25785
- return { kind: "unreachable", msg: result.msg };
25786
- }
25787
- const resp = result.resp;
25788
- if (resp.ok && "put" in resp) {
25789
- return { kind: "ok" };
25790
- }
25791
- if (!resp.ok) {
25792
- if (resp.code === "UNKNOWN_KEY") {
25793
- return { kind: "not_found", code: resp.code, msg: resp.msg };
25794
- }
25795
- return { kind: "denied", code: resp.code, msg: resp.msg };
25796
- }
25797
- return { kind: "unreachable", msg: "unexpected broker response shape" };
25798
- }
25799
- var DEFAULT_TIMEOUT_MS3 = 2000, LEGACY_SOCKET_PATH, OPERATOR_SOCKET_PATH;
25800
- var init_client2 = __esm(() => {
25801
- init_protocol2();
25802
- init_peercred();
25803
- LEGACY_SOCKET_PATH = join15(homedir7(), ".switchroom", "vault-broker.sock");
25804
- OPERATOR_SOCKET_PATH = join15(homedir7(), ".switchroom", "broker-operator", "sock");
25805
- });
25806
-
25807
- // ../src/drive/deep-links.ts
25808
- function classifyMimeType(mimeType) {
25809
- if (!mimeType)
25810
- return "file";
25811
- switch (mimeType) {
25812
- case "application/vnd.google-apps.document":
25813
- return "doc";
25814
- case "application/vnd.google-apps.spreadsheet":
25815
- return "spreadsheet";
25816
- case "application/vnd.google-apps.presentation":
25817
- return "presentation";
25818
- case "application/vnd.google-apps.form":
25819
- return "form";
25820
- case "application/vnd.google-apps.drawing":
25821
- return "drawing";
25822
- case "application/vnd.google-apps.folder":
25823
- return "folder";
25824
- default:
25825
- return "file";
25826
- }
25827
- }
25828
- function openInDriveUrl(options) {
25829
- validateDriveId2(options.fileId, "fileId");
25830
- if (options.discussionId !== undefined) {
25831
- validateDriveId2(options.discussionId, "discussionId");
25832
- }
25833
- const kind = options.isFolder ? "folder" : classifyMimeType(options.mimeType);
25834
- const base = baseUrlFor(kind, options.fileId);
25835
- if (options.discussionId !== undefined) {
25836
- return `${base}?disco=${encodeURIComponent(options.discussionId)}`;
25837
- }
25838
- return base;
25839
- }
25840
- function baseUrlFor(kind, fileId) {
25841
- switch (kind) {
25842
- case "doc":
25843
- return `https://docs.google.com/document/d/${fileId}/edit`;
25844
- case "spreadsheet":
25845
- return `https://docs.google.com/spreadsheets/d/${fileId}/edit`;
25846
- case "presentation":
25847
- return `https://docs.google.com/presentation/d/${fileId}/edit`;
25848
- case "form":
25849
- return `https://docs.google.com/forms/d/${fileId}/edit`;
25850
- case "drawing":
25851
- return `https://docs.google.com/drawings/d/${fileId}/edit`;
25852
- case "folder":
25853
- return `https://drive.google.com/drive/folders/${fileId}`;
25854
- case "file":
25855
- return `https://drive.google.com/file/d/${fileId}/view`;
25856
- }
25857
- }
25858
- function validateDriveId2(id, fieldName) {
25859
- if (id.length === 0) {
25860
- throw new Error(`Drive ${fieldName} must not be empty`);
25861
- }
25862
- if (!/^[A-Za-z0-9_-]+$/.test(id)) {
25863
- throw new Error(`Drive ${fieldName} '${id.slice(0, 30)}${id.length > 30 ? "\u2026" : ""}' contains invalid characters. Expected base64-url-safe (alphanumerics + - + _).`);
25864
- }
25865
- }
25866
- function openInDriveButton(options) {
25867
- return {
25868
- text: "\uD83D\uDCD6 Open in Drive",
25869
- url: openInDriveUrl(options)
25870
- };
25871
- }
25872
- function scopeToOpenInDriveButton(scope, mimeTypeHint) {
25873
- const parsed = parseDriveScope(scope);
25874
- if (parsed === null)
25875
- return null;
25876
- if (parsed.target.kind === "all")
25877
- return null;
25878
- if (parsed.target.kind === "folder") {
25879
- return openInDriveButton({ fileId: parsed.target.folder_id, isFolder: true });
25880
- }
25881
- return openInDriveButton({
25882
- fileId: parsed.target.doc_id,
25883
- mimeType: mimeTypeHint
25884
- });
25885
- }
25886
- function parseDriveScope(scope) {
25887
- if (!scope.startsWith("doc:gdrive:"))
25888
- return null;
25889
- let rest = scope.slice("doc:gdrive:".length);
25890
- let action = "read";
25891
- if (rest.startsWith("write:")) {
25892
- action = "write";
25893
- rest = rest.slice("write:".length);
25894
- } else if (rest.startsWith("suggest:")) {
25895
- action = "suggest";
25896
- rest = rest.slice("suggest:".length);
25897
- }
25898
- if (rest === "**")
25899
- return { action, target: { kind: "all" } };
25900
- if (rest.startsWith("folder/")) {
25901
- const tail = rest.slice("folder/".length);
25902
- if (!tail.endsWith("/**"))
25903
- return null;
25904
- const folder_id = tail.slice(0, -"/**".length);
25905
- if (!/^[A-Za-z0-9_-]+$/.test(folder_id))
25906
- return null;
25907
- return { action, target: { kind: "folder", folder_id } };
25908
- }
25909
- if (!/^[A-Za-z0-9_-]+$/.test(rest))
25910
- return null;
25911
- return { action, target: { kind: "doc", doc_id: rest } };
25912
- }
25913
-
25914
- // gateway/oversize-card-body.ts
25915
- function truncateRawToFit(input) {
25916
- const { raw, render, cap, sentinel } = input;
25917
- const hardLimit = input.hardLimit ?? cap + 196;
25918
- const fullBody = render(raw);
25919
- if (fullBody.length <= cap) {
25920
- return { body: fullBody, truncated: false };
25921
- }
25922
- let lo = 0;
25923
- let hi = raw.length;
25924
- let bestSliceLen = 0;
25925
- while (lo <= hi) {
25926
- const mid = lo + hi >>> 1;
25927
- const candidate = raw.slice(0, mid) + sentinel;
25928
- if (render(candidate).length <= cap) {
25929
- bestSliceLen = mid;
25930
- lo = mid + 1;
25931
- } else {
25932
- hi = mid - 1;
25933
- }
25934
- }
25935
- let chosenRaw = raw.slice(0, bestSliceLen);
25936
- const lastNl = chosenRaw.lastIndexOf(`
25937
- `);
25938
- if (lastNl > 0)
25939
- chosenRaw = chosenRaw.slice(0, lastNl);
25940
- let body = render(chosenRaw + sentinel);
25941
- if (body.length > hardLimit) {
25942
- body = body.slice(0, hardLimit - 1);
25943
- }
25944
- return { body, truncated: true };
25945
- }
25946
-
25947
- // secret-detect/suppressor.ts
25948
- function isSuppressed(text, start, end) {
25949
- const left = Math.max(0, start - WINDOW);
25950
- const right = Math.min(text.length, end + WINDOW);
25951
- const context = text.slice(left, start) + text.slice(end, right);
25952
- return MARKER_RE.test(context);
25953
- }
25954
- var MARKERS, WINDOW = 40, MARKER_RE;
25955
- var init_suppressor = __esm(() => {
25956
- MARKERS = ["test", "mock", "example", "fixture", "dummy"];
25957
- MARKER_RE = new RegExp(`\\b(?:${MARKERS.join("|")})\\b`, "i");
25958
- });
25959
-
25960
- // secret-detect/slug.ts
25961
- function sanitizeKeyName(raw) {
25962
- const up = raw.toUpperCase();
25963
- const cleaned = up.replace(/[^A-Z0-9_]+/g, "_").replace(/_+/g, "_").replace(/^_+|_+$/g, "");
25964
- return cleaned.length > 0 ? cleaned : "SECRET";
25965
- }
25966
- function datePart(now = new Date) {
25967
- const y = now.getUTCFullYear();
25968
- const m = String(now.getUTCMonth() + 1).padStart(2, "0");
25969
- const d = String(now.getUTCDate()).padStart(2, "0");
25970
- return `${y}${m}${d}`;
25971
- }
25972
- function deriveSlug(inputs, existing) {
25973
- let base;
25974
- if (inputs.key_name && inputs.key_name.trim().length > 0) {
25975
- base = sanitizeKeyName(inputs.key_name);
25976
- } else {
25977
- base = `${inputs.rule_id}_${datePart(inputs.now)}`;
25978
- }
25979
- if (!existing.has(base))
25980
- return base;
25981
- let n = 2;
25982
- while (existing.has(`${base}_${n}`))
25983
- n++;
25984
- return `${base}_${n}`;
25985
- }
25986
-
25987
- // ../node_modules/.bun/boundary@2.0.0/node_modules/boundary/lib/index.js
25988
- var require_lib = __commonJS((exports2) => {
25989
- Object.defineProperty(exports2, "__esModule", { value: true });
25990
- exports2.binarySearch = exports2.upperBound = exports2.lowerBound = exports2.compare = undefined;
25991
- function compare(v1, v2) {
25992
- return v1 < v2;
25993
- }
25994
- exports2.compare = compare;
25995
- function upperBound(array, value, comp = compare) {
25996
- let len = array.length;
25997
- let i = 0;
25998
- while (len) {
25999
- let diff = len >>> 1;
26000
- let cursor = i + diff;
26001
- if (comp(value, array[cursor])) {
26002
- len = diff;
26003
- } else {
26004
- i = cursor + 1;
26005
- len -= diff + 1;
26006
- }
26007
- }
26008
- return i;
26009
- }
26010
- exports2.upperBound = upperBound;
26011
- function lowerBound(array, value, comp = compare) {
26012
- let len = array.length;
26013
- let i = 0;
26014
- while (len) {
26015
- let diff = len >>> 1;
26016
- let cursor = i + diff;
26017
- if (comp(array[cursor], value)) {
26018
- i = cursor + 1;
26019
- len -= diff + 1;
26020
- } else {
26021
- len = diff;
26022
- }
26023
- }
26024
- return i;
26025
- }
26026
- exports2.lowerBound = lowerBound;
26027
- function binarySearch(array, value, comp = compare) {
26028
- let cursor = lowerBound(array, value, comp);
26029
- return cursor !== array.length && !comp(value, array[cursor]);
26030
- }
26031
- exports2.binarySearch = binarySearch;
26032
- });
26033
-
26034
- // ../node_modules/.bun/structured-source@4.0.0/node_modules/structured-source/lib/structured-source.js
26035
- var require_structured_source = __commonJS((exports2) => {
26036
- Object.defineProperty(exports2, "__esModule", { value: true });
26037
- exports2.StructuredSource = undefined;
26038
- var boundary_1 = require_lib();
26039
-
26040
- class StructuredSource {
26041
- constructor(source) {
26042
- this.indice = [0];
26043
- let regexp = /[\r\n\u2028\u2029]/g;
26044
- const length = source.length;
26045
- regexp.lastIndex = 0;
26046
- while (true) {
26047
- let result = regexp.exec(source);
26048
- if (!result) {
26049
- break;
26050
- }
26051
- let index = result.index;
26052
- if (source.charCodeAt(index) === 13 && source.charCodeAt(index + 1) === 10) {
26053
- index += 1;
26054
- }
26055
- let nextIndex = index + 1;
26056
- if (length < nextIndex) {
26057
- break;
26058
- }
26059
- this.indice.push(nextIndex);
26060
- regexp.lastIndex = nextIndex;
26061
- }
26062
- }
26063
- get line() {
26064
- return this.indice.length;
26065
- }
26066
- locationToRange(loc) {
26067
- return [this.positionToIndex(loc.start), this.positionToIndex(loc.end)];
26068
- }
26069
- rangeToLocation(range) {
26070
- return {
26071
- start: this.indexToPosition(range[0]),
26072
- end: this.indexToPosition(range[1])
26073
- };
26074
- }
26075
- positionToIndex(pos) {
26076
- let start = this.indice[pos.line - 1];
26077
- return start + pos.column;
26078
- }
26079
- indexToPosition(index) {
26080
- const startLine = (0, boundary_1.upperBound)(this.indice, index);
26081
- return {
26082
- line: startLine,
26083
- column: index - this.indice[startLine - 1]
26084
- };
26085
- }
26086
- }
26087
- exports2.StructuredSource = StructuredSource;
26088
- });
26089
- // ../node_modules/.bun/@secretlint+core@12.2.0/node_modules/@secretlint/core/module/SecretLintSourceCodeImpl.js
26090
- var import_structured_source;
26091
- var init_SecretLintSourceCodeImpl = __esm(() => {
26092
- import_structured_source = __toESM(require_structured_source(), 1);
26093
- });
26094
-
26095
- // ../node_modules/.bun/@secretlint+core@12.2.0/node_modules/@secretlint/core/module/helper/promise-event-emitter.js
26096
- class EventEmitter {
26097
- #listeners = new Map;
26098
- on(type, listener) {
26099
- const prevSet = this.#listeners.get(type);
26100
- const listenerSet = prevSet ?? new Set;
26101
- listenerSet?.add(listener);
26102
- this.#listeners.set(type, listenerSet);
26103
- }
26104
- emit(type, ...args) {
26105
- const listenerSet = this.#listeners.get(type);
26106
- if (!listenerSet) {
26107
- return;
26108
- }
26109
- for (const listenerSetElement of listenerSet) {
26110
- listenerSetElement(...args);
26111
- }
26112
- }
26113
- off(type, listener) {
26114
- const listenerSet = this.#listeners.get(type);
26115
- if (!listenerSet) {
26116
- return;
26117
- }
26118
- for (const listenerSetElement of listenerSet) {
26119
- if (listenerSetElement === listener) {
26120
- listenerSet.delete(listener);
26121
- }
26122
- }
26123
- }
26124
- removeAllListeners() {
26125
- this.#listeners.clear();
26126
- }
26127
- listenerCount(type) {
26128
- return this.#listeners.get(type)?.size ?? 0;
26129
- }
26130
- listeners(type) {
26131
- return Array.from(this.#listeners.get(type) ?? []);
25234
+ }
25235
+ removeAllListeners() {
25236
+ this.#listeners.clear();
25237
+ }
25238
+ listenerCount(type) {
25239
+ return this.#listeners.get(type)?.size ?? 0;
25240
+ }
25241
+ listeners(type) {
25242
+ return Array.from(this.#listeners.get(type) ?? []);
26132
25243
  }
26133
25244
  }
26134
25245
  // ../node_modules/.bun/@secretlint+core@12.2.0/node_modules/@secretlint/core/module/RuleContext.js
@@ -27665,253 +26776,1464 @@ function requireLodash_sortby() {
27665
26776
  return object[key] === srcValue && (srcValue !== undefined || (key in Object(object)));
27666
26777
  };
27667
26778
  }
27668
- var stringToPath = memoize(function(string) {
27669
- string = toString(string);
27670
- var result = [];
27671
- if (reLeadingDot.test(string)) {
27672
- result.push("");
27673
- }
27674
- string.replace(rePropName, function(match, number, quote, string2) {
27675
- result.push(quote ? string2.replace(reEscapeChar, "$1") : number || match);
27676
- });
27677
- return result;
27678
- });
27679
- function toKey(value) {
27680
- if (typeof value == "string" || isSymbol(value)) {
27681
- return value;
27682
- }
27683
- var result = value + "";
27684
- return result == "0" && 1 / value == -Infinity ? "-0" : result;
26779
+ var stringToPath = memoize(function(string) {
26780
+ string = toString(string);
26781
+ var result = [];
26782
+ if (reLeadingDot.test(string)) {
26783
+ result.push("");
26784
+ }
26785
+ string.replace(rePropName, function(match, number, quote, string2) {
26786
+ result.push(quote ? string2.replace(reEscapeChar, "$1") : number || match);
26787
+ });
26788
+ return result;
26789
+ });
26790
+ function toKey(value) {
26791
+ if (typeof value == "string" || isSymbol(value)) {
26792
+ return value;
26793
+ }
26794
+ var result = value + "";
26795
+ return result == "0" && 1 / value == -Infinity ? "-0" : result;
26796
+ }
26797
+ function toSource(func) {
26798
+ if (func != null) {
26799
+ try {
26800
+ return funcToString.call(func);
26801
+ } catch (e) {}
26802
+ try {
26803
+ return func + "";
26804
+ } catch (e) {}
26805
+ }
26806
+ return "";
26807
+ }
26808
+ var sortBy = baseRest(function(collection, iteratees) {
26809
+ if (collection == null) {
26810
+ return [];
26811
+ }
26812
+ var length = iteratees.length;
26813
+ if (length > 1 && isIterateeCall(collection, iteratees[0], iteratees[1])) {
26814
+ iteratees = [];
26815
+ } else if (length > 2 && isIterateeCall(iteratees[0], iteratees[1], iteratees[2])) {
26816
+ iteratees = [iteratees[0]];
26817
+ }
26818
+ return baseOrderBy(collection, baseFlatten(iteratees), []);
26819
+ });
26820
+ function memoize(func, resolver) {
26821
+ if (typeof func != "function" || resolver && typeof resolver != "function") {
26822
+ throw new TypeError(FUNC_ERROR_TEXT);
26823
+ }
26824
+ var memoized = function() {
26825
+ var args = arguments, key = resolver ? resolver.apply(this, args) : args[0], cache = memoized.cache;
26826
+ if (cache.has(key)) {
26827
+ return cache.get(key);
26828
+ }
26829
+ var result = func.apply(this, args);
26830
+ memoized.cache = cache.set(key, result);
26831
+ return result;
26832
+ };
26833
+ memoized.cache = new (memoize.Cache || MapCache);
26834
+ return memoized;
26835
+ }
26836
+ memoize.Cache = MapCache;
26837
+ function eq(value, other) {
26838
+ return value === other || value !== value && other !== other;
26839
+ }
26840
+ function isArguments(value) {
26841
+ return isArrayLikeObject(value) && hasOwnProperty.call(value, "callee") && (!propertyIsEnumerable.call(value, "callee") || objectToString.call(value) == argsTag);
26842
+ }
26843
+ var isArray2 = Array.isArray;
26844
+ function isArrayLike(value) {
26845
+ return value != null && isLength(value.length) && !isFunction2(value);
26846
+ }
26847
+ function isArrayLikeObject(value) {
26848
+ return isObjectLike(value) && isArrayLike(value);
26849
+ }
26850
+ function isFunction2(value) {
26851
+ var tag = isObject2(value) ? objectToString.call(value) : "";
26852
+ return tag == funcTag || tag == genTag;
26853
+ }
26854
+ function isLength(value) {
26855
+ return typeof value == "number" && value > -1 && value % 1 == 0 && value <= MAX_SAFE_INTEGER;
26856
+ }
26857
+ function isObject2(value) {
26858
+ var type = typeof value;
26859
+ return !!value && (type == "object" || type == "function");
26860
+ }
26861
+ function isObjectLike(value) {
26862
+ return !!value && typeof value == "object";
26863
+ }
26864
+ function isSymbol(value) {
26865
+ return typeof value == "symbol" || isObjectLike(value) && objectToString.call(value) == symbolTag;
26866
+ }
26867
+ var isTypedArray = nodeIsTypedArray ? baseUnary(nodeIsTypedArray) : baseIsTypedArray;
26868
+ function toString(value) {
26869
+ return value == null ? "" : baseToString(value);
26870
+ }
26871
+ function get(object, path, defaultValue) {
26872
+ var result = object == null ? undefined : baseGet(object, path);
26873
+ return result === undefined ? defaultValue : result;
26874
+ }
26875
+ function hasIn(object, path) {
26876
+ return object != null && hasPath(object, path, baseHasIn);
26877
+ }
26878
+ function keys(object) {
26879
+ return isArrayLike(object) ? arrayLikeKeys(object) : baseKeys(object);
26880
+ }
26881
+ function identity2(value) {
26882
+ return value;
26883
+ }
26884
+ function property(path) {
26885
+ return isKey(path) ? baseProperty(toKey(path)) : basePropertyDeep(path);
26886
+ }
26887
+ module.exports = sortBy;
26888
+ })(lodash_sortby, lodash_sortby.exports);
26889
+ return lodash_sortby.exports;
26890
+ }
26891
+ function requireEscapeStringRegexp() {
26892
+ if (hasRequiredEscapeStringRegexp)
26893
+ return escapeStringRegexp;
26894
+ hasRequiredEscapeStringRegexp = 1;
26895
+ escapeStringRegexp = (string) => {
26896
+ if (typeof string !== "string") {
26897
+ throw new TypeError("Expected a string");
26898
+ }
26899
+ return string.replace(/[|\\{}()[\]^$+*?.]/g, "\\$&").replace(/-/g, "\\x2d");
26900
+ };
26901
+ return escapeStringRegexp;
26902
+ }
26903
+ function requireRegexpParse() {
26904
+ if (hasRequiredRegexpParse)
26905
+ return regexpParse;
26906
+ hasRequiredRegexpParse = 1;
26907
+ Object.defineProperty(regexpParse, "__esModule", { value: true });
26908
+ regexpParse.isRegExpString = regexpParse.parseRegExpString = undefined;
26909
+ var REGEXP_LITERAL_PATTERN = /^\/(.+)\/([guimysd]*)$/;
26910
+ var parseRegExpString = function(str) {
26911
+ var result = str.match(REGEXP_LITERAL_PATTERN);
26912
+ if (!result) {
26913
+ return null;
26914
+ }
26915
+ return {
26916
+ source: result[1],
26917
+ flagString: result[2]
26918
+ };
26919
+ };
26920
+ regexpParse.parseRegExpString = parseRegExpString;
26921
+ var isRegExpString = function(str) {
26922
+ return REGEXP_LITERAL_PATTERN.test(str);
26923
+ };
26924
+ regexpParse.isRegExpString = isRegExpString;
26925
+ return regexpParse;
26926
+ }
26927
+ function requireRegexpStringMatcher() {
26928
+ if (hasRequiredRegexpStringMatcher)
26929
+ return regexpStringMatcher;
26930
+ hasRequiredRegexpStringMatcher = 1;
26931
+ (function(exports$1) {
26932
+ var __importDefault = regexpStringMatcher && regexpStringMatcher.__importDefault || function(mod) {
26933
+ return mod && mod.__esModule ? mod : { default: mod };
26934
+ };
26935
+ Object.defineProperty(exports$1, "__esModule", { value: true });
26936
+ exports$1.matchPatterns = exports$1.createRegExp = undefined;
26937
+ var lodash_uniq_1 = __importDefault(requireLodash_uniq());
26938
+ var lodash_uniqwith_1 = __importDefault(requireLodash_uniqwith());
26939
+ var lodash_sortby_1 = __importDefault(requireLodash_sortby());
26940
+ var escape_string_regexp_1 = __importDefault(requireEscapeStringRegexp());
26941
+ var regexp_parse_1 = requireRegexpParse();
26942
+ var DEFAULT_FLAGS = "ug";
26943
+ var defaultFlags = function(flagsString) {
26944
+ if (flagsString.length === 0) {
26945
+ return DEFAULT_FLAGS;
26946
+ }
26947
+ return (0, lodash_uniq_1.default)((flagsString + DEFAULT_FLAGS).split("")).join("");
26948
+ };
26949
+ var createRegExp = function(patternString, defaultFlag) {
26950
+ if (defaultFlag === undefined) {
26951
+ defaultFlag = DEFAULT_FLAGS;
26952
+ }
26953
+ if (patternString.length === 0) {
26954
+ throw new Error("Empty string can not handled");
26955
+ }
26956
+ if ((0, regexp_parse_1.isRegExpString)(patternString)) {
26957
+ var regExpStructure = (0, regexp_parse_1.parseRegExpString)(patternString);
26958
+ if (regExpStructure) {
26959
+ return new RegExp(regExpStructure.source, defaultFlags(regExpStructure.flagString));
26960
+ }
26961
+ throw new Error('"'.concat(patternString, '" can not parse as RegExp.'));
26962
+ } else {
26963
+ return new RegExp((0, escape_string_regexp_1.default)(patternString), defaultFlag);
26964
+ }
26965
+ };
26966
+ exports$1.createRegExp = createRegExp;
26967
+ var isEqualMatchPatternResult = function(a, b) {
26968
+ return a.startIndex === b.startIndex && a.endIndex === b.endIndex && a.match === b.match;
26969
+ };
26970
+ var matchPatterns = function(text, regExpLikeStrings) {
26971
+ var matchPatternResults = [];
26972
+ regExpLikeStrings.map(function(patternString) {
26973
+ return (0, exports$1.createRegExp)(patternString);
26974
+ }).forEach(function(regExp) {
26975
+ var results = text.matchAll(regExp);
26976
+ Array.from(results).forEach(function(result) {
26977
+ if (result.index === undefined) {
26978
+ return;
26979
+ }
26980
+ var match = result[0];
26981
+ var index = result.index;
26982
+ matchPatternResults.push({
26983
+ match,
26984
+ captures: result.slice(1),
26985
+ startIndex: index,
26986
+ endIndex: index + match.length
26987
+ });
26988
+ });
26989
+ });
26990
+ var uniqResults = (0, lodash_uniqwith_1.default)(matchPatternResults, isEqualMatchPatternResult);
26991
+ return (0, lodash_sortby_1.default)(uniqResults, ["startIndex", "endIndex"]);
26992
+ };
26993
+ exports$1.matchPatterns = matchPatterns;
26994
+ })(regexpStringMatcher);
26995
+ return regexpStringMatcher;
26996
+ }
26997
+ var commonjsGlobal, regexpStringMatcher, lodash_uniq, hasRequiredLodash_uniq, lodash_uniqwith, hasRequiredLodash_uniqwith, lodash_sortby, hasRequiredLodash_sortby, escapeStringRegexp, hasRequiredEscapeStringRegexp, regexpParse, hasRequiredRegexpParse, hasRequiredRegexpStringMatcher, regexpStringMatcherExports, OID_DATA, OID_SHA1, OID_SHA256, OID_SHA384, OID_SHA512, typeMap;
26998
+ var init_module2 = __esm(() => {
26999
+ commonjsGlobal = typeof globalThis !== "undefined" ? globalThis : typeof window !== "undefined" ? window : typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : {};
27000
+ regexpStringMatcher = {};
27001
+ lodash_sortby = { exports: {} };
27002
+ lodash_sortby.exports;
27003
+ regexpParse = {};
27004
+ regexpStringMatcherExports = requireRegexpStringMatcher();
27005
+ OID_DATA = new Uint8Array([42, 134, 72, 134, 247, 13, 1, 7, 1]);
27006
+ OID_SHA1 = new Uint8Array([43, 14, 3, 2, 26]);
27007
+ OID_SHA256 = new Uint8Array([96, 134, 72, 1, 101, 3, 4, 2, 1]);
27008
+ OID_SHA384 = new Uint8Array([96, 134, 72, 1, 101, 3, 4, 2, 2]);
27009
+ OID_SHA512 = new Uint8Array([96, 134, 72, 1, 101, 3, 4, 2, 3]);
27010
+ typeMap = new Map([
27011
+ ["ghp", "GitHub personal access tokens"],
27012
+ ["gho", "OAuth access tokens"],
27013
+ ["ghu", "GitHub user-to-server tokens"],
27014
+ ["ghs", "GitHub server-to-server tokens"],
27015
+ ["ghr", "refresh tokens"],
27016
+ ["github_pat", "fine-grained personal access tokens"]
27017
+ ]);
27018
+ });
27019
+
27020
+ // secret-detect/secretlint-source.ts
27021
+ var init_secretlint_source = __esm(() => {
27022
+ init_module();
27023
+ init_module2();
27024
+ init_suppressor();
27025
+ });
27026
+
27027
+ // secret-detect/index.ts
27028
+ function detectSecrets(text) {
27029
+ if (!text || text.length === 0)
27030
+ return [];
27031
+ const windows = chunk(text);
27032
+ const raw = [];
27033
+ for (const win of windows) {
27034
+ for (const p of ALL_PATTERNS) {
27035
+ const re = new RegExp(p.regex.source, p.regex.flags.includes("g") ? p.regex.flags : p.regex.flags + "g");
27036
+ let m;
27037
+ while ((m = re.exec(win.text)) !== null) {
27038
+ if (m[0].length === 0) {
27039
+ re.lastIndex++;
27040
+ continue;
27041
+ }
27042
+ const cap = p.captureIndex === 0 ? m[0] : m[p.captureIndex];
27043
+ if (!cap)
27044
+ continue;
27045
+ const matchStart = p.captureIndex === 0 ? m.index : m.index + m[0].indexOf(cap);
27046
+ if (matchStart < 0)
27047
+ continue;
27048
+ const globalStart = win.offset + matchStart;
27049
+ const globalEnd = globalStart + cap.length;
27050
+ const keyName = p.rule_id === "env_key_value" ? m[1] : undefined;
27051
+ if (p.rule_id === "env_key_value") {
27052
+ const ENV_KV_MIN_LEN = 12;
27053
+ const ENV_KV_MIN_ENTROPY = 3.5;
27054
+ if (cap.length < ENV_KV_MIN_LEN)
27055
+ continue;
27056
+ if (shannonEntropy(cap) < ENV_KV_MIN_ENTROPY)
27057
+ continue;
27058
+ }
27059
+ raw.push({
27060
+ rule_id: p.rule_id,
27061
+ start: globalStart,
27062
+ end: globalEnd,
27063
+ matched_text: cap,
27064
+ key_name: keyName,
27065
+ confidence: "high"
27066
+ });
27067
+ }
27068
+ }
27069
+ const kvHits = scanKeyValue(win.text);
27070
+ for (const h of kvHits) {
27071
+ raw.push({ ...h, start: h.start + win.offset, end: h.end + win.offset });
27072
+ }
27073
+ }
27074
+ const deduped = dedupeRaw(raw);
27075
+ const final = dropOverlaps(deduped);
27076
+ const existing = new Set;
27077
+ const out = [];
27078
+ for (const h of final) {
27079
+ const suggested_slug = deriveSlug({ key_name: h.key_name, rule_id: h.rule_id }, existing);
27080
+ existing.add(suggested_slug);
27081
+ out.push({
27082
+ rule_id: h.rule_id,
27083
+ matched_text: h.matched_text,
27084
+ start: h.start,
27085
+ end: h.end,
27086
+ confidence: h.confidence,
27087
+ suppressed: isSuppressed(text, h.start, h.end),
27088
+ suggested_slug,
27089
+ key_name: h.key_name
27090
+ });
27091
+ }
27092
+ out.sort((a, b) => a.start - b.start);
27093
+ return out;
27094
+ }
27095
+ function dedupeRaw(raw) {
27096
+ const seen = new Map;
27097
+ for (const h of raw) {
27098
+ const key = `${h.start}:${h.end}`;
27099
+ const existing = seen.get(key);
27100
+ if (!existing) {
27101
+ seen.set(key, h);
27102
+ continue;
27103
+ }
27104
+ if (existing.confidence === "ambiguous" && h.confidence === "high") {
27105
+ seen.set(key, h);
27106
+ }
27107
+ }
27108
+ return Array.from(seen.values());
27109
+ }
27110
+ function dropOverlaps(hits) {
27111
+ const sorted = [...hits].sort((a, b) => a.end - a.start - (b.end - b.start));
27112
+ const out = [];
27113
+ for (const h of sorted) {
27114
+ const contained = out.some((existing) => existing !== h && existing.start <= h.start && existing.end >= h.end && !(existing.start === h.start && existing.end === h.end));
27115
+ if (!contained)
27116
+ out.push(h);
27117
+ }
27118
+ out.sort((a, b) => a.start - b.start || a.end - b.end);
27119
+ return out;
27120
+ }
27121
+ var init_secret_detect = __esm(() => {
27122
+ init_patterns();
27123
+ init_kv_scanner();
27124
+ init_chunker();
27125
+ init_suppressor();
27126
+ init_url_redact();
27127
+ init_secretlint_source();
27128
+ });
27129
+
27130
+ // secret-detect/redact.ts
27131
+ function redact(text) {
27132
+ if (!text || text.length === 0)
27133
+ return text;
27134
+ const urlScrubbed = redactUrls(text);
27135
+ const hits = detectSecrets(urlScrubbed);
27136
+ if (hits.length === 0)
27137
+ return urlScrubbed;
27138
+ const sorted = [...hits].sort((a, b) => b.start - a.start);
27139
+ let out = urlScrubbed;
27140
+ for (const h of sorted) {
27141
+ out = out.slice(0, h.start) + redactedMarker(h.rule_id) + out.slice(h.end);
27142
+ }
27143
+ return out;
27144
+ }
27145
+ function redactedMarker(ruleId) {
27146
+ const trimmed = ruleId.replace(/^(kv|env)_/, "");
27147
+ if (!trimmed || trimmed === "key_value" || trimmed === "kv_entropy") {
27148
+ return REDACTED_MARKER;
27149
+ }
27150
+ return `[REDACTED:${trimmed}]`;
27151
+ }
27152
+ var REDACTED_MARKER = "[REDACTED]";
27153
+ var init_redact = __esm(() => {
27154
+ init_secret_detect();
27155
+ init_url_redact();
27156
+ });
27157
+
27158
+ // history.ts
27159
+ var exports_history = {};
27160
+ __export(exports_history, {
27161
+ recordReaction: () => recordReaction,
27162
+ recordOutbound: () => recordOutbound,
27163
+ recordInbound: () => recordInbound,
27164
+ recordEdit: () => recordEdit,
27165
+ query: () => query,
27166
+ pruneMessagesOlderThanDays: () => pruneMessagesOlderThanDays,
27167
+ lookupMessageRoleAndText: () => lookupMessageRoleAndText,
27168
+ initHistory: () => initHistory,
27169
+ getRecentOutboundCount: () => getRecentOutboundCount,
27170
+ getLatestInboundMessageId: () => getLatestInboundMessageId,
27171
+ deleteFromHistory: () => deleteFromHistory,
27172
+ checkpointWal: () => checkpointWal,
27173
+ _resetForTests: () => _resetForTests
27174
+ });
27175
+ import { chmodSync as chmodSync2, mkdirSync as mkdirSync9 } from "fs";
27176
+ import { join as join11 } from "path";
27177
+ function loadDatabaseClass() {
27178
+ if (DatabaseClass != null)
27179
+ return DatabaseClass;
27180
+ try {
27181
+ const metaRequire = import.meta.require;
27182
+ if (typeof metaRequire !== "function") {
27183
+ throw new Error("import.meta.require not available \u2014 Bun runtime required");
27184
+ }
27185
+ const mod = metaRequire("bun:sqlite");
27186
+ if (!mod.Database)
27187
+ throw new Error("bun:sqlite did not export Database");
27188
+ DatabaseClass = mod.Database;
27189
+ return DatabaseClass;
27190
+ } catch (err) {
27191
+ throw new Error(`history.ts requires Bun runtime (bun:sqlite). Caller: ${err.message}`);
27192
+ }
27193
+ }
27194
+ function initHistory(stateDir, retentionDays = 30) {
27195
+ if (db != null)
27196
+ return;
27197
+ const Database = loadDatabaseClass();
27198
+ mkdirSync9(stateDir, { recursive: true, mode: 448 });
27199
+ const path = join11(stateDir, "history.db");
27200
+ db = new Database(path, { create: true });
27201
+ db.exec("PRAGMA journal_mode = WAL");
27202
+ db.exec("PRAGMA synchronous = NORMAL");
27203
+ db.exec(`
27204
+ CREATE TABLE IF NOT EXISTS messages (
27205
+ chat_id TEXT NOT NULL,
27206
+ thread_id INTEGER,
27207
+ message_id INTEGER NOT NULL,
27208
+ role TEXT NOT NULL,
27209
+ user TEXT,
27210
+ user_id TEXT,
27211
+ ts INTEGER NOT NULL,
27212
+ text TEXT NOT NULL,
27213
+ attachment_kind TEXT,
27214
+ group_id INTEGER,
27215
+ reply_to_message_id INTEGER,
27216
+ reply_to_text TEXT,
27217
+ PRIMARY KEY (chat_id, thread_id, message_id)
27218
+ )
27219
+ `);
27220
+ db.exec(`
27221
+ CREATE INDEX IF NOT EXISTS idx_messages_recent
27222
+ ON messages (chat_id, thread_id, ts DESC)
27223
+ `);
27224
+ for (const column of ["reply_to_message_id INTEGER", "reply_to_text TEXT", "user_reaction TEXT"]) {
27225
+ try {
27226
+ db.exec(`ALTER TABLE messages ADD COLUMN ${column}`);
27227
+ } catch (err) {
27228
+ const msg = err instanceof Error ? err.message : String(err);
27229
+ if (!/duplicate column name/i.test(msg))
27230
+ throw err;
27231
+ }
27232
+ }
27233
+ try {
27234
+ chmodSync2(path, 384);
27235
+ } catch {}
27236
+ if (retentionDays > 0) {
27237
+ const cutoff = Math.floor(Date.now() / 1000) - retentionDays * 86400;
27238
+ db.prepare("DELETE FROM messages WHERE ts < ?").run(cutoff);
27239
+ }
27240
+ }
27241
+ function _resetForTests() {
27242
+ if (db != null) {
27243
+ db.close();
27244
+ db = null;
27245
+ }
27246
+ }
27247
+ function checkpointWal() {
27248
+ if (db == null)
27249
+ return false;
27250
+ try {
27251
+ db.prepare("PRAGMA wal_checkpoint(TRUNCATE)").run();
27252
+ return true;
27253
+ } catch {
27254
+ return false;
27255
+ }
27256
+ }
27257
+ function pruneMessagesOlderThanDays(retentionDays, nowSec, batchLimit = 5000) {
27258
+ if (db == null)
27259
+ return 0;
27260
+ if (retentionDays <= 0)
27261
+ return 0;
27262
+ const cutoffSec = (nowSec ?? Math.floor(Date.now() / 1000)) - retentionDays * 86400;
27263
+ const stmt = db.prepare(`
27264
+ DELETE FROM messages
27265
+ WHERE rowid IN (
27266
+ SELECT rowid FROM messages WHERE ts < ? LIMIT ?
27267
+ )
27268
+ `);
27269
+ let total = 0;
27270
+ for (let i = 0;i < 1000; i++) {
27271
+ const result = stmt.run(cutoffSec, batchLimit);
27272
+ const n = result.changes ?? 0;
27273
+ total += n;
27274
+ if (n === 0)
27275
+ break;
27276
+ }
27277
+ return total;
27278
+ }
27279
+ function requireDb() {
27280
+ if (db == null) {
27281
+ throw new Error("history: initHistory() must be called before any record/query operation");
27282
+ }
27283
+ return db;
27284
+ }
27285
+ function recordInbound(args) {
27286
+ if (args.message_id == null)
27287
+ return;
27288
+ const stmt = requireDb().prepare(`
27289
+ INSERT OR REPLACE INTO messages
27290
+ (chat_id, thread_id, message_id, role, user, user_id, ts, text, attachment_kind, group_id, reply_to_message_id, reply_to_text)
27291
+ VALUES (?, ?, ?, 'user', ?, ?, ?, ?, ?, NULL, ?, ?)
27292
+ `);
27293
+ stmt.run(args.chat_id, args.thread_id ?? null, args.message_id, args.user ?? null, args.user_id ?? null, args.ts, redact(args.text), args.attachment_kind ?? null, args.reply_to_message_id ?? null, args.reply_to_text != null ? redact(args.reply_to_text) : args.reply_to_text ?? null);
27294
+ }
27295
+ function recordOutbound(args) {
27296
+ if (args.message_ids.length === 0)
27297
+ return;
27298
+ const ts = args.ts ?? Math.floor(Date.now() / 1000);
27299
+ const groupId = args.message_ids[0];
27300
+ const stmt = requireDb().prepare(`
27301
+ INSERT OR REPLACE INTO messages
27302
+ (chat_id, thread_id, message_id, role, user, user_id, ts, text, attachment_kind, group_id)
27303
+ VALUES (?, ?, ?, 'assistant', NULL, NULL, ?, ?, ?, ?)
27304
+ `);
27305
+ const tx = requireDb().transaction((rows2) => {
27306
+ for (const [msgId, text, attachKind] of rows2) {
27307
+ stmt.run(args.chat_id, args.thread_id ?? null, msgId, ts, text, attachKind, groupId);
27308
+ }
27309
+ });
27310
+ const rows = args.message_ids.map((id, i) => [
27311
+ id,
27312
+ redact(args.texts[i] ?? ""),
27313
+ args.attachment_kinds?.[i] ?? null
27314
+ ]);
27315
+ tx(rows);
27316
+ }
27317
+ function recordEdit(args) {
27318
+ requireDb().prepare(`
27319
+ UPDATE messages
27320
+ SET text = ?
27321
+ WHERE chat_id = ? AND message_id = ?
27322
+ `).run(redact(args.text), args.chat_id, args.message_id);
27323
+ }
27324
+ function recordReaction(args) {
27325
+ requireDb().prepare(`
27326
+ UPDATE messages
27327
+ SET user_reaction = ?
27328
+ WHERE chat_id = ? AND message_id = ?
27329
+ `).run(args.emoji, args.chat_id, args.message_id);
27330
+ }
27331
+ function deleteFromHistory(args) {
27332
+ requireDb().prepare(`
27333
+ DELETE FROM messages
27334
+ WHERE chat_id = ? AND message_id = ?
27335
+ `).run(args.chat_id, args.message_id);
27336
+ }
27337
+ function getLatestInboundMessageId(chatId, threadId) {
27338
+ const params = [chatId];
27339
+ let sql = "SELECT message_id FROM messages WHERE chat_id = ? AND role = 'user'";
27340
+ if (threadId !== undefined) {
27341
+ if (threadId === null) {
27342
+ sql += " AND thread_id IS NULL";
27343
+ } else {
27344
+ sql += " AND thread_id = ?";
27345
+ params.push(threadId);
27346
+ }
27347
+ }
27348
+ sql += " ORDER BY ts DESC, message_id DESC LIMIT 1";
27349
+ const row = requireDb().prepare(sql).get(...params);
27350
+ return row?.message_id ?? null;
27351
+ }
27352
+ function lookupMessageRoleAndText(chatId, messageId) {
27353
+ const row = requireDb().prepare(`SELECT role, text FROM messages WHERE chat_id = ? AND message_id = ? LIMIT 1`).get(chatId, messageId);
27354
+ if (!row)
27355
+ return null;
27356
+ return { role: row.role, text: row.text ?? "" };
27357
+ }
27358
+ function getRecentOutboundCount(chatId, withinSeconds) {
27359
+ const cutoff = Math.floor(Date.now() / 1000) - withinSeconds;
27360
+ const row = requireDb().prepare("SELECT COUNT(*) as cnt FROM messages WHERE chat_id = ? AND role = ? AND ts >= ?").get(chatId, "assistant", cutoff);
27361
+ return row?.cnt ?? 0;
27362
+ }
27363
+ function query(opts) {
27364
+ const limit = Math.min(MAX_LIMIT, Math.max(1, opts.limit ?? DEFAULT_LIMIT));
27365
+ const params = [opts.chat_id];
27366
+ let sql = "SELECT * FROM messages WHERE chat_id = ?";
27367
+ if (opts.thread_id !== undefined) {
27368
+ if (opts.thread_id === null) {
27369
+ sql += " AND thread_id IS NULL";
27370
+ } else {
27371
+ sql += " AND thread_id = ?";
27372
+ params.push(opts.thread_id);
27373
+ }
27374
+ }
27375
+ if (opts.before_message_id != null) {
27376
+ sql += " AND message_id < ?";
27377
+ params.push(opts.before_message_id);
27378
+ }
27379
+ sql += " ORDER BY ts DESC, message_id DESC LIMIT ?";
27380
+ params.push(limit);
27381
+ const rows = requireDb().prepare(sql).all(...params);
27382
+ rows.reverse();
27383
+ return rows;
27384
+ }
27385
+ var DatabaseClass = null, DEFAULT_LIMIT = 10, MAX_LIMIT = 50, db = null;
27386
+ var init_history = __esm(() => {
27387
+ init_redact();
27388
+ });
27389
+
27390
+ // quota-check.ts
27391
+ import { readFileSync as readFileSync8, existsSync as existsSync12 } from "fs";
27392
+ import { join as join12 } from "path";
27393
+ function readOauthToken(claudeConfigDir) {
27394
+ const tokenFile = join12(claudeConfigDir, ".oauth-token");
27395
+ if (!existsSync12(tokenFile))
27396
+ return null;
27397
+ try {
27398
+ const raw = readFileSync8(tokenFile, "utf-8").trim();
27399
+ return raw.length > 0 ? raw : null;
27400
+ } catch {
27401
+ return null;
27402
+ }
27403
+ }
27404
+ function parseFloatHeader(headers, name) {
27405
+ const v = headers.get(name);
27406
+ if (v == null || v.trim().length === 0)
27407
+ return null;
27408
+ const n = Number(v);
27409
+ return Number.isFinite(n) ? n : null;
27410
+ }
27411
+ function parseEpochHeader(headers, name) {
27412
+ const v = headers.get(name);
27413
+ if (v == null)
27414
+ return null;
27415
+ const n = Number(v);
27416
+ if (!Number.isFinite(n) || n <= 0)
27417
+ return null;
27418
+ return new Date(n * 1000);
27419
+ }
27420
+ function parseQuotaHeaders(headers) {
27421
+ const fiveHour = parseFloatHeader(headers, "anthropic-ratelimit-unified-5h-utilization");
27422
+ const sevenDay = parseFloatHeader(headers, "anthropic-ratelimit-unified-7d-utilization");
27423
+ if (fiveHour == null && sevenDay == null) {
27424
+ return {
27425
+ ok: false,
27426
+ reason: "no unified rate-limit headers in response (API token, not OAuth?)"
27427
+ };
27428
+ }
27429
+ return {
27430
+ ok: true,
27431
+ data: {
27432
+ fiveHourUtilizationPct: (fiveHour ?? 0) * 100,
27433
+ sevenDayUtilizationPct: (sevenDay ?? 0) * 100,
27434
+ fiveHourResetAt: parseEpochHeader(headers, "anthropic-ratelimit-unified-5h-reset"),
27435
+ sevenDayResetAt: parseEpochHeader(headers, "anthropic-ratelimit-unified-7d-reset"),
27436
+ representativeClaim: headers.get("anthropic-ratelimit-unified-representative-claim"),
27437
+ overageStatus: headers.get("anthropic-ratelimit-unified-overage-status"),
27438
+ overageDisabledReason: headers.get("anthropic-ratelimit-unified-overage-disabled-reason")
27439
+ }
27440
+ };
27441
+ }
27442
+ async function fetchQuota(opts) {
27443
+ let token;
27444
+ if (opts.accessToken && opts.claudeConfigDir) {
27445
+ return {
27446
+ ok: false,
27447
+ reason: "pass only one of `accessToken` or `claudeConfigDir`, not both"
27448
+ };
27449
+ }
27450
+ if (opts.accessToken) {
27451
+ token = opts.accessToken.trim().length > 0 ? opts.accessToken : null;
27452
+ } else if (opts.claudeConfigDir) {
27453
+ token = readOauthToken(opts.claudeConfigDir);
27454
+ } else {
27455
+ return {
27456
+ ok: false,
27457
+ reason: "fetchQuota requires `accessToken` or `claudeConfigDir`"
27458
+ };
27459
+ }
27460
+ if (!token) {
27461
+ return { ok: false, reason: "no OAuth token at .oauth-token" };
27462
+ }
27463
+ const controller = new AbortController;
27464
+ const timeout = setTimeout(() => controller.abort(), opts.timeoutMs ?? 1e4);
27465
+ const fetchFn = opts.fetchImpl ?? fetch;
27466
+ let resp;
27467
+ try {
27468
+ resp = await fetchFn("https://api.anthropic.com/v1/messages", {
27469
+ method: "POST",
27470
+ headers: {
27471
+ "anthropic-version": "2023-06-01",
27472
+ "anthropic-beta": OAUTH_BETA,
27473
+ authorization: `Bearer ${token}`,
27474
+ "x-app": "cli",
27475
+ "user-agent": DEFAULT_USER_AGENT,
27476
+ "content-type": "application/json"
27477
+ },
27478
+ body: JSON.stringify({
27479
+ model: opts.model ?? DEFAULT_PROBE_MODEL,
27480
+ max_tokens: 1,
27481
+ messages: [{ role: "user", content: "hi" }]
27482
+ }),
27483
+ signal: controller.signal
27484
+ });
27485
+ } catch (err) {
27486
+ const msg = err?.message ?? String(err);
27487
+ return { ok: false, reason: `request failed: ${msg}` };
27488
+ } finally {
27489
+ clearTimeout(timeout);
27490
+ }
27491
+ if (resp.status === 401 || resp.status === 403) {
27492
+ return { ok: false, reason: `auth rejected (HTTP ${resp.status})` };
27493
+ }
27494
+ const parsed = parseQuotaHeaders(resp.headers);
27495
+ if (!parsed.ok && resp.status >= 400) {
27496
+ return { ok: false, reason: `HTTP ${resp.status}, ${parsed.reason}` };
27497
+ }
27498
+ return parsed;
27499
+ }
27500
+ function formatQuotaLine(q) {
27501
+ const fmt = (n) => `${Math.round(n)}%`;
27502
+ return `${fmt(q.fiveHourUtilizationPct)} / 5h \u00b7 ${fmt(q.sevenDayUtilizationPct)} / 7d`;
27503
+ }
27504
+ function formatResetRelative(target, now = new Date) {
27505
+ if (!target)
27506
+ return "\u2014";
27507
+ const deltaMs = target.getTime() - now.getTime();
27508
+ if (deltaMs <= 0)
27509
+ return "resets now";
27510
+ const totalMin = Math.round(deltaMs / 60000);
27511
+ if (totalMin < 60)
27512
+ return `resets in ${totalMin}m`;
27513
+ const hours = Math.floor(totalMin / 60);
27514
+ const mins = totalMin % 60;
27515
+ if (hours < 24)
27516
+ return mins > 0 ? `resets in ${hours}h ${mins}m` : `resets in ${hours}h`;
27517
+ const days = Math.floor(hours / 24);
27518
+ const remH = hours % 24;
27519
+ return remH > 0 ? `resets in ${days}d ${remH}h` : `resets in ${days}d`;
27520
+ }
27521
+ var OAUTH_BETA = "oauth-2025-04-20", DEFAULT_USER_AGENT = "claude-cli/1.0.0 (external, cli)", DEFAULT_PROBE_MODEL = "claude-haiku-4-5-20251001";
27522
+ var init_quota_check = () => {};
27523
+
27524
+ // ../src/vault/broker/peercred-ffi.ts
27525
+ function getPeerCred(fd) {
27526
+ if (process.platform !== "linux")
27527
+ return null;
27528
+ try {
27529
+ const ffi = __require("bun:ffi");
27530
+ const { dlopen, FFIType, ptr } = ffi;
27531
+ const SOL_SOCKET = 1;
27532
+ const SO_PEERCRED = 17;
27533
+ const UCRED_SIZE = 12;
27534
+ const cache = getPeerCred;
27535
+ const lib = cache._lib ?? (() => {
27536
+ const candidates = ["libc.so.6", "libc.so"];
27537
+ const symbolSpec = {
27538
+ getsockopt: {
27539
+ args: [FFIType.i32, FFIType.i32, FFIType.i32, FFIType.ptr, FFIType.ptr],
27540
+ returns: FFIType.i32
27541
+ }
27542
+ };
27543
+ const errors3 = [];
27544
+ for (const name of candidates) {
27545
+ try {
27546
+ const opened = dlopen(name, symbolSpec);
27547
+ cache._lib = opened;
27548
+ return opened;
27549
+ } catch (e) {
27550
+ errors3.push(`${name}: ${e instanceof Error ? e.message : String(e)}`);
27551
+ }
27552
+ }
27553
+ process.stderr.write(`[vault-broker] peercred-ffi: dlopen failed for all libc candidates ` + `(${errors3.join("; ")}); falling back to ss-parsing.
27554
+ `);
27555
+ throw new Error("no libc candidate could be opened");
27556
+ })();
27557
+ const credBuf = new ArrayBuffer(UCRED_SIZE);
27558
+ const lenBuf = new Uint32Array(1);
27559
+ lenBuf[0] = UCRED_SIZE;
27560
+ const rc = lib.symbols.getsockopt(fd, SOL_SOCKET, SO_PEERCRED, ptr(credBuf), ptr(lenBuf.buffer));
27561
+ if (rc !== 0)
27562
+ return null;
27563
+ if (lenBuf[0] !== UCRED_SIZE)
27564
+ return null;
27565
+ const view = new DataView(credBuf);
27566
+ return {
27567
+ pid: view.getInt32(0, true),
27568
+ uid: view.getInt32(4, true),
27569
+ gid: view.getInt32(8, true)
27570
+ };
27571
+ } catch {
27572
+ return null;
27573
+ }
27574
+ }
27575
+
27576
+ // ../src/vault/broker/peercred.ts
27577
+ function isReservedAgentName(name) {
27578
+ return RESERVED_AGENT_NAMES.has(name);
27579
+ }
27580
+ function unlockSocketFor(dataSocketPath) {
27581
+ if (dataSocketPath.endsWith("/sock")) {
27582
+ return dataSocketPath.slice(0, -"/sock".length) + "/unlock";
27583
+ }
27584
+ return dataSocketPath.replace(/\.sock$/, ".unlock.sock");
27585
+ }
27586
+ var RESERVED_AGENT_NAMES;
27587
+ var init_peercred = __esm(() => {
27588
+ RESERVED_AGENT_NAMES = new Set(["operator", "hostd"]);
27589
+ });
27590
+
27591
+ // ../src/vault/broker/protocol.ts
27592
+ function encodeRequest2(req) {
27593
+ const json = JSON.stringify(req);
27594
+ if (Buffer.byteLength(json, "utf8") > MAX_FRAME_BYTES2) {
27595
+ throw new Error(`Request frame too large (${Buffer.byteLength(json, "utf8")} bytes; max ${MAX_FRAME_BYTES2})`);
27596
+ }
27597
+ return json + `
27598
+ `;
27599
+ }
27600
+ function decodeResponse2(line) {
27601
+ if (Buffer.byteLength(line, "utf8") > MAX_FRAME_BYTES2) {
27602
+ throw new RangeError(`Response frame too large (${Buffer.byteLength(line, "utf8")} bytes; max ${MAX_FRAME_BYTES2})`);
27603
+ }
27604
+ const obj = JSON.parse(line);
27605
+ return ResponseSchema2.parse(obj);
27606
+ }
27607
+ var MAX_FRAME_BYTES2, AgentNameSchema, GetRequestSchema, PutRequestSchema, ListRequestSchema, MintGrantRequestSchema, ListGrantsRequestSchema, RevokeGrantRequestSchema, StatusRequestSchema, LockRequestSchema, PreflightAccessRequestSchema, OkPreflightAccessResponseSchema, ApprovalRequestRequestSchema, ApprovalLookupRequestSchema, ApprovalConsumeRequestSchema, ApprovalRevokeRequestSchema, ApprovalListRequestSchema, ApprovalDecisionModeSchema, ApprovalRecordRequestSchema, ApprovalConsumeRecordRequestSchema, RequestSchema2, VaultEntrySchema, ErrorCode, OkEntryResponseSchema, OkKeysResponseSchema, BrokerStatus, OkStatusResponseSchema, OkLockResponseSchema, OkPutResponseSchema, OkMintGrantResponseSchema, GrantMetaSchema, OkListGrantsResponseSchema, OkRevokeGrantResponseSchema, OkApprovalRequestResponseSchema, ApprovalDecisionMetaSchema, OkApprovalLookupResponseSchema, OkApprovalConsumeResponseSchema, OkApprovalRevokeResponseSchema, OkApprovalListResponseSchema, OkApprovalRecordResponseSchema, OkApprovalConsumeRecordResponseSchema, ErrorResponseSchema2, ResponseSchema2;
27608
+ var init_protocol2 = __esm(() => {
27609
+ init_zod();
27610
+ init_peercred();
27611
+ MAX_FRAME_BYTES2 = 64 * 1024;
27612
+ AgentNameSchema = exports_external.string().min(1).max(64, "agent name max 64 chars").regex(/^[a-zA-Z0-9][a-zA-Z0-9_-]*$/, "agent name must be kebab-case ASCII (alnum + _- only, first char alnum)").refine((s) => !isReservedAgentName(s), {
27613
+ message: "agent name is reserved"
27614
+ });
27615
+ GetRequestSchema = exports_external.object({
27616
+ v: exports_external.literal(1),
27617
+ op: exports_external.literal("get"),
27618
+ key: exports_external.string().min(1),
27619
+ filename: exports_external.string().optional(),
27620
+ token: exports_external.string().optional()
27621
+ });
27622
+ PutRequestSchema = exports_external.object({
27623
+ v: exports_external.literal(1),
27624
+ op: exports_external.literal("put"),
27625
+ key: exports_external.string().min(1),
27626
+ entry: exports_external.union([
27627
+ exports_external.object({ kind: exports_external.literal("string"), value: exports_external.string() }),
27628
+ exports_external.object({ kind: exports_external.literal("binary"), value: exports_external.string() })
27629
+ ]),
27630
+ token: exports_external.string().optional(),
27631
+ passphrase: exports_external.string().optional(),
27632
+ attest_via_posture: exports_external.boolean().optional()
27633
+ });
27634
+ ListRequestSchema = exports_external.object({
27635
+ v: exports_external.literal(1),
27636
+ op: exports_external.literal("list"),
27637
+ token: exports_external.string().optional()
27638
+ });
27639
+ MintGrantRequestSchema = exports_external.object({
27640
+ v: exports_external.literal(1),
27641
+ op: exports_external.literal("mint_grant"),
27642
+ agent: AgentNameSchema,
27643
+ keys: exports_external.array(exports_external.string().min(1)),
27644
+ ttl_seconds: exports_external.number().int().positive().nullable(),
27645
+ description: exports_external.string().optional(),
27646
+ write_keys: exports_external.array(exports_external.string().min(1)).optional(),
27647
+ passphrase: exports_external.string().optional(),
27648
+ attest_via_posture: exports_external.boolean().optional()
27649
+ });
27650
+ ListGrantsRequestSchema = exports_external.object({
27651
+ v: exports_external.literal(1),
27652
+ op: exports_external.literal("list_grants"),
27653
+ agent: AgentNameSchema.optional(),
27654
+ passphrase: exports_external.string().optional(),
27655
+ attest_via_posture: exports_external.boolean().optional()
27656
+ });
27657
+ RevokeGrantRequestSchema = exports_external.object({
27658
+ v: exports_external.literal(1),
27659
+ op: exports_external.literal("revoke_grant"),
27660
+ id: exports_external.string().min(1)
27661
+ });
27662
+ StatusRequestSchema = exports_external.object({
27663
+ v: exports_external.literal(1),
27664
+ op: exports_external.literal("status")
27665
+ });
27666
+ LockRequestSchema = exports_external.object({
27667
+ v: exports_external.literal(1),
27668
+ op: exports_external.literal("lock")
27669
+ });
27670
+ PreflightAccessRequestSchema = exports_external.object({
27671
+ v: exports_external.literal(1),
27672
+ op: exports_external.literal("preflight_access"),
27673
+ agent: AgentNameSchema,
27674
+ keys: exports_external.array(exports_external.string().min(1)).min(1).max(128)
27675
+ });
27676
+ OkPreflightAccessResponseSchema = exports_external.object({
27677
+ ok: exports_external.literal(true),
27678
+ op: exports_external.literal("preflight_access"),
27679
+ results: exports_external.array(exports_external.object({
27680
+ key: exports_external.string(),
27681
+ exists: exports_external.boolean(),
27682
+ acl_ok: exports_external.boolean(),
27683
+ acl_reason: exports_external.string().optional(),
27684
+ scope_ok: exports_external.boolean(),
27685
+ scope_reason: exports_external.string().optional()
27686
+ }))
27687
+ });
27688
+ ApprovalRequestRequestSchema = exports_external.object({
27689
+ v: exports_external.literal(1),
27690
+ op: exports_external.literal("approval_request"),
27691
+ agent_unit: exports_external.string().min(1),
27692
+ scope: exports_external.string().min(1),
27693
+ action: exports_external.string().min(1),
27694
+ approver_set: exports_external.array(exports_external.string()),
27695
+ why: exports_external.string().optional(),
27696
+ ttl_ms: exports_external.number().int().positive().optional()
27697
+ });
27698
+ ApprovalLookupRequestSchema = exports_external.object({
27699
+ v: exports_external.literal(1),
27700
+ op: exports_external.literal("approval_lookup"),
27701
+ agent_unit: exports_external.string().min(1),
27702
+ scope: exports_external.string().min(1),
27703
+ action: exports_external.string().min(1),
27704
+ current_approver_set: exports_external.array(exports_external.string())
27705
+ });
27706
+ ApprovalConsumeRequestSchema = exports_external.object({
27707
+ v: exports_external.literal(1),
27708
+ op: exports_external.literal("approval_consume"),
27709
+ request_id: exports_external.string().regex(/^[0-9a-f]{32}$/)
27710
+ });
27711
+ ApprovalRevokeRequestSchema = exports_external.object({
27712
+ v: exports_external.literal(1),
27713
+ op: exports_external.literal("approval_revoke"),
27714
+ decision_id: exports_external.string().min(1),
27715
+ actor: exports_external.string().min(1),
27716
+ reason: exports_external.string().optional()
27717
+ });
27718
+ ApprovalListRequestSchema = exports_external.object({
27719
+ v: exports_external.literal(1),
27720
+ op: exports_external.literal("approval_list"),
27721
+ agent_unit: exports_external.string().optional()
27722
+ });
27723
+ ApprovalDecisionModeSchema = exports_external.enum([
27724
+ "allow_once",
27725
+ "allow_always",
27726
+ "allow_ttl",
27727
+ "deny",
27728
+ "deny_perm"
27729
+ ]);
27730
+ ApprovalRecordRequestSchema = exports_external.object({
27731
+ v: exports_external.literal(1),
27732
+ op: exports_external.literal("approval_record"),
27733
+ request_id: exports_external.string().regex(/^[0-9a-f]{32}$/),
27734
+ decision: ApprovalDecisionModeSchema,
27735
+ approver_set: exports_external.array(exports_external.string()),
27736
+ granted_by_user_id: exports_external.number().int(),
27737
+ ttl_ms: exports_external.number().int().positive().nullable().optional()
27738
+ });
27739
+ ApprovalConsumeRecordRequestSchema = exports_external.object({
27740
+ v: exports_external.literal(1),
27741
+ op: exports_external.literal("approval_consume_record"),
27742
+ request_id: exports_external.string().regex(/^[0-9a-f]{32}$/),
27743
+ decision: ApprovalDecisionModeSchema,
27744
+ approver_set: exports_external.array(exports_external.string()),
27745
+ granted_by_user_id: exports_external.number().int(),
27746
+ ttl_ms: exports_external.number().int().positive().nullable().optional()
27747
+ });
27748
+ RequestSchema2 = exports_external.discriminatedUnion("op", [
27749
+ GetRequestSchema,
27750
+ PutRequestSchema,
27751
+ ListRequestSchema,
27752
+ StatusRequestSchema,
27753
+ LockRequestSchema,
27754
+ PreflightAccessRequestSchema,
27755
+ MintGrantRequestSchema,
27756
+ ListGrantsRequestSchema,
27757
+ RevokeGrantRequestSchema,
27758
+ ApprovalRequestRequestSchema,
27759
+ ApprovalLookupRequestSchema,
27760
+ ApprovalConsumeRequestSchema,
27761
+ ApprovalRevokeRequestSchema,
27762
+ ApprovalListRequestSchema,
27763
+ ApprovalRecordRequestSchema,
27764
+ ApprovalConsumeRecordRequestSchema
27765
+ ]);
27766
+ VaultEntrySchema = exports_external.union([
27767
+ exports_external.object({ kind: exports_external.literal("string"), value: exports_external.string() }),
27768
+ exports_external.object({ kind: exports_external.literal("binary"), value: exports_external.string() }),
27769
+ exports_external.object({
27770
+ kind: exports_external.literal("files"),
27771
+ files: exports_external.record(exports_external.string(), exports_external.object({
27772
+ encoding: exports_external.enum(["utf8", "base64"]),
27773
+ value: exports_external.string()
27774
+ }))
27775
+ })
27776
+ ]);
27777
+ ErrorCode = exports_external.enum([
27778
+ "LOCKED",
27779
+ "DENIED",
27780
+ "UNKNOWN_KEY",
27781
+ "BAD_REQUEST",
27782
+ "INTERNAL"
27783
+ ]);
27784
+ OkEntryResponseSchema = exports_external.object({
27785
+ ok: exports_external.literal(true),
27786
+ entry: VaultEntrySchema
27787
+ });
27788
+ OkKeysResponseSchema = exports_external.object({
27789
+ ok: exports_external.literal(true),
27790
+ keys: exports_external.array(exports_external.string())
27791
+ });
27792
+ BrokerStatus = exports_external.object({
27793
+ unlocked: exports_external.boolean(),
27794
+ keyCount: exports_external.number().int().nonnegative(),
27795
+ uptimeSec: exports_external.number().nonnegative()
27796
+ });
27797
+ OkStatusResponseSchema = exports_external.object({
27798
+ ok: exports_external.literal(true),
27799
+ status: BrokerStatus
27800
+ });
27801
+ OkLockResponseSchema = exports_external.object({
27802
+ ok: exports_external.literal(true),
27803
+ locked: exports_external.literal(true)
27804
+ });
27805
+ OkPutResponseSchema = exports_external.object({
27806
+ ok: exports_external.literal(true),
27807
+ put: exports_external.literal(true),
27808
+ key: exports_external.string()
27809
+ });
27810
+ OkMintGrantResponseSchema = exports_external.object({
27811
+ ok: exports_external.literal(true),
27812
+ token: exports_external.string(),
27813
+ id: exports_external.string(),
27814
+ expires_at: exports_external.number().nullable()
27815
+ });
27816
+ GrantMetaSchema = exports_external.object({
27817
+ id: exports_external.string(),
27818
+ agent_slug: exports_external.string(),
27819
+ key_allow: exports_external.array(exports_external.string()),
27820
+ write_allow: exports_external.array(exports_external.string()).default([]),
27821
+ expires_at: exports_external.number().nullable(),
27822
+ created_at: exports_external.number(),
27823
+ description: exports_external.string().nullable()
27824
+ });
27825
+ OkListGrantsResponseSchema = exports_external.object({
27826
+ ok: exports_external.literal(true),
27827
+ grants: exports_external.array(GrantMetaSchema)
27828
+ });
27829
+ OkRevokeGrantResponseSchema = exports_external.object({
27830
+ ok: exports_external.literal(true),
27831
+ revoked: exports_external.boolean()
27832
+ });
27833
+ OkApprovalRequestResponseSchema = exports_external.discriminatedUnion("state", [
27834
+ exports_external.object({
27835
+ ok: exports_external.literal(true),
27836
+ kind: exports_external.literal("approval_request"),
27837
+ state: exports_external.literal("pending"),
27838
+ request_id: exports_external.string(),
27839
+ expires_at: exports_external.number()
27840
+ }),
27841
+ exports_external.object({
27842
+ ok: exports_external.literal(true),
27843
+ kind: exports_external.literal("approval_request"),
27844
+ state: exports_external.literal("rate_limited"),
27845
+ retry_after_ms: exports_external.number()
27846
+ })
27847
+ ]);
27848
+ ApprovalDecisionMetaSchema = exports_external.object({
27849
+ id: exports_external.string(),
27850
+ agent_unit: exports_external.string(),
27851
+ scope: exports_external.string(),
27852
+ action: exports_external.string(),
27853
+ decision: ApprovalDecisionModeSchema,
27854
+ granted_at: exports_external.number(),
27855
+ granted_by_user_id: exports_external.number(),
27856
+ ttl_expires_at: exports_external.number().nullable(),
27857
+ last_used_at: exports_external.number().nullable(),
27858
+ revoked_at: exports_external.number().nullable(),
27859
+ revoke_reason: exports_external.string().nullable()
27860
+ });
27861
+ OkApprovalLookupResponseSchema = exports_external.object({
27862
+ ok: exports_external.literal(true),
27863
+ state: exports_external.enum(["granted", "denied", "pending", "expired", "drift_revoked", "no_decision"]),
27864
+ decision: ApprovalDecisionMetaSchema.nullable().optional()
27865
+ });
27866
+ OkApprovalConsumeResponseSchema = exports_external.object({
27867
+ ok: exports_external.literal(true),
27868
+ consumed: exports_external.boolean(),
27869
+ agent_unit: exports_external.string().optional(),
27870
+ scope: exports_external.string().optional(),
27871
+ action: exports_external.string().optional(),
27872
+ why: exports_external.string().nullable().optional()
27873
+ });
27874
+ OkApprovalRevokeResponseSchema = exports_external.object({
27875
+ ok: exports_external.literal(true),
27876
+ revoked: exports_external.boolean()
27877
+ });
27878
+ OkApprovalListResponseSchema = exports_external.object({
27879
+ ok: exports_external.literal(true),
27880
+ decisions: exports_external.array(ApprovalDecisionMetaSchema)
27881
+ });
27882
+ OkApprovalRecordResponseSchema = exports_external.object({
27883
+ ok: exports_external.literal(true),
27884
+ decision_id: exports_external.string()
27885
+ });
27886
+ OkApprovalConsumeRecordResponseSchema = exports_external.object({
27887
+ ok: exports_external.literal(true),
27888
+ consumed: exports_external.boolean(),
27889
+ decision_id: exports_external.string().optional(),
27890
+ agent_unit: exports_external.string().optional(),
27891
+ scope: exports_external.string().optional(),
27892
+ action: exports_external.string().optional(),
27893
+ why: exports_external.string().nullable().optional()
27894
+ });
27895
+ ErrorResponseSchema2 = exports_external.object({
27896
+ ok: exports_external.literal(false),
27897
+ code: ErrorCode,
27898
+ msg: exports_external.string()
27899
+ });
27900
+ ResponseSchema2 = exports_external.union([
27901
+ OkEntryResponseSchema,
27902
+ OkKeysResponseSchema,
27903
+ OkStatusResponseSchema,
27904
+ OkLockResponseSchema,
27905
+ OkPreflightAccessResponseSchema,
27906
+ OkPutResponseSchema,
27907
+ OkMintGrantResponseSchema,
27908
+ OkListGrantsResponseSchema,
27909
+ OkRevokeGrantResponseSchema,
27910
+ OkApprovalRequestResponseSchema,
27911
+ OkApprovalLookupResponseSchema,
27912
+ OkApprovalConsumeResponseSchema,
27913
+ OkApprovalRevokeResponseSchema,
27914
+ OkApprovalListResponseSchema,
27915
+ OkApprovalRecordResponseSchema,
27916
+ OkApprovalConsumeRecordResponseSchema,
27917
+ ErrorResponseSchema2
27918
+ ]);
27919
+ });
27920
+
27921
+ // ../src/runtime-mode.ts
27922
+ function isDockerRuntime() {
27923
+ return process.env.SWITCHROOM_RUNTIME === "docker";
27924
+ }
27925
+
27926
+ // ../src/vault/broker/client.ts
27927
+ import * as net3 from "node:net";
27928
+ import * as fs from "node:fs";
27929
+ import { homedir as homedir7 } from "node:os";
27930
+ import { join as join15 } from "node:path";
27931
+ function defaultBrokerSocketPath() {
27932
+ if (fs.existsSync(OPERATOR_SOCKET_PATH))
27933
+ return OPERATOR_SOCKET_PATH;
27934
+ if (isDockerRuntime())
27935
+ return OPERATOR_SOCKET_PATH;
27936
+ return LEGACY_SOCKET_PATH;
27937
+ }
27938
+ function vaultTokenFilePath(agentSlug) {
27939
+ return join15(homedir7(), ".switchroom", "agents", agentSlug, ".vault-token");
27940
+ }
27941
+ function readVaultTokenFile(agentSlug) {
27942
+ const filePath = vaultTokenFilePath(agentSlug);
27943
+ try {
27944
+ const stat = fs.statSync(filePath);
27945
+ const mode = stat.mode & 511;
27946
+ if ((mode & 63) !== 0) {
27947
+ process.stderr.write(`[vault-broker] Refusing to read ${filePath} with mode ${mode.toString(8).padStart(3, "0")} ` + `(must be 0600). Delete the file and re-mint with 'switchroom vault grant mint <agent>'. ` + `Falling through to peercred ACL.
27948
+ `);
27949
+ return null;
27950
+ }
27951
+ const raw = fs.readFileSync(filePath, "utf8");
27952
+ const token = raw.split(`
27953
+ `)[0].trim();
27954
+ return token.length > 0 ? token : null;
27955
+ } catch (err) {
27956
+ const code = err.code;
27957
+ if (code === "ENOENT") {
27958
+ return null;
27685
27959
  }
27686
- function toSource(func) {
27687
- if (func != null) {
27688
- try {
27689
- return funcToString.call(func);
27690
- } catch (e) {}
27960
+ const reason = code === "EACCES" ? "permission denied" : err instanceof Error ? err.message : String(err);
27961
+ process.stderr.write(`[vault-broker] Warning: could not read token file ${filePath}: ${reason}. ` + `Falling through to peercred ACL.
27962
+ `);
27963
+ return null;
27964
+ }
27965
+ }
27966
+ function resolveBrokerSocketPath(opts) {
27967
+ if (opts?.socket)
27968
+ return opts.socket;
27969
+ const env = process.env.SWITCHROOM_VAULT_BROKER_SOCK;
27970
+ if (env)
27971
+ return env;
27972
+ if (opts?.vaultBrokerSocket)
27973
+ return opts.vaultBrokerSocket;
27974
+ return defaultBrokerSocketPath();
27975
+ }
27976
+ async function rpcRaw(req, opts) {
27977
+ return rpc(req, opts);
27978
+ }
27979
+ async function rpc(req, opts) {
27980
+ const socketPath = resolveBrokerSocketPath(opts);
27981
+ const timeoutMs = opts?.timeoutMs ?? DEFAULT_TIMEOUT_MS3;
27982
+ return new Promise((resolve5) => {
27983
+ let settled = false;
27984
+ const settle = (val) => {
27985
+ if (settled)
27986
+ return;
27987
+ settled = true;
27988
+ resolve5(val);
27989
+ };
27990
+ const client3 = new net3.Socket;
27991
+ const timer3 = setTimeout(() => {
27992
+ client3.destroy();
27993
+ settle({ kind: "unreachable", msg: `broker did not respond within ${timeoutMs}ms` });
27994
+ }, timeoutMs);
27995
+ client3.on("error", (err) => {
27996
+ clearTimeout(timer3);
27997
+ const code = err.code ?? "ERR";
27998
+ let msg;
27999
+ if (code === "ENOENT")
28000
+ msg = "broker socket not found (is the daemon running?)";
28001
+ else if (code === "ECONNREFUSED")
28002
+ msg = "broker socket exists but refused connection";
28003
+ else if (code === "EACCES")
28004
+ msg = "broker socket access denied (wrong UID?)";
28005
+ else
28006
+ msg = `broker connection failed: ${err.message}`;
28007
+ settle({ kind: "unreachable", msg });
28008
+ });
28009
+ let buffer = "";
28010
+ client3.on("data", (chunk2) => {
28011
+ buffer += chunk2.toString("utf8");
28012
+ const newlineIdx = buffer.indexOf(`
28013
+ `);
28014
+ if (newlineIdx !== -1) {
28015
+ const line = buffer.slice(0, newlineIdx).trimEnd();
28016
+ clearTimeout(timer3);
28017
+ client3.destroy();
27691
28018
  try {
27692
- return func + "";
27693
- } catch (e) {}
27694
- }
27695
- return "";
27696
- }
27697
- var sortBy = baseRest(function(collection, iteratees) {
27698
- if (collection == null) {
27699
- return [];
27700
- }
27701
- var length = iteratees.length;
27702
- if (length > 1 && isIterateeCall(collection, iteratees[0], iteratees[1])) {
27703
- iteratees = [];
27704
- } else if (length > 2 && isIterateeCall(iteratees[0], iteratees[1], iteratees[2])) {
27705
- iteratees = [iteratees[0]];
28019
+ const resp = decodeResponse2(line);
28020
+ settle({ kind: "response", resp });
28021
+ } catch (err) {
28022
+ settle({
28023
+ kind: "unreachable",
28024
+ msg: `unparseable broker response: ${err instanceof Error ? err.message : String(err)}`
28025
+ });
28026
+ }
27706
28027
  }
27707
- return baseOrderBy(collection, baseFlatten(iteratees), []);
27708
28028
  });
27709
- function memoize(func, resolver) {
27710
- if (typeof func != "function" || resolver && typeof resolver != "function") {
27711
- throw new TypeError(FUNC_ERROR_TEXT);
28029
+ client3.on("connect", () => {
28030
+ try {
28031
+ client3.write(encodeRequest2(req));
28032
+ } catch (err) {
28033
+ clearTimeout(timer3);
28034
+ client3.destroy();
28035
+ settle({
28036
+ kind: "unreachable",
28037
+ msg: `failed to send request: ${err instanceof Error ? err.message : String(err)}`
28038
+ });
27712
28039
  }
27713
- var memoized = function() {
27714
- var args = arguments, key = resolver ? resolver.apply(this, args) : args[0], cache = memoized.cache;
27715
- if (cache.has(key)) {
27716
- return cache.get(key);
27717
- }
27718
- var result = func.apply(this, args);
27719
- memoized.cache = cache.set(key, result);
27720
- return result;
27721
- };
27722
- memoized.cache = new (memoize.Cache || MapCache);
27723
- return memoized;
27724
- }
27725
- memoize.Cache = MapCache;
27726
- function eq(value, other) {
27727
- return value === other || value !== value && other !== other;
27728
- }
27729
- function isArguments(value) {
27730
- return isArrayLikeObject(value) && hasOwnProperty.call(value, "callee") && (!propertyIsEnumerable.call(value, "callee") || objectToString.call(value) == argsTag);
27731
- }
27732
- var isArray2 = Array.isArray;
27733
- function isArrayLike(value) {
27734
- return value != null && isLength(value.length) && !isFunction2(value);
27735
- }
27736
- function isArrayLikeObject(value) {
27737
- return isObjectLike(value) && isArrayLike(value);
27738
- }
27739
- function isFunction2(value) {
27740
- var tag = isObject2(value) ? objectToString.call(value) : "";
27741
- return tag == funcTag || tag == genTag;
27742
- }
27743
- function isLength(value) {
27744
- return typeof value == "number" && value > -1 && value % 1 == 0 && value <= MAX_SAFE_INTEGER;
27745
- }
27746
- function isObject2(value) {
27747
- var type = typeof value;
27748
- return !!value && (type == "object" || type == "function");
27749
- }
27750
- function isObjectLike(value) {
27751
- return !!value && typeof value == "object";
27752
- }
27753
- function isSymbol(value) {
27754
- return typeof value == "symbol" || isObjectLike(value) && objectToString.call(value) == symbolTag;
27755
- }
27756
- var isTypedArray = nodeIsTypedArray ? baseUnary(nodeIsTypedArray) : baseIsTypedArray;
27757
- function toString(value) {
27758
- return value == null ? "" : baseToString(value);
27759
- }
27760
- function get(object, path, defaultValue) {
27761
- var result = object == null ? undefined : baseGet(object, path);
27762
- return result === undefined ? defaultValue : result;
27763
- }
27764
- function hasIn(object, path) {
27765
- return object != null && hasPath(object, path, baseHasIn);
27766
- }
27767
- function keys(object) {
27768
- return isArrayLike(object) ? arrayLikeKeys(object) : baseKeys(object);
27769
- }
27770
- function identity2(value) {
27771
- return value;
27772
- }
27773
- function property(path) {
27774
- return isKey(path) ? baseProperty(toKey(path)) : basePropertyDeep(path);
27775
- }
27776
- module.exports = sortBy;
27777
- })(lodash_sortby, lodash_sortby.exports);
27778
- return lodash_sortby.exports;
28040
+ });
28041
+ client3.connect({ path: socketPath });
28042
+ });
27779
28043
  }
27780
- function requireEscapeStringRegexp() {
27781
- if (hasRequiredEscapeStringRegexp)
27782
- return escapeStringRegexp;
27783
- hasRequiredEscapeStringRegexp = 1;
27784
- escapeStringRegexp = (string) => {
27785
- if (typeof string !== "string") {
27786
- throw new TypeError("Expected a string");
28044
+ async function getViaBrokerStructured(key, opts) {
28045
+ const token = opts?.token;
28046
+ const result = await rpc({ v: 1, op: "get", key, ...token ? { token } : {} }, opts);
28047
+ if (result.kind === "unreachable") {
28048
+ return { kind: "unreachable", msg: result.msg };
28049
+ }
28050
+ const resp = result.resp;
28051
+ if (resp.ok && "entry" in resp) {
28052
+ return { kind: "ok", entry: resp.entry };
28053
+ }
28054
+ if (!resp.ok) {
28055
+ if (resp.code === "UNKNOWN_KEY") {
28056
+ return { kind: "not_found", code: resp.code, msg: resp.msg };
27787
28057
  }
27788
- return string.replace(/[|\\{}()[\]^$+*?.]/g, "\\$&").replace(/-/g, "\\x2d");
27789
- };
27790
- return escapeStringRegexp;
28058
+ return { kind: "denied", code: resp.code, msg: resp.msg };
28059
+ }
28060
+ return { kind: "unreachable", msg: "unexpected broker response shape" };
27791
28061
  }
27792
- function requireRegexpParse() {
27793
- if (hasRequiredRegexpParse)
27794
- return regexpParse;
27795
- hasRequiredRegexpParse = 1;
27796
- Object.defineProperty(regexpParse, "__esModule", { value: true });
27797
- regexpParse.isRegExpString = regexpParse.parseRegExpString = undefined;
27798
- var REGEXP_LITERAL_PATTERN = /^\/(.+)\/([guimysd]*)$/;
27799
- var parseRegExpString = function(str) {
27800
- var result = str.match(REGEXP_LITERAL_PATTERN);
27801
- if (!result) {
27802
- return null;
28062
+ async function putViaBroker(key, entry, opts) {
28063
+ const token = opts?.token;
28064
+ const passphrase = opts?.passphrase;
28065
+ const attestViaPosture = opts?.attest_via_posture === true;
28066
+ const result = await rpc({
28067
+ v: 1,
28068
+ op: "put",
28069
+ key,
28070
+ entry,
28071
+ ...token ? { token } : {},
28072
+ ...passphrase ? { passphrase } : {},
28073
+ ...attestViaPosture ? { attest_via_posture: true } : {}
28074
+ }, opts);
28075
+ if (result.kind === "unreachable") {
28076
+ return { kind: "unreachable", msg: result.msg };
28077
+ }
28078
+ const resp = result.resp;
28079
+ if (resp.ok && "put" in resp) {
28080
+ return { kind: "ok" };
28081
+ }
28082
+ if (!resp.ok) {
28083
+ if (resp.code === "UNKNOWN_KEY") {
28084
+ return { kind: "not_found", code: resp.code, msg: resp.msg };
27803
28085
  }
27804
- return {
27805
- source: result[1],
27806
- flagString: result[2]
27807
- };
27808
- };
27809
- regexpParse.parseRegExpString = parseRegExpString;
27810
- var isRegExpString = function(str) {
27811
- return REGEXP_LITERAL_PATTERN.test(str);
28086
+ return { kind: "denied", code: resp.code, msg: resp.msg };
28087
+ }
28088
+ return { kind: "unreachable", msg: "unexpected broker response shape" };
28089
+ }
28090
+ var DEFAULT_TIMEOUT_MS3 = 2000, LEGACY_SOCKET_PATH, OPERATOR_SOCKET_PATH;
28091
+ var init_client2 = __esm(() => {
28092
+ init_protocol2();
28093
+ init_peercred();
28094
+ LEGACY_SOCKET_PATH = join15(homedir7(), ".switchroom", "vault-broker.sock");
28095
+ OPERATOR_SOCKET_PATH = join15(homedir7(), ".switchroom", "broker-operator", "sock");
28096
+ });
28097
+
28098
+ // ../src/drive/deep-links.ts
28099
+ function classifyMimeType(mimeType) {
28100
+ if (!mimeType)
28101
+ return "file";
28102
+ switch (mimeType) {
28103
+ case "application/vnd.google-apps.document":
28104
+ return "doc";
28105
+ case "application/vnd.google-apps.spreadsheet":
28106
+ return "spreadsheet";
28107
+ case "application/vnd.google-apps.presentation":
28108
+ return "presentation";
28109
+ case "application/vnd.google-apps.form":
28110
+ return "form";
28111
+ case "application/vnd.google-apps.drawing":
28112
+ return "drawing";
28113
+ case "application/vnd.google-apps.folder":
28114
+ return "folder";
28115
+ default:
28116
+ return "file";
28117
+ }
28118
+ }
28119
+ function openInDriveUrl(options) {
28120
+ validateDriveId2(options.fileId, "fileId");
28121
+ if (options.discussionId !== undefined) {
28122
+ validateDriveId2(options.discussionId, "discussionId");
28123
+ }
28124
+ const kind = options.isFolder ? "folder" : classifyMimeType(options.mimeType);
28125
+ const base = baseUrlFor(kind, options.fileId);
28126
+ if (options.discussionId !== undefined) {
28127
+ return `${base}?disco=${encodeURIComponent(options.discussionId)}`;
28128
+ }
28129
+ return base;
28130
+ }
28131
+ function baseUrlFor(kind, fileId) {
28132
+ switch (kind) {
28133
+ case "doc":
28134
+ return `https://docs.google.com/document/d/${fileId}/edit`;
28135
+ case "spreadsheet":
28136
+ return `https://docs.google.com/spreadsheets/d/${fileId}/edit`;
28137
+ case "presentation":
28138
+ return `https://docs.google.com/presentation/d/${fileId}/edit`;
28139
+ case "form":
28140
+ return `https://docs.google.com/forms/d/${fileId}/edit`;
28141
+ case "drawing":
28142
+ return `https://docs.google.com/drawings/d/${fileId}/edit`;
28143
+ case "folder":
28144
+ return `https://drive.google.com/drive/folders/${fileId}`;
28145
+ case "file":
28146
+ return `https://drive.google.com/file/d/${fileId}/view`;
28147
+ }
28148
+ }
28149
+ function validateDriveId2(id, fieldName) {
28150
+ if (id.length === 0) {
28151
+ throw new Error(`Drive ${fieldName} must not be empty`);
28152
+ }
28153
+ if (!/^[A-Za-z0-9_-]+$/.test(id)) {
28154
+ throw new Error(`Drive ${fieldName} '${id.slice(0, 30)}${id.length > 30 ? "\u2026" : ""}' contains invalid characters. Expected base64-url-safe (alphanumerics + - + _).`);
28155
+ }
28156
+ }
28157
+ function openInDriveButton(options) {
28158
+ return {
28159
+ text: "\uD83D\uDCD6 Open in Drive",
28160
+ url: openInDriveUrl(options)
27812
28161
  };
27813
- regexpParse.isRegExpString = isRegExpString;
27814
- return regexpParse;
27815
28162
  }
27816
- function requireRegexpStringMatcher() {
27817
- if (hasRequiredRegexpStringMatcher)
27818
- return regexpStringMatcher;
27819
- hasRequiredRegexpStringMatcher = 1;
27820
- (function(exports$1) {
27821
- var __importDefault = regexpStringMatcher && regexpStringMatcher.__importDefault || function(mod) {
27822
- return mod && mod.__esModule ? mod : { default: mod };
27823
- };
27824
- Object.defineProperty(exports$1, "__esModule", { value: true });
27825
- exports$1.matchPatterns = exports$1.createRegExp = undefined;
27826
- var lodash_uniq_1 = __importDefault(requireLodash_uniq());
27827
- var lodash_uniqwith_1 = __importDefault(requireLodash_uniqwith());
27828
- var lodash_sortby_1 = __importDefault(requireLodash_sortby());
27829
- var escape_string_regexp_1 = __importDefault(requireEscapeStringRegexp());
27830
- var regexp_parse_1 = requireRegexpParse();
27831
- var DEFAULT_FLAGS = "ug";
27832
- var defaultFlags = function(flagsString) {
27833
- if (flagsString.length === 0) {
27834
- return DEFAULT_FLAGS;
27835
- }
27836
- return (0, lodash_uniq_1.default)((flagsString + DEFAULT_FLAGS).split("")).join("");
27837
- };
27838
- var createRegExp = function(patternString, defaultFlag) {
27839
- if (defaultFlag === undefined) {
27840
- defaultFlag = DEFAULT_FLAGS;
27841
- }
27842
- if (patternString.length === 0) {
27843
- throw new Error("Empty string can not handled");
27844
- }
27845
- if ((0, regexp_parse_1.isRegExpString)(patternString)) {
27846
- var regExpStructure = (0, regexp_parse_1.parseRegExpString)(patternString);
27847
- if (regExpStructure) {
27848
- return new RegExp(regExpStructure.source, defaultFlags(regExpStructure.flagString));
27849
- }
27850
- throw new Error('"'.concat(patternString, '" can not parse as RegExp.'));
27851
- } else {
27852
- return new RegExp((0, escape_string_regexp_1.default)(patternString), defaultFlag);
27853
- }
27854
- };
27855
- exports$1.createRegExp = createRegExp;
27856
- var isEqualMatchPatternResult = function(a, b) {
27857
- return a.startIndex === b.startIndex && a.endIndex === b.endIndex && a.match === b.match;
27858
- };
27859
- var matchPatterns = function(text, regExpLikeStrings) {
27860
- var matchPatternResults = [];
27861
- regExpLikeStrings.map(function(patternString) {
27862
- return (0, exports$1.createRegExp)(patternString);
27863
- }).forEach(function(regExp) {
27864
- var results = text.matchAll(regExp);
27865
- Array.from(results).forEach(function(result) {
27866
- if (result.index === undefined) {
27867
- return;
27868
- }
27869
- var match = result[0];
27870
- var index = result.index;
27871
- matchPatternResults.push({
27872
- match,
27873
- captures: result.slice(1),
27874
- startIndex: index,
27875
- endIndex: index + match.length
27876
- });
27877
- });
27878
- });
27879
- var uniqResults = (0, lodash_uniqwith_1.default)(matchPatternResults, isEqualMatchPatternResult);
27880
- return (0, lodash_sortby_1.default)(uniqResults, ["startIndex", "endIndex"]);
27881
- };
27882
- exports$1.matchPatterns = matchPatterns;
27883
- })(regexpStringMatcher);
27884
- return regexpStringMatcher;
28163
+ function scopeToOpenInDriveButton(scope, mimeTypeHint) {
28164
+ const parsed = parseDriveScope(scope);
28165
+ if (parsed === null)
28166
+ return null;
28167
+ if (parsed.target.kind === "all")
28168
+ return null;
28169
+ if (parsed.target.kind === "folder") {
28170
+ return openInDriveButton({ fileId: parsed.target.folder_id, isFolder: true });
28171
+ }
28172
+ return openInDriveButton({
28173
+ fileId: parsed.target.doc_id,
28174
+ mimeType: mimeTypeHint
28175
+ });
28176
+ }
28177
+ function parseDriveScope(scope) {
28178
+ if (!scope.startsWith("doc:gdrive:"))
28179
+ return null;
28180
+ let rest = scope.slice("doc:gdrive:".length);
28181
+ let action = "read";
28182
+ if (rest.startsWith("write:")) {
28183
+ action = "write";
28184
+ rest = rest.slice("write:".length);
28185
+ } else if (rest.startsWith("suggest:")) {
28186
+ action = "suggest";
28187
+ rest = rest.slice("suggest:".length);
28188
+ }
28189
+ if (rest === "**")
28190
+ return { action, target: { kind: "all" } };
28191
+ if (rest.startsWith("folder/")) {
28192
+ const tail = rest.slice("folder/".length);
28193
+ if (!tail.endsWith("/**"))
28194
+ return null;
28195
+ const folder_id = tail.slice(0, -"/**".length);
28196
+ if (!/^[A-Za-z0-9_-]+$/.test(folder_id))
28197
+ return null;
28198
+ return { action, target: { kind: "folder", folder_id } };
28199
+ }
28200
+ if (!/^[A-Za-z0-9_-]+$/.test(rest))
28201
+ return null;
28202
+ return { action, target: { kind: "doc", doc_id: rest } };
27885
28203
  }
27886
- var commonjsGlobal, regexpStringMatcher, lodash_uniq, hasRequiredLodash_uniq, lodash_uniqwith, hasRequiredLodash_uniqwith, lodash_sortby, hasRequiredLodash_sortby, escapeStringRegexp, hasRequiredEscapeStringRegexp, regexpParse, hasRequiredRegexpParse, hasRequiredRegexpStringMatcher, regexpStringMatcherExports, OID_DATA, OID_SHA1, OID_SHA256, OID_SHA384, OID_SHA512, typeMap;
27887
- var init_module2 = __esm(() => {
27888
- commonjsGlobal = typeof globalThis !== "undefined" ? globalThis : typeof window !== "undefined" ? window : typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : {};
27889
- regexpStringMatcher = {};
27890
- lodash_sortby = { exports: {} };
27891
- lodash_sortby.exports;
27892
- regexpParse = {};
27893
- regexpStringMatcherExports = requireRegexpStringMatcher();
27894
- OID_DATA = new Uint8Array([42, 134, 72, 134, 247, 13, 1, 7, 1]);
27895
- OID_SHA1 = new Uint8Array([43, 14, 3, 2, 26]);
27896
- OID_SHA256 = new Uint8Array([96, 134, 72, 1, 101, 3, 4, 2, 1]);
27897
- OID_SHA384 = new Uint8Array([96, 134, 72, 1, 101, 3, 4, 2, 2]);
27898
- OID_SHA512 = new Uint8Array([96, 134, 72, 1, 101, 3, 4, 2, 3]);
27899
- typeMap = new Map([
27900
- ["ghp", "GitHub personal access tokens"],
27901
- ["gho", "OAuth access tokens"],
27902
- ["ghu", "GitHub user-to-server tokens"],
27903
- ["ghs", "GitHub server-to-server tokens"],
27904
- ["ghr", "refresh tokens"],
27905
- ["github_pat", "fine-grained personal access tokens"]
27906
- ]);
27907
- });
27908
28204
 
27909
- // secret-detect/secretlint-source.ts
27910
- var init_secretlint_source = __esm(() => {
27911
- init_module();
27912
- init_module2();
27913
- init_suppressor();
27914
- });
28205
+ // gateway/oversize-card-body.ts
28206
+ function truncateRawToFit(input) {
28207
+ const { raw, render, cap, sentinel } = input;
28208
+ const hardLimit = input.hardLimit ?? cap + 196;
28209
+ const fullBody = render(raw);
28210
+ if (fullBody.length <= cap) {
28211
+ return { body: fullBody, truncated: false };
28212
+ }
28213
+ let lo = 0;
28214
+ let hi = raw.length;
28215
+ let bestSliceLen = 0;
28216
+ while (lo <= hi) {
28217
+ const mid = lo + hi >>> 1;
28218
+ const candidate = raw.slice(0, mid) + sentinel;
28219
+ if (render(candidate).length <= cap) {
28220
+ bestSliceLen = mid;
28221
+ lo = mid + 1;
28222
+ } else {
28223
+ hi = mid - 1;
28224
+ }
28225
+ }
28226
+ let chosenRaw = raw.slice(0, bestSliceLen);
28227
+ const lastNl = chosenRaw.lastIndexOf(`
28228
+ `);
28229
+ if (lastNl > 0)
28230
+ chosenRaw = chosenRaw.slice(0, lastNl);
28231
+ let body = render(chosenRaw + sentinel);
28232
+ if (body.length > hardLimit) {
28233
+ body = body.slice(0, hardLimit - 1);
28234
+ }
28235
+ return { body, truncated: true };
28236
+ }
27915
28237
 
27916
28238
  // gateway/auth-line.ts
27917
28239
  function escapeHtml8(s) {
@@ -32025,7 +32347,7 @@ function renderWorkerActivity(v) {
32025
32347
  const toolWord = v.toolCount === 1 ? "tool" : "tools";
32026
32348
  const header = `\uD83D\uDEE0 <b>Worker</b> \u00b7 <i>${escapeHtml(desc)}</i>`;
32027
32349
  const finished = v.state === "done" || v.state === "failed";
32028
- const steps = (v.narrativeLines ?? []).map((s) => stripMarkdown(s)).filter((s) => s.length > 0).map((s) => escapeHtml(truncate(s, STEP_MAX)));
32350
+ const steps = (v.narrativeLines ?? []).map((s) => stripMarkdown(s).replace(/\s+/g, " ").trim()).filter((s) => s.length > 0).map((s) => escapeHtml(truncate(s, STEP_MAX)));
32029
32351
  if (finished) {
32030
32352
  const verb = v.state === "done" ? "completed" : "failed";
32031
32353
  const lines2 = [header, `<i>finished \u00b7 ${verb} \u00b7 ${v.toolCount} ${toolWord} \u00b7 ${elapsed}</i>`];
@@ -32043,7 +32365,7 @@ function renderWorkerActivity(v) {
32043
32365
  if (steps.length > 0) {
32044
32366
  appendStepFeed(lines, steps, false);
32045
32367
  } else {
32046
- const summary = stripMarkdown(v.latestSummary);
32368
+ const summary = stripMarkdown(v.latestSummary).replace(/\s+/g, " ").trim();
32047
32369
  if (summary.length > 0) {
32048
32370
  lines.push(`<b>\u2192 ${escapeHtml(truncate(summary, STEP_MAX))}</b>`);
32049
32371
  } else {
@@ -41633,7 +41955,7 @@ var INLINE_PH = `${NULL}VS_INLINE`;
41633
41955
  var HTML_CODE_PH = `${NULL}VS_HTMLCODE`;
41634
41956
  var HTML_PRE_PH = `${NULL}VS_HTMLPRE`;
41635
41957
  var URL_PH = `${NULL}VS_URL`;
41636
- var URL_RE = /https?:\/\/\S+/g;
41958
+ var URL_RE2 = /https?:\/\/\S+/g;
41637
41959
  function enabled4() {
41638
41960
  const v = process.env.SWITCHROOM_DISABLE_VOICE_SCRUB;
41639
41961
  return !(v === "1" || v === "true");
@@ -41661,7 +41983,7 @@ function park(text) {
41661
41983
  parts.push({ prefix: INLINE_PH, idx, raw: m });
41662
41984
  return `${INLINE_PH}${idx}${NULL}`;
41663
41985
  });
41664
- parked = parked.replace(URL_RE, (m) => {
41986
+ parked = parked.replace(URL_RE2, (m) => {
41665
41987
  const idx = parts.length;
41666
41988
  parts.push({ prefix: URL_PH, idx, raw: m });
41667
41989
  return `${URL_PH}${idx}${NULL}`;
@@ -42412,10 +42734,10 @@ class PreambleSuppressor {
42412
42734
  this.setTimer = deps.setTimer ?? ((fn, ms) => setTimeout(fn, ms));
42413
42735
  this.clearTimer = deps.clearTimer ?? ((h) => clearTimeout(h));
42414
42736
  }
42415
- onText(chunk) {
42416
- if (chunk.length === 0)
42737
+ onText(chunk2) {
42738
+ if (chunk2.length === 0)
42417
42739
  return;
42418
- this.pendingBuffer += chunk;
42740
+ this.pendingBuffer += chunk2;
42419
42741
  if (this.pendingTimer != null)
42420
42742
  this.clearTimer(this.pendingTimer);
42421
42743
  this.pendingTimer = this.setTimer(() => this.flushNow(), this.bufferMs);
@@ -44667,8 +44989,8 @@ async function hostdRequest(opts, req) {
44667
44989
  reject(err);
44668
44990
  }
44669
44991
  });
44670
- socket.on("data", (chunk) => {
44671
- buf += chunk.toString("utf8");
44992
+ socket.on("data", (chunk2) => {
44993
+ buf += chunk2.toString("utf8");
44672
44994
  if (Buffer.byteLength(buf, "utf8") > MAX_FRAME_BYTES3 * 2) {
44673
44995
  if (settled)
44674
44996
  return;
@@ -44910,10 +45232,10 @@ function startWebhookIngestServer(opts) {
44910
45232
  } catch {}
44911
45233
  conn.end();
44912
45234
  };
44913
- conn.on("data", (chunk) => {
45235
+ conn.on("data", (chunk2) => {
44914
45236
  if (handled)
44915
45237
  return;
44916
- buf += chunk;
45238
+ buf += chunk2;
44917
45239
  if (buf.length > MAX_REQUEST_BYTES) {
44918
45240
  reply({ status: "error", error: "request too large" });
44919
45241
  return;
@@ -48121,253 +48443,8 @@ function resolveShutdownMarker(prior, signal, now, maxPreserveAgeMs = REASON_PRE
48121
48443
  return { ts: now, signal, reason: EXTERNAL_RESTART_FALLBACK_REASON };
48122
48444
  }
48123
48445
 
48124
- // secret-detect/patterns.ts
48125
- var ANCHORED_PATTERNS = [
48126
- { rule_id: "anthropic_api_key", regex: /\b(sk-ant-[A-Za-z0-9_-]{8,})\b/g, captureIndex: 1, slugHint: "anthropic_api_key" },
48127
- { rule_id: "anthropic_oauth_code", regex: /(?:^|\s)([A-Za-z0-9_-]{20,}#[A-Za-z0-9_-]{20,})(?=\s|$)/gm, captureIndex: 1, slugHint: "anthropic_oauth_code" },
48128
- { rule_id: "openai_api_key", regex: /\b(sk-[A-Za-z0-9_-]{20,})\b/g, captureIndex: 1, slugHint: "openai_api_key" },
48129
- { rule_id: "github_pat_classic", regex: /\b(ghp_[A-Za-z0-9]{20,})\b/g, captureIndex: 1, slugHint: "github_pat" },
48130
- { rule_id: "github_pat_fine_grained", regex: /\b(github_pat_[A-Za-z0-9_]{20,})\b/g, captureIndex: 1, slugHint: "github_pat" },
48131
- { rule_id: "slack_token", regex: /\b(xox[baprs]-[A-Za-z0-9-]{10,})\b/g, captureIndex: 1, slugHint: "slack_token" },
48132
- { rule_id: "slack_app_token", regex: /\b(xapp-[A-Za-z0-9-]{10,})\b/g, captureIndex: 1, slugHint: "slack_app_token" },
48133
- { rule_id: "groq_api_key", regex: /\b(gsk_[A-Za-z0-9_-]{10,})\b/g, captureIndex: 1, slugHint: "groq_api_key" },
48134
- { rule_id: "google_api_key", regex: /\b(AIza[0-9A-Za-z\-_]{20,})\b/g, captureIndex: 1, slugHint: "google_api_key" },
48135
- { rule_id: "perplexity_api_key", regex: /\b(pplx-[A-Za-z0-9_-]{10,})\b/g, captureIndex: 1, slugHint: "perplexity_api_key" },
48136
- { rule_id: "npm_token", regex: /\b(npm_[A-Za-z0-9]{10,})\b/g, captureIndex: 1, slugHint: "npm_token" },
48137
- { rule_id: "telegram_bot_token_prefixed", regex: /\bbot(\d{6,}:[A-Za-z0-9_-]{20,})\b/g, captureIndex: 1, slugHint: "telegram_bot_token" },
48138
- { rule_id: "telegram_bot_token", regex: /\b(\d{6,}:[A-Za-z0-9_-]{20,})\b/g, captureIndex: 1, slugHint: "telegram_bot_token" },
48139
- { rule_id: "aws_access_key", regex: /\b(AKIA[0-9A-Z]{16})\b/g, captureIndex: 1, slugHint: "aws_access_key" },
48140
- { rule_id: "jwt", regex: /\b(eyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,})\b/g, captureIndex: 1, slugHint: "jwt" }
48141
- ];
48142
- var STRUCTURED_PATTERNS = [
48143
- {
48144
- rule_id: "env_key_value",
48145
- regex: /\b([A-Z0-9_]*(?:KEY|TOKEN|SECRET|PASSWORD|PASSWD))\b\s*[=:]\s*(["']?)([^\s"'\\]+)\2/g,
48146
- captureIndex: 3,
48147
- slugHint: "env"
48148
- },
48149
- {
48150
- rule_id: "json_secret_field",
48151
- regex: /"(?:apiKey|token|secret|password|passwd|accessToken|refreshToken)"\s*:\s*"([^"]+)"/g,
48152
- captureIndex: 1,
48153
- slugHint: "json_secret"
48154
- },
48155
- {
48156
- rule_id: "cli_flag",
48157
- regex: /--(?:api[-_]?key|hook[-_]?token|token|secret|password|passwd)\s+(["']?)([^\s"']+)\1/g,
48158
- captureIndex: 2,
48159
- slugHint: "cli_flag"
48160
- },
48161
- {
48162
- rule_id: "bearer_auth_header",
48163
- regex: /Authorization\s*[:=]\s*Bearer\s+([A-Za-z0-9._\-+=]+)/g,
48164
- captureIndex: 1,
48165
- slugHint: "bearer_token"
48166
- },
48167
- {
48168
- rule_id: "bearer_loose",
48169
- regex: /\bBearer\s+([A-Za-z0-9._\-+=]{18,})\b/g,
48170
- captureIndex: 1,
48171
- slugHint: "bearer_token"
48172
- },
48173
- {
48174
- rule_id: "pem_private_key",
48175
- regex: /-----BEGIN [A-Z ]*PRIVATE KEY-----[\s\S]+?-----END [A-Z ]*PRIVATE KEY-----/g,
48176
- captureIndex: 0,
48177
- slugHint: "pem_private_key"
48178
- }
48179
- ];
48180
- var ALL_PATTERNS = [...ANCHORED_PATTERNS, ...STRUCTURED_PATTERNS];
48181
-
48182
- // secret-detect/entropy.ts
48183
- function shannonEntropy(s) {
48184
- if (s.length === 0)
48185
- return 0;
48186
- const counts = new Map;
48187
- for (const ch of s) {
48188
- counts.set(ch, (counts.get(ch) ?? 0) + 1);
48189
- }
48190
- let h = 0;
48191
- const len = s.length;
48192
- for (const c of counts.values()) {
48193
- const p = c / len;
48194
- h -= p * Math.log2(p);
48195
- }
48196
- return h;
48197
- }
48198
-
48199
- // secret-detect/kv-scanner.ts
48200
- var KV_RE = /\b([A-Za-z_][A-Za-z0-9_-]*(?:password|passwd|token|secret|key|api[_-]?key))\s*[:=]\s*["']?([^\s"'\\]{8,})["']?/gi;
48201
- var KV_ENTROPY_THRESHOLD = 4;
48202
- function scanKeyValue(text) {
48203
- const hits = [];
48204
- KV_RE.lastIndex = 0;
48205
- let m;
48206
- while ((m = KV_RE.exec(text)) !== null) {
48207
- const [, keyName, value] = m;
48208
- if (!value)
48209
- continue;
48210
- const h = shannonEntropy(value);
48211
- if (h < KV_ENTROPY_THRESHOLD)
48212
- continue;
48213
- const valueOffsetInMatch = m[0].indexOf(value, keyName.length);
48214
- if (valueOffsetInMatch < 0)
48215
- continue;
48216
- const start = m.index + valueOffsetInMatch;
48217
- const end = start + value.length;
48218
- hits.push({
48219
- rule_id: "kv_entropy",
48220
- start,
48221
- end,
48222
- matched_text: value,
48223
- key_name: keyName,
48224
- confidence: "ambiguous"
48225
- });
48226
- }
48227
- return hits;
48228
- }
48229
-
48230
- // secret-detect/chunker.ts
48231
- var CHUNK_THRESHOLD = 32 * 1024;
48232
- var WINDOW_SIZE = 16 * 1024;
48233
- var OVERLAP = 1024;
48234
- function chunk(text) {
48235
- if (text.length <= CHUNK_THRESHOLD) {
48236
- return [{ offset: 0, text }];
48237
- }
48238
- const out = [];
48239
- let offset = 0;
48240
- while (offset < text.length) {
48241
- const end = Math.min(offset + WINDOW_SIZE, text.length);
48242
- out.push({ offset, text: text.slice(offset, end) });
48243
- if (end >= text.length)
48244
- break;
48245
- offset = end - OVERLAP;
48246
- }
48247
- return out;
48248
- }
48249
-
48250
- // secret-detect/index.ts
48251
- init_suppressor();
48252
-
48253
- // secret-detect/mask.ts
48254
- function maskToken(s) {
48255
- if (s.length >= 18) {
48256
- return `${s.slice(0, 6)}...${s.slice(-4)}`;
48257
- }
48258
- return "***";
48259
- }
48260
- // secret-detect/url-redact.ts
48261
- var SENSITIVE_PARAMS = new Set([
48262
- "token",
48263
- "key",
48264
- "api_key",
48265
- "apikey",
48266
- "secret",
48267
- "access_token",
48268
- "password",
48269
- "pass",
48270
- "auth",
48271
- "client_secret",
48272
- "refresh_token",
48273
- "signature"
48274
- ]);
48275
-
48276
- // secret-detect/index.ts
48277
- init_secretlint_source();
48278
- function detectSecrets(text) {
48279
- if (!text || text.length === 0)
48280
- return [];
48281
- const windows = chunk(text);
48282
- const raw = [];
48283
- for (const win of windows) {
48284
- for (const p of ALL_PATTERNS) {
48285
- const re = new RegExp(p.regex.source, p.regex.flags.includes("g") ? p.regex.flags : p.regex.flags + "g");
48286
- let m;
48287
- while ((m = re.exec(win.text)) !== null) {
48288
- if (m[0].length === 0) {
48289
- re.lastIndex++;
48290
- continue;
48291
- }
48292
- const cap = p.captureIndex === 0 ? m[0] : m[p.captureIndex];
48293
- if (!cap)
48294
- continue;
48295
- const matchStart = p.captureIndex === 0 ? m.index : m.index + m[0].indexOf(cap);
48296
- if (matchStart < 0)
48297
- continue;
48298
- const globalStart = win.offset + matchStart;
48299
- const globalEnd = globalStart + cap.length;
48300
- const keyName = p.rule_id === "env_key_value" ? m[1] : undefined;
48301
- if (p.rule_id === "env_key_value") {
48302
- const ENV_KV_MIN_LEN = 12;
48303
- const ENV_KV_MIN_ENTROPY = 3.5;
48304
- if (cap.length < ENV_KV_MIN_LEN)
48305
- continue;
48306
- if (shannonEntropy(cap) < ENV_KV_MIN_ENTROPY)
48307
- continue;
48308
- }
48309
- raw.push({
48310
- rule_id: p.rule_id,
48311
- start: globalStart,
48312
- end: globalEnd,
48313
- matched_text: cap,
48314
- key_name: keyName,
48315
- confidence: "high"
48316
- });
48317
- }
48318
- }
48319
- const kvHits = scanKeyValue(win.text);
48320
- for (const h of kvHits) {
48321
- raw.push({ ...h, start: h.start + win.offset, end: h.end + win.offset });
48322
- }
48323
- }
48324
- const deduped = dedupeRaw(raw);
48325
- const final = dropOverlaps(deduped);
48326
- const existing = new Set;
48327
- const out = [];
48328
- for (const h of final) {
48329
- const suggested_slug = deriveSlug({ key_name: h.key_name, rule_id: h.rule_id }, existing);
48330
- existing.add(suggested_slug);
48331
- out.push({
48332
- rule_id: h.rule_id,
48333
- matched_text: h.matched_text,
48334
- start: h.start,
48335
- end: h.end,
48336
- confidence: h.confidence,
48337
- suppressed: isSuppressed(text, h.start, h.end),
48338
- suggested_slug,
48339
- key_name: h.key_name
48340
- });
48341
- }
48342
- out.sort((a, b) => a.start - b.start);
48343
- return out;
48344
- }
48345
- function dedupeRaw(raw) {
48346
- const seen = new Map;
48347
- for (const h of raw) {
48348
- const key = `${h.start}:${h.end}`;
48349
- const existing = seen.get(key);
48350
- if (!existing) {
48351
- seen.set(key, h);
48352
- continue;
48353
- }
48354
- if (existing.confidence === "ambiguous" && h.confidence === "high") {
48355
- seen.set(key, h);
48356
- }
48357
- }
48358
- return Array.from(seen.values());
48359
- }
48360
- function dropOverlaps(hits) {
48361
- const sorted = [...hits].sort((a, b) => a.end - a.start - (b.end - b.start));
48362
- const out = [];
48363
- for (const h of sorted) {
48364
- const contained = out.some((existing) => existing !== h && existing.start <= h.start && existing.end >= h.end && !(existing.start === h.start && existing.end === h.end));
48365
- if (!contained)
48366
- out.push(h);
48367
- }
48368
- out.sort((a, b) => a.start - b.start || a.end - b.end);
48369
- return out;
48370
- }
48446
+ // secret-detect/pipeline.ts
48447
+ init_secret_detect();
48371
48448
 
48372
48449
  // secret-detect/rewrite.ts
48373
48450
  function rewritePrompt(text, targets) {
@@ -48731,7 +48808,11 @@ function recentDenialsFromAuditLog(rawAuditLog, opts) {
48731
48808
  }
48732
48809
 
48733
48810
  // secret-detect/index.ts
48811
+ init_patterns();
48812
+ init_kv_scanner();
48813
+ init_chunker();
48734
48814
  init_suppressor();
48815
+ init_url_redact();
48735
48816
  init_secretlint_source();
48736
48817
  function detectSecrets2(text) {
48737
48818
  if (!text || text.length === 0)
@@ -48827,6 +48908,32 @@ function dropOverlaps2(hits) {
48827
48908
  return out;
48828
48909
  }
48829
48910
 
48911
+ // secret-detect/redact.ts
48912
+ init_secret_detect();
48913
+ init_url_redact();
48914
+ var REDACTED_MARKER2 = "[REDACTED]";
48915
+ function redact2(text) {
48916
+ if (!text || text.length === 0)
48917
+ return text;
48918
+ const urlScrubbed = redactUrls(text);
48919
+ const hits = detectSecrets(urlScrubbed);
48920
+ if (hits.length === 0)
48921
+ return urlScrubbed;
48922
+ const sorted = [...hits].sort((a, b) => b.start - a.start);
48923
+ let out = urlScrubbed;
48924
+ for (const h of sorted) {
48925
+ out = out.slice(0, h.start) + redactedMarker2(h.rule_id) + out.slice(h.end);
48926
+ }
48927
+ return out;
48928
+ }
48929
+ function redactedMarker2(ruleId) {
48930
+ const trimmed = ruleId.replace(/^(kv|env)_/, "");
48931
+ if (!trimmed || trimmed === "key_value" || trimmed === "kv_entropy") {
48932
+ return REDACTED_MARKER2;
48933
+ }
48934
+ return `[REDACTED:${trimmed}]`;
48935
+ }
48936
+
48830
48937
  // admin-commands/index.ts
48831
48938
  var ADMIN_COMMAND_NAMES = new Set([
48832
48939
  "agents",
@@ -49053,6 +49160,10 @@ function projectSubagentLine(line, agentId, state4) {
49053
49160
  events.push({ kind: "sub_agent_text", agentId, text });
49054
49161
  }
49055
49162
  }
49163
+ const stopReason = message?.stop_reason;
49164
+ if (stopReason === "end_turn") {
49165
+ events.push({ kind: "sub_agent_turn_end", agentId });
49166
+ }
49056
49167
  return events;
49057
49168
  }
49058
49169
  if (type === "system" && obj.subtype === "turn_duration") {
@@ -49190,6 +49301,10 @@ function redactSecrets(text) {
49190
49301
  return out;
49191
49302
  }
49192
49303
  // registry/subagents-schema.ts
49304
+ function countRunningBackgroundSubagents(db2) {
49305
+ const row = db2.prepare("SELECT count(*) AS n FROM subagents WHERE background = 1 AND status = 'running'").get();
49306
+ return row?.n ?? 0;
49307
+ }
49193
49308
  function recordSubagentEnd(db2, args) {
49194
49309
  db2.prepare(`
49195
49310
  UPDATE subagents
@@ -49899,6 +50014,15 @@ function startSubagentWatcher(config) {
49899
50014
  },
49900
50015
  getRegistry() {
49901
50016
  return registry;
50017
+ },
50018
+ countRunningBackgroundWorkers() {
50019
+ if (db2 == null)
50020
+ return null;
50021
+ try {
50022
+ return countRunningBackgroundSubagents(db2);
50023
+ } catch {
50024
+ return null;
50025
+ }
49902
50026
  }
49903
50027
  };
49904
50028
  }
@@ -50371,6 +50495,9 @@ var SEVERITY_RANK2 = {
50371
50495
  critical: 3
50372
50496
  };
50373
50497
 
50498
+ // ../src/secret-detect/redact.ts
50499
+ init_redact();
50500
+
50374
50501
  // ../src/issues/store.ts
50375
50502
  var ISSUES_FILE = "issues.jsonl";
50376
50503
  var ISSUES_LOCK = "issues.lock";
@@ -50786,6 +50913,18 @@ function describeGrant(toolName, inputPreview, option) {
50786
50913
  return naturalAction(toolName, inputPreview);
50787
50914
  }
50788
50915
  }
50916
+ function formatPermissionResumeMessage(opts) {
50917
+ const who = opts.agentName && opts.agentName.length > 0 ? `<b>${escapeTgHtml(capFirst(opts.agentName))}</b>` : `<b>Agent</b>`;
50918
+ const act = (opts.action ?? "").trim();
50919
+ const hasAction = act.length > 0;
50920
+ if (opts.behavior === "allow") {
50921
+ return hasAction ? `\u25b6\ufe0f ${who} \u2014 got it, continuing: <i>${escapeTgHtml(act)}</i>` : `\u25b6\ufe0f ${who} \u2014 got it, back to work.`;
50922
+ }
50923
+ if (opts.timeoutMinutes != null) {
50924
+ return hasAction ? `\uD83D\uDEAB ${who} \u2014 no answer in ${opts.timeoutMinutes}m, continuing without it (<i>${escapeTgHtml(act)}</i>).` : `\uD83D\uDEAB ${who} \u2014 no answer in ${opts.timeoutMinutes}m, continuing without it.`;
50925
+ }
50926
+ return hasAction ? `\uD83D\uDEAB ${who} \u2014 noted, I won't ${escapeTgHtml(lowerFirst(act))}. Continuing without it.` : `\uD83D\uDEAB ${who} \u2014 noted, continuing without it.`;
50927
+ }
50789
50928
  function resolveSkillName(input) {
50790
50929
  return readString(input, "skill") ?? readString(input, "skill_name") ?? readString(input, "skillName") ?? readString(input, "name") ?? skillBasenameFromPath(input);
50791
50930
  }
@@ -51520,10 +51659,10 @@ function sweepStaleTurnActiveMarker(stateDir, opts) {
51520
51659
  }
51521
51660
 
51522
51661
  // ../src/build-info.ts
51523
- var VERSION = "0.14.27";
51524
- var COMMIT_SHA = "5ae9596e";
51525
- var COMMIT_DATE = "2026-06-01T01:58:04Z";
51526
- var LATEST_PR = 2042;
51662
+ var VERSION = "0.14.29";
51663
+ var COMMIT_SHA = "33f6300c";
51664
+ var COMMIT_DATE = "2026-06-01T04:54:34Z";
51665
+ var LATEST_PR = 2052;
51527
51666
  var COMMITS_AHEAD_OF_TAG = 0;
51528
51667
 
51529
51668
  // gateway/boot-version.ts
@@ -52941,6 +53080,9 @@ function finalizeStatusReaction(chatId, threadId, reason = "done") {
52941
53080
  purgeReactionTracking(key);
52942
53081
  }
52943
53082
  function countRunningWorkers() {
53083
+ const dbCount = subagentWatcher?.countRunningBackgroundWorkers?.();
53084
+ if (dbCount != null)
53085
+ return dbCount;
52944
53086
  const reg = subagentWatcher?.getRegistry();
52945
53087
  if (reg == null)
52946
53088
  return 0;
@@ -52957,6 +53099,31 @@ function resumeReactionAfterVerdict() {
52957
53099
  return;
52958
53100
  activeStatusReactions.get(statusKey(turn.sessionChatId, turn.sessionThreadId))?.setThinking();
52959
53101
  }
53102
+ function postPermissionResumeMessage(opts) {
53103
+ if (process.env.SWITCHROOM_RESUME_MSG === "0")
53104
+ return;
53105
+ const text = formatPermissionResumeMessage({
53106
+ agentName: process.env.SWITCHROOM_AGENT_NAME ?? null,
53107
+ behavior: opts.behavior,
53108
+ action: opts.action,
53109
+ timeoutMinutes: opts.timeoutMinutes
53110
+ });
53111
+ const turn = currentTurn;
53112
+ const targets = turn != null ? [{ chatId: turn.sessionChatId, threadId: turn.sessionThreadId }] : loadAccess().allowFrom.map((chatId) => ({
53113
+ chatId,
53114
+ threadId: resolveAgentOutboundTopic({
53115
+ kind: "permission",
53116
+ turnInitiated: false,
53117
+ originThreadId: undefined
53118
+ })
53119
+ }));
53120
+ for (const { chatId, threadId } of targets) {
53121
+ swallowingApiCall(() => bot.api.sendMessage(chatId, text, {
53122
+ parse_mode: "HTML",
53123
+ ...threadId != null ? { message_thread_id: threadId } : {}
53124
+ }), { chat_id: chatId, verb: "permission-resume", ...threadId != null ? { threadId } : {} });
53125
+ }
53126
+ }
52960
53127
  function resolveThreadId(chat_id, explicit) {
52961
53128
  if (explicit != null)
52962
53129
  return Number(explicit);
@@ -53371,6 +53538,11 @@ var pendingStateReaper = setInterval(() => {
53371
53538
  if (now - v.startedAt > PERMISSION_TTL_MS) {
53372
53539
  dispatchPermissionVerdict({ type: "permission", requestId: k, behavior: "deny" });
53373
53540
  resumeReactionAfterVerdict();
53541
+ postPermissionResumeMessage({
53542
+ behavior: "deny",
53543
+ action: naturalAction(v.tool_name, v.input_preview),
53544
+ timeoutMinutes: Math.round(PERMISSION_TTL_MS / 60000)
53545
+ });
53374
53546
  process.stderr.write(`telegram gateway: permission TTL expired \u2014 auto-deny request=${k} tool=${v.tool_name} (no operator response in ${Math.round(PERMISSION_TTL_MS / 60000)}m)
53375
53547
  `);
53376
53548
  pendingPermissions.delete(k);
@@ -54397,7 +54569,8 @@ var ALLOWED_TOOLS = new Set([
54397
54569
  "send_sticker",
54398
54570
  "send_gif",
54399
54571
  "vault_request_save",
54400
- "vault_request_access"
54572
+ "vault_request_access",
54573
+ "request_secret"
54401
54574
  ]);
54402
54575
  async function executeToolCall(tool, args) {
54403
54576
  if (!ALLOWED_TOOLS.has(tool)) {
@@ -54440,6 +54613,8 @@ async function executeToolCall(tool, args) {
54440
54613
  return executeVaultRequestSave(args);
54441
54614
  case "vault_request_access":
54442
54615
  return executeVaultRequestAccess(args);
54616
+ case "request_secret":
54617
+ return executeRequestSecret(args);
54443
54618
  default:
54444
54619
  throw new Error(`unknown tool: ${tool}`);
54445
54620
  }
@@ -54485,6 +54660,14 @@ async function executeUpdateChecklist(args) {
54485
54660
  `);
54486
54661
  return { content: [{ type: "text", text: `checklist updated (id: ${message_id})` }] };
54487
54662
  }
54663
+ function redactOutboundText(text, site) {
54664
+ const masked = redact2(text);
54665
+ if (masked !== text) {
54666
+ process.stderr.write(`telegram gateway: outbound secret masked site=${site}
54667
+ `);
54668
+ }
54669
+ return masked;
54670
+ }
54488
54671
  async function executeReply(args) {
54489
54672
  const turn = currentTurn;
54490
54673
  const chat_id = args.chat_id;
@@ -54494,6 +54677,7 @@ async function executeReply(args) {
54494
54677
  if (rawText == null || rawText === "")
54495
54678
  throw new Error("reply: text is required and cannot be empty");
54496
54679
  let text = repairEscapedWhitespace(rawText);
54680
+ text = redactOutboundText(text, "reply");
54497
54681
  {
54498
54682
  const scrub = scrubVoice(text);
54499
54683
  if (scrub.replaced > 0) {
@@ -54916,6 +55100,7 @@ async function executeStreamReply(args) {
54916
55100
  throw new Error("stream_reply: chat_id is required");
54917
55101
  if (args.text == null || args.text === "")
54918
55102
  throw new Error("stream_reply: text is required and cannot be empty");
55103
+ args.text = redactOutboundText(args.text, "stream_reply");
54919
55104
  {
54920
55105
  const scrub = scrubVoice(args.text);
54921
55106
  if (scrub.replaced > 0) {
@@ -55434,6 +55619,213 @@ async function executeVaultRequestSave(args) {
55434
55619
  ]
55435
55620
  };
55436
55621
  }
55622
+ var pendingSecretRequests = new Map;
55623
+ var armedSecretCaptures = new Map;
55624
+ var PENDING_SECRET_REQUEST_TTL_MS = 1800000;
55625
+ var ARMED_SECRET_CAPTURE_TTL_MS = 600000;
55626
+ function sweepSecretRequests() {
55627
+ const now = Date.now();
55628
+ for (const [k, v] of pendingSecretRequests) {
55629
+ if (now - v.staged_at > PENDING_SECRET_REQUEST_TTL_MS)
55630
+ pendingSecretRequests.delete(k);
55631
+ }
55632
+ for (const [k, v] of armedSecretCaptures) {
55633
+ if (now - v.armed_at > ARMED_SECRET_CAPTURE_TTL_MS)
55634
+ armedSecretCaptures.delete(k);
55635
+ }
55636
+ }
55637
+ function buildSecretRequestKeyboard(stageId) {
55638
+ return {
55639
+ inline_keyboard: [
55640
+ [
55641
+ { text: "\uD83D\uDD10 Provide securely", callback_data: `vsp:provide:${stageId}` },
55642
+ { text: "\uD83D\uDEAB Decline", callback_data: `vsp:decline:${stageId}` }
55643
+ ]
55644
+ ]
55645
+ };
55646
+ }
55647
+ function renderSecretRequestCard(req) {
55648
+ const lines = [
55649
+ `\uD83D\uDD12 <b>${escapeHtmlForTg(req.agent)}</b> needs a secret:`,
55650
+ `<code>${escapeHtmlForTg(req.key)}</code>`
55651
+ ];
55652
+ if (req.reason)
55653
+ lines.push(`<i>${escapeHtmlForTg(req.reason)}</i>`);
55654
+ lines.push("", "Tap <b>Provide securely</b>, then send the value as your next message. I\u2019ll delete it instantly and store it in the vault \u2014 it is never shown in chat or to the agent.");
55655
+ return lines.join(`
55656
+ `);
55657
+ }
55658
+ async function executeRequestSecret(args) {
55659
+ const chat_id = args.chat_id;
55660
+ if (!chat_id)
55661
+ throw new Error("request_secret: chat_id is required");
55662
+ const key = args.key;
55663
+ if (!key || typeof key !== "string")
55664
+ throw new Error("request_secret: key is required");
55665
+ const reason = typeof args.reason === "string" ? args.reason : undefined;
55666
+ assertAllowedChat(chat_id);
55667
+ if (!VAULT_KEY_REGEX.test(key)) {
55668
+ throw new Error(`request_secret: key must match ${VAULT_KEY_REGEX_LABEL}`);
55669
+ }
55670
+ const agentSlug = process.env.SWITCHROOM_AGENT_NAME || "agent";
55671
+ for (const [sid, p] of pendingSecretRequests) {
55672
+ if (p.chat_id === chat_id && p.key === key)
55673
+ pendingSecretRequests.delete(sid);
55674
+ }
55675
+ const stageId = randomBytes6(4).toString("hex");
55676
+ const pending2 = { agent: agentSlug, chat_id, key, reason, staged_at: Date.now() };
55677
+ pendingSecretRequests.set(stageId, pending2);
55678
+ sweepSecretRequests();
55679
+ const text = renderSecretRequestCard(pending2);
55680
+ const threadId = args.message_thread_id != null ? Number(args.message_thread_id) : undefined;
55681
+ const sent = await retryWithThreadFallback(robustApiCall, (tid) => lockedBot.api.sendMessage(chat_id, text, {
55682
+ parse_mode: "HTML",
55683
+ reply_markup: buildSecretRequestKeyboard(stageId),
55684
+ ...tid != null && Number.isFinite(tid) ? { message_thread_id: tid } : {}
55685
+ }), { threadId, chat_id, verb: "request_secret.card" });
55686
+ pending2.card_message_id = sent.message_id;
55687
+ return {
55688
+ content: [
55689
+ {
55690
+ type: "text",
55691
+ text: `request_secret: card sent (stage_id=${stageId}, key=${key}). END YOUR TURN now and wait \u2014 a fresh inbound message arrives once the operator provides (or declines) the secret. Do NOT ask them to paste it as a normal message; the card handles it securely.`
55692
+ }
55693
+ ]
55694
+ };
55695
+ }
55696
+ async function writeRequestedSecret(key, value, chat_id) {
55697
+ if (VAULT_APPROVAL_AUTH_MODE === "telegram-id") {
55698
+ return await defaultVaultWritePosture(key, value);
55699
+ }
55700
+ const cached = vaultPassphraseCache.get(chat_id);
55701
+ if (!cached || cached.expiresAt <= Date.now()) {
55702
+ return { ok: false, output: "VAULT-LOCKED: run /vault unlock once in this chat, then have the agent re-request." };
55703
+ }
55704
+ return defaultVaultWrite(key, value, cached.passphrase);
55705
+ }
55706
+ async function captureProvidedSecret(ctx, chat_id, msgId, value) {
55707
+ const armed = armedSecretCaptures.get(chat_id);
55708
+ if (!armed || Date.now() - armed.armed_at > ARMED_SECRET_CAPTURE_TTL_MS) {
55709
+ if (armed)
55710
+ armedSecretCaptures.delete(chat_id);
55711
+ return false;
55712
+ }
55713
+ armedSecretCaptures.delete(chat_id);
55714
+ const pending2 = pendingSecretRequests.get(armed.stageId);
55715
+ pendingSecretRequests.delete(armed.stageId);
55716
+ if (msgId != null)
55717
+ await deleteSensitiveMessage(chat_id, msgId, "provided secret value");
55718
+ const write = await writeRequestedSecret(armed.key, value, chat_id);
55719
+ if (!write.ok) {
55720
+ const parsed = parseVaultCliError(write.output);
55721
+ const rendered = renderVaultCliError(parsed, { verb: "save", key: armed.key });
55722
+ const body = rendered.suppressRaw ? rendered.html : `\u26A0\uFE0F vault write failed:
55723
+ <pre>${escapeHtmlForTg(write.output)}</pre>`;
55724
+ await switchroomReply(ctx, `${body}
55725
+
55726
+ <i>The secret was NOT saved. The agent can re-request with <code>request_secret</code>.</i>`, { html: true });
55727
+ const fts = Date.now();
55728
+ const failMsg = {
55729
+ type: "inbound",
55730
+ chatId: chat_id,
55731
+ messageId: fts,
55732
+ user: "vault-broker",
55733
+ userId: 0,
55734
+ ts: fts,
55735
+ text: `\u26A0\uFE0F The secret you requested for \`vault:${armed.key}\` could NOT be saved (vault write failed). Do not assume it is available; tell the operator or try request_secret again.`,
55736
+ meta: { source: "secret_provide_failed", agent: armed.agent, key: armed.key, stage_id: armed.stageId }
55737
+ };
55738
+ const fdelivered = ipcServer.sendToAgent(armed.agent, failMsg);
55739
+ if (fdelivered)
55740
+ markClaudeBusyForInbound(failMsg);
55741
+ else
55742
+ pendingInboundBuffer.push(armed.agent, failMsg);
55743
+ return true;
55744
+ }
55745
+ await switchroomReply(ctx, `\u2705 saved as <code>vault:${escapeHtmlForTg(armed.key)}</code> (masked: <code>${escapeHtmlForTg(maskToken2(value))}</code>). The agent can now reference it.`, { html: true });
55746
+ const ts = Date.now();
55747
+ const synthetic = {
55748
+ type: "inbound",
55749
+ chatId: chat_id,
55750
+ messageId: ts,
55751
+ user: "vault-broker",
55752
+ userId: 0,
55753
+ ts,
55754
+ text: `\u2705 Operator provided the secret you requested. It is saved as ` + `\`vault:${armed.key}\` \u2014 reference it the usual way. Resume the task ` + `that was waiting on this credential. Do NOT ask the operator to paste it.`,
55755
+ meta: {
55756
+ source: "secret_provided",
55757
+ agent: armed.agent,
55758
+ key: armed.key,
55759
+ stage_id: armed.stageId
55760
+ }
55761
+ };
55762
+ const delivered = ipcServer.sendToAgent(armed.agent, synthetic);
55763
+ if (delivered)
55764
+ markClaudeBusyForInbound(synthetic);
55765
+ else
55766
+ pendingInboundBuffer.push(armed.agent, synthetic);
55767
+ process.stderr.write(`telegram gateway: secret_provided injection agent=${armed.agent} key=${armed.key} stage=${armed.stageId} delivered=${delivered}
55768
+ `);
55769
+ return true;
55770
+ }
55771
+ async function handleSecretRequestCallback(ctx, data) {
55772
+ const senderId = String(ctx.from?.id ?? "");
55773
+ const access = loadAccess();
55774
+ if (!access.allowFrom.includes(senderId)) {
55775
+ await ctx.answerCallbackQuery({ text: "Not authorized." }).catch(() => {});
55776
+ return;
55777
+ }
55778
+ const parts = data.split(":");
55779
+ const action = parts[1];
55780
+ const stageId = parts[2] ?? "";
55781
+ const pending2 = pendingSecretRequests.get(stageId);
55782
+ if (!pending2) {
55783
+ await ctx.answerCallbackQuery({ text: "This request expired." }).catch(() => {});
55784
+ return;
55785
+ }
55786
+ if (action === "provide") {
55787
+ armedSecretCaptures.set(pending2.chat_id, {
55788
+ key: pending2.key,
55789
+ agent: pending2.agent,
55790
+ stageId,
55791
+ armed_at: Date.now()
55792
+ });
55793
+ await ctx.answerCallbackQuery({ text: "Send the value now \u2014 it auto-deletes." }).catch(() => {});
55794
+ if (pending2.card_message_id != null) {
55795
+ await ctx.api.editMessageText(pending2.chat_id, pending2.card_message_id, `\uD83D\uDD10 Send the value for <code>${escapeHtmlForTg(pending2.key)}</code> as your next message \u2014 a single message, exactly as-is (don't add other text). I\u2019ll delete it instantly and store it in the vault.`, { parse_mode: "HTML", reply_markup: { inline_keyboard: [] } }).catch(() => {});
55796
+ }
55797
+ return;
55798
+ }
55799
+ if (action === "decline") {
55800
+ pendingSecretRequests.delete(stageId);
55801
+ armedSecretCaptures.delete(pending2.chat_id);
55802
+ await ctx.answerCallbackQuery({ text: "Declined." }).catch(() => {});
55803
+ if (pending2.card_message_id != null) {
55804
+ await ctx.api.editMessageText(pending2.chat_id, pending2.card_message_id, `\uD83D\uDEAB Declined \u2014 <code>${escapeHtmlForTg(pending2.key)}</code> not provided.`, {
55805
+ parse_mode: "HTML",
55806
+ reply_markup: { inline_keyboard: [] }
55807
+ }).catch(() => {});
55808
+ }
55809
+ const ts = Date.now();
55810
+ const synthetic = {
55811
+ type: "inbound",
55812
+ chatId: pending2.chat_id,
55813
+ messageId: ts,
55814
+ user: "vault-broker",
55815
+ userId: 0,
55816
+ ts,
55817
+ text: `\uD83D\uDEAB Operator declined your request for \`vault:${pending2.key}\`. Proceed without it or ask how they'd like to handle the task.`,
55818
+ meta: { source: "secret_declined", agent: pending2.agent, key: pending2.key, stage_id: stageId }
55819
+ };
55820
+ const delivered = ipcServer.sendToAgent(pending2.agent, synthetic);
55821
+ if (delivered)
55822
+ markClaudeBusyForInbound(synthetic);
55823
+ else
55824
+ pendingInboundBuffer.push(pending2.agent, synthetic);
55825
+ return;
55826
+ }
55827
+ await ctx.answerCallbackQuery().catch(() => {});
55828
+ }
55437
55829
  function buildVaultRequestAccessKeyboard(stageId) {
55438
55830
  return {
55439
55831
  inline_keyboard: [
@@ -55595,6 +55987,7 @@ async function executeEditMessage(args) {
55595
55987
  const editConfigMode = editAccess.parseMode ?? "html";
55596
55988
  const editFormat = args.format ?? editConfigMode;
55597
55989
  let editRawText = repairEscapedWhitespace(args.text);
55990
+ editRawText = redactOutboundText(editRawText, "edit_message");
55598
55991
  {
55599
55992
  const scrub = scrubVoice(editRawText);
55600
55993
  if (scrub.replaced > 0) {
@@ -56257,6 +56650,7 @@ function handleSessionEvent(ev) {
56257
56650
  const backstopChatId = chatId;
56258
56651
  const backstopThreadId = threadId;
56259
56652
  const backstopCtrl = ctrl;
56653
+ capturedText = redactOutboundText(capturedText, "turn_flush");
56260
56654
  {
56261
56655
  const scrub = scrubVoice(capturedText);
56262
56656
  if (scrub.replaced > 0) {
@@ -56792,6 +57186,11 @@ async function handleInbound(ctx, text, downloadImage, attachment, extraAttachme
56792
57186
  behavior
56793
57187
  });
56794
57188
  resumeReactionAfterVerdict();
57189
+ const ftDetails = pendingPermissions.get(request_id);
57190
+ postPermissionResumeMessage({
57191
+ behavior,
57192
+ action: ftDetails ? naturalAction(ftDetails.tool_name, ftDetails.input_preview) : ""
57193
+ });
56795
57194
  if (msgId != null) {
56796
57195
  const emoji = behavior === "allow" ? "\u2705" : "\u274C";
56797
57196
  bot.api.setMessageReaction(chat_id, msgId, [
@@ -57002,6 +57401,11 @@ ${preBlock(write.output)}`;
57002
57401
  const parsedQueue = isSteerPrefix ? { queued: false, body: parsedSteer.body } : parseQueuePrefix(text);
57003
57402
  const isQueuedPrefix = parsedQueue.queued;
57004
57403
  let effectiveText = isSteerPrefix ? parsedSteer.body : isQueuedPrefix ? parsedQueue.body : text;
57404
+ if (armedSecretCaptures.has(chat_id)) {
57405
+ const consumed = await captureProvidedSecret(ctx, chat_id, msgId ?? undefined, text);
57406
+ if (consumed)
57407
+ return;
57408
+ }
57005
57409
  try {
57006
57410
  const authCodeSentAt = awaitingAuthCodeAt.get(chat_id);
57007
57411
  const isAuthFlowContext = authCodeSentAt !== undefined && Date.now() - authCodeSentAt < AUTH_CODE_CONTEXT_TTL_MS;
@@ -58535,6 +58939,10 @@ async function handlePermissionSlash(ctx, behavior) {
58535
58939
  }
58536
58940
  dispatchPermissionVerdict({ type: "permission", requestId: request_id, behavior });
58537
58941
  resumeReactionAfterVerdict();
58942
+ postPermissionResumeMessage({
58943
+ behavior,
58944
+ action: naturalAction(details.tool_name, details.input_preview)
58945
+ });
58538
58946
  pendingPermissions.delete(request_id);
58539
58947
  process.stderr.write(`[telegram gateway] slash-${behavior} request_id=${request_id} tool=${details.tool_name} by=${senderId}
58540
58948
  `);
@@ -60652,6 +61060,10 @@ ${preBlock(formatSwitchroomOutput(err.message ?? "unknown error"))}`, { html: tr
60652
61060
  await handleVaultRequestAccessCallback(ctx, data);
60653
61061
  return;
60654
61062
  }
61063
+ if (data.startsWith("vsp:")) {
61064
+ await handleSecretRequestCallback(ctx, data);
61065
+ return;
61066
+ }
60655
61067
  if (data.startsWith("vrd:")) {
60656
61068
  await handleVaultRecentDenialCallback(ctx, data);
60657
61069
  return;
@@ -60832,6 +61244,10 @@ ${preBlock(formatSwitchroomOutput(err.message ?? "unknown error"))}`, { html: tr
60832
61244
  rule: chosen.rule
60833
61245
  });
60834
61246
  resumeReactionAfterVerdict();
61247
+ postPermissionResumeMessage({
61248
+ behavior: "allow",
61249
+ action: naturalAction(details.tool_name, details.input_preview)
61250
+ });
60835
61251
  let durable = false;
60836
61252
  let legacy = false;
60837
61253
  let failReason = "";
@@ -60932,10 +61348,12 @@ ${editLabel}` : editLabel,
60932
61348
  });
60933
61349
  return;
60934
61350
  }
61351
+ const resumeAction = (() => {
61352
+ const d = pendingPermissions.get(request_id);
61353
+ return d ? naturalAction(d.tool_name, d.input_preview) : "";
61354
+ })();
60935
61355
  pendingPermissions.delete(request_id);
60936
- const resumeAgent = process.env.SWITCHROOM_AGENT_NAME;
60937
- const resumeBeat = resumeAgent ? `\u25B6\uFE0F ${escapeHtmlForTg(resumeAgent)} resuming\u2026` : "\u25B6\uFE0F resuming\u2026";
60938
- const label = `${behavior === "allow" ? "\u2705 Allowed" : "\u274C Denied"} \xB7 ${resumeBeat}`;
61356
+ const label = behavior === "allow" ? "\u2705 Allowed" : "\u274C Denied";
60939
61357
  const msg = ctx.callbackQuery?.message;
60940
61358
  const baseText = msg && "text" in msg && msg.text ? escapeHtmlForTg(msg.text) : "";
60941
61359
  await finalizeCallback(ctx, {
@@ -60951,6 +61369,10 @@ ${label}` : label,
60951
61369
  behavior
60952
61370
  });
60953
61371
  resumeReactionAfterVerdict();
61372
+ postPermissionResumeMessage({
61373
+ behavior,
61374
+ action: resumeAction
61375
+ });
60954
61376
  }
60955
61377
  });
60956
61378
  });