switchroom 0.19.34 → 0.19.36
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/auth-broker/index.js +7 -1
- package/dist/cli/skill-validate-pretool.mjs +15 -2
- package/dist/cli/switchroom.js +1005 -482
- package/dist/host-control/main.js +156 -4
- package/dist/vault/approvals/kernel-server.js +7 -1
- package/dist/vault/broker/server.js +7 -1
- package/package.json +4 -2
- package/profiles/_base/start.sh.hbs +37 -12
- package/telegram-plugin/dist/gateway/gateway.js +476 -116
- package/telegram-plugin/format.ts +70 -15
- package/telegram-plugin/gateway/gateway.ts +27 -31
- package/telegram-plugin/gateway/ipc-server.ts +18 -15
- package/telegram-plugin/gateway/model-command.ts +279 -23
- package/telegram-plugin/gateway/outbound-send-path.ts +12 -1
- package/telegram-plugin/operator-events.ts +40 -16
- 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 +93 -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/model-command.test.ts +415 -4
- package/telegram-plugin/tests/outbound-send-path.test.ts +1 -1
- 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/subagent_retain.py +103 -7
- package/vendor/hindsight-memory/scripts/tests/test_secret_redact.py +522 -0
- package/vendor/hindsight-memory/scripts/tests/test_subagent_retain_learnings.py +377 -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.36", COMMIT_SHA = "ba505b15";
|
|
2124
2124
|
|
|
2125
2125
|
// src/cli/resolve-version.ts
|
|
2126
2126
|
import { existsSync, readFileSync } from "node:fs";
|
|
@@ -15552,16 +15552,56 @@ var init_users = __esm(() => {
|
|
|
15552
15552
|
init_merge();
|
|
15553
15553
|
});
|
|
15554
15554
|
|
|
15555
|
+
// src/config/thinking-effort-risk.ts
|
|
15556
|
+
function isRiskyThinkingEffort(effort) {
|
|
15557
|
+
if (!effort)
|
|
15558
|
+
return false;
|
|
15559
|
+
return RISKY_EFFORTS.has(effort.trim().toLowerCase());
|
|
15560
|
+
}
|
|
15561
|
+
function emitsAdaptiveThinking(model) {
|
|
15562
|
+
if (!model)
|
|
15563
|
+
return false;
|
|
15564
|
+
const m = model.trim().toLowerCase();
|
|
15565
|
+
return m === "opus" || m.startsWith("claude-opus-");
|
|
15566
|
+
}
|
|
15567
|
+
function isAdaptiveThinkingOpus(model) {
|
|
15568
|
+
if (!model)
|
|
15569
|
+
return false;
|
|
15570
|
+
const m = model.trim().toLowerCase();
|
|
15571
|
+
return m.startsWith("claude-opus-4");
|
|
15572
|
+
}
|
|
15573
|
+
function assessThinkingEffortRisk(model, effort) {
|
|
15574
|
+
if (!isRiskyThinkingEffort(effort))
|
|
15575
|
+
return { risky: false };
|
|
15576
|
+
if (!isAdaptiveThinkingOpus(model))
|
|
15577
|
+
return { risky: false };
|
|
15578
|
+
return {
|
|
15579
|
+
risky: true,
|
|
15580
|
+
reason: `thinking_effort '${effort}' on pinned Opus 4.x model '${model}' could trigger ` + `'400 thinking/redacted_thinking blocks cannot be modified' errors when work runs ` + `through concurrent sub-agents (issue #1978). The upstream claude-CLI fix shipped in ` + `${CLAUDE_CLI_THINKING_MERGE_FIX_VERSION}, but the Opus 4.x reproduction has not been ` + `re-tested since, so pin 'thinking_effort: low' or move the agent to a current Opus model.`
|
|
15581
|
+
};
|
|
15582
|
+
}
|
|
15583
|
+
var CLAUDE_CLI_THINKING_MERGE_FIX_VERSION = "2.1.156", RISKY_EFFORTS;
|
|
15584
|
+
var init_thinking_effort_risk = __esm(() => {
|
|
15585
|
+
RISKY_EFFORTS = new Set(["medium", "high", "xhigh", "max"]);
|
|
15586
|
+
});
|
|
15587
|
+
|
|
15555
15588
|
// telegram-plugin/gateway/model-command.ts
|
|
15556
15589
|
function isClaudeModel(name) {
|
|
15557
15590
|
const lower = name.toLowerCase();
|
|
15558
15591
|
if (MODEL_ALIASES.includes(lower))
|
|
15559
15592
|
return true;
|
|
15593
|
+
if (lower in CLAUDE_MODEL_ALIASES)
|
|
15594
|
+
return true;
|
|
15560
15595
|
return lower.startsWith("claude-");
|
|
15561
15596
|
}
|
|
15562
|
-
var MODEL_ALIASES;
|
|
15597
|
+
var MODEL_ALIASES, CLAUDE_MODEL_ALIASES;
|
|
15563
15598
|
var init_model_command = __esm(() => {
|
|
15599
|
+
init_thinking_effort_risk();
|
|
15564
15600
|
MODEL_ALIASES = ["opus", "sonnet", "haiku", "fable", "default"];
|
|
15601
|
+
CLAUDE_MODEL_ALIASES = {
|
|
15602
|
+
opus48: "claude-opus-4-8",
|
|
15603
|
+
"opus-4-8": "claude-opus-4-8"
|
|
15604
|
+
};
|
|
15565
15605
|
});
|
|
15566
15606
|
|
|
15567
15607
|
// src/litellm/timeout-budget.ts
|
|
@@ -15790,7 +15830,7 @@ function hindsightPgEnv(overrides = new Map) {
|
|
|
15790
15830
|
}
|
|
15791
15831
|
return out;
|
|
15792
15832
|
}
|
|
15793
|
-
var HINDSIGHT_PG_MEM_LIMIT_MIB_FOR_DERIVATION, HINDSIGHT_PG_APP_ANON_MIB = 2560, HINDSIGHT_PG_PAGE_CACHE_FLOOR_MIB = 2048, HINDSIGHT_PG_SHARED_BUFFERS_BUDGET_MIB, HINDSIGHT_PG_DEFAULT_SHARED_BUFFERS_MIB = 3072, HINDSIGHT_PG_DEFAULT_EFFECTIVE_CACHE_SIZE_MIB = 7168, HINDSIGHT_PG_EFFECTIVE_CACHE_SIZE_ENV = "SWITCHROOM_HINDSIGHT_PG_EFFECTIVE_CACHE_SIZE", HINDSIGHT_PG_SHARED_BUFFERS_ENV = "SWITCHROOM_HINDSIGHT_PG_SHARED_BUFFERS", HINDSIGHT_PG_DEFAULTS, HINDSIGHT_PG_ENV_KEYS;
|
|
15833
|
+
var HINDSIGHT_PG_MEM_LIMIT_MIB_FOR_DERIVATION, HINDSIGHT_PG_APP_ANON_MIB = 2560, HINDSIGHT_PG_PAGE_CACHE_FLOOR_MIB = 2048, HINDSIGHT_PG_SHARED_BUFFERS_BUDGET_MIB, HINDSIGHT_PG_DEFAULT_SHARED_BUFFERS_MIB = 3072, HINDSIGHT_PG_DEFAULT_EFFECTIVE_CACHE_SIZE_MIB = 7168, HINDSIGHT_PG_EFFECTIVE_CACHE_SIZE_ENV = "SWITCHROOM_HINDSIGHT_PG_EFFECTIVE_CACHE_SIZE", HINDSIGHT_PG_SHARED_BUFFERS_ENV = "SWITCHROOM_HINDSIGHT_PG_SHARED_BUFFERS", HINDSIGHT_PG_FSYNC_ENV = "SWITCHROOM_HINDSIGHT_PG_FSYNC", HINDSIGHT_PG_DEFAULT_FSYNC = "on", HINDSIGHT_PG_DEFAULTS, HINDSIGHT_PG_ENV_KEYS;
|
|
15794
15834
|
var init_hindsight_pg_defaults = __esm(() => {
|
|
15795
15835
|
HINDSIGHT_PG_MEM_LIMIT_MIB_FOR_DERIVATION = 16 * 1024;
|
|
15796
15836
|
HINDSIGHT_PG_SHARED_BUFFERS_BUDGET_MIB = HINDSIGHT_PG_MEM_LIMIT_MIB_FOR_DERIVATION - HINDSIGHT_PG_APP_ANON_MIB - HINDSIGHT_PG_PAGE_CACHE_FLOOR_MIB;
|
|
@@ -15799,7 +15839,8 @@ var init_hindsight_pg_defaults = __esm(() => {
|
|
|
15799
15839
|
HINDSIGHT_PG_EFFECTIVE_CACHE_SIZE_ENV,
|
|
15800
15840
|
pgMib(HINDSIGHT_PG_DEFAULT_EFFECTIVE_CACHE_SIZE_MIB)
|
|
15801
15841
|
],
|
|
15802
|
-
[HINDSIGHT_PG_SHARED_BUFFERS_ENV, pgMib(HINDSIGHT_PG_DEFAULT_SHARED_BUFFERS_MIB)]
|
|
15842
|
+
[HINDSIGHT_PG_SHARED_BUFFERS_ENV, pgMib(HINDSIGHT_PG_DEFAULT_SHARED_BUFFERS_MIB)],
|
|
15843
|
+
[HINDSIGHT_PG_FSYNC_ENV, HINDSIGHT_PG_DEFAULT_FSYNC]
|
|
15803
15844
|
];
|
|
15804
15845
|
HINDSIGHT_PG_ENV_KEYS = new Set(HINDSIGHT_PG_DEFAULTS.map(([k]) => k));
|
|
15805
15846
|
});
|
|
@@ -35491,7 +35532,7 @@ class AuthBrokerClient {
|
|
|
35491
35532
|
sock.once("error", onError);
|
|
35492
35533
|
sock.once("connect", () => {
|
|
35493
35534
|
sock.removeListener("error", onError);
|
|
35494
|
-
sock.on("data", (
|
|
35535
|
+
sock.on("data", (chunk2) => this.onData(chunk2));
|
|
35495
35536
|
sock.on("error", (err) => this.onSocketError(err));
|
|
35496
35537
|
sock.on("close", () => this.onSocketClose());
|
|
35497
35538
|
this.socket = sock;
|
|
@@ -35505,8 +35546,8 @@ class AuthBrokerClient {
|
|
|
35505
35546
|
this.connecting = null;
|
|
35506
35547
|
}
|
|
35507
35548
|
}
|
|
35508
|
-
onData(
|
|
35509
|
-
this.buffer +=
|
|
35549
|
+
onData(chunk2) {
|
|
35550
|
+
this.buffer += chunk2.toString("utf8");
|
|
35510
35551
|
let idx;
|
|
35511
35552
|
while ((idx = this.buffer.indexOf(`
|
|
35512
35553
|
`)) !== -1) {
|
|
@@ -39484,11 +39525,11 @@ function readAuditRaw(logPath, opts = {}) {
|
|
|
39484
39525
|
const gen = `${logPath}.${n}`;
|
|
39485
39526
|
if (!existsSync51(gen))
|
|
39486
39527
|
break;
|
|
39487
|
-
const
|
|
39488
|
-
if (
|
|
39528
|
+
const chunk2 = tailBytes(gen, budget, strict);
|
|
39529
|
+
if (chunk2.length === 0)
|
|
39489
39530
|
continue;
|
|
39490
|
-
chunks.push(
|
|
39491
|
-
budget -= Buffer.byteLength(
|
|
39531
|
+
chunks.push(chunk2);
|
|
39532
|
+
budget -= Buffer.byteLength(chunk2, "utf-8");
|
|
39492
39533
|
}
|
|
39493
39534
|
return chunks.reverse().filter((c) => c.length > 0).map((c) => c.endsWith(`
|
|
39494
39535
|
`) ? c : c + `
|
|
@@ -44410,8 +44451,8 @@ async function hostdRequest(opts, req) {
|
|
|
44410
44451
|
reject(err);
|
|
44411
44452
|
}
|
|
44412
44453
|
});
|
|
44413
|
-
socket.on("data", (
|
|
44414
|
-
buf +=
|
|
44454
|
+
socket.on("data", (chunk2) => {
|
|
44455
|
+
buf += chunk2.toString("utf8");
|
|
44415
44456
|
if (Buffer.byteLength(buf, "utf8") > MAX_FRAME_BYTES3 * 2) {
|
|
44416
44457
|
if (settled)
|
|
44417
44458
|
return;
|
|
@@ -44826,39 +44867,6 @@ var init_doctor_status = __esm(() => {
|
|
|
44826
44867
|
init_source();
|
|
44827
44868
|
});
|
|
44828
44869
|
|
|
44829
|
-
// src/config/thinking-effort-risk.ts
|
|
44830
|
-
function isRiskyThinkingEffort(effort) {
|
|
44831
|
-
if (!effort)
|
|
44832
|
-
return false;
|
|
44833
|
-
return RISKY_EFFORTS.has(effort.trim().toLowerCase());
|
|
44834
|
-
}
|
|
44835
|
-
function emitsAdaptiveThinking(model) {
|
|
44836
|
-
if (!model)
|
|
44837
|
-
return false;
|
|
44838
|
-
const m = model.trim().toLowerCase();
|
|
44839
|
-
return m === "opus" || m.startsWith("claude-opus-");
|
|
44840
|
-
}
|
|
44841
|
-
function isAdaptiveThinkingOpus(model) {
|
|
44842
|
-
if (!model)
|
|
44843
|
-
return false;
|
|
44844
|
-
const m = model.trim().toLowerCase();
|
|
44845
|
-
return m.startsWith("claude-opus-4");
|
|
44846
|
-
}
|
|
44847
|
-
function assessThinkingEffortRisk(model, effort) {
|
|
44848
|
-
if (!isRiskyThinkingEffort(effort))
|
|
44849
|
-
return { risky: false };
|
|
44850
|
-
if (!isAdaptiveThinkingOpus(model))
|
|
44851
|
-
return { risky: false };
|
|
44852
|
-
return {
|
|
44853
|
-
risky: true,
|
|
44854
|
-
reason: `thinking_effort '${effort}' on pinned Opus 4.x model '${model}' could trigger ` + `'400 thinking/redacted_thinking blocks cannot be modified' errors when work runs ` + `through concurrent sub-agents (issue #1978). The upstream claude-CLI fix shipped in ` + `${CLAUDE_CLI_THINKING_MERGE_FIX_VERSION}, but the Opus 4.x reproduction has not been ` + `re-tested since, so pin 'thinking_effort: low' or move the agent to a current Opus model.`
|
|
44855
|
-
};
|
|
44856
|
-
}
|
|
44857
|
-
var CLAUDE_CLI_THINKING_MERGE_FIX_VERSION = "2.1.156", RISKY_EFFORTS;
|
|
44858
|
-
var init_thinking_effort_risk = __esm(() => {
|
|
44859
|
-
RISKY_EFFORTS = new Set(["medium", "high", "xhigh", "max"]);
|
|
44860
|
-
});
|
|
44861
|
-
|
|
44862
44870
|
// src/manifest.ts
|
|
44863
44871
|
import {
|
|
44864
44872
|
existsSync as existsSync61,
|
|
@@ -45983,7 +45991,7 @@ var init_install_cron = () => {};
|
|
|
45983
45991
|
function inParts(memories) {
|
|
45984
45992
|
return Math.round(memories * PARTS_PER_MEMORY);
|
|
45985
45993
|
}
|
|
45986
|
-
var RING_MAX = 8, MAX_SAMPLE_AGE_MS, MIN_RETAIN_SAMPLES = 20, RETAIN_FAILURE_RATE = 0.1, PARTS_PER_MEMORY = 6, QUEUE_FLOOR_MEMORIES = 100, QUEUE_FLOOR, QUEUE_GROWTH_FRACTION = 0.1, QUEUE_GROWTH_MIN_ABS_MEMORIES = 20, QUEUE_GROWTH_MIN_ABS, BREACHES_TO_FIRE_LEVEL = 2, BREACHES_TO_FIRE_EDGE = 1, CLEARS_TO_RESOLVE = 2, RENOTIFY_MS, DEFAULT_METRICS_URL = "http://127.0.0.1:18888/metrics", DEFAULT_CONTAINER = "switchroom-hindsight", METRICS_MAX_BYTES, METRICS_TIMEOUT_MS = 1e4, RECALL_OWN_BANK_TIMEOUT_WARN = 0.05, RECALL_OWN_BANK_TIMEOUT_PAGE = 0.15, RECALL_ZERO_MEMORY_WARN = 0.25, RECALL_ZERO_MEMORY_PAGE = 0.4, RECALL_POOL_MEDIAN_WARN = 8, RECALL_POOL_MEDIAN_PAGE = 3, RECALL_SCORE_P50_WARN = 0.02, RECALL_SCORE_P50_PAGE = 0.01, CONSOLIDATION_AGE_WARN_S, CONSOLIDATION_AGE_PAGE_S, RECALL_MIN_SAMPLES = 30, LLM_FAILURE_RATE_WARN = 0.02, LLM_FAILURE_RATE_PAGE = 0.1, LLM_MIN_SAMPLES = 20;
|
|
45994
|
+
var RING_MAX = 8, MAX_SAMPLE_AGE_MS, MIN_RETAIN_SAMPLES = 20, RETAIN_FAILURE_RATE = 0.1, PARTS_PER_MEMORY = 6, QUEUE_FLOOR_MEMORIES = 100, QUEUE_FLOOR, QUEUE_GROWTH_FRACTION = 0.1, QUEUE_GROWTH_MIN_ABS_MEMORIES = 20, QUEUE_GROWTH_MIN_ABS, BREACHES_TO_FIRE_LEVEL = 2, BREACHES_TO_FIRE_EDGE = 1, CLEARS_TO_RESOLVE = 2, RENOTIFY_MS, DEFAULT_METRICS_URL = "http://127.0.0.1:18888/metrics", DEFAULT_CONTAINER = "switchroom-hindsight", METRICS_MAX_BYTES, METRICS_TIMEOUT_MS = 1e4, RECALL_OWN_BANK_TIMEOUT_WARN = 0.05, RECALL_OWN_BANK_TIMEOUT_PAGE = 0.15, RECALL_ZERO_MEMORY_WARN = 0.25, RECALL_ZERO_MEMORY_PAGE = 0.4, RECALL_POOL_MEDIAN_WARN = 8, RECALL_POOL_MEDIAN_PAGE = 3, RECALL_SCORE_P50_WARN = 0.02, RECALL_SCORE_P50_PAGE = 0.01, CONSOLIDATION_AGE_WARN_S, CONSOLIDATION_AGE_PAGE_S, CONSOLIDATION_FAILURE_STREAK_WARN = 3, CONSOLIDATION_FAILURE_STREAK_PAGE = 10, CONSOLIDATION_STREAK_RECENCY_S, PENDING_CONSOLIDATION_FLOOR = 500, PENDING_CONSOLIDATION_GROWTH_FRACTION = 0.5, PENDING_CONSOLIDATION_GROWTH_WARN_ABS = 500, PENDING_CONSOLIDATION_GROWTH_PAGE_ABS = 5000, VECTOR_INDEX_SAMPLE_ROWS = 3, RECALL_MIN_SAMPLES = 30, LLM_FAILURE_RATE_WARN = 0.02, LLM_FAILURE_RATE_PAGE = 0.1, LLM_MIN_SAMPLES = 20;
|
|
45987
45995
|
var init_thresholds = __esm(() => {
|
|
45988
45996
|
MAX_SAMPLE_AGE_MS = 3 * 60 * 60000;
|
|
45989
45997
|
QUEUE_FLOOR = inParts(QUEUE_FLOOR_MEMORIES);
|
|
@@ -45992,6 +46000,7 @@ var init_thresholds = __esm(() => {
|
|
|
45992
46000
|
METRICS_MAX_BYTES = 16 * 1024 * 1024;
|
|
45993
46001
|
CONSOLIDATION_AGE_WARN_S = 2 * 60 * 60;
|
|
45994
46002
|
CONSOLIDATION_AGE_PAGE_S = 12 * 60 * 60;
|
|
46003
|
+
CONSOLIDATION_STREAK_RECENCY_S = 2 * 60 * 60;
|
|
45995
46004
|
});
|
|
45996
46005
|
|
|
45997
46006
|
// src/hindsight-watch/types.ts
|
|
@@ -46022,7 +46031,9 @@ function loadState(path6) {
|
|
|
46022
46031
|
const ring = s.ring.filter(isSample).map((sample) => ({
|
|
46023
46032
|
...sample,
|
|
46024
46033
|
recall: normalizeRecallSample(sample.recall),
|
|
46025
|
-
consolidation: normalizeConsolidationSample(sample.consolidation)
|
|
46034
|
+
consolidation: normalizeConsolidationSample(sample.consolidation),
|
|
46035
|
+
banks: normalizeBankSample(sample.banks),
|
|
46036
|
+
vectorIndex: normalizeVectorIndexSample(sample.vectorIndex)
|
|
46026
46037
|
})).slice(-RING_MAX);
|
|
46027
46038
|
const signals = typeof s.signals === "object" && s.signals !== null ? s.signals : {};
|
|
46028
46039
|
return { v: 1, ring, signals };
|
|
@@ -46066,6 +46077,36 @@ function normalizeConsolidationSample(x) {
|
|
|
46066
46077
|
return null;
|
|
46067
46078
|
return x;
|
|
46068
46079
|
}
|
|
46080
|
+
function normalizeBankSample(x) {
|
|
46081
|
+
if (x === null || typeof x !== "object")
|
|
46082
|
+
return null;
|
|
46083
|
+
const b = x;
|
|
46084
|
+
if (!Array.isArray(b.streaks) || !Array.isArray(b.pending))
|
|
46085
|
+
return null;
|
|
46086
|
+
const streaks = b.streaks.filter((r) => {
|
|
46087
|
+
if (typeof r !== "object" || r === null)
|
|
46088
|
+
return false;
|
|
46089
|
+
const s = r;
|
|
46090
|
+
return typeof s.bank === "string" && typeof s.operationType === "string" && isCount(s.streak) && isCount(s.newestFailureAgeS);
|
|
46091
|
+
});
|
|
46092
|
+
const pending = b.pending.filter((r) => {
|
|
46093
|
+
if (typeof r !== "object" || r === null)
|
|
46094
|
+
return false;
|
|
46095
|
+
const p = r;
|
|
46096
|
+
return typeof p.bank === "string" && isCount(p.pending);
|
|
46097
|
+
});
|
|
46098
|
+
return { streaks, pending };
|
|
46099
|
+
}
|
|
46100
|
+
function normalizeVectorIndexSample(x) {
|
|
46101
|
+
if (x === null || typeof x !== "object")
|
|
46102
|
+
return null;
|
|
46103
|
+
const v = x;
|
|
46104
|
+
if (!isCount(v.probed))
|
|
46105
|
+
return null;
|
|
46106
|
+
if (!Array.isArray(v.corrupt) || !v.corrupt.every((s) => typeof s === "string"))
|
|
46107
|
+
return null;
|
|
46108
|
+
return { probed: v.probed, corrupt: v.corrupt };
|
|
46109
|
+
}
|
|
46069
46110
|
function isSample(x) {
|
|
46070
46111
|
if (typeof x !== "object" || x === null)
|
|
46071
46112
|
return false;
|
|
@@ -71102,9 +71143,600 @@ import { execFileSync as execFileSync11 } from "node:child_process";
|
|
|
71102
71143
|
|
|
71103
71144
|
// src/agents/handoff-summarizer.ts
|
|
71104
71145
|
init_atomic();
|
|
71105
|
-
init_observation_scopes();
|
|
71106
71146
|
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";
|
|
71107
71147
|
import { join as join18 } from "node:path";
|
|
71148
|
+
|
|
71149
|
+
// telegram-plugin/secret-detect/patterns.ts
|
|
71150
|
+
var ANCHORED_PATTERNS = [
|
|
71151
|
+
{ rule_id: "anthropic_api_key", regex: /\b(sk-ant-[A-Za-z0-9_-]{8,})\b/g, captureIndex: 1, slugHint: "anthropic_api_key" },
|
|
71152
|
+
{ 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" },
|
|
71153
|
+
{ rule_id: "openai_api_key", regex: /\b(sk-[A-Za-z0-9_-]{20,})\b/g, captureIndex: 1, slugHint: "openai_api_key" },
|
|
71154
|
+
{ rule_id: "github_pat_classic", regex: /\b(ghp_[A-Za-z0-9]{20,})\b/g, captureIndex: 1, slugHint: "github_pat" },
|
|
71155
|
+
{ rule_id: "github_pat_fine_grained", regex: /\b(github_pat_[A-Za-z0-9_]{20,})\b/g, captureIndex: 1, slugHint: "github_pat" },
|
|
71156
|
+
{ rule_id: "slack_token", regex: /\b(xox[baprs]-[A-Za-z0-9-]{10,})\b/g, captureIndex: 1, slugHint: "slack_token" },
|
|
71157
|
+
{ rule_id: "slack_app_token", regex: /\b(xapp-[A-Za-z0-9-]{10,})\b/g, captureIndex: 1, slugHint: "slack_app_token" },
|
|
71158
|
+
{ rule_id: "groq_api_key", regex: /\b(gsk_[A-Za-z0-9_-]{10,})\b/g, captureIndex: 1, slugHint: "groq_api_key" },
|
|
71159
|
+
{ rule_id: "google_api_key", regex: /\b(AIza[0-9A-Za-z\-_]{20,})\b/g, captureIndex: 1, slugHint: "google_api_key" },
|
|
71160
|
+
{ rule_id: "perplexity_api_key", regex: /\b(pplx-[A-Za-z0-9_-]{10,})\b/g, captureIndex: 1, slugHint: "perplexity_api_key" },
|
|
71161
|
+
{ rule_id: "npm_token", regex: /\b(npm_[A-Za-z0-9]{10,})\b/g, captureIndex: 1, slugHint: "npm_token" },
|
|
71162
|
+
{ rule_id: "telegram_bot_token_prefixed", regex: /\bbot(\d{6,}:[A-Za-z0-9_-]{20,})\b/g, captureIndex: 1, slugHint: "telegram_bot_token" },
|
|
71163
|
+
{ rule_id: "telegram_bot_token", regex: /\b(\d{6,}:[A-Za-z0-9_-]{20,})\b/g, captureIndex: 1, slugHint: "telegram_bot_token" },
|
|
71164
|
+
{ rule_id: "laravel_sanctum_token", regex: /\b(\d+\|[A-Za-z0-9]{40,})\b/g, captureIndex: 1, slugHint: "api_token" },
|
|
71165
|
+
{ rule_id: "aws_access_key", regex: /\b(AKIA[0-9A-Z]{16})\b/g, captureIndex: 1, slugHint: "aws_access_key" },
|
|
71166
|
+
{ 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" }
|
|
71167
|
+
];
|
|
71168
|
+
var STRUCTURED_PATTERNS = [
|
|
71169
|
+
{
|
|
71170
|
+
rule_id: "env_key_value",
|
|
71171
|
+
regex: /\b([A-Z0-9_]*(?:KEY|TOKEN|SECRET|PASSWORD|PASSWD))\b\s*[=:]\s*(["']?)([^\s"'\\]+)\2/g,
|
|
71172
|
+
captureIndex: 3,
|
|
71173
|
+
slugHint: "env"
|
|
71174
|
+
},
|
|
71175
|
+
{
|
|
71176
|
+
rule_id: "json_secret_field",
|
|
71177
|
+
regex: /"(?:apiKey|token|secret|password|passwd|accessToken|refreshToken)"\s*:\s*"([^"]+)"/g,
|
|
71178
|
+
captureIndex: 1,
|
|
71179
|
+
slugHint: "json_secret"
|
|
71180
|
+
},
|
|
71181
|
+
{
|
|
71182
|
+
rule_id: "cli_flag",
|
|
71183
|
+
regex: /--(?:api[-_]?key|hook[-_]?token|token|secret|password|passwd)\s+(["']?)([^\s"']+)\1/g,
|
|
71184
|
+
captureIndex: 2,
|
|
71185
|
+
slugHint: "cli_flag"
|
|
71186
|
+
},
|
|
71187
|
+
{
|
|
71188
|
+
rule_id: "bearer_auth_header",
|
|
71189
|
+
regex: /Authorization\s*[:=]\s*Bearer\s+([A-Za-z0-9._\-+=]+)/gi,
|
|
71190
|
+
captureIndex: 1,
|
|
71191
|
+
slugHint: "bearer_token"
|
|
71192
|
+
},
|
|
71193
|
+
{
|
|
71194
|
+
rule_id: "bearer_loose",
|
|
71195
|
+
regex: /\bBearer\s+([A-Za-z0-9._\-+=]{18,})\b/gi,
|
|
71196
|
+
captureIndex: 1,
|
|
71197
|
+
slugHint: "bearer_token"
|
|
71198
|
+
},
|
|
71199
|
+
{
|
|
71200
|
+
rule_id: "basic_auth_header",
|
|
71201
|
+
regex: /Authorization\s*[:=]\s*Basic\s+([A-Za-z0-9+/=]{8,})/gi,
|
|
71202
|
+
captureIndex: 1,
|
|
71203
|
+
slugHint: "basic_auth"
|
|
71204
|
+
},
|
|
71205
|
+
{
|
|
71206
|
+
rule_id: "pem_private_key",
|
|
71207
|
+
regex: /-----BEGIN [A-Z ]*PRIVATE KEY-----[\s\S]+?-----END [A-Z ]*PRIVATE KEY-----/g,
|
|
71208
|
+
captureIndex: 0,
|
|
71209
|
+
slugHint: "pem_private_key"
|
|
71210
|
+
}
|
|
71211
|
+
];
|
|
71212
|
+
var PROVIDER_PATTERNS = [
|
|
71213
|
+
{ rule_id: "slack_webhook", regex: /(https:\/\/hooks\.slack\.com\/services\/[A-Za-z0-9_/]+)/g, captureIndex: 1, slugHint: "slack_webhook" },
|
|
71214
|
+
{ rule_id: "stripe_live_secret", regex: /\b(sk_live_[A-Za-z0-9]{24,})\b/g, captureIndex: 1, slugHint: "stripe_key" },
|
|
71215
|
+
{ rule_id: "stripe_restricted", regex: /\b(rk_live_[A-Za-z0-9]{24,})\b/g, captureIndex: 1, slugHint: "stripe_key" },
|
|
71216
|
+
{ 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" },
|
|
71217
|
+
{ rule_id: "gitlab_pat", regex: /\b(glpat-[A-Za-z0-9_-]{20})\b/g, captureIndex: 1, slugHint: "gitlab_pat" },
|
|
71218
|
+
{ rule_id: "huggingface_token", regex: /\b(hf_[A-Za-z0-9]{34,})\b/g, captureIndex: 1, slugHint: "huggingface_token" },
|
|
71219
|
+
{ rule_id: "twilio_api_key", regex: /\b(SK[0-9a-f]{32})\b/g, captureIndex: 1, slugHint: "twilio_api_key" },
|
|
71220
|
+
{ rule_id: "mailgun_key", regex: /\b(key-[0-9a-f]{32})\b/g, captureIndex: 1, slugHint: "mailgun_key" },
|
|
71221
|
+
{ rule_id: "digitalocean_pat", regex: /\b(dop_v1_[a-f0-9]{64})\b/g, captureIndex: 1, slugHint: "digitalocean_token" },
|
|
71222
|
+
{ rule_id: "digitalocean_oauth", regex: /\b(doo_v1_[a-f0-9]{64})\b/g, captureIndex: 1, slugHint: "digitalocean_token" },
|
|
71223
|
+
{ rule_id: "digitalocean_refresh", regex: /\b(dor_v1_[a-f0-9]{64})\b/g, captureIndex: 1, slugHint: "digitalocean_token" },
|
|
71224
|
+
{ 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" },
|
|
71225
|
+
{ rule_id: "linear_api_key", regex: /\b(lin_api_[A-Za-z0-9]{40})\b/g, captureIndex: 1, slugHint: "linear_api_key" },
|
|
71226
|
+
{ rule_id: "shopify_access_token", regex: /\b(shpat_[a-fA-F0-9]{32})\b/g, captureIndex: 1, slugHint: "shopify_token" },
|
|
71227
|
+
{ rule_id: "shopify_shared_secret", regex: /\b(shpss_[a-fA-F0-9]{32})\b/g, captureIndex: 1, slugHint: "shopify_token" },
|
|
71228
|
+
{ rule_id: "shopify_private_app", regex: /\b(shppa_[a-fA-F0-9]{32})\b/g, captureIndex: 1, slugHint: "shopify_token" },
|
|
71229
|
+
{ rule_id: "square_access_token", regex: /\b(sq0atp-[A-Za-z0-9_-]{22})\b/g, captureIndex: 1, slugHint: "square_token" },
|
|
71230
|
+
{ rule_id: "square_oauth_secret", regex: /\b(sq0csp-[A-Za-z0-9_-]{43})\b/g, captureIndex: 1, slugHint: "square_token" },
|
|
71231
|
+
{ rule_id: "newrelic_key", regex: /\b(NRAK-[A-Z0-9]{27})\b/g, captureIndex: 1, slugHint: "newrelic_key" },
|
|
71232
|
+
{ rule_id: "notion_token", regex: /\b(ntn_[A-Za-z0-9]{46})\b/g, captureIndex: 1, slugHint: "notion_token" },
|
|
71233
|
+
{ rule_id: "planetscale_password", regex: /\b(pscale_pw_[A-Za-z0-9_.-]{43})\b/g, captureIndex: 1, slugHint: "planetscale_token" },
|
|
71234
|
+
{ rule_id: "planetscale_token", regex: /\b(pscale_tkn_[A-Za-z0-9_.-]{43})\b/g, captureIndex: 1, slugHint: "planetscale_token" },
|
|
71235
|
+
{ rule_id: "supabase_service_key", regex: /\b(sbp_[a-f0-9]{40})\b/g, captureIndex: 1, slugHint: "supabase_key" },
|
|
71236
|
+
{ rule_id: "atlassian_token", regex: /\b(ATATT[A-Za-z0-9_\-=]{20,})\b/g, captureIndex: 1, slugHint: "atlassian_token" },
|
|
71237
|
+
{ rule_id: "dropbox_token", regex: /\b(sl\.[A-Za-z0-9_-]{130,})/g, captureIndex: 1, slugHint: "dropbox_token" },
|
|
71238
|
+
{ rule_id: "databricks_token", regex: /\b(dapi[a-f0-9]{32})\b/g, captureIndex: 1, slugHint: "databricks_token" },
|
|
71239
|
+
{ 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" },
|
|
71240
|
+
{ rule_id: "pypi_token", regex: /\b(pypi-AgEIcHlwaS[A-Za-z0-9_-]{50,})/g, captureIndex: 1, slugHint: "pypi_token" },
|
|
71241
|
+
{ rule_id: "aws_temp_access_key", regex: /\b(ASIA[0-9A-Z]{16})\b/g, captureIndex: 1, slugHint: "aws_access_key" },
|
|
71242
|
+
{ rule_id: "gcp_oauth_token", regex: /\b(ya29\.[A-Za-z0-9_-]{30,})/g, captureIndex: 1, slugHint: "gcp_oauth_token" }
|
|
71243
|
+
];
|
|
71244
|
+
var ALL_PATTERNS = [...ANCHORED_PATTERNS, ...PROVIDER_PATTERNS, ...STRUCTURED_PATTERNS];
|
|
71245
|
+
|
|
71246
|
+
// telegram-plugin/secret-detect/entropy.ts
|
|
71247
|
+
function shannonEntropy(s) {
|
|
71248
|
+
if (s.length === 0)
|
|
71249
|
+
return 0;
|
|
71250
|
+
const counts = new Map;
|
|
71251
|
+
for (const ch of s) {
|
|
71252
|
+
counts.set(ch, (counts.get(ch) ?? 0) + 1);
|
|
71253
|
+
}
|
|
71254
|
+
let h = 0;
|
|
71255
|
+
const len = s.length;
|
|
71256
|
+
for (const c of counts.values()) {
|
|
71257
|
+
const p = c / len;
|
|
71258
|
+
h -= p * Math.log2(p);
|
|
71259
|
+
}
|
|
71260
|
+
return h;
|
|
71261
|
+
}
|
|
71262
|
+
|
|
71263
|
+
// telegram-plugin/secret-detect/inert-values.ts
|
|
71264
|
+
var INERT_VALUE_RE = [
|
|
71265
|
+
/^\[REDACTED(?::[A-Za-z0-9_]+)?\]$/i,
|
|
71266
|
+
/^\$\{?[A-Za-z_][A-Za-z0-9_]*\}?$/,
|
|
71267
|
+
/^\{\{[^\n\r\u2028\u2029]*\}\}$/,
|
|
71268
|
+
/^<[^<>\n\r\u2028\u2029]*>$/,
|
|
71269
|
+
/^%[A-Za-z_][A-Za-z0-9_]*%$/,
|
|
71270
|
+
/^vault:[A-Za-z0-9_./-]*$/i,
|
|
71271
|
+
/^[*x\u2022.]+$/i,
|
|
71272
|
+
/^(?:process\.env|import\.meta\.env|os\.environ)(?:\.[A-Za-z_$][A-Za-z0-9_$]*|\[["']?[A-Za-z_$][A-Za-z0-9_$]*["']?\])*$/,
|
|
71273
|
+
/^(?: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,
|
|
71274
|
+
/^[a-z]+(?: [a-z]+){2,}$/
|
|
71275
|
+
];
|
|
71276
|
+
var CREDENTIAL_RUN_RE = /[A-Za-z0-9]{12,}/g;
|
|
71277
|
+
var MIXED_CLASS_RUN_MIN = 12;
|
|
71278
|
+
var SINGLE_CLASS_RUN_MIN = 16;
|
|
71279
|
+
function runIsCredentialShaped(run) {
|
|
71280
|
+
let classes = 0;
|
|
71281
|
+
if (/[a-z]/.test(run))
|
|
71282
|
+
classes++;
|
|
71283
|
+
if (/[A-Z]/.test(run))
|
|
71284
|
+
classes++;
|
|
71285
|
+
if (/[0-9]/.test(run))
|
|
71286
|
+
classes++;
|
|
71287
|
+
return run.length >= (classes >= 2 ? MIXED_CLASS_RUN_MIN : SINGLE_CLASS_RUN_MIN);
|
|
71288
|
+
}
|
|
71289
|
+
function hasCredentialShapedRun(value) {
|
|
71290
|
+
for (const run of value.match(CREDENTIAL_RUN_RE) ?? []) {
|
|
71291
|
+
if (runIsCredentialShaped(run))
|
|
71292
|
+
return true;
|
|
71293
|
+
}
|
|
71294
|
+
return false;
|
|
71295
|
+
}
|
|
71296
|
+
function isInertValue(value) {
|
|
71297
|
+
if (!INERT_VALUE_RE.some((re) => re.test(value)))
|
|
71298
|
+
return false;
|
|
71299
|
+
return !hasCredentialShapedRun(value);
|
|
71300
|
+
}
|
|
71301
|
+
var INERT_GATED_RULES = new Set([
|
|
71302
|
+
"env_key_value",
|
|
71303
|
+
"json_secret_field",
|
|
71304
|
+
"cli_flag"
|
|
71305
|
+
]);
|
|
71306
|
+
var TRAILING_PUNCT_RE = /[.,;:!?)\]}]+$/;
|
|
71307
|
+
function stripTrailingPunctuation(value) {
|
|
71308
|
+
return value.replace(TRAILING_PUNCT_RE, "");
|
|
71309
|
+
}
|
|
71310
|
+
|
|
71311
|
+
// telegram-plugin/secret-detect/kv-scanner.ts
|
|
71312
|
+
var KV_RE = /\b([A-Za-z_][A-Za-z0-9_-]*(?:password|passwd|token|secret|key|api[_-]?key))\s*[:=]\s*["']?([^\s"'\\]{8,})["']?/gi;
|
|
71313
|
+
var KV_ENTROPY_THRESHOLD = 4;
|
|
71314
|
+
var MEMORABLE_PW_RE = /\b([A-Za-z0-9_-]*(?:password|passwd|passphrase|pwd))\b\s*(?:[:=]\s*|\s+is\s+)(["']?)([^\s"']{8,64})\2/gi;
|
|
71315
|
+
var MEMORABLE_PW_RULE_ID = "memorable_password";
|
|
71316
|
+
var MEMORABLE_PW_MIN_CLASSES = 2;
|
|
71317
|
+
function charClassCount(value) {
|
|
71318
|
+
let n = 0;
|
|
71319
|
+
if (/[a-z]/.test(value))
|
|
71320
|
+
n++;
|
|
71321
|
+
if (/[A-Z]/.test(value))
|
|
71322
|
+
n++;
|
|
71323
|
+
if (/[0-9]/.test(value))
|
|
71324
|
+
n++;
|
|
71325
|
+
if (/[^A-Za-z0-9]/.test(value))
|
|
71326
|
+
n++;
|
|
71327
|
+
return n;
|
|
71328
|
+
}
|
|
71329
|
+
function looksLikeMemorablePassword(value) {
|
|
71330
|
+
if (isInertValue(value))
|
|
71331
|
+
return false;
|
|
71332
|
+
const core = stripTrailingPunctuation(value);
|
|
71333
|
+
if (core.length < 8 || core.length > 64)
|
|
71334
|
+
return false;
|
|
71335
|
+
if (isInertValue(core))
|
|
71336
|
+
return false;
|
|
71337
|
+
if (new Set(core).size < 4)
|
|
71338
|
+
return false;
|
|
71339
|
+
return charClassCount(core) >= MEMORABLE_PW_MIN_CLASSES;
|
|
71340
|
+
}
|
|
71341
|
+
function scanMemorablePasswords(text) {
|
|
71342
|
+
const hits = [];
|
|
71343
|
+
MEMORABLE_PW_RE.lastIndex = 0;
|
|
71344
|
+
let m;
|
|
71345
|
+
while ((m = MEMORABLE_PW_RE.exec(text)) !== null) {
|
|
71346
|
+
const keyName = m[1];
|
|
71347
|
+
const value = m[3];
|
|
71348
|
+
if (!value || !looksLikeMemorablePassword(value))
|
|
71349
|
+
continue;
|
|
71350
|
+
const valueOffsetInMatch = m[0].indexOf(value, keyName.length);
|
|
71351
|
+
if (valueOffsetInMatch < 0)
|
|
71352
|
+
continue;
|
|
71353
|
+
const start = m.index + valueOffsetInMatch;
|
|
71354
|
+
hits.push({
|
|
71355
|
+
rule_id: MEMORABLE_PW_RULE_ID,
|
|
71356
|
+
start,
|
|
71357
|
+
end: start + value.length,
|
|
71358
|
+
matched_text: value,
|
|
71359
|
+
key_name: keyName,
|
|
71360
|
+
confidence: "ambiguous"
|
|
71361
|
+
});
|
|
71362
|
+
}
|
|
71363
|
+
return hits;
|
|
71364
|
+
}
|
|
71365
|
+
function scanKeyValue(text) {
|
|
71366
|
+
const hits = [];
|
|
71367
|
+
KV_RE.lastIndex = 0;
|
|
71368
|
+
let m;
|
|
71369
|
+
while ((m = KV_RE.exec(text)) !== null) {
|
|
71370
|
+
const [, keyName, value] = m;
|
|
71371
|
+
if (!value)
|
|
71372
|
+
continue;
|
|
71373
|
+
if (isInertValue(value))
|
|
71374
|
+
continue;
|
|
71375
|
+
const h = shannonEntropy(value);
|
|
71376
|
+
if (h < KV_ENTROPY_THRESHOLD)
|
|
71377
|
+
continue;
|
|
71378
|
+
const valueOffsetInMatch = m[0].indexOf(value, keyName.length);
|
|
71379
|
+
if (valueOffsetInMatch < 0)
|
|
71380
|
+
continue;
|
|
71381
|
+
const start = m.index + valueOffsetInMatch;
|
|
71382
|
+
const end = start + value.length;
|
|
71383
|
+
hits.push({
|
|
71384
|
+
rule_id: "kv_entropy",
|
|
71385
|
+
start,
|
|
71386
|
+
end,
|
|
71387
|
+
matched_text: value,
|
|
71388
|
+
key_name: keyName,
|
|
71389
|
+
confidence: "ambiguous"
|
|
71390
|
+
});
|
|
71391
|
+
}
|
|
71392
|
+
return hits;
|
|
71393
|
+
}
|
|
71394
|
+
|
|
71395
|
+
// telegram-plugin/secret-detect/db-uri.ts
|
|
71396
|
+
var DB_URI_RE = /\b([a-zA-Z][a-zA-Z0-9+.-]+):\/\/([^\s:/@]+):([^\s/?#]+)@([^\s/@?#]+)/g;
|
|
71397
|
+
var DB_URI_RULE_ID = "db_uri_password";
|
|
71398
|
+
function isInertPassword(value) {
|
|
71399
|
+
if (value.startsWith("[REDACTED"))
|
|
71400
|
+
return true;
|
|
71401
|
+
return /^[*x]+$/i.test(value);
|
|
71402
|
+
}
|
|
71403
|
+
function scanDbUris(text) {
|
|
71404
|
+
const hits = [];
|
|
71405
|
+
DB_URI_RE.lastIndex = 0;
|
|
71406
|
+
let m;
|
|
71407
|
+
while ((m = DB_URI_RE.exec(text)) !== null) {
|
|
71408
|
+
const scheme = m[1];
|
|
71409
|
+
const user = m[2];
|
|
71410
|
+
const password = m[3];
|
|
71411
|
+
if (isInertPassword(password))
|
|
71412
|
+
continue;
|
|
71413
|
+
const start = m.index + scheme.length + 3 + user.length + 1;
|
|
71414
|
+
hits.push({
|
|
71415
|
+
rule_id: DB_URI_RULE_ID,
|
|
71416
|
+
start,
|
|
71417
|
+
end: start + password.length,
|
|
71418
|
+
matched_text: password,
|
|
71419
|
+
key_name: `${scheme}_password`,
|
|
71420
|
+
confidence: "ambiguous"
|
|
71421
|
+
});
|
|
71422
|
+
}
|
|
71423
|
+
return hits;
|
|
71424
|
+
}
|
|
71425
|
+
|
|
71426
|
+
// telegram-plugin/secret-detect/generic-entropy.ts
|
|
71427
|
+
var CANDIDATE_RE = /[A-Za-z0-9]{28,}/g;
|
|
71428
|
+
var GENERIC_MIN_DISTINCT = 18;
|
|
71429
|
+
var MAX_GENERIC_HITS = 20;
|
|
71430
|
+
function hasDistinctChars(tok, n) {
|
|
71431
|
+
const seen = new Uint8Array(128);
|
|
71432
|
+
let distinct = 0;
|
|
71433
|
+
for (let i = 0;i < tok.length; i++) {
|
|
71434
|
+
const c = tok.charCodeAt(i);
|
|
71435
|
+
if (seen[c] === 0) {
|
|
71436
|
+
seen[c] = 1;
|
|
71437
|
+
if (++distinct >= n)
|
|
71438
|
+
return true;
|
|
71439
|
+
}
|
|
71440
|
+
}
|
|
71441
|
+
return false;
|
|
71442
|
+
}
|
|
71443
|
+
function hasDigit(tok) {
|
|
71444
|
+
for (let i = 0;i < tok.length; i++) {
|
|
71445
|
+
const c = tok.charCodeAt(i);
|
|
71446
|
+
if (c >= 48 && c <= 57)
|
|
71447
|
+
return true;
|
|
71448
|
+
}
|
|
71449
|
+
return false;
|
|
71450
|
+
}
|
|
71451
|
+
function scanGenericSecrets(text) {
|
|
71452
|
+
const hits = [];
|
|
71453
|
+
CANDIDATE_RE.lastIndex = 0;
|
|
71454
|
+
let m;
|
|
71455
|
+
while ((m = CANDIDATE_RE.exec(text)) !== null) {
|
|
71456
|
+
if (hits.length >= MAX_GENERIC_HITS)
|
|
71457
|
+
break;
|
|
71458
|
+
const tok = m[0];
|
|
71459
|
+
if (!hasDigit(tok))
|
|
71460
|
+
continue;
|
|
71461
|
+
if (!hasDistinctChars(tok, GENERIC_MIN_DISTINCT))
|
|
71462
|
+
continue;
|
|
71463
|
+
hits.push({
|
|
71464
|
+
rule_id: "generic_high_entropy",
|
|
71465
|
+
start: m.index,
|
|
71466
|
+
end: m.index + tok.length,
|
|
71467
|
+
matched_text: tok,
|
|
71468
|
+
confidence: "ambiguous"
|
|
71469
|
+
});
|
|
71470
|
+
}
|
|
71471
|
+
return hits;
|
|
71472
|
+
}
|
|
71473
|
+
|
|
71474
|
+
// telegram-plugin/secret-detect/chunker.ts
|
|
71475
|
+
var CHUNK_THRESHOLD = 32 * 1024;
|
|
71476
|
+
var WINDOW_SIZE = 16 * 1024;
|
|
71477
|
+
var OVERLAP = 8 * 1024;
|
|
71478
|
+
function chunk(text) {
|
|
71479
|
+
if (text.length <= CHUNK_THRESHOLD) {
|
|
71480
|
+
return [{ offset: 0, text }];
|
|
71481
|
+
}
|
|
71482
|
+
const out = [];
|
|
71483
|
+
let offset = 0;
|
|
71484
|
+
while (offset < text.length) {
|
|
71485
|
+
const end = Math.min(offset + WINDOW_SIZE, text.length);
|
|
71486
|
+
out.push({ offset, text: text.slice(offset, end) });
|
|
71487
|
+
if (end >= text.length)
|
|
71488
|
+
break;
|
|
71489
|
+
offset = end - OVERLAP;
|
|
71490
|
+
}
|
|
71491
|
+
return out;
|
|
71492
|
+
}
|
|
71493
|
+
|
|
71494
|
+
// telegram-plugin/secret-detect/suppressor.ts
|
|
71495
|
+
var MARKERS = ["test", "mock", "example", "fixture", "dummy"];
|
|
71496
|
+
var WINDOW = 40;
|
|
71497
|
+
var MARKER_RE = new RegExp(`\\b(?:${MARKERS.join("|")})\\b`, "i");
|
|
71498
|
+
function isSuppressed(text, start, end) {
|
|
71499
|
+
const left = Math.max(0, start - WINDOW);
|
|
71500
|
+
const right = Math.min(text.length, end + WINDOW);
|
|
71501
|
+
const context = text.slice(left, start) + text.slice(end, right);
|
|
71502
|
+
return MARKER_RE.test(context);
|
|
71503
|
+
}
|
|
71504
|
+
|
|
71505
|
+
// telegram-plugin/secret-detect/slug.ts
|
|
71506
|
+
function sanitizeKeyName(raw) {
|
|
71507
|
+
const up = raw.toUpperCase();
|
|
71508
|
+
const cleaned = up.replace(/[^A-Z0-9_]+/g, "_").replace(/_+/g, "_").replace(/^_+|_+$/g, "");
|
|
71509
|
+
return cleaned.length > 0 ? cleaned : "SECRET";
|
|
71510
|
+
}
|
|
71511
|
+
function datePart(now = new Date) {
|
|
71512
|
+
const y = now.getUTCFullYear();
|
|
71513
|
+
const m = String(now.getUTCMonth() + 1).padStart(2, "0");
|
|
71514
|
+
const d = String(now.getUTCDate()).padStart(2, "0");
|
|
71515
|
+
return `${y}${m}${d}`;
|
|
71516
|
+
}
|
|
71517
|
+
function deriveSlug(inputs, existing) {
|
|
71518
|
+
let base;
|
|
71519
|
+
if (inputs.key_name && inputs.key_name.trim().length > 0) {
|
|
71520
|
+
base = sanitizeKeyName(inputs.key_name);
|
|
71521
|
+
} else {
|
|
71522
|
+
base = `${inputs.rule_id}_${datePart(inputs.now)}`;
|
|
71523
|
+
}
|
|
71524
|
+
if (!existing.has(base))
|
|
71525
|
+
return base;
|
|
71526
|
+
let n = 2;
|
|
71527
|
+
while (existing.has(`${base}_${n}`))
|
|
71528
|
+
n++;
|
|
71529
|
+
return `${base}_${n}`;
|
|
71530
|
+
}
|
|
71531
|
+
// telegram-plugin/secret-detect/url-redact.ts
|
|
71532
|
+
var SENSITIVE_PARAMS = new Set([
|
|
71533
|
+
"token",
|
|
71534
|
+
"key",
|
|
71535
|
+
"api_key",
|
|
71536
|
+
"apikey",
|
|
71537
|
+
"secret",
|
|
71538
|
+
"access_token",
|
|
71539
|
+
"password",
|
|
71540
|
+
"pass",
|
|
71541
|
+
"auth",
|
|
71542
|
+
"client_secret",
|
|
71543
|
+
"refresh_token",
|
|
71544
|
+
"signature"
|
|
71545
|
+
]);
|
|
71546
|
+
var URL_RE = /\b(?:https?|wss?|ftp):\/\/[^\s<>"']+/gi;
|
|
71547
|
+
function redactUrls(text) {
|
|
71548
|
+
return text.replace(URL_RE, (m) => {
|
|
71549
|
+
const redacted = redactOne(m);
|
|
71550
|
+
return redacted ?? m;
|
|
71551
|
+
});
|
|
71552
|
+
}
|
|
71553
|
+
function redactOne(raw) {
|
|
71554
|
+
let u;
|
|
71555
|
+
try {
|
|
71556
|
+
u = new URL(raw);
|
|
71557
|
+
} catch {
|
|
71558
|
+
return null;
|
|
71559
|
+
}
|
|
71560
|
+
let changed = false;
|
|
71561
|
+
if (u.username || u.password) {
|
|
71562
|
+
u.username = "***";
|
|
71563
|
+
u.password = "";
|
|
71564
|
+
changed = true;
|
|
71565
|
+
}
|
|
71566
|
+
for (const [key] of u.searchParams) {
|
|
71567
|
+
if (SENSITIVE_PARAMS.has(key.toLowerCase())) {
|
|
71568
|
+
u.searchParams.set(key, "***");
|
|
71569
|
+
changed = true;
|
|
71570
|
+
}
|
|
71571
|
+
}
|
|
71572
|
+
return changed ? u.toString() : null;
|
|
71573
|
+
}
|
|
71574
|
+
|
|
71575
|
+
// telegram-plugin/secret-detect/index.ts
|
|
71576
|
+
function detectSecrets(text) {
|
|
71577
|
+
if (!text || text.length === 0)
|
|
71578
|
+
return [];
|
|
71579
|
+
const windows = chunk(text);
|
|
71580
|
+
const raw = [];
|
|
71581
|
+
for (const win of windows) {
|
|
71582
|
+
for (const p of ALL_PATTERNS) {
|
|
71583
|
+
const re = new RegExp(p.regex.source, p.regex.flags.includes("g") ? p.regex.flags : p.regex.flags + "g");
|
|
71584
|
+
let m;
|
|
71585
|
+
while ((m = re.exec(win.text)) !== null) {
|
|
71586
|
+
if (m[0].length === 0) {
|
|
71587
|
+
re.lastIndex++;
|
|
71588
|
+
continue;
|
|
71589
|
+
}
|
|
71590
|
+
const cap = p.captureIndex === 0 ? m[0] : m[p.captureIndex];
|
|
71591
|
+
if (!cap)
|
|
71592
|
+
continue;
|
|
71593
|
+
const matchStart = p.captureIndex === 0 ? m.index : m.index + m[0].indexOf(cap);
|
|
71594
|
+
if (matchStart < 0)
|
|
71595
|
+
continue;
|
|
71596
|
+
const globalStart = win.offset + matchStart;
|
|
71597
|
+
const globalEnd = globalStart + cap.length;
|
|
71598
|
+
const keyName = p.rule_id === "env_key_value" ? m[1] : undefined;
|
|
71599
|
+
if (INERT_GATED_RULES.has(p.rule_id) && isInertValue(cap))
|
|
71600
|
+
continue;
|
|
71601
|
+
if (p.rule_id === "env_key_value") {
|
|
71602
|
+
const ENV_KV_MIN_LEN = 12;
|
|
71603
|
+
const ENV_KV_MIN_ENTROPY = 3.5;
|
|
71604
|
+
if (cap.length < ENV_KV_MIN_LEN)
|
|
71605
|
+
continue;
|
|
71606
|
+
if (shannonEntropy(cap) < ENV_KV_MIN_ENTROPY)
|
|
71607
|
+
continue;
|
|
71608
|
+
}
|
|
71609
|
+
raw.push({
|
|
71610
|
+
rule_id: p.rule_id,
|
|
71611
|
+
start: globalStart,
|
|
71612
|
+
end: globalEnd,
|
|
71613
|
+
matched_text: cap,
|
|
71614
|
+
key_name: keyName,
|
|
71615
|
+
confidence: "high"
|
|
71616
|
+
});
|
|
71617
|
+
}
|
|
71618
|
+
}
|
|
71619
|
+
const kvHits = scanKeyValue(win.text);
|
|
71620
|
+
for (const h of kvHits) {
|
|
71621
|
+
raw.push({ ...h, start: h.start + win.offset, end: h.end + win.offset });
|
|
71622
|
+
}
|
|
71623
|
+
for (const h of scanDbUris(win.text)) {
|
|
71624
|
+
raw.push({ ...h, start: h.start + win.offset, end: h.end + win.offset });
|
|
71625
|
+
}
|
|
71626
|
+
for (const h of scanMemorablePasswords(win.text)) {
|
|
71627
|
+
raw.push({ ...h, start: h.start + win.offset, end: h.end + win.offset });
|
|
71628
|
+
}
|
|
71629
|
+
const genHits = scanGenericSecrets(win.text);
|
|
71630
|
+
for (const h of genHits) {
|
|
71631
|
+
raw.push({ ...h, start: h.start + win.offset, end: h.end + win.offset });
|
|
71632
|
+
}
|
|
71633
|
+
}
|
|
71634
|
+
const deduped = dedupeRaw(raw);
|
|
71635
|
+
const final = dropOverlaps(deduped);
|
|
71636
|
+
const existing = new Set;
|
|
71637
|
+
const out = [];
|
|
71638
|
+
for (const h of final) {
|
|
71639
|
+
const suggested_slug = deriveSlug({ key_name: h.key_name, rule_id: h.rule_id }, existing);
|
|
71640
|
+
existing.add(suggested_slug);
|
|
71641
|
+
out.push({
|
|
71642
|
+
rule_id: h.rule_id,
|
|
71643
|
+
matched_text: h.matched_text,
|
|
71644
|
+
start: h.start,
|
|
71645
|
+
end: h.end,
|
|
71646
|
+
confidence: h.confidence,
|
|
71647
|
+
suppressed: isSuppressed(text, h.start, h.end),
|
|
71648
|
+
suggested_slug,
|
|
71649
|
+
key_name: h.key_name
|
|
71650
|
+
});
|
|
71651
|
+
}
|
|
71652
|
+
out.sort((a, b) => a.start - b.start);
|
|
71653
|
+
return out;
|
|
71654
|
+
}
|
|
71655
|
+
function dedupeRaw(raw) {
|
|
71656
|
+
const seen = new Map;
|
|
71657
|
+
for (const h of raw) {
|
|
71658
|
+
const key = `${h.start}:${h.end}`;
|
|
71659
|
+
const existing = seen.get(key);
|
|
71660
|
+
if (!existing) {
|
|
71661
|
+
seen.set(key, h);
|
|
71662
|
+
continue;
|
|
71663
|
+
}
|
|
71664
|
+
if (existing.confidence === "ambiguous" && h.confidence === "high") {
|
|
71665
|
+
seen.set(key, h);
|
|
71666
|
+
}
|
|
71667
|
+
}
|
|
71668
|
+
return Array.from(seen.values());
|
|
71669
|
+
}
|
|
71670
|
+
function dropOverlaps(hits) {
|
|
71671
|
+
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))));
|
|
71672
|
+
out.sort((a, b) => a.start - b.start || a.end - b.end);
|
|
71673
|
+
return out;
|
|
71674
|
+
}
|
|
71675
|
+
|
|
71676
|
+
// telegram-plugin/secret-detect/redact.ts
|
|
71677
|
+
var REDACTED_MARKER = "[REDACTED]";
|
|
71678
|
+
function redact(text) {
|
|
71679
|
+
if (!text || text.length === 0)
|
|
71680
|
+
return text;
|
|
71681
|
+
const urlScrubbed = redactUrls(text);
|
|
71682
|
+
const hits = detectSecrets(urlScrubbed).filter((h) => h.rule_id !== "generic_high_entropy");
|
|
71683
|
+
if (hits.length === 0)
|
|
71684
|
+
return urlScrubbed;
|
|
71685
|
+
const sorted = [...hits].sort((a, b) => b.start - a.start);
|
|
71686
|
+
let out = urlScrubbed;
|
|
71687
|
+
for (const h of sorted) {
|
|
71688
|
+
out = out.slice(0, h.start) + redactedMarker(h.rule_id) + out.slice(h.end);
|
|
71689
|
+
}
|
|
71690
|
+
return out;
|
|
71691
|
+
}
|
|
71692
|
+
function redactedMarker(ruleId) {
|
|
71693
|
+
const trimmed = ruleId.replace(/^(kv|env)_/, "");
|
|
71694
|
+
if (!trimmed || trimmed === "key_value" || trimmed === "kv_entropy") {
|
|
71695
|
+
return REDACTED_MARKER;
|
|
71696
|
+
}
|
|
71697
|
+
return `[REDACTED:${trimmed}]`;
|
|
71698
|
+
}
|
|
71699
|
+
// src/memory/hindsight-write-redaction.ts
|
|
71700
|
+
function redactJsonStrings(value) {
|
|
71701
|
+
if (typeof value === "string")
|
|
71702
|
+
return redact(value);
|
|
71703
|
+
if (Array.isArray(value))
|
|
71704
|
+
return value.map(redactJsonStrings);
|
|
71705
|
+
if (value && typeof value === "object") {
|
|
71706
|
+
const out = {};
|
|
71707
|
+
for (const [k, v] of Object.entries(value)) {
|
|
71708
|
+
out[k] = redactJsonStrings(v);
|
|
71709
|
+
}
|
|
71710
|
+
return out;
|
|
71711
|
+
}
|
|
71712
|
+
return value;
|
|
71713
|
+
}
|
|
71714
|
+
var REDACTED_ITEM_FIELDS = ["content", "context"];
|
|
71715
|
+
function redactMemoryWriteBody(body) {
|
|
71716
|
+
if (!body || typeof body !== "object" || Array.isArray(body))
|
|
71717
|
+
return body;
|
|
71718
|
+
const src = body;
|
|
71719
|
+
if (!Array.isArray(src.items))
|
|
71720
|
+
return body;
|
|
71721
|
+
const items = src.items.map((item) => {
|
|
71722
|
+
if (!item || typeof item !== "object" || Array.isArray(item))
|
|
71723
|
+
return item;
|
|
71724
|
+
const row = { ...item };
|
|
71725
|
+
for (const field of REDACTED_ITEM_FIELDS) {
|
|
71726
|
+
if (typeof row[field] === "string") {
|
|
71727
|
+
row[field] = redact(row[field]);
|
|
71728
|
+
}
|
|
71729
|
+
}
|
|
71730
|
+
if (row.metadata && typeof row.metadata === "object") {
|
|
71731
|
+
row.metadata = redactJsonStrings(row.metadata);
|
|
71732
|
+
}
|
|
71733
|
+
return row;
|
|
71734
|
+
});
|
|
71735
|
+
return { ...src, items };
|
|
71736
|
+
}
|
|
71737
|
+
|
|
71738
|
+
// src/agents/handoff-summarizer.ts
|
|
71739
|
+
init_observation_scopes();
|
|
71108
71740
|
var HANDOFF_STATUS_MIRROR_SKIPPED = "mirror-skipped-invalid-scope";
|
|
71109
71741
|
var DEFAULT_MAX_TURNS = 50;
|
|
71110
71742
|
var TOPIC_MAX_CHARS = 117;
|
|
@@ -71291,7 +71923,7 @@ async function mirrorToHindsight(briefing, opts) {
|
|
|
71291
71923
|
`);
|
|
71292
71924
|
return false;
|
|
71293
71925
|
}
|
|
71294
|
-
const body = {
|
|
71926
|
+
const body = redactMemoryWriteBody({
|
|
71295
71927
|
items: [
|
|
71296
71928
|
{
|
|
71297
71929
|
content: briefing,
|
|
@@ -71301,7 +71933,7 @@ async function mirrorToHindsight(briefing, opts) {
|
|
|
71301
71933
|
}
|
|
71302
71934
|
],
|
|
71303
71935
|
async: true
|
|
71304
|
-
};
|
|
71936
|
+
});
|
|
71305
71937
|
try {
|
|
71306
71938
|
await fetchFn(endpoint, {
|
|
71307
71939
|
method: "POST",
|
|
@@ -74307,8 +74939,8 @@ Bootstrapping agent: ${name}
|
|
|
74307
74939
|
const code = await new Promise((resolve24) => {
|
|
74308
74940
|
process.stdin.setEncoding("utf-8");
|
|
74309
74941
|
let buf = "";
|
|
74310
|
-
process.stdin.on("data", (
|
|
74311
|
-
buf +=
|
|
74942
|
+
process.stdin.on("data", (chunk2) => {
|
|
74943
|
+
buf += chunk2.toString();
|
|
74312
74944
|
const newlineIdx = buf.indexOf(`
|
|
74313
74945
|
`);
|
|
74314
74946
|
if (newlineIdx !== -1) {
|
|
@@ -74390,8 +75022,8 @@ switchroom agent add: ${name}
|
|
|
74390
75022
|
return new Promise((resolveLine) => {
|
|
74391
75023
|
process.stdin.setEncoding("utf-8");
|
|
74392
75024
|
let buf = "";
|
|
74393
|
-
const onData = (
|
|
74394
|
-
buf +=
|
|
75025
|
+
const onData = (chunk2) => {
|
|
75026
|
+
buf += chunk2.toString();
|
|
74395
75027
|
const newlineIdx = buf.indexOf(`
|
|
74396
75028
|
`);
|
|
74397
75029
|
if (newlineIdx !== -1) {
|
|
@@ -76422,8 +77054,8 @@ async function readHiddenLine2(prompt) {
|
|
|
76422
77054
|
stdin.setRawMode(true);
|
|
76423
77055
|
}
|
|
76424
77056
|
let line = "";
|
|
76425
|
-
const onData = (
|
|
76426
|
-
const s =
|
|
77057
|
+
const onData = (chunk2) => {
|
|
77058
|
+
const s = chunk2.toString("utf8");
|
|
76427
77059
|
for (const ch of s) {
|
|
76428
77060
|
if (ch === `
|
|
76429
77061
|
` || ch === "\r") {
|
|
@@ -77352,7 +77984,7 @@ function getVaultPath2(configPath) {
|
|
|
77352
77984
|
return resolvePath("~/.switchroom/vault.enc");
|
|
77353
77985
|
}
|
|
77354
77986
|
}
|
|
77355
|
-
function
|
|
77987
|
+
function maskToken2(s) {
|
|
77356
77988
|
if (s.length >= 18)
|
|
77357
77989
|
return `${s.slice(0, 6)}...${s.slice(-4)}`;
|
|
77358
77990
|
return "***";
|
|
@@ -77640,7 +78272,7 @@ function registerVaultSweep(vault, program3) {
|
|
|
77640
78272
|
console.log("");
|
|
77641
78273
|
console.log(source_default.dim(`masked values for display only:`));
|
|
77642
78274
|
for (const v of values) {
|
|
77643
|
-
console.log(` vault:${v.key} \u2192 ${
|
|
78275
|
+
console.log(` vault:${v.key} \u2192 ${maskToken2(v.value)}`);
|
|
77644
78276
|
}
|
|
77645
78277
|
if (opts.dryRun) {
|
|
77646
78278
|
console.log(source_default.yellow("dry-run \u2014 no files modified. Rerun without --dry-run to apply."));
|
|
@@ -80613,8 +81245,8 @@ class VaultBroker {
|
|
|
80613
81245
|
peer = this.testOpts._testIdentify ? this.testOpts._testIdentify(listenerSocketPath, socket) : identify(listenerSocketPath, socket);
|
|
80614
81246
|
}
|
|
80615
81247
|
let buffer = "";
|
|
80616
|
-
socket.on("data", (
|
|
80617
|
-
buffer +=
|
|
81248
|
+
socket.on("data", (chunk2) => {
|
|
81249
|
+
buffer += chunk2.toString("utf8");
|
|
80618
81250
|
if (Buffer.byteLength(buffer, "utf8") > MAX_FRAME_BYTES) {
|
|
80619
81251
|
const resp = encodeResponse(errorResponse("BAD_REQUEST", "Frame exceeds 64 KiB limit"));
|
|
80620
81252
|
socket.end(resp);
|
|
@@ -81723,8 +82355,8 @@ class VaultBroker {
|
|
|
81723
82355
|
const auditCaller = isOperator ? "operator" : unlockPeer !== null ? callerFromPeer(unlockPeer) : `pid:${process.pid}`;
|
|
81724
82356
|
const auditCgroup = isOperator ? undefined : unlockPeer?.systemdUnit ?? undefined;
|
|
81725
82357
|
let buffer = "";
|
|
81726
|
-
socket.on("data", (
|
|
81727
|
-
buffer +=
|
|
82358
|
+
socket.on("data", (chunk2) => {
|
|
82359
|
+
buffer += chunk2.toString("utf8");
|
|
81728
82360
|
const newlineIdx = buffer.indexOf(`
|
|
81729
82361
|
`);
|
|
81730
82362
|
if (newlineIdx === -1) {
|
|
@@ -83291,7 +83923,7 @@ function promptLine2(prompt, hidden = false) {
|
|
|
83291
83923
|
function readStdinToEnd() {
|
|
83292
83924
|
return new Promise((resolve30, reject) => {
|
|
83293
83925
|
const chunks = [];
|
|
83294
|
-
process.stdin.on("data", (
|
|
83926
|
+
process.stdin.on("data", (chunk2) => chunks.push(chunk2));
|
|
83295
83927
|
process.stdin.on("end", () => {
|
|
83296
83928
|
resolve30(Buffer.concat(chunks).toString("utf8"));
|
|
83297
83929
|
});
|
|
@@ -85443,9 +86075,9 @@ Repairing vector index coverage for ${target}${opts.dryRun ? source_default.yell
|
|
|
85443
86075
|
`));
|
|
85444
86076
|
const child = spawn5("docker", argv, { stdio: ["ignore", "pipe", "inherit"] });
|
|
85445
86077
|
let captured = "";
|
|
85446
|
-
child.stdout?.on("data", (
|
|
85447
|
-
captured +=
|
|
85448
|
-
process.stdout.write(
|
|
86078
|
+
child.stdout?.on("data", (chunk2) => {
|
|
86079
|
+
captured += chunk2.toString();
|
|
86080
|
+
process.stdout.write(chunk2);
|
|
85449
86081
|
});
|
|
85450
86082
|
const childExit = await new Promise((resolve30) => {
|
|
85451
86083
|
child.on("error", () => resolve30(127));
|
|
@@ -85546,14 +86178,21 @@ Demoting memory ${source_default.cyan(memoryId)}`));
|
|
|
85546
86178
|
const timeoutMs = Math.max(1000, parseInt(opts.timeout, 10) || 60000);
|
|
85547
86179
|
const ctrl = new AbortController;
|
|
85548
86180
|
const t = setTimeout(() => ctrl.abort(), timeoutMs);
|
|
86181
|
+
const payload = redactMemoryWriteBody({
|
|
86182
|
+
items: [{ content, tags: ["operator-authored", "profile"] }],
|
|
86183
|
+
async: false
|
|
86184
|
+
});
|
|
86185
|
+
const stored = payload.items[0].content;
|
|
86186
|
+
if (stored !== content) {
|
|
86187
|
+
console.error(source_default.yellow(`\u26a0 Credential-shaped text in this fact was replaced with a
|
|
86188
|
+
` + ` [REDACTED] marker before it was stored. Put secrets in the
|
|
86189
|
+
` + " vault (`switchroom vault set <key>`) and reference them as\n" + " `vault:<key>` instead."));
|
|
86190
|
+
}
|
|
85549
86191
|
try {
|
|
85550
86192
|
const res = await fetch(url, {
|
|
85551
86193
|
method: "POST",
|
|
85552
86194
|
headers: { "Content-Type": "application/json" },
|
|
85553
|
-
body: JSON.stringify(
|
|
85554
|
-
items: [{ content, tags: ["operator-authored", "profile"] }],
|
|
85555
|
-
async: false
|
|
85556
|
-
}),
|
|
86195
|
+
body: JSON.stringify(payload),
|
|
85557
86196
|
signal: ctrl.signal
|
|
85558
86197
|
});
|
|
85559
86198
|
if (!res.ok) {
|
|
@@ -85561,7 +86200,7 @@ Demoting memory ${source_default.cyan(memoryId)}`));
|
|
|
85561
86200
|
process.exit(1);
|
|
85562
86201
|
}
|
|
85563
86202
|
console.log(source_default.green(`\u2713 Added to profile bank "${bank}"`));
|
|
85564
|
-
console.log(source_default.gray(` ${
|
|
86203
|
+
console.log(source_default.gray(` ${stored}`));
|
|
85565
86204
|
console.log(source_default.gray(" (fact extraction runs in the background \u2014 `profile list` may lag a few seconds)"));
|
|
85566
86205
|
console.log(source_default.gray(`
|
|
85567
86206
|
Wire it into agents via memory.recall.additional_banks: ["${bank}"] in switchroom.yaml,
|
|
@@ -87854,8 +88493,8 @@ function forwardToGateway(socketPath, req, opts = {}) {
|
|
|
87854
88493
|
conn.write(JSON.stringify(req) + `
|
|
87855
88494
|
`);
|
|
87856
88495
|
});
|
|
87857
|
-
conn.on("data", (
|
|
87858
|
-
buf +=
|
|
88496
|
+
conn.on("data", (chunk2) => {
|
|
88497
|
+
buf += chunk2;
|
|
87859
88498
|
const nl = buf.indexOf(`
|
|
87860
88499
|
`);
|
|
87861
88500
|
if (nl === -1)
|
|
@@ -94034,410 +94673,6 @@ function computeFingerprint(source, code) {
|
|
|
94034
94673
|
return `${source}::${code}`;
|
|
94035
94674
|
}
|
|
94036
94675
|
|
|
94037
|
-
// telegram-plugin/secret-detect/patterns.ts
|
|
94038
|
-
var ANCHORED_PATTERNS = [
|
|
94039
|
-
{ rule_id: "anthropic_api_key", regex: /\b(sk-ant-[A-Za-z0-9_-]{8,})\b/g, captureIndex: 1, slugHint: "anthropic_api_key" },
|
|
94040
|
-
{ 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" },
|
|
94041
|
-
{ rule_id: "openai_api_key", regex: /\b(sk-[A-Za-z0-9_-]{20,})\b/g, captureIndex: 1, slugHint: "openai_api_key" },
|
|
94042
|
-
{ rule_id: "github_pat_classic", regex: /\b(ghp_[A-Za-z0-9]{20,})\b/g, captureIndex: 1, slugHint: "github_pat" },
|
|
94043
|
-
{ rule_id: "github_pat_fine_grained", regex: /\b(github_pat_[A-Za-z0-9_]{20,})\b/g, captureIndex: 1, slugHint: "github_pat" },
|
|
94044
|
-
{ rule_id: "slack_token", regex: /\b(xox[baprs]-[A-Za-z0-9-]{10,})\b/g, captureIndex: 1, slugHint: "slack_token" },
|
|
94045
|
-
{ rule_id: "slack_app_token", regex: /\b(xapp-[A-Za-z0-9-]{10,})\b/g, captureIndex: 1, slugHint: "slack_app_token" },
|
|
94046
|
-
{ rule_id: "groq_api_key", regex: /\b(gsk_[A-Za-z0-9_-]{10,})\b/g, captureIndex: 1, slugHint: "groq_api_key" },
|
|
94047
|
-
{ rule_id: "google_api_key", regex: /\b(AIza[0-9A-Za-z\-_]{20,})\b/g, captureIndex: 1, slugHint: "google_api_key" },
|
|
94048
|
-
{ rule_id: "perplexity_api_key", regex: /\b(pplx-[A-Za-z0-9_-]{10,})\b/g, captureIndex: 1, slugHint: "perplexity_api_key" },
|
|
94049
|
-
{ rule_id: "npm_token", regex: /\b(npm_[A-Za-z0-9]{10,})\b/g, captureIndex: 1, slugHint: "npm_token" },
|
|
94050
|
-
{ rule_id: "telegram_bot_token_prefixed", regex: /\bbot(\d{6,}:[A-Za-z0-9_-]{20,})\b/g, captureIndex: 1, slugHint: "telegram_bot_token" },
|
|
94051
|
-
{ rule_id: "telegram_bot_token", regex: /\b(\d{6,}:[A-Za-z0-9_-]{20,})\b/g, captureIndex: 1, slugHint: "telegram_bot_token" },
|
|
94052
|
-
{ rule_id: "laravel_sanctum_token", regex: /\b(\d+\|[A-Za-z0-9]{40,})\b/g, captureIndex: 1, slugHint: "api_token" },
|
|
94053
|
-
{ rule_id: "aws_access_key", regex: /\b(AKIA[0-9A-Z]{16})\b/g, captureIndex: 1, slugHint: "aws_access_key" },
|
|
94054
|
-
{ 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" }
|
|
94055
|
-
];
|
|
94056
|
-
var STRUCTURED_PATTERNS = [
|
|
94057
|
-
{
|
|
94058
|
-
rule_id: "env_key_value",
|
|
94059
|
-
regex: /\b([A-Z0-9_]*(?:KEY|TOKEN|SECRET|PASSWORD|PASSWD))\b\s*[=:]\s*(["']?)([^\s"'\\]+)\2/g,
|
|
94060
|
-
captureIndex: 3,
|
|
94061
|
-
slugHint: "env"
|
|
94062
|
-
},
|
|
94063
|
-
{
|
|
94064
|
-
rule_id: "json_secret_field",
|
|
94065
|
-
regex: /"(?:apiKey|token|secret|password|passwd|accessToken|refreshToken)"\s*:\s*"([^"]+)"/g,
|
|
94066
|
-
captureIndex: 1,
|
|
94067
|
-
slugHint: "json_secret"
|
|
94068
|
-
},
|
|
94069
|
-
{
|
|
94070
|
-
rule_id: "cli_flag",
|
|
94071
|
-
regex: /--(?:api[-_]?key|hook[-_]?token|token|secret|password|passwd)\s+(["']?)([^\s"']+)\1/g,
|
|
94072
|
-
captureIndex: 2,
|
|
94073
|
-
slugHint: "cli_flag"
|
|
94074
|
-
},
|
|
94075
|
-
{
|
|
94076
|
-
rule_id: "bearer_auth_header",
|
|
94077
|
-
regex: /Authorization\s*[:=]\s*Bearer\s+([A-Za-z0-9._\-+=]+)/g,
|
|
94078
|
-
captureIndex: 1,
|
|
94079
|
-
slugHint: "bearer_token"
|
|
94080
|
-
},
|
|
94081
|
-
{
|
|
94082
|
-
rule_id: "bearer_loose",
|
|
94083
|
-
regex: /\bBearer\s+([A-Za-z0-9._\-+=]{18,})\b/g,
|
|
94084
|
-
captureIndex: 1,
|
|
94085
|
-
slugHint: "bearer_token"
|
|
94086
|
-
},
|
|
94087
|
-
{
|
|
94088
|
-
rule_id: "pem_private_key",
|
|
94089
|
-
regex: /-----BEGIN [A-Z ]*PRIVATE KEY-----[\s\S]+?-----END [A-Z ]*PRIVATE KEY-----/g,
|
|
94090
|
-
captureIndex: 0,
|
|
94091
|
-
slugHint: "pem_private_key"
|
|
94092
|
-
}
|
|
94093
|
-
];
|
|
94094
|
-
var PROVIDER_PATTERNS = [
|
|
94095
|
-
{ rule_id: "slack_webhook", regex: /(https:\/\/hooks\.slack\.com\/services\/[A-Za-z0-9_/]+)/g, captureIndex: 1, slugHint: "slack_webhook" },
|
|
94096
|
-
{ rule_id: "stripe_live_secret", regex: /\b(sk_live_[A-Za-z0-9]{24,})\b/g, captureIndex: 1, slugHint: "stripe_key" },
|
|
94097
|
-
{ rule_id: "stripe_restricted", regex: /\b(rk_live_[A-Za-z0-9]{24,})\b/g, captureIndex: 1, slugHint: "stripe_key" },
|
|
94098
|
-
{ 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" },
|
|
94099
|
-
{ rule_id: "gitlab_pat", regex: /\b(glpat-[A-Za-z0-9_-]{20})\b/g, captureIndex: 1, slugHint: "gitlab_pat" },
|
|
94100
|
-
{ rule_id: "huggingface_token", regex: /\b(hf_[A-Za-z0-9]{34,})\b/g, captureIndex: 1, slugHint: "huggingface_token" },
|
|
94101
|
-
{ rule_id: "twilio_api_key", regex: /\b(SK[0-9a-f]{32})\b/g, captureIndex: 1, slugHint: "twilio_api_key" },
|
|
94102
|
-
{ rule_id: "mailgun_key", regex: /\b(key-[0-9a-f]{32})\b/g, captureIndex: 1, slugHint: "mailgun_key" },
|
|
94103
|
-
{ rule_id: "digitalocean_pat", regex: /\b(dop_v1_[a-f0-9]{64})\b/g, captureIndex: 1, slugHint: "digitalocean_token" },
|
|
94104
|
-
{ rule_id: "digitalocean_oauth", regex: /\b(doo_v1_[a-f0-9]{64})\b/g, captureIndex: 1, slugHint: "digitalocean_token" },
|
|
94105
|
-
{ rule_id: "digitalocean_refresh", regex: /\b(dor_v1_[a-f0-9]{64})\b/g, captureIndex: 1, slugHint: "digitalocean_token" },
|
|
94106
|
-
{ 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" },
|
|
94107
|
-
{ rule_id: "linear_api_key", regex: /\b(lin_api_[A-Za-z0-9]{40})\b/g, captureIndex: 1, slugHint: "linear_api_key" },
|
|
94108
|
-
{ rule_id: "shopify_access_token", regex: /\b(shpat_[a-fA-F0-9]{32})\b/g, captureIndex: 1, slugHint: "shopify_token" },
|
|
94109
|
-
{ rule_id: "shopify_shared_secret", regex: /\b(shpss_[a-fA-F0-9]{32})\b/g, captureIndex: 1, slugHint: "shopify_token" },
|
|
94110
|
-
{ rule_id: "shopify_private_app", regex: /\b(shppa_[a-fA-F0-9]{32})\b/g, captureIndex: 1, slugHint: "shopify_token" },
|
|
94111
|
-
{ rule_id: "square_access_token", regex: /\b(sq0atp-[A-Za-z0-9_-]{22})\b/g, captureIndex: 1, slugHint: "square_token" },
|
|
94112
|
-
{ rule_id: "square_oauth_secret", regex: /\b(sq0csp-[A-Za-z0-9_-]{43})\b/g, captureIndex: 1, slugHint: "square_token" },
|
|
94113
|
-
{ rule_id: "newrelic_key", regex: /\b(NRAK-[A-Z0-9]{27})\b/g, captureIndex: 1, slugHint: "newrelic_key" },
|
|
94114
|
-
{ rule_id: "notion_token", regex: /\b(ntn_[A-Za-z0-9]{46})\b/g, captureIndex: 1, slugHint: "notion_token" },
|
|
94115
|
-
{ rule_id: "planetscale_password", regex: /\b(pscale_pw_[A-Za-z0-9_.-]{43})\b/g, captureIndex: 1, slugHint: "planetscale_token" },
|
|
94116
|
-
{ rule_id: "planetscale_token", regex: /\b(pscale_tkn_[A-Za-z0-9_.-]{43})\b/g, captureIndex: 1, slugHint: "planetscale_token" },
|
|
94117
|
-
{ rule_id: "supabase_service_key", regex: /\b(sbp_[a-f0-9]{40})\b/g, captureIndex: 1, slugHint: "supabase_key" },
|
|
94118
|
-
{ rule_id: "atlassian_token", regex: /\b(ATATT[A-Za-z0-9_\-=]{20,})\b/g, captureIndex: 1, slugHint: "atlassian_token" },
|
|
94119
|
-
{ rule_id: "dropbox_token", regex: /\b(sl\.[A-Za-z0-9_-]{130,})/g, captureIndex: 1, slugHint: "dropbox_token" },
|
|
94120
|
-
{ rule_id: "databricks_token", regex: /\b(dapi[a-f0-9]{32})\b/g, captureIndex: 1, slugHint: "databricks_token" },
|
|
94121
|
-
{ 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" },
|
|
94122
|
-
{ rule_id: "pypi_token", regex: /\b(pypi-AgEIcHlwaS[A-Za-z0-9_-]{50,})/g, captureIndex: 1, slugHint: "pypi_token" },
|
|
94123
|
-
{ rule_id: "aws_temp_access_key", regex: /\b(ASIA[0-9A-Z]{16})\b/g, captureIndex: 1, slugHint: "aws_access_key" },
|
|
94124
|
-
{ rule_id: "gcp_oauth_token", regex: /\b(ya29\.[A-Za-z0-9_-]{30,})/g, captureIndex: 1, slugHint: "gcp_oauth_token" }
|
|
94125
|
-
];
|
|
94126
|
-
var ALL_PATTERNS = [...ANCHORED_PATTERNS, ...PROVIDER_PATTERNS, ...STRUCTURED_PATTERNS];
|
|
94127
|
-
|
|
94128
|
-
// telegram-plugin/secret-detect/entropy.ts
|
|
94129
|
-
function shannonEntropy(s) {
|
|
94130
|
-
if (s.length === 0)
|
|
94131
|
-
return 0;
|
|
94132
|
-
const counts = new Map;
|
|
94133
|
-
for (const ch of s) {
|
|
94134
|
-
counts.set(ch, (counts.get(ch) ?? 0) + 1);
|
|
94135
|
-
}
|
|
94136
|
-
let h = 0;
|
|
94137
|
-
const len = s.length;
|
|
94138
|
-
for (const c of counts.values()) {
|
|
94139
|
-
const p = c / len;
|
|
94140
|
-
h -= p * Math.log2(p);
|
|
94141
|
-
}
|
|
94142
|
-
return h;
|
|
94143
|
-
}
|
|
94144
|
-
|
|
94145
|
-
// telegram-plugin/secret-detect/kv-scanner.ts
|
|
94146
|
-
var KV_RE = /\b([A-Za-z_][A-Za-z0-9_-]*(?:password|passwd|token|secret|key|api[_-]?key))\s*[:=]\s*["']?([^\s"'\\]{8,})["']?/gi;
|
|
94147
|
-
var KV_ENTROPY_THRESHOLD = 4;
|
|
94148
|
-
function scanKeyValue(text) {
|
|
94149
|
-
const hits = [];
|
|
94150
|
-
KV_RE.lastIndex = 0;
|
|
94151
|
-
let m;
|
|
94152
|
-
while ((m = KV_RE.exec(text)) !== null) {
|
|
94153
|
-
const [, keyName, value] = m;
|
|
94154
|
-
if (!value)
|
|
94155
|
-
continue;
|
|
94156
|
-
const h = shannonEntropy(value);
|
|
94157
|
-
if (h < KV_ENTROPY_THRESHOLD)
|
|
94158
|
-
continue;
|
|
94159
|
-
const valueOffsetInMatch = m[0].indexOf(value, keyName.length);
|
|
94160
|
-
if (valueOffsetInMatch < 0)
|
|
94161
|
-
continue;
|
|
94162
|
-
const start = m.index + valueOffsetInMatch;
|
|
94163
|
-
const end = start + value.length;
|
|
94164
|
-
hits.push({
|
|
94165
|
-
rule_id: "kv_entropy",
|
|
94166
|
-
start,
|
|
94167
|
-
end,
|
|
94168
|
-
matched_text: value,
|
|
94169
|
-
key_name: keyName,
|
|
94170
|
-
confidence: "ambiguous"
|
|
94171
|
-
});
|
|
94172
|
-
}
|
|
94173
|
-
return hits;
|
|
94174
|
-
}
|
|
94175
|
-
|
|
94176
|
-
// telegram-plugin/secret-detect/generic-entropy.ts
|
|
94177
|
-
var CANDIDATE_RE = /[A-Za-z0-9]{28,}/g;
|
|
94178
|
-
var GENERIC_MIN_DISTINCT = 18;
|
|
94179
|
-
var MAX_GENERIC_HITS = 20;
|
|
94180
|
-
function hasDistinctChars(tok, n) {
|
|
94181
|
-
const seen = new Uint8Array(128);
|
|
94182
|
-
let distinct = 0;
|
|
94183
|
-
for (let i = 0;i < tok.length; i++) {
|
|
94184
|
-
const c = tok.charCodeAt(i);
|
|
94185
|
-
if (seen[c] === 0) {
|
|
94186
|
-
seen[c] = 1;
|
|
94187
|
-
if (++distinct >= n)
|
|
94188
|
-
return true;
|
|
94189
|
-
}
|
|
94190
|
-
}
|
|
94191
|
-
return false;
|
|
94192
|
-
}
|
|
94193
|
-
function hasDigit(tok) {
|
|
94194
|
-
for (let i = 0;i < tok.length; i++) {
|
|
94195
|
-
const c = tok.charCodeAt(i);
|
|
94196
|
-
if (c >= 48 && c <= 57)
|
|
94197
|
-
return true;
|
|
94198
|
-
}
|
|
94199
|
-
return false;
|
|
94200
|
-
}
|
|
94201
|
-
function scanGenericSecrets(text) {
|
|
94202
|
-
const hits = [];
|
|
94203
|
-
CANDIDATE_RE.lastIndex = 0;
|
|
94204
|
-
let m;
|
|
94205
|
-
while ((m = CANDIDATE_RE.exec(text)) !== null) {
|
|
94206
|
-
if (hits.length >= MAX_GENERIC_HITS)
|
|
94207
|
-
break;
|
|
94208
|
-
const tok = m[0];
|
|
94209
|
-
if (!hasDigit(tok))
|
|
94210
|
-
continue;
|
|
94211
|
-
if (!hasDistinctChars(tok, GENERIC_MIN_DISTINCT))
|
|
94212
|
-
continue;
|
|
94213
|
-
hits.push({
|
|
94214
|
-
rule_id: "generic_high_entropy",
|
|
94215
|
-
start: m.index,
|
|
94216
|
-
end: m.index + tok.length,
|
|
94217
|
-
matched_text: tok,
|
|
94218
|
-
confidence: "ambiguous"
|
|
94219
|
-
});
|
|
94220
|
-
}
|
|
94221
|
-
return hits;
|
|
94222
|
-
}
|
|
94223
|
-
|
|
94224
|
-
// telegram-plugin/secret-detect/chunker.ts
|
|
94225
|
-
var CHUNK_THRESHOLD = 32 * 1024;
|
|
94226
|
-
var WINDOW_SIZE = 16 * 1024;
|
|
94227
|
-
var OVERLAP = 8 * 1024;
|
|
94228
|
-
function chunk(text) {
|
|
94229
|
-
if (text.length <= CHUNK_THRESHOLD) {
|
|
94230
|
-
return [{ offset: 0, text }];
|
|
94231
|
-
}
|
|
94232
|
-
const out = [];
|
|
94233
|
-
let offset = 0;
|
|
94234
|
-
while (offset < text.length) {
|
|
94235
|
-
const end = Math.min(offset + WINDOW_SIZE, text.length);
|
|
94236
|
-
out.push({ offset, text: text.slice(offset, end) });
|
|
94237
|
-
if (end >= text.length)
|
|
94238
|
-
break;
|
|
94239
|
-
offset = end - OVERLAP;
|
|
94240
|
-
}
|
|
94241
|
-
return out;
|
|
94242
|
-
}
|
|
94243
|
-
|
|
94244
|
-
// telegram-plugin/secret-detect/suppressor.ts
|
|
94245
|
-
var MARKERS = ["test", "mock", "example", "fixture", "dummy"];
|
|
94246
|
-
var WINDOW = 40;
|
|
94247
|
-
var MARKER_RE = new RegExp(`\\b(?:${MARKERS.join("|")})\\b`, "i");
|
|
94248
|
-
function isSuppressed(text, start, end) {
|
|
94249
|
-
const left = Math.max(0, start - WINDOW);
|
|
94250
|
-
const right = Math.min(text.length, end + WINDOW);
|
|
94251
|
-
const context = text.slice(left, start) + text.slice(end, right);
|
|
94252
|
-
return MARKER_RE.test(context);
|
|
94253
|
-
}
|
|
94254
|
-
|
|
94255
|
-
// telegram-plugin/secret-detect/slug.ts
|
|
94256
|
-
function sanitizeKeyName(raw) {
|
|
94257
|
-
const up = raw.toUpperCase();
|
|
94258
|
-
const cleaned = up.replace(/[^A-Z0-9_]+/g, "_").replace(/_+/g, "_").replace(/^_+|_+$/g, "");
|
|
94259
|
-
return cleaned.length > 0 ? cleaned : "SECRET";
|
|
94260
|
-
}
|
|
94261
|
-
function datePart(now = new Date) {
|
|
94262
|
-
const y = now.getUTCFullYear();
|
|
94263
|
-
const m = String(now.getUTCMonth() + 1).padStart(2, "0");
|
|
94264
|
-
const d = String(now.getUTCDate()).padStart(2, "0");
|
|
94265
|
-
return `${y}${m}${d}`;
|
|
94266
|
-
}
|
|
94267
|
-
function deriveSlug(inputs, existing) {
|
|
94268
|
-
let base;
|
|
94269
|
-
if (inputs.key_name && inputs.key_name.trim().length > 0) {
|
|
94270
|
-
base = sanitizeKeyName(inputs.key_name);
|
|
94271
|
-
} else {
|
|
94272
|
-
base = `${inputs.rule_id}_${datePart(inputs.now)}`;
|
|
94273
|
-
}
|
|
94274
|
-
if (!existing.has(base))
|
|
94275
|
-
return base;
|
|
94276
|
-
let n = 2;
|
|
94277
|
-
while (existing.has(`${base}_${n}`))
|
|
94278
|
-
n++;
|
|
94279
|
-
return `${base}_${n}`;
|
|
94280
|
-
}
|
|
94281
|
-
// telegram-plugin/secret-detect/url-redact.ts
|
|
94282
|
-
var SENSITIVE_PARAMS = new Set([
|
|
94283
|
-
"token",
|
|
94284
|
-
"key",
|
|
94285
|
-
"api_key",
|
|
94286
|
-
"apikey",
|
|
94287
|
-
"secret",
|
|
94288
|
-
"access_token",
|
|
94289
|
-
"password",
|
|
94290
|
-
"pass",
|
|
94291
|
-
"auth",
|
|
94292
|
-
"client_secret",
|
|
94293
|
-
"refresh_token",
|
|
94294
|
-
"signature"
|
|
94295
|
-
]);
|
|
94296
|
-
var URL_RE = /\b(?:https?|wss?|ftp):\/\/[^\s<>"']+/gi;
|
|
94297
|
-
function redactUrls(text) {
|
|
94298
|
-
return text.replace(URL_RE, (m) => {
|
|
94299
|
-
const redacted = redactOne(m);
|
|
94300
|
-
return redacted ?? m;
|
|
94301
|
-
});
|
|
94302
|
-
}
|
|
94303
|
-
function redactOne(raw) {
|
|
94304
|
-
let u;
|
|
94305
|
-
try {
|
|
94306
|
-
u = new URL(raw);
|
|
94307
|
-
} catch {
|
|
94308
|
-
return null;
|
|
94309
|
-
}
|
|
94310
|
-
let changed = false;
|
|
94311
|
-
if (u.username || u.password) {
|
|
94312
|
-
u.username = "***";
|
|
94313
|
-
u.password = "";
|
|
94314
|
-
changed = true;
|
|
94315
|
-
}
|
|
94316
|
-
for (const [key] of u.searchParams) {
|
|
94317
|
-
if (SENSITIVE_PARAMS.has(key.toLowerCase())) {
|
|
94318
|
-
u.searchParams.set(key, "***");
|
|
94319
|
-
changed = true;
|
|
94320
|
-
}
|
|
94321
|
-
}
|
|
94322
|
-
return changed ? u.toString() : null;
|
|
94323
|
-
}
|
|
94324
|
-
|
|
94325
|
-
// telegram-plugin/secret-detect/index.ts
|
|
94326
|
-
function detectSecrets(text) {
|
|
94327
|
-
if (!text || text.length === 0)
|
|
94328
|
-
return [];
|
|
94329
|
-
const windows = chunk(text);
|
|
94330
|
-
const raw = [];
|
|
94331
|
-
for (const win of windows) {
|
|
94332
|
-
for (const p of ALL_PATTERNS) {
|
|
94333
|
-
const re = new RegExp(p.regex.source, p.regex.flags.includes("g") ? p.regex.flags : p.regex.flags + "g");
|
|
94334
|
-
let m;
|
|
94335
|
-
while ((m = re.exec(win.text)) !== null) {
|
|
94336
|
-
if (m[0].length === 0) {
|
|
94337
|
-
re.lastIndex++;
|
|
94338
|
-
continue;
|
|
94339
|
-
}
|
|
94340
|
-
const cap = p.captureIndex === 0 ? m[0] : m[p.captureIndex];
|
|
94341
|
-
if (!cap)
|
|
94342
|
-
continue;
|
|
94343
|
-
const matchStart = p.captureIndex === 0 ? m.index : m.index + m[0].indexOf(cap);
|
|
94344
|
-
if (matchStart < 0)
|
|
94345
|
-
continue;
|
|
94346
|
-
const globalStart = win.offset + matchStart;
|
|
94347
|
-
const globalEnd = globalStart + cap.length;
|
|
94348
|
-
const keyName = p.rule_id === "env_key_value" ? m[1] : undefined;
|
|
94349
|
-
if (p.rule_id === "env_key_value") {
|
|
94350
|
-
const ENV_KV_MIN_LEN = 12;
|
|
94351
|
-
const ENV_KV_MIN_ENTROPY = 3.5;
|
|
94352
|
-
if (cap.length < ENV_KV_MIN_LEN)
|
|
94353
|
-
continue;
|
|
94354
|
-
if (shannonEntropy(cap) < ENV_KV_MIN_ENTROPY)
|
|
94355
|
-
continue;
|
|
94356
|
-
}
|
|
94357
|
-
raw.push({
|
|
94358
|
-
rule_id: p.rule_id,
|
|
94359
|
-
start: globalStart,
|
|
94360
|
-
end: globalEnd,
|
|
94361
|
-
matched_text: cap,
|
|
94362
|
-
key_name: keyName,
|
|
94363
|
-
confidence: "high"
|
|
94364
|
-
});
|
|
94365
|
-
}
|
|
94366
|
-
}
|
|
94367
|
-
const kvHits = scanKeyValue(win.text);
|
|
94368
|
-
for (const h of kvHits) {
|
|
94369
|
-
raw.push({ ...h, start: h.start + win.offset, end: h.end + win.offset });
|
|
94370
|
-
}
|
|
94371
|
-
const genHits = scanGenericSecrets(win.text);
|
|
94372
|
-
for (const h of genHits) {
|
|
94373
|
-
raw.push({ ...h, start: h.start + win.offset, end: h.end + win.offset });
|
|
94374
|
-
}
|
|
94375
|
-
}
|
|
94376
|
-
const deduped = dedupeRaw(raw);
|
|
94377
|
-
const final = dropOverlaps(deduped);
|
|
94378
|
-
const existing = new Set;
|
|
94379
|
-
const out = [];
|
|
94380
|
-
for (const h of final) {
|
|
94381
|
-
const suggested_slug = deriveSlug({ key_name: h.key_name, rule_id: h.rule_id }, existing);
|
|
94382
|
-
existing.add(suggested_slug);
|
|
94383
|
-
out.push({
|
|
94384
|
-
rule_id: h.rule_id,
|
|
94385
|
-
matched_text: h.matched_text,
|
|
94386
|
-
start: h.start,
|
|
94387
|
-
end: h.end,
|
|
94388
|
-
confidence: h.confidence,
|
|
94389
|
-
suppressed: isSuppressed(text, h.start, h.end),
|
|
94390
|
-
suggested_slug,
|
|
94391
|
-
key_name: h.key_name
|
|
94392
|
-
});
|
|
94393
|
-
}
|
|
94394
|
-
out.sort((a, b) => a.start - b.start);
|
|
94395
|
-
return out;
|
|
94396
|
-
}
|
|
94397
|
-
function dedupeRaw(raw) {
|
|
94398
|
-
const seen = new Map;
|
|
94399
|
-
for (const h of raw) {
|
|
94400
|
-
const key = `${h.start}:${h.end}`;
|
|
94401
|
-
const existing = seen.get(key);
|
|
94402
|
-
if (!existing) {
|
|
94403
|
-
seen.set(key, h);
|
|
94404
|
-
continue;
|
|
94405
|
-
}
|
|
94406
|
-
if (existing.confidence === "ambiguous" && h.confidence === "high") {
|
|
94407
|
-
seen.set(key, h);
|
|
94408
|
-
}
|
|
94409
|
-
}
|
|
94410
|
-
return Array.from(seen.values());
|
|
94411
|
-
}
|
|
94412
|
-
function dropOverlaps(hits) {
|
|
94413
|
-
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))));
|
|
94414
|
-
out.sort((a, b) => a.start - b.start || a.end - b.end);
|
|
94415
|
-
return out;
|
|
94416
|
-
}
|
|
94417
|
-
|
|
94418
|
-
// telegram-plugin/secret-detect/redact.ts
|
|
94419
|
-
var REDACTED_MARKER = "[REDACTED]";
|
|
94420
|
-
function redact(text) {
|
|
94421
|
-
if (!text || text.length === 0)
|
|
94422
|
-
return text;
|
|
94423
|
-
const urlScrubbed = redactUrls(text);
|
|
94424
|
-
const hits = detectSecrets(urlScrubbed).filter((h) => h.rule_id !== "generic_high_entropy");
|
|
94425
|
-
if (hits.length === 0)
|
|
94426
|
-
return urlScrubbed;
|
|
94427
|
-
const sorted = [...hits].sort((a, b) => b.start - a.start);
|
|
94428
|
-
let out = urlScrubbed;
|
|
94429
|
-
for (const h of sorted) {
|
|
94430
|
-
out = out.slice(0, h.start) + redactedMarker(h.rule_id) + out.slice(h.end);
|
|
94431
|
-
}
|
|
94432
|
-
return out;
|
|
94433
|
-
}
|
|
94434
|
-
function redactedMarker(ruleId) {
|
|
94435
|
-
const trimmed = ruleId.replace(/^(kv|env)_/, "");
|
|
94436
|
-
if (!trimmed || trimmed === "key_value" || trimmed === "kv_entropy") {
|
|
94437
|
-
return REDACTED_MARKER;
|
|
94438
|
-
}
|
|
94439
|
-
return `[REDACTED:${trimmed}]`;
|
|
94440
|
-
}
|
|
94441
94676
|
// src/issues/store.ts
|
|
94442
94677
|
var ISSUES_FILE = "issues.jsonl";
|
|
94443
94678
|
var ISSUES_LOCK = "issues.lock";
|
|
@@ -98387,6 +98622,42 @@ function withSynthesizedTools(result) {
|
|
|
98387
98622
|
]
|
|
98388
98623
|
};
|
|
98389
98624
|
}
|
|
98625
|
+
var READ_ONLY_TOOL_NAMES = new Set([
|
|
98626
|
+
"recall",
|
|
98627
|
+
"reflect",
|
|
98628
|
+
"search",
|
|
98629
|
+
"list_memories",
|
|
98630
|
+
"list_directives",
|
|
98631
|
+
"list_mental_models",
|
|
98632
|
+
"get_mental_model",
|
|
98633
|
+
"get_memory",
|
|
98634
|
+
"get_bank"
|
|
98635
|
+
]);
|
|
98636
|
+
function redactToolArguments(value) {
|
|
98637
|
+
if (typeof value === "string")
|
|
98638
|
+
return redact(value);
|
|
98639
|
+
if (Array.isArray(value))
|
|
98640
|
+
return value.map(redactToolArguments);
|
|
98641
|
+
if (value && typeof value === "object") {
|
|
98642
|
+
const out = {};
|
|
98643
|
+
for (const [k, v] of Object.entries(value)) {
|
|
98644
|
+
out[k] = redactToolArguments(v);
|
|
98645
|
+
}
|
|
98646
|
+
return out;
|
|
98647
|
+
}
|
|
98648
|
+
return value;
|
|
98649
|
+
}
|
|
98650
|
+
function redactToolCallParams(params) {
|
|
98651
|
+
const called = params;
|
|
98652
|
+
if (!called || typeof called !== "object")
|
|
98653
|
+
return params;
|
|
98654
|
+
if (called.arguments === undefined || called.arguments === null)
|
|
98655
|
+
return params;
|
|
98656
|
+
if (typeof called.name === "string" && READ_ONLY_TOOL_NAMES.has(called.name)) {
|
|
98657
|
+
return params;
|
|
98658
|
+
}
|
|
98659
|
+
return { ...called, arguments: redactToolArguments(called.arguments) };
|
|
98660
|
+
}
|
|
98390
98661
|
function buildFallbackToolsList() {
|
|
98391
98662
|
const tools = Object.entries(FALLBACK_TOOL_TABLE).map(([name, [required, props]]) => ({
|
|
98392
98663
|
name,
|
|
@@ -98713,8 +98984,9 @@ class HindsightShim {
|
|
|
98713
98984
|
if (called?.name && SYNTHESIZED_TOOL_NAMES.includes(called.name)) {
|
|
98714
98985
|
return this.synthesizedCall(called.name, called.arguments);
|
|
98715
98986
|
}
|
|
98987
|
+
const forwarded = redactToolCallParams(params);
|
|
98716
98988
|
try {
|
|
98717
|
-
const res = await this.upstream.request("tools/call",
|
|
98989
|
+
const res = await this.upstream.request("tools/call", forwarded, this.opts.toolsCallTimeoutMs ?? TOOLS_CALL_TIMEOUT_MS);
|
|
98718
98990
|
this.notifyIfRecovered();
|
|
98719
98991
|
if (res.error) {
|
|
98720
98992
|
return {
|
|
@@ -104653,7 +104925,7 @@ function probeSpool(agentsDir = resolveAgentsDir2()) {
|
|
|
104653
104925
|
}
|
|
104654
104926
|
return { pending, dead, evicted, drops, agents };
|
|
104655
104927
|
}
|
|
104656
|
-
var
|
|
104928
|
+
var PSQL_BOOTSTRAP_SH = `
|
|
104657
104929
|
set -e
|
|
104658
104930
|
B="$(ls -d /home/hindsight/.pg0/installation/*/bin 2>/dev/null | head -1)"
|
|
104659
104931
|
PSQL="$(command -v psql 2>/dev/null || echo "\${B:-/nonexistent}/psql")"
|
|
@@ -104670,9 +104942,63 @@ d=json.load(open(sys.argv[1]))
|
|
|
104670
104942
|
q=lambda k,dflt: shlex.quote(str(d.get(k) or dflt))
|
|
104671
104943
|
print("U=%s DB=%s P=%s PW=%s"%(q("username","hindsight"),q("database","hindsight"),q("port",5432),q("password","")))' "$D")"
|
|
104672
104944
|
[ -n "$PW" ] || exit 0
|
|
104945
|
+
`;
|
|
104946
|
+
var CONSOLIDATION_QUEUE_SH = `${PSQL_BOOTSTRAP_SH}
|
|
104673
104947
|
PGPASSWORD="$PW" "$PSQL" -U "$U" -h /tmp -p "$P" -d "$DB" -tAc \\
|
|
104674
104948
|
"SELECT count(*)||'|'||coalesce(max(extract(epoch from (now()-created_at)))::bigint,0) FROM async_operations WHERE status IN ('pending','processing')"
|
|
104675
104949
|
`;
|
|
104950
|
+
var BANK_CONSOLIDATION_SH = `${PSQL_BOOTSTRAP_SH}
|
|
104951
|
+
PGPASSWORD="$PW" "$PSQL" -U "$U" -h /tmp -p "$P" -d "$DB" -tAc \\
|
|
104952
|
+
"SELECT 'S|'||a.bank_id||'|'||a.operation_type||'|'||count(*)||'|'||extract(epoch from (now()-max(a.created_at)))::bigint
|
|
104953
|
+
FROM async_operations a
|
|
104954
|
+
WHERE a.status = 'failed'
|
|
104955
|
+
AND a.created_at > coalesce((SELECT max(b.created_at) FROM async_operations b
|
|
104956
|
+
WHERE b.bank_id = a.bank_id
|
|
104957
|
+
AND b.operation_type = a.operation_type
|
|
104958
|
+
AND b.status = 'completed'), '-infinity'::timestamptz)
|
|
104959
|
+
GROUP BY a.bank_id, a.operation_type
|
|
104960
|
+
UNION ALL
|
|
104961
|
+
SELECT 'P|'||m.bank_id||'|'||count(*) FILTER (WHERE m.consolidated_at IS NULL AND m.fact_type IN ('experience','world'))
|
|
104962
|
+
FROM memory_units m
|
|
104963
|
+
GROUP BY m.bank_id"
|
|
104964
|
+
`;
|
|
104965
|
+
function vectorIndexSh(sampleRows) {
|
|
104966
|
+
const limit = Math.max(1, Math.floor(Number.isFinite(sampleRows) ? sampleRows : 1));
|
|
104967
|
+
return `${PSQL_BOOTSTRAP_SH}
|
|
104968
|
+
PGPASSWORD="$PW" PGOPTIONS='-c client_min_messages=notice' \\
|
|
104969
|
+
"$PSQL" -U "$U" -h /tmp -p "$P" -d "$DB" -tAc "
|
|
104970
|
+
DO \\$\\$
|
|
104971
|
+
DECLARE r record; bad text := ''; n int := 0; probed int := 0;
|
|
104972
|
+
BEGIN
|
|
104973
|
+
FOR r IN
|
|
104974
|
+
SELECT c.relname AS idx,
|
|
104975
|
+
i.indrelid::regclass::text AS tbl,
|
|
104976
|
+
coalesce(pg_get_expr(i.indpred, i.indrelid), 'true') AS pred
|
|
104977
|
+
FROM pg_index i
|
|
104978
|
+
JOIN pg_class c ON c.oid = i.indexrelid
|
|
104979
|
+
JOIN pg_am am ON am.oid = c.relam
|
|
104980
|
+
WHERE am.amname = 'hnsw'
|
|
104981
|
+
ORDER BY c.relname
|
|
104982
|
+
LOOP
|
|
104983
|
+
BEGIN
|
|
104984
|
+
EXECUTE format(
|
|
104985
|
+
'SELECT count(nn) FROM (SELECT (SELECT m2.ctid FROM %1\\$s m2 WHERE %2\\$s ORDER BY m2.embedding <=> s.embedding LIMIT 1) AS nn FROM (SELECT embedding FROM %1\\$s s0 WHERE %2\\$s AND embedding IS NOT NULL LIMIT %3\\$s) s) t',
|
|
104986
|
+
r.tbl, r.pred, ${limit});
|
|
104987
|
+
probed := probed + 1;
|
|
104988
|
+
EXCEPTION WHEN others THEN
|
|
104989
|
+
n := n + 1;
|
|
104990
|
+
-- Both delimiters are stripped from the message BEFORE it is appended.
|
|
104991
|
+
-- ';' separates entries and a newline would split the NOTICE line, so an
|
|
104992
|
+
-- SQLERRM containing either would desynchronise the parsed list from n,
|
|
104993
|
+
-- and probeVectorIndexes rejects a mismatched block as unreadable \u2014
|
|
104994
|
+
-- turning a real corruption into a silent no-data.
|
|
104995
|
+
bad := bad || r.idx || '=' || translate(SQLERRM, ';' || chr(10) || chr(13), ', ') || ';';
|
|
104996
|
+
END;
|
|
104997
|
+
END LOOP;
|
|
104998
|
+
RAISE NOTICE 'VECIDX|%|%|%', probed, n, bad;
|
|
104999
|
+
END \\$\\$;" 2>&1
|
|
105000
|
+
`;
|
|
105001
|
+
}
|
|
104676
105002
|
function probeConsolidationQueue(container = DEFAULT_CONTAINER, run = defaultRunner4) {
|
|
104677
105003
|
const r = run("docker", ["exec", container, "sh", "-c", CONSOLIDATION_QUEUE_SH]);
|
|
104678
105004
|
if (r.status !== 0)
|
|
@@ -104686,6 +105012,61 @@ function probeConsolidationQueue(container = DEFAULT_CONTAINER, run = defaultRun
|
|
|
104686
105012
|
return null;
|
|
104687
105013
|
return { pending, oldestAgeS };
|
|
104688
105014
|
}
|
|
105015
|
+
function probeBankConsolidation(container = DEFAULT_CONTAINER, run = defaultRunner4) {
|
|
105016
|
+
const r = run("docker", ["exec", container, "sh", "-c", BANK_CONSOLIDATION_SH]);
|
|
105017
|
+
if (r.status !== 0)
|
|
105018
|
+
return null;
|
|
105019
|
+
const streaks = [];
|
|
105020
|
+
const pending = [];
|
|
105021
|
+
let parsedAny = false;
|
|
105022
|
+
for (const line of r.stdout.split(`
|
|
105023
|
+
`)) {
|
|
105024
|
+
const t = line.trim();
|
|
105025
|
+
if (t === "")
|
|
105026
|
+
continue;
|
|
105027
|
+
const f = t.split("|");
|
|
105028
|
+
if (f[0] === "S" && f.length === 5) {
|
|
105029
|
+
const streak = Number(f[3]);
|
|
105030
|
+
const age = Number(f[4]);
|
|
105031
|
+
if (!Number.isFinite(streak) || !Number.isFinite(age) || streak < 0 || age < 0)
|
|
105032
|
+
continue;
|
|
105033
|
+
streaks.push({ bank: f[1], operationType: f[2], streak, newestFailureAgeS: age });
|
|
105034
|
+
parsedAny = true;
|
|
105035
|
+
} else if (f[0] === "P" && f.length === 3) {
|
|
105036
|
+
const n = Number(f[2]);
|
|
105037
|
+
if (!Number.isFinite(n) || n < 0)
|
|
105038
|
+
continue;
|
|
105039
|
+
pending.push({ bank: f[1], pending: n });
|
|
105040
|
+
parsedAny = true;
|
|
105041
|
+
}
|
|
105042
|
+
}
|
|
105043
|
+
if (!parsedAny)
|
|
105044
|
+
return null;
|
|
105045
|
+
return { streaks, pending };
|
|
105046
|
+
}
|
|
105047
|
+
function probeVectorIndexes(container = DEFAULT_CONTAINER, run = defaultRunner4, sampleRows = VECTOR_INDEX_SAMPLE_ROWS) {
|
|
105048
|
+
const sh = vectorIndexSh(sampleRows);
|
|
105049
|
+
if (!sh.includes("count(nn)"))
|
|
105050
|
+
return null;
|
|
105051
|
+
const r = run("docker", ["exec", container, "sh", "-c", sh]);
|
|
105052
|
+
if (r.status !== 0)
|
|
105053
|
+
return null;
|
|
105054
|
+
const line = r.stdout.split(`
|
|
105055
|
+
`).find((l) => l.includes("VECIDX|"));
|
|
105056
|
+
if (line === undefined)
|
|
105057
|
+
return null;
|
|
105058
|
+
const f = line.slice(line.indexOf("VECIDX|")).trim().split("|");
|
|
105059
|
+
if (f.length < 4)
|
|
105060
|
+
return null;
|
|
105061
|
+
const probed = Number(f[1]);
|
|
105062
|
+
const count = Number(f[2]);
|
|
105063
|
+
if (!Number.isFinite(probed) || !Number.isFinite(count) || probed < 0 || count < 0)
|
|
105064
|
+
return null;
|
|
105065
|
+
const corrupt = f.slice(3).join("|").split(";").map((s) => s.trim()).filter((s) => s !== "");
|
|
105066
|
+
if (corrupt.length !== count)
|
|
105067
|
+
return null;
|
|
105068
|
+
return { probed, corrupt };
|
|
105069
|
+
}
|
|
104689
105070
|
async function probeOnce(opts = {}) {
|
|
104690
105071
|
const now = opts.now ?? Date.now;
|
|
104691
105072
|
const ts = now();
|
|
@@ -104706,6 +105087,8 @@ async function probeOnce(opts = {}) {
|
|
|
104706
105087
|
throw new ProbeError(`reading recall logs: ${e.message}`, "spool");
|
|
104707
105088
|
}
|
|
104708
105089
|
const consolidation = probeConsolidationQueue(opts.container, opts.run);
|
|
105090
|
+
const banks = probeBankConsolidation(opts.container, opts.run);
|
|
105091
|
+
const vectorIndex = probeVectorIndexes(opts.container, opts.run);
|
|
104709
105092
|
return {
|
|
104710
105093
|
sample: {
|
|
104711
105094
|
ts,
|
|
@@ -104721,7 +105104,9 @@ async function probeOnce(opts = {}) {
|
|
|
104721
105104
|
startedAt: container.startedAt,
|
|
104722
105105
|
health: container.health,
|
|
104723
105106
|
recall,
|
|
104724
|
-
consolidation
|
|
105107
|
+
consolidation,
|
|
105108
|
+
banks,
|
|
105109
|
+
vectorIndex
|
|
104725
105110
|
},
|
|
104726
105111
|
spool,
|
|
104727
105112
|
recall
|
|
@@ -105051,6 +105436,136 @@ function evaluateConsolidationQueueAge(ring) {
|
|
|
105051
105436
|
measured: { pending: c.pending, oldestAgeS: c.oldestAgeS }
|
|
105052
105437
|
};
|
|
105053
105438
|
}
|
|
105439
|
+
function evaluateConsolidationFailureStreak(ring) {
|
|
105440
|
+
const signal = "consolidation-failure-streak";
|
|
105441
|
+
if (ring.length === 0)
|
|
105442
|
+
return { signal, state: "no-data", detail: "no samples" };
|
|
105443
|
+
const banks = ring[ring.length - 1].banks ?? null;
|
|
105444
|
+
if (banks === null) {
|
|
105445
|
+
return {
|
|
105446
|
+
signal,
|
|
105447
|
+
state: "no-data",
|
|
105448
|
+
detail: "per-bank consolidation not probed this tick (no psql client, no pg descriptor, " + "or the container is down) \u2014 the `container` signal covers the last of those"
|
|
105449
|
+
};
|
|
105450
|
+
}
|
|
105451
|
+
const live = banks.streaks.filter((s) => s.newestFailureAgeS <= CONSOLIDATION_STREAK_RECENCY_S);
|
|
105452
|
+
let worst = null;
|
|
105453
|
+
for (const s of live)
|
|
105454
|
+
if (worst === null || s.streak > worst.streak)
|
|
105455
|
+
worst = s;
|
|
105456
|
+
if (worst === null || worst.streak < CONSOLIDATION_FAILURE_STREAK_WARN) {
|
|
105457
|
+
const observed = worst?.streak ?? 0;
|
|
105458
|
+
return {
|
|
105459
|
+
signal,
|
|
105460
|
+
state: "ok",
|
|
105461
|
+
detail: `longest live consolidation-failure streak ${observed} across ` + `${banks.streaks.length} bank/op pair(s) \u2014 warn \u2265${CONSOLIDATION_FAILURE_STREAK_WARN}, ` + `page \u2265${CONSOLIDATION_FAILURE_STREAK_PAGE}`,
|
|
105462
|
+
measured: { streak: observed, pairs: banks.streaks.length }
|
|
105463
|
+
};
|
|
105464
|
+
}
|
|
105465
|
+
const { state, severity } = scoreHigherIsWorse(worst.streak, CONSOLIDATION_FAILURE_STREAK_WARN, CONSOLIDATION_FAILURE_STREAK_PAGE);
|
|
105466
|
+
const breaching = live.filter((s) => s.streak >= CONSOLIDATION_FAILURE_STREAK_WARN);
|
|
105467
|
+
const others = breaching.filter((s) => s !== worst).map((s) => `${s.bank}/${s.operationType}\u00d7${s.streak}`);
|
|
105468
|
+
return {
|
|
105469
|
+
signal,
|
|
105470
|
+
state,
|
|
105471
|
+
severity,
|
|
105472
|
+
detail: `bank "${worst.bank}": ${worst.streak} consecutive FAILED ${worst.operationType} ` + `operation(s) with no completion in between, newest ` + `${Math.round(worst.newestFailureAgeS / 60)}m ago \u2014 ` + `warn \u2265${CONSOLIDATION_FAILURE_STREAK_WARN}, page \u2265${CONSOLIDATION_FAILURE_STREAK_PAGE}. ` + "The pending/processing queue reads EMPTY while this holds, so " + "`consolidation-queue-age` cannot see it." + (others.length > 0 ? `
|
|
105473
|
+
also breaching: ${others.join(", ")}` : ""),
|
|
105474
|
+
measured: {
|
|
105475
|
+
streak: worst.streak,
|
|
105476
|
+
bank: worst.bank,
|
|
105477
|
+
operationType: worst.operationType,
|
|
105478
|
+
newestFailureAgeS: worst.newestFailureAgeS,
|
|
105479
|
+
breachingPairs: breaching.length
|
|
105480
|
+
}
|
|
105481
|
+
};
|
|
105482
|
+
}
|
|
105483
|
+
function evaluatePendingConsolidationDepth(ring) {
|
|
105484
|
+
const signal = "pending-consolidation-depth";
|
|
105485
|
+
const withBanks = ring.filter((s) => (s.banks ?? null) !== null);
|
|
105486
|
+
if (withBanks.length < 2) {
|
|
105487
|
+
return {
|
|
105488
|
+
signal,
|
|
105489
|
+
state: "no-data",
|
|
105490
|
+
detail: `only ${withBanks.length} tick(s) carried a per-bank block \u2014 need two to see a trend`,
|
|
105491
|
+
measured: { samples: withBanks.length }
|
|
105492
|
+
};
|
|
105493
|
+
}
|
|
105494
|
+
const first = new Map(withBanks[0].banks.pending.map((p) => [p.bank, p.pending]));
|
|
105495
|
+
const last = withBanks[withBanks.length - 1].banks.pending;
|
|
105496
|
+
const mins = windowMinutes(withBanks);
|
|
105497
|
+
let worst = null;
|
|
105498
|
+
for (const p of last) {
|
|
105499
|
+
const from = first.get(p.bank);
|
|
105500
|
+
if (from === undefined)
|
|
105501
|
+
continue;
|
|
105502
|
+
const growth = p.pending - from;
|
|
105503
|
+
const need = Math.max(PENDING_CONSOLIDATION_GROWTH_WARN_ABS, Math.ceil(PENDING_CONSOLIDATION_GROWTH_FRACTION * from));
|
|
105504
|
+
if (p.pending < PENDING_CONSOLIDATION_FLOOR || growth < need)
|
|
105505
|
+
continue;
|
|
105506
|
+
if (worst === null || growth > worst.growth) {
|
|
105507
|
+
worst = { bank: p.bank, from, to: p.pending, growth, need };
|
|
105508
|
+
}
|
|
105509
|
+
}
|
|
105510
|
+
if (worst === null) {
|
|
105511
|
+
return {
|
|
105512
|
+
signal,
|
|
105513
|
+
state: "ok",
|
|
105514
|
+
detail: `no bank's unconsolidated depth is both \u2265${PENDING_CONSOLIDATION_FLOOR} and rising ` + `over ${mins}m (${last.length} bank(s))`,
|
|
105515
|
+
measured: { banks: last.length, windowMinutes: mins }
|
|
105516
|
+
};
|
|
105517
|
+
}
|
|
105518
|
+
const { state, severity } = scoreHigherIsWorse(worst.growth, PENDING_CONSOLIDATION_GROWTH_WARN_ABS, PENDING_CONSOLIDATION_GROWTH_PAGE_ABS);
|
|
105519
|
+
const perHour = mins > 0 ? Math.round(worst.growth * 60 / mins) : worst.growth;
|
|
105520
|
+
return {
|
|
105521
|
+
signal,
|
|
105522
|
+
state,
|
|
105523
|
+
severity,
|
|
105524
|
+
detail: `bank "${worst.bank}": unconsolidated memories ${worst.from} \u2192 ${worst.to} ` + `(+${worst.growth}, \u2248${perHour}/h) over ${mins}m \u2014 fires at ` + `\u2265${PENDING_CONSOLIDATION_FLOOR} deep AND +${worst.need} growth, pages at ` + `+${PENDING_CONSOLIDATION_GROWTH_PAGE_ABS}. Rising depth with no failed operations ` + `means consolidation is not being attempted at all.`,
|
|
105525
|
+
measured: {
|
|
105526
|
+
bank: worst.bank,
|
|
105527
|
+
pending: worst.to,
|
|
105528
|
+
growth: worst.growth,
|
|
105529
|
+
growthPerHour: perHour,
|
|
105530
|
+
growthNeeded: worst.need,
|
|
105531
|
+
windowMinutes: mins
|
|
105532
|
+
}
|
|
105533
|
+
};
|
|
105534
|
+
}
|
|
105535
|
+
function evaluateVectorIndexCorruption(ring) {
|
|
105536
|
+
const signal = "vector-index-corruption";
|
|
105537
|
+
if (ring.length === 0)
|
|
105538
|
+
return { signal, state: "no-data", detail: "no samples" };
|
|
105539
|
+
const v = ring[ring.length - 1].vectorIndex ?? null;
|
|
105540
|
+
if (v === null) {
|
|
105541
|
+
return {
|
|
105542
|
+
signal,
|
|
105543
|
+
state: "no-data",
|
|
105544
|
+
detail: "vector-index canary did not run this tick (no psql client, no pg descriptor, " + "or the container is down) \u2014 the `container` signal covers the last of those"
|
|
105545
|
+
};
|
|
105546
|
+
}
|
|
105547
|
+
if (v.corrupt.length === 0) {
|
|
105548
|
+
return {
|
|
105549
|
+
signal,
|
|
105550
|
+
state: "ok",
|
|
105551
|
+
detail: `${v.probed} HNSW index(es) answered a nearest-neighbour probe cleanly`,
|
|
105552
|
+
measured: { probed: v.probed, corrupt: 0 }
|
|
105553
|
+
};
|
|
105554
|
+
}
|
|
105555
|
+
const shown = v.corrupt.slice(0, 5).join(`
|
|
105556
|
+
`);
|
|
105557
|
+
const more = v.corrupt.length > 5 ? `
|
|
105558
|
+
\u2026and ${v.corrupt.length - 5} more` : "";
|
|
105559
|
+
return {
|
|
105560
|
+
signal,
|
|
105561
|
+
state: "breach",
|
|
105562
|
+
severity: "page",
|
|
105563
|
+
detail: `${v.corrupt.length} of ${v.probed + v.corrupt.length} HNSW index(es) RAISED on a ` + `nearest-neighbour probe \u2014 the index is corrupt, and every consolidation or recall ` + `that touches it fails:
|
|
105564
|
+
${shown}${more}
|
|
105565
|
+
` + `Fix: REINDEX the named index(es). amcheck cannot verify HNSW, so this probe is the ` + `only detector.`,
|
|
105566
|
+
measured: { probed: v.probed, corrupt: v.corrupt.length, first: v.corrupt[0] }
|
|
105567
|
+
};
|
|
105568
|
+
}
|
|
105054
105569
|
function evaluateLlmFallback(ring) {
|
|
105055
105570
|
const signal = "llm-fallback-ineffective";
|
|
105056
105571
|
if (ring.length < 2) {
|
|
@@ -105099,6 +105614,9 @@ function evaluateAll(ring) {
|
|
|
105099
105614
|
evaluateRecallInjectedScore(ring),
|
|
105100
105615
|
evaluateRecallZeroMemory(ring),
|
|
105101
105616
|
evaluateConsolidationQueueAge(ring),
|
|
105617
|
+
evaluateConsolidationFailureStreak(ring),
|
|
105618
|
+
evaluatePendingConsolidationDepth(ring),
|
|
105619
|
+
evaluateVectorIndexCorruption(ring),
|
|
105102
105620
|
evaluateLlmFallback(ring)
|
|
105103
105621
|
];
|
|
105104
105622
|
}
|
|
@@ -105107,7 +105625,12 @@ function evaluateAll(ring) {
|
|
|
105107
105625
|
init_state2();
|
|
105108
105626
|
init_thresholds();
|
|
105109
105627
|
init_types5();
|
|
105110
|
-
var EDGE_SIGNALS = new Set([
|
|
105628
|
+
var EDGE_SIGNALS = new Set([
|
|
105629
|
+
"retain-loss",
|
|
105630
|
+
"container",
|
|
105631
|
+
"consolidation-failure-streak",
|
|
105632
|
+
"vector-index-corruption"
|
|
105633
|
+
]);
|
|
105111
105634
|
function breachesToFire(signal) {
|
|
105112
105635
|
return EDGE_SIGNALS.has(signal) ? BREACHES_TO_FIRE_EDGE : BREACHES_TO_FIRE_LEVEL;
|
|
105113
105636
|
}
|