switchroom 0.14.27 → 0.14.28

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.
@@ -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",
@@ -50371,6 +50478,9 @@ var SEVERITY_RANK2 = {
50371
50478
  critical: 3
50372
50479
  };
50373
50480
 
50481
+ // ../src/secret-detect/redact.ts
50482
+ init_redact();
50483
+
50374
50484
  // ../src/issues/store.ts
50375
50485
  var ISSUES_FILE = "issues.jsonl";
50376
50486
  var ISSUES_LOCK = "issues.lock";
@@ -50786,6 +50896,18 @@ function describeGrant(toolName, inputPreview, option) {
50786
50896
  return naturalAction(toolName, inputPreview);
50787
50897
  }
50788
50898
  }
50899
+ function formatPermissionResumeMessage(opts) {
50900
+ const who = opts.agentName && opts.agentName.length > 0 ? `<b>${escapeTgHtml(capFirst(opts.agentName))}</b>` : `<b>Agent</b>`;
50901
+ const act = (opts.action ?? "").trim();
50902
+ const hasAction = act.length > 0;
50903
+ if (opts.behavior === "allow") {
50904
+ return hasAction ? `\u25b6\ufe0f ${who} \u2014 got it, continuing: <i>${escapeTgHtml(act)}</i>` : `\u25b6\ufe0f ${who} \u2014 got it, back to work.`;
50905
+ }
50906
+ if (opts.timeoutMinutes != null) {
50907
+ 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.`;
50908
+ }
50909
+ return hasAction ? `\uD83D\uDEAB ${who} \u2014 noted, I won't ${escapeTgHtml(lowerFirst(act))}. Continuing without it.` : `\uD83D\uDEAB ${who} \u2014 noted, continuing without it.`;
50910
+ }
50789
50911
  function resolveSkillName(input) {
50790
50912
  return readString(input, "skill") ?? readString(input, "skill_name") ?? readString(input, "skillName") ?? readString(input, "name") ?? skillBasenameFromPath(input);
50791
50913
  }
@@ -51520,10 +51642,10 @@ function sweepStaleTurnActiveMarker(stateDir, opts) {
51520
51642
  }
51521
51643
 
51522
51644
  // ../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;
51645
+ var VERSION = "0.14.28";
51646
+ var COMMIT_SHA = "cb64351f";
51647
+ var COMMIT_DATE = "2026-06-01T04:23:25Z";
51648
+ var LATEST_PR = 2049;
51527
51649
  var COMMITS_AHEAD_OF_TAG = 0;
51528
51650
 
51529
51651
  // gateway/boot-version.ts
@@ -52957,6 +53079,31 @@ function resumeReactionAfterVerdict() {
52957
53079
  return;
52958
53080
  activeStatusReactions.get(statusKey(turn.sessionChatId, turn.sessionThreadId))?.setThinking();
52959
53081
  }
53082
+ function postPermissionResumeMessage(opts) {
53083
+ if (process.env.SWITCHROOM_RESUME_MSG === "0")
53084
+ return;
53085
+ const text = formatPermissionResumeMessage({
53086
+ agentName: process.env.SWITCHROOM_AGENT_NAME ?? null,
53087
+ behavior: opts.behavior,
53088
+ action: opts.action,
53089
+ timeoutMinutes: opts.timeoutMinutes
53090
+ });
53091
+ const turn = currentTurn;
53092
+ const targets = turn != null ? [{ chatId: turn.sessionChatId, threadId: turn.sessionThreadId }] : loadAccess().allowFrom.map((chatId) => ({
53093
+ chatId,
53094
+ threadId: resolveAgentOutboundTopic({
53095
+ kind: "permission",
53096
+ turnInitiated: false,
53097
+ originThreadId: undefined
53098
+ })
53099
+ }));
53100
+ for (const { chatId, threadId } of targets) {
53101
+ swallowingApiCall(() => bot.api.sendMessage(chatId, text, {
53102
+ parse_mode: "HTML",
53103
+ ...threadId != null ? { message_thread_id: threadId } : {}
53104
+ }), { chat_id: chatId, verb: "permission-resume", ...threadId != null ? { threadId } : {} });
53105
+ }
53106
+ }
52960
53107
  function resolveThreadId(chat_id, explicit) {
52961
53108
  if (explicit != null)
52962
53109
  return Number(explicit);
@@ -53371,6 +53518,11 @@ var pendingStateReaper = setInterval(() => {
53371
53518
  if (now - v.startedAt > PERMISSION_TTL_MS) {
53372
53519
  dispatchPermissionVerdict({ type: "permission", requestId: k, behavior: "deny" });
53373
53520
  resumeReactionAfterVerdict();
53521
+ postPermissionResumeMessage({
53522
+ behavior: "deny",
53523
+ action: naturalAction(v.tool_name, v.input_preview),
53524
+ timeoutMinutes: Math.round(PERMISSION_TTL_MS / 60000)
53525
+ });
53374
53526
  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
53527
  `);
53376
53528
  pendingPermissions.delete(k);
@@ -54397,7 +54549,8 @@ var ALLOWED_TOOLS = new Set([
54397
54549
  "send_sticker",
54398
54550
  "send_gif",
54399
54551
  "vault_request_save",
54400
- "vault_request_access"
54552
+ "vault_request_access",
54553
+ "request_secret"
54401
54554
  ]);
54402
54555
  async function executeToolCall(tool, args) {
54403
54556
  if (!ALLOWED_TOOLS.has(tool)) {
@@ -54440,6 +54593,8 @@ async function executeToolCall(tool, args) {
54440
54593
  return executeVaultRequestSave(args);
54441
54594
  case "vault_request_access":
54442
54595
  return executeVaultRequestAccess(args);
54596
+ case "request_secret":
54597
+ return executeRequestSecret(args);
54443
54598
  default:
54444
54599
  throw new Error(`unknown tool: ${tool}`);
54445
54600
  }
@@ -54485,6 +54640,14 @@ async function executeUpdateChecklist(args) {
54485
54640
  `);
54486
54641
  return { content: [{ type: "text", text: `checklist updated (id: ${message_id})` }] };
54487
54642
  }
54643
+ function redactOutboundText(text, site) {
54644
+ const masked = redact2(text);
54645
+ if (masked !== text) {
54646
+ process.stderr.write(`telegram gateway: outbound secret masked site=${site}
54647
+ `);
54648
+ }
54649
+ return masked;
54650
+ }
54488
54651
  async function executeReply(args) {
54489
54652
  const turn = currentTurn;
54490
54653
  const chat_id = args.chat_id;
@@ -54494,6 +54657,7 @@ async function executeReply(args) {
54494
54657
  if (rawText == null || rawText === "")
54495
54658
  throw new Error("reply: text is required and cannot be empty");
54496
54659
  let text = repairEscapedWhitespace(rawText);
54660
+ text = redactOutboundText(text, "reply");
54497
54661
  {
54498
54662
  const scrub = scrubVoice(text);
54499
54663
  if (scrub.replaced > 0) {
@@ -54916,6 +55080,7 @@ async function executeStreamReply(args) {
54916
55080
  throw new Error("stream_reply: chat_id is required");
54917
55081
  if (args.text == null || args.text === "")
54918
55082
  throw new Error("stream_reply: text is required and cannot be empty");
55083
+ args.text = redactOutboundText(args.text, "stream_reply");
54919
55084
  {
54920
55085
  const scrub = scrubVoice(args.text);
54921
55086
  if (scrub.replaced > 0) {
@@ -55434,6 +55599,213 @@ async function executeVaultRequestSave(args) {
55434
55599
  ]
55435
55600
  };
55436
55601
  }
55602
+ var pendingSecretRequests = new Map;
55603
+ var armedSecretCaptures = new Map;
55604
+ var PENDING_SECRET_REQUEST_TTL_MS = 1800000;
55605
+ var ARMED_SECRET_CAPTURE_TTL_MS = 600000;
55606
+ function sweepSecretRequests() {
55607
+ const now = Date.now();
55608
+ for (const [k, v] of pendingSecretRequests) {
55609
+ if (now - v.staged_at > PENDING_SECRET_REQUEST_TTL_MS)
55610
+ pendingSecretRequests.delete(k);
55611
+ }
55612
+ for (const [k, v] of armedSecretCaptures) {
55613
+ if (now - v.armed_at > ARMED_SECRET_CAPTURE_TTL_MS)
55614
+ armedSecretCaptures.delete(k);
55615
+ }
55616
+ }
55617
+ function buildSecretRequestKeyboard(stageId) {
55618
+ return {
55619
+ inline_keyboard: [
55620
+ [
55621
+ { text: "\uD83D\uDD10 Provide securely", callback_data: `vsp:provide:${stageId}` },
55622
+ { text: "\uD83D\uDEAB Decline", callback_data: `vsp:decline:${stageId}` }
55623
+ ]
55624
+ ]
55625
+ };
55626
+ }
55627
+ function renderSecretRequestCard(req) {
55628
+ const lines = [
55629
+ `\uD83D\uDD12 <b>${escapeHtmlForTg(req.agent)}</b> needs a secret:`,
55630
+ `<code>${escapeHtmlForTg(req.key)}</code>`
55631
+ ];
55632
+ if (req.reason)
55633
+ lines.push(`<i>${escapeHtmlForTg(req.reason)}</i>`);
55634
+ 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.");
55635
+ return lines.join(`
55636
+ `);
55637
+ }
55638
+ async function executeRequestSecret(args) {
55639
+ const chat_id = args.chat_id;
55640
+ if (!chat_id)
55641
+ throw new Error("request_secret: chat_id is required");
55642
+ const key = args.key;
55643
+ if (!key || typeof key !== "string")
55644
+ throw new Error("request_secret: key is required");
55645
+ const reason = typeof args.reason === "string" ? args.reason : undefined;
55646
+ assertAllowedChat(chat_id);
55647
+ if (!VAULT_KEY_REGEX.test(key)) {
55648
+ throw new Error(`request_secret: key must match ${VAULT_KEY_REGEX_LABEL}`);
55649
+ }
55650
+ const agentSlug = process.env.SWITCHROOM_AGENT_NAME || "agent";
55651
+ for (const [sid, p] of pendingSecretRequests) {
55652
+ if (p.chat_id === chat_id && p.key === key)
55653
+ pendingSecretRequests.delete(sid);
55654
+ }
55655
+ const stageId = randomBytes6(4).toString("hex");
55656
+ const pending2 = { agent: agentSlug, chat_id, key, reason, staged_at: Date.now() };
55657
+ pendingSecretRequests.set(stageId, pending2);
55658
+ sweepSecretRequests();
55659
+ const text = renderSecretRequestCard(pending2);
55660
+ const threadId = args.message_thread_id != null ? Number(args.message_thread_id) : undefined;
55661
+ const sent = await retryWithThreadFallback(robustApiCall, (tid) => lockedBot.api.sendMessage(chat_id, text, {
55662
+ parse_mode: "HTML",
55663
+ reply_markup: buildSecretRequestKeyboard(stageId),
55664
+ ...tid != null && Number.isFinite(tid) ? { message_thread_id: tid } : {}
55665
+ }), { threadId, chat_id, verb: "request_secret.card" });
55666
+ pending2.card_message_id = sent.message_id;
55667
+ return {
55668
+ content: [
55669
+ {
55670
+ type: "text",
55671
+ 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.`
55672
+ }
55673
+ ]
55674
+ };
55675
+ }
55676
+ async function writeRequestedSecret(key, value, chat_id) {
55677
+ if (VAULT_APPROVAL_AUTH_MODE === "telegram-id") {
55678
+ return await defaultVaultWritePosture(key, value);
55679
+ }
55680
+ const cached = vaultPassphraseCache.get(chat_id);
55681
+ if (!cached || cached.expiresAt <= Date.now()) {
55682
+ return { ok: false, output: "VAULT-LOCKED: run /vault unlock once in this chat, then have the agent re-request." };
55683
+ }
55684
+ return defaultVaultWrite(key, value, cached.passphrase);
55685
+ }
55686
+ async function captureProvidedSecret(ctx, chat_id, msgId, value) {
55687
+ const armed = armedSecretCaptures.get(chat_id);
55688
+ if (!armed || Date.now() - armed.armed_at > ARMED_SECRET_CAPTURE_TTL_MS) {
55689
+ if (armed)
55690
+ armedSecretCaptures.delete(chat_id);
55691
+ return false;
55692
+ }
55693
+ armedSecretCaptures.delete(chat_id);
55694
+ const pending2 = pendingSecretRequests.get(armed.stageId);
55695
+ pendingSecretRequests.delete(armed.stageId);
55696
+ if (msgId != null)
55697
+ await deleteSensitiveMessage(chat_id, msgId, "provided secret value");
55698
+ const write = await writeRequestedSecret(armed.key, value, chat_id);
55699
+ if (!write.ok) {
55700
+ const parsed = parseVaultCliError(write.output);
55701
+ const rendered = renderVaultCliError(parsed, { verb: "save", key: armed.key });
55702
+ const body = rendered.suppressRaw ? rendered.html : `\u26A0\uFE0F vault write failed:
55703
+ <pre>${escapeHtmlForTg(write.output)}</pre>`;
55704
+ await switchroomReply(ctx, `${body}
55705
+
55706
+ <i>The secret was NOT saved. The agent can re-request with <code>request_secret</code>.</i>`, { html: true });
55707
+ const fts = Date.now();
55708
+ const failMsg = {
55709
+ type: "inbound",
55710
+ chatId: chat_id,
55711
+ messageId: fts,
55712
+ user: "vault-broker",
55713
+ userId: 0,
55714
+ ts: fts,
55715
+ 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.`,
55716
+ meta: { source: "secret_provide_failed", agent: armed.agent, key: armed.key, stage_id: armed.stageId }
55717
+ };
55718
+ const fdelivered = ipcServer.sendToAgent(armed.agent, failMsg);
55719
+ if (fdelivered)
55720
+ markClaudeBusyForInbound(failMsg);
55721
+ else
55722
+ pendingInboundBuffer.push(armed.agent, failMsg);
55723
+ return true;
55724
+ }
55725
+ 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 });
55726
+ const ts = Date.now();
55727
+ const synthetic = {
55728
+ type: "inbound",
55729
+ chatId: chat_id,
55730
+ messageId: ts,
55731
+ user: "vault-broker",
55732
+ userId: 0,
55733
+ ts,
55734
+ 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.`,
55735
+ meta: {
55736
+ source: "secret_provided",
55737
+ agent: armed.agent,
55738
+ key: armed.key,
55739
+ stage_id: armed.stageId
55740
+ }
55741
+ };
55742
+ const delivered = ipcServer.sendToAgent(armed.agent, synthetic);
55743
+ if (delivered)
55744
+ markClaudeBusyForInbound(synthetic);
55745
+ else
55746
+ pendingInboundBuffer.push(armed.agent, synthetic);
55747
+ process.stderr.write(`telegram gateway: secret_provided injection agent=${armed.agent} key=${armed.key} stage=${armed.stageId} delivered=${delivered}
55748
+ `);
55749
+ return true;
55750
+ }
55751
+ async function handleSecretRequestCallback(ctx, data) {
55752
+ const senderId = String(ctx.from?.id ?? "");
55753
+ const access = loadAccess();
55754
+ if (!access.allowFrom.includes(senderId)) {
55755
+ await ctx.answerCallbackQuery({ text: "Not authorized." }).catch(() => {});
55756
+ return;
55757
+ }
55758
+ const parts = data.split(":");
55759
+ const action = parts[1];
55760
+ const stageId = parts[2] ?? "";
55761
+ const pending2 = pendingSecretRequests.get(stageId);
55762
+ if (!pending2) {
55763
+ await ctx.answerCallbackQuery({ text: "This request expired." }).catch(() => {});
55764
+ return;
55765
+ }
55766
+ if (action === "provide") {
55767
+ armedSecretCaptures.set(pending2.chat_id, {
55768
+ key: pending2.key,
55769
+ agent: pending2.agent,
55770
+ stageId,
55771
+ armed_at: Date.now()
55772
+ });
55773
+ await ctx.answerCallbackQuery({ text: "Send the value now \u2014 it auto-deletes." }).catch(() => {});
55774
+ if (pending2.card_message_id != null) {
55775
+ 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(() => {});
55776
+ }
55777
+ return;
55778
+ }
55779
+ if (action === "decline") {
55780
+ pendingSecretRequests.delete(stageId);
55781
+ armedSecretCaptures.delete(pending2.chat_id);
55782
+ await ctx.answerCallbackQuery({ text: "Declined." }).catch(() => {});
55783
+ if (pending2.card_message_id != null) {
55784
+ await ctx.api.editMessageText(pending2.chat_id, pending2.card_message_id, `\uD83D\uDEAB Declined \u2014 <code>${escapeHtmlForTg(pending2.key)}</code> not provided.`, {
55785
+ parse_mode: "HTML",
55786
+ reply_markup: { inline_keyboard: [] }
55787
+ }).catch(() => {});
55788
+ }
55789
+ const ts = Date.now();
55790
+ const synthetic = {
55791
+ type: "inbound",
55792
+ chatId: pending2.chat_id,
55793
+ messageId: ts,
55794
+ user: "vault-broker",
55795
+ userId: 0,
55796
+ ts,
55797
+ text: `\uD83D\uDEAB Operator declined your request for \`vault:${pending2.key}\`. Proceed without it or ask how they'd like to handle the task.`,
55798
+ meta: { source: "secret_declined", agent: pending2.agent, key: pending2.key, stage_id: stageId }
55799
+ };
55800
+ const delivered = ipcServer.sendToAgent(pending2.agent, synthetic);
55801
+ if (delivered)
55802
+ markClaudeBusyForInbound(synthetic);
55803
+ else
55804
+ pendingInboundBuffer.push(pending2.agent, synthetic);
55805
+ return;
55806
+ }
55807
+ await ctx.answerCallbackQuery().catch(() => {});
55808
+ }
55437
55809
  function buildVaultRequestAccessKeyboard(stageId) {
55438
55810
  return {
55439
55811
  inline_keyboard: [
@@ -55595,6 +55967,7 @@ async function executeEditMessage(args) {
55595
55967
  const editConfigMode = editAccess.parseMode ?? "html";
55596
55968
  const editFormat = args.format ?? editConfigMode;
55597
55969
  let editRawText = repairEscapedWhitespace(args.text);
55970
+ editRawText = redactOutboundText(editRawText, "edit_message");
55598
55971
  {
55599
55972
  const scrub = scrubVoice(editRawText);
55600
55973
  if (scrub.replaced > 0) {
@@ -56257,6 +56630,7 @@ function handleSessionEvent(ev) {
56257
56630
  const backstopChatId = chatId;
56258
56631
  const backstopThreadId = threadId;
56259
56632
  const backstopCtrl = ctrl;
56633
+ capturedText = redactOutboundText(capturedText, "turn_flush");
56260
56634
  {
56261
56635
  const scrub = scrubVoice(capturedText);
56262
56636
  if (scrub.replaced > 0) {
@@ -56792,6 +57166,11 @@ async function handleInbound(ctx, text, downloadImage, attachment, extraAttachme
56792
57166
  behavior
56793
57167
  });
56794
57168
  resumeReactionAfterVerdict();
57169
+ const ftDetails = pendingPermissions.get(request_id);
57170
+ postPermissionResumeMessage({
57171
+ behavior,
57172
+ action: ftDetails ? naturalAction(ftDetails.tool_name, ftDetails.input_preview) : ""
57173
+ });
56795
57174
  if (msgId != null) {
56796
57175
  const emoji = behavior === "allow" ? "\u2705" : "\u274C";
56797
57176
  bot.api.setMessageReaction(chat_id, msgId, [
@@ -57002,6 +57381,11 @@ ${preBlock(write.output)}`;
57002
57381
  const parsedQueue = isSteerPrefix ? { queued: false, body: parsedSteer.body } : parseQueuePrefix(text);
57003
57382
  const isQueuedPrefix = parsedQueue.queued;
57004
57383
  let effectiveText = isSteerPrefix ? parsedSteer.body : isQueuedPrefix ? parsedQueue.body : text;
57384
+ if (armedSecretCaptures.has(chat_id)) {
57385
+ const consumed = await captureProvidedSecret(ctx, chat_id, msgId ?? undefined, text);
57386
+ if (consumed)
57387
+ return;
57388
+ }
57005
57389
  try {
57006
57390
  const authCodeSentAt = awaitingAuthCodeAt.get(chat_id);
57007
57391
  const isAuthFlowContext = authCodeSentAt !== undefined && Date.now() - authCodeSentAt < AUTH_CODE_CONTEXT_TTL_MS;
@@ -58535,6 +58919,10 @@ async function handlePermissionSlash(ctx, behavior) {
58535
58919
  }
58536
58920
  dispatchPermissionVerdict({ type: "permission", requestId: request_id, behavior });
58537
58921
  resumeReactionAfterVerdict();
58922
+ postPermissionResumeMessage({
58923
+ behavior,
58924
+ action: naturalAction(details.tool_name, details.input_preview)
58925
+ });
58538
58926
  pendingPermissions.delete(request_id);
58539
58927
  process.stderr.write(`[telegram gateway] slash-${behavior} request_id=${request_id} tool=${details.tool_name} by=${senderId}
58540
58928
  `);
@@ -60652,6 +61040,10 @@ ${preBlock(formatSwitchroomOutput(err.message ?? "unknown error"))}`, { html: tr
60652
61040
  await handleVaultRequestAccessCallback(ctx, data);
60653
61041
  return;
60654
61042
  }
61043
+ if (data.startsWith("vsp:")) {
61044
+ await handleSecretRequestCallback(ctx, data);
61045
+ return;
61046
+ }
60655
61047
  if (data.startsWith("vrd:")) {
60656
61048
  await handleVaultRecentDenialCallback(ctx, data);
60657
61049
  return;
@@ -60832,6 +61224,10 @@ ${preBlock(formatSwitchroomOutput(err.message ?? "unknown error"))}`, { html: tr
60832
61224
  rule: chosen.rule
60833
61225
  });
60834
61226
  resumeReactionAfterVerdict();
61227
+ postPermissionResumeMessage({
61228
+ behavior: "allow",
61229
+ action: naturalAction(details.tool_name, details.input_preview)
61230
+ });
60835
61231
  let durable = false;
60836
61232
  let legacy = false;
60837
61233
  let failReason = "";
@@ -60932,10 +61328,12 @@ ${editLabel}` : editLabel,
60932
61328
  });
60933
61329
  return;
60934
61330
  }
61331
+ const resumeAction = (() => {
61332
+ const d = pendingPermissions.get(request_id);
61333
+ return d ? naturalAction(d.tool_name, d.input_preview) : "";
61334
+ })();
60935
61335
  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}`;
61336
+ const label = behavior === "allow" ? "\u2705 Allowed" : "\u274C Denied";
60939
61337
  const msg = ctx.callbackQuery?.message;
60940
61338
  const baseText = msg && "text" in msg && msg.text ? escapeHtmlForTg(msg.text) : "";
60941
61339
  await finalizeCallback(ctx, {
@@ -60951,6 +61349,10 @@ ${label}` : label,
60951
61349
  behavior
60952
61350
  });
60953
61351
  resumeReactionAfterVerdict();
61352
+ postPermissionResumeMessage({
61353
+ behavior,
61354
+ action: resumeAction
61355
+ });
60954
61356
  }
60955
61357
  });
60956
61358
  });