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.
- package/dist/cli/skill-validate-pretool.mjs +15 -2
- package/dist/cli/switchroom.js +678 -444
- package/dist/host-control/main.js +149 -3
- package/package.json +4 -2
- package/profiles/_base/start.sh.hbs +37 -12
- package/telegram-plugin/bridge/bridge.ts +4 -4
- package/telegram-plugin/dist/bridge/bridge.js +4 -4
- package/telegram-plugin/dist/gateway/gateway.js +317 -95
- package/telegram-plugin/dist/server.js +4 -4
- package/telegram-plugin/format.ts +100 -29
- package/telegram-plugin/gateway/gateway.ts +22 -29
- package/telegram-plugin/gateway/ipc-server.ts +18 -15
- package/telegram-plugin/gateway/model-command.ts +34 -0
- package/telegram-plugin/gateway/outbound-send-path.ts +12 -1
- package/telegram-plugin/operator-events.ts +40 -16
- package/telegram-plugin/render/unsupported-token-guard.ts +10 -6
- package/telegram-plugin/secret-detect/db-uri.ts +90 -0
- package/telegram-plugin/secret-detect/index.ts +24 -1
- package/telegram-plugin/secret-detect/inert-values.ts +147 -0
- package/telegram-plugin/secret-detect/kv-scanner.ts +108 -0
- package/telegram-plugin/secret-detect/patterns.ts +24 -4
- package/telegram-plugin/tests/format-consistency.test.ts +111 -0
- package/telegram-plugin/tests/gateway-session-model-relaunch.test.ts +40 -2
- package/telegram-plugin/tests/ipc-server-validate-operator.test.ts +20 -11
- package/telegram-plugin/tests/mcp-instructions-budget.test.ts +8 -1
- package/telegram-plugin/tests/outbound-send-path.test.ts +1 -1
- package/telegram-plugin/tests/render/unsupported-token-guard.test.ts +36 -5
- package/telegram-plugin/tests/secret-detect-cross-engine.test.ts +263 -0
- package/telegram-plugin/tests/secret-detect-write-path.test.ts +403 -0
- package/telegram-plugin/tests/turn-flush-safety.test.ts +2 -2
- package/vendor/hindsight-memory/scripts/lib/client.py +58 -0
- package/vendor/hindsight-memory/scripts/lib/secret_patterns.json +431 -0
- package/vendor/hindsight-memory/scripts/lib/secret_redact.py +563 -0
- package/vendor/hindsight-memory/scripts/lib/secret_redaction_vectors.json +397 -0
- package/vendor/hindsight-memory/scripts/tests/test_secret_redact.py +522 -0
package/dist/cli/switchroom.js
CHANGED
|
@@ -2120,7 +2120,7 @@ var init_esm = __esm(() => {
|
|
|
2120
2120
|
});
|
|
2121
2121
|
|
|
2122
2122
|
// src/build-info.ts
|
|
2123
|
-
var VERSION = "0.19.
|
|
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
|
-
|
|
31359
|
-
|
|
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.
|
|
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", (
|
|
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(
|
|
35550
|
-
this.buffer +=
|
|
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
|
|
39529
|
-
if (
|
|
39531
|
+
const chunk2 = tailBytes(gen, budget, strict);
|
|
39532
|
+
if (chunk2.length === 0)
|
|
39530
39533
|
continue;
|
|
39531
|
-
chunks.push(
|
|
39532
|
-
budget -= Buffer.byteLength(
|
|
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", (
|
|
44455
|
-
buf +=
|
|
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", (
|
|
74352
|
-
buf +=
|
|
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 = (
|
|
74435
|
-
buf +=
|
|
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 = (
|
|
76467
|
-
const s =
|
|
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
|
|
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 ${
|
|
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", (
|
|
80658
|
-
buffer +=
|
|
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", (
|
|
81768
|
-
buffer +=
|
|
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", (
|
|
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", (
|
|
85488
|
-
captured +=
|
|
85489
|
-
process.stdout.write(
|
|
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(` ${
|
|
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", (
|
|
87899
|
-
buf +=
|
|
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",
|
|
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 {
|