switchroom 0.19.19 → 0.19.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/auth-broker/index.js +53 -0
- package/dist/cli/switchroom.js +2444 -1264
- package/dist/host-control/main.js +54 -1
- package/dist/vault/approvals/kernel-server.js +53 -0
- package/dist/vault/broker/server.js +53 -0
- package/package.json +4 -2
- package/skills/switchroom-release/SKILL.md +103 -20
- package/telegram-plugin/card-format.ts +92 -3
- package/telegram-plugin/dist/gateway/gateway.js +769 -172
- package/telegram-plugin/edit-flood-fuse.ts +477 -0
- package/telegram-plugin/format.ts +19 -7
- package/telegram-plugin/gateway/boot-sweep-gate.ts +164 -0
- package/telegram-plugin/gateway/callback-query-handlers.ts +454 -81
- package/telegram-plugin/gateway/gateway.ts +66 -56
- package/telegram-plugin/gateway/inbound-interceptors.ts +27 -4
- package/telegram-plugin/gateway/narrative-lane.ts +49 -3
- package/telegram-plugin/gateway/status-pin-api.ts +145 -0
- package/telegram-plugin/hooks/subagent-tracker-posttool.mjs +325 -45
- package/telegram-plugin/retry-api-call.ts +15 -2
- package/telegram-plugin/send-gate.ts +1 -1
- package/telegram-plugin/status-no-truncate.ts +64 -1
- package/telegram-plugin/status-pin-driver.ts +50 -27
- package/telegram-plugin/status-pin.ts +43 -5
- package/telegram-plugin/tests/activity-card-send-gate.test.ts +275 -0
- package/telegram-plugin/tests/activity-card-wiring.test.ts +16 -7
- package/telegram-plugin/tests/boot-pin-sweep-wiring.test.ts +101 -0
- package/telegram-plugin/tests/boot-sweep-gate.test.ts +293 -0
- package/telegram-plugin/tests/boot-version-string.test.ts +0 -0
- package/telegram-plugin/tests/edit-flood-fuse.test.ts +431 -0
- package/telegram-plugin/tests/pinned-card-collapse.test.ts +356 -0
- package/telegram-plugin/tests/status-pin-api.test.ts +178 -0
- package/telegram-plugin/tests/status-pin-boot-recovery.test.ts +94 -11
- package/telegram-plugin/tests/status-pin.test.ts +106 -5
- package/telegram-plugin/tests/subagent-tracker-hooks.test.ts +631 -1
- package/telegram-plugin/tests/tool-activity-summary.test.ts +19 -10
- package/telegram-plugin/tests/vault-approval-posture.test.ts +6 -1
- package/telegram-plugin/tests/vault-passphrase-retry.test.ts +666 -0
- package/telegram-plugin/tests/vault-request-access-unlock-resume.test.ts +42 -21
- package/telegram-plugin/tests/worker-feed-coalesce.test.ts +233 -1
- package/telegram-plugin/tool-activity-summary.ts +85 -13
- package/telegram-plugin/worker-activity-feed.ts +5 -1
- package/vendor/hindsight-memory/scripts/drain_pending.py +193 -25
- package/vendor/hindsight-memory/scripts/lib/pending.py +84 -5
- package/vendor/hindsight-memory/scripts/lib/retain_split.py +21 -10
- package/vendor/hindsight-memory/scripts/recall.py +74 -5
- package/vendor/hindsight-memory/scripts/tests/test_pending_drops.py +158 -4
- package/vendor/hindsight-memory/scripts/tests/test_pending_failure_class.py +105 -0
- package/vendor/hindsight-memory/scripts/tests/test_pending_wedge.py +300 -0
- package/vendor/hindsight-memory/scripts/tests/test_recall_degraded_notice.py +365 -0
- package/vendor/hindsight-memory/scripts/tests/test_recall_envelope_strip_telemetry.py +12 -4
- package/vendor/hindsight-memory/scripts/tests/test_recall_transcript_fallback.py +27 -2
- package/vendor/hindsight-memory/scripts/tests/test_retain_split.py +19 -11
- package/vendor/hindsight-memory/tests/test_drain_pending.py +28 -2
|
@@ -7379,7 +7379,17 @@ var init_text_voice_scrub = __esm(() => {
|
|
|
7379
7379
|
});
|
|
7380
7380
|
|
|
7381
7381
|
// card-format.ts
|
|
7382
|
-
function
|
|
7382
|
+
function normalizeLineTail(line, collapseSafe) {
|
|
7383
|
+
let out = line.replace(/[ \t\r]+$/, "");
|
|
7384
|
+
if (!collapseSafe || COLLAPSE_SAFE_SEPARATOR.length === 0)
|
|
7385
|
+
return out;
|
|
7386
|
+
while (out.endsWith(COLLAPSE_SAFE_SEPARATOR)) {
|
|
7387
|
+
out = out.slice(0, -COLLAPSE_SAFE_SEPARATOR.length).replace(/[ \t\r]+$/, "");
|
|
7388
|
+
}
|
|
7389
|
+
return out;
|
|
7390
|
+
}
|
|
7391
|
+
function stackCardLines(lines, opts) {
|
|
7392
|
+
const collapseSafe = opts?.collapseSafe === true;
|
|
7383
7393
|
const pieces = [];
|
|
7384
7394
|
for (let i = 0;i < lines.length; i++) {
|
|
7385
7395
|
const line = lines[i];
|
|
@@ -7389,11 +7399,14 @@ function stackCardLines(lines) {
|
|
|
7389
7399
|
const cur = line.trim();
|
|
7390
7400
|
const next = lines[i + 1].trim();
|
|
7391
7401
|
if (cur === "" || next === "") {
|
|
7402
|
+
if (collapseSafe && cur !== "") {
|
|
7403
|
+
pieces[pieces.length - 1] = normalizeLineTail(line, true) + COLLAPSE_SAFE_SEPARATOR;
|
|
7404
|
+
}
|
|
7392
7405
|
pieces.push(`
|
|
7393
7406
|
`);
|
|
7394
7407
|
continue;
|
|
7395
7408
|
}
|
|
7396
|
-
pieces[pieces.length - 1] = line
|
|
7409
|
+
pieces[pieces.length - 1] = normalizeLineTail(line, collapseSafe) + (collapseSafe ? COLLAPSE_SAFE_SEPARATOR : "");
|
|
7397
7410
|
pieces.push(`
|
|
7398
7411
|
`);
|
|
7399
7412
|
}
|
|
@@ -7439,6 +7452,7 @@ function cleanWorkerResultParagraph(s) {
|
|
|
7439
7452
|
}
|
|
7440
7453
|
return kept.join(" ").replace(/\s+/g, " ").trim();
|
|
7441
7454
|
}
|
|
7455
|
+
var COLLAPSE_SAFE_SEPARATOR = "\u00a0";
|
|
7442
7456
|
var init_card_format = __esm(() => {
|
|
7443
7457
|
init_format();
|
|
7444
7458
|
init_text_voice_scrub();
|
|
@@ -35381,7 +35395,7 @@ function renderAuthLine(state7, agentName3, now = Date.now()) {
|
|
|
35381
35395
|
|
|
35382
35396
|
// gateway/quota-cache.ts
|
|
35383
35397
|
import { existsSync as existsSync45, readFileSync as readFileSync43, writeFileSync as writeFileSync36, mkdirSync as mkdirSync38 } from "fs";
|
|
35384
|
-
import { join as join51, dirname as
|
|
35398
|
+
import { join as join51, dirname as dirname18 } from "path";
|
|
35385
35399
|
function defaultCachePath() {
|
|
35386
35400
|
return process.env.SWITCHROOM_QUOTA_CACHE_PATH ?? join51(process.env.HOME ?? "/tmp", ".switchroom", "quota-cache.json");
|
|
35387
35401
|
}
|
|
@@ -35418,7 +35432,7 @@ function writeQuotaCache(result, opts = {}) {
|
|
|
35418
35432
|
result
|
|
35419
35433
|
};
|
|
35420
35434
|
try {
|
|
35421
|
-
mkdirSync38(
|
|
35435
|
+
mkdirSync38(dirname18(path3), { recursive: true });
|
|
35422
35436
|
writeFileSync36(path3, JSON.stringify(entry, null, 2), { mode: 384 });
|
|
35423
35437
|
} catch {}
|
|
35424
35438
|
}
|
|
@@ -36291,7 +36305,7 @@ var init_boot_probes = __esm(() => {
|
|
|
36291
36305
|
|
|
36292
36306
|
// gateway/boot-issue-cache.ts
|
|
36293
36307
|
import { existsSync as existsSync47, readFileSync as readFileSync45, writeFileSync as writeFileSync37, mkdirSync as mkdirSync39, renameSync as renameSync21 } from "fs";
|
|
36294
|
-
import { dirname as
|
|
36308
|
+
import { dirname as dirname19 } from "path";
|
|
36295
36309
|
function fingerprintProbe(key, r) {
|
|
36296
36310
|
if (r.status === "ok")
|
|
36297
36311
|
return `${key}:ok`;
|
|
@@ -36417,7 +36431,7 @@ function applyAndSave(path3, cache, diff) {
|
|
|
36417
36431
|
}
|
|
36418
36432
|
}
|
|
36419
36433
|
try {
|
|
36420
|
-
mkdirSync39(
|
|
36434
|
+
mkdirSync39(dirname19(path3), { recursive: true });
|
|
36421
36435
|
const tmp = `${path3}.tmp`;
|
|
36422
36436
|
writeFileSync37(tmp, JSON.stringify(next), { mode: 384 });
|
|
36423
36437
|
renameSync21(tmp, path3);
|
|
@@ -36434,7 +36448,7 @@ var init_boot_issue_cache = __esm(() => {
|
|
|
36434
36448
|
// gateway/config-snapshot.ts
|
|
36435
36449
|
import { createHash as createHash6 } from "crypto";
|
|
36436
36450
|
import { existsSync as existsSync48, readFileSync as readFileSync46, writeFileSync as writeFileSync38, mkdirSync as mkdirSync40, renameSync as renameSync22 } from "fs";
|
|
36437
|
-
import { dirname as
|
|
36451
|
+
import { dirname as dirname20 } from "path";
|
|
36438
36452
|
function hashStringArray(items) {
|
|
36439
36453
|
if (!items || items.length === 0)
|
|
36440
36454
|
return null;
|
|
@@ -36532,7 +36546,7 @@ function loadSnapshot(path3, now = Date.now) {
|
|
|
36532
36546
|
}
|
|
36533
36547
|
function persistSnapshot(path3, snapshot) {
|
|
36534
36548
|
try {
|
|
36535
|
-
mkdirSync40(
|
|
36549
|
+
mkdirSync40(dirname20(path3), { recursive: true });
|
|
36536
36550
|
const tmp = `${path3}.tmp`;
|
|
36537
36551
|
writeFileSync38(tmp, JSON.stringify(snapshot), { mode: 384 });
|
|
36538
36552
|
renameSync22(tmp, path3);
|
|
@@ -38714,7 +38728,7 @@ import {
|
|
|
38714
38728
|
unlinkSync as unlinkSync28,
|
|
38715
38729
|
appendFileSync as appendFileSync9
|
|
38716
38730
|
} from "fs";
|
|
38717
|
-
import { homedir as
|
|
38731
|
+
import { homedir as homedir19 } from "os";
|
|
38718
38732
|
import { join as join65, sep as sep4, basename as basename17 } from "path";
|
|
38719
38733
|
|
|
38720
38734
|
// plugin-logger.ts
|
|
@@ -43330,13 +43344,27 @@ async function interceptVault(p, deps) {
|
|
|
43330
43344
|
deps.vaultPassphraseCache.set(p.chat_id, { passphrase, expiresAt: Date.now() + deps.vaultPassphraseTtlMs });
|
|
43331
43345
|
if (p.msgId != null)
|
|
43332
43346
|
await deps.deleteSensitiveMessage(p.chat_id, p.msgId, "vault passphrase");
|
|
43347
|
+
const mismatched = [];
|
|
43348
|
+
let mismatchMsg = "";
|
|
43333
43349
|
for (const item of pendingVault.items) {
|
|
43334
43350
|
const stagedAccess = deps.pendingVaultRequestAccesses.get(item.stageId);
|
|
43335
43351
|
if (!stagedAccess) {
|
|
43336
43352
|
await p.ctx.api.editMessageText(item.cardChatId, item.cardMessageId, richMessage(`\u231b _Vault unlocked, but this access-request card expired or was denied before you replied. Ask the agent to re-issue if still needed._`), { reply_markup: { inline_keyboard: [] } }).catch(() => {});
|
|
43337
43353
|
continue;
|
|
43338
43354
|
}
|
|
43339
|
-
await deps.callbackQueryHandlers().performVaultAccessApproval(p.ctx, stagedAccess, item.stageId, item.senderId, { kind: "passphrase", passphrase });
|
|
43355
|
+
const outcome = await deps.callbackQueryHandlers().performVaultAccessApproval(p.ctx, stagedAccess, item.stageId, item.senderId, { kind: "passphrase", passphrase });
|
|
43356
|
+
if (outcome?.kind === "passphrase-mismatch") {
|
|
43357
|
+
mismatched.push(item);
|
|
43358
|
+
mismatchMsg = outcome.msg;
|
|
43359
|
+
}
|
|
43360
|
+
}
|
|
43361
|
+
if (mismatched.length > 0) {
|
|
43362
|
+
await deps.callbackQueryHandlers().resolveAccessApprovalPassphraseMismatch(p.ctx, {
|
|
43363
|
+
chat_id: p.chat_id,
|
|
43364
|
+
failed: mismatched,
|
|
43365
|
+
priorAttempts: pendingVault.attempts ?? 0,
|
|
43366
|
+
brokerMsg: mismatchMsg
|
|
43367
|
+
});
|
|
43340
43368
|
}
|
|
43341
43369
|
} else if (pendingVault.kind === "grant-wizard" && pendingVault.awaitingCustomDuration) {
|
|
43342
43370
|
const input = p.text.trim();
|
|
@@ -43965,13 +43993,27 @@ async function interceptVault2(p, deps) {
|
|
|
43965
43993
|
deps.vaultPassphraseCache.set(p.chat_id, { passphrase, expiresAt: Date.now() + deps.vaultPassphraseTtlMs });
|
|
43966
43994
|
if (p.msgId != null)
|
|
43967
43995
|
await deps.deleteSensitiveMessage(p.chat_id, p.msgId, "vault passphrase");
|
|
43996
|
+
const mismatched = [];
|
|
43997
|
+
let mismatchMsg = "";
|
|
43968
43998
|
for (const item of pendingVault.items) {
|
|
43969
43999
|
const stagedAccess = deps.pendingVaultRequestAccesses.get(item.stageId);
|
|
43970
44000
|
if (!stagedAccess) {
|
|
43971
44001
|
await p.ctx.api.editMessageText(item.cardChatId, item.cardMessageId, richMessage(`\u231b _Vault unlocked, but this access-request card expired or was denied before you replied. Ask the agent to re-issue if still needed._`), { reply_markup: { inline_keyboard: [] } }).catch(() => {});
|
|
43972
44002
|
continue;
|
|
43973
44003
|
}
|
|
43974
|
-
await deps.callbackQueryHandlers().performVaultAccessApproval(p.ctx, stagedAccess, item.stageId, item.senderId, { kind: "passphrase", passphrase });
|
|
44004
|
+
const outcome = await deps.callbackQueryHandlers().performVaultAccessApproval(p.ctx, stagedAccess, item.stageId, item.senderId, { kind: "passphrase", passphrase });
|
|
44005
|
+
if (outcome?.kind === "passphrase-mismatch") {
|
|
44006
|
+
mismatched.push(item);
|
|
44007
|
+
mismatchMsg = outcome.msg;
|
|
44008
|
+
}
|
|
44009
|
+
}
|
|
44010
|
+
if (mismatched.length > 0) {
|
|
44011
|
+
await deps.callbackQueryHandlers().resolveAccessApprovalPassphraseMismatch(p.ctx, {
|
|
44012
|
+
chat_id: p.chat_id,
|
|
44013
|
+
failed: mismatched,
|
|
44014
|
+
priorAttempts: pendingVault.attempts ?? 0,
|
|
44015
|
+
brokerMsg: mismatchMsg
|
|
44016
|
+
});
|
|
43975
44017
|
}
|
|
43976
44018
|
} else if (pendingVault.kind === "grant-wizard" && pendingVault.awaitingCustomDuration) {
|
|
43977
44019
|
const input = p.text.trim();
|
|
@@ -46435,6 +46477,7 @@ var WORKER_HISTORY_MAX = 6;
|
|
|
46435
46477
|
var STATUS_LINE_MAX = 200;
|
|
46436
46478
|
var STATUS_CARD_CHAR_BUDGET = RICH_MESSAGE_MAX_CHARS;
|
|
46437
46479
|
var NESTED_PREFIX = " \u21b3 ";
|
|
46480
|
+
var WORKER_STEP_INDENT = "\u2800\u2800\u2800";
|
|
46438
46481
|
|
|
46439
46482
|
// hooks/tool-label-pretool.mjs
|
|
46440
46483
|
import { readFileSync as readFileSync9, mkdirSync as mkdirSync10, appendFileSync as appendFileSync2, existsSync as existsSync9 } from "node:fs";
|
|
@@ -46858,16 +46901,16 @@ function escapeStepLine(raw) {
|
|
|
46858
46901
|
const cleaned = stripMarkdown(raw).replace(/\s+/g, " ").trim();
|
|
46859
46902
|
return escapeMarkdown(truncate(cleaned, STATUS_LINE_MAX));
|
|
46860
46903
|
}
|
|
46861
|
-
function renderStepFeed(out, steps, allDone, liveSuffix = "", window2 = STATUS_ROLLING_LINES) {
|
|
46904
|
+
function renderStepFeed(out, steps, allDone, liveSuffix = "", window2 = STATUS_ROLLING_LINES, indent = "") {
|
|
46862
46905
|
if (steps.length === 0)
|
|
46863
46906
|
return;
|
|
46864
46907
|
const shown = steps.slice(-Math.max(1, window2));
|
|
46865
46908
|
const hidden = steps.length - shown.length;
|
|
46866
46909
|
if (hidden > 0)
|
|
46867
|
-
out.push(
|
|
46910
|
+
out.push(`${indent}_\u2713 +${hidden} earlier\u2026_`);
|
|
46868
46911
|
const lastIdx = shown.length - 1;
|
|
46869
46912
|
shown.forEach((s, i) => {
|
|
46870
|
-
out.push(!allDone && i === lastIdx ?
|
|
46913
|
+
out.push(!allDone && i === lastIdx ? `${indent}**\u2192 ${s}${liveSuffix}**` : `${indent}~~_\u2713 ${s}_~~`);
|
|
46871
46914
|
});
|
|
46872
46915
|
}
|
|
46873
46916
|
function renderStatusCard(opts) {
|
|
@@ -46907,7 +46950,7 @@ function renderStatusCard(opts) {
|
|
|
46907
46950
|
}
|
|
46908
46951
|
if (out.length === 0)
|
|
46909
46952
|
return null;
|
|
46910
|
-
const joined = stackCardLines(out);
|
|
46953
|
+
const joined = stackCardLines(out, { collapseSafe: true });
|
|
46911
46954
|
if (joined.length <= STATUS_CARD_CHAR_BUDGET)
|
|
46912
46955
|
return joined;
|
|
46913
46956
|
return fitCardToBudget(opts, headerLines);
|
|
@@ -46942,7 +46985,7 @@ function fitCardToBudget(opts, headerLines) {
|
|
|
46942
46985
|
const lastIdx = shown.length - 1;
|
|
46943
46986
|
shown.forEach((esc, i) => lines2.push(buildBullet(esc, i === lastIdx)));
|
|
46944
46987
|
lines2.push(...footerLines);
|
|
46945
|
-
const candidate = stackCardLines(lines2);
|
|
46988
|
+
const candidate = stackCardLines(lines2, { collapseSafe: true });
|
|
46946
46989
|
if (candidate.length <= STATUS_CARD_CHAR_BUDGET)
|
|
46947
46990
|
return candidate;
|
|
46948
46991
|
}
|
|
@@ -46963,7 +47006,7 @@ function fitCardToBudget(opts, headerLines) {
|
|
|
46963
47006
|
lines.push(parentMarker);
|
|
46964
47007
|
lines.push(newestLine);
|
|
46965
47008
|
lines.push(...footerLines);
|
|
46966
|
-
return stackCardLines(lines);
|
|
47009
|
+
return stackCardLines(lines, { collapseSafe: true });
|
|
46967
47010
|
}
|
|
46968
47011
|
function renderActivityFeed(lines, final = false, liveSuffix = "", stepCount, header) {
|
|
46969
47012
|
if (lines.length === 0 && header == null)
|
|
@@ -47016,6 +47059,13 @@ function combinedHistoryDepth(w) {
|
|
|
47016
47059
|
return Math.min(WORKER_HISTORY_MAX, Math.max(MIN_WORKER_DEPTH, 7 - w));
|
|
47017
47060
|
}
|
|
47018
47061
|
var workerHistoryDepth = combinedHistoryDepth;
|
|
47062
|
+
function glanceLine(rows) {
|
|
47063
|
+
const oldestMs = rows.reduce((m, r) => Math.max(m, r.elapsedMs), 0);
|
|
47064
|
+
const tools = rows.reduce((n, r) => n + r.toolCount, 0);
|
|
47065
|
+
const tok = rows.reduce((n, r) => n + (r.totalTokens ?? 0), 0);
|
|
47066
|
+
const toolWord = tools === 1 ? "tool" : "tools";
|
|
47067
|
+
return `\uD83D\uDEE0 **Workers** \u00b7 _${rows.length} running \u00b7 oldest ${formatFeedElapsed(oldestMs)}` + ` \u00b7 ${tools} ${toolWord}${tokenSegment(tok)}_`;
|
|
47068
|
+
}
|
|
47019
47069
|
function renderCombinedWorkerFeed(rows, opts) {
|
|
47020
47070
|
if (rows.length === 0)
|
|
47021
47071
|
return null;
|
|
@@ -47038,22 +47088,22 @@ function renderCombinedWorkerFeed(rows, opts) {
|
|
|
47038
47088
|
const shown = rows.slice(0, visibleCount);
|
|
47039
47089
|
const hidden = rows.length - shown.length;
|
|
47040
47090
|
const depth = combinedHistoryDepth(shown.length);
|
|
47041
|
-
const chrome = [
|
|
47091
|
+
const chrome = [glanceLine(rows)];
|
|
47042
47092
|
const bodyOut = [];
|
|
47043
47093
|
for (const r of shown) {
|
|
47044
47094
|
bodyOut.push(rowHeader(r));
|
|
47045
47095
|
const hist = rowHistory(r);
|
|
47046
47096
|
if (hist.length === 0) {
|
|
47047
|
-
bodyOut.push(
|
|
47097
|
+
bodyOut.push(`${WORKER_STEP_INDENT}\u2192 _starting\u2026_`);
|
|
47048
47098
|
continue;
|
|
47049
47099
|
}
|
|
47050
47100
|
const esc = hist.slice(-depth).map(escapeStepLine);
|
|
47051
|
-
renderStepFeed(bodyOut, esc, false, "", depth);
|
|
47101
|
+
renderStepFeed(bodyOut, esc, false, "", depth, WORKER_STEP_INDENT);
|
|
47052
47102
|
}
|
|
47053
47103
|
const out = [...chrome, ...bodyOut];
|
|
47054
47104
|
if (hidden > 0)
|
|
47055
47105
|
out.push(`_+${hidden} more working\u2026_`);
|
|
47056
|
-
return { body: stackCardLines(out), bodyLines: bodyOut.length };
|
|
47106
|
+
return { body: stackCardLines(out, { collapseSafe: true }), bodyLines: bodyOut.length };
|
|
47057
47107
|
};
|
|
47058
47108
|
let visible = Math.min(rows.length, maxRows);
|
|
47059
47109
|
let { body, bodyLines } = compose(visible);
|
|
@@ -47074,6 +47124,10 @@ function appendActivityLabel(lines, label) {
|
|
|
47074
47124
|
}
|
|
47075
47125
|
|
|
47076
47126
|
// send-gate.ts
|
|
47127
|
+
var systemClock = {
|
|
47128
|
+
now: () => Date.now(),
|
|
47129
|
+
sleep: (ms) => new Promise((r) => setTimeout(r, ms))
|
|
47130
|
+
};
|
|
47077
47131
|
var SEND_GATE_SHED = Symbol.for("switchroom.send-gate.shed");
|
|
47078
47132
|
function isSendGateShed(value) {
|
|
47079
47133
|
return value === SEND_GATE_SHED;
|
|
@@ -47131,7 +47185,7 @@ function renderWorkerActivity(v, liveSuffix = "") {
|
|
|
47131
47185
|
return `\uD83D\uDEE0 **Worker** \u00b7 _starting\u2026_`;
|
|
47132
47186
|
}
|
|
47133
47187
|
if (!finished && steps.length === 0) {
|
|
47134
|
-
return `${card}
|
|
47188
|
+
return `${card}${COLLAPSE_SAFE_SEPARATOR}
|
|
47135
47189
|
_starting\u2026_`;
|
|
47136
47190
|
}
|
|
47137
47191
|
return card;
|
|
@@ -48032,6 +48086,16 @@ function errorDescription(err) {
|
|
|
48032
48086
|
function isPinRightsError(err) {
|
|
48033
48087
|
return errorDescription(err).includes("not enough rights");
|
|
48034
48088
|
}
|
|
48089
|
+
function isUnpinTerminalError(err) {
|
|
48090
|
+
if (isPinRightsError(err))
|
|
48091
|
+
return true;
|
|
48092
|
+
if (err != null && typeof err === "object") {
|
|
48093
|
+
const code = err.error_code;
|
|
48094
|
+
if (typeof code === "number")
|
|
48095
|
+
return code >= 400 && code < 500 && code !== 429;
|
|
48096
|
+
}
|
|
48097
|
+
return false;
|
|
48098
|
+
}
|
|
48035
48099
|
|
|
48036
48100
|
class PinRightsCache {
|
|
48037
48101
|
blocked = new Set;
|
|
@@ -48055,18 +48119,20 @@ async function reconcilePin(args) {
|
|
|
48055
48119
|
if (action.kind === "noop")
|
|
48056
48120
|
return args.prevState;
|
|
48057
48121
|
if (action.kind === "unpin") {
|
|
48058
|
-
if (
|
|
48059
|
-
|
|
48060
|
-
|
|
48061
|
-
|
|
48062
|
-
|
|
48063
|
-
|
|
48064
|
-
|
|
48065
|
-
|
|
48066
|
-
|
|
48067
|
-
|
|
48068
|
-
|
|
48122
|
+
if (args.rightsCache?.isBlocked(args.chatId))
|
|
48123
|
+
return null;
|
|
48124
|
+
try {
|
|
48125
|
+
await args.api.unpinChatMessage(args.chatId, action.messageId);
|
|
48126
|
+
} catch (err) {
|
|
48127
|
+
if (args.rightsCache && isPinRightsError(err)) {
|
|
48128
|
+
const firstTime = args.rightsCache.block(args.chatId);
|
|
48129
|
+
if (firstTime)
|
|
48130
|
+
args.onPinRightsDisabled?.(args.chatId);
|
|
48131
|
+
} else {
|
|
48132
|
+
args.onError?.("unpin", err);
|
|
48069
48133
|
}
|
|
48134
|
+
if (!isUnpinTerminalError(err))
|
|
48135
|
+
return args.prevState;
|
|
48070
48136
|
}
|
|
48071
48137
|
return null;
|
|
48072
48138
|
}
|
|
@@ -48498,8 +48564,7 @@ init_rich_send();
|
|
|
48498
48564
|
var import_grammy4 = __toESM(require_mod2(), 1);
|
|
48499
48565
|
import { execFileSync as execFileSync3 } from "child_process";
|
|
48500
48566
|
import { mkdirSync as mkdirSync13, writeFileSync as writeFileSync9 } from "fs";
|
|
48501
|
-
import {
|
|
48502
|
-
import { join as join13 } from "path";
|
|
48567
|
+
import { dirname as dirname5 } from "path";
|
|
48503
48568
|
init_client();
|
|
48504
48569
|
|
|
48505
48570
|
// gateway/vault-grant-inbound-builders.ts
|
|
@@ -49532,6 +49597,48 @@ var _deprecationSeen = new Set;
|
|
|
49532
49597
|
|
|
49533
49598
|
// gateway/callback-query-handlers.ts
|
|
49534
49599
|
var AUTH_REFRESH_THROTTLE_MS = 5000;
|
|
49600
|
+
var MAX_VAULT_PASSPHRASE_ATTEMPTS = 3;
|
|
49601
|
+
function isPassphraseMismatchBrokerError(msg) {
|
|
49602
|
+
const m = msg.toLowerCase();
|
|
49603
|
+
if (!m.includes("passphrase"))
|
|
49604
|
+
return false;
|
|
49605
|
+
return m.includes("does not match") || m.includes("mismatch");
|
|
49606
|
+
}
|
|
49607
|
+
function buildAccessPassphrasePromptText(opts) {
|
|
49608
|
+
const header = `**\uD83D\uDEA8\uD83D\uDD10 ACTION NEEDED: passphrase required**`;
|
|
49609
|
+
if (opts.kind === "retry") {
|
|
49610
|
+
const plural = opts.retryRemaining === 1 ? "attempt" : "attempts";
|
|
49611
|
+
return `${header}
|
|
49612
|
+
|
|
49613
|
+
` + `Wrong passphrase. ${opts.retryRemaining} ${plural} remaining.
|
|
49614
|
+
` + `Type your vault passphrase again as your **next message**.
|
|
49615
|
+
` + (opts.itemCount > 1 ? `One entry covers **${opts.itemCount}** pending approvals in this chat.
|
|
49616
|
+
` : ``) + `
|
|
49617
|
+
_We delete the passphrase message the moment we read it._`;
|
|
49618
|
+
}
|
|
49619
|
+
if (opts.variant === "batch") {
|
|
49620
|
+
return `${header}
|
|
49621
|
+
|
|
49622
|
+
` + `Type your vault passphrase as your **next message**.
|
|
49623
|
+
` + `One entry covers **${opts.itemCount}** pending approvals in this chat, no re-type per card.
|
|
49624
|
+
|
|
49625
|
+
` + `_We delete the passphrase message the moment we read it._`;
|
|
49626
|
+
}
|
|
49627
|
+
if (opts.variant === "admin-only") {
|
|
49628
|
+
return `${header}
|
|
49629
|
+
|
|
49630
|
+
` + `\`${opts.key}\` is an **admin-only credential**.
|
|
49631
|
+
` + `Type your vault passphrase as your **next message** to mint the grant for **${opts.agentEscaped}**.
|
|
49632
|
+
|
|
49633
|
+
` + `_The passphrase is what proves it's you. An agent can never mint this key on its own. We delete the passphrase message the moment we read it._`;
|
|
49634
|
+
}
|
|
49635
|
+
return `${header}
|
|
49636
|
+
|
|
49637
|
+
` + `Your vault is locked.
|
|
49638
|
+
` + `Reply with your passphrase as your **next message** to unlock and mint the grant for **${opts.agentEscaped}**.
|
|
49639
|
+
|
|
49640
|
+
` + `_Mint authority stays operator-only: the broker only accepts the grant when the passphrase matches. We delete the passphrase message the moment we read it._`;
|
|
49641
|
+
}
|
|
49535
49642
|
function createCallbackQueryHandlers(deps) {
|
|
49536
49643
|
const {
|
|
49537
49644
|
loadAccess,
|
|
@@ -49574,6 +49681,10 @@ function createCallbackQueryHandlers(deps) {
|
|
|
49574
49681
|
} = deps;
|
|
49575
49682
|
const bot = deps.bot;
|
|
49576
49683
|
const lockedBot = deps.lockedBot;
|
|
49684
|
+
const mintGrantViaBroker2 = deps.brokerMintGrant ?? mintGrantViaBroker;
|
|
49685
|
+
const listViaBroker2 = deps.brokerList ?? listViaBroker;
|
|
49686
|
+
const listGrantsViaBroker2 = deps.brokerListGrants ?? listGrantsViaBroker;
|
|
49687
|
+
const vaultTokenFilePath2 = deps.brokerVaultTokenFilePath ?? vaultTokenFilePath;
|
|
49577
49688
|
async function handleVaultRecentDenialCallback(ctx, data) {
|
|
49578
49689
|
const senderId = String(ctx.from?.id ?? "");
|
|
49579
49690
|
const access = loadAccess();
|
|
@@ -49596,7 +49707,7 @@ function createCallbackQueryHandlers(deps) {
|
|
|
49596
49707
|
return;
|
|
49597
49708
|
}
|
|
49598
49709
|
await ctx.answerCallbackQuery({ text: "\u23f3 Minting 30-day read grant\u2026" }).catch(() => {});
|
|
49599
|
-
const result = await
|
|
49710
|
+
const result = await mintGrantViaBroker2({
|
|
49600
49711
|
agent: agentName,
|
|
49601
49712
|
keys: [keyName],
|
|
49602
49713
|
ttl_seconds: 30 * 24 * 60 * 60,
|
|
@@ -49611,9 +49722,9 @@ function createCallbackQueryHandlers(deps) {
|
|
|
49611
49722
|
return;
|
|
49612
49723
|
}
|
|
49613
49724
|
const { token, id } = result;
|
|
49614
|
-
const tokenPath =
|
|
49725
|
+
const tokenPath = vaultTokenFilePath2(agentName);
|
|
49615
49726
|
try {
|
|
49616
|
-
mkdirSync13(
|
|
49727
|
+
mkdirSync13(dirname5(tokenPath), { recursive: true });
|
|
49617
49728
|
writeFileSync9(tokenPath, token, { mode: 384 });
|
|
49618
49729
|
} catch (err) {
|
|
49619
49730
|
await switchroomReply(ctx, `**Grant created (${escapeHtmlForTg2(id)}) but token write failed:** ` + `${escapeHtmlForTg2(String(err))}
|
|
@@ -49630,25 +49741,93 @@ function createCallbackQueryHandlers(deps) {
|
|
|
49630
49741
|
newText: baseText ? `${baseText}${statusLine}` : statusLine
|
|
49631
49742
|
});
|
|
49632
49743
|
}
|
|
49744
|
+
async function editResolvedCard(ctx, target, messageId, markdown, label) {
|
|
49745
|
+
if (messageId > 0) {
|
|
49746
|
+
try {
|
|
49747
|
+
await robustApiCall(() => ctx.api.editMessageText(target.chat_id, messageId, richMessage(markdown), {
|
|
49748
|
+
reply_markup: { inline_keyboard: [] }
|
|
49749
|
+
}), { chat_id: target.chat_id, verb: `vault_request_access.${label}_edit` });
|
|
49750
|
+
return;
|
|
49751
|
+
} catch (err) {
|
|
49752
|
+
process.stderr.write(`telegram gateway: vault card resolution edit FAILED (${label}) ` + `chat=${target.chat_id} msg=${messageId}: ${String(err)} \u2014 sending fallback message
|
|
49753
|
+
`);
|
|
49754
|
+
}
|
|
49755
|
+
}
|
|
49756
|
+
try {
|
|
49757
|
+
await retryWithThreadFallback(robustApiCall, (tid) => lockedBot.api.sendRichMessage(target.chat_id, richMessage(markdown), {
|
|
49758
|
+
...tid != null && Number.isFinite(tid) ? { message_thread_id: tid } : {}
|
|
49759
|
+
}), {
|
|
49760
|
+
threadId: target.threadId,
|
|
49761
|
+
chat_id: target.chat_id,
|
|
49762
|
+
verb: `vault_request_access.${label}_fallback`
|
|
49763
|
+
});
|
|
49764
|
+
} catch (err) {
|
|
49765
|
+
process.stderr.write(`telegram gateway: vault card resolution FALLBACK SEND failed (${label}) ` + `chat=${target.chat_id}: ${String(err)}
|
|
49766
|
+
`);
|
|
49767
|
+
}
|
|
49768
|
+
}
|
|
49769
|
+
async function sendAccessPassphrasePrompt(target, spec) {
|
|
49770
|
+
const promptText = buildAccessPassphrasePromptText(spec);
|
|
49771
|
+
await retryWithThreadFallback(robustApiCall, (tid) => lockedBot.api.sendRichMessage(target.chat_id, richMessage(promptText), {
|
|
49772
|
+
...tid != null && Number.isFinite(tid) ? { message_thread_id: tid } : {}
|
|
49773
|
+
}), {
|
|
49774
|
+
threadId: target.threadId,
|
|
49775
|
+
chat_id: target.chat_id,
|
|
49776
|
+
verb: "vault_request_access.passphrase_prompt"
|
|
49777
|
+
}).catch((err) => {
|
|
49778
|
+
process.stderr.write(`telegram gateway: vault passphrase prompt send FAILED chat=${target.chat_id} ` + `kind=${spec.kind}: ${String(err)}
|
|
49779
|
+
`);
|
|
49780
|
+
});
|
|
49781
|
+
}
|
|
49782
|
+
async function resolveAccessApprovalPassphraseMismatch(ctx, args) {
|
|
49783
|
+
const { chat_id, failed, priorAttempts, brokerMsg } = args;
|
|
49784
|
+
if (failed.length === 0)
|
|
49785
|
+
return;
|
|
49786
|
+
vaultPassphraseCache.delete(chat_id);
|
|
49787
|
+
const attempts = priorAttempts + 1;
|
|
49788
|
+
const remaining = MAX_VAULT_PASSPHRASE_ATTEMPTS - attempts;
|
|
49789
|
+
const threadId = failed.find((it) => it.threadId != null)?.threadId;
|
|
49790
|
+
process.stderr.write(`telegram gateway: vault_request_access passphrase mismatch chat=${chat_id} ` + `stages=${failed.map((f) => f.stageId).join(",")} attempts=${attempts}/${MAX_VAULT_PASSPHRASE_ATTEMPTS}
|
|
49791
|
+
`);
|
|
49792
|
+
if (remaining > 0) {
|
|
49793
|
+
const open = pendingVaultOps.get(chat_id);
|
|
49794
|
+
const carried = open?.kind === "passphrase-for-access-approve" ? open.items.filter((it) => !failed.some((f) => f.stageId === it.stageId)) : [];
|
|
49795
|
+
const requeued = [...failed, ...carried];
|
|
49796
|
+
pendingVaultOps.set(chat_id, {
|
|
49797
|
+
kind: "passphrase-for-access-approve",
|
|
49798
|
+
items: requeued,
|
|
49799
|
+
attempts,
|
|
49800
|
+
startedAt: Date.now()
|
|
49801
|
+
});
|
|
49802
|
+
await sendAccessPassphrasePrompt({ chat_id, ...threadId != null ? { threadId } : {} }, { kind: "retry", retryRemaining: remaining, itemCount: requeued.length });
|
|
49803
|
+
return;
|
|
49804
|
+
}
|
|
49805
|
+
for (const item of failed) {
|
|
49806
|
+
pendingVaultRequestAccesses.delete(item.stageId);
|
|
49807
|
+
pendingCardStore.remove(item.stageId);
|
|
49808
|
+
await editResolvedCard(ctx, { chat_id: item.cardChatId, ...item.threadId != null ? { threadId: item.threadId } : {} }, item.cardMessageId, `\u274c **Too many wrong passphrase attempts** (${MAX_VAULT_PASSPHRASE_ATTEMPTS}). ` + `This request was cancelled \u2014 ask the agent to re-issue it.
|
|
49809
|
+
` + `_Broker: ${escapeHtmlForTg2(brokerMsg)}_`, "passphrase_lockout");
|
|
49810
|
+
}
|
|
49811
|
+
}
|
|
49633
49812
|
async function performVaultAccessApproval(ctx, pending, stageId, senderId, attestation) {
|
|
49634
49813
|
const brokerAuthOpts = attestation.kind === "passphrase" ? { passphrase: attestation.passphrase } : { attest_via_posture: true };
|
|
49635
49814
|
if (pending.scope === "read") {
|
|
49636
49815
|
try {
|
|
49637
|
-
const visible = await
|
|
49816
|
+
const visible = await listViaBroker2();
|
|
49638
49817
|
if (visible !== null && visible.includes(pending.key)) {
|
|
49639
49818
|
pendingVaultRequestAccesses.delete(stageId);
|
|
49640
49819
|
pendingCardStore.remove(stageId);
|
|
49641
49820
|
if (pending.card_message_id != null) {
|
|
49642
|
-
await ctx
|
|
49821
|
+
await editResolvedCard(ctx, pending, pending.card_message_id, `\u2139\ufe0f **${escapeHtmlForTg2(pending.agent)}** already has standing-ACL access to ` + `\`${pending.key}\` (schedule.secrets[]). ` + `**No grant minted** \u2014 a token would shadow the standing ACL. ` + `The agent can read it directly.`, "standing_acl");
|
|
49643
49822
|
}
|
|
49644
|
-
return;
|
|
49823
|
+
return { kind: "ok" };
|
|
49645
49824
|
}
|
|
49646
49825
|
} catch {}
|
|
49647
49826
|
}
|
|
49648
49827
|
let existingReadKeys = [];
|
|
49649
49828
|
let existingWriteKeys = [];
|
|
49650
49829
|
if (pending.scope === "read" || pending.scope === "write") {
|
|
49651
|
-
const list = await
|
|
49830
|
+
const list = await listGrantsViaBroker2(pending.agent, brokerAuthOpts);
|
|
49652
49831
|
if (list.kind === "ok") {
|
|
49653
49832
|
const now = Math.floor(Date.now() / 1000);
|
|
49654
49833
|
const active = list.grants.filter((g) => g.expires_at === null || g.expires_at > now).sort((a, b) => {
|
|
@@ -49677,28 +49856,31 @@ function createCallbackQueryHandlers(deps) {
|
|
|
49677
49856
|
...writeKeys.size > 0 ? { write_keys: Array.from(writeKeys) } : {},
|
|
49678
49857
|
...brokerAuthOpts
|
|
49679
49858
|
};
|
|
49680
|
-
const result = await
|
|
49859
|
+
const result = await mintGrantViaBroker2(mintArgs);
|
|
49681
49860
|
if (result.kind === "unreachable") {
|
|
49682
49861
|
await switchroomReply(ctx, `\uD83D\uDD34 Broker unreachable: ${escapeHtmlForTg2(result.msg)}`, { html: true });
|
|
49683
|
-
return;
|
|
49862
|
+
return { kind: "failed", msg: result.msg };
|
|
49684
49863
|
}
|
|
49685
49864
|
if (result.kind === "error") {
|
|
49865
|
+
if (attestation.kind === "passphrase" && isPassphraseMismatchBrokerError(result.msg)) {
|
|
49866
|
+
return { kind: "passphrase-mismatch", msg: result.msg };
|
|
49867
|
+
}
|
|
49686
49868
|
pendingVaultRequestAccesses.delete(stageId);
|
|
49687
49869
|
pendingCardStore.remove(stageId);
|
|
49688
49870
|
if (pending.card_message_id != null) {
|
|
49689
|
-
await ctx
|
|
49871
|
+
await editResolvedCard(ctx, pending, pending.card_message_id, `**mint_grant failed:** ${escapeHtmlForTg2(result.msg)}`, "mint_failed");
|
|
49690
49872
|
}
|
|
49691
|
-
return;
|
|
49873
|
+
return { kind: "failed", msg: result.msg };
|
|
49692
49874
|
}
|
|
49693
49875
|
const { token, id } = result;
|
|
49694
|
-
const tokenPath =
|
|
49876
|
+
const tokenPath = vaultTokenFilePath2(pending.agent);
|
|
49695
49877
|
try {
|
|
49696
|
-
mkdirSync13(
|
|
49878
|
+
mkdirSync13(dirname5(tokenPath), { recursive: true });
|
|
49697
49879
|
writeFileSync9(tokenPath, token, { mode: 384 });
|
|
49698
49880
|
} catch (err) {
|
|
49699
49881
|
await switchroomReply(ctx, `**Grant created (${escapeHtmlForTg2(id)}) but token write failed:** ` + `${escapeHtmlForTg2(String(err))}
|
|
49700
49882
|
` + `_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 });
|
|
49701
|
-
return;
|
|
49883
|
+
return { kind: "failed", msg: String(err) };
|
|
49702
49884
|
}
|
|
49703
49885
|
pendingVaultRequestAccesses.delete(stageId);
|
|
49704
49886
|
pendingCardStore.remove(stageId);
|
|
@@ -49707,7 +49889,7 @@ function createCallbackQueryHandlers(deps) {
|
|
|
49707
49889
|
const reasonNormalized = normalizeGrantReason(pending.reason);
|
|
49708
49890
|
const footer = getVaultApprovalAuthMode() === "telegram-id" ? `
|
|
49709
49891
|
_Approver verified by Telegram identity \u2014 broker auto-unlocked at startup._` : "";
|
|
49710
|
-
await ctx
|
|
49892
|
+
await editResolvedCard(ctx, pending, pending.card_message_id, buildVaultGrantApprovedCardText({
|
|
49711
49893
|
agentEscaped: escapeHtmlForTg2(pending.agent),
|
|
49712
49894
|
scope: pending.scope,
|
|
49713
49895
|
key: pending.key,
|
|
@@ -49715,7 +49897,7 @@ _Approver verified by Telegram identity \u2014 broker auto-unlocked at startup._
|
|
|
49715
49897
|
grantId: id,
|
|
49716
49898
|
reasonEscaped: reasonNormalized.length > 0 ? escapeHtmlForTg2(reasonNormalized) : undefined,
|
|
49717
49899
|
footer
|
|
49718
|
-
})
|
|
49900
|
+
}), "grant_approved");
|
|
49719
49901
|
}
|
|
49720
49902
|
const synthetic = buildVaultGrantApprovedInbound({
|
|
49721
49903
|
ctx: {
|
|
@@ -49733,6 +49915,7 @@ _Approver verified by Telegram identity \u2014 broker auto-unlocked at startup._
|
|
|
49733
49915
|
const delivered = deliverResumeSyntheticOrBuffer(pending.agent, synthetic);
|
|
49734
49916
|
process.stderr.write(`telegram gateway: vault_grant_approved injection agent=${pending.agent} ` + `key=${pending.key} stage=${stageId} delivered=${delivered}
|
|
49735
49917
|
`);
|
|
49918
|
+
return { kind: "ok" };
|
|
49736
49919
|
}
|
|
49737
49920
|
async function handleSkillProposalCallback(ctx, data) {
|
|
49738
49921
|
const senderId = String(ctx.from?.id ?? "");
|
|
@@ -50025,40 +50208,53 @@ _Approver verified by Telegram identity \u2014 broker auto-unlocked at startup._
|
|
|
50025
50208
|
stageId,
|
|
50026
50209
|
cardChatId: pending.chat_id,
|
|
50027
50210
|
cardMessageId: pending.card_message_id,
|
|
50028
|
-
senderId
|
|
50211
|
+
senderId,
|
|
50212
|
+
...pending.threadId != null ? { threadId: pending.threadId } : {}
|
|
50029
50213
|
};
|
|
50030
50214
|
const items = existing?.kind === "passphrase-for-access-approve" ? [...existing.items.filter((it) => it.stageId !== stageId), newItem] : [newItem];
|
|
50031
50215
|
pendingVaultOps.set(pending.chat_id, {
|
|
50032
50216
|
kind: "passphrase-for-access-approve",
|
|
50033
50217
|
items,
|
|
50218
|
+
...existing?.kind === "passphrase-for-access-approve" && existing.attempts ? { attempts: existing.attempts } : {},
|
|
50034
50219
|
startedAt: existing?.kind === "passphrase-for-access-approve" ? existing.startedAt : Date.now()
|
|
50035
50220
|
});
|
|
50036
50221
|
const joiningBatch = items.length > 1;
|
|
50037
50222
|
await ctx.answerCallbackQuery({ text: joiningBatch ? `\uD83D\uDD10 Queued \u2014 one passphrase covers ${items.length} cards` : "\uD83D\uDD10 Send your passphrase\u2026" }).catch(() => {});
|
|
50038
50223
|
await ctx.api.editMessageText(pending.chat_id, pending.card_message_id, richMessage(`\uD83D\uDD10 _Approved \u2014 waiting for your vault passphrase. See the prompt below._`), { reply_markup: { inline_keyboard: [] } }).catch(() => {});
|
|
50039
|
-
|
|
50040
|
-
|
|
50041
|
-
|
|
50042
|
-
|
|
50043
|
-
|
|
50044
|
-
|
|
50045
|
-
|
|
50046
|
-
|
|
50047
|
-
|
|
50048
|
-
|
|
50049
|
-
` + `_The passphrase is what proves it's you. An agent can never mint this key on its own. We delete the passphrase message the moment we read it._` : `**\u26a0\ufe0f\uD83D\uDD10 ACTION NEEDED: passphrase required**
|
|
50050
|
-
|
|
50051
|
-
` + `Your vault is locked.
|
|
50052
|
-
` + `Reply with your passphrase as your **next message** to unlock and mint the grant for **${escapeHtmlForTg2(pending.agent)}**.
|
|
50053
|
-
|
|
50054
|
-
` + `_Mint authority stays operator-only: the broker only accepts the grant when the passphrase matches. We delete the passphrase message the moment we read it._`;
|
|
50055
|
-
await retryWithThreadFallback(robustApiCall, (tid) => lockedBot.api.sendRichMessage(pending.chat_id, richMessage(promptText), {
|
|
50056
|
-
...tid != null && Number.isFinite(tid) ? { message_thread_id: tid } : {}
|
|
50057
|
-
}), { threadId: pending.threadId, chat_id: pending.chat_id, verb: "vault_request_access.passphrase_prompt" }).catch(() => {});
|
|
50224
|
+
await sendAccessPassphrasePrompt({
|
|
50225
|
+
chat_id: pending.chat_id,
|
|
50226
|
+
...pending.threadId != null ? { threadId: pending.threadId } : {}
|
|
50227
|
+
}, {
|
|
50228
|
+
kind: "first",
|
|
50229
|
+
variant: joiningBatch ? "batch" : isAdminOnly ? "admin-only" : "locked",
|
|
50230
|
+
itemCount: items.length,
|
|
50231
|
+
agentEscaped: escapeHtmlForTg2(pending.agent),
|
|
50232
|
+
key: pending.key
|
|
50233
|
+
});
|
|
50058
50234
|
return;
|
|
50059
50235
|
}
|
|
50060
50236
|
await ctx.answerCallbackQuery({ text: "\u23f3 Minting grant\u2026" }).catch(() => {});
|
|
50061
|
-
await performVaultAccessApproval(ctx, pending, stageId, senderId, {
|
|
50237
|
+
const outcome = await performVaultAccessApproval(ctx, pending, stageId, senderId, {
|
|
50238
|
+
kind: "passphrase",
|
|
50239
|
+
passphrase: cached.passphrase
|
|
50240
|
+
});
|
|
50241
|
+
if (outcome.kind === "passphrase-mismatch") {
|
|
50242
|
+
const openOp = pendingVaultOps.get(pending.chat_id);
|
|
50243
|
+
await resolveAccessApprovalPassphraseMismatch(ctx, {
|
|
50244
|
+
chat_id: pending.chat_id,
|
|
50245
|
+
failed: [
|
|
50246
|
+
{
|
|
50247
|
+
stageId,
|
|
50248
|
+
cardChatId: pending.chat_id,
|
|
50249
|
+
cardMessageId: pending.card_message_id ?? 0,
|
|
50250
|
+
senderId,
|
|
50251
|
+
...pending.threadId != null ? { threadId: pending.threadId } : {}
|
|
50252
|
+
}
|
|
50253
|
+
],
|
|
50254
|
+
priorAttempts: openOp?.kind === "passphrase-for-access-approve" ? openOp.attempts ?? 0 : 0,
|
|
50255
|
+
brokerMsg: outcome.msg
|
|
50256
|
+
});
|
|
50257
|
+
}
|
|
50062
50258
|
return;
|
|
50063
50259
|
}
|
|
50064
50260
|
await ctx.answerCallbackQuery({ text: "Unknown action" }).catch(() => {});
|
|
@@ -50399,7 +50595,7 @@ Which agent?`, { html: true, reply_markup: kb });
|
|
|
50399
50595
|
});
|
|
50400
50596
|
}
|
|
50401
50597
|
async function grantWizardStep2(ctx, chatId, agent, wizardMsgId) {
|
|
50402
|
-
const keys = await
|
|
50598
|
+
const keys = await listViaBroker2();
|
|
50403
50599
|
if (!keys) {
|
|
50404
50600
|
await switchroomReply(ctx, "\uD83D\uDD34 Broker is not running (or unreachable). Cannot list vault keys.", { html: true });
|
|
50405
50601
|
pendingVaultOps.delete(chatId);
|
|
@@ -50490,7 +50686,7 @@ ${keyList}`,
|
|
|
50490
50686
|
} catch {
|
|
50491
50687
|
return;
|
|
50492
50688
|
}
|
|
50493
|
-
const result = await
|
|
50689
|
+
const result = await mintGrantViaBroker2({
|
|
50494
50690
|
agent: state.agent,
|
|
50495
50691
|
keys: state.selectedKeys,
|
|
50496
50692
|
ttl_seconds: state.ttlSeconds ?? null,
|
|
@@ -50505,9 +50701,9 @@ ${keyList}`,
|
|
|
50505
50701
|
return;
|
|
50506
50702
|
}
|
|
50507
50703
|
const { token, id } = result;
|
|
50508
|
-
const tokenPath =
|
|
50704
|
+
const tokenPath = vaultTokenFilePath2(state.agent);
|
|
50509
50705
|
try {
|
|
50510
|
-
mkdirSync13(
|
|
50706
|
+
mkdirSync13(dirname5(tokenPath), { recursive: true });
|
|
50511
50707
|
writeFileSync9(tokenPath, token, { mode: 384 });
|
|
50512
50708
|
} catch (err) {
|
|
50513
50709
|
await switchroomReply(ctx, `**Grant created but token write failed:** ${escapeHtmlForTg2(String(err))}`, { html: true });
|
|
@@ -50534,7 +50730,7 @@ ${keyList}`,
|
|
|
50534
50730
|
const revokeMatch = /^vg:revoke:(.+)$/.exec(data);
|
|
50535
50731
|
if (revokeMatch) {
|
|
50536
50732
|
const grantId = revokeMatch[1];
|
|
50537
|
-
const result = await
|
|
50733
|
+
const result = await listGrantsViaBroker2(undefined);
|
|
50538
50734
|
if (result.kind !== "ok") {
|
|
50539
50735
|
await ctx.answerCallbackQuery({ text: "Broker unreachable." }).catch(() => {});
|
|
50540
50736
|
return;
|
|
@@ -50983,6 +51179,7 @@ ${trimmed.replace(/```/g, "`\u200b``")}
|
|
|
50983
51179
|
return {
|
|
50984
51180
|
handleVaultRecentDenialCallback,
|
|
50985
51181
|
performVaultAccessApproval,
|
|
51182
|
+
resolveAccessApprovalPassphraseMismatch,
|
|
50986
51183
|
handleSkillProposalCallback,
|
|
50987
51184
|
handleMentalModelProposeCallback,
|
|
50988
51185
|
handleVaultRequestAccessCallback,
|
|
@@ -51955,7 +52152,7 @@ function pickRecoveredPermissionOrigin(recentTurns, now, maxAgeMs) {
|
|
|
51955
52152
|
|
|
51956
52153
|
// gateway/approval-hold.ts
|
|
51957
52154
|
import { mkdirSync as mkdirSync14, writeFileSync as writeFileSync11, readFileSync as readFileSync13, unlinkSync as unlinkSync9, chmodSync as chmodSync4, lstatSync as lstatSync2 } from "node:fs";
|
|
51958
|
-
import { join as join16, dirname as
|
|
52155
|
+
import { join as join16, dirname as dirname6 } from "node:path";
|
|
51959
52156
|
var BLOCKED_APPROVAL_FILE_MODE = 420;
|
|
51960
52157
|
var BLOCKED_APPROVAL_DIR_MODE = 1023;
|
|
51961
52158
|
var HELD_RETRY_BACKOFF_MS = 60000;
|
|
@@ -51988,7 +52185,7 @@ function createBlockedApprovalStore(dir, agent, fallbackDir) {
|
|
|
51988
52185
|
const fallback = fallbackDir != null ? join16(fallbackDir, "blocked-approval.json") : null;
|
|
51989
52186
|
let active = primary;
|
|
51990
52187
|
function tryWrite(target, body, dirMode) {
|
|
51991
|
-
const parent =
|
|
52188
|
+
const parent = dirname6(target);
|
|
51992
52189
|
try {
|
|
51993
52190
|
mkdirSync14(parent, { recursive: true });
|
|
51994
52191
|
try {
|
|
@@ -62658,9 +62855,249 @@ async function retryWithThreadFallback2(retry, send, opts) {
|
|
|
62658
62855
|
}
|
|
62659
62856
|
}
|
|
62660
62857
|
|
|
62858
|
+
// edit-flood-fuse.ts
|
|
62859
|
+
var EDIT_METHODS = new Set([
|
|
62860
|
+
"editMessageText",
|
|
62861
|
+
"editMessageCaption",
|
|
62862
|
+
"editMessageMedia",
|
|
62863
|
+
"editMessageReplyMarkup",
|
|
62864
|
+
"editMessageLiveLocation",
|
|
62865
|
+
"editMessageChecklist"
|
|
62866
|
+
]);
|
|
62867
|
+
var SEND_METHODS = new Set([
|
|
62868
|
+
"sendMessage",
|
|
62869
|
+
"sendPhoto",
|
|
62870
|
+
"sendDocument",
|
|
62871
|
+
"sendMediaGroup",
|
|
62872
|
+
"sendAnimation",
|
|
62873
|
+
"sendVideo",
|
|
62874
|
+
"sendVoice",
|
|
62875
|
+
"sendAudio",
|
|
62876
|
+
"sendSticker",
|
|
62877
|
+
"sendLocation",
|
|
62878
|
+
"forwardMessage",
|
|
62879
|
+
"forwardMessages",
|
|
62880
|
+
"copyMessage",
|
|
62881
|
+
"copyMessages",
|
|
62882
|
+
"sendRichMessage"
|
|
62883
|
+
]);
|
|
62884
|
+
var EDIT_FLOOD_FUSE_DEFAULTS = {
|
|
62885
|
+
perMessageMaxPerWindow: 20,
|
|
62886
|
+
perMessageWindowMs: 60000,
|
|
62887
|
+
perChatEditMaxPerWindow: 30,
|
|
62888
|
+
perChatSendMaxPerWindow: 25,
|
|
62889
|
+
perChatWindowMs: 60000,
|
|
62890
|
+
maxDeferMs: 30000,
|
|
62891
|
+
tightenFactor: 0.5,
|
|
62892
|
+
tightenMs: 600000
|
|
62893
|
+
};
|
|
62894
|
+
var DROPPED_RESULT = true;
|
|
62895
|
+
function createEditFloodFuse(config = {}) {
|
|
62896
|
+
const enabled2 = config.enabled ?? true;
|
|
62897
|
+
const clock = config.clock ?? systemClock;
|
|
62898
|
+
const D = EDIT_FLOOD_FUSE_DEFAULTS;
|
|
62899
|
+
const perMessageMax = config.perMessageMaxPerWindow ?? D.perMessageMaxPerWindow;
|
|
62900
|
+
const perMessageWindowMs = config.perMessageWindowMs ?? D.perMessageWindowMs;
|
|
62901
|
+
const perChatEditMax = config.perChatEditMaxPerWindow ?? D.perChatEditMaxPerWindow;
|
|
62902
|
+
const perChatSendMax = config.perChatSendMaxPerWindow ?? D.perChatSendMaxPerWindow;
|
|
62903
|
+
const perChatWindowMs = config.perChatWindowMs ?? D.perChatWindowMs;
|
|
62904
|
+
const maxDeferMs = config.maxDeferMs ?? D.maxDeferMs;
|
|
62905
|
+
const tightenFactor = config.tightenFactor ?? D.tightenFactor;
|
|
62906
|
+
const tightenMs = config.tightenMs ?? D.tightenMs;
|
|
62907
|
+
const onTrip = config.onTrip;
|
|
62908
|
+
const windows = new Map;
|
|
62909
|
+
const counters = { deferred: 0, dropped: 0, superseded: 0, floodObserved: 0 };
|
|
62910
|
+
let tightenedUntil = 0;
|
|
62911
|
+
function isTightened(now) {
|
|
62912
|
+
return tightenedUntil > now;
|
|
62913
|
+
}
|
|
62914
|
+
function ceiling(base, now) {
|
|
62915
|
+
if (!isTightened(now))
|
|
62916
|
+
return base;
|
|
62917
|
+
return Math.max(1, Math.floor(base * tightenFactor));
|
|
62918
|
+
}
|
|
62919
|
+
function win(key) {
|
|
62920
|
+
let w = windows.get(key);
|
|
62921
|
+
if (w === undefined) {
|
|
62922
|
+
w = { ts: [], waiter: null, inflight: 0 };
|
|
62923
|
+
windows.set(key, w);
|
|
62924
|
+
}
|
|
62925
|
+
return w;
|
|
62926
|
+
}
|
|
62927
|
+
function prune(w, now, windowMs) {
|
|
62928
|
+
const cutoff = now - windowMs;
|
|
62929
|
+
while (w.ts.length > 0 && w.ts[0] <= cutoff)
|
|
62930
|
+
w.ts.shift();
|
|
62931
|
+
}
|
|
62932
|
+
let sinceEvict = 0;
|
|
62933
|
+
function evict(now) {
|
|
62934
|
+
if (windows.size < 4096)
|
|
62935
|
+
return;
|
|
62936
|
+
if (++sinceEvict < 1024)
|
|
62937
|
+
return;
|
|
62938
|
+
sinceEvict = 0;
|
|
62939
|
+
const widest = Math.max(perMessageWindowMs, perChatWindowMs);
|
|
62940
|
+
for (const [k, w] of windows) {
|
|
62941
|
+
if (w.waiter === null && w.inflight === 0 && (w.ts.length === 0 || w.ts[w.ts.length - 1] <= now - widest)) {
|
|
62942
|
+
windows.delete(k);
|
|
62943
|
+
}
|
|
62944
|
+
}
|
|
62945
|
+
}
|
|
62946
|
+
function waitFor(w, now, windowMs, max) {
|
|
62947
|
+
prune(w, now, windowMs);
|
|
62948
|
+
if (w.ts.length < max)
|
|
62949
|
+
return 0;
|
|
62950
|
+
return Math.max(1, w.ts[0] + windowMs - now);
|
|
62951
|
+
}
|
|
62952
|
+
function payloadKeys(payload) {
|
|
62953
|
+
const p = payload ?? {};
|
|
62954
|
+
const chat = p.chat_id != null ? String(p.chat_id) : null;
|
|
62955
|
+
const msg = p.message_id != null ? String(p.message_id) : null;
|
|
62956
|
+
return { chat, msg };
|
|
62957
|
+
}
|
|
62958
|
+
function noteFlood(now) {
|
|
62959
|
+
counters.floodObserved++;
|
|
62960
|
+
tightenedUntil = now + tightenMs;
|
|
62961
|
+
}
|
|
62962
|
+
function looksLikeFlood(err) {
|
|
62963
|
+
const e = err;
|
|
62964
|
+
if (e != null && typeof e === "object") {
|
|
62965
|
+
if (e.error_code === 429)
|
|
62966
|
+
return true;
|
|
62967
|
+
if (e.parameters != null && typeof e.parameters.retry_after === "number")
|
|
62968
|
+
return true;
|
|
62969
|
+
}
|
|
62970
|
+
const msg = err instanceof Error ? err.message : String(err ?? "");
|
|
62971
|
+
return /too many requests/i.test(msg) || /retry[ _-]?after/i.test(msg);
|
|
62972
|
+
}
|
|
62973
|
+
function unreserve(key, at) {
|
|
62974
|
+
const w = windows.get(key);
|
|
62975
|
+
if (w === undefined)
|
|
62976
|
+
return;
|
|
62977
|
+
const i = w.ts.lastIndexOf(at);
|
|
62978
|
+
if (i >= 0)
|
|
62979
|
+
w.ts.splice(i, 1);
|
|
62980
|
+
}
|
|
62981
|
+
async function awaitRoom(key, windowMs, max, method, mode, dropGuard) {
|
|
62982
|
+
const w = win(key);
|
|
62983
|
+
const deadline = clock.now() + maxDeferMs;
|
|
62984
|
+
let counted = false;
|
|
62985
|
+
for (;; ) {
|
|
62986
|
+
const now = clock.now();
|
|
62987
|
+
const wait = waitFor(w, now, windowMs, ceiling(max, now));
|
|
62988
|
+
if (wait === 0) {
|
|
62989
|
+
w.ts.push(now);
|
|
62990
|
+
return now;
|
|
62991
|
+
}
|
|
62992
|
+
if (now >= deadline) {
|
|
62993
|
+
if (mode !== "release" && (dropGuard === undefined || dropGuard())) {
|
|
62994
|
+
counters.dropped++;
|
|
62995
|
+
onTrip?.({ method, key, action: "dropped" });
|
|
62996
|
+
return null;
|
|
62997
|
+
}
|
|
62998
|
+
w.ts.push(now);
|
|
62999
|
+
return now;
|
|
63000
|
+
}
|
|
63001
|
+
if (!counted) {
|
|
63002
|
+
counters.deferred++;
|
|
63003
|
+
counted = true;
|
|
63004
|
+
onTrip?.({ method, key, action: "deferred" });
|
|
63005
|
+
}
|
|
63006
|
+
let killed = false;
|
|
63007
|
+
if (mode === "supersede") {
|
|
63008
|
+
w.waiter?.kill();
|
|
63009
|
+
const superseded = new Promise((resolve6) => {
|
|
63010
|
+
w.waiter = { kill: () => {
|
|
63011
|
+
killed = true;
|
|
63012
|
+
resolve6();
|
|
63013
|
+
} };
|
|
63014
|
+
});
|
|
63015
|
+
await Promise.race([clock.sleep(Math.min(wait, deadline - now)), superseded]);
|
|
63016
|
+
if (killed) {
|
|
63017
|
+
counters.superseded++;
|
|
63018
|
+
onTrip?.({ method, key, action: "superseded" });
|
|
63019
|
+
return null;
|
|
63020
|
+
}
|
|
63021
|
+
w.waiter = null;
|
|
63022
|
+
} else {
|
|
63023
|
+
await clock.sleep(Math.min(wait, deadline - now));
|
|
63024
|
+
}
|
|
63025
|
+
}
|
|
63026
|
+
}
|
|
63027
|
+
async function apply(method, payload, next) {
|
|
63028
|
+
if (!enabled2)
|
|
63029
|
+
return next();
|
|
63030
|
+
const isEdit = EDIT_METHODS.has(method);
|
|
63031
|
+
const isSend = SEND_METHODS.has(method);
|
|
63032
|
+
if (!isEdit && !isSend)
|
|
63033
|
+
return runObserved(next);
|
|
63034
|
+
const { chat, msg } = payloadKeys(payload);
|
|
63035
|
+
if (chat == null)
|
|
63036
|
+
return runObserved(next);
|
|
63037
|
+
const now = clock.now();
|
|
63038
|
+
evict(now);
|
|
63039
|
+
if (isEdit) {
|
|
63040
|
+
if (msg == null)
|
|
63041
|
+
return runObserved(next);
|
|
63042
|
+
const msgKey = `m:${chat}:${msg}`;
|
|
63043
|
+
const mw = win(msgKey);
|
|
63044
|
+
mw.inflight++;
|
|
63045
|
+
try {
|
|
63046
|
+
const msgSlot = await awaitRoom(msgKey, perMessageWindowMs, perMessageMax, method, "supersede");
|
|
63047
|
+
if (msgSlot === null)
|
|
63048
|
+
return DROPPED_RESULT;
|
|
63049
|
+
const chatKey3 = `ce:${chat}`;
|
|
63050
|
+
const chatSlot = await awaitRoom(chatKey3, perChatWindowMs, perChatEditMax, method, "drop", () => mw.inflight > 1);
|
|
63051
|
+
if (chatSlot === null) {
|
|
63052
|
+
unreserve(msgKey, msgSlot);
|
|
63053
|
+
return DROPPED_RESULT;
|
|
63054
|
+
}
|
|
63055
|
+
return runObserved(next);
|
|
63056
|
+
} finally {
|
|
63057
|
+
mw.inflight--;
|
|
63058
|
+
}
|
|
63059
|
+
}
|
|
63060
|
+
const chatKey2 = `cs:${chat}`;
|
|
63061
|
+
await awaitRoom(chatKey2, perChatWindowMs, perChatSendMax, method, "release");
|
|
63062
|
+
return runObserved(next);
|
|
63063
|
+
}
|
|
63064
|
+
async function runObserved(next) {
|
|
63065
|
+
try {
|
|
63066
|
+
const res = await next();
|
|
63067
|
+
const r = res;
|
|
63068
|
+
if (r != null && typeof r === "object" && r.ok === false && r.error_code === 429) {
|
|
63069
|
+
noteFlood(clock.now());
|
|
63070
|
+
}
|
|
63071
|
+
return res;
|
|
63072
|
+
} catch (err) {
|
|
63073
|
+
if (looksLikeFlood(err))
|
|
63074
|
+
noteFlood(clock.now());
|
|
63075
|
+
throw err;
|
|
63076
|
+
}
|
|
63077
|
+
}
|
|
63078
|
+
function stats() {
|
|
63079
|
+
const now = clock.now();
|
|
63080
|
+
return {
|
|
63081
|
+
enabled: enabled2,
|
|
63082
|
+
deferred: counters.deferred,
|
|
63083
|
+
dropped: counters.dropped,
|
|
63084
|
+
superseded: counters.superseded,
|
|
63085
|
+
floodObserved: counters.floodObserved,
|
|
63086
|
+
tightened: isTightened(now),
|
|
63087
|
+
perMessageCeiling: ceiling(perMessageMax, now)
|
|
63088
|
+
};
|
|
63089
|
+
}
|
|
63090
|
+
return { apply, stats };
|
|
63091
|
+
}
|
|
63092
|
+
function installEditFloodFuse(bot, config = {}) {
|
|
63093
|
+
const fuse = createEditFloodFuse(config);
|
|
63094
|
+
bot.api.config.use(async (prev, method, payload, signal) => fuse.apply(method, payload, () => prev(method, payload, signal)));
|
|
63095
|
+
return fuse;
|
|
63096
|
+
}
|
|
63097
|
+
|
|
62661
63098
|
// send-gate.ts
|
|
62662
63099
|
import { createHash } from "node:crypto";
|
|
62663
|
-
var
|
|
63100
|
+
var systemClock2 = {
|
|
62664
63101
|
now: () => Date.now(),
|
|
62665
63102
|
sleep: (ms) => new Promise((r) => setTimeout(r, ms))
|
|
62666
63103
|
};
|
|
@@ -62761,7 +63198,7 @@ var SEND_GATE_DEFAULTS = {
|
|
|
62761
63198
|
};
|
|
62762
63199
|
function createSendGate(config) {
|
|
62763
63200
|
const enabled2 = config.enabled;
|
|
62764
|
-
const clock = config.clock ??
|
|
63201
|
+
const clock = config.clock ?? systemClock2;
|
|
62765
63202
|
const globalPerSec = config.globalPerSec ?? SEND_GATE_DEFAULTS.globalPerSec;
|
|
62766
63203
|
const globalBurst = config.globalBurst ?? SEND_GATE_DEFAULTS.globalBurst;
|
|
62767
63204
|
const perChatPerSec = config.perChatPerSec ?? SEND_GATE_DEFAULTS.perChatPerSec;
|
|
@@ -63491,7 +63928,7 @@ import {
|
|
|
63491
63928
|
unlinkSync as unlinkSync10,
|
|
63492
63929
|
renameSync as renameSync9
|
|
63493
63930
|
} from "node:fs";
|
|
63494
|
-
import { dirname as
|
|
63931
|
+
import { dirname as dirname7, join as join17 } from "node:path";
|
|
63495
63932
|
var FLOOD_STATE_FILE = "flood-wait.json";
|
|
63496
63933
|
var FLOOD_STATE_MODE = 420;
|
|
63497
63934
|
function floodStatePath(stateDir) {
|
|
@@ -63544,7 +63981,7 @@ function readFloodState(path2) {
|
|
|
63544
63981
|
function writeFloodState(path2, state, log = (l) => process.stderr.write(l)) {
|
|
63545
63982
|
const payload = JSON.stringify(state);
|
|
63546
63983
|
try {
|
|
63547
|
-
mkdirSync15(
|
|
63984
|
+
mkdirSync15(dirname7(path2), { recursive: true });
|
|
63548
63985
|
} catch {}
|
|
63549
63986
|
try {
|
|
63550
63987
|
writeFileSync12(path2, payload, { mode: FLOOD_STATE_MODE });
|
|
@@ -63675,7 +64112,7 @@ function readFloodWindows(path2, now, log = (l) => process.stderr.write(l)) {
|
|
|
63675
64112
|
}
|
|
63676
64113
|
function writeFloodWindow(path2, record, now) {
|
|
63677
64114
|
try {
|
|
63678
|
-
mkdirSync15(
|
|
64115
|
+
mkdirSync15(dirname7(path2), { recursive: true });
|
|
63679
64116
|
const existing = readFloodWindows(path2, now);
|
|
63680
64117
|
const byScope = new Map;
|
|
63681
64118
|
for (const r of existing)
|
|
@@ -63850,7 +64287,7 @@ function __resetAllForTests() {
|
|
|
63850
64287
|
}
|
|
63851
64288
|
|
|
63852
64289
|
// ../node_modules/.bun/posthog-node@5.29.2/node_modules/posthog-node/dist/extensions/error-tracking/modifiers/module.node.mjs
|
|
63853
|
-
import { dirname as
|
|
64290
|
+
import { dirname as dirname8, posix, sep as sep3 } from "path";
|
|
63854
64291
|
function createModulerModifier() {
|
|
63855
64292
|
const getModuleFromFileName = createGetModuleFromFilename();
|
|
63856
64293
|
return async (frames) => {
|
|
@@ -63859,7 +64296,7 @@ function createModulerModifier() {
|
|
|
63859
64296
|
return frames;
|
|
63860
64297
|
};
|
|
63861
64298
|
}
|
|
63862
|
-
function createGetModuleFromFilename(basePath = process.argv[1] ?
|
|
64299
|
+
function createGetModuleFromFilename(basePath = process.argv[1] ? dirname8(process.argv[1]) : process.cwd(), isWindows = sep3 === "\\") {
|
|
63863
64300
|
const normalizedBase = isWindows ? normalizeWindowsPath(basePath) : basePath;
|
|
63864
64301
|
return (filename) => {
|
|
63865
64302
|
if (!filename)
|
|
@@ -68291,7 +68728,7 @@ class PostHog extends PostHogBackendClient {
|
|
|
68291
68728
|
|
|
68292
68729
|
// analytics-posthog.ts
|
|
68293
68730
|
import { existsSync as existsSync13, mkdirSync as mkdirSync16, readFileSync as readFileSync15, writeFileSync as writeFileSync13 } from "node:fs";
|
|
68294
|
-
import { dirname as
|
|
68731
|
+
import { dirname as dirname9, join as join19 } from "node:path";
|
|
68295
68732
|
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
68296
68733
|
var DEFAULT_KEY = "phc_qKY87cKWZm6ZyCtk7LcRd2cU8Sg42u7Ywhui5stYCegd";
|
|
68297
68734
|
var DEFAULT_HOST = "https://us.i.posthog.com";
|
|
@@ -68330,7 +68767,7 @@ function getDistinctId() {
|
|
|
68330
68767
|
const id = randomUUID2();
|
|
68331
68768
|
cachedDistinctId = id;
|
|
68332
68769
|
try {
|
|
68333
|
-
mkdirSync16(
|
|
68770
|
+
mkdirSync16(dirname9(fallbackPath), { recursive: true });
|
|
68334
68771
|
writeFileSync13(fallbackPath, id, "utf-8");
|
|
68335
68772
|
} catch {}
|
|
68336
68773
|
return id;
|
|
@@ -68403,11 +68840,11 @@ function installGlobalErrorHandlers() {
|
|
|
68403
68840
|
|
|
68404
68841
|
// runtime-metrics.ts
|
|
68405
68842
|
import { mkdirSync as mkdirSync18, appendFileSync as appendFileSync3 } from "node:fs";
|
|
68406
|
-
import { dirname as
|
|
68843
|
+
import { dirname as dirname11, join as join21 } from "node:path";
|
|
68407
68844
|
|
|
68408
68845
|
// analytics-posthog.ts
|
|
68409
68846
|
import { existsSync as existsSync14, mkdirSync as mkdirSync17, readFileSync as readFileSync16, writeFileSync as writeFileSync14 } from "node:fs";
|
|
68410
|
-
import { dirname as
|
|
68847
|
+
import { dirname as dirname10, join as join20 } from "node:path";
|
|
68411
68848
|
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
68412
68849
|
var DEFAULT_KEY2 = "phc_qKY87cKWZm6ZyCtk7LcRd2cU8Sg42u7Ywhui5stYCegd";
|
|
68413
68850
|
var DEFAULT_HOST2 = "https://us.i.posthog.com";
|
|
@@ -68445,7 +68882,7 @@ function getDistinctId2() {
|
|
|
68445
68882
|
const id = randomUUID3();
|
|
68446
68883
|
cachedDistinctId2 = id;
|
|
68447
68884
|
try {
|
|
68448
|
-
mkdirSync17(
|
|
68885
|
+
mkdirSync17(dirname10(fallbackPath), { recursive: true });
|
|
68449
68886
|
writeFileSync14(fallbackPath, id, "utf-8");
|
|
68450
68887
|
} catch {}
|
|
68451
68888
|
return id;
|
|
@@ -68500,7 +68937,7 @@ function resolveJsonlPath() {
|
|
|
68500
68937
|
function appendJsonl(line) {
|
|
68501
68938
|
const path2 = resolveJsonlPath();
|
|
68502
68939
|
try {
|
|
68503
|
-
mkdirSync18(
|
|
68940
|
+
mkdirSync18(dirname11(path2), { recursive: true });
|
|
68504
68941
|
appendFileSync3(path2, line + `
|
|
68505
68942
|
`, "utf-8");
|
|
68506
68943
|
} catch (err) {
|
|
@@ -69169,8 +69606,8 @@ function __resetAllForTests3() {
|
|
|
69169
69606
|
|
|
69170
69607
|
// silent-end.ts
|
|
69171
69608
|
import { existsSync as existsSync16, readFileSync as readFileSync17, writeFileSync as writeFileSync15, unlinkSync as unlinkSync11, mkdirSync as mkdirSync19 } from "node:fs";
|
|
69172
|
-
import { dirname as
|
|
69173
|
-
import { homedir as
|
|
69609
|
+
import { dirname as dirname12, join as join22 } from "node:path";
|
|
69610
|
+
import { homedir as homedir5 } from "node:os";
|
|
69174
69611
|
var SILENT_END_MAX_RETRIES = 2;
|
|
69175
69612
|
var SILENT_END_STALE_RECORD_MAX_AGE_MS = 30 * 60000;
|
|
69176
69613
|
function resolveStateDir(deps) {
|
|
@@ -69179,7 +69616,7 @@ function resolveStateDir(deps) {
|
|
|
69179
69616
|
const env = process.env.TELEGRAM_STATE_DIR;
|
|
69180
69617
|
if (env != null && env !== "")
|
|
69181
69618
|
return env;
|
|
69182
|
-
const home2 = process.env.HOME ??
|
|
69619
|
+
const home2 = process.env.HOME ?? homedir5();
|
|
69183
69620
|
return join22(home2, ".claude", "channels", "telegram");
|
|
69184
69621
|
}
|
|
69185
69622
|
function resolveStatePath2(deps) {
|
|
@@ -69212,7 +69649,7 @@ function writeSilentEndState(args, deps) {
|
|
|
69212
69649
|
timestamp: Date.now()
|
|
69213
69650
|
};
|
|
69214
69651
|
try {
|
|
69215
|
-
mkdirSync19(
|
|
69652
|
+
mkdirSync19(dirname12(statePath), { recursive: true });
|
|
69216
69653
|
writeFileSync15(statePath, JSON.stringify(state3), "utf8");
|
|
69217
69654
|
emitLog(deps, `silent-end: wrote state file turnKey=${args.turnKey} retryCount=${retryCount}
|
|
69218
69655
|
`);
|
|
@@ -69301,7 +69738,7 @@ function resolveAnswerLaneConfig(input) {
|
|
|
69301
69738
|
}
|
|
69302
69739
|
|
|
69303
69740
|
// session-tail.ts
|
|
69304
|
-
import { homedir as
|
|
69741
|
+
import { homedir as homedir6 } from "os";
|
|
69305
69742
|
import { basename as basename8, join as join24 } from "path";
|
|
69306
69743
|
|
|
69307
69744
|
// operator-events.ts
|
|
@@ -69704,7 +70141,7 @@ var OPERATOR_ACTIONABLE_KINDS = new Set([
|
|
|
69704
70141
|
function sanitizeCwdToProjectName(cwd) {
|
|
69705
70142
|
return cwd.replace(/[^a-zA-Z0-9]/g, "-");
|
|
69706
70143
|
}
|
|
69707
|
-
function getProjectsDirForCwd(cwd = process.cwd(), claudeHome = process.env.CLAUDE_CONFIG_DIR ?? join24(
|
|
70144
|
+
function getProjectsDirForCwd(cwd = process.cwd(), claudeHome = process.env.CLAUDE_CONFIG_DIR ?? join24(homedir6(), ".claude")) {
|
|
69708
70145
|
return join24(claudeHome, "projects", sanitizeCwdToProjectName(cwd));
|
|
69709
70146
|
}
|
|
69710
70147
|
function parseChannelMeta(content3) {
|
|
@@ -71048,7 +71485,7 @@ function defaultAddAccount(label, credentials, opts) {
|
|
|
71048
71485
|
// ../src/auth/broker/client.ts
|
|
71049
71486
|
init_protocol2();
|
|
71050
71487
|
import * as net3 from "node:net";
|
|
71051
|
-
import { homedir as
|
|
71488
|
+
import { homedir as homedir7 } from "node:os";
|
|
71052
71489
|
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
71053
71490
|
import { join as join26 } from "node:path";
|
|
71054
71491
|
var DEFAULT_TIMEOUT_MS3 = 5000;
|
|
@@ -71060,7 +71497,7 @@ function reviveDate2(v) {
|
|
|
71060
71497
|
const d = new Date(v);
|
|
71061
71498
|
return Number.isNaN(d.getTime()) ? null : d;
|
|
71062
71499
|
}
|
|
71063
|
-
function operatorSocketPath2(home2 =
|
|
71500
|
+
function operatorSocketPath2(home2 = homedir7()) {
|
|
71064
71501
|
return join26(home2, ".switchroom", "state", "auth-broker-operator", "sock");
|
|
71065
71502
|
}
|
|
71066
71503
|
function resolveAuthBrokerSocketPath2(opts) {
|
|
@@ -71629,7 +72066,7 @@ function resolveExhaustUntil(resetAtMs, now = Date.now()) {
|
|
|
71629
72066
|
// gateway/auth-add-flow.ts
|
|
71630
72067
|
import { execFileSync as execFileSync4 } from "node:child_process";
|
|
71631
72068
|
import { existsSync as existsSync22, mkdirSync as mkdirSync23, readFileSync as readFileSync21, readdirSync as readdirSync6, rmSync as rmSync5, statSync as statSync9, writeFileSync as writeFileSync19 } from "node:fs";
|
|
71632
|
-
import { homedir as
|
|
72069
|
+
import { homedir as homedir8 } from "node:os";
|
|
71633
72070
|
import { join as join27 } from "node:path";
|
|
71634
72071
|
import { randomBytes as randomBytes8 } from "node:crypto";
|
|
71635
72072
|
|
|
@@ -71735,7 +72172,7 @@ function makeAuthAddTmuxOps(tmuxBin = "tmux") {
|
|
|
71735
72172
|
};
|
|
71736
72173
|
}
|
|
71737
72174
|
var pendingAuthAddFlows = new Map;
|
|
71738
|
-
function pickScratchDir(label, home2 =
|
|
72175
|
+
function pickScratchDir(label, home2 = homedir8()) {
|
|
71739
72176
|
const suffix = randomBytes8(8).toString("hex");
|
|
71740
72177
|
return join27(home2, ".switchroom", "accounts", ".in-progress", `${label}-${suffix}`);
|
|
71741
72178
|
}
|
|
@@ -71786,7 +72223,7 @@ async function startAccountAuthSession(label, opts = {}) {
|
|
|
71786
72223
|
if (process.env.SWITCHROOM_TMUX_SUPERVISOR !== "1" && !opts.tmuxOps) {
|
|
71787
72224
|
throw new Error('tmux supervisor required for /auth add: SWITCHROOM_TMUX_SUPERVISOR is not set to "1". ' + "Legacy pipe-based setup-token is unsupported (setup-token writes to /dev/tty, not stdout/stderr).");
|
|
71788
72225
|
}
|
|
71789
|
-
const home2 = opts.home ??
|
|
72226
|
+
const home2 = opts.home ?? homedir8();
|
|
71790
72227
|
const urlTimeoutMs = opts.urlTimeoutMs ?? 30000;
|
|
71791
72228
|
const agentName3 = opts.agentName ?? process.env.SWITCHROOM_AGENT_NAME ?? "gateway";
|
|
71792
72229
|
const tmux = opts.tmuxOps ?? makeAuthAddTmuxOps(opts.tmuxBin);
|
|
@@ -74756,7 +75193,7 @@ import {
|
|
|
74756
75193
|
appendFileSync as appendFileSync4
|
|
74757
75194
|
} from "node:fs";
|
|
74758
75195
|
import { join as join29 } from "node:path";
|
|
74759
|
-
import { homedir as
|
|
75196
|
+
import { homedir as homedir9 } from "node:os";
|
|
74760
75197
|
function sha256Hex(s) {
|
|
74761
75198
|
return createHash3("sha256").update(s, "utf8").digest("hex");
|
|
74762
75199
|
}
|
|
@@ -74766,7 +75203,7 @@ function resolveStateDir2(explicit) {
|
|
|
74766
75203
|
const env = process.env.TELEGRAM_STATE_DIR;
|
|
74767
75204
|
if (env != null && env !== "")
|
|
74768
75205
|
return env;
|
|
74769
|
-
const home2 = process.env.HOME ??
|
|
75206
|
+
const home2 = process.env.HOME ?? homedir9();
|
|
74770
75207
|
return join29(home2, ".claude", "channels", "telegram");
|
|
74771
75208
|
}
|
|
74772
75209
|
function resolveOutboxDir(stateDir) {
|
|
@@ -75620,8 +76057,8 @@ function monotonicNowMs2() {
|
|
|
75620
76057
|
|
|
75621
76058
|
// silent-end.ts
|
|
75622
76059
|
import { existsSync as existsSync26, readFileSync as readFileSync24, writeFileSync as writeFileSync22, unlinkSync as unlinkSync13, mkdirSync as mkdirSync27 } from "node:fs";
|
|
75623
|
-
import { dirname as
|
|
75624
|
-
import { homedir as
|
|
76060
|
+
import { dirname as dirname13, join as join31 } from "node:path";
|
|
76061
|
+
import { homedir as homedir10 } from "node:os";
|
|
75625
76062
|
var SILENT_END_MAX_RETRIES2 = 2;
|
|
75626
76063
|
var SILENT_END_STALE_RECORD_MAX_AGE_MS2 = 30 * 60000;
|
|
75627
76064
|
function silentEndFallbackText(turnDurationMs) {
|
|
@@ -75634,7 +76071,7 @@ function resolveStateDir3(deps) {
|
|
|
75634
76071
|
const env = process.env.TELEGRAM_STATE_DIR;
|
|
75635
76072
|
if (env != null && env !== "")
|
|
75636
76073
|
return env;
|
|
75637
|
-
const home2 = process.env.HOME ??
|
|
76074
|
+
const home2 = process.env.HOME ?? homedir10();
|
|
75638
76075
|
return join31(home2, ".claude", "channels", "telegram");
|
|
75639
76076
|
}
|
|
75640
76077
|
function resolveStatePath3(deps) {
|
|
@@ -75667,7 +76104,7 @@ function writeSilentEndState2(args, deps) {
|
|
|
75667
76104
|
timestamp: Date.now()
|
|
75668
76105
|
};
|
|
75669
76106
|
try {
|
|
75670
|
-
mkdirSync27(
|
|
76107
|
+
mkdirSync27(dirname13(statePath), { recursive: true });
|
|
75671
76108
|
writeFileSync22(statePath, JSON.stringify(state3), "utf8");
|
|
75672
76109
|
emitLog2(deps, `silent-end: wrote state file turnKey=${args.turnKey} retryCount=${retryCount}
|
|
75673
76110
|
`);
|
|
@@ -77261,7 +77698,7 @@ init_rich_send();
|
|
|
77261
77698
|
|
|
77262
77699
|
// runtime-metrics.ts
|
|
77263
77700
|
import { mkdirSync as mkdirSync29, appendFileSync as appendFileSync6 } from "node:fs";
|
|
77264
|
-
import { dirname as
|
|
77701
|
+
import { dirname as dirname14, join as join33 } from "node:path";
|
|
77265
77702
|
function resolveJsonlPath2() {
|
|
77266
77703
|
const override = process.env.SWITCHROOM_RUNTIME_METRICS_PATH;
|
|
77267
77704
|
if (override && override.trim() !== "")
|
|
@@ -77272,7 +77709,7 @@ function resolveJsonlPath2() {
|
|
|
77272
77709
|
function appendJsonl2(line) {
|
|
77273
77710
|
const path2 = resolveJsonlPath2();
|
|
77274
77711
|
try {
|
|
77275
|
-
mkdirSync29(
|
|
77712
|
+
mkdirSync29(dirname14(path2), { recursive: true });
|
|
77276
77713
|
appendFileSync6(path2, line + `
|
|
77277
77714
|
`, "utf-8");
|
|
77278
77715
|
} catch (err) {
|
|
@@ -79230,7 +79667,16 @@ function createNarrativeLane(deps) {
|
|
|
79230
79667
|
reconcileStatusPin(`fg:${statusKey(chat, thread)}`, chat, { pinned: true, messageId: sent.message_id });
|
|
79231
79668
|
} else {
|
|
79232
79669
|
const id = turn.activityMessageId;
|
|
79233
|
-
await robustApiCall(() => bot.api.editMessageText(chat, id, richMessage(html), {}), {
|
|
79670
|
+
const editRes = await robustApiCall(() => bot.api.editMessageText(chat, id, richMessage(html), {}), {
|
|
79671
|
+
chat_id: chat,
|
|
79672
|
+
...thread != null ? { threadId: thread } : {},
|
|
79673
|
+
verb: "activity-summary.edit",
|
|
79674
|
+
priorityClass: "cosmetic",
|
|
79675
|
+
messageId: id,
|
|
79676
|
+
editPayload: html
|
|
79677
|
+
});
|
|
79678
|
+
if (isSendGateShed(editRes))
|
|
79679
|
+
break;
|
|
79234
79680
|
}
|
|
79235
79681
|
turn.activityLastSentRender = target;
|
|
79236
79682
|
} catch (err) {
|
|
@@ -79431,7 +79877,14 @@ function createNarrativeLane(deps) {
|
|
|
79431
79877
|
if (finalHtml == null)
|
|
79432
79878
|
return;
|
|
79433
79879
|
try {
|
|
79434
|
-
await robustApiCall(() => bot.api.editMessageText(chat, id, richMessage(finalHtml), {}), {
|
|
79880
|
+
await robustApiCall(() => bot.api.editMessageText(chat, id, richMessage(finalHtml), {}), {
|
|
79881
|
+
chat_id: chat,
|
|
79882
|
+
...thread != null ? { threadId: thread } : {},
|
|
79883
|
+
verb: "activity-summary.finalize",
|
|
79884
|
+
priorityClass: "useful",
|
|
79885
|
+
messageId: id,
|
|
79886
|
+
editPayload: finalHtml
|
|
79887
|
+
});
|
|
79435
79888
|
} catch (err) {
|
|
79436
79889
|
const msg = err instanceof Error ? err.message : String(err);
|
|
79437
79890
|
const low = msg.toLowerCase();
|
|
@@ -79738,12 +80191,12 @@ class AnswerReadyFlushController {
|
|
|
79738
80191
|
}
|
|
79739
80192
|
|
|
79740
80193
|
// agent-dir.ts
|
|
79741
|
-
import { dirname as
|
|
80194
|
+
import { dirname as dirname15 } from "node:path";
|
|
79742
80195
|
function resolveAgentDirFromEnv() {
|
|
79743
80196
|
const state6 = process.env.TELEGRAM_STATE_DIR;
|
|
79744
80197
|
if (!state6 || state6.trim().length === 0)
|
|
79745
80198
|
return null;
|
|
79746
|
-
return
|
|
80199
|
+
return dirname15(state6);
|
|
79747
80200
|
}
|
|
79748
80201
|
|
|
79749
80202
|
// active-reactions.ts
|
|
@@ -82450,7 +82903,7 @@ async function discoverModels(agentName3, opts = {}) {
|
|
|
82450
82903
|
}
|
|
82451
82904
|
|
|
82452
82905
|
// ../src/agents/scaffold.ts
|
|
82453
|
-
import { dirname as
|
|
82906
|
+
import { dirname as dirname16, isAbsolute, join as join42, relative, resolve as resolve8 } from "node:path";
|
|
82454
82907
|
init_atomic();
|
|
82455
82908
|
|
|
82456
82909
|
// ../src/agents/agent-uid.ts
|
|
@@ -82469,7 +82922,7 @@ init_timezone();
|
|
|
82469
82922
|
|
|
82470
82923
|
// ../src/cli/agent-config.ts
|
|
82471
82924
|
import { join as join40 } from "node:path";
|
|
82472
|
-
import { homedir as
|
|
82925
|
+
import { homedir as homedir11 } from "node:os";
|
|
82473
82926
|
|
|
82474
82927
|
// ../src/cli/helpers.ts
|
|
82475
82928
|
init_loader();
|
|
@@ -82489,7 +82942,7 @@ var WEBKITE_VAULT_KEYS = new Set([
|
|
|
82489
82942
|
init_overlay_loader();
|
|
82490
82943
|
|
|
82491
82944
|
// ../src/cli/agent-config.ts
|
|
82492
|
-
var AUDIT_ROOT = join40(
|
|
82945
|
+
var AUDIT_ROOT = join40(homedir11(), ".switchroom", "audit");
|
|
82493
82946
|
|
|
82494
82947
|
// ../src/agents/profiles.ts
|
|
82495
82948
|
var import_handlebars = __toESM(require_lib(), 1);
|
|
@@ -82527,6 +82980,47 @@ for (const name of SHARED_FRAGMENTS) {
|
|
|
82527
82980
|
}
|
|
82528
82981
|
}
|
|
82529
82982
|
|
|
82983
|
+
// ../src/litellm/timeout-budget.ts
|
|
82984
|
+
var LITELLM_ROUTER_MARGIN_S = 10;
|
|
82985
|
+
var LITELLM_TIMEOUT_TIERS = {
|
|
82986
|
+
interactive: {
|
|
82987
|
+
group: "gpt-oss-20b",
|
|
82988
|
+
localTimeoutS: 90,
|
|
82989
|
+
fallbackGroup: "gpt-oss-20b-openrouter",
|
|
82990
|
+
fallbackTimeoutS: 60
|
|
82991
|
+
},
|
|
82992
|
+
retain: {
|
|
82993
|
+
group: "gpt-oss-20b-retain",
|
|
82994
|
+
localTimeoutS: 200,
|
|
82995
|
+
fallbackGroup: "gpt-oss-20b-retain-openrouter",
|
|
82996
|
+
fallbackTimeoutS: 90
|
|
82997
|
+
},
|
|
82998
|
+
consolidation: {
|
|
82999
|
+
group: "gpt-oss-20b-consolidation",
|
|
83000
|
+
localTimeoutS: 200,
|
|
83001
|
+
fallbackGroup: "gpt-oss-20b-consolidation-openrouter",
|
|
83002
|
+
fallbackTimeoutS: 90
|
|
83003
|
+
}
|
|
83004
|
+
};
|
|
83005
|
+
function litellmChainSeconds(tier) {
|
|
83006
|
+
assertPositive(tier.localTimeoutS, `${tier.group} localTimeoutS`);
|
|
83007
|
+
if (tier.fallbackGroup === null)
|
|
83008
|
+
return tier.localTimeoutS;
|
|
83009
|
+
assertPositive(tier.fallbackTimeoutS, `${tier.fallbackGroup} fallbackTimeoutS`);
|
|
83010
|
+
return tier.localTimeoutS + tier.fallbackTimeoutS;
|
|
83011
|
+
}
|
|
83012
|
+
function minimumClientBudgetSeconds(tier, marginS = LITELLM_ROUTER_MARGIN_S) {
|
|
83013
|
+
return litellmChainSeconds(tier) + marginS;
|
|
83014
|
+
}
|
|
83015
|
+
function clientBudgetSeconds(tier, floorS = 0, marginS = LITELLM_ROUTER_MARGIN_S) {
|
|
83016
|
+
return Math.max(Math.ceil(floorS), minimumClientBudgetSeconds(tier, marginS));
|
|
83017
|
+
}
|
|
83018
|
+
function assertPositive(value, label) {
|
|
83019
|
+
if (!Number.isFinite(value) || value <= 0) {
|
|
83020
|
+
throw new Error(`litellm timeout budget: ${label} must be a positive number, got ${value}`);
|
|
83021
|
+
}
|
|
83022
|
+
}
|
|
83023
|
+
|
|
82530
83024
|
// ../src/setup/hindsight.ts
|
|
82531
83025
|
var HINDSIGHT_DEFAULT_API_PORT = 18888;
|
|
82532
83026
|
var HINDSIGHT_DEFAULT_MCP_URL = `http://127.0.0.1:${HINDSIGHT_DEFAULT_API_PORT}/mcp/`;
|
|
@@ -82536,6 +83030,18 @@ var HINDSIGHT_IMAGE_REPO = "ghcr.io/switchroom/switchroom-hindsight";
|
|
|
82536
83030
|
var HINDSIGHT_IMAGE = `${HINDSIGHT_IMAGE_REPO}:latest`;
|
|
82537
83031
|
var HINDSIGHT_BROKER_SOCK_VOLUME = `auth-broker-${HINDSIGHT_CONSUMER_NAME}-sock`;
|
|
82538
83032
|
var HINDSIGHT_CREDS_MIRROR_VOLUME = `consumer-creds-${HINDSIGHT_CONSUMER_NAME}`;
|
|
83033
|
+
var HINDSIGHT_DEFAULT_RETAIN_MAX_COMPLETION_TOKENS = 16384;
|
|
83034
|
+
var HINDSIGHT_RETAIN_MIN_TOKENS_PER_SECOND = 80.6;
|
|
83035
|
+
function hindsightRetainLlmTimeoutSeconds(maxCompletionTokens = HINDSIGHT_DEFAULT_RETAIN_MAX_COMPLETION_TOKENS, tokensPerSecond = HINDSIGHT_RETAIN_MIN_TOKENS_PER_SECOND) {
|
|
83036
|
+
if (!(maxCompletionTokens > 0) || !(tokensPerSecond > 0)) {
|
|
83037
|
+
throw new Error(`hindsight retain budget: maxCompletionTokens (${maxCompletionTokens}) and ` + `tokensPerSecond (${tokensPerSecond}) must both be positive`);
|
|
83038
|
+
}
|
|
83039
|
+
return Math.ceil(maxCompletionTokens / tokensPerSecond);
|
|
83040
|
+
}
|
|
83041
|
+
function hindsightRetainClientTimeoutSeconds() {
|
|
83042
|
+
return clientBudgetSeconds(LITELLM_TIMEOUT_TIERS.retain, hindsightRetainLlmTimeoutSeconds());
|
|
83043
|
+
}
|
|
83044
|
+
var HINDSIGHT_RETAIN_CLIENT_DEADLINE_S = hindsightRetainClientTimeoutSeconds() + LITELLM_ROUTER_MARGIN_S;
|
|
82539
83045
|
var HINDSIGHT_HEALTHCHECK_PY = 'import urllib.request,sys; sys.exit(0 if urllib.request.urlopen("http://localhost:8888/health",timeout=4).getcode()==200 else 1)';
|
|
82540
83046
|
var HINDSIGHT_HEALTHCHECK_CMD = `python3 -c '${HINDSIGHT_HEALTHCHECK_PY}'`;
|
|
82541
83047
|
var DOCKER_PROBE_TIMEOUT_MS = 60 * 1000;
|
|
@@ -83149,7 +83655,7 @@ init_merge();
|
|
|
83149
83655
|
init_timezone();
|
|
83150
83656
|
var import_yaml4 = __toESM(require_dist(), 1);
|
|
83151
83657
|
import { readFileSync as readFileSync32, existsSync as existsSync35 } from "node:fs";
|
|
83152
|
-
import { homedir as
|
|
83658
|
+
import { homedir as homedir12 } from "node:os";
|
|
83153
83659
|
import { resolve as resolve9 } from "node:path";
|
|
83154
83660
|
|
|
83155
83661
|
class ConfigError2 extends Error {
|
|
@@ -83206,7 +83712,7 @@ function coerceLegacyGoogleWorkspaceKeys2(parsed, filePath) {
|
|
|
83206
83712
|
}
|
|
83207
83713
|
function findConfigFile2(startDir) {
|
|
83208
83714
|
const envPath = process.env.SWITCHROOM_CONFIG;
|
|
83209
|
-
const home2 =
|
|
83715
|
+
const home2 = homedir12();
|
|
83210
83716
|
const userDir = resolve9(home2, ".switchroom");
|
|
83211
83717
|
const searchPaths = [
|
|
83212
83718
|
envPath ? resolve9(envPath) : null,
|
|
@@ -84156,6 +84662,94 @@ function shouldSweepChatAtBoot(chatId) {
|
|
|
84156
84662
|
return n < 0;
|
|
84157
84663
|
}
|
|
84158
84664
|
|
|
84665
|
+
// gateway/boot-sweep-gate.ts
|
|
84666
|
+
function createBootSweepGate(args) {
|
|
84667
|
+
let armed = false;
|
|
84668
|
+
let ready = false;
|
|
84669
|
+
let started = false;
|
|
84670
|
+
const maybeRun = () => {
|
|
84671
|
+
if (started || !armed || !ready)
|
|
84672
|
+
return;
|
|
84673
|
+
started = true;
|
|
84674
|
+
(async () => {
|
|
84675
|
+
try {
|
|
84676
|
+
await args.run();
|
|
84677
|
+
} catch (err) {
|
|
84678
|
+
args.onError?.(err);
|
|
84679
|
+
}
|
|
84680
|
+
})();
|
|
84681
|
+
};
|
|
84682
|
+
return {
|
|
84683
|
+
arm() {
|
|
84684
|
+
armed = true;
|
|
84685
|
+
maybeRun();
|
|
84686
|
+
},
|
|
84687
|
+
botReady() {
|
|
84688
|
+
ready = true;
|
|
84689
|
+
maybeRun();
|
|
84690
|
+
},
|
|
84691
|
+
hasRun() {
|
|
84692
|
+
return started;
|
|
84693
|
+
}
|
|
84694
|
+
};
|
|
84695
|
+
}
|
|
84696
|
+
async function step(name, fn, log) {
|
|
84697
|
+
try {
|
|
84698
|
+
await fn();
|
|
84699
|
+
} catch (err) {
|
|
84700
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
84701
|
+
try {
|
|
84702
|
+
log(`telegram gateway: boot pin sweep step '${name}' failed: ${msg}
|
|
84703
|
+
`);
|
|
84704
|
+
} catch {}
|
|
84705
|
+
}
|
|
84706
|
+
}
|
|
84707
|
+
async function runBootPinSweepSteps(deps) {
|
|
84708
|
+
const log = deps.log ?? ((l) => process.stderr.write(l));
|
|
84709
|
+
let dmChatIds = [];
|
|
84710
|
+
await step("dm-chat-scan", async () => {
|
|
84711
|
+
dmChatIds = deps.scanDmChatIds();
|
|
84712
|
+
}, log);
|
|
84713
|
+
await step("status-pin-cleanup", deps.statusPinCleanup, log);
|
|
84714
|
+
await step("activity-card-reaper", deps.activityCardReaper, log);
|
|
84715
|
+
await step("queued-card-reaper", deps.queuedCardReaper, log);
|
|
84716
|
+
await step("enable-dm-sweep", async () => deps.enableDmSweep(), log);
|
|
84717
|
+
for (const id of dmChatIds)
|
|
84718
|
+
await step(`dm-sweep:${id}`, () => deps.sweepDm(id), log);
|
|
84719
|
+
}
|
|
84720
|
+
|
|
84721
|
+
// gateway/status-pin-api.ts
|
|
84722
|
+
class BotNotReadyError extends Error {
|
|
84723
|
+
constructor(what) {
|
|
84724
|
+
super(`STATUS_PIN_BOT_NOT_READY: ${what} used before initGatewayBot() assigned ` + `lockedBot \u2014 the boot sweep must run behind the boot-sweep gate`);
|
|
84725
|
+
this.name = "BotNotReadyError";
|
|
84726
|
+
}
|
|
84727
|
+
}
|
|
84728
|
+
function assertBotReady(bot, what) {
|
|
84729
|
+
if (bot == null)
|
|
84730
|
+
throw new BotNotReadyError(what);
|
|
84731
|
+
return bot;
|
|
84732
|
+
}
|
|
84733
|
+
|
|
84734
|
+
class SendGateShedError extends Error {
|
|
84735
|
+
constructor(verb) {
|
|
84736
|
+
super(`STATUS_PIN_SEND_SHED: ${verb} was shed by the send gate and never reached Telegram`);
|
|
84737
|
+
this.name = "SendGateShedError";
|
|
84738
|
+
}
|
|
84739
|
+
}
|
|
84740
|
+
function assertLanded(result, verb) {
|
|
84741
|
+
if (result === SEND_GATE_SHED)
|
|
84742
|
+
throw new SendGateShedError(verb);
|
|
84743
|
+
return result;
|
|
84744
|
+
}
|
|
84745
|
+
function createStatusPinApi(getBot, robust) {
|
|
84746
|
+
const call = (verb, fn, chatId) => robust(() => fn(assertBotReady(getBot(), verb)), { chat_id: chatId, verb }).then((r) => assertLanded(r, verb));
|
|
84747
|
+
return {
|
|
84748
|
+
pinChatMessage: (chat_id, message_id, opts) => call("status-pin.pin", (bot) => bot.api.pinChatMessage(chat_id, message_id, opts), String(chat_id)),
|
|
84749
|
+
unpinChatMessage: (chat_id, message_id) => call("status-pin.unpin", (bot) => bot.api.unpinChatMessage(chat_id, message_id), String(chat_id))
|
|
84750
|
+
};
|
|
84751
|
+
}
|
|
84752
|
+
|
|
84159
84753
|
// gateway/dm-pin-sweep.ts
|
|
84160
84754
|
function isDmChatId(chatId) {
|
|
84161
84755
|
const n = Number(chatId);
|
|
@@ -84339,7 +84933,7 @@ function startWebhookIngestServer(opts) {
|
|
|
84339
84933
|
// ../src/web/webhook-gateway-record.ts
|
|
84340
84934
|
import { appendFileSync as appendFileSync8, mkdirSync as mkdirSync35 } from "fs";
|
|
84341
84935
|
import { join as join47 } from "path";
|
|
84342
|
-
import { homedir as
|
|
84936
|
+
import { homedir as homedir14 } from "os";
|
|
84343
84937
|
|
|
84344
84938
|
// ../src/web/webhook-handler.ts
|
|
84345
84939
|
import { appendFileSync as appendFileSync7, existsSync as existsSync40, mkdirSync as mkdirSync33, readFileSync as readFileSync34, writeFileSync as writeFileSync30 } from "fs";
|
|
@@ -84493,21 +85087,21 @@ function sweepStaleParks(lockPath) {
|
|
|
84493
85087
|
} catch {}
|
|
84494
85088
|
}
|
|
84495
85089
|
function fsyncAndMeasure(snapshotPath, logPath, tag) {
|
|
84496
|
-
let
|
|
85090
|
+
let step2 = "open";
|
|
84497
85091
|
try {
|
|
84498
85092
|
const fd = fs2.openSync(snapshotPath, "r");
|
|
84499
85093
|
try {
|
|
84500
|
-
|
|
85094
|
+
step2 = "fsync";
|
|
84501
85095
|
fs2.fsyncSync(fd);
|
|
84502
|
-
|
|
85096
|
+
step2 = "measure";
|
|
84503
85097
|
const size = fs2.fstatSync(fd).size;
|
|
84504
|
-
|
|
85098
|
+
step2 = "close";
|
|
84505
85099
|
return size;
|
|
84506
85100
|
} finally {
|
|
84507
85101
|
fs2.closeSync(fd);
|
|
84508
85102
|
}
|
|
84509
85103
|
} catch (err) {
|
|
84510
|
-
process.stderr.write(`[${tag}] ERROR: could not ${
|
|
85104
|
+
process.stderr.write(`[${tag}] ERROR: could not ${step2} snapshot ${snapshotPath}; leaving active log ${logPath} intact to avoid data loss: ${err.message}
|
|
84511
85105
|
`);
|
|
84512
85106
|
return null;
|
|
84513
85107
|
}
|
|
@@ -84729,7 +85323,7 @@ var throttleIssueWindow = new Map;
|
|
|
84729
85323
|
// ../src/web/webhook-dispatch.ts
|
|
84730
85324
|
import { existsSync as existsSync41, mkdirSync as mkdirSync34, readFileSync as readFileSync35, writeFileSync as writeFileSync31 } from "fs";
|
|
84731
85325
|
import { join as join46 } from "path";
|
|
84732
|
-
import { homedir as
|
|
85326
|
+
import { homedir as homedir13 } from "os";
|
|
84733
85327
|
|
|
84734
85328
|
// ../src/agent-scheduler/ipc-client.ts
|
|
84735
85329
|
import { createConnection as createConnection2 } from "node:net";
|
|
@@ -85106,7 +85700,7 @@ async function defaultInject(socketPath, agentName3, inbound) {
|
|
|
85106
85700
|
}
|
|
85107
85701
|
function injectWebhookInbound(agent, prompt, ctx, deps = {}) {
|
|
85108
85702
|
const log = deps.log ?? ((s) => process.stderr.write(s));
|
|
85109
|
-
const resolveAgentDir = deps.resolveAgentDir ?? ((a) => join46(
|
|
85703
|
+
const resolveAgentDir = deps.resolveAgentDir ?? ((a) => join46(homedir13(), ".switchroom", "agents", a));
|
|
85110
85704
|
const now = (deps.now ?? Date.now)();
|
|
85111
85705
|
const socketPath = join46(resolveAgentDir(agent), "telegram", "gateway.sock");
|
|
85112
85706
|
const inbound = {
|
|
@@ -85180,7 +85774,7 @@ function evaluateDispatch(args, deps = {}) {
|
|
|
85180
85774
|
const log = deps.log ?? ((s) => process.stderr.write(s));
|
|
85181
85775
|
const now = (deps.now ?? Date.now)();
|
|
85182
85776
|
const nowDate = deps.nowDate ?? (() => new Date(now));
|
|
85183
|
-
const resolveAgentDir = deps.resolveAgentDir ?? ((a) => join46(
|
|
85777
|
+
const resolveAgentDir = deps.resolveAgentDir ?? ((a) => join46(homedir13(), ".switchroom", "agents", a));
|
|
85184
85778
|
const cooldownStore = deps.cooldownStore ?? createFileCooldownStore(resolveAgentDir);
|
|
85185
85779
|
if (!DISPATCH_SOURCES.includes(args.source))
|
|
85186
85780
|
return 0;
|
|
@@ -85258,7 +85852,7 @@ var CONSOLIDATION_COMPLETED_EVENT = "consolidation.completed";
|
|
|
85258
85852
|
function recordWebhookEvent(rec, deps = {}) {
|
|
85259
85853
|
const log = deps.log ?? ((s) => process.stderr.write(s));
|
|
85260
85854
|
const now = rec.ts || (deps.now ?? Date.now)();
|
|
85261
|
-
const resolveAgentDir = deps.resolveAgentDir ?? ((a) => join47(
|
|
85855
|
+
const resolveAgentDir = deps.resolveAgentDir ?? ((a) => join47(homedir14(), ".switchroom", "agents", a));
|
|
85262
85856
|
const dedupStore = deps.dedupStore ?? createFileDedupStore(resolveAgentDir);
|
|
85263
85857
|
const agent = rec.agent;
|
|
85264
85858
|
const telegramDir = join47(resolveAgentDir(agent), "telegram");
|
|
@@ -93053,9 +93647,9 @@ import {
|
|
|
93053
93647
|
renameSync as renameSync20
|
|
93054
93648
|
} from "node:fs";
|
|
93055
93649
|
import { join as join50, resolve as resolve10 } from "node:path";
|
|
93056
|
-
import { homedir as
|
|
93650
|
+
import { homedir as homedir15 } from "node:os";
|
|
93057
93651
|
function registryDir() {
|
|
93058
|
-
return resolve10(process.env.SWITCHROOM_WORKTREE_DIR ?? join50(
|
|
93652
|
+
return resolve10(process.env.SWITCHROOM_WORKTREE_DIR ?? join50(homedir15(), ".switchroom", "worktrees"));
|
|
93059
93653
|
}
|
|
93060
93654
|
function recordPath(id) {
|
|
93061
93655
|
return join50(registryDir(), `${id}.json`);
|
|
@@ -93238,17 +93832,17 @@ init_boot_card();
|
|
|
93238
93832
|
// gateway/update-announce.ts
|
|
93239
93833
|
import { existsSync as existsSync49, mkdirSync as mkdirSync41, openSync as openSync10, closeSync as closeSync10, readFileSync as readFileSync49 } from "node:fs";
|
|
93240
93834
|
import { join as join55 } from "node:path";
|
|
93241
|
-
import { homedir as
|
|
93835
|
+
import { homedir as homedir17 } from "node:os";
|
|
93242
93836
|
|
|
93243
93837
|
// ../src/host-control/audit-reader.ts
|
|
93244
|
-
import { homedir as
|
|
93838
|
+
import { homedir as homedir16 } from "node:os";
|
|
93245
93839
|
import { join as join54 } from "node:path";
|
|
93246
93840
|
|
|
93247
93841
|
// ../src/host-control/audit-rotation-config.ts
|
|
93248
93842
|
var DEFAULT_HOSTD_AUDIT_MAX_BYTES = 32 * 1024 * 1024;
|
|
93249
93843
|
|
|
93250
93844
|
// ../src/host-control/audit-reader.ts
|
|
93251
|
-
function defaultAuditLogPath(home2 =
|
|
93845
|
+
function defaultAuditLogPath(home2 = homedir16()) {
|
|
93252
93846
|
return join54(home2, ".switchroom", "host-control-audit.log");
|
|
93253
93847
|
}
|
|
93254
93848
|
var AUDIT_READ_WINDOW_BYTES = 4 * 1024 * 1024;
|
|
@@ -93435,7 +94029,7 @@ function renderUpdateOutcomeLine(entry) {
|
|
|
93435
94029
|
`);
|
|
93436
94030
|
}
|
|
93437
94031
|
function claimUpdateAnnouncement(requestId, opts = {}) {
|
|
93438
|
-
const stateDir = opts.stateDir ?? process.env.TELEGRAM_STATE_DIR ?? join55(
|
|
94032
|
+
const stateDir = opts.stateDir ?? process.env.TELEGRAM_STATE_DIR ?? join55(homedir17(), ".switchroom");
|
|
93439
94033
|
const dir = join55(stateDir, "update-announced");
|
|
93440
94034
|
try {
|
|
93441
94035
|
mkdirSync41(dir, { recursive: true });
|
|
@@ -95737,10 +96331,10 @@ function startOutboxSweep(deps) {
|
|
|
95737
96331
|
}
|
|
95738
96332
|
|
|
95739
96333
|
// ../src/build-info.ts
|
|
95740
|
-
var VERSION2 = "0.19.
|
|
95741
|
-
var COMMIT_SHA = "
|
|
95742
|
-
var COMMIT_DATE = "2026-07-
|
|
95743
|
-
var LATEST_PR =
|
|
96334
|
+
var VERSION2 = "0.19.22";
|
|
96335
|
+
var COMMIT_SHA = "50ef9fb5";
|
|
96336
|
+
var COMMIT_DATE = "2026-07-26T06:22:32Z";
|
|
96337
|
+
var LATEST_PR = 3696;
|
|
95744
96338
|
var COMMITS_AHEAD_OF_TAG = 0;
|
|
95745
96339
|
|
|
95746
96340
|
// gateway/boot-version.ts
|
|
@@ -95817,12 +96411,12 @@ init_protocol();
|
|
|
95817
96411
|
init_peercred();
|
|
95818
96412
|
import * as net5 from "node:net";
|
|
95819
96413
|
import * as fs3 from "node:fs";
|
|
95820
|
-
import { homedir as
|
|
96414
|
+
import { homedir as homedir18 } from "node:os";
|
|
95821
96415
|
import { join as join63 } from "node:path";
|
|
95822
96416
|
var DEFAULT_TIMEOUT_MS4 = 2000;
|
|
95823
96417
|
var UNLOCK_TIMEOUT_MS = 30000;
|
|
95824
|
-
var LEGACY_SOCKET_PATH2 = join63(
|
|
95825
|
-
var OPERATOR_SOCKET_PATH2 = join63(
|
|
96418
|
+
var LEGACY_SOCKET_PATH2 = join63(homedir18(), ".switchroom", "vault-broker.sock");
|
|
96419
|
+
var OPERATOR_SOCKET_PATH2 = join63(homedir18(), ".switchroom", "broker-operator", "sock");
|
|
95826
96420
|
function defaultBrokerSocketPath2() {
|
|
95827
96421
|
if (fs3.existsSync(OPERATOR_SOCKET_PATH2))
|
|
95828
96422
|
return OPERATOR_SOCKET_PATH2;
|
|
@@ -97596,7 +98190,7 @@ if (isGatewayMain) {
|
|
|
97596
98190
|
shutdownAnalytics();
|
|
97597
98191
|
});
|
|
97598
98192
|
}
|
|
97599
|
-
var STATE_DIR = process.env.TELEGRAM_STATE_DIR ?? join65(
|
|
98193
|
+
var STATE_DIR = process.env.TELEGRAM_STATE_DIR ?? join65(homedir19(), ".claude", "channels", "telegram");
|
|
97600
98194
|
var permCardStore = createPermissionCardStore(STATE_DIR);
|
|
97601
98195
|
var BLOCKED_APPROVALS_DIR = process.env.SWITCHROOM_BLOCKED_APPROVALS_DIR ?? "/state/blocked-approvals";
|
|
97602
98196
|
var AGENT_NAME = process.env.SWITCHROOM_AGENT_NAME ?? "agent";
|
|
@@ -98853,9 +99447,9 @@ function maybePostBusyAck(gateDecision, chatId, threadId) {
|
|
|
98853
99447
|
const inFlight = currentTurn;
|
|
98854
99448
|
const inFlightKey = inFlight != null ? statusKey(inFlight.sessionChatId, inFlight.sessionThreadId) : key;
|
|
98855
99449
|
const now = Date.now();
|
|
98856
|
-
const
|
|
99450
|
+
const step2 = longestInFlightTool(inFlightKey, now);
|
|
98857
99451
|
const midToolCall = toolFlightTracker.isMidToolCall();
|
|
98858
|
-
const stepAgeMs =
|
|
99452
|
+
const stepAgeMs = step2?.durationMs ?? null;
|
|
98859
99453
|
const alreadyAcked = queuedStatusMsgIds.has(key) || busyAckPostedKeys.has(key);
|
|
98860
99454
|
const fire = shouldPostBusyAck({ gateDecision, midToolCall, stepAgeMs, alreadyAcked });
|
|
98861
99455
|
if (!fire) {
|
|
@@ -98879,10 +99473,10 @@ function maybePostBusyAck(gateDecision, chatId, threadId) {
|
|
|
98879
99473
|
busyAckPostedKeys.add(key);
|
|
98880
99474
|
const text5 = formatBusyAckText({
|
|
98881
99475
|
gateDecision,
|
|
98882
|
-
toolName:
|
|
98883
|
-
toolLabel:
|
|
99476
|
+
toolName: step2?.name ?? null,
|
|
99477
|
+
toolLabel: step2?.label ?? null
|
|
98884
99478
|
});
|
|
98885
|
-
process.stderr.write(`telegram gateway: mid-flight busy ack chat=${chatId} thread=${threadId ?? "-"} decision=${gateDecision} step=${
|
|
99479
|
+
process.stderr.write(`telegram gateway: mid-flight busy ack chat=${chatId} thread=${threadId ?? "-"} decision=${gateDecision} step=${step2?.name ?? "-"} step_age_ms=${step2?.durationMs ?? "-"}
|
|
98886
99480
|
`);
|
|
98887
99481
|
postBusyAck(chatId, threadId, text5);
|
|
98888
99482
|
}
|
|
@@ -100842,10 +101436,7 @@ var TOOL_PIN_TTL_MS = (() => {
|
|
|
100842
101436
|
return Number.isFinite(v) && v > 0 ? v : 604800000;
|
|
100843
101437
|
})();
|
|
100844
101438
|
function statusPinApi() {
|
|
100845
|
-
return
|
|
100846
|
-
pinChatMessage: (chat_id, message_id, opts) => robustApiCall(() => lockedBot.api.pinChatMessage(chat_id, message_id, opts), { chat_id: String(chat_id), verb: "status-pin.pin" }),
|
|
100847
|
-
unpinChatMessage: (chat_id, message_id) => robustApiCall(() => lockedBot.api.unpinChatMessage(chat_id, message_id), { chat_id: String(chat_id), verb: "status-pin.unpin" })
|
|
100848
|
-
};
|
|
101439
|
+
return createStatusPinApi(() => lockedBot, robustApiCall);
|
|
100849
101440
|
}
|
|
100850
101441
|
async function statusPinBootCleanup() {
|
|
100851
101442
|
if (!statusPinPersistEnabled && !bannerPinPersistEnabled && !toolPinPersistEnabled)
|
|
@@ -101165,25 +101756,25 @@ var dmPinSweeper = createDmPinSweeper({
|
|
|
101165
101756
|
eligible: () => dmPinSweepEligible,
|
|
101166
101757
|
log: (line) => process.stderr.write(line)
|
|
101167
101758
|
});
|
|
101168
|
-
|
|
101169
|
-
|
|
101170
|
-
|
|
101171
|
-
dmChatIds = collectDmChatIdsFromStores({
|
|
101759
|
+
function runBootPinCleanupAndDmSweep() {
|
|
101760
|
+
return runBootPinSweepSteps({
|
|
101761
|
+
scanDmChatIds: () => collectDmChatIdsFromStores({
|
|
101172
101762
|
statusPins: statusPinPersistEnabled || bannerPinPersistEnabled || toolPinPersistEnabled ? loadStatusPins(STATUS_PIN_STORE_PATH, statusPinStoreFs) : [],
|
|
101173
101763
|
activityCards: activityCardPersistEnabled ? loadActivityCards2(ACTIVITY_CARD_STORE_PATH, activityCardStoreFs) : [],
|
|
101174
101764
|
queuedCards: queuedCardPersistEnabled ? loadQueuedCards(QUEUED_CARD_STORE_PATH, queuedCardStoreFs) : []
|
|
101175
|
-
})
|
|
101176
|
-
|
|
101177
|
-
|
|
101178
|
-
|
|
101179
|
-
|
|
101180
|
-
|
|
101181
|
-
|
|
101182
|
-
|
|
101183
|
-
|
|
101184
|
-
|
|
101185
|
-
await dmPinSweeper.sweep(id);
|
|
101765
|
+
}),
|
|
101766
|
+
statusPinCleanup: statusPinBootCleanup,
|
|
101767
|
+
activityCardReaper: activityCardBootReaper,
|
|
101768
|
+
queuedCardReaper: queuedCardBootReaper,
|
|
101769
|
+
enableDmSweep: () => {
|
|
101770
|
+
dmPinSweepEligible = true;
|
|
101771
|
+
},
|
|
101772
|
+
sweepDm: (id) => dmPinSweeper.sweep(id),
|
|
101773
|
+
log: (line) => process.stderr.write(line)
|
|
101774
|
+
});
|
|
101186
101775
|
}
|
|
101776
|
+
var bootPinSweepGate = createBootSweepGate({ run: runBootPinCleanupAndDmSweep, onError: (err) => process.stderr.write(`telegram gateway: boot pin cleanup / DM sweep failed: ${err.message}
|
|
101777
|
+
`) });
|
|
101187
101778
|
var progressDriver = null;
|
|
101188
101779
|
var unpinProgressCardForChat = null;
|
|
101189
101780
|
var getPinnedProgressCardMessageId = null;
|
|
@@ -101255,7 +101846,7 @@ if (isGatewayMain) {
|
|
|
101255
101846
|
if (carrierAgentDir != null)
|
|
101256
101847
|
consumeSessionModelCarrierOnHealthyBoot(carrierAgentDir);
|
|
101257
101848
|
}
|
|
101258
|
-
|
|
101849
|
+
bootPinSweepGate.arm();
|
|
101259
101850
|
} catch (err) {
|
|
101260
101851
|
process.stderr.write(`telegram gateway: boot.lock_acquire_failed err=${err.message} agent=${SWITCHROOM_AGENT_NAME}
|
|
101261
101852
|
`);
|
|
@@ -101268,7 +101859,7 @@ if (isGatewayMain) {
|
|
|
101268
101859
|
if (carrierAgentDir != null)
|
|
101269
101860
|
consumeSessionModelCarrierOnHealthyBoot(carrierAgentDir);
|
|
101270
101861
|
}
|
|
101271
|
-
|
|
101862
|
+
bootPinSweepGate.arm();
|
|
101272
101863
|
} catch (writeErr) {
|
|
101273
101864
|
process.stderr.write(`telegram gateway: writePidFile failed: ${writeErr}
|
|
101274
101865
|
`);
|
|
@@ -105097,7 +105688,7 @@ ${preBlock(formatSwitchroomOutput(detail))}`, { html: true });
|
|
|
105097
105688
|
}
|
|
105098
105689
|
function readRecentDenialsForAgent(agentName3, windowMs, limit) {
|
|
105099
105690
|
try {
|
|
105100
|
-
const auditPath = join65(
|
|
105691
|
+
const auditPath = join65(homedir19(), ".switchroom", "vault-audit.log");
|
|
105101
105692
|
if (!existsSync58(auditPath))
|
|
105102
105693
|
return [];
|
|
105103
105694
|
const raw = readFileSync58(auditPath, "utf8");
|
|
@@ -108608,6 +109199,11 @@ async function initGatewayBot() {
|
|
|
108608
109199
|
bot = new import_grammy16.Bot(TOKEN);
|
|
108609
109200
|
installTgPostLogger(bot);
|
|
108610
109201
|
installRichMarkdownGuard(bot);
|
|
109202
|
+
installEditFloodFuse(bot, {
|
|
109203
|
+
enabled: process.env.SWITCHROOM_EDIT_FUSE !== "0",
|
|
109204
|
+
onTrip: (i) => process.stderr.write(`edit-flood-fuse ${i.action} method=${i.method} key=${i.key}
|
|
109205
|
+
`)
|
|
109206
|
+
});
|
|
108611
109207
|
installUpdateTap(bot, (line) => process.stderr.write(line));
|
|
108612
109208
|
bot.api.config.use(async (prev, method, payload, signal) => {
|
|
108613
109209
|
try {
|
|
@@ -108698,6 +109294,7 @@ async function initGatewayBot() {
|
|
|
108698
109294
|
MENTAL_MODEL_PROPOSE_MAX_PER_WINDOW
|
|
108699
109295
|
});
|
|
108700
109296
|
registerGatewayHandlers(bot, {});
|
|
109297
|
+
bootPinSweepGate.botReady();
|
|
108701
109298
|
}
|
|
108702
109299
|
var didOneTimeSetup = false;
|
|
108703
109300
|
async function startGateway() {
|
|
@@ -108960,7 +109557,7 @@ async function startGateway() {
|
|
|
108960
109557
|
return;
|
|
108961
109558
|
}
|
|
108962
109559
|
})();
|
|
108963
|
-
const resolvedAgentDirForBootCard = agentDir ?? join65(
|
|
109560
|
+
const resolvedAgentDirForBootCard = agentDir ?? join65(homedir19(), ".switchroom", "agents", agentSlug);
|
|
108964
109561
|
const handle = await startBootCard(chatId, threadId, botApiForCard, {
|
|
108965
109562
|
agentName: agentDisplayName,
|
|
108966
109563
|
agentSlug,
|