switchroom 0.19.4 → 0.19.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/auth-broker/index.js +7 -3
- package/dist/cli/autoaccept-poll.js +8 -2
- package/dist/cli/switchroom.js +20 -5
- package/dist/host-control/main.js +1 -1
- package/package.json +1 -1
- package/profiles/_base/start.sh.hbs +67 -4
- package/telegram-plugin/dist/gateway/gateway.js +524 -293
- package/telegram-plugin/gateway/command-format.ts +253 -0
- package/telegram-plugin/gateway/gateway-heartbeat.ts +72 -0
- package/telegram-plugin/gateway/gateway.ts +97 -255
- package/telegram-plugin/gateway/hang-restart-decision.ts +189 -0
- package/telegram-plugin/gateway/liveness-wiring.ts +35 -1
- package/telegram-plugin/gateway/session-model-file.ts +13 -0
- package/telegram-plugin/gateway/stream-render.ts +18 -1
- package/telegram-plugin/gateway/turn-active-marker.ts +29 -17
- package/telegram-plugin/gateway/worker-feed-dispatch.ts +139 -0
- package/telegram-plugin/hooks/silent-end-interrupt-stop.mjs +87 -36
- package/telegram-plugin/hooks/silent-end-scan.mjs +263 -3
- package/telegram-plugin/render/line-start-guard.ts +76 -4
- package/telegram-plugin/rich-send.ts +8 -1
- package/telegram-plugin/tests/command-format.test.ts +212 -0
- package/telegram-plugin/tests/gateway-heartbeat.test.ts +70 -0
- package/telegram-plugin/tests/hang-restart-decision.test.ts +146 -0
- package/telegram-plugin/tests/hang-restart-marker-integration.test.ts +98 -0
- package/telegram-plugin/tests/render/heading-guard-blockquote-glued-hash.test.ts +86 -0
- package/telegram-plugin/tests/render/heading-guard.test.ts +114 -0
- package/telegram-plugin/tests/render/rich-corpus-seam-regression.test.ts +76 -0
- package/telegram-plugin/tests/silent-end-interrupt-stop-integration.test.ts +63 -0
- package/telegram-plugin/tests/silent-end-interrupt-stop-scan.test.ts +60 -16
- package/telegram-plugin/tests/silent-end-single-writer-election.test.ts +193 -0
- package/telegram-plugin/tests/silent-end.test.ts +60 -5
- package/telegram-plugin/tests/worker-feed-origin-race-defer.test.ts +321 -0
|
@@ -8674,10 +8674,42 @@ function guardAccidentalBlockConstructs(text) {
|
|
|
8674
8674
|
}
|
|
8675
8675
|
return out;
|
|
8676
8676
|
}
|
|
8677
|
-
|
|
8677
|
+
function escapeAccidentalHeadingLine(line) {
|
|
8678
|
+
return line.replace(ACCIDENTAL_HEADING, "$1\\$2");
|
|
8679
|
+
}
|
|
8680
|
+
function guardAccidentalHeading(text) {
|
|
8681
|
+
if (!text.includes("#"))
|
|
8682
|
+
return text;
|
|
8683
|
+
const segments = splitProtectedSegments(text);
|
|
8684
|
+
let out = "";
|
|
8685
|
+
let atLineStart = true;
|
|
8686
|
+
for (const seg of segments) {
|
|
8687
|
+
if (seg.code) {
|
|
8688
|
+
out += seg.text;
|
|
8689
|
+
atLineStart = seg.text.endsWith(`
|
|
8690
|
+
`);
|
|
8691
|
+
continue;
|
|
8692
|
+
}
|
|
8693
|
+
const lines = seg.text.split(`
|
|
8694
|
+
`);
|
|
8695
|
+
for (let k = 0;k < lines.length; k++) {
|
|
8696
|
+
const lineIsAtStart = k === 0 ? atLineStart : true;
|
|
8697
|
+
const processed = lineIsAtStart ? escapeAccidentalHeadingLine(lines[k]) : lines[k];
|
|
8698
|
+
out += processed;
|
|
8699
|
+
if (k < lines.length - 1)
|
|
8700
|
+
out += `
|
|
8701
|
+
`;
|
|
8702
|
+
}
|
|
8703
|
+
atLineStart = seg.text.endsWith(`
|
|
8704
|
+
`);
|
|
8705
|
+
}
|
|
8706
|
+
return out;
|
|
8707
|
+
}
|
|
8708
|
+
var ACCIDENTAL_BLOCKQUOTE, ACCIDENTAL_ORDERED_LIST, ACCIDENTAL_HEADING;
|
|
8678
8709
|
var init_line_start_guard = __esm(() => {
|
|
8679
8710
|
ACCIDENTAL_BLOCKQUOTE = /^>[0-9=]/;
|
|
8680
8711
|
ACCIDENTAL_ORDERED_LIST = /^(\d{4,})([.)])(\s|$)/;
|
|
8712
|
+
ACCIDENTAL_HEADING = /^([ \t]{0,3})(#{1,6})(?=[^\s#])/;
|
|
8681
8713
|
});
|
|
8682
8714
|
|
|
8683
8715
|
// render/inline-pairs-guard.ts
|
|
@@ -8727,6 +8759,7 @@ var init_inline_pairs_guard = __esm(() => {
|
|
|
8727
8759
|
function guardAccidentalFormatting(markdown) {
|
|
8728
8760
|
let out = markdown;
|
|
8729
8761
|
out = guardAccidentalEmphasis(out);
|
|
8762
|
+
out = guardAccidentalHeading(out);
|
|
8730
8763
|
out = guardAccidentalBlockConstructs(out);
|
|
8731
8764
|
out = guardAccidentalInlinePairs(out);
|
|
8732
8765
|
out = guardDollarMath(out);
|
|
@@ -37338,7 +37371,7 @@ __export(exports_tmux2, {
|
|
|
37338
37371
|
captureAgentPane: () => captureAgentPane2
|
|
37339
37372
|
});
|
|
37340
37373
|
import { execFileSync as execFileSync8 } from "node:child_process";
|
|
37341
|
-
import { chmodSync as chmodSync13, mkdirSync as
|
|
37374
|
+
import { chmodSync as chmodSync13, mkdirSync as mkdirSync46, readdirSync as readdirSync12, statSync as statSync18, unlinkSync as unlinkSync25, writeFileSync as writeFileSync47 } from "node:fs";
|
|
37342
37375
|
import { resolve as resolve12 } from "node:path";
|
|
37343
37376
|
function captureAgentPane2(opts) {
|
|
37344
37377
|
const { agentName: agentName3, agentDir, reason } = opts;
|
|
@@ -37350,7 +37383,7 @@ function captureAgentPane2(opts) {
|
|
|
37350
37383
|
const reasonSlug = sanitizeReason2(reason);
|
|
37351
37384
|
const outPath = resolve12(outDir, `${ts}-${reasonSlug}.txt`);
|
|
37352
37385
|
try {
|
|
37353
|
-
|
|
37386
|
+
mkdirSync46(outDir, { recursive: true, mode: 448 });
|
|
37354
37387
|
} catch (err) {
|
|
37355
37388
|
const msg = `mkdir crash-reports failed: ${err.message}`;
|
|
37356
37389
|
console.error(`[tmux-capture] ${agentName3}: ${msg}`);
|
|
@@ -37389,7 +37422,7 @@ function captureAgentPane2(opts) {
|
|
|
37389
37422
|
` + `
|
|
37390
37423
|
`;
|
|
37391
37424
|
try {
|
|
37392
|
-
|
|
37425
|
+
writeFileSync47(outPath, Buffer.concat([Buffer.from(header, "utf8"), body]), {
|
|
37393
37426
|
mode: 384
|
|
37394
37427
|
});
|
|
37395
37428
|
} catch (err) {
|
|
@@ -38210,7 +38243,7 @@ function buildGrantedKeyboard(scope) {
|
|
|
38210
38243
|
const btn = scopeToOpenInDriveButton(scope);
|
|
38211
38244
|
if (btn === null)
|
|
38212
38245
|
return;
|
|
38213
|
-
return new
|
|
38246
|
+
return new import_grammy14.InlineKeyboard().url(btn.text, btn.url);
|
|
38214
38247
|
}
|
|
38215
38248
|
async function handleApprovalCallback(ctx, data) {
|
|
38216
38249
|
const parsed = parseApprovalCallback(data);
|
|
@@ -38256,11 +38289,11 @@ async function handleApprovalCallback(ctx, data) {
|
|
|
38256
38289
|
} catch {}
|
|
38257
38290
|
await ctx.answerCallbackQuery({ text: granted ? "Approved" : "Denied" });
|
|
38258
38291
|
}
|
|
38259
|
-
var
|
|
38292
|
+
var import_grammy14;
|
|
38260
38293
|
var init_approval_callback = __esm(() => {
|
|
38261
38294
|
init_approval_card();
|
|
38262
38295
|
init_client3();
|
|
38263
|
-
|
|
38296
|
+
import_grammy14 = __toESM(require_mod2(), 1);
|
|
38264
38297
|
});
|
|
38265
38298
|
|
|
38266
38299
|
// ../src/telegram/materialize-bot-token.ts
|
|
@@ -38433,14 +38466,14 @@ var init_approvals_commands = __esm(() => {
|
|
|
38433
38466
|
});
|
|
38434
38467
|
|
|
38435
38468
|
// gateway/gateway.ts
|
|
38436
|
-
var
|
|
38469
|
+
var import_grammy15 = __toESM(require_mod(), 1);
|
|
38437
38470
|
var import_runner3 = __toESM(require_mod3(), 1);
|
|
38438
38471
|
import { randomBytes as randomBytes10, createHash as createHash4 } from "crypto";
|
|
38439
38472
|
import { execFileSync as execFileSync9, execSync as execSync2, spawn as spawn2 } from "child_process";
|
|
38440
38473
|
import {
|
|
38441
38474
|
readFileSync as readFileSync58,
|
|
38442
|
-
writeFileSync as
|
|
38443
|
-
mkdirSync as
|
|
38475
|
+
writeFileSync as writeFileSync48,
|
|
38476
|
+
mkdirSync as mkdirSync47,
|
|
38444
38477
|
readdirSync as readdirSync13,
|
|
38445
38478
|
rmSync as rmSync6,
|
|
38446
38479
|
statSync as statSync19,
|
|
@@ -38454,7 +38487,7 @@ import {
|
|
|
38454
38487
|
appendFileSync as appendFileSync7
|
|
38455
38488
|
} from "fs";
|
|
38456
38489
|
import { homedir as homedir19 } from "os";
|
|
38457
|
-
import { join as
|
|
38490
|
+
import { join as join60, sep as sep4, basename as basename15 } from "path";
|
|
38458
38491
|
|
|
38459
38492
|
// plugin-logger.ts
|
|
38460
38493
|
import { appendFileSync, mkdirSync, renameSync, statSync, existsSync } from "fs";
|
|
@@ -50451,6 +50484,25 @@ function sweepPermissionTtl(deps) {
|
|
|
50451
50484
|
return expired;
|
|
50452
50485
|
}
|
|
50453
50486
|
|
|
50487
|
+
// gateway/hang-restart-decision.ts
|
|
50488
|
+
var HUMAN_WAIT_TOOLS = new Set(["ask_user"]);
|
|
50489
|
+
var BACKGROUND_TOOLS = new Set(["Task", "Agent"]);
|
|
50490
|
+
var HANG_PROTECTED_EXACT = new Set([
|
|
50491
|
+
"Task",
|
|
50492
|
+
"Agent",
|
|
50493
|
+
"Bash",
|
|
50494
|
+
"WebFetch",
|
|
50495
|
+
"WebSearch"
|
|
50496
|
+
]);
|
|
50497
|
+
var DEFAULT_HANG_STALENESS_MS = 300000;
|
|
50498
|
+
function hangStalenessMs(env = process.env) {
|
|
50499
|
+
const raw = env.TURN_HANG_SECS;
|
|
50500
|
+
const n = Number(raw);
|
|
50501
|
+
if (!Number.isFinite(n) || n <= 0)
|
|
50502
|
+
return DEFAULT_HANG_STALENESS_MS;
|
|
50503
|
+
return Math.floor(n * 1000);
|
|
50504
|
+
}
|
|
50505
|
+
|
|
50454
50506
|
// gateway/missed-approvals-store.ts
|
|
50455
50507
|
import { readFileSync as readFileSync13, writeFileSync as writeFileSync11, unlinkSync as unlinkSync7 } from "node:fs";
|
|
50456
50508
|
import { join as join13 } from "node:path";
|
|
@@ -73377,15 +73429,142 @@ function backOffOpenInline2(text4, cut) {
|
|
|
73377
73429
|
return earliest;
|
|
73378
73430
|
}
|
|
73379
73431
|
|
|
73432
|
+
// gateway/command-format.ts
|
|
73433
|
+
init_format();
|
|
73434
|
+
var import_grammy8 = __toESM(require_mod2(), 1);
|
|
73435
|
+
function formatSwitchroomOutput(output, maxLen = RICH_MESSAGE_MAX_CHARS) {
|
|
73436
|
+
const trimmed = output.trim();
|
|
73437
|
+
if (trimmed.length <= maxLen)
|
|
73438
|
+
return trimmed;
|
|
73439
|
+
return trimmed.slice(0, maxLen - 20) + `
|
|
73440
|
+
... (truncated)`;
|
|
73441
|
+
}
|
|
73442
|
+
function stripAnsi4(text4) {
|
|
73443
|
+
return text4.replace(/\x1b\[[0-9;]*[a-zA-Z]/g, "");
|
|
73444
|
+
}
|
|
73445
|
+
function escapeHtmlForTg2(text4) {
|
|
73446
|
+
return text4.replace(/([\\`*_~=\[\]|])/g, "\\$1");
|
|
73447
|
+
}
|
|
73448
|
+
function preBlock(text4) {
|
|
73449
|
+
return "```\n" + text4.replace(/```/g, "`\u200b``") + "\n```";
|
|
73450
|
+
}
|
|
73451
|
+
function getCommandArgs(ctx) {
|
|
73452
|
+
const fromMatch = typeof ctx.match === "string" ? ctx.match.trim() : "";
|
|
73453
|
+
if (fromMatch)
|
|
73454
|
+
return fromMatch;
|
|
73455
|
+
const text4 = ctx.msg?.text ?? ctx.message?.text ?? "";
|
|
73456
|
+
const m = text4.match(/^\/\S+\s+([\s\S]*)$/);
|
|
73457
|
+
return m ? m[1].trim() : "";
|
|
73458
|
+
}
|
|
73459
|
+
function hasDemoFlag(args) {
|
|
73460
|
+
return /(?:^|\s)demo$/i.test(args.trim());
|
|
73461
|
+
}
|
|
73462
|
+
function assertSafeAgentName(name) {
|
|
73463
|
+
if (!/^[a-zA-Z0-9_-]{1,64}$/.test(name) && name !== "all") {
|
|
73464
|
+
throw new Error(`invalid agent name: ${name}`);
|
|
73465
|
+
}
|
|
73466
|
+
}
|
|
73467
|
+
function formatAuthOutputForTelegram(output) {
|
|
73468
|
+
const trimmed = stripAnsi4(output).trim();
|
|
73469
|
+
const url = trimmed.match(/https:\/\/\S+/)?.[0] ?? null;
|
|
73470
|
+
const lines = trimmed.split(/\n+/).map((l) => l.trim()).filter(Boolean);
|
|
73471
|
+
if (!url)
|
|
73472
|
+
return { text: preBlock(formatSwitchroomOutput(trimmed)), url: null };
|
|
73473
|
+
const body = lines.filter((line) => {
|
|
73474
|
+
if (line === url)
|
|
73475
|
+
return false;
|
|
73476
|
+
if (line.startsWith("switchroom auth code"))
|
|
73477
|
+
return false;
|
|
73478
|
+
if (line.startsWith("switchroom auth cancel"))
|
|
73479
|
+
return false;
|
|
73480
|
+
if (line.startsWith("Use 'tmux attach"))
|
|
73481
|
+
return false;
|
|
73482
|
+
if (line.startsWith("After Claude shows you a browser code"))
|
|
73483
|
+
return false;
|
|
73484
|
+
if (line.startsWith("Then finish with:"))
|
|
73485
|
+
return false;
|
|
73486
|
+
if (line.startsWith("Cancel with:"))
|
|
73487
|
+
return false;
|
|
73488
|
+
return true;
|
|
73489
|
+
});
|
|
73490
|
+
const rendered = body.map((line) => {
|
|
73491
|
+
if (line.startsWith("Started Claude auth") || line.startsWith("Auth session already running"))
|
|
73492
|
+
return `**${escapeHtmlForTg2(line)}**`;
|
|
73493
|
+
if (line.startsWith("Open this URL"))
|
|
73494
|
+
return `_${escapeHtmlForTg2(line)}_`;
|
|
73495
|
+
return escapeHtmlForTg2(line);
|
|
73496
|
+
});
|
|
73497
|
+
rendered.push("", "\uD83D\uDC47 Tap **\uD83D\uDD10 Open Claude auth** below, then **reply with the browser code**.", "", `_Wrong Anthropic account getting authorized? Long-press the URL below and choose "Copy Link" or "Open in Browser" \u2014 lands in your main browser where the right account is signed in, bypassing Telegram's in-app browser cookies._`, "", url);
|
|
73498
|
+
return { text: rendered.join(`
|
|
73499
|
+
`), url };
|
|
73500
|
+
}
|
|
73501
|
+
function buildAuthUrlKeyboard(authorizeUrl) {
|
|
73502
|
+
return new import_grammy8.InlineKeyboard().url("\uD83D\uDD10 Open Claude auth", authorizeUrl);
|
|
73503
|
+
}
|
|
73504
|
+
function buildDeferredSecretKeyboard(deferKey) {
|
|
73505
|
+
const unlockData = `vd:unlock:${deferKey}`;
|
|
73506
|
+
const cancelData = `vd:cancel:${deferKey}`;
|
|
73507
|
+
if (unlockData.length > 64 || cancelData.length > 64) {
|
|
73508
|
+
process.stderr.write(`telegram gateway: callback_data overflow \u2014 deferKey=${deferKey} unlockLen=${unlockData.length} cancelLen=${cancelData.length}
|
|
73509
|
+
`);
|
|
73510
|
+
throw new Error(`callback_data overflow: deferKey too long (${deferKey.length} chars)`);
|
|
73511
|
+
}
|
|
73512
|
+
return new import_grammy8.InlineKeyboard().text("\uD83D\uDD13 Unlock vault & save", unlockData).text("\uD83D\uDDD1 Discard", cancelData);
|
|
73513
|
+
}
|
|
73514
|
+
function renderVaultOpFailure(verbLabel, cliOutput, key) {
|
|
73515
|
+
const parsed = parseVaultCliError(cliOutput);
|
|
73516
|
+
const verb = verbLabel === "delete" ? "remove" : verbLabel;
|
|
73517
|
+
const rendered = renderVaultCliError(parsed, { verb, key });
|
|
73518
|
+
if (rendered.suppressRaw)
|
|
73519
|
+
return rendered.html;
|
|
73520
|
+
return `**vault ${verbLabel} failed:**
|
|
73521
|
+
${preBlock(cliOutput)}`;
|
|
73522
|
+
}
|
|
73523
|
+
function statusIcon(status) {
|
|
73524
|
+
if (status === "active" || status === "running")
|
|
73525
|
+
return "\uD83D\uDFE2";
|
|
73526
|
+
if (status === "inactive" || status === "stopped" || status === "dead")
|
|
73527
|
+
return "\uD83D\uDD34";
|
|
73528
|
+
if (status === "failed")
|
|
73529
|
+
return "\u26a0\ufe0f";
|
|
73530
|
+
return "\u26aa";
|
|
73531
|
+
}
|
|
73532
|
+
function renderAuthCodeOutcome(outcome) {
|
|
73533
|
+
if (!outcome || outcome.kind === "success")
|
|
73534
|
+
return null;
|
|
73535
|
+
const tail = outcome.paneTailText ? `
|
|
73536
|
+
_${escapeHtmlForTg2(outcome.paneTailText)}_` : "";
|
|
73537
|
+
switch (outcome.kind) {
|
|
73538
|
+
case "invalid-code":
|
|
73539
|
+
case "expired-code":
|
|
73540
|
+
return `Code rejected by Claude \u2014 tap **Restart flow** for a fresh URL.${tail}`;
|
|
73541
|
+
case "pane-not-ready":
|
|
73542
|
+
return `Auth pane not ready \u2014 tap **Retry**.`;
|
|
73543
|
+
case "timeout":
|
|
73544
|
+
return `Still waiting after 2 min \u2014 tap **Retry** or check \`switchroom auth list\`.${tail}`;
|
|
73545
|
+
}
|
|
73546
|
+
}
|
|
73547
|
+
function buildDoctorScopeKeyboard() {
|
|
73548
|
+
return new import_grammy8.InlineKeyboard().text("\uD83E\uDE7A Whole fleet", "dr:fleet").text("\uD83E\uDE7A This agent", "dr:self");
|
|
73549
|
+
}
|
|
73550
|
+
function formatDoctorReport(raw) {
|
|
73551
|
+
const trimmed = stripAnsi4(raw).trim();
|
|
73552
|
+
if (!trimmed)
|
|
73553
|
+
return "doctor: no output";
|
|
73554
|
+
const pretty = trimmed.replace(/^( *)\u2713 /gm, "$1\uD83D\uDFE2 ").replace(/^( *)\u2717 /gm, "$1\uD83D\uDD34 ").replace(/^( *)! /gm, "$1\uD83D\uDFE1 ");
|
|
73555
|
+
return preBlock(formatSwitchroomOutput(pretty));
|
|
73556
|
+
}
|
|
73557
|
+
|
|
73380
73558
|
// rich-send.ts
|
|
73381
73559
|
init_dollar_math_guard();
|
|
73382
73560
|
init_emphasis_guard();
|
|
73383
73561
|
init_line_start_guard();
|
|
73384
73562
|
init_inline_pairs_guard();
|
|
73385
|
-
var
|
|
73563
|
+
var import_grammy9 = __toESM(require_mod2(), 1);
|
|
73386
73564
|
function guardAccidentalFormatting2(markdown) {
|
|
73387
73565
|
let out = markdown;
|
|
73388
73566
|
out = guardAccidentalEmphasis(out);
|
|
73567
|
+
out = guardAccidentalHeading(out);
|
|
73389
73568
|
out = guardAccidentalBlockConstructs(out);
|
|
73390
73569
|
out = guardAccidentalInlinePairs(out);
|
|
73391
73570
|
out = guardDollarMath(out);
|
|
@@ -73537,7 +73716,7 @@ function scrubVoice2(text4) {
|
|
|
73537
73716
|
// gateway/outbound-send-path.ts
|
|
73538
73717
|
init_format();
|
|
73539
73718
|
init_text_voice_scrub();
|
|
73540
|
-
var
|
|
73719
|
+
var import_grammy10 = __toESM(require_mod2(), 1);
|
|
73541
73720
|
import { statSync as statSync9 } from "fs";
|
|
73542
73721
|
import { extname } from "path";
|
|
73543
73722
|
|
|
@@ -74326,7 +74505,7 @@ async function sendReplyChunks(deps, state3) {
|
|
|
74326
74505
|
return { threadId, previewMessageId };
|
|
74327
74506
|
}
|
|
74328
74507
|
function classifyReadBackError(err) {
|
|
74329
|
-
if (err instanceof
|
|
74508
|
+
if (err instanceof import_grammy10.GrammyError && err.error_code === 400) {
|
|
74330
74509
|
const d = (err.description || "").toLowerCase();
|
|
74331
74510
|
if (d.includes("message to edit not found"))
|
|
74332
74511
|
return "absent";
|
|
@@ -74901,7 +75080,7 @@ ${url}`;
|
|
|
74901
75080
|
opts.message_thread_id = tid;
|
|
74902
75081
|
else
|
|
74903
75082
|
delete opts.message_thread_id;
|
|
74904
|
-
return lockedBot.api.sendVoice(chat_id, new
|
|
75083
|
+
return lockedBot.api.sendVoice(chat_id, new import_grammy10.InputFile(Buffer.from(oggBytes)), opts);
|
|
74905
75084
|
}, { threadId, chat_id, verb: "sendVoice" });
|
|
74906
75085
|
sentIds.push(sentVoice.message_id);
|
|
74907
75086
|
logOutbound("reply", chat_id, sentVoice.message_id, voiceOutPlan?.ttsChunks[v]?.length ?? 0, `voice-note=${v + 1}/${voiceOggs.length}`);
|
|
@@ -74974,12 +75153,12 @@ ${url}`;
|
|
|
74974
75153
|
...replyParams,
|
|
74975
75154
|
...tid != null ? { message_thread_id: tid } : {}
|
|
74976
75155
|
};
|
|
74977
|
-
return lockedBot.api.sendDocument(chat_id, new
|
|
75156
|
+
return lockedBot.api.sendDocument(chat_id, new import_grammy10.InputFile(f), baseOpts);
|
|
74978
75157
|
}, { threadId, chat_id, verb: "sendDocument" });
|
|
74979
75158
|
if (allPhotos) {
|
|
74980
75159
|
const media = files.map((f) => ({
|
|
74981
75160
|
type: "photo",
|
|
74982
|
-
media: new
|
|
75161
|
+
media: new import_grammy10.InputFile(f)
|
|
74983
75162
|
}));
|
|
74984
75163
|
let sent;
|
|
74985
75164
|
try {
|
|
@@ -75012,7 +75191,7 @@ ${url}`;
|
|
|
75012
75191
|
sentIds.push(m.message_id);
|
|
75013
75192
|
} else {
|
|
75014
75193
|
for (const f of files) {
|
|
75015
|
-
const input = new
|
|
75194
|
+
const input = new import_grammy10.InputFile(f);
|
|
75016
75195
|
const isPhoto = sendableAsPhoto(f);
|
|
75017
75196
|
let sent;
|
|
75018
75197
|
try {
|
|
@@ -76202,6 +76381,15 @@ function removeTurnActiveMarker(stateDir) {
|
|
|
76202
76381
|
unlinkSync13(join31(stateDir, TURN_ACTIVE_MARKER_FILE));
|
|
76203
76382
|
} catch {}
|
|
76204
76383
|
}
|
|
76384
|
+
function readTurnActiveMarkerAgeMs(stateDir, now) {
|
|
76385
|
+
const path2 = join31(stateDir, TURN_ACTIVE_MARKER_FILE);
|
|
76386
|
+
try {
|
|
76387
|
+
const st = statSync10(path2);
|
|
76388
|
+
return (now ?? Date.now()) - st.mtimeMs;
|
|
76389
|
+
} catch {
|
|
76390
|
+
return null;
|
|
76391
|
+
}
|
|
76392
|
+
}
|
|
76205
76393
|
|
|
76206
76394
|
// gateway/turn-end-gate-backstop.ts
|
|
76207
76395
|
function withTurnEndGateBackstop(key, endingTurn, body, deps) {
|
|
@@ -77149,10 +77337,14 @@ function handleSessionEvent(deps, ev) {
|
|
|
77149
77337
|
ended_via: outboundMetrics.outboundCount > 0 ? "reply" : "silent"
|
|
77150
77338
|
});
|
|
77151
77339
|
if (turnEndDecision === "reprompt") {
|
|
77340
|
+
const gatewayCapturedEmpty = turn.capturedText.join(`
|
|
77341
|
+
|
|
77342
|
+
`).trim().length === 0;
|
|
77343
|
+
const proseMinChars = !turn.replyCalled && gatewayCapturedEmpty ? 1 : CAPTURED_PROSE_MIN_CHARS;
|
|
77152
77344
|
const proseDecision = CAPTURED_PROSE_DELIVERY_ENABLED ? decideCapturedProseDelivery({
|
|
77153
77345
|
turnKey: tKey,
|
|
77154
77346
|
turnId: turn.turnId,
|
|
77155
|
-
minChars:
|
|
77347
|
+
minChars: proseMinChars
|
|
77156
77348
|
}) : { deliver: false, reason: "no-state" };
|
|
77157
77349
|
if (proseDecision.deliver && proseDecision.text != null) {
|
|
77158
77350
|
process.stderr.write(`telegram gateway: captured-prose delivery engaged on first silent-end chat=${chatId} turnKey=${tKey} (#3227)
|
|
@@ -78555,7 +78747,7 @@ async function loadFromAuthBroker(options = {}) {
|
|
|
78555
78747
|
}
|
|
78556
78748
|
|
|
78557
78749
|
// gateway/folder-picker-handler.ts
|
|
78558
|
-
var
|
|
78750
|
+
var import_grammy11 = __toESM(require_mod2(), 1);
|
|
78559
78751
|
|
|
78560
78752
|
// ../src/drive/folder-picker.ts
|
|
78561
78753
|
var DRIVE_ID_RE2 = /^[A-Za-z0-9_-]+$/;
|
|
@@ -78874,7 +79066,7 @@ async function recordGrantAndConfirm(ctx, deps, parsed) {
|
|
|
78874
79066
|
const confirmText = `\u2705 Granted ${deps.agentName} access to folder ${parsed.folder_id}
|
|
78875
79067
|
` + `Scope: \`${scope}\`
|
|
78876
79068
|
` + `Revoke with: \`/approvals revoke ${decisionId}\``;
|
|
78877
|
-
const kb = new
|
|
79069
|
+
const kb = new import_grammy11.InlineKeyboard().url("\uD83D\uDCD6 Open in Drive", `https://drive.google.com/drive/folders/${parsed.folder_id}`);
|
|
78878
79070
|
try {
|
|
78879
79071
|
await ctx.editMessageText({ markdown: confirmText }, {
|
|
78880
79072
|
reply_markup: kb
|
|
@@ -78883,7 +79075,7 @@ async function recordGrantAndConfirm(ctx, deps, parsed) {
|
|
|
78883
79075
|
await ctx.answerCallbackQuery({ text: "Allowed" });
|
|
78884
79076
|
}
|
|
78885
79077
|
function toKeyboard(spec) {
|
|
78886
|
-
const kb = new
|
|
79078
|
+
const kb = new import_grammy11.InlineKeyboard;
|
|
78887
79079
|
for (let r = 0;r < spec.rows.length; r++) {
|
|
78888
79080
|
const row = spec.rows[r];
|
|
78889
79081
|
for (const btn of row) {
|
|
@@ -80346,6 +80538,9 @@ function writeSessionModelFile(agentDir, model, configuredDefaultAtWrite) {
|
|
|
80346
80538
|
if (!isValidModelArg(model)) {
|
|
80347
80539
|
throw new Error(`refusing to persist non-canonical session model token: ${JSON.stringify(model)}`);
|
|
80348
80540
|
}
|
|
80541
|
+
try {
|
|
80542
|
+
rmSync4(join35(agentDir, SESSION_MODEL_BOOT_ATTEMPTS_FILE), { force: true });
|
|
80543
|
+
} catch {}
|
|
80349
80544
|
atomicWrite(join35(agentDir, SESSION_MODEL_FILE), serializeSessionModel({ model, configuredDefaultAtWrite, ts: Date.now() }));
|
|
80350
80545
|
}
|
|
80351
80546
|
function readSessionModelFileRaw(agentDir) {
|
|
@@ -81151,20 +81346,20 @@ function registerOpsInfoCommands(bot, deps) {
|
|
|
81151
81346
|
isAuthorizedSender,
|
|
81152
81347
|
getMyAgentName,
|
|
81153
81348
|
switchroomReply,
|
|
81154
|
-
buildDoctorScopeKeyboard,
|
|
81349
|
+
buildDoctorScopeKeyboard: buildDoctorScopeKeyboard2,
|
|
81155
81350
|
renderSelfDoctor,
|
|
81156
|
-
preBlock,
|
|
81157
|
-
formatSwitchroomOutput,
|
|
81158
|
-
getCommandArgs,
|
|
81159
|
-
assertSafeAgentName,
|
|
81351
|
+
preBlock: preBlock2,
|
|
81352
|
+
formatSwitchroomOutput: formatSwitchroomOutput2,
|
|
81353
|
+
getCommandArgs: getCommandArgs2,
|
|
81354
|
+
assertSafeAgentName: assertSafeAgentName2,
|
|
81160
81355
|
runSwitchroomCommand,
|
|
81161
81356
|
switchroomExecCombined,
|
|
81162
|
-
stripAnsi:
|
|
81163
|
-
hasDemoFlag,
|
|
81164
|
-
escapeHtmlForTg:
|
|
81357
|
+
stripAnsi: stripAnsi5,
|
|
81358
|
+
hasDemoFlag: hasDemoFlag2,
|
|
81359
|
+
escapeHtmlForTg: escapeHtmlForTg3
|
|
81165
81360
|
} = deps;
|
|
81166
81361
|
function formatWhoamiCard(v, demo = false) {
|
|
81167
|
-
const esc =
|
|
81362
|
+
const esc = escapeHtmlForTg3;
|
|
81168
81363
|
const yn = (b) => b ? "\u2713" : "\u2717";
|
|
81169
81364
|
const lines = [];
|
|
81170
81365
|
lines.push(`\uD83D\uDC64 **${esc(v.name ?? "?")}** \u00b7 ${esc(v.tier ?? "standard")}`);
|
|
@@ -81196,20 +81391,20 @@ function registerOpsInfoCommands(bot, deps) {
|
|
|
81196
81391
|
if (AGENT_ADMIN && hostdWillBeUsed(getMyAgentName())) {
|
|
81197
81392
|
await switchroomReply(ctx, "\uD83E\uDE7A **Doctor** \u2014 which scope?", {
|
|
81198
81393
|
html: true,
|
|
81199
|
-
reply_markup:
|
|
81394
|
+
reply_markup: buildDoctorScopeKeyboard2()
|
|
81200
81395
|
});
|
|
81201
81396
|
return;
|
|
81202
81397
|
}
|
|
81203
81398
|
await renderSelfDoctor(ctx);
|
|
81204
81399
|
} catch (err) {
|
|
81205
81400
|
await switchroomReply(ctx, `**doctor failed:**
|
|
81206
|
-
${
|
|
81401
|
+
${preBlock2(formatSwitchroomOutput2(err.message ?? "unknown error"))}`, { html: true });
|
|
81207
81402
|
}
|
|
81208
81403
|
});
|
|
81209
81404
|
bot.command("grant", async (ctx) => {
|
|
81210
81405
|
if (!isAuthorizedSender(ctx))
|
|
81211
81406
|
return;
|
|
81212
|
-
const parts =
|
|
81407
|
+
const parts = getCommandArgs2(ctx).split(/\s+/).filter(Boolean);
|
|
81213
81408
|
if (parts.length === 0) {
|
|
81214
81409
|
await switchroomReply(ctx, "Usage: /grant <tool> or /grant <agent> <tool>");
|
|
81215
81410
|
return;
|
|
@@ -81224,7 +81419,7 @@ ${preBlock(formatSwitchroomOutput(err.message ?? "unknown error"))}`, { html: tr
|
|
|
81224
81419
|
tool = parts.slice(1).join(" ");
|
|
81225
81420
|
}
|
|
81226
81421
|
try {
|
|
81227
|
-
|
|
81422
|
+
assertSafeAgentName2(agentName3);
|
|
81228
81423
|
} catch {
|
|
81229
81424
|
await switchroomReply(ctx, "Invalid agent name.");
|
|
81230
81425
|
return;
|
|
@@ -81234,7 +81429,7 @@ ${preBlock(formatSwitchroomOutput(err.message ?? "unknown error"))}`, { html: tr
|
|
|
81234
81429
|
bot.command("dangerous", async (ctx) => {
|
|
81235
81430
|
if (!isAuthorizedSender(ctx))
|
|
81236
81431
|
return;
|
|
81237
|
-
const parts =
|
|
81432
|
+
const parts = getCommandArgs2(ctx).split(/\s+/).filter(Boolean);
|
|
81238
81433
|
let agentName3;
|
|
81239
81434
|
let off = false;
|
|
81240
81435
|
if (parts.length === 0) {
|
|
@@ -81248,7 +81443,7 @@ ${preBlock(formatSwitchroomOutput(err.message ?? "unknown error"))}`, { html: tr
|
|
|
81248
81443
|
off = true;
|
|
81249
81444
|
}
|
|
81250
81445
|
try {
|
|
81251
|
-
|
|
81446
|
+
assertSafeAgentName2(agentName3);
|
|
81252
81447
|
} catch {
|
|
81253
81448
|
await switchroomReply(ctx, "Invalid agent name.");
|
|
81254
81449
|
return;
|
|
@@ -81263,7 +81458,7 @@ ${preBlock(formatSwitchroomOutput(err.message ?? "unknown error"))}`, { html: tr
|
|
|
81263
81458
|
return;
|
|
81264
81459
|
const agentName3 = (typeof ctx.match === "string" ? ctx.match : "").trim() || getMyAgentName();
|
|
81265
81460
|
try {
|
|
81266
|
-
|
|
81461
|
+
assertSafeAgentName2(agentName3);
|
|
81267
81462
|
} catch {
|
|
81268
81463
|
await switchroomReply(ctx, "Invalid agent name.");
|
|
81269
81464
|
return;
|
|
@@ -81280,21 +81475,21 @@ ${preBlock(formatSwitchroomOutput(err.message ?? "unknown error"))}`, { html: tr
|
|
|
81280
81475
|
} catch (err) {
|
|
81281
81476
|
output = err.stdout ?? err.message ?? "version failed";
|
|
81282
81477
|
}
|
|
81283
|
-
const trimmed =
|
|
81478
|
+
const trimmed = stripAnsi5(output).trim();
|
|
81284
81479
|
if (!trimmed) {
|
|
81285
81480
|
await switchroomReply(ctx, "version: no output");
|
|
81286
81481
|
return;
|
|
81287
81482
|
}
|
|
81288
|
-
await switchroomReply(ctx,
|
|
81483
|
+
await switchroomReply(ctx, preBlock2(formatSwitchroomOutput2(trimmed)), { html: true });
|
|
81289
81484
|
} catch (err) {
|
|
81290
81485
|
await switchroomReply(ctx, `**version failed:**
|
|
81291
|
-
${
|
|
81486
|
+
${preBlock2(formatSwitchroomOutput2(err.message ?? "unknown error"))}`, { html: true });
|
|
81292
81487
|
}
|
|
81293
81488
|
});
|
|
81294
81489
|
bot.command("whoami", async (ctx) => {
|
|
81295
81490
|
if (!isAuthorizedSender(ctx))
|
|
81296
81491
|
return;
|
|
81297
|
-
const demo =
|
|
81492
|
+
const demo = hasDemoFlag2(getCommandArgs2(ctx));
|
|
81298
81493
|
try {
|
|
81299
81494
|
let raw;
|
|
81300
81495
|
try {
|
|
@@ -81302,18 +81497,18 @@ ${preBlock(formatSwitchroomOutput(err.message ?? "unknown error"))}`, { html: tr
|
|
|
81302
81497
|
} catch (err) {
|
|
81303
81498
|
raw = err.stdout ?? err.message ?? "whoami failed";
|
|
81304
81499
|
}
|
|
81305
|
-
const trimmed =
|
|
81500
|
+
const trimmed = stripAnsi5(raw).trim();
|
|
81306
81501
|
let card;
|
|
81307
81502
|
try {
|
|
81308
81503
|
card = formatWhoamiCard(JSON.parse(trimmed.split(`
|
|
81309
81504
|
`).pop() ?? trimmed), demo);
|
|
81310
81505
|
} catch {
|
|
81311
|
-
card =
|
|
81506
|
+
card = preBlock2(formatSwitchroomOutput2(trimmed || "whoami: no output"));
|
|
81312
81507
|
}
|
|
81313
81508
|
await switchroomReply(ctx, card, { html: true });
|
|
81314
81509
|
} catch (err) {
|
|
81315
81510
|
await switchroomReply(ctx, `**whoami failed:**
|
|
81316
|
-
${
|
|
81511
|
+
${preBlock2(formatSwitchroomOutput2(err.message ?? "unknown error"))}`, { html: true });
|
|
81317
81512
|
}
|
|
81318
81513
|
});
|
|
81319
81514
|
bot.command("commands", async (ctx) => {
|
|
@@ -84359,7 +84554,7 @@ async function handleRequestMs365Approval(client3, msg, deps) {
|
|
|
84359
84554
|
|
|
84360
84555
|
// gateway/diff-preview-card.ts
|
|
84361
84556
|
init_format();
|
|
84362
|
-
var
|
|
84557
|
+
var import_grammy12 = __toESM(require_mod2(), 1);
|
|
84363
84558
|
var REQUEST_ID_RE = /^[0-9a-f]{32}$/;
|
|
84364
84559
|
var PENDING_FILE_ID_SENTINEL = "pending-create";
|
|
84365
84560
|
function buildDiffPreviewCard(input) {
|
|
@@ -84377,7 +84572,7 @@ function buildDiffPreviewCard(input) {
|
|
|
84377
84572
|
}
|
|
84378
84573
|
const text4 = bodyLines.join(`
|
|
84379
84574
|
`);
|
|
84380
|
-
const kb = new
|
|
84575
|
+
const kb = new import_grammy12.InlineKeyboard;
|
|
84381
84576
|
const ROW_BREAK_AFTER = [
|
|
84382
84577
|
"apply_suggestion"
|
|
84383
84578
|
];
|
|
@@ -87221,6 +87416,49 @@ function purgeStaleTurnsForChat(chatId, keys, purger, isStale = () => true) {
|
|
|
87221
87416
|
return { purged };
|
|
87222
87417
|
}
|
|
87223
87418
|
|
|
87419
|
+
// gateway/hang-restart-decision.ts
|
|
87420
|
+
var HUMAN_WAIT_TOOLS2 = new Set(["ask_user"]);
|
|
87421
|
+
var BACKGROUND_TOOLS2 = new Set(["Task", "Agent"]);
|
|
87422
|
+
function classifyToolClass(toolName, opts = {}) {
|
|
87423
|
+
if (opts.awaitingApproval === true)
|
|
87424
|
+
return "human";
|
|
87425
|
+
if (HUMAN_WAIT_TOOLS2.has(toolName))
|
|
87426
|
+
return "human";
|
|
87427
|
+
if (BACKGROUND_TOOLS2.has(toolName))
|
|
87428
|
+
return "background";
|
|
87429
|
+
if (toolName === "Bash" && opts.backgroundBash === true)
|
|
87430
|
+
return "background";
|
|
87431
|
+
return "standard";
|
|
87432
|
+
}
|
|
87433
|
+
var HANG_PROTECTED_EXACT2 = new Set([
|
|
87434
|
+
"Task",
|
|
87435
|
+
"Agent",
|
|
87436
|
+
"Bash",
|
|
87437
|
+
"WebFetch",
|
|
87438
|
+
"WebSearch"
|
|
87439
|
+
]);
|
|
87440
|
+
var HANG_PROTECTED_SUBSTRINGS = ["research", "perplexity", "crawl", "deep_research", "webkite"];
|
|
87441
|
+
function isHangRestartProtectedTool(toolName) {
|
|
87442
|
+
if (HANG_PROTECTED_EXACT2.has(toolName))
|
|
87443
|
+
return true;
|
|
87444
|
+
if (classifyToolClass(toolName) !== "standard")
|
|
87445
|
+
return true;
|
|
87446
|
+
const lower = toolName.toLowerCase();
|
|
87447
|
+
return HANG_PROTECTED_SUBSTRINGS.some((s) => lower.includes(s));
|
|
87448
|
+
}
|
|
87449
|
+
function decideHangRestart(input) {
|
|
87450
|
+
if (input.inFlightToolNames.length === 0)
|
|
87451
|
+
return { restart: false, reason: "not-mid-tool" };
|
|
87452
|
+
const protectedName = input.inFlightToolNames.find(isHangRestartProtectedTool);
|
|
87453
|
+
if (protectedName != null) {
|
|
87454
|
+
return { restart: false, reason: `protected-long-tool:${protectedName}` };
|
|
87455
|
+
}
|
|
87456
|
+
if (input.markerAgeMs != null && input.markerAgeMs < input.stalenessThresholdMs) {
|
|
87457
|
+
return { restart: false, reason: "marker-advancing" };
|
|
87458
|
+
}
|
|
87459
|
+
return { restart: true, reason: "mid-tool-marker-stale" };
|
|
87460
|
+
}
|
|
87461
|
+
|
|
87224
87462
|
// gateway/update-status-line.ts
|
|
87225
87463
|
function latestHostdPhase(tail) {
|
|
87226
87464
|
if (!tail)
|
|
@@ -87276,7 +87514,8 @@ function buildSilencePokeOptions(deps) {
|
|
|
87276
87514
|
getPendingInboundBuffer,
|
|
87277
87515
|
trackRedeliveredInbound,
|
|
87278
87516
|
closeActivityLane,
|
|
87279
|
-
closeProgressLane
|
|
87517
|
+
closeProgressLane,
|
|
87518
|
+
hangRestart
|
|
87280
87519
|
} = deps;
|
|
87281
87520
|
return {
|
|
87282
87521
|
thresholdsMs: { fallback: SILENCE_FALLBACK_MS, fallbackHardCeiling: SILENCE_FALLBACK_HARD_MS, floor: SILENCE_FLOOR_MS },
|
|
@@ -87324,6 +87563,21 @@ function buildSilencePokeOptions(deps) {
|
|
|
87324
87563
|
endTurn2(ctx.key);
|
|
87325
87564
|
return;
|
|
87326
87565
|
}
|
|
87566
|
+
if (hangRestart != null && ctx.inFlightTools.length > 0) {
|
|
87567
|
+
const markerAgeMs = readTurnActiveMarkerAgeMs(STATE_DIR);
|
|
87568
|
+
const inFlightToolNames = ctx.inFlightTools.map((t) => t.name);
|
|
87569
|
+
const decision = decideHangRestart({
|
|
87570
|
+
inFlightToolNames,
|
|
87571
|
+
markerAgeMs,
|
|
87572
|
+
stalenessThresholdMs: hangRestart.stalenessThresholdMs
|
|
87573
|
+
});
|
|
87574
|
+
process.stderr.write(`telegram gateway: [hang-watchdog] fallback mid-tool chat=${ctx.chatId} ` + `thread=${ctx.threadId ?? "-"} silence_ms=${ctx.silenceMs} ` + `tools=${inFlightToolNames.join(",")} marker_age_ms=${markerAgeMs ?? "null"} ` + `restart=${decision.restart} reason=${decision.reason}
|
|
87575
|
+
`);
|
|
87576
|
+
if (decision.restart) {
|
|
87577
|
+
hangRestart.request(decision.reason, markerAgeMs ?? ctx.silenceMs);
|
|
87578
|
+
return;
|
|
87579
|
+
}
|
|
87580
|
+
}
|
|
87327
87581
|
let text4 = null;
|
|
87328
87582
|
const blockedOnApproval = activeStatusReactions.get(statusKey(ctx.chatId, ctx.threadId))?.isAwaiting() ?? false;
|
|
87329
87583
|
const upd = getInFlightUpdate();
|
|
@@ -93121,7 +93375,7 @@ function sweepStaleTurnActiveMarker(stateDir, opts) {
|
|
|
93121
93375
|
return false;
|
|
93122
93376
|
}
|
|
93123
93377
|
}
|
|
93124
|
-
function
|
|
93378
|
+
function readTurnActiveMarkerAgeMs2(stateDir, now) {
|
|
93125
93379
|
const path2 = join56(stateDir, TURN_ACTIVE_MARKER_FILE2);
|
|
93126
93380
|
try {
|
|
93127
93381
|
const st = statSync16(path2);
|
|
@@ -93134,11 +93388,36 @@ function effectiveTurnAgeMs(markerAgeMs, turnStartedAt, now) {
|
|
|
93134
93388
|
return markerAgeMs ?? now - turnStartedAt;
|
|
93135
93389
|
}
|
|
93136
93390
|
|
|
93391
|
+
// gateway/gateway-heartbeat.ts
|
|
93392
|
+
import { mkdirSync as mkdirSync44, utimesSync as utimesSync3, writeFileSync as writeFileSync45 } from "node:fs";
|
|
93393
|
+
import { join as join57 } from "node:path";
|
|
93394
|
+
var GATEWAY_HEARTBEAT_FILE = "gateway-heartbeat";
|
|
93395
|
+
var GATEWAY_HEARTBEAT_INTERVAL_MS = 15000;
|
|
93396
|
+
function touchGatewayHeartbeat(stateDir) {
|
|
93397
|
+
const path2 = join57(stateDir, GATEWAY_HEARTBEAT_FILE);
|
|
93398
|
+
const now = new Date;
|
|
93399
|
+
try {
|
|
93400
|
+
utimesSync3(path2, now, now);
|
|
93401
|
+
} catch {
|
|
93402
|
+
try {
|
|
93403
|
+
mkdirSync44(stateDir, { recursive: true });
|
|
93404
|
+
writeFileSync45(path2, `${Date.now()}
|
|
93405
|
+
`, { mode: 384 });
|
|
93406
|
+
} catch {}
|
|
93407
|
+
}
|
|
93408
|
+
}
|
|
93409
|
+
function startGatewayHeartbeat(stateDir, intervalMs = GATEWAY_HEARTBEAT_INTERVAL_MS) {
|
|
93410
|
+
touchGatewayHeartbeat(stateDir);
|
|
93411
|
+
const timer3 = setInterval(() => touchGatewayHeartbeat(stateDir), intervalMs);
|
|
93412
|
+
timer3.unref?.();
|
|
93413
|
+
return timer3;
|
|
93414
|
+
}
|
|
93415
|
+
|
|
93137
93416
|
// ../src/build-info.ts
|
|
93138
|
-
var VERSION = "0.19.
|
|
93139
|
-
var COMMIT_SHA = "
|
|
93140
|
-
var COMMIT_DATE = "2026-07-
|
|
93141
|
-
var LATEST_PR =
|
|
93417
|
+
var VERSION = "0.19.5";
|
|
93418
|
+
var COMMIT_SHA = "c5feaef8";
|
|
93419
|
+
var COMMIT_DATE = "2026-07-20T10:20:34Z";
|
|
93420
|
+
var LATEST_PR = 3474;
|
|
93142
93421
|
var COMMITS_AHEAD_OF_TAG = 0;
|
|
93143
93422
|
|
|
93144
93423
|
// gateway/boot-version.ts
|
|
@@ -93183,10 +93462,10 @@ function composeBootVersionString(inputs) {
|
|
|
93183
93462
|
}
|
|
93184
93463
|
|
|
93185
93464
|
// gateway/unhandled-rejection-policy.ts
|
|
93186
|
-
var
|
|
93465
|
+
var import_grammy13 = __toESM(require_mod2(), 1);
|
|
93187
93466
|
function classifyRejection(err, opts = {}) {
|
|
93188
|
-
const isGrammy = opts.isGrammyError != null ? opts.isGrammyError(err) : err instanceof
|
|
93189
|
-
const isHttp = opts.isHttpError != null ? opts.isHttpError(err) : err instanceof
|
|
93467
|
+
const isGrammy = opts.isGrammyError != null ? opts.isGrammyError(err) : err instanceof import_grammy13.GrammyError;
|
|
93468
|
+
const isHttp = opts.isHttpError != null ? opts.isHttpError(err) : err instanceof import_grammy13.HttpError;
|
|
93190
93469
|
if (isHttp)
|
|
93191
93470
|
return "log_only";
|
|
93192
93471
|
if (err instanceof Error && err.message === FLOOD_WAIT_ACTIVE)
|
|
@@ -93216,11 +93495,11 @@ init_peercred();
|
|
|
93216
93495
|
import * as net5 from "node:net";
|
|
93217
93496
|
import * as fs2 from "node:fs";
|
|
93218
93497
|
import { homedir as homedir18 } from "node:os";
|
|
93219
|
-
import { join as
|
|
93498
|
+
import { join as join58 } from "node:path";
|
|
93220
93499
|
var DEFAULT_TIMEOUT_MS4 = 2000;
|
|
93221
93500
|
var UNLOCK_TIMEOUT_MS = 30000;
|
|
93222
|
-
var LEGACY_SOCKET_PATH2 =
|
|
93223
|
-
var OPERATOR_SOCKET_PATH2 =
|
|
93501
|
+
var LEGACY_SOCKET_PATH2 = join58(homedir18(), ".switchroom", "vault-broker.sock");
|
|
93502
|
+
var OPERATOR_SOCKET_PATH2 = join58(homedir18(), ".switchroom", "broker-operator", "sock");
|
|
93224
93503
|
function defaultBrokerSocketPath2() {
|
|
93225
93504
|
if (fs2.existsSync(OPERATOR_SOCKET_PATH2))
|
|
93226
93505
|
return OPERATOR_SOCKET_PATH2;
|
|
@@ -94102,8 +94381,8 @@ function resolveVaultApprovalPosture(broker) {
|
|
|
94102
94381
|
}
|
|
94103
94382
|
|
|
94104
94383
|
// registry/turns-schema.ts
|
|
94105
|
-
import { chmodSync as chmodSync12, mkdirSync as
|
|
94106
|
-
import { join as
|
|
94384
|
+
import { chmodSync as chmodSync12, mkdirSync as mkdirSync45 } from "fs";
|
|
94385
|
+
import { join as join59 } from "path";
|
|
94107
94386
|
var DatabaseClass3 = null;
|
|
94108
94387
|
function loadDatabaseClass3() {
|
|
94109
94388
|
if (DatabaseClass3 != null)
|
|
@@ -94175,9 +94454,9 @@ function applySchema(db3) {
|
|
|
94175
94454
|
}
|
|
94176
94455
|
function openTurnsDb(agentDir) {
|
|
94177
94456
|
const Database = loadDatabaseClass3();
|
|
94178
|
-
const dir =
|
|
94179
|
-
|
|
94180
|
-
const path2 =
|
|
94457
|
+
const dir = join59(agentDir, "telegram");
|
|
94458
|
+
mkdirSync45(dir, { recursive: true, mode: 448 });
|
|
94459
|
+
const path2 = join59(dir, "registry.db");
|
|
94181
94460
|
const db3 = new Database(path2, { create: true });
|
|
94182
94461
|
applySchema(db3);
|
|
94183
94462
|
try {
|
|
@@ -94521,7 +94800,7 @@ function selectResumeBuilder(endedVia, opts) {
|
|
|
94521
94800
|
}
|
|
94522
94801
|
|
|
94523
94802
|
// gateway/bridge-dead-watchdog.ts
|
|
94524
|
-
import { readFileSync as readFileSync56, writeFileSync as
|
|
94803
|
+
import { readFileSync as readFileSync56, writeFileSync as writeFileSync46, renameSync as renameSync20, unlinkSync as unlinkSync24 } from "node:fs";
|
|
94525
94804
|
|
|
94526
94805
|
// gateway/cron-session.ts
|
|
94527
94806
|
var CRON_IDENTITY_SUFFIX2 = "-cron";
|
|
@@ -94575,7 +94854,7 @@ function readFreshCrashLogTail(path2, opts = {}) {
|
|
|
94575
94854
|
}
|
|
94576
94855
|
function writeBridgeDeadEscalationMarker(path2, marker) {
|
|
94577
94856
|
const tmp = `${path2}.tmp-${process.pid}-${Date.now()}`;
|
|
94578
|
-
|
|
94857
|
+
writeFileSync46(tmp, JSON.stringify(marker), "utf8");
|
|
94579
94858
|
renameSync20(tmp, path2);
|
|
94580
94859
|
}
|
|
94581
94860
|
function consumeBridgeDeadEscalationMarker(path2, nowMs3 = Date.now(), maxAgeMs = ESCALATION_MARKER_MAX_AGE_MS) {
|
|
@@ -94900,6 +95179,46 @@ function handleWorkerResume(feed, agentId, log) {
|
|
|
94900
95179
|
}
|
|
94901
95180
|
log(`telegram gateway: worker ${agentId} card RE-SURFACED \u2014 resumed via SendMessage after a genuine terminal (issue #3373)`);
|
|
94902
95181
|
}
|
|
95182
|
+
function decideWorkerFeedOriginDefer(input) {
|
|
95183
|
+
const { originResolved, cardExists, priorDeferrals, maxDeferrals } = input;
|
|
95184
|
+
if (originResolved || cardExists)
|
|
95185
|
+
return { defer: false, deferrals: 0 };
|
|
95186
|
+
const deferrals = priorDeferrals + 1;
|
|
95187
|
+
if (deferrals >= maxDeferrals)
|
|
95188
|
+
return { defer: false, deferrals };
|
|
95189
|
+
return { defer: true, deferrals };
|
|
95190
|
+
}
|
|
95191
|
+
function decideWorkerFeedDestination(input) {
|
|
95192
|
+
const { origin, cardExists, priorDeferrals, maxDeferrals, fleetChatId, stampChatId, stampThreadId, ownerDm } = input;
|
|
95193
|
+
const originResolved = origin != null;
|
|
95194
|
+
const { defer, deferrals } = decideWorkerFeedOriginDefer({
|
|
95195
|
+
originResolved,
|
|
95196
|
+
cardExists,
|
|
95197
|
+
priorDeferrals,
|
|
95198
|
+
maxDeferrals
|
|
95199
|
+
});
|
|
95200
|
+
if (defer)
|
|
95201
|
+
return { action: "defer", deferrals };
|
|
95202
|
+
const exhausted = !originResolved && !cardExists;
|
|
95203
|
+
const usingStampFallback = fleetChatId.length === 0;
|
|
95204
|
+
const workerFleetChatId = usingStampFallback ? stampChatId ?? fleetChatId : fleetChatId;
|
|
95205
|
+
const fallbackThreadId = usingStampFallback ? stampThreadId : undefined;
|
|
95206
|
+
if (origin != null && origin.chatId.length > 0) {
|
|
95207
|
+
return { action: "paint", chatId: origin.chatId, threadId: origin.threadId, deferrals, exhausted, ownerDmFallback: false };
|
|
95208
|
+
}
|
|
95209
|
+
if (workerFleetChatId.length > 0) {
|
|
95210
|
+
return { action: "paint", chatId: workerFleetChatId, threadId: fallbackThreadId, deferrals, exhausted, ownerDmFallback: false };
|
|
95211
|
+
}
|
|
95212
|
+
const ownerDmFallback = origin == null && workerFleetChatId.length === 0 && ownerDm.length > 0;
|
|
95213
|
+
return {
|
|
95214
|
+
action: "paint",
|
|
95215
|
+
chatId: ownerDm,
|
|
95216
|
+
threadId: origin?.threadId ?? fallbackThreadId,
|
|
95217
|
+
deferrals,
|
|
95218
|
+
exhausted,
|
|
95219
|
+
ownerDmFallback
|
|
95220
|
+
};
|
|
95221
|
+
}
|
|
94903
95222
|
function resolveWorkerFeedDispatch(sub, watcherDescription, entryBackground) {
|
|
94904
95223
|
return {
|
|
94905
95224
|
isBackground: sub?.background ?? entryBackground ?? false,
|
|
@@ -94952,7 +95271,7 @@ if (isGatewayMain) {
|
|
|
94952
95271
|
shutdownAnalytics();
|
|
94953
95272
|
});
|
|
94954
95273
|
}
|
|
94955
|
-
var STATE_DIR = process.env.TELEGRAM_STATE_DIR ??
|
|
95274
|
+
var STATE_DIR = process.env.TELEGRAM_STATE_DIR ?? join60(homedir19(), ".claude", "channels", "telegram");
|
|
94956
95275
|
var permCardStore = createPermissionCardStore(STATE_DIR);
|
|
94957
95276
|
var BLOCKED_APPROVALS_DIR = process.env.SWITCHROOM_BLOCKED_APPROVALS_DIR ?? "/state/blocked-approvals";
|
|
94958
95277
|
var AGENT_NAME = process.env.SWITCHROOM_AGENT_NAME ?? "agent";
|
|
@@ -95052,11 +95371,11 @@ function scheduleAlwaysAllowPersistDrain() {
|
|
|
95052
95371
|
}, ALWAYS_ALLOW_DRAIN_INTERVAL_MS);
|
|
95053
95372
|
timer3.unref?.();
|
|
95054
95373
|
}
|
|
95055
|
-
var ACCESS_FILE =
|
|
95056
|
-
var APPROVED_DIR =
|
|
95057
|
-
var ENV_FILE =
|
|
95058
|
-
var INBOX_DIR =
|
|
95059
|
-
var PEOPLE_FILE =
|
|
95374
|
+
var ACCESS_FILE = join60(STATE_DIR, "access.json");
|
|
95375
|
+
var APPROVED_DIR = join60(STATE_DIR, "approved");
|
|
95376
|
+
var ENV_FILE = join60(STATE_DIR, ".env");
|
|
95377
|
+
var INBOX_DIR = join60(STATE_DIR, "inbox");
|
|
95378
|
+
var PEOPLE_FILE = join60(STATE_DIR, "people.json");
|
|
95060
95379
|
function triggerSelfRestart(targetAgent, reason, delayMs = 300) {
|
|
95061
95380
|
const isDocker = process.env.SWITCHROOM_RUNTIME === "docker";
|
|
95062
95381
|
const selfAgent = process.env.SWITCHROOM_AGENT_NAME;
|
|
@@ -95205,7 +95524,7 @@ function assertSendable(f) {
|
|
|
95205
95524
|
} catch {
|
|
95206
95525
|
throw new Error(`refusing to send file \u2014 cannot resolve real path: ${f}`);
|
|
95207
95526
|
}
|
|
95208
|
-
const inbox =
|
|
95527
|
+
const inbox = join60(stateReal, "inbox");
|
|
95209
95528
|
if (real.startsWith(stateReal + sep4) && !real.startsWith(inbox + sep4)) {
|
|
95210
95529
|
throw new Error(`refusing to send channel state: ${f}`);
|
|
95211
95530
|
}
|
|
@@ -95308,9 +95627,9 @@ function assertAllowedChat(chat_id) {
|
|
|
95308
95627
|
function saveAccess(a) {
|
|
95309
95628
|
if (STATIC)
|
|
95310
95629
|
return;
|
|
95311
|
-
|
|
95630
|
+
mkdirSync47(STATE_DIR, { recursive: true, mode: 448 });
|
|
95312
95631
|
const tmp = ACCESS_FILE + ".tmp";
|
|
95313
|
-
|
|
95632
|
+
writeFileSync48(tmp, JSON.stringify(a, null, 2) + `
|
|
95314
95633
|
`, { mode: 384 });
|
|
95315
95634
|
renameSync21(tmp, ACCESS_FILE);
|
|
95316
95635
|
}
|
|
@@ -95330,7 +95649,7 @@ var HISTORY_ENABLED = HISTORY_ACCESS.historyEnabled !== false;
|
|
|
95330
95649
|
if (isGatewayMain && HISTORY_ENABLED) {
|
|
95331
95650
|
try {
|
|
95332
95651
|
initHistory(STATE_DIR, HISTORY_ACCESS.historyRetentionDays ?? 30);
|
|
95333
|
-
process.stderr.write(`telegram gateway: history capture enabled at ${
|
|
95652
|
+
process.stderr.write(`telegram gateway: history capture enabled at ${join60(STATE_DIR, "history.db")}
|
|
95334
95653
|
`);
|
|
95335
95654
|
} catch (err) {
|
|
95336
95655
|
process.stderr.write(`telegram gateway: history init failed (${err.message}) \u2014 capture disabled
|
|
@@ -95349,7 +95668,7 @@ if (isGatewayMain)
|
|
|
95349
95668
|
let markerTurnKey = null;
|
|
95350
95669
|
let markerAgeMs = null;
|
|
95351
95670
|
try {
|
|
95352
|
-
const markerPath =
|
|
95671
|
+
const markerPath = join60(STATE_DIR, TURN_ACTIVE_MARKER_FILE2);
|
|
95353
95672
|
if (existsSync54(markerPath)) {
|
|
95354
95673
|
const st = statSync19(markerPath);
|
|
95355
95674
|
markerAgeMs = Date.now() - st.mtimeMs;
|
|
@@ -95374,10 +95693,10 @@ if (isGatewayMain)
|
|
|
95374
95693
|
process.stderr.write(`telegram gateway: turn-registry boot-reaper stamped ${reaped} orphaned turn(s)` + `${timeoutTurnKey ? ` (turnKey=${timeoutTurnKey} as 'timeout', markerAgeMs=${markerAgeMs})` : " as 'restart'"}
|
|
95375
95694
|
`);
|
|
95376
95695
|
} else {
|
|
95377
|
-
process.stderr.write(`telegram gateway: turn-registry initialized at ${
|
|
95696
|
+
process.stderr.write(`telegram gateway: turn-registry initialized at ${join60(agentDir, "telegram", "registry.db")}
|
|
95378
95697
|
`);
|
|
95379
95698
|
}
|
|
95380
|
-
const bridgeDeadMarker = consumeBridgeDeadEscalationMarker(
|
|
95699
|
+
const bridgeDeadMarker = consumeBridgeDeadEscalationMarker(join60(STATE_DIR, "bridge-dead-escalation.json"));
|
|
95381
95700
|
if (bridgeDeadMarker != null) {
|
|
95382
95701
|
bridgeDeadPriorStreak = bridgeDeadMarker.count ?? 1;
|
|
95383
95702
|
process.stderr.write(`telegram gateway: boot: prior restart was a bridge-dead escalation (reason=${bridgeDeadMarker.reason}` + `, consecutive=${bridgeDeadPriorStreak}` + `${bridgeDeadMarker.crashTail ? `, crashTail=${bridgeDeadMarker.crashTail}` : ""})
|
|
@@ -95390,7 +95709,7 @@ if (isGatewayMain)
|
|
|
95390
95709
|
const pending2 = findLatestTurnIfInterrupted(turnsDb);
|
|
95391
95710
|
const selfAgent = process.env.SWITCHROOM_AGENT_NAME ?? "";
|
|
95392
95711
|
if (pending2 != null && selfAgent) {
|
|
95393
|
-
const bootResumeMarkerPath = process.env.SWITCHROOM_GATEWAY_CLEAN_SHUTDOWN_MARKER ??
|
|
95712
|
+
const bootResumeMarkerPath = process.env.SWITCHROOM_GATEWAY_CLEAN_SHUTDOWN_MARKER ?? join60(STATE_DIR, "clean-shutdown.json");
|
|
95394
95713
|
const bootResumeCleanMarker = readCleanShutdownMarker(bootResumeMarkerPath);
|
|
95395
95714
|
const bootResumeForceAlways = process.env.SWITCHROOM_BOOT_RESUME_ALWAYS === "1";
|
|
95396
95715
|
const bootResumeMode = parseBootResumeMode(process.env.SWITCHROOM_BOOT_RESUME);
|
|
@@ -95503,7 +95822,7 @@ if (isGatewayMain)
|
|
|
95503
95822
|
`);
|
|
95504
95823
|
}
|
|
95505
95824
|
}
|
|
95506
|
-
const pendingEnvPath =
|
|
95825
|
+
const pendingEnvPath = join60(agentDir, ".pending-turn.env");
|
|
95507
95826
|
try {
|
|
95508
95827
|
if (pending2 != null) {
|
|
95509
95828
|
const lines = [
|
|
@@ -95517,7 +95836,7 @@ if (isGatewayMain)
|
|
|
95517
95836
|
pending2.interrupt_reason != null ? `SWITCHROOM_PENDING_INTERRUPT_REASON=${pending2.interrupt_reason}` : `SWITCHROOM_PENDING_INTERRUPT_REASON=`
|
|
95518
95837
|
];
|
|
95519
95838
|
const pendingEnvTmp = `${pendingEnvPath}.tmp-${process.pid}`;
|
|
95520
|
-
|
|
95839
|
+
writeFileSync48(pendingEnvTmp, lines.join(`
|
|
95521
95840
|
`) + `
|
|
95522
95841
|
`, { mode: 384 });
|
|
95523
95842
|
renameSync21(pendingEnvTmp, pendingEnvPath);
|
|
@@ -95564,26 +95883,31 @@ var WORKER_FEED_GROUP_MESSAGE_LIFETIME_CAP_MS = (() => {
|
|
|
95564
95883
|
return Number.isFinite(v) && v > 0 ? v : 60 * 60000;
|
|
95565
95884
|
})();
|
|
95566
95885
|
var workerFeedOwnerDmFallbackLogged = new Set;
|
|
95567
|
-
function
|
|
95886
|
+
function noteWorkerFeedOwnerDmFallback(agentId) {
|
|
95887
|
+
if (workerFeedOwnerDmFallbackLogged.has(agentId))
|
|
95888
|
+
return;
|
|
95889
|
+
workerFeedOwnerDmFallbackLogged.add(agentId);
|
|
95890
|
+
if (workerFeedOwnerDmFallbackLogged.size > WORKER_FEED_FALLBACK_LOG_CAP) {
|
|
95891
|
+
const oldest = workerFeedOwnerDmFallbackLogged.values().next().value;
|
|
95892
|
+
if (oldest != null)
|
|
95893
|
+
workerFeedOwnerDmFallbackLogged.delete(oldest);
|
|
95894
|
+
}
|
|
95895
|
+
process.stderr.write(`telegram gateway: worker-feed origin unresolved agent=${agentId} \u2014 routing card to owner DM
|
|
95896
|
+
`);
|
|
95897
|
+
}
|
|
95898
|
+
var workerFeedOriginDeferrals = new Map;
|
|
95899
|
+
var WORKER_FEED_ORIGIN_DEFER_MAX = 10;
|
|
95900
|
+
function resolveWorkerFeedChat(agentId, fleetChatId, fallbackThreadId) {
|
|
95568
95901
|
const origin = resolveSubagentOriginChat(agentId);
|
|
95569
95902
|
if (origin != null && origin.chatId.length > 0)
|
|
95570
95903
|
return origin;
|
|
95571
95904
|
if (fleetChatId.length > 0)
|
|
95572
|
-
return { chatId: fleetChatId };
|
|
95905
|
+
return { chatId: fleetChatId, threadId: fallbackThreadId };
|
|
95573
95906
|
const ownerDm = loadAccess().allowFrom[0] ?? "";
|
|
95574
95907
|
if (origin == null && fleetChatId.length === 0 && ownerDm.length > 0) {
|
|
95575
|
-
|
|
95576
|
-
workerFeedOwnerDmFallbackLogged.add(agentId);
|
|
95577
|
-
if (workerFeedOwnerDmFallbackLogged.size > WORKER_FEED_FALLBACK_LOG_CAP) {
|
|
95578
|
-
const oldest = workerFeedOwnerDmFallbackLogged.values().next().value;
|
|
95579
|
-
if (oldest != null)
|
|
95580
|
-
workerFeedOwnerDmFallbackLogged.delete(oldest);
|
|
95581
|
-
}
|
|
95582
|
-
process.stderr.write(`telegram gateway: worker-feed origin unresolved agent=${agentId} \u2014 routing card to owner DM
|
|
95583
|
-
`);
|
|
95584
|
-
}
|
|
95908
|
+
noteWorkerFeedOwnerDmFallback(agentId);
|
|
95585
95909
|
}
|
|
95586
|
-
return { chatId: ownerDm, threadId: origin?.threadId };
|
|
95910
|
+
return { chatId: ownerDm, threadId: origin?.threadId ?? fallbackThreadId };
|
|
95587
95911
|
}
|
|
95588
95912
|
var REGISTRY_REAPER_INTERVAL_MS = 6 * 60 * 60 * 1000;
|
|
95589
95913
|
function runHistoryReaperNow(reason) {
|
|
@@ -95626,7 +95950,7 @@ function checkApprovals() {
|
|
|
95626
95950
|
return;
|
|
95627
95951
|
}
|
|
95628
95952
|
for (const senderId of files) {
|
|
95629
|
-
const file =
|
|
95953
|
+
const file = join60(APPROVED_DIR, senderId);
|
|
95630
95954
|
bot.api.sendMessage(senderId, "Paired! Say hi to Claude.").then(() => rmSync6(file, { force: true }), (err) => {
|
|
95631
95955
|
process.stderr.write(`telegram gateway: failed to send approval confirm: ${err}
|
|
95632
95956
|
`);
|
|
@@ -95636,6 +95960,8 @@ function checkApprovals() {
|
|
|
95636
95960
|
}
|
|
95637
95961
|
if (isGatewayMain && !STATIC)
|
|
95638
95962
|
setInterval(checkApprovals, 5000).unref();
|
|
95963
|
+
if (isGatewayMain && !STATIC)
|
|
95964
|
+
startGatewayHeartbeat(STATE_DIR);
|
|
95639
95965
|
var IDLE_CLEAR_CHECK_MS = Number(process.env.SWITCHROOM_IDLE_CLEAR_CHECK_MS ?? 60000);
|
|
95640
95966
|
if (isGatewayMain && !STATIC && IDLE_CLEAR_CHECK_MS > 0)
|
|
95641
95967
|
setInterval(maybeIdleClear, IDLE_CLEAR_CHECK_MS).unref();
|
|
@@ -95797,10 +96123,10 @@ function noteAgentOutputAt(key, ts) {
|
|
|
95797
96123
|
lastAgentOutputAt.delete(oldest);
|
|
95798
96124
|
}
|
|
95799
96125
|
}
|
|
95800
|
-
var OBLIGATION_STORE_PATH =
|
|
96126
|
+
var OBLIGATION_STORE_PATH = join60(STATE_DIR, "obligations.json");
|
|
95801
96127
|
var obligationStoreFs = {
|
|
95802
96128
|
readFileSync: (p) => readFileSync58(p, "utf8"),
|
|
95803
|
-
writeFileSync: (p, d) =>
|
|
96129
|
+
writeFileSync: (p, d) => writeFileSync48(p, d),
|
|
95804
96130
|
renameSync: (a, b) => renameSync21(a, b),
|
|
95805
96131
|
existsSync: (p) => existsSync54(p)
|
|
95806
96132
|
};
|
|
@@ -95854,7 +96180,7 @@ function turnInFlightMachineOnly() {
|
|
|
95854
96180
|
function liveTurnAgeMs(now) {
|
|
95855
96181
|
if (currentTurn === null)
|
|
95856
96182
|
return null;
|
|
95857
|
-
return effectiveTurnAgeMs(
|
|
96183
|
+
return effectiveTurnAgeMs(readTurnActiveMarkerAgeMs2(STATE_DIR, now), currentTurn.startedAt, now);
|
|
95858
96184
|
}
|
|
95859
96185
|
function oldestPendingApprovalAgeMs(now) {
|
|
95860
96186
|
let oldest = null;
|
|
@@ -97061,7 +97387,7 @@ function wrapBootCardApi(threadId) {
|
|
|
97061
97387
|
await lockedBot.api.editMessageText(cid, mid, richMessage2(text5), editOpts);
|
|
97062
97388
|
return "edited";
|
|
97063
97389
|
} catch (err) {
|
|
97064
|
-
const desc = err instanceof
|
|
97390
|
+
const desc = err instanceof import_grammy15.GrammyError ? err.description : err instanceof Error ? err.message : String(err);
|
|
97065
97391
|
if (typeof desc === "string" && desc.toLowerCase().includes("not modified"))
|
|
97066
97392
|
return "edited";
|
|
97067
97393
|
return "gone";
|
|
@@ -98141,26 +98467,26 @@ var statusPinState = new Map;
|
|
|
98141
98467
|
var statusPinChatIds = new Map;
|
|
98142
98468
|
var statusPinPinnedAt = new Map;
|
|
98143
98469
|
var statusPinRightsCache = new PinRightsCache2;
|
|
98144
|
-
var STATUS_PIN_STORE_PATH =
|
|
98470
|
+
var STATUS_PIN_STORE_PATH = join60(STATE_DIR, "status-pins.json");
|
|
98145
98471
|
var statusPinStoreFs = {
|
|
98146
98472
|
readFileSync: (p) => readFileSync58(p, "utf8"),
|
|
98147
|
-
writeFileSync: (p, d) =>
|
|
98473
|
+
writeFileSync: (p, d) => writeFileSync48(p, d),
|
|
98148
98474
|
renameSync: (a, b) => renameSync21(a, b),
|
|
98149
98475
|
existsSync: (p) => existsSync54(p)
|
|
98150
98476
|
};
|
|
98151
98477
|
var statusPinPersistEnabled = !STATIC && PIN_STATUS_WHILE_WORKING;
|
|
98152
|
-
var ACTIVITY_CARD_STORE_PATH =
|
|
98478
|
+
var ACTIVITY_CARD_STORE_PATH = join60(STATE_DIR, "activity-cards-pending.json");
|
|
98153
98479
|
var activityCardStoreFs = {
|
|
98154
98480
|
readFileSync: (p) => readFileSync58(p, "utf8"),
|
|
98155
|
-
writeFileSync: (p, d) =>
|
|
98481
|
+
writeFileSync: (p, d) => writeFileSync48(p, d),
|
|
98156
98482
|
renameSync: (a, b) => renameSync21(a, b),
|
|
98157
98483
|
existsSync: (p) => existsSync54(p)
|
|
98158
98484
|
};
|
|
98159
98485
|
var activityCardPersistEnabled = !STATIC;
|
|
98160
|
-
var QUEUED_CARD_STORE_PATH =
|
|
98486
|
+
var QUEUED_CARD_STORE_PATH = join60(STATE_DIR, "queued-cards-pending.json");
|
|
98161
98487
|
var queuedCardStoreFs = {
|
|
98162
98488
|
readFileSync: (p) => readFileSync58(p, "utf8"),
|
|
98163
|
-
writeFileSync: (p, d) =>
|
|
98489
|
+
writeFileSync: (p, d) => writeFileSync48(p, d),
|
|
98164
98490
|
renameSync: (a, b) => renameSync21(a, b),
|
|
98165
98491
|
existsSync: (p) => existsSync54(p)
|
|
98166
98492
|
};
|
|
@@ -98535,12 +98861,12 @@ var getPinnedProgressCardMessageId = null;
|
|
|
98535
98861
|
var completeProgressCardTurn = null;
|
|
98536
98862
|
var subagentWatcher = null;
|
|
98537
98863
|
var workerActivityFeed = null;
|
|
98538
|
-
var SOCKET_PATH = process.env.SWITCHROOM_GATEWAY_SOCKET ??
|
|
98864
|
+
var SOCKET_PATH = process.env.SWITCHROOM_GATEWAY_SOCKET ?? join60(STATE_DIR, "gateway.sock");
|
|
98539
98865
|
if (isGatewayMain)
|
|
98540
|
-
|
|
98541
|
-
var GATEWAY_PID_PATH = process.env.SWITCHROOM_GATEWAY_PID_FILE ??
|
|
98542
|
-
var GATEWAY_SESSION_MARKER_PATH = process.env.SWITCHROOM_GATEWAY_SESSION_MARKER ??
|
|
98543
|
-
var GATEWAY_CLEAN_SHUTDOWN_MARKER_PATH = process.env.SWITCHROOM_GATEWAY_CLEAN_SHUTDOWN_MARKER ??
|
|
98866
|
+
mkdirSync47(STATE_DIR, { recursive: true, mode: 448 });
|
|
98867
|
+
var GATEWAY_PID_PATH = process.env.SWITCHROOM_GATEWAY_PID_FILE ?? join60(STATE_DIR, "gateway.pid.json");
|
|
98868
|
+
var GATEWAY_SESSION_MARKER_PATH = process.env.SWITCHROOM_GATEWAY_SESSION_MARKER ?? join60(STATE_DIR, "gateway-session.json");
|
|
98869
|
+
var GATEWAY_CLEAN_SHUTDOWN_MARKER_PATH = process.env.SWITCHROOM_GATEWAY_CLEAN_SHUTDOWN_MARKER ?? join60(STATE_DIR, "clean-shutdown.json");
|
|
98544
98870
|
var GATEWAY_STARTED_AT_MS = Date.now();
|
|
98545
98871
|
var BOOT_CARD_ENABLED = process.env.SWITCHROOM_BOOT_CARD !== "false";
|
|
98546
98872
|
var activeBootCard = null;
|
|
@@ -98569,7 +98895,7 @@ function ensureIssuesCard(chatId, threadId) {
|
|
|
98569
98895
|
bot: botApi,
|
|
98570
98896
|
log: (msg) => process.stderr.write(`telegram gateway: ${msg}
|
|
98571
98897
|
`),
|
|
98572
|
-
persistPath:
|
|
98898
|
+
persistPath: join60(stateDir, "issues-card.json")
|
|
98573
98899
|
});
|
|
98574
98900
|
activeIssuesWatcher = startIssuesWatcher({
|
|
98575
98901
|
stateDir,
|
|
@@ -98688,7 +99014,13 @@ function gatewayLivenessWiringDeps() {
|
|
|
98688
99014
|
getPendingInboundBuffer: () => pendingInboundBuffer,
|
|
98689
99015
|
trackRedeliveredInbound,
|
|
98690
99016
|
closeActivityLane,
|
|
98691
|
-
closeProgressLane
|
|
99017
|
+
closeProgressLane,
|
|
99018
|
+
hangRestart: {
|
|
99019
|
+
stalenessThresholdMs: hangStalenessMs(),
|
|
99020
|
+
request: (reason, idleMs) => {
|
|
99021
|
+
triggerSelfRestart(getMyAgentName(), `hang-watchdog:${reason} idle=${Math.round(idleMs)}ms`);
|
|
99022
|
+
}
|
|
99023
|
+
}
|
|
98692
99024
|
};
|
|
98693
99025
|
}
|
|
98694
99026
|
if (isGatewayMain)
|
|
@@ -98760,11 +99092,11 @@ if (isGatewayMain)
|
|
|
98760
99092
|
var inboundSpool;
|
|
98761
99093
|
if (isGatewayMain)
|
|
98762
99094
|
inboundSpool = STATIC ? undefined : createInboundSpool({
|
|
98763
|
-
path:
|
|
99095
|
+
path: join60(STATE_DIR, "inbound-spool.jsonl"),
|
|
98764
99096
|
fs: {
|
|
98765
99097
|
appendFileSync: (p, d) => appendFileSync7(p, d),
|
|
98766
99098
|
readFileSync: (p) => readFileSync58(p, "utf8"),
|
|
98767
|
-
writeFileSync: (p, d) =>
|
|
99099
|
+
writeFileSync: (p, d) => writeFileSync48(p, d),
|
|
98768
99100
|
renameSync: (a, b) => renameSync21(a, b),
|
|
98769
99101
|
existsSync: (p) => existsSync54(p),
|
|
98770
99102
|
statSizeSync: (p) => statSync19(p).size
|
|
@@ -98794,7 +99126,7 @@ var pendingInboundBuffer = createPendingInboundBuffer({
|
|
|
98794
99126
|
function agentHasInFlightBackgroundWork(now) {
|
|
98795
99127
|
if (countRunningWorkers() > 0)
|
|
98796
99128
|
return true;
|
|
98797
|
-
const ageMs =
|
|
99129
|
+
const ageMs = readTurnActiveMarkerAgeMs2(STATE_DIR, now);
|
|
98798
99130
|
return ageMs != null && ageMs < TURN_ACTIVE_MARKER_FRESH_MS;
|
|
98799
99131
|
}
|
|
98800
99132
|
async function deliverCapturedProse2(args) {
|
|
@@ -98827,7 +99159,7 @@ async function maybeRedeliverUndeliveredAnswer() {
|
|
|
98827
99159
|
let transcriptText;
|
|
98828
99160
|
try {
|
|
98829
99161
|
const projectsDir = getProjectsDirForCwd();
|
|
98830
|
-
const path2 =
|
|
99162
|
+
const path2 = join60(projectsDir, `${sessionId}.jsonl`);
|
|
98831
99163
|
if (!existsSync54(path2)) {
|
|
98832
99164
|
process.stderr.write(`telegram gateway: crash-redelivery \u2014 transcript not found for turnKey=${turn.turn_key} session=${sessionId} (${path2}); skipping
|
|
98833
99165
|
`);
|
|
@@ -98930,7 +99262,7 @@ if (isGatewayMain && inboundSpool != null) {
|
|
|
98930
99262
|
}
|
|
98931
99263
|
var pendingPermissionBuffer = createPendingPermissionBuffer();
|
|
98932
99264
|
function buildPermissionActionRow(requestId, showAlways) {
|
|
98933
|
-
const kb = new
|
|
99265
|
+
const kb = new import_grammy15.InlineKeyboard().text("\u274C Deny", `perm:deny:${requestId}`).text("\u2705 Allow", `perm:allow:${requestId}`);
|
|
98934
99266
|
if (showAlways)
|
|
98935
99267
|
kb.text("\uD83D\uDD01 Always\u2026", `perm:always:${requestId}`);
|
|
98936
99268
|
return kb;
|
|
@@ -98992,8 +99324,8 @@ var bridgeDeadWatchdog = createBridgeDeadWatchdog({
|
|
|
98992
99324
|
isSessionAlive: () => findAgentProcessInContainer2()?.comm === "claude",
|
|
98993
99325
|
isShuttingDown: () => shuttingDown,
|
|
98994
99326
|
escalate: (reason) => triggerSelfRestart(process.env.SWITCHROOM_AGENT_NAME ?? "", reason, 1500),
|
|
98995
|
-
crashLogPath:
|
|
98996
|
-
markerPath:
|
|
99327
|
+
crashLogPath: join60(STATE_DIR, "bridge-crash.log"),
|
|
99328
|
+
markerPath: join60(STATE_DIR, "bridge-dead-escalation.json"),
|
|
98997
99329
|
log: (line) => process.stderr.write(`${line}
|
|
98998
99330
|
`),
|
|
98999
99331
|
priorStreak: bridgeDeadPriorStreak,
|
|
@@ -99099,8 +99431,8 @@ if (isGatewayMain)
|
|
|
99099
99431
|
probeQuotaViaBroker: (t) => probeQuotaForBootCard(agentSlug, t),
|
|
99100
99432
|
tmuxSupervisor: process.env.SWITCHROOM_TMUX_SUPERVISOR === "1",
|
|
99101
99433
|
dockerMode: process.env.SWITCHROOM_RUNTIME === "docker",
|
|
99102
|
-
configSnapshotPath:
|
|
99103
|
-
bootCardStatePath:
|
|
99434
|
+
configSnapshotPath: join60(resolvedAgentDirForCard, ".config-snapshot.json"),
|
|
99435
|
+
bootCardStatePath: join60(resolvedAgentDirForCard, ".boot-card-msgid.json"),
|
|
99104
99436
|
floodStatePath: FLOOD_STATE_PATH,
|
|
99105
99437
|
...updateOutcomeLine ? { updateOutcomeLine } : {}
|
|
99106
99438
|
}, ackMsgId).then((handle) => {
|
|
@@ -99453,7 +99785,7 @@ if (isGatewayMain)
|
|
|
99453
99785
|
},
|
|
99454
99786
|
async onRequestConfigApproval(client3, msg) {
|
|
99455
99787
|
const { handleRequestConfigApproval: handleRequestConfigApproval2 } = await Promise.resolve().then(() => (init_config_approval_handler(), exports_config_approval_handler));
|
|
99456
|
-
const { InlineKeyboard:
|
|
99788
|
+
const { InlineKeyboard: InlineKeyboard8 } = await Promise.resolve().then(() => __toESM(require_mod(), 1));
|
|
99457
99789
|
await handleRequestConfigApproval2(client3, msg, {
|
|
99458
99790
|
agentName: getMyAgentName(),
|
|
99459
99791
|
loadTargetChat: () => {
|
|
@@ -99475,7 +99807,7 @@ if (isGatewayMain)
|
|
|
99475
99807
|
...cfgTopic != null ? { threadId: cfgTopic } : {}
|
|
99476
99808
|
};
|
|
99477
99809
|
},
|
|
99478
|
-
buildKeyboard: (requestId, epoch) => new
|
|
99810
|
+
buildKeyboard: (requestId, epoch) => new InlineKeyboard8().text("\u2705 Approve", `cfg:${requestId}:${epoch}:approve`).text("\uD83D\uDEAB Deny", `cfg:${requestId}:${epoch}:deny`),
|
|
99479
99811
|
postCard: async (args) => {
|
|
99480
99812
|
try {
|
|
99481
99813
|
const sent = await robustApiCall(() => bot.api.sendRichMessage(args.chatId, richMessage2(args.text), {
|
|
@@ -99504,7 +99836,7 @@ if (isGatewayMain)
|
|
|
99504
99836
|
}
|
|
99505
99837
|
},
|
|
99506
99838
|
postAttachment: async (args) => {
|
|
99507
|
-
const input = new
|
|
99839
|
+
const input = new import_grammy15.InputFile(Buffer.from(args.content, "utf8"), args.filename);
|
|
99508
99840
|
await robustApiCall(() => bot.api.sendDocument(args.chatId, input, {
|
|
99509
99841
|
...args.threadId !== undefined ? { message_thread_id: args.threadId } : {}
|
|
99510
99842
|
}), {
|
|
@@ -99782,7 +100114,7 @@ if (isGatewayMain)
|
|
|
99782
100114
|
const receiverUid = receiverUidRaw ? Number(receiverUidRaw) : NaN;
|
|
99783
100115
|
if (Number.isInteger(receiverUid))
|
|
99784
100116
|
allowedUids.push(receiverUid);
|
|
99785
|
-
const socketPath =
|
|
100117
|
+
const socketPath = join60(STATE_DIR, "webhook.sock");
|
|
99786
100118
|
const webhookInject = (agentName3, inbound) => {
|
|
99787
100119
|
const msg = inbound;
|
|
99788
100120
|
const delivered = ipcServer.sendToAgent(agentName3, msg);
|
|
@@ -100010,9 +100342,9 @@ function redactOutboundText(text5, site) {
|
|
|
100010
100342
|
var VOICE_OUT_DEFAULT_CHUNK_CHARS = 600;
|
|
100011
100343
|
var VOICE_OUT_HARD_CHUNK_CAP = 4096;
|
|
100012
100344
|
var voiceOnDemandCache = new VoiceOnDemandCache({
|
|
100013
|
-
persistPath:
|
|
100345
|
+
persistPath: join60(STATE_DIR, "voice-ondemand.json")
|
|
100014
100346
|
});
|
|
100015
|
-
var VOICE_CACHE_DIR =
|
|
100347
|
+
var VOICE_CACHE_DIR = join60(STATE_DIR, "voice-cache");
|
|
100016
100348
|
var voicePreSynthQueue = new PreSynthQueue({
|
|
100017
100349
|
runJob: async (job) => {
|
|
100018
100350
|
const sidecarToken = await materializeSidecarToken2();
|
|
@@ -100278,7 +100610,7 @@ async function executeAskUser(rawArgs) {
|
|
|
100278
100610
|
}
|
|
100279
100611
|
}
|
|
100280
100612
|
const askId = generateAskId();
|
|
100281
|
-
const keyboard = new
|
|
100613
|
+
const keyboard = new import_grammy15.InlineKeyboard;
|
|
100282
100614
|
for (let i = 0;i < args.options.length; i++) {
|
|
100283
100615
|
keyboard.text(args.options[i], encodeAskCallback(askId, i));
|
|
100284
100616
|
if (i < args.options.length - 1)
|
|
@@ -100413,7 +100745,7 @@ async function executeSendGif(rawArgs) {
|
|
|
100413
100745
|
};
|
|
100414
100746
|
}
|
|
100415
100747
|
async function publishToTelegraph(text5, shortName, authorName) {
|
|
100416
|
-
const accountPath =
|
|
100748
|
+
const accountPath = join60(STATE_DIR, "telegraph-account.json");
|
|
100417
100749
|
let account = null;
|
|
100418
100750
|
try {
|
|
100419
100751
|
if (existsSync54(accountPath)) {
|
|
@@ -100436,8 +100768,8 @@ async function publishToTelegraph(text5, shortName, authorName) {
|
|
|
100436
100768
|
}
|
|
100437
100769
|
account = created.value;
|
|
100438
100770
|
try {
|
|
100439
|
-
|
|
100440
|
-
|
|
100771
|
+
mkdirSync47(STATE_DIR, { recursive: true, mode: 448 });
|
|
100772
|
+
writeFileSync48(accountPath, JSON.stringify(account, null, 2), { mode: 384 });
|
|
100441
100773
|
} catch (err) {
|
|
100442
100774
|
process.stderr.write(`telegram gateway: telegraph cache write failed: ${err.message}
|
|
100443
100775
|
`);
|
|
@@ -100592,9 +100924,9 @@ async function executeDownloadAttachment(args) {
|
|
|
100592
100924
|
fileUniqueId: file.file_unique_id,
|
|
100593
100925
|
now: Date.now()
|
|
100594
100926
|
});
|
|
100595
|
-
|
|
100927
|
+
mkdirSync47(INBOX_DIR, { recursive: true, mode: 448 });
|
|
100596
100928
|
assertInsideInbox2(INBOX_DIR, dlPath);
|
|
100597
|
-
|
|
100929
|
+
writeFileSync48(dlPath, buf, { mode: 384 });
|
|
100598
100930
|
return { content: [{ type: "text", text: dlPath }] };
|
|
100599
100931
|
}
|
|
100600
100932
|
async function executeEditMessage(args) {
|
|
@@ -101909,24 +102241,8 @@ function switchroomExecCombined(args, timeoutMs = 15000) {
|
|
|
101909
102241
|
shell: "/bin/bash"
|
|
101910
102242
|
});
|
|
101911
102243
|
}
|
|
101912
|
-
function formatSwitchroomOutput(output, maxLen = RICH_MESSAGE_MAX_CHARS2) {
|
|
101913
|
-
const trimmed = output.trim();
|
|
101914
|
-
if (trimmed.length <= maxLen)
|
|
101915
|
-
return trimmed;
|
|
101916
|
-
return trimmed.slice(0, maxLen - 20) + `
|
|
101917
|
-
... (truncated)`;
|
|
101918
|
-
}
|
|
101919
|
-
function stripAnsi4(text5) {
|
|
101920
|
-
return text5.replace(/\x1b\[[0-9;]*[a-zA-Z]/g, "");
|
|
101921
|
-
}
|
|
101922
|
-
function escapeHtmlForTg2(text5) {
|
|
101923
|
-
return text5.replace(/([\\`*_~=\[\]|])/g, "\\$1");
|
|
101924
|
-
}
|
|
101925
102244
|
var buttonConfirmParseModeWarned = new Set;
|
|
101926
102245
|
var buttonConfirmSingleUseWarned = new Set;
|
|
101927
|
-
function preBlock(text5) {
|
|
101928
|
-
return "```\n" + text5.replace(/```/g, "`\u200B``") + "\n```";
|
|
101929
|
-
}
|
|
101930
102246
|
async function switchroomReply(ctx, text5, options = {}) {
|
|
101931
102247
|
const chatId = String(ctx.chat.id);
|
|
101932
102248
|
const baseThreadId = resolveThreadId(chatId, ctx.message?.message_thread_id);
|
|
@@ -101956,22 +102272,6 @@ Please delete message \`${msgId}\` manually so the value is not retained in chat
|
|
|
101956
102272
|
_Reason: ${escapeHtmlForTg2(msg)}_`), {}), { chat_id: chatId, verb: "deleteSensitiveMessage.warning" });
|
|
101957
102273
|
}
|
|
101958
102274
|
}
|
|
101959
|
-
function getCommandArgs(ctx) {
|
|
101960
|
-
const fromMatch = typeof ctx.match === "string" ? ctx.match.trim() : "";
|
|
101961
|
-
if (fromMatch)
|
|
101962
|
-
return fromMatch;
|
|
101963
|
-
const text5 = ctx.msg?.text ?? ctx.message?.text ?? "";
|
|
101964
|
-
const m = text5.match(/^\/\S+\s+([\s\S]*)$/);
|
|
101965
|
-
return m ? m[1].trim() : "";
|
|
101966
|
-
}
|
|
101967
|
-
function hasDemoFlag(args) {
|
|
101968
|
-
return /(?:^|\s)demo$/i.test(args.trim());
|
|
101969
|
-
}
|
|
101970
|
-
function assertSafeAgentName(name) {
|
|
101971
|
-
if (!/^[a-zA-Z0-9_-]{1,64}$/.test(name) && name !== "all") {
|
|
101972
|
-
throw new Error(`invalid agent name: ${name}`);
|
|
101973
|
-
}
|
|
101974
|
-
}
|
|
101975
102275
|
function getMyAgentName() {
|
|
101976
102276
|
const fromEnv = process.env.SWITCHROOM_AGENT_NAME;
|
|
101977
102277
|
if (fromEnv && fromEnv.trim().length > 0)
|
|
@@ -101989,14 +102289,14 @@ function restartMarkerPath() {
|
|
|
101989
102289
|
const agentDir = resolveAgentDirFromEnv();
|
|
101990
102290
|
if (!agentDir)
|
|
101991
102291
|
return null;
|
|
101992
|
-
return
|
|
102292
|
+
return join60(agentDir, "restart-pending.json");
|
|
101993
102293
|
}
|
|
101994
102294
|
function writeRestartMarker(marker) {
|
|
101995
102295
|
const p = restartMarkerPath();
|
|
101996
102296
|
if (!p)
|
|
101997
102297
|
return;
|
|
101998
102298
|
try {
|
|
101999
|
-
|
|
102299
|
+
writeFileSync48(p, JSON.stringify(marker));
|
|
102000
102300
|
lastPlannedRestartAt = Date.now();
|
|
102001
102301
|
process.stderr.write(`telegram gateway: restart-marker: write chat_id=${marker.chat_id} thread_id=${marker.thread_id ?? "-"} ack=${marker.ack_message_id ?? "-"} path=${p}
|
|
102002
102302
|
`);
|
|
@@ -102181,12 +102481,12 @@ function _resetDockerReachableCache() {
|
|
|
102181
102481
|
}
|
|
102182
102482
|
function spawnSwitchroomDetached(args, onFailure) {
|
|
102183
102483
|
const fullArgs = SWITCHROOM_CONFIG ? ["--config", SWITCHROOM_CONFIG, ...args] : args;
|
|
102184
|
-
const logPath =
|
|
102484
|
+
const logPath = join60(STATE_DIR, "detached-spawn.log");
|
|
102185
102485
|
let outFd = null;
|
|
102186
102486
|
try {
|
|
102187
|
-
|
|
102487
|
+
mkdirSync47(STATE_DIR, { recursive: true });
|
|
102188
102488
|
outFd = openSync12(logPath, "a");
|
|
102189
|
-
|
|
102489
|
+
writeFileSync48(logPath, `
|
|
102190
102490
|
[${new Date().toISOString()}] spawn ${SWITCHROOM_CLI} ${fullArgs.join(" ")}
|
|
102191
102491
|
`, { flag: "a" });
|
|
102192
102492
|
} catch {}
|
|
@@ -102286,53 +102586,6 @@ function scheduleGrantRestart(agentName3, chatId, threadId, reason) {
|
|
|
102286
102586
|
}
|
|
102287
102587
|
return decision;
|
|
102288
102588
|
}
|
|
102289
|
-
function formatAuthOutputForTelegram(output) {
|
|
102290
|
-
const trimmed = stripAnsi4(output).trim();
|
|
102291
|
-
const url = trimmed.match(/https:\/\/\S+/)?.[0] ?? null;
|
|
102292
|
-
const lines = trimmed.split(/\n+/).map((l) => l.trim()).filter(Boolean);
|
|
102293
|
-
if (!url)
|
|
102294
|
-
return { text: preBlock(formatSwitchroomOutput(trimmed)), url: null };
|
|
102295
|
-
const body = lines.filter((line) => {
|
|
102296
|
-
if (line === url)
|
|
102297
|
-
return false;
|
|
102298
|
-
if (line.startsWith("switchroom auth code"))
|
|
102299
|
-
return false;
|
|
102300
|
-
if (line.startsWith("switchroom auth cancel"))
|
|
102301
|
-
return false;
|
|
102302
|
-
if (line.startsWith("Use 'tmux attach"))
|
|
102303
|
-
return false;
|
|
102304
|
-
if (line.startsWith("After Claude shows you a browser code"))
|
|
102305
|
-
return false;
|
|
102306
|
-
if (line.startsWith("Then finish with:"))
|
|
102307
|
-
return false;
|
|
102308
|
-
if (line.startsWith("Cancel with:"))
|
|
102309
|
-
return false;
|
|
102310
|
-
return true;
|
|
102311
|
-
});
|
|
102312
|
-
const rendered = body.map((line) => {
|
|
102313
|
-
if (line.startsWith("Started Claude auth") || line.startsWith("Auth session already running"))
|
|
102314
|
-
return `**${escapeHtmlForTg2(line)}**`;
|
|
102315
|
-
if (line.startsWith("Open this URL"))
|
|
102316
|
-
return `_${escapeHtmlForTg2(line)}_`;
|
|
102317
|
-
return escapeHtmlForTg2(line);
|
|
102318
|
-
});
|
|
102319
|
-
rendered.push("", "\uD83D\uDC47 Tap **\uD83D\uDD10 Open Claude auth** below, then **reply with the browser code**.", "", `_Wrong Anthropic account getting authorized? Long-press the URL below and choose "Copy Link" or "Open in Browser" \u2014 lands in your main browser where the right account is signed in, bypassing Telegram's in-app browser cookies._`, "", url);
|
|
102320
|
-
return { text: rendered.join(`
|
|
102321
|
-
`), url };
|
|
102322
|
-
}
|
|
102323
|
-
function buildAuthUrlKeyboard(authorizeUrl) {
|
|
102324
|
-
return new import_grammy14.InlineKeyboard().url("\uD83D\uDD10 Open Claude auth", authorizeUrl);
|
|
102325
|
-
}
|
|
102326
|
-
function buildDeferredSecretKeyboard(deferKey) {
|
|
102327
|
-
const unlockData = `vd:unlock:${deferKey}`;
|
|
102328
|
-
const cancelData = `vd:cancel:${deferKey}`;
|
|
102329
|
-
if (unlockData.length > 64 || cancelData.length > 64) {
|
|
102330
|
-
process.stderr.write(`telegram gateway: callback_data overflow \u2014 deferKey=${deferKey} unlockLen=${unlockData.length} cancelLen=${cancelData.length}
|
|
102331
|
-
`);
|
|
102332
|
-
throw new Error(`callback_data overflow: deferKey too long (${deferKey.length} chars)`);
|
|
102333
|
-
}
|
|
102334
|
-
return new import_grammy14.InlineKeyboard().text("\uD83D\uDD13 Unlock vault & save", unlockData).text("\uD83D\uDDD1 Discard", cancelData);
|
|
102335
|
-
}
|
|
102336
102589
|
async function runSwitchroomAuthCommand(ctx, args, label) {
|
|
102337
102590
|
try {
|
|
102338
102591
|
const output = switchroomExecCombined(args, 30000);
|
|
@@ -102383,15 +102636,6 @@ function runVaultCli(args, passphrase, stdinValue) {
|
|
|
102383
102636
|
`).trim() };
|
|
102384
102637
|
}
|
|
102385
102638
|
}
|
|
102386
|
-
function renderVaultOpFailure(verbLabel, cliOutput, key) {
|
|
102387
|
-
const parsed = parseVaultCliError2(cliOutput);
|
|
102388
|
-
const verb = verbLabel === "delete" ? "remove" : verbLabel;
|
|
102389
|
-
const rendered = renderVaultCliError2(parsed, { verb, key });
|
|
102390
|
-
if (rendered.suppressRaw)
|
|
102391
|
-
return rendered.html;
|
|
102392
|
-
return `**vault ${verbLabel} failed:**
|
|
102393
|
-
${preBlock(cliOutput)}`;
|
|
102394
|
-
}
|
|
102395
102639
|
async function executeVaultOp(ctx, chatId, op, key, passphrase, setValue) {
|
|
102396
102640
|
if (op === "list") {
|
|
102397
102641
|
const r = runVaultCli(["list"], passphrase);
|
|
@@ -102495,30 +102739,6 @@ function switchroomExecJson(args) {
|
|
|
102495
102739
|
return null;
|
|
102496
102740
|
}
|
|
102497
102741
|
}
|
|
102498
|
-
function statusIcon(status) {
|
|
102499
|
-
if (status === "active" || status === "running")
|
|
102500
|
-
return "\uD83D\uDFE2";
|
|
102501
|
-
if (status === "inactive" || status === "stopped" || status === "dead")
|
|
102502
|
-
return "\uD83D\uDD34";
|
|
102503
|
-
if (status === "failed")
|
|
102504
|
-
return "\u26A0\uFE0F";
|
|
102505
|
-
return "\u26AA";
|
|
102506
|
-
}
|
|
102507
|
-
function renderAuthCodeOutcome(outcome) {
|
|
102508
|
-
if (!outcome || outcome.kind === "success")
|
|
102509
|
-
return null;
|
|
102510
|
-
const tail = outcome.paneTailText ? `
|
|
102511
|
-
_${escapeHtmlForTg2(outcome.paneTailText)}_` : "";
|
|
102512
|
-
switch (outcome.kind) {
|
|
102513
|
-
case "invalid-code":
|
|
102514
|
-
case "expired-code":
|
|
102515
|
-
return `Code rejected by Claude \u2014 tap **Restart flow** for a fresh URL.${tail}`;
|
|
102516
|
-
case "pane-not-ready":
|
|
102517
|
-
return `Auth pane not ready \u2014 tap **Retry**.`;
|
|
102518
|
-
case "timeout":
|
|
102519
|
-
return `Still waiting after 2 min \u2014 tap **Retry** or check \`switchroom auth list\`.${tail}`;
|
|
102520
|
-
}
|
|
102521
|
-
}
|
|
102522
102742
|
function execAuthCode(agent, code2) {
|
|
102523
102743
|
try {
|
|
102524
102744
|
const output = switchroomExec(["auth", "code", agent, code2, "--json"], 150000);
|
|
@@ -102554,7 +102774,7 @@ ${preBlock(formatSwitchroomOutput(detail))}`, { html: true });
|
|
|
102554
102774
|
}
|
|
102555
102775
|
function readRecentDenialsForAgent(agentName3, windowMs, limit) {
|
|
102556
102776
|
try {
|
|
102557
|
-
const auditPath =
|
|
102777
|
+
const auditPath = join60(homedir19(), ".switchroom", "vault-audit.log");
|
|
102558
102778
|
if (!existsSync54(auditPath))
|
|
102559
102779
|
return [];
|
|
102560
102780
|
const raw = readFileSync58(auditPath, "utf8");
|
|
@@ -102608,7 +102828,7 @@ async function buildAgentMetadata(agentName3) {
|
|
|
102608
102828
|
try {
|
|
102609
102829
|
const agentDir = resolveAgentDirFromEnv();
|
|
102610
102830
|
if (agentDir) {
|
|
102611
|
-
const raw = readFileSync58(
|
|
102831
|
+
const raw = readFileSync58(join60(agentDir, ".claude", ".claude.json"), "utf8");
|
|
102612
102832
|
claudeJson = JSON.parse(raw);
|
|
102613
102833
|
}
|
|
102614
102834
|
} catch {}
|
|
@@ -102737,7 +102957,7 @@ function buildModelDeps(restartCtx) {
|
|
|
102737
102957
|
try {
|
|
102738
102958
|
const agentDir = resolveAgentDirFromEnv();
|
|
102739
102959
|
if (agentDir) {
|
|
102740
|
-
const local = await fetchQuota2({ claudeConfigDir:
|
|
102960
|
+
const local = await fetchQuota2({ claudeConfigDir: join60(agentDir, ".claude") });
|
|
102741
102961
|
if (local.ok)
|
|
102742
102962
|
return formatQuotaLine2(local.data);
|
|
102743
102963
|
}
|
|
@@ -102832,7 +103052,7 @@ function buildModelDeps(restartCtx) {
|
|
|
102832
103052
|
function modelMenuReplyMarkup(reply) {
|
|
102833
103053
|
if (!reply.keyboard)
|
|
102834
103054
|
return;
|
|
102835
|
-
const kb = new
|
|
103055
|
+
const kb = new import_grammy15.InlineKeyboard;
|
|
102836
103056
|
for (const row of reply.keyboard) {
|
|
102837
103057
|
for (const btn of row)
|
|
102838
103058
|
kb.text(btn.text, btn.callback_data);
|
|
@@ -102972,7 +103192,7 @@ function buildEffortDeps() {
|
|
|
102972
103192
|
function effortMenuReplyMarkup(reply) {
|
|
102973
103193
|
if (!reply.keyboard)
|
|
102974
103194
|
return;
|
|
102975
|
-
const kb = new
|
|
103195
|
+
const kb = new import_grammy15.InlineKeyboard;
|
|
102976
103196
|
for (const row of reply.keyboard) {
|
|
102977
103197
|
for (const btn of row)
|
|
102978
103198
|
kb.text(btn.text, btn.callback_data);
|
|
@@ -102983,7 +103203,7 @@ function effortMenuReplyMarkup(reply) {
|
|
|
102983
103203
|
function flushAgentHandoff(agentDir) {
|
|
102984
103204
|
let removed = 0;
|
|
102985
103205
|
for (const fname of [".handoff.md", ".handoff-topic"]) {
|
|
102986
|
-
const p =
|
|
103206
|
+
const p = join60(agentDir, fname);
|
|
102987
103207
|
try {
|
|
102988
103208
|
if (existsSync54(p)) {
|
|
102989
103209
|
unlinkSync26(p);
|
|
@@ -103041,7 +103261,7 @@ async function handleNewCommand(ctx) {
|
|
|
103041
103261
|
writeRestartMarker({ chat_id: chatId, thread_id: threadId ?? null, ack_message_id: ackId, ts: Date.now() });
|
|
103042
103262
|
if (agentDir != null) {
|
|
103043
103263
|
try {
|
|
103044
|
-
|
|
103264
|
+
writeFileSync48(join60(agentDir, ".force-fresh-session"), `${kind} at ${new Date().toISOString()}
|
|
103045
103265
|
`, "utf8");
|
|
103046
103266
|
} catch (err) {
|
|
103047
103267
|
process.stderr.write(`telegram gateway: failed to write force-fresh marker: ${err}
|
|
@@ -103155,10 +103375,10 @@ function buildFolderPickerDeps() {
|
|
|
103155
103375
|
}
|
|
103156
103376
|
var lockoutOps = {
|
|
103157
103377
|
readFileSync: (p, enc) => readFileSync58(p, enc),
|
|
103158
|
-
writeFileSync: (p, data, opts) =>
|
|
103378
|
+
writeFileSync: (p, data, opts) => writeFileSync48(p, data, opts),
|
|
103159
103379
|
existsSync: (p) => existsSync54(p),
|
|
103160
|
-
mkdirSync: (p, opts) =>
|
|
103161
|
-
joinPath: (...parts) =>
|
|
103380
|
+
mkdirSync: (p, opts) => mkdirSync47(p, opts),
|
|
103381
|
+
joinPath: (...parts) => join60(...parts)
|
|
103162
103382
|
};
|
|
103163
103383
|
var FLEET_FALLBACK_DEDUP_MS = 30000;
|
|
103164
103384
|
function isAuthBrokerSocketReachable() {
|
|
@@ -103418,7 +103638,7 @@ async function runCreditWatch() {
|
|
|
103418
103638
|
if (!agentDir)
|
|
103419
103639
|
return;
|
|
103420
103640
|
const agentName3 = getMyAgentName();
|
|
103421
|
-
const claudeConfigDir =
|
|
103641
|
+
const claudeConfigDir = join60(agentDir, ".claude");
|
|
103422
103642
|
const stateDir = STATE_DIR;
|
|
103423
103643
|
const reason = readClaudeJsonOverage(claudeConfigDir);
|
|
103424
103644
|
const prev = loadCreditState(stateDir);
|
|
@@ -103738,16 +103958,6 @@ async function probeQuotaForBootCard(agent, timeoutMs) {
|
|
|
103738
103958
|
}
|
|
103739
103959
|
var callbackQueryHandlers;
|
|
103740
103960
|
var cardToolHandlers;
|
|
103741
|
-
function buildDoctorScopeKeyboard() {
|
|
103742
|
-
return new import_grammy14.InlineKeyboard().text("\uD83E\uDE7A Whole fleet", "dr:fleet").text("\uD83E\uDE7A This agent", "dr:self");
|
|
103743
|
-
}
|
|
103744
|
-
function formatDoctorReport(raw) {
|
|
103745
|
-
const trimmed = stripAnsi4(raw).trim();
|
|
103746
|
-
if (!trimmed)
|
|
103747
|
-
return "doctor: no output";
|
|
103748
|
-
const pretty = trimmed.replace(/^( *)\u2713 /gm, "$1\uD83D\uDFE2 ").replace(/^( *)\u2717 /gm, "$1\uD83D\uDD34 ").replace(/^( *)! /gm, "$1\uD83D\uDFE1 ");
|
|
103749
|
-
return preBlock(formatSwitchroomOutput(pretty));
|
|
103750
|
-
}
|
|
103751
103961
|
async function renderSelfDoctor(ctx) {
|
|
103752
103962
|
let output;
|
|
103753
103963
|
try {
|
|
@@ -104635,7 +104845,7 @@ _Google accounts are connected from the host CLI._`, { html: true });
|
|
|
104635
104845
|
return;
|
|
104636
104846
|
}
|
|
104637
104847
|
const appNote = started.source === "default" ? "_Using switchroom's shipped Microsoft app._" : "_Using your configured Microsoft app._";
|
|
104638
|
-
const keyboard = new
|
|
104848
|
+
const keyboard = new import_grammy15.InlineKeyboard().url("\uD83D\uDD10 Sign in to Microsoft", started.device.verification_uri).row().text("\u2716 Cancel", `cn:cancel:${key}`);
|
|
104639
104849
|
const sent = await ctx.replyWithRichMessage(richMessage2(`\uD83D\uDD17 **Connect a Microsoft account**
|
|
104640
104850
|
|
|
104641
104851
|
` + `1. Tap **Sign in to Microsoft** below.
|
|
@@ -104771,7 +104981,7 @@ Send \`/auth ${parsed.provider} cancel\` to abort.`, { html: true });
|
|
|
104771
104981
|
tz: process.env.SWITCHROOM_TIMEZONE ?? process.env.TZ
|
|
104772
104982
|
});
|
|
104773
104983
|
if (reply.keyboard && reply.keyboard.length > 0) {
|
|
104774
|
-
const kb = new
|
|
104984
|
+
const kb = new import_grammy15.InlineKeyboard;
|
|
104775
104985
|
for (let i = 0;i < reply.keyboard.length; i++) {
|
|
104776
104986
|
const row = reply.keyboard[i];
|
|
104777
104987
|
for (const b of row) {
|
|
@@ -104913,7 +105123,7 @@ Send \`/auth ${parsed.provider} cancel\` to abort.`, { html: true });
|
|
|
104913
105123
|
}
|
|
104914
105124
|
lines.push("");
|
|
104915
105125
|
const denials = readRecentDenialsForAgent(targetAgent, 604800000, 5);
|
|
104916
|
-
const denialKeyboard = new
|
|
105126
|
+
const denialKeyboard = new import_grammy15.InlineKeyboard;
|
|
104917
105127
|
if (denials.length > 0) {
|
|
104918
105128
|
lines.push("**Recent denials (last 7d):**");
|
|
104919
105129
|
for (const d of denials) {
|
|
@@ -104961,7 +105171,7 @@ Send \`/auth ${parsed.provider} cancel\` to abort.`, { html: true });
|
|
|
104961
105171
|
byAgent.set(g.agent_slug, list3);
|
|
104962
105172
|
}
|
|
104963
105173
|
const lines = ["**\uD83D\uDCDC Active grants**", ""];
|
|
104964
|
-
const keyboard = new
|
|
105174
|
+
const keyboard = new import_grammy15.InlineKeyboard;
|
|
104965
105175
|
for (const [agentName3, agentGrants] of byAgent) {
|
|
104966
105176
|
lines.push(`**${escapeHtmlForTg2(agentName3)}:**`);
|
|
104967
105177
|
for (const g of agentGrants) {
|
|
@@ -105158,7 +105368,7 @@ Send \`/auth ${parsed.provider} cancel\` to abort.`, { html: true });
|
|
|
105158
105368
|
if (ctx.chat?.type !== "private") {
|
|
105159
105369
|
kbRows = kbRows.filter((row) => !row.some((b) => b.callbackData?.startsWith("auth:use:")));
|
|
105160
105370
|
}
|
|
105161
|
-
const keyboard = new
|
|
105371
|
+
const keyboard = new import_grammy15.InlineKeyboard;
|
|
105162
105372
|
kbRows.forEach((row, ri) => {
|
|
105163
105373
|
if (ri > 0)
|
|
105164
105374
|
keyboard.row();
|
|
@@ -105184,7 +105394,7 @@ Send \`/auth ${parsed.provider} cancel\` to abort.`, { html: true });
|
|
|
105184
105394
|
await switchroomReply(ctx, "**/usage:** cannot resolve agent dir.", { html: true });
|
|
105185
105395
|
return;
|
|
105186
105396
|
}
|
|
105187
|
-
const result = await fetchQuota2({ claudeConfigDir:
|
|
105397
|
+
const result = await fetchQuota2({ claudeConfigDir: join60(agentDir, ".claude") });
|
|
105188
105398
|
if (!result.ok) {
|
|
105189
105399
|
await switchroomReply(ctx, `**/usage:** ${escapeHtmlForTg2(result.reason)}`, { html: true });
|
|
105190
105400
|
return;
|
|
@@ -105510,7 +105720,7 @@ ${preBlock(formatSwitchroomOutput(err.message ?? "unknown error"))}`, { html: tr
|
|
|
105510
105720
|
await ctx.answerCallbackQuery({ text: "No always-allow rule for this tool." }).catch(() => {});
|
|
105511
105721
|
return;
|
|
105512
105722
|
}
|
|
105513
|
-
keyboard = new
|
|
105723
|
+
keyboard = new import_grammy15.InlineKeyboard().text("\u2190 Back", `perm:back:${request_id}`);
|
|
105514
105724
|
if (choices.specific)
|
|
105515
105725
|
keyboard.text(choices.specific.buttonLabel, `perm:asn:${request_id}`);
|
|
105516
105726
|
keyboard.text(`${choices.broad.buttonLabel} \u26A0\uFE0F`, `perm:asb:${request_id}`);
|
|
@@ -105812,7 +106022,7 @@ ${labelWithResume}` : labelWithResume,
|
|
|
105812
106022
|
verb: "voice-ondemand.sendVoice",
|
|
105813
106023
|
...threadId != null ? { threadId } : {}
|
|
105814
106024
|
}),
|
|
105815
|
-
sendVoiceByUpload: (chatId, audio, sendOpts, threadId) => robustApiCall(() => bot2.api.sendVoice(chatId, new
|
|
106025
|
+
sendVoiceByUpload: (chatId, audio, sendOpts, threadId) => robustApiCall(() => bot2.api.sendVoice(chatId, new import_grammy15.InputFile(Buffer.from(audio)), sendOpts), {
|
|
105816
106026
|
chat_id: chatId,
|
|
105817
106027
|
verb: "voice-ondemand.sendVoice",
|
|
105818
106028
|
...threadId != null ? { threadId } : {}
|
|
@@ -106071,7 +106281,7 @@ async function initGatewayBot() {
|
|
|
106071
106281
|
`);
|
|
106072
106282
|
process.exit(1);
|
|
106073
106283
|
}
|
|
106074
|
-
bot = new
|
|
106284
|
+
bot = new import_grammy15.Bot(TOKEN);
|
|
106075
106285
|
installTgPostLogger(bot);
|
|
106076
106286
|
installUpdateTap(bot, (line) => process.stderr.write(line));
|
|
106077
106287
|
bot.api.config.use(async (prev, method, payload, signal) => {
|
|
@@ -106425,7 +106635,7 @@ async function startGateway() {
|
|
|
106425
106635
|
return;
|
|
106426
106636
|
}
|
|
106427
106637
|
})();
|
|
106428
|
-
const resolvedAgentDirForBootCard = agentDir ??
|
|
106638
|
+
const resolvedAgentDirForBootCard = agentDir ?? join60(homedir19(), ".switchroom", "agents", agentSlug);
|
|
106429
106639
|
const handle = await startBootCard(chatId, threadId, botApiForCard, {
|
|
106430
106640
|
agentName: agentDisplayName,
|
|
106431
106641
|
agentSlug,
|
|
@@ -106439,8 +106649,8 @@ async function startGateway() {
|
|
|
106439
106649
|
probeQuotaViaBroker: (t) => probeQuotaForBootCard(agentSlug, t),
|
|
106440
106650
|
tmuxSupervisor: process.env.SWITCHROOM_TMUX_SUPERVISOR === "1",
|
|
106441
106651
|
dockerMode: process.env.SWITCHROOM_RUNTIME === "docker",
|
|
106442
|
-
configSnapshotPath:
|
|
106443
|
-
bootCardStatePath:
|
|
106652
|
+
configSnapshotPath: join60(resolvedAgentDirForBootCard, ".config-snapshot.json"),
|
|
106653
|
+
bootCardStatePath: join60(resolvedAgentDirForBootCard, ".boot-card-msgid.json"),
|
|
106444
106654
|
floodStatePath: FLOOD_STATE_PATH,
|
|
106445
106655
|
...updateOutcomeLine ? { updateOutcomeLine } : {}
|
|
106446
106656
|
}, ackMsgId);
|
|
@@ -106471,7 +106681,7 @@ async function startGateway() {
|
|
|
106471
106681
|
try {
|
|
106472
106682
|
const smAgentDir = resolveAgentDirFromEnv();
|
|
106473
106683
|
if (smAgentDir) {
|
|
106474
|
-
const activePath =
|
|
106684
|
+
const activePath = join60(smAgentDir, ".active-session-model");
|
|
106475
106685
|
if (existsSync54(activePath)) {
|
|
106476
106686
|
try {
|
|
106477
106687
|
const launched = readFileSync58(activePath, "utf8").trim();
|
|
@@ -106507,12 +106717,12 @@ async function startGateway() {
|
|
|
106507
106717
|
deliverModelSwitchBootNotice({
|
|
106508
106718
|
...modelBootCardDeps,
|
|
106509
106719
|
confirmation,
|
|
106510
|
-
hasSessionModelAlert: existsSync54(
|
|
106720
|
+
hasSessionModelAlert: existsSync54(join60(smAgentDir, ".session-model-alert"))
|
|
106511
106721
|
});
|
|
106512
106722
|
}
|
|
106513
106723
|
} catch {}
|
|
106514
106724
|
}
|
|
106515
|
-
const activeEffortPath =
|
|
106725
|
+
const activeEffortPath = join60(smAgentDir, ".active-session-effort");
|
|
106516
106726
|
if (existsSync54(activeEffortPath)) {
|
|
106517
106727
|
try {
|
|
106518
106728
|
const launchedEffort = readFileSync58(activeEffortPath, "utf8").trim();
|
|
@@ -106520,7 +106730,7 @@ async function startGateway() {
|
|
|
106520
106730
|
sessionEffortOverride = launchedEffort.length > 0 && launchedEffort !== configuredEffort ? launchedEffort : null;
|
|
106521
106731
|
} catch {}
|
|
106522
106732
|
}
|
|
106523
|
-
const alertPath =
|
|
106733
|
+
const alertPath = join60(smAgentDir, ".session-model-alert");
|
|
106524
106734
|
if (existsSync54(alertPath)) {
|
|
106525
106735
|
let alertText = null;
|
|
106526
106736
|
try {
|
|
@@ -106700,6 +106910,7 @@ async function startGateway() {
|
|
|
106700
106910
|
`);
|
|
106701
106911
|
},
|
|
106702
106912
|
onTerminalCleanup: (agentId) => {
|
|
106913
|
+
workerFeedOriginDeferrals.delete(agentId);
|
|
106703
106914
|
try {
|
|
106704
106915
|
workerActivityFeed?.terminate(agentId);
|
|
106705
106916
|
} catch (err) {
|
|
@@ -106708,6 +106919,7 @@ async function startGateway() {
|
|
|
106708
106919
|
}
|
|
106709
106920
|
},
|
|
106710
106921
|
onFinish: ({ agentId, outcome, description, resultText, toolCount, totalTokens, durationMs, background: entryBackground }) => {
|
|
106922
|
+
workerFeedOriginDeferrals.delete(agentId);
|
|
106711
106923
|
deferredDoneReactions.promote();
|
|
106712
106924
|
let fleetChatId = "";
|
|
106713
106925
|
try {
|
|
@@ -106928,9 +107140,28 @@ async function startGateway() {
|
|
|
106928
107140
|
stampTurn.subagentActivityAt = Date.now();
|
|
106929
107141
|
}
|
|
106930
107142
|
if (workerFeedEnabled) {
|
|
106931
|
-
const
|
|
106932
|
-
|
|
106933
|
-
|
|
107143
|
+
const dest = decideWorkerFeedDestination({
|
|
107144
|
+
origin: resolveSubagentOriginChat(agentId),
|
|
107145
|
+
cardExists: workerActivityFeed?.has(agentId) === true,
|
|
107146
|
+
priorDeferrals: workerFeedOriginDeferrals.get(agentId) ?? 0,
|
|
107147
|
+
maxDeferrals: WORKER_FEED_ORIGIN_DEFER_MAX,
|
|
107148
|
+
fleetChatId,
|
|
107149
|
+
stampChatId: stampTurn?.sessionChatId,
|
|
107150
|
+
stampThreadId: stampTurn?.sessionThreadId,
|
|
107151
|
+
ownerDm: loadAccess().allowFrom[0] ?? ""
|
|
107152
|
+
});
|
|
107153
|
+
if (dest.action === "defer") {
|
|
107154
|
+
workerFeedOriginDeferrals.set(agentId, dest.deferrals);
|
|
107155
|
+
return;
|
|
107156
|
+
}
|
|
107157
|
+
workerFeedOriginDeferrals.delete(agentId);
|
|
107158
|
+
if (dest.exhausted) {
|
|
107159
|
+
process.stderr.write(`telegram gateway: worker-feed origin backfill never linked agent=${agentId} after ${dest.deferrals} deferrals \u2014 painting card
|
|
107160
|
+
`);
|
|
107161
|
+
}
|
|
107162
|
+
if (dest.ownerDmFallback)
|
|
107163
|
+
noteWorkerFeedOwnerDmFallback(agentId);
|
|
107164
|
+
workerActivityFeed?.update(agentId, dest.chatId, {
|
|
106934
107165
|
description: dispatch.feedDescription,
|
|
106935
107166
|
lastTool,
|
|
106936
107167
|
toolCount,
|
|
@@ -106939,7 +107170,7 @@ async function startGateway() {
|
|
|
106939
107170
|
state: "running",
|
|
106940
107171
|
model: feedModel,
|
|
106941
107172
|
totalTokens
|
|
106942
|
-
},
|
|
107173
|
+
}, dest.threadId);
|
|
106943
107174
|
return;
|
|
106944
107175
|
}
|
|
106945
107176
|
const progressOrigin = resolveSubagentOriginChat(agentId);
|
|
@@ -107004,7 +107235,7 @@ async function startGateway() {
|
|
|
107004
107235
|
await runnerHandle.task();
|
|
107005
107236
|
return;
|
|
107006
107237
|
} catch (err) {
|
|
107007
|
-
if (err instanceof
|
|
107238
|
+
if (err instanceof import_grammy15.GrammyError && err.error_code === 409) {
|
|
107008
107239
|
const delay = Math.min(1000 * attempt, 15000);
|
|
107009
107240
|
const agentName3 = process.env.SWITCHROOM_AGENT_NAME ?? "-";
|
|
107010
107241
|
process.stderr.write(`telegram gateway: poll.409.detected attempt=${attempt} retry_in_ms=${delay} agent=${agentName3}
|