switchroom 0.19.4 → 0.19.6
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 +585 -302
- package/telegram-plugin/flushed-turn-supersede.ts +43 -7
- package/telegram-plugin/gateway/command-format.ts +253 -0
- package/telegram-plugin/gateway/gateway-heartbeat.ts +72 -0
- package/telegram-plugin/gateway/gateway.ts +128 -259
- package/telegram-plugin/gateway/hang-restart-decision.ts +189 -0
- package/telegram-plugin/gateway/liveness-wiring.ts +35 -1
- package/telegram-plugin/gateway/outbound-send-path.ts +51 -11
- package/telegram-plugin/gateway/pending-inbound-buffer.ts +27 -0
- package/telegram-plugin/gateway/session-model-file.ts +13 -0
- package/telegram-plugin/gateway/stream-render.ts +18 -1
- package/telegram-plugin/gateway/subagent-handback-marker.ts +42 -0
- 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/reply-owner-resolve.ts +43 -7
- package/telegram-plugin/rich-send.ts +8 -1
- package/telegram-plugin/tests/command-format.test.ts +212 -0
- package/telegram-plugin/tests/flushed-turn-supersede.test.ts +89 -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/narrative-lane-golden.test.ts +2 -1
- 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/reply-owner-resolve.test.ts +74 -0
- package/telegram-plugin/tests/send-reply-golden.test.ts +221 -6
- 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/stream-render-golden.test.ts +2 -1
- package/telegram-plugin/tests/subagent-handback-marker.test.ts +36 -0
- 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";
|
|
@@ -40393,7 +40426,7 @@ function decideSupersede(record, args) {
|
|
|
40393
40426
|
if (!sameTurn) {
|
|
40394
40427
|
return { supersede: false, deleteMessageIds: [], reason: "different-turn" };
|
|
40395
40428
|
}
|
|
40396
|
-
if (args.replyText != null && !flushedAnswerMatchesReply(record.text, args.replyText)) {
|
|
40429
|
+
if (!args.positiveAttribution && args.replyText != null && !flushedAnswerMatchesReply(record.text, args.replyText)) {
|
|
40397
40430
|
return { supersede: false, deleteMessageIds: [], reason: "new-content", recordText: record.text };
|
|
40398
40431
|
}
|
|
40399
40432
|
return {
|
|
@@ -40436,6 +40469,7 @@ class FlushedTurnSupersedeRegistry {
|
|
|
40436
40469
|
return decideSupersede(rec, {
|
|
40437
40470
|
liveTurnId: args.liveTurnId,
|
|
40438
40471
|
replyText: args.replyText,
|
|
40472
|
+
positiveAttribution: args.positiveAttribution,
|
|
40439
40473
|
now: args.now,
|
|
40440
40474
|
ttlMs: this.ttlMs
|
|
40441
40475
|
});
|
|
@@ -50451,6 +50485,25 @@ function sweepPermissionTtl(deps) {
|
|
|
50451
50485
|
return expired;
|
|
50452
50486
|
}
|
|
50453
50487
|
|
|
50488
|
+
// gateway/hang-restart-decision.ts
|
|
50489
|
+
var HUMAN_WAIT_TOOLS = new Set(["ask_user"]);
|
|
50490
|
+
var BACKGROUND_TOOLS = new Set(["Task", "Agent"]);
|
|
50491
|
+
var HANG_PROTECTED_EXACT = new Set([
|
|
50492
|
+
"Task",
|
|
50493
|
+
"Agent",
|
|
50494
|
+
"Bash",
|
|
50495
|
+
"WebFetch",
|
|
50496
|
+
"WebSearch"
|
|
50497
|
+
]);
|
|
50498
|
+
var DEFAULT_HANG_STALENESS_MS = 300000;
|
|
50499
|
+
function hangStalenessMs(env = process.env) {
|
|
50500
|
+
const raw = env.TURN_HANG_SECS;
|
|
50501
|
+
const n = Number(raw);
|
|
50502
|
+
if (!Number.isFinite(n) || n <= 0)
|
|
50503
|
+
return DEFAULT_HANG_STALENESS_MS;
|
|
50504
|
+
return Math.floor(n * 1000);
|
|
50505
|
+
}
|
|
50506
|
+
|
|
50454
50507
|
// gateway/missed-approvals-store.ts
|
|
50455
50508
|
import { readFileSync as readFileSync13, writeFileSync as writeFileSync11, unlinkSync as unlinkSync7 } from "node:fs";
|
|
50456
50509
|
import { join as join13 } from "node:path";
|
|
@@ -73377,15 +73430,142 @@ function backOffOpenInline2(text4, cut) {
|
|
|
73377
73430
|
return earliest;
|
|
73378
73431
|
}
|
|
73379
73432
|
|
|
73433
|
+
// gateway/command-format.ts
|
|
73434
|
+
init_format();
|
|
73435
|
+
var import_grammy8 = __toESM(require_mod2(), 1);
|
|
73436
|
+
function formatSwitchroomOutput(output, maxLen = RICH_MESSAGE_MAX_CHARS) {
|
|
73437
|
+
const trimmed = output.trim();
|
|
73438
|
+
if (trimmed.length <= maxLen)
|
|
73439
|
+
return trimmed;
|
|
73440
|
+
return trimmed.slice(0, maxLen - 20) + `
|
|
73441
|
+
... (truncated)`;
|
|
73442
|
+
}
|
|
73443
|
+
function stripAnsi4(text4) {
|
|
73444
|
+
return text4.replace(/\x1b\[[0-9;]*[a-zA-Z]/g, "");
|
|
73445
|
+
}
|
|
73446
|
+
function escapeHtmlForTg2(text4) {
|
|
73447
|
+
return text4.replace(/([\\`*_~=\[\]|])/g, "\\$1");
|
|
73448
|
+
}
|
|
73449
|
+
function preBlock(text4) {
|
|
73450
|
+
return "```\n" + text4.replace(/```/g, "`\u200b``") + "\n```";
|
|
73451
|
+
}
|
|
73452
|
+
function getCommandArgs(ctx) {
|
|
73453
|
+
const fromMatch = typeof ctx.match === "string" ? ctx.match.trim() : "";
|
|
73454
|
+
if (fromMatch)
|
|
73455
|
+
return fromMatch;
|
|
73456
|
+
const text4 = ctx.msg?.text ?? ctx.message?.text ?? "";
|
|
73457
|
+
const m = text4.match(/^\/\S+\s+([\s\S]*)$/);
|
|
73458
|
+
return m ? m[1].trim() : "";
|
|
73459
|
+
}
|
|
73460
|
+
function hasDemoFlag(args) {
|
|
73461
|
+
return /(?:^|\s)demo$/i.test(args.trim());
|
|
73462
|
+
}
|
|
73463
|
+
function assertSafeAgentName(name) {
|
|
73464
|
+
if (!/^[a-zA-Z0-9_-]{1,64}$/.test(name) && name !== "all") {
|
|
73465
|
+
throw new Error(`invalid agent name: ${name}`);
|
|
73466
|
+
}
|
|
73467
|
+
}
|
|
73468
|
+
function formatAuthOutputForTelegram(output) {
|
|
73469
|
+
const trimmed = stripAnsi4(output).trim();
|
|
73470
|
+
const url = trimmed.match(/https:\/\/\S+/)?.[0] ?? null;
|
|
73471
|
+
const lines = trimmed.split(/\n+/).map((l) => l.trim()).filter(Boolean);
|
|
73472
|
+
if (!url)
|
|
73473
|
+
return { text: preBlock(formatSwitchroomOutput(trimmed)), url: null };
|
|
73474
|
+
const body = lines.filter((line) => {
|
|
73475
|
+
if (line === url)
|
|
73476
|
+
return false;
|
|
73477
|
+
if (line.startsWith("switchroom auth code"))
|
|
73478
|
+
return false;
|
|
73479
|
+
if (line.startsWith("switchroom auth cancel"))
|
|
73480
|
+
return false;
|
|
73481
|
+
if (line.startsWith("Use 'tmux attach"))
|
|
73482
|
+
return false;
|
|
73483
|
+
if (line.startsWith("After Claude shows you a browser code"))
|
|
73484
|
+
return false;
|
|
73485
|
+
if (line.startsWith("Then finish with:"))
|
|
73486
|
+
return false;
|
|
73487
|
+
if (line.startsWith("Cancel with:"))
|
|
73488
|
+
return false;
|
|
73489
|
+
return true;
|
|
73490
|
+
});
|
|
73491
|
+
const rendered = body.map((line) => {
|
|
73492
|
+
if (line.startsWith("Started Claude auth") || line.startsWith("Auth session already running"))
|
|
73493
|
+
return `**${escapeHtmlForTg2(line)}**`;
|
|
73494
|
+
if (line.startsWith("Open this URL"))
|
|
73495
|
+
return `_${escapeHtmlForTg2(line)}_`;
|
|
73496
|
+
return escapeHtmlForTg2(line);
|
|
73497
|
+
});
|
|
73498
|
+
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);
|
|
73499
|
+
return { text: rendered.join(`
|
|
73500
|
+
`), url };
|
|
73501
|
+
}
|
|
73502
|
+
function buildAuthUrlKeyboard(authorizeUrl) {
|
|
73503
|
+
return new import_grammy8.InlineKeyboard().url("\uD83D\uDD10 Open Claude auth", authorizeUrl);
|
|
73504
|
+
}
|
|
73505
|
+
function buildDeferredSecretKeyboard(deferKey) {
|
|
73506
|
+
const unlockData = `vd:unlock:${deferKey}`;
|
|
73507
|
+
const cancelData = `vd:cancel:${deferKey}`;
|
|
73508
|
+
if (unlockData.length > 64 || cancelData.length > 64) {
|
|
73509
|
+
process.stderr.write(`telegram gateway: callback_data overflow \u2014 deferKey=${deferKey} unlockLen=${unlockData.length} cancelLen=${cancelData.length}
|
|
73510
|
+
`);
|
|
73511
|
+
throw new Error(`callback_data overflow: deferKey too long (${deferKey.length} chars)`);
|
|
73512
|
+
}
|
|
73513
|
+
return new import_grammy8.InlineKeyboard().text("\uD83D\uDD13 Unlock vault & save", unlockData).text("\uD83D\uDDD1 Discard", cancelData);
|
|
73514
|
+
}
|
|
73515
|
+
function renderVaultOpFailure(verbLabel, cliOutput, key) {
|
|
73516
|
+
const parsed = parseVaultCliError(cliOutput);
|
|
73517
|
+
const verb = verbLabel === "delete" ? "remove" : verbLabel;
|
|
73518
|
+
const rendered = renderVaultCliError(parsed, { verb, key });
|
|
73519
|
+
if (rendered.suppressRaw)
|
|
73520
|
+
return rendered.html;
|
|
73521
|
+
return `**vault ${verbLabel} failed:**
|
|
73522
|
+
${preBlock(cliOutput)}`;
|
|
73523
|
+
}
|
|
73524
|
+
function statusIcon(status) {
|
|
73525
|
+
if (status === "active" || status === "running")
|
|
73526
|
+
return "\uD83D\uDFE2";
|
|
73527
|
+
if (status === "inactive" || status === "stopped" || status === "dead")
|
|
73528
|
+
return "\uD83D\uDD34";
|
|
73529
|
+
if (status === "failed")
|
|
73530
|
+
return "\u26a0\ufe0f";
|
|
73531
|
+
return "\u26aa";
|
|
73532
|
+
}
|
|
73533
|
+
function renderAuthCodeOutcome(outcome) {
|
|
73534
|
+
if (!outcome || outcome.kind === "success")
|
|
73535
|
+
return null;
|
|
73536
|
+
const tail = outcome.paneTailText ? `
|
|
73537
|
+
_${escapeHtmlForTg2(outcome.paneTailText)}_` : "";
|
|
73538
|
+
switch (outcome.kind) {
|
|
73539
|
+
case "invalid-code":
|
|
73540
|
+
case "expired-code":
|
|
73541
|
+
return `Code rejected by Claude \u2014 tap **Restart flow** for a fresh URL.${tail}`;
|
|
73542
|
+
case "pane-not-ready":
|
|
73543
|
+
return `Auth pane not ready \u2014 tap **Retry**.`;
|
|
73544
|
+
case "timeout":
|
|
73545
|
+
return `Still waiting after 2 min \u2014 tap **Retry** or check \`switchroom auth list\`.${tail}`;
|
|
73546
|
+
}
|
|
73547
|
+
}
|
|
73548
|
+
function buildDoctorScopeKeyboard() {
|
|
73549
|
+
return new import_grammy8.InlineKeyboard().text("\uD83E\uDE7A Whole fleet", "dr:fleet").text("\uD83E\uDE7A This agent", "dr:self");
|
|
73550
|
+
}
|
|
73551
|
+
function formatDoctorReport(raw) {
|
|
73552
|
+
const trimmed = stripAnsi4(raw).trim();
|
|
73553
|
+
if (!trimmed)
|
|
73554
|
+
return "doctor: no output";
|
|
73555
|
+
const pretty = trimmed.replace(/^( *)\u2713 /gm, "$1\uD83D\uDFE2 ").replace(/^( *)\u2717 /gm, "$1\uD83D\uDD34 ").replace(/^( *)! /gm, "$1\uD83D\uDFE1 ");
|
|
73556
|
+
return preBlock(formatSwitchroomOutput(pretty));
|
|
73557
|
+
}
|
|
73558
|
+
|
|
73380
73559
|
// rich-send.ts
|
|
73381
73560
|
init_dollar_math_guard();
|
|
73382
73561
|
init_emphasis_guard();
|
|
73383
73562
|
init_line_start_guard();
|
|
73384
73563
|
init_inline_pairs_guard();
|
|
73385
|
-
var
|
|
73564
|
+
var import_grammy9 = __toESM(require_mod2(), 1);
|
|
73386
73565
|
function guardAccidentalFormatting2(markdown) {
|
|
73387
73566
|
let out = markdown;
|
|
73388
73567
|
out = guardAccidentalEmphasis(out);
|
|
73568
|
+
out = guardAccidentalHeading(out);
|
|
73389
73569
|
out = guardAccidentalBlockConstructs(out);
|
|
73390
73570
|
out = guardAccidentalInlinePairs(out);
|
|
73391
73571
|
out = guardDollarMath(out);
|
|
@@ -73537,7 +73717,7 @@ function scrubVoice2(text4) {
|
|
|
73537
73717
|
// gateway/outbound-send-path.ts
|
|
73538
73718
|
init_format();
|
|
73539
73719
|
init_text_voice_scrub();
|
|
73540
|
-
var
|
|
73720
|
+
var import_grammy10 = __toESM(require_mod2(), 1);
|
|
73541
73721
|
import { statSync as statSync9 } from "fs";
|
|
73542
73722
|
import { extname } from "path";
|
|
73543
73723
|
|
|
@@ -73775,7 +73955,7 @@ function decideSupersede2(record, args) {
|
|
|
73775
73955
|
if (!sameTurn) {
|
|
73776
73956
|
return { supersede: false, deleteMessageIds: [], reason: "different-turn" };
|
|
73777
73957
|
}
|
|
73778
|
-
if (args.replyText != null && !flushedAnswerMatchesReply2(record.text, args.replyText)) {
|
|
73958
|
+
if (!args.positiveAttribution && args.replyText != null && !flushedAnswerMatchesReply2(record.text, args.replyText)) {
|
|
73779
73959
|
return { supersede: false, deleteMessageIds: [], reason: "new-content", recordText: record.text };
|
|
73780
73960
|
}
|
|
73781
73961
|
return {
|
|
@@ -73825,6 +74005,7 @@ class FlushedTurnSupersedeRegistry2 {
|
|
|
73825
74005
|
return decideSupersede2(rec, {
|
|
73826
74006
|
liveTurnId: args.liveTurnId,
|
|
73827
74007
|
replyText: args.replyText,
|
|
74008
|
+
positiveAttribution: args.positiveAttribution,
|
|
73828
74009
|
now: args.now,
|
|
73829
74010
|
ttlMs: this.ttlMs
|
|
73830
74011
|
});
|
|
@@ -74326,7 +74507,7 @@ async function sendReplyChunks(deps, state3) {
|
|
|
74326
74507
|
return { threadId, previewMessageId };
|
|
74327
74508
|
}
|
|
74328
74509
|
function classifyReadBackError(err) {
|
|
74329
|
-
if (err instanceof
|
|
74510
|
+
if (err instanceof import_grammy10.GrammyError && err.error_code === 400) {
|
|
74330
74511
|
const d = (err.description || "").toLowerCase();
|
|
74331
74512
|
if (d.includes("message to edit not found"))
|
|
74332
74513
|
return "absent";
|
|
@@ -74404,6 +74585,7 @@ async function sendReply(deps, req) {
|
|
|
74404
74585
|
resolveAnswerThreadWithLog,
|
|
74405
74586
|
resolveThreadId,
|
|
74406
74587
|
getLatestInboundMessageId: getLatestInboundMessageId2,
|
|
74588
|
+
getLastSubagentHandbackAt,
|
|
74407
74589
|
recordOutbound: recordOutbound2,
|
|
74408
74590
|
emissionAuthorityFor,
|
|
74409
74591
|
clearActivitySummary,
|
|
@@ -74477,9 +74659,14 @@ async function sendReply(deps, req) {
|
|
|
74477
74659
|
let supersedeFlushIds = [];
|
|
74478
74660
|
{
|
|
74479
74661
|
const replyThreadId = args.message_thread_id != null ? Number(args.message_thread_id) : undefined;
|
|
74480
|
-
const ownerTurn = resolveReplyOwnerTurn(turn, chat_id, args);
|
|
74662
|
+
const { turn: ownerTurn, tier: ownerTier } = resolveReplyOwnerTurn(turn, chat_id, args);
|
|
74481
74663
|
const resolvedTurnId = ownerTurn?.turnId ?? null;
|
|
74482
|
-
const
|
|
74664
|
+
const ownerEndedAt = ownerTurn?.endedAt ?? null;
|
|
74665
|
+
const handbackAt = getLastSubagentHandbackAt(chat_id);
|
|
74666
|
+
const now = Date.now();
|
|
74667
|
+
const handbackCouldOwnReply = handbackAt != null && ownerEndedAt != null && handbackAt > ownerEndedAt && now - handbackAt <= DEFAULT_SUPERSEDE_TTL_MS2;
|
|
74668
|
+
const replyIsOwnAnswer = ownerTier === "live" || !handbackCouldOwnReply;
|
|
74669
|
+
const decision = flushedTurnSupersede.take(chat_id, replyThreadId, { liveTurnId: resolvedTurnId, replyText: text4, positiveAttribution: replyIsOwnAnswer, now });
|
|
74483
74670
|
if (decision.supersede) {
|
|
74484
74671
|
process.stderr.write(`telegram gateway: reply: superseding flushed turn message(s) ` + `chatId=${chat_id} ids=${JSON.stringify(decision.deleteMessageIds)}
|
|
74485
74672
|
`);
|
|
@@ -74901,7 +75088,7 @@ ${url}`;
|
|
|
74901
75088
|
opts.message_thread_id = tid;
|
|
74902
75089
|
else
|
|
74903
75090
|
delete opts.message_thread_id;
|
|
74904
|
-
return lockedBot.api.sendVoice(chat_id, new
|
|
75091
|
+
return lockedBot.api.sendVoice(chat_id, new import_grammy10.InputFile(Buffer.from(oggBytes)), opts);
|
|
74905
75092
|
}, { threadId, chat_id, verb: "sendVoice" });
|
|
74906
75093
|
sentIds.push(sentVoice.message_id);
|
|
74907
75094
|
logOutbound("reply", chat_id, sentVoice.message_id, voiceOutPlan?.ttsChunks[v]?.length ?? 0, `voice-note=${v + 1}/${voiceOggs.length}`);
|
|
@@ -74974,12 +75161,12 @@ ${url}`;
|
|
|
74974
75161
|
...replyParams,
|
|
74975
75162
|
...tid != null ? { message_thread_id: tid } : {}
|
|
74976
75163
|
};
|
|
74977
|
-
return lockedBot.api.sendDocument(chat_id, new
|
|
75164
|
+
return lockedBot.api.sendDocument(chat_id, new import_grammy10.InputFile(f), baseOpts);
|
|
74978
75165
|
}, { threadId, chat_id, verb: "sendDocument" });
|
|
74979
75166
|
if (allPhotos) {
|
|
74980
75167
|
const media = files.map((f) => ({
|
|
74981
75168
|
type: "photo",
|
|
74982
|
-
media: new
|
|
75169
|
+
media: new import_grammy10.InputFile(f)
|
|
74983
75170
|
}));
|
|
74984
75171
|
let sent;
|
|
74985
75172
|
try {
|
|
@@ -75012,7 +75199,7 @@ ${url}`;
|
|
|
75012
75199
|
sentIds.push(m.message_id);
|
|
75013
75200
|
} else {
|
|
75014
75201
|
for (const f of files) {
|
|
75015
|
-
const input = new
|
|
75202
|
+
const input = new import_grammy10.InputFile(f);
|
|
75016
75203
|
const isPhoto = sendableAsPhoto(f);
|
|
75017
75204
|
let sent;
|
|
75018
75205
|
try {
|
|
@@ -76202,6 +76389,15 @@ function removeTurnActiveMarker(stateDir) {
|
|
|
76202
76389
|
unlinkSync13(join31(stateDir, TURN_ACTIVE_MARKER_FILE));
|
|
76203
76390
|
} catch {}
|
|
76204
76391
|
}
|
|
76392
|
+
function readTurnActiveMarkerAgeMs(stateDir, now) {
|
|
76393
|
+
const path2 = join31(stateDir, TURN_ACTIVE_MARKER_FILE);
|
|
76394
|
+
try {
|
|
76395
|
+
const st = statSync10(path2);
|
|
76396
|
+
return (now ?? Date.now()) - st.mtimeMs;
|
|
76397
|
+
} catch {
|
|
76398
|
+
return null;
|
|
76399
|
+
}
|
|
76400
|
+
}
|
|
76205
76401
|
|
|
76206
76402
|
// gateway/turn-end-gate-backstop.ts
|
|
76207
76403
|
function withTurnEndGateBackstop(key, endingTurn, body, deps) {
|
|
@@ -77149,10 +77345,14 @@ function handleSessionEvent(deps, ev) {
|
|
|
77149
77345
|
ended_via: outboundMetrics.outboundCount > 0 ? "reply" : "silent"
|
|
77150
77346
|
});
|
|
77151
77347
|
if (turnEndDecision === "reprompt") {
|
|
77348
|
+
const gatewayCapturedEmpty = turn.capturedText.join(`
|
|
77349
|
+
|
|
77350
|
+
`).trim().length === 0;
|
|
77351
|
+
const proseMinChars = !turn.replyCalled && gatewayCapturedEmpty ? 1 : CAPTURED_PROSE_MIN_CHARS;
|
|
77152
77352
|
const proseDecision = CAPTURED_PROSE_DELIVERY_ENABLED ? decideCapturedProseDelivery({
|
|
77153
77353
|
turnKey: tKey,
|
|
77154
77354
|
turnId: turn.turnId,
|
|
77155
|
-
minChars:
|
|
77355
|
+
minChars: proseMinChars
|
|
77156
77356
|
}) : { deliver: false, reason: "no-state" };
|
|
77157
77357
|
if (proseDecision.deliver && proseDecision.text != null) {
|
|
77158
77358
|
process.stderr.write(`telegram gateway: captured-prose delivery engaged on first silent-end chat=${chatId} turnKey=${tKey} (#3227)
|
|
@@ -77950,8 +78150,41 @@ function latestEndedAccepted(candidates) {
|
|
|
77950
78150
|
return true;
|
|
77951
78151
|
return age <= ttl;
|
|
77952
78152
|
}
|
|
78153
|
+
function resolveReplyOwnerTier(candidates) {
|
|
78154
|
+
if (candidates.liveTurnId != null)
|
|
78155
|
+
return "live";
|
|
78156
|
+
if (candidates.originTurnId != null)
|
|
78157
|
+
return "origin";
|
|
78158
|
+
if (candidates.quotedTurnId != null)
|
|
78159
|
+
return "quoted";
|
|
78160
|
+
if (latestEndedAccepted(candidates))
|
|
78161
|
+
return "latest-ended";
|
|
78162
|
+
return "none";
|
|
78163
|
+
}
|
|
77953
78164
|
function resolveReplyOwnerTurnId(candidates) {
|
|
77954
|
-
|
|
78165
|
+
switch (resolveReplyOwnerTier(candidates)) {
|
|
78166
|
+
case "live":
|
|
78167
|
+
return candidates.liveTurnId;
|
|
78168
|
+
case "origin":
|
|
78169
|
+
return candidates.originTurnId;
|
|
78170
|
+
case "quoted":
|
|
78171
|
+
return candidates.quotedTurnId;
|
|
78172
|
+
case "latest-ended":
|
|
78173
|
+
return candidates.latestEndedTurnId;
|
|
78174
|
+
case "none":
|
|
78175
|
+
return null;
|
|
78176
|
+
}
|
|
78177
|
+
}
|
|
78178
|
+
|
|
78179
|
+
// gateway/subagent-handback-marker.ts
|
|
78180
|
+
class SubagentHandbackMarker {
|
|
78181
|
+
lastAtByChat = new Map;
|
|
78182
|
+
record(chatId, now) {
|
|
78183
|
+
this.lastAtByChat.set(chatId, now);
|
|
78184
|
+
}
|
|
78185
|
+
lastAt(chatId) {
|
|
78186
|
+
return this.lastAtByChat.get(chatId) ?? null;
|
|
78187
|
+
}
|
|
77955
78188
|
}
|
|
77956
78189
|
|
|
77957
78190
|
// answer-ready-flush.ts
|
|
@@ -78555,7 +78788,7 @@ async function loadFromAuthBroker(options = {}) {
|
|
|
78555
78788
|
}
|
|
78556
78789
|
|
|
78557
78790
|
// gateway/folder-picker-handler.ts
|
|
78558
|
-
var
|
|
78791
|
+
var import_grammy11 = __toESM(require_mod2(), 1);
|
|
78559
78792
|
|
|
78560
78793
|
// ../src/drive/folder-picker.ts
|
|
78561
78794
|
var DRIVE_ID_RE2 = /^[A-Za-z0-9_-]+$/;
|
|
@@ -78874,7 +79107,7 @@ async function recordGrantAndConfirm(ctx, deps, parsed) {
|
|
|
78874
79107
|
const confirmText = `\u2705 Granted ${deps.agentName} access to folder ${parsed.folder_id}
|
|
78875
79108
|
` + `Scope: \`${scope}\`
|
|
78876
79109
|
` + `Revoke with: \`/approvals revoke ${decisionId}\``;
|
|
78877
|
-
const kb = new
|
|
79110
|
+
const kb = new import_grammy11.InlineKeyboard().url("\uD83D\uDCD6 Open in Drive", `https://drive.google.com/drive/folders/${parsed.folder_id}`);
|
|
78878
79111
|
try {
|
|
78879
79112
|
await ctx.editMessageText({ markdown: confirmText }, {
|
|
78880
79113
|
reply_markup: kb
|
|
@@ -78883,7 +79116,7 @@ async function recordGrantAndConfirm(ctx, deps, parsed) {
|
|
|
78883
79116
|
await ctx.answerCallbackQuery({ text: "Allowed" });
|
|
78884
79117
|
}
|
|
78885
79118
|
function toKeyboard(spec) {
|
|
78886
|
-
const kb = new
|
|
79119
|
+
const kb = new import_grammy11.InlineKeyboard;
|
|
78887
79120
|
for (let r = 0;r < spec.rows.length; r++) {
|
|
78888
79121
|
const row = spec.rows[r];
|
|
78889
79122
|
for (const btn of row) {
|
|
@@ -80346,6 +80579,9 @@ function writeSessionModelFile(agentDir, model, configuredDefaultAtWrite) {
|
|
|
80346
80579
|
if (!isValidModelArg(model)) {
|
|
80347
80580
|
throw new Error(`refusing to persist non-canonical session model token: ${JSON.stringify(model)}`);
|
|
80348
80581
|
}
|
|
80582
|
+
try {
|
|
80583
|
+
rmSync4(join35(agentDir, SESSION_MODEL_BOOT_ATTEMPTS_FILE), { force: true });
|
|
80584
|
+
} catch {}
|
|
80349
80585
|
atomicWrite(join35(agentDir, SESSION_MODEL_FILE), serializeSessionModel({ model, configuredDefaultAtWrite, ts: Date.now() }));
|
|
80350
80586
|
}
|
|
80351
80587
|
function readSessionModelFileRaw(agentDir) {
|
|
@@ -81151,20 +81387,20 @@ function registerOpsInfoCommands(bot, deps) {
|
|
|
81151
81387
|
isAuthorizedSender,
|
|
81152
81388
|
getMyAgentName,
|
|
81153
81389
|
switchroomReply,
|
|
81154
|
-
buildDoctorScopeKeyboard,
|
|
81390
|
+
buildDoctorScopeKeyboard: buildDoctorScopeKeyboard2,
|
|
81155
81391
|
renderSelfDoctor,
|
|
81156
|
-
preBlock,
|
|
81157
|
-
formatSwitchroomOutput,
|
|
81158
|
-
getCommandArgs,
|
|
81159
|
-
assertSafeAgentName,
|
|
81392
|
+
preBlock: preBlock2,
|
|
81393
|
+
formatSwitchroomOutput: formatSwitchroomOutput2,
|
|
81394
|
+
getCommandArgs: getCommandArgs2,
|
|
81395
|
+
assertSafeAgentName: assertSafeAgentName2,
|
|
81160
81396
|
runSwitchroomCommand,
|
|
81161
81397
|
switchroomExecCombined,
|
|
81162
|
-
stripAnsi:
|
|
81163
|
-
hasDemoFlag,
|
|
81164
|
-
escapeHtmlForTg:
|
|
81398
|
+
stripAnsi: stripAnsi5,
|
|
81399
|
+
hasDemoFlag: hasDemoFlag2,
|
|
81400
|
+
escapeHtmlForTg: escapeHtmlForTg3
|
|
81165
81401
|
} = deps;
|
|
81166
81402
|
function formatWhoamiCard(v, demo = false) {
|
|
81167
|
-
const esc =
|
|
81403
|
+
const esc = escapeHtmlForTg3;
|
|
81168
81404
|
const yn = (b) => b ? "\u2713" : "\u2717";
|
|
81169
81405
|
const lines = [];
|
|
81170
81406
|
lines.push(`\uD83D\uDC64 **${esc(v.name ?? "?")}** \u00b7 ${esc(v.tier ?? "standard")}`);
|
|
@@ -81196,20 +81432,20 @@ function registerOpsInfoCommands(bot, deps) {
|
|
|
81196
81432
|
if (AGENT_ADMIN && hostdWillBeUsed(getMyAgentName())) {
|
|
81197
81433
|
await switchroomReply(ctx, "\uD83E\uDE7A **Doctor** \u2014 which scope?", {
|
|
81198
81434
|
html: true,
|
|
81199
|
-
reply_markup:
|
|
81435
|
+
reply_markup: buildDoctorScopeKeyboard2()
|
|
81200
81436
|
});
|
|
81201
81437
|
return;
|
|
81202
81438
|
}
|
|
81203
81439
|
await renderSelfDoctor(ctx);
|
|
81204
81440
|
} catch (err) {
|
|
81205
81441
|
await switchroomReply(ctx, `**doctor failed:**
|
|
81206
|
-
${
|
|
81442
|
+
${preBlock2(formatSwitchroomOutput2(err.message ?? "unknown error"))}`, { html: true });
|
|
81207
81443
|
}
|
|
81208
81444
|
});
|
|
81209
81445
|
bot.command("grant", async (ctx) => {
|
|
81210
81446
|
if (!isAuthorizedSender(ctx))
|
|
81211
81447
|
return;
|
|
81212
|
-
const parts =
|
|
81448
|
+
const parts = getCommandArgs2(ctx).split(/\s+/).filter(Boolean);
|
|
81213
81449
|
if (parts.length === 0) {
|
|
81214
81450
|
await switchroomReply(ctx, "Usage: /grant <tool> or /grant <agent> <tool>");
|
|
81215
81451
|
return;
|
|
@@ -81224,7 +81460,7 @@ ${preBlock(formatSwitchroomOutput(err.message ?? "unknown error"))}`, { html: tr
|
|
|
81224
81460
|
tool = parts.slice(1).join(" ");
|
|
81225
81461
|
}
|
|
81226
81462
|
try {
|
|
81227
|
-
|
|
81463
|
+
assertSafeAgentName2(agentName3);
|
|
81228
81464
|
} catch {
|
|
81229
81465
|
await switchroomReply(ctx, "Invalid agent name.");
|
|
81230
81466
|
return;
|
|
@@ -81234,7 +81470,7 @@ ${preBlock(formatSwitchroomOutput(err.message ?? "unknown error"))}`, { html: tr
|
|
|
81234
81470
|
bot.command("dangerous", async (ctx) => {
|
|
81235
81471
|
if (!isAuthorizedSender(ctx))
|
|
81236
81472
|
return;
|
|
81237
|
-
const parts =
|
|
81473
|
+
const parts = getCommandArgs2(ctx).split(/\s+/).filter(Boolean);
|
|
81238
81474
|
let agentName3;
|
|
81239
81475
|
let off = false;
|
|
81240
81476
|
if (parts.length === 0) {
|
|
@@ -81248,7 +81484,7 @@ ${preBlock(formatSwitchroomOutput(err.message ?? "unknown error"))}`, { html: tr
|
|
|
81248
81484
|
off = true;
|
|
81249
81485
|
}
|
|
81250
81486
|
try {
|
|
81251
|
-
|
|
81487
|
+
assertSafeAgentName2(agentName3);
|
|
81252
81488
|
} catch {
|
|
81253
81489
|
await switchroomReply(ctx, "Invalid agent name.");
|
|
81254
81490
|
return;
|
|
@@ -81263,7 +81499,7 @@ ${preBlock(formatSwitchroomOutput(err.message ?? "unknown error"))}`, { html: tr
|
|
|
81263
81499
|
return;
|
|
81264
81500
|
const agentName3 = (typeof ctx.match === "string" ? ctx.match : "").trim() || getMyAgentName();
|
|
81265
81501
|
try {
|
|
81266
|
-
|
|
81502
|
+
assertSafeAgentName2(agentName3);
|
|
81267
81503
|
} catch {
|
|
81268
81504
|
await switchroomReply(ctx, "Invalid agent name.");
|
|
81269
81505
|
return;
|
|
@@ -81280,21 +81516,21 @@ ${preBlock(formatSwitchroomOutput(err.message ?? "unknown error"))}`, { html: tr
|
|
|
81280
81516
|
} catch (err) {
|
|
81281
81517
|
output = err.stdout ?? err.message ?? "version failed";
|
|
81282
81518
|
}
|
|
81283
|
-
const trimmed =
|
|
81519
|
+
const trimmed = stripAnsi5(output).trim();
|
|
81284
81520
|
if (!trimmed) {
|
|
81285
81521
|
await switchroomReply(ctx, "version: no output");
|
|
81286
81522
|
return;
|
|
81287
81523
|
}
|
|
81288
|
-
await switchroomReply(ctx,
|
|
81524
|
+
await switchroomReply(ctx, preBlock2(formatSwitchroomOutput2(trimmed)), { html: true });
|
|
81289
81525
|
} catch (err) {
|
|
81290
81526
|
await switchroomReply(ctx, `**version failed:**
|
|
81291
|
-
${
|
|
81527
|
+
${preBlock2(formatSwitchroomOutput2(err.message ?? "unknown error"))}`, { html: true });
|
|
81292
81528
|
}
|
|
81293
81529
|
});
|
|
81294
81530
|
bot.command("whoami", async (ctx) => {
|
|
81295
81531
|
if (!isAuthorizedSender(ctx))
|
|
81296
81532
|
return;
|
|
81297
|
-
const demo =
|
|
81533
|
+
const demo = hasDemoFlag2(getCommandArgs2(ctx));
|
|
81298
81534
|
try {
|
|
81299
81535
|
let raw;
|
|
81300
81536
|
try {
|
|
@@ -81302,18 +81538,18 @@ ${preBlock(formatSwitchroomOutput(err.message ?? "unknown error"))}`, { html: tr
|
|
|
81302
81538
|
} catch (err) {
|
|
81303
81539
|
raw = err.stdout ?? err.message ?? "whoami failed";
|
|
81304
81540
|
}
|
|
81305
|
-
const trimmed =
|
|
81541
|
+
const trimmed = stripAnsi5(raw).trim();
|
|
81306
81542
|
let card;
|
|
81307
81543
|
try {
|
|
81308
81544
|
card = formatWhoamiCard(JSON.parse(trimmed.split(`
|
|
81309
81545
|
`).pop() ?? trimmed), demo);
|
|
81310
81546
|
} catch {
|
|
81311
|
-
card =
|
|
81547
|
+
card = preBlock2(formatSwitchroomOutput2(trimmed || "whoami: no output"));
|
|
81312
81548
|
}
|
|
81313
81549
|
await switchroomReply(ctx, card, { html: true });
|
|
81314
81550
|
} catch (err) {
|
|
81315
81551
|
await switchroomReply(ctx, `**whoami failed:**
|
|
81316
|
-
${
|
|
81552
|
+
${preBlock2(formatSwitchroomOutput2(err.message ?? "unknown error"))}`, { html: true });
|
|
81317
81553
|
}
|
|
81318
81554
|
});
|
|
81319
81555
|
bot.command("commands", async (ctx) => {
|
|
@@ -84359,7 +84595,7 @@ async function handleRequestMs365Approval(client3, msg, deps) {
|
|
|
84359
84595
|
|
|
84360
84596
|
// gateway/diff-preview-card.ts
|
|
84361
84597
|
init_format();
|
|
84362
|
-
var
|
|
84598
|
+
var import_grammy12 = __toESM(require_mod2(), 1);
|
|
84363
84599
|
var REQUEST_ID_RE = /^[0-9a-f]{32}$/;
|
|
84364
84600
|
var PENDING_FILE_ID_SENTINEL = "pending-create";
|
|
84365
84601
|
function buildDiffPreviewCard(input) {
|
|
@@ -84377,7 +84613,7 @@ function buildDiffPreviewCard(input) {
|
|
|
84377
84613
|
}
|
|
84378
84614
|
const text4 = bodyLines.join(`
|
|
84379
84615
|
`);
|
|
84380
|
-
const kb = new
|
|
84616
|
+
const kb = new import_grammy12.InlineKeyboard;
|
|
84381
84617
|
const ROW_BREAK_AFTER = [
|
|
84382
84618
|
"apply_suggestion"
|
|
84383
84619
|
];
|
|
@@ -84541,6 +84777,11 @@ function createPendingInboundBuffer(opts = {}) {
|
|
|
84541
84777
|
}
|
|
84542
84778
|
}
|
|
84543
84779
|
q.push(msg);
|
|
84780
|
+
if (msg.meta?.source === "subagent_handback" && opts.onHandbackEnqueue != null) {
|
|
84781
|
+
try {
|
|
84782
|
+
opts.onHandbackEnqueue(msg.chatId, msg.ts);
|
|
84783
|
+
} catch {}
|
|
84784
|
+
}
|
|
84544
84785
|
spool?.put(agent, msg);
|
|
84545
84786
|
log(`pending-inbound-buffer: agent=${agent} buffered source=${msg.meta?.source ?? "-"} ` + `depth_after=${q.length} evicted=${evicted}
|
|
84546
84787
|
`);
|
|
@@ -87221,6 +87462,49 @@ function purgeStaleTurnsForChat(chatId, keys, purger, isStale = () => true) {
|
|
|
87221
87462
|
return { purged };
|
|
87222
87463
|
}
|
|
87223
87464
|
|
|
87465
|
+
// gateway/hang-restart-decision.ts
|
|
87466
|
+
var HUMAN_WAIT_TOOLS2 = new Set(["ask_user"]);
|
|
87467
|
+
var BACKGROUND_TOOLS2 = new Set(["Task", "Agent"]);
|
|
87468
|
+
function classifyToolClass(toolName, opts = {}) {
|
|
87469
|
+
if (opts.awaitingApproval === true)
|
|
87470
|
+
return "human";
|
|
87471
|
+
if (HUMAN_WAIT_TOOLS2.has(toolName))
|
|
87472
|
+
return "human";
|
|
87473
|
+
if (BACKGROUND_TOOLS2.has(toolName))
|
|
87474
|
+
return "background";
|
|
87475
|
+
if (toolName === "Bash" && opts.backgroundBash === true)
|
|
87476
|
+
return "background";
|
|
87477
|
+
return "standard";
|
|
87478
|
+
}
|
|
87479
|
+
var HANG_PROTECTED_EXACT2 = new Set([
|
|
87480
|
+
"Task",
|
|
87481
|
+
"Agent",
|
|
87482
|
+
"Bash",
|
|
87483
|
+
"WebFetch",
|
|
87484
|
+
"WebSearch"
|
|
87485
|
+
]);
|
|
87486
|
+
var HANG_PROTECTED_SUBSTRINGS = ["research", "perplexity", "crawl", "deep_research", "webkite"];
|
|
87487
|
+
function isHangRestartProtectedTool(toolName) {
|
|
87488
|
+
if (HANG_PROTECTED_EXACT2.has(toolName))
|
|
87489
|
+
return true;
|
|
87490
|
+
if (classifyToolClass(toolName) !== "standard")
|
|
87491
|
+
return true;
|
|
87492
|
+
const lower = toolName.toLowerCase();
|
|
87493
|
+
return HANG_PROTECTED_SUBSTRINGS.some((s) => lower.includes(s));
|
|
87494
|
+
}
|
|
87495
|
+
function decideHangRestart(input) {
|
|
87496
|
+
if (input.inFlightToolNames.length === 0)
|
|
87497
|
+
return { restart: false, reason: "not-mid-tool" };
|
|
87498
|
+
const protectedName = input.inFlightToolNames.find(isHangRestartProtectedTool);
|
|
87499
|
+
if (protectedName != null) {
|
|
87500
|
+
return { restart: false, reason: `protected-long-tool:${protectedName}` };
|
|
87501
|
+
}
|
|
87502
|
+
if (input.markerAgeMs != null && input.markerAgeMs < input.stalenessThresholdMs) {
|
|
87503
|
+
return { restart: false, reason: "marker-advancing" };
|
|
87504
|
+
}
|
|
87505
|
+
return { restart: true, reason: "mid-tool-marker-stale" };
|
|
87506
|
+
}
|
|
87507
|
+
|
|
87224
87508
|
// gateway/update-status-line.ts
|
|
87225
87509
|
function latestHostdPhase(tail) {
|
|
87226
87510
|
if (!tail)
|
|
@@ -87276,7 +87560,8 @@ function buildSilencePokeOptions(deps) {
|
|
|
87276
87560
|
getPendingInboundBuffer,
|
|
87277
87561
|
trackRedeliveredInbound,
|
|
87278
87562
|
closeActivityLane,
|
|
87279
|
-
closeProgressLane
|
|
87563
|
+
closeProgressLane,
|
|
87564
|
+
hangRestart
|
|
87280
87565
|
} = deps;
|
|
87281
87566
|
return {
|
|
87282
87567
|
thresholdsMs: { fallback: SILENCE_FALLBACK_MS, fallbackHardCeiling: SILENCE_FALLBACK_HARD_MS, floor: SILENCE_FLOOR_MS },
|
|
@@ -87324,6 +87609,21 @@ function buildSilencePokeOptions(deps) {
|
|
|
87324
87609
|
endTurn2(ctx.key);
|
|
87325
87610
|
return;
|
|
87326
87611
|
}
|
|
87612
|
+
if (hangRestart != null && ctx.inFlightTools.length > 0) {
|
|
87613
|
+
const markerAgeMs = readTurnActiveMarkerAgeMs(STATE_DIR);
|
|
87614
|
+
const inFlightToolNames = ctx.inFlightTools.map((t) => t.name);
|
|
87615
|
+
const decision = decideHangRestart({
|
|
87616
|
+
inFlightToolNames,
|
|
87617
|
+
markerAgeMs,
|
|
87618
|
+
stalenessThresholdMs: hangRestart.stalenessThresholdMs
|
|
87619
|
+
});
|
|
87620
|
+
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}
|
|
87621
|
+
`);
|
|
87622
|
+
if (decision.restart) {
|
|
87623
|
+
hangRestart.request(decision.reason, markerAgeMs ?? ctx.silenceMs);
|
|
87624
|
+
return;
|
|
87625
|
+
}
|
|
87626
|
+
}
|
|
87327
87627
|
let text4 = null;
|
|
87328
87628
|
const blockedOnApproval = activeStatusReactions.get(statusKey(ctx.chatId, ctx.threadId))?.isAwaiting() ?? false;
|
|
87329
87629
|
const upd = getInFlightUpdate();
|
|
@@ -93121,7 +93421,7 @@ function sweepStaleTurnActiveMarker(stateDir, opts) {
|
|
|
93121
93421
|
return false;
|
|
93122
93422
|
}
|
|
93123
93423
|
}
|
|
93124
|
-
function
|
|
93424
|
+
function readTurnActiveMarkerAgeMs2(stateDir, now) {
|
|
93125
93425
|
const path2 = join56(stateDir, TURN_ACTIVE_MARKER_FILE2);
|
|
93126
93426
|
try {
|
|
93127
93427
|
const st = statSync16(path2);
|
|
@@ -93134,11 +93434,36 @@ function effectiveTurnAgeMs(markerAgeMs, turnStartedAt, now) {
|
|
|
93134
93434
|
return markerAgeMs ?? now - turnStartedAt;
|
|
93135
93435
|
}
|
|
93136
93436
|
|
|
93437
|
+
// gateway/gateway-heartbeat.ts
|
|
93438
|
+
import { mkdirSync as mkdirSync44, utimesSync as utimesSync3, writeFileSync as writeFileSync45 } from "node:fs";
|
|
93439
|
+
import { join as join57 } from "node:path";
|
|
93440
|
+
var GATEWAY_HEARTBEAT_FILE = "gateway-heartbeat";
|
|
93441
|
+
var GATEWAY_HEARTBEAT_INTERVAL_MS = 15000;
|
|
93442
|
+
function touchGatewayHeartbeat(stateDir) {
|
|
93443
|
+
const path2 = join57(stateDir, GATEWAY_HEARTBEAT_FILE);
|
|
93444
|
+
const now = new Date;
|
|
93445
|
+
try {
|
|
93446
|
+
utimesSync3(path2, now, now);
|
|
93447
|
+
} catch {
|
|
93448
|
+
try {
|
|
93449
|
+
mkdirSync44(stateDir, { recursive: true });
|
|
93450
|
+
writeFileSync45(path2, `${Date.now()}
|
|
93451
|
+
`, { mode: 384 });
|
|
93452
|
+
} catch {}
|
|
93453
|
+
}
|
|
93454
|
+
}
|
|
93455
|
+
function startGatewayHeartbeat(stateDir, intervalMs = GATEWAY_HEARTBEAT_INTERVAL_MS) {
|
|
93456
|
+
touchGatewayHeartbeat(stateDir);
|
|
93457
|
+
const timer3 = setInterval(() => touchGatewayHeartbeat(stateDir), intervalMs);
|
|
93458
|
+
timer3.unref?.();
|
|
93459
|
+
return timer3;
|
|
93460
|
+
}
|
|
93461
|
+
|
|
93137
93462
|
// ../src/build-info.ts
|
|
93138
|
-
var VERSION = "0.19.
|
|
93139
|
-
var COMMIT_SHA = "
|
|
93140
|
-
var COMMIT_DATE = "2026-07-
|
|
93141
|
-
var LATEST_PR =
|
|
93463
|
+
var VERSION = "0.19.6";
|
|
93464
|
+
var COMMIT_SHA = "ae57dd83";
|
|
93465
|
+
var COMMIT_DATE = "2026-07-20T16:11:50Z";
|
|
93466
|
+
var LATEST_PR = 3476;
|
|
93142
93467
|
var COMMITS_AHEAD_OF_TAG = 0;
|
|
93143
93468
|
|
|
93144
93469
|
// gateway/boot-version.ts
|
|
@@ -93183,10 +93508,10 @@ function composeBootVersionString(inputs) {
|
|
|
93183
93508
|
}
|
|
93184
93509
|
|
|
93185
93510
|
// gateway/unhandled-rejection-policy.ts
|
|
93186
|
-
var
|
|
93511
|
+
var import_grammy13 = __toESM(require_mod2(), 1);
|
|
93187
93512
|
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
|
|
93513
|
+
const isGrammy = opts.isGrammyError != null ? opts.isGrammyError(err) : err instanceof import_grammy13.GrammyError;
|
|
93514
|
+
const isHttp = opts.isHttpError != null ? opts.isHttpError(err) : err instanceof import_grammy13.HttpError;
|
|
93190
93515
|
if (isHttp)
|
|
93191
93516
|
return "log_only";
|
|
93192
93517
|
if (err instanceof Error && err.message === FLOOD_WAIT_ACTIVE)
|
|
@@ -93216,11 +93541,11 @@ init_peercred();
|
|
|
93216
93541
|
import * as net5 from "node:net";
|
|
93217
93542
|
import * as fs2 from "node:fs";
|
|
93218
93543
|
import { homedir as homedir18 } from "node:os";
|
|
93219
|
-
import { join as
|
|
93544
|
+
import { join as join58 } from "node:path";
|
|
93220
93545
|
var DEFAULT_TIMEOUT_MS4 = 2000;
|
|
93221
93546
|
var UNLOCK_TIMEOUT_MS = 30000;
|
|
93222
|
-
var LEGACY_SOCKET_PATH2 =
|
|
93223
|
-
var OPERATOR_SOCKET_PATH2 =
|
|
93547
|
+
var LEGACY_SOCKET_PATH2 = join58(homedir18(), ".switchroom", "vault-broker.sock");
|
|
93548
|
+
var OPERATOR_SOCKET_PATH2 = join58(homedir18(), ".switchroom", "broker-operator", "sock");
|
|
93224
93549
|
function defaultBrokerSocketPath2() {
|
|
93225
93550
|
if (fs2.existsSync(OPERATOR_SOCKET_PATH2))
|
|
93226
93551
|
return OPERATOR_SOCKET_PATH2;
|
|
@@ -94102,8 +94427,8 @@ function resolveVaultApprovalPosture(broker) {
|
|
|
94102
94427
|
}
|
|
94103
94428
|
|
|
94104
94429
|
// registry/turns-schema.ts
|
|
94105
|
-
import { chmodSync as chmodSync12, mkdirSync as
|
|
94106
|
-
import { join as
|
|
94430
|
+
import { chmodSync as chmodSync12, mkdirSync as mkdirSync45 } from "fs";
|
|
94431
|
+
import { join as join59 } from "path";
|
|
94107
94432
|
var DatabaseClass3 = null;
|
|
94108
94433
|
function loadDatabaseClass3() {
|
|
94109
94434
|
if (DatabaseClass3 != null)
|
|
@@ -94175,9 +94500,9 @@ function applySchema(db3) {
|
|
|
94175
94500
|
}
|
|
94176
94501
|
function openTurnsDb(agentDir) {
|
|
94177
94502
|
const Database = loadDatabaseClass3();
|
|
94178
|
-
const dir =
|
|
94179
|
-
|
|
94180
|
-
const path2 =
|
|
94503
|
+
const dir = join59(agentDir, "telegram");
|
|
94504
|
+
mkdirSync45(dir, { recursive: true, mode: 448 });
|
|
94505
|
+
const path2 = join59(dir, "registry.db");
|
|
94181
94506
|
const db3 = new Database(path2, { create: true });
|
|
94182
94507
|
applySchema(db3);
|
|
94183
94508
|
try {
|
|
@@ -94521,7 +94846,7 @@ function selectResumeBuilder(endedVia, opts) {
|
|
|
94521
94846
|
}
|
|
94522
94847
|
|
|
94523
94848
|
// gateway/bridge-dead-watchdog.ts
|
|
94524
|
-
import { readFileSync as readFileSync56, writeFileSync as
|
|
94849
|
+
import { readFileSync as readFileSync56, writeFileSync as writeFileSync46, renameSync as renameSync20, unlinkSync as unlinkSync24 } from "node:fs";
|
|
94525
94850
|
|
|
94526
94851
|
// gateway/cron-session.ts
|
|
94527
94852
|
var CRON_IDENTITY_SUFFIX2 = "-cron";
|
|
@@ -94575,7 +94900,7 @@ function readFreshCrashLogTail(path2, opts = {}) {
|
|
|
94575
94900
|
}
|
|
94576
94901
|
function writeBridgeDeadEscalationMarker(path2, marker) {
|
|
94577
94902
|
const tmp = `${path2}.tmp-${process.pid}-${Date.now()}`;
|
|
94578
|
-
|
|
94903
|
+
writeFileSync46(tmp, JSON.stringify(marker), "utf8");
|
|
94579
94904
|
renameSync20(tmp, path2);
|
|
94580
94905
|
}
|
|
94581
94906
|
function consumeBridgeDeadEscalationMarker(path2, nowMs3 = Date.now(), maxAgeMs = ESCALATION_MARKER_MAX_AGE_MS) {
|
|
@@ -94900,6 +95225,46 @@ function handleWorkerResume(feed, agentId, log) {
|
|
|
94900
95225
|
}
|
|
94901
95226
|
log(`telegram gateway: worker ${agentId} card RE-SURFACED \u2014 resumed via SendMessage after a genuine terminal (issue #3373)`);
|
|
94902
95227
|
}
|
|
95228
|
+
function decideWorkerFeedOriginDefer(input) {
|
|
95229
|
+
const { originResolved, cardExists, priorDeferrals, maxDeferrals } = input;
|
|
95230
|
+
if (originResolved || cardExists)
|
|
95231
|
+
return { defer: false, deferrals: 0 };
|
|
95232
|
+
const deferrals = priorDeferrals + 1;
|
|
95233
|
+
if (deferrals >= maxDeferrals)
|
|
95234
|
+
return { defer: false, deferrals };
|
|
95235
|
+
return { defer: true, deferrals };
|
|
95236
|
+
}
|
|
95237
|
+
function decideWorkerFeedDestination(input) {
|
|
95238
|
+
const { origin, cardExists, priorDeferrals, maxDeferrals, fleetChatId, stampChatId, stampThreadId, ownerDm } = input;
|
|
95239
|
+
const originResolved = origin != null;
|
|
95240
|
+
const { defer, deferrals } = decideWorkerFeedOriginDefer({
|
|
95241
|
+
originResolved,
|
|
95242
|
+
cardExists,
|
|
95243
|
+
priorDeferrals,
|
|
95244
|
+
maxDeferrals
|
|
95245
|
+
});
|
|
95246
|
+
if (defer)
|
|
95247
|
+
return { action: "defer", deferrals };
|
|
95248
|
+
const exhausted = !originResolved && !cardExists;
|
|
95249
|
+
const usingStampFallback = fleetChatId.length === 0;
|
|
95250
|
+
const workerFleetChatId = usingStampFallback ? stampChatId ?? fleetChatId : fleetChatId;
|
|
95251
|
+
const fallbackThreadId = usingStampFallback ? stampThreadId : undefined;
|
|
95252
|
+
if (origin != null && origin.chatId.length > 0) {
|
|
95253
|
+
return { action: "paint", chatId: origin.chatId, threadId: origin.threadId, deferrals, exhausted, ownerDmFallback: false };
|
|
95254
|
+
}
|
|
95255
|
+
if (workerFleetChatId.length > 0) {
|
|
95256
|
+
return { action: "paint", chatId: workerFleetChatId, threadId: fallbackThreadId, deferrals, exhausted, ownerDmFallback: false };
|
|
95257
|
+
}
|
|
95258
|
+
const ownerDmFallback = origin == null && workerFleetChatId.length === 0 && ownerDm.length > 0;
|
|
95259
|
+
return {
|
|
95260
|
+
action: "paint",
|
|
95261
|
+
chatId: ownerDm,
|
|
95262
|
+
threadId: origin?.threadId ?? fallbackThreadId,
|
|
95263
|
+
deferrals,
|
|
95264
|
+
exhausted,
|
|
95265
|
+
ownerDmFallback
|
|
95266
|
+
};
|
|
95267
|
+
}
|
|
94903
95268
|
function resolveWorkerFeedDispatch(sub, watcherDescription, entryBackground) {
|
|
94904
95269
|
return {
|
|
94905
95270
|
isBackground: sub?.background ?? entryBackground ?? false,
|
|
@@ -94952,7 +95317,7 @@ if (isGatewayMain) {
|
|
|
94952
95317
|
shutdownAnalytics();
|
|
94953
95318
|
});
|
|
94954
95319
|
}
|
|
94955
|
-
var STATE_DIR = process.env.TELEGRAM_STATE_DIR ??
|
|
95320
|
+
var STATE_DIR = process.env.TELEGRAM_STATE_DIR ?? join60(homedir19(), ".claude", "channels", "telegram");
|
|
94956
95321
|
var permCardStore = createPermissionCardStore(STATE_DIR);
|
|
94957
95322
|
var BLOCKED_APPROVALS_DIR = process.env.SWITCHROOM_BLOCKED_APPROVALS_DIR ?? "/state/blocked-approvals";
|
|
94958
95323
|
var AGENT_NAME = process.env.SWITCHROOM_AGENT_NAME ?? "agent";
|
|
@@ -95052,11 +95417,11 @@ function scheduleAlwaysAllowPersistDrain() {
|
|
|
95052
95417
|
}, ALWAYS_ALLOW_DRAIN_INTERVAL_MS);
|
|
95053
95418
|
timer3.unref?.();
|
|
95054
95419
|
}
|
|
95055
|
-
var ACCESS_FILE =
|
|
95056
|
-
var APPROVED_DIR =
|
|
95057
|
-
var ENV_FILE =
|
|
95058
|
-
var INBOX_DIR =
|
|
95059
|
-
var PEOPLE_FILE =
|
|
95420
|
+
var ACCESS_FILE = join60(STATE_DIR, "access.json");
|
|
95421
|
+
var APPROVED_DIR = join60(STATE_DIR, "approved");
|
|
95422
|
+
var ENV_FILE = join60(STATE_DIR, ".env");
|
|
95423
|
+
var INBOX_DIR = join60(STATE_DIR, "inbox");
|
|
95424
|
+
var PEOPLE_FILE = join60(STATE_DIR, "people.json");
|
|
95060
95425
|
function triggerSelfRestart(targetAgent, reason, delayMs = 300) {
|
|
95061
95426
|
const isDocker = process.env.SWITCHROOM_RUNTIME === "docker";
|
|
95062
95427
|
const selfAgent = process.env.SWITCHROOM_AGENT_NAME;
|
|
@@ -95205,7 +95570,7 @@ function assertSendable(f) {
|
|
|
95205
95570
|
} catch {
|
|
95206
95571
|
throw new Error(`refusing to send file \u2014 cannot resolve real path: ${f}`);
|
|
95207
95572
|
}
|
|
95208
|
-
const inbox =
|
|
95573
|
+
const inbox = join60(stateReal, "inbox");
|
|
95209
95574
|
if (real.startsWith(stateReal + sep4) && !real.startsWith(inbox + sep4)) {
|
|
95210
95575
|
throw new Error(`refusing to send channel state: ${f}`);
|
|
95211
95576
|
}
|
|
@@ -95308,9 +95673,9 @@ function assertAllowedChat(chat_id) {
|
|
|
95308
95673
|
function saveAccess(a) {
|
|
95309
95674
|
if (STATIC)
|
|
95310
95675
|
return;
|
|
95311
|
-
|
|
95676
|
+
mkdirSync47(STATE_DIR, { recursive: true, mode: 448 });
|
|
95312
95677
|
const tmp = ACCESS_FILE + ".tmp";
|
|
95313
|
-
|
|
95678
|
+
writeFileSync48(tmp, JSON.stringify(a, null, 2) + `
|
|
95314
95679
|
`, { mode: 384 });
|
|
95315
95680
|
renameSync21(tmp, ACCESS_FILE);
|
|
95316
95681
|
}
|
|
@@ -95330,7 +95695,7 @@ var HISTORY_ENABLED = HISTORY_ACCESS.historyEnabled !== false;
|
|
|
95330
95695
|
if (isGatewayMain && HISTORY_ENABLED) {
|
|
95331
95696
|
try {
|
|
95332
95697
|
initHistory(STATE_DIR, HISTORY_ACCESS.historyRetentionDays ?? 30);
|
|
95333
|
-
process.stderr.write(`telegram gateway: history capture enabled at ${
|
|
95698
|
+
process.stderr.write(`telegram gateway: history capture enabled at ${join60(STATE_DIR, "history.db")}
|
|
95334
95699
|
`);
|
|
95335
95700
|
} catch (err) {
|
|
95336
95701
|
process.stderr.write(`telegram gateway: history init failed (${err.message}) \u2014 capture disabled
|
|
@@ -95349,7 +95714,7 @@ if (isGatewayMain)
|
|
|
95349
95714
|
let markerTurnKey = null;
|
|
95350
95715
|
let markerAgeMs = null;
|
|
95351
95716
|
try {
|
|
95352
|
-
const markerPath =
|
|
95717
|
+
const markerPath = join60(STATE_DIR, TURN_ACTIVE_MARKER_FILE2);
|
|
95353
95718
|
if (existsSync54(markerPath)) {
|
|
95354
95719
|
const st = statSync19(markerPath);
|
|
95355
95720
|
markerAgeMs = Date.now() - st.mtimeMs;
|
|
@@ -95374,10 +95739,10 @@ if (isGatewayMain)
|
|
|
95374
95739
|
process.stderr.write(`telegram gateway: turn-registry boot-reaper stamped ${reaped} orphaned turn(s)` + `${timeoutTurnKey ? ` (turnKey=${timeoutTurnKey} as 'timeout', markerAgeMs=${markerAgeMs})` : " as 'restart'"}
|
|
95375
95740
|
`);
|
|
95376
95741
|
} else {
|
|
95377
|
-
process.stderr.write(`telegram gateway: turn-registry initialized at ${
|
|
95742
|
+
process.stderr.write(`telegram gateway: turn-registry initialized at ${join60(agentDir, "telegram", "registry.db")}
|
|
95378
95743
|
`);
|
|
95379
95744
|
}
|
|
95380
|
-
const bridgeDeadMarker = consumeBridgeDeadEscalationMarker(
|
|
95745
|
+
const bridgeDeadMarker = consumeBridgeDeadEscalationMarker(join60(STATE_DIR, "bridge-dead-escalation.json"));
|
|
95381
95746
|
if (bridgeDeadMarker != null) {
|
|
95382
95747
|
bridgeDeadPriorStreak = bridgeDeadMarker.count ?? 1;
|
|
95383
95748
|
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 +95755,7 @@ if (isGatewayMain)
|
|
|
95390
95755
|
const pending2 = findLatestTurnIfInterrupted(turnsDb);
|
|
95391
95756
|
const selfAgent = process.env.SWITCHROOM_AGENT_NAME ?? "";
|
|
95392
95757
|
if (pending2 != null && selfAgent) {
|
|
95393
|
-
const bootResumeMarkerPath = process.env.SWITCHROOM_GATEWAY_CLEAN_SHUTDOWN_MARKER ??
|
|
95758
|
+
const bootResumeMarkerPath = process.env.SWITCHROOM_GATEWAY_CLEAN_SHUTDOWN_MARKER ?? join60(STATE_DIR, "clean-shutdown.json");
|
|
95394
95759
|
const bootResumeCleanMarker = readCleanShutdownMarker(bootResumeMarkerPath);
|
|
95395
95760
|
const bootResumeForceAlways = process.env.SWITCHROOM_BOOT_RESUME_ALWAYS === "1";
|
|
95396
95761
|
const bootResumeMode = parseBootResumeMode(process.env.SWITCHROOM_BOOT_RESUME);
|
|
@@ -95503,7 +95868,7 @@ if (isGatewayMain)
|
|
|
95503
95868
|
`);
|
|
95504
95869
|
}
|
|
95505
95870
|
}
|
|
95506
|
-
const pendingEnvPath =
|
|
95871
|
+
const pendingEnvPath = join60(agentDir, ".pending-turn.env");
|
|
95507
95872
|
try {
|
|
95508
95873
|
if (pending2 != null) {
|
|
95509
95874
|
const lines = [
|
|
@@ -95517,7 +95882,7 @@ if (isGatewayMain)
|
|
|
95517
95882
|
pending2.interrupt_reason != null ? `SWITCHROOM_PENDING_INTERRUPT_REASON=${pending2.interrupt_reason}` : `SWITCHROOM_PENDING_INTERRUPT_REASON=`
|
|
95518
95883
|
];
|
|
95519
95884
|
const pendingEnvTmp = `${pendingEnvPath}.tmp-${process.pid}`;
|
|
95520
|
-
|
|
95885
|
+
writeFileSync48(pendingEnvTmp, lines.join(`
|
|
95521
95886
|
`) + `
|
|
95522
95887
|
`, { mode: 384 });
|
|
95523
95888
|
renameSync21(pendingEnvTmp, pendingEnvPath);
|
|
@@ -95564,26 +95929,31 @@ var WORKER_FEED_GROUP_MESSAGE_LIFETIME_CAP_MS = (() => {
|
|
|
95564
95929
|
return Number.isFinite(v) && v > 0 ? v : 60 * 60000;
|
|
95565
95930
|
})();
|
|
95566
95931
|
var workerFeedOwnerDmFallbackLogged = new Set;
|
|
95567
|
-
function
|
|
95932
|
+
function noteWorkerFeedOwnerDmFallback(agentId) {
|
|
95933
|
+
if (workerFeedOwnerDmFallbackLogged.has(agentId))
|
|
95934
|
+
return;
|
|
95935
|
+
workerFeedOwnerDmFallbackLogged.add(agentId);
|
|
95936
|
+
if (workerFeedOwnerDmFallbackLogged.size > WORKER_FEED_FALLBACK_LOG_CAP) {
|
|
95937
|
+
const oldest = workerFeedOwnerDmFallbackLogged.values().next().value;
|
|
95938
|
+
if (oldest != null)
|
|
95939
|
+
workerFeedOwnerDmFallbackLogged.delete(oldest);
|
|
95940
|
+
}
|
|
95941
|
+
process.stderr.write(`telegram gateway: worker-feed origin unresolved agent=${agentId} \u2014 routing card to owner DM
|
|
95942
|
+
`);
|
|
95943
|
+
}
|
|
95944
|
+
var workerFeedOriginDeferrals = new Map;
|
|
95945
|
+
var WORKER_FEED_ORIGIN_DEFER_MAX = 10;
|
|
95946
|
+
function resolveWorkerFeedChat(agentId, fleetChatId, fallbackThreadId) {
|
|
95568
95947
|
const origin = resolveSubagentOriginChat(agentId);
|
|
95569
95948
|
if (origin != null && origin.chatId.length > 0)
|
|
95570
95949
|
return origin;
|
|
95571
95950
|
if (fleetChatId.length > 0)
|
|
95572
|
-
return { chatId: fleetChatId };
|
|
95951
|
+
return { chatId: fleetChatId, threadId: fallbackThreadId };
|
|
95573
95952
|
const ownerDm = loadAccess().allowFrom[0] ?? "";
|
|
95574
95953
|
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
|
-
}
|
|
95954
|
+
noteWorkerFeedOwnerDmFallback(agentId);
|
|
95585
95955
|
}
|
|
95586
|
-
return { chatId: ownerDm, threadId: origin?.threadId };
|
|
95956
|
+
return { chatId: ownerDm, threadId: origin?.threadId ?? fallbackThreadId };
|
|
95587
95957
|
}
|
|
95588
95958
|
var REGISTRY_REAPER_INTERVAL_MS = 6 * 60 * 60 * 1000;
|
|
95589
95959
|
function runHistoryReaperNow(reason) {
|
|
@@ -95626,7 +95996,7 @@ function checkApprovals() {
|
|
|
95626
95996
|
return;
|
|
95627
95997
|
}
|
|
95628
95998
|
for (const senderId of files) {
|
|
95629
|
-
const file =
|
|
95999
|
+
const file = join60(APPROVED_DIR, senderId);
|
|
95630
96000
|
bot.api.sendMessage(senderId, "Paired! Say hi to Claude.").then(() => rmSync6(file, { force: true }), (err) => {
|
|
95631
96001
|
process.stderr.write(`telegram gateway: failed to send approval confirm: ${err}
|
|
95632
96002
|
`);
|
|
@@ -95636,6 +96006,8 @@ function checkApprovals() {
|
|
|
95636
96006
|
}
|
|
95637
96007
|
if (isGatewayMain && !STATIC)
|
|
95638
96008
|
setInterval(checkApprovals, 5000).unref();
|
|
96009
|
+
if (isGatewayMain && !STATIC)
|
|
96010
|
+
startGatewayHeartbeat(STATE_DIR);
|
|
95639
96011
|
var IDLE_CLEAR_CHECK_MS = Number(process.env.SWITCHROOM_IDLE_CLEAR_CHECK_MS ?? 60000);
|
|
95640
96012
|
if (isGatewayMain && !STATIC && IDLE_CLEAR_CHECK_MS > 0)
|
|
95641
96013
|
setInterval(maybeIdleClear, IDLE_CLEAR_CHECK_MS).unref();
|
|
@@ -95797,10 +96169,10 @@ function noteAgentOutputAt(key, ts) {
|
|
|
95797
96169
|
lastAgentOutputAt.delete(oldest);
|
|
95798
96170
|
}
|
|
95799
96171
|
}
|
|
95800
|
-
var OBLIGATION_STORE_PATH =
|
|
96172
|
+
var OBLIGATION_STORE_PATH = join60(STATE_DIR, "obligations.json");
|
|
95801
96173
|
var obligationStoreFs = {
|
|
95802
96174
|
readFileSync: (p) => readFileSync58(p, "utf8"),
|
|
95803
|
-
writeFileSync: (p, d) =>
|
|
96175
|
+
writeFileSync: (p, d) => writeFileSync48(p, d),
|
|
95804
96176
|
renameSync: (a, b) => renameSync21(a, b),
|
|
95805
96177
|
existsSync: (p) => existsSync54(p)
|
|
95806
96178
|
};
|
|
@@ -95854,7 +96226,7 @@ function turnInFlightMachineOnly() {
|
|
|
95854
96226
|
function liveTurnAgeMs(now) {
|
|
95855
96227
|
if (currentTurn === null)
|
|
95856
96228
|
return null;
|
|
95857
|
-
return effectiveTurnAgeMs(
|
|
96229
|
+
return effectiveTurnAgeMs(readTurnActiveMarkerAgeMs2(STATE_DIR, now), currentTurn.startedAt, now);
|
|
95858
96230
|
}
|
|
95859
96231
|
function oldestPendingApprovalAgeMs(now) {
|
|
95860
96232
|
let oldest = null;
|
|
@@ -96009,6 +96381,8 @@ function cardDrainGate(turn, ea, run4) {
|
|
|
96009
96381
|
var RECENT_TURNS_MAX = 32;
|
|
96010
96382
|
var recentTurnsById = new Map;
|
|
96011
96383
|
var recentTurnIdBySourceMessageId = new Map;
|
|
96384
|
+
var subagentHandbackMarker = new SubagentHandbackMarker;
|
|
96385
|
+
var getLastSubagentHandbackAt = (chatId) => subagentHandbackMarker.lastAt(chatId);
|
|
96012
96386
|
function rememberRecentTurn(turn) {
|
|
96013
96387
|
recentTurnsById.set(turn.turnId, turn);
|
|
96014
96388
|
if (turn.sourceMessageId != null) {
|
|
@@ -96075,15 +96449,17 @@ function resolveReplyOwnerTurn(liveTurn, chatId, args) {
|
|
|
96075
96449
|
byId.set(t.turnId, t);
|
|
96076
96450
|
}
|
|
96077
96451
|
const latestEndedAgeMs = latestEnded?.endedAt != null ? Date.now() - latestEnded.endedAt : null;
|
|
96078
|
-
const
|
|
96452
|
+
const candidates = {
|
|
96079
96453
|
liveTurnId: liveTurn?.turnId ?? null,
|
|
96080
96454
|
originTurnId: origin?.turnId ?? null,
|
|
96081
96455
|
quotedTurnId: quoted?.turnId ?? null,
|
|
96082
96456
|
latestEndedTurnId: latestEnded?.turnId ?? null,
|
|
96083
96457
|
latestEndedAgeMs,
|
|
96084
96458
|
latestEndedTtlMs: DEFAULT_SUPERSEDE_TTL_MS
|
|
96085
|
-
}
|
|
96086
|
-
|
|
96459
|
+
};
|
|
96460
|
+
const tier = resolveReplyOwnerTier(candidates);
|
|
96461
|
+
const winnerId = resolveReplyOwnerTurnId(candidates);
|
|
96462
|
+
return { turn: winnerId != null ? byId.get(winnerId) ?? null : null, tier };
|
|
96087
96463
|
}
|
|
96088
96464
|
function resolveAnswerThreadWithLog(chatId, explicitThreadId, originTurn, originVia, liveTurn, surface) {
|
|
96089
96465
|
const recovered = LATE_REPLY_TOPIC_RECOVERY_ENABLED && explicitThreadId == null && originTurn == null && liveTurn == null ? findLatestEndedTurnForChat(chatId) : null;
|
|
@@ -97061,7 +97437,7 @@ function wrapBootCardApi(threadId) {
|
|
|
97061
97437
|
await lockedBot.api.editMessageText(cid, mid, richMessage2(text5), editOpts);
|
|
97062
97438
|
return "edited";
|
|
97063
97439
|
} catch (err) {
|
|
97064
|
-
const desc = err instanceof
|
|
97440
|
+
const desc = err instanceof import_grammy15.GrammyError ? err.description : err instanceof Error ? err.message : String(err);
|
|
97065
97441
|
if (typeof desc === "string" && desc.toLowerCase().includes("not modified"))
|
|
97066
97442
|
return "edited";
|
|
97067
97443
|
return "gone";
|
|
@@ -98141,26 +98517,26 @@ var statusPinState = new Map;
|
|
|
98141
98517
|
var statusPinChatIds = new Map;
|
|
98142
98518
|
var statusPinPinnedAt = new Map;
|
|
98143
98519
|
var statusPinRightsCache = new PinRightsCache2;
|
|
98144
|
-
var STATUS_PIN_STORE_PATH =
|
|
98520
|
+
var STATUS_PIN_STORE_PATH = join60(STATE_DIR, "status-pins.json");
|
|
98145
98521
|
var statusPinStoreFs = {
|
|
98146
98522
|
readFileSync: (p) => readFileSync58(p, "utf8"),
|
|
98147
|
-
writeFileSync: (p, d) =>
|
|
98523
|
+
writeFileSync: (p, d) => writeFileSync48(p, d),
|
|
98148
98524
|
renameSync: (a, b) => renameSync21(a, b),
|
|
98149
98525
|
existsSync: (p) => existsSync54(p)
|
|
98150
98526
|
};
|
|
98151
98527
|
var statusPinPersistEnabled = !STATIC && PIN_STATUS_WHILE_WORKING;
|
|
98152
|
-
var ACTIVITY_CARD_STORE_PATH =
|
|
98528
|
+
var ACTIVITY_CARD_STORE_PATH = join60(STATE_DIR, "activity-cards-pending.json");
|
|
98153
98529
|
var activityCardStoreFs = {
|
|
98154
98530
|
readFileSync: (p) => readFileSync58(p, "utf8"),
|
|
98155
|
-
writeFileSync: (p, d) =>
|
|
98531
|
+
writeFileSync: (p, d) => writeFileSync48(p, d),
|
|
98156
98532
|
renameSync: (a, b) => renameSync21(a, b),
|
|
98157
98533
|
existsSync: (p) => existsSync54(p)
|
|
98158
98534
|
};
|
|
98159
98535
|
var activityCardPersistEnabled = !STATIC;
|
|
98160
|
-
var QUEUED_CARD_STORE_PATH =
|
|
98536
|
+
var QUEUED_CARD_STORE_PATH = join60(STATE_DIR, "queued-cards-pending.json");
|
|
98161
98537
|
var queuedCardStoreFs = {
|
|
98162
98538
|
readFileSync: (p) => readFileSync58(p, "utf8"),
|
|
98163
|
-
writeFileSync: (p, d) =>
|
|
98539
|
+
writeFileSync: (p, d) => writeFileSync48(p, d),
|
|
98164
98540
|
renameSync: (a, b) => renameSync21(a, b),
|
|
98165
98541
|
existsSync: (p) => existsSync54(p)
|
|
98166
98542
|
};
|
|
@@ -98535,12 +98911,12 @@ var getPinnedProgressCardMessageId = null;
|
|
|
98535
98911
|
var completeProgressCardTurn = null;
|
|
98536
98912
|
var subagentWatcher = null;
|
|
98537
98913
|
var workerActivityFeed = null;
|
|
98538
|
-
var SOCKET_PATH = process.env.SWITCHROOM_GATEWAY_SOCKET ??
|
|
98914
|
+
var SOCKET_PATH = process.env.SWITCHROOM_GATEWAY_SOCKET ?? join60(STATE_DIR, "gateway.sock");
|
|
98539
98915
|
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 ??
|
|
98916
|
+
mkdirSync47(STATE_DIR, { recursive: true, mode: 448 });
|
|
98917
|
+
var GATEWAY_PID_PATH = process.env.SWITCHROOM_GATEWAY_PID_FILE ?? join60(STATE_DIR, "gateway.pid.json");
|
|
98918
|
+
var GATEWAY_SESSION_MARKER_PATH = process.env.SWITCHROOM_GATEWAY_SESSION_MARKER ?? join60(STATE_DIR, "gateway-session.json");
|
|
98919
|
+
var GATEWAY_CLEAN_SHUTDOWN_MARKER_PATH = process.env.SWITCHROOM_GATEWAY_CLEAN_SHUTDOWN_MARKER ?? join60(STATE_DIR, "clean-shutdown.json");
|
|
98544
98920
|
var GATEWAY_STARTED_AT_MS = Date.now();
|
|
98545
98921
|
var BOOT_CARD_ENABLED = process.env.SWITCHROOM_BOOT_CARD !== "false";
|
|
98546
98922
|
var activeBootCard = null;
|
|
@@ -98569,7 +98945,7 @@ function ensureIssuesCard(chatId, threadId) {
|
|
|
98569
98945
|
bot: botApi,
|
|
98570
98946
|
log: (msg) => process.stderr.write(`telegram gateway: ${msg}
|
|
98571
98947
|
`),
|
|
98572
|
-
persistPath:
|
|
98948
|
+
persistPath: join60(stateDir, "issues-card.json")
|
|
98573
98949
|
});
|
|
98574
98950
|
activeIssuesWatcher = startIssuesWatcher({
|
|
98575
98951
|
stateDir,
|
|
@@ -98688,7 +99064,13 @@ function gatewayLivenessWiringDeps() {
|
|
|
98688
99064
|
getPendingInboundBuffer: () => pendingInboundBuffer,
|
|
98689
99065
|
trackRedeliveredInbound,
|
|
98690
99066
|
closeActivityLane,
|
|
98691
|
-
closeProgressLane
|
|
99067
|
+
closeProgressLane,
|
|
99068
|
+
hangRestart: {
|
|
99069
|
+
stalenessThresholdMs: hangStalenessMs(),
|
|
99070
|
+
request: (reason, idleMs) => {
|
|
99071
|
+
triggerSelfRestart(getMyAgentName(), `hang-watchdog:${reason} idle=${Math.round(idleMs)}ms`);
|
|
99072
|
+
}
|
|
99073
|
+
}
|
|
98692
99074
|
};
|
|
98693
99075
|
}
|
|
98694
99076
|
if (isGatewayMain)
|
|
@@ -98760,11 +99142,11 @@ if (isGatewayMain)
|
|
|
98760
99142
|
var inboundSpool;
|
|
98761
99143
|
if (isGatewayMain)
|
|
98762
99144
|
inboundSpool = STATIC ? undefined : createInboundSpool({
|
|
98763
|
-
path:
|
|
99145
|
+
path: join60(STATE_DIR, "inbound-spool.jsonl"),
|
|
98764
99146
|
fs: {
|
|
98765
99147
|
appendFileSync: (p, d) => appendFileSync7(p, d),
|
|
98766
99148
|
readFileSync: (p) => readFileSync58(p, "utf8"),
|
|
98767
|
-
writeFileSync: (p, d) =>
|
|
99149
|
+
writeFileSync: (p, d) => writeFileSync48(p, d),
|
|
98768
99150
|
renameSync: (a, b) => renameSync21(a, b),
|
|
98769
99151
|
existsSync: (p) => existsSync54(p),
|
|
98770
99152
|
statSizeSync: (p) => statSync19(p).size
|
|
@@ -98789,12 +99171,13 @@ var pendingInboundBuffer = createPendingInboundBuffer({
|
|
|
98789
99171
|
evictNoticeByChat.set(key, nowMs3);
|
|
98790
99172
|
const threadOpts = evThread != null ? { message_thread_id: evThread } : {};
|
|
98791
99173
|
swallowingApiCall(() => bot.api.sendMessage(chat, "\u23F3 Messages are arriving faster than I can process them. Your messages are saved and will be handled once I finish the current turn \u2014 if any can't be picked up, I'll ask you to resend it.", { ...threadOpts }), { chat_id: chat, verb: "inbound-buffer-eviction" });
|
|
98792
|
-
}
|
|
99174
|
+
},
|
|
99175
|
+
onHandbackEnqueue: (chatId, ts) => subagentHandbackMarker.record(chatId, ts)
|
|
98793
99176
|
});
|
|
98794
99177
|
function agentHasInFlightBackgroundWork(now) {
|
|
98795
99178
|
if (countRunningWorkers() > 0)
|
|
98796
99179
|
return true;
|
|
98797
|
-
const ageMs =
|
|
99180
|
+
const ageMs = readTurnActiveMarkerAgeMs2(STATE_DIR, now);
|
|
98798
99181
|
return ageMs != null && ageMs < TURN_ACTIVE_MARKER_FRESH_MS;
|
|
98799
99182
|
}
|
|
98800
99183
|
async function deliverCapturedProse2(args) {
|
|
@@ -98827,7 +99210,7 @@ async function maybeRedeliverUndeliveredAnswer() {
|
|
|
98827
99210
|
let transcriptText;
|
|
98828
99211
|
try {
|
|
98829
99212
|
const projectsDir = getProjectsDirForCwd();
|
|
98830
|
-
const path2 =
|
|
99213
|
+
const path2 = join60(projectsDir, `${sessionId}.jsonl`);
|
|
98831
99214
|
if (!existsSync54(path2)) {
|
|
98832
99215
|
process.stderr.write(`telegram gateway: crash-redelivery \u2014 transcript not found for turnKey=${turn.turn_key} session=${sessionId} (${path2}); skipping
|
|
98833
99216
|
`);
|
|
@@ -98930,7 +99313,7 @@ if (isGatewayMain && inboundSpool != null) {
|
|
|
98930
99313
|
}
|
|
98931
99314
|
var pendingPermissionBuffer = createPendingPermissionBuffer();
|
|
98932
99315
|
function buildPermissionActionRow(requestId, showAlways) {
|
|
98933
|
-
const kb = new
|
|
99316
|
+
const kb = new import_grammy15.InlineKeyboard().text("\u274C Deny", `perm:deny:${requestId}`).text("\u2705 Allow", `perm:allow:${requestId}`);
|
|
98934
99317
|
if (showAlways)
|
|
98935
99318
|
kb.text("\uD83D\uDD01 Always\u2026", `perm:always:${requestId}`);
|
|
98936
99319
|
return kb;
|
|
@@ -98992,8 +99375,8 @@ var bridgeDeadWatchdog = createBridgeDeadWatchdog({
|
|
|
98992
99375
|
isSessionAlive: () => findAgentProcessInContainer2()?.comm === "claude",
|
|
98993
99376
|
isShuttingDown: () => shuttingDown,
|
|
98994
99377
|
escalate: (reason) => triggerSelfRestart(process.env.SWITCHROOM_AGENT_NAME ?? "", reason, 1500),
|
|
98995
|
-
crashLogPath:
|
|
98996
|
-
markerPath:
|
|
99378
|
+
crashLogPath: join60(STATE_DIR, "bridge-crash.log"),
|
|
99379
|
+
markerPath: join60(STATE_DIR, "bridge-dead-escalation.json"),
|
|
98997
99380
|
log: (line) => process.stderr.write(`${line}
|
|
98998
99381
|
`),
|
|
98999
99382
|
priorStreak: bridgeDeadPriorStreak,
|
|
@@ -99099,8 +99482,8 @@ if (isGatewayMain)
|
|
|
99099
99482
|
probeQuotaViaBroker: (t) => probeQuotaForBootCard(agentSlug, t),
|
|
99100
99483
|
tmuxSupervisor: process.env.SWITCHROOM_TMUX_SUPERVISOR === "1",
|
|
99101
99484
|
dockerMode: process.env.SWITCHROOM_RUNTIME === "docker",
|
|
99102
|
-
configSnapshotPath:
|
|
99103
|
-
bootCardStatePath:
|
|
99485
|
+
configSnapshotPath: join60(resolvedAgentDirForCard, ".config-snapshot.json"),
|
|
99486
|
+
bootCardStatePath: join60(resolvedAgentDirForCard, ".boot-card-msgid.json"),
|
|
99104
99487
|
floodStatePath: FLOOD_STATE_PATH,
|
|
99105
99488
|
...updateOutcomeLine ? { updateOutcomeLine } : {}
|
|
99106
99489
|
}, ackMsgId).then((handle) => {
|
|
@@ -99453,7 +99836,7 @@ if (isGatewayMain)
|
|
|
99453
99836
|
},
|
|
99454
99837
|
async onRequestConfigApproval(client3, msg) {
|
|
99455
99838
|
const { handleRequestConfigApproval: handleRequestConfigApproval2 } = await Promise.resolve().then(() => (init_config_approval_handler(), exports_config_approval_handler));
|
|
99456
|
-
const { InlineKeyboard:
|
|
99839
|
+
const { InlineKeyboard: InlineKeyboard8 } = await Promise.resolve().then(() => __toESM(require_mod(), 1));
|
|
99457
99840
|
await handleRequestConfigApproval2(client3, msg, {
|
|
99458
99841
|
agentName: getMyAgentName(),
|
|
99459
99842
|
loadTargetChat: () => {
|
|
@@ -99475,7 +99858,7 @@ if (isGatewayMain)
|
|
|
99475
99858
|
...cfgTopic != null ? { threadId: cfgTopic } : {}
|
|
99476
99859
|
};
|
|
99477
99860
|
},
|
|
99478
|
-
buildKeyboard: (requestId, epoch) => new
|
|
99861
|
+
buildKeyboard: (requestId, epoch) => new InlineKeyboard8().text("\u2705 Approve", `cfg:${requestId}:${epoch}:approve`).text("\uD83D\uDEAB Deny", `cfg:${requestId}:${epoch}:deny`),
|
|
99479
99862
|
postCard: async (args) => {
|
|
99480
99863
|
try {
|
|
99481
99864
|
const sent = await robustApiCall(() => bot.api.sendRichMessage(args.chatId, richMessage2(args.text), {
|
|
@@ -99504,7 +99887,7 @@ if (isGatewayMain)
|
|
|
99504
99887
|
}
|
|
99505
99888
|
},
|
|
99506
99889
|
postAttachment: async (args) => {
|
|
99507
|
-
const input = new
|
|
99890
|
+
const input = new import_grammy15.InputFile(Buffer.from(args.content, "utf8"), args.filename);
|
|
99508
99891
|
await robustApiCall(() => bot.api.sendDocument(args.chatId, input, {
|
|
99509
99892
|
...args.threadId !== undefined ? { message_thread_id: args.threadId } : {}
|
|
99510
99893
|
}), {
|
|
@@ -99782,7 +100165,7 @@ if (isGatewayMain)
|
|
|
99782
100165
|
const receiverUid = receiverUidRaw ? Number(receiverUidRaw) : NaN;
|
|
99783
100166
|
if (Number.isInteger(receiverUid))
|
|
99784
100167
|
allowedUids.push(receiverUid);
|
|
99785
|
-
const socketPath =
|
|
100168
|
+
const socketPath = join60(STATE_DIR, "webhook.sock");
|
|
99786
100169
|
const webhookInject = (agentName3, inbound) => {
|
|
99787
100170
|
const msg = inbound;
|
|
99788
100171
|
const delivered = ipcServer.sendToAgent(agentName3, msg);
|
|
@@ -100010,9 +100393,9 @@ function redactOutboundText(text5, site) {
|
|
|
100010
100393
|
var VOICE_OUT_DEFAULT_CHUNK_CHARS = 600;
|
|
100011
100394
|
var VOICE_OUT_HARD_CHUNK_CAP = 4096;
|
|
100012
100395
|
var voiceOnDemandCache = new VoiceOnDemandCache({
|
|
100013
|
-
persistPath:
|
|
100396
|
+
persistPath: join60(STATE_DIR, "voice-ondemand.json")
|
|
100014
100397
|
});
|
|
100015
|
-
var VOICE_CACHE_DIR =
|
|
100398
|
+
var VOICE_CACHE_DIR = join60(STATE_DIR, "voice-cache");
|
|
100016
100399
|
var voicePreSynthQueue = new PreSynthQueue({
|
|
100017
100400
|
runJob: async (job) => {
|
|
100018
100401
|
const sidecarToken = await materializeSidecarToken2();
|
|
@@ -100167,6 +100550,7 @@ function gatewaySendReplyDeps() {
|
|
|
100167
100550
|
resolveAnswerThreadWithLog,
|
|
100168
100551
|
resolveThreadId,
|
|
100169
100552
|
getLatestInboundMessageId,
|
|
100553
|
+
getLastSubagentHandbackAt,
|
|
100170
100554
|
recordOutbound,
|
|
100171
100555
|
emissionAuthorityFor,
|
|
100172
100556
|
clearActivitySummary,
|
|
@@ -100278,7 +100662,7 @@ async function executeAskUser(rawArgs) {
|
|
|
100278
100662
|
}
|
|
100279
100663
|
}
|
|
100280
100664
|
const askId = generateAskId();
|
|
100281
|
-
const keyboard = new
|
|
100665
|
+
const keyboard = new import_grammy15.InlineKeyboard;
|
|
100282
100666
|
for (let i = 0;i < args.options.length; i++) {
|
|
100283
100667
|
keyboard.text(args.options[i], encodeAskCallback(askId, i));
|
|
100284
100668
|
if (i < args.options.length - 1)
|
|
@@ -100413,7 +100797,7 @@ async function executeSendGif(rawArgs) {
|
|
|
100413
100797
|
};
|
|
100414
100798
|
}
|
|
100415
100799
|
async function publishToTelegraph(text5, shortName, authorName) {
|
|
100416
|
-
const accountPath =
|
|
100800
|
+
const accountPath = join60(STATE_DIR, "telegraph-account.json");
|
|
100417
100801
|
let account = null;
|
|
100418
100802
|
try {
|
|
100419
100803
|
if (existsSync54(accountPath)) {
|
|
@@ -100436,8 +100820,8 @@ async function publishToTelegraph(text5, shortName, authorName) {
|
|
|
100436
100820
|
}
|
|
100437
100821
|
account = created.value;
|
|
100438
100822
|
try {
|
|
100439
|
-
|
|
100440
|
-
|
|
100823
|
+
mkdirSync47(STATE_DIR, { recursive: true, mode: 448 });
|
|
100824
|
+
writeFileSync48(accountPath, JSON.stringify(account, null, 2), { mode: 384 });
|
|
100441
100825
|
} catch (err) {
|
|
100442
100826
|
process.stderr.write(`telegram gateway: telegraph cache write failed: ${err.message}
|
|
100443
100827
|
`);
|
|
@@ -100592,9 +100976,9 @@ async function executeDownloadAttachment(args) {
|
|
|
100592
100976
|
fileUniqueId: file.file_unique_id,
|
|
100593
100977
|
now: Date.now()
|
|
100594
100978
|
});
|
|
100595
|
-
|
|
100979
|
+
mkdirSync47(INBOX_DIR, { recursive: true, mode: 448 });
|
|
100596
100980
|
assertInsideInbox2(INBOX_DIR, dlPath);
|
|
100597
|
-
|
|
100981
|
+
writeFileSync48(dlPath, buf, { mode: 384 });
|
|
100598
100982
|
return { content: [{ type: "text", text: dlPath }] };
|
|
100599
100983
|
}
|
|
100600
100984
|
async function executeEditMessage(args) {
|
|
@@ -101909,24 +102293,8 @@ function switchroomExecCombined(args, timeoutMs = 15000) {
|
|
|
101909
102293
|
shell: "/bin/bash"
|
|
101910
102294
|
});
|
|
101911
102295
|
}
|
|
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
102296
|
var buttonConfirmParseModeWarned = new Set;
|
|
101926
102297
|
var buttonConfirmSingleUseWarned = new Set;
|
|
101927
|
-
function preBlock(text5) {
|
|
101928
|
-
return "```\n" + text5.replace(/```/g, "`\u200B``") + "\n```";
|
|
101929
|
-
}
|
|
101930
102298
|
async function switchroomReply(ctx, text5, options = {}) {
|
|
101931
102299
|
const chatId = String(ctx.chat.id);
|
|
101932
102300
|
const baseThreadId = resolveThreadId(chatId, ctx.message?.message_thread_id);
|
|
@@ -101956,22 +102324,6 @@ Please delete message \`${msgId}\` manually so the value is not retained in chat
|
|
|
101956
102324
|
_Reason: ${escapeHtmlForTg2(msg)}_`), {}), { chat_id: chatId, verb: "deleteSensitiveMessage.warning" });
|
|
101957
102325
|
}
|
|
101958
102326
|
}
|
|
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
102327
|
function getMyAgentName() {
|
|
101976
102328
|
const fromEnv = process.env.SWITCHROOM_AGENT_NAME;
|
|
101977
102329
|
if (fromEnv && fromEnv.trim().length > 0)
|
|
@@ -101989,14 +102341,14 @@ function restartMarkerPath() {
|
|
|
101989
102341
|
const agentDir = resolveAgentDirFromEnv();
|
|
101990
102342
|
if (!agentDir)
|
|
101991
102343
|
return null;
|
|
101992
|
-
return
|
|
102344
|
+
return join60(agentDir, "restart-pending.json");
|
|
101993
102345
|
}
|
|
101994
102346
|
function writeRestartMarker(marker) {
|
|
101995
102347
|
const p = restartMarkerPath();
|
|
101996
102348
|
if (!p)
|
|
101997
102349
|
return;
|
|
101998
102350
|
try {
|
|
101999
|
-
|
|
102351
|
+
writeFileSync48(p, JSON.stringify(marker));
|
|
102000
102352
|
lastPlannedRestartAt = Date.now();
|
|
102001
102353
|
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
102354
|
`);
|
|
@@ -102181,12 +102533,12 @@ function _resetDockerReachableCache() {
|
|
|
102181
102533
|
}
|
|
102182
102534
|
function spawnSwitchroomDetached(args, onFailure) {
|
|
102183
102535
|
const fullArgs = SWITCHROOM_CONFIG ? ["--config", SWITCHROOM_CONFIG, ...args] : args;
|
|
102184
|
-
const logPath =
|
|
102536
|
+
const logPath = join60(STATE_DIR, "detached-spawn.log");
|
|
102185
102537
|
let outFd = null;
|
|
102186
102538
|
try {
|
|
102187
|
-
|
|
102539
|
+
mkdirSync47(STATE_DIR, { recursive: true });
|
|
102188
102540
|
outFd = openSync12(logPath, "a");
|
|
102189
|
-
|
|
102541
|
+
writeFileSync48(logPath, `
|
|
102190
102542
|
[${new Date().toISOString()}] spawn ${SWITCHROOM_CLI} ${fullArgs.join(" ")}
|
|
102191
102543
|
`, { flag: "a" });
|
|
102192
102544
|
} catch {}
|
|
@@ -102286,53 +102638,6 @@ function scheduleGrantRestart(agentName3, chatId, threadId, reason) {
|
|
|
102286
102638
|
}
|
|
102287
102639
|
return decision;
|
|
102288
102640
|
}
|
|
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
102641
|
async function runSwitchroomAuthCommand(ctx, args, label) {
|
|
102337
102642
|
try {
|
|
102338
102643
|
const output = switchroomExecCombined(args, 30000);
|
|
@@ -102383,15 +102688,6 @@ function runVaultCli(args, passphrase, stdinValue) {
|
|
|
102383
102688
|
`).trim() };
|
|
102384
102689
|
}
|
|
102385
102690
|
}
|
|
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
102691
|
async function executeVaultOp(ctx, chatId, op, key, passphrase, setValue) {
|
|
102396
102692
|
if (op === "list") {
|
|
102397
102693
|
const r = runVaultCli(["list"], passphrase);
|
|
@@ -102495,30 +102791,6 @@ function switchroomExecJson(args) {
|
|
|
102495
102791
|
return null;
|
|
102496
102792
|
}
|
|
102497
102793
|
}
|
|
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
102794
|
function execAuthCode(agent, code2) {
|
|
102523
102795
|
try {
|
|
102524
102796
|
const output = switchroomExec(["auth", "code", agent, code2, "--json"], 150000);
|
|
@@ -102554,7 +102826,7 @@ ${preBlock(formatSwitchroomOutput(detail))}`, { html: true });
|
|
|
102554
102826
|
}
|
|
102555
102827
|
function readRecentDenialsForAgent(agentName3, windowMs, limit) {
|
|
102556
102828
|
try {
|
|
102557
|
-
const auditPath =
|
|
102829
|
+
const auditPath = join60(homedir19(), ".switchroom", "vault-audit.log");
|
|
102558
102830
|
if (!existsSync54(auditPath))
|
|
102559
102831
|
return [];
|
|
102560
102832
|
const raw = readFileSync58(auditPath, "utf8");
|
|
@@ -102608,7 +102880,7 @@ async function buildAgentMetadata(agentName3) {
|
|
|
102608
102880
|
try {
|
|
102609
102881
|
const agentDir = resolveAgentDirFromEnv();
|
|
102610
102882
|
if (agentDir) {
|
|
102611
|
-
const raw = readFileSync58(
|
|
102883
|
+
const raw = readFileSync58(join60(agentDir, ".claude", ".claude.json"), "utf8");
|
|
102612
102884
|
claudeJson = JSON.parse(raw);
|
|
102613
102885
|
}
|
|
102614
102886
|
} catch {}
|
|
@@ -102737,7 +103009,7 @@ function buildModelDeps(restartCtx) {
|
|
|
102737
103009
|
try {
|
|
102738
103010
|
const agentDir = resolveAgentDirFromEnv();
|
|
102739
103011
|
if (agentDir) {
|
|
102740
|
-
const local = await fetchQuota2({ claudeConfigDir:
|
|
103012
|
+
const local = await fetchQuota2({ claudeConfigDir: join60(agentDir, ".claude") });
|
|
102741
103013
|
if (local.ok)
|
|
102742
103014
|
return formatQuotaLine2(local.data);
|
|
102743
103015
|
}
|
|
@@ -102832,7 +103104,7 @@ function buildModelDeps(restartCtx) {
|
|
|
102832
103104
|
function modelMenuReplyMarkup(reply) {
|
|
102833
103105
|
if (!reply.keyboard)
|
|
102834
103106
|
return;
|
|
102835
|
-
const kb = new
|
|
103107
|
+
const kb = new import_grammy15.InlineKeyboard;
|
|
102836
103108
|
for (const row of reply.keyboard) {
|
|
102837
103109
|
for (const btn of row)
|
|
102838
103110
|
kb.text(btn.text, btn.callback_data);
|
|
@@ -102972,7 +103244,7 @@ function buildEffortDeps() {
|
|
|
102972
103244
|
function effortMenuReplyMarkup(reply) {
|
|
102973
103245
|
if (!reply.keyboard)
|
|
102974
103246
|
return;
|
|
102975
|
-
const kb = new
|
|
103247
|
+
const kb = new import_grammy15.InlineKeyboard;
|
|
102976
103248
|
for (const row of reply.keyboard) {
|
|
102977
103249
|
for (const btn of row)
|
|
102978
103250
|
kb.text(btn.text, btn.callback_data);
|
|
@@ -102983,7 +103255,7 @@ function effortMenuReplyMarkup(reply) {
|
|
|
102983
103255
|
function flushAgentHandoff(agentDir) {
|
|
102984
103256
|
let removed = 0;
|
|
102985
103257
|
for (const fname of [".handoff.md", ".handoff-topic"]) {
|
|
102986
|
-
const p =
|
|
103258
|
+
const p = join60(agentDir, fname);
|
|
102987
103259
|
try {
|
|
102988
103260
|
if (existsSync54(p)) {
|
|
102989
103261
|
unlinkSync26(p);
|
|
@@ -103041,7 +103313,7 @@ async function handleNewCommand(ctx) {
|
|
|
103041
103313
|
writeRestartMarker({ chat_id: chatId, thread_id: threadId ?? null, ack_message_id: ackId, ts: Date.now() });
|
|
103042
103314
|
if (agentDir != null) {
|
|
103043
103315
|
try {
|
|
103044
|
-
|
|
103316
|
+
writeFileSync48(join60(agentDir, ".force-fresh-session"), `${kind} at ${new Date().toISOString()}
|
|
103045
103317
|
`, "utf8");
|
|
103046
103318
|
} catch (err) {
|
|
103047
103319
|
process.stderr.write(`telegram gateway: failed to write force-fresh marker: ${err}
|
|
@@ -103155,10 +103427,10 @@ function buildFolderPickerDeps() {
|
|
|
103155
103427
|
}
|
|
103156
103428
|
var lockoutOps = {
|
|
103157
103429
|
readFileSync: (p, enc) => readFileSync58(p, enc),
|
|
103158
|
-
writeFileSync: (p, data, opts) =>
|
|
103430
|
+
writeFileSync: (p, data, opts) => writeFileSync48(p, data, opts),
|
|
103159
103431
|
existsSync: (p) => existsSync54(p),
|
|
103160
|
-
mkdirSync: (p, opts) =>
|
|
103161
|
-
joinPath: (...parts) =>
|
|
103432
|
+
mkdirSync: (p, opts) => mkdirSync47(p, opts),
|
|
103433
|
+
joinPath: (...parts) => join60(...parts)
|
|
103162
103434
|
};
|
|
103163
103435
|
var FLEET_FALLBACK_DEDUP_MS = 30000;
|
|
103164
103436
|
function isAuthBrokerSocketReachable() {
|
|
@@ -103418,7 +103690,7 @@ async function runCreditWatch() {
|
|
|
103418
103690
|
if (!agentDir)
|
|
103419
103691
|
return;
|
|
103420
103692
|
const agentName3 = getMyAgentName();
|
|
103421
|
-
const claudeConfigDir =
|
|
103693
|
+
const claudeConfigDir = join60(agentDir, ".claude");
|
|
103422
103694
|
const stateDir = STATE_DIR;
|
|
103423
103695
|
const reason = readClaudeJsonOverage(claudeConfigDir);
|
|
103424
103696
|
const prev = loadCreditState(stateDir);
|
|
@@ -103738,16 +104010,6 @@ async function probeQuotaForBootCard(agent, timeoutMs) {
|
|
|
103738
104010
|
}
|
|
103739
104011
|
var callbackQueryHandlers;
|
|
103740
104012
|
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
104013
|
async function renderSelfDoctor(ctx) {
|
|
103752
104014
|
let output;
|
|
103753
104015
|
try {
|
|
@@ -104635,7 +104897,7 @@ _Google accounts are connected from the host CLI._`, { html: true });
|
|
|
104635
104897
|
return;
|
|
104636
104898
|
}
|
|
104637
104899
|
const appNote = started.source === "default" ? "_Using switchroom's shipped Microsoft app._" : "_Using your configured Microsoft app._";
|
|
104638
|
-
const keyboard = new
|
|
104900
|
+
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
104901
|
const sent = await ctx.replyWithRichMessage(richMessage2(`\uD83D\uDD17 **Connect a Microsoft account**
|
|
104640
104902
|
|
|
104641
104903
|
` + `1. Tap **Sign in to Microsoft** below.
|
|
@@ -104771,7 +105033,7 @@ Send \`/auth ${parsed.provider} cancel\` to abort.`, { html: true });
|
|
|
104771
105033
|
tz: process.env.SWITCHROOM_TIMEZONE ?? process.env.TZ
|
|
104772
105034
|
});
|
|
104773
105035
|
if (reply.keyboard && reply.keyboard.length > 0) {
|
|
104774
|
-
const kb = new
|
|
105036
|
+
const kb = new import_grammy15.InlineKeyboard;
|
|
104775
105037
|
for (let i = 0;i < reply.keyboard.length; i++) {
|
|
104776
105038
|
const row = reply.keyboard[i];
|
|
104777
105039
|
for (const b of row) {
|
|
@@ -104913,7 +105175,7 @@ Send \`/auth ${parsed.provider} cancel\` to abort.`, { html: true });
|
|
|
104913
105175
|
}
|
|
104914
105176
|
lines.push("");
|
|
104915
105177
|
const denials = readRecentDenialsForAgent(targetAgent, 604800000, 5);
|
|
104916
|
-
const denialKeyboard = new
|
|
105178
|
+
const denialKeyboard = new import_grammy15.InlineKeyboard;
|
|
104917
105179
|
if (denials.length > 0) {
|
|
104918
105180
|
lines.push("**Recent denials (last 7d):**");
|
|
104919
105181
|
for (const d of denials) {
|
|
@@ -104961,7 +105223,7 @@ Send \`/auth ${parsed.provider} cancel\` to abort.`, { html: true });
|
|
|
104961
105223
|
byAgent.set(g.agent_slug, list3);
|
|
104962
105224
|
}
|
|
104963
105225
|
const lines = ["**\uD83D\uDCDC Active grants**", ""];
|
|
104964
|
-
const keyboard = new
|
|
105226
|
+
const keyboard = new import_grammy15.InlineKeyboard;
|
|
104965
105227
|
for (const [agentName3, agentGrants] of byAgent) {
|
|
104966
105228
|
lines.push(`**${escapeHtmlForTg2(agentName3)}:**`);
|
|
104967
105229
|
for (const g of agentGrants) {
|
|
@@ -105158,7 +105420,7 @@ Send \`/auth ${parsed.provider} cancel\` to abort.`, { html: true });
|
|
|
105158
105420
|
if (ctx.chat?.type !== "private") {
|
|
105159
105421
|
kbRows = kbRows.filter((row) => !row.some((b) => b.callbackData?.startsWith("auth:use:")));
|
|
105160
105422
|
}
|
|
105161
|
-
const keyboard = new
|
|
105423
|
+
const keyboard = new import_grammy15.InlineKeyboard;
|
|
105162
105424
|
kbRows.forEach((row, ri) => {
|
|
105163
105425
|
if (ri > 0)
|
|
105164
105426
|
keyboard.row();
|
|
@@ -105184,7 +105446,7 @@ Send \`/auth ${parsed.provider} cancel\` to abort.`, { html: true });
|
|
|
105184
105446
|
await switchroomReply(ctx, "**/usage:** cannot resolve agent dir.", { html: true });
|
|
105185
105447
|
return;
|
|
105186
105448
|
}
|
|
105187
|
-
const result = await fetchQuota2({ claudeConfigDir:
|
|
105449
|
+
const result = await fetchQuota2({ claudeConfigDir: join60(agentDir, ".claude") });
|
|
105188
105450
|
if (!result.ok) {
|
|
105189
105451
|
await switchroomReply(ctx, `**/usage:** ${escapeHtmlForTg2(result.reason)}`, { html: true });
|
|
105190
105452
|
return;
|
|
@@ -105510,7 +105772,7 @@ ${preBlock(formatSwitchroomOutput(err.message ?? "unknown error"))}`, { html: tr
|
|
|
105510
105772
|
await ctx.answerCallbackQuery({ text: "No always-allow rule for this tool." }).catch(() => {});
|
|
105511
105773
|
return;
|
|
105512
105774
|
}
|
|
105513
|
-
keyboard = new
|
|
105775
|
+
keyboard = new import_grammy15.InlineKeyboard().text("\u2190 Back", `perm:back:${request_id}`);
|
|
105514
105776
|
if (choices.specific)
|
|
105515
105777
|
keyboard.text(choices.specific.buttonLabel, `perm:asn:${request_id}`);
|
|
105516
105778
|
keyboard.text(`${choices.broad.buttonLabel} \u26A0\uFE0F`, `perm:asb:${request_id}`);
|
|
@@ -105812,7 +106074,7 @@ ${labelWithResume}` : labelWithResume,
|
|
|
105812
106074
|
verb: "voice-ondemand.sendVoice",
|
|
105813
106075
|
...threadId != null ? { threadId } : {}
|
|
105814
106076
|
}),
|
|
105815
|
-
sendVoiceByUpload: (chatId, audio, sendOpts, threadId) => robustApiCall(() => bot2.api.sendVoice(chatId, new
|
|
106077
|
+
sendVoiceByUpload: (chatId, audio, sendOpts, threadId) => robustApiCall(() => bot2.api.sendVoice(chatId, new import_grammy15.InputFile(Buffer.from(audio)), sendOpts), {
|
|
105816
106078
|
chat_id: chatId,
|
|
105817
106079
|
verb: "voice-ondemand.sendVoice",
|
|
105818
106080
|
...threadId != null ? { threadId } : {}
|
|
@@ -106071,7 +106333,7 @@ async function initGatewayBot() {
|
|
|
106071
106333
|
`);
|
|
106072
106334
|
process.exit(1);
|
|
106073
106335
|
}
|
|
106074
|
-
bot = new
|
|
106336
|
+
bot = new import_grammy15.Bot(TOKEN);
|
|
106075
106337
|
installTgPostLogger(bot);
|
|
106076
106338
|
installUpdateTap(bot, (line) => process.stderr.write(line));
|
|
106077
106339
|
bot.api.config.use(async (prev, method, payload, signal) => {
|
|
@@ -106425,7 +106687,7 @@ async function startGateway() {
|
|
|
106425
106687
|
return;
|
|
106426
106688
|
}
|
|
106427
106689
|
})();
|
|
106428
|
-
const resolvedAgentDirForBootCard = agentDir ??
|
|
106690
|
+
const resolvedAgentDirForBootCard = agentDir ?? join60(homedir19(), ".switchroom", "agents", agentSlug);
|
|
106429
106691
|
const handle = await startBootCard(chatId, threadId, botApiForCard, {
|
|
106430
106692
|
agentName: agentDisplayName,
|
|
106431
106693
|
agentSlug,
|
|
@@ -106439,8 +106701,8 @@ async function startGateway() {
|
|
|
106439
106701
|
probeQuotaViaBroker: (t) => probeQuotaForBootCard(agentSlug, t),
|
|
106440
106702
|
tmuxSupervisor: process.env.SWITCHROOM_TMUX_SUPERVISOR === "1",
|
|
106441
106703
|
dockerMode: process.env.SWITCHROOM_RUNTIME === "docker",
|
|
106442
|
-
configSnapshotPath:
|
|
106443
|
-
bootCardStatePath:
|
|
106704
|
+
configSnapshotPath: join60(resolvedAgentDirForBootCard, ".config-snapshot.json"),
|
|
106705
|
+
bootCardStatePath: join60(resolvedAgentDirForBootCard, ".boot-card-msgid.json"),
|
|
106444
106706
|
floodStatePath: FLOOD_STATE_PATH,
|
|
106445
106707
|
...updateOutcomeLine ? { updateOutcomeLine } : {}
|
|
106446
106708
|
}, ackMsgId);
|
|
@@ -106471,7 +106733,7 @@ async function startGateway() {
|
|
|
106471
106733
|
try {
|
|
106472
106734
|
const smAgentDir = resolveAgentDirFromEnv();
|
|
106473
106735
|
if (smAgentDir) {
|
|
106474
|
-
const activePath =
|
|
106736
|
+
const activePath = join60(smAgentDir, ".active-session-model");
|
|
106475
106737
|
if (existsSync54(activePath)) {
|
|
106476
106738
|
try {
|
|
106477
106739
|
const launched = readFileSync58(activePath, "utf8").trim();
|
|
@@ -106507,12 +106769,12 @@ async function startGateway() {
|
|
|
106507
106769
|
deliverModelSwitchBootNotice({
|
|
106508
106770
|
...modelBootCardDeps,
|
|
106509
106771
|
confirmation,
|
|
106510
|
-
hasSessionModelAlert: existsSync54(
|
|
106772
|
+
hasSessionModelAlert: existsSync54(join60(smAgentDir, ".session-model-alert"))
|
|
106511
106773
|
});
|
|
106512
106774
|
}
|
|
106513
106775
|
} catch {}
|
|
106514
106776
|
}
|
|
106515
|
-
const activeEffortPath =
|
|
106777
|
+
const activeEffortPath = join60(smAgentDir, ".active-session-effort");
|
|
106516
106778
|
if (existsSync54(activeEffortPath)) {
|
|
106517
106779
|
try {
|
|
106518
106780
|
const launchedEffort = readFileSync58(activeEffortPath, "utf8").trim();
|
|
@@ -106520,7 +106782,7 @@ async function startGateway() {
|
|
|
106520
106782
|
sessionEffortOverride = launchedEffort.length > 0 && launchedEffort !== configuredEffort ? launchedEffort : null;
|
|
106521
106783
|
} catch {}
|
|
106522
106784
|
}
|
|
106523
|
-
const alertPath =
|
|
106785
|
+
const alertPath = join60(smAgentDir, ".session-model-alert");
|
|
106524
106786
|
if (existsSync54(alertPath)) {
|
|
106525
106787
|
let alertText = null;
|
|
106526
106788
|
try {
|
|
@@ -106700,6 +106962,7 @@ async function startGateway() {
|
|
|
106700
106962
|
`);
|
|
106701
106963
|
},
|
|
106702
106964
|
onTerminalCleanup: (agentId) => {
|
|
106965
|
+
workerFeedOriginDeferrals.delete(agentId);
|
|
106703
106966
|
try {
|
|
106704
106967
|
workerActivityFeed?.terminate(agentId);
|
|
106705
106968
|
} catch (err) {
|
|
@@ -106708,6 +106971,7 @@ async function startGateway() {
|
|
|
106708
106971
|
}
|
|
106709
106972
|
},
|
|
106710
106973
|
onFinish: ({ agentId, outcome, description, resultText, toolCount, totalTokens, durationMs, background: entryBackground }) => {
|
|
106974
|
+
workerFeedOriginDeferrals.delete(agentId);
|
|
106711
106975
|
deferredDoneReactions.promote();
|
|
106712
106976
|
let fleetChatId = "";
|
|
106713
106977
|
try {
|
|
@@ -106928,9 +107192,28 @@ async function startGateway() {
|
|
|
106928
107192
|
stampTurn.subagentActivityAt = Date.now();
|
|
106929
107193
|
}
|
|
106930
107194
|
if (workerFeedEnabled) {
|
|
106931
|
-
const
|
|
106932
|
-
|
|
106933
|
-
|
|
107195
|
+
const dest = decideWorkerFeedDestination({
|
|
107196
|
+
origin: resolveSubagentOriginChat(agentId),
|
|
107197
|
+
cardExists: workerActivityFeed?.has(agentId) === true,
|
|
107198
|
+
priorDeferrals: workerFeedOriginDeferrals.get(agentId) ?? 0,
|
|
107199
|
+
maxDeferrals: WORKER_FEED_ORIGIN_DEFER_MAX,
|
|
107200
|
+
fleetChatId,
|
|
107201
|
+
stampChatId: stampTurn?.sessionChatId,
|
|
107202
|
+
stampThreadId: stampTurn?.sessionThreadId,
|
|
107203
|
+
ownerDm: loadAccess().allowFrom[0] ?? ""
|
|
107204
|
+
});
|
|
107205
|
+
if (dest.action === "defer") {
|
|
107206
|
+
workerFeedOriginDeferrals.set(agentId, dest.deferrals);
|
|
107207
|
+
return;
|
|
107208
|
+
}
|
|
107209
|
+
workerFeedOriginDeferrals.delete(agentId);
|
|
107210
|
+
if (dest.exhausted) {
|
|
107211
|
+
process.stderr.write(`telegram gateway: worker-feed origin backfill never linked agent=${agentId} after ${dest.deferrals} deferrals \u2014 painting card
|
|
107212
|
+
`);
|
|
107213
|
+
}
|
|
107214
|
+
if (dest.ownerDmFallback)
|
|
107215
|
+
noteWorkerFeedOwnerDmFallback(agentId);
|
|
107216
|
+
workerActivityFeed?.update(agentId, dest.chatId, {
|
|
106934
107217
|
description: dispatch.feedDescription,
|
|
106935
107218
|
lastTool,
|
|
106936
107219
|
toolCount,
|
|
@@ -106939,7 +107222,7 @@ async function startGateway() {
|
|
|
106939
107222
|
state: "running",
|
|
106940
107223
|
model: feedModel,
|
|
106941
107224
|
totalTokens
|
|
106942
|
-
},
|
|
107225
|
+
}, dest.threadId);
|
|
106943
107226
|
return;
|
|
106944
107227
|
}
|
|
106945
107228
|
const progressOrigin = resolveSubagentOriginChat(agentId);
|
|
@@ -107004,7 +107287,7 @@ async function startGateway() {
|
|
|
107004
107287
|
await runnerHandle.task();
|
|
107005
107288
|
return;
|
|
107006
107289
|
} catch (err) {
|
|
107007
|
-
if (err instanceof
|
|
107290
|
+
if (err instanceof import_grammy15.GrammyError && err.error_code === 409) {
|
|
107008
107291
|
const delay = Math.min(1000 * attempt, 15000);
|
|
107009
107292
|
const agentName3 = process.env.SWITCHROOM_AGENT_NAME ?? "-";
|
|
107010
107293
|
process.stderr.write(`telegram gateway: poll.409.detected attempt=${attempt} retry_in_ms=${delay} agent=${agentName3}
|