switchroom 0.20.7 → 0.20.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent-scheduler/index.js +111 -14
- package/dist/auth-broker/index.js +113 -30
- package/dist/cli/autoaccept-poll.js +5 -3
- package/dist/cli/drive-write-pretool.mjs +5 -3
- package/dist/cli/ms-365-write-pretool.mjs +5 -3
- package/dist/cli/notion-write-pretool.mjs +67 -6
- package/dist/cli/switchroom.js +389 -31
- package/dist/host-control/main.js +69 -8
- package/dist/vault/approvals/kernel-server.js +68 -7
- package/dist/vault/broker/server.js +68 -7
- package/package.json +1 -1
- package/profiles/default/CLAUDE.md.hbs +12 -13
- package/telegram-plugin/ask-user.ts +6 -7
- package/telegram-plugin/bridge/ipc-client.ts +17 -1
- package/telegram-plugin/dist/bridge/bridge.js +5 -2
- package/telegram-plugin/dist/gateway/gateway.js +410 -178
- package/telegram-plugin/dist/server.js +5 -2
- package/telegram-plugin/gateway/auth-broker-client.ts +1 -1
- package/telegram-plugin/gateway/auth-command.ts +4 -2
- package/telegram-plugin/gateway/boot-reason.ts +61 -0
- package/telegram-plugin/gateway/checklist-fallback.ts +8 -1
- package/telegram-plugin/gateway/cron-session.ts +66 -0
- package/telegram-plugin/gateway/gateway.ts +36 -34
- package/telegram-plugin/gateway/narrative-lane.ts +21 -1
- package/telegram-plugin/gateway/outbound-send-path.ts +9 -1
- package/telegram-plugin/gateway/represent-delivery-guard.ts +33 -2
- package/telegram-plugin/gateway/stream-render.ts +11 -2
- package/telegram-plugin/gateway/subagent-handback-inbound-builder.ts +21 -1
- package/telegram-plugin/gateway/subagent-handback-marker.ts +94 -0
- package/telegram-plugin/gateway/throttle-tier-wiring.ts +93 -18
- package/telegram-plugin/render/emphasis-guard.ts +92 -12
- package/telegram-plugin/render/line-start-guard.ts +27 -2
- package/telegram-plugin/sticker-aliases.ts +12 -14
- package/telegram-plugin/tests/ask-user.test.ts +15 -0
- package/telegram-plugin/tests/boot-card-reason.test.ts +88 -0
- package/telegram-plugin/tests/checklist-fallback.test.ts +21 -0
- package/telegram-plugin/tests/cron-bridge-drain-spool-ack.test.ts +150 -0
- package/telegram-plugin/tests/handback-tasknotif-dedup.test.ts +248 -0
- package/telegram-plugin/tests/ipc-client-reconnect-rejection.test.ts +70 -0
- package/telegram-plugin/tests/narrative-lane-golden.test.ts +86 -0
- package/telegram-plugin/tests/queued-card-surface.test.ts +66 -0
- package/telegram-plugin/tests/render/emphasis-guard.test.ts +105 -6
- package/telegram-plugin/tests/render/heading-guard-blockquote-glued-hash.test.ts +123 -36
- package/telegram-plugin/tests/reply-quote-wire.test.ts +47 -0
- package/telegram-plugin/tests/represent-guard.test.ts +45 -0
- package/telegram-plugin/tests/sticker-aliases.test.ts +43 -0
- package/telegram-plugin/tests/throttle-tier-probe-only.test.ts +216 -0
- package/telegram-plugin/tests/throttle-tier-route-429-wiring.test.ts +92 -0
- package/telegram-plugin/tests/throttle-tier-route-429.test.ts +71 -0
- package/telegram-plugin/tests/turn-flush-safety.test.ts +83 -0
- package/telegram-plugin/throttle-tier.ts +59 -0
- package/telegram-plugin/turn-flush-safety.ts +79 -0
|
@@ -11329,7 +11329,8 @@ var init_protocol = __esm(() => {
|
|
|
11329
11329
|
v: exports_external.literal(PROTOCOL_VERSION),
|
|
11330
11330
|
op: exports_external.literal("mark-throttled"),
|
|
11331
11331
|
id: exports_external.string().min(1),
|
|
11332
|
-
until: exports_external.number().int().positive()
|
|
11332
|
+
until: exports_external.number().int().positive(),
|
|
11333
|
+
probeOnly: exports_external.boolean().optional()
|
|
11333
11334
|
});
|
|
11334
11335
|
RefreshAccountRequestSchema = exports_external.object({
|
|
11335
11336
|
v: exports_external.literal(PROTOCOL_VERSION),
|
|
@@ -11725,12 +11726,13 @@ class AuthBrokerClient {
|
|
|
11725
11726
|
const data = await this.send(req);
|
|
11726
11727
|
return data;
|
|
11727
11728
|
}
|
|
11728
|
-
async markThrottled(until) {
|
|
11729
|
+
async markThrottled(until, probeOnly = false) {
|
|
11729
11730
|
const data = await this.send({
|
|
11730
11731
|
v: PROTOCOL_VERSION,
|
|
11731
11732
|
id: randomUUID2(),
|
|
11732
11733
|
op: "mark-throttled",
|
|
11733
|
-
until
|
|
11734
|
+
until,
|
|
11735
|
+
...probeOnly ? { probeOnly: true } : {}
|
|
11734
11736
|
});
|
|
11735
11737
|
return data;
|
|
11736
11738
|
}
|
|
@@ -13480,12 +13482,27 @@ var init_dollar_math_guard = __esm(() => {
|
|
|
13480
13482
|
});
|
|
13481
13483
|
|
|
13482
13484
|
// render/emphasis-guard.ts
|
|
13485
|
+
function isLineLeadingBullet(text, starIndex) {
|
|
13486
|
+
const next = text[starIndex + 1];
|
|
13487
|
+
if (next !== " " && next !== "\t")
|
|
13488
|
+
return false;
|
|
13489
|
+
let i = starIndex - 1;
|
|
13490
|
+
let indent = 0;
|
|
13491
|
+
while (i >= 0 && (text[i] === " " || text[i] === "\t")) {
|
|
13492
|
+
if (++indent > 3)
|
|
13493
|
+
return false;
|
|
13494
|
+
i--;
|
|
13495
|
+
}
|
|
13496
|
+
return i < 0 || text[i] === `
|
|
13497
|
+
`;
|
|
13498
|
+
}
|
|
13483
13499
|
function guardAccidentalEmphasis(text) {
|
|
13484
13500
|
if (!text.includes("_") && !text.includes("*"))
|
|
13485
13501
|
return text;
|
|
13486
13502
|
const segments = splitProtectedSegments(text);
|
|
13487
13503
|
let hasIntraUnderscore = false;
|
|
13488
13504
|
let hasIntraAsterisk = false;
|
|
13505
|
+
let hasBoundaryAsterisk = false;
|
|
13489
13506
|
let underscoreCount = 0;
|
|
13490
13507
|
let asteriskCount = 0;
|
|
13491
13508
|
for (const seg of segments) {
|
|
@@ -13495,14 +13512,24 @@ function guardAccidentalEmphasis(text) {
|
|
|
13495
13512
|
hasIntraUnderscore = true;
|
|
13496
13513
|
if (INTRA_WORD_ASTERISK.test(seg.text))
|
|
13497
13514
|
hasIntraAsterisk = true;
|
|
13515
|
+
if (!hasBoundaryAsterisk) {
|
|
13516
|
+
for (const m of seg.text.matchAll(BOUNDARY_FLANKED_ASTERISK)) {
|
|
13517
|
+
if (!isLineLeadingBullet(seg.text, m.index ?? 0)) {
|
|
13518
|
+
hasBoundaryAsterisk = true;
|
|
13519
|
+
break;
|
|
13520
|
+
}
|
|
13521
|
+
}
|
|
13522
|
+
}
|
|
13498
13523
|
underscoreCount += seg.text.match(ANY_UNDERSCORE)?.length ?? 0;
|
|
13499
13524
|
asteriskCount += seg.text.match(ANY_ASTERISK)?.length ?? 0;
|
|
13500
13525
|
}
|
|
13501
13526
|
INTRA_WORD_UNDERSCORE.lastIndex = 0;
|
|
13502
13527
|
INTRA_WORD_ASTERISK.lastIndex = 0;
|
|
13528
|
+
BOUNDARY_FLANKED_ASTERISK.lastIndex = 0;
|
|
13503
13529
|
const armUnderscore = hasIntraUnderscore && underscoreCount >= 2;
|
|
13504
13530
|
const armAsterisk = hasIntraAsterisk && asteriskCount >= 2;
|
|
13505
|
-
|
|
13531
|
+
const armBoundaryAsterisk = hasBoundaryAsterisk;
|
|
13532
|
+
if (!armUnderscore && !armAsterisk && !armBoundaryAsterisk)
|
|
13506
13533
|
return text;
|
|
13507
13534
|
return segments.map((seg) => {
|
|
13508
13535
|
if (seg.code)
|
|
@@ -13512,13 +13539,17 @@ function guardAccidentalEmphasis(text) {
|
|
|
13512
13539
|
out = out.replace(INTRA_WORD_UNDERSCORE, "\\_");
|
|
13513
13540
|
if (armAsterisk)
|
|
13514
13541
|
out = out.replace(INTRA_WORD_ASTERISK, "\\*");
|
|
13542
|
+
if (armBoundaryAsterisk) {
|
|
13543
|
+
out = out.replace(BOUNDARY_FLANKED_ASTERISK, (m, offset, str) => isLineLeadingBullet(str, offset) ? m : "\\*");
|
|
13544
|
+
}
|
|
13515
13545
|
return out;
|
|
13516
13546
|
}).join("");
|
|
13517
13547
|
}
|
|
13518
|
-
var INTRA_WORD_UNDERSCORE, INTRA_WORD_ASTERISK, ANY_UNDERSCORE, ANY_ASTERISK;
|
|
13548
|
+
var INTRA_WORD_UNDERSCORE, INTRA_WORD_ASTERISK, BOUNDARY_FLANKED_ASTERISK, ANY_UNDERSCORE, ANY_ASTERISK;
|
|
13519
13549
|
var init_emphasis_guard = __esm(() => {
|
|
13520
13550
|
INTRA_WORD_UNDERSCORE = /(?<=[A-Za-z0-9])_(?=[A-Za-z0-9])/g;
|
|
13521
13551
|
INTRA_WORD_ASTERISK = /(?<=[A-Za-z0-9])\*(?=[A-Za-z0-9])/g;
|
|
13552
|
+
BOUNDARY_FLANKED_ASTERISK = /(?<=^|\s)\*(?=\s|$)/g;
|
|
13522
13553
|
ANY_UNDERSCORE = /(?<!\\)_/g;
|
|
13523
13554
|
ANY_ASTERISK = /(?<!\\)\*/g;
|
|
13524
13555
|
});
|
|
@@ -13569,7 +13600,7 @@ function guardAccidentalBlockConstructs(text) {
|
|
|
13569
13600
|
return out;
|
|
13570
13601
|
}
|
|
13571
13602
|
function escapeAccidentalHeadingLine(line) {
|
|
13572
|
-
return line.replace(ACCIDENTAL_HEADING, "$1\\$2");
|
|
13603
|
+
return line.replace(ACCIDENTAL_HEADING, "$1\\$2").replace(ACCIDENTAL_HEADING_AFTER_MARKER, "$1\\$2");
|
|
13573
13604
|
}
|
|
13574
13605
|
function guardAccidentalHeading(text) {
|
|
13575
13606
|
if (!text.includes("#"))
|
|
@@ -13599,11 +13630,12 @@ function guardAccidentalHeading(text) {
|
|
|
13599
13630
|
}
|
|
13600
13631
|
return out;
|
|
13601
13632
|
}
|
|
13602
|
-
var ACCIDENTAL_BLOCKQUOTE, ACCIDENTAL_ORDERED_LIST, ACCIDENTAL_HEADING;
|
|
13633
|
+
var ACCIDENTAL_BLOCKQUOTE, ACCIDENTAL_ORDERED_LIST, ACCIDENTAL_HEADING, ACCIDENTAL_HEADING_AFTER_MARKER;
|
|
13603
13634
|
var init_line_start_guard = __esm(() => {
|
|
13604
13635
|
ACCIDENTAL_BLOCKQUOTE = /^>[0-9=]/;
|
|
13605
13636
|
ACCIDENTAL_ORDERED_LIST = /^(\d{4,})([.)])(\s|$)/;
|
|
13606
13637
|
ACCIDENTAL_HEADING = /^([ \t]{0,3})(#{1,6})(?=[^\s#])/;
|
|
13638
|
+
ACCIDENTAL_HEADING_AFTER_MARKER = /^([ \t]{0,3}(?:(?:[-*+]|\d{1,3}[.)])[ \t]+|>[ \t]*)+)(#{1,6})(?=[^\s#])/;
|
|
13607
13639
|
});
|
|
13608
13640
|
|
|
13609
13641
|
// render/inline-pairs-guard.ts
|
|
@@ -22593,6 +22625,20 @@ var init_overlay_schema = __esm(() => {
|
|
|
22593
22625
|
// ../src/config/overlay-loader.ts
|
|
22594
22626
|
import { existsSync as existsSync5, readFileSync as readFileSync5, readdirSync as readdirSync3, statSync as statSync5 } from "node:fs";
|
|
22595
22627
|
import { basename as basename4, resolve as resolve4 } from "node:path";
|
|
22628
|
+
function recordReadFailure(agentCfg, failure) {
|
|
22629
|
+
const node = agentCfg;
|
|
22630
|
+
const existing = node[OVERLAY_READ_FAILURES];
|
|
22631
|
+
if (Array.isArray(existing)) {
|
|
22632
|
+
existing.push(failure);
|
|
22633
|
+
return;
|
|
22634
|
+
}
|
|
22635
|
+
Object.defineProperty(agentCfg, OVERLAY_READ_FAILURES, {
|
|
22636
|
+
value: [failure],
|
|
22637
|
+
enumerable: false,
|
|
22638
|
+
configurable: true,
|
|
22639
|
+
writable: false
|
|
22640
|
+
});
|
|
22641
|
+
}
|
|
22596
22642
|
function deriveOverlayTitle(raw, fileName) {
|
|
22597
22643
|
const titleFromComment = raw.match(/^#[^\S\n]*name:[^\S\n]*(\S.*?)[^\S\n]*$/m)?.[1];
|
|
22598
22644
|
if (titleFromComment)
|
|
@@ -22602,17 +22648,39 @@ function deriveOverlayTitle(raw, fileName) {
|
|
|
22602
22648
|
return;
|
|
22603
22649
|
return base.length > 0 ? base : undefined;
|
|
22604
22650
|
}
|
|
22651
|
+
function readOverlayFile(agentName, file, agentCfg, warnings, source) {
|
|
22652
|
+
try {
|
|
22653
|
+
return readFileSync5(file, "utf-8");
|
|
22654
|
+
} catch (err) {
|
|
22655
|
+
const code = err.code;
|
|
22656
|
+
if (code === "ENOENT")
|
|
22657
|
+
return;
|
|
22658
|
+
const w = {
|
|
22659
|
+
agent: agentName,
|
|
22660
|
+
file,
|
|
22661
|
+
reason: `read error: ${err.message}`,
|
|
22662
|
+
code: code ?? "EUNKNOWN"
|
|
22663
|
+
};
|
|
22664
|
+
recordReadFailure(agentCfg, { file, code: w.code, source });
|
|
22665
|
+
warnings.push(w);
|
|
22666
|
+
console.warn(`[switchroom] overlay-loader: agent='${agentName}' file='${file}': ${w.reason}`);
|
|
22667
|
+
return;
|
|
22668
|
+
}
|
|
22669
|
+
}
|
|
22605
22670
|
function overlayDirFor(agentName, subdir) {
|
|
22606
22671
|
const base = resolveDualPath(`~/.switchroom/agents/${agentName}/${subdir}`);
|
|
22607
22672
|
return resolve4(base);
|
|
22608
22673
|
}
|
|
22609
|
-
function listYamlFiles(dir) {
|
|
22674
|
+
function listYamlFiles(dir, onUnreadableDir) {
|
|
22610
22675
|
if (!existsSync5(dir))
|
|
22611
22676
|
return [];
|
|
22612
22677
|
let entries;
|
|
22613
22678
|
try {
|
|
22614
22679
|
entries = readdirSync3(dir);
|
|
22615
|
-
} catch {
|
|
22680
|
+
} catch (err) {
|
|
22681
|
+
const code = err.code;
|
|
22682
|
+
if (code !== "ENOENT")
|
|
22683
|
+
onUnreadableDir?.(code ?? "EUNKNOWN");
|
|
22616
22684
|
return [];
|
|
22617
22685
|
}
|
|
22618
22686
|
const out = [];
|
|
@@ -22650,12 +22718,24 @@ function applyAgentOverlays(config) {
|
|
|
22650
22718
|
for (const [agentName, agentCfg] of Object.entries(agents)) {
|
|
22651
22719
|
try {
|
|
22652
22720
|
const scheduleDir = overlayDirFor(agentName, "schedule.d");
|
|
22653
|
-
const files = listYamlFiles(scheduleDir)
|
|
22721
|
+
const files = listYamlFiles(scheduleDir, (code) => {
|
|
22722
|
+
const w = {
|
|
22723
|
+
agent: agentName,
|
|
22724
|
+
file: scheduleDir,
|
|
22725
|
+
reason: `read error: cannot list overlay directory (${code})`,
|
|
22726
|
+
code
|
|
22727
|
+
};
|
|
22728
|
+
recordReadFailure(agentCfg, { file: scheduleDir, code, source: "schedule" });
|
|
22729
|
+
warnings.push(w);
|
|
22730
|
+
console.warn(`[switchroom] overlay-loader: agent='${agentName}' file='${scheduleDir}': ${w.reason}`);
|
|
22731
|
+
});
|
|
22654
22732
|
if (files.length > 0) {
|
|
22655
22733
|
const merged = [...agentCfg.schedule ?? []];
|
|
22656
22734
|
for (const file of files) {
|
|
22735
|
+
const raw = readOverlayFile(agentName, file, agentCfg, warnings, "schedule");
|
|
22736
|
+
if (raw === undefined)
|
|
22737
|
+
continue;
|
|
22657
22738
|
try {
|
|
22658
|
-
const raw = readFileSync5(file, "utf-8");
|
|
22659
22739
|
const parsed = import_yaml.parse(raw);
|
|
22660
22740
|
const doc = OverlayDocSchema.parse(parsed);
|
|
22661
22741
|
const title = deriveOverlayTitle(raw, basename4(file));
|
|
@@ -22690,13 +22770,25 @@ function applyAgentOverlays(config) {
|
|
|
22690
22770
|
}
|
|
22691
22771
|
try {
|
|
22692
22772
|
const skillsDir = overlayDirFor(agentName, "skills.d");
|
|
22693
|
-
const skillFiles = listYamlFiles(skillsDir)
|
|
22773
|
+
const skillFiles = listYamlFiles(skillsDir, (code) => {
|
|
22774
|
+
const w = {
|
|
22775
|
+
agent: agentName,
|
|
22776
|
+
file: skillsDir,
|
|
22777
|
+
reason: `read error: cannot list overlay directory (${code})`,
|
|
22778
|
+
code
|
|
22779
|
+
};
|
|
22780
|
+
recordReadFailure(agentCfg, { file: skillsDir, code, source: "skills" });
|
|
22781
|
+
warnings.push(w);
|
|
22782
|
+
console.warn(`[switchroom] overlay-loader: agent='${agentName}' file='${skillsDir}': ${w.reason}`);
|
|
22783
|
+
});
|
|
22694
22784
|
if (skillFiles.length === 0) {} else {
|
|
22695
22785
|
const merged = [...agentCfg.skills ?? []];
|
|
22696
22786
|
const seen = new Set(merged);
|
|
22697
22787
|
for (const file of skillFiles) {
|
|
22788
|
+
const raw = readOverlayFile(agentName, file, agentCfg, warnings, "skills");
|
|
22789
|
+
if (raw === undefined)
|
|
22790
|
+
continue;
|
|
22698
22791
|
try {
|
|
22699
|
-
const raw = readFileSync5(file, "utf-8");
|
|
22700
22792
|
const parsed = import_yaml.parse(raw);
|
|
22701
22793
|
const doc = OverlayDocSchema.parse(parsed);
|
|
22702
22794
|
for (const skillName of doc.skills ?? []) {
|
|
@@ -22724,7 +22816,7 @@ function applyAgentOverlays(config) {
|
|
|
22724
22816
|
}
|
|
22725
22817
|
return { config, warnings };
|
|
22726
22818
|
}
|
|
22727
|
-
var import_yaml, OVERLAY_SOURCE, OVERLAY_TITLE;
|
|
22819
|
+
var import_yaml, OVERLAY_SOURCE, OVERLAY_TITLE, OVERLAY_READ_FAILURES;
|
|
22728
22820
|
var init_overlay_loader = __esm(() => {
|
|
22729
22821
|
init_zod();
|
|
22730
22822
|
init_overlay_schema();
|
|
@@ -22732,6 +22824,7 @@ var init_overlay_loader = __esm(() => {
|
|
|
22732
22824
|
import_yaml = __toESM(require_dist(), 1);
|
|
22733
22825
|
OVERLAY_SOURCE = Symbol.for("switchroom.config.overlay-source");
|
|
22734
22826
|
OVERLAY_TITLE = Symbol.for("switchroom.config.overlay-title");
|
|
22827
|
+
OVERLAY_READ_FAILURES = Symbol.for("switchroom.config.overlay-read-failures");
|
|
22735
22828
|
});
|
|
22736
22829
|
|
|
22737
22830
|
// ../src/config/merge.ts
|
|
@@ -39556,6 +39649,22 @@ function redactAuthCodeMessage(api, chatId, messageId, log) {
|
|
|
39556
39649
|
|
|
39557
39650
|
// ask-user.ts
|
|
39558
39651
|
import { randomBytes as randomBytes2 } from "crypto";
|
|
39652
|
+
|
|
39653
|
+
// gateway/source-message-id.ts
|
|
39654
|
+
var MAX_TELEGRAM_MESSAGE_ID = 2 ** 31;
|
|
39655
|
+
function parseSourceMessageId(raw) {
|
|
39656
|
+
if (raw == null)
|
|
39657
|
+
return null;
|
|
39658
|
+
const s = String(raw);
|
|
39659
|
+
if (!/^\d+$/.test(s))
|
|
39660
|
+
return null;
|
|
39661
|
+
const n = Number(s);
|
|
39662
|
+
if (!Number.isSafeInteger(n) || n <= 0 || n >= MAX_TELEGRAM_MESSAGE_ID)
|
|
39663
|
+
return null;
|
|
39664
|
+
return n;
|
|
39665
|
+
}
|
|
39666
|
+
|
|
39667
|
+
// ask-user.ts
|
|
39559
39668
|
var ASK_USER_DEFAULT_TIMEOUT_MS = 300000;
|
|
39560
39669
|
var ASK_USER_MAX_TIMEOUT_MS = 1800000;
|
|
39561
39670
|
var ASK_USER_MIN_TIMEOUT_MS = 5000;
|
|
@@ -39592,13 +39701,7 @@ function validateAskUserArgs(args) {
|
|
|
39592
39701
|
throw new Error("ask_user: message_thread_id must be a positive integer string");
|
|
39593
39702
|
}
|
|
39594
39703
|
}
|
|
39595
|
-
|
|
39596
|
-
if (args.reply_to != null) {
|
|
39597
|
-
replyTo = Number(args.reply_to);
|
|
39598
|
-
if (!Number.isFinite(replyTo) || replyTo <= 0) {
|
|
39599
|
-
throw new Error("ask_user: reply_to must be a positive integer string");
|
|
39600
|
-
}
|
|
39601
|
-
}
|
|
39704
|
+
const replyTo = parseSourceMessageId(args.reply_to) ?? undefined;
|
|
39602
39705
|
let timeoutMs = args.timeout_ms ?? ASK_USER_DEFAULT_TIMEOUT_MS;
|
|
39603
39706
|
if (typeof timeoutMs !== "number" || !Number.isFinite(timeoutMs)) {
|
|
39604
39707
|
throw new Error("ask_user: timeout_ms must be a number");
|
|
@@ -39678,6 +39781,7 @@ function applyChecklistPatch(cl, patch) {
|
|
|
39678
39781
|
return { title: patch.title ?? cl.title, tasks };
|
|
39679
39782
|
}
|
|
39680
39783
|
function buildNativeChecklistPayload(p) {
|
|
39784
|
+
const replyAnchor = parseSourceMessageId(p.replyToMessageId);
|
|
39681
39785
|
return {
|
|
39682
39786
|
business_connection_id: p.businessConnectionId,
|
|
39683
39787
|
chat_id: p.chatId,
|
|
@@ -39685,7 +39789,7 @@ function buildNativeChecklistPayload(p) {
|
|
|
39685
39789
|
title: p.title,
|
|
39686
39790
|
tasks: p.tasks.map((t) => ({ id: t.id, text: t.text }))
|
|
39687
39791
|
},
|
|
39688
|
-
...
|
|
39792
|
+
...replyAnchor != null ? { reply_parameters: { message_id: replyAnchor } } : {},
|
|
39689
39793
|
...p.protectContent === true ? { protect_content: true } : {}
|
|
39690
39794
|
};
|
|
39691
39795
|
}
|
|
@@ -39969,13 +40073,7 @@ function resolveStickerSendArgs(raw, aliasMap) {
|
|
|
39969
40073
|
throw new Error("send_sticker: message_thread_id must be a positive integer string");
|
|
39970
40074
|
}
|
|
39971
40075
|
}
|
|
39972
|
-
|
|
39973
|
-
if (raw.reply_to != null) {
|
|
39974
|
-
replyTo = Number(raw.reply_to);
|
|
39975
|
-
if (!Number.isFinite(replyTo) || replyTo <= 0) {
|
|
39976
|
-
throw new Error("send_sticker: reply_to must be a positive integer string");
|
|
39977
|
-
}
|
|
39978
|
-
}
|
|
40076
|
+
const replyTo = parseSourceMessageId(raw.reply_to) ?? undefined;
|
|
39979
40077
|
return {
|
|
39980
40078
|
chatId: raw.chat_id,
|
|
39981
40079
|
fileId,
|
|
@@ -40027,13 +40125,7 @@ function resolveGifSendArgs(raw) {
|
|
|
40027
40125
|
throw new Error("send_gif: message_thread_id must be a positive integer string");
|
|
40028
40126
|
}
|
|
40029
40127
|
}
|
|
40030
|
-
|
|
40031
|
-
if (raw.reply_to != null) {
|
|
40032
|
-
replyTo = Number(raw.reply_to);
|
|
40033
|
-
if (!Number.isFinite(replyTo) || replyTo <= 0) {
|
|
40034
|
-
throw new Error("send_gif: reply_to must be a positive integer string");
|
|
40035
|
-
}
|
|
40036
|
-
}
|
|
40128
|
+
const replyTo = parseSourceMessageId(raw.reply_to) ?? undefined;
|
|
40037
40129
|
return {
|
|
40038
40130
|
chatId: raw.chat_id,
|
|
40039
40131
|
animationRef,
|
|
@@ -50147,6 +50239,20 @@ class PinRightsCache2 {
|
|
|
50147
50239
|
}
|
|
50148
50240
|
}
|
|
50149
50241
|
|
|
50242
|
+
// gateway/source-message-id.ts
|
|
50243
|
+
var MAX_TELEGRAM_MESSAGE_ID2 = 2 ** 31;
|
|
50244
|
+
function parseSourceMessageId2(raw) {
|
|
50245
|
+
if (raw == null)
|
|
50246
|
+
return null;
|
|
50247
|
+
const s = String(raw);
|
|
50248
|
+
if (!/^\d+$/.test(s))
|
|
50249
|
+
return null;
|
|
50250
|
+
const n = Number(s);
|
|
50251
|
+
if (!Number.isSafeInteger(n) || n <= 0 || n >= MAX_TELEGRAM_MESSAGE_ID2)
|
|
50252
|
+
return null;
|
|
50253
|
+
return n;
|
|
50254
|
+
}
|
|
50255
|
+
|
|
50150
50256
|
// gateway/permission-timeout.ts
|
|
50151
50257
|
var SIGNATURE_SEP = String.fromCharCode(0);
|
|
50152
50258
|
function permissionSignature(toolName, inputPreview) {
|
|
@@ -51160,7 +51266,7 @@ function createAuthBrokerClient() {
|
|
|
51160
51266
|
listState: () => broker.listState(),
|
|
51161
51267
|
setActive: (label) => broker.setActive(label),
|
|
51162
51268
|
markExhausted: (until) => broker.markExhausted(until),
|
|
51163
|
-
markThrottled: (until) => broker.markThrottled(until),
|
|
51269
|
+
markThrottled: (until, probeOnly) => broker.markThrottled(until, probeOnly),
|
|
51164
51270
|
rmAccount: (label) => broker.rmAccount(label),
|
|
51165
51271
|
refreshAccount: (label) => broker.refreshAccount(label),
|
|
51166
51272
|
setOverride: (agent, account) => broker.setOverride(agent, account),
|
|
@@ -73918,7 +74024,7 @@ function createAuthBrokerClient2() {
|
|
|
73918
74024
|
listState: () => broker.listState(),
|
|
73919
74025
|
setActive: (label) => broker.setActive(label),
|
|
73920
74026
|
markExhausted: (until) => broker.markExhausted(until),
|
|
73921
|
-
markThrottled: (until) => broker.markThrottled(until),
|
|
74027
|
+
markThrottled: (until, probeOnly) => broker.markThrottled(until, probeOnly),
|
|
73922
74028
|
rmAccount: (label) => broker.rmAccount(label),
|
|
73923
74029
|
refreshAccount: (label) => broker.refreshAccount(label),
|
|
73924
74030
|
setOverride: (agent, account) => broker.setOverride(agent, account),
|
|
@@ -74337,12 +74443,13 @@ class AuthBrokerClient2 {
|
|
|
74337
74443
|
const data = await this.send(req);
|
|
74338
74444
|
return data;
|
|
74339
74445
|
}
|
|
74340
|
-
async markThrottled(until) {
|
|
74446
|
+
async markThrottled(until, probeOnly = false) {
|
|
74341
74447
|
const data = await this.send({
|
|
74342
74448
|
v: PROTOCOL_VERSION,
|
|
74343
74449
|
id: randomUUID5(),
|
|
74344
74450
|
op: "mark-throttled",
|
|
74345
|
-
until
|
|
74451
|
+
until,
|
|
74452
|
+
...probeOnly ? { probeOnly: true } : {}
|
|
74346
74453
|
});
|
|
74347
74454
|
return data;
|
|
74348
74455
|
}
|
|
@@ -77042,6 +77149,15 @@ function classify429Detail2(text4) {
|
|
|
77042
77149
|
return "litellm-local";
|
|
77043
77150
|
return "generic-transient";
|
|
77044
77151
|
}
|
|
77152
|
+
function classification429WarrantsCorroboration(classification) {
|
|
77153
|
+
return classification === "generic-transient";
|
|
77154
|
+
}
|
|
77155
|
+
function routeRateLimit429(classification, runner, agent) {
|
|
77156
|
+
if (!classification429WarrantsCorroboration(classification))
|
|
77157
|
+
return false;
|
|
77158
|
+
runner.fireProbeOnly(agent);
|
|
77159
|
+
return true;
|
|
77160
|
+
}
|
|
77045
77161
|
function build429ClassifiedMetric(opts) {
|
|
77046
77162
|
const detail = typeof opts.detail === "string" ? opts.detail : "";
|
|
77047
77163
|
const litellm = parseLitellmLimitDetail(detail, new Date(opts.now));
|
|
@@ -77138,6 +77254,12 @@ function createThrottleTierRunner(deps) {
|
|
|
77138
77254
|
deps.log(`[throttle-tier] resume suppressed (${verdict}) reason=${reason}`);
|
|
77139
77255
|
}
|
|
77140
77256
|
}
|
|
77257
|
+
async function announceEscalation(client3, account, rolledTo, triggerAgent, armedAtMs) {
|
|
77258
|
+
deps.log(`[throttle-tier] escalated to wall account=${account ?? "?"} ` + `rolledTo=${rolledTo ?? "none (all blocked)"}`);
|
|
77259
|
+
await broadcastDeduped(client3, "throttle-escalation", account, renderThrottleEscalationNotice({ account, agent: triggerAgent, rolledTo }));
|
|
77260
|
+
if (rolledTo)
|
|
77261
|
+
nudgeResume("throttle-escalation-resume", armedAtMs);
|
|
77262
|
+
}
|
|
77141
77263
|
async function fire(triggerAgent, throttledUntilMs, resetParsed) {
|
|
77142
77264
|
const armedAtMs = now();
|
|
77143
77265
|
let client3 = null;
|
|
@@ -77158,10 +77280,7 @@ function createThrottleTierRunner(deps) {
|
|
|
77158
77280
|
deps.log(`[throttle-tier] markThrottled failed agent=${triggerAgent}: ${err?.message ?? err}`);
|
|
77159
77281
|
}
|
|
77160
77282
|
if (escalated) {
|
|
77161
|
-
|
|
77162
|
-
await broadcastDeduped(client3, "throttle-escalation", account, renderThrottleEscalationNotice({ account, agent: triggerAgent, rolledTo }));
|
|
77163
|
-
if (rolledTo)
|
|
77164
|
-
nudgeResume("throttle-escalation-resume", armedAtMs);
|
|
77283
|
+
await announceEscalation(client3, account, rolledTo, triggerAgent, armedAtMs);
|
|
77165
77284
|
return;
|
|
77166
77285
|
}
|
|
77167
77286
|
const cooldownKey = account ?? `agent:${triggerAgent}`;
|
|
@@ -77186,8 +77305,34 @@ function createThrottleTierRunner(deps) {
|
|
|
77186
77305
|
nudgeResume("throttle-retry-resume", armedAtMs);
|
|
77187
77306
|
}, delayMs);
|
|
77188
77307
|
}
|
|
77308
|
+
async function fireProbeOnly(triggerAgent) {
|
|
77309
|
+
const armedAtMs = now();
|
|
77310
|
+
let client3 = null;
|
|
77311
|
+
let account = null;
|
|
77312
|
+
let escalated = false;
|
|
77313
|
+
let rolledTo = null;
|
|
77314
|
+
try {
|
|
77315
|
+
client3 = await deps.getBrokerClient();
|
|
77316
|
+
if (client3) {
|
|
77317
|
+
const r = await client3.markThrottled(now() + 1, true);
|
|
77318
|
+
account = r.account;
|
|
77319
|
+
escalated = r.escalated;
|
|
77320
|
+
rolledTo = r.rolledTo ?? null;
|
|
77321
|
+
} else {
|
|
77322
|
+
deps.log(`[throttle-tier] broker unreachable \u2014 probe-only skipped agent=${triggerAgent}`);
|
|
77323
|
+
}
|
|
77324
|
+
} catch (err) {
|
|
77325
|
+
deps.log(`[throttle-tier] probe-only markThrottled failed agent=${triggerAgent}: ${err?.message ?? err}`);
|
|
77326
|
+
}
|
|
77327
|
+
if (escalated) {
|
|
77328
|
+
await announceEscalation(client3, account, rolledTo, triggerAgent, armedAtMs);
|
|
77329
|
+
return;
|
|
77330
|
+
}
|
|
77331
|
+
deps.log(`[throttle-tier] generic-transient probe-only inert (no wall) agent=${triggerAgent}`);
|
|
77332
|
+
}
|
|
77189
77333
|
return {
|
|
77190
77334
|
fire,
|
|
77335
|
+
fireProbeOnly,
|
|
77191
77336
|
inspect: () => ({ noticeState, nudgePending: pendingNudge != null })
|
|
77192
77337
|
};
|
|
77193
77338
|
}
|
|
@@ -79396,6 +79541,26 @@ function stampsHandbackMarker(source) {
|
|
|
79396
79541
|
return known.decoupledCompletion;
|
|
79397
79542
|
}
|
|
79398
79543
|
var HANDBACK_RECENCY_WINDOW_MS = 60000;
|
|
79544
|
+
var TASK_NOTIFICATION_DEDUP_TTL_MS = 30000;
|
|
79545
|
+
var NOTIF_TERMINAL_STATUSES = new Set(["completed", "failed", "killed"]);
|
|
79546
|
+
|
|
79547
|
+
class CliTaskNotificationLedger {
|
|
79548
|
+
seen = new Map;
|
|
79549
|
+
record(taskId, status, now) {
|
|
79550
|
+
if (taskId.length === 0 || !NOTIF_TERMINAL_STATUSES.has(status))
|
|
79551
|
+
return;
|
|
79552
|
+
this.seen.set(taskId, now);
|
|
79553
|
+
for (const [id, ts] of this.seen) {
|
|
79554
|
+
if (now - ts > TASK_NOTIFICATION_DEDUP_TTL_MS)
|
|
79555
|
+
this.seen.delete(id);
|
|
79556
|
+
}
|
|
79557
|
+
}
|
|
79558
|
+
seenRecently(taskId, now) {
|
|
79559
|
+
const ts = this.seen.get(taskId);
|
|
79560
|
+
return ts != null && now - ts <= TASK_NOTIFICATION_DEDUP_TTL_MS;
|
|
79561
|
+
}
|
|
79562
|
+
}
|
|
79563
|
+
var cliTaskNotifLedger = new CliTaskNotificationLedger;
|
|
79399
79564
|
var MAIN_THREAD_KEY = "<main>";
|
|
79400
79565
|
|
|
79401
79566
|
class SubagentHandbackMarker {
|
|
@@ -80144,7 +80309,7 @@ async function sendReply(deps, req) {
|
|
|
80144
80309
|
}
|
|
80145
80310
|
const files = args.files ?? [];
|
|
80146
80311
|
const quoteOptIn = args.quote !== false;
|
|
80147
|
-
let reply_to =
|
|
80312
|
+
let reply_to = parseSourceMessageId(args.reply_to) ?? undefined;
|
|
80148
80313
|
const protectContent = args.protect_content === true;
|
|
80149
80314
|
const quoteText = args.quote_text;
|
|
80150
80315
|
const access = loadAccess();
|
|
@@ -80949,6 +81114,22 @@ function endsWithSilentMarker(text4) {
|
|
|
80949
81114
|
return false;
|
|
80950
81115
|
return isSilentFlushMarker(lines[lines.length - 1]);
|
|
80951
81116
|
}
|
|
81117
|
+
function isSilentSentinelCardOutcome(input) {
|
|
81118
|
+
if (input.finalAnswerEverDelivered)
|
|
81119
|
+
return false;
|
|
81120
|
+
if (isSilentFlushMarker(input.lastReplyText) || isCompositeSilentNoise(input.lastReplyText)) {
|
|
81121
|
+
return true;
|
|
81122
|
+
}
|
|
81123
|
+
if (!input.replyCalled) {
|
|
81124
|
+
const joined = input.capturedText.join(`
|
|
81125
|
+
|
|
81126
|
+
`).trim();
|
|
81127
|
+
if (joined.length > 0 && (isSilentFlushMarker(joined) || isCompositeSilentNoise(joined) || endsWithSilentMarker(joined))) {
|
|
81128
|
+
return true;
|
|
81129
|
+
}
|
|
81130
|
+
}
|
|
81131
|
+
return false;
|
|
81132
|
+
}
|
|
80952
81133
|
function decideTurnFlush(input) {
|
|
80953
81134
|
const flushEnabled = input.flushEnabled !== false;
|
|
80954
81135
|
if (!flushEnabled)
|
|
@@ -81934,20 +82115,6 @@ function formatEventDetail(event) {
|
|
|
81934
82115
|
}
|
|
81935
82116
|
}
|
|
81936
82117
|
|
|
81937
|
-
// gateway/source-message-id.ts
|
|
81938
|
-
var MAX_TELEGRAM_MESSAGE_ID = 2 ** 31;
|
|
81939
|
-
function parseSourceMessageId(raw) {
|
|
81940
|
-
if (raw == null)
|
|
81941
|
-
return null;
|
|
81942
|
-
const s = String(raw);
|
|
81943
|
-
if (!/^\d+$/.test(s))
|
|
81944
|
-
return null;
|
|
81945
|
-
const n = Number(s);
|
|
81946
|
-
if (!Number.isSafeInteger(n) || n <= 0 || n >= MAX_TELEGRAM_MESSAGE_ID)
|
|
81947
|
-
return null;
|
|
81948
|
-
return n;
|
|
81949
|
-
}
|
|
81950
|
-
|
|
81951
82118
|
// gateway/status-surface-log.ts
|
|
81952
82119
|
function formatTurnLifecycle(action, reason, t, now) {
|
|
81953
82120
|
const ageMs = action === "clear" ? Math.max(0, now - t.startedAt) : 0;
|
|
@@ -82614,8 +82781,7 @@ function handleSessionEvent(deps, ev) {
|
|
|
82614
82781
|
}
|
|
82615
82782
|
if (QUEUED_CARD_ENABLED && !handbackOwnsSurface) {
|
|
82616
82783
|
const cardChatId = ev.chatId;
|
|
82617
|
-
const
|
|
82618
|
-
const replyTo = replyToRaw != null && Number.isFinite(replyToRaw) ? replyToRaw : null;
|
|
82784
|
+
const replyTo = parseSourceMessageId(ev.messageId);
|
|
82619
82785
|
openQueuedCard(deps, cardChatId, enqThreadIdNum ?? null, replyTo).then((cardId) => {
|
|
82620
82786
|
if (cardId == null)
|
|
82621
82787
|
return;
|
|
@@ -83899,7 +84065,13 @@ function createNarrativeLane(deps) {
|
|
|
83899
84065
|
clearActivityCardRecord(ACTIVITY_CARD_STORE_PATH, activityCardStoreFs, statusKey(chat, thread), id);
|
|
83900
84066
|
}
|
|
83901
84067
|
await reconcileStatusPin(`fg:${statusKey(chat, thread)}`, chat, { pinned: false });
|
|
83902
|
-
|
|
84068
|
+
const silentSentinelTurn = isSilentSentinelCardOutcome({
|
|
84069
|
+
replyCalled: turn.replyCalled,
|
|
84070
|
+
lastReplyText: turn.lastReplyText,
|
|
84071
|
+
capturedText: turn.capturedText,
|
|
84072
|
+
finalAnswerEverDelivered: turn.finalAnswerEverDelivered
|
|
84073
|
+
});
|
|
84074
|
+
if (CLEAR_STATUS_ON_COMPLETION || silentSentinelTurn) {
|
|
83903
84075
|
try {
|
|
83904
84076
|
await robustApiCall(() => bot.api.deleteMessage(chat, id), { chat_id: chat, ...thread != null ? { threadId: thread } : {}, verb: "activity-summary.delete" });
|
|
83905
84077
|
} catch (err) {
|
|
@@ -84148,6 +84320,26 @@ function isTurnFlushSafetyEnabled(env = process.env) {
|
|
|
84148
84320
|
}
|
|
84149
84321
|
|
|
84150
84322
|
// gateway/subagent-handback-marker.ts
|
|
84323
|
+
var TASK_NOTIFICATION_DEDUP_TTL_MS2 = 30000;
|
|
84324
|
+
var NOTIF_TERMINAL_STATUSES2 = new Set(["completed", "failed", "killed"]);
|
|
84325
|
+
|
|
84326
|
+
class CliTaskNotificationLedger2 {
|
|
84327
|
+
seen = new Map;
|
|
84328
|
+
record(taskId, status, now) {
|
|
84329
|
+
if (taskId.length === 0 || !NOTIF_TERMINAL_STATUSES2.has(status))
|
|
84330
|
+
return;
|
|
84331
|
+
this.seen.set(taskId, now);
|
|
84332
|
+
for (const [id, ts] of this.seen) {
|
|
84333
|
+
if (now - ts > TASK_NOTIFICATION_DEDUP_TTL_MS2)
|
|
84334
|
+
this.seen.delete(id);
|
|
84335
|
+
}
|
|
84336
|
+
}
|
|
84337
|
+
seenRecently(taskId, now) {
|
|
84338
|
+
const ts = this.seen.get(taskId);
|
|
84339
|
+
return ts != null && now - ts <= TASK_NOTIFICATION_DEDUP_TTL_MS2;
|
|
84340
|
+
}
|
|
84341
|
+
}
|
|
84342
|
+
var cliTaskNotifLedger2 = new CliTaskNotificationLedger2;
|
|
84151
84343
|
var MAIN_THREAD_KEY2 = "<main>";
|
|
84152
84344
|
|
|
84153
84345
|
class SubagentHandbackMarker2 {
|
|
@@ -92982,19 +93174,121 @@ function makeRepresentRedeliveryGuard(deps) {
|
|
|
92982
93174
|
return true;
|
|
92983
93175
|
};
|
|
92984
93176
|
}
|
|
92985
|
-
|
|
93177
|
+
var DEFAULT_DRAIN_DEFER_STALE_GAP_MS = 15000;
|
|
93178
|
+
function makeSessionBusyDrainDeferral(boundMs, staleGapMs = DEFAULT_DRAIN_DEFER_STALE_GAP_MS) {
|
|
92986
93179
|
let deferringSince = null;
|
|
93180
|
+
let lastCallAt = null;
|
|
92987
93181
|
return (busy, now) => {
|
|
93182
|
+
const gapSinceLastCall = lastCallAt == null ? null : now - lastCallAt;
|
|
93183
|
+
lastCallAt = now;
|
|
92988
93184
|
if (!busy || boundMs <= 0) {
|
|
92989
93185
|
deferringSince = null;
|
|
92990
93186
|
return false;
|
|
92991
93187
|
}
|
|
92992
|
-
if (deferringSince == null)
|
|
93188
|
+
if (deferringSince == null || staleGapMs > 0 && gapSinceLastCall != null && gapSinceLastCall > staleGapMs) {
|
|
92993
93189
|
deferringSince = now;
|
|
93190
|
+
}
|
|
92994
93191
|
return now - deferringSince < boundMs;
|
|
92995
93192
|
};
|
|
92996
93193
|
}
|
|
92997
93194
|
|
|
93195
|
+
// gateway/pending-inbound-buffer.ts
|
|
93196
|
+
function redeliverBufferedInbound2(buffer, agent, send, spool, onDelivered) {
|
|
93197
|
+
const pending = buffer.drain(agent);
|
|
93198
|
+
let redelivered = 0;
|
|
93199
|
+
let rebuffered = 0;
|
|
93200
|
+
let retracted = 0;
|
|
93201
|
+
for (const { merged, originals } of planBufferedRedelivery2(pending)) {
|
|
93202
|
+
let proceed = true;
|
|
93203
|
+
if (buffer.beforeRedeliver != null) {
|
|
93204
|
+
try {
|
|
93205
|
+
proceed = buffer.beforeRedeliver(merged);
|
|
93206
|
+
} catch (e) {
|
|
93207
|
+
proceed = true;
|
|
93208
|
+
process.stderr.write(`redeliver beforeRedeliver threw \u2014 failing open (deliver): ${String(e)}
|
|
93209
|
+
`);
|
|
93210
|
+
}
|
|
93211
|
+
}
|
|
93212
|
+
if (!proceed) {
|
|
93213
|
+
for (const o of originals)
|
|
93214
|
+
spool?.ack(o);
|
|
93215
|
+
retracted += originals.length;
|
|
93216
|
+
continue;
|
|
93217
|
+
}
|
|
93218
|
+
let delivered = false;
|
|
93219
|
+
try {
|
|
93220
|
+
delivered = send(merged);
|
|
93221
|
+
} catch {
|
|
93222
|
+
delivered = false;
|
|
93223
|
+
}
|
|
93224
|
+
if (delivered) {
|
|
93225
|
+
for (const o of originals)
|
|
93226
|
+
spool?.ack(o);
|
|
93227
|
+
redelivered += originals.length;
|
|
93228
|
+
onDelivered?.(merged, originals);
|
|
93229
|
+
} else {
|
|
93230
|
+
for (const o of originals)
|
|
93231
|
+
buffer.push(agent, o);
|
|
93232
|
+
rebuffered += originals.length;
|
|
93233
|
+
}
|
|
93234
|
+
}
|
|
93235
|
+
return { drained: pending.length, redelivered, rebuffered, retracted };
|
|
93236
|
+
}
|
|
93237
|
+
function isMergeableUserInbound2(msg) {
|
|
93238
|
+
return msg.type === "inbound" && (msg.meta == null || msg.meta.source == null && msg.meta.button_callback == null);
|
|
93239
|
+
}
|
|
93240
|
+
function inboundHasMedia2(msg) {
|
|
93241
|
+
return msg.imagePath != null || msg.attachment != null;
|
|
93242
|
+
}
|
|
93243
|
+
function planBufferedRedelivery2(pending) {
|
|
93244
|
+
const out = [];
|
|
93245
|
+
let run3 = [];
|
|
93246
|
+
let runHasMedia = false;
|
|
93247
|
+
const sameTarget = (a, b) => a.chatId === b.chatId && (a.threadId ?? null) === (b.threadId ?? null) && a.userId === b.userId;
|
|
93248
|
+
const flush = () => {
|
|
93249
|
+
if (run3.length === 0)
|
|
93250
|
+
return;
|
|
93251
|
+
out.push({ merged: run3.length === 1 ? run3[0] : mergeRun2(run3), originals: run3 });
|
|
93252
|
+
run3 = [];
|
|
93253
|
+
runHasMedia = false;
|
|
93254
|
+
};
|
|
93255
|
+
for (const msg of pending) {
|
|
93256
|
+
const msgHasMedia = inboundHasMedia2(msg);
|
|
93257
|
+
const canJoin = run3.length > 0 && isMergeableUserInbound2(msg) && isMergeableUserInbound2(run3[run3.length - 1]) && sameTarget(run3[run3.length - 1], msg) && !(runHasMedia && msgHasMedia);
|
|
93258
|
+
if (!canJoin)
|
|
93259
|
+
flush();
|
|
93260
|
+
run3.push(msg);
|
|
93261
|
+
runHasMedia = runHasMedia || msgHasMedia;
|
|
93262
|
+
}
|
|
93263
|
+
flush();
|
|
93264
|
+
return out;
|
|
93265
|
+
}
|
|
93266
|
+
var ATTACHMENT_META_RE2 = /^(image_path|attachment_)/;
|
|
93267
|
+
function mergeRun2(run3) {
|
|
93268
|
+
const last = run3[run3.length - 1];
|
|
93269
|
+
const mediaEntry = run3.find(inboundHasMedia2);
|
|
93270
|
+
const merged = {
|
|
93271
|
+
...last,
|
|
93272
|
+
text: run3.map((m) => m.text).join(`
|
|
93273
|
+
`)
|
|
93274
|
+
};
|
|
93275
|
+
delete merged.imagePath;
|
|
93276
|
+
delete merged.attachment;
|
|
93277
|
+
if (mediaEntry != null && mediaEntry !== last) {
|
|
93278
|
+
const splicedMeta = { ...merged.meta };
|
|
93279
|
+
for (const [k, v] of Object.entries(mediaEntry.meta)) {
|
|
93280
|
+
if (ATTACHMENT_META_RE2.test(k))
|
|
93281
|
+
splicedMeta[k] = v;
|
|
93282
|
+
}
|
|
93283
|
+
merged.meta = splicedMeta;
|
|
93284
|
+
}
|
|
93285
|
+
if (mediaEntry?.imagePath != null)
|
|
93286
|
+
merged.imagePath = mediaEntry.imagePath;
|
|
93287
|
+
if (mediaEntry?.attachment != null)
|
|
93288
|
+
merged.attachment = mediaEntry.attachment;
|
|
93289
|
+
return merged;
|
|
93290
|
+
}
|
|
93291
|
+
|
|
92998
93292
|
// gateway/cron-session.ts
|
|
92999
93293
|
var CRON_IDENTITY_SUFFIX = "-cron";
|
|
93000
93294
|
function cronIdentity(agent) {
|
|
@@ -93024,6 +93318,23 @@ function deliverInjectWithFallback(agentName3, meta, send) {
|
|
|
93024
93318
|
}
|
|
93025
93319
|
return { target, delivered: false, fellBackToMain: false };
|
|
93026
93320
|
}
|
|
93321
|
+
function drainCronBridgeOnRegister(client3, buffer, spool, log) {
|
|
93322
|
+
client3.send({ type: "status", status: "agent_connected" });
|
|
93323
|
+
const send = (msg) => {
|
|
93324
|
+
try {
|
|
93325
|
+
client3.send(msg);
|
|
93326
|
+
return true;
|
|
93327
|
+
} catch {
|
|
93328
|
+
return false;
|
|
93329
|
+
}
|
|
93330
|
+
};
|
|
93331
|
+
const result = redeliverBufferedInbound2(buffer, client3.agentName ?? "", send, spool);
|
|
93332
|
+
if (result.drained > 0 && log != null) {
|
|
93333
|
+
log(`telegram gateway: cron-bridge drain agent=${client3.agentName} ` + `drained=${result.drained} redelivered=${result.redelivered} ` + `rebuffered=${result.rebuffered}
|
|
93334
|
+
`);
|
|
93335
|
+
}
|
|
93336
|
+
return result;
|
|
93337
|
+
}
|
|
93027
93338
|
|
|
93028
93339
|
// gateway/obligation-ledger.ts
|
|
93029
93340
|
class ObligationLedger {
|
|
@@ -94586,103 +94897,6 @@ function formatEventDetail2(event) {
|
|
|
94586
94897
|
}
|
|
94587
94898
|
}
|
|
94588
94899
|
|
|
94589
|
-
// gateway/pending-inbound-buffer.ts
|
|
94590
|
-
function redeliverBufferedInbound2(buffer, agent, send, spool, onDelivered) {
|
|
94591
|
-
const pending = buffer.drain(agent);
|
|
94592
|
-
let redelivered = 0;
|
|
94593
|
-
let rebuffered = 0;
|
|
94594
|
-
let retracted = 0;
|
|
94595
|
-
for (const { merged, originals } of planBufferedRedelivery2(pending)) {
|
|
94596
|
-
let proceed = true;
|
|
94597
|
-
if (buffer.beforeRedeliver != null) {
|
|
94598
|
-
try {
|
|
94599
|
-
proceed = buffer.beforeRedeliver(merged);
|
|
94600
|
-
} catch (e) {
|
|
94601
|
-
proceed = true;
|
|
94602
|
-
process.stderr.write(`redeliver beforeRedeliver threw \u2014 failing open (deliver): ${String(e)}
|
|
94603
|
-
`);
|
|
94604
|
-
}
|
|
94605
|
-
}
|
|
94606
|
-
if (!proceed) {
|
|
94607
|
-
for (const o of originals)
|
|
94608
|
-
spool?.ack(o);
|
|
94609
|
-
retracted += originals.length;
|
|
94610
|
-
continue;
|
|
94611
|
-
}
|
|
94612
|
-
let delivered = false;
|
|
94613
|
-
try {
|
|
94614
|
-
delivered = send(merged);
|
|
94615
|
-
} catch {
|
|
94616
|
-
delivered = false;
|
|
94617
|
-
}
|
|
94618
|
-
if (delivered) {
|
|
94619
|
-
for (const o of originals)
|
|
94620
|
-
spool?.ack(o);
|
|
94621
|
-
redelivered += originals.length;
|
|
94622
|
-
onDelivered?.(merged, originals);
|
|
94623
|
-
} else {
|
|
94624
|
-
for (const o of originals)
|
|
94625
|
-
buffer.push(agent, o);
|
|
94626
|
-
rebuffered += originals.length;
|
|
94627
|
-
}
|
|
94628
|
-
}
|
|
94629
|
-
return { drained: pending.length, redelivered, rebuffered, retracted };
|
|
94630
|
-
}
|
|
94631
|
-
function isMergeableUserInbound2(msg) {
|
|
94632
|
-
return msg.type === "inbound" && (msg.meta == null || msg.meta.source == null && msg.meta.button_callback == null);
|
|
94633
|
-
}
|
|
94634
|
-
function inboundHasMedia2(msg) {
|
|
94635
|
-
return msg.imagePath != null || msg.attachment != null;
|
|
94636
|
-
}
|
|
94637
|
-
function planBufferedRedelivery2(pending) {
|
|
94638
|
-
const out = [];
|
|
94639
|
-
let run3 = [];
|
|
94640
|
-
let runHasMedia = false;
|
|
94641
|
-
const sameTarget = (a, b) => a.chatId === b.chatId && (a.threadId ?? null) === (b.threadId ?? null) && a.userId === b.userId;
|
|
94642
|
-
const flush = () => {
|
|
94643
|
-
if (run3.length === 0)
|
|
94644
|
-
return;
|
|
94645
|
-
out.push({ merged: run3.length === 1 ? run3[0] : mergeRun2(run3), originals: run3 });
|
|
94646
|
-
run3 = [];
|
|
94647
|
-
runHasMedia = false;
|
|
94648
|
-
};
|
|
94649
|
-
for (const msg of pending) {
|
|
94650
|
-
const msgHasMedia = inboundHasMedia2(msg);
|
|
94651
|
-
const canJoin = run3.length > 0 && isMergeableUserInbound2(msg) && isMergeableUserInbound2(run3[run3.length - 1]) && sameTarget(run3[run3.length - 1], msg) && !(runHasMedia && msgHasMedia);
|
|
94652
|
-
if (!canJoin)
|
|
94653
|
-
flush();
|
|
94654
|
-
run3.push(msg);
|
|
94655
|
-
runHasMedia = runHasMedia || msgHasMedia;
|
|
94656
|
-
}
|
|
94657
|
-
flush();
|
|
94658
|
-
return out;
|
|
94659
|
-
}
|
|
94660
|
-
var ATTACHMENT_META_RE2 = /^(image_path|attachment_)/;
|
|
94661
|
-
function mergeRun2(run3) {
|
|
94662
|
-
const last = run3[run3.length - 1];
|
|
94663
|
-
const mediaEntry = run3.find(inboundHasMedia2);
|
|
94664
|
-
const merged = {
|
|
94665
|
-
...last,
|
|
94666
|
-
text: run3.map((m) => m.text).join(`
|
|
94667
|
-
`)
|
|
94668
|
-
};
|
|
94669
|
-
delete merged.imagePath;
|
|
94670
|
-
delete merged.attachment;
|
|
94671
|
-
if (mediaEntry != null && mediaEntry !== last) {
|
|
94672
|
-
const splicedMeta = { ...merged.meta };
|
|
94673
|
-
for (const [k, v] of Object.entries(mediaEntry.meta)) {
|
|
94674
|
-
if (ATTACHMENT_META_RE2.test(k))
|
|
94675
|
-
splicedMeta[k] = v;
|
|
94676
|
-
}
|
|
94677
|
-
merged.meta = splicedMeta;
|
|
94678
|
-
}
|
|
94679
|
-
if (mediaEntry?.imagePath != null)
|
|
94680
|
-
merged.imagePath = mediaEntry.imagePath;
|
|
94681
|
-
if (mediaEntry?.attachment != null)
|
|
94682
|
-
merged.attachment = mediaEntry.attachment;
|
|
94683
|
-
return merged;
|
|
94684
|
-
}
|
|
94685
|
-
|
|
94686
94900
|
// gateway/obligation-turn-end.ts
|
|
94687
94901
|
function decideObligationTurnEnd(finalAnswerDelivered, replyCalled) {
|
|
94688
94902
|
return finalAnswerDelivered || replyCalled ? "close" : "note-ended";
|
|
@@ -96907,6 +97121,9 @@ function decideSubagentHandback(input) {
|
|
|
96907
97121
|
if (!input.isBackground) {
|
|
96908
97122
|
return { deliver: false, reason: "foreground" };
|
|
96909
97123
|
}
|
|
97124
|
+
if (input.cliTaskNotificationSeen === true) {
|
|
97125
|
+
return { deliver: false, reason: "cli-task-notification" };
|
|
97126
|
+
}
|
|
96910
97127
|
const chatId = input.fleetChatId || input.ownerChatId;
|
|
96911
97128
|
if (!chatId) {
|
|
96912
97129
|
return { deliver: false, reason: "no-chat" };
|
|
@@ -99732,6 +99949,14 @@ function determineRestartReason(opts) {
|
|
|
99732
99949
|
return "crash";
|
|
99733
99950
|
return "fresh";
|
|
99734
99951
|
}
|
|
99952
|
+
var BOOT_REASON_REUSE_WINDOW_MS = 5 * 60000;
|
|
99953
|
+
function determineBridgeReconnectReason(opts) {
|
|
99954
|
+
const { gatewayStartedAtMs, bootReason, bootReasonReuseWindowMs = BOOT_REASON_REUSE_WINDOW_MS } = opts;
|
|
99955
|
+
if (opts.marker == null && opts.cleanMarker == null && bootReason != null && opts.now - gatewayStartedAtMs < bootReasonReuseWindowMs) {
|
|
99956
|
+
return bootReason;
|
|
99957
|
+
}
|
|
99958
|
+
return determineRestartReason(opts);
|
|
99959
|
+
}
|
|
99735
99960
|
|
|
99736
99961
|
// gateway/update-announce.ts
|
|
99737
99962
|
import { existsSync as existsSync50, mkdirSync as mkdirSync44, openSync as openSync12, closeSync as closeSync12, readFileSync as readFileSync53 } from "node:fs";
|
|
@@ -102581,10 +102806,10 @@ function startOutboxSweep(deps) {
|
|
|
102581
102806
|
}
|
|
102582
102807
|
|
|
102583
102808
|
// ../src/build-info.ts
|
|
102584
|
-
var VERSION2 = "0.20.
|
|
102585
|
-
var COMMIT_SHA = "
|
|
102586
|
-
var COMMIT_DATE = "2026-08-
|
|
102587
|
-
var LATEST_PR =
|
|
102809
|
+
var VERSION2 = "0.20.9";
|
|
102810
|
+
var COMMIT_SHA = "63c4c44b";
|
|
102811
|
+
var COMMIT_DATE = "2026-08-05T03:51:28Z";
|
|
102812
|
+
var LATEST_PR = 4388;
|
|
102588
102813
|
var COMMITS_AHEAD_OF_TAG = 0;
|
|
102589
102814
|
|
|
102590
102815
|
// gateway/boot-version.ts
|
|
@@ -107761,6 +107986,7 @@ function emitGatewayOperatorEvent(event) {
|
|
|
107761
107986
|
agent,
|
|
107762
107987
|
shouldEmitCard: (a) => shouldEmitOperatorEvent(a, "rate-limited")
|
|
107763
107988
|
});
|
|
107989
|
+
routeRateLimit429(rateLimit429Classification, throttleTierRunner, agent);
|
|
107764
107990
|
if (surface === "litellm-local-notice") {
|
|
107765
107991
|
process.stderr.write(`telegram gateway: 429 classified litellm-proxy-local agent=${agent} \u2014 ` + `calm path, no account attribution, no failover
|
|
107766
107992
|
`);
|
|
@@ -108342,6 +108568,7 @@ var GATEWAY_STARTED_AT_MS = Date.now();
|
|
|
108342
108568
|
var BOOT_CARD_ENABLED = process.env.SWITCHROOM_BOOT_CARD !== "false";
|
|
108343
108569
|
var activeBootCard = null;
|
|
108344
108570
|
var bootCardPending = false;
|
|
108571
|
+
var bootReasonAtStartup = null;
|
|
108345
108572
|
var ISSUES_CARD_ENABLED = process.env.SWITCHROOM_ISSUES_CARD !== "false";
|
|
108346
108573
|
var activeIssuesCard = null;
|
|
108347
108574
|
var activeIssuesWatcher = null;
|
|
@@ -108835,13 +109062,7 @@ if (isGatewayMain)
|
|
|
108835
109062
|
process.stderr.write(`telegram gateway: bridge registered \u2014 agent=${client3.agentName}
|
|
108836
109063
|
`);
|
|
108837
109064
|
if (isCronIdentity(client3.agentName)) {
|
|
108838
|
-
client3
|
|
108839
|
-
const pending2 = pendingInboundBuffer.drain(client3.agentName ?? "");
|
|
108840
|
-
for (const m of pending2) {
|
|
108841
|
-
try {
|
|
108842
|
-
client3.send(m);
|
|
108843
|
-
} catch {}
|
|
108844
|
-
}
|
|
109065
|
+
drainCronBridgeOnRegister(client3, pendingInboundBuffer, inboundSpool ?? undefined, (l) => process.stderr.write(l));
|
|
108845
109066
|
return;
|
|
108846
109067
|
}
|
|
108847
109068
|
const bridgeUpEffects = client3.agentName != null ? shadowEmit2({ kind: "bridgeUp", at: Date.now() }) : [];
|
|
@@ -108888,7 +109109,14 @@ if (isGatewayMain)
|
|
|
108888
109109
|
`);
|
|
108889
109110
|
clearRestartMarker();
|
|
108890
109111
|
}
|
|
108891
|
-
const reason =
|
|
109112
|
+
const reason = determineBridgeReconnectReason({
|
|
109113
|
+
marker,
|
|
109114
|
+
cleanMarker,
|
|
109115
|
+
sessionMarker: storedSession,
|
|
109116
|
+
now: nowMs3,
|
|
109117
|
+
gatewayStartedAtMs: GATEWAY_STARTED_AT_MS,
|
|
109118
|
+
bootReason: bootReasonAtStartup
|
|
109119
|
+
});
|
|
108892
109120
|
const target = resolveBootChatId(marker, markerAgeMs);
|
|
108893
109121
|
if (target) {
|
|
108894
109122
|
const { chatId, threadId, ackMsgId } = target;
|
|
@@ -109036,6 +109264,8 @@ if (isGatewayMain)
|
|
|
109036
109264
|
}
|
|
109037
109265
|
}
|
|
109038
109266
|
const ev = msg.event;
|
|
109267
|
+
if (ev.kind === "task_notification")
|
|
109268
|
+
cliTaskNotifLedger2.record(ev.taskId, ev.status, Date.now());
|
|
109039
109269
|
handleSessionEvent2(ev);
|
|
109040
109270
|
toolFlightTracker.onEvent(ev);
|
|
109041
109271
|
if (pendingDeferredInterrupt != null && !toolFlightTracker.isMidToolCall()) {
|
|
@@ -109635,7 +109865,7 @@ if (isGatewayMain)
|
|
|
109635
109865
|
}
|
|
109636
109866
|
})();
|
|
109637
109867
|
var IDLE_DRAIN_INTERVAL_MS = 5000;
|
|
109638
|
-
var idleDrainBusyDefer = makeSessionBusyDrainDeferral(OBLIGATION_BACKGROUND_WORK_GRACE_MS);
|
|
109868
|
+
var idleDrainBusyDefer = makeSessionBusyDrainDeferral(OBLIGATION_BACKGROUND_WORK_GRACE_MS, IDLE_DRAIN_INTERVAL_MS * 3);
|
|
109639
109869
|
if (isGatewayMain && !STATIC) {
|
|
109640
109870
|
setInterval(() => {
|
|
109641
109871
|
const selfAgent = process.env.SWITCHROOM_AGENT_NAME ?? "";
|
|
@@ -109753,7 +109983,7 @@ async function executeSendChecklist(args) {
|
|
|
109753
109983
|
if (!Array.isArray(tasks) || tasks.length === 0)
|
|
109754
109984
|
throw new Error("send_checklist: tasks must be a non-empty array");
|
|
109755
109985
|
const threadId = args.message_thread_id != null ? Number(args.message_thread_id) : undefined;
|
|
109756
|
-
const replyTo =
|
|
109986
|
+
const replyTo = parseSourceMessageId2(args.reply_to) ?? undefined;
|
|
109757
109987
|
const protectContent = args.protect_content === true;
|
|
109758
109988
|
assertAllowedChat(chat_id);
|
|
109759
109989
|
const { title: redactedTitle, tasks: redactedTasks } = redactChecklistFields(title, tasks, (t) => redactOutboundText(t, "send_checklist"));
|
|
@@ -116154,6 +116384,7 @@ async function startGateway() {
|
|
|
116154
116384
|
} else {
|
|
116155
116385
|
const markerAgeMs = marker ? nowMs3 - marker.ts : undefined;
|
|
116156
116386
|
const reason = determineRestartReason({ marker, cleanMarker, sessionMarker: storedSession, now: nowMs3 });
|
|
116387
|
+
bootReasonAtStartup = reason;
|
|
116157
116388
|
const target = resolveBootChatId(marker, markerAgeMs);
|
|
116158
116389
|
if (reason === "crash") {
|
|
116159
116390
|
const cleanMarkerStale = cleanMarker ? !shouldSuppressRecoveryBanner(cleanMarker, nowMs3, DEFAULT_MAX_AGE_MS) : false;
|
|
@@ -116600,11 +116831,12 @@ async function startGateway() {
|
|
|
116600
116831
|
ownerChatId: hbOwnerDm,
|
|
116601
116832
|
taskDescription: description2,
|
|
116602
116833
|
resultText,
|
|
116603
|
-
jsonlAgentId: agentId
|
|
116834
|
+
jsonlAgentId: agentId,
|
|
116835
|
+
cliTaskNotificationSeen: cliTaskNotifLedger2.seenRecently(agentId, Date.now())
|
|
116604
116836
|
});
|
|
116605
116837
|
if (!decision.deliver) {
|
|
116606
|
-
if (decision.reason === "no-chat") {
|
|
116607
|
-
process.stderr.write(`telegram gateway: subagent-handback ${agentId} \u2014 no chat to deliver to
|
|
116838
|
+
if (decision.reason === "no-chat" || decision.reason === "cli-task-notification") {
|
|
116839
|
+
process.stderr.write(`telegram gateway: subagent-handback ${agentId} skipped \u2014 ${decision.reason === "no-chat" ? "no chat to deliver to" : "CLI task-notification already woke the parent for this completion (double-wake dedup)"}
|
|
116608
116840
|
`);
|
|
116609
116841
|
}
|
|
116610
116842
|
return;
|