switchroom 0.19.35 → 0.19.37

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/dist/cli/skill-validate-pretool.mjs +15 -2
  2. package/dist/cli/switchroom.js +678 -444
  3. package/dist/host-control/main.js +149 -3
  4. package/package.json +4 -2
  5. package/profiles/_base/start.sh.hbs +37 -12
  6. package/telegram-plugin/bridge/bridge.ts +4 -4
  7. package/telegram-plugin/dist/bridge/bridge.js +4 -4
  8. package/telegram-plugin/dist/gateway/gateway.js +317 -95
  9. package/telegram-plugin/dist/server.js +4 -4
  10. package/telegram-plugin/format.ts +100 -29
  11. package/telegram-plugin/gateway/gateway.ts +22 -29
  12. package/telegram-plugin/gateway/ipc-server.ts +18 -15
  13. package/telegram-plugin/gateway/model-command.ts +34 -0
  14. package/telegram-plugin/gateway/outbound-send-path.ts +12 -1
  15. package/telegram-plugin/operator-events.ts +40 -16
  16. package/telegram-plugin/render/unsupported-token-guard.ts +10 -6
  17. package/telegram-plugin/secret-detect/db-uri.ts +90 -0
  18. package/telegram-plugin/secret-detect/index.ts +24 -1
  19. package/telegram-plugin/secret-detect/inert-values.ts +147 -0
  20. package/telegram-plugin/secret-detect/kv-scanner.ts +108 -0
  21. package/telegram-plugin/secret-detect/patterns.ts +24 -4
  22. package/telegram-plugin/tests/format-consistency.test.ts +111 -0
  23. package/telegram-plugin/tests/gateway-session-model-relaunch.test.ts +40 -2
  24. package/telegram-plugin/tests/ipc-server-validate-operator.test.ts +20 -11
  25. package/telegram-plugin/tests/mcp-instructions-budget.test.ts +8 -1
  26. package/telegram-plugin/tests/outbound-send-path.test.ts +1 -1
  27. package/telegram-plugin/tests/render/unsupported-token-guard.test.ts +36 -5
  28. package/telegram-plugin/tests/secret-detect-cross-engine.test.ts +263 -0
  29. package/telegram-plugin/tests/secret-detect-write-path.test.ts +403 -0
  30. package/telegram-plugin/tests/turn-flush-safety.test.ts +2 -2
  31. package/vendor/hindsight-memory/scripts/lib/client.py +58 -0
  32. package/vendor/hindsight-memory/scripts/lib/secret_patterns.json +431 -0
  33. package/vendor/hindsight-memory/scripts/lib/secret_redact.py +563 -0
  34. package/vendor/hindsight-memory/scripts/lib/secret_redaction_vectors.json +397 -0
  35. package/vendor/hindsight-memory/scripts/tests/test_secret_redact.py +522 -0
@@ -2120,7 +2120,7 @@ var init_esm = __esm(() => {
2120
2120
  });
2121
2121
 
2122
2122
  // src/build-info.ts
2123
- var VERSION = "0.19.35", COMMIT_SHA = "76ae22ca";
2123
+ var VERSION = "0.19.37", COMMIT_SHA = "5d2f9a17";
2124
2124
 
2125
2125
  // src/cli/resolve-version.ts
2126
2126
  import { existsSync, readFileSync } from "node:fs";
@@ -31355,8 +31355,8 @@ into your system prompt every session; this is the conversational summary.
31355
31355
  Kicking off a worker to crawl the changelog.
31356
31356
 
31357
31357
  **What it'll do**
31358
- \u2022 pull every entry since v0.14
31359
- \u2022 flag anything user-facing
31358
+ - pull every entry since v0.14
31359
+ - flag anything user-facing
31360
31360
 
31361
31361
  **Back in** ~2 min with a synthesized summary.
31362
31362
  - Use backtick-wrapped \`inline code\` for filenames, commands, and
@@ -31540,7 +31540,8 @@ You're writing for a phone screen in Telegram. Every reply renders as rich Markd
31540
31540
  fenced code blocks ALWAYS with a language
31541
31541
  hint (\`\`\`diff, \`\`\`json, \`\`\`bash \u2014 bare fence only for non-code fixed-width output);
31542
31542
  and the flagship \u2014 the **expandable blockquote** \`**> first line\` + \`> continuation\`
31543
- for a long quote, stack trace, or detailed aside the reader can collapse. Use it
31543
+ for a long quote, stack trace, or detailed aside the reader can collapse. The \`**>\`
31544
+ opener must be at the line start (column 0) \u2014 never indented \u2014 or it won't parse. Use it
31544
31545
  whenever a bulky supporting block would otherwise dominate the message.
31545
31546
 
31546
31547
  Renders wrong on this path \u2014 never emit: underline (\`__x__\` renders as bold),
@@ -31623,6 +31624,8 @@ var init_scaffold = __esm(() => {
31623
31624
  "mcp__hindsight__list_banks",
31624
31625
  "mcp__hindsight__get_memory",
31625
31626
  "mcp__hindsight__list_memories",
31627
+ "mcp__hindsight__invalidate_memory",
31628
+ "mcp__hindsight__update_memory",
31626
31629
  "mcp__hindsight__get_document",
31627
31630
  "mcp__hindsight__list_documents",
31628
31631
  "mcp__hindsight__delete_document",
@@ -35532,7 +35535,7 @@ class AuthBrokerClient {
35532
35535
  sock.once("error", onError);
35533
35536
  sock.once("connect", () => {
35534
35537
  sock.removeListener("error", onError);
35535
- sock.on("data", (chunk) => this.onData(chunk));
35538
+ sock.on("data", (chunk2) => this.onData(chunk2));
35536
35539
  sock.on("error", (err) => this.onSocketError(err));
35537
35540
  sock.on("close", () => this.onSocketClose());
35538
35541
  this.socket = sock;
@@ -35546,8 +35549,8 @@ class AuthBrokerClient {
35546
35549
  this.connecting = null;
35547
35550
  }
35548
35551
  }
35549
- onData(chunk) {
35550
- this.buffer += chunk.toString("utf8");
35552
+ onData(chunk2) {
35553
+ this.buffer += chunk2.toString("utf8");
35551
35554
  let idx;
35552
35555
  while ((idx = this.buffer.indexOf(`
35553
35556
  `)) !== -1) {
@@ -39525,11 +39528,11 @@ function readAuditRaw(logPath, opts = {}) {
39525
39528
  const gen = `${logPath}.${n}`;
39526
39529
  if (!existsSync51(gen))
39527
39530
  break;
39528
- const chunk = tailBytes(gen, budget, strict);
39529
- if (chunk.length === 0)
39531
+ const chunk2 = tailBytes(gen, budget, strict);
39532
+ if (chunk2.length === 0)
39530
39533
  continue;
39531
- chunks.push(chunk);
39532
- budget -= Buffer.byteLength(chunk, "utf-8");
39534
+ chunks.push(chunk2);
39535
+ budget -= Buffer.byteLength(chunk2, "utf-8");
39533
39536
  }
39534
39537
  return chunks.reverse().filter((c) => c.length > 0).map((c) => c.endsWith(`
39535
39538
  `) ? c : c + `
@@ -44451,8 +44454,8 @@ async function hostdRequest(opts, req) {
44451
44454
  reject(err);
44452
44455
  }
44453
44456
  });
