switchroom 0.19.42 → 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 +1 -1
- package/dist/auth-broker/index.js +32 -7
- package/dist/cli/notion-write-pretool.mjs +1 -1
- package/dist/cli/switchroom.js +1187 -669
- package/dist/host-control/main.js +205 -28
- package/dist/vault/approvals/kernel-server.js +32 -7
- package/dist/vault/broker/server.js +32 -7
- 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 +1225 -614
- 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/boot-card.ts +7 -2
- package/telegram-plugin/gateway/flood-reply-queue.ts +5 -0
- package/telegram-plugin/gateway/gateway.ts +49 -51
- 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/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 +5 -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/handback-orphan-recovery.test.ts +131 -0
- package/telegram-plugin/tests/handback-preturn-signal.test.ts +212 -17
- 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
|
@@ -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")
|
|
@@ -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
|
}
|
|
@@ -22165,7 +21969,7 @@ var init_schema = __esm(() => {
|
|
|
22165
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."),
|
|
22166
21970
|
consolidation: HindsightPerOpLlmSchema.optional().describe("Per-op override for the `consolidation` LLM op (background memory " + "merge). Emits `HINDSIGHT_API_CONSOLIDATION_LLM_*`. Absent \u2192 global.")
|
|
22167
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."),
|
|
22168
|
-
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).")
|
|
22169
21973
|
});
|
|
22170
21974
|
MicrosoftWorkspaceConfigSchema = exports_external.object({
|
|
22171
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)."),
|
|
@@ -22698,7 +22502,7 @@ var init_schema = __esm(() => {
|
|
|
22698
22502
|
});
|
|
22699
22503
|
|
|
22700
22504
|
// ../src/config/paths.ts
|
|
22701
|
-
import { existsSync as
|
|
22505
|
+
import { existsSync as existsSync3 } from "node:fs";
|
|
22702
22506
|
import { resolve as resolve3 } from "node:path";
|
|
22703
22507
|
function home() {
|
|
22704
22508
|
return process.env.HOME ?? "/root";
|
|
@@ -22714,7 +22518,7 @@ function resolveStatePath(fragment) {
|
|
|
22714
22518
|
const h = home();
|
|
22715
22519
|
const primary = resolve3(h, DEFAULT_STATE_DIR, fragment);
|
|
22716
22520
|
const legacy = resolve3(h, LEGACY_STATE_DIR, fragment);
|
|
22717
|
-
if (!
|
|
22521
|
+
if (!existsSync3(primary) && existsSync3(legacy)) {
|
|
22718
22522
|
warnLegacyStateOnce(legacy);
|
|
22719
22523
|
return legacy;
|
|
22720
22524
|
}
|
|
@@ -22727,9 +22531,9 @@ function resolveDualPath(pathStr) {
|
|
|
22727
22531
|
const absolute = resolve3(h, rest);
|
|
22728
22532
|
if (rest.startsWith(`${DEFAULT_STATE_DIR}/`)) {
|
|
22729
22533
|
const frag = rest.slice(DEFAULT_STATE_DIR.length + 1);
|
|
22730
|
-
if (!
|
|
22534
|
+
if (!existsSync3(absolute)) {
|
|
22731
22535
|
const legacy = resolve3(h, LEGACY_STATE_DIR, frag);
|
|
22732
|
-
if (
|
|
22536
|
+
if (existsSync3(legacy)) {
|
|
22733
22537
|
warnLegacyStateOnce(legacy);
|
|
22734
22538
|
return legacy;
|
|
22735
22539
|
}
|
|
@@ -22754,7 +22558,7 @@ var init_overlay_schema = __esm(() => {
|
|
|
22754
22558
|
});
|
|
22755
22559
|
|
|
22756
22560
|
// ../src/config/overlay-loader.ts
|
|
22757
|
-
import { existsSync as
|
|
22561
|
+
import { existsSync as existsSync4, readFileSync as readFileSync4, readdirSync as readdirSync3, statSync as statSync5 } from "node:fs";
|
|
22758
22562
|
import { basename as basename4, resolve as resolve4 } from "node:path";
|
|
22759
22563
|
function deriveOverlayTitle(raw, fileName) {
|
|
22760
22564
|
const titleFromComment = raw.match(/^#[^\S\n]*name:[^\S\n]*(\S.*?)[^\S\n]*$/m)?.[1];
|
|
@@ -22770,7 +22574,7 @@ function overlayDirFor(agentName, subdir) {
|
|
|
22770
22574
|
return resolve4(base);
|
|
22771
22575
|
}
|
|
22772
22576
|
function listYamlFiles(dir) {
|
|
22773
|
-
if (!
|
|
22577
|
+
if (!existsSync4(dir))
|
|
22774
22578
|
return [];
|
|
22775
22579
|
let entries;
|
|
22776
22580
|
try {
|
|
@@ -22818,7 +22622,7 @@ function applyAgentOverlays(config) {
|
|
|
22818
22622
|
const merged = [...agentCfg.schedule ?? []];
|
|
22819
22623
|
for (const file of files) {
|
|
22820
22624
|
try {
|
|
22821
|
-
const raw =
|
|
22625
|
+
const raw = readFileSync4(file, "utf-8");
|
|
22822
22626
|
const parsed = import_yaml.parse(raw);
|
|
22823
22627
|
const doc = OverlayDocSchema.parse(parsed);
|
|
22824
22628
|
const title = deriveOverlayTitle(raw, basename4(file));
|
|
@@ -22859,7 +22663,7 @@ function applyAgentOverlays(config) {
|
|
|
22859
22663
|
const seen = new Set(merged);
|
|
22860
22664
|
for (const file of skillFiles) {
|
|
22861
22665
|
try {
|
|
22862
|
-
const raw =
|
|
22666
|
+
const raw = readFileSync4(file, "utf-8");
|
|
22863
22667
|
const parsed = import_yaml.parse(raw);
|
|
22864
22668
|
const doc = OverlayDocSchema.parse(parsed);
|
|
22865
22669
|
for (const skillName of doc.skills ?? []) {
|
|
@@ -23329,7 +23133,7 @@ var init_timezone = __esm(() => {
|
|
|
23329
23133
|
});
|
|
23330
23134
|
|
|
23331
23135
|
// ../src/config/loader.ts
|
|
23332
|
-
import { readFileSync as
|
|
23136
|
+
import { readFileSync as readFileSync5, existsSync as existsSync5 } from "node:fs";
|
|
23333
23137
|
import { homedir as homedir4 } from "node:os";
|
|
23334
23138
|
import { resolve as resolve5 } from "node:path";
|
|
23335
23139
|
function formatZodErrors(error) {
|
|
@@ -23396,7 +23200,7 @@ function findConfigFile(startDir) {
|
|
|
23396
23200
|
resolve5(userDir, "clerk.yml")
|
|
23397
23201
|
].filter(Boolean);
|
|
23398
23202
|
for (const path of searchPaths) {
|
|
23399
|
-
if (
|
|
23203
|
+
if (existsSync5(path)) {
|
|
23400
23204
|
return path;
|
|
23401
23205
|
}
|
|
23402
23206
|
}
|
|
@@ -23404,12 +23208,12 @@ function findConfigFile(startDir) {
|
|
|
23404
23208
|
}
|
|
23405
23209
|
function loadConfig(configPath) {
|
|
23406
23210
|
const filePath = configPath ?? findConfigFile();
|
|
23407
|
-
if (!
|
|
23211
|
+
if (!existsSync5(filePath)) {
|
|
23408
23212
|
throw new ConfigError(`Config file not found: ${filePath}`);
|
|
23409
23213
|
}
|
|
23410
23214
|
let raw;
|
|
23411
23215
|
try {
|
|
23412
|
-
raw =
|
|
23216
|
+
raw = readFileSync5(filePath, "utf-8");
|
|
23413
23217
|
} catch (err) {
|
|
23414
23218
|
throw new ConfigError(`Failed to read config file: ${filePath}`, [
|
|
23415
23219
|
` ${err.message}`
|
|
@@ -23523,12 +23327,12 @@ var init_flock = () => {};
|
|
|
23523
23327
|
// ../src/vault/vault.ts
|
|
23524
23328
|
import { randomBytes as randomBytes4, scryptSync, createCipheriv, createDecipheriv } from "node:crypto";
|
|
23525
23329
|
import {
|
|
23526
|
-
readFileSync as
|
|
23330
|
+
readFileSync as readFileSync6,
|
|
23527
23331
|
writeSync as writeSync2,
|
|
23528
|
-
existsSync as
|
|
23529
|
-
renameSync as
|
|
23530
|
-
mkdirSync as
|
|
23531
|
-
unlinkSync as
|
|
23332
|
+
existsSync as existsSync6,
|
|
23333
|
+
renameSync as renameSync6,
|
|
23334
|
+
mkdirSync as mkdirSync7,
|
|
23335
|
+
unlinkSync as unlinkSync3,
|
|
23532
23336
|
fsyncSync as fsyncSync2,
|
|
23533
23337
|
openSync as openSync2,
|
|
23534
23338
|
closeSync as closeSync2,
|
|
@@ -23566,12 +23370,12 @@ function normalizeSecrets(raw) {
|
|
|
23566
23370
|
return out;
|
|
23567
23371
|
}
|
|
23568
23372
|
function openVault(passphrase, vaultPath) {
|
|
23569
|
-
if (!
|
|
23373
|
+
if (!existsSync6(vaultPath)) {
|
|
23570
23374
|
throw new VaultError(`Vault file not found: ${vaultPath}`);
|
|
23571
23375
|
}
|
|
23572
23376
|
let vaultFile;
|
|
23573
23377
|
try {
|
|
23574
|
-
vaultFile = JSON.parse(
|
|
23378
|
+
vaultFile = JSON.parse(readFileSync6(vaultPath, "utf8"));
|
|
23575
23379
|
} catch {
|
|
23576
23380
|
throw new VaultError(`Failed to read vault file: ${vaultPath}`);
|
|
23577
23381
|
}
|
|
@@ -23616,16 +23420,16 @@ var init_vault = __esm(() => {
|
|
|
23616
23420
|
|
|
23617
23421
|
// ../src/vault/resolver.ts
|
|
23618
23422
|
import {
|
|
23619
|
-
chmodSync as
|
|
23423
|
+
chmodSync as chmodSync2,
|
|
23620
23424
|
closeSync as closeSync3,
|
|
23621
|
-
mkdirSync as
|
|
23425
|
+
mkdirSync as mkdirSync8,
|
|
23622
23426
|
mkdtempSync,
|
|
23623
23427
|
openSync as openSync3,
|
|
23624
23428
|
rmSync as rmSync2,
|
|
23625
23429
|
statSync as statSync6,
|
|
23626
23430
|
writeSync as writeSync3
|
|
23627
23431
|
} from "node:fs";
|
|
23628
|
-
import { join as
|
|
23432
|
+
import { join as join6 } from "node:path";
|
|
23629
23433
|
import { tmpdir } from "node:os";
|
|
23630
23434
|
import { constants as fsConstants } from "node:fs";
|
|
23631
23435
|
function isVaultReference(value) {
|
|
@@ -23677,13 +23481,13 @@ function materializationRoot() {
|
|
|
23677
23481
|
return cachedRoot;
|
|
23678
23482
|
const xdg = process.env.XDG_RUNTIME_DIR;
|
|
23679
23483
|
if (xdg) {
|
|
23680
|
-
const base =
|
|
23681
|
-
|
|
23682
|
-
cachedRoot = mkdtempSync(
|
|
23484
|
+
const base = join6(xdg, "switchroom", "vault");
|
|
23485
|
+
mkdirSync8(base, { recursive: true, mode: 448 });
|
|
23486
|
+
cachedRoot = mkdtempSync(join6(base, "run-"));
|
|
23683
23487
|
} else {
|
|
23684
|
-
cachedRoot = mkdtempSync(
|
|
23488
|
+
cachedRoot = mkdtempSync(join6(tmpdir(), "switchroom-vault-"));
|
|
23685
23489
|
}
|
|
23686
|
-
|
|
23490
|
+
chmodSync2(cachedRoot, 448);
|
|
23687
23491
|
return cachedRoot;
|
|
23688
23492
|
}
|
|
23689
23493
|
function writeFileExclusive(filePath, content) {
|
|
@@ -23696,14 +23500,14 @@ function writeFileExclusive(filePath, content) {
|
|
|
23696
23500
|
}
|
|
23697
23501
|
}
|
|
23698
23502
|
function materializeFilesEntry(key, files) {
|
|
23699
|
-
const dir =
|
|
23503
|
+
const dir = join6(materializationRoot(), key);
|
|
23700
23504
|
if (materializedDirs.has(dir)) {
|
|
23701
23505
|
try {
|
|
23702
23506
|
rmSync2(dir, { recursive: true, force: true });
|
|
23703
23507
|
} catch {}
|
|
23704
23508
|
}
|
|
23705
|
-
|
|
23706
|
-
|
|
23509
|
+
mkdirSync8(dir, { recursive: true, mode: 448 });
|
|
23510
|
+
chmodSync2(dir, 448);
|
|
23707
23511
|
const st = statSync6(dir);
|
|
23708
23512
|
if (typeof process.getuid === "function" && st.uid !== process.getuid()) {
|
|
23709
23513
|
throw new Error(`Refusing to materialize vault entry: ${dir} not owned by caller`);
|
|
@@ -23712,7 +23516,7 @@ function materializeFilesEntry(key, files) {
|
|
|
23712
23516
|
if (filename.includes("/") || filename.includes("\\") || filename === ".." || filename === "." || filename.includes("\x00")) {
|
|
23713
23517
|
throw new Error(`Refusing to materialize vault file with unsafe name: ${filename}`);
|
|
23714
23518
|
}
|
|
23715
|
-
const filePath =
|
|
23519
|
+
const filePath = join6(dir, filename);
|
|
23716
23520
|
const content = encoding === "base64" ? Buffer.from(value, "base64") : value;
|
|
23717
23521
|
writeFileExclusive(filePath, content);
|
|
23718
23522
|
}
|
|
@@ -23839,9 +23643,16 @@ var init_resolver = __esm(() => {
|
|
|
23839
23643
|
materializedDirs = new Set;
|
|
23840
23644
|
});
|
|
23841
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
|
+
|
|
23842
23653
|
// ../src/util/atomic.ts
|
|
23843
23654
|
import { randomBytes as randomBytes5 } from "node:crypto";
|
|
23844
|
-
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";
|
|
23845
23656
|
function atomicWriteFileSync2(destPath, contents, modeOrOpts = 384) {
|
|
23846
23657
|
const opts = typeof modeOrOpts === "number" ? { mode: modeOrOpts } : modeOrOpts;
|
|
23847
23658
|
const mode = opts.mode ?? 384;
|
|
@@ -23865,7 +23676,7 @@ function atomicWriteFileSync2(destPath, contents, modeOrOpts = 384) {
|
|
|
23865
23676
|
fsyncSync3(fd);
|
|
23866
23677
|
closeSync4(fd);
|
|
23867
23678
|
fd = null;
|
|
23868
|
-
|
|
23679
|
+
renameSync7(tmp, destPath);
|
|
23869
23680
|
} catch (err) {
|
|
23870
23681
|
if (fd !== null) {
|
|
23871
23682
|
try {
|
|
@@ -23931,6 +23742,276 @@ var init_approval_card = __esm(() => {
|
|
|
23931
23742
|
import_grammy5 = __toESM(require_mod2(), 1);
|
|
23932
23743
|
});
|
|
23933
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
|
+
|
|
23934
24015
|
// quota-check.ts
|
|
23935
24016
|
import { readFileSync as readFileSync19, existsSync as existsSync17 } from "fs";
|
|
23936
24017
|
import { join as join25 } from "path";
|
|
@@ -37216,7 +37297,7 @@ function renderBootCard(opts) {
|
|
|
37216
37297
|
const ageStr = restartAgeMs != null && restartAgeMs > 0 ? ` \u00b7 ${(restartAgeMs / 1000).toFixed(1)}s ago` : "";
|
|
37217
37298
|
degradedRows.push(`\u26a0\ufe0f **Restart** ${escapeMarkdown(REASON_LABEL.crash)}${ageStr}`);
|
|
37218
37299
|
const tailCmd = process.env.SWITCHROOM_RUNTIME === "docker" ? `docker logs --tail 100 switchroom-${agentSlug}` : `journalctl --user -u switchroom-${agentSlug} -n 100`;
|
|
37219
|
-
degradedRows.push(
|
|
37300
|
+
degradedRows.push(`${NESTED_PREFIX}Tail logs: \`${tailCmd}\``);
|
|
37220
37301
|
}
|
|
37221
37302
|
if (probes) {
|
|
37222
37303
|
for (const key of PROBE_KEYS) {
|
|
@@ -37230,7 +37311,7 @@ function renderBootCard(opts) {
|
|
|
37230
37311
|
const dot = DOT[r.status] ?? DOT.fail;
|
|
37231
37312
|
degradedRows.push(`${dot} **${PROBE_LABELS[key]}** ${escapeMarkdown(r.detail)}`);
|
|
37232
37313
|
if (r.nextStep) {
|
|
37233
|
-
degradedRows.push(
|
|
37314
|
+
degradedRows.push(`${NESTED_PREFIX}${renderNextStep(r.nextStep)}`);
|
|
37234
37315
|
}
|
|
37235
37316
|
}
|
|
37236
37317
|
}
|
|
@@ -37534,6 +37615,7 @@ var SETTLE_WINDOW_MS = 6000, DOT, PROBE_LABELS, PROBE_KEYS, REASON_EMOJI, REASON
|
|
|
37534
37615
|
var init_boot_card = __esm(() => {
|
|
37535
37616
|
init_boot_probes();
|
|
37536
37617
|
init_card_format();
|
|
37618
|
+
init_status_no_truncate();
|
|
37537
37619
|
init_boot_issue_cache();
|
|
37538
37620
|
init_config_snapshot();
|
|
37539
37621
|
init_boot_card_msgid();
|
|
@@ -38999,7 +39081,7 @@ function buildGrantedKeyboard(scope) {
|
|
|
38999
39081
|
const btn = scopeToOpenInDriveButton(scope);
|
|
39000
39082
|
if (btn === null)
|
|
39001
39083
|
return;
|
|
39002
|
-
return new
|
|
39084
|
+
return new import_grammy16.InlineKeyboard().url(btn.text, btn.url);
|
|
39003
39085
|
}
|
|
39004
39086
|
async function handleApprovalCallback(ctx, data) {
|
|
39005
39087
|
const parsed = parseApprovalCallback(data);
|
|
@@ -39045,11 +39127,11 @@ async function handleApprovalCallback(ctx, data) {
|
|
|
39045
39127
|
} catch {}
|
|
39046
39128
|
await ctx.answerCallbackQuery({ text: granted ? "Approved" : "Denied" });
|
|
39047
39129
|
}
|
|
39048
|
-
var
|
|
39130
|
+
var import_grammy16;
|
|
39049
39131
|
var init_approval_callback = __esm(() => {
|
|
39050
39132
|
init_approval_card();
|
|
39051
39133
|
init_client3();
|
|
39052
|
-
|
|
39134
|
+
import_grammy16 = __toESM(require_mod2(), 1);
|
|
39053
39135
|
});
|
|
39054
39136
|
|
|
39055
39137
|
// ../src/telegram/materialize-bot-token.ts
|
|
@@ -39222,7 +39304,7 @@ var init_approvals_commands = __esm(() => {
|
|
|
39222
39304
|
});
|
|
39223
39305
|
|
|
39224
39306
|
// gateway/gateway.ts
|
|
39225
|
-
var
|
|
39307
|
+
var import_grammy17 = __toESM(require_mod(), 1);
|
|
39226
39308
|
var import_runner3 = __toESM(require_mod3(), 1);
|
|
39227
39309
|
import { randomBytes as randomBytes13, createHash as createHash8 } from "crypto";
|
|
39228
39310
|
import { execFileSync as execFileSync9, execSync as execSync2, spawn as spawn2 } from "child_process";
|
|
@@ -43201,10 +43283,64 @@ init_rich_send();
|
|
|
43201
43283
|
// shared/bot-runtime.ts
|
|
43202
43284
|
var import_grammy3 = __toESM(require_mod2(), 1);
|
|
43203
43285
|
var import_runner = __toESM(require_mod4(), 1);
|
|
43204
|
-
import { AsyncLocalStorage } from "async_hooks";
|
|
43286
|
+
import { AsyncLocalStorage as AsyncLocalStorage2 } from "async_hooks";
|
|
43205
43287
|
|
|
43206
43288
|
// retry-api-call.ts
|
|
43207
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
|
+
}
|
|
43208
43344
|
function classifyBenignTelegram400(errorCode, description) {
|
|
43209
43345
|
if (errorCode !== 400)
|
|
43210
43346
|
return null;
|
|
@@ -43272,7 +43408,7 @@ function createRetryApiCall(config = {}) {
|
|
|
43272
43408
|
throw gated;
|
|
43273
43409
|
}
|
|
43274
43410
|
try {
|
|
43275
|
-
return await fn
|
|
43411
|
+
return await withAttemptContext({ attempt, maxRetries, maxFloodSleepMs }, fn);
|
|
43276
43412
|
} catch (err) {
|
|
43277
43413
|
const isGrammyErr = err instanceof import_grammy2.GrammyError;
|
|
43278
43414
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -43304,12 +43440,14 @@ function createRetryApiCall(config = {}) {
|
|
|
43304
43440
|
const benignKind = isGrammyErr ? classifyBenignTelegram400(err.error_code, desc) : null;
|
|
43305
43441
|
if (benignKind !== null) {
|
|
43306
43442
|
observer?.onBenign?.({ kind: benignKind });
|
|
43443
|
+
if (opts?.rethrowBenign400)
|
|
43444
|
+
throw err;
|
|
43307
43445
|
return;
|
|
43308
43446
|
}
|
|
43309
43447
|
if (isGrammyErr && err.error_code === 400 && desc.includes("thread not found") && opts?.threadId && opts?.chat_id) {
|
|
43310
43448
|
throw Object.assign(new Error("THREAD_NOT_FOUND"), { original: err });
|
|
43311
43449
|
}
|
|
43312
|
-
if (
|
|
43450
|
+
if (isTransientTransportError(err)) {
|
|
43313
43451
|
if (attempt < maxRetries - 1) {
|
|
43314
43452
|
const delayMs = Math.pow(2, attempt) * 1000;
|
|
43315
43453
|
log?.(`telegram gateway: network error, retrying in ${delayMs / 1000}s: ${msg}
|
|
@@ -43367,7 +43505,6 @@ function isPhotoDimensionRejectError(err) {
|
|
|
43367
43505
|
}
|
|
43368
43506
|
|
|
43369
43507
|
// shared/bot-runtime.ts
|
|
43370
|
-
init_flood_circuit_breaker();
|
|
43371
43508
|
init_format();
|
|
43372
43509
|
|
|
43373
43510
|
// shared/gw-trace-gate.ts
|
|
@@ -43380,7 +43517,7 @@ var ZERO_SIGNAL_POLL_METHODS = new Set(["getUpdates", "getMe"]);
|
|
|
43380
43517
|
function shouldEmitTgPost(method, status, verbose = gwTraceVerbose) {
|
|
43381
43518
|
if (verbose)
|
|
43382
43519
|
return true;
|
|
43383
|
-
if (status === "err")
|
|
43520
|
+
if (status === "err" || status === "retry")
|
|
43384
43521
|
return true;
|
|
43385
43522
|
return !ZERO_SIGNAL_POLL_METHODS.has(method);
|
|
43386
43523
|
}
|
|
@@ -43397,7 +43534,7 @@ function shouldEmitShadowTrace(eventKind, effectCount, globalKind, verbose = gwT
|
|
|
43397
43534
|
|
|
43398
43535
|
// shared/bot-runtime.ts
|
|
43399
43536
|
init_rich_send();
|
|
43400
|
-
var tgPostTagStore = new
|
|
43537
|
+
var tgPostTagStore = new AsyncLocalStorage2;
|
|
43401
43538
|
function escapeHtmlForTg(text) {
|
|
43402
43539
|
return text.replace(/([\\`*_~=\[\]|])/g, "\\$1");
|
|
43403
43540
|
}
|
|
@@ -45005,10 +45142,10 @@ async function handleAnimationMessage(ctx, deps) {
|
|
|
45005
45142
|
}
|
|
45006
45143
|
|
|
45007
45144
|
// gateway/photo-message-handler.ts
|
|
45008
|
-
import { mkdirSync as
|
|
45145
|
+
import { mkdirSync as mkdirSync6, writeFileSync as writeFileSync5 } from "node:fs";
|
|
45009
45146
|
|
|
45010
45147
|
// attachment-path.ts
|
|
45011
|
-
import { join as
|
|
45148
|
+
import { join as join5, basename as basename3, resolve as resolve2, sep } from "node:path";
|
|
45012
45149
|
function sanitizeExtension(ext) {
|
|
45013
45150
|
if (ext == null)
|
|
45014
45151
|
return "bin";
|
|
@@ -45031,7 +45168,7 @@ function buildAttachmentPath(input) {
|
|
|
45031
45168
|
const ext = extractExtension(input.telegramFilePath);
|
|
45032
45169
|
const uid = sanitizeUniqueId(input.fileUniqueId);
|
|
45033
45170
|
const filename = `${input.now}-${uid}.${ext}`;
|
|
45034
|
-
return
|
|
45171
|
+
return join5(input.inboxDir, filename);
|
|
45035
45172
|
}
|
|
45036
45173
|
function assertInsideInbox(inboxDir, candidatePath) {
|
|
45037
45174
|
const inboxReal = resolve2(inboxDir);
|
|
@@ -45066,9 +45203,9 @@ async function handlePhotoMessage(ctx, deps) {
|
|
|
45066
45203
|
fileUniqueId: best.file_unique_id,
|
|
45067
45204
|
now: Date.now()
|
|
45068
45205
|
});
|
|
45069
|
-
|
|
45206
|
+
mkdirSync6(deps.inboxDir, { recursive: true, mode: 448 });
|
|
45070
45207
|
assertInsideInbox(deps.inboxDir, dlPath);
|
|
45071
|
-
|
|
45208
|
+
writeFileSync5(dlPath, buf, { mode: 384 });
|
|
45072
45209
|
return dlPath;
|
|
45073
45210
|
} catch (err) {
|
|
45074
45211
|
const msg = err instanceof Error ? err.message : "unknown error";
|
|
@@ -46294,7 +46431,7 @@ async function handleAskCallback(ctx, data, deps) {
|
|
|
46294
46431
|
}
|
|
46295
46432
|
|
|
46296
46433
|
// gateway/voice-ondemand-callback-handler.ts
|
|
46297
|
-
import { readFileSync as
|
|
46434
|
+
import { readFileSync as readFileSync7 } from "node:fs";
|
|
46298
46435
|
|
|
46299
46436
|
// voice-send.ts
|
|
46300
46437
|
function extractVoiceFileId(sent) {
|
|
@@ -46732,14 +46869,14 @@ table omitted.`);
|
|
|
46732
46869
|
init_loader();
|
|
46733
46870
|
init_resolver();
|
|
46734
46871
|
init_vault();
|
|
46735
|
-
import { existsSync as
|
|
46872
|
+
import { existsSync as existsSync7 } from "node:fs";
|
|
46736
46873
|
var VOICE_SIDECAR_TOKEN_KEY = "voice/sidecar-token";
|
|
46737
46874
|
var DEFAULT_VOICE_SIDECAR_TOKEN_REF = `vault:${VOICE_SIDECAR_TOKEN_KEY}`;
|
|
46738
46875
|
function tryDirectVaultRead(ref, config, passphrase) {
|
|
46739
46876
|
if (!passphrase)
|
|
46740
46877
|
return null;
|
|
46741
46878
|
const vaultPath = resolvePath(config.vault?.path ?? "~/.switchroom/vault.enc");
|
|
46742
|
-
if (!
|
|
46879
|
+
if (!existsSync7(vaultPath))
|
|
46743
46880
|
return null;
|
|
46744
46881
|
try {
|
|
46745
46882
|
const secrets = openVault(passphrase, vaultPath);
|
|
@@ -46826,7 +46963,7 @@ async function handleVoiceOnDemandCallback(ctx, data, deps) {
|
|
|
46826
46963
|
let audio = null;
|
|
46827
46964
|
if (entry.filePath != null) {
|
|
46828
46965
|
try {
|
|
46829
|
-
audio =
|
|
46966
|
+
audio = readFileSync7(entry.filePath);
|
|
46830
46967
|
} catch {
|
|
46831
46968
|
audio = null;
|
|
46832
46969
|
}
|
|
@@ -47411,18 +47548,11 @@ class DeferredDoneReactions {
|
|
|
47411
47548
|
|
|
47412
47549
|
// worker-activity-feed.ts
|
|
47413
47550
|
init_card_format();
|
|
47414
|
-
|
|
47415
|
-
// status-no-truncate.ts
|
|
47416
|
-
init_format();
|
|
47417
|
-
var STATUS_ROLLING_LINES = 5;
|
|
47418
|
-
var WORKER_HISTORY_MAX = 6;
|
|
47419
|
-
var STATUS_LINE_MAX = 200;
|
|
47420
|
-
var STATUS_CARD_CHAR_BUDGET = RICH_MESSAGE_MAX_CHARS;
|
|
47421
|
-
var NESTED_PREFIX = " \u21b3 ";
|
|
47422
|
-
var WORKER_STEP_INDENT = "\u2800\u2800\u2800";
|
|
47551
|
+
init_status_no_truncate();
|
|
47423
47552
|
|
|
47424
47553
|
// card-layout.ts
|
|
47425
47554
|
init_card_format();
|
|
47555
|
+
init_format();
|
|
47426
47556
|
|
|
47427
47557
|
// model-label.ts
|
|
47428
47558
|
var DATE_SUFFIX = /^\d{8}$/;
|
|
@@ -47461,6 +47591,7 @@ function formatModelLabel(model) {
|
|
|
47461
47591
|
}
|
|
47462
47592
|
|
|
47463
47593
|
// card-layout.ts
|
|
47594
|
+
init_status_no_truncate();
|
|
47464
47595
|
function formatFeedElapsed(ms) {
|
|
47465
47596
|
const s = Math.floor(ms / 1000);
|
|
47466
47597
|
if (s < 60)
|
|
@@ -47559,15 +47690,36 @@ function fitCardToBudget(build, maxLevel) {
|
|
|
47559
47690
|
if (candidate.overflow !== true && text.length <= STATUS_CARD_CHAR_BUDGET)
|
|
47560
47691
|
return text;
|
|
47561
47692
|
}
|
|
47562
|
-
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]+$/, "");
|
|
47563
47715
|
}
|
|
47564
47716
|
|
|
47565
47717
|
// hooks/tool-label-pretool.mjs
|
|
47566
|
-
import { readFileSync as
|
|
47567
|
-
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";
|
|
47568
47720
|
function readStdin() {
|
|
47569
47721
|
try {
|
|
47570
|
-
return
|
|
47722
|
+
return readFileSync8(0, "utf8");
|
|
47571
47723
|
} catch {
|
|
47572
47724
|
return "";
|
|
47573
47725
|
}
|
|
@@ -47873,10 +48025,10 @@ function main() {
|
|
|
47873
48025
|
}) + `
|
|
47874
48026
|
`;
|
|
47875
48027
|
try {
|
|
47876
|
-
if (!
|
|
47877
|
-
|
|
48028
|
+
if (!existsSync8(stateDir)) {
|
|
48029
|
+
mkdirSync9(stateDir, { recursive: true });
|
|
47878
48030
|
}
|
|
47879
|
-
const target =
|
|
48031
|
+
const target = join7(stateDir, `tool-labels-${sessionId}.jsonl`);
|
|
47880
48032
|
appendFileSync2(target, line);
|
|
47881
48033
|
} catch (err) {
|
|
47882
48034
|
try {
|
|
@@ -47898,6 +48050,7 @@ if (isMain)
|
|
|
47898
48050
|
main();
|
|
47899
48051
|
|
|
47900
48052
|
// tool-activity-summary.ts
|
|
48053
|
+
init_status_no_truncate();
|
|
47901
48054
|
init_card_format();
|
|
47902
48055
|
init_redact();
|
|
47903
48056
|
|
|
@@ -47975,14 +48128,14 @@ function renderStatusCard(opts) {
|
|
|
47975
48128
|
return null;
|
|
47976
48129
|
const parentMarker = hasChildren && rawSteps.length > 0 ? `_\u2713 +${rawSteps.length} earlier\u2026_` : null;
|
|
47977
48130
|
const deepest = Math.max(1, escapedBody.length);
|
|
48131
|
+
const markerSection = {
|
|
48132
|
+
steps: [],
|
|
48133
|
+
window: 1,
|
|
48134
|
+
placeholder: parentMarker ?? undefined
|
|
48135
|
+
};
|
|
47978
48136
|
return fitCardToBudget((level) => {
|
|
47979
48137
|
if (level === 0)
|
|
47980
48138
|
return { spec: fullSpec() };
|
|
47981
|
-
const markerSection = {
|
|
47982
|
-
steps: [],
|
|
47983
|
-
window: 1,
|
|
47984
|
-
placeholder: parentMarker ?? undefined
|
|
47985
|
-
};
|
|
47986
48139
|
if (level < escapedBody.length) {
|
|
47987
48140
|
return {
|
|
47988
48141
|
spec: {
|
|
@@ -48011,23 +48164,38 @@ function renderStatusCard(opts) {
|
|
|
48011
48164
|
}
|
|
48012
48165
|
};
|
|
48013
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
|
+
}
|
|
48014
48177
|
function truncatedNewestLine() {
|
|
48015
|
-
const fixedCost = [...chrome, ...footer].join(`
|
|
48016
|
-
`).length;
|
|
48017
48178
|
const rawNewest = rawBody.length > 0 ? cleanStepLine(rawBody[rawBody.length - 1]) : "";
|
|
48018
|
-
const
|
|
48019
|
-
const
|
|
48020
|
-
const budget = STATUS_CARD_CHAR_BUDGET -
|
|
48021
|
-
|
|
48022
|
-
|
|
48023
|
-
|
|
48024
|
-
|
|
48025
|
-
|
|
48026
|
-
|
|
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));
|
|
48027
48192
|
}
|
|
48028
|
-
return final ? `${bodyIndent}_\u2713 ${newest}_` : `${bodyIndent}**\u2192 ${newest}${liveSuffix}**`;
|
|
48029
48193
|
}
|
|
48030
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
|
+
}
|
|
48031
48199
|
var WORKER_RESULT_RULE = "\u2500\u2500\u2500\u2500\u2500";
|
|
48032
48200
|
var WORKER_RESULT_MAX = 320;
|
|
48033
48201
|
function deriveCardResult(state, summary) {
|
|
@@ -48128,8 +48296,8 @@ function appendActivityLabel(lines, label) {
|
|
|
48128
48296
|
}
|
|
48129
48297
|
|
|
48130
48298
|
// outbound-class.ts
|
|
48131
|
-
import { AsyncLocalStorage as
|
|
48132
|
-
var store = new
|
|
48299
|
+
import { AsyncLocalStorage as AsyncLocalStorage3 } from "async_hooks";
|
|
48300
|
+
var store = new AsyncLocalStorage3;
|
|
48133
48301
|
function withOutboundClass(cls, fn) {
|
|
48134
48302
|
return store.run(cls, fn);
|
|
48135
48303
|
}
|
|
@@ -48233,7 +48401,7 @@ function classifyEditError(err) {
|
|
|
48233
48401
|
function createWorkerActivityFeed(opts) {
|
|
48234
48402
|
const log = opts.log ?? (() => {});
|
|
48235
48403
|
const nowFn = opts.now ?? Date.now;
|
|
48236
|
-
const
|
|
48404
|
+
const floodWaitRemainingMs = opts.floodWaitRemainingMs ?? (() => 0);
|
|
48237
48405
|
const minEditInterval = opts.minEditIntervalMs ?? 2500;
|
|
48238
48406
|
const elapsedRefreshMs = Math.max(minEditInterval, Math.floor(opts.elapsedRefreshMs ?? 15000));
|
|
48239
48407
|
const firstPaintMin = opts.firstPaintMinMs ?? 8000;
|
|
@@ -48286,7 +48454,7 @@ function createWorkerActivityFeed(opts) {
|
|
|
48286
48454
|
log(`worker-feed: ${label} 429 \u2014 backing off ${retryAfter}s`);
|
|
48287
48455
|
}
|
|
48288
48456
|
function parkIfFloodWindowOpen(g) {
|
|
48289
|
-
const remaining =
|
|
48457
|
+
const remaining = floodWaitRemainingMs();
|
|
48290
48458
|
if (remaining <= 0)
|
|
48291
48459
|
return false;
|
|
48292
48460
|
const until = nowFn() + remaining + COOLDOWN_JITTER_MS;
|
|
@@ -49243,13 +49411,13 @@ function buildCancelledCardEdits(cardText, cards) {
|
|
|
49243
49411
|
var STALE_TAP_NOTICE = "This request already resolved (timed out) \u2014 ask again to act.";
|
|
49244
49412
|
|
|
49245
49413
|
// gateway/permission-card-store.ts
|
|
49246
|
-
import { readFileSync as
|
|
49247
|
-
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";
|
|
49248
49416
|
function createPermissionCardStore(stateDir) {
|
|
49249
|
-
const filePath =
|
|
49417
|
+
const filePath = join8(stateDir, "pending-perm-cards.json");
|
|
49250
49418
|
function read() {
|
|
49251
49419
|
try {
|
|
49252
|
-
const raw =
|
|
49420
|
+
const raw = readFileSync9(filePath, "utf-8");
|
|
49253
49421
|
const parsed = JSON.parse(raw);
|
|
49254
49422
|
return Array.isArray(parsed) ? parsed : [];
|
|
49255
49423
|
} catch {
|
|
@@ -49258,7 +49426,7 @@ function createPermissionCardStore(stateDir) {
|
|
|
49258
49426
|
}
|
|
49259
49427
|
function write(entries) {
|
|
49260
49428
|
try {
|
|
49261
|
-
|
|
49429
|
+
writeFileSync6(filePath, JSON.stringify(entries), { encoding: "utf-8", mode: 384 });
|
|
49262
49430
|
} catch (err) {
|
|
49263
49431
|
process.stderr.write(`telegram gateway: permission-card-store write failed: ${err.message}
|
|
49264
49432
|
`);
|
|
@@ -49287,7 +49455,7 @@ function createPermissionCardStore(stateDir) {
|
|
|
49287
49455
|
},
|
|
49288
49456
|
clear() {
|
|
49289
49457
|
try {
|
|
49290
|
-
|
|
49458
|
+
unlinkSync4(filePath);
|
|
49291
49459
|
} catch {}
|
|
49292
49460
|
}
|
|
49293
49461
|
};
|
|
@@ -49295,13 +49463,13 @@ function createPermissionCardStore(stateDir) {
|
|
|
49295
49463
|
|
|
49296
49464
|
// gateway/pending-card-store.ts
|
|
49297
49465
|
init_atomic();
|
|
49298
|
-
import { unlinkSync as
|
|
49299
|
-
import { join as
|
|
49466
|
+
import { unlinkSync as unlinkSync5 } from "node:fs";
|
|
49467
|
+
import { join as join10 } from "node:path";
|
|
49300
49468
|
|
|
49301
49469
|
// gateway/store-file.ts
|
|
49302
49470
|
import { randomBytes as randomBytes6 } from "node:crypto";
|
|
49303
|
-
import { readFileSync as
|
|
49304
|
-
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";
|
|
49305
49473
|
var defaultLog = (line) => {
|
|
49306
49474
|
process.stderr.write(line);
|
|
49307
49475
|
};
|
|
@@ -49312,18 +49480,18 @@ function quarantinePath(filePath) {
|
|
|
49312
49480
|
return `${filePath}.corrupt-${Date.now()}-${seq}-${randomBytes6(4).toString("hex")}`;
|
|
49313
49481
|
}
|
|
49314
49482
|
function reapOldQuarantines(filePath, log) {
|
|
49315
|
-
const dir =
|
|
49483
|
+
const dir = dirname4(filePath);
|
|
49316
49484
|
const prefix = `${basename6(filePath)}.corrupt-`;
|
|
49317
49485
|
try {
|
|
49318
49486
|
const copies = readdirSync4(dir).filter((f) => f.startsWith(prefix)).map((name) => {
|
|
49319
49487
|
let mtimeNs = 0n;
|
|
49320
49488
|
try {
|
|
49321
|
-
mtimeNs = statSync7(
|
|
49489
|
+
mtimeNs = statSync7(join9(dir, name), { bigint: true }).mtimeNs;
|
|
49322
49490
|
} catch {}
|
|
49323
49491
|
return { name, mtimeNs };
|
|
49324
49492
|
}).sort((a, b) => a.mtimeNs === b.mtimeNs ? a.name.localeCompare(b.name) : a.mtimeNs < b.mtimeNs ? -1 : 1);
|
|
49325
49493
|
for (const stale of copies.slice(0, Math.max(0, copies.length - MAX_QUARANTINED_COPIES))) {
|
|
49326
|
-
rmSync4(
|
|
49494
|
+
rmSync4(join9(dir, stale.name), { force: true });
|
|
49327
49495
|
}
|
|
49328
49496
|
} catch (err) {
|
|
49329
49497
|
log(`telegram gateway: quarantine reap failed dir=${dir}: ${err.message}
|
|
@@ -49334,7 +49502,7 @@ function quarantineCorruptStoreFile(filePath, store2, reason, log = defaultLog)
|
|
|
49334
49502
|
const target = quarantinePath(filePath);
|
|
49335
49503
|
let preserved = target;
|
|
49336
49504
|
try {
|
|
49337
|
-
|
|
49505
|
+
renameSync8(filePath, target);
|
|
49338
49506
|
} catch (err) {
|
|
49339
49507
|
preserved = `NOT preserved (${err.message})`;
|
|
49340
49508
|
}
|
|
@@ -49345,7 +49513,7 @@ function quarantineCorruptStoreFile(filePath, store2, reason, log = defaultLog)
|
|
|
49345
49513
|
function preserveUnreadableStoreFile(filePath, store2, log = defaultLog) {
|
|
49346
49514
|
const target = quarantinePath(filePath);
|
|
49347
49515
|
try {
|
|
49348
|
-
|
|
49516
|
+
renameSync8(filePath, target);
|
|
49349
49517
|
} catch (err) {
|
|
49350
49518
|
if (err.code === "ENOENT") {
|
|
49351
49519
|
return;
|
|
@@ -49361,7 +49529,7 @@ function preserveUnreadableStoreFile(filePath, store2, log = defaultLog) {
|
|
|
49361
49529
|
function readStoreJsonSync(filePath, store2, log = defaultLog) {
|
|
49362
49530
|
let raw;
|
|
49363
49531
|
try {
|
|
49364
|
-
raw =
|
|
49532
|
+
raw = readFileSync10(filePath, "utf-8");
|
|
49365
49533
|
} catch (err) {
|
|
49366
49534
|
if (err.code === "ENOENT")
|
|
49367
49535
|
return { status: "missing" };
|
|
@@ -49379,7 +49547,7 @@ function readStoreJsonSync(filePath, store2, log = defaultLog) {
|
|
|
49379
49547
|
|
|
49380
49548
|
// gateway/pending-card-store.ts
|
|
49381
49549
|
function createPendingCardStore(stateDir, log = (l) => process.stderr.write(l)) {
|
|
49382
|
-
const filePath =
|
|
49550
|
+
const filePath = join10(stateDir, "pending-approval-cards.json");
|
|
49383
49551
|
let unreadable = false;
|
|
49384
49552
|
function read() {
|
|
49385
49553
|
const result = readStoreJsonSync(filePath, "pending-card-store", log);
|
|
@@ -49427,7 +49595,7 @@ function createPendingCardStore(stateDir, log = (l) => process.stderr.write(l))
|
|
|
49427
49595
|
},
|
|
49428
49596
|
clear() {
|
|
49429
49597
|
try {
|
|
49430
|
-
|
|
49598
|
+
unlinkSync5(filePath);
|
|
49431
49599
|
} catch {}
|
|
49432
49600
|
}
|
|
49433
49601
|
};
|
|
@@ -49566,8 +49734,8 @@ function createSweepableCardStore(deps) {
|
|
|
49566
49734
|
init_rich_send();
|
|
49567
49735
|
var import_grammy4 = __toESM(require_mod2(), 1);
|
|
49568
49736
|
import { execFileSync as execFileSync3 } from "child_process";
|
|
49569
|
-
import { mkdirSync as
|
|
49570
|
-
import { dirname as
|
|
49737
|
+
import { mkdirSync as mkdirSync12, writeFileSync as writeFileSync8 } from "fs";
|
|
49738
|
+
import { dirname as dirname5 } from "path";
|
|
49571
49739
|
init_client2();
|
|
49572
49740
|
|
|
49573
49741
|
// gateway/vault-grant-inbound-builders.ts
|
|
@@ -49699,26 +49867,26 @@ var import_yaml3 = __toESM(require_dist(), 1);
|
|
|
49699
49867
|
|
|
49700
49868
|
// ../src/web/config-diff.ts
|
|
49701
49869
|
import {
|
|
49702
|
-
mkdirSync as
|
|
49870
|
+
mkdirSync as mkdirSync10,
|
|
49703
49871
|
mkdtempSync as mkdtemp,
|
|
49704
49872
|
rmSync as rmrf,
|
|
49705
|
-
writeFileSync as
|
|
49873
|
+
writeFileSync as writeFileSync7
|
|
49706
49874
|
} from "node:fs";
|
|
49707
49875
|
import { spawnSync } from "node:child_process";
|
|
49708
49876
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
49709
|
-
import { join as
|
|
49877
|
+
import { join as join11 } from "node:path";
|
|
49710
49878
|
|
|
49711
49879
|
class ConfigDiffError extends Error {
|
|
49712
49880
|
}
|
|
49713
49881
|
function generateUnifiedDiff(before, after, name = "switchroom.yaml", gitBin = "git") {
|
|
49714
49882
|
if (before === after)
|
|
49715
49883
|
return "";
|
|
49716
|
-
const dir = mkdtemp(
|
|
49884
|
+
const dir = mkdtemp(join11(tmpdir2(), "switchroom-config-diff-"));
|
|
49717
49885
|
try {
|
|
49718
|
-
|
|
49719
|
-
|
|
49720
|
-
|
|
49721
|
-
|
|
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);
|
|
49722
49890
|
const r = spawnSync(gitBin, ["diff", "--no-index", "--no-color", "--", `cur/${name}`, `new/${name}`], { cwd: dir, encoding: "utf-8", timeout: 1e4 });
|
|
49723
49891
|
if (r.status === 0)
|
|
49724
49892
|
return "";
|
|
@@ -50049,25 +50217,25 @@ function buildSkillProposalApplyInbound(opts) {
|
|
|
50049
50217
|
// ../src/self-improve/skill-proposals.ts
|
|
50050
50218
|
import {
|
|
50051
50219
|
closeSync as closeSync5,
|
|
50052
|
-
existsSync as
|
|
50053
|
-
mkdirSync as
|
|
50220
|
+
existsSync as existsSync9,
|
|
50221
|
+
mkdirSync as mkdirSync11,
|
|
50054
50222
|
openSync as openSync5,
|
|
50055
|
-
readFileSync as
|
|
50223
|
+
readFileSync as readFileSync11,
|
|
50056
50224
|
writeSync as writeSync5
|
|
50057
50225
|
} from "node:fs";
|
|
50058
|
-
import { join as
|
|
50226
|
+
import { join as join12 } from "node:path";
|
|
50059
50227
|
var PROPOSALS_FILE = "skill-proposals.jsonl";
|
|
50060
50228
|
var REJECTED_FILE = "skill-proposals-rejected.jsonl";
|
|
50061
50229
|
var REJECTION_TTL_MS = 90 * 24 * 60 * 60 * 1000;
|
|
50062
50230
|
function proposalsPath(stateDir) {
|
|
50063
|
-
return
|
|
50231
|
+
return join12(stateDir, PROPOSALS_FILE);
|
|
50064
50232
|
}
|
|
50065
50233
|
function rejectedPath(stateDir) {
|
|
50066
|
-
return
|
|
50234
|
+
return join12(stateDir, REJECTED_FILE);
|
|
50067
50235
|
}
|
|
50068
50236
|
function ensureDir2(stateDir) {
|
|
50069
|
-
if (!
|
|
50070
|
-
|
|
50237
|
+
if (!existsSync9(stateDir)) {
|
|
50238
|
+
mkdirSync11(stateDir, { recursive: true, mode: 493 });
|
|
50071
50239
|
}
|
|
50072
50240
|
}
|
|
50073
50241
|
function appendLine(path, obj) {
|
|
@@ -50080,11 +50248,11 @@ function appendLine(path, obj) {
|
|
|
50080
50248
|
}
|
|
50081
50249
|
}
|
|
50082
50250
|
function readLines(path, isValid2) {
|
|
50083
|
-
if (!
|
|
50251
|
+
if (!existsSync9(path))
|
|
50084
50252
|
return [];
|
|
50085
50253
|
let raw;
|
|
50086
50254
|
try {
|
|
50087
|
-
raw =
|
|
50255
|
+
raw = readFileSync11(path, "utf-8");
|
|
50088
50256
|
} catch {
|
|
50089
50257
|
return [];
|
|
50090
50258
|
}
|
|
@@ -50226,7 +50394,7 @@ async function addAccountViaBroker(label, credentials, opts = {}) {
|
|
|
50226
50394
|
}
|
|
50227
50395
|
|
|
50228
50396
|
// gateway/hostd-dispatch.ts
|
|
50229
|
-
import { existsSync as
|
|
50397
|
+
import { existsSync as existsSync10 } from "node:fs";
|
|
50230
50398
|
import { randomBytes as randomBytes7 } from "node:crypto";
|
|
50231
50399
|
|
|
50232
50400
|
// ../src/host-control/client.ts
|
|
@@ -50549,13 +50717,13 @@ function hostdSocketPath(agentName) {
|
|
|
50549
50717
|
function hostdWillBeUsed(agentName) {
|
|
50550
50718
|
if (!isHostdEnabled())
|
|
50551
50719
|
return false;
|
|
50552
|
-
return
|
|
50720
|
+
return existsSync10(hostdSocketPath(agentName));
|
|
50553
50721
|
}
|
|
50554
50722
|
async function tryHostdDispatch(agentName, req, timeoutMs = 5000) {
|
|
50555
50723
|
if (!isHostdEnabled())
|
|
50556
50724
|
return "not-configured";
|
|
50557
50725
|
const sockPath = hostdSocketPath(agentName);
|
|
50558
|
-
if (!
|
|
50726
|
+
if (!existsSync10(sockPath))
|
|
50559
50727
|
return "not-configured";
|
|
50560
50728
|
try {
|
|
50561
50729
|
return await hostdRequest({ socketPath: sockPath, timeoutMs }, req);
|
|
@@ -50579,7 +50747,7 @@ async function hostdGetStatusOnce(agentName, targetRequestId) {
|
|
|
50579
50747
|
if (!isHostdEnabled())
|
|
50580
50748
|
return "not-configured";
|
|
50581
50749
|
const sockPath = hostdSocketPath(agentName);
|
|
50582
|
-
if (!
|
|
50750
|
+
if (!existsSync10(sockPath))
|
|
50583
50751
|
return "not-configured";
|
|
50584
50752
|
try {
|
|
50585
50753
|
const resp = await hostdRequest({ socketPath: sockPath, timeoutMs: 3000 }, {
|
|
@@ -50727,8 +50895,8 @@ function createCallbackQueryHandlers(deps) {
|
|
|
50727
50895
|
const { token, id } = result;
|
|
50728
50896
|
const tokenPath = vaultTokenFilePath2(agentName);
|
|
50729
50897
|
try {
|
|
50730
|
-
|
|
50731
|
-
|
|
50898
|
+
mkdirSync12(dirname5(tokenPath), { recursive: true });
|
|
50899
|
+
writeFileSync8(tokenPath, token, { mode: 384 });
|
|
50732
50900
|
} catch (err) {
|
|
50733
50901
|
await switchroomReply(ctx, `**Grant created (${escapeHtmlForTg2(id)}) but token write failed:** ` + `${escapeHtmlForTg2(String(err))}
|
|
50734
50902
|
` + `_Recover with: \`switchroom vault grant ${escapeHtmlForTg2(agentName)} ` + `--keys ${escapeHtmlForTg2(keyName)} --duration 30d\` on the host._`, { html: true });
|
|
@@ -50879,8 +51047,8 @@ function createCallbackQueryHandlers(deps) {
|
|
|
50879
51047
|
const { token, id } = result;
|
|
50880
51048
|
const tokenPath = vaultTokenFilePath2(pending.agent);
|
|
50881
51049
|
try {
|
|
50882
|
-
|
|
50883
|
-
|
|
51050
|
+
mkdirSync12(dirname5(tokenPath), { recursive: true });
|
|
51051
|
+
writeFileSync8(tokenPath, token, { mode: 384 });
|
|
50884
51052
|
} catch (err) {
|
|
50885
51053
|
await switchroomReply(ctx, `**Grant created (${escapeHtmlForTg2(id)}) but token write failed:** ` + `${escapeHtmlForTg2(String(err))}
|
|
50886
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 });
|
|
@@ -51708,8 +51876,8 @@ ${keyList}`,
|
|
|
51708
51876
|
const { token, id } = result;
|
|
51709
51877
|
const tokenPath = vaultTokenFilePath2(state.agent);
|
|
51710
51878
|
try {
|
|
51711
|
-
|
|
51712
|
-
|
|
51879
|
+
mkdirSync12(dirname5(tokenPath), { recursive: true });
|
|
51880
|
+
writeFileSync8(tokenPath, token, { mode: 384 });
|
|
51713
51881
|
} catch (err) {
|
|
51714
51882
|
await switchroomReply(ctx, `**Grant created but token write failed:** ${escapeHtmlForTg2(String(err))}`, { html: true });
|
|
51715
51883
|
return;
|
|
@@ -52767,12 +52935,12 @@ function hangStalenessMs(env = process.env) {
|
|
|
52767
52935
|
|
|
52768
52936
|
// gateway/missed-approvals-store.ts
|
|
52769
52937
|
init_atomic();
|
|
52770
|
-
import { unlinkSync as
|
|
52771
|
-
import { join as
|
|
52938
|
+
import { unlinkSync as unlinkSync6 } from "node:fs";
|
|
52939
|
+
import { join as join14 } from "node:path";
|
|
52772
52940
|
var MAX_PENDING = 50;
|
|
52773
52941
|
var MAX_DELIVERED = 20;
|
|
52774
52942
|
function createMissedApprovalsStore(stateDir, log = (l) => process.stderr.write(l)) {
|
|
52775
|
-
const filePath =
|
|
52943
|
+
const filePath = join14(stateDir, "missed-approvals.json");
|
|
52776
52944
|
const EMPTY = () => ({ pending: [], delivered: [] });
|
|
52777
52945
|
let unreadable = false;
|
|
52778
52946
|
function read() {
|
|
@@ -52852,7 +53020,7 @@ function createMissedApprovalsStore(stateDir, log = (l) => process.stderr.write(
|
|
|
52852
53020
|
},
|
|
52853
53021
|
clear() {
|
|
52854
53022
|
try {
|
|
52855
|
-
|
|
53023
|
+
unlinkSync6(filePath);
|
|
52856
53024
|
} catch {}
|
|
52857
53025
|
}
|
|
52858
53026
|
};
|
|
@@ -52860,8 +53028,8 @@ function createMissedApprovalsStore(stateDir, log = (l) => process.stderr.write(
|
|
|
52860
53028
|
|
|
52861
53029
|
// gateway/always-allow-persist-queue.ts
|
|
52862
53030
|
init_atomic();
|
|
52863
|
-
import { unlinkSync as
|
|
52864
|
-
import { join as
|
|
53031
|
+
import { unlinkSync as unlinkSync7 } from "node:fs";
|
|
53032
|
+
import { join as join15 } from "node:path";
|
|
52865
53033
|
var MAX_ATTEMPTS = 5;
|
|
52866
53034
|
var MAX_AGE_MS = 24 * 60 * 60 * 1000;
|
|
52867
53035
|
var MAX_QUEUE_SIZE = 50;
|
|
@@ -52885,7 +53053,7 @@ var atomicWriteSeam = (path, data, opts) => {
|
|
|
52885
53053
|
atomicWriteFileSync2(path, data, mode);
|
|
52886
53054
|
};
|
|
52887
53055
|
function createAlwaysAllowPersistQueue(stateDir, writeFileSyncFn = atomicWriteSeam, log = (l) => process.stderr.write(l)) {
|
|
52888
|
-
const filePath =
|
|
53056
|
+
const filePath = join15(stateDir, "always-allow-persist-queue.json");
|
|
52889
53057
|
let lock = Promise.resolve();
|
|
52890
53058
|
function withLock(fn) {
|
|
52891
53059
|
const result = lock.then(fn, fn);
|
|
@@ -52997,7 +53165,7 @@ function createAlwaysAllowPersistQueue(stateDir, writeFileSyncFn = atomicWriteSe
|
|
|
52997
53165
|
},
|
|
52998
53166
|
clear() {
|
|
52999
53167
|
try {
|
|
53000
|
-
|
|
53168
|
+
unlinkSync7(filePath);
|
|
53001
53169
|
} catch {}
|
|
53002
53170
|
}
|
|
53003
53171
|
};
|
|
@@ -53160,8 +53328,8 @@ function pickRecoveredPermissionOrigin(recentTurns, now, maxAgeMs) {
|
|
|
53160
53328
|
}
|
|
53161
53329
|
|
|
53162
53330
|
// gateway/approval-hold.ts
|
|
53163
|
-
import { mkdirSync as
|
|
53164
|
-
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";
|
|
53165
53333
|
var BLOCKED_APPROVAL_FILE_MODE = 420;
|
|
53166
53334
|
var BLOCKED_APPROVAL_DIR_MODE = 1023;
|
|
53167
53335
|
var HELD_RETRY_BACKOFF_MS = 60000;
|
|
@@ -53190,24 +53358,24 @@ function safeActionForRecord(naturalAction2, toolName, inputPreview) {
|
|
|
53190
53358
|
return ACTION_SAFE_WITH_INPUT2.has(toolName) ? naturalAction2(toolName, inputPreview) : naturalAction2(toolName, undefined);
|
|
53191
53359
|
}
|
|
53192
53360
|
function createBlockedApprovalStore(dir, agent, fallbackDir) {
|
|
53193
|
-
const primary =
|
|
53194
|
-
const fallback = fallbackDir != null ?
|
|
53361
|
+
const primary = join16(dir, `${agent}.json`);
|
|
53362
|
+
const fallback = fallbackDir != null ? join16(fallbackDir, "blocked-approval.json") : null;
|
|
53195
53363
|
let active = primary;
|
|
53196
53364
|
function tryWrite(target, body, dirMode) {
|
|
53197
|
-
const parent =
|
|
53365
|
+
const parent = dirname6(target);
|
|
53198
53366
|
try {
|
|
53199
|
-
|
|
53367
|
+
mkdirSync13(parent, { recursive: true });
|
|
53200
53368
|
try {
|
|
53201
53369
|
if (lstatSync2(target).isSymbolicLink())
|
|
53202
53370
|
return false;
|
|
53203
53371
|
} catch {}
|
|
53204
53372
|
if (dirMode != null) {
|
|
53205
53373
|
try {
|
|
53206
|
-
|
|
53374
|
+
chmodSync3(parent, dirMode);
|
|
53207
53375
|
} catch {}
|
|
53208
53376
|
}
|
|
53209
|
-
|
|
53210
|
-
|
|
53377
|
+
writeFileSync10(target, body, { encoding: "utf-8", mode: BLOCKED_APPROVAL_FILE_MODE });
|
|
53378
|
+
chmodSync3(target, BLOCKED_APPROVAL_FILE_MODE);
|
|
53211
53379
|
return true;
|
|
53212
53380
|
} catch {
|
|
53213
53381
|
return false;
|
|
@@ -53224,7 +53392,7 @@ function createBlockedApprovalStore(dir, agent, fallbackDir) {
|
|
|
53224
53392
|
active = primary;
|
|
53225
53393
|
if (fallback != null) {
|
|
53226
53394
|
try {
|
|
53227
|
-
|
|
53395
|
+
unlinkSync8(fallback);
|
|
53228
53396
|
} catch {}
|
|
53229
53397
|
}
|
|
53230
53398
|
return;
|
|
@@ -53243,7 +53411,7 @@ function createBlockedApprovalStore(dir, agent, fallbackDir) {
|
|
|
53243
53411
|
if (p == null)
|
|
53244
53412
|
continue;
|
|
53245
53413
|
try {
|
|
53246
|
-
|
|
53414
|
+
unlinkSync8(p);
|
|
53247
53415
|
} catch {}
|
|
53248
53416
|
}
|
|
53249
53417
|
active = primary;
|
|
@@ -53253,7 +53421,7 @@ function createBlockedApprovalStore(dir, agent, fallbackDir) {
|
|
|
53253
53421
|
if (p == null)
|
|
53254
53422
|
continue;
|
|
53255
53423
|
try {
|
|
53256
|
-
const parsed = JSON.parse(
|
|
53424
|
+
const parsed = JSON.parse(readFileSync12(p, "utf-8"));
|
|
53257
53425
|
if (parsed != null && typeof parsed === "object")
|
|
53258
53426
|
return parsed;
|
|
53259
53427
|
} catch {}
|
|
@@ -53332,6 +53500,7 @@ function isTelegramSurfaceTool2(toolName) {
|
|
|
53332
53500
|
}
|
|
53333
53501
|
|
|
53334
53502
|
// tool-activity-summary.ts
|
|
53503
|
+
init_status_no_truncate();
|
|
53335
53504
|
init_card_format();
|
|
53336
53505
|
init_redact();
|
|
53337
53506
|
function clipNarrative2(s) {
|
|
@@ -53550,6 +53719,7 @@ function createTurnTypingLoop(deps) {
|
|
|
53550
53719
|
|
|
53551
53720
|
// gateway/handback-preturn-signal.ts
|
|
53552
53721
|
var PRETURN_TURNKEY_PREFIX = "preturn:";
|
|
53722
|
+
var HANDBACK_REINJECT_COUNT_META_KEY = "handbackReinjectCount";
|
|
53553
53723
|
function isHandbackInbound(msg) {
|
|
53554
53724
|
return msg.type === "inbound" && msg.meta?.source === "subagent_handback";
|
|
53555
53725
|
}
|
|
@@ -53557,6 +53727,8 @@ function createHandbackPreturnSignal(deps) {
|
|
|
53557
53727
|
const now = deps.now ?? (() => Date.now());
|
|
53558
53728
|
const debounceMs = deps.debounceMs ?? 700;
|
|
53559
53729
|
const adoptTimeoutMs = deps.adoptTimeoutMs ?? 30000;
|
|
53730
|
+
const deliveredTtlMs = deps.deliveredTtlMs ?? 300000;
|
|
53731
|
+
const maxReinjects = deps.maxReinjects ?? 2;
|
|
53560
53732
|
const setTimer = deps.setTimer ?? ((fn, ms) => {
|
|
53561
53733
|
const t = setTimeout(fn, ms);
|
|
53562
53734
|
t.unref?.();
|
|
@@ -53596,7 +53768,7 @@ function createHandbackPreturnSignal(deps) {
|
|
|
53596
53768
|
if (messageId == null)
|
|
53597
53769
|
return;
|
|
53598
53770
|
if (entry.consumed) {
|
|
53599
|
-
deps.
|
|
53771
|
+
deps.deleteCard({
|
|
53600
53772
|
turnKey: entry.syntheticTurnKey,
|
|
53601
53773
|
chatId: entry.chatId,
|
|
53602
53774
|
threadId: entry.threadId,
|
|
@@ -53629,12 +53801,15 @@ function createHandbackPreturnSignal(deps) {
|
|
|
53629
53801
|
}
|
|
53630
53802
|
deps.stopTypingLoop(entry.chatId, entry.threadId);
|
|
53631
53803
|
}
|
|
53804
|
+
function deliveredAwaitingTurnStart(entry) {
|
|
53805
|
+
return now() - entry.deliveredAt < deliveredTtlMs;
|
|
53806
|
+
}
|
|
53632
53807
|
function reap(entry) {
|
|
53633
53808
|
entry.reapTimer = null;
|
|
53634
53809
|
if (entry.consumed)
|
|
53635
53810
|
return;
|
|
53636
|
-
if (deps.isClaudeBusy?.() === true) {
|
|
53637
|
-
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)
|
|
53638
53813
|
`);
|
|
53639
53814
|
entry.reapTimer = setTimer(() => reap(entry), adoptTimeoutMs);
|
|
53640
53815
|
return;
|
|
@@ -53651,12 +53826,40 @@ function createHandbackPreturnSignal(deps) {
|
|
|
53651
53826
|
pinned: entry.pinned
|
|
53652
53827
|
};
|
|
53653
53828
|
deps.clearCardRecord(entry.syntheticTurnKey, entry.activityMessageId);
|
|
53654
|
-
Promise.resolve(deps.
|
|
53655
|
-
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)}
|
|
53656
53831
|
`);
|
|
53657
53832
|
});
|
|
53658
53833
|
}
|
|
53659
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
|
+
}
|
|
53660
53863
|
}
|
|
53661
53864
|
return {
|
|
53662
53865
|
noteHandbackRelease(inbound) {
|
|
@@ -53683,6 +53886,9 @@ function createHandbackPreturnSignal(deps) {
|
|
|
53683
53886
|
`);
|
|
53684
53887
|
const startedAt = now();
|
|
53685
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;
|
|
53686
53892
|
const entry = {
|
|
53687
53893
|
statusKey,
|
|
53688
53894
|
chatId,
|
|
@@ -53690,6 +53896,9 @@ function createHandbackPreturnSignal(deps) {
|
|
|
53690
53896
|
adoptTurnId,
|
|
53691
53897
|
syntheticTurnKey,
|
|
53692
53898
|
startedAt,
|
|
53899
|
+
deliveredAt: startedAt,
|
|
53900
|
+
reinjectCount,
|
|
53901
|
+
inbound,
|
|
53693
53902
|
pinned: false,
|
|
53694
53903
|
debounceTimer: null,
|
|
53695
53904
|
reapTimer: null,
|
|
@@ -53772,6 +53981,24 @@ function createHandbackPreturnSignal(deps) {
|
|
|
53772
53981
|
};
|
|
53773
53982
|
}
|
|
53774
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
|
+
|
|
53775
54002
|
// gateway/derive-turn-id.ts
|
|
53776
54003
|
function deriveTurnId2(chatId, threadId, messageId) {
|
|
53777
54004
|
if (messageId == null || messageId === "" || String(messageId) === "0")
|
|
@@ -63395,7 +63622,6 @@ function renderSafe(doc, source, maxLen = RICH_MESSAGE_MAX_CHARS) {
|
|
|
63395
63622
|
|
|
63396
63623
|
// render/rich-render.ts
|
|
63397
63624
|
init_format();
|
|
63398
|
-
var PLAIN_TEXT_MAX_CHARS = 4096;
|
|
63399
63625
|
function parseRichRenderEnabled(raw) {
|
|
63400
63626
|
if (raw == null)
|
|
63401
63627
|
return true;
|
|
@@ -63431,6 +63657,48 @@ function renderOutboundChunks(text4, env = process.env, maxLen = RICH_MESSAGE_MA
|
|
|
63431
63657
|
return out.length > 0 ? out : [whole];
|
|
63432
63658
|
}
|
|
63433
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
|
+
|
|
63434
63702
|
// stream-controller.ts
|
|
63435
63703
|
function createStreamController(cfg) {
|
|
63436
63704
|
const {
|
|
@@ -63458,13 +63726,10 @@ function createStreamController(cfg) {
|
|
|
63458
63726
|
...disableLinkPreview ? { link_preview_options: { is_disabled: true } } : {},
|
|
63459
63727
|
...replyMarkup != null ? { reply_markup: replyMarkup } : {}
|
|
63460
63728
|
};
|
|
63461
|
-
|
|
63729
|
+
let sendOpts = {
|
|
63462
63730
|
...baseOpts,
|
|
63463
63731
|
...replyToMessageId != null ? {
|
|
63464
|
-
reply_parameters:
|
|
63465
|
-
message_id: replyToMessageId,
|
|
63466
|
-
...quoteText != null ? { quote: { text: quoteText, position: 0 } } : {}
|
|
63467
|
-
}
|
|
63732
|
+
reply_parameters: buildReplyParameters(replyToMessageId, quoteText)
|
|
63468
63733
|
} : {},
|
|
63469
63734
|
...protectContent === true ? { protect_content: true } : {},
|
|
63470
63735
|
...cfg.disableNotification === true ? { disable_notification: true } : {}
|
|
@@ -63481,6 +63746,17 @@ function createStreamController(cfg) {
|
|
|
63481
63746
|
delete richOpts.link_preview_options;
|
|
63482
63747
|
return bot.api.sendRichMessage(chatId, richMessage(piece.text), richOpts);
|
|
63483
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
|
+
};
|
|
63484
63760
|
const editPiece = (id, piece, opts) => {
|
|
63485
63761
|
if (!piece.rich)
|
|
63486
63762
|
return bot.api.editMessageText(chatId, id, piece.text, opts);
|
|
@@ -63523,7 +63799,7 @@ function createStreamController(cfg) {
|
|
|
63523
63799
|
return false;
|
|
63524
63800
|
}
|
|
63525
63801
|
try {
|
|
63526
|
-
const sent = await retry(() =>
|
|
63802
|
+
const sent = await retry(() => sendPieceWithQuoteFallback(piece), { threadId, chat_id: chatId });
|
|
63527
63803
|
tailIds[ti] = sent.message_id;
|
|
63528
63804
|
tailLastText[ti] = piece.text;
|
|
63529
63805
|
onSend?.(sent.message_id, piece.text.length);
|
|
@@ -63545,7 +63821,7 @@ function createStreamController(cfg) {
|
|
|
63545
63821
|
const head = pieces[0];
|
|
63546
63822
|
let anchorId;
|
|
63547
63823
|
try {
|
|
63548
|
-
const sent = await retry(() =>
|
|
63824
|
+
const sent = await retry(() => sendPieceWithQuoteFallback(head), { threadId, chat_id: chatId });
|
|
63549
63825
|
anchorId = sent.message_id;
|
|
63550
63826
|
} catch (err) {
|
|
63551
63827
|
if (!literalText && head.rich && isParseEntitiesError(err)) {
|
|
@@ -63730,7 +64006,42 @@ function createChatLock() {
|
|
|
63730
64006
|
}
|
|
63731
64007
|
|
|
63732
64008
|
// retry-api-call.ts
|
|
63733
|
-
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
|
+
}
|
|
63734
64045
|
function classifyBenignTelegram4002(errorCode, description) {
|
|
63735
64046
|
if (errorCode !== 400)
|
|
63736
64047
|
return null;
|
|
@@ -63775,12 +64086,12 @@ function createRetryApiCall2(config = {}) {
|
|
|
63775
64086
|
const observer = config.observer;
|
|
63776
64087
|
const log = config.log;
|
|
63777
64088
|
const onFloodWait = config.onFloodWait;
|
|
63778
|
-
const
|
|
64089
|
+
const floodWaitRemainingMs = config.floodWaitRemainingMs;
|
|
63779
64090
|
function openWindowMs() {
|
|
63780
|
-
if (!
|
|
64091
|
+
if (!floodWaitRemainingMs)
|
|
63781
64092
|
return 0;
|
|
63782
64093
|
try {
|
|
63783
|
-
const ms =
|
|
64094
|
+
const ms = floodWaitRemainingMs();
|
|
63784
64095
|
return Number.isFinite(ms) && ms > 0 ? ms : 0;
|
|
63785
64096
|
} catch {
|
|
63786
64097
|
return 0;
|
|
@@ -63798,9 +64109,9 @@ function createRetryApiCall2(config = {}) {
|
|
|
63798
64109
|
throw gated;
|
|
63799
64110
|
}
|
|
63800
64111
|
try {
|
|
63801
|
-
return await fn
|
|
64112
|
+
return await withAttemptContext2({ attempt, maxRetries, maxFloodSleepMs }, fn);
|
|
63802
64113
|
} catch (err) {
|
|
63803
|
-
const isGrammyErr = err instanceof
|
|
64114
|
+
const isGrammyErr = err instanceof import_grammy7.GrammyError;
|
|
63804
64115
|
const msg = err instanceof Error ? err.message : String(err);
|
|
63805
64116
|
const desc = isGrammyErr ? err.description : msg;
|
|
63806
64117
|
if (isLocalResourceError2(err)) {
|
|
@@ -63830,12 +64141,14 @@ function createRetryApiCall2(config = {}) {
|
|
|
63830
64141
|
const benignKind = isGrammyErr ? classifyBenignTelegram4002(err.error_code, desc) : null;
|
|
63831
64142
|
if (benignKind !== null) {
|
|
63832
64143
|
observer?.onBenign?.({ kind: benignKind });
|
|
64144
|
+
if (opts?.rethrowBenign400)
|
|
64145
|
+
throw err;
|
|
63833
64146
|
return;
|
|
63834
64147
|
}
|
|
63835
64148
|
if (isGrammyErr && err.error_code === 400 && desc.includes("thread not found") && opts?.threadId && opts?.chat_id) {
|
|
63836
64149
|
throw Object.assign(new Error("THREAD_NOT_FOUND"), { original: err });
|
|
63837
64150
|
}
|
|
63838
|
-
if (
|
|
64151
|
+
if (isTransientTransportError2(err)) {
|
|
63839
64152
|
if (attempt < maxRetries - 1) {
|
|
63840
64153
|
const delayMs = Math.pow(2, attempt) * 1000;
|
|
63841
64154
|
log?.(`telegram gateway: network error, retrying in ${delayMs / 1000}s: ${msg}
|
|
@@ -65136,14 +65449,13 @@ function createFloodWindowObserver(config) {
|
|
|
65136
65449
|
}
|
|
65137
65450
|
|
|
65138
65451
|
// shared/bot-runtime.ts
|
|
65139
|
-
var
|
|
65452
|
+
var import_grammy8 = __toESM(require_mod2(), 1);
|
|
65140
65453
|
var import_runner2 = __toESM(require_mod4(), 1);
|
|
65141
65454
|
import { createHash as createHash3 } from "crypto";
|
|
65142
|
-
import { AsyncLocalStorage as
|
|
65143
|
-
init_flood_circuit_breaker();
|
|
65455
|
+
import { AsyncLocalStorage as AsyncLocalStorage5 } from "async_hooks";
|
|
65144
65456
|
init_format();
|
|
65145
65457
|
init_rich_send();
|
|
65146
|
-
var tgPostTagStore2 = new
|
|
65458
|
+
var tgPostTagStore2 = new AsyncLocalStorage5;
|
|
65147
65459
|
function _getTgPostTags() {
|
|
65148
65460
|
return tgPostTagStore2.getStore();
|
|
65149
65461
|
}
|
|
@@ -65186,16 +65498,27 @@ function installTgPostLogger(bot) {
|
|
|
65186
65498
|
if (r != null && typeof r === "object" && r.ok === false) {
|
|
65187
65499
|
const code2 = r.error_code;
|
|
65188
65500
|
const benign = classifyBenignTelegram400(code2, r.description);
|
|
65189
|
-
|
|
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));
|
|
65190
65507
|
return res;
|
|
65191
65508
|
}
|
|
65192
65509
|
emit("ok", "-", "-", "-");
|
|
65193
65510
|
return res;
|
|
65194
65511
|
} catch (err) {
|
|
65195
|
-
const errClass = err instanceof
|
|
65196
|
-
const code2 = err instanceof
|
|
65197
|
-
const rawDesc = err instanceof
|
|
65198
|
-
|
|
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));
|
|
65199
65522
|
throw err;
|
|
65200
65523
|
}
|
|
65201
65524
|
});
|
|
@@ -69909,11 +70232,11 @@ class PostHogBackendClient extends PostHogCoreStateless {
|
|
|
69909
70232
|
}
|
|
69910
70233
|
|
|
69911
70234
|
// ../node_modules/.bun/posthog-node@5.29.2/node_modules/posthog-node/dist/extensions/context/context.mjs
|
|
69912
|
-
import { AsyncLocalStorage as
|
|
70235
|
+
import { AsyncLocalStorage as AsyncLocalStorage6 } from "node:async_hooks";
|
|
69913
70236
|
|
|
69914
70237
|
class PostHogContext {
|
|
69915
70238
|
constructor() {
|
|
69916
|
-
this.storage = new
|
|
70239
|
+
this.storage = new AsyncLocalStorage6;
|
|
69917
70240
|
}
|
|
69918
70241
|
get() {
|
|
69919
70242
|
return this.storage.getStore();
|
|
@@ -76366,9 +76689,7 @@ function splitMarkdownChunks2(text4, maxLen = RICH_MESSAGE_MAX_CHARS2) {
|
|
|
76366
76689
|
} else if (spaceIdx > 0) {
|
|
76367
76690
|
cut = spaceIdx;
|
|
76368
76691
|
}
|
|
76369
|
-
cut =
|
|
76370
|
-
cut = backOffTableRow2(rest, cut);
|
|
76371
|
-
cut = backOffOpenInline2(rest, cut);
|
|
76692
|
+
cut = safeMarkdownCut2(rest, cut);
|
|
76372
76693
|
if (cut <= 0) {
|
|
76373
76694
|
const sliced = hardSliceToCap2(rest, maxLen);
|
|
76374
76695
|
chunks.push(stripBoundarySpacers2(sliced[0], "trailing"));
|
|
@@ -76421,8 +76742,29 @@ var INLINE_SPAN_PATTERNS2 = [
|
|
|
76421
76742
|
/\*\*[^*\n]+\*\*/g,
|
|
76422
76743
|
/__[^_\n]+__/g,
|
|
76423
76744
|
/(?<![\w*])_[^_\n]+_(?![\w*])/g,
|
|
76424
|
-
/\[[^\]\n]*\]\([^)\n]*\)/g
|
|
76745
|
+
/\[[^\]\n]*\]\([^)\n]*\)/g,
|
|
76746
|
+
/~~[^~\n]+~~/g,
|
|
76747
|
+
/\|\|[^|\n]+\|\|/g
|
|
76425
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
|
+
}
|
|
76426
76768
|
function backOffOpenInline2(text4, cut) {
|
|
76427
76769
|
if (cut <= 0 || cut >= text4.length)
|
|
76428
76770
|
return cut;
|
|
@@ -76446,7 +76788,7 @@ function backOffOpenInline2(text4, cut) {
|
|
|
76446
76788
|
|
|
76447
76789
|
// gateway/command-format.ts
|
|
76448
76790
|
init_format();
|
|
76449
|
-
var
|
|
76791
|
+
var import_grammy9 = __toESM(require_mod2(), 1);
|
|
76450
76792
|
function formatSwitchroomOutput(output, maxLen = RICH_MESSAGE_MAX_CHARS) {
|
|
76451
76793
|
const trimmed = output.trim();
|
|
76452
76794
|
if (trimmed.length <= maxLen)
|
|
@@ -76514,7 +76856,7 @@ function formatAuthOutputForTelegram(output) {
|
|
|
76514
76856
|
`), url };
|
|
76515
76857
|
}
|
|
76516
76858
|
function buildAuthUrlKeyboard(authorizeUrl) {
|
|
76517
|
-
return new
|
|
76859
|
+
return new import_grammy9.InlineKeyboard().url("\uD83D\uDD10 Open Claude auth", authorizeUrl);
|
|
76518
76860
|
}
|
|
76519
76861
|
function buildDeferredSecretKeyboard(deferKey) {
|
|
76520
76862
|
const unlockData = `vd:unlock:${deferKey}`;
|
|
@@ -76524,7 +76866,7 @@ function buildDeferredSecretKeyboard(deferKey) {
|
|
|
76524
76866
|
`);
|
|
76525
76867
|
throw new Error(`callback_data overflow: deferKey too long (${deferKey.length} chars)`);
|
|
76526
76868
|
}
|
|
76527
|
-
return new
|
|
76869
|
+
return new import_grammy9.InlineKeyboard().text("\uD83D\uDD13 Unlock vault & save", unlockData).text("\uD83D\uDDD1 Discard", cancelData);
|
|
76528
76870
|
}
|
|
76529
76871
|
function renderVaultOpFailure(verbLabel, cliOutput, key) {
|
|
76530
76872
|
const parsed = parseVaultCliError(cliOutput);
|
|
@@ -76560,7 +76902,7 @@ _${escapeHtmlForTg2(outcome.paneTailText)}_` : "";
|
|
|
76560
76902
|
}
|
|
76561
76903
|
}
|
|
76562
76904
|
function buildDoctorScopeKeyboard() {
|
|
76563
|
-
return new
|
|
76905
|
+
return new import_grammy9.InlineKeyboard().text("\uD83E\uDE7A Whole fleet", "dr:fleet").text("\uD83E\uDE7A This agent", "dr:self");
|
|
76564
76906
|
}
|
|
76565
76907
|
function formatDoctorReport(raw) {
|
|
76566
76908
|
const trimmed = stripAnsi4(raw).trim();
|
|
@@ -76576,7 +76918,7 @@ init_emphasis_guard();
|
|
|
76576
76918
|
init_line_start_guard();
|
|
76577
76919
|
init_inline_pairs_guard();
|
|
76578
76920
|
init_unsupported_token_guard();
|
|
76579
|
-
var
|
|
76921
|
+
var import_grammy10 = __toESM(require_mod2(), 1);
|
|
76580
76922
|
function guardAccidentalFormatting2(markdown) {
|
|
76581
76923
|
let out = markdown;
|
|
76582
76924
|
out = guardUnsupportedTokens(out);
|
|
@@ -76833,7 +77175,7 @@ function normalizeTemporal(text4, tz, nowMs2) {
|
|
|
76833
77175
|
}
|
|
76834
77176
|
|
|
76835
77177
|
// gateway/outbound-send-path.ts
|
|
76836
|
-
var
|
|
77178
|
+
var import_grammy11 = __toESM(require_mod2(), 1);
|
|
76837
77179
|
import { statSync as statSync11 } from "fs";
|
|
76838
77180
|
import { extname } from "path";
|
|
76839
77181
|
|
|
@@ -76980,6 +77322,42 @@ import {
|
|
|
76980
77322
|
} from "node:fs";
|
|
76981
77323
|
import { join as join31 } from "node:path";
|
|
76982
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
|
|
76983
77361
|
function sha256Hex(s) {
|
|
76984
77362
|
return createHash4("sha256").update(s, "utf8").digest("hex");
|
|
76985
77363
|
}
|
|
@@ -77159,7 +77537,8 @@ function decideOutboxSweep(input) {
|
|
|
77159
77537
|
routePrefix = "",
|
|
77160
77538
|
quietMs = OUTBOX_QUIET_MS,
|
|
77161
77539
|
maxAgeMs = OUTBOX_MAX_AGE_MS,
|
|
77162
|
-
shownLedgerHit = false
|
|
77540
|
+
shownLedgerHit = false,
|
|
77541
|
+
provenanceFraming = true
|
|
77163
77542
|
} = input;
|
|
77164
77543
|
if (deliveredNonces.has(record.turnNonce))
|
|
77165
77544
|
return { action: "skip-journaled" };
|
|
@@ -77174,7 +77553,13 @@ function decideOutboxSweep(input) {
|
|
|
77174
77553
|
return { action: "skip-unroutable" };
|
|
77175
77554
|
const delayed = age > maxAgeMs;
|
|
77176
77555
|
const prefix = (delayed ? "(delayed) " : "") + routePrefix;
|
|
77177
|
-
|
|
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
|
+
};
|
|
77178
77563
|
}
|
|
77179
77564
|
function resolveOutboxChat(record, deps) {
|
|
77180
77565
|
if (record.chatId != null && record.chatId !== "") {
|
|
@@ -77469,7 +77854,8 @@ function journalExternalDelivery(args, stateDir, now = Date.now()) {
|
|
|
77469
77854
|
tgMessageId: args.tgMessageId,
|
|
77470
77855
|
ts: now,
|
|
77471
77856
|
deliverySource: args.deliverySource ?? "reply-tool",
|
|
77472
|
-
...args.replyAlreadyDeliveredThisTurn == null ? {} : { replyAlreadyDeliveredThisTurn: args.replyAlreadyDeliveredThisTurn }
|
|
77857
|
+
...args.replyAlreadyDeliveredThisTurn == null ? {} : { replyAlreadyDeliveredThisTurn: args.replyAlreadyDeliveredThisTurn },
|
|
77858
|
+
...args.framedProvenance == null ? {} : { framedProvenance: args.framedProvenance }
|
|
77473
77859
|
}, stateDir);
|
|
77474
77860
|
clearOutboxRecord(nonce, stateDir);
|
|
77475
77861
|
}
|
|
@@ -77524,6 +77910,7 @@ function queueFloodBlockedReply(input) {
|
|
|
77524
77910
|
textSha256: sha256Hex(text4),
|
|
77525
77911
|
createdAt,
|
|
77526
77912
|
source: FLOOD_QUEUED_SOURCE,
|
|
77913
|
+
audience: "user",
|
|
77527
77914
|
...input.originChatId != null ? { originChatId: input.originChatId } : {},
|
|
77528
77915
|
...input.originThreadId != null ? { originThreadId: input.originThreadId } : {}
|
|
77529
77916
|
};
|
|
@@ -78042,7 +78429,12 @@ function decideCapturedProseDelivery(args, deps) {
|
|
|
78042
78429
|
if (deps?.backstopDeliveredNonceHit?.(state3.turnId) === true) {
|
|
78043
78430
|
return { deliver: false, reason: "already-delivered" };
|
|
78044
78431
|
}
|
|
78045
|
-
return {
|
|
78432
|
+
return {
|
|
78433
|
+
deliver: true,
|
|
78434
|
+
text: text4,
|
|
78435
|
+
reason: "captured-prose",
|
|
78436
|
+
...state3.replyToolThrewThisTurn === true ? { replyToolThrewThisTurn: true } : {}
|
|
78437
|
+
};
|
|
78046
78438
|
}
|
|
78047
78439
|
function settleCapturedProseDelivery(outcome, effects) {
|
|
78048
78440
|
if (outcome === "failed") {
|
|
@@ -78179,10 +78571,23 @@ async function sendReplyChunks(deps, state3) {
|
|
|
78179
78571
|
}
|
|
78180
78572
|
const sendChunkPlainText = async (opts) => {
|
|
78181
78573
|
const plain = chunks[i].length > 0 ? chunks[i] : "\u26a0\ufe0f (a fragment could not be rendered for Telegram)";
|
|
78182
|
-
const
|
|
78183
|
-
|
|
78184
|
-
|
|
78185
|
-
|
|
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)` : "") + `
|
|
78186
78591
|
`);
|
|
78187
78592
|
};
|
|
78188
78593
|
const sendChunk = (opts, wrapped) => {
|
|
@@ -78231,6 +78636,13 @@ async function sendReplyChunks(deps, state3) {
|
|
|
78231
78636
|
else
|
|
78232
78637
|
throw retryErr;
|
|
78233
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
|
+
`);
|
|
78234
78646
|
} else if (isMessageTooLongError(err)) {
|
|
78235
78647
|
await sendChunkResplit(sendOpts);
|
|
78236
78648
|
} else if (isHtmlParseRejectError(err)) {
|
|
@@ -78243,7 +78655,7 @@ async function sendReplyChunks(deps, state3) {
|
|
|
78243
78655
|
return { threadId, previewMessageId };
|
|
78244
78656
|
}
|
|
78245
78657
|
function classifyReadBackError(err) {
|
|
78246
|
-
if (err instanceof
|
|
78658
|
+
if (err instanceof import_grammy11.GrammyError && err.error_code === 400) {
|
|
78247
78659
|
const d = (err.description || "").toLowerCase();
|
|
78248
78660
|
if (d.includes("message to edit not found"))
|
|
78249
78661
|
return "absent";
|
|
@@ -78798,10 +79210,7 @@ ${url}`;
|
|
|
78798
79210
|
const shouldReplyTo = reply_to != null && replyMode !== "off" && (replyMode === "all" || i === 0);
|
|
78799
79211
|
return {
|
|
78800
79212
|
...shouldReplyTo ? {
|
|
78801
|
-
reply_parameters:
|
|
78802
|
-
message_id: reply_to,
|
|
78803
|
-
...quoteText != null ? { quote: { text: quoteText, position: 0 } } : {}
|
|
78804
|
-
}
|
|
79213
|
+
reply_parameters: buildReplyParameters(reply_to, quoteText)
|
|
78805
79214
|
} : {},
|
|
78806
79215
|
...tid != null ? { message_thread_id: tid } : {},
|
|
78807
79216
|
...disableLinkPreview ? { link_preview_options: { is_disabled: true } } : {},
|
|
@@ -78860,7 +79269,7 @@ ${url}`;
|
|
|
78860
79269
|
opts.message_thread_id = tid;
|
|
78861
79270
|
else
|
|
78862
79271
|
delete opts.message_thread_id;
|
|
78863
|
-
return lockedBot.api.sendVoice(chat_id, new
|
|
79272
|
+
return lockedBot.api.sendVoice(chat_id, new import_grammy11.InputFile(Buffer.from(oggBytes)), opts);
|
|
78864
79273
|
}, { threadId, chat_id, verb: "sendVoice" });
|
|
78865
79274
|
sentIds.push(sentVoice.message_id);
|
|
78866
79275
|
logOutbound("reply", chat_id, sentVoice.message_id, voiceOutPlan?.ttsChunks[v]?.length ?? 0, `voice-note=${v + 1}/${voiceOggs.length}`);
|
|
@@ -78933,12 +79342,12 @@ ${url}`;
|
|
|
78933
79342
|
...replyParams,
|
|
78934
79343
|
...tid != null ? { message_thread_id: tid } : {}
|
|
78935
79344
|
};
|
|
78936
|
-
return lockedBot.api.sendDocument(chat_id, new
|
|
79345
|
+
return lockedBot.api.sendDocument(chat_id, new import_grammy11.InputFile(f), baseOpts);
|
|
78937
79346
|
}, { threadId, chat_id, verb: "sendDocument" });
|
|
78938
79347
|
if (allPhotos) {
|
|
78939
79348
|
const media = files.map((f) => ({
|
|
78940
79349
|
type: "photo",
|
|
78941
|
-
media: new
|
|
79350
|
+
media: new import_grammy11.InputFile(f)
|
|
78942
79351
|
}));
|
|
78943
79352
|
let sent;
|
|
78944
79353
|
try {
|
|
@@ -78971,7 +79380,7 @@ ${url}`;
|
|
|
78971
79380
|
sentIds.push(m.message_id);
|
|
78972
79381
|
} else {
|
|
78973
79382
|
for (const f of files) {
|
|
78974
|
-
const input = new
|
|
79383
|
+
const input = new import_grammy11.InputFile(f);
|
|
78975
79384
|
const isPhoto = sendableAsPhoto(f);
|
|
78976
79385
|
let sent;
|
|
78977
79386
|
try {
|
|
@@ -79069,10 +79478,11 @@ async function deliverCapturedProse(deps, args) {
|
|
|
79069
79478
|
} = deps;
|
|
79070
79479
|
const { chatId, threadId, statusKeyStr, registryKey, originTurnId, text: text4, turnDurationMs } = args;
|
|
79071
79480
|
const now = Date.now();
|
|
79481
|
+
const framed = shouldFrameReplyThrow({ replyToolThrewThisTurn: args.replyToolThrewThisTurn }, { frameEnabled: process.env.SWITCHROOM_TG_OUTBOX_PROVENANCE_FRAMING !== "0" });
|
|
79072
79482
|
let outcome;
|
|
79073
79483
|
const already = outboundDedup.check(chatId, threadId, text4, now, registryKey);
|
|
79074
79484
|
if (already == null) {
|
|
79075
|
-
let out = normalizeParagraphBreaks(repairEscapedWhitespace(text4));
|
|
79485
|
+
let out = normalizeParagraphBreaks(repairEscapedWhitespace(framed ? applyReplyThrowFraming(text4) : text4));
|
|
79076
79486
|
out = redactOutboundText(out, "captured_prose");
|
|
79077
79487
|
const chunks = splitMarkdownChunks(out, RICH_MESSAGE_MAX_CHARS);
|
|
79078
79488
|
const sentIds = [];
|
|
@@ -79102,9 +79512,24 @@ async function deliverCapturedProse(deps, args) {
|
|
|
79102
79512
|
} catch {}
|
|
79103
79513
|
}
|
|
79104
79514
|
outboundDedup.record(chatId, threadId, text4, now, registryKey);
|
|
79105
|
-
journalExternalDelivery({
|
|
79515
|
+
journalExternalDelivery({
|
|
79516
|
+
turnNonce: originTurnId,
|
|
79517
|
+
text: text4,
|
|
79518
|
+
tgMessageId: sentIds[sentIds.length - 1],
|
|
79519
|
+
replyAlreadyDeliveredThisTurn: false,
|
|
79520
|
+
...framed ? { framedProvenance: "reply-throw" } : {}
|
|
79521
|
+
});
|
|
79106
79522
|
process.stderr.write(`telegram gateway: captured-prose delivery \u2014 sent ${out.length} chars recovered from ` + `transcript scan (chat=${chatId} origin=${originTurnId})
|
|
79107
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
|
+
}
|
|
79108
79533
|
outcome = "sent";
|
|
79109
79534
|
} catch (err) {
|
|
79110
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})
|
|
@@ -79142,7 +79567,7 @@ async function deliverCapturedProse(deps, args) {
|
|
|
79142
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})
|
|
79143
79568
|
`);
|
|
79144
79569
|
const plain = redactOutboundText(text4, "captured_prose");
|
|
79145
|
-
const plainChunks =
|
|
79570
|
+
const plainChunks = splitPlainTextToCap(plain);
|
|
79146
79571
|
try {
|
|
79147
79572
|
let liveThreadId = threadId;
|
|
79148
79573
|
const plainSentIds = [];
|
|
@@ -79549,7 +79974,7 @@ class LivenessTracker {
|
|
|
79549
79974
|
// gateway/outbound-send-path.ts
|
|
79550
79975
|
init_format();
|
|
79551
79976
|
init_text_voice_scrub();
|
|
79552
|
-
var
|
|
79977
|
+
var import_grammy12 = __toESM(require_mod2(), 1);
|
|
79553
79978
|
init_format();
|
|
79554
79979
|
init_rich_send();
|
|
79555
79980
|
function normalizeOutboundBody2(rawText, site, redact2, opts = {}) {
|
|
@@ -81422,6 +81847,7 @@ function handleSessionEvent(deps, ev) {
|
|
|
81422
81847
|
registryKey: turn.registryKey ?? null,
|
|
81423
81848
|
originTurnId: turn.turnId,
|
|
81424
81849
|
text: proseDecision.text,
|
|
81850
|
+
replyToolThrewThisTurn: proseDecision.replyToolThrewThisTurn === true,
|
|
81425
81851
|
turnDurationMs
|
|
81426
81852
|
});
|
|
81427
81853
|
} else {
|
|
@@ -82942,7 +83368,7 @@ async function loadFromAuthBroker(options = {}) {
|
|
|
82942
83368
|
}
|
|
82943
83369
|
|
|
82944
83370
|
// gateway/folder-picker-handler.ts
|
|
82945
|
-
var
|
|
83371
|
+
var import_grammy13 = __toESM(require_mod2(), 1);
|
|
82946
83372
|
|
|
82947
83373
|
// ../src/drive/folder-picker.ts
|
|
82948
83374
|
var DRIVE_ID_RE2 = /^[A-Za-z0-9_-]+$/;
|
|
@@ -83261,7 +83687,7 @@ async function recordGrantAndConfirm(ctx, deps, parsed) {
|
|
|
83261
83687
|
const confirmText = `\u2705 Granted ${deps.agentName} access to folder ${parsed.folder_id}
|
|
83262
83688
|
` + `Scope: \`${scope}\`
|
|
83263
83689
|
` + `Revoke with: \`/approvals revoke ${decisionId}\``;
|
|
83264
|
-
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}`);
|
|
83265
83691
|
try {
|
|
83266
83692
|
await ctx.editMessageText({ markdown: confirmText }, {
|
|
83267
83693
|
reply_markup: kb
|
|
@@ -83270,7 +83696,7 @@ async function recordGrantAndConfirm(ctx, deps, parsed) {
|
|
|
83270
83696
|
await ctx.answerCallbackQuery({ text: "Allowed" });
|
|
83271
83697
|
}
|
|
83272
83698
|
function toKeyboard(spec) {
|
|
83273
|
-
const kb = new
|
|
83699
|
+
const kb = new import_grammy13.InlineKeyboard;
|
|
83274
83700
|
for (let r = 0;r < spec.rows.length; r++) {
|
|
83275
83701
|
const row = spec.rows[r];
|
|
83276
83702
|
for (const btn of row) {
|
|
@@ -85346,7 +85772,9 @@ var HINDSIGHT_DEFAULT_RECALL_MAX_CONCURRENT = 8;
|
|
|
85346
85772
|
var HINDSIGHT_DEFAULT_REFLECT_WALL_TIMEOUT_S = 600;
|
|
85347
85773
|
var HINDSIGHT_DEFAULT_CONSOLIDATION_MAX_MEMORIES_PER_ROUND = 250;
|
|
85348
85774
|
var HINDSIGHT_DEFAULT_CONSOLIDATION_SLOT_LIMIT = 6;
|
|
85349
|
-
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";
|
|
85350
85778
|
var HINDSIGHT_DEFAULT_RECENCY_DECAY_FUNCTION = "exponential";
|
|
85351
85779
|
var HINDSIGHT_DEFAULT_RECENCY_DECAY_HALFLIFE_DAYS = 30;
|
|
85352
85780
|
var HINDSIGHT_PERF_DEFAULTS_UNGATED = [
|
|
@@ -85392,8 +85820,8 @@ var HINDSIGHT_PERF_DEFAULTS_UNGATED = [
|
|
|
85392
85820
|
String(HINDSIGHT_DEFAULT_REFLECT_WALL_TIMEOUT_S)
|
|
85393
85821
|
],
|
|
85394
85822
|
[
|
|
85395
|
-
"
|
|
85396
|
-
String(
|
|
85823
|
+
"HINDSIGHT_API_WORKER_CONSOLIDATION_RESERVED_SLOTS",
|
|
85824
|
+
String(HINDSIGHT_DEFAULT_CONSOLIDATION_RESERVED_SLOTS)
|
|
85397
85825
|
],
|
|
85398
85826
|
[
|
|
85399
85827
|
"HINDSIGHT_API_WORKER_CONSOLIDATION_SLOT_LIMIT",
|
|
@@ -85410,6 +85838,10 @@ var HINDSIGHT_PERF_DEFAULTS_UNGATED = [
|
|
|
85410
85838
|
[
|
|
85411
85839
|
"HINDSIGHT_API_RECENCY_DECAY_HALFLIFE_DAYS",
|
|
85412
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)
|
|
85413
85845
|
]
|
|
85414
85846
|
];
|
|
85415
85847
|
var HINDSIGHT_PERF_DEFAULTS_GPU = [
|
|
@@ -85430,22 +85862,41 @@ var HINDSIGHT_PERF_DEFAULTS_LOCAL_LLM = [
|
|
|
85430
85862
|
String(HINDSIGHT_DEFAULT_CONSOLIDATION_LLM_MAX_CONCURRENT)
|
|
85431
85863
|
],
|
|
85432
85864
|
["HINDSIGHT_API_LLM_STRICT_SCHEMA", HINDSIGHT_DEFAULT_LLM_STRICT_SCHEMA],
|
|
85433
|
-
["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]
|
|
85434
85867
|
];
|
|
85435
85868
|
var HINDSIGHT_PERF_OVERRIDE_ONLY_KEYS = new Set([
|
|
85436
85869
|
"HINDSIGHT_API_WORKER_CONSOLIDATION_BANK_PRIORITY",
|
|
85437
85870
|
"HINDSIGHT_CE_DECISIVE_RELATIVE_GAP",
|
|
85438
85871
|
"HINDSIGHT_API_RECENCY_DECAY_LINEAR_WINDOW_DAYS",
|
|
85439
85872
|
"HINDSIGHT_API_WORKER_MAX_SLOTS",
|
|
85440
|
-
"
|
|
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"
|
|
85441
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
|
+
]));
|
|
85442
85891
|
var HINDSIGHT_PERF_ENV_KEYS = new Set([
|
|
85443
85892
|
...[
|
|
85444
85893
|
...HINDSIGHT_PERF_DEFAULTS_UNGATED,
|
|
85445
85894
|
...HINDSIGHT_PERF_DEFAULTS_GPU,
|
|
85446
85895
|
...HINDSIGHT_PERF_DEFAULTS_LOCAL_LLM
|
|
85447
85896
|
].map(([k]) => k),
|
|
85448
|
-
...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()
|
|
85449
85900
|
]);
|
|
85450
85901
|
|
|
85451
85902
|
// ../src/setup/hindsight-context-budget.ts
|
|
@@ -89841,7 +90292,9 @@ function createIpcServer(options) {
|
|
|
89841
90292
|
case "rollout_status_edit":
|
|
89842
90293
|
if (onRolloutStatusEdit) {
|
|
89843
90294
|
try {
|
|
89844
|
-
onRolloutStatusEdit(client3, msg)
|
|
90295
|
+
Promise.resolve(onRolloutStatusEdit(client3, msg)).catch((err) => {
|
|
90296
|
+
log(`rollout_status_edit handler rejected (client=${client3.id}): ${err.message}`);
|
|
90297
|
+
});
|
|
89845
90298
|
} catch (err) {
|
|
89846
90299
|
log(`rollout_status_edit handler threw (client=${client3.id}): ${err.message}`);
|
|
89847
90300
|
}
|
|
@@ -90027,6 +90480,58 @@ function createIpcServer(options) {
|
|
|
90027
90480
|
return ipcServer;
|
|
90028
90481
|
}
|
|
90029
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
|
+
|
|
90030
90535
|
// ../src/drive/diff-preview.ts
|
|
90031
90536
|
function buildDiffPreview(input) {
|
|
90032
90537
|
validateInput(input);
|
|
@@ -90474,7 +90979,7 @@ async function handleRequestMs365Approval(client3, msg, deps) {
|
|
|
90474
90979
|
|
|
90475
90980
|
// gateway/diff-preview-card.ts
|
|
90476
90981
|
init_format();
|
|
90477
|
-
var
|
|
90982
|
+
var import_grammy14 = __toESM(require_mod2(), 1);
|
|
90478
90983
|
var REQUEST_ID_RE = /^[0-9a-f]{32}$/;
|
|
90479
90984
|
var PENDING_FILE_ID_SENTINEL = "pending-create";
|
|
90480
90985
|
function buildDiffPreviewCard(input) {
|
|
@@ -90492,7 +90997,7 @@ function buildDiffPreviewCard(input) {
|
|
|
90492
90997
|
}
|
|
90493
90998
|
const text4 = bodyLines.join(`
|
|
90494
90999
|
`);
|
|
90495
|
-
const kb = new
|
|
91000
|
+
const kb = new import_grammy14.InlineKeyboard;
|
|
90496
91001
|
const ROW_BREAK_AFTER = [
|
|
90497
91002
|
"apply_suggestion"
|
|
90498
91003
|
];
|
|
@@ -90975,6 +91480,23 @@ function loadObligations(path3, fs3) {
|
|
|
90975
91480
|
return [];
|
|
90976
91481
|
return env.obligations.filter(isObligationRow).map(sanitizeCapturedDelivery);
|
|
90977
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
|
+
}
|
|
90978
91500
|
function persistObligations(path3, fs3, snapshot, log = (l) => process.stderr.write(l)) {
|
|
90979
91501
|
const env = { v: 1, obligations: [...snapshot] };
|
|
90980
91502
|
const tmp = path3 + ".tmp";
|
|
@@ -97505,6 +98027,7 @@ function readAndFilter(raw, filters, limit) {
|
|
|
97505
98027
|
}
|
|
97506
98028
|
|
|
97507
98029
|
// gateway/update-announce.ts
|
|
98030
|
+
init_status_no_truncate();
|
|
97508
98031
|
var DEFAULT_LOOKBACK_MS = 10 * 60 * 1000;
|
|
97509
98032
|
function readLastTerminalUpdateAudit(opts = {}) {
|
|
97510
98033
|
const path3 = opts.auditLogPath ?? defaultAuditLogPath();
|
|
@@ -97565,7 +98088,10 @@ function renderUpdateOutcomeLine(entry) {
|
|
|
97565
98088
|
const stderrTail = (entry.stderr_tail ?? entry.error ?? "").slice(-400);
|
|
97566
98089
|
const opStep = entry.op;
|
|
97567
98090
|
const hint = recoveryHint(entry.install_context?.install_type);
|
|
97568
|
-
const lines = [
|
|
98091
|
+
const lines = [
|
|
98092
|
+
`\u274c update failed at ${opStep}: ${stderrTail || "(no stderr captured)"}`,
|
|
98093
|
+
`${NESTED_PREFIX}Recovery: ${hint}`
|
|
98094
|
+
];
|
|
97569
98095
|
return lines.join(`
|
|
97570
98096
|
`);
|
|
97571
98097
|
}
|
|
@@ -99894,8 +100420,37 @@ async function sweepOutbox(deps) {
|
|
|
99894
100420
|
return summary;
|
|
99895
100421
|
const deliveredNonces = readDeliveredNonces(deps.stateDir);
|
|
99896
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;
|
|
99897
100427
|
for (const record2 of records) {
|
|
99898
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
|
+
}
|
|
99899
100454
|
const resolved = resolveOutboxChat(record2, {
|
|
99900
100455
|
registryChainLookup: (anchorContent) => {
|
|
99901
100456
|
const taskId = extractTaskId(anchorContent);
|
|
@@ -99913,7 +100468,8 @@ async function sweepOutbox(deps) {
|
|
|
99913
100468
|
routable: resolved != null,
|
|
99914
100469
|
routePrefix,
|
|
99915
100470
|
quietMs: deps.quietMs ?? OUTBOX_QUIET_MS,
|
|
99916
|
-
shownLedgerHit: isShownBlock(record2.turnNonce, record2.text, deps.stateDir)
|
|
100471
|
+
shownLedgerHit: isShownBlock(record2.turnNonce, record2.text, deps.stateDir),
|
|
100472
|
+
provenanceFraming: provenanceFramingEnabled
|
|
99917
100473
|
});
|
|
99918
100474
|
if (decision.action !== "send" && decision.action !== "send-delayed") {
|
|
99919
100475
|
summary.skipped++;
|
|
@@ -99956,8 +100512,19 @@ async function sweepOutbox(deps) {
|
|
|
99956
100512
|
tgMessageId: sendResult.messageId,
|
|
99957
100513
|
ts: now,
|
|
99958
100514
|
deliverySource: "sweep",
|
|
99959
|
-
replyAlreadyDeliveredThisTurn: record2.replyAlreadyDeliveredThisTurn === true
|
|
100515
|
+
replyAlreadyDeliveredThisTurn: record2.replyAlreadyDeliveredThisTurn === true,
|
|
100516
|
+
...decision.framedProvenance != null ? { framedProvenance: decision.framedProvenance } : {}
|
|
99960
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
|
+
}
|
|
99961
100528
|
if (deps.recordOutbound != null && sendResult.chunks.length > 0) {
|
|
99962
100529
|
try {
|
|
99963
100530
|
deps.recordOutbound(resolvedChat.chatId, resolvedChat.threadId, sendResult.chunks.map((c) => c.messageId), sendResult.chunks.map((c) => c.text));
|
|
@@ -99977,6 +100544,9 @@ async function sweepOutbox(deps) {
|
|
|
99977
100544
|
}
|
|
99978
100545
|
return summary;
|
|
99979
100546
|
}
|
|
100547
|
+
function turnIdFromNonce(nonce) {
|
|
100548
|
+
return /^-?\d+:[^#]*#\d+$/.test(nonce) ? nonce : null;
|
|
100549
|
+
}
|
|
99980
100550
|
function probeFloodWindow(probe) {
|
|
99981
100551
|
if (probe == null)
|
|
99982
100552
|
return 0;
|
|
@@ -100017,15 +100587,29 @@ function createSweepBackoff(cfg = {}) {
|
|
|
100017
100587
|
failures: () => failures
|
|
100018
100588
|
};
|
|
100019
100589
|
}
|
|
100020
|
-
|
|
100021
|
-
|
|
100022
|
-
|
|
100023
|
-
|
|
100024
|
-
|
|
100025
|
-
|
|
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;
|
|
100026
100602
|
}
|
|
100027
|
-
throw err;
|
|
100028
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;
|
|
100029
100613
|
}
|
|
100030
100614
|
function createOutboxSend(deps) {
|
|
100031
100615
|
return async (chatId, threadId, text4) => {
|
|
@@ -100040,14 +100624,16 @@ function createOutboxSend(deps) {
|
|
|
100040
100624
|
for (let idx = 0;idx < chunks.length; idx++) {
|
|
100041
100625
|
const chunk2 = chunks[idx];
|
|
100042
100626
|
const isLast = idx === chunks.length - 1;
|
|
100627
|
+
const chunkState = newChunkSendState();
|
|
100043
100628
|
const res = await retryWithThreadFallback(deps.retry, (tid) => {
|
|
100044
100629
|
const base = tid != null ? { message_thread_id: tid } : {};
|
|
100045
100630
|
const opts = isLast && replyMarkup != null ? { ...base, reply_markup: replyMarkup } : base;
|
|
100046
|
-
return sendChunkRich(bot, chatId, chunk2, opts);
|
|
100631
|
+
return sendChunkRich(bot, chatId, chunk2, opts, chunkState);
|
|
100047
100632
|
}, { threadId: threadId ?? undefined, chat_id: chatId, verb: "outbox-sweep.sendMessage" });
|
|
100048
|
-
const
|
|
100049
|
-
|
|
100050
|
-
|
|
100633
|
+
for (const sent of res ?? []) {
|
|
100634
|
+
if (sent.message_id != null)
|
|
100635
|
+
landed.push({ messageId: sent.message_id, text: sent.text });
|
|
100636
|
+
}
|
|
100051
100637
|
}
|
|
100052
100638
|
return {
|
|
100053
100639
|
messageId: landed.length > 0 ? landed[landed.length - 1].messageId : undefined,
|
|
@@ -100055,6 +100641,54 @@ function createOutboxSend(deps) {
|
|
|
100055
100641
|
};
|
|
100056
100642
|
};
|
|
100057
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
|
+
}
|
|
100058
100692
|
function startOutboxSweep(deps) {
|
|
100059
100693
|
if (!deps.isGatewayMain || process.env.SWITCHROOM_TG_OUTBOX_DELIVERY === "0")
|
|
100060
100694
|
return;
|
|
@@ -100071,15 +100705,11 @@ function startOutboxSweep(deps) {
|
|
|
100071
100705
|
...deps.resolveReplyMarkup != null ? { resolveReplyMarkup: deps.resolveReplyMarkup } : {}
|
|
100072
100706
|
});
|
|
100073
100707
|
const backoff = deps.backoff ?? createSweepBackoff();
|
|
100074
|
-
|
|
100075
|
-
|
|
100076
|
-
|
|
100077
|
-
|
|
100078
|
-
|
|
100079
|
-
const now = Date.now();
|
|
100080
|
-
if (!backoff.ready(now))
|
|
100081
|
-
return;
|
|
100082
|
-
sweepOutbox({
|
|
100708
|
+
const tick3 = createOutboxSweepTick({
|
|
100709
|
+
getBot: deps.getBot,
|
|
100710
|
+
backoff,
|
|
100711
|
+
log: deps.log,
|
|
100712
|
+
runSweep: () => sweepOutbox({
|
|
100083
100713
|
stateDir: deps.stateDir,
|
|
100084
100714
|
log: deps.log,
|
|
100085
100715
|
send,
|
|
@@ -100093,36 +100723,18 @@ function startOutboxSweep(deps) {
|
|
|
100093
100723
|
const turnKey3 = resolveSubagentOriginTurnKey(db3, taskId);
|
|
100094
100724
|
return turnKey3 == null ? null : chatFromTurnKey(turnKey3);
|
|
100095
100725
|
}
|
|
100096
|
-
})
|
|
100097
|
-
|
|
100098
|
-
const at = Date.now();
|
|
100099
|
-
if (at - lastDeferLogAt >= OUTBOX_SWEEP_DEFER_LOG_INTERVAL_MS) {
|
|
100100
|
-
lastDeferLogAt = at;
|
|
100101
|
-
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)
|
|
100102
|
-
`);
|
|
100103
|
-
}
|
|
100104
|
-
return;
|
|
100105
|
-
}
|
|
100106
|
-
if (summary.sendFailures > 0) {
|
|
100107
|
-
const delay = backoff.noteFailure(Date.now());
|
|
100108
|
-
deps.log?.(`outbox-sweep: ${summary.sendFailures} send failure(s) \u2014 backing off ` + `${Math.round(delay / 1000)}s before the next sweep (attempt ${backoff.failures()})
|
|
100109
|
-
`);
|
|
100110
|
-
} else {
|
|
100111
|
-
backoff.noteSuccess();
|
|
100112
|
-
}
|
|
100113
|
-
}).catch((err) => deps.log?.(`outbox-sweep: tick failed: ${err.message}
|
|
100114
|
-
`));
|
|
100115
|
-
};
|
|
100726
|
+
})
|
|
100727
|
+
});
|
|
100116
100728
|
const timer3 = setInterval(tick3, OUTBOX_SWEEP_INTERVAL_MS);
|
|
100117
100729
|
timer3.unref?.();
|
|
100118
100730
|
return timer3;
|
|
100119
100731
|
}
|
|
100120
100732
|
|
|
100121
100733
|
// ../src/build-info.ts
|
|
100122
|
-
var VERSION2 = "0.19.
|
|
100123
|
-
var COMMIT_SHA = "
|
|
100124
|
-
var COMMIT_DATE = "2026-08-
|
|
100125
|
-
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;
|
|
100126
100738
|
var COMMITS_AHEAD_OF_TAG = 0;
|
|
100127
100739
|
|
|
100128
100740
|
// gateway/boot-version.ts
|
|
@@ -100167,10 +100779,10 @@ function composeBootVersionString(inputs) {
|
|
|
100167
100779
|
}
|
|
100168
100780
|
|
|
100169
100781
|
// gateway/unhandled-rejection-policy.ts
|
|
100170
|
-
var
|
|
100782
|
+
var import_grammy15 = __toESM(require_mod2(), 1);
|
|
100171
100783
|
function classifyRejection(err, opts = {}) {
|
|
100172
|
-
const isGrammy = opts.isGrammyError != null ? opts.isGrammyError(err) : err instanceof
|
|
100173
|
-
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;
|
|
100174
100786
|
if (isHttp)
|
|
100175
100787
|
return "log_only";
|
|
100176
100788
|
if (err instanceof Error && err.message === FLOOD_WAIT_ACTIVE)
|
|
@@ -102820,7 +103432,8 @@ var obligationStoreFs = {
|
|
|
102820
103432
|
renameSync: (a, b) => renameSync27(a, b),
|
|
102821
103433
|
existsSync: (p) => existsSync57(p),
|
|
102822
103434
|
fsyncFileSync: fsyncPathSync,
|
|
102823
|
-
fsyncDirSync: fsyncPathSync
|
|
103435
|
+
fsyncDirSync: fsyncPathSync,
|
|
103436
|
+
unlinkSync: unlinkSync31
|
|
102824
103437
|
};
|
|
102825
103438
|
var obligationLedger = new ObligationLedger(OBLIGATION_REPRESENT_MAX, {
|
|
102826
103439
|
onChange: STATIC || !OBLIGATION_LEDGER_ENABLED ? undefined : (snapshot) => persistObligations(OBLIGATION_STORE_PATH, obligationStoreFs, snapshot)
|
|
@@ -102832,7 +103445,8 @@ if (isGatewayMain && !STATIC && OBLIGATION_LEDGER_ENABLED) {
|
|
|
102832
103445
|
process.stderr.write(`telegram gateway: obligation-ledger hydrated ${restored.length} open obligation(s) from ${OBLIGATION_STORE_PATH}
|
|
102833
103446
|
`);
|
|
102834
103447
|
}
|
|
102835
|
-
}
|
|
103448
|
+
} else if (isGatewayMain)
|
|
103449
|
+
removeUnmaintainedSnapshot(OBLIGATION_STORE_PATH, obligationStoreFs, `static=${STATIC} enabled=${OBLIGATION_LEDGER_ENABLED}`);
|
|
102836
103450
|
var obligationEscalateInFlight = new Set;
|
|
102837
103451
|
var capturedResume = createCapturedResumeDispatcher({
|
|
102838
103452
|
deliverAnswer: (a) => deliverAnswer(a),
|
|
@@ -104081,7 +104695,7 @@ function wrapBootCardApi(threadId) {
|
|
|
104081
104695
|
await lockedBot.api.editMessageText(cid, mid, richMessage2(text5), editOpts);
|
|
104082
104696
|
return "edited";
|
|
104083
104697
|
} catch (err) {
|
|
104084
|
-
const desc = err instanceof
|
|
104698
|
+
const desc = err instanceof import_grammy17.GrammyError ? err.description : err instanceof Error ? err.message : String(err);
|
|
104085
104699
|
if (typeof desc === "string" && desc.toLowerCase().includes("not modified"))
|
|
104086
104700
|
return "edited";
|
|
104087
104701
|
return "gone";
|
|
@@ -105920,7 +106534,7 @@ if (isGatewayMain && inboundSpool != null) {
|
|
|
105920
106534
|
}
|
|
105921
106535
|
var pendingPermissionBuffer = createPendingPermissionBuffer();
|
|
105922
106536
|
function buildPermissionActionRow(requestId, showAlways) {
|
|
105923
|
-
const kb = new
|
|
106537
|
+
const kb = new import_grammy17.InlineKeyboard().text("\u274C Deny", `perm:deny:${requestId}`).text("\u2705 Allow", `perm:allow:${requestId}`);
|
|
105924
106538
|
if (showAlways)
|
|
105925
106539
|
kb.text("\uD83D\uDD01 Always\u2026", `perm:always:${requestId}`);
|
|
105926
106540
|
return kb;
|
|
@@ -106480,7 +107094,7 @@ if (isGatewayMain)
|
|
|
106480
107094
|
}
|
|
106481
107095
|
},
|
|
106482
107096
|
postAttachment: async (args) => {
|
|
106483
|
-
const input = new
|
|
107097
|
+
const input = new import_grammy17.InputFile(Buffer.from(args.content, "utf8"), args.filename);
|
|
106484
107098
|
await robustApiCall(() => bot.api.sendDocument(args.chatId, input, {
|
|
106485
107099
|
...args.threadId !== undefined ? { message_thread_id: args.threadId } : {}
|
|
106486
107100
|
}), {
|
|
@@ -106565,20 +107179,19 @@ if (isGatewayMain)
|
|
|
106565
107179
|
} catch {}
|
|
106566
107180
|
}
|
|
106567
107181
|
},
|
|
106568
|
-
onRolloutStatusEdit(
|
|
106569
|
-
|
|
106570
|
-
|
|
106571
|
-
|
|
106572
|
-
|
|
106573
|
-
|
|
106574
|
-
|
|
106575
|
-
|
|
106576
|
-
|
|
106577
|
-
|
|
106578
|
-
`
|
|
106579
|
-
|
|
106580
|
-
}
|
|
106581
|
-
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);
|
|
106582
107195
|
},
|
|
106583
107196
|
onInjectInbound(_client, msg) {
|
|
106584
107197
|
if (!isCronInjectFire(msg.inbound.meta))
|
|
@@ -107278,7 +107891,7 @@ async function executeAskUser(rawArgs) {
|
|
|
107278
107891
|
}
|
|
107279
107892
|
}
|
|
107280
107893
|
const askId = generateAskId();
|
|
107281
|
-
const keyboard = new
|
|
107894
|
+
const keyboard = new import_grammy17.InlineKeyboard;
|
|
107282
107895
|
for (let i = 0;i < args.options.length; i++) {
|
|
107283
107896
|
keyboard.text(args.options[i], encodeAskCallback(askId, i));
|
|
107284
107897
|
if (i < args.options.length - 1)
|
|
@@ -107926,7 +108539,6 @@ if (isGatewayMain && !STATIC && FEED_HEARTBEAT_ENABLED) {
|
|
|
107926
108539
|
}
|
|
107927
108540
|
var HANDBACK_PRETURN_ENABLED = !STATIC && process.env.SWITCHROOM_HANDBACK_PRETURN !== "0";
|
|
107928
108541
|
var HANDBACK_PRETURN_HTML = "\uD83E\uDD1D Reading the worker\u2019s results\u2026";
|
|
107929
|
-
var HANDBACK_PRETURN_ORPHAN_HTML = "\uD83E\uDD1D A background worker finished, but the handback never started \u2014 it may need a nudge.";
|
|
107930
108542
|
async function openHandbackPreTurnCard(chatId, threadId) {
|
|
107931
108543
|
if (STATIC)
|
|
107932
108544
|
return null;
|
|
@@ -107946,24 +108558,23 @@ async function openHandbackPreTurnCard(chatId, threadId) {
|
|
|
107946
108558
|
return null;
|
|
107947
108559
|
}
|
|
107948
108560
|
}
|
|
107949
|
-
|
|
107950
|
-
|
|
107951
|
-
chat_id:
|
|
107952
|
-
...
|
|
107953
|
-
verb: "handback-preturn.orphan-
|
|
107954
|
-
})
|
|
107955
|
-
|
|
107956
|
-
|
|
107957
|
-
return;
|
|
107958
|
-
});
|
|
107959
|
-
}
|
|
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
|
+
});
|
|
107960
108569
|
var handbackPreturnSignal = createHandbackPreturnSignal({
|
|
107961
108570
|
chatKey: (chatId, threadId) => chatKey2(chatId, threadId),
|
|
107962
108571
|
deriveTurnId: (chatId, threadId, messageId) => deriveTurnId2(chatId, threadId, messageId),
|
|
107963
108572
|
startTypingLoop: (chatId, threadId) => startTurnTypingLoop(chatId, threadId),
|
|
107964
108573
|
stopTypingLoop: (chatId, threadId) => stopTurnTypingLoop(chatId, threadId),
|
|
107965
108574
|
openCard: openHandbackPreTurnCard,
|
|
107966
|
-
|
|
108575
|
+
deleteCard: handbackOrphanRecovery.deleteCard,
|
|
108576
|
+
reinjectHandback: handbackOrphanRecovery.reinjectHandback,
|
|
108577
|
+
escalateOrphan: handbackOrphanRecovery.escalateOrphan,
|
|
107967
108578
|
writeCardRecord: (record2) => {
|
|
107968
108579
|
if (!activityCardPersistEnabled)
|
|
107969
108580
|
return;
|
|
@@ -109732,7 +110343,7 @@ function buildModelDeps(restartCtx) {
|
|
|
109732
110343
|
function modelMenuReplyMarkup(reply) {
|
|
109733
110344
|
if (!reply.keyboard)
|
|
109734
110345
|
return;
|
|
109735
|
-
const kb = new
|
|
110346
|
+
const kb = new import_grammy17.InlineKeyboard;
|
|
109736
110347
|
for (const row of reply.keyboard) {
|
|
109737
110348
|
for (const btn of row)
|
|
109738
110349
|
kb.text(btn.text, btn.callback_data);
|
|
@@ -109872,7 +110483,7 @@ function buildEffortDeps() {
|
|
|
109872
110483
|
function effortMenuReplyMarkup(reply) {
|
|
109873
110484
|
if (!reply.keyboard)
|
|
109874
110485
|
return;
|
|
109875
|
-
const kb = new
|
|
110486
|
+
const kb = new import_grammy17.InlineKeyboard;
|
|
109876
110487
|
for (const row of reply.keyboard) {
|
|
109877
110488
|
for (const btn of row)
|
|
109878
110489
|
kb.text(btn.text, btn.callback_data);
|
|
@@ -111525,7 +112136,7 @@ _Google accounts are connected from the host CLI._`, { html: true });
|
|
|
111525
112136
|
return;
|
|
111526
112137
|
}
|
|
111527
112138
|
const appNote = started.source === "default" ? "_Using switchroom's shipped Microsoft app._" : "_Using your configured Microsoft app._";
|
|
111528
|
-
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}`);
|
|
111529
112140
|
const sent = await ctx.replyWithRichMessage(richMessage2(`\uD83D\uDD17 **Connect a Microsoft account**
|
|
111530
112141
|
|
|
111531
112142
|
` + `1. Tap **Sign in to Microsoft** below.
|
|
@@ -111661,7 +112272,7 @@ Send \`/auth ${parsed.provider} cancel\` to abort.`, { html: true });
|
|
|
111661
112272
|
tz: process.env.SWITCHROOM_TIMEZONE ?? process.env.TZ
|
|
111662
112273
|
});
|
|
111663
112274
|
if (reply.keyboard && reply.keyboard.length > 0) {
|
|
111664
|
-
const kb = new
|
|
112275
|
+
const kb = new import_grammy17.InlineKeyboard;
|
|
111665
112276
|
for (let i = 0;i < reply.keyboard.length; i++) {
|
|
111666
112277
|
const row = reply.keyboard[i];
|
|
111667
112278
|
for (const b of row) {
|
|
@@ -111803,7 +112414,7 @@ Send \`/auth ${parsed.provider} cancel\` to abort.`, { html: true });
|
|
|
111803
112414
|
}
|
|
111804
112415
|
lines.push("");
|
|
111805
112416
|
const denials = readRecentDenialsForAgent(targetAgent, 604800000, 5);
|
|
111806
|
-
const denialKeyboard = new
|
|
112417
|
+
const denialKeyboard = new import_grammy17.InlineKeyboard;
|
|
111807
112418
|
if (denials.length > 0) {
|
|
111808
112419
|
lines.push("**Recent denials (last 7d):**");
|
|
111809
112420
|
for (const d of denials) {
|
|
@@ -111851,7 +112462,7 @@ Send \`/auth ${parsed.provider} cancel\` to abort.`, { html: true });
|
|
|
111851
112462
|
byAgent.set(g.agent_slug, list3);
|
|
111852
112463
|
}
|
|
111853
112464
|
const lines = ["**\uD83D\uDCDC Active grants**", ""];
|
|
111854
|
-
const keyboard = new
|
|
112465
|
+
const keyboard = new import_grammy17.InlineKeyboard;
|
|
111855
112466
|
for (const [agentName3, agentGrants] of byAgent) {
|
|
111856
112467
|
lines.push(`**${escapeHtmlForTg2(agentName3)}:**`);
|
|
111857
112468
|
for (const g of agentGrants) {
|
|
@@ -112048,7 +112659,7 @@ Send \`/auth ${parsed.provider} cancel\` to abort.`, { html: true });
|
|
|
112048
112659
|
if (ctx.chat?.type !== "private") {
|
|
112049
112660
|
kbRows = kbRows.filter((row) => !row.some((b) => b.callbackData?.startsWith("auth:use:")));
|
|
112050
112661
|
}
|
|
112051
|
-
const keyboard = new
|
|
112662
|
+
const keyboard = new import_grammy17.InlineKeyboard;
|
|
112052
112663
|
kbRows.forEach((row, ri) => {
|
|
112053
112664
|
if (ri > 0)
|
|
112054
112665
|
keyboard.row();
|
|
@@ -112400,7 +113011,7 @@ ${preBlock(formatSwitchroomOutput(err.message ?? "unknown error"))}`, { html: tr
|
|
|
112400
113011
|
await ctx.answerCallbackQuery({ text: "No always-allow rule for this tool." }).catch(() => {});
|
|
112401
113012
|
return;
|
|
112402
113013
|
}
|
|
112403
|
-
keyboard = new
|
|
113014
|
+
keyboard = new import_grammy17.InlineKeyboard().text("\u2190 Back", `perm:back:${request_id}`);
|
|
112404
113015
|
if (choices.specific)
|
|
112405
113016
|
keyboard.text(choices.specific.buttonLabel, `perm:asn:${request_id}`);
|
|
112406
113017
|
keyboard.text(`${choices.broad.buttonLabel} \u26A0\uFE0F`, `perm:asb:${request_id}`);
|
|
@@ -112703,7 +113314,7 @@ ${labelWithResume}` : labelWithResume,
|
|
|
112703
113314
|
verb: "voice-ondemand.sendVoice",
|
|
112704
113315
|
...threadId != null ? { threadId } : {}
|
|
112705
113316
|
}),
|
|
112706
|
-
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), {
|
|
112707
113318
|
chat_id: chatId,
|
|
112708
113319
|
verb: "voice-ondemand.sendVoice",
|
|
112709
113320
|
...threadId != null ? { threadId } : {}
|
|
@@ -112969,7 +113580,7 @@ async function initGatewayBot() {
|
|
|
112969
113580
|
`);
|
|
112970
113581
|
process.exit(1);
|
|
112971
113582
|
}
|
|
112972
|
-
bot = new
|
|
113583
|
+
bot = new import_grammy17.Bot(TOKEN);
|
|
112973
113584
|
installTgPostLogger(bot);
|
|
112974
113585
|
installRichMarkdownGuard(bot);
|
|
112975
113586
|
installEditFloodFuse(bot, {
|
|
@@ -113941,7 +114552,7 @@ async function startGateway() {
|
|
|
113941
114552
|
await runnerHandle.task();
|
|
113942
114553
|
return;
|
|
113943
114554
|
} catch (err) {
|
|
113944
|
-
if (err instanceof
|
|
114555
|
+
if (err instanceof import_grammy17.GrammyError && err.error_code === 409) {
|
|
113945
114556
|
const delay = Math.min(1000 * attempt, 15000);
|
|
113946
114557
|
const agentName3 = process.env.SWITCHROOM_AGENT_NAME ?? "-";
|
|
113947
114558
|
process.stderr.write(`telegram gateway: poll.409.detected attempt=${attempt} retry_in_ms=${delay} agent=${agentName3}
|