switchroom 0.19.41 → 0.19.43
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/agent-scheduler/index.js +23 -3
- package/dist/auth-broker/index.js +54 -9
- package/dist/cli/notion-write-pretool.mjs +23 -3
- package/dist/cli/switchroom.js +1956 -900
- package/dist/host-control/main.js +227 -30
- package/dist/vault/approvals/kernel-server.js +54 -9
- package/dist/vault/broker/server.js +54 -9
- package/package.json +3 -3
- package/profiles/_base/start.sh.hbs +82 -4
- package/telegram-plugin/bridge/bridge.ts +1 -1
- package/telegram-plugin/card-layout.ts +63 -1
- package/telegram-plugin/dist/bridge/bridge.js +4 -1
- package/telegram-plugin/dist/gateway/gateway.js +1318 -693
- package/telegram-plugin/dist/server.js +5 -2
- package/telegram-plugin/flood-429-ledger.ts +5 -3
- package/telegram-plugin/flood-circuit-breaker.ts +11 -3
- package/telegram-plugin/format.ts +223 -13
- package/telegram-plugin/gateway/approval-callback-consume-record.test.ts +132 -0
- package/telegram-plugin/gateway/boot-card.ts +7 -2
- package/telegram-plugin/gateway/flood-reply-queue.ts +5 -0
- package/telegram-plugin/gateway/gateway.ts +56 -58
- package/telegram-plugin/gateway/handback-orphan-recovery.ts +69 -0
- package/telegram-plugin/gateway/handback-preturn-signal.ts +170 -32
- package/telegram-plugin/gateway/ipc-protocol.ts +31 -3
- package/telegram-plugin/gateway/ipc-server.ts +13 -4
- package/telegram-plugin/gateway/liveness-wiring.ts +12 -0
- package/telegram-plugin/gateway/model-command.ts +21 -109
- package/telegram-plugin/gateway/obligation-store.ts +55 -0
- package/telegram-plugin/gateway/outbound-send-path.ts +127 -11
- package/telegram-plugin/gateway/outbox-sweep.ts +371 -61
- package/telegram-plugin/gateway/rollout-status-edit.ts +175 -0
- package/telegram-plugin/gateway/stream-render.ts +62 -0
- package/telegram-plugin/gateway/update-announce.ts +11 -4
- package/telegram-plugin/hooks/audience-classify.d.mts +50 -0
- package/telegram-plugin/hooks/audience-classify.mjs +364 -0
- package/telegram-plugin/hooks/silent-end-interrupt-stop.mjs +86 -3
- package/telegram-plugin/hooks/silent-end-scan.mjs +80 -4
- package/telegram-plugin/outbox.ts +103 -2
- package/telegram-plugin/render/rich-render.ts +10 -2
- package/telegram-plugin/reply-quote.ts +150 -0
- package/telegram-plugin/retry-api-call.ts +254 -11
- package/telegram-plugin/shared/bot-runtime.ts +69 -34
- package/telegram-plugin/shared/gw-trace-gate.ts +14 -2
- package/telegram-plugin/silent-end.ts +34 -2
- package/telegram-plugin/status-no-truncate.ts +31 -16
- package/telegram-plugin/stream-controller.ts +44 -9
- package/telegram-plugin/stream-reply-handler.ts +5 -3
- package/telegram-plugin/tests/boot-card-render.test.ts +2 -1
- package/telegram-plugin/tests/card-budget-never-overflows.test.ts +165 -0
- package/telegram-plugin/tests/card-hard-truncate-entity-safe.test.ts +153 -0
- package/telegram-plugin/tests/card-variants.golden.txt +9 -9
- package/telegram-plugin/tests/flood-reply-queue.test.ts +89 -4
- package/telegram-plugin/tests/format-consistency.test.ts +160 -14
- package/telegram-plugin/tests/framework-fallback-drains-parked.test.ts +134 -0
- package/telegram-plugin/tests/handback-orphan-recovery.test.ts +131 -0
- package/telegram-plugin/tests/handback-preturn-signal.test.ts +212 -17
- package/telegram-plugin/tests/helpers/liveness-wiring-fixture.ts +3 -0
- package/telegram-plugin/tests/nested-indent-idiom.test.ts +162 -0
- package/telegram-plugin/tests/nested-prefix-indent.test.ts +115 -0
- package/telegram-plugin/tests/no-robust-api-call-factory.test.ts +62 -0
- package/telegram-plugin/tests/obligation-store.test.ts +51 -0
- package/telegram-plugin/tests/outbox-audience-3865.test.ts +579 -0
- package/telegram-plugin/tests/outbox-provenance-4141.test.ts +1126 -0
- package/telegram-plugin/tests/outbox-sweep-single-flight.test.ts +138 -0
- package/telegram-plugin/tests/pinned-card-collapse.test.ts +12 -10
- package/telegram-plugin/tests/plain-fallback-4096-cap.test.ts +509 -0
- package/telegram-plugin/tests/reply-quote-wire.test.ts +433 -0
- package/telegram-plugin/tests/retry-grammy-http-error.test.ts +404 -0
- package/telegram-plugin/tests/rollout-narration-edit-socket.test.ts +170 -0
- package/telegram-plugin/tests/rollout-status-edit-retry-policy.test.ts +366 -0
- package/telegram-plugin/tests/rollout-status-edit.test.ts +140 -0
- package/telegram-plugin/tests/rollout-status-wiring.test.ts +40 -7
- package/telegram-plugin/tests/tg-post-logger-error-shape.test.ts +4 -0
- package/telegram-plugin/tests/tg-post-retry-attribution.test.ts +196 -0
- package/telegram-plugin/tests/tool-activity-summary.test.ts +23 -17
- package/telegram-plugin/tool-activity-summary.ts +58 -30
- package/telegram-plugin/uat/scenarios/jtbd-foreground-subagent-activity-dm.test.ts +3 -2
- package/vendor/hindsight-memory/CHANGELOG.md +28 -0
- package/vendor/hindsight-memory/docs/measurements/subagent-volume-gate-3994.md +84 -0
- package/vendor/hindsight-memory/scripts/backfill_transcripts.py +254 -40
- package/vendor/hindsight-memory/scripts/lib/content.py +11 -2
- package/vendor/hindsight-memory/scripts/recall.py +18 -3
- package/vendor/hindsight-memory/scripts/subagent_retain.py +59 -38
- package/vendor/hindsight-memory/scripts/tests/data/replay_volume_gate_3994.py +295 -0
- package/vendor/hindsight-memory/scripts/tests/test_backfill_from_logs.py +109 -0
- package/vendor/hindsight-memory/scripts/tests/test_overlap_tokens.py +135 -0
- package/vendor/hindsight-memory/scripts/tests/test_recall_no_lexical_gate.py +20 -23
- package/vendor/hindsight-memory/scripts/tests/test_recall_query_shaping.py +48 -0
- package/vendor/hindsight-memory/scripts/tests/test_subagent_retain.py +94 -1
- package/vendor/hindsight-memory/scripts/tests/test_subagent_retain_learnings.py +98 -42
|
@@ -6965,7 +6965,9 @@ function stripExcessBold(text, onStrip) {
|
|
|
6965
6965
|
const ratio = boldChars / visible.length;
|
|
6966
6966
|
const blocks = masked.split(/\n{2,}/);
|
|
6967
6967
|
if (ratio > 0.3) {
|
|
6968
|
-
const
|
|
6968
|
+
const contentBlocks = blocks.filter((b) => stripPlaceholders(b).trim() !== "");
|
|
6969
|
+
const allHeadings = contentBlocks.length > 0 && contentBlocks.every(isPseudoHeadingBlock);
|
|
6970
|
+
const rebuilt2 = blocks.map((block) => !allHeadings && isPseudoHeadingBlock(block) ? block : unbold(block));
|
|
6969
6971
|
const out2 = rejoinBlocks(masked, rebuilt2);
|
|
6970
6972
|
if (out2 !== masked)
|
|
6971
6973
|
onStrip?.({ rule: "global", ratio });
|
|
@@ -7114,6 +7116,25 @@ function hardSliceToCap(text, cap = RICH_MESSAGE_MAX_CHARS) {
|
|
|
7114
7116
|
}
|
|
7115
7117
|
return out;
|
|
7116
7118
|
}
|
|
7119
|
+
function splitPlainTextToCap(text, cap = PLAIN_TEXT_MAX_CHARS) {
|
|
7120
|
+
if (cap <= 0)
|
|
7121
|
+
return [text];
|
|
7122
|
+
if (text.length <= cap)
|
|
7123
|
+
return [text];
|
|
7124
|
+
const out = [];
|
|
7125
|
+
for (const piece of splitMarkdownChunks(text, cap)) {
|
|
7126
|
+
if (piece.length <= cap) {
|
|
7127
|
+
if (piece.length > 0)
|
|
7128
|
+
out.push(piece);
|
|
7129
|
+
continue;
|
|
7130
|
+
}
|
|
7131
|
+
for (const slice of hardSliceToCap(piece, cap)) {
|
|
7132
|
+
if (slice.length > 0)
|
|
7133
|
+
out.push(slice);
|
|
7134
|
+
}
|
|
7135
|
+
}
|
|
7136
|
+
return out.length > 0 ? out : [text];
|
|
7137
|
+
}
|
|
7117
7138
|
function splitMarkdownChunks(text, maxLen = RICH_MESSAGE_MAX_CHARS) {
|
|
7118
7139
|
if (text.length <= maxLen)
|
|
7119
7140
|
return [text];
|
|
@@ -7138,9 +7159,7 @@ function splitMarkdownChunks(text, maxLen = RICH_MESSAGE_MAX_CHARS) {
|
|
|
7138
7159
|
} else if (spaceIdx > 0) {
|
|
7139
7160
|
cut = spaceIdx;
|
|
7140
7161
|
}
|
|
7141
|
-
cut =
|
|
7142
|
-
cut = backOffTableRow(rest, cut);
|
|
7143
|
-
cut = backOffOpenInline(rest, cut);
|
|
7162
|
+
cut = safeMarkdownCut(rest, cut);
|
|
7144
7163
|
if (cut <= 0) {
|
|
7145
7164
|
const sliced = hardSliceToCap(rest, maxLen);
|
|
7146
7165
|
chunks.push(stripBoundarySpacers(sliced[0], "trailing"));
|
|
@@ -7186,6 +7205,55 @@ function backOffTableRow(text, cut) {
|
|
|
7186
7205
|
}
|
|
7187
7206
|
return cut;
|
|
7188
7207
|
}
|
|
7208
|
+
function safeMarkdownCut(text, cut) {
|
|
7209
|
+
if (cut <= 0)
|
|
7210
|
+
return 0;
|
|
7211
|
+
if (cut >= text.length)
|
|
7212
|
+
return text.length;
|
|
7213
|
+
let safe = backOffOpenFence(text, cut);
|
|
7214
|
+
safe = backOffTableRow(text, safe);
|
|
7215
|
+
safe = backOffOpenInline(text, safe);
|
|
7216
|
+
while (safe > 0 && DELIMITER_RUN_CHARS.has(text[safe]) && text[safe - 1] === text[safe])
|
|
7217
|
+
safe--;
|
|
7218
|
+
if (safe <= 0)
|
|
7219
|
+
return 0;
|
|
7220
|
+
const prev = text.charCodeAt(safe - 1);
|
|
7221
|
+
const here = text.charCodeAt(safe);
|
|
7222
|
+
if (prev >= 55296 && prev <= 56319 && here >= 56320 && here <= 57343)
|
|
7223
|
+
safe -= 1;
|
|
7224
|
+
return safe;
|
|
7225
|
+
}
|
|
7226
|
+
function truncateMarkdownSafe(text, budget) {
|
|
7227
|
+
if (budget <= 0)
|
|
7228
|
+
return "";
|
|
7229
|
+
if (text.length <= budget)
|
|
7230
|
+
return text;
|
|
7231
|
+
let t = text;
|
|
7232
|
+
let end = budget;
|
|
7233
|
+
for (let pass = 0;pass <= MAX_OPENER_REPAIRS; pass++) {
|
|
7234
|
+
const safe = safeMarkdownCut(t, end);
|
|
7235
|
+
if (safe * 2 >= end)
|
|
7236
|
+
return t.slice(0, safe);
|
|
7237
|
+
const repaired = dropLeadingOpener(t.slice(safe));
|
|
7238
|
+
if (repaired == null)
|
|
7239
|
+
return t.slice(0, safe);
|
|
7240
|
+
end -= t.length - safe - repaired.length;
|
|
7241
|
+
t = t.slice(0, safe) + repaired;
|
|
7242
|
+
}
|
|
7243
|
+
return t.slice(0, Math.max(0, safeMarkdownCut(t, end)));
|
|
7244
|
+
}
|
|
7245
|
+
function dropLeadingOpener(rest) {
|
|
7246
|
+
const fence = FENCE_OPENER_AT_HEAD.exec(rest);
|
|
7247
|
+
if (fence != null)
|
|
7248
|
+
return fence[1] + rest.slice(fence[0].length);
|
|
7249
|
+
const inline = INLINE_OPENER_AT_HEAD.exec(rest);
|
|
7250
|
+
if (inline != null)
|
|
7251
|
+
return rest.slice(inline[0].length);
|
|
7252
|
+
const row = TABLE_ROW_OPENER_AT_HEAD.exec(rest);
|
|
7253
|
+
if (row != null)
|
|
7254
|
+
return row[1] + rest.slice(row[0].length);
|
|
7255
|
+
return null;
|
|
7256
|
+
}
|
|
7189
7257
|
function backOffOpenInline(text, cut) {
|
|
7190
7258
|
if (cut <= 0 || cut >= text.length)
|
|
7191
7259
|
return cut;
|
|
@@ -7206,7 +7274,7 @@ function backOffOpenInline(text, cut) {
|
|
|
7206
7274
|
}
|
|
7207
7275
|
return earliest;
|
|
7208
7276
|
}
|
|
7209
|
-
var RICH_MESSAGE_MAX_CHARS = 32768, PARAGRAPH_SPACER = "\u00a0", PSEUDO_HEADING_MAX_CHARS =
|
|
7277
|
+
var RICH_MESSAGE_MAX_CHARS = 32768, PLAIN_TEXT_MAX_CHARS = 4096, PARAGRAPH_SPACER = "\u00a0", PSEUDO_HEADING_MAX_CHARS = 64, INLINE_SPAN_PATTERNS, DELIMITER_RUN_CHARS, FENCE_OPENER_AT_HEAD, INLINE_OPENER_AT_HEAD, TABLE_ROW_OPENER_AT_HEAD, MAX_OPENER_REPAIRS = 8;
|
|
7210
7278
|
var init_format = __esm(() => {
|
|
7211
7279
|
INLINE_SPAN_PATTERNS = [
|
|
7212
7280
|
/`[^`\n]+`/g,
|
|
@@ -7215,8 +7283,14 @@ var init_format = __esm(() => {
|
|
|
7215
7283
|
/\*\*[^*\n]+\*\*/g,
|
|
7216
7284
|
/__[^_\n]+__/g,
|
|
7217
7285
|
/(?<![\w*])_[^_\n]+_(?![\w*])/g,
|
|
7218
|
-
/\[[^\]\n]*\]\([^)\n]*\)/g
|
|
7286
|
+
/\[[^\]\n]*\]\([^)\n]*\)/g,
|
|
7287
|
+
/~~[^~\n]+~~/g,
|
|
7288
|
+
/\|\|[^|\n]+\|\|/g
|
|
7219
7289
|
];
|
|
7290
|
+
DELIMITER_RUN_CHARS = new Set(["*", "_", "`", "~", "|"]);
|
|
7291
|
+
FENCE_OPENER_AT_HEAD = /^(\n?)(?:`{3,}|~{3,})[^\n]*/;
|
|
7292
|
+
INLINE_OPENER_AT_HEAD = /^(?:\*{1,3}|_{1,3}|`+|~~|\|\||\[)/;
|
|
7293
|
+
TABLE_ROW_OPENER_AT_HEAD = /^(\n?[ \t]*)\|/;
|
|
7220
7294
|
});
|
|
7221
7295
|
|
|
7222
7296
|
// ../src/auth/quota.ts
|
|
@@ -13686,276 +13760,6 @@ var require_mod4 = __commonJS((exports) => {
|
|
|
13686
13760
|
__exportStar(require_worker(), exports);
|
|
13687
13761
|
});
|
|
13688
13762
|
|
|
13689
|
-
// flood-429-ledger.ts
|
|
13690
|
-
import { readFileSync as readFileSync3, writeFileSync as writeFileSync4, mkdirSync as mkdirSync5, chmodSync, unlinkSync as unlinkSync2 } from "node:fs";
|
|
13691
|
-
import { dirname as dirname4, join as join4 } from "node:path";
|
|
13692
|
-
function flood429LedgerPathFromFloodState(floodStatePath) {
|
|
13693
|
-
return join4(dirname4(floodStatePath), FLOOD_429_LEDGER_FILE);
|
|
13694
|
-
}
|
|
13695
|
-
function belongsToEpisode(ep, obs) {
|
|
13696
|
-
const impliedUntil = obs.ts + Math.max(0, obs.retryAfterSec) * 1000;
|
|
13697
|
-
if (Math.abs(impliedUntil - ep.untilTs) <= EPISODE_MERGE_MS)
|
|
13698
|
-
return true;
|
|
13699
|
-
return obs.ts >= ep.firstTs && obs.ts <= ep.untilTs;
|
|
13700
|
-
}
|
|
13701
|
-
function foldFlood429Observation(episodes, obs, now = obs.ts) {
|
|
13702
|
-
const retryAfterSec = Math.max(0, Math.floor(obs.retryAfterSec));
|
|
13703
|
-
const impliedUntil = obs.ts + retryAfterSec * 1000;
|
|
13704
|
-
const next = episodes.slice();
|
|
13705
|
-
const last = next[next.length - 1];
|
|
13706
|
-
if (last && belongsToEpisode(last, obs)) {
|
|
13707
|
-
next[next.length - 1] = {
|
|
13708
|
-
firstTs: Math.min(last.firstTs, obs.ts),
|
|
13709
|
-
lastTs: Math.max(last.lastTs, obs.ts),
|
|
13710
|
-
peakRetryAfterSec: Math.max(last.peakRetryAfterSec, retryAfterSec),
|
|
13711
|
-
untilTs: Math.max(last.untilTs, impliedUntil),
|
|
13712
|
-
count: last.count + 1
|
|
13713
|
-
};
|
|
13714
|
-
} else {
|
|
13715
|
-
next.push({
|
|
13716
|
-
firstTs: obs.ts,
|
|
13717
|
-
lastTs: obs.ts,
|
|
13718
|
-
peakRetryAfterSec: retryAfterSec,
|
|
13719
|
-
untilTs: impliedUntil,
|
|
13720
|
-
count: 1
|
|
13721
|
-
});
|
|
13722
|
-
}
|
|
13723
|
-
return pruneFlood429Episodes(next, now);
|
|
13724
|
-
}
|
|
13725
|
-
function pruneFlood429Episodes(episodes, now) {
|
|
13726
|
-
const floor = now - FLOOD_429_LEDGER_RETENTION_MS;
|
|
13727
|
-
const kept = episodes.filter((e) => e.lastTs >= floor);
|
|
13728
|
-
return kept.length > FLOOD_429_LEDGER_MAX_EPISODES ? kept.slice(kept.length - FLOOD_429_LEDGER_MAX_EPISODES) : kept;
|
|
13729
|
-
}
|
|
13730
|
-
function parseEpisode(raw) {
|
|
13731
|
-
if (typeof raw !== "object" || raw === null)
|
|
13732
|
-
return null;
|
|
13733
|
-
const r = raw;
|
|
13734
|
-
const num = (v) => typeof v === "number" && Number.isFinite(v) ? v : null;
|
|
13735
|
-
const firstTs = num(r.firstTs);
|
|
13736
|
-
const untilTs = num(r.untilTs);
|
|
13737
|
-
const peak = num(r.peakRetryAfterSec);
|
|
13738
|
-
if (firstTs === null || untilTs === null || peak === null)
|
|
13739
|
-
return null;
|
|
13740
|
-
return {
|
|
13741
|
-
firstTs,
|
|
13742
|
-
lastTs: num(r.lastTs) ?? firstTs,
|
|
13743
|
-
peakRetryAfterSec: peak,
|
|
13744
|
-
untilTs,
|
|
13745
|
-
count: num(r.count) ?? 1
|
|
13746
|
-
};
|
|
13747
|
-
}
|
|
13748
|
-
function readFlood429LedgerResult(path, readFile = (p) => readFileSync3(p, "utf-8")) {
|
|
13749
|
-
let text;
|
|
13750
|
-
try {
|
|
13751
|
-
text = readFile(path);
|
|
13752
|
-
} catch (err) {
|
|
13753
|
-
const code = err?.code;
|
|
13754
|
-
if (code === "ENOENT" || code === "ENOTDIR")
|
|
13755
|
-
return { status: "absent", episodes: [] };
|
|
13756
|
-
return {
|
|
13757
|
-
status: "unreadable",
|
|
13758
|
-
episodes: [],
|
|
13759
|
-
error: `${code ?? "EUNKNOWN"}: ${err?.message ?? String(err)}`
|
|
13760
|
-
};
|
|
13761
|
-
}
|
|
13762
|
-
let parsed;
|
|
13763
|
-
try {
|
|
13764
|
-
parsed = JSON.parse(text);
|
|
13765
|
-
} catch (err) {
|
|
13766
|
-
return { status: "corrupt", episodes: [], error: err?.message ?? String(err) };
|
|
13767
|
-
}
|
|
13768
|
-
if (!Array.isArray(parsed)) {
|
|
13769
|
-
return { status: "corrupt", episodes: [], error: "ledger is not a JSON array" };
|
|
13770
|
-
}
|
|
13771
|
-
const out = [];
|
|
13772
|
-
let dropped = 0;
|
|
13773
|
-
for (const raw of parsed) {
|
|
13774
|
-
const ep = parseEpisode(raw);
|
|
13775
|
-
if (ep)
|
|
13776
|
-
out.push(ep);
|
|
13777
|
-
else
|
|
13778
|
-
dropped += 1;
|
|
13779
|
-
}
|
|
13780
|
-
if (dropped > 0 && out.length === 0) {
|
|
13781
|
-
return { status: "corrupt", episodes: [], error: `${dropped} unparseable entr(ies)` };
|
|
13782
|
-
}
|
|
13783
|
-
return { status: "ok", episodes: out };
|
|
13784
|
-
}
|
|
13785
|
-
function readFlood429Ledger(path, readFile = (p) => readFileSync3(p, "utf-8")) {
|
|
13786
|
-
return readFlood429LedgerResult(path, readFile).episodes;
|
|
13787
|
-
}
|
|
13788
|
-
function writeFlood429Ledger(path, episodes, log = (l) => process.stderr.write(l)) {
|
|
13789
|
-
const payload = JSON.stringify(episodes);
|
|
13790
|
-
try {
|
|
13791
|
-
mkdirSync5(dirname4(path), { recursive: true });
|
|
13792
|
-
} catch {}
|
|
13793
|
-
try {
|
|
13794
|
-
writeFileSync4(path, payload, { mode: FLOOD_429_LEDGER_MODE });
|
|
13795
|
-
try {
|
|
13796
|
-
chmodSync(path, FLOOD_429_LEDGER_MODE);
|
|
13797
|
-
} catch {}
|
|
13798
|
-
return;
|
|
13799
|
-
} catch (err) {
|
|
13800
|
-
const code = err?.code;
|
|
13801
|
-
if (code !== "EACCES" && code !== "EPERM") {
|
|
13802
|
-
log(`telegram gateway: 429-ledger: could not persist ${path} (${code ?? "error"})
|
|
13803
|
-
`);
|
|
13804
|
-
return;
|
|
13805
|
-
}
|
|
13806
|
-
try {
|
|
13807
|
-
unlinkSync2(path);
|
|
13808
|
-
writeFileSync4(path, payload, { mode: FLOOD_429_LEDGER_MODE });
|
|
13809
|
-
} catch (err2) {
|
|
13810
|
-
log(`telegram gateway: 429-ledger: cannot persist the 429 pressure ledger to ${path} ` + `(${code}, recreate failed: ${err2?.message ?? String(err2)}). ` + `Flood-pressure history will be missing from \`switchroom doctor\`
|
|
13811
|
-
`);
|
|
13812
|
-
}
|
|
13813
|
-
}
|
|
13814
|
-
}
|
|
13815
|
-
function recordFlood429(path, obs, log = (l) => process.stderr.write(l)) {
|
|
13816
|
-
const folded = foldFlood429Observation(readFlood429Ledger(path), obs, obs.ts);
|
|
13817
|
-
writeFlood429Ledger(path, folded, log);
|
|
13818
|
-
}
|
|
13819
|
-
var FLOOD_429_LEDGER_FILE = "429-ledger.json", FLOOD_429_LEDGER_MODE = 420, EPISODE_MERGE_MS = 60000, FLOOD_429_LEDGER_MAX_EPISODES = 400, FLOOD_429_LEDGER_RETENTION_MS, PENALTY_WINDOW_MS, TRIVIAL_PRESSURE_WINDOW_MS;
|
|
13820
|
-
var init_flood_429_ledger = __esm(() => {
|
|
13821
|
-
FLOOD_429_LEDGER_RETENTION_MS = 30 * 24 * 60 * 60 * 1000;
|
|
13822
|
-
PENALTY_WINDOW_MS = 7 * 24 * 60 * 60 * 1000;
|
|
13823
|
-
TRIVIAL_PRESSURE_WINDOW_MS = 24 * 60 * 60 * 1000;
|
|
13824
|
-
});
|
|
13825
|
-
|
|
13826
|
-
// flood-circuit-breaker.ts
|
|
13827
|
-
import {
|
|
13828
|
-
existsSync as existsSync2,
|
|
13829
|
-
readFileSync as readFileSync4,
|
|
13830
|
-
writeFileSync as writeFileSync5,
|
|
13831
|
-
mkdirSync as mkdirSync6,
|
|
13832
|
-
chmodSync as chmodSync2,
|
|
13833
|
-
unlinkSync as unlinkSync3,
|
|
13834
|
-
renameSync as renameSync6
|
|
13835
|
-
} from "node:fs";
|
|
13836
|
-
import { dirname as dirname5, join as join5 } from "node:path";
|
|
13837
|
-
function floodStatePath(stateDir) {
|
|
13838
|
-
return join5(stateDir, FLOOD_STATE_FILE);
|
|
13839
|
-
}
|
|
13840
|
-
function computeFloodWait(prior, retryAfterSec, now) {
|
|
13841
|
-
const candidate = now + Math.max(0, retryAfterSec) * 1000;
|
|
13842
|
-
const untilTs = prior && prior.untilTs > candidate ? prior.untilTs : candidate;
|
|
13843
|
-
return { untilTs, retryAfterSec, recordedTs: now };
|
|
13844
|
-
}
|
|
13845
|
-
function floodWaitRemainingMs(state, now) {
|
|
13846
|
-
if (!state)
|
|
13847
|
-
return 0;
|
|
13848
|
-
return Math.max(0, state.untilTs - now);
|
|
13849
|
-
}
|
|
13850
|
-
function readFloodStateResult(path) {
|
|
13851
|
-
let text;
|
|
13852
|
-
try {
|
|
13853
|
-
text = readFileSync4(path, "utf-8");
|
|
13854
|
-
} catch (err) {
|
|
13855
|
-
const code = err?.code;
|
|
13856
|
-
if (code === "ENOENT" || code === "ENOTDIR")
|
|
13857
|
-
return { status: "absent", state: null };
|
|
13858
|
-
return {
|
|
13859
|
-
status: "unreadable",
|
|
13860
|
-
state: null,
|
|
13861
|
-
error: `${code ?? "EUNKNOWN"}: ${err?.message ?? String(err)}`
|
|
13862
|
-
};
|
|
13863
|
-
}
|
|
13864
|
-
try {
|
|
13865
|
-
const raw = JSON.parse(text);
|
|
13866
|
-
if (typeof raw.untilTs !== "number" || !Number.isFinite(raw.untilTs)) {
|
|
13867
|
-
return { status: "corrupt", state: null, error: "untilTs is not a finite number" };
|
|
13868
|
-
}
|
|
13869
|
-
return {
|
|
13870
|
-
status: "ok",
|
|
13871
|
-
state: {
|
|
13872
|
-
untilTs: raw.untilTs,
|
|
13873
|
-
retryAfterSec: typeof raw.retryAfterSec === "number" ? raw.retryAfterSec : 0,
|
|
13874
|
-
recordedTs: typeof raw.recordedTs === "number" ? raw.recordedTs : 0
|
|
13875
|
-
}
|
|
13876
|
-
};
|
|
13877
|
-
} catch (err) {
|
|
13878
|
-
return { status: "corrupt", state: null, error: err?.message ?? String(err) };
|
|
13879
|
-
}
|
|
13880
|
-
}
|
|
13881
|
-
function readFloodState(path) {
|
|
13882
|
-
return readFloodStateResult(path).state;
|
|
13883
|
-
}
|
|
13884
|
-
function writeFloodState(path, state, log = (l) => process.stderr.write(l)) {
|
|
13885
|
-
const payload = JSON.stringify(state);
|
|
13886
|
-
try {
|
|
13887
|
-
mkdirSync6(dirname5(path), { recursive: true });
|
|
13888
|
-
} catch {}
|
|
13889
|
-
try {
|
|
13890
|
-
writeFileSync5(path, payload, { mode: FLOOD_STATE_MODE });
|
|
13891
|
-
try {
|
|
13892
|
-
chmodSync2(path, FLOOD_STATE_MODE);
|
|
13893
|
-
} catch {}
|
|
13894
|
-
return;
|
|
13895
|
-
} catch (err) {
|
|
13896
|
-
const code = err?.code;
|
|
13897
|
-
if (code !== "EACCES" && code !== "EPERM") {
|
|
13898
|
-
log(`telegram gateway: flood-breaker: could not persist ${path} (${code ?? "error"})
|
|
13899
|
-
`);
|
|
13900
|
-
return;
|
|
13901
|
-
}
|
|
13902
|
-
try {
|
|
13903
|
-
unlinkSync3(path);
|
|
13904
|
-
writeFileSync5(path, payload, { mode: FLOOD_STATE_MODE });
|
|
13905
|
-
log(`telegram gateway: flood-breaker: recreated ${path} \u2014 it was owned by another uid ` + `and could not be updated (${code}); the breaker was blind (issue #3106)
|
|
13906
|
-
`);
|
|
13907
|
-
} catch (err2) {
|
|
13908
|
-
log(`telegram gateway: flood-breaker: BLIND \u2014 cannot persist the flood-wait window to ${path} ` + `(${code}, and recreate failed: ${err2?.message ?? String(err2)}). ` + `Flood bans will NOT be recorded. Fix the file's ownership/mode (issue #3106)
|
|
13909
|
-
`);
|
|
13910
|
-
}
|
|
13911
|
-
}
|
|
13912
|
-
}
|
|
13913
|
-
function makeFloodWaitRecorder(path, now = Date.now, log = (l) => process.stderr.write(l)) {
|
|
13914
|
-
return (retryAfterSec) => {
|
|
13915
|
-
const t = now();
|
|
13916
|
-
const next = computeFloodWait(readFloodState(path), retryAfterSec, t);
|
|
13917
|
-
writeFloodState(path, next, log);
|
|
13918
|
-
try {
|
|
13919
|
-
recordFlood429(flood429LedgerPathFromFloodState(path), { ts: t, retryAfterSec }, log);
|
|
13920
|
-
} catch {}
|
|
13921
|
-
};
|
|
13922
|
-
}
|
|
13923
|
-
function warnBlind(path, error, now, log) {
|
|
13924
|
-
const last = blindLoggedAt.get(path);
|
|
13925
|
-
if (last !== undefined && now - last < BLIND_LOG_INTERVAL_MS)
|
|
13926
|
-
return;
|
|
13927
|
-
blindLoggedAt.set(path, now);
|
|
13928
|
-
log(`telegram gateway: flood-breaker: BLIND \u2014 cannot read the flood-wait marker ${path} ` + `(${error ?? "unknown error"}). The breaker cannot tell whether a Telegram flood ban is ` + `open. Essential sends PROCEED (fail-open); non-essential sends (boot card, typing) are ` + `SUPPRESSED. Fix the file's ownership/mode (issue #3106)
|
|
13929
|
-
`);
|
|
13930
|
-
}
|
|
13931
|
-
function makeFloodWaitProbe(path, now = Date.now, log = (l) => process.stderr.write(l)) {
|
|
13932
|
-
return () => {
|
|
13933
|
-
const t = now();
|
|
13934
|
-
const res = readFloodStateResult(path);
|
|
13935
|
-
if (res.status === "unreadable") {
|
|
13936
|
-
warnBlind(path, res.error, t, log);
|
|
13937
|
-
return 0;
|
|
13938
|
-
}
|
|
13939
|
-
return floodWaitRemainingMs(res.state, t);
|
|
13940
|
-
};
|
|
13941
|
-
}
|
|
13942
|
-
function nonEssentialSendSuppression(path, now) {
|
|
13943
|
-
const res = readFloodStateResult(path);
|
|
13944
|
-
if (res.status === "unreadable") {
|
|
13945
|
-
return { suppress: true, reason: "blind", error: res.error ?? "unknown error" };
|
|
13946
|
-
}
|
|
13947
|
-
const remainingMs = floodWaitRemainingMs(res.state, now);
|
|
13948
|
-
if (remainingMs > 0)
|
|
13949
|
-
return { suppress: true, reason: "flood_wait", remainingMs };
|
|
13950
|
-
return { suppress: false };
|
|
13951
|
-
}
|
|
13952
|
-
var FLOOD_STATE_FILE = "flood-wait.json", FLOOD_STATE_MODE = 420, BLIND_LOG_INTERVAL_MS = 60000, blindLoggedAt, FLOOD_WINDOWS_CORRUPT_SUPPRESS_MS;
|
|
13953
|
-
var init_flood_circuit_breaker = __esm(() => {
|
|
13954
|
-
init_flood_429_ledger();
|
|
13955
|
-
blindLoggedAt = new Map;
|
|
13956
|
-
FLOOD_WINDOWS_CORRUPT_SUPPRESS_MS = 5 * 60000;
|
|
13957
|
-
});
|
|
13958
|
-
|
|
13959
13763
|
// ../src/vault/broker/peercred-ffi.ts
|
|
13960
13764
|
function getPeerCred(fd) {
|
|
13961
13765
|
if (process.platform !== "linux")
|
|
@@ -14354,11 +14158,11 @@ var init_protocol2 = __esm(() => {
|
|
|
14354
14158
|
OkRevokeGrantResponseSchema,
|
|
14355
14159
|
OkApprovalRequestResponseSchema,
|
|
14356
14160
|
OkApprovalLookupResponseSchema,
|
|
14161
|
+
OkApprovalConsumeRecordResponseSchema,
|
|
14357
14162
|
OkApprovalConsumeResponseSchema,
|
|
14358
14163
|
OkApprovalRevokeResponseSchema,
|
|
14359
14164
|
OkApprovalListResponseSchema,
|
|
14360
14165
|
OkApprovalRecordResponseSchema,
|
|
14361
|
-
OkApprovalConsumeRecordResponseSchema,
|
|
14362
14166
|
ErrorResponseSchema2
|
|
14363
14167
|
]);
|
|
14364
14168
|
});
|
|
@@ -14372,7 +14176,7 @@ function isDockerRuntime() {
|
|
|
14372
14176
|
import * as net2 from "node:net";
|
|
14373
14177
|
import * as fs from "node:fs";
|
|
14374
14178
|
import { homedir as homedir3 } from "node:os";
|
|
14375
|
-
import { join as
|
|
14179
|
+
import { join as join4 } from "node:path";
|
|
14376
14180
|
function defaultBrokerSocketPath() {
|
|
14377
14181
|
if (fs.existsSync(OPERATOR_SOCKET_PATH))
|
|
14378
14182
|
return OPERATOR_SOCKET_PATH;
|
|
@@ -14381,8 +14185,8 @@ function defaultBrokerSocketPath() {
|
|
|
14381
14185
|
return LEGACY_SOCKET_PATH;
|
|
14382
14186
|
}
|
|
14383
14187
|
function vaultTokenFilePath(agentSlug) {
|
|
14384
|
-
const base = process.env.SWITCHROOM_AGENTS_DIR ||
|
|
14385
|
-
return
|
|
14188
|
+
const base = process.env.SWITCHROOM_AGENTS_DIR || join4(homedir3(), ".switchroom", "agents");
|
|
14189
|
+
return join4(base, agentSlug, ".vault-token");
|
|
14386
14190
|
}
|
|
14387
14191
|
function readVaultTokenFile(agentSlug) {
|
|
14388
14192
|
const filePath = vaultTokenFilePath(agentSlug);
|
|
@@ -14605,8 +14409,8 @@ var DEFAULT_TIMEOUT_MS2 = 2000, LEGACY_SOCKET_PATH, OPERATOR_SOCKET_PATH;
|
|
|
14605
14409
|
var init_client2 = __esm(() => {
|
|
14606
14410
|
init_protocol2();
|
|
14607
14411
|
init_peercred();
|
|
14608
|
-
LEGACY_SOCKET_PATH =
|
|
14609
|
-
OPERATOR_SOCKET_PATH =
|
|
14412
|
+
LEGACY_SOCKET_PATH = join4(homedir3(), ".switchroom", "vault-broker.sock");
|
|
14413
|
+
OPERATOR_SOCKET_PATH = join4(homedir3(), ".switchroom", "broker-operator", "sock");
|
|
14610
14414
|
});
|
|
14611
14415
|
|
|
14612
14416
|
// ../src/agents/tmux.ts
|
|
@@ -14617,7 +14421,7 @@ __export(exports_tmux, {
|
|
|
14617
14421
|
captureAgentPane: () => captureAgentPane
|
|
14618
14422
|
});
|
|
14619
14423
|
import { execFileSync as execFileSync2 } from "node:child_process";
|
|
14620
|
-
import { chmodSync
|
|
14424
|
+
import { chmodSync, mkdirSync as mkdirSync5, readdirSync as readdirSync2, statSync as statSync4, unlinkSync as unlinkSync2, writeFileSync as writeFileSync4 } from "node:fs";
|
|
14621
14425
|
import { resolve } from "node:path";
|
|
14622
14426
|
function captureAgentPane(opts) {
|
|
14623
14427
|
const { agentName, agentDir, reason } = opts;
|
|
@@ -14629,14 +14433,14 @@ function captureAgentPane(opts) {
|
|
|
14629
14433
|
const reasonSlug = sanitizeReason(reason);
|
|
14630
14434
|
const outPath = resolve(outDir, `${ts}-${reasonSlug}.txt`);
|
|
14631
14435
|
try {
|
|
14632
|
-
|
|
14436
|
+
mkdirSync5(outDir, { recursive: true, mode: 448 });
|
|
14633
14437
|
} catch (err) {
|
|
14634
14438
|
const msg = `mkdir crash-reports failed: ${err.message}`;
|
|
14635
14439
|
console.error(`[tmux-capture] ${agentName}: ${msg}`);
|
|
14636
14440
|
return { error: msg };
|
|
14637
14441
|
}
|
|
14638
14442
|
try {
|
|
14639
|
-
|
|
14443
|
+
chmodSync(outDir, 448);
|
|
14640
14444
|
} catch (err) {
|
|
14641
14445
|
console.error(`[tmux-capture] ${agentName}: chmod crash-reports 0700 failed: ${err.message}`);
|
|
14642
14446
|
}
|
|
@@ -14668,7 +14472,7 @@ function captureAgentPane(opts) {
|
|
|
14668
14472
|
` + `
|
|
14669
14473
|
`;
|
|
14670
14474
|
try {
|
|
14671
|
-
|
|
14475
|
+
writeFileSync4(outPath, Buffer.concat([Buffer.from(header, "utf8"), body]), {
|
|
14672
14476
|
mode: 384
|
|
14673
14477
|
});
|
|
14674
14478
|
} catch (err) {
|
|
@@ -14752,7 +14556,7 @@ function pruneOldReports(dir, retain) {
|
|
|
14752
14556
|
}).sort((a, b) => b.mtimeMs - a.mtimeMs);
|
|
14753
14557
|
for (const stale of files.slice(retain)) {
|
|
14754
14558
|
try {
|
|
14755
|
-
|
|
14559
|
+
unlinkSync2(stale.full);
|
|
14756
14560
|
} catch {}
|
|
14757
14561
|
}
|
|
14758
14562
|
}
|
|
@@ -21865,6 +21669,7 @@ var init_schema = __esm(() => {
|
|
|
21865
21669
|
auto_recall: exports_external.boolean().default(true).describe("Auto-search memories before each response"),
|
|
21866
21670
|
file: exports_external.boolean().default(true).describe("Maintain a curated workspace MEMORY.md file (seeded once, " + "auto-loaded every turn). Set false for hindsight-only memory: " + "the file is not seeded or re-created, so once migrated into " + "Hindsight and deleted it stays gone. Recall + directives carry " + "the memory instead. Cascade: override (per-agent wins over default)."),
|
|
21867
21671
|
isolation: exports_external.enum(["default", "strict"]).default("default").describe("strict = never shared cross-agent, default = eligible for reflect"),
|
|
21672
|
+
profile: exports_external.string().optional().describe("Memory profile bank this agent's curated memory defaults key off \u2014 " + "the built-in disposition + observations_mission in PROFILE_MEMORY_DEFAULTS. " + "Decouples the memory profile from `extends` (the filesystem persona " + "profile), so an agent on `extends: default` can opt into the `coding` " + "memory bundle via `memory.profile: coding` without inheriting the coding " + "persona. Resolution: memory.profile \u2192 extends \u2192 DEFAULT_PROFILE (see " + "resolveMemoryProfile). Unset \u21d2 byte-identical to keying off `extends`."),
|
|
21868
21673
|
bank_mission: exports_external.string().optional().describe("Bank-level mission statement used during recall to contextualize " + "results. NOTE: this is an alias for the Hindsight engine's " + "`reflect_mission` field (verified live: switchroom's bank_mission " + "lands in `config.reflect_mission`). Prefer `reflect_mission` going " + "forward; `bank_mission` is retained for back-compat. If both are " + "set, `reflect_mission` wins. Cascade: override."),
|
|
21869
21674
|
reflect_mission: exports_external.string().optional().describe("Mission/context steering Hindsight Reflect operations (the bank's " + "'who am I / what matters' framing applied during recall). The " + "engine-accurate name for what `bank_mission` sets. Cascade: override."),
|
|
21870
21675
|
retain_mission: exports_external.string().optional().describe("Instructions for the fact extraction LLM during retain. Cascade: override."),
|
|
@@ -22158,11 +21963,13 @@ var init_schema = __esm(() => {
|
|
|
22158
21963
|
provider: exports_external.string().min(1).optional().describe("Hindsight LLM provider (upstream `HINDSIGHT_API_LLM_PROVIDER`). " + "Defaults to `claude-code` (subscription-honest, broker-fed OAuth). " + "Any litellm-routable provider the upstream image supports is valid. " + "Serves as the GLOBAL default for every op absent a per-op override."),
|
|
22159
21964
|
model: exports_external.string().min(1).optional().describe("Hindsight LLM model (upstream `HINDSIGHT_API_LLM_MODEL`). Defaults " + "to HINDSIGHT_DEFAULT_MODEL. Any model your LiteLLM proxy can route " + "is valid, e.g. `openrouter/z-ai/glm-5.2` when routing through the " + "fleet proxy. With provider=claude-code this value is ALSO exported " + "as `ANTHROPIC_MODEL` to the claude subprocess. Serves as the GLOBAL " + "default for every op absent a per-op override."),
|
|
22160
21965
|
context_window: exports_external.number().int().positive().optional().describe("GLOBAL context window (tokens) of the backend serving hindsight's " + "LLM ops \u2014 the declared size switchroom derives every token budget " + "from. Set this to the real window of whatever you point " + "`hindsight.llm` at (e.g. 32768 for a llama.cpp slot launched with " + "`-c 65536 -np 2`, 131072 for a large-window OpenRouter model). " + "Absent \u2192 a per-provider default: 200000 for `claude-code`, a " + "conservative 32768 for everything else. Overflowing a local " + "backend's window does NOT error \u2014 llama.cpp context-shift silently " + "drops the system prompt and the model answers conversationally " + "with HTTP 200 \u2014 so this value is what makes the failure " + "detectable at setup time instead of never."),
|
|
21966
|
+
base_url: exports_external.string().min(1).optional().describe("GLOBAL LLM base URL (upstream `HINDSIGHT_API_LLM_BASE_URL`). The " + "default endpoint every op inherits absent a per-op " + "`hindsight.llm.<op>.base_url`. Optional passthrough; unset \u2192 the " + "engine's provider default. When this points at host loopback the " + "hindsight container is forced onto host networking, same as a " + "per-op base URL, so the endpoint stays reachable (#3687)."),
|
|
21967
|
+
api_key: exports_external.string().min(1).optional().describe("GLOBAL LLM API key (upstream `HINDSIGHT_API_LLM_API_KEY`). Literal " + "or `vault:` reference. The default credential every op inherits " + "absent a per-op `hindsight.llm.<op>.api_key`; the engine reads it as " + "a plain env fallback. Optional passthrough (#3687)."),
|
|
22161
21968
|
retain: HindsightPerOpLlmSchema.optional().describe("Per-op override for the `retain` LLM op (memory ingestion). Emits " + "`HINDSIGHT_API_RETAIN_LLM_*`. Absent \u2192 uses the global model/provider."),
|
|
22162
21969
|
reflect: HindsightPerOpLlmSchema.optional().describe("Per-op override for the `reflect` LLM op (synthesis / mental-model " + "refresh). Emits `HINDSIGHT_API_REFLECT_LLM_*`. Absent \u2192 uses global."),
|
|
22163
21970
|
consolidation: HindsightPerOpLlmSchema.optional().describe("Per-op override for the `consolidation` LLM op (background memory " + "merge). Emits `HINDSIGHT_API_CONSOLIDATION_LLM_*`. Absent \u2192 global.")
|
|
22164
21971
|
}).optional().describe("LLM knob for the hindsight container. The flat `provider`/`model` set " + "the global default (backward-compatible); optional `retain`/`reflect`/" + "`consolidation` blocks override individual ops. All fields optional; " + "unset fields fall back to the hard-coded defaults."),
|
|
22165
|
-
env: exports_external.record(exports_external.union([exports_external.string(), exports_external.number(), exports_external.boolean()])).optional().describe("Operator overrides for switchroom's capability-gated Hindsight " + "performance defaults. Only the keys switchroom actually manages are " + "honoured (`HINDSIGHT_PERF_ENV_KEYS` in " + "src/setup/hindsight-perf-defaults.ts: RERANKER_LOCAL_FP16, " + "RERANKER_LOCAL_BATCH_SIZE, LLM_MAX_CONCURRENT, " + "RETAIN/CONSOLIDATION_LLM_MAX_CONCURRENT, LLM_STRICT_SCHEMA, " + "LLM_MAX_RETRIES, CONSOLIDATION_LLM_PARALLELISM, " + "MAX_OBSERVATIONS_PER_SCOPE, " + "RECALL_MAX_CANDIDATES_PER_SOURCE, LINK_EXPANSION_PER_ENTITY_LIMIT, " + "LINK_EXPANSION_TIMEOUT, LLM_REASONING_EFFORT, " + "RERANKER_LOCAL_BUCKET_BATCHING, RERANKER_MAX_CANDIDATES, " + "RERANKER_LOCAL_MAX_CONCURRENT, RECALL_MAX_CONCURRENT, " + "REFLECT_WALL_TIMEOUT,
|
|
21972
|
+
env: exports_external.record(exports_external.union([exports_external.string(), exports_external.number(), exports_external.boolean()])).optional().describe("Operator overrides for switchroom's capability-gated Hindsight " + "performance defaults. Only the keys switchroom actually manages are " + "honoured (`HINDSIGHT_PERF_ENV_KEYS` in " + "src/setup/hindsight-perf-defaults.ts: RERANKER_LOCAL_FP16, " + "RERANKER_LOCAL_BATCH_SIZE, LLM_MAX_CONCURRENT, " + "RETAIN/CONSOLIDATION_LLM_MAX_CONCURRENT, LLM_STRICT_SCHEMA, " + "LLM_MAX_RETRIES, CONSOLIDATION_LLM_PARALLELISM, " + "MAX_OBSERVATIONS_PER_SCOPE, " + "RECALL_MAX_CANDIDATES_PER_SOURCE, LINK_EXPANSION_PER_ENTITY_LIMIT, " + "LINK_EXPANSION_TIMEOUT, LLM_REASONING_EFFORT, " + "RERANKER_LOCAL_BUCKET_BATCHING, RERANKER_MAX_CANDIDATES, " + "RERANKER_LOCAL_MAX_CONCURRENT, RECALL_MAX_CONCURRENT, " + "REFLECT_WALL_TIMEOUT, WORKER_CONSOLIDATION_RESERVED_SLOTS, " + "WORKER_CONSOLIDATION_SLOT_LIMIT, " + "CONSOLIDATION_MAX_MEMORIES_PER_ROUND, GRAPH_SEED_MIN_SIMILARITY, " + "LLM_SUPPORTS_MAX_ITEMS, RECENCY_DECAY_FUNCTION, " + "RECENCY_DECAY_HALFLIFE_DAYS \u2014 switchroom defaults recall's recency " + "curve to `exponential` with a 30-day half-life so a fact retained " + "today outranks a stale one, instead of upstream's near-flat " + "linear/365-day window), the override-only keys " + "switchroom manages but ships NO default for " + "(`HINDSIGHT_PERF_OVERRIDE_ONLY_KEYS`: " + "HINDSIGHT_API_WORKER_CONSOLIDATION_BANK_PRIORITY \u2014 a per-deployment " + "`bank-pattern:priority,...` map; unset means upstream's flat " + "created_at FIFO across banks; and " + "HINDSIGHT_CE_DECISIVE_RELATIVE_GAP \u2014 the rollback knob for " + "switchroom's CE-saturation damping patch, a float; >= ~0.65 backs the " + "damping out entirely, unset means the patch's own derived gap; and " + "HINDSIGHT_API_RECENCY_DECAY_LINEAR_WINDOW_DAYS \u2014 only read when the " + "decay function is `linear`, so switchroom ships no default for it but " + "still honours an operator who flips the function back; and " + "HINDSIGHT_API_WORKER_MAX_SLOTS \u2014 the worker poller's TOTAL in-flight " + "task budget, the pool WORKER_CONSOLIDATION_RESERVED_SLOTS reserves out of; " + "unset means upstream's own default; and " + "HINDSIGHT_API_WORKER_RETAIN_RESERVED_SLOTS \u2014 the reserved slot FLOOR for the " + "retain (memory write) lane, carved from that same total; unset means " + "upstream's own 0, i.e. no floor and retain competes for the shared " + "pool; and " + "the pre-0.8.6 name HINDSIGHT_API_WORKER_<TYPE>_MAX_SLOTS is still " + "accepted for every operation type and normalised to " + "..._RESERVED_SLOTS on the way in, because setting both names for " + "one type is a hard boot failure in the engine and switchroom " + "therefore emits only the canonical name; and " + "HINDSIGHT_API_SEMANTIC_MIN_SIMILARITY \u2014 the cosine-similarity floor a " + "candidate must reach to be returned by the semantic retrieval arm at " + "all (0..1); unset means upstream's own 0.3, and where it should sit " + "depends on the bank's embedding model and phrasing diversity, so " + "switchroom ships no opinion; and " + "HINDSIGHT_MCP_RECALL_BUDGET_MODE \u2014 the rollback knob for switchroom's " + "mcp-recall-token-budget image patch; `legacy` restores upstream's " + "exact pre-patch recall returns, anything else or unset is the " + "honest-envelope mode; and " + "HINDSIGHT_API_LLM_TEMPERATURE_REFLECT \u2014 upstream's own per-op reflect " + "temperature knob, made live by switchroom's reflect-temperature image " + "patch; a float, or `none` to omit the kwarg (provider default, " + "upstream's accidental pre-patch behaviour); unset means the image's " + "baked default 0.1), plus the " + "embedded-PostgreSQL (pg0) sizing keys switchroom manages in " + "src/setup/hindsight-pg-defaults.ts (`HINDSIGHT_PG_ENV_KEYS`: " + "SWITCHROOM_HINDSIGHT_PG_EFFECTIVE_CACHE_SIZE, " + "SWITCHROOM_HINDSIGHT_PG_SHARED_BUFFERS \u2014 a postgres size string such " + "as `4GB`, or the sentinel `off` to leave pg0's own default for that " + "one knob). A value set here " + "REPLACES switchroom's default and is emitted even when the gating " + "capability is absent, so an operator can always force a knob. Other " + "`HINDSIGHT_API_*` keys are deliberately IGNORED \u2014 a blanket " + "passthrough would collide with the vars startHindsight() derives " + "itself (HINDSIGHT_API_PORT, the retain token/deadline budget).")
|
|
22166
21973
|
});
|
|
22167
21974
|
MicrosoftWorkspaceConfigSchema = exports_external.object({
|
|
22168
21975
|
microsoft_client_id: exports_external.string().min(1).optional().describe("Microsoft OAuth application (client) ID from Entra portal " + "(literal string or vault reference e.g. " + "'vault:microsoft-oauth-client-id'). OPTIONAL \u2014 omit it to use " + "switchroom's shipped default Microsoft app (zero-config). " + "Set it only to bring your own Entra app (BYO)."),
|
|
@@ -22294,6 +22101,7 @@ var init_schema = __esm(() => {
|
|
|
22294
22101
|
auto_recall: exports_external.boolean().optional(),
|
|
22295
22102
|
file: exports_external.boolean().optional(),
|
|
22296
22103
|
isolation: exports_external.enum(["default", "strict"]).optional(),
|
|
22104
|
+
profile: exports_external.string().optional(),
|
|
22297
22105
|
directive_capture_nudge: exports_external.boolean().optional(),
|
|
22298
22106
|
observation_scopes: ObservationScopesSchema,
|
|
22299
22107
|
observation_scope_strategy: ObservationScopeStrategySchema,
|
|
@@ -22335,7 +22143,23 @@ var init_schema = __esm(() => {
|
|
|
22335
22143
|
max_query_chars: exports_external.number().int().min(1).optional(),
|
|
22336
22144
|
parallel: exports_external.boolean().optional(),
|
|
22337
22145
|
additional_banks: exports_external.array(exports_external.string()).optional(),
|
|
22338
|
-
sender_banks: exports_external.record(exports_external.string(), exports_external.string()).optional()
|
|
22146
|
+
sender_banks: exports_external.record(exports_external.string(), exports_external.string()).optional(),
|
|
22147
|
+
types: exports_external.array(exports_external.string()).optional(),
|
|
22148
|
+
skip_trivial: exports_external.boolean().optional(),
|
|
22149
|
+
topic_filter_mode: exports_external.enum(["soft-preamble", "hard-filter"]).optional()
|
|
22150
|
+
}).optional(),
|
|
22151
|
+
retain: exports_external.object({
|
|
22152
|
+
every_n_turns: exports_external.number().int().min(1).optional(),
|
|
22153
|
+
overlap_turns: exports_external.number().int().min(0).optional()
|
|
22154
|
+
}).optional(),
|
|
22155
|
+
bank_mission: exports_external.string().optional(),
|
|
22156
|
+
reflect_mission: exports_external.string().optional(),
|
|
22157
|
+
retain_mission: exports_external.string().optional(),
|
|
22158
|
+
observations_mission: exports_external.string().optional(),
|
|
22159
|
+
disposition: exports_external.object({
|
|
22160
|
+
skepticism: exports_external.number().int().min(1).max(5).optional(),
|
|
22161
|
+
literalism: exports_external.number().int().min(1).max(5).optional(),
|
|
22162
|
+
empathy: exports_external.number().int().min(1).max(5).optional()
|
|
22339
22163
|
}).optional()
|
|
22340
22164
|
}).optional(),
|
|
22341
22165
|
schedule: exports_external.array(ScheduleEntrySchema).optional(),
|
|
@@ -22678,7 +22502,7 @@ var init_schema = __esm(() => {
|
|
|
22678
22502
|
});
|
|
22679
22503
|
|
|
22680
22504
|
// ../src/config/paths.ts
|
|
22681
|
-
import { existsSync as
|
|
22505
|
+
import { existsSync as existsSync3 } from "node:fs";
|
|
22682
22506
|
import { resolve as resolve3 } from "node:path";
|
|
22683
22507
|
function home() {
|
|
22684
22508
|
return process.env.HOME ?? "/root";
|
|
@@ -22694,7 +22518,7 @@ function resolveStatePath(fragment) {
|
|
|
22694
22518
|
const h = home();
|
|
22695
22519
|
const primary = resolve3(h, DEFAULT_STATE_DIR, fragment);
|
|
22696
22520
|
const legacy = resolve3(h, LEGACY_STATE_DIR, fragment);
|
|
22697
|
-
if (!
|
|
22521
|
+
if (!existsSync3(primary) && existsSync3(legacy)) {
|
|
22698
22522
|
warnLegacyStateOnce(legacy);
|
|
22699
22523
|
return legacy;
|
|
22700
22524
|
}
|
|
@@ -22707,9 +22531,9 @@ function resolveDualPath(pathStr) {
|
|
|
22707
22531
|
const absolute = resolve3(h, rest);
|
|
22708
22532
|
if (rest.startsWith(`${DEFAULT_STATE_DIR}/`)) {
|
|
22709
22533
|
const frag = rest.slice(DEFAULT_STATE_DIR.length + 1);
|
|
22710
|
-
if (!
|
|
22534
|
+
if (!existsSync3(absolute)) {
|
|
22711
22535
|
const legacy = resolve3(h, LEGACY_STATE_DIR, frag);
|
|
22712
|
-
if (
|
|
22536
|
+
if (existsSync3(legacy)) {
|
|
22713
22537
|
warnLegacyStateOnce(legacy);
|
|
22714
22538
|
return legacy;
|
|
22715
22539
|
}
|
|
@@ -22734,7 +22558,7 @@ var init_overlay_schema = __esm(() => {
|
|
|
22734
22558
|
});
|
|
22735
22559
|
|
|
22736
22560
|
// ../src/config/overlay-loader.ts
|
|
22737
|
-
import { existsSync as
|
|
22561
|
+
import { existsSync as existsSync4, readFileSync as readFileSync4, readdirSync as readdirSync3, statSync as statSync5 } from "node:fs";
|
|
22738
22562
|
import { basename as basename4, resolve as resolve4 } from "node:path";
|
|
22739
22563
|
function deriveOverlayTitle(raw, fileName) {
|
|
22740
22564
|
const titleFromComment = raw.match(/^#[^\S\n]*name:[^\S\n]*(\S.*?)[^\S\n]*$/m)?.[1];
|
|
@@ -22750,7 +22574,7 @@ function overlayDirFor(agentName, subdir) {
|
|
|
22750
22574
|
return resolve4(base);
|
|
22751
22575
|
}
|
|
22752
22576
|
function listYamlFiles(dir) {
|
|
22753
|
-
if (!
|
|
22577
|
+
if (!existsSync4(dir))
|
|
22754
22578
|
return [];
|
|
22755
22579
|
let entries;
|
|
22756
22580
|
try {
|
|
@@ -22798,7 +22622,7 @@ function applyAgentOverlays(config) {
|
|
|
22798
22622
|
const merged = [...agentCfg.schedule ?? []];
|
|
22799
22623
|
for (const file of files) {
|
|
22800
22624
|
try {
|
|
22801
|
-
const raw =
|
|
22625
|
+
const raw = readFileSync4(file, "utf-8");
|
|
22802
22626
|
const parsed = import_yaml.parse(raw);
|
|
22803
22627
|
const doc = OverlayDocSchema.parse(parsed);
|
|
22804
22628
|
const title = deriveOverlayTitle(raw, basename4(file));
|
|
@@ -22839,7 +22663,7 @@ function applyAgentOverlays(config) {
|
|
|
22839
22663
|
const seen = new Set(merged);
|
|
22840
22664
|
for (const file of skillFiles) {
|
|
22841
22665
|
try {
|
|
22842
|
-
const raw =
|
|
22666
|
+
const raw = readFileSync4(file, "utf-8");
|
|
22843
22667
|
const parsed = import_yaml.parse(raw);
|
|
22844
22668
|
const doc = OverlayDocSchema.parse(parsed);
|
|
22845
22669
|
for (const skillName of doc.skills ?? []) {
|
|
@@ -23309,7 +23133,7 @@ var init_timezone = __esm(() => {
|
|
|
23309
23133
|
});
|
|
23310
23134
|
|
|
23311
23135
|
// ../src/config/loader.ts
|
|
23312
|
-
import { readFileSync as
|
|
23136
|
+
import { readFileSync as readFileSync5, existsSync as existsSync5 } from "node:fs";
|
|
23313
23137
|
import { homedir as homedir4 } from "node:os";
|
|
23314
23138
|
import { resolve as resolve5 } from "node:path";
|
|
23315
23139
|
function formatZodErrors(error) {
|
|
@@ -23376,7 +23200,7 @@ function findConfigFile(startDir) {
|
|
|
23376
23200
|
resolve5(userDir, "clerk.yml")
|
|
23377
23201
|
].filter(Boolean);
|
|
23378
23202
|
for (const path of searchPaths) {
|
|
23379
|
-
if (
|
|
23203
|
+
if (existsSync5(path)) {
|
|
23380
23204
|
return path;
|
|
23381
23205
|
}
|
|
23382
23206
|
}
|
|
@@ -23384,12 +23208,12 @@ function findConfigFile(startDir) {
|
|
|
23384
23208
|
}
|
|
23385
23209
|
function loadConfig(configPath) {
|
|
23386
23210
|
const filePath = configPath ?? findConfigFile();
|
|
23387
|
-
if (!
|
|
23211
|
+
if (!existsSync5(filePath)) {
|
|
23388
23212
|
throw new ConfigError(`Config file not found: ${filePath}`);
|
|
23389
23213
|
}
|
|
23390
23214
|
let raw;
|
|
23391
23215
|
try {
|
|
23392
|
-
raw =
|
|
23216
|
+
raw = readFileSync5(filePath, "utf-8");
|
|
23393
23217
|
} catch (err) {
|
|
23394
23218
|
throw new ConfigError(`Failed to read config file: ${filePath}`, [
|
|
23395
23219
|
` ${err.message}`
|
|
@@ -23503,12 +23327,12 @@ var init_flock = () => {};
|
|
|
23503
23327
|
// ../src/vault/vault.ts
|
|
23504
23328
|
import { randomBytes as randomBytes4, scryptSync, createCipheriv, createDecipheriv } from "node:crypto";
|
|
23505
23329
|
import {
|
|
23506
|
-
readFileSync as
|
|
23330
|
+
readFileSync as readFileSync6,
|
|
23507
23331
|
writeSync as writeSync2,
|
|
23508
|
-
existsSync as
|
|
23509
|
-
renameSync as
|
|
23510
|
-
mkdirSync as
|
|
23511
|
-
unlinkSync as
|
|
23332
|
+
existsSync as existsSync6,
|
|
23333
|
+
renameSync as renameSync6,
|
|
23334
|
+
mkdirSync as mkdirSync7,
|
|
23335
|
+
unlinkSync as unlinkSync3,
|
|
23512
23336
|
fsyncSync as fsyncSync2,
|
|
23513
23337
|
openSync as openSync2,
|
|
23514
23338
|
closeSync as closeSync2,
|
|
@@ -23546,12 +23370,12 @@ function normalizeSecrets(raw) {
|
|
|
23546
23370
|
return out;
|
|
23547
23371
|
}
|
|
23548
23372
|
function openVault(passphrase, vaultPath) {
|
|
23549
|
-
if (!
|
|
23373
|
+
if (!existsSync6(vaultPath)) {
|
|
23550
23374
|
throw new VaultError(`Vault file not found: ${vaultPath}`);
|
|
23551
23375
|
}
|
|
23552
23376
|
let vaultFile;
|
|
23553
23377
|
try {
|
|
23554
|
-
vaultFile = JSON.parse(
|
|
23378
|
+
vaultFile = JSON.parse(readFileSync6(vaultPath, "utf8"));
|
|
23555
23379
|
} catch {
|
|
23556
23380
|
throw new VaultError(`Failed to read vault file: ${vaultPath}`);
|
|
23557
23381
|
}
|
|
@@ -23596,16 +23420,16 @@ var init_vault = __esm(() => {
|
|
|
23596
23420
|
|
|
23597
23421
|
// ../src/vault/resolver.ts
|
|
23598
23422
|
import {
|
|
23599
|
-
chmodSync as
|
|
23423
|
+
chmodSync as chmodSync2,
|
|
23600
23424
|
closeSync as closeSync3,
|
|
23601
|
-
mkdirSync as
|
|
23425
|
+
mkdirSync as mkdirSync8,
|
|
23602
23426
|
mkdtempSync,
|
|
23603
23427
|
openSync as openSync3,
|
|
23604
23428
|
rmSync as rmSync2,
|
|
23605
23429
|
statSync as statSync6,
|
|
23606
23430
|
writeSync as writeSync3
|
|
23607
23431
|
} from "node:fs";
|
|
23608
|
-
import { join as
|
|
23432
|
+
import { join as join6 } from "node:path";
|
|
23609
23433
|
import { tmpdir } from "node:os";
|
|
23610
23434
|
import { constants as fsConstants } from "node:fs";
|
|
23611
23435
|
function isVaultReference(value) {
|
|
@@ -23657,13 +23481,13 @@ function materializationRoot() {
|
|
|
23657
23481
|
return cachedRoot;
|
|
23658
23482
|
const xdg = process.env.XDG_RUNTIME_DIR;
|
|
23659
23483
|
if (xdg) {
|
|
23660
|
-
const base =
|
|
23661
|
-
|
|
23662
|
-
cachedRoot = mkdtempSync(
|
|
23484
|
+
const base = join6(xdg, "switchroom", "vault");
|
|
23485
|
+
mkdirSync8(base, { recursive: true, mode: 448 });
|
|
23486
|
+
cachedRoot = mkdtempSync(join6(base, "run-"));
|
|
23663
23487
|
} else {
|
|
23664
|
-
cachedRoot = mkdtempSync(
|
|
23488
|
+
cachedRoot = mkdtempSync(join6(tmpdir(), "switchroom-vault-"));
|
|
23665
23489
|
}
|
|
23666
|
-
|
|
23490
|
+
chmodSync2(cachedRoot, 448);
|
|
23667
23491
|
return cachedRoot;
|
|
23668
23492
|
}
|
|
23669
23493
|
function writeFileExclusive(filePath, content) {
|
|
@@ -23676,14 +23500,14 @@ function writeFileExclusive(filePath, content) {
|
|
|
23676
23500
|
}
|
|
23677
23501
|
}
|
|
23678
23502
|
function materializeFilesEntry(key, files) {
|
|
23679
|
-
const dir =
|
|
23503
|
+
const dir = join6(materializationRoot(), key);
|
|
23680
23504
|
if (materializedDirs.has(dir)) {
|
|
23681
23505
|
try {
|
|
23682
23506
|
rmSync2(dir, { recursive: true, force: true });
|
|
23683
23507
|
} catch {}
|
|
23684
23508
|
}
|
|
23685
|
-
|
|
23686
|
-
|
|
23509
|
+
mkdirSync8(dir, { recursive: true, mode: 448 });
|
|
23510
|
+
chmodSync2(dir, 448);
|
|
23687
23511
|
const st = statSync6(dir);
|
|
23688
23512
|
if (typeof process.getuid === "function" && st.uid !== process.getuid()) {
|
|
23689
23513
|
throw new Error(`Refusing to materialize vault entry: ${dir} not owned by caller`);
|
|
@@ -23692,7 +23516,7 @@ function materializeFilesEntry(key, files) {
|
|
|
23692
23516
|
if (filename.includes("/") || filename.includes("\\") || filename === ".." || filename === "." || filename.includes("\x00")) {
|
|
23693
23517
|
throw new Error(`Refusing to materialize vault file with unsafe name: ${filename}`);
|
|
23694
23518
|
}
|
|
23695
|
-
const filePath =
|
|
23519
|
+
const filePath = join6(dir, filename);
|
|
23696
23520
|
const content = encoding === "base64" ? Buffer.from(value, "base64") : value;
|
|
23697
23521
|
writeFileExclusive(filePath, content);
|
|
23698
23522
|
}
|
|
@@ -23819,9 +23643,16 @@ var init_resolver = __esm(() => {
|
|
|
23819
23643
|
materializedDirs = new Set;
|
|
23820
23644
|
});
|
|
23821
23645
|
|
|
23646
|
+
// status-no-truncate.ts
|
|
23647
|
+
var STATUS_ROLLING_LINES = 5, WORKER_HISTORY_MAX = 6, STATUS_LINE_MAX = 200, STATUS_CARD_CHAR_BUDGET, NESTED_PREFIX = "\u2800\u2800\u2800\u21b3 ", WORKER_STEP_INDENT = "\u2800\u2800\u2800";
|
|
23648
|
+
var init_status_no_truncate = __esm(() => {
|
|
23649
|
+
init_format();
|
|
23650
|
+
STATUS_CARD_CHAR_BUDGET = RICH_MESSAGE_MAX_CHARS;
|
|
23651
|
+
});
|
|
23652
|
+
|
|
23822
23653
|
// ../src/util/atomic.ts
|
|
23823
23654
|
import { randomBytes as randomBytes5 } from "node:crypto";
|
|
23824
|
-
import { closeSync as closeSync4, constants as constants2, fchmodSync as fchmodSync2, fchownSync as fchownSync2, fsyncSync as fsyncSync3, openSync as openSync4, renameSync as
|
|
23655
|
+
import { closeSync as closeSync4, constants as constants2, fchmodSync as fchmodSync2, fchownSync as fchownSync2, fsyncSync as fsyncSync3, openSync as openSync4, renameSync as renameSync7, rmSync as rmSync3, writeSync as writeSync4 } from "node:fs";
|
|
23825
23656
|
function atomicWriteFileSync2(destPath, contents, modeOrOpts = 384) {
|
|
23826
23657
|
const opts = typeof modeOrOpts === "number" ? { mode: modeOrOpts } : modeOrOpts;
|
|
23827
23658
|
const mode = opts.mode ?? 384;
|
|
@@ -23845,7 +23676,7 @@ function atomicWriteFileSync2(destPath, contents, modeOrOpts = 384) {
|
|
|
23845
23676
|
fsyncSync3(fd);
|
|
23846
23677
|
closeSync4(fd);
|
|
23847
23678
|
fd = null;
|
|
23848
|
-
|
|
23679
|
+
renameSync7(tmp, destPath);
|
|
23849
23680
|
} catch (err) {
|
|
23850
23681
|
if (fd !== null) {
|
|
23851
23682
|
try {
|
|
@@ -23911,6 +23742,276 @@ var init_approval_card = __esm(() => {
|
|
|
23911
23742
|
import_grammy5 = __toESM(require_mod2(), 1);
|
|
23912
23743
|
});
|
|
23913
23744
|
|
|
23745
|
+
// flood-429-ledger.ts
|
|
23746
|
+
import { readFileSync as readFileSync13, writeFileSync as writeFileSync11, mkdirSync as mkdirSync14, chmodSync as chmodSync4, unlinkSync as unlinkSync9 } from "node:fs";
|
|
23747
|
+
import { dirname as dirname7, join as join17 } from "node:path";
|
|
23748
|
+
function flood429LedgerPathFromFloodState(floodStatePath) {
|
|
23749
|
+
return join17(dirname7(floodStatePath), FLOOD_429_LEDGER_FILE);
|
|
23750
|
+
}
|
|
23751
|
+
function belongsToEpisode(ep, obs) {
|
|
23752
|
+
const impliedUntil = obs.ts + Math.max(0, obs.retryAfterSec) * 1000;
|
|
23753
|
+
if (Math.abs(impliedUntil - ep.untilTs) <= EPISODE_MERGE_MS)
|
|
23754
|
+
return true;
|
|
23755
|
+
return obs.ts >= ep.firstTs && obs.ts <= ep.untilTs;
|
|
23756
|
+
}
|
|
23757
|
+
function foldFlood429Observation(episodes, obs, now = obs.ts) {
|
|
23758
|
+
const retryAfterSec = Math.max(0, Math.floor(obs.retryAfterSec));
|
|
23759
|
+
const impliedUntil = obs.ts + retryAfterSec * 1000;
|
|
23760
|
+
const next = episodes.slice();
|
|
23761
|
+
const last = next[next.length - 1];
|
|
23762
|
+
if (last && belongsToEpisode(last, obs)) {
|
|
23763
|
+
next[next.length - 1] = {
|
|
23764
|
+
firstTs: Math.min(last.firstTs, obs.ts),
|
|
23765
|
+
lastTs: Math.max(last.lastTs, obs.ts),
|
|
23766
|
+
peakRetryAfterSec: Math.max(last.peakRetryAfterSec, retryAfterSec),
|
|
23767
|
+
untilTs: Math.max(last.untilTs, impliedUntil),
|
|
23768
|
+
count: last.count + 1
|
|
23769
|
+
};
|
|
23770
|
+
} else {
|
|
23771
|
+
next.push({
|
|
23772
|
+
firstTs: obs.ts,
|
|
23773
|
+
lastTs: obs.ts,
|
|
23774
|
+
peakRetryAfterSec: retryAfterSec,
|
|
23775
|
+
untilTs: impliedUntil,
|
|
23776
|
+
count: 1
|
|
23777
|
+
});
|
|
23778
|
+
}
|
|
23779
|
+
return pruneFlood429Episodes(next, now);
|
|
23780
|
+
}
|
|
23781
|
+
function pruneFlood429Episodes(episodes, now) {
|
|
23782
|
+
const floor = now - FLOOD_429_LEDGER_RETENTION_MS;
|
|
23783
|
+
const kept = episodes.filter((e) => e.lastTs >= floor);
|
|
23784
|
+
return kept.length > FLOOD_429_LEDGER_MAX_EPISODES ? kept.slice(kept.length - FLOOD_429_LEDGER_MAX_EPISODES) : kept;
|
|
23785
|
+
}
|
|
23786
|
+
function parseEpisode(raw) {
|
|
23787
|
+
if (typeof raw !== "object" || raw === null)
|
|
23788
|
+
return null;
|
|
23789
|
+
const r = raw;
|
|
23790
|
+
const num = (v) => typeof v === "number" && Number.isFinite(v) ? v : null;
|
|
23791
|
+
const firstTs = num(r.firstTs);
|
|
23792
|
+
const untilTs = num(r.untilTs);
|
|
23793
|
+
const peak = num(r.peakRetryAfterSec);
|
|
23794
|
+
if (firstTs === null || untilTs === null || peak === null)
|
|
23795
|
+
return null;
|
|
23796
|
+
return {
|
|
23797
|
+
firstTs,
|
|
23798
|
+
lastTs: num(r.lastTs) ?? firstTs,
|
|
23799
|
+
peakRetryAfterSec: peak,
|
|
23800
|
+
untilTs,
|
|
23801
|
+
count: num(r.count) ?? 1
|
|
23802
|
+
};
|
|
23803
|
+
}
|
|
23804
|
+
function readFlood429LedgerResult(path2, readFile = (p) => readFileSync13(p, "utf-8")) {
|
|
23805
|
+
let text4;
|
|
23806
|
+
try {
|
|
23807
|
+
text4 = readFile(path2);
|
|
23808
|
+
} catch (err) {
|
|
23809
|
+
const code2 = err?.code;
|
|
23810
|
+
if (code2 === "ENOENT" || code2 === "ENOTDIR")
|
|
23811
|
+
return { status: "absent", episodes: [] };
|
|
23812
|
+
return {
|
|
23813
|
+
status: "unreadable",
|
|
23814
|
+
episodes: [],
|
|
23815
|
+
error: `${code2 ?? "EUNKNOWN"}: ${err?.message ?? String(err)}`
|
|
23816
|
+
};
|
|
23817
|
+
}
|
|
23818
|
+
let parsed;
|
|
23819
|
+
try {
|
|
23820
|
+
parsed = JSON.parse(text4);
|
|
23821
|
+
} catch (err) {
|
|
23822
|
+
return { status: "corrupt", episodes: [], error: err?.message ?? String(err) };
|
|
23823
|
+
}
|
|
23824
|
+
if (!Array.isArray(parsed)) {
|
|
23825
|
+
return { status: "corrupt", episodes: [], error: "ledger is not a JSON array" };
|
|
23826
|
+
}
|
|
23827
|
+
const out = [];
|
|
23828
|
+
let dropped = 0;
|
|
23829
|
+
for (const raw of parsed) {
|
|
23830
|
+
const ep = parseEpisode(raw);
|
|
23831
|
+
if (ep)
|
|
23832
|
+
out.push(ep);
|
|
23833
|
+
else
|
|
23834
|
+
dropped += 1;
|
|
23835
|
+
}
|
|
23836
|
+
if (dropped > 0 && out.length === 0) {
|
|
23837
|
+
return { status: "corrupt", episodes: [], error: `${dropped} unparseable entr(ies)` };
|
|
23838
|
+
}
|
|
23839
|
+
return { status: "ok", episodes: out };
|
|
23840
|
+
}
|
|
23841
|
+
function readFlood429Ledger(path2, readFile = (p) => readFileSync13(p, "utf-8")) {
|
|
23842
|
+
return readFlood429LedgerResult(path2, readFile).episodes;
|
|
23843
|
+
}
|
|
23844
|
+
function writeFlood429Ledger(path2, episodes, log = (l) => process.stderr.write(l)) {
|
|
23845
|
+
const payload = JSON.stringify(episodes);
|
|
23846
|
+
try {
|
|
23847
|
+
mkdirSync14(dirname7(path2), { recursive: true });
|
|
23848
|
+
} catch {}
|
|
23849
|
+
try {
|
|
23850
|
+
writeFileSync11(path2, payload, { mode: FLOOD_429_LEDGER_MODE });
|
|
23851
|
+
try {
|
|
23852
|
+
chmodSync4(path2, FLOOD_429_LEDGER_MODE);
|
|
23853
|
+
} catch {}
|
|
23854
|
+
return;
|
|
23855
|
+
} catch (err) {
|
|
23856
|
+
const code2 = err?.code;
|
|
23857
|
+
if (code2 !== "EACCES" && code2 !== "EPERM") {
|
|
23858
|
+
log(`telegram gateway: 429-ledger: could not persist ${path2} (${code2 ?? "error"})
|
|
23859
|
+
`);
|
|
23860
|
+
return;
|
|
23861
|
+
}
|
|
23862
|
+
try {
|
|
23863
|
+
unlinkSync9(path2);
|
|
23864
|
+
writeFileSync11(path2, payload, { mode: FLOOD_429_LEDGER_MODE });
|
|
23865
|
+
} catch (err2) {
|
|
23866
|
+
log(`telegram gateway: 429-ledger: cannot persist the 429 pressure ledger to ${path2} ` + `(${code2}, recreate failed: ${err2?.message ?? String(err2)}). ` + `Flood-pressure history will be missing from \`switchroom doctor\`
|
|
23867
|
+
`);
|
|
23868
|
+
}
|
|
23869
|
+
}
|
|
23870
|
+
}
|
|
23871
|
+
function recordFlood429(path2, obs, log = (l) => process.stderr.write(l)) {
|
|
23872
|
+
const folded = foldFlood429Observation(readFlood429Ledger(path2), obs, obs.ts);
|
|
23873
|
+
writeFlood429Ledger(path2, folded, log);
|
|
23874
|
+
}
|
|
23875
|
+
var FLOOD_429_LEDGER_FILE = "429-ledger.json", FLOOD_429_LEDGER_MODE = 420, EPISODE_MERGE_MS = 60000, FLOOD_429_LEDGER_MAX_EPISODES = 400, FLOOD_429_LEDGER_RETENTION_MS, PENALTY_WINDOW_MS, TRIVIAL_PRESSURE_WINDOW_MS;
|
|
23876
|
+
var init_flood_429_ledger = __esm(() => {
|
|
23877
|
+
FLOOD_429_LEDGER_RETENTION_MS = 30 * 24 * 60 * 60 * 1000;
|
|
23878
|
+
PENALTY_WINDOW_MS = 7 * 24 * 60 * 60 * 1000;
|
|
23879
|
+
TRIVIAL_PRESSURE_WINDOW_MS = 24 * 60 * 60 * 1000;
|
|
23880
|
+
});
|
|
23881
|
+
|
|
23882
|
+
// flood-circuit-breaker.ts
|
|
23883
|
+
import {
|
|
23884
|
+
existsSync as existsSync11,
|
|
23885
|
+
readFileSync as readFileSync14,
|
|
23886
|
+
writeFileSync as writeFileSync12,
|
|
23887
|
+
mkdirSync as mkdirSync15,
|
|
23888
|
+
chmodSync as chmodSync5,
|
|
23889
|
+
unlinkSync as unlinkSync10,
|
|
23890
|
+
renameSync as renameSync9
|
|
23891
|
+
} from "node:fs";
|
|
23892
|
+
import { dirname as dirname8, join as join18 } from "node:path";
|
|
23893
|
+
function floodStatePath(stateDir) {
|
|
23894
|
+
return join18(stateDir, FLOOD_STATE_FILE);
|
|
23895
|
+
}
|
|
23896
|
+
function computeFloodWait(prior, retryAfterSec, now) {
|
|
23897
|
+
const candidate = now + Math.max(0, retryAfterSec) * 1000;
|
|
23898
|
+
const untilTs = prior && prior.untilTs > candidate ? prior.untilTs : candidate;
|
|
23899
|
+
return { untilTs, retryAfterSec, recordedTs: now };
|
|
23900
|
+
}
|
|
23901
|
+
function floodWaitRemainingMs(state, now) {
|
|
23902
|
+
if (!state)
|
|
23903
|
+
return 0;
|
|
23904
|
+
return Math.max(0, state.untilTs - now);
|
|
23905
|
+
}
|
|
23906
|
+
function readFloodStateResult(path2) {
|
|
23907
|
+
let text4;
|
|
23908
|
+
try {
|
|
23909
|
+
text4 = readFileSync14(path2, "utf-8");
|
|
23910
|
+
} catch (err) {
|
|
23911
|
+
const code2 = err?.code;
|
|
23912
|
+
if (code2 === "ENOENT" || code2 === "ENOTDIR")
|
|
23913
|
+
return { status: "absent", state: null };
|
|
23914
|
+
return {
|
|
23915
|
+
status: "unreadable",
|
|
23916
|
+
state: null,
|
|
23917
|
+
error: `${code2 ?? "EUNKNOWN"}: ${err?.message ?? String(err)}`
|
|
23918
|
+
};
|
|
23919
|
+
}
|
|
23920
|
+
try {
|
|
23921
|
+
const raw = JSON.parse(text4);
|
|
23922
|
+
if (typeof raw.untilTs !== "number" || !Number.isFinite(raw.untilTs)) {
|
|
23923
|
+
return { status: "corrupt", state: null, error: "untilTs is not a finite number" };
|
|
23924
|
+
}
|
|
23925
|
+
return {
|
|
23926
|
+
status: "ok",
|
|
23927
|
+
state: {
|
|
23928
|
+
untilTs: raw.untilTs,
|
|
23929
|
+
retryAfterSec: typeof raw.retryAfterSec === "number" ? raw.retryAfterSec : 0,
|
|
23930
|
+
recordedTs: typeof raw.recordedTs === "number" ? raw.recordedTs : 0
|
|
23931
|
+
}
|
|
23932
|
+
};
|
|
23933
|
+
} catch (err) {
|
|
23934
|
+
return { status: "corrupt", state: null, error: err?.message ?? String(err) };
|
|
23935
|
+
}
|
|
23936
|
+
}
|
|
23937
|
+
function readFloodState(path2) {
|
|
23938
|
+
return readFloodStateResult(path2).state;
|
|
23939
|
+
}
|
|
23940
|
+
function writeFloodState(path2, state, log = (l) => process.stderr.write(l)) {
|
|
23941
|
+
const payload = JSON.stringify(state);
|
|
23942
|
+
try {
|
|
23943
|
+
mkdirSync15(dirname8(path2), { recursive: true });
|
|
23944
|
+
} catch {}
|
|
23945
|
+
try {
|
|
23946
|
+
writeFileSync12(path2, payload, { mode: FLOOD_STATE_MODE });
|
|
23947
|
+
try {
|
|
23948
|
+
chmodSync5(path2, FLOOD_STATE_MODE);
|
|
23949
|
+
} catch {}
|
|
23950
|
+
return;
|
|
23951
|
+
} catch (err) {
|
|
23952
|
+
const code2 = err?.code;
|
|
23953
|
+
if (code2 !== "EACCES" && code2 !== "EPERM") {
|
|
23954
|
+
log(`telegram gateway: flood-breaker: could not persist ${path2} (${code2 ?? "error"})
|
|
23955
|
+
`);
|
|
23956
|
+
return;
|
|
23957
|
+
}
|
|
23958
|
+
try {
|
|
23959
|
+
unlinkSync10(path2);
|
|
23960
|
+
writeFileSync12(path2, payload, { mode: FLOOD_STATE_MODE });
|
|
23961
|
+
log(`telegram gateway: flood-breaker: recreated ${path2} \u2014 it was owned by another uid ` + `and could not be updated (${code2}); the breaker was blind (issue #3106)
|
|
23962
|
+
`);
|
|
23963
|
+
} catch (err2) {
|
|
23964
|
+
log(`telegram gateway: flood-breaker: BLIND \u2014 cannot persist the flood-wait window to ${path2} ` + `(${code2}, and recreate failed: ${err2?.message ?? String(err2)}). ` + `Flood bans will NOT be recorded. Fix the file's ownership/mode (issue #3106)
|
|
23965
|
+
`);
|
|
23966
|
+
}
|
|
23967
|
+
}
|
|
23968
|
+
}
|
|
23969
|
+
function makeFloodWaitRecorder(path2, now = Date.now, log = (l) => process.stderr.write(l)) {
|
|
23970
|
+
return (retryAfterSec) => {
|
|
23971
|
+
const t = now();
|
|
23972
|
+
const next = computeFloodWait(readFloodState(path2), retryAfterSec, t);
|
|
23973
|
+
writeFloodState(path2, next, log);
|
|
23974
|
+
try {
|
|
23975
|
+
recordFlood429(flood429LedgerPathFromFloodState(path2), { ts: t, retryAfterSec }, log);
|
|
23976
|
+
} catch {}
|
|
23977
|
+
};
|
|
23978
|
+
}
|
|
23979
|
+
function warnBlind(path2, error, now, log) {
|
|
23980
|
+
const last = blindLoggedAt.get(path2);
|
|
23981
|
+
if (last !== undefined && now - last < BLIND_LOG_INTERVAL_MS)
|
|
23982
|
+
return;
|
|
23983
|
+
blindLoggedAt.set(path2, now);
|
|
23984
|
+
log(`telegram gateway: flood-breaker: BLIND \u2014 cannot read the flood-wait marker ${path2} ` + `(${error ?? "unknown error"}). The breaker cannot tell whether a Telegram flood ban is ` + `open. Essential sends PROCEED (fail-open); non-essential sends (boot card, typing) are ` + `SUPPRESSED. Fix the file's ownership/mode (issue #3106)
|
|
23985
|
+
`);
|
|
23986
|
+
}
|
|
23987
|
+
function makeFloodWaitProbe(path2, now = Date.now, log = (l) => process.stderr.write(l)) {
|
|
23988
|
+
return () => {
|
|
23989
|
+
const t = now();
|
|
23990
|
+
const res = readFloodStateResult(path2);
|
|
23991
|
+
if (res.status === "unreadable") {
|
|
23992
|
+
warnBlind(path2, res.error, t, log);
|
|
23993
|
+
return 0;
|
|
23994
|
+
}
|
|
23995
|
+
return floodWaitRemainingMs(res.state, t);
|
|
23996
|
+
};
|
|
23997
|
+
}
|
|
23998
|
+
function nonEssentialSendSuppression(path2, now) {
|
|
23999
|
+
const res = readFloodStateResult(path2);
|
|
24000
|
+
if (res.status === "unreadable") {
|
|
24001
|
+
return { suppress: true, reason: "blind", error: res.error ?? "unknown error" };
|
|
24002
|
+
}
|
|
24003
|
+
const remainingMs = floodWaitRemainingMs(res.state, now);
|
|
24004
|
+
if (remainingMs > 0)
|
|
24005
|
+
return { suppress: true, reason: "flood_wait", remainingMs };
|
|
24006
|
+
return { suppress: false };
|
|
24007
|
+
}
|
|
24008
|
+
var FLOOD_STATE_FILE = "flood-wait.json", FLOOD_STATE_MODE = 420, BLIND_LOG_INTERVAL_MS = 60000, blindLoggedAt, FLOOD_WINDOWS_CORRUPT_SUPPRESS_MS;
|
|
24009
|
+
var init_flood_circuit_breaker = __esm(() => {
|
|
24010
|
+
init_flood_429_ledger();
|
|
24011
|
+
blindLoggedAt = new Map;
|
|
24012
|
+
FLOOD_WINDOWS_CORRUPT_SUPPRESS_MS = 5 * 60000;
|
|
24013
|
+
});
|
|
24014
|
+
|
|
23914
24015
|
// quota-check.ts
|
|
23915
24016
|
import { readFileSync as readFileSync19, existsSync as existsSync17 } from "fs";
|
|
23916
24017
|
import { join as join25 } from "path";
|
|
@@ -37196,7 +37297,7 @@ function renderBootCard(opts) {
|
|
|
37196
37297
|
const ageStr = restartAgeMs != null && restartAgeMs > 0 ? ` \u00b7 ${(restartAgeMs / 1000).toFixed(1)}s ago` : "";
|
|
37197
37298
|
degradedRows.push(`\u26a0\ufe0f **Restart** ${escapeMarkdown(REASON_LABEL.crash)}${ageStr}`);
|
|
37198
37299
|
const tailCmd = process.env.SWITCHROOM_RUNTIME === "docker" ? `docker logs --tail 100 switchroom-${agentSlug}` : `journalctl --user -u switchroom-${agentSlug} -n 100`;
|
|
37199
|
-
degradedRows.push(
|
|
37300
|
+
degradedRows.push(`${NESTED_PREFIX}Tail logs: \`${tailCmd}\``);
|
|
37200
37301
|
}
|
|
37201
37302
|
if (probes) {
|
|
37202
37303
|
for (const key of PROBE_KEYS) {
|
|
@@ -37210,7 +37311,7 @@ function renderBootCard(opts) {
|
|
|
37210
37311
|
const dot = DOT[r.status] ?? DOT.fail;
|
|
37211
37312
|
degradedRows.push(`${dot} **${PROBE_LABELS[key]}** ${escapeMarkdown(r.detail)}`);
|
|
37212
37313
|
if (r.nextStep) {
|
|
37213
|
-
degradedRows.push(
|
|
37314
|
+
degradedRows.push(`${NESTED_PREFIX}${renderNextStep(r.nextStep)}`);
|
|
37214
37315
|
}
|
|
37215
37316
|
}
|
|
37216
37317
|
}
|
|
@@ -37514,6 +37615,7 @@ var SETTLE_WINDOW_MS = 6000, DOT, PROBE_LABELS, PROBE_KEYS, REASON_EMOJI, REASON
|
|
|
37514
37615
|
var init_boot_card = __esm(() => {
|
|
37515
37616
|
init_boot_probes();
|
|
37516
37617
|
init_card_format();
|
|
37618
|
+
init_status_no_truncate();
|
|
37517
37619
|
init_boot_issue_cache();
|
|
37518
37620
|
init_config_snapshot();
|
|
37519
37621
|
init_boot_card_msgid();
|
|
@@ -38979,7 +39081,7 @@ function buildGrantedKeyboard(scope) {
|
|
|
38979
39081
|
const btn = scopeToOpenInDriveButton(scope);
|
|
38980
39082
|
if (btn === null)
|
|
38981
39083
|
return;
|
|
38982
|
-
return new
|
|
39084
|
+
return new import_grammy16.InlineKeyboard().url(btn.text, btn.url);
|
|
38983
39085
|
}
|
|
38984
39086
|
async function handleApprovalCallback(ctx, data) {
|
|
38985
39087
|
const parsed = parseApprovalCallback(data);
|
|
@@ -39025,11 +39127,11 @@ async function handleApprovalCallback(ctx, data) {
|
|
|
39025
39127
|
} catch {}
|
|
39026
39128
|
await ctx.answerCallbackQuery({ text: granted ? "Approved" : "Denied" });
|
|
39027
39129
|
}
|
|
39028
|
-
var
|
|
39130
|
+
var import_grammy16;
|
|
39029
39131
|
var init_approval_callback = __esm(() => {
|
|
39030
39132
|
init_approval_card();
|
|
39031
39133
|
init_client3();
|
|
39032
|
-
|
|
39134
|
+
import_grammy16 = __toESM(require_mod2(), 1);
|
|
39033
39135
|
});
|
|
39034
39136
|
|
|
39035
39137
|
// ../src/telegram/materialize-bot-token.ts
|
|
@@ -39202,7 +39304,7 @@ var init_approvals_commands = __esm(() => {
|
|
|
39202
39304
|
});
|
|
39203
39305
|
|
|
39204
39306
|
// gateway/gateway.ts
|
|
39205
|
-
var
|
|
39307
|
+
var import_grammy17 = __toESM(require_mod(), 1);
|
|
39206
39308
|
var import_runner3 = __toESM(require_mod3(), 1);
|
|
39207
39309
|
import { randomBytes as randomBytes13, createHash as createHash8 } from "crypto";
|
|
39208
39310
|
import { execFileSync as execFileSync9, execSync as execSync2, spawn as spawn2 } from "child_process";
|
|
@@ -43181,10 +43283,64 @@ init_rich_send();
|
|
|
43181
43283
|
// shared/bot-runtime.ts
|
|
43182
43284
|
var import_grammy3 = __toESM(require_mod2(), 1);
|
|
43183
43285
|
var import_runner = __toESM(require_mod4(), 1);
|
|
43184
|
-
import { AsyncLocalStorage } from "async_hooks";
|
|
43286
|
+
import { AsyncLocalStorage as AsyncLocalStorage2 } from "async_hooks";
|
|
43185
43287
|
|
|
43186
43288
|
// retry-api-call.ts
|
|
43187
43289
|
var import_grammy2 = __toESM(require_mod2(), 1);
|
|
43290
|
+
import { AsyncLocalStorage } from "async_hooks";
|
|
43291
|
+
var attemptStore = new AsyncLocalStorage;
|
|
43292
|
+
function _getTgAttemptContext() {
|
|
43293
|
+
return attemptStore.getStore();
|
|
43294
|
+
}
|
|
43295
|
+
function withAttemptContext(ctx, fn) {
|
|
43296
|
+
return attemptStore.run(ctx, fn);
|
|
43297
|
+
}
|
|
43298
|
+
function isTransientNetworkMessage(msg) {
|
|
43299
|
+
return msg.includes("ECONNRESET") || msg.includes("ETIMEDOUT") || msg.includes("fetch failed") || msg.includes("ENOTFOUND");
|
|
43300
|
+
}
|
|
43301
|
+
function isAbortError(err) {
|
|
43302
|
+
const e = err;
|
|
43303
|
+
return e?.name === "AbortError" || e?.code === "ABORT_ERR";
|
|
43304
|
+
}
|
|
43305
|
+
function isTransientTransportError(err) {
|
|
43306
|
+
if (err instanceof import_grammy2.GrammyError)
|
|
43307
|
+
return false;
|
|
43308
|
+
if (isLocalResourceError(err))
|
|
43309
|
+
return false;
|
|
43310
|
+
if (err instanceof import_grammy2.HttpError) {
|
|
43311
|
+
const inner = err.error;
|
|
43312
|
+
if (isLocalResourceError(inner))
|
|
43313
|
+
return false;
|
|
43314
|
+
if (isAbortError(inner))
|
|
43315
|
+
return false;
|
|
43316
|
+
return true;
|
|
43317
|
+
}
|
|
43318
|
+
const msg = err instanceof Error ? err.message : String(err ?? "");
|
|
43319
|
+
if (isTransientNetworkMessage(msg))
|
|
43320
|
+
return true;
|
|
43321
|
+
const cause = err?.cause;
|
|
43322
|
+
if (cause == null || cause === err)
|
|
43323
|
+
return false;
|
|
43324
|
+
if (isLocalResourceError(cause))
|
|
43325
|
+
return false;
|
|
43326
|
+
return isTransientNetworkMessage(cause instanceof Error ? cause.message : String(cause));
|
|
43327
|
+
}
|
|
43328
|
+
function willRetryTelegramFailure(failure, ctx = _getTgAttemptContext()) {
|
|
43329
|
+
if (ctx == null)
|
|
43330
|
+
return false;
|
|
43331
|
+
if (ctx.attempt >= ctx.maxRetries - 1)
|
|
43332
|
+
return false;
|
|
43333
|
+
if (failure.errorCode === 429) {
|
|
43334
|
+
const retryAfter = Number(failure.retryAfterSec ?? 5);
|
|
43335
|
+
const delayMs = (Number.isFinite(retryAfter) ? retryAfter : 5) * 1000;
|
|
43336
|
+
return delayMs <= ctx.maxFloodSleepMs;
|
|
43337
|
+
}
|
|
43338
|
+
if (failure.errorCode != null)
|
|
43339
|
+
return false;
|
|
43340
|
+
if (failure.cause != null)
|
|
43341
|
+
return isTransientTransportError(failure.cause);
|
|
43342
|
+
return isTransientTransportError(new Error(failure.message ?? ""));
|
|
43343
|
+
}
|
|
43188
43344
|
function classifyBenignTelegram400(errorCode, description) {
|
|
43189
43345
|
if (errorCode !== 400)
|
|
43190
43346
|
return null;
|
|
@@ -43252,7 +43408,7 @@ function createRetryApiCall(config = {}) {
|
|
|
43252
43408
|
throw gated;
|
|
43253
43409
|
}
|
|
43254
43410
|
try {
|
|
43255
|
-
return await fn
|
|
43411
|
+
return await withAttemptContext({ attempt, maxRetries, maxFloodSleepMs }, fn);
|
|
43256
43412
|
} catch (err) {
|
|
43257
43413
|
const isGrammyErr = err instanceof import_grammy2.GrammyError;
|
|
43258
43414
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -43284,12 +43440,14 @@ function createRetryApiCall(config = {}) {
|
|
|
43284
43440
|
const benignKind = isGrammyErr ? classifyBenignTelegram400(err.error_code, desc) : null;
|
|
43285
43441
|
if (benignKind !== null) {
|
|
43286
43442
|
observer?.onBenign?.({ kind: benignKind });
|
|
43443
|
+
if (opts?.rethrowBenign400)
|
|
43444
|
+
throw err;
|
|
43287
43445
|
return;
|
|
43288
43446
|
}
|
|
43289
43447
|
if (isGrammyErr && err.error_code === 400 && desc.includes("thread not found") && opts?.threadId && opts?.chat_id) {
|
|
43290
43448
|
throw Object.assign(new Error("THREAD_NOT_FOUND"), { original: err });
|
|
43291
43449
|
}
|
|
43292
|
-
if (
|
|
43450
|
+
if (isTransientTransportError(err)) {
|
|
43293
43451
|
if (attempt < maxRetries - 1) {
|
|
43294
43452
|
const delayMs = Math.pow(2, attempt) * 1000;
|
|
43295
43453
|
log?.(`telegram gateway: network error, retrying in ${delayMs / 1000}s: ${msg}
|
|
@@ -43347,7 +43505,6 @@ function isPhotoDimensionRejectError(err) {
|
|
|
43347
43505
|
}
|
|
43348
43506
|
|
|
43349
43507
|
// shared/bot-runtime.ts
|
|
43350
|
-
init_flood_circuit_breaker();
|
|
43351
43508
|
init_format();
|
|
43352
43509
|
|
|
43353
43510
|
// shared/gw-trace-gate.ts
|
|
@@ -43360,7 +43517,7 @@ var ZERO_SIGNAL_POLL_METHODS = new Set(["getUpdates", "getMe"]);
|
|
|
43360
43517
|
function shouldEmitTgPost(method, status, verbose = gwTraceVerbose) {
|
|
43361
43518
|
if (verbose)
|
|
43362
43519
|
return true;
|
|
43363
|
-
if (status === "err")
|
|
43520
|
+
if (status === "err" || status === "retry")
|
|
43364
43521
|
return true;
|
|
43365
43522
|
return !ZERO_SIGNAL_POLL_METHODS.has(method);
|
|
43366
43523
|
}
|
|
@@ -43377,7 +43534,7 @@ function shouldEmitShadowTrace(eventKind, effectCount, globalKind, verbose = gwT
|
|
|
43377
43534
|
|
|
43378
43535
|
// shared/bot-runtime.ts
|
|
43379
43536
|
init_rich_send();
|
|
43380
|
-
var tgPostTagStore = new
|
|
43537
|
+
var tgPostTagStore = new AsyncLocalStorage2;
|
|
43381
43538
|
function escapeHtmlForTg(text) {
|
|
43382
43539
|
return text.replace(/([\\`*_~=\[\]|])/g, "\\$1");
|
|
43383
43540
|
}
|
|
@@ -44985,10 +45142,10 @@ async function handleAnimationMessage(ctx, deps) {
|
|
|
44985
45142
|
}
|
|
44986
45143
|
|
|
44987
45144
|
// gateway/photo-message-handler.ts
|
|
44988
|
-
import { mkdirSync as
|
|
45145
|
+
import { mkdirSync as mkdirSync6, writeFileSync as writeFileSync5 } from "node:fs";
|
|
44989
45146
|
|
|
44990
45147
|
// attachment-path.ts
|
|
44991
|
-
import { join as
|
|
45148
|
+
import { join as join5, basename as basename3, resolve as resolve2, sep } from "node:path";
|
|
44992
45149
|
function sanitizeExtension(ext) {
|
|
44993
45150
|
if (ext == null)
|
|
44994
45151
|
return "bin";
|
|
@@ -45011,7 +45168,7 @@ function buildAttachmentPath(input) {
|
|
|
45011
45168
|
const ext = extractExtension(input.telegramFilePath);
|
|
45012
45169
|
const uid = sanitizeUniqueId(input.fileUniqueId);
|
|
45013
45170
|
const filename = `${input.now}-${uid}.${ext}`;
|
|
45014
|
-
return
|
|
45171
|
+
return join5(input.inboxDir, filename);
|
|
45015
45172
|
}
|
|
45016
45173
|
function assertInsideInbox(inboxDir, candidatePath) {
|
|
45017
45174
|
const inboxReal = resolve2(inboxDir);
|
|
@@ -45046,9 +45203,9 @@ async function handlePhotoMessage(ctx, deps) {
|
|
|
45046
45203
|
fileUniqueId: best.file_unique_id,
|
|
45047
45204
|
now: Date.now()
|
|
45048
45205
|
});
|
|
45049
|
-
|
|
45206
|
+
mkdirSync6(deps.inboxDir, { recursive: true, mode: 448 });
|
|
45050
45207
|
assertInsideInbox(deps.inboxDir, dlPath);
|
|
45051
|
-
|
|
45208
|
+
writeFileSync5(dlPath, buf, { mode: 384 });
|
|
45052
45209
|
return dlPath;
|
|
45053
45210
|
} catch (err) {
|
|
45054
45211
|
const msg = err instanceof Error ? err.message : "unknown error";
|
|
@@ -45562,12 +45719,42 @@ function assessThinkingEffortRisk(model, effort) {
|
|
|
45562
45719
|
};
|
|
45563
45720
|
}
|
|
45564
45721
|
|
|
45565
|
-
//
|
|
45566
|
-
var MODEL_ALIASES = ["opus", "sonnet", "haiku", "fable", "default"];
|
|
45722
|
+
// ../src/agents/model-aliases.ts
|
|
45567
45723
|
var CLAUDE_MODEL_ALIASES = {
|
|
45568
45724
|
opus48: "claude-opus-4-8",
|
|
45569
45725
|
"opus-4-8": "claude-opus-4-8"
|
|
45570
45726
|
};
|
|
45727
|
+
var SR_MODEL_ALIASES = {
|
|
45728
|
+
flash: "sr-gemini-2.5-flash",
|
|
45729
|
+
gemini: "sr-gemini-2.5-pro",
|
|
45730
|
+
deepseek: "sr-deepseek-v3",
|
|
45731
|
+
r1: "sr-deepseek-r1",
|
|
45732
|
+
glm: "sr-glm-5",
|
|
45733
|
+
codex: "sr-codex-5.5",
|
|
45734
|
+
grok: "sr-grok-4.5",
|
|
45735
|
+
kimi: "sr-kimi-k3",
|
|
45736
|
+
gpt: "sr-gpt-5.6-sol",
|
|
45737
|
+
sol: "sr-gpt-5.6-sol",
|
|
45738
|
+
terra: "sr-gpt-5.6-terra",
|
|
45739
|
+
luna: "sr-gpt-5.6-luna"
|
|
45740
|
+
};
|
|
45741
|
+
function expandSrAlias(arg) {
|
|
45742
|
+
return SR_MODEL_ALIASES[arg.toLowerCase()] ?? arg;
|
|
45743
|
+
}
|
|
45744
|
+
function expandClaudeAlias(arg) {
|
|
45745
|
+
return CLAUDE_MODEL_ALIASES[arg.trim().toLowerCase()] ?? arg;
|
|
45746
|
+
}
|
|
45747
|
+
function canonicalModelToken(arg) {
|
|
45748
|
+
const trimmed = arg.trim();
|
|
45749
|
+
const lower = trimmed.toLowerCase();
|
|
45750
|
+
return lower.startsWith("claude-") ? lower : trimmed;
|
|
45751
|
+
}
|
|
45752
|
+
function expandModelAlias(arg) {
|
|
45753
|
+
return canonicalModelToken(expandSrAlias(expandClaudeAlias(arg)));
|
|
45754
|
+
}
|
|
45755
|
+
|
|
45756
|
+
// gateway/model-command.ts
|
|
45757
|
+
var MODEL_ALIASES = ["opus", "sonnet", "haiku", "fable", "default"];
|
|
45571
45758
|
var MODEL_ARG_RE = /^[A-Za-z0-9][A-Za-z0-9._/\[\]-]{0,99}$/;
|
|
45572
45759
|
function isValidModelArg(arg) {
|
|
45573
45760
|
return MODEL_ARG_RE.test(arg);
|
|
@@ -45767,34 +45954,6 @@ var SR_MODEL_LABELS = {
|
|
|
45767
45954
|
"sr-gpt-5.6-terra": "GPT-5.6 Terra",
|
|
45768
45955
|
"sr-gpt-5.6-luna": "GPT-5.6 Luna"
|
|
45769
45956
|
};
|
|
45770
|
-
var SR_MODEL_ALIASES = {
|
|
45771
|
-
flash: "sr-gemini-2.5-flash",
|
|
45772
|
-
gemini: "sr-gemini-2.5-pro",
|
|
45773
|
-
deepseek: "sr-deepseek-v3",
|
|
45774
|
-
r1: "sr-deepseek-r1",
|
|
45775
|
-
glm: "sr-glm-5",
|
|
45776
|
-
codex: "sr-codex-5.5",
|
|
45777
|
-
grok: "sr-grok-4.5",
|
|
45778
|
-
kimi: "sr-kimi-k3",
|
|
45779
|
-
gpt: "sr-gpt-5.6-sol",
|
|
45780
|
-
sol: "sr-gpt-5.6-sol",
|
|
45781
|
-
terra: "sr-gpt-5.6-terra",
|
|
45782
|
-
luna: "sr-gpt-5.6-luna"
|
|
45783
|
-
};
|
|
45784
|
-
function expandSrAlias(arg) {
|
|
45785
|
-
return SR_MODEL_ALIASES[arg.toLowerCase()] ?? arg;
|
|
45786
|
-
}
|
|
45787
|
-
function canonicalModelToken(arg) {
|
|
45788
|
-
const trimmed = arg.trim();
|
|
45789
|
-
const lower = trimmed.toLowerCase();
|
|
45790
|
-
return lower.startsWith("claude-") ? lower : trimmed;
|
|
45791
|
-
}
|
|
45792
|
-
function expandClaudeAlias(arg) {
|
|
45793
|
-
return CLAUDE_MODEL_ALIASES[arg.trim().toLowerCase()] ?? arg;
|
|
45794
|
-
}
|
|
45795
|
-
function expandModelAlias(arg) {
|
|
45796
|
-
return canonicalModelToken(expandSrAlias(expandClaudeAlias(arg)));
|
|
45797
|
-
}
|
|
45798
45957
|
function srFriendlyLabel(srName) {
|
|
45799
45958
|
return SR_MODEL_LABELS[srName] ?? srName.replace(/^sr-/, "").replace(/-/g, " ");
|
|
45800
45959
|
}
|
|
@@ -46272,7 +46431,7 @@ async function handleAskCallback(ctx, data, deps) {
|
|
|
46272
46431
|
}
|
|
46273
46432
|
|
|
46274
46433
|
// gateway/voice-ondemand-callback-handler.ts
|
|
46275
|
-
import { readFileSync as
|
|
46434
|
+
import { readFileSync as readFileSync7 } from "node:fs";
|
|
46276
46435
|
|
|
46277
46436
|
// voice-send.ts
|
|
46278
46437
|
function extractVoiceFileId(sent) {
|
|
@@ -46710,14 +46869,14 @@ table omitted.`);
|
|
|
46710
46869
|
init_loader();
|
|
46711
46870
|
init_resolver();
|
|
46712
46871
|
init_vault();
|
|
46713
|
-
import { existsSync as
|
|
46872
|
+
import { existsSync as existsSync7 } from "node:fs";
|
|
46714
46873
|
var VOICE_SIDECAR_TOKEN_KEY = "voice/sidecar-token";
|
|
46715
46874
|
var DEFAULT_VOICE_SIDECAR_TOKEN_REF = `vault:${VOICE_SIDECAR_TOKEN_KEY}`;
|
|
46716
46875
|
function tryDirectVaultRead(ref, config, passphrase) {
|
|
46717
46876
|
if (!passphrase)
|
|
46718
46877
|
return null;
|
|
46719
46878
|
const vaultPath = resolvePath(config.vault?.path ?? "~/.switchroom/vault.enc");
|
|
46720
|
-
if (!
|
|
46879
|
+
if (!existsSync7(vaultPath))
|
|
46721
46880
|
return null;
|
|
46722
46881
|
try {
|
|
46723
46882
|
const secrets = openVault(passphrase, vaultPath);
|
|
@@ -46804,7 +46963,7 @@ async function handleVoiceOnDemandCallback(ctx, data, deps) {
|
|
|
46804
46963
|
let audio = null;
|
|
46805
46964
|
if (entry.filePath != null) {
|
|
46806
46965
|
try {
|
|
46807
|
-
audio =
|
|
46966
|
+
audio = readFileSync7(entry.filePath);
|
|
46808
46967
|
} catch {
|
|
46809
46968
|
audio = null;
|
|
46810
46969
|
}
|
|
@@ -47389,18 +47548,11 @@ class DeferredDoneReactions {
|
|
|
47389
47548
|
|
|
47390
47549
|
// worker-activity-feed.ts
|
|
47391
47550
|
init_card_format();
|
|
47392
|
-
|
|
47393
|
-
// status-no-truncate.ts
|
|
47394
|
-
init_format();
|
|
47395
|
-
var STATUS_ROLLING_LINES = 5;
|
|
47396
|
-
var WORKER_HISTORY_MAX = 6;
|
|
47397
|
-
var STATUS_LINE_MAX = 200;
|
|
47398
|
-
var STATUS_CARD_CHAR_BUDGET = RICH_MESSAGE_MAX_CHARS;
|
|
47399
|
-
var NESTED_PREFIX = " \u21b3 ";
|
|
47400
|
-
var WORKER_STEP_INDENT = "\u2800\u2800\u2800";
|
|
47551
|
+
init_status_no_truncate();
|
|
47401
47552
|
|
|
47402
47553
|
// card-layout.ts
|
|
47403
47554
|
init_card_format();
|
|
47555
|
+
init_format();
|
|
47404
47556
|
|
|
47405
47557
|
// model-label.ts
|
|
47406
47558
|
var DATE_SUFFIX = /^\d{8}$/;
|
|
@@ -47439,6 +47591,7 @@ function formatModelLabel(model) {
|
|
|
47439
47591
|
}
|
|
47440
47592
|
|
|
47441
47593
|
// card-layout.ts
|
|
47594
|
+
init_status_no_truncate();
|
|
47442
47595
|
function formatFeedElapsed(ms) {
|
|
47443
47596
|
const s = Math.floor(ms / 1000);
|
|
47444
47597
|
if (s < 60)
|
|
@@ -47537,15 +47690,36 @@ function fitCardToBudget(build, maxLevel) {
|
|
|
47537
47690
|
if (candidate.overflow !== true && text.length <= STATUS_CARD_CHAR_BUDGET)
|
|
47538
47691
|
return text;
|
|
47539
47692
|
}
|
|
47540
|
-
return text;
|
|
47693
|
+
return hardTruncateCard(text);
|
|
47694
|
+
}
|
|
47695
|
+
function hardTruncateCard(text, budget = STATUS_CARD_CHAR_BUDGET) {
|
|
47696
|
+
if (text.length <= budget)
|
|
47697
|
+
return text;
|
|
47698
|
+
let kept = "";
|
|
47699
|
+
for (const line of text.split(`
|
|
47700
|
+
`)) {
|
|
47701
|
+
const next = kept.length === 0 ? line : `${kept}
|
|
47702
|
+
${line}`;
|
|
47703
|
+
if (trimBreakChrome(next).length > budget)
|
|
47704
|
+
break;
|
|
47705
|
+
kept = next;
|
|
47706
|
+
}
|
|
47707
|
+
const target = kept.length > 0 ? kept.length : Math.min(budget, text.length);
|
|
47708
|
+
let out = trimBreakChrome(truncateMarkdownSafe(text, target));
|
|
47709
|
+
if (out.length > budget)
|
|
47710
|
+
out = trimBreakChrome(truncateMarkdownSafe(text, budget));
|
|
47711
|
+
return /[\uD800-\uDBFF]$/.test(out) ? out.slice(0, -1) : out;
|
|
47712
|
+
}
|
|
47713
|
+
function trimBreakChrome(text) {
|
|
47714
|
+
return text.replace(/[ \t\r\u00A0]+$/, "");
|
|
47541
47715
|
}
|
|
47542
47716
|
|
|
47543
47717
|
// hooks/tool-label-pretool.mjs
|
|
47544
|
-
import { readFileSync as
|
|
47545
|
-
import { join as
|
|
47718
|
+
import { readFileSync as readFileSync8, mkdirSync as mkdirSync9, appendFileSync as appendFileSync2, existsSync as existsSync8 } from "node:fs";
|
|
47719
|
+
import { join as join7, basename as basename5 } from "node:path";
|
|
47546
47720
|
function readStdin() {
|
|
47547
47721
|
try {
|
|
47548
|
-
return
|
|
47722
|
+
return readFileSync8(0, "utf8");
|
|
47549
47723
|
} catch {
|
|
47550
47724
|
return "";
|
|
47551
47725
|
}
|
|
@@ -47851,10 +48025,10 @@ function main() {
|
|
|
47851
48025
|
}) + `
|
|
47852
48026
|
`;
|
|
47853
48027
|
try {
|
|
47854
|
-
if (!
|
|
47855
|
-
|
|
48028
|
+
if (!existsSync8(stateDir)) {
|
|
48029
|
+
mkdirSync9(stateDir, { recursive: true });
|
|
47856
48030
|
}
|
|
47857
|
-
const target =
|
|
48031
|
+
const target = join7(stateDir, `tool-labels-${sessionId}.jsonl`);
|
|
47858
48032
|
appendFileSync2(target, line);
|
|
47859
48033
|
} catch (err) {
|
|
47860
48034
|
try {
|
|
@@ -47876,6 +48050,7 @@ if (isMain)
|
|
|
47876
48050
|
main();
|
|
47877
48051
|
|
|
47878
48052
|
// tool-activity-summary.ts
|
|
48053
|
+
init_status_no_truncate();
|
|
47879
48054
|
init_card_format();
|
|
47880
48055
|
init_redact();
|
|
47881
48056
|
|
|
@@ -47953,14 +48128,14 @@ function renderStatusCard(opts) {
|
|
|
47953
48128
|
return null;
|
|
47954
48129
|
const parentMarker = hasChildren && rawSteps.length > 0 ? `_\u2713 +${rawSteps.length} earlier\u2026_` : null;
|
|
47955
48130
|
const deepest = Math.max(1, escapedBody.length);
|
|
48131
|
+
const markerSection = {
|
|
48132
|
+
steps: [],
|
|
48133
|
+
window: 1,
|
|
48134
|
+
placeholder: parentMarker ?? undefined
|
|
48135
|
+
};
|
|
47956
48136
|
return fitCardToBudget((level) => {
|
|
47957
48137
|
if (level === 0)
|
|
47958
48138
|
return { spec: fullSpec() };
|
|
47959
|
-
const markerSection = {
|
|
47960
|
-
steps: [],
|
|
47961
|
-
window: 1,
|
|
47962
|
-
placeholder: parentMarker ?? undefined
|
|
47963
|
-
};
|
|
47964
48139
|
if (level < escapedBody.length) {
|
|
47965
48140
|
return {
|
|
47966
48141
|
spec: {
|
|
@@ -47989,23 +48164,38 @@ function renderStatusCard(opts) {
|
|
|
47989
48164
|
}
|
|
47990
48165
|
};
|
|
47991
48166
|
}, deepest);
|
|
48167
|
+
function deepestSpec(bodyLine) {
|
|
48168
|
+
return {
|
|
48169
|
+
chrome,
|
|
48170
|
+
sections: [markerSection, { steps: [], window: 1, placeholder: bodyLine }],
|
|
48171
|
+
footer
|
|
48172
|
+
};
|
|
48173
|
+
}
|
|
48174
|
+
function wrapNewest(escaped) {
|
|
48175
|
+
return final ? `${bodyIndent}_\u2713 ${escaped}_` : `${bodyIndent}**\u2192 ${escaped}${liveSuffix}**`;
|
|
48176
|
+
}
|
|
47992
48177
|
function truncatedNewestLine() {
|
|
47993
|
-
const fixedCost = [...chrome, ...footer].join(`
|
|
47994
|
-
`).length;
|
|
47995
48178
|
const rawNewest = rawBody.length > 0 ? cleanStepLine(rawBody[rawBody.length - 1]) : "";
|
|
47996
|
-
const
|
|
47997
|
-
const
|
|
47998
|
-
const budget = STATUS_CARD_CHAR_BUDGET -
|
|
47999
|
-
|
|
48000
|
-
|
|
48001
|
-
|
|
48002
|
-
|
|
48003
|
-
|
|
48004
|
-
|
|
48179
|
+
const empty = wrapNewest("");
|
|
48180
|
+
const fixedCost = renderCardSpec(deepestSpec(empty)).length;
|
|
48181
|
+
const budget = STATUS_CARD_CHAR_BUDGET - fixedCost;
|
|
48182
|
+
if (budget <= 0)
|
|
48183
|
+
return empty;
|
|
48184
|
+
let raw = clipRaw(rawNewest, budget);
|
|
48185
|
+
let line = wrapNewest(escapeMarkdown(raw));
|
|
48186
|
+
for (;; ) {
|
|
48187
|
+
const excess = renderCardSpec(deepestSpec(line)).length - STATUS_CARD_CHAR_BUDGET;
|
|
48188
|
+
if (excess <= 0 || raw.length === 0)
|
|
48189
|
+
return line;
|
|
48190
|
+
raw = clipRaw(raw, Math.max(0, raw.length - excess));
|
|
48191
|
+
line = wrapNewest(escapeMarkdown(raw));
|
|
48005
48192
|
}
|
|
48006
|
-
return final ? `${bodyIndent}_\u2713 ${newest}_` : `${bodyIndent}**\u2192 ${newest}${liveSuffix}**`;
|
|
48007
48193
|
}
|
|
48008
48194
|
}
|
|
48195
|
+
function clipRaw(text, n) {
|
|
48196
|
+
const out = text.slice(0, Math.max(0, n));
|
|
48197
|
+
return /[\uD800-\uDBFF]$/.test(out) ? out.slice(0, -1) : out;
|
|
48198
|
+
}
|
|
48009
48199
|
var WORKER_RESULT_RULE = "\u2500\u2500\u2500\u2500\u2500";
|
|
48010
48200
|
var WORKER_RESULT_MAX = 320;
|
|
48011
48201
|
function deriveCardResult(state, summary) {
|
|
@@ -48106,8 +48296,8 @@ function appendActivityLabel(lines, label) {
|
|
|
48106
48296
|
}
|
|
48107
48297
|
|
|
48108
48298
|
// outbound-class.ts
|
|
48109
|
-
import { AsyncLocalStorage as
|
|
48110
|
-
var store = new
|
|
48299
|
+
import { AsyncLocalStorage as AsyncLocalStorage3 } from "async_hooks";
|
|
48300
|
+
var store = new AsyncLocalStorage3;
|
|
48111
48301
|
function withOutboundClass(cls, fn) {
|
|
48112
48302
|
return store.run(cls, fn);
|
|
48113
48303
|
}
|
|
@@ -48211,7 +48401,7 @@ function classifyEditError(err) {
|
|
|
48211
48401
|
function createWorkerActivityFeed(opts) {
|
|
48212
48402
|
const log = opts.log ?? (() => {});
|
|
48213
48403
|
const nowFn = opts.now ?? Date.now;
|
|
48214
|
-
const
|
|
48404
|
+
const floodWaitRemainingMs = opts.floodWaitRemainingMs ?? (() => 0);
|
|
48215
48405
|
const minEditInterval = opts.minEditIntervalMs ?? 2500;
|
|
48216
48406
|
const elapsedRefreshMs = Math.max(minEditInterval, Math.floor(opts.elapsedRefreshMs ?? 15000));
|
|
48217
48407
|
const firstPaintMin = opts.firstPaintMinMs ?? 8000;
|
|
@@ -48264,7 +48454,7 @@ function createWorkerActivityFeed(opts) {
|
|
|
48264
48454
|
log(`worker-feed: ${label} 429 \u2014 backing off ${retryAfter}s`);
|
|
48265
48455
|
}
|
|
48266
48456
|
function parkIfFloodWindowOpen(g) {
|
|
48267
|
-
const remaining =
|
|
48457
|
+
const remaining = floodWaitRemainingMs();
|
|
48268
48458
|
if (remaining <= 0)
|
|
48269
48459
|
return false;
|
|
48270
48460
|
const until = nowFn() + remaining + COOLDOWN_JITTER_MS;
|
|
@@ -49221,13 +49411,13 @@ function buildCancelledCardEdits(cardText, cards) {
|
|
|
49221
49411
|
var STALE_TAP_NOTICE = "This request already resolved (timed out) \u2014 ask again to act.";
|
|
49222
49412
|
|
|
49223
49413
|
// gateway/permission-card-store.ts
|
|
49224
|
-
import { readFileSync as
|
|
49225
|
-
import { join as
|
|
49414
|
+
import { readFileSync as readFileSync9, writeFileSync as writeFileSync6, unlinkSync as unlinkSync4 } from "node:fs";
|
|
49415
|
+
import { join as join8 } from "node:path";
|
|
49226
49416
|
function createPermissionCardStore(stateDir) {
|
|
49227
|
-
const filePath =
|
|
49417
|
+
const filePath = join8(stateDir, "pending-perm-cards.json");
|
|
49228
49418
|
function read() {
|
|
49229
49419
|
try {
|
|
49230
|
-
const raw =
|
|
49420
|
+
const raw = readFileSync9(filePath, "utf-8");
|
|
49231
49421
|
const parsed = JSON.parse(raw);
|
|
49232
49422
|
return Array.isArray(parsed) ? parsed : [];
|
|
49233
49423
|
} catch {
|
|
@@ -49236,7 +49426,7 @@ function createPermissionCardStore(stateDir) {
|
|
|
49236
49426
|
}
|
|
49237
49427
|
function write(entries) {
|
|
49238
49428
|
try {
|
|
49239
|
-
|
|
49429
|
+
writeFileSync6(filePath, JSON.stringify(entries), { encoding: "utf-8", mode: 384 });
|
|
49240
49430
|
} catch (err) {
|
|
49241
49431
|
process.stderr.write(`telegram gateway: permission-card-store write failed: ${err.message}
|
|
49242
49432
|
`);
|
|
@@ -49265,7 +49455,7 @@ function createPermissionCardStore(stateDir) {
|
|
|
49265
49455
|
},
|
|
49266
49456
|
clear() {
|
|
49267
49457
|
try {
|
|
49268
|
-
|
|
49458
|
+
unlinkSync4(filePath);
|
|
49269
49459
|
} catch {}
|
|
49270
49460
|
}
|
|
49271
49461
|
};
|
|
@@ -49273,13 +49463,13 @@ function createPermissionCardStore(stateDir) {
|
|
|
49273
49463
|
|
|
49274
49464
|
// gateway/pending-card-store.ts
|
|
49275
49465
|
init_atomic();
|
|
49276
|
-
import { unlinkSync as
|
|
49277
|
-
import { join as
|
|
49466
|
+
import { unlinkSync as unlinkSync5 } from "node:fs";
|
|
49467
|
+
import { join as join10 } from "node:path";
|
|
49278
49468
|
|
|
49279
49469
|
// gateway/store-file.ts
|
|
49280
49470
|
import { randomBytes as randomBytes6 } from "node:crypto";
|
|
49281
|
-
import { readFileSync as
|
|
49282
|
-
import { basename as basename6, dirname as
|
|
49471
|
+
import { readFileSync as readFileSync10, readdirSync as readdirSync4, renameSync as renameSync8, rmSync as rmSync4, statSync as statSync7 } from "node:fs";
|
|
49472
|
+
import { basename as basename6, dirname as dirname4, join as join9 } from "node:path";
|
|
49283
49473
|
var defaultLog = (line) => {
|
|
49284
49474
|
process.stderr.write(line);
|
|
49285
49475
|
};
|
|
@@ -49290,18 +49480,18 @@ function quarantinePath(filePath) {
|
|
|
49290
49480
|
return `${filePath}.corrupt-${Date.now()}-${seq}-${randomBytes6(4).toString("hex")}`;
|
|
49291
49481
|
}
|
|
49292
49482
|
function reapOldQuarantines(filePath, log) {
|
|
49293
|
-
const dir =
|
|
49483
|
+
const dir = dirname4(filePath);
|
|
49294
49484
|
const prefix = `${basename6(filePath)}.corrupt-`;
|
|
49295
49485
|
try {
|
|
49296
49486
|
const copies = readdirSync4(dir).filter((f) => f.startsWith(prefix)).map((name) => {
|
|
49297
49487
|
let mtimeNs = 0n;
|
|
49298
49488
|
try {
|
|
49299
|
-
mtimeNs = statSync7(
|
|
49489
|
+
mtimeNs = statSync7(join9(dir, name), { bigint: true }).mtimeNs;
|
|
49300
49490
|
} catch {}
|
|
49301
49491
|
return { name, mtimeNs };
|
|
49302
49492
|
}).sort((a, b) => a.mtimeNs === b.mtimeNs ? a.name.localeCompare(b.name) : a.mtimeNs < b.mtimeNs ? -1 : 1);
|
|
49303
49493
|
for (const stale of copies.slice(0, Math.max(0, copies.length - MAX_QUARANTINED_COPIES))) {
|
|
49304
|
-
rmSync4(
|
|
49494
|
+
rmSync4(join9(dir, stale.name), { force: true });
|
|
49305
49495
|
}
|
|
49306
49496
|
} catch (err) {
|
|
49307
49497
|
log(`telegram gateway: quarantine reap failed dir=${dir}: ${err.message}
|
|
@@ -49312,7 +49502,7 @@ function quarantineCorruptStoreFile(filePath, store2, reason, log = defaultLog)
|
|
|
49312
49502
|
const target = quarantinePath(filePath);
|
|
49313
49503
|
let preserved = target;
|
|
49314
49504
|
try {
|
|
49315
|
-
|
|
49505
|
+
renameSync8(filePath, target);
|
|
49316
49506
|
} catch (err) {
|
|
49317
49507
|
preserved = `NOT preserved (${err.message})`;
|
|
49318
49508
|
}
|
|
@@ -49323,7 +49513,7 @@ function quarantineCorruptStoreFile(filePath, store2, reason, log = defaultLog)
|
|
|
49323
49513
|
function preserveUnreadableStoreFile(filePath, store2, log = defaultLog) {
|
|
49324
49514
|
const target = quarantinePath(filePath);
|
|
49325
49515
|
try {
|
|
49326
|
-
|
|
49516
|
+
renameSync8(filePath, target);
|
|
49327
49517
|
} catch (err) {
|
|
49328
49518
|
if (err.code === "ENOENT") {
|
|
49329
49519
|
return;
|
|
@@ -49339,7 +49529,7 @@ function preserveUnreadableStoreFile(filePath, store2, log = defaultLog) {
|
|
|
49339
49529
|
function readStoreJsonSync(filePath, store2, log = defaultLog) {
|
|
49340
49530
|
let raw;
|
|
49341
49531
|
try {
|
|
49342
|
-
raw =
|
|
49532
|
+
raw = readFileSync10(filePath, "utf-8");
|
|
49343
49533
|
} catch (err) {
|
|
49344
49534
|
if (err.code === "ENOENT")
|
|
49345
49535
|
return { status: "missing" };
|
|
@@ -49357,7 +49547,7 @@ function readStoreJsonSync(filePath, store2, log = defaultLog) {
|
|
|
49357
49547
|
|
|
49358
49548
|
// gateway/pending-card-store.ts
|
|
49359
49549
|
function createPendingCardStore(stateDir, log = (l) => process.stderr.write(l)) {
|
|
49360
|
-
const filePath =
|
|
49550
|
+
const filePath = join10(stateDir, "pending-approval-cards.json");
|
|
49361
49551
|
let unreadable = false;
|
|
49362
49552
|
function read() {
|
|
49363
49553
|
const result = readStoreJsonSync(filePath, "pending-card-store", log);
|
|
@@ -49405,7 +49595,7 @@ function createPendingCardStore(stateDir, log = (l) => process.stderr.write(l))
|
|
|
49405
49595
|
},
|
|
49406
49596
|
clear() {
|
|
49407
49597
|
try {
|
|
49408
|
-
|
|
49598
|
+
unlinkSync5(filePath);
|
|
49409
49599
|
} catch {}
|
|
49410
49600
|
}
|
|
49411
49601
|
};
|
|
@@ -49544,8 +49734,8 @@ function createSweepableCardStore(deps) {
|
|
|
49544
49734
|
init_rich_send();
|
|
49545
49735
|
var import_grammy4 = __toESM(require_mod2(), 1);
|
|
49546
49736
|
import { execFileSync as execFileSync3 } from "child_process";
|
|
49547
|
-
import { mkdirSync as
|
|
49548
|
-
import { dirname as
|
|
49737
|
+
import { mkdirSync as mkdirSync12, writeFileSync as writeFileSync8 } from "fs";
|
|
49738
|
+
import { dirname as dirname5 } from "path";
|
|
49549
49739
|
init_client2();
|
|
49550
49740
|
|
|
49551
49741
|
// gateway/vault-grant-inbound-builders.ts
|
|
@@ -49677,26 +49867,26 @@ var import_yaml3 = __toESM(require_dist(), 1);
|
|
|
49677
49867
|
|
|
49678
49868
|
// ../src/web/config-diff.ts
|
|
49679
49869
|
import {
|
|
49680
|
-
mkdirSync as
|
|
49870
|
+
mkdirSync as mkdirSync10,
|
|
49681
49871
|
mkdtempSync as mkdtemp,
|
|
49682
49872
|
rmSync as rmrf,
|
|
49683
|
-
writeFileSync as
|
|
49873
|
+
writeFileSync as writeFileSync7
|
|
49684
49874
|
} from "node:fs";
|
|
49685
49875
|
import { spawnSync } from "node:child_process";
|
|
49686
49876
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
49687
|
-
import { join as
|
|
49877
|
+
import { join as join11 } from "node:path";
|
|
49688
49878
|
|
|
49689
49879
|
class ConfigDiffError extends Error {
|
|
49690
49880
|
}
|
|
49691
49881
|
function generateUnifiedDiff(before, after, name = "switchroom.yaml", gitBin = "git") {
|
|
49692
49882
|
if (before === after)
|
|
49693
49883
|
return "";
|
|
49694
|
-
const dir = mkdtemp(
|
|
49884
|
+
const dir = mkdtemp(join11(tmpdir2(), "switchroom-config-diff-"));
|
|
49695
49885
|
try {
|
|
49696
|
-
|
|
49697
|
-
|
|
49698
|
-
|
|
49699
|
-
|
|
49886
|
+
mkdirSync10(join11(dir, "cur"), { recursive: true });
|
|
49887
|
+
mkdirSync10(join11(dir, "new"), { recursive: true });
|
|
49888
|
+
writeFileSync7(join11(dir, "cur", name), before);
|
|
49889
|
+
writeFileSync7(join11(dir, "new", name), after);
|
|
49700
49890
|
const r = spawnSync(gitBin, ["diff", "--no-index", "--no-color", "--", `cur/${name}`, `new/${name}`], { cwd: dir, encoding: "utf-8", timeout: 1e4 });
|
|
49701
49891
|
if (r.status === 0)
|
|
49702
49892
|
return "";
|
|
@@ -50027,25 +50217,25 @@ function buildSkillProposalApplyInbound(opts) {
|
|
|
50027
50217
|
// ../src/self-improve/skill-proposals.ts
|
|
50028
50218
|
import {
|
|
50029
50219
|
closeSync as closeSync5,
|
|
50030
|
-
existsSync as
|
|
50031
|
-
mkdirSync as
|
|
50220
|
+
existsSync as existsSync9,
|
|
50221
|
+
mkdirSync as mkdirSync11,
|
|
50032
50222
|
openSync as openSync5,
|
|
50033
|
-
readFileSync as
|
|
50223
|
+
readFileSync as readFileSync11,
|
|
50034
50224
|
writeSync as writeSync5
|
|
50035
50225
|
} from "node:fs";
|
|
50036
|
-
import { join as
|
|
50226
|
+
import { join as join12 } from "node:path";
|
|
50037
50227
|
var PROPOSALS_FILE = "skill-proposals.jsonl";
|
|
50038
50228
|
var REJECTED_FILE = "skill-proposals-rejected.jsonl";
|
|
50039
50229
|
var REJECTION_TTL_MS = 90 * 24 * 60 * 60 * 1000;
|
|
50040
50230
|
function proposalsPath(stateDir) {
|
|
50041
|
-
return
|
|
50231
|
+
return join12(stateDir, PROPOSALS_FILE);
|
|
50042
50232
|
}
|
|
50043
50233
|
function rejectedPath(stateDir) {
|
|
50044
|
-
return
|
|
50234
|
+
return join12(stateDir, REJECTED_FILE);
|
|
50045
50235
|
}
|
|
50046
50236
|
function ensureDir2(stateDir) {
|
|
50047
|
-
if (!
|
|
50048
|
-
|
|
50237
|
+
if (!existsSync9(stateDir)) {
|
|
50238
|
+
mkdirSync11(stateDir, { recursive: true, mode: 493 });
|
|
50049
50239
|
}
|
|
50050
50240
|
}
|
|
50051
50241
|
function appendLine(path, obj) {
|
|
@@ -50058,11 +50248,11 @@ function appendLine(path, obj) {
|
|
|
50058
50248
|
}
|
|
50059
50249
|
}
|
|
50060
50250
|
function readLines(path, isValid2) {
|
|
50061
|
-
if (!
|
|
50251
|
+
if (!existsSync9(path))
|
|
50062
50252
|
return [];
|
|
50063
50253
|
let raw;
|
|
50064
50254
|
try {
|
|
50065
|
-
raw =
|
|
50255
|
+
raw = readFileSync11(path, "utf-8");
|
|
50066
50256
|
} catch {
|
|
50067
50257
|
return [];
|
|
50068
50258
|
}
|
|
@@ -50204,7 +50394,7 @@ async function addAccountViaBroker(label, credentials, opts = {}) {
|
|
|
50204
50394
|
}
|
|
50205
50395
|
|
|
50206
50396
|
// gateway/hostd-dispatch.ts
|
|
50207
|
-
import { existsSync as
|
|
50397
|
+
import { existsSync as existsSync10 } from "node:fs";
|
|
50208
50398
|
import { randomBytes as randomBytes7 } from "node:crypto";
|
|
50209
50399
|
|
|
50210
50400
|
// ../src/host-control/client.ts
|
|
@@ -50527,13 +50717,13 @@ function hostdSocketPath(agentName) {
|
|
|
50527
50717
|
function hostdWillBeUsed(agentName) {
|
|
50528
50718
|
if (!isHostdEnabled())
|
|
50529
50719
|
return false;
|
|
50530
|
-
return
|
|
50720
|
+
return existsSync10(hostdSocketPath(agentName));
|
|
50531
50721
|
}
|
|
50532
50722
|
async function tryHostdDispatch(agentName, req, timeoutMs = 5000) {
|
|
50533
50723
|
if (!isHostdEnabled())
|
|
50534
50724
|
return "not-configured";
|
|
50535
50725
|
const sockPath = hostdSocketPath(agentName);
|
|
50536
|
-
if (!
|
|
50726
|
+
if (!existsSync10(sockPath))
|
|
50537
50727
|
return "not-configured";
|
|
50538
50728
|
try {
|
|
50539
50729
|
return await hostdRequest({ socketPath: sockPath, timeoutMs }, req);
|
|
@@ -50557,7 +50747,7 @@ async function hostdGetStatusOnce(agentName, targetRequestId) {
|
|
|
50557
50747
|
if (!isHostdEnabled())
|
|
50558
50748
|
return "not-configured";
|
|
50559
50749
|
const sockPath = hostdSocketPath(agentName);
|
|
50560
|
-
if (!
|
|
50750
|
+
if (!existsSync10(sockPath))
|
|
50561
50751
|
return "not-configured";
|
|
50562
50752
|
try {
|
|
50563
50753
|
const resp = await hostdRequest({ socketPath: sockPath, timeoutMs: 3000 }, {
|
|
@@ -50705,8 +50895,8 @@ function createCallbackQueryHandlers(deps) {
|
|
|
50705
50895
|
const { token, id } = result;
|
|
50706
50896
|
const tokenPath = vaultTokenFilePath2(agentName);
|
|
50707
50897
|
try {
|
|
50708
|
-
|
|
50709
|
-
|
|
50898
|
+
mkdirSync12(dirname5(tokenPath), { recursive: true });
|
|
50899
|
+
writeFileSync8(tokenPath, token, { mode: 384 });
|
|
50710
50900
|
} catch (err) {
|
|
50711
50901
|
await switchroomReply(ctx, `**Grant created (${escapeHtmlForTg2(id)}) but token write failed:** ` + `${escapeHtmlForTg2(String(err))}
|
|
50712
50902
|
` + `_Recover with: \`switchroom vault grant ${escapeHtmlForTg2(agentName)} ` + `--keys ${escapeHtmlForTg2(keyName)} --duration 30d\` on the host._`, { html: true });
|
|
@@ -50857,8 +51047,8 @@ function createCallbackQueryHandlers(deps) {
|
|
|
50857
51047
|
const { token, id } = result;
|
|
50858
51048
|
const tokenPath = vaultTokenFilePath2(pending.agent);
|
|
50859
51049
|
try {
|
|
50860
|
-
|
|
50861
|
-
|
|
51050
|
+
mkdirSync12(dirname5(tokenPath), { recursive: true });
|
|
51051
|
+
writeFileSync8(tokenPath, token, { mode: 384 });
|
|
50862
51052
|
} catch (err) {
|
|
50863
51053
|
await switchroomReply(ctx, `**Grant created (${escapeHtmlForTg2(id)}) but token write failed:** ` + `${escapeHtmlForTg2(String(err))}
|
|
50864
51054
|
` + `_Recover with: \`switchroom vault grant ${escapeHtmlForTg2(pending.agent)} ` + `--keys ${escapeHtmlForTg2(pending.key)} --duration ${Math.round(pending.ttl_seconds / 86400)}d\` on the host._`, { html: true });
|
|
@@ -51686,8 +51876,8 @@ ${keyList}`,
|
|
|
51686
51876
|
const { token, id } = result;
|
|
51687
51877
|
const tokenPath = vaultTokenFilePath2(state.agent);
|
|
51688
51878
|
try {
|
|
51689
|
-
|
|
51690
|
-
|
|
51879
|
+
mkdirSync12(dirname5(tokenPath), { recursive: true });
|
|
51880
|
+
writeFileSync8(tokenPath, token, { mode: 384 });
|
|
51691
51881
|
} catch (err) {
|
|
51692
51882
|
await switchroomReply(ctx, `**Grant created but token write failed:** ${escapeHtmlForTg2(String(err))}`, { html: true });
|
|
51693
51883
|
return;
|
|
@@ -52745,12 +52935,12 @@ function hangStalenessMs(env = process.env) {
|
|
|
52745
52935
|
|
|
52746
52936
|
// gateway/missed-approvals-store.ts
|
|
52747
52937
|
init_atomic();
|
|
52748
|
-
import { unlinkSync as
|
|
52749
|
-
import { join as
|
|
52938
|
+
import { unlinkSync as unlinkSync6 } from "node:fs";
|
|
52939
|
+
import { join as join14 } from "node:path";
|
|
52750
52940
|
var MAX_PENDING = 50;
|
|
52751
52941
|
var MAX_DELIVERED = 20;
|
|
52752
52942
|
function createMissedApprovalsStore(stateDir, log = (l) => process.stderr.write(l)) {
|
|
52753
|
-
const filePath =
|
|
52943
|
+
const filePath = join14(stateDir, "missed-approvals.json");
|
|
52754
52944
|
const EMPTY = () => ({ pending: [], delivered: [] });
|
|
52755
52945
|
let unreadable = false;
|
|
52756
52946
|
function read() {
|
|
@@ -52830,7 +53020,7 @@ function createMissedApprovalsStore(stateDir, log = (l) => process.stderr.write(
|
|
|
52830
53020
|
},
|
|
52831
53021
|
clear() {
|
|
52832
53022
|
try {
|
|
52833
|
-
|
|
53023
|
+
unlinkSync6(filePath);
|
|
52834
53024
|
} catch {}
|
|
52835
53025
|
}
|
|
52836
53026
|
};
|
|
@@ -52838,8 +53028,8 @@ function createMissedApprovalsStore(stateDir, log = (l) => process.stderr.write(
|
|
|
52838
53028
|
|
|
52839
53029
|
// gateway/always-allow-persist-queue.ts
|
|
52840
53030
|
init_atomic();
|
|
52841
|
-
import { unlinkSync as
|
|
52842
|
-
import { join as
|
|
53031
|
+
import { unlinkSync as unlinkSync7 } from "node:fs";
|
|
53032
|
+
import { join as join15 } from "node:path";
|
|
52843
53033
|
var MAX_ATTEMPTS = 5;
|
|
52844
53034
|
var MAX_AGE_MS = 24 * 60 * 60 * 1000;
|
|
52845
53035
|
var MAX_QUEUE_SIZE = 50;
|
|
@@ -52863,7 +53053,7 @@ var atomicWriteSeam = (path, data, opts) => {
|
|
|
52863
53053
|
atomicWriteFileSync2(path, data, mode);
|
|
52864
53054
|
};
|
|
52865
53055
|
function createAlwaysAllowPersistQueue(stateDir, writeFileSyncFn = atomicWriteSeam, log = (l) => process.stderr.write(l)) {
|
|
52866
|
-
const filePath =
|
|
53056
|
+
const filePath = join15(stateDir, "always-allow-persist-queue.json");
|
|
52867
53057
|
let lock = Promise.resolve();
|
|
52868
53058
|
function withLock(fn) {
|
|
52869
53059
|
const result = lock.then(fn, fn);
|
|
@@ -52975,7 +53165,7 @@ function createAlwaysAllowPersistQueue(stateDir, writeFileSyncFn = atomicWriteSe
|
|
|
52975
53165
|
},
|
|
52976
53166
|
clear() {
|
|
52977
53167
|
try {
|
|
52978
|
-
|
|
53168
|
+
unlinkSync7(filePath);
|
|
52979
53169
|
} catch {}
|
|
52980
53170
|
}
|
|
52981
53171
|
};
|
|
@@ -53138,8 +53328,8 @@ function pickRecoveredPermissionOrigin(recentTurns, now, maxAgeMs) {
|
|
|
53138
53328
|
}
|
|
53139
53329
|
|
|
53140
53330
|
// gateway/approval-hold.ts
|
|
53141
|
-
import { mkdirSync as
|
|
53142
|
-
import { join as
|
|
53331
|
+
import { mkdirSync as mkdirSync13, writeFileSync as writeFileSync10, readFileSync as readFileSync12, unlinkSync as unlinkSync8, chmodSync as chmodSync3, lstatSync as lstatSync2 } from "node:fs";
|
|
53332
|
+
import { join as join16, dirname as dirname6 } from "node:path";
|
|
53143
53333
|
var BLOCKED_APPROVAL_FILE_MODE = 420;
|
|
53144
53334
|
var BLOCKED_APPROVAL_DIR_MODE = 1023;
|
|
53145
53335
|
var HELD_RETRY_BACKOFF_MS = 60000;
|
|
@@ -53168,24 +53358,24 @@ function safeActionForRecord(naturalAction2, toolName, inputPreview) {
|
|
|
53168
53358
|
return ACTION_SAFE_WITH_INPUT2.has(toolName) ? naturalAction2(toolName, inputPreview) : naturalAction2(toolName, undefined);
|
|
53169
53359
|
}
|
|
53170
53360
|
function createBlockedApprovalStore(dir, agent, fallbackDir) {
|
|
53171
|
-
const primary =
|
|
53172
|
-
const fallback = fallbackDir != null ?
|
|
53361
|
+
const primary = join16(dir, `${agent}.json`);
|
|
53362
|
+
const fallback = fallbackDir != null ? join16(fallbackDir, "blocked-approval.json") : null;
|
|
53173
53363
|
let active = primary;
|
|
53174
53364
|
function tryWrite(target, body, dirMode) {
|
|
53175
|
-
const parent =
|
|
53365
|
+
const parent = dirname6(target);
|
|
53176
53366
|
try {
|
|
53177
|
-
|
|
53367
|
+
mkdirSync13(parent, { recursive: true });
|
|
53178
53368
|
try {
|
|
53179
53369
|
if (lstatSync2(target).isSymbolicLink())
|
|
53180
53370
|
return false;
|
|
53181
53371
|
} catch {}
|
|
53182
53372
|
if (dirMode != null) {
|
|
53183
53373
|
try {
|
|
53184
|
-
|
|
53374
|
+
chmodSync3(parent, dirMode);
|
|
53185
53375
|
} catch {}
|
|
53186
53376
|
}
|
|
53187
|
-
|
|
53188
|
-
|
|
53377
|
+
writeFileSync10(target, body, { encoding: "utf-8", mode: BLOCKED_APPROVAL_FILE_MODE });
|
|
53378
|
+
chmodSync3(target, BLOCKED_APPROVAL_FILE_MODE);
|
|
53189
53379
|
return true;
|
|
53190
53380
|
} catch {
|
|
53191
53381
|
return false;
|
|
@@ -53202,7 +53392,7 @@ function createBlockedApprovalStore(dir, agent, fallbackDir) {
|
|
|
53202
53392
|
active = primary;
|
|
53203
53393
|
if (fallback != null) {
|
|
53204
53394
|
try {
|
|
53205
|
-
|
|
53395
|
+
unlinkSync8(fallback);
|
|
53206
53396
|
} catch {}
|
|
53207
53397
|
}
|
|
53208
53398
|
return;
|
|
@@ -53221,7 +53411,7 @@ function createBlockedApprovalStore(dir, agent, fallbackDir) {
|
|
|
53221
53411
|
if (p == null)
|
|
53222
53412
|
continue;
|
|
53223
53413
|
try {
|
|
53224
|
-
|
|
53414
|
+
unlinkSync8(p);
|
|
53225
53415
|
} catch {}
|
|
53226
53416
|
}
|
|
53227
53417
|
active = primary;
|
|
@@ -53231,7 +53421,7 @@ function createBlockedApprovalStore(dir, agent, fallbackDir) {
|
|
|
53231
53421
|
if (p == null)
|
|
53232
53422
|
continue;
|
|
53233
53423
|
try {
|
|
53234
|
-
const parsed = JSON.parse(
|
|
53424
|
+
const parsed = JSON.parse(readFileSync12(p, "utf-8"));
|
|
53235
53425
|
if (parsed != null && typeof parsed === "object")
|
|
53236
53426
|
return parsed;
|
|
53237
53427
|
} catch {}
|
|
@@ -53310,6 +53500,7 @@ function isTelegramSurfaceTool2(toolName) {
|
|
|
53310
53500
|
}
|
|
53311
53501
|
|
|
53312
53502
|
// tool-activity-summary.ts
|
|
53503
|
+
init_status_no_truncate();
|
|
53313
53504
|
init_card_format();
|
|
53314
53505
|
init_redact();
|
|
53315
53506
|
function clipNarrative2(s) {
|
|
@@ -53528,6 +53719,7 @@ function createTurnTypingLoop(deps) {
|
|
|
53528
53719
|
|
|
53529
53720
|
// gateway/handback-preturn-signal.ts
|
|
53530
53721
|
var PRETURN_TURNKEY_PREFIX = "preturn:";
|
|
53722
|
+
var HANDBACK_REINJECT_COUNT_META_KEY = "handbackReinjectCount";
|
|
53531
53723
|
function isHandbackInbound(msg) {
|
|
53532
53724
|
return msg.type === "inbound" && msg.meta?.source === "subagent_handback";
|
|
53533
53725
|
}
|
|
@@ -53535,6 +53727,8 @@ function createHandbackPreturnSignal(deps) {
|
|
|
53535
53727
|
const now = deps.now ?? (() => Date.now());
|
|
53536
53728
|
const debounceMs = deps.debounceMs ?? 700;
|
|
53537
53729
|
const adoptTimeoutMs = deps.adoptTimeoutMs ?? 30000;
|
|
53730
|
+
const deliveredTtlMs = deps.deliveredTtlMs ?? 300000;
|
|
53731
|
+
const maxReinjects = deps.maxReinjects ?? 2;
|
|
53538
53732
|
const setTimer = deps.setTimer ?? ((fn, ms) => {
|
|
53539
53733
|
const t = setTimeout(fn, ms);
|
|
53540
53734
|
t.unref?.();
|
|
@@ -53574,7 +53768,7 @@ function createHandbackPreturnSignal(deps) {
|
|
|
53574
53768
|
if (messageId == null)
|
|
53575
53769
|
return;
|
|
53576
53770
|
if (entry.consumed) {
|
|
53577
|
-
deps.
|
|
53771
|
+
deps.deleteCard({
|
|
53578
53772
|
turnKey: entry.syntheticTurnKey,
|
|
53579
53773
|
chatId: entry.chatId,
|
|
53580
53774
|
threadId: entry.threadId,
|
|
@@ -53607,12 +53801,15 @@ function createHandbackPreturnSignal(deps) {
|
|
|
53607
53801
|
}
|
|
53608
53802
|
deps.stopTypingLoop(entry.chatId, entry.threadId);
|
|
53609
53803
|
}
|
|
53804
|
+
function deliveredAwaitingTurnStart(entry) {
|
|
53805
|
+
return now() - entry.deliveredAt < deliveredTtlMs;
|
|
53806
|
+
}
|
|
53610
53807
|
function reap(entry) {
|
|
53611
53808
|
entry.reapTimer = null;
|
|
53612
53809
|
if (entry.consumed)
|
|
53613
53810
|
return;
|
|
53614
|
-
if (deps.isClaudeBusy?.() === true) {
|
|
53615
|
-
log(`handback-preturn-signal: reap deferred key=${entry.statusKey} ` + `(claude busy
|
|
53811
|
+
if (deps.isClaudeBusy?.() === true || deliveredAwaitingTurnStart(entry)) {
|
|
53812
|
+
log(`handback-preturn-signal: reap deferred key=${entry.statusKey} ` + `(${deps.isClaudeBusy?.() === true ? "claude busy" : "delivered, turn not yet streaming"}` + ` \u2014 not orphaned)
|
|
53616
53813
|
`);
|
|
53617
53814
|
entry.reapTimer = setTimer(() => reap(entry), adoptTimeoutMs);
|
|
53618
53815
|
return;
|
|
@@ -53629,12 +53826,40 @@ function createHandbackPreturnSignal(deps) {
|
|
|
53629
53826
|
pinned: entry.pinned
|
|
53630
53827
|
};
|
|
53631
53828
|
deps.clearCardRecord(entry.syntheticTurnKey, entry.activityMessageId);
|
|
53632
|
-
Promise.resolve(deps.
|
|
53633
|
-
log(`handback-preturn-signal: orphan
|
|
53829
|
+
Promise.resolve(deps.deleteCard(record)).catch((err) => {
|
|
53830
|
+
log(`handback-preturn-signal: orphan card delete failed key=${entry.statusKey}: ` + `${err instanceof Error ? err.message : String(err)}
|
|
53634
53831
|
`);
|
|
53635
53832
|
});
|
|
53636
53833
|
}
|
|
53637
53834
|
dropEntry(entry);
|
|
53835
|
+
if (entry.reinjectCount < maxReinjects) {
|
|
53836
|
+
const nextCount = entry.reinjectCount + 1;
|
|
53837
|
+
entry.inbound.meta[HANDBACK_REINJECT_COUNT_META_KEY] = String(nextCount);
|
|
53838
|
+
log(`handback-preturn-signal: orphan reap key=${entry.statusKey} ` + `turnId=${entry.adoptTurnId} \u2014 re-injecting handback (attempt ${nextCount}/${maxReinjects})
|
|
53839
|
+
`);
|
|
53840
|
+
try {
|
|
53841
|
+
deps.reinjectHandback(entry.inbound);
|
|
53842
|
+
} catch (err) {
|
|
53843
|
+
log(`handback-preturn-signal: orphan re-inject failed key=${entry.statusKey}: ` + `${err instanceof Error ? err.message : String(err)}
|
|
53844
|
+
`);
|
|
53845
|
+
}
|
|
53846
|
+
} else {
|
|
53847
|
+
log(`handback-preturn-signal: orphan reap key=${entry.statusKey} ` + `turnId=${entry.adoptTurnId} \u2014 re-injection cap (${maxReinjects}) exhausted, ` + `escalating to fleet-health telemetry
|
|
53848
|
+
`);
|
|
53849
|
+
try {
|
|
53850
|
+
deps.escalateOrphan({
|
|
53851
|
+
statusKey: entry.statusKey,
|
|
53852
|
+
chatId: entry.chatId,
|
|
53853
|
+
threadId: entry.threadId,
|
|
53854
|
+
adoptTurnId: entry.adoptTurnId,
|
|
53855
|
+
reinjectCount: entry.reinjectCount,
|
|
53856
|
+
ageMs: now() - entry.deliveredAt
|
|
53857
|
+
});
|
|
53858
|
+
} catch (err) {
|
|
53859
|
+
log(`handback-preturn-signal: orphan escalation failed key=${entry.statusKey}: ` + `${err instanceof Error ? err.message : String(err)}
|
|
53860
|
+
`);
|
|
53861
|
+
}
|
|
53862
|
+
}
|
|
53638
53863
|
}
|
|
53639
53864
|
return {
|
|
53640
53865
|
noteHandbackRelease(inbound) {
|
|
@@ -53661,6 +53886,9 @@ function createHandbackPreturnSignal(deps) {
|
|
|
53661
53886
|
`);
|
|
53662
53887
|
const startedAt = now();
|
|
53663
53888
|
const syntheticTurnKey = `${PRETURN_TURNKEY_PREFIX}${statusKey}:${startedAt}`;
|
|
53889
|
+
const rawReinject = inbound.meta?.[HANDBACK_REINJECT_COUNT_META_KEY];
|
|
53890
|
+
const parsedReinject = rawReinject != null ? Number.parseInt(rawReinject, 10) : 0;
|
|
53891
|
+
const reinjectCount = Number.isFinite(parsedReinject) && parsedReinject > 0 ? parsedReinject : 0;
|
|
53664
53892
|
const entry = {
|
|
53665
53893
|
statusKey,
|
|
53666
53894
|
chatId,
|
|
@@ -53668,6 +53896,9 @@ function createHandbackPreturnSignal(deps) {
|
|
|
53668
53896
|
adoptTurnId,
|
|
53669
53897
|
syntheticTurnKey,
|
|
53670
53898
|
startedAt,
|
|
53899
|
+
deliveredAt: startedAt,
|
|
53900
|
+
reinjectCount,
|
|
53901
|
+
inbound,
|
|
53671
53902
|
pinned: false,
|
|
53672
53903
|
debounceTimer: null,
|
|
53673
53904
|
reapTimer: null,
|
|
@@ -53750,6 +53981,24 @@ function createHandbackPreturnSignal(deps) {
|
|
|
53750
53981
|
};
|
|
53751
53982
|
}
|
|
53752
53983
|
|
|
53984
|
+
// gateway/handback-orphan-recovery.ts
|
|
53985
|
+
function formatOrphanEscalation(esc) {
|
|
53986
|
+
return `telegram gateway: handback orphan escalation key=${esc.statusKey} ` + `turnId=${esc.adoptTurnId} reinjects=${esc.reinjectCount} ageMs=${esc.ageMs} ` + `\u2014 deterministic recovery exhausted, no operator nudge issued
|
|
53987
|
+
`;
|
|
53988
|
+
}
|
|
53989
|
+
function createHandbackOrphanRecovery(deps) {
|
|
53990
|
+
const writeLog = deps.writeLog ?? ((line) => void process.stderr.write(line));
|
|
53991
|
+
return {
|
|
53992
|
+
deleteCard: (record) => deps.deleteMessage(record.chatId, record.activityMessageId, record.threadId).then(() => {
|
|
53993
|
+
return;
|
|
53994
|
+
}).catch(() => {
|
|
53995
|
+
return;
|
|
53996
|
+
}),
|
|
53997
|
+
reinjectHandback: (inbound) => deps.pushInbound(inbound),
|
|
53998
|
+
escalateOrphan: (esc) => writeLog(formatOrphanEscalation(esc))
|
|
53999
|
+
};
|
|
54000
|
+
}
|
|
54001
|
+
|
|
53753
54002
|
// gateway/derive-turn-id.ts
|
|
53754
54003
|
function deriveTurnId2(chatId, threadId, messageId) {
|
|
53755
54004
|
if (messageId == null || messageId === "" || String(messageId) === "0")
|
|
@@ -63373,7 +63622,6 @@ function renderSafe(doc, source, maxLen = RICH_MESSAGE_MAX_CHARS) {
|
|
|
63373
63622
|
|
|
63374
63623
|
// render/rich-render.ts
|
|
63375
63624
|
init_format();
|
|
63376
|
-
var PLAIN_TEXT_MAX_CHARS = 4096;
|
|
63377
63625
|
function parseRichRenderEnabled(raw) {
|
|
63378
63626
|
if (raw == null)
|
|
63379
63627
|
return true;
|
|
@@ -63409,6 +63657,48 @@ function renderOutboundChunks(text4, env = process.env, maxLen = RICH_MESSAGE_MA
|
|
|
63409
63657
|
return out.length > 0 ? out : [whole];
|
|
63410
63658
|
}
|
|
63411
63659
|
|
|
63660
|
+
// reply-quote.ts
|
|
63661
|
+
var import_grammy6 = __toESM(require_mod2(), 1);
|
|
63662
|
+
var QUOTE_MAX_CHARS = 1024;
|
|
63663
|
+
function normalizeQuoteText(quoteText) {
|
|
63664
|
+
if (typeof quoteText !== "string")
|
|
63665
|
+
return null;
|
|
63666
|
+
if (quoteText.trim().length === 0)
|
|
63667
|
+
return null;
|
|
63668
|
+
if (quoteText.length <= QUOTE_MAX_CHARS)
|
|
63669
|
+
return quoteText;
|
|
63670
|
+
let end = QUOTE_MAX_CHARS;
|
|
63671
|
+
const code2 = quoteText.charCodeAt(end - 1);
|
|
63672
|
+
if (code2 >= 55296 && code2 <= 56319)
|
|
63673
|
+
end -= 1;
|
|
63674
|
+
return quoteText.slice(0, end);
|
|
63675
|
+
}
|
|
63676
|
+
function buildReplyParameters(messageId, quoteText) {
|
|
63677
|
+
const quote = normalizeQuoteText(quoteText);
|
|
63678
|
+
return { message_id: messageId, ...quote != null ? { quote } : {} };
|
|
63679
|
+
}
|
|
63680
|
+
function isQuoteRejectionError(err) {
|
|
63681
|
+
if (!(err instanceof import_grammy6.GrammyError) || err.error_code !== 400)
|
|
63682
|
+
return false;
|
|
63683
|
+
if (isHtmlParseRejectError(err) || isMessageTooLongError(err))
|
|
63684
|
+
return false;
|
|
63685
|
+
return (err.description || "").toLowerCase().includes("quote");
|
|
63686
|
+
}
|
|
63687
|
+
function sendOptsHaveQuote(opts) {
|
|
63688
|
+
const rp = opts.reply_parameters;
|
|
63689
|
+
return rp != null && typeof rp === "object" && rp.quote != null;
|
|
63690
|
+
}
|
|
63691
|
+
function dropQuoteFromSendOpts(opts) {
|
|
63692
|
+
if (!sendOptsHaveQuote(opts))
|
|
63693
|
+
return { ...opts };
|
|
63694
|
+
const rp = { ...opts.reply_parameters };
|
|
63695
|
+
delete rp.quote;
|
|
63696
|
+
delete rp.quote_parse_mode;
|
|
63697
|
+
delete rp.quote_entities;
|
|
63698
|
+
delete rp.quote_position;
|
|
63699
|
+
return { ...opts, reply_parameters: rp };
|
|
63700
|
+
}
|
|
63701
|
+
|
|
63412
63702
|
// stream-controller.ts
|
|
63413
63703
|
function createStreamController(cfg) {
|
|
63414
63704
|
const {
|
|
@@ -63436,13 +63726,10 @@ function createStreamController(cfg) {
|
|
|
63436
63726
|
...disableLinkPreview ? { link_preview_options: { is_disabled: true } } : {},
|
|
63437
63727
|
...replyMarkup != null ? { reply_markup: replyMarkup } : {}
|
|
63438
63728
|
};
|
|
63439
|
-
|
|
63729
|
+
let sendOpts = {
|
|
63440
63730
|
...baseOpts,
|
|
63441
63731
|
...replyToMessageId != null ? {
|
|
63442
|
-
reply_parameters:
|
|
63443
|
-
message_id: replyToMessageId,
|
|
63444
|
-
...quoteText != null ? { quote: { text: quoteText, position: 0 } } : {}
|
|
63445
|
-
}
|
|
63732
|
+
reply_parameters: buildReplyParameters(replyToMessageId, quoteText)
|
|
63446
63733
|
} : {},
|
|
63447
63734
|
...protectContent === true ? { protect_content: true } : {},
|
|
63448
63735
|
...cfg.disableNotification === true ? { disable_notification: true } : {}
|
|
@@ -63459,6 +63746,17 @@ function createStreamController(cfg) {
|
|
|
63459
63746
|
delete richOpts.link_preview_options;
|
|
63460
63747
|
return bot.api.sendRichMessage(chatId, richMessage(piece.text), richOpts);
|
|
63461
63748
|
};
|
|
63749
|
+
const sendPieceWithQuoteFallback = async (piece) => {
|
|
63750
|
+
try {
|
|
63751
|
+
return await sendPiece(piece, sendOpts);
|
|
63752
|
+
} catch (err) {
|
|
63753
|
+
if (!isQuoteRejectionError(err) || !sendOptsHaveQuote(sendOpts))
|
|
63754
|
+
throw err;
|
|
63755
|
+
warn?.(`stream-controller: quote not found in the replied-to message \u2014 re-sending without the quote (${err instanceof Error ? err.message : String(err)})`);
|
|
63756
|
+
sendOpts = dropQuoteFromSendOpts(sendOpts);
|
|
63757
|
+
return await sendPiece(piece, sendOpts);
|
|
63758
|
+
}
|
|
63759
|
+
};
|
|
63462
63760
|
const editPiece = (id, piece, opts) => {
|
|
63463
63761
|
if (!piece.rich)
|
|
63464
63762
|
return bot.api.editMessageText(chatId, id, piece.text, opts);
|
|
@@ -63501,7 +63799,7 @@ function createStreamController(cfg) {
|
|
|
63501
63799
|
return false;
|
|
63502
63800
|
}
|
|
63503
63801
|
try {
|
|
63504
|
-
const sent = await retry(() =>
|
|
63802
|
+
const sent = await retry(() => sendPieceWithQuoteFallback(piece), { threadId, chat_id: chatId });
|
|
63505
63803
|
tailIds[ti] = sent.message_id;
|
|
63506
63804
|
tailLastText[ti] = piece.text;
|
|
63507
63805
|
onSend?.(sent.message_id, piece.text.length);
|
|
@@ -63523,7 +63821,7 @@ function createStreamController(cfg) {
|
|
|
63523
63821
|
const head = pieces[0];
|
|
63524
63822
|
let anchorId;
|
|
63525
63823
|
try {
|
|
63526
|
-
const sent = await retry(() =>
|
|
63824
|
+
const sent = await retry(() => sendPieceWithQuoteFallback(head), { threadId, chat_id: chatId });
|
|
63527
63825
|
anchorId = sent.message_id;
|
|
63528
63826
|
} catch (err) {
|
|
63529
63827
|
if (!literalText && head.rich && isParseEntitiesError(err)) {
|
|
@@ -63708,7 +64006,42 @@ function createChatLock() {
|
|
|
63708
64006
|
}
|
|
63709
64007
|
|
|
63710
64008
|
// retry-api-call.ts
|
|
63711
|
-
var
|
|
64009
|
+
var import_grammy7 = __toESM(require_mod2(), 1);
|
|
64010
|
+
import { AsyncLocalStorage as AsyncLocalStorage4 } from "async_hooks";
|
|
64011
|
+
var attemptStore2 = new AsyncLocalStorage4;
|
|
64012
|
+
function withAttemptContext2(ctx, fn) {
|
|
64013
|
+
return attemptStore2.run(ctx, fn);
|
|
64014
|
+
}
|
|
64015
|
+
function isTransientNetworkMessage2(msg) {
|
|
64016
|
+
return msg.includes("ECONNRESET") || msg.includes("ETIMEDOUT") || msg.includes("fetch failed") || msg.includes("ENOTFOUND");
|
|
64017
|
+
}
|
|
64018
|
+
function isAbortError2(err) {
|
|
64019
|
+
const e = err;
|
|
64020
|
+
return e?.name === "AbortError" || e?.code === "ABORT_ERR";
|
|
64021
|
+
}
|
|
64022
|
+
function isTransientTransportError2(err) {
|
|
64023
|
+
if (err instanceof import_grammy7.GrammyError)
|
|
64024
|
+
return false;
|
|
64025
|
+
if (isLocalResourceError2(err))
|
|
64026
|
+
return false;
|
|
64027
|
+
if (err instanceof import_grammy7.HttpError) {
|
|
64028
|
+
const inner = err.error;
|
|
64029
|
+
if (isLocalResourceError2(inner))
|
|
64030
|
+
return false;
|
|
64031
|
+
if (isAbortError2(inner))
|
|
64032
|
+
return false;
|
|
64033
|
+
return true;
|
|
64034
|
+
}
|
|
64035
|
+
const msg = err instanceof Error ? err.message : String(err ?? "");
|
|
64036
|
+
if (isTransientNetworkMessage2(msg))
|
|
64037
|
+
return true;
|
|
64038
|
+
const cause = err?.cause;
|
|
64039
|
+
if (cause == null || cause === err)
|
|
64040
|
+
return false;
|
|
64041
|
+
if (isLocalResourceError2(cause))
|
|
64042
|
+
return false;
|
|
64043
|
+
return isTransientNetworkMessage2(cause instanceof Error ? cause.message : String(cause));
|
|
64044
|
+
}
|
|
63712
64045
|
function classifyBenignTelegram4002(errorCode, description) {
|
|
63713
64046
|
if (errorCode !== 400)
|
|
63714
64047
|
return null;
|
|
@@ -63753,12 +64086,12 @@ function createRetryApiCall2(config = {}) {
|
|
|
63753
64086
|
const observer = config.observer;
|
|
63754
64087
|
const log = config.log;
|
|
63755
64088
|
const onFloodWait = config.onFloodWait;
|
|
63756
|
-
const
|
|
64089
|
+
const floodWaitRemainingMs = config.floodWaitRemainingMs;
|
|
63757
64090
|
function openWindowMs() {
|
|
63758
|
-
if (!
|
|
64091
|
+
if (!floodWaitRemainingMs)
|
|
63759
64092
|
return 0;
|
|
63760
64093
|
try {
|
|
63761
|
-
const ms =
|
|
64094
|
+
const ms = floodWaitRemainingMs();
|
|
63762
64095
|
return Number.isFinite(ms) && ms > 0 ? ms : 0;
|
|
63763
64096
|
} catch {
|
|
63764
64097
|
return 0;
|
|
@@ -63776,9 +64109,9 @@ function createRetryApiCall2(config = {}) {
|
|
|
63776
64109
|
throw gated;
|
|
63777
64110
|
}
|
|
63778
64111
|
try {
|
|
63779
|
-
return await fn
|
|
64112
|
+
return await withAttemptContext2({ attempt, maxRetries, maxFloodSleepMs }, fn);
|
|
63780
64113
|
} catch (err) {
|
|
63781
|
-
const isGrammyErr = err instanceof
|
|
64114
|
+
const isGrammyErr = err instanceof import_grammy7.GrammyError;
|
|
63782
64115
|
const msg = err instanceof Error ? err.message : String(err);
|
|
63783
64116
|
const desc = isGrammyErr ? err.description : msg;
|
|
63784
64117
|
if (isLocalResourceError2(err)) {
|
|
@@ -63808,12 +64141,14 @@ function createRetryApiCall2(config = {}) {
|
|
|
63808
64141
|
const benignKind = isGrammyErr ? classifyBenignTelegram4002(err.error_code, desc) : null;
|
|
63809
64142
|
if (benignKind !== null) {
|
|
63810
64143
|
observer?.onBenign?.({ kind: benignKind });
|
|
64144
|
+
if (opts?.rethrowBenign400)
|
|
64145
|
+
throw err;
|
|
63811
64146
|
return;
|
|
63812
64147
|
}
|
|
63813
64148
|
if (isGrammyErr && err.error_code === 400 && desc.includes("thread not found") && opts?.threadId && opts?.chat_id) {
|
|
63814
64149
|
throw Object.assign(new Error("THREAD_NOT_FOUND"), { original: err });
|
|
63815
64150
|
}
|
|
63816
|
-
if (
|
|
64151
|
+
if (isTransientTransportError2(err)) {
|
|
63817
64152
|
if (attempt < maxRetries - 1) {
|
|
63818
64153
|
const delayMs = Math.pow(2, attempt) * 1000;
|
|
63819
64154
|
log?.(`telegram gateway: network error, retrying in ${delayMs / 1000}s: ${msg}
|
|
@@ -65114,14 +65449,13 @@ function createFloodWindowObserver(config) {
|
|
|
65114
65449
|
}
|
|
65115
65450
|
|
|
65116
65451
|
// shared/bot-runtime.ts
|
|
65117
|
-
var
|
|
65452
|
+
var import_grammy8 = __toESM(require_mod2(), 1);
|
|
65118
65453
|
var import_runner2 = __toESM(require_mod4(), 1);
|
|
65119
65454
|
import { createHash as createHash3 } from "crypto";
|
|
65120
|
-
import { AsyncLocalStorage as
|
|
65121
|
-
init_flood_circuit_breaker();
|
|
65455
|
+
import { AsyncLocalStorage as AsyncLocalStorage5 } from "async_hooks";
|
|
65122
65456
|
init_format();
|
|
65123
65457
|
init_rich_send();
|
|
65124
|
-
var tgPostTagStore2 = new
|
|
65458
|
+
var tgPostTagStore2 = new AsyncLocalStorage5;
|
|
65125
65459
|
function _getTgPostTags() {
|
|
65126
65460
|
return tgPostTagStore2.getStore();
|
|
65127
65461
|
}
|
|
@@ -65164,16 +65498,27 @@ function installTgPostLogger(bot) {
|
|
|
65164
65498
|
if (r != null && typeof r === "object" && r.ok === false) {
|
|
65165
65499
|
const code2 = r.error_code;
|
|
65166
65500
|
const benign = classifyBenignTelegram400(code2, r.description);
|
|
65167
|
-
|
|
65501
|
+
const retrying = benign === null && willRetryTelegramFailure({
|
|
65502
|
+
errorCode: code2 ?? null,
|
|
65503
|
+
retryAfterSec: r.parameters?.retry_after ?? null,
|
|
65504
|
+
message: r.description ?? null
|
|
65505
|
+
});
|
|
65506
|
+
emit(benign !== null ? "benign" : retrying ? "retry" : "err", `telegram_${code2 ?? "unknown"}`, code2 != null ? String(code2) : "-", shortDesc(r.description));
|
|
65168
65507
|
return res;
|
|
65169
65508
|
}
|
|
65170
65509
|
emit("ok", "-", "-", "-");
|
|
65171
65510
|
return res;
|
|
65172
65511
|
} catch (err) {
|
|
65173
|
-
const errClass = err instanceof
|
|
65174
|
-
const code2 = err instanceof
|
|
65175
|
-
const rawDesc = err instanceof
|
|
65176
|
-
|
|
65512
|
+
const errClass = err instanceof import_grammy8.GrammyError ? `grammy_${err.error_code}` : err?.constructor?.name ?? "Error";
|
|
65513
|
+
const code2 = err instanceof import_grammy8.GrammyError ? String(err.error_code) : "-";
|
|
65514
|
+
const rawDesc = err instanceof import_grammy8.GrammyError ? err.description : err instanceof Error ? err.message : "";
|
|
65515
|
+
const retrying = willRetryTelegramFailure({
|
|
65516
|
+
errorCode: err instanceof import_grammy8.GrammyError ? err.error_code : null,
|
|
65517
|
+
retryAfterSec: err instanceof import_grammy8.GrammyError ? err.parameters?.retry_after ?? null : null,
|
|
65518
|
+
message: err instanceof Error ? err.message : String(err ?? ""),
|
|
65519
|
+
cause: err
|
|
65520
|
+
});
|
|
65521
|
+
emit(retrying ? "retry" : "err", errClass, code2, shortDesc(rawDesc));
|
|
65177
65522
|
throw err;
|
|
65178
65523
|
}
|
|
65179
65524
|
});
|
|
@@ -69887,11 +70232,11 @@ class PostHogBackendClient extends PostHogCoreStateless {
|
|
|
69887
70232
|
}
|
|
69888
70233
|
|
|
69889
70234
|
// ../node_modules/.bun/posthog-node@5.29.2/node_modules/posthog-node/dist/extensions/context/context.mjs
|
|
69890
|
-
import { AsyncLocalStorage as
|
|
70235
|
+
import { AsyncLocalStorage as AsyncLocalStorage6 } from "node:async_hooks";
|
|
69891
70236
|
|
|
69892
70237
|
class PostHogContext {
|
|
69893
70238
|
constructor() {
|
|
69894
|
-
this.storage = new
|
|
70239
|
+
this.storage = new AsyncLocalStorage6;
|
|
69895
70240
|
}
|
|
69896
70241
|
get() {
|
|
69897
70242
|
return this.storage.getStore();
|
|
@@ -76344,9 +76689,7 @@ function splitMarkdownChunks2(text4, maxLen = RICH_MESSAGE_MAX_CHARS2) {
|
|
|
76344
76689
|
} else if (spaceIdx > 0) {
|
|
76345
76690
|
cut = spaceIdx;
|
|
76346
76691
|
}
|
|
76347
|
-
cut =
|
|
76348
|
-
cut = backOffTableRow2(rest, cut);
|
|
76349
|
-
cut = backOffOpenInline2(rest, cut);
|
|
76692
|
+
cut = safeMarkdownCut2(rest, cut);
|
|
76350
76693
|
if (cut <= 0) {
|
|
76351
76694
|
const sliced = hardSliceToCap2(rest, maxLen);
|
|
76352
76695
|
chunks.push(stripBoundarySpacers2(sliced[0], "trailing"));
|
|
@@ -76399,8 +76742,29 @@ var INLINE_SPAN_PATTERNS2 = [
|
|
|
76399
76742
|
/\*\*[^*\n]+\*\*/g,
|
|
76400
76743
|
/__[^_\n]+__/g,
|
|
76401
76744
|
/(?<![\w*])_[^_\n]+_(?![\w*])/g,
|
|
76402
|
-
/\[[^\]\n]*\]\([^)\n]*\)/g
|
|
76745
|
+
/\[[^\]\n]*\]\([^)\n]*\)/g,
|
|
76746
|
+
/~~[^~\n]+~~/g,
|
|
76747
|
+
/\|\|[^|\n]+\|\|/g
|
|
76403
76748
|
];
|
|
76749
|
+
var DELIMITER_RUN_CHARS2 = new Set(["*", "_", "`", "~", "|"]);
|
|
76750
|
+
function safeMarkdownCut2(text4, cut) {
|
|
76751
|
+
if (cut <= 0)
|
|
76752
|
+
return 0;
|
|
76753
|
+
if (cut >= text4.length)
|
|
76754
|
+
return text4.length;
|
|
76755
|
+
let safe = backOffOpenFence2(text4, cut);
|
|
76756
|
+
safe = backOffTableRow2(text4, safe);
|
|
76757
|
+
safe = backOffOpenInline2(text4, safe);
|
|
76758
|
+
while (safe > 0 && DELIMITER_RUN_CHARS2.has(text4[safe]) && text4[safe - 1] === text4[safe])
|
|
76759
|
+
safe--;
|
|
76760
|
+
if (safe <= 0)
|
|
76761
|
+
return 0;
|
|
76762
|
+
const prev = text4.charCodeAt(safe - 1);
|
|
76763
|
+
const here = text4.charCodeAt(safe);
|
|
76764
|
+
if (prev >= 55296 && prev <= 56319 && here >= 56320 && here <= 57343)
|
|
76765
|
+
safe -= 1;
|
|
76766
|
+
return safe;
|
|
76767
|
+
}
|
|
76404
76768
|
function backOffOpenInline2(text4, cut) {
|
|
76405
76769
|
if (cut <= 0 || cut >= text4.length)
|
|
76406
76770
|
return cut;
|
|
@@ -76424,7 +76788,7 @@ function backOffOpenInline2(text4, cut) {
|
|
|
76424
76788
|
|
|
76425
76789
|
// gateway/command-format.ts
|
|
76426
76790
|
init_format();
|
|
76427
|
-
var
|
|
76791
|
+
var import_grammy9 = __toESM(require_mod2(), 1);
|
|
76428
76792
|
function formatSwitchroomOutput(output, maxLen = RICH_MESSAGE_MAX_CHARS) {
|
|
76429
76793
|
const trimmed = output.trim();
|
|
76430
76794
|
if (trimmed.length <= maxLen)
|
|
@@ -76492,7 +76856,7 @@ function formatAuthOutputForTelegram(output) {
|
|
|
76492
76856
|
`), url };
|
|
76493
76857
|
}
|
|
76494
76858
|
function buildAuthUrlKeyboard(authorizeUrl) {
|
|
76495
|
-
return new
|
|
76859
|
+
return new import_grammy9.InlineKeyboard().url("\uD83D\uDD10 Open Claude auth", authorizeUrl);
|
|
76496
76860
|
}
|
|
76497
76861
|
function buildDeferredSecretKeyboard(deferKey) {
|
|
76498
76862
|
const unlockData = `vd:unlock:${deferKey}`;
|
|
@@ -76502,7 +76866,7 @@ function buildDeferredSecretKeyboard(deferKey) {
|
|
|
76502
76866
|
`);
|
|
76503
76867
|
throw new Error(`callback_data overflow: deferKey too long (${deferKey.length} chars)`);
|
|
76504
76868
|
}
|
|
76505
|
-
return new
|
|
76869
|
+
return new import_grammy9.InlineKeyboard().text("\uD83D\uDD13 Unlock vault & save", unlockData).text("\uD83D\uDDD1 Discard", cancelData);
|
|
76506
76870
|
}
|
|
76507
76871
|
function renderVaultOpFailure(verbLabel, cliOutput, key) {
|
|
76508
76872
|
const parsed = parseVaultCliError(cliOutput);
|
|
@@ -76538,7 +76902,7 @@ _${escapeHtmlForTg2(outcome.paneTailText)}_` : "";
|
|
|
76538
76902
|
}
|
|
76539
76903
|
}
|
|
76540
76904
|
function buildDoctorScopeKeyboard() {
|
|
76541
|
-
return new
|
|
76905
|
+
return new import_grammy9.InlineKeyboard().text("\uD83E\uDE7A Whole fleet", "dr:fleet").text("\uD83E\uDE7A This agent", "dr:self");
|
|
76542
76906
|
}
|
|
76543
76907
|
function formatDoctorReport(raw) {
|
|
76544
76908
|
const trimmed = stripAnsi4(raw).trim();
|
|
@@ -76554,7 +76918,7 @@ init_emphasis_guard();
|
|
|
76554
76918
|
init_line_start_guard();
|
|
76555
76919
|
init_inline_pairs_guard();
|
|
76556
76920
|
init_unsupported_token_guard();
|
|
76557
|
-
var
|
|
76921
|
+
var import_grammy10 = __toESM(require_mod2(), 1);
|
|
76558
76922
|
function guardAccidentalFormatting2(markdown) {
|
|
76559
76923
|
let out = markdown;
|
|
76560
76924
|
out = guardUnsupportedTokens(out);
|
|
@@ -76811,7 +77175,7 @@ function normalizeTemporal(text4, tz, nowMs2) {
|
|
|
76811
77175
|
}
|
|
76812
77176
|
|
|
76813
77177
|
// gateway/outbound-send-path.ts
|
|
76814
|
-
var
|
|
77178
|
+
var import_grammy11 = __toESM(require_mod2(), 1);
|
|
76815
77179
|
import { statSync as statSync11 } from "fs";
|
|
76816
77180
|
import { extname } from "path";
|
|
76817
77181
|
|
|
@@ -76958,6 +77322,42 @@ import {
|
|
|
76958
77322
|
} from "node:fs";
|
|
76959
77323
|
import { join as join31 } from "node:path";
|
|
76960
77324
|
import { homedir as homedir9 } from "node:os";
|
|
77325
|
+
|
|
77326
|
+
// hooks/audience-classify.mjs
|
|
77327
|
+
var AUDIENCE_USER = "user";
|
|
77328
|
+
var AUDIENCE_INTERNAL = "internal";
|
|
77329
|
+
function resolveRecordAudience(value) {
|
|
77330
|
+
return value === AUDIENCE_INTERNAL ? AUDIENCE_INTERNAL : AUDIENCE_USER;
|
|
77331
|
+
}
|
|
77332
|
+
function shouldSuppressForAudience(record, opts) {
|
|
77333
|
+
if (opts?.gateEnabled === false)
|
|
77334
|
+
return false;
|
|
77335
|
+
return resolveRecordAudience(record?.audience) === AUDIENCE_INTERNAL;
|
|
77336
|
+
}
|
|
77337
|
+
var REPLY_THROW_PROVENANCE_NOTICE = "(my reply tool errored this turn, so this was never sent as an answer \u2014 " + "the safety net captured the last thing I wrote, which may be working notes)";
|
|
77338
|
+
function shouldFrameReplyThrow(record, opts) {
|
|
77339
|
+
if (opts?.frameEnabled === false)
|
|
77340
|
+
return false;
|
|
77341
|
+
return record?.replyToolThrewThisTurn === true;
|
|
77342
|
+
}
|
|
77343
|
+
function applyReplyThrowFraming(text4) {
|
|
77344
|
+
const body = typeof text4 === "string" ? text4 : "";
|
|
77345
|
+
if (body.trim().length === 0)
|
|
77346
|
+
return body;
|
|
77347
|
+
return `${REPLY_THROW_PROVENANCE_NOTICE}
|
|
77348
|
+
|
|
77349
|
+
${body}`;
|
|
77350
|
+
}
|
|
77351
|
+
function formatReplyThrowFraming(s) {
|
|
77352
|
+
return `telegram gateway: outbox provenance framing nonce=${s.turnNonce} ` + `turnId=${s.turnId ?? "unknown"} chatId=${s.chatId ?? "unresolved"} ` + `sha=${(s.textSha256 ?? "").slice(0, 12)} source=${s.source ?? "unknown"} ` + `audience=user replyToolThrew=true \u2014 captured prose delivered with its ` + `provenance stated, not suppressed
|
|
77353
|
+
`;
|
|
77354
|
+
}
|
|
77355
|
+
function formatInternalSuppression(s) {
|
|
77356
|
+
return `telegram gateway: outbox audience suppression nonce=${s.turnNonce} ` + `turnId=${s.turnId ?? "unknown"} chatId=${s.chatId ?? "unresolved"} ` + `sha=${(s.textSha256 ?? "").slice(0, 12)} ageMs=${s.ageMs ?? 0} ` + `source=${s.source ?? "unknown"} pastWindow=${s.pastWindow === true} ` + `audience=internal \u2014 internal prose withheld from chat, journaled terminally
|
|
77357
|
+
`;
|
|
77358
|
+
}
|
|
77359
|
+
|
|
77360
|
+
// outbox.ts
|
|
76961
77361
|
function sha256Hex(s) {
|
|
76962
77362
|
return createHash4("sha256").update(s, "utf8").digest("hex");
|
|
76963
77363
|
}
|
|
@@ -77137,7 +77537,8 @@ function decideOutboxSweep(input) {
|
|
|
77137
77537
|
routePrefix = "",
|
|
77138
77538
|
quietMs = OUTBOX_QUIET_MS,
|
|
77139
77539
|
maxAgeMs = OUTBOX_MAX_AGE_MS,
|
|
77140
|
-
shownLedgerHit = false
|
|
77540
|
+
shownLedgerHit = false,
|
|
77541
|
+
provenanceFraming = true
|
|
77141
77542
|
} = input;
|
|
77142
77543
|
if (deliveredNonces.has(record.turnNonce))
|
|
77143
77544
|
return { action: "skip-journaled" };
|
|
@@ -77152,7 +77553,13 @@ function decideOutboxSweep(input) {
|
|
|
77152
77553
|
return { action: "skip-unroutable" };
|
|
77153
77554
|
const delayed = age > maxAgeMs;
|
|
77154
77555
|
const prefix = (delayed ? "(delayed) " : "") + routePrefix;
|
|
77155
|
-
|
|
77556
|
+
const framed = shouldFrameReplyThrow(record, { frameEnabled: provenanceFraming });
|
|
77557
|
+
const body = framed ? applyReplyThrowFraming(record.text) : record.text;
|
|
77558
|
+
return {
|
|
77559
|
+
action: delayed ? "send-delayed" : "send",
|
|
77560
|
+
text: prefix + body,
|
|
77561
|
+
...framed && body !== record.text ? { framedProvenance: "reply-throw" } : {}
|
|
77562
|
+
};
|
|
77156
77563
|
}
|
|
77157
77564
|
function resolveOutboxChat(record, deps) {
|
|
77158
77565
|
if (record.chatId != null && record.chatId !== "") {
|
|
@@ -77447,7 +77854,8 @@ function journalExternalDelivery(args, stateDir, now = Date.now()) {
|
|
|
77447
77854
|
tgMessageId: args.tgMessageId,
|
|
77448
77855
|
ts: now,
|
|
77449
77856
|
deliverySource: args.deliverySource ?? "reply-tool",
|
|
77450
|
-
...args.replyAlreadyDeliveredThisTurn == null ? {} : { replyAlreadyDeliveredThisTurn: args.replyAlreadyDeliveredThisTurn }
|
|
77857
|
+
...args.replyAlreadyDeliveredThisTurn == null ? {} : { replyAlreadyDeliveredThisTurn: args.replyAlreadyDeliveredThisTurn },
|
|
77858
|
+
...args.framedProvenance == null ? {} : { framedProvenance: args.framedProvenance }
|
|
77451
77859
|
}, stateDir);
|
|
77452
77860
|
clearOutboxRecord(nonce, stateDir);
|
|
77453
77861
|
}
|
|
@@ -77502,6 +77910,7 @@ function queueFloodBlockedReply(input) {
|
|
|
77502
77910
|
textSha256: sha256Hex(text4),
|
|
77503
77911
|
createdAt,
|
|
77504
77912
|
source: FLOOD_QUEUED_SOURCE,
|
|
77913
|
+
audience: "user",
|
|
77505
77914
|
...input.originChatId != null ? { originChatId: input.originChatId } : {},
|
|
77506
77915
|
...input.originThreadId != null ? { originThreadId: input.originThreadId } : {}
|
|
77507
77916
|
};
|
|
@@ -78020,7 +78429,12 @@ function decideCapturedProseDelivery(args, deps) {
|
|
|
78020
78429
|
if (deps?.backstopDeliveredNonceHit?.(state3.turnId) === true) {
|
|
78021
78430
|
return { deliver: false, reason: "already-delivered" };
|
|
78022
78431
|
}
|
|
78023
|
-
return {
|
|
78432
|
+
return {
|
|
78433
|
+
deliver: true,
|
|
78434
|
+
text: text4,
|
|
78435
|
+
reason: "captured-prose",
|
|
78436
|
+
...state3.replyToolThrewThisTurn === true ? { replyToolThrewThisTurn: true } : {}
|
|
78437
|
+
};
|
|
78024
78438
|
}
|
|
78025
78439
|
function settleCapturedProseDelivery(outcome, effects) {
|
|
78026
78440
|
if (outcome === "failed") {
|
|
@@ -78157,10 +78571,23 @@ async function sendReplyChunks(deps, state3) {
|
|
|
78157
78571
|
}
|
|
78158
78572
|
const sendChunkPlainText = async (opts) => {
|
|
78159
78573
|
const plain = chunks[i].length > 0 ? chunks[i] : "\u26a0\ufe0f (a fragment could not be rendered for Telegram)";
|
|
78160
|
-
const
|
|
78161
|
-
|
|
78162
|
-
|
|
78163
|
-
|
|
78574
|
+
const pieces = splitPlainTextToCap(plain);
|
|
78575
|
+
const pieceOpts = (p) => {
|
|
78576
|
+
if (pieces.length === 1)
|
|
78577
|
+
return opts;
|
|
78578
|
+
const o = { ...opts };
|
|
78579
|
+
if (p !== pieces.length - 1)
|
|
78580
|
+
delete o.reply_markup;
|
|
78581
|
+
if (p !== 0)
|
|
78582
|
+
delete o.reply_parameters;
|
|
78583
|
+
return o;
|
|
78584
|
+
};
|
|
78585
|
+
for (let p = 0;p < pieces.length; p++) {
|
|
78586
|
+
const sent = await deps.sendLiteralRaw(pieceOpts(p), pieces[p]);
|
|
78587
|
+
sentIds.push(sent.message_id);
|
|
78588
|
+
deps.logOutbound("reply", chatId, sent.message_id, pieces[p].length, pieces.length > 1 ? `chunk=${i + 1}/${chunks.length} plaintext-fallback piece=${p + 1}/${pieces.length}` : `chunk=${i + 1}/${chunks.length} plaintext-fallback`);
|
|
78589
|
+
}
|
|
78590
|
+
deps.stderr(`telegram gateway: markdown parse-reject \u2014 resent chunk ${i + 1}/${chunks.length} as plain text` + (pieces.length > 1 ? ` in ${pieces.length} piece(s) (4096-char plain cap)` : "") + `
|
|
78164
78591
|
`);
|
|
78165
78592
|
};
|
|
78166
78593
|
const sendChunk = (opts, wrapped) => {
|
|
@@ -78209,6 +78636,13 @@ async function sendReplyChunks(deps, state3) {
|
|
|
78209
78636
|
else
|
|
78210
78637
|
throw retryErr;
|
|
78211
78638
|
}
|
|
78639
|
+
} else if (isQuoteRejectionError(err) && sendOptsHaveQuote(sendOpts)) {
|
|
78640
|
+
const retryOpts = dropQuoteFromSendOpts(sendOpts);
|
|
78641
|
+
const sent = await sendChunk(retryOpts, false);
|
|
78642
|
+
sentIds.push(sent.message_id);
|
|
78643
|
+
deps.logOutbound("reply", chatId, sent.message_id, chunks[i].length, `chunk=${i + 1}/${chunks.length} quote-dropped`);
|
|
78644
|
+
deps.stderr(`telegram gateway: quote not found in the replied-to message \u2014 resent chunk ${i + 1}/${chunks.length} without the quote
|
|
78645
|
+
`);
|
|
78212
78646
|
} else if (isMessageTooLongError(err)) {
|
|
78213
78647
|
await sendChunkResplit(sendOpts);
|
|
78214
78648
|
} else if (isHtmlParseRejectError(err)) {
|
|
@@ -78221,7 +78655,7 @@ async function sendReplyChunks(deps, state3) {
|
|
|
78221
78655
|
return { threadId, previewMessageId };
|
|
78222
78656
|
}
|
|
78223
78657
|
function classifyReadBackError(err) {
|
|
78224
|
-
if (err instanceof
|
|
78658
|
+
if (err instanceof import_grammy11.GrammyError && err.error_code === 400) {
|
|
78225
78659
|
const d = (err.description || "").toLowerCase();
|
|
78226
78660
|
if (d.includes("message to edit not found"))
|
|
78227
78661
|
return "absent";
|
|
@@ -78776,10 +79210,7 @@ ${url}`;
|
|
|
78776
79210
|
const shouldReplyTo = reply_to != null && replyMode !== "off" && (replyMode === "all" || i === 0);
|
|
78777
79211
|
return {
|
|
78778
79212
|
...shouldReplyTo ? {
|
|
78779
|
-
reply_parameters:
|
|
78780
|
-
message_id: reply_to,
|
|
78781
|
-
...quoteText != null ? { quote: { text: quoteText, position: 0 } } : {}
|
|
78782
|
-
}
|
|
79213
|
+
reply_parameters: buildReplyParameters(reply_to, quoteText)
|
|
78783
79214
|
} : {},
|
|
78784
79215
|
...tid != null ? { message_thread_id: tid } : {},
|
|
78785
79216
|
...disableLinkPreview ? { link_preview_options: { is_disabled: true } } : {},
|
|
@@ -78838,7 +79269,7 @@ ${url}`;
|
|
|
78838
79269
|
opts.message_thread_id = tid;
|
|
78839
79270
|
else
|
|
78840
79271
|
delete opts.message_thread_id;
|
|
78841
|
-
return lockedBot.api.sendVoice(chat_id, new
|
|
79272
|
+
return lockedBot.api.sendVoice(chat_id, new import_grammy11.InputFile(Buffer.from(oggBytes)), opts);
|
|
78842
79273
|
}, { threadId, chat_id, verb: "sendVoice" });
|
|
78843
79274
|
sentIds.push(sentVoice.message_id);
|
|
78844
79275
|
logOutbound("reply", chat_id, sentVoice.message_id, voiceOutPlan?.ttsChunks[v]?.length ?? 0, `voice-note=${v + 1}/${voiceOggs.length}`);
|
|
@@ -78911,12 +79342,12 @@ ${url}`;
|
|
|
78911
79342
|
...replyParams,
|
|
78912
79343
|
...tid != null ? { message_thread_id: tid } : {}
|
|
78913
79344
|
};
|
|
78914
|
-
return lockedBot.api.sendDocument(chat_id, new
|
|
79345
|
+
return lockedBot.api.sendDocument(chat_id, new import_grammy11.InputFile(f), baseOpts);
|
|
78915
79346
|
}, { threadId, chat_id, verb: "sendDocument" });
|
|
78916
79347
|
if (allPhotos) {
|
|
78917
79348
|
const media = files.map((f) => ({
|
|
78918
79349
|
type: "photo",
|
|
78919
|
-
media: new
|
|
79350
|
+
media: new import_grammy11.InputFile(f)
|
|
78920
79351
|
}));
|
|
78921
79352
|
let sent;
|
|
78922
79353
|
try {
|
|
@@ -78949,7 +79380,7 @@ ${url}`;
|
|
|
78949
79380
|
sentIds.push(m.message_id);
|
|
78950
79381
|
} else {
|
|
78951
79382
|
for (const f of files) {
|
|
78952
|
-
const input = new
|
|
79383
|
+
const input = new import_grammy11.InputFile(f);
|
|
78953
79384
|
const isPhoto = sendableAsPhoto(f);
|
|
78954
79385
|
let sent;
|
|
78955
79386
|
try {
|
|
@@ -79047,10 +79478,11 @@ async function deliverCapturedProse(deps, args) {
|
|
|
79047
79478
|
} = deps;
|
|
79048
79479
|
const { chatId, threadId, statusKeyStr, registryKey, originTurnId, text: text4, turnDurationMs } = args;
|
|
79049
79480
|
const now = Date.now();
|
|
79481
|
+
const framed = shouldFrameReplyThrow({ replyToolThrewThisTurn: args.replyToolThrewThisTurn }, { frameEnabled: process.env.SWITCHROOM_TG_OUTBOX_PROVENANCE_FRAMING !== "0" });
|
|
79050
79482
|
let outcome;
|
|
79051
79483
|
const already = outboundDedup.check(chatId, threadId, text4, now, registryKey);
|
|
79052
79484
|
if (already == null) {
|
|
79053
|
-
let out = normalizeParagraphBreaks(repairEscapedWhitespace(text4));
|
|
79485
|
+
let out = normalizeParagraphBreaks(repairEscapedWhitespace(framed ? applyReplyThrowFraming(text4) : text4));
|
|
79054
79486
|
out = redactOutboundText(out, "captured_prose");
|
|
79055
79487
|
const chunks = splitMarkdownChunks(out, RICH_MESSAGE_MAX_CHARS);
|
|
79056
79488
|
const sentIds = [];
|
|
@@ -79080,9 +79512,24 @@ async function deliverCapturedProse(deps, args) {
|
|
|
79080
79512
|
} catch {}
|
|
79081
79513
|
}
|
|
79082
79514
|
outboundDedup.record(chatId, threadId, text4, now, registryKey);
|
|
79083
|
-
journalExternalDelivery({
|
|
79515
|
+
journalExternalDelivery({
|
|
79516
|
+
turnNonce: originTurnId,
|
|
79517
|
+
text: text4,
|
|
79518
|
+
tgMessageId: sentIds[sentIds.length - 1],
|
|
79519
|
+
replyAlreadyDeliveredThisTurn: false,
|
|
79520
|
+
...framed ? { framedProvenance: "reply-throw" } : {}
|
|
79521
|
+
});
|
|
79084
79522
|
process.stderr.write(`telegram gateway: captured-prose delivery \u2014 sent ${out.length} chars recovered from ` + `transcript scan (chat=${chatId} origin=${originTurnId})
|
|
79085
79523
|
`);
|
|
79524
|
+
if (framed) {
|
|
79525
|
+
process.stderr.write(formatReplyThrowFraming({
|
|
79526
|
+
turnNonce: originTurnId,
|
|
79527
|
+
turnId: originTurnId,
|
|
79528
|
+
chatId,
|
|
79529
|
+
textSha256: sha256Hex(text4),
|
|
79530
|
+
source: "captured-prose-bridge"
|
|
79531
|
+
}));
|
|
79532
|
+
}
|
|
79086
79533
|
outcome = "sent";
|
|
79087
79534
|
} catch (err) {
|
|
79088
79535
|
process.stderr.write(`telegram gateway: captured-prose delivery failed: ${err.message} \u2014 ` + `arming the silent-end re-prompt net (recordUndeliveredTurnEnd) so the ` + `answer is recoverable (chat=${chatId} origin=${originTurnId})
|
|
@@ -79120,7 +79567,7 @@ async function deliverCapturedProse(deps, args) {
|
|
|
79120
79567
|
process.stderr.write(`telegram gateway: WARN captured-prose exhausted-boundary fallback \u2014 rich send ` + `failed with the re-prompt budget already spent; attempting a plain-text ` + `delivery of the recovered answer before the generic apology ` + `(chat=${chatId} origin=${originTurnId})
|
|
79121
79568
|
`);
|
|
79122
79569
|
const plain = redactOutboundText(text4, "captured_prose");
|
|
79123
|
-
const plainChunks =
|
|
79570
|
+
const plainChunks = splitPlainTextToCap(plain);
|
|
79124
79571
|
try {
|
|
79125
79572
|
let liveThreadId = threadId;
|
|
79126
79573
|
const plainSentIds = [];
|
|
@@ -79527,7 +79974,7 @@ class LivenessTracker {
|
|
|
79527
79974
|
// gateway/outbound-send-path.ts
|
|
79528
79975
|
init_format();
|
|
79529
79976
|
init_text_voice_scrub();
|
|
79530
|
-
var
|
|
79977
|
+
var import_grammy12 = __toESM(require_mod2(), 1);
|
|
79531
79978
|
init_format();
|
|
79532
79979
|
init_rich_send();
|
|
79533
79980
|
function normalizeOutboundBody2(rawText, site, redact2, opts = {}) {
|
|
@@ -80388,6 +80835,25 @@ function discardParkedTurnStart(rawContent) {
|
|
|
80388
80835
|
}
|
|
80389
80836
|
return null;
|
|
80390
80837
|
}
|
|
80838
|
+
function drainParkedTurnStartsForChat(deps, chatId, threadId) {
|
|
80839
|
+
const wantThread = envThreadIdNum(threadId);
|
|
80840
|
+
const drained = [];
|
|
80841
|
+
for (let i = 0;i < parkedTurnStarts.length; ) {
|
|
80842
|
+
const entry = parkedTurnStarts[i];
|
|
80843
|
+
if (entry.chatId === chatId && envThreadIdNum(entry.threadId) === wantThread) {
|
|
80844
|
+
drained.push(entry);
|
|
80845
|
+
parkedTurnStarts.splice(i, 1);
|
|
80846
|
+
} else {
|
|
80847
|
+
i++;
|
|
80848
|
+
}
|
|
80849
|
+
}
|
|
80850
|
+
for (const env of drained) {
|
|
80851
|
+
process.stderr.write(`telegram gateway: parked-turn-start drained on silence-fallback ` + `chat=${env.chatId ?? "-"} thread=${envThreadIdNum(env.threadId) ?? "-"} ` + `msg=${env.messageId ?? "-"}
|
|
80852
|
+
`);
|
|
80853
|
+
beginTurn(deps, env);
|
|
80854
|
+
}
|
|
80855
|
+
return drained;
|
|
80856
|
+
}
|
|
80391
80857
|
async function openQueuedCard(deps, chatId, threadId, replyToMessageId) {
|
|
80392
80858
|
try {
|
|
80393
80859
|
const sent = await deps.robustApiCall(() => deps.bot.api.sendRichMessage(chatId, richMessage(QUEUED_CARD_HTML), {
|
|
@@ -81381,6 +81847,7 @@ function handleSessionEvent(deps, ev) {
|
|
|
81381
81847
|
registryKey: turn.registryKey ?? null,
|
|
81382
81848
|
originTurnId: turn.turnId,
|
|
81383
81849
|
text: proseDecision.text,
|
|
81850
|
+
replyToolThrewThisTurn: proseDecision.replyToolThrewThisTurn === true,
|
|
81384
81851
|
turnDurationMs
|
|
81385
81852
|
});
|
|
81386
81853
|
} else {
|
|
@@ -82901,7 +83368,7 @@ async function loadFromAuthBroker(options = {}) {
|
|
|
82901
83368
|
}
|
|
82902
83369
|
|
|
82903
83370
|
// gateway/folder-picker-handler.ts
|
|
82904
|
-
var
|
|
83371
|
+
var import_grammy13 = __toESM(require_mod2(), 1);
|
|
82905
83372
|
|
|
82906
83373
|
// ../src/drive/folder-picker.ts
|
|
82907
83374
|
var DRIVE_ID_RE2 = /^[A-Za-z0-9_-]+$/;
|
|
@@ -83220,7 +83687,7 @@ async function recordGrantAndConfirm(ctx, deps, parsed) {
|
|
|
83220
83687
|
const confirmText = `\u2705 Granted ${deps.agentName} access to folder ${parsed.folder_id}
|
|
83221
83688
|
` + `Scope: \`${scope}\`
|
|
83222
83689
|
` + `Revoke with: \`/approvals revoke ${decisionId}\``;
|
|
83223
|
-
const kb = new
|
|
83690
|
+
const kb = new import_grammy13.InlineKeyboard().url("\uD83D\uDCD6 Open in Drive", `https://drive.google.com/drive/folders/${parsed.folder_id}`);
|
|
83224
83691
|
try {
|
|
83225
83692
|
await ctx.editMessageText({ markdown: confirmText }, {
|
|
83226
83693
|
reply_markup: kb
|
|
@@ -83229,7 +83696,7 @@ async function recordGrantAndConfirm(ctx, deps, parsed) {
|
|
|
83229
83696
|
await ctx.answerCallbackQuery({ text: "Allowed" });
|
|
83230
83697
|
}
|
|
83231
83698
|
function toKeyboard(spec) {
|
|
83232
|
-
const kb = new
|
|
83699
|
+
const kb = new import_grammy13.InlineKeyboard;
|
|
83233
83700
|
for (let r = 0;r < spec.rows.length; r++) {
|
|
83234
83701
|
const row = spec.rows[r];
|
|
83235
83702
|
for (const btn of row) {
|
|
@@ -84127,10 +84594,6 @@ Allowed: \`${deps.escapeHtml(allow)}\``, { html: true });
|
|
|
84127
84594
|
|
|
84128
84595
|
// gateway/model-command.ts
|
|
84129
84596
|
var MODEL_ALIASES2 = ["opus", "sonnet", "haiku", "fable", "default"];
|
|
84130
|
-
var CLAUDE_MODEL_ALIASES2 = {
|
|
84131
|
-
opus48: "claude-opus-4-8",
|
|
84132
|
-
"opus-4-8": "claude-opus-4-8"
|
|
84133
|
-
};
|
|
84134
84597
|
var MODEL_ARG_RE2 = /^[A-Za-z0-9][A-Za-z0-9._/\[\]-]{0,99}$/;
|
|
84135
84598
|
function isValidModelArg2(arg) {
|
|
84136
84599
|
return MODEL_ARG_RE2.test(arg);
|
|
@@ -84289,7 +84752,7 @@ function resolveStaleAwareBusy(input) {
|
|
|
84289
84752
|
var PERSIST_NOTE3 = "_A `/model` switch relaunches the session (~30s) on the chosen model. Session-only \u2014 reverts to the configured `model:` on the next restart. `/model default` reverts now. Live scrollback is replaced by a fresh session; memory and the handoff briefing carry the context. To change the default permanently, set `model:` in switchroom.yaml._";
|
|
84290
84753
|
function claudeAliasHints2(prefix) {
|
|
84291
84754
|
const byTarget = new Map;
|
|
84292
|
-
for (const [alias, target] of Object.entries(
|
|
84755
|
+
for (const [alias, target] of Object.entries(CLAUDE_MODEL_ALIASES)) {
|
|
84293
84756
|
const spellings = byTarget.get(target) ?? [];
|
|
84294
84757
|
spellings.push(alias);
|
|
84295
84758
|
byTarget.set(target, spellings);
|
|
@@ -84297,7 +84760,7 @@ function claudeAliasHints2(prefix) {
|
|
|
84297
84760
|
return [...byTarget].map(([target, spellings]) => `${spellings.map((a) => `\`${prefix}${a}\``).join(" \u00b7 ")} \u2192 \`${target}\``).join("; ");
|
|
84298
84761
|
}
|
|
84299
84762
|
function helpText4(deps, reason) {
|
|
84300
|
-
const srAliasExamples = Object.keys(
|
|
84763
|
+
const srAliasExamples = Object.keys(SR_MODEL_ALIASES).map((a) => `\`${a}\``).join(" \u00b7 ");
|
|
84301
84764
|
const claudeAliasExamples = claudeAliasHints2("");
|
|
84302
84765
|
const lines = [];
|
|
84303
84766
|
if (reason)
|
|
@@ -84312,7 +84775,7 @@ async function handleModelCommand2(parsed, deps) {
|
|
|
84312
84775
|
if (parsed.kind === "show") {
|
|
84313
84776
|
const configured = deps.getConfiguredModel();
|
|
84314
84777
|
const shown = configured && configured.length > 0 ? configured : "default";
|
|
84315
|
-
const srAliasExamples = Object.keys(
|
|
84778
|
+
const srAliasExamples = Object.keys(SR_MODEL_ALIASES).map((a) => `\`/model ${a}\``).join(" \u00b7 ");
|
|
84316
84779
|
return {
|
|
84317
84780
|
text: [
|
|
84318
84781
|
`**Model \u2014 ${deps.escapeHtml(deps.getAgentName())}**`,
|
|
@@ -84330,7 +84793,7 @@ async function handleModelCommand2(parsed, deps) {
|
|
|
84330
84793
|
if (!isValidModelArg2(parsed.model)) {
|
|
84331
84794
|
return helpText4(deps, `not a valid model name: ${parsed.model}`);
|
|
84332
84795
|
}
|
|
84333
|
-
const model =
|
|
84796
|
+
const model = expandModelAlias(parsed.model);
|
|
84334
84797
|
if (deps.isBusy()) {
|
|
84335
84798
|
return {
|
|
84336
84799
|
text: "\u23f3 The agent is mid-turn \u2014 a model switch needs an idle session. The switch was not applied.",
|
|
@@ -84357,10 +84820,10 @@ function relaunchErrorReply2(deps, model, err) {
|
|
|
84357
84820
|
return { text: `\u274c Could not schedule model switch: ${deps.escapeHtml(msg)}`, html: true };
|
|
84358
84821
|
}
|
|
84359
84822
|
function unvalidatedIdCaveat2(deps, model) {
|
|
84360
|
-
const lower =
|
|
84823
|
+
const lower = canonicalModelToken(model).toLowerCase();
|
|
84361
84824
|
if (!lower.startsWith("claude-"))
|
|
84362
84825
|
return null;
|
|
84363
|
-
if (Object.values(
|
|
84826
|
+
if (Object.values(CLAUDE_MODEL_ALIASES).includes(lower))
|
|
84364
84827
|
return null;
|
|
84365
84828
|
return `_\`${deps.escapeHtml(model)}\` can't be validated before launch \u2014 if it isn't a real Claude model id, claude will silently serve the configured fallback model instead. I check the first reply and will warn if that happens._`;
|
|
84366
84829
|
}
|
|
@@ -84445,45 +84908,17 @@ var SR_MODEL_LABELS2 = {
|
|
|
84445
84908
|
"sr-gpt-5.6-terra": "GPT-5.6 Terra",
|
|
84446
84909
|
"sr-gpt-5.6-luna": "GPT-5.6 Luna"
|
|
84447
84910
|
};
|
|
84448
|
-
var SR_MODEL_ALIASES2 = {
|
|
84449
|
-
flash: "sr-gemini-2.5-flash",
|
|
84450
|
-
gemini: "sr-gemini-2.5-pro",
|
|
84451
|
-
deepseek: "sr-deepseek-v3",
|
|
84452
|
-
r1: "sr-deepseek-r1",
|
|
84453
|
-
glm: "sr-glm-5",
|
|
84454
|
-
codex: "sr-codex-5.5",
|
|
84455
|
-
grok: "sr-grok-4.5",
|
|
84456
|
-
kimi: "sr-kimi-k3",
|
|
84457
|
-
gpt: "sr-gpt-5.6-sol",
|
|
84458
|
-
sol: "sr-gpt-5.6-sol",
|
|
84459
|
-
terra: "sr-gpt-5.6-terra",
|
|
84460
|
-
luna: "sr-gpt-5.6-luna"
|
|
84461
|
-
};
|
|
84462
84911
|
function isOfflineTrustedModelToken(token) {
|
|
84463
84912
|
const lower = token.toLowerCase();
|
|
84464
84913
|
if (MODEL_ALIASES2.includes(lower))
|
|
84465
84914
|
return true;
|
|
84466
|
-
if (lower in
|
|
84915
|
+
if (lower in SR_MODEL_ALIASES)
|
|
84467
84916
|
return true;
|
|
84468
|
-
if (Object.values(
|
|
84917
|
+
if (Object.values(SR_MODEL_ALIASES).includes(lower))
|
|
84469
84918
|
return true;
|
|
84470
|
-
if (lower in
|
|
84919
|
+
if (lower in CLAUDE_MODEL_ALIASES)
|
|
84471
84920
|
return true;
|
|
84472
|
-
return Object.values(
|
|
84473
|
-
}
|
|
84474
|
-
function expandSrAlias2(arg) {
|
|
84475
|
-
return SR_MODEL_ALIASES2[arg.toLowerCase()] ?? arg;
|
|
84476
|
-
}
|
|
84477
|
-
function canonicalModelToken2(arg) {
|
|
84478
|
-
const trimmed = arg.trim();
|
|
84479
|
-
const lower = trimmed.toLowerCase();
|
|
84480
|
-
return lower.startsWith("claude-") ? lower : trimmed;
|
|
84481
|
-
}
|
|
84482
|
-
function expandClaudeAlias2(arg) {
|
|
84483
|
-
return CLAUDE_MODEL_ALIASES2[arg.trim().toLowerCase()] ?? arg;
|
|
84484
|
-
}
|
|
84485
|
-
function expandModelAlias2(arg) {
|
|
84486
|
-
return canonicalModelToken2(expandSrAlias2(expandClaudeAlias2(arg)));
|
|
84921
|
+
return Object.values(CLAUDE_MODEL_ALIASES).includes(lower);
|
|
84487
84922
|
}
|
|
84488
84923
|
function srFriendlyLabel2(srName) {
|
|
84489
84924
|
return SR_MODEL_LABELS2[srName] ?? srName.replace(/^sr-/, "").replace(/-/g, " ");
|
|
@@ -84563,7 +84998,7 @@ function headerRow2(label) {
|
|
|
84563
84998
|
return [{ text: label, callback_data: MODEL_CALLBACK_HEADER2 }];
|
|
84564
84999
|
}
|
|
84565
85000
|
function externalModelNames2(discovered) {
|
|
84566
|
-
const set = new Set(Object.values(
|
|
85001
|
+
const set = new Set(Object.values(SR_MODEL_ALIASES));
|
|
84567
85002
|
for (const n of discovered) {
|
|
84568
85003
|
if (isSrModel2(n))
|
|
84569
85004
|
set.add(n);
|
|
@@ -84681,7 +85116,7 @@ async function handleModelMenuCallback(data, deps) {
|
|
|
84681
85116
|
if (!isValidModelArg2(token)) {
|
|
84682
85117
|
return { answer: "Invalid model name", reply: await buildModelMenu2(deps) };
|
|
84683
85118
|
}
|
|
84684
|
-
token =
|
|
85119
|
+
token = expandModelAlias(token);
|
|
84685
85120
|
if (!isRecognizedSwitchToken(token)) {
|
|
84686
85121
|
return { answer: "Model list changed \u2014 menu refreshed", reply: await buildModelMenu2(deps) };
|
|
84687
85122
|
}
|
|
@@ -85337,7 +85772,9 @@ var HINDSIGHT_DEFAULT_RECALL_MAX_CONCURRENT = 8;
|
|
|
85337
85772
|
var HINDSIGHT_DEFAULT_REFLECT_WALL_TIMEOUT_S = 600;
|
|
85338
85773
|
var HINDSIGHT_DEFAULT_CONSOLIDATION_MAX_MEMORIES_PER_ROUND = 250;
|
|
85339
85774
|
var HINDSIGHT_DEFAULT_CONSOLIDATION_SLOT_LIMIT = 6;
|
|
85340
|
-
var
|
|
85775
|
+
var HINDSIGHT_DEFAULT_CONSOLIDATION_RESERVED_SLOTS = 1;
|
|
85776
|
+
var HINDSIGHT_DEFAULT_GRAPH_SEED_MIN_SIMILARITY = 0.3;
|
|
85777
|
+
var HINDSIGHT_DEFAULT_LLM_SUPPORTS_MAX_ITEMS = "false";
|
|
85341
85778
|
var HINDSIGHT_DEFAULT_RECENCY_DECAY_FUNCTION = "exponential";
|
|
85342
85779
|
var HINDSIGHT_DEFAULT_RECENCY_DECAY_HALFLIFE_DAYS = 30;
|
|
85343
85780
|
var HINDSIGHT_PERF_DEFAULTS_UNGATED = [
|
|
@@ -85383,8 +85820,8 @@ var HINDSIGHT_PERF_DEFAULTS_UNGATED = [
|
|
|
85383
85820
|
String(HINDSIGHT_DEFAULT_REFLECT_WALL_TIMEOUT_S)
|
|
85384
85821
|
],
|
|
85385
85822
|
[
|
|
85386
|
-
"
|
|
85387
|
-
String(
|
|
85823
|
+
"HINDSIGHT_API_WORKER_CONSOLIDATION_RESERVED_SLOTS",
|
|
85824
|
+
String(HINDSIGHT_DEFAULT_CONSOLIDATION_RESERVED_SLOTS)
|
|
85388
85825
|
],
|
|
85389
85826
|
[
|
|
85390
85827
|
"HINDSIGHT_API_WORKER_CONSOLIDATION_SLOT_LIMIT",
|
|
@@ -85401,6 +85838,10 @@ var HINDSIGHT_PERF_DEFAULTS_UNGATED = [
|
|
|
85401
85838
|
[
|
|
85402
85839
|
"HINDSIGHT_API_RECENCY_DECAY_HALFLIFE_DAYS",
|
|
85403
85840
|
String(HINDSIGHT_DEFAULT_RECENCY_DECAY_HALFLIFE_DAYS)
|
|
85841
|
+
],
|
|
85842
|
+
[
|
|
85843
|
+
"HINDSIGHT_API_GRAPH_SEED_MIN_SIMILARITY",
|
|
85844
|
+
String(HINDSIGHT_DEFAULT_GRAPH_SEED_MIN_SIMILARITY)
|
|
85404
85845
|
]
|
|
85405
85846
|
];
|
|
85406
85847
|
var HINDSIGHT_PERF_DEFAULTS_GPU = [
|
|
@@ -85421,22 +85862,41 @@ var HINDSIGHT_PERF_DEFAULTS_LOCAL_LLM = [
|
|
|
85421
85862
|
String(HINDSIGHT_DEFAULT_CONSOLIDATION_LLM_MAX_CONCURRENT)
|
|
85422
85863
|
],
|
|
85423
85864
|
["HINDSIGHT_API_LLM_STRICT_SCHEMA", HINDSIGHT_DEFAULT_LLM_STRICT_SCHEMA],
|
|
85424
|
-
["HINDSIGHT_API_LLM_MAX_RETRIES", String(HINDSIGHT_DEFAULT_LLM_MAX_RETRIES)]
|
|
85865
|
+
["HINDSIGHT_API_LLM_MAX_RETRIES", String(HINDSIGHT_DEFAULT_LLM_MAX_RETRIES)],
|
|
85866
|
+
["HINDSIGHT_API_LLM_SUPPORTS_MAX_ITEMS", HINDSIGHT_DEFAULT_LLM_SUPPORTS_MAX_ITEMS]
|
|
85425
85867
|
];
|
|
85426
85868
|
var HINDSIGHT_PERF_OVERRIDE_ONLY_KEYS = new Set([
|
|
85427
85869
|
"HINDSIGHT_API_WORKER_CONSOLIDATION_BANK_PRIORITY",
|
|
85428
85870
|
"HINDSIGHT_CE_DECISIVE_RELATIVE_GAP",
|
|
85429
85871
|
"HINDSIGHT_API_RECENCY_DECAY_LINEAR_WINDOW_DAYS",
|
|
85430
85872
|
"HINDSIGHT_API_WORKER_MAX_SLOTS",
|
|
85431
|
-
"
|
|
85873
|
+
"HINDSIGHT_API_WORKER_RETAIN_RESERVED_SLOTS",
|
|
85874
|
+
"HINDSIGHT_API_SEMANTIC_MIN_SIMILARITY",
|
|
85875
|
+
"HINDSIGHT_MCP_RECALL_BUDGET_MODE",
|
|
85876
|
+
"HINDSIGHT_API_LLM_TEMPERATURE_REFLECT",
|
|
85877
|
+
"HINDSIGHT_API_RETAIN_WALL_TIMEOUT"
|
|
85432
85878
|
]);
|
|
85879
|
+
var HINDSIGHT_WORKER_SLOT_TYPES = [
|
|
85880
|
+
"consolidation",
|
|
85881
|
+
"file_convert_retain",
|
|
85882
|
+
"graph_maintenance",
|
|
85883
|
+
"import_documents",
|
|
85884
|
+
"refresh_mental_model",
|
|
85885
|
+
"retain"
|
|
85886
|
+
];
|
|
85887
|
+
var HINDSIGHT_WORKER_RESERVED_SLOT_ALIASES = new Map(HINDSIGHT_WORKER_SLOT_TYPES.map((type) => [
|
|
85888
|
+
`HINDSIGHT_API_WORKER_${type.toUpperCase()}_MAX_SLOTS`,
|
|
85889
|
+
`HINDSIGHT_API_WORKER_${type.toUpperCase()}_RESERVED_SLOTS`
|
|
85890
|
+
]));
|
|
85433
85891
|
var HINDSIGHT_PERF_ENV_KEYS = new Set([
|
|
85434
85892
|
...[
|
|
85435
85893
|
...HINDSIGHT_PERF_DEFAULTS_UNGATED,
|
|
85436
85894
|
...HINDSIGHT_PERF_DEFAULTS_GPU,
|
|
85437
85895
|
...HINDSIGHT_PERF_DEFAULTS_LOCAL_LLM
|
|
85438
85896
|
].map(([k]) => k),
|
|
85439
|
-
...HINDSIGHT_PERF_OVERRIDE_ONLY_KEYS
|
|
85897
|
+
...HINDSIGHT_PERF_OVERRIDE_ONLY_KEYS,
|
|
85898
|
+
...HINDSIGHT_WORKER_RESERVED_SLOT_ALIASES.keys(),
|
|
85899
|
+
...HINDSIGHT_WORKER_RESERVED_SLOT_ALIASES.values()
|
|
85440
85900
|
]);
|
|
85441
85901
|
|
|
85442
85902
|
// ../src/setup/hindsight-context-budget.ts
|
|
@@ -85787,7 +86247,9 @@ function resolveMainModel(model) {
|
|
|
85787
86247
|
return normalizeModelAlias(model);
|
|
85788
86248
|
}
|
|
85789
86249
|
function normalizeModelAlias(model) {
|
|
85790
|
-
|
|
86250
|
+
if (model === "claude-fable-5")
|
|
86251
|
+
return "fable";
|
|
86252
|
+
return expandModelAlias(model);
|
|
85791
86253
|
}
|
|
85792
86254
|
var CLAUDE_MD_YOURS_PLACEHOLDER = "This space is yours. Add per-agent rules, exceptions, or context the " + "Switchroom template doesn't capture. Everything above the marker line is " + "regenerated on every apply; this section is preserved.";
|
|
85793
86255
|
var pendingBankOps = new Set;
|
|
@@ -89830,7 +90292,9 @@ function createIpcServer(options) {
|
|
|
89830
90292
|
case "rollout_status_edit":
|
|
89831
90293
|
if (onRolloutStatusEdit) {
|
|
89832
90294
|
try {
|
|
89833
|
-
onRolloutStatusEdit(client3, msg)
|
|
90295
|
+
Promise.resolve(onRolloutStatusEdit(client3, msg)).catch((err) => {
|
|
90296
|
+
log(`rollout_status_edit handler rejected (client=${client3.id}): ${err.message}`);
|
|
90297
|
+
});
|
|
89834
90298
|
} catch (err) {
|
|
89835
90299
|
log(`rollout_status_edit handler threw (client=${client3.id}): ${err.message}`);
|
|
89836
90300
|
}
|
|
@@ -90016,6 +90480,58 @@ function createIpcServer(options) {
|
|
|
90016
90480
|
return ipcServer;
|
|
90017
90481
|
}
|
|
90018
90482
|
|
|
90483
|
+
// gateway/rollout-status-edit.ts
|
|
90484
|
+
var GONE_PATTERNS = [
|
|
90485
|
+
/message to edit not found/i,
|
|
90486
|
+
/message can't be edited/i,
|
|
90487
|
+
/message_id_invalid/i,
|
|
90488
|
+
/message to be edited not found/i
|
|
90489
|
+
];
|
|
90490
|
+
var NOT_MODIFIED = /message is not modified/i;
|
|
90491
|
+
function classifyRolloutEditError(err) {
|
|
90492
|
+
const e = err;
|
|
90493
|
+
const text4 = [
|
|
90494
|
+
typeof e?.description === "string" ? e.description : "",
|
|
90495
|
+
typeof e?.message === "string" ? e.message : ""
|
|
90496
|
+
].join(" ");
|
|
90497
|
+
if (NOT_MODIFIED.test(text4))
|
|
90498
|
+
return "not-modified";
|
|
90499
|
+
if (GONE_PATTERNS.some((re) => re.test(text4)))
|
|
90500
|
+
return "gone";
|
|
90501
|
+
return "transient";
|
|
90502
|
+
}
|
|
90503
|
+
async function handleRolloutStatusEdit(deps, client3, msg) {
|
|
90504
|
+
const reply = (ok3, extra = {}) => {
|
|
90505
|
+
try {
|
|
90506
|
+
client3.send({ type: "rollout_status_edited", requestId: msg.requestId, ok: ok3, ...extra });
|
|
90507
|
+
} catch {}
|
|
90508
|
+
};
|
|
90509
|
+
const self = deps.selfAgentName;
|
|
90510
|
+
if (self && msg.agentName !== self) {
|
|
90511
|
+
deps.log(`rollout_status_edit rejected \u2014 agent mismatch (${msg.agentName} != ${self})`);
|
|
90512
|
+
reply(false, { gone: false, reason: "agent mismatch" });
|
|
90513
|
+
return;
|
|
90514
|
+
}
|
|
90515
|
+
const operator = deps.operatorChatId();
|
|
90516
|
+
if (operator === undefined) {
|
|
90517
|
+
deps.log("rollout_status_edit \u2014 no operator chat (allowFrom empty)");
|
|
90518
|
+
reply(false, { gone: false, reason: "no operator chat" });
|
|
90519
|
+
return;
|
|
90520
|
+
}
|
|
90521
|
+
try {
|
|
90522
|
+
await deps.editMessage(operator, msg.messageId, msg.text);
|
|
90523
|
+
reply(true);
|
|
90524
|
+
} catch (err) {
|
|
90525
|
+
const cls = classifyRolloutEditError(err);
|
|
90526
|
+
if (cls === "not-modified") {
|
|
90527
|
+
reply(true);
|
|
90528
|
+
return;
|
|
90529
|
+
}
|
|
90530
|
+
deps.log(`rollout_status_edit failed (${cls}) request=${msg.requestId} message_id=${msg.messageId}: ${err.message}`);
|
|
90531
|
+
reply(false, { gone: cls === "gone", reason: err.message });
|
|
90532
|
+
}
|
|
90533
|
+
}
|
|
90534
|
+
|
|
90019
90535
|
// ../src/drive/diff-preview.ts
|
|
90020
90536
|
function buildDiffPreview(input) {
|
|
90021
90537
|
validateInput(input);
|
|
@@ -90463,7 +90979,7 @@ async function handleRequestMs365Approval(client3, msg, deps) {
|
|
|
90463
90979
|
|
|
90464
90980
|
// gateway/diff-preview-card.ts
|
|
90465
90981
|
init_format();
|
|
90466
|
-
var
|
|
90982
|
+
var import_grammy14 = __toESM(require_mod2(), 1);
|
|
90467
90983
|
var REQUEST_ID_RE = /^[0-9a-f]{32}$/;
|
|
90468
90984
|
var PENDING_FILE_ID_SENTINEL = "pending-create";
|
|
90469
90985
|
function buildDiffPreviewCard(input) {
|
|
@@ -90481,7 +90997,7 @@ function buildDiffPreviewCard(input) {
|
|
|
90481
90997
|
}
|
|
90482
90998
|
const text4 = bodyLines.join(`
|
|
90483
90999
|
`);
|
|
90484
|
-
const kb = new
|
|
91000
|
+
const kb = new import_grammy14.InlineKeyboard;
|
|
90485
91001
|
const ROW_BREAK_AFTER = [
|
|
90486
91002
|
"apply_suggestion"
|
|
90487
91003
|
];
|
|
@@ -90964,6 +91480,23 @@ function loadObligations(path3, fs3) {
|
|
|
90964
91480
|
return [];
|
|
90965
91481
|
return env.obligations.filter(isObligationRow).map(sanitizeCapturedDelivery);
|
|
90966
91482
|
}
|
|
91483
|
+
function removeUnmaintainedSnapshot(path3, fs3, reason = "persistence disabled", log = (l) => process.stderr.write(l)) {
|
|
91484
|
+
const unlink = fs3.unlinkSync;
|
|
91485
|
+
if (unlink == null)
|
|
91486
|
+
return false;
|
|
91487
|
+
try {
|
|
91488
|
+
if (!fs3.existsSync(path3))
|
|
91489
|
+
return false;
|
|
91490
|
+
unlink(path3);
|
|
91491
|
+
log(`obligation-store: persistence is off (${reason}) \u2014 removed unmaintained ` + `snapshot path=${path3} so it cannot be read as "nobody is waiting" (#4146)
|
|
91492
|
+
`);
|
|
91493
|
+
return true;
|
|
91494
|
+
} catch (err) {
|
|
91495
|
+
log(`obligation-store: could not remove unmaintained snapshot path=${path3}: ` + `${err.message} \u2014 audience classification falls back to the ` + `reader-side ledger-off guard (#4146)
|
|
91496
|
+
`);
|
|
91497
|
+
return false;
|
|
91498
|
+
}
|
|
91499
|
+
}
|
|
90967
91500
|
function persistObligations(path3, fs3, snapshot, log = (l) => process.stderr.write(l)) {
|
|
90968
91501
|
const env = { v: 1, obligations: [...snapshot] };
|
|
90969
91502
|
const tmp = path3 + ".tmp";
|
|
@@ -93615,6 +94148,7 @@ function buildSilencePokeOptions(deps) {
|
|
|
93615
94148
|
endCurrentTurnForKey,
|
|
93616
94149
|
getPendingInboundBuffer,
|
|
93617
94150
|
trackRedeliveredInbound,
|
|
94151
|
+
drainParkedTurnStarts,
|
|
93618
94152
|
closeActivityLane,
|
|
93619
94153
|
closeProgressLane,
|
|
93620
94154
|
hangRestart
|
|
@@ -93808,7 +94342,8 @@ function buildSilencePokeOptions(deps) {
|
|
|
93808
94342
|
} catch {}
|
|
93809
94343
|
const fbSelfAgent = process.env.SWITCHROOM_AGENT_NAME ?? "";
|
|
93810
94344
|
const fbRedeliver = redeliverBufferedInbound2(getPendingInboundBuffer(), fbSelfAgent, (m) => sendToAgent(fbSelfAgent, m), getInboundSpool(), trackRedeliveredInbound);
|
|
93811
|
-
|
|
94345
|
+
const fbDrainedParked = drainParkedTurnStarts?.(fbChatId, fbThreadId ?? null) ?? 0;
|
|
94346
|
+
process.stderr.write(`telegram gateway: silence-poke framework-fallback ended wedged turn ` + `chat=${fbChatId} thread=${ctx.threadId ?? "-"} silence_ms=${ctx.silenceMs} ` + `currentTurn_nulled=${tearsDownLiveTurn} ` + `drained_buffered=${fbRedeliver.redelivered}/${fbRedeliver.drained}` + `${fbDrainedParked > 0 ? ` drained_parked=${fbDrainedParked}` : ""}` + `${fbRedeliver.rebuffered > 0 ? ` rebuffered=${fbRedeliver.rebuffered}` : ""}` + `${fbExtraPurge.purged.length > 0 ? ` extra_keys_purged=${fbExtraPurge.purged.length}` : ""}
|
|
93812
94347
|
`);
|
|
93813
94348
|
}
|
|
93814
94349
|
};
|
|
@@ -97492,6 +98027,7 @@ function readAndFilter(raw, filters, limit) {
|
|
|
97492
98027
|
}
|
|
97493
98028
|
|
|
97494
98029
|
// gateway/update-announce.ts
|
|
98030
|
+
init_status_no_truncate();
|
|
97495
98031
|
var DEFAULT_LOOKBACK_MS = 10 * 60 * 1000;
|
|
97496
98032
|
function readLastTerminalUpdateAudit(opts = {}) {
|
|
97497
98033
|
const path3 = opts.auditLogPath ?? defaultAuditLogPath();
|
|
@@ -97552,7 +98088,10 @@ function renderUpdateOutcomeLine(entry) {
|
|
|
97552
98088
|
const stderrTail = (entry.stderr_tail ?? entry.error ?? "").slice(-400);
|
|
97553
98089
|
const opStep = entry.op;
|
|
97554
98090
|
const hint = recoveryHint(entry.install_context?.install_type);
|
|
97555
|
-
const lines = [
|
|
98091
|
+
const lines = [
|
|
98092
|
+
`\u274c update failed at ${opStep}: ${stderrTail || "(no stderr captured)"}`,
|
|
98093
|
+
`${NESTED_PREFIX}Recovery: ${hint}`
|
|
98094
|
+
];
|
|
97556
98095
|
return lines.join(`
|
|
97557
98096
|
`);
|
|
97558
98097
|
}
|
|
@@ -99881,8 +100420,37 @@ async function sweepOutbox(deps) {
|
|
|
99881
100420
|
return summary;
|
|
99882
100421
|
const deliveredNonces = readDeliveredNonces(deps.stateDir);
|
|
99883
100422
|
const records = pending.map((fileName) => readOutboxRecord(fileName, deps.stateDir)).filter((r) => r != null).sort((a, b) => a.createdAt !== b.createdAt ? a.createdAt - b.createdAt : a.turnNonce < b.turnNonce ? -1 : a.turnNonce > b.turnNonce ? 1 : 0);
|
|
100423
|
+
const audienceGateEnabled = deps.audienceGateEnabled?.() ?? process.env.SWITCHROOM_TG_OUTBOX_AUDIENCE_GATE !== "0";
|
|
100424
|
+
const escalateInternalSuppression = deps.escalateInternalSuppression ?? log;
|
|
100425
|
+
const provenanceFramingEnabled = deps.provenanceFramingEnabled?.() ?? process.env.SWITCHROOM_TG_OUTBOX_PROVENANCE_FRAMING !== "0";
|
|
100426
|
+
const logProvenanceFraming = deps.logProvenanceFraming ?? log;
|
|
99884
100427
|
for (const record2 of records) {
|
|
99885
100428
|
summary.scanned++;
|
|
100429
|
+
if (shouldSuppressForAudience(record2, { gateEnabled: audienceGateEnabled })) {
|
|
100430
|
+
summary.skipped++;
|
|
100431
|
+
summary.audienceSuppressed = (summary.audienceSuppressed ?? 0) + 1;
|
|
100432
|
+
const ageMs = now - record2.createdAt;
|
|
100433
|
+
appendDelivered({
|
|
100434
|
+
turnNonce: record2.turnNonce,
|
|
100435
|
+
textSha256: record2.textSha256,
|
|
100436
|
+
ts: now,
|
|
100437
|
+
deliverySource: "sweep",
|
|
100438
|
+
replyAlreadyDeliveredThisTurn: record2.replyAlreadyDeliveredThisTurn === true,
|
|
100439
|
+
audience: AUDIENCE_INTERNAL,
|
|
100440
|
+
suppressedAudience: AUDIENCE_INTERNAL
|
|
100441
|
+
}, deps.stateDir);
|
|
100442
|
+
clearOutboxRecord(record2.turnNonce, deps.stateDir);
|
|
100443
|
+
escalateInternalSuppression(formatInternalSuppression({
|
|
100444
|
+
turnNonce: record2.turnNonce,
|
|
100445
|
+
turnId: turnIdFromNonce(record2.turnNonce),
|
|
100446
|
+
chatId: record2.chatId,
|
|
100447
|
+
textSha256: record2.textSha256,
|
|
100448
|
+
ageMs,
|
|
100449
|
+
source: record2.source,
|
|
100450
|
+
pastWindow: ageMs > OUTBOX_MAX_AGE_MS
|
|
100451
|
+
}));
|
|
100452
|
+
continue;
|
|
100453
|
+
}
|
|
99886
100454
|
const resolved = resolveOutboxChat(record2, {
|
|
99887
100455
|
registryChainLookup: (anchorContent) => {
|
|
99888
100456
|
const taskId = extractTaskId(anchorContent);
|
|
@@ -99900,7 +100468,8 @@ async function sweepOutbox(deps) {
|
|
|
99900
100468
|
routable: resolved != null,
|
|
99901
100469
|
routePrefix,
|
|
99902
100470
|
quietMs: deps.quietMs ?? OUTBOX_QUIET_MS,
|
|
99903
|
-
shownLedgerHit: isShownBlock(record2.turnNonce, record2.text, deps.stateDir)
|
|
100471
|
+
shownLedgerHit: isShownBlock(record2.turnNonce, record2.text, deps.stateDir),
|
|
100472
|
+
provenanceFraming: provenanceFramingEnabled
|
|
99904
100473
|
});
|
|
99905
100474
|
if (decision.action !== "send" && decision.action !== "send-delayed") {
|
|
99906
100475
|
summary.skipped++;
|
|
@@ -99943,8 +100512,19 @@ async function sweepOutbox(deps) {
|
|
|
99943
100512
|
tgMessageId: sendResult.messageId,
|
|
99944
100513
|
ts: now,
|
|
99945
100514
|
deliverySource: "sweep",
|
|
99946
|
-
replyAlreadyDeliveredThisTurn: record2.replyAlreadyDeliveredThisTurn === true
|
|
100515
|
+
replyAlreadyDeliveredThisTurn: record2.replyAlreadyDeliveredThisTurn === true,
|
|
100516
|
+
...decision.framedProvenance != null ? { framedProvenance: decision.framedProvenance } : {}
|
|
99947
100517
|
}, deps.stateDir);
|
|
100518
|
+
if (decision.framedProvenance != null) {
|
|
100519
|
+
summary.provenanceFramed = (summary.provenanceFramed ?? 0) + 1;
|
|
100520
|
+
logProvenanceFraming(formatReplyThrowFraming({
|
|
100521
|
+
turnNonce: record2.turnNonce,
|
|
100522
|
+
turnId: turnIdFromNonce(record2.turnNonce),
|
|
100523
|
+
chatId: resolvedChat.chatId,
|
|
100524
|
+
textSha256: record2.textSha256,
|
|
100525
|
+
source: record2.source
|
|
100526
|
+
}));
|
|
100527
|
+
}
|
|
99948
100528
|
if (deps.recordOutbound != null && sendResult.chunks.length > 0) {
|
|
99949
100529
|
try {
|
|
99950
100530
|
deps.recordOutbound(resolvedChat.chatId, resolvedChat.threadId, sendResult.chunks.map((c) => c.messageId), sendResult.chunks.map((c) => c.text));
|
|
@@ -99964,6 +100544,9 @@ async function sweepOutbox(deps) {
|
|
|
99964
100544
|
}
|
|
99965
100545
|
return summary;
|
|
99966
100546
|
}
|
|
100547
|
+
function turnIdFromNonce(nonce) {
|
|
100548
|
+
return /^-?\d+:[^#]*#\d+$/.test(nonce) ? nonce : null;
|
|
100549
|
+
}
|
|
99967
100550
|
function probeFloodWindow(probe) {
|
|
99968
100551
|
if (probe == null)
|
|
99969
100552
|
return 0;
|
|
@@ -100004,15 +100587,29 @@ function createSweepBackoff(cfg = {}) {
|
|
|
100004
100587
|
failures: () => failures
|
|
100005
100588
|
};
|
|
100006
100589
|
}
|
|
100007
|
-
|
|
100008
|
-
|
|
100009
|
-
|
|
100010
|
-
|
|
100011
|
-
|
|
100012
|
-
|
|
100590
|
+
function newChunkSendState() {
|
|
100591
|
+
return { parseRejected: false, landed: [] };
|
|
100592
|
+
}
|
|
100593
|
+
async function sendChunkRich(bot, chatId, chunk2, opts, state7 = newChunkSendState()) {
|
|
100594
|
+
if (!state7.parseRejected) {
|
|
100595
|
+
try {
|
|
100596
|
+
const sent = await bot.api.sendRichMessage(chatId, richMessage(chunk2), opts);
|
|
100597
|
+
return [{ message_id: sent?.message_id, text: chunk2 }];
|
|
100598
|
+
} catch (err) {
|
|
100599
|
+
if (!isParseEntitiesError(err))
|
|
100600
|
+
throw err;
|
|
100601
|
+
state7.parseRejected = true;
|
|
100013
100602
|
}
|
|
100014
|
-
throw err;
|
|
100015
100603
|
}
|
|
100604
|
+
const pieces = splitPlainTextToCap(chunk2);
|
|
100605
|
+
const withoutMarkup = { ...opts };
|
|
100606
|
+
delete withoutMarkup.reply_markup;
|
|
100607
|
+
for (let p = state7.landed.length;p < pieces.length; p++) {
|
|
100608
|
+
const isFinalPiece = p === pieces.length - 1;
|
|
100609
|
+
const sent = await bot.api.sendMessage(chatId, pieces[p], isFinalPiece ? opts : withoutMarkup);
|
|
100610
|
+
state7.landed.push({ message_id: sent?.message_id, text: pieces[p] });
|
|
100611
|
+
}
|
|
100612
|
+
return state7.landed;
|
|
100016
100613
|
}
|
|
100017
100614
|
function createOutboxSend(deps) {
|
|
100018
100615
|
return async (chatId, threadId, text4) => {
|
|
@@ -100027,14 +100624,16 @@ function createOutboxSend(deps) {
|
|
|
100027
100624
|
for (let idx = 0;idx < chunks.length; idx++) {
|
|
100028
100625
|
const chunk2 = chunks[idx];
|
|
100029
100626
|
const isLast = idx === chunks.length - 1;
|
|
100627
|
+
const chunkState = newChunkSendState();
|
|
100030
100628
|
const res = await retryWithThreadFallback(deps.retry, (tid) => {
|
|
100031
100629
|
const base = tid != null ? { message_thread_id: tid } : {};
|
|
100032
100630
|
const opts = isLast && replyMarkup != null ? { ...base, reply_markup: replyMarkup } : base;
|
|
100033
|
-
return sendChunkRich(bot, chatId, chunk2, opts);
|
|
100631
|
+
return sendChunkRich(bot, chatId, chunk2, opts, chunkState);
|
|
100034
100632
|
}, { threadId: threadId ?? undefined, chat_id: chatId, verb: "outbox-sweep.sendMessage" });
|
|
100035
|
-
const
|
|
100036
|
-
|
|
100037
|
-
|
|
100633
|
+
for (const sent of res ?? []) {
|
|
100634
|
+
if (sent.message_id != null)
|
|
100635
|
+
landed.push({ messageId: sent.message_id, text: sent.text });
|
|
100636
|
+
}
|
|
100038
100637
|
}
|
|
100039
100638
|
return {
|
|
100040
100639
|
messageId: landed.length > 0 ? landed[landed.length - 1].messageId : undefined,
|
|
@@ -100042,6 +100641,54 @@ function createOutboxSend(deps) {
|
|
|
100042
100641
|
};
|
|
100043
100642
|
};
|
|
100044
100643
|
}
|
|
100644
|
+
function createOutboxSweepTick(deps) {
|
|
100645
|
+
const now = deps.now ?? (() => Date.now());
|
|
100646
|
+
let lastDeferLogAt = 0;
|
|
100647
|
+
let inFlight = false;
|
|
100648
|
+
let overlapSkips = 0;
|
|
100649
|
+
const tick3 = () => {
|
|
100650
|
+
if (deps.getBot() == null)
|
|
100651
|
+
return;
|
|
100652
|
+
if (!deps.backoff.ready(now()))
|
|
100653
|
+
return;
|
|
100654
|
+
if (inFlight) {
|
|
100655
|
+
overlapSkips++;
|
|
100656
|
+
if (overlapSkips === 1) {
|
|
100657
|
+
deps.log?.(`outbox-sweep: tick skipped \u2014 the previous sweep is still in flight ` + `(single-flight guard; no concurrent sweep started)
|
|
100658
|
+
`);
|
|
100659
|
+
}
|
|
100660
|
+
return;
|
|
100661
|
+
}
|
|
100662
|
+
inFlight = true;
|
|
100663
|
+
deps.runSweep().then((summary) => {
|
|
100664
|
+
if (summary.floodDeferred === true) {
|
|
100665
|
+
const at = now();
|
|
100666
|
+
if (at - lastDeferLogAt >= OUTBOX_SWEEP_DEFER_LOG_INTERVAL_MS) {
|
|
100667
|
+
lastDeferLogAt = at;
|
|
100668
|
+
deps.log?.(`outbox-sweep: deferred \u2014 Telegram flood window still open for ` + `${Math.ceil((summary.floodRemainingMs ?? 0) / 1000)}s; not issuing any ` + `send (would feed the ban)
|
|
100669
|
+
`);
|
|
100670
|
+
}
|
|
100671
|
+
return;
|
|
100672
|
+
}
|
|
100673
|
+
if (summary.sendFailures > 0) {
|
|
100674
|
+
const delay = deps.backoff.noteFailure(now());
|
|
100675
|
+
deps.log?.(`outbox-sweep: ${summary.sendFailures} send failure(s) \u2014 backing off ` + `${Math.round(delay / 1000)}s before the next sweep (attempt ${deps.backoff.failures()})
|
|
100676
|
+
`);
|
|
100677
|
+
} else {
|
|
100678
|
+
deps.backoff.noteSuccess();
|
|
100679
|
+
}
|
|
100680
|
+
}).catch((err) => deps.log?.(`outbox-sweep: tick failed: ${err.message}
|
|
100681
|
+
`)).finally(() => {
|
|
100682
|
+
inFlight = false;
|
|
100683
|
+
if (overlapSkips > 0) {
|
|
100684
|
+
deps.log?.(`outbox-sweep: sweep finished; ${overlapSkips} tick(s) were skipped while it ran
|
|
100685
|
+
`);
|
|
100686
|
+
overlapSkips = 0;
|
|
100687
|
+
}
|
|
100688
|
+
});
|
|
100689
|
+
};
|
|
100690
|
+
return Object.assign(tick3, { inFlight: () => inFlight });
|
|
100691
|
+
}
|
|
100045
100692
|
function startOutboxSweep(deps) {
|
|
100046
100693
|
if (!deps.isGatewayMain || process.env.SWITCHROOM_TG_OUTBOX_DELIVERY === "0")
|
|
100047
100694
|
return;
|
|
@@ -100058,15 +100705,11 @@ function startOutboxSweep(deps) {
|
|
|
100058
100705
|
...deps.resolveReplyMarkup != null ? { resolveReplyMarkup: deps.resolveReplyMarkup } : {}
|
|
100059
100706
|
});
|
|
100060
100707
|
const backoff = deps.backoff ?? createSweepBackoff();
|
|
100061
|
-
|
|
100062
|
-
|
|
100063
|
-
|
|
100064
|
-
|
|
100065
|
-
|
|
100066
|
-
const now = Date.now();
|
|
100067
|
-
if (!backoff.ready(now))
|
|
100068
|
-
return;
|
|
100069
|
-
sweepOutbox({
|
|
100708
|
+
const tick3 = createOutboxSweepTick({
|
|
100709
|
+
getBot: deps.getBot,
|
|
100710
|
+
backoff,
|
|
100711
|
+
log: deps.log,
|
|
100712
|
+
runSweep: () => sweepOutbox({
|
|
100070
100713
|
stateDir: deps.stateDir,
|
|
100071
100714
|
log: deps.log,
|
|
100072
100715
|
send,
|
|
@@ -100080,36 +100723,18 @@ function startOutboxSweep(deps) {
|
|
|
100080
100723
|
const turnKey3 = resolveSubagentOriginTurnKey(db3, taskId);
|
|
100081
100724
|
return turnKey3 == null ? null : chatFromTurnKey(turnKey3);
|
|
100082
100725
|
}
|
|
100083
|
-
})
|
|
100084
|
-
|
|
100085
|
-
const at = Date.now();
|
|
100086
|
-
if (at - lastDeferLogAt >= OUTBOX_SWEEP_DEFER_LOG_INTERVAL_MS) {
|
|
100087
|
-
lastDeferLogAt = at;
|
|
100088
|
-
deps.log?.(`outbox-sweep: deferred \u2014 Telegram flood window still open for ` + `${Math.ceil((summary.floodRemainingMs ?? 0) / 1000)}s; not issuing any ` + `send (would feed the ban)
|
|
100089
|
-
`);
|
|
100090
|
-
}
|
|
100091
|
-
return;
|
|
100092
|
-
}
|
|
100093
|
-
if (summary.sendFailures > 0) {
|
|
100094
|
-
const delay = backoff.noteFailure(Date.now());
|
|
100095
|
-
deps.log?.(`outbox-sweep: ${summary.sendFailures} send failure(s) \u2014 backing off ` + `${Math.round(delay / 1000)}s before the next sweep (attempt ${backoff.failures()})
|
|
100096
|
-
`);
|
|
100097
|
-
} else {
|
|
100098
|
-
backoff.noteSuccess();
|
|
100099
|
-
}
|
|
100100
|
-
}).catch((err) => deps.log?.(`outbox-sweep: tick failed: ${err.message}
|
|
100101
|
-
`));
|
|
100102
|
-
};
|
|
100726
|
+
})
|
|
100727
|
+
});
|
|
100103
100728
|
const timer3 = setInterval(tick3, OUTBOX_SWEEP_INTERVAL_MS);
|
|
100104
100729
|
timer3.unref?.();
|
|
100105
100730
|
return timer3;
|
|
100106
100731
|
}
|
|
100107
100732
|
|
|
100108
100733
|
// ../src/build-info.ts
|
|
100109
|
-
var VERSION2 = "0.19.
|
|
100110
|
-
var COMMIT_SHA = "
|
|
100111
|
-
var COMMIT_DATE = "2026-
|
|
100112
|
-
var LATEST_PR =
|
|
100734
|
+
var VERSION2 = "0.19.43";
|
|
100735
|
+
var COMMIT_SHA = "4aaf442f";
|
|
100736
|
+
var COMMIT_DATE = "2026-08-01T10:38:38Z";
|
|
100737
|
+
var LATEST_PR = 4159;
|
|
100113
100738
|
var COMMITS_AHEAD_OF_TAG = 0;
|
|
100114
100739
|
|
|
100115
100740
|
// gateway/boot-version.ts
|
|
@@ -100154,10 +100779,10 @@ function composeBootVersionString(inputs) {
|
|
|
100154
100779
|
}
|
|
100155
100780
|
|
|
100156
100781
|
// gateway/unhandled-rejection-policy.ts
|
|
100157
|
-
var
|
|
100782
|
+
var import_grammy15 = __toESM(require_mod2(), 1);
|
|
100158
100783
|
function classifyRejection(err, opts = {}) {
|
|
100159
|
-
const isGrammy = opts.isGrammyError != null ? opts.isGrammyError(err) : err instanceof
|
|
100160
|
-
const isHttp = opts.isHttpError != null ? opts.isHttpError(err) : err instanceof
|
|
100784
|
+
const isGrammy = opts.isGrammyError != null ? opts.isGrammyError(err) : err instanceof import_grammy15.GrammyError;
|
|
100785
|
+
const isHttp = opts.isHttpError != null ? opts.isHttpError(err) : err instanceof import_grammy15.HttpError;
|
|
100161
100786
|
if (isHttp)
|
|
100162
100787
|
return "log_only";
|
|
100163
100788
|
if (err instanceof Error && err.message === FLOOD_WAIT_ACTIVE)
|
|
@@ -102807,7 +103432,8 @@ var obligationStoreFs = {
|
|
|
102807
103432
|
renameSync: (a, b) => renameSync27(a, b),
|
|
102808
103433
|
existsSync: (p) => existsSync57(p),
|
|
102809
103434
|
fsyncFileSync: fsyncPathSync,
|
|
102810
|
-
fsyncDirSync: fsyncPathSync
|
|
103435
|
+
fsyncDirSync: fsyncPathSync,
|
|
103436
|
+
unlinkSync: unlinkSync31
|
|
102811
103437
|
};
|
|
102812
103438
|
var obligationLedger = new ObligationLedger(OBLIGATION_REPRESENT_MAX, {
|
|
102813
103439
|
onChange: STATIC || !OBLIGATION_LEDGER_ENABLED ? undefined : (snapshot) => persistObligations(OBLIGATION_STORE_PATH, obligationStoreFs, snapshot)
|
|
@@ -102819,7 +103445,8 @@ if (isGatewayMain && !STATIC && OBLIGATION_LEDGER_ENABLED) {
|
|
|
102819
103445
|
process.stderr.write(`telegram gateway: obligation-ledger hydrated ${restored.length} open obligation(s) from ${OBLIGATION_STORE_PATH}
|
|
102820
103446
|
`);
|
|
102821
103447
|
}
|
|
102822
|
-
}
|
|
103448
|
+
} else if (isGatewayMain)
|
|
103449
|
+
removeUnmaintainedSnapshot(OBLIGATION_STORE_PATH, obligationStoreFs, `static=${STATIC} enabled=${OBLIGATION_LEDGER_ENABLED}`);
|
|
102823
103450
|
var obligationEscalateInFlight = new Set;
|
|
102824
103451
|
var capturedResume = createCapturedResumeDispatcher({
|
|
102825
103452
|
deliverAnswer: (a) => deliverAnswer(a),
|
|
@@ -104068,7 +104695,7 @@ function wrapBootCardApi(threadId) {
|
|
|
104068
104695
|
await lockedBot.api.editMessageText(cid, mid, richMessage2(text5), editOpts);
|
|
104069
104696
|
return "edited";
|
|
104070
104697
|
} catch (err) {
|
|
104071
|
-
const desc = err instanceof
|
|
104698
|
+
const desc = err instanceof import_grammy17.GrammyError ? err.description : err instanceof Error ? err.message : String(err);
|
|
104072
104699
|
if (typeof desc === "string" && desc.toLowerCase().includes("not modified"))
|
|
104073
104700
|
return "edited";
|
|
104074
104701
|
return "gone";
|
|
@@ -105649,6 +106276,7 @@ function gatewayLivenessWiringDeps() {
|
|
|
105649
106276
|
endCurrentTurnForKey,
|
|
105650
106277
|
getPendingInboundBuffer: () => pendingInboundBuffer,
|
|
105651
106278
|
trackRedeliveredInbound,
|
|
106279
|
+
drainParkedTurnStarts: (chatId, threadId) => drainParkedTurnStartsForChat(gatewayStreamRenderDeps(), chatId, threadId != null ? String(threadId) : null).length,
|
|
105652
106280
|
closeActivityLane,
|
|
105653
106281
|
closeProgressLane,
|
|
105654
106282
|
hangRestart: {
|
|
@@ -105906,7 +106534,7 @@ if (isGatewayMain && inboundSpool != null) {
|
|
|
105906
106534
|
}
|
|
105907
106535
|
var pendingPermissionBuffer = createPendingPermissionBuffer();
|
|
105908
106536
|
function buildPermissionActionRow(requestId, showAlways) {
|
|
105909
|
-
const kb = new
|
|
106537
|
+
const kb = new import_grammy17.InlineKeyboard().text("\u274C Deny", `perm:deny:${requestId}`).text("\u2705 Allow", `perm:allow:${requestId}`);
|
|
105910
106538
|
if (showAlways)
|
|
105911
106539
|
kb.text("\uD83D\uDD01 Always\u2026", `perm:always:${requestId}`);
|
|
105912
106540
|
return kb;
|
|
@@ -106466,7 +107094,7 @@ if (isGatewayMain)
|
|
|
106466
107094
|
}
|
|
106467
107095
|
},
|
|
106468
107096
|
postAttachment: async (args) => {
|
|
106469
|
-
const input = new
|
|
107097
|
+
const input = new import_grammy17.InputFile(Buffer.from(args.content, "utf8"), args.filename);
|
|
106470
107098
|
await robustApiCall(() => bot.api.sendDocument(args.chatId, input, {
|
|
106471
107099
|
...args.threadId !== undefined ? { message_thread_id: args.threadId } : {}
|
|
106472
107100
|
}), {
|
|
@@ -106551,20 +107179,19 @@ if (isGatewayMain)
|
|
|
106551
107179
|
} catch {}
|
|
106552
107180
|
}
|
|
106553
107181
|
},
|
|
106554
|
-
onRolloutStatusEdit(
|
|
106555
|
-
|
|
106556
|
-
|
|
106557
|
-
|
|
106558
|
-
|
|
106559
|
-
|
|
106560
|
-
|
|
106561
|
-
|
|
106562
|
-
|
|
106563
|
-
|
|
106564
|
-
`
|
|
106565
|
-
|
|
106566
|
-
}
|
|
106567
|
-
swallowingApiCall(() => bot.api.editMessageText(operator, msg.messageId, richMessage2(msg.text), {}), { chat_id: String(operator), verb: "rollout-status-edit" });
|
|
107182
|
+
onRolloutStatusEdit(client3, msg) {
|
|
107183
|
+
return handleRolloutStatusEdit({
|
|
107184
|
+
selfAgentName: process.env.SWITCHROOM_AGENT_NAME,
|
|
107185
|
+
operatorChatId: () => loadAccess().allowFrom[0],
|
|
107186
|
+
editMessage: (chatId, messageId, text5) => robustApiCall(() => bot.api.editMessageText(chatId, messageId, richMessage2(text5), {}), {
|
|
107187
|
+
chat_id: String(chatId),
|
|
107188
|
+
verb: "rollout-status-edit",
|
|
107189
|
+
priorityClass: "critical",
|
|
107190
|
+
rethrowBenign400: true
|
|
107191
|
+
}),
|
|
107192
|
+
log: (m) => process.stderr.write(`telegram gateway: ${m}
|
|
107193
|
+
`)
|
|
107194
|
+
}, client3, msg);
|
|
106568
107195
|
},
|
|
106569
107196
|
onInjectInbound(_client, msg) {
|
|
106570
107197
|
if (!isCronInjectFire(msg.inbound.meta))
|
|
@@ -107264,7 +107891,7 @@ async function executeAskUser(rawArgs) {
|
|
|
107264
107891
|
}
|
|
107265
107892
|
}
|
|
107266
107893
|
const askId = generateAskId();
|
|
107267
|
-
const keyboard = new
|
|
107894
|
+
const keyboard = new import_grammy17.InlineKeyboard;
|
|
107268
107895
|
for (let i = 0;i < args.options.length; i++) {
|
|
107269
107896
|
keyboard.text(args.options[i], encodeAskCallback(askId, i));
|
|
107270
107897
|
if (i < args.options.length - 1)
|
|
@@ -107912,7 +108539,6 @@ if (isGatewayMain && !STATIC && FEED_HEARTBEAT_ENABLED) {
|
|
|
107912
108539
|
}
|
|
107913
108540
|
var HANDBACK_PRETURN_ENABLED = !STATIC && process.env.SWITCHROOM_HANDBACK_PRETURN !== "0";
|
|
107914
108541
|
var HANDBACK_PRETURN_HTML = "\uD83E\uDD1D Reading the worker\u2019s results\u2026";
|
|
107915
|
-
var HANDBACK_PRETURN_ORPHAN_HTML = "\uD83E\uDD1D A background worker finished, but the handback never started \u2014 it may need a nudge.";
|
|
107916
108542
|
async function openHandbackPreTurnCard(chatId, threadId) {
|
|
107917
108543
|
if (STATIC)
|
|
107918
108544
|
return null;
|
|
@@ -107932,24 +108558,23 @@ async function openHandbackPreTurnCard(chatId, threadId) {
|
|
|
107932
108558
|
return null;
|
|
107933
108559
|
}
|
|
107934
108560
|
}
|
|
107935
|
-
|
|
107936
|
-
|
|
107937
|
-
chat_id:
|
|
107938
|
-
...
|
|
107939
|
-
verb: "handback-preturn.orphan-
|
|
107940
|
-
})
|
|
107941
|
-
|
|
107942
|
-
|
|
107943
|
-
return;
|
|
107944
|
-
});
|
|
107945
|
-
}
|
|
108561
|
+
var handbackOrphanRecovery = createHandbackOrphanRecovery({
|
|
108562
|
+
deleteMessage: (chatId, messageId, threadId) => robustApiCall(() => bot.api.deleteMessage(chatId, messageId), {
|
|
108563
|
+
chat_id: chatId,
|
|
108564
|
+
...threadId != null ? { threadId } : {},
|
|
108565
|
+
verb: "handback-preturn.orphan-delete"
|
|
108566
|
+
}),
|
|
108567
|
+
pushInbound: (inbound) => pendingInboundBuffer.push(process.env.SWITCHROOM_AGENT_NAME ?? "", inbound)
|
|
108568
|
+
});
|
|
107946
108569
|
var handbackPreturnSignal = createHandbackPreturnSignal({
|
|
107947
108570
|
chatKey: (chatId, threadId) => chatKey2(chatId, threadId),
|
|
107948
108571
|
deriveTurnId: (chatId, threadId, messageId) => deriveTurnId2(chatId, threadId, messageId),
|
|
107949
108572
|
startTypingLoop: (chatId, threadId) => startTurnTypingLoop(chatId, threadId),
|
|
107950
108573
|
stopTypingLoop: (chatId, threadId) => stopTurnTypingLoop(chatId, threadId),
|
|
107951
108574
|
openCard: openHandbackPreTurnCard,
|
|
107952
|
-
|
|
108575
|
+
deleteCard: handbackOrphanRecovery.deleteCard,
|
|
108576
|
+
reinjectHandback: handbackOrphanRecovery.reinjectHandback,
|
|
108577
|
+
escalateOrphan: handbackOrphanRecovery.escalateOrphan,
|
|
107953
108578
|
writeCardRecord: (record2) => {
|
|
107954
108579
|
if (!activityCardPersistEnabled)
|
|
107955
108580
|
return;
|
|
@@ -109718,7 +110343,7 @@ function buildModelDeps(restartCtx) {
|
|
|
109718
110343
|
function modelMenuReplyMarkup(reply) {
|
|
109719
110344
|
if (!reply.keyboard)
|
|
109720
110345
|
return;
|
|
109721
|
-
const kb = new
|
|
110346
|
+
const kb = new import_grammy17.InlineKeyboard;
|
|
109722
110347
|
for (const row of reply.keyboard) {
|
|
109723
110348
|
for (const btn of row)
|
|
109724
110349
|
kb.text(btn.text, btn.callback_data);
|
|
@@ -109784,7 +110409,7 @@ function persistQueuedCommandForRestart(action) {
|
|
|
109784
110409
|
return `\u21A9\uFE0F Couldn\u2019t verify \`${escapeHtmlForTg2(action.cmd.targetLabel || action.arg)}\` as a known model without the live session \u2014 it was NOT saved. Re-issue \`/model ${escapeHtmlForTg2(action.arg)}\` once the agent is back.`;
|
|
109785
110410
|
}
|
|
109786
110411
|
const configured = readConfiguredDefaultModel(agentDir) ?? resolveMainModel(undefined);
|
|
109787
|
-
writeSessionModelFile(agentDir,
|
|
110412
|
+
writeSessionModelFile(agentDir, expandModelAlias(action.arg), configured);
|
|
109788
110413
|
break;
|
|
109789
110414
|
}
|
|
109790
110415
|
case "clear-model":
|
|
@@ -109858,7 +110483,7 @@ function buildEffortDeps() {
|
|
|
109858
110483
|
function effortMenuReplyMarkup(reply) {
|
|
109859
110484
|
if (!reply.keyboard)
|
|
109860
110485
|
return;
|
|
109861
|
-
const kb = new
|
|
110486
|
+
const kb = new import_grammy17.InlineKeyboard;
|
|
109862
110487
|
for (const row of reply.keyboard) {
|
|
109863
110488
|
for (const btn of row)
|
|
109864
110489
|
kb.text(btn.text, btn.callback_data);
|
|
@@ -111511,7 +112136,7 @@ _Google accounts are connected from the host CLI._`, { html: true });
|
|
|
111511
112136
|
return;
|
|
111512
112137
|
}
|
|
111513
112138
|
const appNote = started.source === "default" ? "_Using switchroom's shipped Microsoft app._" : "_Using your configured Microsoft app._";
|
|
111514
|
-
const keyboard = new
|
|
112139
|
+
const keyboard = new import_grammy17.InlineKeyboard().url("\uD83D\uDD10 Sign in to Microsoft", started.device.verification_uri).row().text("\u2716 Cancel", `cn:cancel:${key}`);
|
|
111515
112140
|
const sent = await ctx.replyWithRichMessage(richMessage2(`\uD83D\uDD17 **Connect a Microsoft account**
|
|
111516
112141
|
|
|
111517
112142
|
` + `1. Tap **Sign in to Microsoft** below.
|
|
@@ -111647,7 +112272,7 @@ Send \`/auth ${parsed.provider} cancel\` to abort.`, { html: true });
|
|
|
111647
112272
|
tz: process.env.SWITCHROOM_TIMEZONE ?? process.env.TZ
|
|
111648
112273
|
});
|
|
111649
112274
|
if (reply.keyboard && reply.keyboard.length > 0) {
|
|
111650
|
-
const kb = new
|
|
112275
|
+
const kb = new import_grammy17.InlineKeyboard;
|
|
111651
112276
|
for (let i = 0;i < reply.keyboard.length; i++) {
|
|
111652
112277
|
const row = reply.keyboard[i];
|
|
111653
112278
|
for (const b of row) {
|
|
@@ -111789,7 +112414,7 @@ Send \`/auth ${parsed.provider} cancel\` to abort.`, { html: true });
|
|
|
111789
112414
|
}
|
|
111790
112415
|
lines.push("");
|
|
111791
112416
|
const denials = readRecentDenialsForAgent(targetAgent, 604800000, 5);
|
|
111792
|
-
const denialKeyboard = new
|
|
112417
|
+
const denialKeyboard = new import_grammy17.InlineKeyboard;
|
|
111793
112418
|
if (denials.length > 0) {
|
|
111794
112419
|
lines.push("**Recent denials (last 7d):**");
|
|
111795
112420
|
for (const d of denials) {
|
|
@@ -111837,7 +112462,7 @@ Send \`/auth ${parsed.provider} cancel\` to abort.`, { html: true });
|
|
|
111837
112462
|
byAgent.set(g.agent_slug, list3);
|
|
111838
112463
|
}
|
|
111839
112464
|
const lines = ["**\uD83D\uDCDC Active grants**", ""];
|
|
111840
|
-
const keyboard = new
|
|
112465
|
+
const keyboard = new import_grammy17.InlineKeyboard;
|
|
111841
112466
|
for (const [agentName3, agentGrants] of byAgent) {
|
|
111842
112467
|
lines.push(`**${escapeHtmlForTg2(agentName3)}:**`);
|
|
111843
112468
|
for (const g of agentGrants) {
|
|
@@ -112034,7 +112659,7 @@ Send \`/auth ${parsed.provider} cancel\` to abort.`, { html: true });
|
|
|
112034
112659
|
if (ctx.chat?.type !== "private") {
|
|
112035
112660
|
kbRows = kbRows.filter((row) => !row.some((b) => b.callbackData?.startsWith("auth:use:")));
|
|
112036
112661
|
}
|
|
112037
|
-
const keyboard = new
|
|
112662
|
+
const keyboard = new import_grammy17.InlineKeyboard;
|
|
112038
112663
|
kbRows.forEach((row, ri) => {
|
|
112039
112664
|
if (ri > 0)
|
|
112040
112665
|
keyboard.row();
|
|
@@ -112386,7 +113011,7 @@ ${preBlock(formatSwitchroomOutput(err.message ?? "unknown error"))}`, { html: tr
|
|
|
112386
113011
|
await ctx.answerCallbackQuery({ text: "No always-allow rule for this tool." }).catch(() => {});
|
|
112387
113012
|
return;
|
|
112388
113013
|
}
|
|
112389
|
-
keyboard = new
|
|
113014
|
+
keyboard = new import_grammy17.InlineKeyboard().text("\u2190 Back", `perm:back:${request_id}`);
|
|
112390
113015
|
if (choices.specific)
|
|
112391
113016
|
keyboard.text(choices.specific.buttonLabel, `perm:asn:${request_id}`);
|
|
112392
113017
|
keyboard.text(`${choices.broad.buttonLabel} \u26A0\uFE0F`, `perm:asb:${request_id}`);
|
|
@@ -112689,7 +113314,7 @@ ${labelWithResume}` : labelWithResume,
|
|
|
112689
113314
|
verb: "voice-ondemand.sendVoice",
|
|
112690
113315
|
...threadId != null ? { threadId } : {}
|
|
112691
113316
|
}),
|
|
112692
|
-
sendVoiceByUpload: (chatId, audio, sendOpts, threadId) => robustApiCall(() => bot2.api.sendVoice(chatId, new
|
|
113317
|
+
sendVoiceByUpload: (chatId, audio, sendOpts, threadId) => robustApiCall(() => bot2.api.sendVoice(chatId, new import_grammy17.InputFile(Buffer.from(audio)), sendOpts), {
|
|
112693
113318
|
chat_id: chatId,
|
|
112694
113319
|
verb: "voice-ondemand.sendVoice",
|
|
112695
113320
|
...threadId != null ? { threadId } : {}
|
|
@@ -112955,7 +113580,7 @@ async function initGatewayBot() {
|
|
|
112955
113580
|
`);
|
|
112956
113581
|
process.exit(1);
|
|
112957
113582
|
}
|
|
112958
|
-
bot = new
|
|
113583
|
+
bot = new import_grammy17.Bot(TOKEN);
|
|
112959
113584
|
installTgPostLogger(bot);
|
|
112960
113585
|
installRichMarkdownGuard(bot);
|
|
112961
113586
|
installEditFloodFuse(bot, {
|
|
@@ -113927,7 +114552,7 @@ async function startGateway() {
|
|
|
113927
114552
|
await runnerHandle.task();
|
|
113928
114553
|
return;
|
|
113929
114554
|
} catch (err) {
|
|
113930
|
-
if (err instanceof
|
|
114555
|
+
if (err instanceof import_grammy17.GrammyError && err.error_code === 409) {
|
|
113931
114556
|
const delay = Math.min(1000 * attempt, 15000);
|
|
113932
114557
|
const agentName3 = process.env.SWITCHROOM_AGENT_NAME ?? "-";
|
|
113933
114558
|
process.stderr.write(`telegram gateway: poll.409.detected attempt=${attempt} retry_in_ms=${delay} agent=${agentName3}
|