44454
- socket.on("data", (chunk) => {
44455
- buf += chunk.toString("utf8");
44457
+ socket.on("data", (chunk2) => {
44458
+ buf += chunk2.toString("utf8");
44456
44459
  if (Buffer.byteLength(buf, "utf8") > MAX_FRAME_BYTES3 * 2) {
44457
44460
  if (settled)
44458
44461
  return;
@@ -71143,9 +71146,600 @@ import { execFileSync as execFileSync11 } from "node:child_process";
71143
71146
 
71144
71147
  // src/agents/handoff-summarizer.ts
71145
71148
  init_atomic();
71146
- init_observation_scopes();
71147
71149
  import { readFileSync as readFileSync19, writeFileSync as writeFileSync9, renameSync as renameSync7, mkdirSync as mkdirSync16, existsSync as existsSync23, statSync as statSync11, readdirSync as readdirSync10 } from "node:fs";
71148
71150
  import { join as join18 } from "node:path";
71151
+
71152
+ // telegram-plugin/secret-detect/patterns.ts
71153
+ var ANCHORED_PATTERNS = [
71154
+ { rule_id: "anthropic_api_key", regex: /\b(sk-ant-[A-Za-z0-9_-]{8,})\b/g, captureIndex: 1, slugHint: "anthropic_api_key" },
71155
+ { 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" },
71156
+ { rule_id: "openai_api_key", regex: /\b(sk-[A-Za-z0-9_-]{20,})\b/g, captureIndex: 1, slugHint: "openai_api_key" },
71157
+ { rule_id: "github_pat_classic", regex: /\b(ghp_[A-Za-z0-9]{20,})\b/g, captureIndex: 1, slugHint: "github_pat" },
71158
+ { rule_id: "github_pat_fine_grained", regex: /\b(github_pat_[A-Za-z0-9_]{20,})\b/g, captureIndex: 1, slugHint: "github_pat" },
71159
+ { rule_id: "slack_token", regex: /\b(xox[baprs]-[A-Za-z0-9-]{10,})\b/g, captureIndex: 1, slugHint: "slack_token" },
71160
+ { rule_id: "slack_app_token", regex: /\b(xapp-[A-Za-z0-9-]{10,})\b/g, captureIndex: 1, slugHint: "slack_app_token" },
71161
+ { rule_id: "groq_api_key", regex: /\b(gsk_[A-Za-z0-9_-]{10,})\b/g, captureIndex: 1, slugHint: "groq_api_key" },
71162
+ { rule_id: "google_api_key", regex: /\b(AIza[0-9A-Za-z\-_]{20,})\b/g, captureIndex: 1, slugHint: "google_api_key" },
71163
+ { rule_id: "perplexity_api_key", regex: /\b(pplx-[A-Za-z0-9_-]{10,})\b/g, captureIndex: 1, slugHint: "perplexity_api_key" },
71164
+ { rule_id: "npm_token", regex: /\b(npm_[A-Za-z0-9]{10,})\b/g, captureIndex: 1, slugHint: "npm_token" },
71165
+ { rule_id: "telegram_bot_token_prefixed", regex: /\bbot(\d{6,}:[A-Za-z0-9_-]{20,})\b/g, captureIndex: 1, slugHint: "telegram_bot_token" },
71166
+ { rule_id: "telegram_bot_token", regex: /\b(\d{6,}:[A-Za-z0-9_-]{20,})\b/g, captureIndex: 1, slugHint: "telegram_bot_token" },
71167
+ { rule_id: "laravel_sanctum_token", regex: /\b(\d+\|[A-Za-z0-9]{40,})\b/g, captureIndex: 1, slugHint: "api_token" },
71168
+ { rule_id: "aws_access_key", regex: /\b(AKIA[0-9A-Z]{16})\b/g, captureIndex: 1, slugHint: "aws_access_key" },
71169
+ { 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" }
71170
+ ];
71171
+ var STRUCTURED_PATTERNS = [
71172
+ {
71173
+ rule_id: "env_key_value",
71174
+ regex: /\b([A-Z0-9_]*(?:KEY|TOKEN|SECRET|PASSWORD|PASSWD))\b\s*[=:]\s*(["']?)([^\s"'\\]+)\2/g,
71175
+ captureIndex: 3,
71176
+ slugHint: "env"
71177
+ },
71178
+ {
71179
+ rule_id: "json_secret_field",
71180
+ regex: /"(?:apiKey|token|secret|password|passwd|accessToken|refreshToken)"\s*:\s*"([^"]+)"/g,
71181
+ captureIndex: 1,
71182
+ slugHint: "json_secret"
71183
+ },
71184
+ {
71185
+ rule_id: "cli_flag",
71186
+ regex: /--(?:api[-_]?key|hook[-_]?token|token|secret|password|passwd)\s+(["']?)([^\s"']+)\1/g,
71187
+ captureIndex: 2,
71188
+ slugHint: "cli_flag"
71189
+ },
71190
+ {
71191
+ rule_id: "bearer_auth_header",
71192
+ regex: /Authorization\s*[:=]\s*Bearer\s+([A-Za-z0-9._\-+=]+)/gi,
71193
+ captureIndex: 1,
71194
+ slugHint: "bearer_token"
71195
+ },
71196
+ {
71197
+ rule_id: "bearer_loose",
71198
+ regex: /\bBearer\s+([A-Za-z0-9._\-+=]{18,})\b/gi,
71199
+ captureIndex: 1,
71200
+ slugHint: "bearer_token"
71201
+ },
71202
+ {
71203
+ rule_id: "basic_auth_header",
71204
+ regex: /Authorization\s*[:=]\s*Basic\s+([A-Za-z0-9+/=]{8,})/gi,
71205
+ captureIndex: 1,
71206
+ slugHint: "basic_auth"
71207
+ },
71208
+ {
71209
+ rule_id: "pem_private_key",
71210
+ regex: /-----BEGIN [A-Z ]*PRIVATE KEY-----[\s\S]+?-----END [A-Z ]*PRIVATE KEY-----/g,
71211
+ captureIndex: 0,
71212
+ slugHint: "pem_private_key"
71213
+ }
71214
+ ];
71215
+ var PROVIDER_PATTERNS = [
71216
+ { rule_id: "slack_webhook", regex: /(https:\/\/hooks\.slack\.com\/services\/[A-Za-z0-9_/]+)/g, captureIndex: 1, slugHint: "slack_webhook" },
71217
+ { rule_id: "stripe_live_secret", regex: /\b(sk_live_[A-Za-z0-9]{24,})\b/g, captureIndex: 1, slugHint: "stripe_key" },
71218
+ { rule_id: "stripe_restricted", regex: /\b(rk_live_[A-Za-z0-9]{24,})\b/g, captureIndex: 1, slugHint: "stripe_key" },
71219
+ { rule_id: "sendgrid_api_key", regex: /\b(SG\.[A-Za-z0-9_-]{22}\.[A-Za-z0-9_-]{43})\b/g, captureIndex: 1, slugHint: "sendgrid_key" },
71220
+ { rule_id: "gitlab_pat", regex: /\b(glpat-[A-Za-z0-9_-]{20})\b/g, captureIndex: 1, slugHint: "gitlab_pat" },
71221
+ { rule_id: "huggingface_token", regex: /\b(hf_[A-Za-z0-9]{34,})\b/g, captureIndex: 1, slugHint: "huggingface_token" },
71222
+ { rule_id: "twilio_api_key", regex: /\b(SK[0-9a-f]{32})\b/g, captureIndex: 1, slugHint: "twilio_api_key" },
71223
+ { rule_id: "mailgun_key", regex: /\b(key-[0-9a-f]{32})\b/g, captureIndex: 1, slugHint: "mailgun_key" },
71224
+ { rule_id: "digitalocean_pat", regex: /\b(dop_v1_[a-f0-9]{64})\b/g, captureIndex: 1, slugHint: "digitalocean_token" },
71225
+ { rule_id: "digitalocean_oauth", regex: /\b(doo_v1_[a-f0-9]{64})\b/g, captureIndex: 1, slugHint: "digitalocean_token" },
71226
+ { rule_id: "digitalocean_refresh", regex: /\b(dor_v1_[a-f0-9]{64})\b/g, captureIndex: 1, slugHint: "digitalocean_token" },
71227
+ { rule_id: "doppler_token", regex: /\b(dp\.(?:pt|st|ct|sa|scim|audit)\.[A-Za-z0-9]{40,44})\b/g, captureIndex: 1, slugHint: "doppler_token" },
71228
+ { rule_id: "linear_api_key", regex: /\b(lin_api_[A-Za-z0-9]{40})\b/g, captureIndex: 1, slugHint: "linear_api_key" },
71229
+ { rule_id: "shopify_access_token", regex: /\b(shpat_[a-fA-F0-9]{32})\b/g, captureIndex: 1, slugHint: "shopify_token" },
71230
+ { rule_id: "shopify_shared_secret", regex: /\b(shpss_[a-fA-F0-9]{32})\b/g, captureIndex: 1, slugHint: "shopify_token" },
71231
+ { rule_id: "shopify_private_app", regex: /\b(shppa_[a-fA-F0-9]{32})\b/g, captureIndex: 1, slugHint: "shopify_token" },
71232
+ { rule_id: "square_access_token", regex: /\b(sq0atp-[A-Za-z0-9_-]{22})\b/g, captureIndex: 1, slugHint: "square_token" },
71233
+ { rule_id: "square_oauth_secret", regex: /\b(sq0csp-[A-Za-z0-9_-]{43})\b/g, captureIndex: 1, slugHint: "square_token" },
71234
+ { rule_id: "newrelic_key", regex: /\b(NRAK-[A-Z0-9]{27})\b/g, captureIndex: 1, slugHint: "newrelic_key" },
71235
+ { rule_id: "notion_token", regex: /\b(ntn_[A-Za-z0-9]{46})\b/g, captureIndex: 1, slugHint: "notion_token" },
71236
+ { rule_id: "planetscale_password", regex: /\b(pscale_pw_[A-Za-z0-9_.-]{43})\b/g, captureIndex: 1, slugHint: "planetscale_token" },
71237
+ { rule_id: "planetscale_token", regex: /\b(pscale_tkn_[A-Za-z0-9_.-]{43})\b/g, captureIndex: 1, slugHint: "planetscale_token" },
71238
+ { rule_id: "supabase_service_key", regex: /\b(sbp_[a-f0-9]{40})\b/g, captureIndex: 1, slugHint: "supabase_key" },
71239
+ { rule_id: "atlassian_token", regex: /\b(ATATT[A-Za-z0-9_\-=]{20,})\b/g, captureIndex: 1, slugHint: "atlassian_token" },
71240
+ { rule_id: "dropbox_token", regex: /\b(sl\.[A-Za-z0-9_-]{130,})/g, captureIndex: 1, slugHint: "dropbox_token" },
71241
+ { rule_id: "databricks_token", regex: /\b(dapi[a-f0-9]{32})\b/g, captureIndex: 1, slugHint: "databricks_token" },
71242
+ { rule_id: "grafana_service_account", regex: /\b(glsa_[A-Za-z0-9]{32}_[a-fA-F0-9]{8})\b/g, captureIndex: 1, slugHint: "grafana_token" },
71243
+ { rule_id: "pypi_token", regex: /\b(pypi-AgEIcHlwaS[A-Za-z0-9_-]{50,})/g, captureIndex: 1, slugHint: "pypi_token" },
71244
+ { rule_id: "aws_temp_access_key", regex: /\b(ASIA[0-9A-Z]{16})\b/g, captureIndex: 1, slugHint: "aws_access_key" },
71245
+ { rule_id: "gcp_oauth_token", regex: /\b(ya29\.[A-Za-z0-9_-]{30,})/g, captureIndex: 1, slugHint: "gcp_oauth_token" }
71246
+ ];
71247
+ var ALL_PATTERNS = [...ANCHORED_PATTERNS, ...PROVIDER_PATTERNS, ...STRUCTURED_PATTERNS];
71248
+
71249
+ // telegram-plugin/secret-detect/entropy.ts
71250
+ function shannonEntropy(s) {
71251
+ if (s.length === 0)
71252
+ return 0;
71253
+ const counts = new Map;
71254
+ for (const ch of s) {
71255
+ counts.set(ch, (counts.get(ch) ?? 0) + 1);
71256
+ }
71257
+ let h = 0;
71258
+ const len = s.length;
71259
+ for (const c of counts.values()) {
71260
+ const p = c / len;
71261
+ h -= p * Math.log2(p);
71262
+ }
71263
+ return h;
71264
+ }
71265
+
71266
+ // telegram-plugin/secret-detect/inert-values.ts
71267
+ var INERT_VALUE_RE = [
71268
+ /^\[REDACTED(?::[A-Za-z0-9_]+)?\]$/i,
71269
+ /^\$\{?[A-Za-z_][A-Za-z0-9_]*\}?$/,
71270
+ /^\{\{[^\n\r\u2028\u2029]*\}\}$/,
71271
+ /^<[^<>\n\r\u2028\u2029]*>$/,
71272
+ /^%[A-Za-z_][A-Za-z0-9_]*%$/,
71273
+ /^vault:[A-Za-z0-9_./-]*$/i,
71274
+ /^[*x\u2022.]+$/i,
71275
+ /^(?:process\.env|import\.meta\.env|os\.environ)(?:\.[A-Za-z_$][A-Za-z0-9_$]*|\[["']?[A-Za-z_$][A-Za-z0-9_$]*["']?\])*$/,
71276
+ /^(?:changeme|change-me|replaceme|replace-me|placeholder|todo|tbd|yourkey|your-key|yourpassword|your-password|yoursecret|your-secret|yourtoken|your-token)(?:[-_][A-Za-z0-9-]+)?$/i,
71277
+ /^[a-z]+(?: [a-z]+){2,}$/
71278
+ ];
71279
+ var CREDENTIAL_RUN_RE = /[A-Za-z0-9]{12,}/g;
71280
+ var MIXED_CLASS_RUN_MIN = 12;
71281
+ var SINGLE_CLASS_RUN_MIN = 16;
71282
+ function runIsCredentialShaped(run) {
71283
+ let classes = 0;
71284
+ if (/[a-z]/.test(run))
71285
+ classes++;
71286
+ if (/[A-Z]/.test(run))
71287
+ classes++;
71288
+ if (/[0-9]/.test(run))
71289
+ classes++;
71290
+ return run.length >= (classes >= 2 ? MIXED_CLASS_RUN_MIN : SINGLE_CLASS_RUN_MIN);
71291
+ }
71292
+ function hasCredentialShapedRun(value) {
71293
+ for (const run of value.match(CREDENTIAL_RUN_RE) ?? []) {
71294
+ if (runIsCredentialShaped(run))
71295
+ return true;
71296
+ }
71297
+ return false;
71298
+ }
71299
+ function isInertValue(value) {
71300
+ if (!INERT_VALUE_RE.some((re) => re.test(value)))
71301
+ return false;
71302
+ return !hasCredentialShapedRun(value);
71303
+ }
71304
+ var INERT_GATED_RULES = new Set([
71305
+ "env_key_value",
71306
+ "json_secret_field",
71307
+ "cli_flag"
71308
+ ]);
71309
+ var TRAILING_PUNCT_RE = /[.,;:!?)\]}]+$/;
71310
+ function stripTrailingPunctuation(value) {
71311
+ return value.replace(TRAILING_PUNCT_RE, "");
71312
+ }
71313
+
71314
+ // telegram-plugin/secret-detect/kv-scanner.ts
71315
+ var KV_RE = /\b([A-Za-z_][A-Za-z0-9_-]*(?:password|passwd|token|secret|key|api[_-]?key))\s*[:=]\s*["']?([^\s"'\\]{8,})["']?/gi;
71316
+ var KV_ENTROPY_THRESHOLD = 4;
71317
+ var MEMORABLE_PW_RE = /\b([A-Za-z0-9_-]*(?:password|passwd|passphrase|pwd))\b\s*(?:[:=]\s*|\s+is\s+)(["']?)([^\s"']{8,64})\2/gi;
71318
+ var MEMORABLE_PW_RULE_ID = "memorable_password";
71319
+ var MEMORABLE_PW_MIN_CLASSES = 2;
71320
+ function charClassCount(value) {
71321
+ let n = 0;
71322
+ if (/[a-z]/.test(value))
71323
+ n++;
71324
+ if (/[A-Z]/.test(value))
71325
+ n++;
71326
+ if (/[0-9]/.test(value))
71327
+ n++;
71328
+ if (/[^A-Za-z0-9]/.test(value))
71329
+ n++;
71330
+ return n;
71331
+ }
71332
+ function looksLikeMemorablePassword(value) {
71333
+ if (isInertValue(value))
71334
+ return false;
71335
+ const core = stripTrailingPunctuation(value);
71336
+ if (core.length < 8 || core.length > 64)
71337
+ return false;
71338
+ if (isInertValue(core))
71339
+ return false;
71340
+ if (new Set(core).size < 4)
71341
+ return false;
71342
+ return charClassCount(core) >= MEMORABLE_PW_MIN_CLASSES;
71343
+ }
71344
+ function scanMemorablePasswords(text) {
71345
+ const hits = [];
71346
+ MEMORABLE_PW_RE.lastIndex = 0;
71347
+ let m;
71348
+ while ((m = MEMORABLE_PW_RE.exec(text)) !== null) {
71349
+ const keyName = m[1];
71350
+ const value = m[3];
71351
+ if (!value || !looksLikeMemorablePassword(value))
71352
+ continue;
71353
+ const valueOffsetInMatch = m[0].indexOf(value, keyName.length);
71354
+ if (valueOffsetInMatch < 0)
71355
+ continue;
71356
+ const start = m.index + valueOffsetInMatch;
71357
+ hits.push({
71358
+ rule_id: MEMORABLE_PW_RULE_ID,
71359
+ start,
71360
+ end: start + value.length,
71361
+ matched_text: value,
71362
+ key_name: keyName,
71363
+ confidence: "ambiguous"
71364
+ });
71365
+ }
71366
+ return hits;
71367
+ }
71368
+ function scanKeyValue(text) {
71369
+ const hits = [];
71370
+ KV_RE.lastIndex = 0;
71371
+ let m;
71372
+ while ((m = KV_RE.exec(text)) !== null) {
71373
+ const [, keyName, value] = m;
71374
+ if (!value)
71375
+ continue;
71376
+ if (isInertValue(value))
71377
+ continue;
71378
+ const h = shannonEntropy(value);
71379
+ if (h < KV_ENTROPY_THRESHOLD)
71380
+ continue;
71381
+ const valueOffsetInMatch = m[0].indexOf(value, keyName.length);
71382
+ if (valueOffsetInMatch < 0)
71383
+ continue;
71384
+ const start = m.index + valueOffsetInMatch;
71385
+ const end = start + value.length;
71386
+ hits.push({
71387
+ rule_id: "kv_entropy",
71388
+ start,
71389
+ end,
71390
+ matched_text: value,
71391
+ key_name: keyName,
71392
+ confidence: "ambiguous"
71393
+ });
71394
+ }
71395
+ return hits;
71396
+ }
71397
+
71398
+ // telegram-plugin/secret-detect/db-uri.ts
71399
+ var DB_URI_RE = /\b([a-zA-Z][a-zA-Z0-9+.-]+):\/\/([^\s:/@]+):([^\s/?#]+)@([^\s/@?#]+)/g;
71400
+ var DB_URI_RULE_ID = "db_uri_password";
71401
+ function isInertPassword(value) {
71402
+ if (value.startsWith("[REDACTED"))
71403
+ return true;
71404
+ return /^[*x]+$/i.test(value);
71405
+ }
71406
+ function scanDbUris(text) {
71407
+ const hits = [];
71408
+ DB_URI_RE.lastIndex = 0;
71409
+ let m;
71410
+ while ((m = DB_URI_RE.exec(text)) !== null) {
71411
+ const scheme = m[1];
71412
+ const user = m[2];
71413
+ const password = m[3];
71414
+ if (isInertPassword(password))
71415
+ continue;
71416
+ const start = m.index + scheme.length + 3 + user.length + 1;
71417
+ hits.push({
71418
+ rule_id: DB_URI_RULE_ID,
71419
+ start,
71420
+ end: start + password.length,
71421
+ matched_text: password,
71422
+ key_name: `${scheme}_password`,
71423
+ confidence: "ambiguous"
71424
+ });
71425
+ }
71426
+ return hits;
71427
+ }
71428
+
71429
+ // telegram-plugin/secret-detect/generic-entropy.ts
71430
+ var CANDIDATE_RE = /[A-Za-z0-9]{28,}/g;
71431
+ var GENERIC_MIN_DISTINCT = 18;
71432
+ var MAX_GENERIC_HITS = 20;
71433
+ function hasDistinctChars(tok, n) {
71434
+ const seen = new Uint8Array(128);
71435
+ let distinct = 0;
71436
+ for (let i = 0;i < tok.length; i++) {
71437
+ const c = tok.charCodeAt(i);
71438
+ if (seen[c] === 0) {
71439
+ seen[c] = 1;
71440
+ if (++distinct >= n)
71441
+ return true;
71442
+ }
71443
+ }
71444
+ return false;
71445
+ }
71446
+ function hasDigit(tok) {
71447
+ for (let i = 0;i < tok.length; i++) {
71448
+ const c = tok.charCodeAt(i);
71449
+ if (c >= 48 && c <= 57)
71450
+ return true;
71451
+ }
71452
+ return false;
71453
+ }
71454
+ function scanGenericSecrets(text) {
71455
+ const hits = [];
71456
+ CANDIDATE_RE.lastIndex = 0;
71457
+ let m;
71458
+ while ((m = CANDIDATE_RE.exec(text)) !== null) {
71459
+ if (hits.length >= MAX_GENERIC_HITS)
71460
+ break;
71461
+ const tok = m[0];
71462
+ if (!hasDigit(tok))
71463
+ continue;
71464
+ if (!hasDistinctChars(tok, GENERIC_MIN_DISTINCT))
71465
+ continue;
71466
+ hits.push({
71467
+ rule_id: "generic_high_entropy",
71468
+ start: m.index,
71469
+ end: m.index + tok.length,
71470
+ matched_text: tok,
71471
+ confidence: "ambiguous"
71472
+ });
71473
+ }
71474
+ return hits;
71475
+ }
71476
+
71477
+ // telegram-plugin/secret-detect/chunker.ts
71478
+ var CHUNK_THRESHOLD = 32 * 1024;
71479
+ var WINDOW_SIZE = 16 * 1024;
71480
+ var OVERLAP = 8 * 1024;
71481
+ function chunk(text) {
71482
+ if (text.length <= CHUNK_THRESHOLD) {
71483
+ return [{ offset: 0, text }];
71484
+ }
71485
+ const out = [];
71486
+ let offset = 0;
71487
+ while (offset < text.length) {
71488
+ const end = Math.min(offset + WINDOW_SIZE, text.length);
71489
+ out.push({ offset, text: text.slice(offset, end) });
71490
+ if (end >= text.length)
71491
+ break;
71492
+ offset = end - OVERLAP;
71493
+ }
71494
+ return out;
71495
+ }
71496
+
71497
+ // telegram-plugin/secret-detect/suppressor.ts
71498
+ var MARKERS = ["test", "mock", "example", "fixture", "dummy"];
71499
+ var WINDOW = 40;
71500
+ var MARKER_RE = new RegExp(`\\b(?:${MARKERS.join("|")})\\b`, "i");
71501
+ function isSuppressed(text, start, end) {
71502
+ const left = Math.max(0, start - WINDOW);
71503
+ const right = Math.min(text.length, end + WINDOW);
71504
+ const context = text.slice(left, start) + text.slice(end, right);
71505
+ return MARKER_RE.test(context);
71506
+ }
71507
+
71508
+ // telegram-plugin/secret-detect/slug.ts
71509
+ function sanitizeKeyName(raw) {
71510
+ const up = raw.toUpperCase();
71511
+ const cleaned = up.replace(/[^A-Z0-9_]+/g, "_").replace(/_+/g, "_").replace(/^_+|_+$/g, "");
71512
+ return cleaned.length > 0 ? cleaned : "SECRET";
71513
+ }
71514
+ function datePart(now = new Date) {
71515
+ const y = now.getUTCFullYear();
71516
+ const m = String(now.getUTCMonth() + 1).padStart(2, "0");
71517
+ const d = String(now.getUTCDate()).padStart(2, "0");
71518
+ return `${y}${m}${d}`;
71519
+ }
71520
+ function deriveSlug(inputs, existing) {
71521
+ let base;
71522
+ if (inputs.key_name && inputs.key_name.trim().length > 0) {
71523
+ base = sanitizeKeyName(inputs.key_name);
71524
+ } else {
71525
+ base = `${inputs.rule_id}_${datePart(inputs.now)}`;
71526
+ }
71527
+ if (!existing.has(base))
71528
+ return base;
71529
+ let n = 2;
71530
+ while (existing.has(`${base}_${n}`))
71531
+ n++;
71532
+ return `${base}_${n}`;
71533
+ }
71534
+ // telegram-plugin/secret-detect/url-redact.ts
71535
+ var SENSITIVE_PARAMS = new Set([
71536
+ "token",
71537
+ "key",
71538
+ "api_key",
71539
+ "apikey",
71540
+ "secret",
71541
+ "access_token",
71542
+ "password",
71543
+ "pass",
71544
+ "auth",
71545
+ "client_secret",
71546
+ "refresh_token",
71547
+ "signature"
71548
+ ]);
71549
+ var URL_RE = /\b(?:https?|wss?|ftp):\/\/[^\s<>"']+/gi;
71550
+ function redactUrls(text) {
71551
+ return text.replace(URL_RE, (m) => {
71552
+ const redacted = redactOne(m);
71553
+ return redacted ?? m;
71554
+ });
71555
+ }
71556
+ function redactOne(raw) {
71557
+ let u;
71558
+ try {
71559
+ u = new URL(raw);
71560
+ } catch {
71561
+ return null;
71562
+ }
71563
+ let changed = false;
71564
+ if (u.username || u.password) {
71565
+ u.username = "***";
71566
+ u.password = "";
71567
+ changed = true;
71568
+ }
71569
+ for (const [key] of u.searchParams) {
71570
+ if (SENSITIVE_PARAMS.has(key.toLowerCase())) {
71571
+ u.searchParams.set(key, "***");
71572
+ changed = true;
71573
+ }
71574
+ }
71575
+ return changed ? u.toString() : null;
71576
+ }
71577
+
71578
+ // telegram-plugin/secret-detect/index.ts
71579
+ function detectSecrets(text) {
71580
+ if (!text || text.length === 0)
71581
+ return [];
71582
+ const windows = chunk(text);
71583
+ const raw = [];
71584
+ for (const win of windows) {
71585
+ for (const p of ALL_PATTERNS) {
71586
+ const re = new RegExp(p.regex.source, p.regex.flags.includes("g") ? p.regex.flags : p.regex.flags + "g");
71587
+ let m;
71588
+ while ((m = re.exec(win.text)) !== null) {
71589
+ if (m[0].length === 0) {
71590
+ re.lastIndex++;
71591
+ continue;
71592
+ }
71593
+ const cap = p.captureIndex === 0 ? m[0] : m[p.captureIndex];
71594
+ if (!cap)
71595
+ continue;
71596
+ const matchStart = p.captureIndex === 0 ? m.index : m.index + m[0].indexOf(cap);
71597
+ if (matchStart < 0)
71598
+ continue;
71599
+ const globalStart = win.offset + matchStart;
71600
+ const globalEnd = globalStart + cap.length;
71601
+ const keyName = p.rule_id === "env_key_value" ? m[1] : undefined;
71602
+ if (INERT_GATED_RULES.has(p.rule_id) && isInertValue(cap))
71603
+ continue;
71604
+ if (p.rule_id === "env_key_value") {
71605
+ const ENV_KV_MIN_LEN = 12;
71606
+ const ENV_KV_MIN_ENTROPY = 3.5;
71607
+ if (cap.length < ENV_KV_MIN_LEN)
71608
+ continue;
71609
+ if (shannonEntropy(cap) < ENV_KV_MIN_ENTROPY)
71610
+ continue;
71611
+ }
71612
+ raw.push({
71613
+ rule_id: p.rule_id,
71614
+ start: globalStart,
71615
+ end: globalEnd,
71616
+ matched_text: cap,
71617
+ key_name: keyName,
71618
+ confidence: "high"
71619
+ });
71620
+ }
71621
+ }
71622
+ const kvHits = scanKeyValue(win.text);
71623
+ for (const h of kvHits) {
71624
+ raw.push({ ...h, start: h.start + win.offset, end: h.end + win.offset });
71625
+ }
71626
+ for (const h of scanDbUris(win.text)) {
71627
+ raw.push({ ...h, start: h.start + win.offset, end: h.end + win.offset });
71628
+ }
71629
+ for (const h of scanMemorablePasswords(win.text)) {
71630
+ raw.push({ ...h, start: h.start + win.offset, end: h.end + win.offset });
71631
+ }
71632
+ const genHits = scanGenericSecrets(win.text);
71633
+ for (const h of genHits) {
71634
+ raw.push({ ...h, start: h.start + win.offset, end: h.end + win.offset });
71635
+ }
71636
+ }
71637
+ const deduped = dedupeRaw(raw);
71638
+ const final = dropOverlaps(deduped);
71639
+ const existing = new Set;
71640
+ const out = [];
71641
+ for (const h of final) {
71642
+ const suggested_slug = deriveSlug({ key_name: h.key_name, rule_id: h.rule_id }, existing);
71643
+ existing.add(suggested_slug);
71644
+ out.push({
71645
+ rule_id: h.rule_id,
71646
+ matched_text: h.matched_text,
71647
+ start: h.start,
71648
+ end: h.end,
71649
+ confidence: h.confidence,
71650
+ suppressed: isSuppressed(text, h.start, h.end),
71651
+ suggested_slug,
71652
+ key_name: h.key_name
71653
+ });
71654
+ }
71655
+ out.sort((a, b) => a.start - b.start);
71656
+ return out;
71657
+ }
71658
+ function dedupeRaw(raw) {
71659
+ const seen = new Map;
71660
+ for (const h of raw) {
71661
+ const key = `${h.start}:${h.end}`;
71662
+ const existing = seen.get(key);
71663
+ if (!existing) {
71664
+ seen.set(key, h);
71665
+ continue;
71666
+ }
71667
+ if (existing.confidence === "ambiguous" && h.confidence === "high") {
71668
+ seen.set(key, h);
71669
+ }
71670
+ }
71671
+ return Array.from(seen.values());
71672
+ }
71673
+ function dropOverlaps(hits) {
71674
+ const out = hits.filter((h) => !(h.confidence === "ambiguous" && hits.some((o) => o !== h && o.start <= h.start && o.end >= h.end && !(o.start === h.start && o.end === h.end))));
71675
+ out.sort((a, b) => a.start - b.start || a.end - b.end);
71676
+ return out;
71677
+ }
71678
+
71679
+ // telegram-plugin/secret-detect/redact.ts
71680
+ var REDACTED_MARKER = "[REDACTED]";
71681
+ function redact(text) {
71682
+ if (!text || text.length === 0)
71683
+ return text;
71684
+ const urlScrubbed = redactUrls(text);
71685
+ const hits = detectSecrets(urlScrubbed).filter((h) => h.rule_id !== "generic_high_entropy");
71686
+ if (hits.length === 0)
71687
+ return urlScrubbed;
71688
+ const sorted = [...hits].sort((a, b) => b.start - a.start);
71689
+ let out = urlScrubbed;
71690
+ for (const h of sorted) {
71691
+ out = out.slice(0, h.start) + redactedMarker(h.rule_id) + out.slice(h.end);
71692
+ }
71693
+ return out;
71694
+ }
71695
+ function redactedMarker(ruleId) {
71696
+ const trimmed = ruleId.replace(/^(kv|env)_/, "");
71697
+ if (!trimmed || trimmed === "key_value" || trimmed === "kv_entropy") {
71698
+ return REDACTED_MARKER;
71699
+ }
71700
+ return `[REDACTED:${trimmed}]`;
71701
+ }
71702
+ // src/memory/hindsight-write-redaction.ts
71703
+ function redactJsonStrings(value) {
71704
+ if (typeof value === "string")
71705
+ return redact(value);
71706
+ if (Array.isArray(value))
71707
+ return value.map(redactJsonStrings);
71708
+ if (value && typeof value === "object") {
71709
+ const out = {};
71710
+ for (const [k, v] of Object.entries(value)) {
71711
+ out[k] = redactJsonStrings(v);
71712
+ }
71713
+ return out;
71714
+ }
71715
+ return value;
71716
+ }
71717
+ var REDACTED_ITEM_FIELDS = ["content", "context"];
71718
+ function redactMemoryWriteBody(body) {
71719
+ if (!body || typeof body !== "object" || Array.isArray(body))
71720
+ return body;
71721
+ const src = body;
71722
+ if (!Array.isArray(src.items))
71723
+ return body;
71724
+ const items = src.items.map((item) => {
71725
+ if (!item || typeof item !== "object" || Array.isArray(item))
71726
+ return item;
71727
+ const row = { ...item };
71728
+ for (const field of REDACTED_ITEM_FIELDS) {
71729
+ if (typeof row[field] === "string") {
71730
+ row[field] = redact(row[field]);
71731
+ }
71732
+ }
71733
+ if (row.metadata && typeof row.metadata === "object") {
71734
+ row.metadata = redactJsonStrings(row.metadata);
71735
+ }
71736
+ return row;
71737
+ });
71738
+ return { ...src, items };
71739
+ }
71740
+
71741
+ // src/agents/handoff-summarizer.ts
71742
+ init_observation_scopes();
71149
71743
  var HANDOFF_STATUS_MIRROR_SKIPPED = "mirror-skipped-invalid-scope";
71150
71744
  var DEFAULT_MAX_TURNS = 50;
71151
71745
  var TOPIC_MAX_CHARS = 117;
@@ -71332,7 +71926,7 @@ async function mirrorToHindsight(briefing, opts) {
71332
71926
  `);
71333
71927
  return false;
71334
71928
  }
71335
- const body = {
71929
+ const body = redactMemoryWriteBody({
71336
71930
  items: [
71337
71931
  {
71338
71932
  content: briefing,
@@ -71342,7 +71936,7 @@ async function mirrorToHindsight(briefing, opts) {
71342
71936
  }
71343
71937
  ],
71344
71938
  async: true
71345
- };
71939
+ });
71346
71940
  try {
71347
71941
  await fetchFn(endpoint, {
71348
71942
  method: "POST",
@@ -74348,8 +74942,8 @@ Bootstrapping agent: ${name}
74348
74942
  const code = await new Promise((resolve24) => {
74349
74943
  process.stdin.setEncoding("utf-8");
74350
74944
  let buf = "";
74351
- process.stdin.on("data", (chunk) => {
74352
- buf += chunk.toString();
74945
+ process.stdin.on("data", (chunk2) => {
74946
+ buf += chunk2.toString();
74353
74947
  const newlineIdx = buf.indexOf(`
74354
74948
  `);
74355
74949
  if (newlineIdx !== -1) {
@@ -74431,8 +75025,8 @@ switchroom agent add: ${name}
74431
75025
  return new Promise((resolveLine) => {
74432
75026
  process.stdin.setEncoding("utf-8");
74433
75027
  let buf = "";
74434
- const onData = (chunk) => {
74435
- buf += chunk.toString();
75028
+ const onData = (chunk2) => {
75029
+ buf += chunk2.toString();
74436
75030
  const newlineIdx = buf.indexOf(`
74437
75031
  `);
74438
75032
  if (newlineIdx !== -1) {
@@ -76463,8 +77057,8 @@ async function readHiddenLine2(prompt) {
76463
77057
  stdin.setRawMode(true);
76464
77058
  }
76465
77059
  let line = "";
76466
- const onData = (chunk) => {
76467
- const s = chunk.toString("utf8");
77060
+ const onData = (chunk2) => {
77061
+ const s = chunk2.toString("utf8");
76468
77062
  for (const ch of s) {
76469
77063
  if (ch === `
76470
77064
  ` || ch === "\r") {
@@ -77393,7 +77987,7 @@ function getVaultPath2(configPath) {
77393
77987
  return resolvePath("~/.switchroom/vault.enc");
77394
77988
  }
77395
77989
  }
77396
- function maskToken(s) {
77990
+ function maskToken2(s) {
77397
77991
  if (s.length >= 18)
77398
77992
  return `${s.slice(0, 6)}...${s.slice(-4)}`;
77399
77993
  return "***";
@@ -77681,7 +78275,7 @@ function registerVaultSweep(vault, program3) {
77681
78275
  console.log("");
77682
78276
  console.log(source_default.dim(`masked values for display only:`));
77683
78277
  for (const v of values) {
77684
- console.log(` vault:${v.key} \u2192 ${maskToken(v.value)}`);
78278
+ console.log(` vault:${v.key} \u2192 ${maskToken2(v.value)}`);
77685
78279
  }
77686
78280
  if (opts.dryRun) {
77687
78281
  console.log(source_default.yellow("dry-run \u2014 no files modified. Rerun without --dry-run to apply."));
@@ -80654,8 +81248,8 @@ class VaultBroker {
80654
81248
  peer = this.testOpts._testIdentify ? this.testOpts._testIdentify(listenerSocketPath, socket) : identify(listenerSocketPath, socket);
80655
81249
  }
80656
81250
  let buffer = "";
80657
- socket.on("data", (chunk) => {
80658
- buffer += chunk.toString("utf8");
81251
+ socket.on("data", (chunk2) => {
81252
+ buffer += chunk2.toString("utf8");
80659
81253
  if (Buffer.byteLength(buffer, "utf8") > MAX_FRAME_BYTES) {
80660
81254
  const resp = encodeResponse(errorResponse("BAD_REQUEST", "Frame exceeds 64 KiB limit"));
80661
81255
  socket.end(resp);
@@ -81764,8 +82358,8 @@ class VaultBroker {
81764
82358
  const auditCaller = isOperator ? "operator" : unlockPeer !== null ? callerFromPeer(unlockPeer) : `pid:${process.pid}`;
81765
82359
  const auditCgroup = isOperator ? undefined : unlockPeer?.systemdUnit ?? undefined;
81766
82360
  let buffer = "";
81767
- socket.on("data", (chunk) => {
81768
- buffer += chunk.toString("utf8");
82361
+ socket.on("data", (chunk2) => {
82362
+ buffer += chunk2.toString("utf8");
81769
82363
  const newlineIdx = buffer.indexOf(`
81770
82364
  `);
81771
82365
  if (newlineIdx === -1) {
@@ -83332,7 +83926,7 @@ function promptLine2(prompt, hidden = false) {
83332
83926
  function readStdinToEnd() {
83333
83927
  return new Promise((resolve30, reject) => {
83334
83928
  const chunks = [];
83335
- process.stdin.on("data", (chunk) => chunks.push(chunk));
83929
+ process.stdin.on("data", (chunk2) => chunks.push(chunk2));
83336
83930
  process.stdin.on("end", () => {
83337
83931
  resolve30(Buffer.concat(chunks).toString("utf8"));
83338
83932
  });
@@ -85484,9 +86078,9 @@ Repairing vector index coverage for ${target}${opts.dryRun ? source_default.yell
85484
86078
  `));
85485
86079
  const child = spawn5("docker", argv, { stdio: ["ignore", "pipe", "inherit"] });
85486
86080
  let captured = "";
85487
- child.stdout?.on("data", (chunk) => {
85488
- captured += chunk.toString();
85489
- process.stdout.write(chunk);
86081
+ child.stdout?.on("data", (chunk2) => {
86082
+ captured += chunk2.toString();
86083
+ process.stdout.write(chunk2);
85490
86084
  });
85491
86085
  const childExit = await new Promise((resolve30) => {
85492
86086
  child.on("error", () => resolve30(127));
@@ -85587,14 +86181,21 @@ Demoting memory ${source_default.cyan(memoryId)}`));
85587
86181
  const timeoutMs = Math.max(1000, parseInt(opts.timeout, 10) || 60000);
85588
86182
  const ctrl = new AbortController;
85589
86183
  const t = setTimeout(() => ctrl.abort(), timeoutMs);
86184
+ const payload = redactMemoryWriteBody({
86185
+ items: [{ content, tags: ["operator-authored", "profile"] }],
86186
+ async: false
86187
+ });
86188
+ const stored = payload.items[0].content;
86189
+ if (stored !== content) {
86190
+ console.error(source_default.yellow(`\u26a0 Credential-shaped text in this fact was replaced with a
86191
+ ` + ` [REDACTED] marker before it was stored. Put secrets in the
86192
+ ` + " vault (`switchroom vault set <key>`) and reference them as\n" + " `vault:<key>` instead."));
86193
+ }
85590
86194
  try {
85591
86195
  const res = await fetch(url, {
85592
86196
  method: "POST",
85593
86197
  headers: { "Content-Type": "application/json" },
85594
- body: JSON.stringify({
85595
- items: [{ content, tags: ["operator-authored", "profile"] }],
85596
- async: false
85597
- }),
86198
+ body: JSON.stringify(payload),
85598
86199
  signal: ctrl.signal
85599
86200
  });
85600
86201
  if (!res.ok) {
@@ -85602,7 +86203,7 @@ Demoting memory ${source_default.cyan(memoryId)}`));
85602
86203
  process.exit(1);
85603
86204
  }
85604
86205
  console.log(source_default.green(`\u2713 Added to profile bank "${bank}"`));
85605
- console.log(source_default.gray(` ${content}`));
86206
+ console.log(source_default.gray(` ${stored}`));
85606
86207
  console.log(source_default.gray(" (fact extraction runs in the background \u2014 `profile list` may lag a few seconds)"));
85607
86208
  console.log(source_default.gray(`
85608
86209
  Wire it into agents via memory.recall.additional_banks: ["${bank}"] in switchroom.yaml,
@@ -87895,8 +88496,8 @@ function forwardToGateway(socketPath, req, opts = {}) {
87895
88496
  conn.write(JSON.stringify(req) + `
87896
88497
  `);
87897
88498
  });
87898
- conn.on("data", (chunk) => {
87899
- buf += chunk;
88499
+ conn.on("data", (chunk2) => {
88500
+ buf += chunk2;
87900
88501
  const nl = buf.indexOf(`
87901
88502
  `);
87902
88503
  if (nl === -1)
@@ -94075,410 +94676,6 @@ function computeFingerprint(source, code) {
94075
94676
  return `${source}::${code}`;
94076
94677
  }
94077
94678
 
94078
- // telegram-plugin/secret-detect/patterns.ts
94079
- var ANCHORED_PATTERNS = [
94080
- { rule_id: "anthropic_api_key", regex: /\b(sk-ant-[A-Za-z0-9_-]{8,})\b/g, captureIndex: 1, slugHint: "anthropic_api_key" },
94081
- { 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" },
94082
- { rule_id: "openai_api_key", regex: /\b(sk-[A-Za-z0-9_-]{20,})\b/g, captureIndex: 1, slugHint: "openai_api_key" },
94083
- { rule_id: "github_pat_classic", regex: /\b(ghp_[A-Za-z0-9]{20,})\b/g, captureIndex: 1, slugHint: "github_pat" },
94084
- { rule_id: "github_pat_fine_grained", regex: /\b(github_pat_[A-Za-z0-9_]{20,})\b/g, captureIndex: 1, slugHint: "github_pat" },
94085
- { rule_id: "slack_token", regex: /\b(xox[baprs]-[A-Za-z0-9-]{10,})\b/g, captureIndex: 1, slugHint: "slack_token" },
94086
- { rule_id: "slack_app_token", regex: /\b(xapp-[A-Za-z0-9-]{10,})\b/g, captureIndex: 1, slugHint: "slack_app_token" },
94087
- { rule_id: "groq_api_key", regex: /\b(gsk_[A-Za-z0-9_-]{10,})\b/g, captureIndex: 1, slugHint: "groq_api_key" },
94088
- { rule_id: "google_api_key", regex: /\b(AIza[0-9A-Za-z\-_]{20,})\b/g, captureIndex: 1, slugHint: "google_api_key" },
94089
- { rule_id: "perplexity_api_key", regex: /\b(pplx-[A-Za-z0-9_-]{10,})\b/g, captureIndex: 1, slugHint: "perplexity_api_key" },
94090
- { rule_id: "npm_token", regex: /\b(npm_[A-Za-z0-9]{10,})\b/g, captureIndex: 1, slugHint: "npm_token" },
94091
- { rule_id: "telegram_bot_token_prefixed", regex: /\bbot(\d{6,}:[A-Za-z0-9_-]{20,})\b/g, captureIndex: 1, slugHint: "telegram_bot_token" },
94092
- { rule_id: "telegram_bot_token", regex: /\b(\d{6,}:[A-Za-z0-9_-]{20,})\b/g, captureIndex: 1, slugHint: "telegram_bot_token" },
94093
- { rule_id: "laravel_sanctum_token", regex: /\b(\d+\|[A-Za-z0-9]{40,})\b/g, captureIndex: 1, slugHint: "api_token" },
94094
- { rule_id: "aws_access_key", regex: /\b(AKIA[0-9A-Z]{16})\b/g, captureIndex: 1, slugHint: "aws_access_key" },
94095
- { 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" }
94096
- ];
94097
- var STRUCTURED_PATTERNS = [
94098
- {
94099
- rule_id: "env_key_value",
94100
- regex: /\b([A-Z0-9_]*(?:KEY|TOKEN|SECRET|PASSWORD|PASSWD))\b\s*[=:]\s*(["']?)([^\s"'\\]+)\2/g,
94101
- captureIndex: 3,
94102
- slugHint: "env"
94103
- },
94104
- {
94105
- rule_id: "json_secret_field",
94106
- regex: /"(?:apiKey|token|secret|password|passwd|accessToken|refreshToken)"\s*:\s*"([^"]+)"/g,
94107
- captureIndex: 1,
94108
- slugHint: "json_secret"
94109
- },
94110
- {
94111
- rule_id: "cli_flag",
94112
- regex: /--(?:api[-_]?key|hook[-_]?token|token|secret|password|passwd)\s+(["']?)([^\s"']+)\1/g,
94113
- captureIndex: 2,
94114
- slugHint: "cli_flag"
94115
- },
94116
- {
94117
- rule_id: "bearer_auth_header",
94118
- regex: /Authorization\s*[:=]\s*Bearer\s+([A-Za-z0-9._\-+=]+)/g,
94119
- captureIndex: 1,
94120
- slugHint: "bearer_token"
94121
- },
94122
- {
94123
- rule_id: "bearer_loose",
94124
- regex: /\bBearer\s+([A-Za-z0-9._\-+=]{18,})\b/g,
94125
- captureIndex: 1,
94126
- slugHint: "bearer_token"
94127
- },
94128
- {
94129
- rule_id: "pem_private_key",
94130
- regex: /-----BEGIN [A-Z ]*PRIVATE KEY-----[\s\S]+?-----END [A-Z ]*PRIVATE KEY-----/g,
94131
- captureIndex: 0,
94132
- slugHint: "pem_private_key"
94133
- }
94134
- ];
94135
- var PROVIDER_PATTERNS = [
94136
- { rule_id: "slack_webhook", regex: /(https:\/\/hooks\.slack\.com\/services\/[A-Za-z0-9_/]+)/g, captureIndex: 1, slugHint: "slack_webhook" },
94137
- { rule_id: "stripe_live_secret", regex: /\b(sk_live_[A-Za-z0-9]{24,})\b/g, captureIndex: 1, slugHint: "stripe_key" },
94138
- { rule_id: "stripe_restricted", regex: /\b(rk_live_[A-Za-z0-9]{24,})\b/g, captureIndex: 1, slugHint: "stripe_key" },
94139
- { rule_id: "sendgrid_api_key", regex: /\b(SG\.[A-Za-z0-9_-]{22}\.[A-Za-z0-9_-]{43})\b/g, captureIndex: 1, slugHint: "sendgrid_key" },
94140
- { rule_id: "gitlab_pat", regex: /\b(glpat-[A-Za-z0-9_-]{20})\b/g, captureIndex: 1, slugHint: "gitlab_pat" },
94141
- { rule_id: "huggingface_token", regex: /\b(hf_[A-Za-z0-9]{34,})\b/g, captureIndex: 1, slugHint: "huggingface_token" },
94142
- { rule_id: "twilio_api_key", regex: /\b(SK[0-9a-f]{32})\b/g, captureIndex: 1, slugHint: "twilio_api_key" },
94143
- { rule_id: "mailgun_key", regex: /\b(key-[0-9a-f]{32})\b/g, captureIndex: 1, slugHint: "mailgun_key" },
94144
- { rule_id: "digitalocean_pat", regex: /\b(dop_v1_[a-f0-9]{64})\b/g, captureIndex: 1, slugHint: "digitalocean_token" },
94145
- { rule_id: "digitalocean_oauth", regex: /\b(doo_v1_[a-f0-9]{64})\b/g, captureIndex: 1, slugHint: "digitalocean_token" },
94146
- { rule_id: "digitalocean_refresh", regex: /\b(dor_v1_[a-f0-9]{64})\b/g, captureIndex: 1, slugHint: "digitalocean_token" },
94147
- { rule_id: "doppler_token", regex: /\b(dp\.(?:pt|st|ct|sa|scim|audit)\.[A-Za-z0-9]{40,44})\b/g, captureIndex: 1, slugHint: "doppler_token" },
94148
- { rule_id: "linear_api_key", regex: /\b(lin_api_[A-Za-z0-9]{40})\b/g, captureIndex: 1, slugHint: "linear_api_key" },
94149
- { rule_id: "shopify_access_token", regex: /\b(shpat_[a-fA-F0-9]{32})\b/g, captureIndex: 1, slugHint: "shopify_token" },
94150
- { rule_id: "shopify_shared_secret", regex: /\b(shpss_[a-fA-F0-9]{32})\b/g, captureIndex: 1, slugHint: "shopify_token" },
94151
- { rule_id: "shopify_private_app", regex: /\b(shppa_[a-fA-F0-9]{32})\b/g, captureIndex: 1, slugHint: "shopify_token" },
94152
- { rule_id: "square_access_token", regex: /\b(sq0atp-[A-Za-z0-9_-]{22})\b/g, captureIndex: 1, slugHint: "square_token" },
94153
- { rule_id: "square_oauth_secret", regex: /\b(sq0csp-[A-Za-z0-9_-]{43})\b/g, captureIndex: 1, slugHint: "square_token" },
94154
- { rule_id: "newrelic_key", regex: /\b(NRAK-[A-Z0-9]{27})\b/g, captureIndex: 1, slugHint: "newrelic_key" },
94155
- { rule_id: "notion_token", regex: /\b(ntn_[A-Za-z0-9]{46})\b/g, captureIndex: 1, slugHint: "notion_token" },
94156
- { rule_id: "planetscale_password", regex: /\b(pscale_pw_[A-Za-z0-9_.-]{43})\b/g, captureIndex: 1, slugHint: "planetscale_token" },
94157
- { rule_id: "planetscale_token", regex: /\b(pscale_tkn_[A-Za-z0-9_.-]{43})\b/g, captureIndex: 1, slugHint: "planetscale_token" },
94158
- { rule_id: "supabase_service_key", regex: /\b(sbp_[a-f0-9]{40})\b/g, captureIndex: 1, slugHint: "supabase_key" },
94159
- { rule_id: "atlassian_token", regex: /\b(ATATT[A-Za-z0-9_\-=]{20,})\b/g, captureIndex: 1, slugHint: "atlassian_token" },
94160
- { rule_id: "dropbox_token", regex: /\b(sl\.[A-Za-z0-9_-]{130,})/g, captureIndex: 1, slugHint: "dropbox_token" },
94161
- { rule_id: "databricks_token", regex: /\b(dapi[a-f0-9]{32})\b/g, captureIndex: 1, slugHint: "databricks_token" },
94162
- { rule_id: "grafana_service_account", regex: /\b(glsa_[A-Za-z0-9]{32}_[a-fA-F0-9]{8})\b/g, captureIndex: 1, slugHint: "grafana_token" },
94163
- { rule_id: "pypi_token", regex: /\b(pypi-AgEIcHlwaS[A-Za-z0-9_-]{50,})/g, captureIndex: 1, slugHint: "pypi_token" },
94164
- { rule_id: "aws_temp_access_key", regex: /\b(ASIA[0-9A-Z]{16})\b/g, captureIndex: 1, slugHint: "aws_access_key" },
94165
- { rule_id: "gcp_oauth_token", regex: /\b(ya29\.[A-Za-z0-9_-]{30,})/g, captureIndex: 1, slugHint: "gcp_oauth_token" }
94166
- ];
94167
- var ALL_PATTERNS = [...ANCHORED_PATTERNS, ...PROVIDER_PATTERNS, ...STRUCTURED_PATTERNS];
94168
-
94169
- // telegram-plugin/secret-detect/entropy.ts
94170
- function shannonEntropy(s) {
94171
- if (s.length === 0)
94172
- return 0;
94173
- const counts = new Map;
94174
- for (const ch of s) {
94175
- counts.set(ch, (counts.get(ch) ?? 0) + 1);
94176
- }
94177
- let h = 0;
94178
- const len = s.length;
94179
- for (const c of counts.values()) {
94180
- const p = c / len;
94181
- h -= p * Math.log2(p);
94182
- }
94183
- return h;
94184
- }
94185
-
94186
- // telegram-plugin/secret-detect/kv-scanner.ts
94187
- var KV_RE = /\b([A-Za-z_][A-Za-z0-9_-]*(?:password|passwd|token|secret|key|api[_-]?key))\s*[:=]\s*["']?([^\s"'\\]{8,})["']?/gi;
94188
- var KV_ENTROPY_THRESHOLD = 4;
94189
- function scanKeyValue(text) {
94190
- const hits = [];
94191
- KV_RE.lastIndex = 0;
94192
- let m;
94193
- while ((m = KV_RE.exec(text)) !== null) {
94194
- const [, keyName, value] = m;
94195
- if (!value)
94196
- continue;
94197
- const h = shannonEntropy(value);
94198
- if (h < KV_ENTROPY_THRESHOLD)
94199
- continue;
94200
- const valueOffsetInMatch = m[0].indexOf(value, keyName.length);
94201
- if (valueOffsetInMatch < 0)
94202
- continue;
94203
- const start = m.index + valueOffsetInMatch;
94204
- const end = start + value.length;
94205
- hits.push({
94206
- rule_id: "kv_entropy",
94207
- start,
94208
- end,
94209
- matched_text: value,
94210
- key_name: keyName,
94211
- confidence: "ambiguous"
94212
- });
94213
- }
94214
- return hits;
94215
- }
94216
-
94217
- // telegram-plugin/secret-detect/generic-entropy.ts
94218
- var CANDIDATE_RE = /[A-Za-z0-9]{28,}/g;
94219
- var GENERIC_MIN_DISTINCT = 18;
94220
- var MAX_GENERIC_HITS = 20;
94221
- function hasDistinctChars(tok, n) {
94222
- const seen = new Uint8Array(128);
94223
- let distinct = 0;
94224
- for (let i = 0;i < tok.length; i++) {
94225
- const c = tok.charCodeAt(i);
94226
- if (seen[c] === 0) {
94227
- seen[c] = 1;
94228
- if (++distinct >= n)
94229
- return true;
94230
- }
94231
- }
94232
- return false;
94233
- }
94234
- function hasDigit(tok) {
94235
- for (let i = 0;i < tok.length; i++) {
94236
- const c = tok.charCodeAt(i);
94237
- if (c >= 48 && c <= 57)
94238
- return true;
94239
- }
94240
- return false;
94241
- }
94242
- function scanGenericSecrets(text) {
94243
- const hits = [];
94244
- CANDIDATE_RE.lastIndex = 0;
94245
- let m;
94246
- while ((m = CANDIDATE_RE.exec(text)) !== null) {
94247
- if (hits.length >= MAX_GENERIC_HITS)
94248
- break;
94249
- const tok = m[0];
94250
- if (!hasDigit(tok))
94251
- continue;
94252
- if (!hasDistinctChars(tok, GENERIC_MIN_DISTINCT))
94253
- continue;
94254
- hits.push({
94255
- rule_id: "generic_high_entropy",
94256
- start: m.index,
94257
- end: m.index + tok.length,
94258
- matched_text: tok,
94259
- confidence: "ambiguous"
94260
- });
94261
- }
94262
- return hits;
94263
- }
94264
-
94265
- // telegram-plugin/secret-detect/chunker.ts
94266
- var CHUNK_THRESHOLD = 32 * 1024;
94267
- var WINDOW_SIZE = 16 * 1024;
94268
- var OVERLAP = 8 * 1024;
94269
- function chunk(text) {
94270
- if (text.length <= CHUNK_THRESHOLD) {
94271
- return [{ offset: 0, text }];
94272
- }
94273
- const out = [];
94274
- let offset = 0;
94275
- while (offset < text.length) {
94276
- const end = Math.min(offset + WINDOW_SIZE, text.length);
94277
- out.push({ offset, text: text.slice(offset, end) });
94278
- if (end >= text.length)
94279
- break;
94280
- offset = end - OVERLAP;
94281
- }
94282
- return out;
94283
- }
94284
-
94285
- // telegram-plugin/secret-detect/suppressor.ts
94286
- var MARKERS = ["test", "mock", "example", "fixture", "dummy"];
94287
- var WINDOW = 40;
94288
- var MARKER_RE = new RegExp(`\\b(?:${MARKERS.join("|")})\\b`, "i");
94289
- function isSuppressed(text, start, end) {
94290
- const left = Math.max(0, start - WINDOW);
94291
- const right = Math.min(text.length, end + WINDOW);
94292
- const context = text.slice(left, start) + text.slice(end, right);
94293
- return MARKER_RE.test(context);
94294
- }
94295
-
94296
- // telegram-plugin/secret-detect/slug.ts
94297
- function sanitizeKeyName(raw) {
94298
- const up = raw.toUpperCase();
94299
- const cleaned = up.replace(/[^A-Z0-9_]+/g, "_").replace(/_+/g, "_").replace(/^_+|_+$/g, "");
94300
- return cleaned.length > 0 ? cleaned : "SECRET";
94301
- }
94302
- function datePart(now = new Date) {
94303
- const y = now.getUTCFullYear();
94304
- const m = String(now.getUTCMonth() + 1).padStart(2, "0");
94305
- const d = String(now.getUTCDate()).padStart(2, "0");
94306
- return `${y}${m}${d}`;
94307
- }
94308
- function deriveSlug(inputs, existing) {
94309
- let base;
94310
- if (inputs.key_name && inputs.key_name.trim().length > 0) {
94311
- base = sanitizeKeyName(inputs.key_name);
94312
- } else {
94313
- base = `${inputs.rule_id}_${datePart(inputs.now)}`;
94314
- }
94315
- if (!existing.has(base))
94316
- return base;
94317
- let n = 2;
94318
- while (existing.has(`${base}_${n}`))
94319
- n++;
94320
- return `${base}_${n}`;
94321
- }
94322
- // telegram-plugin/secret-detect/url-redact.ts
94323
- var SENSITIVE_PARAMS = new Set([
94324
- "token",
94325
- "key",
94326
- "api_key",
94327
- "apikey",
94328
- "secret",
94329
- "access_token",
94330
- "password",
94331
- "pass",
94332
- "auth",
94333
- "client_secret",
94334
- "refresh_token",
94335
- "signature"
94336
- ]);
94337
- var URL_RE = /\b(?:https?|wss?|ftp):\/\/[^\s<>"']+/gi;
94338
- function redactUrls(text) {
94339
- return text.replace(URL_RE, (m) => {
94340
- const redacted = redactOne(m);
94341
- return redacted ?? m;
94342
- });
94343
- }
94344
- function redactOne(raw) {
94345
- let u;
94346
- try {
94347
- u = new URL(raw);
94348
- } catch {
94349
- return null;
94350
- }
94351
- let changed = false;
94352
- if (u.username || u.password) {
94353
- u.username = "***";
94354
- u.password = "";
94355
- changed = true;
94356
- }
94357
- for (const [key] of u.searchParams) {
94358
- if (SENSITIVE_PARAMS.has(key.toLowerCase())) {
94359
- u.searchParams.set(key, "***");
94360
- changed = true;
94361
- }
94362
- }
94363
- return changed ? u.toString() : null;
94364
- }
94365
-
94366
- // telegram-plugin/secret-detect/index.ts
94367
- function detectSecrets(text) {
94368
- if (!text || text.length === 0)
94369
- return [];
94370
- const windows = chunk(text);
94371
- const raw = [];
94372
- for (const win of windows) {
94373
- for (const p of ALL_PATTERNS) {
94374
- const re = new RegExp(p.regex.source, p.regex.flags.includes("g") ? p.regex.flags : p.regex.flags + "g");
94375
- let m;
94376
- while ((m = re.exec(win.text)) !== null) {
94377
- if (m[0].length === 0) {
94378
- re.lastIndex++;
94379
- continue;
94380
- }
94381
- const cap = p.captureIndex === 0 ? m[0] : m[p.captureIndex];
94382
- if (!cap)
94383
- continue;
94384
- const matchStart = p.captureIndex === 0 ? m.index : m.index + m[0].indexOf(cap);
94385
- if (matchStart < 0)
94386
- continue;
94387
- const globalStart = win.offset + matchStart;
94388
- const globalEnd = globalStart + cap.length;
94389
- const keyName = p.rule_id === "env_key_value" ? m[1] : undefined;
94390
- if (p.rule_id === "env_key_value") {
94391
- const ENV_KV_MIN_LEN = 12;
94392
- const ENV_KV_MIN_ENTROPY = 3.5;
94393
- if (cap.length < ENV_KV_MIN_LEN)
94394
- continue;
94395
- if (shannonEntropy(cap) < ENV_KV_MIN_ENTROPY)
94396
- continue;
94397
- }
94398
- raw.push({
94399
- rule_id: p.rule_id,
94400
- start: globalStart,
94401
- end: globalEnd,
94402
- matched_text: cap,
94403
- key_name: keyName,
94404
- confidence: "high"
94405
- });
94406
- }
94407
- }
94408
- const kvHits = scanKeyValue(win.text);
94409
- for (const h of kvHits) {
94410
- raw.push({ ...h, start: h.start + win.offset, end: h.end + win.offset });
94411
- }
94412
- const genHits = scanGenericSecrets(win.text);
94413
- for (const h of genHits) {
94414
- raw.push({ ...h, start: h.start + win.offset, end: h.end + win.offset });
94415
- }
94416
- }
94417
- const deduped = dedupeRaw(raw);
94418
- const final = dropOverlaps(deduped);
94419
- const existing = new Set;
94420
- const out = [];
94421
- for (const h of final) {
94422
- const suggested_slug = deriveSlug({ key_name: h.key_name, rule_id: h.rule_id }, existing);
94423
- existing.add(suggested_slug);
94424
- out.push({
94425
- rule_id: h.rule_id,
94426
- matched_text: h.matched_text,
94427
- start: h.start,
94428
- end: h.end,
94429
- confidence: h.confidence,
94430
- suppressed: isSuppressed(text, h.start, h.end),
94431
- suggested_slug,
94432
- key_name: h.key_name
94433
- });
94434
- }
94435
- out.sort((a, b) => a.start - b.start);
94436
- return out;
94437
- }
94438
- function dedupeRaw(raw) {
94439
- const seen = new Map;
94440
- for (const h of raw) {
94441
- const key = `${h.start}:${h.end}`;
94442
- const existing = seen.get(key);
94443
- if (!existing) {
94444
- seen.set(key, h);
94445
- continue;
94446
- }
94447
- if (existing.confidence === "ambiguous" && h.confidence === "high") {
94448
- seen.set(key, h);
94449
- }
94450
- }
94451
- return Array.from(seen.values());
94452
- }
94453
- function dropOverlaps(hits) {
94454
- const out = hits.filter((h) => !(h.confidence === "ambiguous" && hits.some((o) => o !== h && o.start <= h.start && o.end >= h.end && !(o.start === h.start && o.end === h.end))));
94455
- out.sort((a, b) => a.start - b.start || a.end - b.end);
94456
- return out;
94457
- }
94458
-
94459
- // telegram-plugin/secret-detect/redact.ts
94460
- var REDACTED_MARKER = "[REDACTED]";
94461
- function redact(text) {
94462
- if (!text || text.length === 0)
94463
- return text;
94464
- const urlScrubbed = redactUrls(text);
94465
- const hits = detectSecrets(urlScrubbed).filter((h) => h.rule_id !== "generic_high_entropy");
94466
- if (hits.length === 0)
94467
- return urlScrubbed;
94468
- const sorted = [...hits].sort((a, b) => b.start - a.start);
94469
- let out = urlScrubbed;
94470
- for (const h of sorted) {
94471
- out = out.slice(0, h.start) + redactedMarker(h.rule_id) + out.slice(h.end);
94472
- }
94473
- return out;
94474
- }
94475
- function redactedMarker(ruleId) {
94476
- const trimmed = ruleId.replace(/^(kv|env)_/, "");
94477
- if (!trimmed || trimmed === "key_value" || trimmed === "kv_entropy") {
94478
- return REDACTED_MARKER;
94479
- }
94480
- return `[REDACTED:${trimmed}]`;
94481
- }
94482
94679
  // src/issues/store.ts
94483
94680
  var ISSUES_FILE = "issues.jsonl";
94484
94681
  var ISSUES_LOCK = "issues.lock";
@@ -98428,6 +98625,42 @@ function withSynthesizedTools(result) {
98428
98625
  ]
98429
98626
  };
98430
98627
  }
98628
+ var READ_ONLY_TOOL_NAMES = new Set([
98629
+ "recall",
98630
+ "reflect",
98631
+ "search",
98632
+ "list_memories",
98633
+ "list_directives",
98634
+ "list_mental_models",
98635
+ "get_mental_model",
98636
+ "get_memory",
98637
+ "get_bank"
98638
+ ]);
98639
+ function redactToolArguments(value) {
98640
+ if (typeof value === "string")
98641
+ return redact(value);
98642
+ if (Array.isArray(value))
98643
+ return value.map(redactToolArguments);
98644
+ if (value && typeof value === "object") {
98645
+ const out = {};
98646
+ for (const [k, v] of Object.entries(value)) {
98647
+ out[k] = redactToolArguments(v);
98648
+ }
98649
+ return out;
98650
+ }
98651
+ return value;
98652
+ }
98653
+ function redactToolCallParams(params) {
98654
+ const called = params;
98655
+ if (!called || typeof called !== "object")
98656
+ return params;
98657
+ if (called.arguments === undefined || called.arguments === null)
98658
+ return params;
98659
+ if (typeof called.name === "string" && READ_ONLY_TOOL_NAMES.has(called.name)) {
98660
+ return params;
98661
+ }
98662
+ return { ...called, arguments: redactToolArguments(called.arguments) };
98663
+ }
98431
98664
  function buildFallbackToolsList() {
98432
98665
  const tools = Object.entries(FALLBACK_TOOL_TABLE).map(([name, [required, props]]) => ({
98433
98666
  name,
@@ -98754,8 +98987,9 @@ class HindsightShim {
98754
98987
  if (called?.name && SYNTHESIZED_TOOL_NAMES.includes(called.name)) {
98755
98988
  return this.synthesizedCall(called.name, called.arguments);
98756
98989
  }
98990
+ const forwarded = redactToolCallParams(params);
98757
98991
  try {
98758
- const res = await this.upstream.request("tools/call", params, this.opts.toolsCallTimeoutMs ?? TOOLS_CALL_TIMEOUT_MS);
98992
+ const res = await this.upstream.request("tools/call", forwarded, this.opts.toolsCallTimeoutMs ?? TOOLS_CALL_TIMEOUT_MS);
98759
98993
  this.notifyIfRecovered();
98760
98994
  if (res.error) {
98761
98995
  return {