switchroom 0.14.14 → 0.14.16
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/cli/switchroom.js +2 -2
- package/package.json +1 -1
- package/telegram-plugin/dist/gateway/gateway.js +448 -162
- package/telegram-plugin/gateway/gateway.ts +144 -8
- package/telegram-plugin/reaction-defer.ts +98 -0
- package/telegram-plugin/status-reactions.ts +31 -1
- package/telegram-plugin/subagent-watcher.ts +13 -0
- package/telegram-plugin/tests/reaction-defer.test.ts +187 -0
- package/telegram-plugin/tests/status-reactions.test.ts +79 -0
- package/telegram-plugin/tests/worker-activity-feed.test.ts +256 -0
- package/telegram-plugin/uat/scenarios/jtbd-worker-activity-feed-dm.test.ts +125 -0
- package/telegram-plugin/worker-activity-feed.ts +314 -0
|
@@ -6532,6 +6532,24 @@ var require_mod3 = __commonJS((exports) => {
|
|
|
6532
6532
|
__exportStar(require_worker(), exports);
|
|
6533
6533
|
});
|
|
6534
6534
|
|
|
6535
|
+
// card-format.ts
|
|
6536
|
+
function formatDuration(ms) {
|
|
6537
|
+
if (ms < 1000)
|
|
6538
|
+
return `${ms}ms`;
|
|
6539
|
+
const s = Math.floor(ms / 1000);
|
|
6540
|
+
if (s < 60)
|
|
6541
|
+
return `00:${s.toString().padStart(2, "0")}`;
|
|
6542
|
+
const m = Math.floor(s / 60);
|
|
6543
|
+
const r = s % 60;
|
|
6544
|
+
return `${m.toString().padStart(2, "0")}:${r.toString().padStart(2, "0")}`;
|
|
6545
|
+
}
|
|
6546
|
+
function escapeHtml(s) {
|
|
6547
|
+
return s.replace(/[&<>]/g, (c) => ({ "&": "&", "<": "<", ">": ">" })[c]);
|
|
6548
|
+
}
|
|
6549
|
+
function truncate(s, n) {
|
|
6550
|
+
return s.length > n ? s.slice(0, n - 1) + "\u2026" : s;
|
|
6551
|
+
}
|
|
6552
|
+
|
|
6535
6553
|
// ../node_modules/.bun/@grammyjs+runner@2.0.3+c6be0243b1bbec89/node_modules/@grammyjs/runner/out/mod.js
|
|
6536
6554
|
var require_mod4 = __commonJS((exports) => {
|
|
6537
6555
|
var __createBinding = exports && exports.__createBinding || (Object.create ? function(o, m, k, k2) {
|
|
@@ -27851,14 +27869,6 @@ var init_secretlint_source = __esm(() => {
|
|
|
27851
27869
|
init_suppressor();
|
|
27852
27870
|
});
|
|
27853
27871
|
|
|
27854
|
-
// card-format.ts
|
|
27855
|
-
function escapeHtml8(s) {
|
|
27856
|
-
return s.replace(/[&<>]/g, (c) => ({ "&": "&", "<": "<", ">": ">" })[c]);
|
|
27857
|
-
}
|
|
27858
|
-
function truncate5(s, n) {
|
|
27859
|
-
return s.length > n ? s.slice(0, n - 1) + "\u2026" : s;
|
|
27860
|
-
}
|
|
27861
|
-
|
|
27862
27872
|
// gateway/auth-line.ts
|
|
27863
27873
|
function escapeHtml9(s) {
|
|
27864
27874
|
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
@@ -28947,13 +28957,13 @@ function diffSnapshots(current, previous) {
|
|
|
28947
28957
|
function renderConfigChangeDim(dim) {
|
|
28948
28958
|
switch (dim.field) {
|
|
28949
28959
|
case "model": {
|
|
28950
|
-
const from =
|
|
28951
|
-
const to =
|
|
28960
|
+
const from = escapeHtml(dim.from ?? "(default)");
|
|
28961
|
+
const to = escapeHtml(dim.to ?? "(default)");
|
|
28952
28962
|
return `\u2699\ufe0f <b>Config</b> model: ${from} \u2192 ${to}`;
|
|
28953
28963
|
}
|
|
28954
28964
|
case "memoryBackend": {
|
|
28955
|
-
const from =
|
|
28956
|
-
const to =
|
|
28965
|
+
const from = escapeHtml(dim.from ?? "(default)");
|
|
28966
|
+
const to = escapeHtml(dim.to ?? "(default)");
|
|
28957
28967
|
return `\u2699\ufe0f <b>Config</b> memory backend: ${from} \u2192 ${to}`;
|
|
28958
28968
|
}
|
|
28959
28969
|
case "tools":
|
|
@@ -29040,14 +29050,14 @@ function shouldSkipDuplicateBootCard(gate, site) {
|
|
|
29040
29050
|
function renderNextStep(text) {
|
|
29041
29051
|
const parts = text.split("`");
|
|
29042
29052
|
if (parts.length % 2 === 0)
|
|
29043
|
-
return
|
|
29044
|
-
return parts.map((p, i) => i % 2 === 0 ?
|
|
29053
|
+
return escapeHtml(text);
|
|
29054
|
+
return parts.map((p, i) => i % 2 === 0 ? escapeHtml(p) : `<code>${escapeHtml(p)}</code>`).join("");
|
|
29045
29055
|
}
|
|
29046
29056
|
function renderBootCard(opts) {
|
|
29047
29057
|
const { agentName: agentName3, version: version2, probes, restartReason, restartAgeMs } = opts;
|
|
29048
29058
|
const agentSlug = opts.agentSlug ?? agentName3;
|
|
29049
29059
|
const ackEmoji = restartReason ? REASON_EMOJI[restartReason] : "\u2705";
|
|
29050
|
-
const ack = `${ackEmoji} <b>${
|
|
29060
|
+
const ack = `${ackEmoji} <b>${escapeHtml(agentName3)}</b> back up \u00b7 ${escapeHtml(version2)}`;
|
|
29051
29061
|
const degradedRows = [];
|
|
29052
29062
|
const snoozeSet = new Set(opts.snoozeRows ?? []);
|
|
29053
29063
|
if (opts.resolvedRows && opts.resolvedRows.length > 0) {
|
|
@@ -29055,13 +29065,13 @@ function renderBootCard(opts) {
|
|
|
29055
29065
|
const lbl = PROBE_LABELS[key];
|
|
29056
29066
|
if (!lbl)
|
|
29057
29067
|
continue;
|
|
29058
|
-
degradedRows.push(`\u2705 <b>${
|
|
29068
|
+
degradedRows.push(`\u2705 <b>${escapeHtml(lbl)}</b> resolved`);
|
|
29059
29069
|
}
|
|
29060
29070
|
}
|
|
29061
29071
|
if (restartReason === "crash") {
|
|
29062
29072
|
const ageStr = restartAgeMs != null && restartAgeMs > 0 ? ` \u00b7 ${(restartAgeMs / 1000).toFixed(1)}s ago` : "";
|
|
29063
|
-
degradedRows.push(`\u26a0\ufe0f <b>Restart</b> ${
|
|
29064
|
-
const tailCmd = process.env.SWITCHROOM_RUNTIME === "docker" ? `docker logs --tail 100 switchroom-${
|
|
29073
|
+
degradedRows.push(`\u26a0\ufe0f <b>Restart</b> ${escapeHtml(REASON_LABEL.crash)}${ageStr}`);
|
|
29074
|
+
const tailCmd = process.env.SWITCHROOM_RUNTIME === "docker" ? `docker logs --tail 100 switchroom-${escapeHtml(agentSlug)}` : `journalctl --user -u switchroom-${escapeHtml(agentSlug)} -n 100`;
|
|
29065
29075
|
degradedRows.push(` \u21b3 Tail logs: <code>${tailCmd}</code>`);
|
|
29066
29076
|
}
|
|
29067
29077
|
if (probes) {
|
|
@@ -29074,7 +29084,7 @@ function renderBootCard(opts) {
|
|
|
29074
29084
|
if (snoozeSet.has(key))
|
|
29075
29085
|
continue;
|
|
29076
29086
|
const dot = DOT[r.status] ?? DOT.fail;
|
|
29077
|
-
degradedRows.push(`${dot} <b>${PROBE_LABELS[key]}</b> ${
|
|
29087
|
+
degradedRows.push(`${dot} <b>${PROBE_LABELS[key]}</b> ${escapeHtml(r.detail)}`);
|
|
29078
29088
|
if (r.nextStep) {
|
|
29079
29089
|
degradedRows.push(` \u21b3 ${renderNextStep(r.nextStep)}`);
|
|
29080
29090
|
}
|
|
@@ -31658,6 +31668,7 @@ class StatusReactionController {
|
|
|
31658
31668
|
stallSoftTimer = null;
|
|
31659
31669
|
stallHardTimer = null;
|
|
31660
31670
|
finished = false;
|
|
31671
|
+
held = false;
|
|
31661
31672
|
debounceMs;
|
|
31662
31673
|
stallSoftMs;
|
|
31663
31674
|
stallHardMs;
|
|
@@ -31697,9 +31708,22 @@ class StatusReactionController {
|
|
|
31697
31708
|
if (this.finished)
|
|
31698
31709
|
return;
|
|
31699
31710
|
this.finished = true;
|
|
31711
|
+
this.held = false;
|
|
31700
31712
|
this.clearDebounceTimer();
|
|
31701
31713
|
this.clearStallTimers();
|
|
31702
31714
|
}
|
|
31715
|
+
hold() {
|
|
31716
|
+
if (this.finished)
|
|
31717
|
+
return;
|
|
31718
|
+
this.held = true;
|
|
31719
|
+
this.clearStallTimers();
|
|
31720
|
+
const working = this.resolveEmoji("tool");
|
|
31721
|
+
if (working != null && working !== this.currentEmoji && working !== this.pendingEmoji) {
|
|
31722
|
+
this.clearDebounceTimer();
|
|
31723
|
+
this.pendingEmoji = working;
|
|
31724
|
+
this.enqueue(working);
|
|
31725
|
+
}
|
|
31726
|
+
}
|
|
31703
31727
|
scheduleState(state, opts = {}) {
|
|
31704
31728
|
if (this.finished)
|
|
31705
31729
|
return;
|
|
@@ -31731,6 +31755,7 @@ class StatusReactionController {
|
|
|
31731
31755
|
if (this.finished)
|
|
31732
31756
|
return;
|
|
31733
31757
|
this.finished = true;
|
|
31758
|
+
this.held = false;
|
|
31734
31759
|
this.clearStallTimers();
|
|
31735
31760
|
const flushPending = this.debounceTimer != null && this.pendingEmoji != null ? this.pendingEmoji : null;
|
|
31736
31761
|
this.clearDebounceTimer();
|
|
@@ -31773,7 +31798,7 @@ class StatusReactionController {
|
|
|
31773
31798
|
}
|
|
31774
31799
|
resetStallTimers() {
|
|
31775
31800
|
this.clearStallTimers();
|
|
31776
|
-
if (this.finished)
|
|
31801
|
+
if (this.finished || this.held)
|
|
31777
31802
|
return;
|
|
31778
31803
|
this.stallSoftTimer = setTimeout(() => {
|
|
31779
31804
|
this.stallSoftTimer = null;
|
|
@@ -31802,6 +31827,206 @@ class StatusReactionController {
|
|
|
31802
31827
|
}
|
|
31803
31828
|
}
|
|
31804
31829
|
|
|
31830
|
+
// reaction-defer.ts
|
|
31831
|
+
class DeferredDoneReactions {
|
|
31832
|
+
deps;
|
|
31833
|
+
map = new Map;
|
|
31834
|
+
constructor(deps) {
|
|
31835
|
+
this.deps = deps;
|
|
31836
|
+
}
|
|
31837
|
+
tryDefer(key, ctrl) {
|
|
31838
|
+
if (this.deps.countRunningWorkers() > 0) {
|
|
31839
|
+
ctrl.hold();
|
|
31840
|
+
this.map.set(key, { ctrl });
|
|
31841
|
+
if (this.deps.countRunningWorkers() === 0)
|
|
31842
|
+
this.promote();
|
|
31843
|
+
return true;
|
|
31844
|
+
}
|
|
31845
|
+
this.map.delete(key);
|
|
31846
|
+
return false;
|
|
31847
|
+
}
|
|
31848
|
+
drop(key) {
|
|
31849
|
+
this.map.delete(key);
|
|
31850
|
+
}
|
|
31851
|
+
promote() {
|
|
31852
|
+
if (this.map.size === 0)
|
|
31853
|
+
return;
|
|
31854
|
+
if (this.deps.countRunningWorkers() > 0)
|
|
31855
|
+
return;
|
|
31856
|
+
for (const [key, { ctrl }] of this.map) {
|
|
31857
|
+
ctrl.finalize("done");
|
|
31858
|
+
if (this.deps.getActive(key) === ctrl)
|
|
31859
|
+
this.deps.purge(key);
|
|
31860
|
+
}
|
|
31861
|
+
this.map.clear();
|
|
31862
|
+
}
|
|
31863
|
+
has(key) {
|
|
31864
|
+
return this.map.has(key);
|
|
31865
|
+
}
|
|
31866
|
+
get size() {
|
|
31867
|
+
return this.map.size;
|
|
31868
|
+
}
|
|
31869
|
+
}
|
|
31870
|
+
|
|
31871
|
+
// worker-activity-feed.ts
|
|
31872
|
+
var DESC_MAX = 80;
|
|
31873
|
+
var TOOL_ARG_MAX = 64;
|
|
31874
|
+
var SUMMARY_MAX = 100;
|
|
31875
|
+
function renderWorkerActivity(v) {
|
|
31876
|
+
const desc = truncate(v.description.trim() || "background task", DESC_MAX);
|
|
31877
|
+
const elapsed = formatDuration(v.elapsedMs);
|
|
31878
|
+
const toolWord = v.toolCount === 1 ? "tool" : "tools";
|
|
31879
|
+
if (v.state === "done" || v.state === "failed") {
|
|
31880
|
+
const head = v.state === "done" ? `\u2705 <b>Worker done</b> \u00b7 <i>${escapeHtml(desc)}</i>` : `\u26a0\ufe0f <b>Worker failed</b> \u00b7 <i>${escapeHtml(desc)}</i>`;
|
|
31881
|
+
return `${head}
|
|
31882
|
+
<i>${v.toolCount} ${toolWord} \u00b7 ${elapsed}</i>`;
|
|
31883
|
+
}
|
|
31884
|
+
const header = `\uD83D\uDD27 <b>Worker</b> \u00b7 <i>${escapeHtml(desc)}</i>`;
|
|
31885
|
+
let activity;
|
|
31886
|
+
if (v.lastTool != null) {
|
|
31887
|
+
const arg = v.lastTool.sanitisedArg.trim();
|
|
31888
|
+
const argPart = arg.length > 0 ? ` ${escapeHtml(truncate(arg, TOOL_ARG_MAX))}` : "";
|
|
31889
|
+
activity = `\u26a1 <code>${escapeHtml(v.lastTool.name)}</code>${argPart} <i>(${v.toolCount} ${toolWord} \u00b7 ${elapsed})</i>`;
|
|
31890
|
+
} else {
|
|
31891
|
+
activity = `<i>starting\u2026 (${elapsed})</i>`;
|
|
31892
|
+
}
|
|
31893
|
+
const summary = v.latestSummary.trim();
|
|
31894
|
+
const lines = [header, activity];
|
|
31895
|
+
if (summary.length > 0) {
|
|
31896
|
+
lines.push(` \u21b3 <i>${escapeHtml(truncate(summary, SUMMARY_MAX))}</i>`);
|
|
31897
|
+
}
|
|
31898
|
+
return lines.join(`
|
|
31899
|
+
`);
|
|
31900
|
+
}
|
|
31901
|
+
var COOLDOWN_JITTER_MS = 500;
|
|
31902
|
+
function extractRetryAfterSecs(err) {
|
|
31903
|
+
if (err == null || typeof err !== "object")
|
|
31904
|
+
return null;
|
|
31905
|
+
const e = err;
|
|
31906
|
+
if (e.error_code !== 429)
|
|
31907
|
+
return null;
|
|
31908
|
+
const ra = e.parameters?.retry_after;
|
|
31909
|
+
if (typeof ra === "number" && Number.isFinite(ra) && ra > 0)
|
|
31910
|
+
return ra;
|
|
31911
|
+
return null;
|
|
31912
|
+
}
|
|
31913
|
+
function createWorkerActivityFeed(opts) {
|
|
31914
|
+
const log = opts.log ?? (() => {});
|
|
31915
|
+
const nowFn = opts.now ?? Date.now;
|
|
31916
|
+
const minEditInterval = opts.minEditIntervalMs ?? 2500;
|
|
31917
|
+
const firstPaintMin = opts.firstPaintMinMs ?? 8000;
|
|
31918
|
+
const handles = new Map;
|
|
31919
|
+
function sendOptsFor(h) {
|
|
31920
|
+
return {
|
|
31921
|
+
parse_mode: "HTML",
|
|
31922
|
+
disable_web_page_preview: true,
|
|
31923
|
+
...h.threadId != null ? { message_thread_id: h.threadId } : {}
|
|
31924
|
+
};
|
|
31925
|
+
}
|
|
31926
|
+
function noteRateLimited(h, err, label) {
|
|
31927
|
+
const retryAfter = extractRetryAfterSecs(err);
|
|
31928
|
+
if (retryAfter == null)
|
|
31929
|
+
return;
|
|
31930
|
+
h.cooldownUntil = nowFn() + retryAfter * 1000 + COOLDOWN_JITTER_MS;
|
|
31931
|
+
log(`worker-feed: ${label} 429 \u2014 backing off ${retryAfter}s`);
|
|
31932
|
+
}
|
|
31933
|
+
async function doUpdate(h, view) {
|
|
31934
|
+
if (nowFn() < h.cooldownUntil)
|
|
31935
|
+
return;
|
|
31936
|
+
const body = renderWorkerActivity(view);
|
|
31937
|
+
if (h.messageId == null) {
|
|
31938
|
+
if (view.elapsedMs < firstPaintMin)
|
|
31939
|
+
return;
|
|
31940
|
+
try {
|
|
31941
|
+
const sent = await opts.bot.sendMessage(h.chatId, body, sendOptsFor(h));
|
|
31942
|
+
h.messageId = sent.message_id;
|
|
31943
|
+
h.lastBody = body;
|
|
31944
|
+
h.lastEditAt = nowFn();
|
|
31945
|
+
} catch (err) {
|
|
31946
|
+
noteRateLimited(h, err, "send");
|
|
31947
|
+
log(`worker-feed: send failed: ${err.message}`);
|
|
31948
|
+
}
|
|
31949
|
+
return;
|
|
31950
|
+
}
|
|
31951
|
+
if (body === h.lastBody)
|
|
31952
|
+
return;
|
|
31953
|
+
if (nowFn() - h.lastEditAt < minEditInterval)
|
|
31954
|
+
return;
|
|
31955
|
+
try {
|
|
31956
|
+
await opts.bot.editMessageText(h.chatId, h.messageId, body, sendOptsFor(h));
|
|
31957
|
+
h.lastBody = body;
|
|
31958
|
+
h.lastEditAt = nowFn();
|
|
31959
|
+
} catch (err) {
|
|
31960
|
+
noteRateLimited(h, err, "edit");
|
|
31961
|
+
log(`worker-feed: edit failed, will re-post: ${err.message}`);
|
|
31962
|
+
h.messageId = null;
|
|
31963
|
+
h.lastBody = null;
|
|
31964
|
+
}
|
|
31965
|
+
}
|
|
31966
|
+
async function doFinish(h, view) {
|
|
31967
|
+
if (h.messageId == null)
|
|
31968
|
+
return;
|
|
31969
|
+
if (nowFn() < h.cooldownUntil) {
|
|
31970
|
+
return;
|
|
31971
|
+
}
|
|
31972
|
+
const body = renderWorkerActivity(view);
|
|
31973
|
+
if (body === h.lastBody)
|
|
31974
|
+
return;
|
|
31975
|
+
try {
|
|
31976
|
+
await opts.bot.editMessageText(h.chatId, h.messageId, body, sendOptsFor(h));
|
|
31977
|
+
h.lastBody = body;
|
|
31978
|
+
h.lastEditAt = nowFn();
|
|
31979
|
+
} catch (err) {
|
|
31980
|
+
noteRateLimited(h, err, "finish");
|
|
31981
|
+
log(`worker-feed: finish edit failed: ${err.message}`);
|
|
31982
|
+
}
|
|
31983
|
+
}
|
|
31984
|
+
return {
|
|
31985
|
+
has(agentId) {
|
|
31986
|
+
return handles.get(agentId)?.messageId != null;
|
|
31987
|
+
},
|
|
31988
|
+
get size() {
|
|
31989
|
+
return handles.size;
|
|
31990
|
+
},
|
|
31991
|
+
update(agentId, chatId, view, threadId) {
|
|
31992
|
+
if (chatId.length === 0)
|
|
31993
|
+
return Promise.resolve();
|
|
31994
|
+
let h = handles.get(agentId);
|
|
31995
|
+
if (h == null) {
|
|
31996
|
+
h = {
|
|
31997
|
+
chatId,
|
|
31998
|
+
threadId,
|
|
31999
|
+
messageId: null,
|
|
32000
|
+
lastBody: null,
|
|
32001
|
+
lastEditAt: 0,
|
|
32002
|
+
cooldownUntil: 0,
|
|
32003
|
+
chain: Promise.resolve()
|
|
32004
|
+
};
|
|
32005
|
+
handles.set(agentId, h);
|
|
32006
|
+
}
|
|
32007
|
+
const handle = h;
|
|
32008
|
+
handle.chain = handle.chain.then(() => doUpdate(handle, view)).catch((err) => {
|
|
32009
|
+
log(`worker-feed: update chain error ${agentId}: ${err.message}`);
|
|
32010
|
+
});
|
|
32011
|
+
return handle.chain;
|
|
32012
|
+
},
|
|
32013
|
+
finish(agentId, view) {
|
|
32014
|
+
const h = handles.get(agentId);
|
|
32015
|
+
if (h == null)
|
|
32016
|
+
return Promise.resolve();
|
|
32017
|
+
h.chain = h.chain.then(() => doFinish(h, view)).catch((err) => {
|
|
32018
|
+
log(`worker-feed: finish chain error ${agentId}: ${err.message}`);
|
|
32019
|
+
}).finally(() => {
|
|
32020
|
+
handles.delete(agentId);
|
|
32021
|
+
});
|
|
32022
|
+
return h.chain;
|
|
32023
|
+
},
|
|
32024
|
+
drop(agentId) {
|
|
32025
|
+
handles.delete(agentId);
|
|
32026
|
+
}
|
|
32027
|
+
};
|
|
32028
|
+
}
|
|
32029
|
+
|
|
31805
32030
|
// tool-names.ts
|
|
31806
32031
|
var TELEGRAM_TOOL_PREFIX_RE = /^mcp__[^_].*?telegram__/;
|
|
31807
32032
|
function stripPrefix(toolName) {
|
|
@@ -31882,10 +32107,10 @@ function hostFromUrl(u) {
|
|
|
31882
32107
|
try {
|
|
31883
32108
|
return new URL(u).host;
|
|
31884
32109
|
} catch {
|
|
31885
|
-
return
|
|
32110
|
+
return truncate2(u);
|
|
31886
32111
|
}
|
|
31887
32112
|
}
|
|
31888
|
-
function
|
|
32113
|
+
function truncate2(s, n = MAX_LABEL_CHARS) {
|
|
31889
32114
|
if (s.length <= n)
|
|
31890
32115
|
return s;
|
|
31891
32116
|
return s.slice(0, n - 1) + "\u2026";
|
|
@@ -31900,7 +32125,7 @@ function firstLine(s) {
|
|
|
31900
32125
|
}
|
|
31901
32126
|
function toolLabel(tool, input, preamble, precomputedLabel) {
|
|
31902
32127
|
if (precomputedLabel && precomputedLabel.trim().length > 0) {
|
|
31903
|
-
return
|
|
32128
|
+
return truncate2(firstLine(precomputedLabel.trim()), MAX_DESCRIPTION_CHARS);
|
|
31904
32129
|
}
|
|
31905
32130
|
if (!input || typeof input !== "object")
|
|
31906
32131
|
return "";
|
|
@@ -31926,26 +32151,26 @@ function toolLabel(tool, input, preamble, precomputedLabel) {
|
|
|
31926
32151
|
const pre = preambleLabel();
|
|
31927
32152
|
if (pre)
|
|
31928
32153
|
return pre;
|
|
31929
|
-
return
|
|
32154
|
+
return truncate2(basename(str("file_path") ?? ""));
|
|
31930
32155
|
}
|
|
31931
32156
|
case "Bash":
|
|
31932
32157
|
case "BashOutput": {
|
|
31933
32158
|
const description = str("description");
|
|
31934
32159
|
if (description)
|
|
31935
|
-
return
|
|
32160
|
+
return truncate2(firstLine(description), MAX_DESCRIPTION_CHARS);
|
|
31936
32161
|
const pre = preambleLabel();
|
|
31937
32162
|
if (pre)
|
|
31938
32163
|
return pre;
|
|
31939
32164
|
const cmd = str("command") ?? str("bash_id") ?? "";
|
|
31940
|
-
return
|
|
32165
|
+
return truncate2(firstLine(cmd), MAX_BASH_CHARS);
|
|
31941
32166
|
}
|
|
31942
32167
|
case "KillShell":
|
|
31943
|
-
return
|
|
32168
|
+
return truncate2(str("shell_id") ?? "");
|
|
31944
32169
|
case "Glob": {
|
|
31945
32170
|
const pre = preambleLabel();
|
|
31946
32171
|
if (pre)
|
|
31947
32172
|
return pre;
|
|
31948
|
-
return
|
|
32173
|
+
return truncate2(str("pattern") ?? "");
|
|
31949
32174
|
}
|
|
31950
32175
|
case "Grep": {
|
|
31951
32176
|
const pre = preambleLabel();
|
|
@@ -31956,18 +32181,18 @@ function toolLabel(tool, input, preamble, precomputedLabel) {
|
|
|
31956
32181
|
return "";
|
|
31957
32182
|
const path = str("path");
|
|
31958
32183
|
const where = shortenGrepPath(path ?? "");
|
|
31959
|
-
return
|
|
32184
|
+
return truncate2(`"${pat}" (in ${where})`);
|
|
31960
32185
|
}
|
|
31961
32186
|
case "WebFetch":
|
|
31962
|
-
return
|
|
32187
|
+
return truncate2(hostFromUrl(str("url") ?? ""));
|
|
31963
32188
|
case "WebSearch": {
|
|
31964
32189
|
const q = str("query") ?? "";
|
|
31965
|
-
return q ?
|
|
32190
|
+
return q ? truncate2(`"${q}"`) : "";
|
|
31966
32191
|
}
|
|
31967
32192
|
case "Task":
|
|
31968
32193
|
case "Agent": {
|
|
31969
32194
|
const desc = str("description") ?? str("subagent_type") ?? "";
|
|
31970
|
-
return
|
|
32195
|
+
return truncate2(desc);
|
|
31971
32196
|
}
|
|
31972
32197
|
case "TodoWrite":
|
|
31973
32198
|
case "TaskCreate":
|
|
@@ -31978,9 +32203,9 @@ function toolLabel(tool, input, preamble, precomputedLabel) {
|
|
|
31978
32203
|
case "TaskOutput":
|
|
31979
32204
|
return "";
|
|
31980
32205
|
case "Skill":
|
|
31981
|
-
return
|
|
32206
|
+
return truncate2(str("skill") ?? "");
|
|
31982
32207
|
case "SlashCommand":
|
|
31983
|
-
return
|
|
32208
|
+
return truncate2(str("command") ?? "");
|
|
31984
32209
|
case "ToolSearch": {
|
|
31985
32210
|
const q = str("query") ?? "";
|
|
31986
32211
|
if (!q)
|
|
@@ -31988,35 +32213,35 @@ function toolLabel(tool, input, preamble, precomputedLabel) {
|
|
|
31988
32213
|
const selectMatch = q.match(/^\s*select\s*:\s*(.+)$/i);
|
|
31989
32214
|
if (selectMatch) {
|
|
31990
32215
|
const names = selectMatch[1].split(",").map((n) => n.trim()).filter((n) => n.length > 0).join(", ");
|
|
31991
|
-
return
|
|
32216
|
+
return truncate2(`Loading schema: ${names}`);
|
|
31992
32217
|
}
|
|
31993
|
-
return
|
|
32218
|
+
return truncate2(`Searching tools: ${q}`);
|
|
31994
32219
|
}
|
|
31995
32220
|
default:
|
|
31996
32221
|
if (tool.startsWith("mcp__")) {
|
|
31997
32222
|
const description = str("description");
|
|
31998
32223
|
if (description)
|
|
31999
|
-
return
|
|
32224
|
+
return truncate2(firstLine(stripHtml(description)), MAX_DESCRIPTION_CHARS);
|
|
32000
32225
|
const label = mcpBaseLabel(tool);
|
|
32001
32226
|
const query = str("query") ?? str("text") ?? str("name");
|
|
32002
32227
|
if (label && query) {
|
|
32003
32228
|
const budget = Math.max(8, MAX_LABEL_CHARS - label.length - 4);
|
|
32004
|
-
const preview =
|
|
32229
|
+
const preview = truncate2(firstLine(stripHtml(query)), budget);
|
|
32005
32230
|
return `${label} (${preview})`;
|
|
32006
32231
|
}
|
|
32007
32232
|
if (label)
|
|
32008
|
-
return
|
|
32233
|
+
return truncate2(label);
|
|
32009
32234
|
}
|
|
32010
32235
|
for (const k of ["description", "file_path", "path", "url", "query", "pattern", "command"]) {
|
|
32011
32236
|
const v = str(k);
|
|
32012
32237
|
if (v != null && v.length > 0) {
|
|
32013
32238
|
if (k === "file_path" || k === "path")
|
|
32014
|
-
return
|
|
32239
|
+
return truncate2(basename(v));
|
|
32015
32240
|
if (k === "url")
|
|
32016
|
-
return
|
|
32241
|
+
return truncate2(hostFromUrl(v));
|
|
32017
32242
|
if (k === "description")
|
|
32018
|
-
return
|
|
32019
|
-
return
|
|
32243
|
+
return truncate2(firstLine(v), MAX_DESCRIPTION_CHARS);
|
|
32244
|
+
return truncate2(firstLine(v));
|
|
32020
32245
|
}
|
|
32021
32246
|
}
|
|
32022
32247
|
return "";
|
|
@@ -38936,16 +39161,16 @@ function renderAccountRow(snap, opts) {
|
|
|
38936
39161
|
const lines = [];
|
|
38937
39162
|
const marker = snap.isActive ? "\u25cf " : "";
|
|
38938
39163
|
if (!snap.quota) {
|
|
38939
|
-
lines.push(`${marker}<code>${
|
|
39164
|
+
lines.push(`${marker}<code>${escapeHtml2(snap.label)}</code> <i>quota probe failed</i>`);
|
|
38940
39165
|
if (snap.quotaError) {
|
|
38941
|
-
lines.push(` <i>${
|
|
39166
|
+
lines.push(` <i>${escapeHtml2(snap.quotaError)}</i>`);
|
|
38942
39167
|
}
|
|
38943
39168
|
return lines;
|
|
38944
39169
|
}
|
|
38945
39170
|
const q = snap.quota;
|
|
38946
39171
|
const fiveStr = fmtPct(q.fiveHourUtilizationPct);
|
|
38947
39172
|
const sevenStr = fmtPct(q.sevenDayUtilizationPct);
|
|
38948
|
-
lines.push(`${marker}<code>${
|
|
39173
|
+
lines.push(`${marker}<code>${escapeHtml2(snap.label)}</code> ${fiveStr} / ${sevenStr}`);
|
|
38949
39174
|
const health = classifyHealth(snap);
|
|
38950
39175
|
if (health === "blocked") {
|
|
38951
39176
|
const win = bindingWindow(q);
|
|
@@ -39051,13 +39276,13 @@ function renderFallbackAnnouncement(input) {
|
|
|
39051
39276
|
const limitWord = input.oldQuota ? limitWordFor(input.oldQuota) : "quota";
|
|
39052
39277
|
const headerLimit = limitWord === "quota" ? "quota cap" : `${limitWord} limit`;
|
|
39053
39278
|
if (!input.newLabel) {
|
|
39054
|
-
lines.push(`\uD83D\uDD34 <b>All accounts blocked \u00b7 ${headerLimit} on ${
|
|
39279
|
+
lines.push(`\uD83D\uDD34 <b>All accounts blocked \u00b7 ${headerLimit} on ${escapeHtml2(input.oldLabel)}</b>`);
|
|
39055
39280
|
lines.push("");
|
|
39056
|
-
lines.push(`Triggered by: agent <b>${
|
|
39281
|
+
lines.push(`Triggered by: agent <b>${escapeHtml2(input.triggerAgent)}</b>`);
|
|
39057
39282
|
if (input.oldQuota) {
|
|
39058
39283
|
const recovery = recoveryAtFor(input.oldQuota);
|
|
39059
39284
|
if (recovery) {
|
|
39060
|
-
lines.push(`${
|
|
39285
|
+
lines.push(`${escapeHtml2(input.oldLabel)} recovers ${formatAbsolute(recovery, tz)} ` + `(in ${formatRelative(recovery, now)})`);
|
|
39061
39286
|
}
|
|
39062
39287
|
}
|
|
39063
39288
|
lines.push("");
|
|
@@ -39065,15 +39290,15 @@ function renderFallbackAnnouncement(input) {
|
|
|
39065
39290
|
return lines.join(`
|
|
39066
39291
|
`);
|
|
39067
39292
|
}
|
|
39068
|
-
lines.push(`\u2713 <b>Switched fleet \u00b7 ${headerLimit} on ${
|
|
39293
|
+
lines.push(`\u2713 <b>Switched fleet \u00b7 ${headerLimit} on ${escapeHtml2(input.oldLabel)}</b>`);
|
|
39069
39294
|
lines.push("");
|
|
39070
|
-
lines.push(`<code>${
|
|
39071
|
-
lines.push(`Triggered by: agent <b>${
|
|
39295
|
+
lines.push(`<code>${escapeHtml2(input.oldLabel)}</code> \u2192 <code>${escapeHtml2(input.newLabel)}</code>`);
|
|
39296
|
+
lines.push(`Triggered by: agent <b>${escapeHtml2(input.triggerAgent)}</b>`);
|
|
39072
39297
|
lines.push("");
|
|
39073
39298
|
if (input.oldQuota) {
|
|
39074
39299
|
const recovery = recoveryAtFor(input.oldQuota);
|
|
39075
39300
|
if (recovery) {
|
|
39076
|
-
lines.push(`<code>${
|
|
39301
|
+
lines.push(`<code>${escapeHtml2(input.oldLabel)}</code> recovers ` + `${formatAbsolute(recovery, tz)} (in ${formatRelative(recovery, now)})`);
|
|
39077
39302
|
}
|
|
39078
39303
|
}
|
|
39079
39304
|
if (input.newQuota) {
|
|
@@ -39081,7 +39306,7 @@ function renderFallbackAnnouncement(input) {
|
|
|
39081
39306
|
const sevenStr = fmtPct(input.newQuota.sevenDayUtilizationPct);
|
|
39082
39307
|
const hasHeadroom = input.newQuota.fiveHourUtilizationPct < THROTTLING_THRESHOLD_PCT && input.newQuota.sevenDayUtilizationPct < THROTTLING_THRESHOLD_PCT;
|
|
39083
39308
|
const headroomStr = hasHeadroom ? "<i>(plenty of headroom)</i>" : "<i>(near limit \u2014 watch this)</i>";
|
|
39084
|
-
lines.push(`<code>${
|
|
39309
|
+
lines.push(`<code>${escapeHtml2(input.newLabel)}</code> now: ${fiveStr} of 5h \u00b7 ${sevenStr} of 7d ${headroomStr}`);
|
|
39085
39310
|
} else {
|
|
39086
39311
|
lines.push(`<i>(quota probe for new account is pending \u2014 will reflect on next /auth)</i>`);
|
|
39087
39312
|
}
|
|
@@ -39140,7 +39365,7 @@ function switchPriority(s) {
|
|
|
39140
39365
|
return 2;
|
|
39141
39366
|
return 3;
|
|
39142
39367
|
}
|
|
39143
|
-
function
|
|
39368
|
+
function escapeHtml2(s) {
|
|
39144
39369
|
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
39145
39370
|
}
|
|
39146
39371
|
function buildSnapshotsFromState(state3, quotas) {
|
|
@@ -39233,7 +39458,7 @@ function parseAuthCommand(text) {
|
|
|
39233
39458
|
if (tail.length > 0) {
|
|
39234
39459
|
return {
|
|
39235
39460
|
kind: "help",
|
|
39236
|
-
reason: `Unknown <code>rm</code> modifier: <code>${
|
|
39461
|
+
reason: `Unknown <code>rm</code> modifier: <code>${escapeHtml3(tail)}</code>. Use <code>/auth rm <label> confirm</code> to confirm.`
|
|
39237
39462
|
};
|
|
39238
39463
|
}
|
|
39239
39464
|
return { kind: "rm-prompt", label };
|
|
@@ -39253,7 +39478,7 @@ function parseAuthCommand(text) {
|
|
|
39253
39478
|
if (sub !== "override") {
|
|
39254
39479
|
return {
|
|
39255
39480
|
kind: "help",
|
|
39256
|
-
reason: `Unknown <code>agent</code> subcommand: <code>${
|
|
39481
|
+
reason: `Unknown <code>agent</code> subcommand: <code>${escapeHtml3(sub || "(none)")}</code>. Try <code>/auth agent override <agent> <label|clear></code>.`
|
|
39257
39482
|
};
|
|
39258
39483
|
}
|
|
39259
39484
|
const agent = parts[2];
|
|
@@ -39275,7 +39500,7 @@ function parseAuthCommand(text) {
|
|
|
39275
39500
|
case "help":
|
|
39276
39501
|
return { kind: "help" };
|
|
39277
39502
|
default:
|
|
39278
|
-
return { kind: "help", reason: `Unknown verb: <code>${
|
|
39503
|
+
return { kind: "help", reason: `Unknown verb: <code>${escapeHtml3(verb)}</code>` };
|
|
39279
39504
|
}
|
|
39280
39505
|
}
|
|
39281
39506
|
async function handleAuthCommand(parsed, ctx) {
|
|
@@ -39329,7 +39554,7 @@ async function handleAuthCommand(parsed, ctx) {
|
|
|
39329
39554
|
};
|
|
39330
39555
|
} catch (err) {
|
|
39331
39556
|
return {
|
|
39332
|
-
text: `<b>/auth show failed:</b> ${
|
|
39557
|
+
text: `<b>/auth show failed:</b> ${escapeHtml3(err?.message ?? String(err))}`,
|
|
39333
39558
|
html: true
|
|
39334
39559
|
};
|
|
39335
39560
|
}
|
|
@@ -39341,7 +39566,7 @@ async function handleAuthCommand(parsed, ctx) {
|
|
|
39341
39566
|
const agent = state3.agents.find((a) => a.name === agentName3);
|
|
39342
39567
|
if (!agent) {
|
|
39343
39568
|
return {
|
|
39344
|
-
text: `<b>/auth show:</b> no agent named <code>${
|
|
39569
|
+
text: `<b>/auth show:</b> no agent named <code>${escapeHtml3(agentName3)}</code> in broker view.
|
|
39345
39570
|
` + `Run <code>/auth show</code> for the fleet snapshot.`,
|
|
39346
39571
|
html: true
|
|
39347
39572
|
};
|
|
@@ -39349,7 +39574,7 @@ async function handleAuthCommand(parsed, ctx) {
|
|
|
39349
39574
|
return { text: renderAgentDetail(state3, agent), html: true };
|
|
39350
39575
|
} catch (err) {
|
|
39351
39576
|
return {
|
|
39352
|
-
text: `<b>/auth show failed:</b> ${
|
|
39577
|
+
text: `<b>/auth show failed:</b> ${escapeHtml3(err?.message ?? String(err))}`,
|
|
39353
39578
|
html: true
|
|
39354
39579
|
};
|
|
39355
39580
|
}
|
|
@@ -39371,13 +39596,13 @@ async function handleAuthCommand(parsed, ctx) {
|
|
|
39371
39596
|
try {
|
|
39372
39597
|
const result = await ctx.client.setActive(parsed.label);
|
|
39373
39598
|
return {
|
|
39374
|
-
text: `<b>Active account \u2192</b> <code>${
|
|
39599
|
+
text: `<b>Active account \u2192</b> <code>${escapeHtml3(result.active)}</code>
|
|
39375
39600
|
` + `Re-mirrored credentials for ${result.fanned.length} agent${result.fanned.length === 1 ? "" : "s"}.`,
|
|
39376
39601
|
html: true
|
|
39377
39602
|
};
|
|
39378
39603
|
} catch (err) {
|
|
39379
39604
|
return {
|
|
39380
|
-
text: `<b>/auth use failed:</b> ${
|
|
39605
|
+
text: `<b>/auth use failed:</b> ${escapeHtml3(err?.message ?? String(err))}`,
|
|
39381
39606
|
html: true
|
|
39382
39607
|
};
|
|
39383
39608
|
}
|
|
@@ -39395,13 +39620,13 @@ async function handleAuthCommand(parsed, ctx) {
|
|
|
39395
39620
|
}
|
|
39396
39621
|
const result = await ctx.client.setActive(nextLabel);
|
|
39397
39622
|
return {
|
|
39398
|
-
text: `<b>Rotated:</b> active \u2192 <code>${
|
|
39623
|
+
text: `<b>Rotated:</b> active \u2192 <code>${escapeHtml3(result.active)}</code>
|
|
39399
39624
|
` + `Re-mirrored credentials for ${result.fanned.length} agent${result.fanned.length === 1 ? "" : "s"}.`,
|
|
39400
39625
|
html: true
|
|
39401
39626
|
};
|
|
39402
39627
|
} catch (err) {
|
|
39403
39628
|
return {
|
|
39404
|
-
text: `<b>/auth rotate failed:</b> ${
|
|
39629
|
+
text: `<b>/auth rotate failed:</b> ${escapeHtml3(err?.message ?? String(err))}`,
|
|
39405
39630
|
html: true
|
|
39406
39631
|
};
|
|
39407
39632
|
}
|
|
@@ -39412,20 +39637,20 @@ async function handleAuthCommand(parsed, ctx) {
|
|
|
39412
39637
|
state3 = await ctx.client.listState();
|
|
39413
39638
|
} catch (err) {
|
|
39414
39639
|
return {
|
|
39415
|
-
text: `<b>/auth rm failed:</b> ${
|
|
39640
|
+
text: `<b>/auth rm failed:</b> ${escapeHtml3(err?.message ?? String(err))}`,
|
|
39416
39641
|
html: true
|
|
39417
39642
|
};
|
|
39418
39643
|
}
|
|
39419
39644
|
const exists = state3.accounts.some((a) => a.label === parsed.label);
|
|
39420
39645
|
if (!exists) {
|
|
39421
39646
|
return {
|
|
39422
|
-
text: `<b>/auth rm:</b> no account named <code>${
|
|
39647
|
+
text: `<b>/auth rm:</b> no account named <code>${escapeHtml3(parsed.label)}</code>. ` + `Run <code>/auth show</code> for the current list.`,
|
|
39423
39648
|
html: true
|
|
39424
39649
|
};
|
|
39425
39650
|
}
|
|
39426
39651
|
if (state3.active === parsed.label) {
|
|
39427
39652
|
return {
|
|
39428
|
-
text: `<b>/auth rm refused.</b> <code>${
|
|
39653
|
+
text: `<b>/auth rm refused.</b> <code>${escapeHtml3(parsed.label)}</code> is the fleet active. ` + `Switch with <code>/auth use <other></code> or <code>/auth rotate</code> first.`,
|
|
39429
39654
|
html: true
|
|
39430
39655
|
};
|
|
39431
39656
|
}
|
|
@@ -39436,10 +39661,10 @@ async function handleAuthCommand(parsed, ctx) {
|
|
|
39436
39661
|
});
|
|
39437
39662
|
}
|
|
39438
39663
|
return {
|
|
39439
|
-
text: `<b>\u26a0 /auth rm</b> \u2014 about to remove <code>${
|
|
39440
|
-
` + `The fleet active is unchanged. Any agent override pointing at <code>${
|
|
39664
|
+
text: `<b>\u26a0 /auth rm</b> \u2014 about to remove <code>${escapeHtml3(parsed.label)}</code> from the broker.
|
|
39665
|
+
` + `The fleet active is unchanged. Any agent override pointing at <code>${escapeHtml3(parsed.label)}</code> will stop working.
|
|
39441
39666
|
|
|
39442
|
-
` + `Send <code>/auth rm ${
|
|
39667
|
+
` + `Send <code>/auth rm ${escapeHtml3(parsed.label)} confirm</code> within ${Math.round(AUTH_RM_CONFIRM_TTL_MS / 1000)}s to proceed.`,
|
|
39443
39668
|
html: true
|
|
39444
39669
|
};
|
|
39445
39670
|
}
|
|
@@ -39451,7 +39676,7 @@ async function handleAuthCommand(parsed, ctx) {
|
|
|
39451
39676
|
pendingAuthRmFlows.delete(ctx.chatId);
|
|
39452
39677
|
}
|
|
39453
39678
|
return {
|
|
39454
|
-
text: `<b>/auth rm:</b> no pending confirm for <code>${
|
|
39679
|
+
text: `<b>/auth rm:</b> no pending confirm for <code>${escapeHtml3(parsed.label)}</code> (expired or not started). ` + `Send <code>/auth rm ${escapeHtml3(parsed.label)}</code> first.`,
|
|
39455
39680
|
html: true
|
|
39456
39681
|
};
|
|
39457
39682
|
}
|
|
@@ -39460,12 +39685,12 @@ async function handleAuthCommand(parsed, ctx) {
|
|
|
39460
39685
|
try {
|
|
39461
39686
|
const data = await ctx.client.rmAccount(parsed.label);
|
|
39462
39687
|
return {
|
|
39463
|
-
text: `<b>Removed</b> <code>${
|
|
39688
|
+
text: `<b>Removed</b> <code>${escapeHtml3(data.label)}</code> from the broker.`,
|
|
39464
39689
|
html: true
|
|
39465
39690
|
};
|
|
39466
39691
|
} catch (err) {
|
|
39467
39692
|
return {
|
|
39468
|
-
text: `<b>/auth rm failed:</b> ${
|
|
39693
|
+
text: `<b>/auth rm failed:</b> ${escapeHtml3(err?.message ?? String(err))}`,
|
|
39469
39694
|
html: true
|
|
39470
39695
|
};
|
|
39471
39696
|
}
|
|
@@ -39476,7 +39701,7 @@ async function handleAuthCommand(parsed, ctx) {
|
|
|
39476
39701
|
const targets = parsed.label ? state3.accounts.filter((a) => a.label === parsed.label).map((a) => a.label) : state3.accounts.map((a) => a.label);
|
|
39477
39702
|
if (parsed.label && targets.length === 0) {
|
|
39478
39703
|
return {
|
|
39479
|
-
text: `<b>/auth refresh:</b> no account named <code>${
|
|
39704
|
+
text: `<b>/auth refresh:</b> no account named <code>${escapeHtml3(parsed.label)}</code>.`,
|
|
39480
39705
|
html: true
|
|
39481
39706
|
};
|
|
39482
39707
|
}
|
|
@@ -39495,10 +39720,10 @@ async function handleAuthCommand(parsed, ctx) {
|
|
|
39495
39720
|
formatExpiryAbs(data.expiresAt)
|
|
39496
39721
|
]);
|
|
39497
39722
|
} catch (err) {
|
|
39498
|
-
failures.push(`${label}: ${
|
|
39723
|
+
failures.push(`${label}: ${escapeHtml3(err?.message ?? String(err))}`);
|
|
39499
39724
|
}
|
|
39500
39725
|
}
|
|
39501
|
-
const head = targets.length === 1 ? `<b>Refreshed</b> <code>${
|
|
39726
|
+
const head = targets.length === 1 ? `<b>Refreshed</b> <code>${escapeHtml3(targets[0])}</code>` : `<b>Refreshed</b> ${rows.length - 1}/${targets.length} account${targets.length === 1 ? "" : "s"}`;
|
|
39502
39727
|
const table = rows.length > 1 ? `
|
|
39503
39728
|
<pre>${alignTable(rows)}</pre>` : "";
|
|
39504
39729
|
const failBlock = failures.length > 0 ? `
|
|
@@ -39508,7 +39733,7 @@ ${failures.map((f) => ` ${f}`).join(`
|
|
|
39508
39733
|
return { text: head + table + failBlock, html: true };
|
|
39509
39734
|
} catch (err) {
|
|
39510
39735
|
return {
|
|
39511
|
-
text: `<b>/auth refresh failed:</b> ${
|
|
39736
|
+
text: `<b>/auth refresh failed:</b> ${escapeHtml3(err?.message ?? String(err))}`,
|
|
39512
39737
|
html: true
|
|
39513
39738
|
};
|
|
39514
39739
|
}
|
|
@@ -39517,12 +39742,12 @@ ${failures.map((f) => ` ${f}`).join(`
|
|
|
39517
39742
|
try {
|
|
39518
39743
|
const data = await ctx.client.setOverride(parsed.agent, parsed.label);
|
|
39519
39744
|
return {
|
|
39520
|
-
text: `<b>Override set.</b> <code>${
|
|
39745
|
+
text: `<b>Override set.</b> <code>${escapeHtml3(data.agent)}</code> is now pinned to ` + `<code>${escapeHtml3(data.account ?? parsed.label)}</code>.`,
|
|
39521
39746
|
html: true
|
|
39522
39747
|
};
|
|
39523
39748
|
} catch (err) {
|
|
39524
39749
|
return {
|
|
39525
|
-
text: `<b>/auth agent override failed:</b> ${
|
|
39750
|
+
text: `<b>/auth agent override failed:</b> ${escapeHtml3(err?.message ?? String(err))}`,
|
|
39526
39751
|
html: true
|
|
39527
39752
|
};
|
|
39528
39753
|
}
|
|
@@ -39531,12 +39756,12 @@ ${failures.map((f) => ` ${f}`).join(`
|
|
|
39531
39756
|
try {
|
|
39532
39757
|
const data = await ctx.client.setOverride(parsed.agent, null);
|
|
39533
39758
|
return {
|
|
39534
|
-
text: `<b>Override cleared</b> on <code>${
|
|
39759
|
+
text: `<b>Override cleared</b> on <code>${escapeHtml3(data.agent)}</code> ` + `\u2014 back to fleet active.`,
|
|
39535
39760
|
html: true
|
|
39536
39761
|
};
|
|
39537
39762
|
} catch (err) {
|
|
39538
39763
|
return {
|
|
39539
|
-
text: `<b>/auth agent override failed:</b> ${
|
|
39764
|
+
text: `<b>/auth agent override failed:</b> ${escapeHtml3(err?.message ?? String(err))}`,
|
|
39540
39765
|
html: true
|
|
39541
39766
|
};
|
|
39542
39767
|
}
|
|
@@ -39615,7 +39840,7 @@ function formatAccountsTable(state3, now) {
|
|
|
39615
39840
|
const status = isActive ? "active" : acc.exhausted ? "exhausted" : "available";
|
|
39616
39841
|
const expires = acc.expiresAt != null ? formatRelativeMs(acc.expiresAt - now) : "\u2014";
|
|
39617
39842
|
const quotaReset = acc.exhausted && acc.exhausted_until != null && acc.exhausted_until > now ? formatRelativeMs(acc.exhausted_until - now) : "\u2014";
|
|
39618
|
-
rows.push([`${marker} ${
|
|
39843
|
+
rows.push([`${marker} ${escapeHtml3(acc.label)}`, status, expires, quotaReset]);
|
|
39619
39844
|
}
|
|
39620
39845
|
return alignTable(rows);
|
|
39621
39846
|
}
|
|
@@ -39623,15 +39848,15 @@ function formatAgentsTable(state3) {
|
|
|
39623
39848
|
const rows = [["AGENT", "ACTIVE", "SOURCE"]];
|
|
39624
39849
|
for (const a of state3.agents) {
|
|
39625
39850
|
const source = a.override ? "override" : a.account === state3.active ? "fleet-active" : "pinned";
|
|
39626
|
-
rows.push([
|
|
39851
|
+
rows.push([escapeHtml3(a.name), escapeHtml3(a.account), source]);
|
|
39627
39852
|
}
|
|
39628
39853
|
return alignTable(rows);
|
|
39629
39854
|
}
|
|
39630
39855
|
function renderAgentDetail(state3, agent, now = Date.now()) {
|
|
39631
39856
|
const lines = [];
|
|
39632
|
-
lines.push(`<b>${
|
|
39857
|
+
lines.push(`<b>${escapeHtml3(agent.name)}</b>`);
|
|
39633
39858
|
const source = agent.override ? "override" : "fleet-active";
|
|
39634
|
-
lines.push(`Active account: <code>${
|
|
39859
|
+
lines.push(`Active account: <code>${escapeHtml3(agent.account)}</code> (${source})`);
|
|
39635
39860
|
const acct = state3.accounts.find((a) => a.label === agent.account);
|
|
39636
39861
|
if (acct) {
|
|
39637
39862
|
const expRel = acct.expiresAt != null ? formatRelativeMs(acct.expiresAt - now) : "\u2014";
|
|
@@ -39654,11 +39879,11 @@ function formatConsumersTable(state3, now) {
|
|
|
39654
39879
|
const rows = [["CONSUMER", "ACTIVE", "STATUS"]];
|
|
39655
39880
|
for (const c of state3.consumers) {
|
|
39656
39881
|
const status = c.last_seen_at == null ? "socket bound" : `socket bound (last seen ${formatRelativeMs(now - c.last_seen_at)} ago)`;
|
|
39657
|
-
rows.push([
|
|
39882
|
+
rows.push([escapeHtml3(c.name), escapeHtml3(c.account), status]);
|
|
39658
39883
|
}
|
|
39659
39884
|
return alignTable(rows);
|
|
39660
39885
|
}
|
|
39661
|
-
function
|
|
39886
|
+
function escapeHtml3(s) {
|
|
39662
39887
|
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
39663
39888
|
}
|
|
39664
39889
|
function formatRelativeMs(ms) {
|
|
@@ -40965,11 +41190,11 @@ function renderTable(headers, rows) {
|
|
|
40965
41190
|
if (colCount <= 3 && rowCount <= 6) {
|
|
40966
41191
|
const bullets = rows.map((row) => {
|
|
40967
41192
|
const cells = headers.map((_, i) => (row[i] ?? "").trim());
|
|
40968
|
-
const key =
|
|
40969
|
-
const rest = cells.slice(1).filter((v) => v !== "").map((v) => ` \u2014 ${
|
|
41193
|
+
const key = escapeHtml5(cells[0] || "\u2014");
|
|
41194
|
+
const rest = cells.slice(1).filter((v) => v !== "").map((v) => ` \u2014 ${escapeHtml5(v)}`).join("");
|
|
40970
41195
|
return `\u2022 <b>${key}</b>${rest}`;
|
|
40971
41196
|
});
|
|
40972
|
-
const headerLine = colCount >= 2 ? `<b>${headers.map((h) =>
|
|
41197
|
+
const headerLine = colCount >= 2 ? `<b>${headers.map((h) => escapeHtml5(h)).join(" / ")}</b>
|
|
40973
41198
|
` : "";
|
|
40974
41199
|
return headerLine + bullets.join(`
|
|
40975
41200
|
`);
|
|
@@ -40984,7 +41209,7 @@ function renderTable(headers, rows) {
|
|
|
40984
41209
|
sepLine,
|
|
40985
41210
|
...rows.map((r) => formatRow(r))
|
|
40986
41211
|
];
|
|
40987
|
-
return `<pre>${
|
|
41212
|
+
return `<pre>${escapeHtml5(lines.join(`
|
|
40988
41213
|
`))}</pre>`;
|
|
40989
41214
|
}
|
|
40990
41215
|
function extractMarkdownTables(text, store2, placeholderPrefix) {
|
|
@@ -41032,7 +41257,7 @@ function markdownToHtml(text) {
|
|
|
41032
41257
|
const INLINE_PH = "\x00CODEINLINE";
|
|
41033
41258
|
const TABLE_PH = "\x00TABLEBLOCK";
|
|
41034
41259
|
let result = text.replace(/```(\w*)\n([\s\S]*?)```/g, (_m, lang, code) => {
|
|
41035
|
-
const escaped =
|
|
41260
|
+
const escaped = escapeHtml5(code.replace(/\n$/, ""));
|
|
41036
41261
|
const cls = lang ? ` class="language-${lang}"` : "";
|
|
41037
41262
|
const idx = codeBlocks.length;
|
|
41038
41263
|
codeBlocks.push(`<pre><code${cls}>${escaped}</code></pre>`);
|
|
@@ -41045,7 +41270,7 @@ function markdownToHtml(text) {
|
|
|
41045
41270
|
const inlineCodes = [];
|
|
41046
41271
|
result = result.replace(/`([^`\n]+)`/g, (_m, code) => {
|
|
41047
41272
|
const idx = inlineCodes.length;
|
|
41048
|
-
inlineCodes.push(`<code>${
|
|
41273
|
+
inlineCodes.push(`<code>${escapeHtml5(code)}</code>`);
|
|
41049
41274
|
return `${INLINE_PH}${idx}\x00`;
|
|
41050
41275
|
});
|
|
41051
41276
|
const htmlTags = [];
|
|
@@ -41057,24 +41282,24 @@ function markdownToHtml(text) {
|
|
|
41057
41282
|
htmlTags.push(match);
|
|
41058
41283
|
return `${HTMLTAG_PH}${idx}\x00`;
|
|
41059
41284
|
});
|
|
41060
|
-
result =
|
|
41285
|
+
result = escapeHtml5(result);
|
|
41061
41286
|
result = result.replace(/\*\*(.+?)\*\*/g, "<b>$1</b>");
|
|
41062
41287
|
result = result.replace(/(?<!\*)\*(?!\*)(.+?)(?<!\*)\*(?!\*)/g, "<i>$1</i>");
|
|
41063
41288
|
result = result.replace(/(?<![\w_])_(?!_)([^_\n]+?)_(?![\w_])/g, "<i>$1</i>");
|
|
41064
41289
|
result = result.replace(/~~(.+?)~~/g, "<s>$1</s>");
|
|
41065
|
-
result = result.replace(new RegExp(`${
|
|
41066
|
-
result = result.replace(new RegExp(`${
|
|
41067
|
-
result = result.replace(new RegExp(`${
|
|
41290
|
+
result = result.replace(new RegExp(`${escapeHtml5(BLOCK_PH)}(\\d+)${escapeHtml5("\x00")}`, "g"), (_m, idx) => codeBlocks[Number(idx)]);
|
|
41291
|
+
result = result.replace(new RegExp(`${escapeHtml5(TABLE_PH)}(\\d+)${escapeHtml5("\x00")}`, "g"), (_m, idx) => codeBlocks[Number(idx)]);
|
|
41292
|
+
result = result.replace(new RegExp(`${escapeHtml5(INLINE_PH)}(\\d+)${escapeHtml5("\x00")}`, "g"), (_m, idx) => inlineCodes[Number(idx)]);
|
|
41068
41293
|
const ALLOWED_LINK_SCHEMES = /^(?:https?|mailto|tel|tg):/i;
|
|
41069
41294
|
result = result.replace(/\[([^\]]+)\]\(([^)]+)\)/g, (_m, linkText, url) => {
|
|
41070
41295
|
const safe = ALLOWED_LINK_SCHEMES.test(url.trim()) ? url.trim() : "#";
|
|
41071
|
-
return `<a href="${
|
|
41296
|
+
return `<a href="${escapeHtml5(safe)}">${linkText}</a>`;
|
|
41072
41297
|
});
|
|
41073
41298
|
result = result.replace(/(?<![<\/\w>])(\b[\w][\w.-]*\.(?:ts|js|py|rs|go|json|yaml|yml|toml|md|txt|sh|bash|zsh|css|html|xml|sql|env|cfg|conf|ini|log|csv|tsx|jsx|vue|svelte|rb|java|kt|swift|c|cpp|h|hpp|zig|asm|wasm|lock|mod|sum)\b)(?![^<]*>)/g, "<code>$1</code>");
|
|
41074
|
-
result = result.replace(new RegExp(`${
|
|
41299
|
+
result = result.replace(new RegExp(`${escapeHtml5(HTMLTAG_PH)}(\\d+)${escapeHtml5("\x00")}`, "g"), (_m, idx) => htmlTags[Number(idx)]);
|
|
41075
41300
|
return result;
|
|
41076
41301
|
}
|
|
41077
|
-
function
|
|
41302
|
+
function escapeHtml5(text) {
|
|
41078
41303
|
return text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
41079
41304
|
}
|
|
41080
41305
|
function telegramHtmlToPlainText(html) {
|
|
@@ -41475,7 +41700,7 @@ async function finalizeCallback(ctx, opts) {
|
|
|
41475
41700
|
}
|
|
41476
41701
|
|
|
41477
41702
|
// welcome-text.ts
|
|
41478
|
-
function
|
|
41703
|
+
function escapeHtml6(text) {
|
|
41479
41704
|
return text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
41480
41705
|
}
|
|
41481
41706
|
function formatAuthLine(auth) {
|
|
@@ -41487,13 +41712,13 @@ function formatAuthLine(auth) {
|
|
|
41487
41712
|
return "\u2717 not authenticated";
|
|
41488
41713
|
}
|
|
41489
41714
|
const sub = auth.subscription_type ?? "subscription";
|
|
41490
|
-
const expires = auth.expires_in ? ` \u00b7 expires ${
|
|
41491
|
-
return `\u2713 ${
|
|
41715
|
+
const expires = auth.expires_in ? ` \u00b7 expires ${escapeHtml6(auth.expires_in)}` : "";
|
|
41716
|
+
return `\u2713 ${escapeHtml6(sub)}${expires}`;
|
|
41492
41717
|
}
|
|
41493
41718
|
function formatAgentLine(meta) {
|
|
41494
41719
|
const m = meta.model && meta.model.length > 0 ? meta.model : "default";
|
|
41495
|
-
const topic = meta.topicName ? ` \u00b7 topic: ${
|
|
41496
|
-
return `<b>${
|
|
41720
|
+
const topic = meta.topicName ? ` \u00b7 topic: ${escapeHtml6([meta.topicEmoji, meta.topicName].filter(Boolean).join(" "))}` : "";
|
|
41721
|
+
return `<b>${escapeHtml6(meta.agentName)}</b> \u00b7 model: <code>${escapeHtml6(m)}</code>${topic}`;
|
|
41497
41722
|
}
|
|
41498
41723
|
function startText(agentName3, dmDisabled) {
|
|
41499
41724
|
if (dmDisabled)
|
|
@@ -41501,7 +41726,7 @@ function startText(agentName3, dmDisabled) {
|
|
|
41501
41726
|
return [
|
|
41502
41727
|
`<b>Switchroom</b> \u2014 Telegram on your Claude Pro or Max subscription.`,
|
|
41503
41728
|
``,
|
|
41504
|
-
`This bot is the <b>${
|
|
41729
|
+
`This bot is the <b>${escapeHtml6(agentName3)}</b> agent. Pair first, then send messages here and they reach the agent; replies and reactions come back.`,
|
|
41505
41730
|
``,
|
|
41506
41731
|
`<b>To pair:</b>`,
|
|
41507
41732
|
`1. DM me anything \u2014 you'll get a 6-char code`,
|
|
@@ -41515,7 +41740,7 @@ function helpText(agentName3) {
|
|
|
41515
41740
|
return [
|
|
41516
41741
|
`<b>Switchroom</b> \u2014 your Pro/Max subscription, wired to Telegram.`,
|
|
41517
41742
|
``,
|
|
41518
|
-
`This bot is the <b>${
|
|
41743
|
+
`This bot is the <b>${escapeHtml6(agentName3)}</b> agent. Text and photos route through to it; replies, reactions and progress cards come back.`,
|
|
41519
41744
|
``,
|
|
41520
41745
|
`Tool approvals surface as inline buttons (\u2705 / \u274c) or via <code>/approve</code>, <code>/deny</code>, <code>/pending</code>. Start a fresh session with <code>/new</code> or <code>/reset</code>.`,
|
|
41521
41746
|
``,
|
|
@@ -41534,40 +41759,40 @@ var STATUS_DOT = {
|
|
|
41534
41759
|
function statusPairedText(params) {
|
|
41535
41760
|
const { user, meta } = params;
|
|
41536
41761
|
const lines = [
|
|
41537
|
-
`Paired as ${
|
|
41762
|
+
`Paired as ${escapeHtml6(user)}.`,
|
|
41538
41763
|
``,
|
|
41539
41764
|
`Agent: ${formatAgentLine(meta)}`,
|
|
41540
41765
|
`Auth: ${formatAuthLine(meta.auth)}`
|
|
41541
41766
|
];
|
|
41542
41767
|
if (meta.status)
|
|
41543
|
-
lines.push(`Status: <code>${
|
|
41768
|
+
lines.push(`Status: <code>${escapeHtml6(meta.status)}</code>${meta.uptime ? ` \u00b7 up ${escapeHtml6(meta.uptime)}` : ""}`);
|
|
41544
41769
|
if (meta.live && meta.live.length > 0) {
|
|
41545
41770
|
lines.push("");
|
|
41546
41771
|
lines.push("<b>Health</b>");
|
|
41547
41772
|
for (const row of meta.live) {
|
|
41548
41773
|
const dot = STATUS_DOT[row.status] ?? STATUS_DOT.fail;
|
|
41549
|
-
lines.push(`${dot} <b>${
|
|
41774
|
+
lines.push(`${dot} <b>${escapeHtml6(row.label)}</b> ${escapeHtml6(row.detail)}`);
|
|
41550
41775
|
}
|
|
41551
41776
|
}
|
|
41552
41777
|
const audit = meta.audit;
|
|
41553
41778
|
if (audit) {
|
|
41554
41779
|
lines.push("");
|
|
41555
41780
|
if (audit.version)
|
|
41556
|
-
lines.push(`<b>Version</b> ${
|
|
41781
|
+
lines.push(`<b>Version</b> ${escapeHtml6(audit.version)}`);
|
|
41557
41782
|
if (meta.extendsProfile)
|
|
41558
|
-
lines.push(`<b>Profile</b> ${
|
|
41783
|
+
lines.push(`<b>Profile</b> ${escapeHtml6(meta.extendsProfile)}`);
|
|
41559
41784
|
if (audit.tools)
|
|
41560
|
-
lines.push(`<b>Tools</b> ${
|
|
41785
|
+
lines.push(`<b>Tools</b> ${escapeHtml6(audit.tools)}`);
|
|
41561
41786
|
if (audit.toolsDeny)
|
|
41562
|
-
lines.push(`<b>Deny</b> ${
|
|
41787
|
+
lines.push(`<b>Deny</b> ${escapeHtml6(audit.toolsDeny)}`);
|
|
41563
41788
|
if (audit.skills)
|
|
41564
|
-
lines.push(`<b>Skills</b> ${
|
|
41789
|
+
lines.push(`<b>Skills</b> ${escapeHtml6(audit.skills)}`);
|
|
41565
41790
|
if (audit.limits)
|
|
41566
|
-
lines.push(`<b>Limits</b> ${
|
|
41791
|
+
lines.push(`<b>Limits</b> ${escapeHtml6(audit.limits)}`);
|
|
41567
41792
|
if (audit.channel)
|
|
41568
|
-
lines.push(`<b>Channel</b> ${
|
|
41793
|
+
lines.push(`<b>Channel</b> ${escapeHtml6(audit.channel)}`);
|
|
41569
41794
|
if (audit.memoryBank)
|
|
41570
|
-
lines.push(`<b>Memory</b> ${
|
|
41795
|
+
lines.push(`<b>Memory</b> ${escapeHtml6(audit.memoryBank)}`);
|
|
41571
41796
|
}
|
|
41572
41797
|
return lines.join(`
|
|
41573
41798
|
`);
|
|
@@ -41575,7 +41800,7 @@ function statusPairedText(params) {
|
|
|
41575
41800
|
function statusPendingText(code) {
|
|
41576
41801
|
return `Pending pairing \u2014 run in Claude Code:
|
|
41577
41802
|
|
|
41578
|
-
<code>/telegram:access pair ${
|
|
41803
|
+
<code>/telegram:access pair ${escapeHtml6(code)}</code>`;
|
|
41579
41804
|
}
|
|
41580
41805
|
function statusUnpairedText() {
|
|
41581
41806
|
return "Not paired. Send me a message to get a pairing code.";
|
|
@@ -41604,7 +41829,7 @@ var TELEGRAM_BASE_COMMANDS = TELEGRAM_MENU_COMMANDS.slice(0, 3);
|
|
|
41604
41829
|
var TELEGRAM_SWITCHROOM_COMMANDS = TELEGRAM_MENU_COMMANDS.slice(3);
|
|
41605
41830
|
function switchroomHelpText(agentName3) {
|
|
41606
41831
|
return [
|
|
41607
|
-
`<b>Switchroom bot</b> \u2014 commands for the <b>${
|
|
41832
|
+
`<b>Switchroom bot</b> \u2014 commands for the <b>${escapeHtml6(agentName3)}</b> agent.`,
|
|
41608
41833
|
``,
|
|
41609
41834
|
`<b>Session & approvals</b>`,
|
|
41610
41835
|
`<code>/new</code> \u2014 fresh session (flush handoff, restart)`,
|
|
@@ -41652,15 +41877,15 @@ function switchroomHelpText(agentName3) {
|
|
|
41652
41877
|
`);
|
|
41653
41878
|
}
|
|
41654
41879
|
function restartAckText(agentName3) {
|
|
41655
|
-
return `\uD83D\uDD04 Restarting <b>${
|
|
41880
|
+
return `\uD83D\uDD04 Restarting <b>${escapeHtml6(agentName3)}</b>\u2026`;
|
|
41656
41881
|
}
|
|
41657
41882
|
function newSessionAckText(agentName3, flushedHandoff) {
|
|
41658
41883
|
const tail = flushedHandoff ? " \u00b7 flushed handoff" : "";
|
|
41659
|
-
return `\uD83C\uDD95 Started fresh session for <b>${
|
|
41884
|
+
return `\uD83C\uDD95 Started fresh session for <b>${escapeHtml6(agentName3)}</b>${tail} \u00b7 restarting\u2026`;
|
|
41660
41885
|
}
|
|
41661
41886
|
function resetSessionAckText(agentName3, flushedHandoff) {
|
|
41662
41887
|
const tail = flushedHandoff ? " \u00b7 flushed handoff" : "";
|
|
41663
|
-
return `\uD83D\uDD04 Reset session for <b>${
|
|
41888
|
+
return `\uD83D\uDD04 Reset session for <b>${escapeHtml6(agentName3)}</b>${tail} \u00b7 restarting\u2026`;
|
|
41664
41889
|
}
|
|
41665
41890
|
|
|
41666
41891
|
// gateway/auth-status-adapter.ts
|
|
@@ -41868,7 +42093,7 @@ function shouldShowHandoffLine() {
|
|
|
41868
42093
|
function formatHandoffLine(topic, format) {
|
|
41869
42094
|
const prefix = "\u21a9\ufe0f Picked up where we left off, ";
|
|
41870
42095
|
if (format === "html") {
|
|
41871
|
-
return `<i>${prefix}${
|
|
42096
|
+
return `<i>${prefix}${escapeHtml7(topic)}</i>
|
|
41872
42097
|
|
|
41873
42098
|
`;
|
|
41874
42099
|
}
|
|
@@ -41883,7 +42108,7 @@ function formatHandoffLine(topic, format) {
|
|
|
41883
42108
|
|
|
41884
42109
|
`;
|
|
41885
42110
|
}
|
|
41886
|
-
function
|
|
42111
|
+
function escapeHtml7(s) {
|
|
41887
42112
|
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
41888
42113
|
}
|
|
41889
42114
|
var MDV2_SPECIALS = /[_*\[\]()~`>#+\-=|{}.!\\]/g;
|
|
@@ -45909,13 +46134,13 @@ function buildMs365CardText(p) {
|
|
|
45909
46134
|
const lines = [];
|
|
45910
46135
|
lines.push(`\uD83D\uDCC4 Microsoft 365 write approval`);
|
|
45911
46136
|
lines.push("");
|
|
45912
|
-
lines.push(`Agent: ${
|
|
45913
|
-
lines.push(`Tool: ${
|
|
45914
|
-
lines.push(`Item: ${
|
|
46137
|
+
lines.push(`Agent: ${truncate3(p.agentName, 64)}`);
|
|
46138
|
+
lines.push(`Tool: ${truncate3(p.toolName.replace(/^mcp__/, ""), 96)}`);
|
|
46139
|
+
lines.push(`Item: ${truncate3(p.itemDisplayName, 256)}`);
|
|
45915
46140
|
if (p.itemId !== "(new)") {
|
|
45916
|
-
lines.push(`ID: ${
|
|
46141
|
+
lines.push(`ID: ${truncate3(p.itemId, 96)}`);
|
|
45917
46142
|
}
|
|
45918
|
-
lines.push(`Account: ${
|
|
46143
|
+
lines.push(`Account: ${truncate3(p.accountEmail, 96)}`);
|
|
45919
46144
|
if (typeof p.sizeBytesBefore === "number" || typeof p.sizeBytesAfter === "number") {
|
|
45920
46145
|
const before = p.sizeBytesBefore ?? 0;
|
|
45921
46146
|
const after = p.sizeBytesAfter ?? 0;
|
|
@@ -45924,18 +46149,18 @@ function buildMs365CardText(p) {
|
|
|
45924
46149
|
lines.push(`Size: ${humanBytes(before)} \u2192 ${humanBytes(after)} (${sign}${humanBytes(delta)})`);
|
|
45925
46150
|
}
|
|
45926
46151
|
if (p.deepLink) {
|
|
45927
|
-
lines.push(`Link: ${
|
|
46152
|
+
lines.push(`Link: ${truncate3(p.deepLink, 256)}`);
|
|
45928
46153
|
}
|
|
45929
46154
|
if (p.agentRationale) {
|
|
45930
46155
|
lines.push("");
|
|
45931
|
-
lines.push(`\uD83D\uDCAC ${
|
|
46156
|
+
lines.push(`\uD83D\uDCAC ${truncate3(p.agentRationale, 512)}`);
|
|
45932
46157
|
}
|
|
45933
46158
|
lines.push("");
|
|
45934
46159
|
lines.push("\u26a0\ufe0f Weak attestation (RFC \u00a78 v1): operator should click through to verify the actual change before approving. Structural diff coming v1.5.");
|
|
45935
46160
|
return lines.join(`
|
|
45936
46161
|
`);
|
|
45937
46162
|
}
|
|
45938
|
-
function
|
|
46163
|
+
function truncate3(s, n) {
|
|
45939
46164
|
if (s.length <= n)
|
|
45940
46165
|
return s;
|
|
45941
46166
|
return s.slice(0, n - 1) + "\u2026";
|
|
@@ -46051,9 +46276,9 @@ function buildDiffPreviewCard(input) {
|
|
|
46051
46276
|
}
|
|
46052
46277
|
const preview = input.preview;
|
|
46053
46278
|
const bodyLines = [];
|
|
46054
|
-
bodyLines.push(`<b>${
|
|
46279
|
+
bodyLines.push(`<b>${escapeHtml8(preview.title)}</b>`);
|
|
46055
46280
|
for (const line of preview.lines) {
|
|
46056
|
-
bodyLines.push(
|
|
46281
|
+
bodyLines.push(escapeHtml8(line.text));
|
|
46057
46282
|
}
|
|
46058
46283
|
const text = bodyLines.join(`
|
|
46059
46284
|
`);
|
|
@@ -46106,7 +46331,7 @@ function buildDiffPreviewCard(input) {
|
|
|
46106
46331
|
}
|
|
46107
46332
|
return { text, reply_markup: kb };
|
|
46108
46333
|
}
|
|
46109
|
-
function
|
|
46334
|
+
function escapeHtml8(s) {
|
|
46110
46335
|
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
46111
46336
|
}
|
|
46112
46337
|
|
|
@@ -46959,14 +47184,14 @@ function buildVaultSaveDiscardedInbound(opts) {
|
|
|
46959
47184
|
// gateway/subagent-handback-inbound-builder.ts
|
|
46960
47185
|
var HANDBACK_RESULT_MAX = 3000;
|
|
46961
47186
|
var HANDBACK_DESC_MAX = 200;
|
|
46962
|
-
function
|
|
47187
|
+
function truncate4(s, max) {
|
|
46963
47188
|
const t = s.trim();
|
|
46964
47189
|
return t.length > max ? t.slice(0, max) + "\u2026" : t;
|
|
46965
47190
|
}
|
|
46966
47191
|
function buildSubagentHandbackInbound(opts) {
|
|
46967
47192
|
const ts = opts.nowMs ?? Date.now();
|
|
46968
|
-
const desc =
|
|
46969
|
-
const result =
|
|
47193
|
+
const desc = truncate4(opts.ctx.taskDescription, HANDBACK_DESC_MAX) || "(no description)";
|
|
47194
|
+
const result = truncate4(opts.ctx.resultText, HANDBACK_RESULT_MAX);
|
|
46970
47195
|
const text = opts.ctx.outcome === "failed" ? `\uD83E\uDD1D A background worker you dispatched has FAILED.
|
|
46971
47196
|
|
|
46972
47197
|
` + `Task: ${desc}
|
|
@@ -47030,7 +47255,7 @@ function decideSubagentHandback(input) {
|
|
|
47030
47255
|
var PROGRESS_RESULT_MAX = 800;
|
|
47031
47256
|
var PROGRESS_DESC_MAX = 200;
|
|
47032
47257
|
var DEFAULT_PROGRESS_INTERVAL_MS = 5 * 60 * 1000;
|
|
47033
|
-
function
|
|
47258
|
+
function truncate5(s, max) {
|
|
47034
47259
|
const t = s.trim();
|
|
47035
47260
|
return t.length > max ? t.slice(0, max) + "\u2026" : t;
|
|
47036
47261
|
}
|
|
@@ -47044,8 +47269,8 @@ function formatElapsed(ms) {
|
|
|
47044
47269
|
}
|
|
47045
47270
|
function buildSubagentProgressInbound(opts) {
|
|
47046
47271
|
const ts = opts.nowMs ?? Date.now();
|
|
47047
|
-
const desc =
|
|
47048
|
-
const summary =
|
|
47272
|
+
const desc = truncate5(opts.ctx.taskDescription, PROGRESS_DESC_MAX) || "(no description)";
|
|
47273
|
+
const summary = truncate5(opts.ctx.latestSummary, PROGRESS_RESULT_MAX);
|
|
47049
47274
|
const elapsed = formatElapsed(opts.ctx.elapsedMs);
|
|
47050
47275
|
const expiresAt = ts + 2 * opts.ctx.progressIntervalMs;
|
|
47051
47276
|
const text = `\uD83D\uDD04 A background worker you dispatched is still running.
|
|
@@ -48852,7 +49077,9 @@ function readSubTail(entry, tail, now, onDescriptionUpdate, fs2, log, db2, paren
|
|
|
48852
49077
|
prevBucketIdx: entry.lastProgressBucketIdx,
|
|
48853
49078
|
setBucketIdx: (b) => {
|
|
48854
49079
|
entry.lastProgressBucketIdx = b;
|
|
48855
|
-
}
|
|
49080
|
+
},
|
|
49081
|
+
lastTool: entry.lastTool,
|
|
49082
|
+
toolCount: entry.toolCount
|
|
48856
49083
|
});
|
|
48857
49084
|
} catch (cbErr) {
|
|
48858
49085
|
log?.(`subagent-watcher: onProgress callback error ${entry.agentId}: ${cbErr.message}`);
|
|
@@ -49088,7 +49315,7 @@ function startSubagentWatcher(config) {
|
|
|
49088
49315
|
if (idleMs >= threshold) {
|
|
49089
49316
|
entry.stallNotified = true;
|
|
49090
49317
|
entry.stalledAt = n;
|
|
49091
|
-
const desc =
|
|
49318
|
+
const desc = escapeHtml(truncate(entry.description, 80));
|
|
49092
49319
|
const idleSec = Math.floor(idleMs / 1000);
|
|
49093
49320
|
log?.(`subagent-watcher: stall detected for ${entry.agentId} (idle ${idleSec}s): ${desc}`);
|
|
49094
49321
|
if (db2 != null) {
|
|
@@ -49562,7 +49789,7 @@ function renderIssuesCard(opts) {
|
|
|
49562
49789
|
const maxSeverity = sorted[0].severity;
|
|
49563
49790
|
const headerEmoji = SEVERITY_EMOJI[maxSeverity];
|
|
49564
49791
|
const count = sorted.length;
|
|
49565
|
-
const header = `${headerEmoji} <b>${
|
|
49792
|
+
const header = `${headerEmoji} <b>${escapeHtml(opts.agentName)}</b> \u00b7 ${count} ${count === 1 ? "issue" : "issues"}`;
|
|
49566
49793
|
const maxRows = opts.maxRows ?? DEFAULT_MAX_ROWS;
|
|
49567
49794
|
const visible = sorted.slice(0, maxRows);
|
|
49568
49795
|
const overflow = sorted.length - visible.length;
|
|
@@ -49571,10 +49798,10 @@ function renderIssuesCard(opts) {
|
|
|
49571
49798
|
const emoji = SEVERITY_EMOJI[e.severity];
|
|
49572
49799
|
const occ = e.occurrences > 1 ? ` <i>(\u00d7${e.occurrences})</i>` : "";
|
|
49573
49800
|
const ago = relTime(now - e.last_seen);
|
|
49574
|
-
const head = `${emoji} <code>${
|
|
49801
|
+
const head = `${emoji} <code>${escapeHtml(e.fingerprint)}</code> ${escapeHtml(e.summary)}${occ} \u2014 <i>${ago}</i>`;
|
|
49575
49802
|
const remediation = formatRemediation(e.detail);
|
|
49576
49803
|
return remediation == null ? head : `${head}
|
|
49577
|
-
\u2192 <i>${
|
|
49804
|
+
\u2192 <i>${escapeHtml(remediation)}</i>`;
|
|
49578
49805
|
});
|
|
49579
49806
|
const lines = [header, "", ...rows];
|
|
49580
49807
|
if (overflow > 0) {
|
|
@@ -49612,7 +49839,7 @@ function relTime(deltaMs) {
|
|
|
49612
49839
|
const d = Math.round(h / 24);
|
|
49613
49840
|
return `${d}d ago`;
|
|
49614
49841
|
}
|
|
49615
|
-
function
|
|
49842
|
+
function extractRetryAfterSecs2(err) {
|
|
49616
49843
|
if (err == null || typeof err !== "object")
|
|
49617
49844
|
return null;
|
|
49618
49845
|
const e = err;
|
|
@@ -49623,7 +49850,7 @@ function extractRetryAfterSecs(err) {
|
|
|
49623
49850
|
return ra;
|
|
49624
49851
|
return null;
|
|
49625
49852
|
}
|
|
49626
|
-
var
|
|
49853
|
+
var COOLDOWN_JITTER_MS2 = 500;
|
|
49627
49854
|
function readPersistedMessageId(path, log) {
|
|
49628
49855
|
try {
|
|
49629
49856
|
const raw = readFileSync29(path, "utf8");
|
|
@@ -49655,10 +49882,10 @@ function createIssuesCardHandle(opts) {
|
|
|
49655
49882
|
let cooldownUntil = 0;
|
|
49656
49883
|
const nowFn = opts.now ?? Date.now;
|
|
49657
49884
|
function noteRateLimited(err, label) {
|
|
49658
|
-
const retryAfter =
|
|
49885
|
+
const retryAfter = extractRetryAfterSecs2(err);
|
|
49659
49886
|
if (retryAfter == null)
|
|
49660
49887
|
return;
|
|
49661
|
-
cooldownUntil = nowFn() + retryAfter * 1000 +
|
|
49888
|
+
cooldownUntil = nowFn() + retryAfter * 1000 + COOLDOWN_JITTER_MS2;
|
|
49662
49889
|
log(`issues-card: ${label} 429 \u2014 backing off ${retryAfter}s (cooldown until ${cooldownUntil})`);
|
|
49663
49890
|
}
|
|
49664
49891
|
function setMessageId(next) {
|
|
@@ -50913,10 +51140,10 @@ function sweepStaleTurnActiveMarker(stateDir, opts) {
|
|
|
50913
51140
|
}
|
|
50914
51141
|
|
|
50915
51142
|
// ../src/build-info.ts
|
|
50916
|
-
var VERSION = "0.14.
|
|
50917
|
-
var COMMIT_SHA = "
|
|
50918
|
-
var COMMIT_DATE = "2026-05-
|
|
50919
|
-
var LATEST_PR =
|
|
51143
|
+
var VERSION = "0.14.16";
|
|
51144
|
+
var COMMIT_SHA = "6f5d9562";
|
|
51145
|
+
var COMMIT_DATE = "2026-05-30T04:37:39Z";
|
|
51146
|
+
var LATEST_PR = 2003;
|
|
50920
51147
|
var COMMITS_AHEAD_OF_TAG = 0;
|
|
50921
51148
|
|
|
50922
51149
|
// gateway/boot-version.ts
|
|
@@ -51827,6 +52054,11 @@ if (!STATIC)
|
|
|
51827
52054
|
var chatThreadMap = new Map;
|
|
51828
52055
|
var activeStatusReactions = new Map;
|
|
51829
52056
|
var activeReactionMsgIds = new Map;
|
|
52057
|
+
var deferredDoneReactions = new DeferredDoneReactions({
|
|
52058
|
+
countRunningWorkers: () => countRunningWorkers(),
|
|
52059
|
+
getActive: (key) => activeStatusReactions.get(key),
|
|
52060
|
+
purge: (key) => purgeReactionTracking(key)
|
|
52061
|
+
});
|
|
51830
52062
|
var outboundDedup = new OutboundDedupCache;
|
|
51831
52063
|
var chatAvailableReactions = new Map;
|
|
51832
52064
|
var chatProbesInFlight = new Set;
|
|
@@ -52084,9 +52316,23 @@ function finalizeStatusReaction(chatId, threadId, reason = "done") {
|
|
|
52084
52316
|
const ctrl = activeStatusReactions.get(key);
|
|
52085
52317
|
if (!ctrl)
|
|
52086
52318
|
return;
|
|
52319
|
+
if (reason === "done" && deferredDoneReactions.tryDefer(key, ctrl))
|
|
52320
|
+
return;
|
|
52321
|
+
deferredDoneReactions.drop(key);
|
|
52087
52322
|
ctrl.finalize(reason);
|
|
52088
52323
|
purgeReactionTracking(key);
|
|
52089
52324
|
}
|
|
52325
|
+
function countRunningWorkers() {
|
|
52326
|
+
const reg = subagentWatcher?.getRegistry();
|
|
52327
|
+
if (reg == null)
|
|
52328
|
+
return 0;
|
|
52329
|
+
let n = 0;
|
|
52330
|
+
for (const e of reg.values()) {
|
|
52331
|
+
if (e.state === "running" && !e.historical)
|
|
52332
|
+
n++;
|
|
52333
|
+
}
|
|
52334
|
+
return n;
|
|
52335
|
+
}
|
|
52090
52336
|
function resolveThreadId(chat_id, explicit) {
|
|
52091
52337
|
if (explicit != null)
|
|
52092
52338
|
return Number(explicit);
|
|
@@ -61042,6 +61288,18 @@ var didOneTimeSetup = false;
|
|
|
61042
61288
|
if (streamMode === "checklist") {
|
|
61043
61289
|
const watcherAgentDir = resolveAgentDirFromEnv();
|
|
61044
61290
|
if (watcherAgentDir != null) {
|
|
61291
|
+
const workerFeedEnabled = process.env.SWITCHROOM_WORKER_ACTIVITY_FEED === "1";
|
|
61292
|
+
const workerActivityFeed = createWorkerActivityFeed({
|
|
61293
|
+
bot: {
|
|
61294
|
+
sendMessage: async (cid, text, sendOpts) => {
|
|
61295
|
+
const sent = await robustApiCall(() => lockedBot.api.sendMessage(cid, text, sendOpts), { chat_id: cid, verb: "worker-feed" });
|
|
61296
|
+
return sent;
|
|
61297
|
+
},
|
|
61298
|
+
editMessageText: (cid, mid, text, editOpts) => robustApiCall(() => lockedBot.api.editMessageText(cid, mid, text, editOpts), { chat_id: cid, verb: "worker-feed" })
|
|
61299
|
+
},
|
|
61300
|
+
log: (msg) => process.stderr.write(`telegram gateway: ${msg}
|
|
61301
|
+
`)
|
|
61302
|
+
});
|
|
61045
61303
|
subagentWatcher = startSubagentWatcher({
|
|
61046
61304
|
agentDir: watcherAgentDir,
|
|
61047
61305
|
agentCwd: watcherAgentDir,
|
|
@@ -61071,9 +61329,11 @@ var didOneTimeSetup = false;
|
|
|
61071
61329
|
`);
|
|
61072
61330
|
}
|
|
61073
61331
|
},
|
|
61074
|
-
onFinish: ({ agentId, outcome, description, resultText }) => {
|
|
61332
|
+
onFinish: ({ agentId, outcome, description, resultText, toolCount, durationMs }) => {
|
|
61333
|
+
deferredDoneReactions.promote();
|
|
61075
61334
|
let fleetChatId = "";
|
|
61076
61335
|
let isBackground = false;
|
|
61336
|
+
let dispatchDesc = "";
|
|
61077
61337
|
try {
|
|
61078
61338
|
const fleets = progressDriver?.peekAllFleets() ?? [];
|
|
61079
61339
|
for (const f of fleets) {
|
|
@@ -61085,11 +61345,23 @@ var didOneTimeSetup = false;
|
|
|
61085
61345
|
} catch {}
|
|
61086
61346
|
if (turnsDb != null) {
|
|
61087
61347
|
try {
|
|
61088
|
-
const row = turnsDb.prepare("SELECT background FROM subagents WHERE jsonl_agent_id = ?").get(agentId);
|
|
61089
|
-
if (row != null)
|
|
61348
|
+
const row = turnsDb.prepare("SELECT background, description FROM subagents WHERE jsonl_agent_id = ?").get(agentId);
|
|
61349
|
+
if (row != null) {
|
|
61090
61350
|
isBackground = row.background === 1;
|
|
61351
|
+
dispatchDesc = row.description ?? "";
|
|
61352
|
+
}
|
|
61091
61353
|
} catch {}
|
|
61092
61354
|
}
|
|
61355
|
+
if (workerFeedEnabled) {
|
|
61356
|
+
workerActivityFeed.finish(agentId, {
|
|
61357
|
+
description: dispatchDesc || description,
|
|
61358
|
+
lastTool: null,
|
|
61359
|
+
toolCount,
|
|
61360
|
+
latestSummary: resultText,
|
|
61361
|
+
elapsedMs: durationMs,
|
|
61362
|
+
state: outcome === "failed" ? "failed" : "done"
|
|
61363
|
+
});
|
|
61364
|
+
}
|
|
61093
61365
|
const decision = decideSubagentHandback({
|
|
61094
61366
|
handbackEnvValue: process.env.SWITCHROOM_SUBAGENT_HANDBACK,
|
|
61095
61367
|
outcome,
|
|
@@ -61122,9 +61394,10 @@ var didOneTimeSetup = false;
|
|
|
61122
61394
|
process.stderr.write(`telegram gateway: subagent-handback queued agent=${agentId} outcome=${outcome} chat=${decision.chatId} resultChars=${resultText.length}
|
|
61123
61395
|
`);
|
|
61124
61396
|
},
|
|
61125
|
-
onProgress: ({ agentId, description, latestSummary, elapsedMs, prevBucketIdx, setBucketIdx }) => {
|
|
61397
|
+
onProgress: ({ agentId, description, latestSummary, elapsedMs, prevBucketIdx, setBucketIdx, lastTool, toolCount }) => {
|
|
61126
61398
|
let fleetChatId = "";
|
|
61127
61399
|
let isBackground = false;
|
|
61400
|
+
let dispatchDesc = "";
|
|
61128
61401
|
try {
|
|
61129
61402
|
const fleets = progressDriver?.peekAllFleets() ?? [];
|
|
61130
61403
|
for (const f of fleets) {
|
|
@@ -61136,13 +61409,26 @@ var didOneTimeSetup = false;
|
|
|
61136
61409
|
} catch {}
|
|
61137
61410
|
if (turnsDb != null) {
|
|
61138
61411
|
try {
|
|
61139
|
-
const row = turnsDb.prepare("SELECT background FROM subagents WHERE jsonl_agent_id = ?").get(agentId);
|
|
61140
|
-
if (row != null)
|
|
61412
|
+
const row = turnsDb.prepare("SELECT background, description FROM subagents WHERE jsonl_agent_id = ?").get(agentId);
|
|
61413
|
+
if (row != null) {
|
|
61141
61414
|
isBackground = row.background === 1;
|
|
61415
|
+
dispatchDesc = row.description ?? "";
|
|
61416
|
+
}
|
|
61142
61417
|
} catch {}
|
|
61143
61418
|
}
|
|
61144
61419
|
if (!isBackground)
|
|
61145
61420
|
return;
|
|
61421
|
+
if (workerFeedEnabled) {
|
|
61422
|
+
workerActivityFeed.update(agentId, fleetChatId || (loadAccess().allowFrom[0] ?? ""), {
|
|
61423
|
+
description: dispatchDesc || description,
|
|
61424
|
+
lastTool,
|
|
61425
|
+
toolCount,
|
|
61426
|
+
latestSummary,
|
|
61427
|
+
elapsedMs,
|
|
61428
|
+
state: "running"
|
|
61429
|
+
});
|
|
61430
|
+
return;
|
|
61431
|
+
}
|
|
61146
61432
|
const decision = decideSubagentProgress({
|
|
61147
61433
|
disableEnvValue: process.env.SWITCHROOM_DISABLE_SUBAGENT_PROGRESS,
|
|
61148
61434
|
isBackground,
|