switchroom 0.18.23 → 0.18.25
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 +59 -11
- package/dist/host-control/main.js +1 -1
- package/package.json +1 -1
- package/telegram-plugin/dist/bridge/bridge.js +26 -0
- package/telegram-plugin/dist/gateway/gateway.js +1608 -841
- package/telegram-plugin/dist/server.js +26 -0
- package/telegram-plugin/gateway/callback-query-handlers.ts +7 -0
- package/telegram-plugin/gateway/gateway.ts +524 -16
- package/telegram-plugin/gateway/model-command.ts +188 -56
- package/telegram-plugin/gateway/redelivery-decision.ts +139 -0
- package/telegram-plugin/gateway/vault-grant-inbound-builders.ts +42 -1
- package/telegram-plugin/history.ts +118 -0
- package/telegram-plugin/registry/turns-schema.ts +89 -1
- package/telegram-plugin/reply-owner-resolve.ts +160 -0
- package/telegram-plugin/session-tail.ts +185 -0
- package/telegram-plugin/subagent-watcher.ts +45 -0
- package/telegram-plugin/tests/crash-redelivery-resume-exclusion.test.ts +133 -0
- package/telegram-plugin/tests/crash-redelivery-wiring.test.ts +72 -0
- package/telegram-plugin/tests/history.test.ts +91 -0
- package/telegram-plugin/tests/model-command.test.ts +189 -12
- package/telegram-plugin/tests/redelivery-decision.test.ts +84 -0
- package/telegram-plugin/tests/registry-turns.test.ts +51 -0
- package/telegram-plugin/tests/reply-owner-resolve.test.ts +279 -0
- package/telegram-plugin/tests/session-model-source.test.ts +11 -0
- package/telegram-plugin/tests/session-tail.test.ts +145 -0
- package/telegram-plugin/tests/subagent-watcher.test.ts +50 -0
- package/telegram-plugin/tests/tool-activity-summary.test.ts +109 -0
- package/telegram-plugin/tests/trailing-answer-projector.test.ts +124 -0
- package/telegram-plugin/tests/vault-grant-inbound-builders.test.ts +125 -0
- package/telegram-plugin/tests/worker-feed-coalesce.test.ts +117 -1
- package/telegram-plugin/tests/worker-feed-pin-persistence.test.ts +306 -0
- package/telegram-plugin/tool-activity-summary.ts +54 -3
- package/telegram-plugin/worker-activity-feed.ts +222 -10
- package/vendor/hindsight-memory/scripts/backfill_transcripts.py +762 -0
- package/vendor/hindsight-memory/scripts/drain_pending.py +13 -1
- package/vendor/hindsight-memory/scripts/lib/client.py +14 -4
- package/vendor/hindsight-memory/scripts/lib/config.py +8 -0
- package/vendor/hindsight-memory/scripts/lib/pacing.py +102 -0
- package/vendor/hindsight-memory/scripts/lib/watermark.py +213 -0
- package/vendor/hindsight-memory/scripts/reconcile_tail.py +344 -0
- package/vendor/hindsight-memory/scripts/retain.py +299 -143
- package/vendor/hindsight-memory/scripts/session_start.py +14 -0
- package/vendor/hindsight-memory/scripts/tests/test_backfill.py +362 -0
- package/vendor/hindsight-memory/scripts/tests/test_reconcile_durability.py +350 -0
- package/vendor/hindsight-memory/tests/test_hooks.py +8 -2
|
@@ -21866,6 +21866,142 @@ var init_loader = __esm(() => {
|
|
|
21866
21866
|
};
|
|
21867
21867
|
});
|
|
21868
21868
|
|
|
21869
|
+
// quota-check.ts
|
|
21870
|
+
import { readFileSync as readFileSync17, existsSync as existsSync15 } from "fs";
|
|
21871
|
+
import { join as join20 } from "path";
|
|
21872
|
+
function readOauthToken(claudeConfigDir) {
|
|
21873
|
+
const tokenFile = join20(claudeConfigDir, ".oauth-token");
|
|
21874
|
+
if (!existsSync15(tokenFile))
|
|
21875
|
+
return null;
|
|
21876
|
+
try {
|
|
21877
|
+
const raw = readFileSync17(tokenFile, "utf-8").trim();
|
|
21878
|
+
return raw.length > 0 ? raw : null;
|
|
21879
|
+
} catch {
|
|
21880
|
+
return null;
|
|
21881
|
+
}
|
|
21882
|
+
}
|
|
21883
|
+
function parseFloatHeader(headers, name) {
|
|
21884
|
+
const v = headers.get(name);
|
|
21885
|
+
if (v == null || v.trim().length === 0)
|
|
21886
|
+
return null;
|
|
21887
|
+
const n = Number(v);
|
|
21888
|
+
return Number.isFinite(n) ? n : null;
|
|
21889
|
+
}
|
|
21890
|
+
function parseEpochHeader(headers, name) {
|
|
21891
|
+
const v = headers.get(name);
|
|
21892
|
+
if (v == null)
|
|
21893
|
+
return null;
|
|
21894
|
+
const n = Number(v);
|
|
21895
|
+
if (!Number.isFinite(n) || n <= 0)
|
|
21896
|
+
return null;
|
|
21897
|
+
return new Date(n * 1000);
|
|
21898
|
+
}
|
|
21899
|
+
function parseQuotaHeaders(headers) {
|
|
21900
|
+
const fiveHour = parseFloatHeader(headers, "anthropic-ratelimit-unified-5h-utilization");
|
|
21901
|
+
const sevenDay = parseFloatHeader(headers, "anthropic-ratelimit-unified-7d-utilization");
|
|
21902
|
+
if (fiveHour == null && sevenDay == null) {
|
|
21903
|
+
return {
|
|
21904
|
+
ok: false,
|
|
21905
|
+
reason: "no unified rate-limit headers in response (API token, not OAuth?)"
|
|
21906
|
+
};
|
|
21907
|
+
}
|
|
21908
|
+
return {
|
|
21909
|
+
ok: true,
|
|
21910
|
+
data: {
|
|
21911
|
+
fiveHourUtilizationPct: (fiveHour ?? 0) * 100,
|
|
21912
|
+
sevenDayUtilizationPct: (sevenDay ?? 0) * 100,
|
|
21913
|
+
fiveHourUtilPresent: fiveHour != null,
|
|
21914
|
+
sevenDayUtilPresent: sevenDay != null,
|
|
21915
|
+
fiveHourResetAt: parseEpochHeader(headers, "anthropic-ratelimit-unified-5h-reset"),
|
|
21916
|
+
sevenDayResetAt: parseEpochHeader(headers, "anthropic-ratelimit-unified-7d-reset"),
|
|
21917
|
+
representativeClaim: headers.get("anthropic-ratelimit-unified-representative-claim"),
|
|
21918
|
+
overageStatus: headers.get("anthropic-ratelimit-unified-overage-status"),
|
|
21919
|
+
overageDisabledReason: headers.get("anthropic-ratelimit-unified-overage-disabled-reason")
|
|
21920
|
+
}
|
|
21921
|
+
};
|
|
21922
|
+
}
|
|
21923
|
+
async function fetchQuota(opts) {
|
|
21924
|
+
let token;
|
|
21925
|
+
if (opts.accessToken && opts.claudeConfigDir) {
|
|
21926
|
+
return {
|
|
21927
|
+
ok: false,
|
|
21928
|
+
reason: "pass only one of `accessToken` or `claudeConfigDir`, not both"
|
|
21929
|
+
};
|
|
21930
|
+
}
|
|
21931
|
+
if (opts.accessToken) {
|
|
21932
|
+
token = opts.accessToken.trim().length > 0 ? opts.accessToken : null;
|
|
21933
|
+
} else if (opts.claudeConfigDir) {
|
|
21934
|
+
token = readOauthToken(opts.claudeConfigDir);
|
|
21935
|
+
} else {
|
|
21936
|
+
return {
|
|
21937
|
+
ok: false,
|
|
21938
|
+
reason: "fetchQuota requires `accessToken` or `claudeConfigDir`"
|
|
21939
|
+
};
|
|
21940
|
+
}
|
|
21941
|
+
if (!token) {
|
|
21942
|
+
return { ok: false, reason: "no OAuth token at .oauth-token" };
|
|
21943
|
+
}
|
|
21944
|
+
const controller = new AbortController;
|
|
21945
|
+
const timeout = setTimeout(() => controller.abort(), opts.timeoutMs ?? 1e4);
|
|
21946
|
+
const fetchFn = opts.fetchImpl ?? fetch;
|
|
21947
|
+
let resp;
|
|
21948
|
+
try {
|
|
21949
|
+
resp = await fetchFn("https://api.anthropic.com/v1/messages", {
|
|
21950
|
+
method: "POST",
|
|
21951
|
+
headers: {
|
|
21952
|
+
"anthropic-version": "2023-06-01",
|
|
21953
|
+
"anthropic-beta": OAUTH_BETA,
|
|
21954
|
+
authorization: `Bearer ${token}`,
|
|
21955
|
+
"x-app": "cli",
|
|
21956
|
+
"user-agent": DEFAULT_USER_AGENT,
|
|
21957
|
+
"content-type": "application/json"
|
|
21958
|
+
},
|
|
21959
|
+
body: JSON.stringify({
|
|
21960
|
+
model: opts.model ?? DEFAULT_PROBE_MODEL,
|
|
21961
|
+
max_tokens: 1,
|
|
21962
|
+
messages: [{ role: "user", content: "hi" }]
|
|
21963
|
+
}),
|
|
21964
|
+
signal: controller.signal
|
|
21965
|
+
});
|
|
21966
|
+
} catch (err) {
|
|
21967
|
+
const msg = err?.message ?? String(err);
|
|
21968
|
+
return { ok: false, reason: `request failed: ${msg}` };
|
|
21969
|
+
} finally {
|
|
21970
|
+
clearTimeout(timeout);
|
|
21971
|
+
}
|
|
21972
|
+
if (resp.status === 401 || resp.status === 403) {
|
|
21973
|
+
return { ok: false, reason: `auth rejected (HTTP ${resp.status})` };
|
|
21974
|
+
}
|
|
21975
|
+
const parsed = parseQuotaHeaders(resp.headers);
|
|
21976
|
+
if (!parsed.ok && resp.status >= 400) {
|
|
21977
|
+
return { ok: false, reason: `HTTP ${resp.status}, ${parsed.reason}` };
|
|
21978
|
+
}
|
|
21979
|
+
return parsed;
|
|
21980
|
+
}
|
|
21981
|
+
function formatQuotaLine(q) {
|
|
21982
|
+
const fmt = (n) => `${Math.round(n)}%`;
|
|
21983
|
+
return `${fmt(q.fiveHourUtilizationPct)} / 5h \u00b7 ${fmt(q.sevenDayUtilizationPct)} / 7d`;
|
|
21984
|
+
}
|
|
21985
|
+
function formatResetRelative(target, now = new Date) {
|
|
21986
|
+
if (!target)
|
|
21987
|
+
return "\u2014";
|
|
21988
|
+
const deltaMs = target.getTime() - now.getTime();
|
|
21989
|
+
if (deltaMs <= 0)
|
|
21990
|
+
return "resets now";
|
|
21991
|
+
const totalMin = Math.round(deltaMs / 60000);
|
|
21992
|
+
if (totalMin < 60)
|
|
21993
|
+
return `resets in ${totalMin}m`;
|
|
21994
|
+
const hours = Math.floor(totalMin / 60);
|
|
21995
|
+
const mins = totalMin % 60;
|
|
21996
|
+
if (hours < 24)
|
|
21997
|
+
return mins > 0 ? `resets in ${hours}h ${mins}m` : `resets in ${hours}h`;
|
|
21998
|
+
const days = Math.floor(hours / 24);
|
|
21999
|
+
const remH = hours % 24;
|
|
22000
|
+
return remH > 0 ? `resets in ${days}d ${remH}h` : `resets in ${days}d`;
|
|
22001
|
+
}
|
|
22002
|
+
var OAUTH_BETA = "oauth-2025-04-20", DEFAULT_USER_AGENT = "claude-cli/1.0.0 (external, cli)", DEFAULT_PROBE_MODEL = "claude-haiku-4-5-20251001";
|
|
22003
|
+
var init_quota_check = () => {};
|
|
22004
|
+
|
|
21869
22005
|
// ../node_modules/.bun/@xterm+headless@6.0.0/node_modules/@xterm/headless/lib-headless/xterm-headless.js
|
|
21870
22006
|
var require_xterm_headless = __commonJS((exports2) => {
|
|
21871
22007
|
(() => {
|
|
@@ -27427,9 +27563,9 @@ var init_flock = () => {};
|
|
|
27427
27563
|
// ../src/vault/vault.ts
|
|
27428
27564
|
import { randomBytes as randomBytes4, scryptSync, createCipheriv, createDecipheriv } from "node:crypto";
|
|
27429
27565
|
import {
|
|
27430
|
-
readFileSync as
|
|
27566
|
+
readFileSync as readFileSync18,
|
|
27431
27567
|
writeSync as writeSync2,
|
|
27432
|
-
existsSync as
|
|
27568
|
+
existsSync as existsSync16,
|
|
27433
27569
|
renameSync as renameSync6,
|
|
27434
27570
|
mkdirSync as mkdirSync16,
|
|
27435
27571
|
unlinkSync as unlinkSync10,
|
|
@@ -27470,12 +27606,12 @@ function normalizeSecrets(raw) {
|
|
|
27470
27606
|
return out;
|
|
27471
27607
|
}
|
|
27472
27608
|
function openVault(passphrase, vaultPath) {
|
|
27473
|
-
if (!
|
|
27609
|
+
if (!existsSync16(vaultPath)) {
|
|
27474
27610
|
throw new VaultError(`Vault file not found: ${vaultPath}`);
|
|
27475
27611
|
}
|
|
27476
27612
|
let vaultFile;
|
|
27477
27613
|
try {
|
|
27478
|
-
vaultFile = JSON.parse(
|
|
27614
|
+
vaultFile = JSON.parse(readFileSync18(vaultPath, "utf8"));
|
|
27479
27615
|
} catch {
|
|
27480
27616
|
throw new VaultError(`Failed to read vault file: ${vaultPath}`);
|
|
27481
27617
|
}
|
|
@@ -27529,7 +27665,7 @@ import {
|
|
|
27529
27665
|
statSync as statSync5,
|
|
27530
27666
|
writeSync as writeSync3
|
|
27531
27667
|
} from "node:fs";
|
|
27532
|
-
import { join as
|
|
27668
|
+
import { join as join23 } from "node:path";
|
|
27533
27669
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
27534
27670
|
import { constants as fsConstants } from "node:fs";
|
|
27535
27671
|
function isVaultReference(value) {
|
|
@@ -27581,11 +27717,11 @@ function materializationRoot() {
|
|
|
27581
27717
|
return cachedRoot;
|
|
27582
27718
|
const xdg = process.env.XDG_RUNTIME_DIR;
|
|
27583
27719
|
if (xdg) {
|
|
27584
|
-
const base =
|
|
27720
|
+
const base = join23(xdg, "switchroom", "vault");
|
|
27585
27721
|
mkdirSync17(base, { recursive: true, mode: 448 });
|
|
27586
|
-
cachedRoot = mkdtempSync(
|
|
27722
|
+
cachedRoot = mkdtempSync(join23(base, "run-"));
|
|
27587
27723
|
} else {
|
|
27588
|
-
cachedRoot = mkdtempSync(
|
|
27724
|
+
cachedRoot = mkdtempSync(join23(tmpdir2(), "switchroom-vault-"));
|
|
27589
27725
|
}
|
|
27590
27726
|
chmodSync5(cachedRoot, 448);
|
|
27591
27727
|
return cachedRoot;
|
|
@@ -27600,7 +27736,7 @@ function writeFileExclusive(filePath, content3) {
|
|
|
27600
27736
|
}
|
|
27601
27737
|
}
|
|
27602
27738
|
function materializeFilesEntry(key, files) {
|
|
27603
|
-
const dir =
|
|
27739
|
+
const dir = join23(materializationRoot(), key);
|
|
27604
27740
|
if (materializedDirs.has(dir)) {
|
|
27605
27741
|
try {
|
|
27606
27742
|
rmSync(dir, { recursive: true, force: true });
|
|
@@ -27616,7 +27752,7 @@ function materializeFilesEntry(key, files) {
|
|
|
27616
27752
|
if (filename.includes("/") || filename.includes("\\") || filename === ".." || filename === "." || filename.includes("\x00")) {
|
|
27617
27753
|
throw new Error(`Refusing to materialize vault file with unsafe name: ${filename}`);
|
|
27618
27754
|
}
|
|
27619
|
-
const filePath =
|
|
27755
|
+
const filePath = join23(dir, filename);
|
|
27620
27756
|
const content3 = encoding === "base64" ? Buffer.from(value, "base64") : value;
|
|
27621
27757
|
writeFileExclusive(filePath, content3);
|
|
27622
27758
|
}
|
|
@@ -28184,17 +28320,21 @@ __export(exports_history, {
|
|
|
28184
28320
|
recordEdit: () => recordEdit,
|
|
28185
28321
|
query: () => query,
|
|
28186
28322
|
pruneMessagesOlderThanDays: () => pruneMessagesOlderThanDays,
|
|
28323
|
+
normalizeDeliveryText: () => normalizeDeliveryText,
|
|
28187
28324
|
lookupMessageRoleAndText: () => lookupMessageRoleAndText,
|
|
28188
28325
|
initHistory: () => initHistory,
|
|
28326
|
+
hasOutboundWithText: () => hasOutboundWithText,
|
|
28189
28327
|
hasOutboundDeliveredSince: () => hasOutboundDeliveredSince,
|
|
28190
28328
|
getRecentOutboundCount: () => getRecentOutboundCount,
|
|
28191
28329
|
getLatestInboundMessageId: () => getLatestInboundMessageId,
|
|
28330
|
+
deliveryTextMatch: () => deliveryTextMatch,
|
|
28192
28331
|
deleteFromHistory: () => deleteFromHistory,
|
|
28193
28332
|
checkpointWal: () => checkpointWal,
|
|
28194
|
-
_resetForTests: () => _resetForTests
|
|
28333
|
+
_resetForTests: () => _resetForTests,
|
|
28334
|
+
MIN_PREFIX_MATCH_CHARS: () => MIN_PREFIX_MATCH_CHARS
|
|
28195
28335
|
});
|
|
28196
|
-
import { chmodSync as chmodSync7, existsSync as
|
|
28197
|
-
import { join as
|
|
28336
|
+
import { chmodSync as chmodSync7, existsSync as existsSync22, mkdirSync as mkdirSync21 } from "fs";
|
|
28337
|
+
import { join as join26 } from "path";
|
|
28198
28338
|
function loadDatabaseClass() {
|
|
28199
28339
|
if (DatabaseClass != null)
|
|
28200
28340
|
return DatabaseClass;
|
|
@@ -28217,7 +28357,7 @@ function initHistory(stateDir, retentionDays = 30) {
|
|
|
28217
28357
|
return;
|
|
28218
28358
|
const Database = loadDatabaseClass();
|
|
28219
28359
|
mkdirSync21(stateDir, { recursive: true, mode: 448 });
|
|
28220
|
-
const path2 =
|
|
28360
|
+
const path2 = join26(stateDir, "history.db");
|
|
28221
28361
|
dbPath = path2;
|
|
28222
28362
|
db = new Database(path2, { create: true });
|
|
28223
28363
|
db.exec("PRAGMA journal_mode = WAL");
|
|
@@ -28283,7 +28423,7 @@ function initHistory(stateDir, retentionDays = 30) {
|
|
|
28283
28423
|
}
|
|
28284
28424
|
for (const suffix of ["", "-shm", "-wal"]) {
|
|
28285
28425
|
const f = path2 + suffix;
|
|
28286
|
-
if (
|
|
28426
|
+
if (existsSync22(f)) {
|
|
28287
28427
|
try {
|
|
28288
28428
|
chmodSync7(f, 420);
|
|
28289
28429
|
} catch {}
|
|
@@ -28308,7 +28448,7 @@ function checkpointWal() {
|
|
|
28308
28448
|
if (dbPath) {
|
|
28309
28449
|
for (const suffix of ["-shm", "-wal"]) {
|
|
28310
28450
|
const f = dbPath + suffix;
|
|
28311
|
-
if (
|
|
28451
|
+
if (existsSync22(f)) {
|
|
28312
28452
|
try {
|
|
28313
28453
|
chmodSync7(f, 420);
|
|
28314
28454
|
} catch {}
|
|
@@ -28447,6 +28587,50 @@ function hasOutboundDeliveredSince(chatId, sinceMs, threadId, minChars = 200) {
|
|
|
28447
28587
|
return false;
|
|
28448
28588
|
}
|
|
28449
28589
|
}
|
|
28590
|
+
function hasOutboundWithText(chatId, text4, threadId, sinceMs) {
|
|
28591
|
+
const needle = normalizeDeliveryText(text4);
|
|
28592
|
+
if (needle.length === 0)
|
|
28593
|
+
return false;
|
|
28594
|
+
try {
|
|
28595
|
+
const params = [chatId];
|
|
28596
|
+
let sql = "SELECT text FROM messages WHERE chat_id = ? AND role = 'assistant'";
|
|
28597
|
+
if (threadId !== undefined) {
|
|
28598
|
+
if (threadId === null) {
|
|
28599
|
+
sql += " AND thread_id IS NULL";
|
|
28600
|
+
} else {
|
|
28601
|
+
sql += " AND thread_id = ?";
|
|
28602
|
+
params.push(threadId);
|
|
28603
|
+
}
|
|
28604
|
+
}
|
|
28605
|
+
if (sinceMs != null && Number.isFinite(sinceMs)) {
|
|
28606
|
+
sql += " AND ts >= ?";
|
|
28607
|
+
params.push(Math.floor(sinceMs / 1000));
|
|
28608
|
+
}
|
|
28609
|
+
sql += " ORDER BY ts DESC LIMIT 500";
|
|
28610
|
+
const rows = requireDb().prepare(sql).all(...params);
|
|
28611
|
+
for (const r of rows) {
|
|
28612
|
+
const hay = normalizeDeliveryText(r.text ?? "");
|
|
28613
|
+
if (hay.length === 0)
|
|
28614
|
+
continue;
|
|
28615
|
+
if (deliveryTextMatch(hay, needle))
|
|
28616
|
+
return true;
|
|
28617
|
+
}
|
|
28618
|
+
return false;
|
|
28619
|
+
} catch {
|
|
28620
|
+
return false;
|
|
28621
|
+
}
|
|
28622
|
+
}
|
|
28623
|
+
function normalizeDeliveryText(text4) {
|
|
28624
|
+
return text4.replace(/\s+/g, " ").trim();
|
|
28625
|
+
}
|
|
28626
|
+
function deliveryTextMatch(hay, needle) {
|
|
28627
|
+
if (hay === needle)
|
|
28628
|
+
return true;
|
|
28629
|
+
const shorter = Math.min(hay.length, needle.length);
|
|
28630
|
+
if (shorter < MIN_PREFIX_MATCH_CHARS)
|
|
28631
|
+
return false;
|
|
28632
|
+
return hay.startsWith(needle) || needle.startsWith(hay);
|
|
28633
|
+
}
|
|
28450
28634
|
function query(opts) {
|
|
28451
28635
|
const limit = Math.min(MAX_LIMIT, Math.max(1, opts.limit ?? DEFAULT_LIMIT));
|
|
28452
28636
|
const params = [opts.chat_id];
|
|
@@ -28469,147 +28653,11 @@ function query(opts) {
|
|
|
28469
28653
|
rows.reverse();
|
|
28470
28654
|
return rows;
|
|
28471
28655
|
}
|
|
28472
|
-
var DatabaseClass = null, DEFAULT_LIMIT = 10, MAX_LIMIT = 50, db = null, dbPath = null;
|
|
28656
|
+
var DatabaseClass = null, DEFAULT_LIMIT = 10, MAX_LIMIT = 50, db = null, dbPath = null, MIN_PREFIX_MATCH_CHARS = 40;
|
|
28473
28657
|
var init_history = __esm(() => {
|
|
28474
28658
|
init_redact();
|
|
28475
28659
|
});
|
|
28476
28660
|
|
|
28477
|
-
// quota-check.ts
|
|
28478
|
-
import { readFileSync as readFileSync21, existsSync as existsSync22 } from "fs";
|
|
28479
|
-
import { join as join25 } from "path";
|
|
28480
|
-
function readOauthToken(claudeConfigDir) {
|
|
28481
|
-
const tokenFile = join25(claudeConfigDir, ".oauth-token");
|
|
28482
|
-
if (!existsSync22(tokenFile))
|
|
28483
|
-
return null;
|
|
28484
|
-
try {
|
|
28485
|
-
const raw = readFileSync21(tokenFile, "utf-8").trim();
|
|
28486
|
-
return raw.length > 0 ? raw : null;
|
|
28487
|
-
} catch {
|
|
28488
|
-
return null;
|
|
28489
|
-
}
|
|
28490
|
-
}
|
|
28491
|
-
function parseFloatHeader(headers, name) {
|
|
28492
|
-
const v = headers.get(name);
|
|
28493
|
-
if (v == null || v.trim().length === 0)
|
|
28494
|
-
return null;
|
|
28495
|
-
const n = Number(v);
|
|
28496
|
-
return Number.isFinite(n) ? n : null;
|
|
28497
|
-
}
|
|
28498
|
-
function parseEpochHeader(headers, name) {
|
|
28499
|
-
const v = headers.get(name);
|
|
28500
|
-
if (v == null)
|
|
28501
|
-
return null;
|
|
28502
|
-
const n = Number(v);
|
|
28503
|
-
if (!Number.isFinite(n) || n <= 0)
|
|
28504
|
-
return null;
|
|
28505
|
-
return new Date(n * 1000);
|
|
28506
|
-
}
|
|
28507
|
-
function parseQuotaHeaders(headers) {
|
|
28508
|
-
const fiveHour = parseFloatHeader(headers, "anthropic-ratelimit-unified-5h-utilization");
|
|
28509
|
-
const sevenDay = parseFloatHeader(headers, "anthropic-ratelimit-unified-7d-utilization");
|
|
28510
|
-
if (fiveHour == null && sevenDay == null) {
|
|
28511
|
-
return {
|
|
28512
|
-
ok: false,
|
|
28513
|
-
reason: "no unified rate-limit headers in response (API token, not OAuth?)"
|
|
28514
|
-
};
|
|
28515
|
-
}
|
|
28516
|
-
return {
|
|
28517
|
-
ok: true,
|
|
28518
|
-
data: {
|
|
28519
|
-
fiveHourUtilizationPct: (fiveHour ?? 0) * 100,
|
|
28520
|
-
sevenDayUtilizationPct: (sevenDay ?? 0) * 100,
|
|
28521
|
-
fiveHourUtilPresent: fiveHour != null,
|
|
28522
|
-
sevenDayUtilPresent: sevenDay != null,
|
|
28523
|
-
fiveHourResetAt: parseEpochHeader(headers, "anthropic-ratelimit-unified-5h-reset"),
|
|
28524
|
-
sevenDayResetAt: parseEpochHeader(headers, "anthropic-ratelimit-unified-7d-reset"),
|
|
28525
|
-
representativeClaim: headers.get("anthropic-ratelimit-unified-representative-claim"),
|
|
28526
|
-
overageStatus: headers.get("anthropic-ratelimit-unified-overage-status"),
|
|
28527
|
-
overageDisabledReason: headers.get("anthropic-ratelimit-unified-overage-disabled-reason")
|
|
28528
|
-
}
|
|
28529
|
-
};
|
|
28530
|
-
}
|
|
28531
|
-
async function fetchQuota(opts) {
|
|
28532
|
-
let token;
|
|
28533
|
-
if (opts.accessToken && opts.claudeConfigDir) {
|
|
28534
|
-
return {
|
|
28535
|
-
ok: false,
|
|
28536
|
-
reason: "pass only one of `accessToken` or `claudeConfigDir`, not both"
|
|
28537
|
-
};
|
|
28538
|
-
}
|
|
28539
|
-
if (opts.accessToken) {
|
|
28540
|
-
token = opts.accessToken.trim().length > 0 ? opts.accessToken : null;
|
|
28541
|
-
} else if (opts.claudeConfigDir) {
|
|
28542
|
-
token = readOauthToken(opts.claudeConfigDir);
|
|
28543
|
-
} else {
|
|
28544
|
-
return {
|
|
28545
|
-
ok: false,
|
|
28546
|
-
reason: "fetchQuota requires `accessToken` or `claudeConfigDir`"
|
|
28547
|
-
};
|
|
28548
|
-
}
|
|
28549
|
-
if (!token) {
|
|
28550
|
-
return { ok: false, reason: "no OAuth token at .oauth-token" };
|
|
28551
|
-
}
|
|
28552
|
-
const controller = new AbortController;
|
|
28553
|
-
const timeout = setTimeout(() => controller.abort(), opts.timeoutMs ?? 1e4);
|
|
28554
|
-
const fetchFn = opts.fetchImpl ?? fetch;
|
|
28555
|
-
let resp;
|
|
28556
|
-
try {
|
|
28557
|
-
resp = await fetchFn("https://api.anthropic.com/v1/messages", {
|
|
28558
|
-
method: "POST",
|
|
28559
|
-
headers: {
|
|
28560
|
-
"anthropic-version": "2023-06-01",
|
|
28561
|
-
"anthropic-beta": OAUTH_BETA,
|
|
28562
|
-
authorization: `Bearer ${token}`,
|
|
28563
|
-
"x-app": "cli",
|
|
28564
|
-
"user-agent": DEFAULT_USER_AGENT,
|
|
28565
|
-
"content-type": "application/json"
|
|
28566
|
-
},
|
|
28567
|
-
body: JSON.stringify({
|
|
28568
|
-
model: opts.model ?? DEFAULT_PROBE_MODEL,
|
|
28569
|
-
max_tokens: 1,
|
|
28570
|
-
messages: [{ role: "user", content: "hi" }]
|
|
28571
|
-
}),
|
|
28572
|
-
signal: controller.signal
|
|
28573
|
-
});
|
|
28574
|
-
} catch (err) {
|
|
28575
|
-
const msg = err?.message ?? String(err);
|
|
28576
|
-
return { ok: false, reason: `request failed: ${msg}` };
|
|
28577
|
-
} finally {
|
|
28578
|
-
clearTimeout(timeout);
|
|
28579
|
-
}
|
|
28580
|
-
if (resp.status === 401 || resp.status === 403) {
|
|
28581
|
-
return { ok: false, reason: `auth rejected (HTTP ${resp.status})` };
|
|
28582
|
-
}
|
|
28583
|
-
const parsed = parseQuotaHeaders(resp.headers);
|
|
28584
|
-
if (!parsed.ok && resp.status >= 400) {
|
|
28585
|
-
return { ok: false, reason: `HTTP ${resp.status}, ${parsed.reason}` };
|
|
28586
|
-
}
|
|
28587
|
-
return parsed;
|
|
28588
|
-
}
|
|
28589
|
-
function formatQuotaLine(q) {
|
|
28590
|
-
const fmt = (n) => `${Math.round(n)}%`;
|
|
28591
|
-
return `${fmt(q.fiveHourUtilizationPct)} / 5h \u00b7 ${fmt(q.sevenDayUtilizationPct)} / 7d`;
|
|
28592
|
-
}
|
|
28593
|
-
function formatResetRelative(target, now = new Date) {
|
|
28594
|
-
if (!target)
|
|
28595
|
-
return "\u2014";
|
|
28596
|
-
const deltaMs = target.getTime() - now.getTime();
|
|
28597
|
-
if (deltaMs <= 0)
|
|
28598
|
-
return "resets now";
|
|
28599
|
-
const totalMin = Math.round(deltaMs / 60000);
|
|
28600
|
-
if (totalMin < 60)
|
|
28601
|
-
return `resets in ${totalMin}m`;
|
|
28602
|
-
const hours = Math.floor(totalMin / 60);
|
|
28603
|
-
const mins = totalMin % 60;
|
|
28604
|
-
if (hours < 24)
|
|
28605
|
-
return mins > 0 ? `resets in ${hours}h ${mins}m` : `resets in ${hours}h`;
|
|
28606
|
-
const days = Math.floor(hours / 24);
|
|
28607
|
-
const remH = hours % 24;
|
|
28608
|
-
return remH > 0 ? `resets in ${days}d ${remH}h` : `resets in ${days}d`;
|
|
28609
|
-
}
|
|
28610
|
-
var OAUTH_BETA = "oauth-2025-04-20", DEFAULT_USER_AGENT = "claude-cli/1.0.0 (external, cli)", DEFAULT_PROBE_MODEL = "claude-haiku-4-5-20251001";
|
|
28611
|
-
var init_quota_check = () => {};
|
|
28612
|
-
|
|
28613
28661
|
// ../src/util/atomic.ts
|
|
28614
28662
|
import { closeSync as closeSync5, constants as constants2, fsyncSync as fsyncSync2, openSync as openSync5, renameSync as renameSync10, rmSync as rmSync5, writeSync as writeSync4 } from "node:fs";
|
|
28615
28663
|
var TMP_OPEN_FLAGS;
|
|
@@ -31573,7 +31621,7 @@ var require_util = __commonJS((exports2) => {
|
|
|
31573
31621
|
return path2;
|
|
31574
31622
|
}
|
|
31575
31623
|
exports2.normalize = normalize;
|
|
31576
|
-
function
|
|
31624
|
+
function join32(aRoot, aPath) {
|
|
31577
31625
|
if (aRoot === "") {
|
|
31578
31626
|
aRoot = ".";
|
|
31579
31627
|
}
|
|
@@ -31605,7 +31653,7 @@ var require_util = __commonJS((exports2) => {
|
|
|
31605
31653
|
}
|
|
31606
31654
|
return joined;
|
|
31607
31655
|
}
|
|
31608
|
-
exports2.join =
|
|
31656
|
+
exports2.join = join32;
|
|
31609
31657
|
exports2.isAbsolute = function(aPath) {
|
|
31610
31658
|
return aPath.charAt(0) === "/" || urlRegexp.test(aPath);
|
|
31611
31659
|
};
|
|
@@ -31778,7 +31826,7 @@ var require_util = __commonJS((exports2) => {
|
|
|
31778
31826
|
parsed.path = parsed.path.substring(0, index2 + 1);
|
|
31779
31827
|
}
|
|
31780
31828
|
}
|
|
31781
|
-
sourceURL =
|
|
31829
|
+
sourceURL = join32(urlGenerate(parsed), sourceURL);
|
|
31782
31830
|
}
|
|
31783
31831
|
return normalize(sourceURL);
|
|
31784
31832
|
}
|
|
@@ -34384,9 +34432,9 @@ function renderAuthLine(state4, agentName3, now = Date.now()) {
|
|
|
34384
34432
|
|
|
34385
34433
|
// gateway/quota-cache.ts
|
|
34386
34434
|
import { existsSync as existsSync37, readFileSync as readFileSync39, writeFileSync as writeFileSync31, mkdirSync as mkdirSync30 } from "fs";
|
|
34387
|
-
import { join as
|
|
34435
|
+
import { join as join42, dirname as dirname11 } from "path";
|
|
34388
34436
|
function defaultCachePath() {
|
|
34389
|
-
return process.env.SWITCHROOM_QUOTA_CACHE_PATH ??
|
|
34437
|
+
return process.env.SWITCHROOM_QUOTA_CACHE_PATH ?? join42(process.env.HOME ?? "/tmp", ".switchroom", "quota-cache.json");
|
|
34390
34438
|
}
|
|
34391
34439
|
function readQuotaCache(opts = {}) {
|
|
34392
34440
|
const path2 = opts.path ?? defaultCachePath();
|
|
@@ -34433,7 +34481,7 @@ var init_quota_cache = __esm(() => {
|
|
|
34433
34481
|
|
|
34434
34482
|
// gateway/boot-probes.ts
|
|
34435
34483
|
import { readFileSync as readFileSync40, readdirSync as readdirSync8, existsSync as existsSync38 } from "fs";
|
|
34436
|
-
import { join as
|
|
34484
|
+
import { join as join43 } from "path";
|
|
34437
34485
|
import { execFile as execFileCb } from "child_process";
|
|
34438
34486
|
import { promisify } from "util";
|
|
34439
34487
|
async function withTimeout(label, p, timeoutMs = PROBE_TIMEOUT_MS) {
|
|
@@ -34475,8 +34523,8 @@ function mapPlan(billingType, hasExtra) {
|
|
|
34475
34523
|
}
|
|
34476
34524
|
async function probeAccount(agentDir) {
|
|
34477
34525
|
return withTimeout("Account", (async () => {
|
|
34478
|
-
const claudeDir =
|
|
34479
|
-
const claudeJsonPath =
|
|
34526
|
+
const claudeDir = join43(agentDir, ".claude");
|
|
34527
|
+
const claudeJsonPath = join43(claudeDir, ".claude.json");
|
|
34480
34528
|
let cfg = {};
|
|
34481
34529
|
try {
|
|
34482
34530
|
const raw = readFileSync40(claudeJsonPath, "utf8");
|
|
@@ -34497,8 +34545,8 @@ async function probeAccount(agentDir) {
|
|
|
34497
34545
|
let tokenStr = "";
|
|
34498
34546
|
let status = "ok";
|
|
34499
34547
|
for (const candidate of [
|
|
34500
|
-
|
|
34501
|
-
|
|
34548
|
+
join43(claudeDir, ".oauth-token.meta.json"),
|
|
34549
|
+
join43(claudeDir, "accounts", "default", ".oauth-token.meta.json")
|
|
34502
34550
|
]) {
|
|
34503
34551
|
if (existsSync38(candidate)) {
|
|
34504
34552
|
try {
|
|
@@ -34884,9 +34932,9 @@ async function probeQuota(claudeConfigDir, _agentDir, fetchImpl = fetch, opts =
|
|
|
34884
34932
|
let claudeDirForProbe = null;
|
|
34885
34933
|
for (const candidate of [
|
|
34886
34934
|
claudeConfigDir,
|
|
34887
|
-
|
|
34935
|
+
join43(claudeConfigDir, "accounts", "default")
|
|
34888
34936
|
]) {
|
|
34889
|
-
if (existsSync38(
|
|
34937
|
+
if (existsSync38(join43(candidate, ".oauth-token"))) {
|
|
34890
34938
|
claudeDirForProbe = candidate;
|
|
34891
34939
|
break;
|
|
34892
34940
|
}
|
|
@@ -35121,7 +35169,7 @@ async function probeSkills(agentDir, opts = {}) {
|
|
|
35121
35169
|
return withTimeout("Skills", (async () => {
|
|
35122
35170
|
const fs2 = opts.fs ?? realSkillsFs;
|
|
35123
35171
|
const max = opts.maxNamesShown ?? 3;
|
|
35124
|
-
const skillsDir =
|
|
35172
|
+
const skillsDir = join43(agentDir, ".claude", "skills");
|
|
35125
35173
|
if (!fs2.exists(skillsDir)) {
|
|
35126
35174
|
return { status: "ok", label: "Skills", detail: "no skills dir" };
|
|
35127
35175
|
}
|
|
@@ -35136,17 +35184,17 @@ async function probeSkills(agentDir, opts = {}) {
|
|
|
35136
35184
|
}
|
|
35137
35185
|
const dangling = [];
|
|
35138
35186
|
for (const name of entries) {
|
|
35139
|
-
const skillPath =
|
|
35187
|
+
const skillPath = join43(skillsDir, name);
|
|
35140
35188
|
if (!fs2.exists(skillPath)) {
|
|
35141
35189
|
dangling.push(name);
|
|
35142
35190
|
continue;
|
|
35143
35191
|
}
|
|
35144
|
-
const skillMd =
|
|
35192
|
+
const skillMd = join43(skillPath, "SKILL.md");
|
|
35145
35193
|
if (!fs2.exists(skillMd) && !fs2.exists(skillPath + ".md")) {
|
|
35146
35194
|
continue;
|
|
35147
35195
|
}
|
|
35148
35196
|
}
|
|
35149
|
-
const overlayDir = opts.overlaySkillsDir ??
|
|
35197
|
+
const overlayDir = opts.overlaySkillsDir ?? join43(agentDir, "skills.d");
|
|
35150
35198
|
const overlaySlugs = new Set;
|
|
35151
35199
|
if (fs2.exists(overlayDir)) {
|
|
35152
35200
|
let overlayEntries = [];
|
|
@@ -35188,7 +35236,7 @@ function renderBucketedSkills(switchroom, agent) {
|
|
|
35188
35236
|
}
|
|
35189
35237
|
async function probeConnections(agentDir, opts = {}) {
|
|
35190
35238
|
return withTimeout("Connections", (async () => {
|
|
35191
|
-
const path2 =
|
|
35239
|
+
const path2 = join43(agentDir, ".claude", "connection-health.json");
|
|
35192
35240
|
const read = opts.readFileImpl ?? ((p) => readFileSync40(p, "utf8"));
|
|
35193
35241
|
let issues = [];
|
|
35194
35242
|
try {
|
|
@@ -35530,7 +35578,7 @@ __export(exports_boot_card, {
|
|
|
35530
35578
|
renderBootCard: () => renderBootCard,
|
|
35531
35579
|
renderAccountRows: () => renderAuthLine
|
|
35532
35580
|
});
|
|
35533
|
-
import { join as
|
|
35581
|
+
import { join as join44 } from "path";
|
|
35534
35582
|
function resolvePersonaName(slug, loadConfig3) {
|
|
35535
35583
|
try {
|
|
35536
35584
|
const config = loadConfig3 ? loadConfig3() : loadConfig();
|
|
@@ -35621,7 +35669,7 @@ function renderBootCard(opts) {
|
|
|
35621
35669
|
return stackCardLines(flatLines);
|
|
35622
35670
|
}
|
|
35623
35671
|
async function runAllProbes(opts) {
|
|
35624
|
-
const claudeDir =
|
|
35672
|
+
const claudeDir = join44(opts.agentDir, ".claude");
|
|
35625
35673
|
const probes = {};
|
|
35626
35674
|
const slug = opts.agentSlug ?? opts.agentName;
|
|
35627
35675
|
await Promise.allSettled([
|
|
@@ -37300,8 +37348,8 @@ import {
|
|
|
37300
37348
|
unlinkSync as unlinkSync24,
|
|
37301
37349
|
appendFileSync as appendFileSync6
|
|
37302
37350
|
} from "fs";
|
|
37303
|
-
import { homedir as
|
|
37304
|
-
import { join as
|
|
37351
|
+
import { homedir as homedir18 } from "os";
|
|
37352
|
+
import { join as join55, extname, sep as sep3, basename as basename13 } from "path";
|
|
37305
37353
|
|
|
37306
37354
|
// plugin-logger.ts
|
|
37307
37355
|
import { appendFileSync, mkdirSync, renameSync, statSync, existsSync } from "fs";
|
|
@@ -40211,16 +40259,34 @@ function clipNarrative(s) {
|
|
|
40211
40259
|
return s.split(`
|
|
40212
40260
|
`)[0].trim().slice(0, STATUS_LINE_MAX);
|
|
40213
40261
|
}
|
|
40214
|
-
function renderActivityHeader(emoji, label, description, elapsedMs, toolCount, state, model) {
|
|
40262
|
+
function renderActivityHeader(emoji, label, description, elapsedMs, toolCount, state, model, totalTokens) {
|
|
40215
40263
|
const toolWord = toolCount === 1 ? "tool" : "tools";
|
|
40216
40264
|
const elapsed = formatFeedElapsed(elapsedMs);
|
|
40217
40265
|
const descPart = description.length > 0 ? ` \u00b7 _${escapeMarkdown(description)}_` : "";
|
|
40218
40266
|
const line1 = `${emoji} **${escapeMarkdown(label)}**${descPart}`;
|
|
40267
|
+
const tokPart = tokenSegment(totalTokens);
|
|
40219
40268
|
const modelLabel = formatModelLabel(model);
|
|
40220
40269
|
const modelPart = modelLabel != null ? ` \u00b7 ${escapeMarkdown(modelLabel)}` : "";
|
|
40221
|
-
const line2 = state === "running" ? `_${elapsed} \u00b7 ${toolCount} ${toolWord}${modelPart}_` : `_${state} \u00b7 ${toolCount} ${toolWord} \u00b7 ${elapsed}${modelPart}_`;
|
|
40270
|
+
const line2 = state === "running" ? `_${elapsed} \u00b7 ${toolCount} ${toolWord}${tokPart}${modelPart}_` : `_${state} \u00b7 ${toolCount} ${toolWord}${tokPart} \u00b7 ${elapsed}${modelPart}_`;
|
|
40222
40271
|
return [line1, line2];
|
|
40223
40272
|
}
|
|
40273
|
+
function formatTokenCount(n) {
|
|
40274
|
+
if (!Number.isFinite(n) || n <= 0)
|
|
40275
|
+
return "0";
|
|
40276
|
+
if (n < 1000)
|
|
40277
|
+
return String(Math.floor(n));
|
|
40278
|
+
if (n < 1e6) {
|
|
40279
|
+
const k = Number((n / 1000).toFixed(1));
|
|
40280
|
+
if (k < 1000)
|
|
40281
|
+
return `${k.toFixed(1)}k`;
|
|
40282
|
+
}
|
|
40283
|
+
return `${(n / 1e6).toFixed(1)}M`;
|
|
40284
|
+
}
|
|
40285
|
+
function tokenSegment(totalTokens) {
|
|
40286
|
+
if (totalTokens == null || totalTokens <= 0)
|
|
40287
|
+
return "";
|
|
40288
|
+
return ` \u00b7 ${formatTokenCount(totalTokens)} tok`;
|
|
40289
|
+
}
|
|
40224
40290
|
function formatFeedElapsed(ms) {
|
|
40225
40291
|
const s = Math.floor(ms / 1000);
|
|
40226
40292
|
if (s < 60)
|
|
@@ -40257,7 +40323,7 @@ function renderStatusCard(opts) {
|
|
|
40257
40323
|
const hasChildren = rawChildren.length > 0;
|
|
40258
40324
|
const steps = rawSteps.map(escapeStepLine);
|
|
40259
40325
|
const children = rawChildren.map(escapeStepLine);
|
|
40260
|
-
const headerLines = header != null ? renderActivityHeader(header.emoji, header.label, header.description ?? "", header.elapsedMs, header.toolCount, header.state, header.model) : [];
|
|
40326
|
+
const headerLines = header != null ? renderActivityHeader(header.emoji, header.label, header.description ?? "", header.elapsedMs, header.toolCount, header.state, header.model, header.totalTokens) : [];
|
|
40261
40327
|
const out = [...headerLines];
|
|
40262
40328
|
if (hasChildren) {
|
|
40263
40329
|
const shownParent = steps.slice(-STATUS_ROLLING_LINES);
|
|
@@ -40354,7 +40420,8 @@ function renderActivityFeed(lines, final = false, liveSuffix = "", stepCount, he
|
|
|
40354
40420
|
elapsedMs: header.elapsedMs,
|
|
40355
40421
|
toolCount: header.toolCount,
|
|
40356
40422
|
state: header.state,
|
|
40357
|
-
model: header.model
|
|
40423
|
+
model: header.model,
|
|
40424
|
+
totalTokens: header.totalTokens
|
|
40358
40425
|
} : undefined,
|
|
40359
40426
|
steps: lines,
|
|
40360
40427
|
final,
|
|
@@ -40373,7 +40440,8 @@ function renderActivityFeedWithNested(lines, childLines, final = false, liveSuff
|
|
|
40373
40440
|
elapsedMs: header.elapsedMs,
|
|
40374
40441
|
toolCount: header.toolCount,
|
|
40375
40442
|
state: header.state,
|
|
40376
|
-
model: header.model
|
|
40443
|
+
model: header.model,
|
|
40444
|
+
totalTokens: header.totalTokens
|
|
40377
40445
|
} : undefined,
|
|
40378
40446
|
steps: lines,
|
|
40379
40447
|
childSteps: children,
|
|
@@ -40398,9 +40466,10 @@ function renderCombinedWorkerFeed(rows, opts) {
|
|
|
40398
40466
|
const rowHeader = (r) => {
|
|
40399
40467
|
const desc = escapeMarkdown(truncate(stripMarkdown(r.description).replace(/\s+/g, " ").trim() || "background task", COMBINED_ROW_DESC_MAX));
|
|
40400
40468
|
const toolWord = r.toolCount === 1 ? "tool" : "tools";
|
|
40469
|
+
const tokPart = tokenSegment(r.totalTokens);
|
|
40401
40470
|
const modelLabel = formatModelLabel(r.model);
|
|
40402
40471
|
const modelPart = modelLabel != null ? ` \u00b7 ${escapeMarkdown(modelLabel)}` : "";
|
|
40403
|
-
return `**${desc}** _\u00b7 ${formatFeedElapsed(r.elapsedMs)} \u00b7 ${r.toolCount} ${toolWord}${modelPart}_`;
|
|
40472
|
+
return `**${desc}** _\u00b7 ${formatFeedElapsed(r.elapsedMs)} \u00b7 ${r.toolCount} ${toolWord}${tokPart}${modelPart}_`;
|
|
40404
40473
|
};
|
|
40405
40474
|
const rowHistory = (r) => {
|
|
40406
40475
|
const src = r.historyLines != null && r.historyLines.length > 0 ? r.historyLines : [r.currentStep];
|
|
@@ -40511,7 +40580,8 @@ function renderWorkerActivity(v, liveSuffix = "") {
|
|
|
40511
40580
|
elapsedMs: v.elapsedMs,
|
|
40512
40581
|
toolCount: v.toolCount,
|
|
40513
40582
|
state: v.state,
|
|
40514
|
-
model: v.model
|
|
40583
|
+
model: v.model,
|
|
40584
|
+
totalTokens: v.totalTokens
|
|
40515
40585
|
};
|
|
40516
40586
|
let result;
|
|
40517
40587
|
if (finished && v.state !== "incomplete") {
|
|
@@ -40536,6 +40606,9 @@ _starting\u2026_`;
|
|
|
40536
40606
|
return card;
|
|
40537
40607
|
}
|
|
40538
40608
|
var COOLDOWN_JITTER_MS = 500;
|
|
40609
|
+
var WORKER_CARD_SUPERSEDED_BODY = `\uD83D\uDEE0 **Worker** \u00b7 _continued_
|
|
40610
|
+
|
|
40611
|
+
_Live progress moved to a fresh card to stay pinned._`;
|
|
40539
40612
|
function extractRetryAfterSecs(err) {
|
|
40540
40613
|
if (err == null || typeof err !== "object")
|
|
40541
40614
|
return null;
|
|
@@ -40569,6 +40642,8 @@ function createWorkerActivityFeed(opts) {
|
|
|
40569
40642
|
const heartbeatTickMs = opts.heartbeatTickMs ?? 6000;
|
|
40570
40643
|
const maxRows = Math.max(1, Math.floor(opts.maxRows ?? 8));
|
|
40571
40644
|
const staleWorkerTtlMs = Math.max(1, Math.floor(opts.staleWorkerTtlMs ?? 50 * 60000));
|
|
40645
|
+
const absoluteRowLifetimeCapMs = Math.max(1, Math.floor(opts.absoluteRowLifetimeCapMs ?? 6 * 60 * 60000));
|
|
40646
|
+
const groupMessageLifetimeCapMs = Math.max(1, Math.floor(opts.groupMessageLifetimeCapMs ?? 60 * 60000));
|
|
40572
40647
|
const reconcilePinFn = opts.reconcilePin ?? (() => {});
|
|
40573
40648
|
const setIntervalFn = opts.setInterval ?? ((cb, ms) => {
|
|
40574
40649
|
const t = setInterval(cb, ms);
|
|
@@ -40669,6 +40744,7 @@ function createWorkerActivityFeed(opts) {
|
|
|
40669
40744
|
description: v.description,
|
|
40670
40745
|
elapsedMs: elapsedFor(r),
|
|
40671
40746
|
toolCount: v.toolCount,
|
|
40747
|
+
totalTokens: v.totalTokens,
|
|
40672
40748
|
currentStep,
|
|
40673
40749
|
historyLines: r.narrative.length > 0 ? [...r.narrative] : undefined,
|
|
40674
40750
|
model: v.model
|
|
@@ -40746,6 +40822,7 @@ function createWorkerActivityFeed(opts) {
|
|
|
40746
40822
|
return;
|
|
40747
40823
|
}
|
|
40748
40824
|
g.messageId = sent.message_id;
|
|
40825
|
+
g.messageCreatedAtMs = now;
|
|
40749
40826
|
g.lastBody = body;
|
|
40750
40827
|
g.lastEditAt = now;
|
|
40751
40828
|
g.terminalPainted = false;
|
|
@@ -40776,6 +40853,7 @@ function createWorkerActivityFeed(opts) {
|
|
|
40776
40853
|
log(`worker-feed: finish feed=${g.feedKey} chat=${g.chatId} thread=${g.threadId ?? "-"} ` + `msgId=${g.messageId} agent=${finishingAgentId ?? "-"} ` + `state=${opts2.terminalRecap?.state ?? "done"} bytes=${body.length}`);
|
|
40777
40854
|
} else {
|
|
40778
40855
|
log(`worker-feed: edit feed=${g.feedKey} chat=${g.chatId} ` + `thread=${g.threadId ?? "-"} msgId=${g.messageId} workers=${g.workers.size} bytes=${body.length}`);
|
|
40856
|
+
syncPin(g);
|
|
40779
40857
|
}
|
|
40780
40858
|
if (isTerminal)
|
|
40781
40859
|
clearStaged();
|
|
@@ -40794,6 +40872,7 @@ function createWorkerActivityFeed(opts) {
|
|
|
40794
40872
|
}
|
|
40795
40873
|
if (outcome === "gone") {
|
|
40796
40874
|
g.messageId = null;
|
|
40875
|
+
g.messageCreatedAtMs = 0;
|
|
40797
40876
|
g.lastBody = null;
|
|
40798
40877
|
if (isTerminal)
|
|
40799
40878
|
clearStaged();
|
|
@@ -40837,6 +40916,7 @@ function createWorkerActivityFeed(opts) {
|
|
|
40837
40916
|
description: lv?.description ?? "background task",
|
|
40838
40917
|
lastTool: null,
|
|
40839
40918
|
toolCount: lv?.toolCount ?? 0,
|
|
40919
|
+
totalTokens: lv?.totalTokens,
|
|
40840
40920
|
latestSummary: "",
|
|
40841
40921
|
elapsedMs: liveElapsed(row, nowFn()),
|
|
40842
40922
|
state: "incomplete",
|
|
@@ -40850,22 +40930,36 @@ function createWorkerActivityFeed(opts) {
|
|
|
40850
40930
|
const staleFinished = [];
|
|
40851
40931
|
for (const g of groups.values()) {
|
|
40852
40932
|
for (const row of g.workers.values()) {
|
|
40853
|
-
|
|
40854
|
-
|
|
40855
|
-
|
|
40856
|
-
|
|
40857
|
-
|
|
40858
|
-
|
|
40933
|
+
const silent = now - row.lastUpdateAt >= staleWorkerTtlMs;
|
|
40934
|
+
const tooOld = now - row.createdAtMs >= absoluteRowLifetimeCapMs;
|
|
40935
|
+
if (!silent && !tooOld)
|
|
40936
|
+
continue;
|
|
40937
|
+
const reason = silent ? "silence" : "absolute";
|
|
40938
|
+
if (row.finished)
|
|
40939
|
+
staleFinished.push({ g, agentId: row.agentId, reason });
|
|
40940
|
+
else
|
|
40941
|
+
staleAgentIds.push({ agentId: row.agentId, reason });
|
|
40859
40942
|
}
|
|
40860
40943
|
}
|
|
40861
|
-
for (const { g, agentId } of staleFinished) {
|
|
40862
|
-
|
|
40944
|
+
for (const { g, agentId, reason } of staleFinished) {
|
|
40945
|
+
if (reason === "absolute") {
|
|
40946
|
+
const age = Math.floor((now - (g.workers.get(agentId)?.createdAtMs ?? now)) / 1000);
|
|
40947
|
+
log(`worker-feed: ABSOLUTE cap GC finished row agent=${agentId} feed=${g.feedKey} \u2014 age ${age}s (>= ${Math.floor(absoluteRowLifetimeCapMs / 1000)}s); reaping immortal finished row`);
|
|
40948
|
+
} else {
|
|
40949
|
+
log(`worker-feed: TTL GC finished row agent=${agentId} feed=${g.feedKey} \u2014 reaping leaked finished row`);
|
|
40950
|
+
}
|
|
40863
40951
|
g.pendingFinalize.delete(agentId);
|
|
40864
40952
|
removeWorker(g, agentId);
|
|
40865
40953
|
syncPin(g);
|
|
40866
40954
|
}
|
|
40867
|
-
for (const agentId of staleAgentIds) {
|
|
40868
|
-
|
|
40955
|
+
for (const { agentId, reason } of staleAgentIds) {
|
|
40956
|
+
const row = groupOfAgent(agentId)?.workers.get(agentId);
|
|
40957
|
+
if (reason === "absolute") {
|
|
40958
|
+
const age = Math.floor((now - (row?.createdAtMs ?? now)) / 1000);
|
|
40959
|
+
log(`worker-feed: ABSOLUTE cap reap agent=${agentId} \u2014 row age ${age}s (>= ${Math.floor(absoluteRowLifetimeCapMs / 1000)}s); force-terminating immortal row (survives lastUpdateAt reset)`);
|
|
40960
|
+
} else {
|
|
40961
|
+
log(`worker-feed: TTL reap agent=${agentId} \u2014 no update in ${Math.floor((now - (row?.lastUpdateAt ?? now)) / 1000)}s (>= ${Math.floor(staleWorkerTtlMs / 1000)}s); force-terminating leaked row`);
|
|
40962
|
+
}
|
|
40869
40963
|
terminateWorker(agentId);
|
|
40870
40964
|
}
|
|
40871
40965
|
for (const g of [...groups.values()]) {
|
|
@@ -40882,6 +40976,16 @@ function createWorkerActivityFeed(opts) {
|
|
|
40882
40976
|
const running = runningRows(g);
|
|
40883
40977
|
if (running.length === 0)
|
|
40884
40978
|
continue;
|
|
40979
|
+
if (g.messageId != null && now - g.messageCreatedAtMs >= groupMessageLifetimeCapMs) {
|
|
40980
|
+
const age = Math.floor((now - g.messageCreatedAtMs) / 1000);
|
|
40981
|
+
const retiredId = g.messageId;
|
|
40982
|
+
log(`worker-feed: group-message lifetime cap rotate feed=${g.feedKey} ` + `msgId=${retiredId} \u2014 age ${age}s (>= ${Math.floor(groupMessageLifetimeCapMs / 1000)}s); ` + `rotating to a fresh message to re-establish the pin surface`);
|
|
40983
|
+
g.messageId = null;
|
|
40984
|
+
g.messageCreatedAtMs = 0;
|
|
40985
|
+
g.lastBody = null;
|
|
40986
|
+
syncPin(g);
|
|
40987
|
+
opts.bot.editMessageText(g.chatId, retiredId, WORKER_CARD_SUPERSEDED_BODY, sendOptsFor(g)).catch(() => {});
|
|
40988
|
+
}
|
|
40885
40989
|
if (g.messageId == null) {
|
|
40886
40990
|
const maxElapsed = Math.max(0, ...running.map((r) => liveElapsed(r, now)));
|
|
40887
40991
|
if (maxElapsed < firstPaintMin)
|
|
@@ -40936,6 +41040,7 @@ function createWorkerActivityFeed(opts) {
|
|
|
40936
41040
|
chatId,
|
|
40937
41041
|
threadId,
|
|
40938
41042
|
messageId: null,
|
|
41043
|
+
messageCreatedAtMs: 0,
|
|
40939
41044
|
lastBody: null,
|
|
40940
41045
|
lastEditAt: 0,
|
|
40941
41046
|
cooldownUntil: 0,
|
|
@@ -40948,6 +41053,7 @@ function createWorkerActivityFeed(opts) {
|
|
|
40948
41053
|
}
|
|
40949
41054
|
if (!g.workers.has(agentId) && g.terminalPainted && !hasLiveWorker(g)) {
|
|
40950
41055
|
g.messageId = null;
|
|
41056
|
+
g.messageCreatedAtMs = 0;
|
|
40951
41057
|
g.lastBody = null;
|
|
40952
41058
|
g.pendingFinalize.clear();
|
|
40953
41059
|
g.terminalPainted = false;
|
|
@@ -40962,6 +41068,7 @@ function createWorkerActivityFeed(opts) {
|
|
|
40962
41068
|
state: "running",
|
|
40963
41069
|
finished: false,
|
|
40964
41070
|
lastUpdateAt: nowFn(),
|
|
41071
|
+
createdAtMs: nowFn(),
|
|
40965
41072
|
dispatchAtMs: null,
|
|
40966
41073
|
stepStartedAtMs: null
|
|
40967
41074
|
};
|
|
@@ -41018,6 +41125,21 @@ function createWorkerActivityFeed(opts) {
|
|
|
41018
41125
|
log(`worker-feed: resurrect agent=${agentId} \u2014 cleared finalized gate; card will repaint on next running cue`);
|
|
41019
41126
|
}
|
|
41020
41127
|
},
|
|
41128
|
+
purgeAllOnBoot() {
|
|
41129
|
+
for (const g of [...groups.values()]) {
|
|
41130
|
+
if (g.messageId != null) {
|
|
41131
|
+
reconcilePinFn({ feedKey: g.feedKey, chatId: g.chatId, threadId: g.threadId, messageId: null });
|
|
41132
|
+
}
|
|
41133
|
+
for (const agentId of [...g.workers.keys()]) {
|
|
41134
|
+
markFinalized(agentId);
|
|
41135
|
+
agentIndex.delete(agentId);
|
|
41136
|
+
}
|
|
41137
|
+
g.workers.clear();
|
|
41138
|
+
g.pendingFinalize.clear();
|
|
41139
|
+
groups.delete(g.feedKey);
|
|
41140
|
+
}
|
|
41141
|
+
log("worker-feed: purgeAllOnBoot \u2014 reconciled feed to empty and released all group pins");
|
|
41142
|
+
},
|
|
41021
41143
|
heartbeatTick,
|
|
41022
41144
|
stop() {
|
|
41023
41145
|
if (heartbeatTimer != null) {
|
|
@@ -41858,8 +41980,18 @@ function buildVaultGrantDeniedInbound(opts) {
|
|
|
41858
41980
|
}
|
|
41859
41981
|
};
|
|
41860
41982
|
}
|
|
41983
|
+
var MAX_GRANT_REASON_CHARS = 300;
|
|
41984
|
+
function normalizeGrantReason(raw) {
|
|
41985
|
+
if (raw == null)
|
|
41986
|
+
return "";
|
|
41987
|
+
const collapsed = raw.replace(/\s+/g, " ").trim();
|
|
41988
|
+
if (collapsed.length === 0)
|
|
41989
|
+
return "";
|
|
41990
|
+
return collapsed.length > MAX_GRANT_REASON_CHARS ? collapsed.slice(0, MAX_GRANT_REASON_CHARS - 1) + "\u2026" : collapsed;
|
|
41991
|
+
}
|
|
41861
41992
|
function buildVaultGrantApprovedCardText(opts) {
|
|
41862
|
-
|
|
41993
|
+
const reasonClause = opts.reasonEscaped != null && opts.reasonEscaped.length > 0 ? ` _Reason: ${opts.reasonEscaped}_` : "";
|
|
41994
|
+
return `\u2705 Granted **${opts.agentEscaped}** ${opts.scope} access to ` + `\`${opts.key}\` for ${opts.days}d. ` + `(grant \`${opts.grantId}\`)` + reasonClause + (opts.footer ?? "");
|
|
41863
41995
|
}
|
|
41864
41996
|
function buildVaultSaveCompletedInbound(opts) {
|
|
41865
41997
|
const ts = opts.nowMs ?? Date.now();
|
|
@@ -43132,6 +43264,7 @@ function createCallbackQueryHandlers(deps) {
|
|
|
43132
43264
|
pendingCardStore.remove(stageId);
|
|
43133
43265
|
if (pending.card_message_id != null) {
|
|
43134
43266
|
const days = Math.round(pending.ttl_seconds / 86400);
|
|
43267
|
+
const reasonNormalized = normalizeGrantReason(pending.reason);
|
|
43135
43268
|
const footer = getVaultApprovalAuthMode() === "telegram-id" ? `
|
|
43136
43269
|
_Approver verified by Telegram identity \u2014 broker auto-unlocked at startup._` : "";
|
|
43137
43270
|
await ctx.api.editMessageText(pending.chat_id, pending.card_message_id, richMessage(buildVaultGrantApprovedCardText({
|
|
@@ -43140,6 +43273,7 @@ _Approver verified by Telegram identity \u2014 broker auto-unlocked at startup._
|
|
|
43140
43273
|
key: pending.key,
|
|
43141
43274
|
days,
|
|
43142
43275
|
grantId: id,
|
|
43276
|
+
reasonEscaped: reasonNormalized.length > 0 ? escapeHtmlForTg2(reasonNormalized) : undefined,
|
|
43143
43277
|
footer
|
|
43144
43278
|
})), { reply_markup: { inline_keyboard: [] } }).catch(() => {});
|
|
43145
43279
|
}
|
|
@@ -45004,16 +45138,34 @@ function clipNarrative2(s) {
|
|
|
45004
45138
|
return s.split(`
|
|
45005
45139
|
`)[0].trim().slice(0, STATUS_LINE_MAX);
|
|
45006
45140
|
}
|
|
45007
|
-
function renderActivityHeader2(emoji, label, description, elapsedMs, toolCount, state, model) {
|
|
45141
|
+
function renderActivityHeader2(emoji, label, description, elapsedMs, toolCount, state, model, totalTokens) {
|
|
45008
45142
|
const toolWord = toolCount === 1 ? "tool" : "tools";
|
|
45009
45143
|
const elapsed = formatFeedElapsed2(elapsedMs);
|
|
45010
45144
|
const descPart = description.length > 0 ? ` \u00b7 _${escapeMarkdown(description)}_` : "";
|
|
45011
45145
|
const line1 = `${emoji} **${escapeMarkdown(label)}**${descPart}`;
|
|
45146
|
+
const tokPart = tokenSegment2(totalTokens);
|
|
45012
45147
|
const modelLabel = formatModelLabel(model);
|
|
45013
45148
|
const modelPart = modelLabel != null ? ` \u00b7 ${escapeMarkdown(modelLabel)}` : "";
|
|
45014
|
-
const line2 = state === "running" ? `_${elapsed} \u00b7 ${toolCount} ${toolWord}${modelPart}_` : `_${state} \u00b7 ${toolCount} ${toolWord} \u00b7 ${elapsed}${modelPart}_`;
|
|
45149
|
+
const line2 = state === "running" ? `_${elapsed} \u00b7 ${toolCount} ${toolWord}${tokPart}${modelPart}_` : `_${state} \u00b7 ${toolCount} ${toolWord}${tokPart} \u00b7 ${elapsed}${modelPart}_`;
|
|
45015
45150
|
return [line1, line2];
|
|
45016
45151
|
}
|
|
45152
|
+
function formatTokenCount2(n) {
|
|
45153
|
+
if (!Number.isFinite(n) || n <= 0)
|
|
45154
|
+
return "0";
|
|
45155
|
+
if (n < 1000)
|
|
45156
|
+
return String(Math.floor(n));
|
|
45157
|
+
if (n < 1e6) {
|
|
45158
|
+
const k = Number((n / 1000).toFixed(1));
|
|
45159
|
+
if (k < 1000)
|
|
45160
|
+
return `${k.toFixed(1)}k`;
|
|
45161
|
+
}
|
|
45162
|
+
return `${(n / 1e6).toFixed(1)}M`;
|
|
45163
|
+
}
|
|
45164
|
+
function tokenSegment2(totalTokens) {
|
|
45165
|
+
if (totalTokens == null || totalTokens <= 0)
|
|
45166
|
+
return "";
|
|
45167
|
+
return ` \u00b7 ${formatTokenCount2(totalTokens)} tok`;
|
|
45168
|
+
}
|
|
45017
45169
|
function formatFeedElapsed2(ms) {
|
|
45018
45170
|
const s = Math.floor(ms / 1000);
|
|
45019
45171
|
if (s < 60)
|
|
@@ -45050,7 +45202,7 @@ function renderStatusCard2(opts) {
|
|
|
45050
45202
|
const hasChildren = rawChildren.length > 0;
|
|
45051
45203
|
const steps = rawSteps.map(escapeStepLine2);
|
|
45052
45204
|
const children = rawChildren.map(escapeStepLine2);
|
|
45053
|
-
const headerLines = header != null ? renderActivityHeader2(header.emoji, header.label, header.description ?? "", header.elapsedMs, header.toolCount, header.state, header.model) : [];
|
|
45205
|
+
const headerLines = header != null ? renderActivityHeader2(header.emoji, header.label, header.description ?? "", header.elapsedMs, header.toolCount, header.state, header.model, header.totalTokens) : [];
|
|
45054
45206
|
const out = [...headerLines];
|
|
45055
45207
|
if (hasChildren) {
|
|
45056
45208
|
const shownParent = steps.slice(-STATUS_ROLLING_LINES);
|
|
@@ -45147,7 +45299,8 @@ function renderActivityFeed2(lines, final = false, liveSuffix = "", stepCount, h
|
|
|
45147
45299
|
elapsedMs: header.elapsedMs,
|
|
45148
45300
|
toolCount: header.toolCount,
|
|
45149
45301
|
state: header.state,
|
|
45150
|
-
model: header.model
|
|
45302
|
+
model: header.model,
|
|
45303
|
+
totalTokens: header.totalTokens
|
|
45151
45304
|
} : undefined,
|
|
45152
45305
|
steps: lines,
|
|
45153
45306
|
final,
|
|
@@ -45166,7 +45319,8 @@ function renderActivityFeedWithNested2(lines, childLines, final = false, liveSuf
|
|
|
45166
45319
|
elapsedMs: header.elapsedMs,
|
|
45167
45320
|
toolCount: header.toolCount,
|
|
45168
45321
|
state: header.state,
|
|
45169
|
-
model: header.model
|
|
45322
|
+
model: header.model,
|
|
45323
|
+
totalTokens: header.totalTokens
|
|
45170
45324
|
} : undefined,
|
|
45171
45325
|
steps: lines,
|
|
45172
45326
|
childSteps: children,
|
|
@@ -62841,6 +62995,597 @@ function resolveAnswerLaneConfig(input) {
|
|
|
62841
62995
|
};
|
|
62842
62996
|
}
|
|
62843
62997
|
|
|
62998
|
+
// session-tail.ts
|
|
62999
|
+
import { homedir as homedir7 } from "os";
|
|
63000
|
+
import { basename as basename5, join as join21 } from "path";
|
|
63001
|
+
|
|
63002
|
+
// operator-events.ts
|
|
63003
|
+
init_format();
|
|
63004
|
+
|
|
63005
|
+
// raw-error-scrub.ts
|
|
63006
|
+
function stripRawErrorBytes(raw) {
|
|
63007
|
+
if (typeof raw !== "string" || raw.length === 0)
|
|
63008
|
+
return "";
|
|
63009
|
+
let s = raw;
|
|
63010
|
+
s = s.replace(/API Error:?\s*\d*\s*/gi, " ");
|
|
63011
|
+
s = s.replace(/\bb'[^']*'/g, " ");
|
|
63012
|
+
s = s.replace(/\bb"[^"]*"/g, " ");
|
|
63013
|
+
s = s.replace(/[\u00b7\-\s]*\{[\s\S]*?["']type["']\s*:\s*["']error["'][\s\S]*$/i, " ");
|
|
63014
|
+
s = s.replace(/^\s*\{[\s\S]*\}\s*$/g, " ");
|
|
63015
|
+
s = s.replace(/\s+/g, " ").replace(/[\u00b7:\-\s]+$/g, "").replace(/^[\u00b7:\-\s]+/g, "").trim();
|
|
63016
|
+
return s;
|
|
63017
|
+
}
|
|
63018
|
+
function extractRequestId(raw) {
|
|
63019
|
+
if (typeof raw !== "string" || raw.length === 0)
|
|
63020
|
+
return;
|
|
63021
|
+
const m = raw.match(/["']request[_-]?id["']\s*:\s*["']([A-Za-z0-9._-]+)["']/i) ?? raw.match(/\brequest[_-]?id[=:]\s*([A-Za-z0-9._-]+)/i);
|
|
63022
|
+
return m ? m[1] : undefined;
|
|
63023
|
+
}
|
|
63024
|
+
|
|
63025
|
+
// operator-events.ts
|
|
63026
|
+
function classifyClaudeError(raw) {
|
|
63027
|
+
try {
|
|
63028
|
+
return classifyInner(raw);
|
|
63029
|
+
} catch {
|
|
63030
|
+
return "unknown-4xx";
|
|
63031
|
+
}
|
|
63032
|
+
}
|
|
63033
|
+
function classifyInner(raw) {
|
|
63034
|
+
if (raw == null)
|
|
63035
|
+
return "unknown-4xx";
|
|
63036
|
+
const obj = typeof raw === "object" ? raw : {};
|
|
63037
|
+
const errorType = extractString(obj, "error_type") ?? extractString(obj, "type") ?? extractString(getNestedObj(obj, "error"), "type") ?? "";
|
|
63038
|
+
const errorCode = extractString(obj, "code") ?? extractString(getNestedObj(obj, "error"), "code") ?? "";
|
|
63039
|
+
const message = extractString(obj, "message") ?? extractString(getNestedObj(obj, "error"), "message") ?? (typeof raw === "string" ? raw : "") ?? "";
|
|
63040
|
+
const status = extractNumber(obj, "status") ?? extractNumber(obj, "statusCode") ?? extractNumber(obj, "status_code") ?? null;
|
|
63041
|
+
const sdkCode = extractString(obj, "error_code") ?? "";
|
|
63042
|
+
if (errorType === "authentication_error" || errorCode === "authentication_error" || sdkCode === "authentication_error" || message.toLowerCase().includes("authentication_error")) {
|
|
63043
|
+
const msg = message.toLowerCase();
|
|
63044
|
+
if (msg.includes("expired") || msg.includes("refresh")) {
|
|
63045
|
+
return "credentials-expired";
|
|
63046
|
+
}
|
|
63047
|
+
return "credentials-invalid";
|
|
63048
|
+
}
|
|
63049
|
+
if (errorType === "invalid_api_key" || errorCode === "invalid_api_key" || sdkCode === "invalid_api_key" || message.toLowerCase().includes("invalid_api_key") || message.toLowerCase().includes("invalid api key")) {
|
|
63050
|
+
return "credentials-invalid";
|
|
63051
|
+
}
|
|
63052
|
+
if (errorType === "credit_balance_too_low" || errorCode === "credit_balance_too_low" || sdkCode === "credit_balance_too_low" || message.toLowerCase().includes("credit_balance_too_low") || message.toLowerCase().includes("credit balance")) {
|
|
63053
|
+
return "credit-exhausted";
|
|
63054
|
+
}
|
|
63055
|
+
if (errorType === "rate_limit_error" || errorCode === "rate_limit_error" || sdkCode === "rate_limit_error" || message.toLowerCase().includes("rate_limit_error") || message.toLowerCase().includes("rate limit")) {
|
|
63056
|
+
return "rate-limited";
|
|
63057
|
+
}
|
|
63058
|
+
if (errorType === "overloaded_error" || errorCode === "overloaded_error" || sdkCode === "overloaded_error" || message.toLowerCase().includes("overloaded_error") || message.toLowerCase().includes("overloaded")) {
|
|
63059
|
+
return "rate-limited";
|
|
63060
|
+
}
|
|
63061
|
+
if (errorType === "agent-crashed" || errorCode === "agent-crashed") {
|
|
63062
|
+
return "agent-crashed";
|
|
63063
|
+
}
|
|
63064
|
+
if (errorType === "agent-restarted-unexpectedly" || errorCode === "agent-restarted-unexpectedly") {
|
|
63065
|
+
return "agent-restarted-unexpectedly";
|
|
63066
|
+
}
|
|
63067
|
+
if (status != null) {
|
|
63068
|
+
if (status >= 400 && status < 500)
|
|
63069
|
+
return "unknown-4xx";
|
|
63070
|
+
if (status >= 500 && status < 600)
|
|
63071
|
+
return "unknown-5xx";
|
|
63072
|
+
}
|
|
63073
|
+
return "unknown-4xx";
|
|
63074
|
+
}
|
|
63075
|
+
function extractString(obj, key) {
|
|
63076
|
+
const v = obj[key];
|
|
63077
|
+
return typeof v === "string" && v.length > 0 ? v : null;
|
|
63078
|
+
}
|
|
63079
|
+
function extractNumber(obj, key) {
|
|
63080
|
+
const v = obj[key];
|
|
63081
|
+
return typeof v === "number" ? v : null;
|
|
63082
|
+
}
|
|
63083
|
+
function getNestedObj(obj, key) {
|
|
63084
|
+
const v = obj[key];
|
|
63085
|
+
return typeof v === "object" && v != null ? v : {};
|
|
63086
|
+
}
|
|
63087
|
+
var DEFAULT_OPERATOR_EVENT_COOLDOWN_MS = 5 * 60000;
|
|
63088
|
+
var cooldownMap = new Map;
|
|
63089
|
+
|
|
63090
|
+
// model-unavailable.ts
|
|
63091
|
+
init_quota_check();
|
|
63092
|
+
init_card_format();
|
|
63093
|
+
var transientUpstreamSignals = [
|
|
63094
|
+
"not your usage limit",
|
|
63095
|
+
"not your account",
|
|
63096
|
+
"not your account's",
|
|
63097
|
+
"temporarily limiting requests",
|
|
63098
|
+
"temporarily rate",
|
|
63099
|
+
"server is temporarily",
|
|
63100
|
+
"would exceed your account\u2019s rate limit",
|
|
63101
|
+
"would exceed your account's rate limit"
|
|
63102
|
+
];
|
|
63103
|
+
function isTransientUpstreamSignal(text4) {
|
|
63104
|
+
if (typeof text4 !== "string" || text4.length === 0)
|
|
63105
|
+
return false;
|
|
63106
|
+
const sample = text4.length > 16384 ? text4.slice(0, 16384) : text4;
|
|
63107
|
+
const lower = sample.toLowerCase();
|
|
63108
|
+
return transientUpstreamSignals.some((s) => lower.includes(s));
|
|
63109
|
+
}
|
|
63110
|
+
var litellmProxyLocal429Signals = [
|
|
63111
|
+
"deployment over user-defined ratelimit",
|
|
63112
|
+
"model rate limit exceeded. tpm limit",
|
|
63113
|
+
"model rate limit exceeded. rpm limit",
|
|
63114
|
+
"deployment over defined rpm limit",
|
|
63115
|
+
"no deployments available for selected model",
|
|
63116
|
+
"litellm rate limit handler",
|
|
63117
|
+
"crossed tpm / rpm",
|
|
63118
|
+
"max parallel request limit reached"
|
|
63119
|
+
];
|
|
63120
|
+
var litellmV3LimiterSignalPair = ["rate limit exceeded for ", "limit type:"];
|
|
63121
|
+
function isLitellmProxyLocal429(text4) {
|
|
63122
|
+
if (typeof text4 !== "string" || text4.length === 0)
|
|
63123
|
+
return false;
|
|
63124
|
+
const sample = text4.length > 16384 ? text4.slice(0, 16384) : text4;
|
|
63125
|
+
const lower = sample.toLowerCase();
|
|
63126
|
+
if (litellmProxyLocal429Signals.some((s) => lower.includes(s)))
|
|
63127
|
+
return true;
|
|
63128
|
+
return litellmV3LimiterSignalPair.every((s) => lower.includes(s));
|
|
63129
|
+
}
|
|
63130
|
+
function parseLitellmLimitDetail(text4, parseTimeNow = new Date) {
|
|
63131
|
+
const empty2 = { limitType: null, limit: null, currentUsage: null, resetAtMs: null };
|
|
63132
|
+
if (typeof text4 !== "string" || text4.length === 0)
|
|
63133
|
+
return empty2;
|
|
63134
|
+
const sample = text4.length > 16384 ? text4.slice(0, 16384) : text4;
|
|
63135
|
+
const lower = sample.toLowerCase();
|
|
63136
|
+
let limitType = null;
|
|
63137
|
+
let limit = null;
|
|
63138
|
+
const eqLimit = lower.match(/\b([tr]pm)[ _]limit[=:]\s*(\d+)/);
|
|
63139
|
+
if (eqLimit) {
|
|
63140
|
+
limitType = eqLimit[1];
|
|
63141
|
+
limit = Number(eqLimit[2]);
|
|
63142
|
+
}
|
|
63143
|
+
if (limitType == null) {
|
|
63144
|
+
const v3Type = lower.match(/limit type:\s*(tokens|requests|max_parallel_requests)/);
|
|
63145
|
+
if (v3Type)
|
|
63146
|
+
limitType = v3Type[1];
|
|
63147
|
+
}
|
|
63148
|
+
if (limit == null) {
|
|
63149
|
+
const v3Limit = lower.match(/current limit:\s*(\d+)/);
|
|
63150
|
+
if (v3Limit)
|
|
63151
|
+
limit = Number(v3Limit[1]);
|
|
63152
|
+
}
|
|
63153
|
+
let currentUsage = null;
|
|
63154
|
+
const usage = lower.match(/current usage=(\d+)/);
|
|
63155
|
+
if (usage)
|
|
63156
|
+
currentUsage = Number(usage[1]);
|
|
63157
|
+
let resetAtMs = null;
|
|
63158
|
+
const resetsAt = sample.match(/limit resets at:\s*(\d{4}-\d{2}-\d{2}) (\d{2}:\d{2}:\d{2}) UTC/i);
|
|
63159
|
+
if (resetsAt) {
|
|
63160
|
+
const d = new Date(`${resetsAt[1]}T${resetsAt[2]}Z`);
|
|
63161
|
+
if (!Number.isNaN(d.getTime()))
|
|
63162
|
+
resetAtMs = d.getTime();
|
|
63163
|
+
}
|
|
63164
|
+
if (resetAtMs == null) {
|
|
63165
|
+
const tryAgain = lower.match(/try again in\s+(\d+(?:\.\d+)?)\s*seconds/);
|
|
63166
|
+
if (tryAgain) {
|
|
63167
|
+
const secs = Number(tryAgain[1]);
|
|
63168
|
+
if (Number.isFinite(secs) && secs > 0 && secs < 7 * 24 * 3600) {
|
|
63169
|
+
resetAtMs = parseTimeNow.getTime() + Math.round(secs * 1000);
|
|
63170
|
+
}
|
|
63171
|
+
}
|
|
63172
|
+
}
|
|
63173
|
+
return {
|
|
63174
|
+
limitType,
|
|
63175
|
+
limit: limit != null && Number.isFinite(limit) ? limit : null,
|
|
63176
|
+
currentUsage: currentUsage != null && Number.isFinite(currentUsage) ? currentUsage : null,
|
|
63177
|
+
resetAtMs
|
|
63178
|
+
};
|
|
63179
|
+
}
|
|
63180
|
+
function detectModelUnavailable(stderr) {
|
|
63181
|
+
if (typeof stderr !== "string" || stderr.length === 0)
|
|
63182
|
+
return null;
|
|
63183
|
+
const sample = stderr.length > 16384 ? stderr.slice(0, 16384) : stderr;
|
|
63184
|
+
const lower = sample.toLowerCase();
|
|
63185
|
+
if (transientUpstreamSignals.some((s) => lower.includes(s))) {
|
|
63186
|
+
const resetAt = parseResetTime(sample);
|
|
63187
|
+
return resetAt !== undefined ? { kind: "overload", resetAt, raw: stderr } : { kind: "overload", raw: stderr };
|
|
63188
|
+
}
|
|
63189
|
+
if (isLitellmProxyLocal429(sample)) {
|
|
63190
|
+
const resetAt = parseResetTime(sample);
|
|
63191
|
+
return resetAt !== undefined ? { kind: "overload", resetAt, raw: stderr } : { kind: "overload", raw: stderr };
|
|
63192
|
+
}
|
|
63193
|
+
const quotaSignals = [
|
|
63194
|
+
"out of extra usage",
|
|
63195
|
+
"extra usage",
|
|
63196
|
+
"credit_balance_too_low",
|
|
63197
|
+
"credit balance too low",
|
|
63198
|
+
"usage limit",
|
|
63199
|
+
"usage_limit",
|
|
63200
|
+
"quota exhausted",
|
|
63201
|
+
"quota_exhausted",
|
|
63202
|
+
"plan limit",
|
|
63203
|
+
"subscription limit",
|
|
63204
|
+
"hit your limit",
|
|
63205
|
+
"hit the limit",
|
|
63206
|
+
"session limit",
|
|
63207
|
+
"session cap"
|
|
63208
|
+
];
|
|
63209
|
+
if (quotaSignals.some((s) => lower.includes(s))) {
|
|
63210
|
+
const resetAt = parseResetTime(sample);
|
|
63211
|
+
return resetAt !== undefined ? { kind: "quota_exhausted", resetAt, raw: stderr } : { kind: "quota_exhausted", raw: stderr };
|
|
63212
|
+
}
|
|
63213
|
+
const overloadSignals = [
|
|
63214
|
+
"overloaded_error",
|
|
63215
|
+
"overloaded",
|
|
63216
|
+
"rate_limit_error",
|
|
63217
|
+
"rate limit",
|
|
63218
|
+
"rate-limited",
|
|
63219
|
+
"http 429",
|
|
63220
|
+
'"status":429',
|
|
63221
|
+
"status: 429",
|
|
63222
|
+
" 429 ",
|
|
63223
|
+
"503 service",
|
|
63224
|
+
"service unavailable",
|
|
63225
|
+
'"status":529',
|
|
63226
|
+
"http 529",
|
|
63227
|
+
" 529 "
|
|
63228
|
+
];
|
|
63229
|
+
if (overloadSignals.some((s) => lower.includes(s))) {
|
|
63230
|
+
const resetAt = parseResetTime(sample);
|
|
63231
|
+
return resetAt !== undefined ? { kind: "overload", resetAt, raw: stderr } : { kind: "overload", raw: stderr };
|
|
63232
|
+
}
|
|
63233
|
+
const networkSignals = [
|
|
63234
|
+
"econnrefused",
|
|
63235
|
+
"econnreset",
|
|
63236
|
+
"etimedout",
|
|
63237
|
+
"enotfound",
|
|
63238
|
+
"eai_again",
|
|
63239
|
+
"fetch failed",
|
|
63240
|
+
"network error",
|
|
63241
|
+
"socket hang up",
|
|
63242
|
+
"request timed out",
|
|
63243
|
+
"connection refused",
|
|
63244
|
+
"getaddrinfo"
|
|
63245
|
+
];
|
|
63246
|
+
if (networkSignals.some((s) => lower.includes(s))) {
|
|
63247
|
+
return { kind: "network", raw: stderr };
|
|
63248
|
+
}
|
|
63249
|
+
return null;
|
|
63250
|
+
}
|
|
63251
|
+
function parseResetTime(text4, parseTimeNow = new Date) {
|
|
63252
|
+
const lower = text4.toLowerCase();
|
|
63253
|
+
const retryAfter = lower.match(/retry[\s-]*after[:\s]+(\d+)\s*(seconds?|s\b|minutes?|m\b|hours?|h\b)?/);
|
|
63254
|
+
if (retryAfter) {
|
|
63255
|
+
const n = Number(retryAfter[1]);
|
|
63256
|
+
if (Number.isFinite(n) && n > 0 && n < 7 * 24 * 3600) {
|
|
63257
|
+
const unit = (retryAfter[2] ?? "seconds").toLowerCase();
|
|
63258
|
+
const ms = unit.startsWith("h") ? n * 3600000 : unit.startsWith("m") ? n * 60000 : n * 1000;
|
|
63259
|
+
return new Date(parseTimeNow.getTime() + ms);
|
|
63260
|
+
}
|
|
63261
|
+
}
|
|
63262
|
+
const relReset = lower.match(/resets?\s+in\s+([0-9hms\s]+)/);
|
|
63263
|
+
if (relReset) {
|
|
63264
|
+
const ms = parseRelativeDuration(relReset[1]);
|
|
63265
|
+
if (ms != null)
|
|
63266
|
+
return new Date(parseTimeNow.getTime() + ms);
|
|
63267
|
+
}
|
|
63268
|
+
const iso = text4.match(/\b\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:?\d{2})?\b/);
|
|
63269
|
+
if (iso) {
|
|
63270
|
+
const d = new Date(iso[0]);
|
|
63271
|
+
if (!Number.isNaN(d.getTime()))
|
|
63272
|
+
return d;
|
|
63273
|
+
}
|
|
63274
|
+
const calReset = text4.match(/resets?\s+(?:at\s+)?([A-Z][a-z]{2,8}\s+\d{1,2}(?:,?\s*(?:\d{1,2}(?::\d{2})?\s*(?:am|pm|AM|PM)?))?)/);
|
|
63275
|
+
if (calReset) {
|
|
63276
|
+
const candidate = `${calReset[1]} ${parseTimeNow.getUTCFullYear()}`;
|
|
63277
|
+
const d = new Date(candidate);
|
|
63278
|
+
if (!Number.isNaN(d.getTime()))
|
|
63279
|
+
return d;
|
|
63280
|
+
}
|
|
63281
|
+
const timeOnly = text4.match(/resets?\s+(?:at\s+)?(?!(?:jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec)[a-z]*\b)(\d{1,2})(?::(\d{2}))?\s*(am|pm)?\s*(?:\(([^)]+)\))?/i);
|
|
63282
|
+
if (timeOnly) {
|
|
63283
|
+
const d = resolveNextWallClock(Number(timeOnly[1]), timeOnly[2] ? Number(timeOnly[2]) : 0, timeOnly[3]?.toLowerCase(), timeOnly[4]?.trim(), parseTimeNow);
|
|
63284
|
+
if (d != null)
|
|
63285
|
+
return d;
|
|
63286
|
+
}
|
|
63287
|
+
return;
|
|
63288
|
+
}
|
|
63289
|
+
function resolveNextWallClock(hour12or24, minute, ampm, tz, nowDate) {
|
|
63290
|
+
let hour = hour12or24;
|
|
63291
|
+
if (ampm === "pm" && hour < 12)
|
|
63292
|
+
hour += 12;
|
|
63293
|
+
if (ampm === "am" && hour === 12)
|
|
63294
|
+
hour = 0;
|
|
63295
|
+
if (!Number.isFinite(hour) || hour > 23 || hour < 0)
|
|
63296
|
+
return;
|
|
63297
|
+
if (!Number.isFinite(minute) || minute > 59 || minute < 0)
|
|
63298
|
+
return;
|
|
63299
|
+
const nowMs2 = nowDate.getTime();
|
|
63300
|
+
const base = new Date(nowMs2);
|
|
63301
|
+
for (let dayOffset = 0;dayOffset <= 2; dayOffset++) {
|
|
63302
|
+
const dateParts = tzDateParts(new Date(nowMs2 + dayOffset * 86400000), tz);
|
|
63303
|
+
if (dateParts == null)
|
|
63304
|
+
return;
|
|
63305
|
+
const epoch = wallClockToEpoch(dateParts.year, dateParts.month, dateParts.day, hour, minute, tz);
|
|
63306
|
+
if (epoch != null && epoch > nowMs2)
|
|
63307
|
+
return new Date(epoch);
|
|
63308
|
+
}
|
|
63309
|
+
return;
|
|
63310
|
+
}
|
|
63311
|
+
function tzDateParts(d, tz) {
|
|
63312
|
+
if (!tz) {
|
|
63313
|
+
return { year: d.getUTCFullYear(), month: d.getUTCMonth(), day: d.getUTCDate() };
|
|
63314
|
+
}
|
|
63315
|
+
try {
|
|
63316
|
+
const fmt = new Intl.DateTimeFormat("en-US", {
|
|
63317
|
+
timeZone: tz,
|
|
63318
|
+
year: "numeric",
|
|
63319
|
+
month: "2-digit",
|
|
63320
|
+
day: "2-digit"
|
|
63321
|
+
});
|
|
63322
|
+
const parts = Object.fromEntries(fmt.formatToParts(d).filter((p) => p.type !== "literal").map((p) => [p.type, p.value]));
|
|
63323
|
+
return {
|
|
63324
|
+
year: Number(parts.year),
|
|
63325
|
+
month: Number(parts.month) - 1,
|
|
63326
|
+
day: Number(parts.day)
|
|
63327
|
+
};
|
|
63328
|
+
} catch {
|
|
63329
|
+
return null;
|
|
63330
|
+
}
|
|
63331
|
+
}
|
|
63332
|
+
function wallClockToEpoch(year, month, day, hour, minute, tz) {
|
|
63333
|
+
const asUtc = Date.UTC(year, month, day, hour, minute, 0);
|
|
63334
|
+
if (!tz)
|
|
63335
|
+
return asUtc;
|
|
63336
|
+
try {
|
|
63337
|
+
const fmt = new Intl.DateTimeFormat("en-US", {
|
|
63338
|
+
timeZone: tz,
|
|
63339
|
+
year: "numeric",
|
|
63340
|
+
month: "2-digit",
|
|
63341
|
+
day: "2-digit",
|
|
63342
|
+
hour: "2-digit",
|
|
63343
|
+
minute: "2-digit",
|
|
63344
|
+
second: "2-digit",
|
|
63345
|
+
hour12: false
|
|
63346
|
+
});
|
|
63347
|
+
const parts = Object.fromEntries(fmt.formatToParts(new Date(asUtc)).filter((p) => p.type !== "literal").map((p) => [p.type, p.value]));
|
|
63348
|
+
const shown = Date.UTC(Number(parts.year), Number(parts.month) - 1, Number(parts.day), Number(parts.hour) % 24, Number(parts.minute), Number(parts.second));
|
|
63349
|
+
const offset = shown - asUtc;
|
|
63350
|
+
return asUtc - offset;
|
|
63351
|
+
} catch {
|
|
63352
|
+
return null;
|
|
63353
|
+
}
|
|
63354
|
+
}
|
|
63355
|
+
function parseRelativeDuration(s) {
|
|
63356
|
+
let total = 0;
|
|
63357
|
+
let matched = false;
|
|
63358
|
+
const re = /(\d+)\s*(h|hours?|m|minutes?|s|seconds?)/g;
|
|
63359
|
+
let m;
|
|
63360
|
+
while ((m = re.exec(s)) != null) {
|
|
63361
|
+
matched = true;
|
|
63362
|
+
const n = Number(m[1]);
|
|
63363
|
+
const unit = m[2].toLowerCase();
|
|
63364
|
+
if (unit.startsWith("h"))
|
|
63365
|
+
total += n * 3600000;
|
|
63366
|
+
else if (unit.startsWith("m"))
|
|
63367
|
+
total += n * 60000;
|
|
63368
|
+
else
|
|
63369
|
+
total += n * 1000;
|
|
63370
|
+
}
|
|
63371
|
+
return matched && total > 0 ? total : null;
|
|
63372
|
+
}
|
|
63373
|
+
|
|
63374
|
+
// session-tail.ts
|
|
63375
|
+
function sanitizeCwdToProjectName(cwd) {
|
|
63376
|
+
return cwd.replace(/[^a-zA-Z0-9]/g, "-");
|
|
63377
|
+
}
|
|
63378
|
+
function getProjectsDirForCwd(cwd = process.cwd(), claudeHome = process.env.CLAUDE_CONFIG_DIR ?? join21(homedir7(), ".claude")) {
|
|
63379
|
+
return join21(claudeHome, "projects", sanitizeCwdToProjectName(cwd));
|
|
63380
|
+
}
|
|
63381
|
+
function parseChannelMeta(content3) {
|
|
63382
|
+
const grab = (key) => {
|
|
63383
|
+
const m = content3.match(new RegExp(`(?:^|[\\s"'])${key}="([^"]+)"`));
|
|
63384
|
+
return m ? m[1] : null;
|
|
63385
|
+
};
|
|
63386
|
+
return {
|
|
63387
|
+
chatId: grab("chat_id"),
|
|
63388
|
+
messageId: grab("message_id"),
|
|
63389
|
+
threadId: grab("message_thread_id")
|
|
63390
|
+
};
|
|
63391
|
+
}
|
|
63392
|
+
var MAX_JSONL_LINE_BYTES = 2 * 1024 * 1024;
|
|
63393
|
+
var MAX_ERROR_TEXT_CHARS = 500;
|
|
63394
|
+
function extractToolResultErrorText(content3) {
|
|
63395
|
+
if (typeof content3 === "string") {
|
|
63396
|
+
return content3.slice(0, MAX_ERROR_TEXT_CHARS);
|
|
63397
|
+
}
|
|
63398
|
+
if (Array.isArray(content3)) {
|
|
63399
|
+
const parts = [];
|
|
63400
|
+
for (const block of content3) {
|
|
63401
|
+
if (typeof block === "object" && block != null) {
|
|
63402
|
+
const b = block;
|
|
63403
|
+
if (b.type === "text" && typeof b.text === "string") {
|
|
63404
|
+
parts.push(b.text);
|
|
63405
|
+
}
|
|
63406
|
+
}
|
|
63407
|
+
}
|
|
63408
|
+
return parts.join(`
|
|
63409
|
+
`).slice(0, MAX_ERROR_TEXT_CHARS);
|
|
63410
|
+
}
|
|
63411
|
+
return "";
|
|
63412
|
+
}
|
|
63413
|
+
function projectAssistantTextBlocks(content3, make) {
|
|
63414
|
+
const out = new Map;
|
|
63415
|
+
let lastToolUseIdx = -1;
|
|
63416
|
+
content3.forEach((c, i) => {
|
|
63417
|
+
if (c.type === "tool_use")
|
|
63418
|
+
lastToolUseIdx = i;
|
|
63419
|
+
});
|
|
63420
|
+
content3.forEach((c, i) => {
|
|
63421
|
+
if (c.type !== "text")
|
|
63422
|
+
return;
|
|
63423
|
+
const text4 = c.text ?? "";
|
|
63424
|
+
if (text4.trim().length === 0)
|
|
63425
|
+
return;
|
|
63426
|
+
out.set(i, make(text4, i, i > lastToolUseIdx));
|
|
63427
|
+
});
|
|
63428
|
+
return out;
|
|
63429
|
+
}
|
|
63430
|
+
function sumUsageTokens(usage) {
|
|
63431
|
+
if (usage == null || typeof usage !== "object")
|
|
63432
|
+
return 0;
|
|
63433
|
+
const u = usage;
|
|
63434
|
+
const n = (v) => typeof v === "number" && Number.isFinite(v) ? v : 0;
|
|
63435
|
+
return n(u.input_tokens) + n(u.output_tokens) + n(u.cache_creation_input_tokens);
|
|
63436
|
+
}
|
|
63437
|
+
function projectTrailingAnswerFromTranscript(transcriptText) {
|
|
63438
|
+
const buf = [];
|
|
63439
|
+
let lastMeaningful = null;
|
|
63440
|
+
for (const rawLine of transcriptText.split(`
|
|
63441
|
+
`)) {
|
|
63442
|
+
const line = rawLine.trim();
|
|
63443
|
+
if (!line)
|
|
63444
|
+
continue;
|
|
63445
|
+
if (isRealUserTurnBoundary(line)) {
|
|
63446
|
+
buf.length = 0;
|
|
63447
|
+
lastMeaningful = null;
|
|
63448
|
+
continue;
|
|
63449
|
+
}
|
|
63450
|
+
for (const ev of projectTranscriptLine(line)) {
|
|
63451
|
+
if (ev.kind === "enqueue") {
|
|
63452
|
+
buf.length = 0;
|
|
63453
|
+
lastMeaningful = null;
|
|
63454
|
+
} else if (ev.kind === "tool_use") {
|
|
63455
|
+
buf.length = 0;
|
|
63456
|
+
lastMeaningful = "tool_use";
|
|
63457
|
+
} else if (ev.kind === "text") {
|
|
63458
|
+
const t = ev.text ?? "";
|
|
63459
|
+
if (t.trim().length > 0) {
|
|
63460
|
+
buf.push(t);
|
|
63461
|
+
lastMeaningful = "text";
|
|
63462
|
+
}
|
|
63463
|
+
}
|
|
63464
|
+
}
|
|
63465
|
+
}
|
|
63466
|
+
const text4 = buf.join("").trim();
|
|
63467
|
+
return { text: text4, trailingIsText: lastMeaningful === "text" && text4.length > 0 };
|
|
63468
|
+
}
|
|
63469
|
+
function isRealUserTurnBoundary(line) {
|
|
63470
|
+
let obj;
|
|
63471
|
+
try {
|
|
63472
|
+
obj = JSON.parse(line);
|
|
63473
|
+
} catch {
|
|
63474
|
+
return false;
|
|
63475
|
+
}
|
|
63476
|
+
if (obj.type !== "user")
|
|
63477
|
+
return false;
|
|
63478
|
+
const message = obj.message;
|
|
63479
|
+
const content3 = message?.content;
|
|
63480
|
+
if (typeof content3 === "string")
|
|
63481
|
+
return content3.trim().length > 0;
|
|
63482
|
+
if (Array.isArray(content3)) {
|
|
63483
|
+
for (const c of content3) {
|
|
63484
|
+
if (typeof c === "object" && c != null && c.type === "text") {
|
|
63485
|
+
const t = String(c.text ?? "");
|
|
63486
|
+
if (t.trim().length > 0)
|
|
63487
|
+
return true;
|
|
63488
|
+
}
|
|
63489
|
+
}
|
|
63490
|
+
}
|
|
63491
|
+
return false;
|
|
63492
|
+
}
|
|
63493
|
+
function projectTranscriptLine(line) {
|
|
63494
|
+
if (line.length > MAX_JSONL_LINE_BYTES)
|
|
63495
|
+
return [];
|
|
63496
|
+
let obj;
|
|
63497
|
+
try {
|
|
63498
|
+
obj = JSON.parse(line);
|
|
63499
|
+
} catch {
|
|
63500
|
+
return [];
|
|
63501
|
+
}
|
|
63502
|
+
const type = obj.type;
|
|
63503
|
+
if (!type)
|
|
63504
|
+
return [];
|
|
63505
|
+
if (type === "queue-operation") {
|
|
63506
|
+
const op = obj.operation;
|
|
63507
|
+
if (op === "enqueue") {
|
|
63508
|
+
const content3 = obj.content ?? "";
|
|
63509
|
+
const { chatId, messageId, threadId } = parseChannelMeta(content3);
|
|
63510
|
+
return [{ kind: "enqueue", chatId, messageId, threadId, rawContent: content3 }];
|
|
63511
|
+
}
|
|
63512
|
+
if (op === "dequeue") {
|
|
63513
|
+
return [{ kind: "dequeue" }];
|
|
63514
|
+
}
|
|
63515
|
+
return [];
|
|
63516
|
+
}
|
|
63517
|
+
if (type === "assistant") {
|
|
63518
|
+
const message = obj.message;
|
|
63519
|
+
const content3 = message?.content;
|
|
63520
|
+
if (!Array.isArray(content3))
|
|
63521
|
+
return [];
|
|
63522
|
+
if (obj.isApiErrorMessage === true) {
|
|
63523
|
+
const mainModel2 = message?.model;
|
|
63524
|
+
return typeof mainModel2 === "string" && !isModelSentinel(mainModel2) ? [{ kind: "model", model: mainModel2 }] : [];
|
|
63525
|
+
}
|
|
63526
|
+
const events = [];
|
|
63527
|
+
const mainModel = message?.model;
|
|
63528
|
+
if (typeof mainModel === "string" && !isModelSentinel(mainModel)) {
|
|
63529
|
+
events.push({ kind: "model", model: mainModel });
|
|
63530
|
+
}
|
|
63531
|
+
const mainUsageTotal = sumUsageTokens(message?.usage);
|
|
63532
|
+
if (mainUsageTotal > 0) {
|
|
63533
|
+
const mainMsgId = message?.id;
|
|
63534
|
+
events.push({
|
|
63535
|
+
kind: "usage",
|
|
63536
|
+
messageId: typeof mainMsgId === "string" ? mainMsgId : null,
|
|
63537
|
+
totalTokens: mainUsageTotal
|
|
63538
|
+
});
|
|
63539
|
+
}
|
|
63540
|
+
const textEvents = projectAssistantTextBlocks(content3, (text4, blockIndex, lastInMessage) => ({ kind: "text", text: text4, blockIndex, lastInMessage }));
|
|
63541
|
+
content3.forEach((c, i) => {
|
|
63542
|
+
const ct = c.type;
|
|
63543
|
+
if (ct === "thinking") {
|
|
63544
|
+
events.push({ kind: "thinking" });
|
|
63545
|
+
} else if (ct === "tool_use") {
|
|
63546
|
+
const input = c.input;
|
|
63547
|
+
events.push({
|
|
63548
|
+
kind: "tool_use",
|
|
63549
|
+
toolName: c.name ?? "",
|
|
63550
|
+
toolUseId: c.id ?? null,
|
|
63551
|
+
input: input && typeof input === "object" ? input : undefined
|
|
63552
|
+
});
|
|
63553
|
+
} else if (ct === "text") {
|
|
63554
|
+
const ev = textEvents.get(i);
|
|
63555
|
+
if (ev != null)
|
|
63556
|
+
events.push(ev);
|
|
63557
|
+
}
|
|
63558
|
+
});
|
|
63559
|
+
return events;
|
|
63560
|
+
}
|
|
63561
|
+
if (type === "user") {
|
|
63562
|
+
const message = obj.message;
|
|
63563
|
+
const content3 = message?.content;
|
|
63564
|
+
if (!Array.isArray(content3))
|
|
63565
|
+
return [];
|
|
63566
|
+
const events = [];
|
|
63567
|
+
for (const c of content3) {
|
|
63568
|
+
if (c.type === "tool_result") {
|
|
63569
|
+
const isError2 = c.is_error === true ? true : undefined;
|
|
63570
|
+
events.push({
|
|
63571
|
+
kind: "tool_result",
|
|
63572
|
+
toolUseId: c.tool_use_id ?? "",
|
|
63573
|
+
toolName: null,
|
|
63574
|
+
isError: isError2,
|
|
63575
|
+
errorText: isError2 ? extractToolResultErrorText(c.content) : undefined
|
|
63576
|
+
});
|
|
63577
|
+
}
|
|
63578
|
+
}
|
|
63579
|
+
return events;
|
|
63580
|
+
}
|
|
63581
|
+
if (type === "system" && obj.subtype === "turn_duration") {
|
|
63582
|
+
return [
|
|
63583
|
+
{ kind: "turn_end", durationMs: obj.durationMs ?? 0 }
|
|
63584
|
+
];
|
|
63585
|
+
}
|
|
63586
|
+
return [];
|
|
63587
|
+
}
|
|
63588
|
+
|
|
62844
63589
|
// pty-tail.ts
|
|
62845
63590
|
var import_headless = __toESM(require_xterm_headless(), 1);
|
|
62846
63591
|
var PTY_DEBUG = process.env.SWITCHROOM_PTY_DEBUG === "1";
|
|
@@ -62925,7 +63670,7 @@ async function gatewayStartupRetry(fn, opts = {}) {
|
|
|
62925
63670
|
|
|
62926
63671
|
// gateway/quarantine.ts
|
|
62927
63672
|
import { mkdirSync as mkdirSync15, writeFileSync as writeFileSync15 } from "node:fs";
|
|
62928
|
-
import { join as
|
|
63673
|
+
import { join as join22 } from "node:path";
|
|
62929
63674
|
var QUARANTINE_FILENAME = "quarantine.json";
|
|
62930
63675
|
function writeQuarantineMarker(telegramStateDir, reason, detail, nowFn = Date.now) {
|
|
62931
63676
|
mkdirSync15(telegramStateDir, { recursive: true, mode: 448 });
|
|
@@ -62935,7 +63680,7 @@ function writeQuarantineMarker(telegramStateDir, reason, detail, nowFn = Date.no
|
|
|
62935
63680
|
ts: nowFn(),
|
|
62936
63681
|
detail
|
|
62937
63682
|
};
|
|
62938
|
-
writeFileSync15(
|
|
63683
|
+
writeFileSync15(join22(telegramStateDir, QUARANTINE_FILENAME), JSON.stringify(marker) + `
|
|
62939
63684
|
`, "utf-8");
|
|
62940
63685
|
}
|
|
62941
63686
|
|
|
@@ -63954,9 +64699,9 @@ function defaultAddAccount(label, credentials, opts) {
|
|
|
63954
64699
|
// ../src/auth/broker/client.ts
|
|
63955
64700
|
init_protocol2();
|
|
63956
64701
|
import * as net3 from "node:net";
|
|
63957
|
-
import { homedir as
|
|
64702
|
+
import { homedir as homedir8 } from "node:os";
|
|
63958
64703
|
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
63959
|
-
import { join as
|
|
64704
|
+
import { join as join24 } from "node:path";
|
|
63960
64705
|
var DEFAULT_TIMEOUT_MS3 = 5000;
|
|
63961
64706
|
function reviveDate2(v) {
|
|
63962
64707
|
if (v == null)
|
|
@@ -63966,8 +64711,8 @@ function reviveDate2(v) {
|
|
|
63966
64711
|
const d = new Date(v);
|
|
63967
64712
|
return Number.isNaN(d.getTime()) ? null : d;
|
|
63968
64713
|
}
|
|
63969
|
-
function operatorSocketPath2(home2 =
|
|
63970
|
-
return
|
|
64714
|
+
function operatorSocketPath2(home2 = homedir8()) {
|
|
64715
|
+
return join24(home2, ".switchroom", "state", "auth-broker-operator", "sock");
|
|
63971
64716
|
}
|
|
63972
64717
|
function resolveAuthBrokerSocketPath2(opts) {
|
|
63973
64718
|
if (opts?.socket)
|
|
@@ -64281,13 +65026,13 @@ class AuthBrokerClient2 {
|
|
|
64281
65026
|
init_loader();
|
|
64282
65027
|
init_resolver();
|
|
64283
65028
|
init_vault();
|
|
64284
|
-
import { existsSync as
|
|
65029
|
+
import { existsSync as existsSync17 } from "node:fs";
|
|
64285
65030
|
var DEFAULT_VOICE_API_KEY_REF = "vault:openai/api-key";
|
|
64286
65031
|
function tryDirectVaultRead(ref, config, passphrase) {
|
|
64287
65032
|
if (!passphrase)
|
|
64288
65033
|
return null;
|
|
64289
65034
|
const vaultPath = resolvePath(config.vault?.path ?? "~/.switchroom/vault.enc");
|
|
64290
|
-
if (!
|
|
65035
|
+
if (!existsSync17(vaultPath))
|
|
64291
65036
|
return null;
|
|
64292
65037
|
try {
|
|
64293
65038
|
const secrets = openVault(passphrase, vaultPath);
|
|
@@ -64343,14 +65088,14 @@ async function materializeVoiceKey(opts = {}, logger2 = (line) => process.stderr
|
|
|
64343
65088
|
init_loader();
|
|
64344
65089
|
init_resolver();
|
|
64345
65090
|
init_vault();
|
|
64346
|
-
import { existsSync as
|
|
65091
|
+
import { existsSync as existsSync18 } from "node:fs";
|
|
64347
65092
|
var VOICE_SIDECAR_TOKEN_KEY = "voice/sidecar-token";
|
|
64348
65093
|
var DEFAULT_VOICE_SIDECAR_TOKEN_REF = `vault:${VOICE_SIDECAR_TOKEN_KEY}`;
|
|
64349
65094
|
function tryDirectVaultRead2(ref, config, passphrase) {
|
|
64350
65095
|
if (!passphrase)
|
|
64351
65096
|
return null;
|
|
64352
65097
|
const vaultPath = resolvePath(config.vault?.path ?? "~/.switchroom/vault.enc");
|
|
64353
|
-
if (!
|
|
65098
|
+
if (!existsSync18(vaultPath))
|
|
64354
65099
|
return null;
|
|
64355
65100
|
try {
|
|
64356
65101
|
const secrets = openVault(passphrase, vaultPath);
|
|
@@ -64403,17 +65148,17 @@ async function materializeSidecarToken(opts = {}, logger2 = (line) => process.st
|
|
|
64403
65148
|
}
|
|
64404
65149
|
|
|
64405
65150
|
// ../src/setup/host-capabilities.ts
|
|
64406
|
-
import { existsSync as
|
|
65151
|
+
import { existsSync as existsSync19, mkdirSync as mkdirSync18, readFileSync as readFileSync19, writeFileSync as writeFileSync16 } from "node:fs";
|
|
64407
65152
|
init_paths();
|
|
64408
65153
|
function hostCapabilitiesPath() {
|
|
64409
65154
|
return resolveStatePath("host-capabilities.json");
|
|
64410
65155
|
}
|
|
64411
65156
|
function loadHostCapabilities() {
|
|
64412
65157
|
const path2 = hostCapabilitiesPath();
|
|
64413
|
-
if (!
|
|
65158
|
+
if (!existsSync19(path2))
|
|
64414
65159
|
return null;
|
|
64415
65160
|
try {
|
|
64416
|
-
const parsed = JSON.parse(
|
|
65161
|
+
const parsed = JSON.parse(readFileSync19(path2, "utf-8"));
|
|
64417
65162
|
if (parsed && typeof parsed === "object" && "voice" in parsed && typeof parsed.voice === "object") {
|
|
64418
65163
|
return parsed;
|
|
64419
65164
|
}
|
|
@@ -64521,16 +65266,16 @@ function resolveExhaustUntil(resetAtMs, now = Date.now()) {
|
|
|
64521
65266
|
|
|
64522
65267
|
// gateway/auth-add-flow.ts
|
|
64523
65268
|
import { execFileSync as execFileSync3 } from "node:child_process";
|
|
64524
|
-
import { existsSync as
|
|
64525
|
-
import { homedir as
|
|
64526
|
-
import { join as
|
|
65269
|
+
import { existsSync as existsSync21, mkdirSync as mkdirSync20, readFileSync as readFileSync21, readdirSync as readdirSync4, rmSync as rmSync3, statSync as statSync7, writeFileSync as writeFileSync18 } from "node:fs";
|
|
65270
|
+
import { homedir as homedir9 } from "node:os";
|
|
65271
|
+
import { join as join25 } from "node:path";
|
|
64527
65272
|
import { randomBytes as randomBytes5 } from "node:crypto";
|
|
64528
65273
|
|
|
64529
65274
|
// ../src/auth/manager.ts
|
|
64530
65275
|
import {
|
|
64531
|
-
readFileSync as
|
|
65276
|
+
readFileSync as readFileSync20,
|
|
64532
65277
|
readdirSync as readdirSync3,
|
|
64533
|
-
existsSync as
|
|
65278
|
+
existsSync as existsSync20,
|
|
64534
65279
|
writeFileSync as writeFileSync17,
|
|
64535
65280
|
mkdirSync as mkdirSync19,
|
|
64536
65281
|
mkdtempSync as mkdtempSync2,
|
|
@@ -64557,9 +65302,9 @@ function parseSetupTokenUrl(output) {
|
|
|
64557
65302
|
}
|
|
64558
65303
|
function readTokenFromCredentialsFile(credentialsFilePath) {
|
|
64559
65304
|
try {
|
|
64560
|
-
if (!
|
|
65305
|
+
if (!existsSync20(credentialsFilePath))
|
|
64561
65306
|
return null;
|
|
64562
|
-
const raw =
|
|
65307
|
+
const raw = readFileSync20(credentialsFilePath, "utf-8");
|
|
64563
65308
|
const parsed = JSON.parse(raw);
|
|
64564
65309
|
const token = parsed?.claudeAiOauth?.accessToken;
|
|
64565
65310
|
if (typeof token !== "string")
|
|
@@ -64617,9 +65362,9 @@ function makeAuthAddTmuxOps(tmuxBin = "tmux") {
|
|
|
64617
65362
|
};
|
|
64618
65363
|
}
|
|
64619
65364
|
var pendingAuthAddFlows = new Map;
|
|
64620
|
-
function pickScratchDir(label, home2 =
|
|
65365
|
+
function pickScratchDir(label, home2 = homedir9()) {
|
|
64621
65366
|
const suffix = randomBytes5(8).toString("hex");
|
|
64622
|
-
return
|
|
65367
|
+
return join25(home2, ".switchroom", "accounts", ".in-progress", `${label}-${suffix}`);
|
|
64623
65368
|
}
|
|
64624
65369
|
function cleanScratchDir(scratchDir) {
|
|
64625
65370
|
try {
|
|
@@ -64628,8 +65373,8 @@ function cleanScratchDir(scratchDir) {
|
|
|
64628
65373
|
}
|
|
64629
65374
|
var AUTH_TMUX_SESSION_FILE = ".auth-tmux-session";
|
|
64630
65375
|
function sweepOrphanSessions(home2, tmux, nowMs2 = Date.now()) {
|
|
64631
|
-
const inProgressDir =
|
|
64632
|
-
if (!
|
|
65376
|
+
const inProgressDir = join25(home2, ".switchroom", "accounts", ".in-progress");
|
|
65377
|
+
if (!existsSync21(inProgressDir))
|
|
64633
65378
|
return;
|
|
64634
65379
|
let entries;
|
|
64635
65380
|
try {
|
|
@@ -64639,16 +65384,16 @@ function sweepOrphanSessions(home2, tmux, nowMs2 = Date.now()) {
|
|
|
64639
65384
|
}
|
|
64640
65385
|
const tenMinMs = 10 * 60000;
|
|
64641
65386
|
for (const entry of entries) {
|
|
64642
|
-
const dir =
|
|
64643
|
-
const sessionFile =
|
|
64644
|
-
if (!
|
|
65387
|
+
const dir = join25(inProgressDir, entry);
|
|
65388
|
+
const sessionFile = join25(dir, AUTH_TMUX_SESSION_FILE);
|
|
65389
|
+
if (!existsSync21(sessionFile))
|
|
64645
65390
|
continue;
|
|
64646
65391
|
let fileContents;
|
|
64647
65392
|
let fileMtime;
|
|
64648
65393
|
try {
|
|
64649
65394
|
const stat = statSync7(sessionFile);
|
|
64650
65395
|
fileMtime = stat.mtimeMs;
|
|
64651
|
-
fileContents =
|
|
65396
|
+
fileContents = readFileSync21(sessionFile, "utf8").trim();
|
|
64652
65397
|
} catch {
|
|
64653
65398
|
continue;
|
|
64654
65399
|
}
|
|
@@ -64668,7 +65413,7 @@ async function startAccountAuthSession(label, opts = {}) {
|
|
|
64668
65413
|
if (process.env.SWITCHROOM_TMUX_SUPERVISOR !== "1" && !opts.tmuxOps) {
|
|
64669
65414
|
throw new Error('tmux supervisor required for /auth add: SWITCHROOM_TMUX_SUPERVISOR is not set to "1". ' + "Legacy pipe-based setup-token is unsupported (setup-token writes to /dev/tty, not stdout/stderr).");
|
|
64670
65415
|
}
|
|
64671
|
-
const home2 = opts.home ??
|
|
65416
|
+
const home2 = opts.home ?? homedir9();
|
|
64672
65417
|
const urlTimeoutMs = opts.urlTimeoutMs ?? 12000;
|
|
64673
65418
|
const agentName3 = opts.agentName ?? process.env.SWITCHROOM_AGENT_NAME ?? "gateway";
|
|
64674
65419
|
const tmux = opts.tmuxOps ?? makeAuthAddTmuxOps(opts.tmuxBin);
|
|
@@ -64680,7 +65425,7 @@ async function startAccountAuthSession(label, opts = {}) {
|
|
|
64680
65425
|
const tmuxSocket = `switchroom-${agentName3}`;
|
|
64681
65426
|
const tmuxSession = `auth-add-${label}-${hexSuffix}`.slice(0, 64);
|
|
64682
65427
|
try {
|
|
64683
|
-
writeFileSync18(
|
|
65428
|
+
writeFileSync18(join25(scratchDir, AUTH_TMUX_SESSION_FILE), `${tmuxSocket}
|
|
64684
65429
|
${tmuxSession}`, "utf8");
|
|
64685
65430
|
} catch {}
|
|
64686
65431
|
const sessionEnv = {
|
|
@@ -64729,7 +65474,7 @@ async function submitAccountAuthCode(flow3, code2, opts = {}) {
|
|
|
64729
65474
|
const pollIntervalMs = opts.pollIntervalMs ?? 250;
|
|
64730
65475
|
const pollTimeoutMs = opts.pollTimeoutMs ?? 300000;
|
|
64731
65476
|
const tmux = opts.tmuxOps ?? makeAuthAddTmuxOps();
|
|
64732
|
-
const credentialsPath =
|
|
65477
|
+
const credentialsPath = join25(flow3.scratchDir, ".credentials.json");
|
|
64733
65478
|
try {
|
|
64734
65479
|
tmux.send(flow3.tmuxSocket, flow3.tmuxSession, code2);
|
|
64735
65480
|
} catch (err) {
|
|
@@ -64739,11 +65484,11 @@ async function submitAccountAuthCode(flow3, code2, opts = {}) {
|
|
|
64739
65484
|
const deadline = Date.now() + pollTimeoutMs;
|
|
64740
65485
|
while (Date.now() < deadline) {
|
|
64741
65486
|
await new Promise((r) => setTimeout(r, pollIntervalMs));
|
|
64742
|
-
if (
|
|
65487
|
+
if (existsSync21(credentialsPath)) {
|
|
64743
65488
|
const token = readTokenFromCredentialsFile(credentialsPath);
|
|
64744
65489
|
if (token) {
|
|
64745
65490
|
try {
|
|
64746
|
-
const raw =
|
|
65491
|
+
const raw = readFileSync21(credentialsPath, "utf-8");
|
|
64747
65492
|
const parsed = JSON.parse(raw);
|
|
64748
65493
|
if (parsed.claudeAiOauth?.accessToken) {
|
|
64749
65494
|
tmux.killSession(flow3.tmuxSocket, flow3.tmuxSession);
|
|
@@ -64753,7 +65498,7 @@ async function submitAccountAuthCode(flow3, code2, opts = {}) {
|
|
|
64753
65498
|
}
|
|
64754
65499
|
}
|
|
64755
65500
|
if (!tmux.hasSession(flow3.tmuxSocket, flow3.tmuxSession)) {
|
|
64756
|
-
if (!
|
|
65501
|
+
if (!existsSync21(credentialsPath)) {
|
|
64757
65502
|
cleanScratchDir(flow3.scratchDir);
|
|
64758
65503
|
throw new Error("claude setup-token exited without writing credentials \u2014 the code may be invalid or expired");
|
|
64759
65504
|
}
|
|
@@ -65205,28 +65950,6 @@ function autoClassifyMidTurnInbound(i) {
|
|
|
65205
65950
|
|
|
65206
65951
|
// operator-events.ts
|
|
65207
65952
|
init_format();
|
|
65208
|
-
|
|
65209
|
-
// raw-error-scrub.ts
|
|
65210
|
-
function stripRawErrorBytes(raw) {
|
|
65211
|
-
if (typeof raw !== "string" || raw.length === 0)
|
|
65212
|
-
return "";
|
|
65213
|
-
let s = raw;
|
|
65214
|
-
s = s.replace(/API Error:?\s*\d*\s*/gi, " ");
|
|
65215
|
-
s = s.replace(/\bb'[^']*'/g, " ");
|
|
65216
|
-
s = s.replace(/\bb"[^"]*"/g, " ");
|
|
65217
|
-
s = s.replace(/[\u00b7\-\s]*\{[\s\S]*?["']type["']\s*:\s*["']error["'][\s\S]*$/i, " ");
|
|
65218
|
-
s = s.replace(/^\s*\{[\s\S]*\}\s*$/g, " ");
|
|
65219
|
-
s = s.replace(/\s+/g, " ").replace(/[\u00b7:\-\s]+$/g, "").replace(/^[\u00b7:\-\s]+/g, "").trim();
|
|
65220
|
-
return s;
|
|
65221
|
-
}
|
|
65222
|
-
function extractRequestId(raw) {
|
|
65223
|
-
if (typeof raw !== "string" || raw.length === 0)
|
|
65224
|
-
return;
|
|
65225
|
-
const m = raw.match(/["']request[_-]?id["']\s*:\s*["']([A-Za-z0-9._-]+)["']/i) ?? raw.match(/\brequest[_-]?id[=:]\s*([A-Za-z0-9._-]+)/i);
|
|
65226
|
-
return m ? m[1] : undefined;
|
|
65227
|
-
}
|
|
65228
|
-
|
|
65229
|
-
// operator-events.ts
|
|
65230
65953
|
function renderOperatorEvent(ev) {
|
|
65231
65954
|
const agent = escapeMarkdown(ev.agent);
|
|
65232
65955
|
const detail = escapeMarkdown(stripRawErrorBytes(ev.detail));
|
|
@@ -65414,15 +66137,15 @@ function renderOperatorEvent(ev) {
|
|
|
65414
66137
|
};
|
|
65415
66138
|
}
|
|
65416
66139
|
}
|
|
65417
|
-
var
|
|
65418
|
-
var
|
|
65419
|
-
function shouldEmitOperatorEvent(agent, kind, now = Date.now(), cooldownMs =
|
|
66140
|
+
var DEFAULT_OPERATOR_EVENT_COOLDOWN_MS2 = 5 * 60000;
|
|
66141
|
+
var cooldownMap2 = new Map;
|
|
66142
|
+
function shouldEmitOperatorEvent(agent, kind, now = Date.now(), cooldownMs = DEFAULT_OPERATOR_EVENT_COOLDOWN_MS2) {
|
|
65420
66143
|
const key = `${agent}:${kind}`;
|
|
65421
|
-
const last =
|
|
66144
|
+
const last = cooldownMap2.get(key);
|
|
65422
66145
|
if (last != null && now - last < cooldownMs) {
|
|
65423
66146
|
return false;
|
|
65424
66147
|
}
|
|
65425
|
-
|
|
66148
|
+
cooldownMap2.set(key, now);
|
|
65426
66149
|
return true;
|
|
65427
66150
|
}
|
|
65428
66151
|
|
|
@@ -65433,290 +66156,6 @@ function recordOperatorEvent(event, now = Date.now()) {
|
|
|
65433
66156
|
store.set(event.agent, { event, storedAt: now });
|
|
65434
66157
|
}
|
|
65435
66158
|
|
|
65436
|
-
// model-unavailable.ts
|
|
65437
|
-
init_quota_check();
|
|
65438
|
-
init_card_format();
|
|
65439
|
-
var transientUpstreamSignals = [
|
|
65440
|
-
"not your usage limit",
|
|
65441
|
-
"not your account",
|
|
65442
|
-
"not your account's",
|
|
65443
|
-
"temporarily limiting requests",
|
|
65444
|
-
"temporarily rate",
|
|
65445
|
-
"server is temporarily",
|
|
65446
|
-
"would exceed your account\u2019s rate limit",
|
|
65447
|
-
"would exceed your account's rate limit"
|
|
65448
|
-
];
|
|
65449
|
-
function isTransientUpstreamSignal(text4) {
|
|
65450
|
-
if (typeof text4 !== "string" || text4.length === 0)
|
|
65451
|
-
return false;
|
|
65452
|
-
const sample = text4.length > 16384 ? text4.slice(0, 16384) : text4;
|
|
65453
|
-
const lower = sample.toLowerCase();
|
|
65454
|
-
return transientUpstreamSignals.some((s) => lower.includes(s));
|
|
65455
|
-
}
|
|
65456
|
-
var litellmProxyLocal429Signals = [
|
|
65457
|
-
"deployment over user-defined ratelimit",
|
|
65458
|
-
"model rate limit exceeded. tpm limit",
|
|
65459
|
-
"model rate limit exceeded. rpm limit",
|
|
65460
|
-
"deployment over defined rpm limit",
|
|
65461
|
-
"no deployments available for selected model",
|
|
65462
|
-
"litellm rate limit handler",
|
|
65463
|
-
"crossed tpm / rpm",
|
|
65464
|
-
"max parallel request limit reached"
|
|
65465
|
-
];
|
|
65466
|
-
var litellmV3LimiterSignalPair = ["rate limit exceeded for ", "limit type:"];
|
|
65467
|
-
function isLitellmProxyLocal429(text4) {
|
|
65468
|
-
if (typeof text4 !== "string" || text4.length === 0)
|
|
65469
|
-
return false;
|
|
65470
|
-
const sample = text4.length > 16384 ? text4.slice(0, 16384) : text4;
|
|
65471
|
-
const lower = sample.toLowerCase();
|
|
65472
|
-
if (litellmProxyLocal429Signals.some((s) => lower.includes(s)))
|
|
65473
|
-
return true;
|
|
65474
|
-
return litellmV3LimiterSignalPair.every((s) => lower.includes(s));
|
|
65475
|
-
}
|
|
65476
|
-
function parseLitellmLimitDetail(text4, parseTimeNow = new Date) {
|
|
65477
|
-
const empty2 = { limitType: null, limit: null, currentUsage: null, resetAtMs: null };
|
|
65478
|
-
if (typeof text4 !== "string" || text4.length === 0)
|
|
65479
|
-
return empty2;
|
|
65480
|
-
const sample = text4.length > 16384 ? text4.slice(0, 16384) : text4;
|
|
65481
|
-
const lower = sample.toLowerCase();
|
|
65482
|
-
let limitType = null;
|
|
65483
|
-
let limit = null;
|
|
65484
|
-
const eqLimit = lower.match(/\b([tr]pm)[ _]limit[=:]\s*(\d+)/);
|
|
65485
|
-
if (eqLimit) {
|
|
65486
|
-
limitType = eqLimit[1];
|
|
65487
|
-
limit = Number(eqLimit[2]);
|
|
65488
|
-
}
|
|
65489
|
-
if (limitType == null) {
|
|
65490
|
-
const v3Type = lower.match(/limit type:\s*(tokens|requests|max_parallel_requests)/);
|
|
65491
|
-
if (v3Type)
|
|
65492
|
-
limitType = v3Type[1];
|
|
65493
|
-
}
|
|
65494
|
-
if (limit == null) {
|
|
65495
|
-
const v3Limit = lower.match(/current limit:\s*(\d+)/);
|
|
65496
|
-
if (v3Limit)
|
|
65497
|
-
limit = Number(v3Limit[1]);
|
|
65498
|
-
}
|
|
65499
|
-
let currentUsage = null;
|
|
65500
|
-
const usage = lower.match(/current usage=(\d+)/);
|
|
65501
|
-
if (usage)
|
|
65502
|
-
currentUsage = Number(usage[1]);
|
|
65503
|
-
let resetAtMs = null;
|
|
65504
|
-
const resetsAt = sample.match(/limit resets at:\s*(\d{4}-\d{2}-\d{2}) (\d{2}:\d{2}:\d{2}) UTC/i);
|
|
65505
|
-
if (resetsAt) {
|
|
65506
|
-
const d = new Date(`${resetsAt[1]}T${resetsAt[2]}Z`);
|
|
65507
|
-
if (!Number.isNaN(d.getTime()))
|
|
65508
|
-
resetAtMs = d.getTime();
|
|
65509
|
-
}
|
|
65510
|
-
if (resetAtMs == null) {
|
|
65511
|
-
const tryAgain = lower.match(/try again in\s+(\d+(?:\.\d+)?)\s*seconds/);
|
|
65512
|
-
if (tryAgain) {
|
|
65513
|
-
const secs = Number(tryAgain[1]);
|
|
65514
|
-
if (Number.isFinite(secs) && secs > 0 && secs < 7 * 24 * 3600) {
|
|
65515
|
-
resetAtMs = parseTimeNow.getTime() + Math.round(secs * 1000);
|
|
65516
|
-
}
|
|
65517
|
-
}
|
|
65518
|
-
}
|
|
65519
|
-
return {
|
|
65520
|
-
limitType,
|
|
65521
|
-
limit: limit != null && Number.isFinite(limit) ? limit : null,
|
|
65522
|
-
currentUsage: currentUsage != null && Number.isFinite(currentUsage) ? currentUsage : null,
|
|
65523
|
-
resetAtMs
|
|
65524
|
-
};
|
|
65525
|
-
}
|
|
65526
|
-
function detectModelUnavailable(stderr) {
|
|
65527
|
-
if (typeof stderr !== "string" || stderr.length === 0)
|
|
65528
|
-
return null;
|
|
65529
|
-
const sample = stderr.length > 16384 ? stderr.slice(0, 16384) : stderr;
|
|
65530
|
-
const lower = sample.toLowerCase();
|
|
65531
|
-
if (transientUpstreamSignals.some((s) => lower.includes(s))) {
|
|
65532
|
-
const resetAt = parseResetTime(sample);
|
|
65533
|
-
return resetAt !== undefined ? { kind: "overload", resetAt, raw: stderr } : { kind: "overload", raw: stderr };
|
|
65534
|
-
}
|
|
65535
|
-
if (isLitellmProxyLocal429(sample)) {
|
|
65536
|
-
const resetAt = parseResetTime(sample);
|
|
65537
|
-
return resetAt !== undefined ? { kind: "overload", resetAt, raw: stderr } : { kind: "overload", raw: stderr };
|
|
65538
|
-
}
|
|
65539
|
-
const quotaSignals = [
|
|
65540
|
-
"out of extra usage",
|
|
65541
|
-
"extra usage",
|
|
65542
|
-
"credit_balance_too_low",
|
|
65543
|
-
"credit balance too low",
|
|
65544
|
-
"usage limit",
|
|
65545
|
-
"usage_limit",
|
|
65546
|
-
"quota exhausted",
|
|
65547
|
-
"quota_exhausted",
|
|
65548
|
-
"plan limit",
|
|
65549
|
-
"subscription limit",
|
|
65550
|
-
"hit your limit",
|
|
65551
|
-
"hit the limit",
|
|
65552
|
-
"session limit",
|
|
65553
|
-
"session cap"
|
|
65554
|
-
];
|
|
65555
|
-
if (quotaSignals.some((s) => lower.includes(s))) {
|
|
65556
|
-
const resetAt = parseResetTime(sample);
|
|
65557
|
-
return resetAt !== undefined ? { kind: "quota_exhausted", resetAt, raw: stderr } : { kind: "quota_exhausted", raw: stderr };
|
|
65558
|
-
}
|
|
65559
|
-
const overloadSignals = [
|
|
65560
|
-
"overloaded_error",
|
|
65561
|
-
"overloaded",
|
|
65562
|
-
"rate_limit_error",
|
|
65563
|
-
"rate limit",
|
|
65564
|
-
"rate-limited",
|
|
65565
|
-
"http 429",
|
|
65566
|
-
'"status":429',
|
|
65567
|
-
"status: 429",
|
|
65568
|
-
" 429 ",
|
|
65569
|
-
"503 service",
|
|
65570
|
-
"service unavailable",
|
|
65571
|
-
'"status":529',
|
|
65572
|
-
"http 529",
|
|
65573
|
-
" 529 "
|
|
65574
|
-
];
|
|
65575
|
-
if (overloadSignals.some((s) => lower.includes(s))) {
|
|
65576
|
-
const resetAt = parseResetTime(sample);
|
|
65577
|
-
return resetAt !== undefined ? { kind: "overload", resetAt, raw: stderr } : { kind: "overload", raw: stderr };
|
|
65578
|
-
}
|
|
65579
|
-
const networkSignals = [
|
|
65580
|
-
"econnrefused",
|
|
65581
|
-
"econnreset",
|
|
65582
|
-
"etimedout",
|
|
65583
|
-
"enotfound",
|
|
65584
|
-
"eai_again",
|
|
65585
|
-
"fetch failed",
|
|
65586
|
-
"network error",
|
|
65587
|
-
"socket hang up",
|
|
65588
|
-
"request timed out",
|
|
65589
|
-
"connection refused",
|
|
65590
|
-
"getaddrinfo"
|
|
65591
|
-
];
|
|
65592
|
-
if (networkSignals.some((s) => lower.includes(s))) {
|
|
65593
|
-
return { kind: "network", raw: stderr };
|
|
65594
|
-
}
|
|
65595
|
-
return null;
|
|
65596
|
-
}
|
|
65597
|
-
function parseResetTime(text4, parseTimeNow = new Date) {
|
|
65598
|
-
const lower = text4.toLowerCase();
|
|
65599
|
-
const retryAfter = lower.match(/retry[\s-]*after[:\s]+(\d+)\s*(seconds?|s\b|minutes?|m\b|hours?|h\b)?/);
|
|
65600
|
-
if (retryAfter) {
|
|
65601
|
-
const n = Number(retryAfter[1]);
|
|
65602
|
-
if (Number.isFinite(n) && n > 0 && n < 7 * 24 * 3600) {
|
|
65603
|
-
const unit = (retryAfter[2] ?? "seconds").toLowerCase();
|
|
65604
|
-
const ms = unit.startsWith("h") ? n * 3600000 : unit.startsWith("m") ? n * 60000 : n * 1000;
|
|
65605
|
-
return new Date(parseTimeNow.getTime() + ms);
|
|
65606
|
-
}
|
|
65607
|
-
}
|
|
65608
|
-
const relReset = lower.match(/resets?\s+in\s+([0-9hms\s]+)/);
|
|
65609
|
-
if (relReset) {
|
|
65610
|
-
const ms = parseRelativeDuration(relReset[1]);
|
|
65611
|
-
if (ms != null)
|
|
65612
|
-
return new Date(parseTimeNow.getTime() + ms);
|
|
65613
|
-
}
|
|
65614
|
-
const iso = text4.match(/\b\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:?\d{2})?\b/);
|
|
65615
|
-
if (iso) {
|
|
65616
|
-
const d = new Date(iso[0]);
|
|
65617
|
-
if (!Number.isNaN(d.getTime()))
|
|
65618
|
-
return d;
|
|
65619
|
-
}
|
|
65620
|
-
const calReset = text4.match(/resets?\s+(?:at\s+)?([A-Z][a-z]{2,8}\s+\d{1,2}(?:,?\s*(?:\d{1,2}(?::\d{2})?\s*(?:am|pm|AM|PM)?))?)/);
|
|
65621
|
-
if (calReset) {
|
|
65622
|
-
const candidate = `${calReset[1]} ${parseTimeNow.getUTCFullYear()}`;
|
|
65623
|
-
const d = new Date(candidate);
|
|
65624
|
-
if (!Number.isNaN(d.getTime()))
|
|
65625
|
-
return d;
|
|
65626
|
-
}
|
|
65627
|
-
const timeOnly = text4.match(/resets?\s+(?:at\s+)?(?!(?:jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec)[a-z]*\b)(\d{1,2})(?::(\d{2}))?\s*(am|pm)?\s*(?:\(([^)]+)\))?/i);
|
|
65628
|
-
if (timeOnly) {
|
|
65629
|
-
const d = resolveNextWallClock(Number(timeOnly[1]), timeOnly[2] ? Number(timeOnly[2]) : 0, timeOnly[3]?.toLowerCase(), timeOnly[4]?.trim(), parseTimeNow);
|
|
65630
|
-
if (d != null)
|
|
65631
|
-
return d;
|
|
65632
|
-
}
|
|
65633
|
-
return;
|
|
65634
|
-
}
|
|
65635
|
-
function resolveNextWallClock(hour12or24, minute, ampm, tz, nowDate) {
|
|
65636
|
-
let hour = hour12or24;
|
|
65637
|
-
if (ampm === "pm" && hour < 12)
|
|
65638
|
-
hour += 12;
|
|
65639
|
-
if (ampm === "am" && hour === 12)
|
|
65640
|
-
hour = 0;
|
|
65641
|
-
if (!Number.isFinite(hour) || hour > 23 || hour < 0)
|
|
65642
|
-
return;
|
|
65643
|
-
if (!Number.isFinite(minute) || minute > 59 || minute < 0)
|
|
65644
|
-
return;
|
|
65645
|
-
const nowMs2 = nowDate.getTime();
|
|
65646
|
-
const base = new Date(nowMs2);
|
|
65647
|
-
for (let dayOffset = 0;dayOffset <= 2; dayOffset++) {
|
|
65648
|
-
const dateParts = tzDateParts(new Date(nowMs2 + dayOffset * 86400000), tz);
|
|
65649
|
-
if (dateParts == null)
|
|
65650
|
-
return;
|
|
65651
|
-
const epoch = wallClockToEpoch(dateParts.year, dateParts.month, dateParts.day, hour, minute, tz);
|
|
65652
|
-
if (epoch != null && epoch > nowMs2)
|
|
65653
|
-
return new Date(epoch);
|
|
65654
|
-
}
|
|
65655
|
-
return;
|
|
65656
|
-
}
|
|
65657
|
-
function tzDateParts(d, tz) {
|
|
65658
|
-
if (!tz) {
|
|
65659
|
-
return { year: d.getUTCFullYear(), month: d.getUTCMonth(), day: d.getUTCDate() };
|
|
65660
|
-
}
|
|
65661
|
-
try {
|
|
65662
|
-
const fmt = new Intl.DateTimeFormat("en-US", {
|
|
65663
|
-
timeZone: tz,
|
|
65664
|
-
year: "numeric",
|
|
65665
|
-
month: "2-digit",
|
|
65666
|
-
day: "2-digit"
|
|
65667
|
-
});
|
|
65668
|
-
const parts = Object.fromEntries(fmt.formatToParts(d).filter((p) => p.type !== "literal").map((p) => [p.type, p.value]));
|
|
65669
|
-
return {
|
|
65670
|
-
year: Number(parts.year),
|
|
65671
|
-
month: Number(parts.month) - 1,
|
|
65672
|
-
day: Number(parts.day)
|
|
65673
|
-
};
|
|
65674
|
-
} catch {
|
|
65675
|
-
return null;
|
|
65676
|
-
}
|
|
65677
|
-
}
|
|
65678
|
-
function wallClockToEpoch(year, month, day, hour, minute, tz) {
|
|
65679
|
-
const asUtc = Date.UTC(year, month, day, hour, minute, 0);
|
|
65680
|
-
if (!tz)
|
|
65681
|
-
return asUtc;
|
|
65682
|
-
try {
|
|
65683
|
-
const fmt = new Intl.DateTimeFormat("en-US", {
|
|
65684
|
-
timeZone: tz,
|
|
65685
|
-
year: "numeric",
|
|
65686
|
-
month: "2-digit",
|
|
65687
|
-
day: "2-digit",
|
|
65688
|
-
hour: "2-digit",
|
|
65689
|
-
minute: "2-digit",
|
|
65690
|
-
second: "2-digit",
|
|
65691
|
-
hour12: false
|
|
65692
|
-
});
|
|
65693
|
-
const parts = Object.fromEntries(fmt.formatToParts(new Date(asUtc)).filter((p) => p.type !== "literal").map((p) => [p.type, p.value]));
|
|
65694
|
-
const shown = Date.UTC(Number(parts.year), Number(parts.month) - 1, Number(parts.day), Number(parts.hour) % 24, Number(parts.minute), Number(parts.second));
|
|
65695
|
-
const offset = shown - asUtc;
|
|
65696
|
-
return asUtc - offset;
|
|
65697
|
-
} catch {
|
|
65698
|
-
return null;
|
|
65699
|
-
}
|
|
65700
|
-
}
|
|
65701
|
-
function parseRelativeDuration(s) {
|
|
65702
|
-
let total = 0;
|
|
65703
|
-
let matched = false;
|
|
65704
|
-
const re = /(\d+)\s*(h|hours?|m|minutes?|s|seconds?)/g;
|
|
65705
|
-
let m;
|
|
65706
|
-
while ((m = re.exec(s)) != null) {
|
|
65707
|
-
matched = true;
|
|
65708
|
-
const n = Number(m[1]);
|
|
65709
|
-
const unit = m[2].toLowerCase();
|
|
65710
|
-
if (unit.startsWith("h"))
|
|
65711
|
-
total += n * 3600000;
|
|
65712
|
-
else if (unit.startsWith("m"))
|
|
65713
|
-
total += n * 60000;
|
|
65714
|
-
else
|
|
65715
|
-
total += n * 1000;
|
|
65716
|
-
}
|
|
65717
|
-
return matched && total > 0 ? total : null;
|
|
65718
|
-
}
|
|
65719
|
-
|
|
65720
66159
|
// throttle-tier.ts
|
|
65721
66160
|
init_card_format();
|
|
65722
66161
|
init_quota_check();
|
|
@@ -65776,72 +66215,6 @@ function renderThrottleEscalationNotice(opts) {
|
|
|
65776
66215
|
${tail}`;
|
|
65777
66216
|
}
|
|
65778
66217
|
|
|
65779
|
-
// operator-events.ts
|
|
65780
|
-
init_format();
|
|
65781
|
-
function classifyClaudeError(raw) {
|
|
65782
|
-
try {
|
|
65783
|
-
return classifyInner(raw);
|
|
65784
|
-
} catch {
|
|
65785
|
-
return "unknown-4xx";
|
|
65786
|
-
}
|
|
65787
|
-
}
|
|
65788
|
-
function classifyInner(raw) {
|
|
65789
|
-
if (raw == null)
|
|
65790
|
-
return "unknown-4xx";
|
|
65791
|
-
const obj = typeof raw === "object" ? raw : {};
|
|
65792
|
-
const errorType = extractString(obj, "error_type") ?? extractString(obj, "type") ?? extractString(getNestedObj(obj, "error"), "type") ?? "";
|
|
65793
|
-
const errorCode = extractString(obj, "code") ?? extractString(getNestedObj(obj, "error"), "code") ?? "";
|
|
65794
|
-
const message = extractString(obj, "message") ?? extractString(getNestedObj(obj, "error"), "message") ?? (typeof raw === "string" ? raw : "") ?? "";
|
|
65795
|
-
const status = extractNumber(obj, "status") ?? extractNumber(obj, "statusCode") ?? extractNumber(obj, "status_code") ?? null;
|
|
65796
|
-
const sdkCode = extractString(obj, "error_code") ?? "";
|
|
65797
|
-
if (errorType === "authentication_error" || errorCode === "authentication_error" || sdkCode === "authentication_error" || message.toLowerCase().includes("authentication_error")) {
|
|
65798
|
-
const msg = message.toLowerCase();
|
|
65799
|
-
if (msg.includes("expired") || msg.includes("refresh")) {
|
|
65800
|
-
return "credentials-expired";
|
|
65801
|
-
}
|
|
65802
|
-
return "credentials-invalid";
|
|
65803
|
-
}
|
|
65804
|
-
if (errorType === "invalid_api_key" || errorCode === "invalid_api_key" || sdkCode === "invalid_api_key" || message.toLowerCase().includes("invalid_api_key") || message.toLowerCase().includes("invalid api key")) {
|
|
65805
|
-
return "credentials-invalid";
|
|
65806
|
-
}
|
|
65807
|
-
if (errorType === "credit_balance_too_low" || errorCode === "credit_balance_too_low" || sdkCode === "credit_balance_too_low" || message.toLowerCase().includes("credit_balance_too_low") || message.toLowerCase().includes("credit balance")) {
|
|
65808
|
-
return "credit-exhausted";
|
|
65809
|
-
}
|
|
65810
|
-
if (errorType === "rate_limit_error" || errorCode === "rate_limit_error" || sdkCode === "rate_limit_error" || message.toLowerCase().includes("rate_limit_error") || message.toLowerCase().includes("rate limit")) {
|
|
65811
|
-
return "rate-limited";
|
|
65812
|
-
}
|
|
65813
|
-
if (errorType === "overloaded_error" || errorCode === "overloaded_error" || sdkCode === "overloaded_error" || message.toLowerCase().includes("overloaded_error") || message.toLowerCase().includes("overloaded")) {
|
|
65814
|
-
return "rate-limited";
|
|
65815
|
-
}
|
|
65816
|
-
if (errorType === "agent-crashed" || errorCode === "agent-crashed") {
|
|
65817
|
-
return "agent-crashed";
|
|
65818
|
-
}
|
|
65819
|
-
if (errorType === "agent-restarted-unexpectedly" || errorCode === "agent-restarted-unexpectedly") {
|
|
65820
|
-
return "agent-restarted-unexpectedly";
|
|
65821
|
-
}
|
|
65822
|
-
if (status != null) {
|
|
65823
|
-
if (status >= 400 && status < 500)
|
|
65824
|
-
return "unknown-4xx";
|
|
65825
|
-
if (status >= 500 && status < 600)
|
|
65826
|
-
return "unknown-5xx";
|
|
65827
|
-
}
|
|
65828
|
-
return "unknown-4xx";
|
|
65829
|
-
}
|
|
65830
|
-
function extractString(obj, key) {
|
|
65831
|
-
const v = obj[key];
|
|
65832
|
-
return typeof v === "string" && v.length > 0 ? v : null;
|
|
65833
|
-
}
|
|
65834
|
-
function extractNumber(obj, key) {
|
|
65835
|
-
const v = obj[key];
|
|
65836
|
-
return typeof v === "number" ? v : null;
|
|
65837
|
-
}
|
|
65838
|
-
function getNestedObj(obj, key) {
|
|
65839
|
-
const v = obj[key];
|
|
65840
|
-
return typeof v === "object" && v != null ? v : {};
|
|
65841
|
-
}
|
|
65842
|
-
var DEFAULT_OPERATOR_EVENT_COOLDOWN_MS2 = 5 * 60000;
|
|
65843
|
-
var cooldownMap2 = new Map;
|
|
65844
|
-
|
|
65845
66218
|
// llm-error-present.ts
|
|
65846
66219
|
function extractModel(raw) {
|
|
65847
66220
|
const m = raw.match(/["']?model["']?\s*[=:]\s*["']?((?:claude|sr)[A-Za-z0-9._-]+)/i);
|
|
@@ -67342,6 +67715,35 @@ function richMessage2(markdown) {
|
|
|
67342
67715
|
return { markdown };
|
|
67343
67716
|
}
|
|
67344
67717
|
|
|
67718
|
+
// gateway/redelivery-decision.ts
|
|
67719
|
+
var REDELIVERY_PREFIX = "Recovered from an interrupted turn:";
|
|
67720
|
+
function decideRedeliverCapture(input) {
|
|
67721
|
+
if (input.willBeResumed)
|
|
67722
|
+
return { capture: false, skipReason: "will-be-resumed" };
|
|
67723
|
+
if (!input.hasSessionId)
|
|
67724
|
+
return { capture: false, skipReason: "no-session-id" };
|
|
67725
|
+
return { capture: true };
|
|
67726
|
+
}
|
|
67727
|
+
function frameRedelivery(capturedText) {
|
|
67728
|
+
return `${REDELIVERY_PREFIX}
|
|
67729
|
+
|
|
67730
|
+
${capturedText.trim()}`;
|
|
67731
|
+
}
|
|
67732
|
+
function decideRedeliver(input) {
|
|
67733
|
+
const text4 = input.capturedText.trim();
|
|
67734
|
+
if (text4.length === 0)
|
|
67735
|
+
return { redeliver: false, skipReason: "empty-text" };
|
|
67736
|
+
if (!input.trailingIsText)
|
|
67737
|
+
return { redeliver: false, skipReason: "trailing-not-text" };
|
|
67738
|
+
if (input.alreadyRedelivered)
|
|
67739
|
+
return { redeliver: false, skipReason: "already-redelivered" };
|
|
67740
|
+
if (input.hasDeliveredText)
|
|
67741
|
+
return { redeliver: false, skipReason: "already-delivered" };
|
|
67742
|
+
if (input.ageMs > input.maxAgeMs)
|
|
67743
|
+
return { redeliver: false, skipReason: "stale" };
|
|
67744
|
+
return { redeliver: true, framedText: frameRedelivery(text4) };
|
|
67745
|
+
}
|
|
67746
|
+
|
|
67345
67747
|
// text-voice-scrub.ts
|
|
67346
67748
|
var NULL3 = "\x00";
|
|
67347
67749
|
var FENCE_PH2 = `${NULL3}VS_FENCE`;
|
|
@@ -68165,6 +68567,7 @@ function endsWithSilentMarker2(text4) {
|
|
|
68165
68567
|
return false;
|
|
68166
68568
|
return isSilentFlushMarker2(lines[lines.length - 1]);
|
|
68167
68569
|
}
|
|
68570
|
+
var FLUSH_SUBSTANTIVE_MIN_CHARS = 200;
|
|
68168
68571
|
function selectFlushDeliveryText2(blocks) {
|
|
68169
68572
|
const candidates = blocks.map((b) => b.trim()).filter((b) => b.length > 0);
|
|
68170
68573
|
if (candidates.length === 0)
|
|
@@ -68213,6 +68616,29 @@ function isTurnFlushSafetyEnabled(env = process.env) {
|
|
|
68213
68616
|
return true;
|
|
68214
68617
|
}
|
|
68215
68618
|
|
|
68619
|
+
// reply-owner-resolve.ts
|
|
68620
|
+
function latestEndedAccepted(candidates) {
|
|
68621
|
+
if (candidates.latestEndedTurnId == null)
|
|
68622
|
+
return false;
|
|
68623
|
+
const age = candidates.latestEndedAgeMs;
|
|
68624
|
+
const ttl = candidates.latestEndedTtlMs;
|
|
68625
|
+
if (age == null || ttl == null)
|
|
68626
|
+
return true;
|
|
68627
|
+
return age <= ttl;
|
|
68628
|
+
}
|
|
68629
|
+
function resolveReplyOwnerTurnId(candidates) {
|
|
68630
|
+
return candidates.liveTurnId ?? candidates.originTurnId ?? candidates.quotedTurnId ?? (latestEndedAccepted(candidates) ? candidates.latestEndedTurnId : null) ?? null;
|
|
68631
|
+
}
|
|
68632
|
+
function decideAnswerLatchSuppression(input) {
|
|
68633
|
+
if (input.superseded)
|
|
68634
|
+
return false;
|
|
68635
|
+
if (!input.replySubstantive)
|
|
68636
|
+
return false;
|
|
68637
|
+
if (!input.isLateReply)
|
|
68638
|
+
return false;
|
|
68639
|
+
return input.ownerAnswerDelivered;
|
|
68640
|
+
}
|
|
68641
|
+
|
|
68216
68642
|
// answer-ready-flush.ts
|
|
68217
68643
|
var ANSWER_READY_FLUSH_MS = 1000;
|
|
68218
68644
|
function resolveAnswerReadyFlushMs(env) {
|
|
@@ -68305,10 +68731,10 @@ function resolveAgentDirFromEnv() {
|
|
|
68305
68731
|
|
|
68306
68732
|
// active-reactions.ts
|
|
68307
68733
|
import { readFileSync as readFileSync22, writeFileSync as writeFileSync19, renameSync as renameSync7, existsSync as existsSync23, unlinkSync as unlinkSync11 } from "node:fs";
|
|
68308
|
-
import { join as
|
|
68734
|
+
import { join as join27 } from "node:path";
|
|
68309
68735
|
var ACTIVE_REACTIONS_FILENAME = ".active-reactions.json";
|
|
68310
68736
|
function reactionsPath(agentDir) {
|
|
68311
|
-
return
|
|
68737
|
+
return join27(agentDir, ACTIVE_REACTIONS_FILENAME);
|
|
68312
68738
|
}
|
|
68313
68739
|
function readActiveReactions(agentDir) {
|
|
68314
68740
|
const p = reactionsPath(agentDir);
|
|
@@ -68373,10 +68799,10 @@ function clearActiveReactions(agentDir) {
|
|
|
68373
68799
|
|
|
68374
68800
|
// active-reactions.ts
|
|
68375
68801
|
import { readFileSync as readFileSync23, writeFileSync as writeFileSync20, renameSync as renameSync8, existsSync as existsSync24, unlinkSync as unlinkSync12 } from "node:fs";
|
|
68376
|
-
import { join as
|
|
68802
|
+
import { join as join28 } from "node:path";
|
|
68377
68803
|
var ACTIVE_REACTIONS_FILENAME2 = ".active-reactions.json";
|
|
68378
68804
|
function reactionsPath2(agentDir) {
|
|
68379
|
-
return
|
|
68805
|
+
return join28(agentDir, ACTIVE_REACTIONS_FILENAME2);
|
|
68380
68806
|
}
|
|
68381
68807
|
function readActiveReactions2(agentDir) {
|
|
68382
68808
|
const p = reactionsPath2(agentDir);
|
|
@@ -69294,12 +69720,12 @@ async function approvalRecord(args, opts) {
|
|
|
69294
69720
|
|
|
69295
69721
|
// quota-check.ts
|
|
69296
69722
|
import { readFileSync as readFileSync24, existsSync as existsSync25 } from "fs";
|
|
69297
|
-
import { join as
|
|
69723
|
+
import { join as join29 } from "path";
|
|
69298
69724
|
var OAUTH_BETA2 = "oauth-2025-04-20";
|
|
69299
69725
|
var DEFAULT_USER_AGENT2 = "claude-cli/1.0.0 (external, cli)";
|
|
69300
69726
|
var DEFAULT_PROBE_MODEL2 = "claude-haiku-4-5-20251001";
|
|
69301
69727
|
function readOauthToken2(claudeConfigDir) {
|
|
69302
|
-
const tokenFile =
|
|
69728
|
+
const tokenFile = join29(claudeConfigDir, ".oauth-token");
|
|
69303
69729
|
if (!existsSync25(tokenFile))
|
|
69304
69730
|
return null;
|
|
69305
69731
|
try {
|
|
@@ -69699,18 +70125,32 @@ async function injectSlashCommand(agentName3, command, opts = {}) {
|
|
|
69699
70125
|
const socket = opts.socketName ?? defaultSocketName(agentName3);
|
|
69700
70126
|
const session = opts.sessionName ?? agentName3;
|
|
69701
70127
|
const settleMs = opts.settleMs ?? 2000;
|
|
69702
|
-
const
|
|
70128
|
+
const signalMode = !!(opts.successPattern || opts.errorPattern);
|
|
70129
|
+
const timeoutMs = opts.timeoutMs ?? (signalMode ? 8000 : 5000);
|
|
69703
70130
|
return withPaneLock(`${socket}:${session}`, () => injectSlashCommandWith(makeTmuxRunner(tmuxBin), {
|
|
69704
70131
|
socket,
|
|
69705
70132
|
session,
|
|
69706
70133
|
command: command.trim(),
|
|
69707
70134
|
settleMs,
|
|
69708
70135
|
timeoutMs,
|
|
69709
|
-
precondition: opts.precondition
|
|
70136
|
+
precondition: opts.precondition,
|
|
70137
|
+
successPattern: opts.successPattern,
|
|
70138
|
+
errorPattern: opts.errorPattern,
|
|
70139
|
+
settleBeforeSendMs: opts.settleBeforeSendMs
|
|
69710
70140
|
}));
|
|
69711
70141
|
}
|
|
69712
70142
|
async function injectSlashCommandWith(runner, args) {
|
|
69713
|
-
const {
|
|
70143
|
+
const {
|
|
70144
|
+
socket,
|
|
70145
|
+
session,
|
|
70146
|
+
command,
|
|
70147
|
+
settleMs,
|
|
70148
|
+
timeoutMs,
|
|
70149
|
+
precondition,
|
|
70150
|
+
successPattern,
|
|
70151
|
+
errorPattern,
|
|
70152
|
+
settleBeforeSendMs
|
|
70153
|
+
} = args;
|
|
69714
70154
|
let bareVerb;
|
|
69715
70155
|
try {
|
|
69716
70156
|
bareVerb = validateInjectCommand(command);
|
|
@@ -69751,6 +70191,17 @@ async function injectSlashCommandWith(runner, args) {
|
|
|
69751
70191
|
errorMessage: "inject precondition returned false at write time; send aborted " + "(no keys sent)."
|
|
69752
70192
|
};
|
|
69753
70193
|
}
|
|
70194
|
+
if (settleBeforeSendMs && settleBeforeSendMs > 0) {
|
|
70195
|
+
const settleStart = Date.now();
|
|
70196
|
+
let prevSettle = runner.capture(socket, session) ?? "";
|
|
70197
|
+
while (Date.now() - settleStart < settleBeforeSendMs) {
|
|
70198
|
+
await sleep(POLL_INTERVAL_MS2);
|
|
70199
|
+
const cur = runner.capture(socket, session) ?? "";
|
|
70200
|
+
if (cur === prevSettle)
|
|
70201
|
+
break;
|
|
70202
|
+
prevSettle = cur;
|
|
70203
|
+
}
|
|
70204
|
+
}
|
|
69754
70205
|
const before = runner.capture(socket, session) ?? "";
|
|
69755
70206
|
try {
|
|
69756
70207
|
runner.send(socket, session, ["send-keys", "-l", command]);
|
|
@@ -69769,9 +70220,23 @@ async function injectSlashCommandWith(runner, args) {
|
|
|
69769
70220
|
const start = Date.now();
|
|
69770
70221
|
let last = before;
|
|
69771
70222
|
let stableSince = null;
|
|
70223
|
+
const signalMode = !!(successPattern || errorPattern);
|
|
69772
70224
|
while (Date.now() - start < timeoutMs) {
|
|
69773
70225
|
await sleep(POLL_INTERVAL_MS2);
|
|
69774
70226
|
const cur = runner.capture(socket, session) ?? "";
|
|
70227
|
+
if (signalMode) {
|
|
70228
|
+
last = cur;
|
|
70229
|
+
if (cur !== before) {
|
|
70230
|
+
const { output: region } = diffPane(before, cur, command);
|
|
70231
|
+
const regionLines = region.split(`
|
|
70232
|
+
`).map((l) => l.trim());
|
|
70233
|
+
if (errorPattern && regionLines.some((l) => errorPattern.test(l)))
|
|
70234
|
+
break;
|
|
70235
|
+
if (successPattern && regionLines.some((l) => successPattern.test(l)))
|
|
70236
|
+
break;
|
|
70237
|
+
}
|
|
70238
|
+
continue;
|
|
70239
|
+
}
|
|
69775
70240
|
if (cur === last && cur !== before) {
|
|
69776
70241
|
if (stableSince === null) {
|
|
69777
70242
|
stableSince = Date.now();
|
|
@@ -70206,7 +70671,11 @@ async function handleModelCommand(parsed, deps) {
|
|
|
70206
70671
|
const verbHtml = `\`/model ${deps.escapeHtml(model)}\``;
|
|
70207
70672
|
let result;
|
|
70208
70673
|
try {
|
|
70209
|
-
result = await deps.inject(deps.getAgentName(), `/model ${model}
|
|
70674
|
+
result = await deps.inject(deps.getAgentName(), `/model ${model}`, {
|
|
70675
|
+
successPattern: MODEL_SWITCH_CONFIRMATION_PREFIX,
|
|
70676
|
+
errorPattern: MODEL_SWITCH_ERROR_RE,
|
|
70677
|
+
settleBeforeSendMs: 1500
|
|
70678
|
+
});
|
|
70210
70679
|
} catch (err) {
|
|
70211
70680
|
const msg = err instanceof Error ? err.message : String(err);
|
|
70212
70681
|
return {
|
|
@@ -70214,21 +70683,21 @@ async function handleModelCommand(parsed, deps) {
|
|
|
70214
70683
|
html: true
|
|
70215
70684
|
};
|
|
70216
70685
|
}
|
|
70217
|
-
if (result.outcome === "ok") {
|
|
70218
|
-
const
|
|
70219
|
-
if (errLine) {
|
|
70220
|
-
return {
|
|
70221
|
-
text: [
|
|
70222
|
-
`\u274c ${verbHtml} \u2014 the switch did not take:`,
|
|
70223
|
-
deps.preBlock(errLine),
|
|
70224
|
-
"Check `/model` for valid model names."
|
|
70225
|
-
].join(`
|
|
70226
|
-
`),
|
|
70227
|
-
html: true
|
|
70228
|
-
};
|
|
70229
|
-
}
|
|
70230
|
-
const confirmation = modelSwitchConfirmationLine(result.output);
|
|
70686
|
+
if (result.outcome === "ok" || result.outcome === "ok_no_output") {
|
|
70687
|
+
const confirmation = result.outcome === "ok" ? modelSwitchConfirmationLine(result.output) : null;
|
|
70231
70688
|
if (confirmation) {
|
|
70689
|
+
if (isKeptModelConfirmation(confirmation)) {
|
|
70690
|
+
return {
|
|
70691
|
+
text: [
|
|
70692
|
+
`${verbHtml}`,
|
|
70693
|
+
deps.preBlock(confirmation),
|
|
70694
|
+
...result.truncated ? ["_truncated_"] : [],
|
|
70695
|
+
PERSIST_NOTE
|
|
70696
|
+
].join(`
|
|
70697
|
+
`),
|
|
70698
|
+
html: true
|
|
70699
|
+
};
|
|
70700
|
+
}
|
|
70232
70701
|
const confirmed = sessionModelFromConfirmation(confirmation) ?? model;
|
|
70233
70702
|
return {
|
|
70234
70703
|
text: [
|
|
@@ -70239,26 +70708,31 @@ async function handleModelCommand(parsed, deps) {
|
|
|
70239
70708
|
].join(`
|
|
70240
70709
|
`),
|
|
70241
70710
|
html: true,
|
|
70242
|
-
|
|
70711
|
+
selectedModel: confirmed
|
|
70243
70712
|
};
|
|
70244
70713
|
}
|
|
70245
|
-
|
|
70246
|
-
|
|
70247
|
-
|
|
70248
|
-
|
|
70249
|
-
|
|
70714
|
+
const errLine = result.outcome === "ok" ? modelSwitchErrorLine(result.output) : null;
|
|
70715
|
+
if (errLine) {
|
|
70716
|
+
return {
|
|
70717
|
+
text: [
|
|
70718
|
+
`\u274c ${verbHtml} \u2014 the switch did not take:`,
|
|
70719
|
+
deps.preBlock(errLine),
|
|
70720
|
+
"Check `/model` for a valid, available model."
|
|
70721
|
+
].join(`
|
|
70250
70722
|
`),
|
|
70251
|
-
|
|
70252
|
-
|
|
70253
|
-
|
|
70254
|
-
|
|
70723
|
+
html: true
|
|
70724
|
+
};
|
|
70725
|
+
}
|
|
70726
|
+
const optimisticLabel = optimisticModelRecordLabel(model);
|
|
70255
70727
|
return {
|
|
70256
70728
|
text: [
|
|
70257
|
-
`${verbHtml} \u2014 sent, but
|
|
70729
|
+
`${verbHtml} \u2014 sent, but couldn't read a confirmation line. \`/status\` will show the live model once it's confirmed.`,
|
|
70258
70730
|
PERSIST_NOTE
|
|
70259
70731
|
].join(`
|
|
70260
70732
|
`),
|
|
70261
|
-
html: true
|
|
70733
|
+
html: true,
|
|
70734
|
+
selectedModel: optimisticLabel,
|
|
70735
|
+
optimistic: true
|
|
70262
70736
|
};
|
|
70263
70737
|
}
|
|
70264
70738
|
if (result.errorCode === "session_missing") {
|
|
@@ -70321,6 +70795,15 @@ function expandSrAlias(arg) {
|
|
|
70321
70795
|
function srFriendlyLabel(srName) {
|
|
70322
70796
|
return SR_MODEL_LABELS[srName] ?? srName.replace(/^sr-/, "").replace(/-/g, " ");
|
|
70323
70797
|
}
|
|
70798
|
+
function optimisticModelRecordLabel(token) {
|
|
70799
|
+
if (isSrModel(token))
|
|
70800
|
+
return token;
|
|
70801
|
+
const lower = token.toLowerCase();
|
|
70802
|
+
if (MODEL_ALIASES.includes(lower)) {
|
|
70803
|
+
return lower.charAt(0).toUpperCase() + lower.slice(1);
|
|
70804
|
+
}
|
|
70805
|
+
return token;
|
|
70806
|
+
}
|
|
70324
70807
|
function classifyDiscoveredOptions(options) {
|
|
70325
70808
|
return {
|
|
70326
70809
|
claude: options.filter((o) => !o.label.startsWith("sr-") && !o.label.includes("/") && (/^[A-Z]/.test(o.label) || o.label.startsWith("claude-"))),
|
|
@@ -70493,7 +70976,11 @@ async function handleModelMenuCallback(data, deps) {
|
|
|
70493
70976
|
}
|
|
70494
70977
|
let aliasResult;
|
|
70495
70978
|
try {
|
|
70496
|
-
aliasResult = await deps.inject(deps.getAgentName(), `/model ${alias}
|
|
70979
|
+
aliasResult = await deps.inject(deps.getAgentName(), `/model ${alias}`, {
|
|
70980
|
+
successPattern: MODEL_SWITCH_CONFIRMATION_PREFIX,
|
|
70981
|
+
errorPattern: MODEL_SWITCH_ERROR_RE,
|
|
70982
|
+
settleBeforeSendMs: 1500
|
|
70983
|
+
});
|
|
70497
70984
|
} catch (err) {
|
|
70498
70985
|
const msg = err instanceof Error ? err.message : String(err);
|
|
70499
70986
|
return {
|
|
@@ -70501,16 +70988,32 @@ async function handleModelMenuCallback(data, deps) {
|
|
|
70501
70988
|
reply: await menuWithBanner(deps, `\u274c Switch to **${deps.escapeHtml(alias)}** failed: ${deps.escapeHtml(msg)}`)
|
|
70502
70989
|
};
|
|
70503
70990
|
}
|
|
70504
|
-
if (aliasResult.outcome === "ok") {
|
|
70505
|
-
const confirmation = modelSwitchConfirmationLine(aliasResult.output)
|
|
70506
|
-
|
|
70991
|
+
if (aliasResult.outcome === "ok" || aliasResult.outcome === "ok_no_output") {
|
|
70992
|
+
const confirmation = aliasResult.outcome === "ok" ? modelSwitchConfirmationLine(aliasResult.output) : null;
|
|
70993
|
+
if (confirmation) {
|
|
70994
|
+
const kept = isKeptModelConfirmation(confirmation);
|
|
70995
|
+
return {
|
|
70996
|
+
answer: confirmation,
|
|
70997
|
+
reply: await menuWithBannerStatic(deps, `\u2705 ${deps.escapeHtml(confirmation)}`),
|
|
70998
|
+
...kept ? {} : {
|
|
70999
|
+
selectedModel: sessionModelFromConfirmation(confirmation) ?? optimisticModelRecordLabel(alias),
|
|
71000
|
+
selectedModelToken: alias
|
|
71001
|
+
}
|
|
71002
|
+
};
|
|
71003
|
+
}
|
|
71004
|
+
const aliasErr = aliasResult.outcome === "ok" ? modelSwitchErrorLine(aliasResult.output) : null;
|
|
71005
|
+
if (aliasErr) {
|
|
71006
|
+
return {
|
|
71007
|
+
answer: "Switch failed",
|
|
71008
|
+
reply: await menuWithBanner(deps, `\u274c Switch to **${deps.escapeHtml(alias)}** did not take: ${deps.escapeHtml(aliasErr)}`)
|
|
71009
|
+
};
|
|
71010
|
+
}
|
|
71011
|
+
const optimisticLabel = optimisticModelRecordLabel(alias);
|
|
70507
71012
|
return {
|
|
70508
|
-
answer:
|
|
70509
|
-
reply: await menuWithBannerStatic(deps,
|
|
70510
|
-
|
|
70511
|
-
|
|
70512
|
-
selectedModelToken: alias
|
|
70513
|
-
}
|
|
71013
|
+
answer: `Sent /model ${alias} \u2014 check /status`,
|
|
71014
|
+
reply: await menuWithBannerStatic(deps, `Sent \`/model ${deps.escapeHtml(alias)}\` \u2014 couldn\u2019t read a confirmation line. \`/status\` will show the live model once it\u2019s confirmed.`),
|
|
71015
|
+
selectedModel: optimisticLabel,
|
|
71016
|
+
selectedModelToken: alias
|
|
70514
71017
|
};
|
|
70515
71018
|
}
|
|
70516
71019
|
return {
|
|
@@ -70607,7 +71110,7 @@ function modelSwitchConfirmationLine(output) {
|
|
|
70607
71110
|
`).map((l) => l.trim()).find((l) => MODEL_SWITCH_CONFIRMATION_PREFIX.test(l));
|
|
70608
71111
|
return line && line.length > 0 ? line : null;
|
|
70609
71112
|
}
|
|
70610
|
-
var MODEL_SWITCH_ERROR_RE = /^\s*[\u23fa\u25cf\u2022>\u23bf-]?\s*(?:Error:\s*)?(?:Model(?:\s+'[^']+')?\s+not found|Invalid model|Unknown model|No such model)\b/i;
|
|
71113
|
+
var MODEL_SWITCH_ERROR_RE = /^\s*[\u23fa\u25cf\u2022>\u23bf-]?\s*(?:Error:\s*)?(?:Model(?:\s+'[^']+')?\s+not found|Invalid model|Unknown model|No such model|(?:[\w'\u2019.\-]+\s+){0,4}(?:(?:is |are )?(?:not available|unavailable|not enabled|not supported)|access denied|requires\b[^\n]{0,40}\b(?:subscription|plan)|no access)\b)/i;
|
|
70611
71114
|
function modelSwitchErrorLine(output) {
|
|
70612
71115
|
const line = output.split(`
|
|
70613
71116
|
`).map((l) => l.trim()).find((l) => MODEL_SWITCH_ERROR_RE.test(l));
|
|
@@ -70656,7 +71159,7 @@ async function menuWithBannerStatic(deps, banner) {
|
|
|
70656
71159
|
|
|
70657
71160
|
// gateway/session-model-file.ts
|
|
70658
71161
|
import { readFileSync as readFileSync25, writeFileSync as writeFileSync21, renameSync as renameSync9, rmSync as rmSync4 } from "node:fs";
|
|
70659
|
-
import { join as
|
|
71162
|
+
import { join as join30 } from "node:path";
|
|
70660
71163
|
|
|
70661
71164
|
// gateway/model-command.ts
|
|
70662
71165
|
var MODEL_ARG_RE2 = /^[A-Za-z0-9][A-Za-z0-9._/\[\]-]{0,99}$/;
|
|
@@ -70685,18 +71188,18 @@ function writeSessionModelFile(agentDir, model, configuredDefaultAtWrite) {
|
|
|
70685
71188
|
if (!isValidModelArg2(model)) {
|
|
70686
71189
|
throw new Error(`refusing to persist non-canonical session model token: ${JSON.stringify(model)}`);
|
|
70687
71190
|
}
|
|
70688
|
-
atomicWrite(
|
|
71191
|
+
atomicWrite(join30(agentDir, SESSION_MODEL_FILE), serializeSessionModel({ model, configuredDefaultAtWrite, ts: Date.now() }));
|
|
70689
71192
|
}
|
|
70690
71193
|
function readSessionModelFileRaw(agentDir) {
|
|
70691
71194
|
try {
|
|
70692
|
-
return readFileSync25(
|
|
71195
|
+
return readFileSync25(join30(agentDir, SESSION_MODEL_FILE), "utf8");
|
|
70693
71196
|
} catch {
|
|
70694
71197
|
return null;
|
|
70695
71198
|
}
|
|
70696
71199
|
}
|
|
70697
71200
|
function clearSessionModelFile(agentDir) {
|
|
70698
71201
|
try {
|
|
70699
|
-
rmSync4(
|
|
71202
|
+
rmSync4(join30(agentDir, SESSION_MODEL_FILE), { force: true });
|
|
70700
71203
|
} catch {}
|
|
70701
71204
|
}
|
|
70702
71205
|
function restoreSessionModelFileRaw(agentDir, raw) {
|
|
@@ -70705,12 +71208,12 @@ function restoreSessionModelFileRaw(agentDir, raw) {
|
|
|
70705
71208
|
return;
|
|
70706
71209
|
}
|
|
70707
71210
|
try {
|
|
70708
|
-
atomicWrite(
|
|
71211
|
+
atomicWrite(join30(agentDir, SESSION_MODEL_FILE), raw);
|
|
70709
71212
|
} catch {}
|
|
70710
71213
|
}
|
|
70711
71214
|
function readConfiguredDefaultModel(agentDir) {
|
|
70712
71215
|
try {
|
|
70713
|
-
const v = readFileSync25(
|
|
71216
|
+
const v = readFileSync25(join30(agentDir, CONFIGURED_DEFAULT_MODEL_FILE), "utf8").trim();
|
|
70714
71217
|
return v.length > 0 ? v : null;
|
|
70715
71218
|
} catch {
|
|
70716
71219
|
return null;
|
|
@@ -70722,12 +71225,12 @@ function writeSessionEffortFile(agentDir, level, configuredDefaultAtWrite) {
|
|
|
70722
71225
|
if (!EFFORT_LEVEL_RE.test(level)) {
|
|
70723
71226
|
throw new Error(`refusing to persist non-allowlisted effort level: ${JSON.stringify(level)}`);
|
|
70724
71227
|
}
|
|
70725
|
-
atomicWrite(
|
|
71228
|
+
atomicWrite(join30(agentDir, SESSION_EFFORT_FILE), `${JSON.stringify({ level, configuredDefaultAtWrite: configuredDefaultAtWrite ?? "", ts: Date.now() })}
|
|
70726
71229
|
`);
|
|
70727
71230
|
}
|
|
70728
71231
|
function clearSessionEffortFile(agentDir) {
|
|
70729
71232
|
try {
|
|
70730
|
-
rmSync4(
|
|
71233
|
+
rmSync4(join30(agentDir, SESSION_EFFORT_FILE), { force: true });
|
|
70731
71234
|
} catch {}
|
|
70732
71235
|
}
|
|
70733
71236
|
var PREMIUM_RECOVERY_FILE = ".premium-recovery";
|
|
@@ -70750,13 +71253,13 @@ function writePremiumRecoveryFile(agentDir, premiumModel, chats) {
|
|
|
70750
71253
|
if (clean.length === 0) {
|
|
70751
71254
|
throw new Error("refusing to persist premium-recovery marker with no chats to notify");
|
|
70752
71255
|
}
|
|
70753
|
-
atomicWrite(
|
|
71256
|
+
atomicWrite(join30(agentDir, PREMIUM_RECOVERY_FILE), `${JSON.stringify({ premiumModel, chats: clean, ts: Date.now() })}
|
|
70754
71257
|
`);
|
|
70755
71258
|
}
|
|
70756
71259
|
function readPremiumRecoveryFile(agentDir) {
|
|
70757
71260
|
let raw;
|
|
70758
71261
|
try {
|
|
70759
|
-
raw = readFileSync25(
|
|
71262
|
+
raw = readFileSync25(join30(agentDir, PREMIUM_RECOVERY_FILE), "utf8");
|
|
70760
71263
|
} catch {
|
|
70761
71264
|
return null;
|
|
70762
71265
|
}
|
|
@@ -70769,7 +71272,7 @@ function readPremiumRecoveryFile(agentDir) {
|
|
|
70769
71272
|
}
|
|
70770
71273
|
function clearPremiumRecoveryFile(agentDir) {
|
|
70771
71274
|
try {
|
|
70772
|
-
rmSync4(
|
|
71275
|
+
rmSync4(join30(agentDir, PREMIUM_RECOVERY_FILE), { force: true });
|
|
70773
71276
|
} catch {}
|
|
70774
71277
|
}
|
|
70775
71278
|
|
|
@@ -70961,7 +71464,7 @@ function makeIo(agentName3, opts) {
|
|
|
70961
71464
|
socket: opts.socketName ?? `switchroom-${agentName3}`,
|
|
70962
71465
|
session: opts.sessionName ?? agentName3,
|
|
70963
71466
|
stepMs: opts.stepMs ?? 600,
|
|
70964
|
-
timeoutMs: opts.timeoutMs ??
|
|
71467
|
+
timeoutMs: opts.timeoutMs ?? 12000,
|
|
70965
71468
|
sleep: opts._sleep ?? realSleep,
|
|
70966
71469
|
log: opts._log ?? ((line) => process.stderr.write(`${line}
|
|
70967
71470
|
`)),
|
|
@@ -70984,7 +71487,7 @@ function sendLiteral(io, text4) {
|
|
|
70984
71487
|
function sendKey(io, key) {
|
|
70985
71488
|
io.runner.send(io.socket, io.session, ["send-keys", key]);
|
|
70986
71489
|
}
|
|
70987
|
-
async function openPicker(io) {
|
|
71490
|
+
async function openPicker(io, deadlineMs) {
|
|
70988
71491
|
sendLiteral(io, "/model");
|
|
70989
71492
|
sendKey(io, "Enter");
|
|
70990
71493
|
for (;; ) {
|
|
@@ -70993,10 +71496,24 @@ async function openPicker(io) {
|
|
|
70993
71496
|
const parsed = parseModelPicker(pane);
|
|
70994
71497
|
if (parsed?.footerSeen)
|
|
70995
71498
|
return parsed;
|
|
70996
|
-
if (expired(io))
|
|
71499
|
+
if (deadlineMs != null && Date.now() >= deadlineMs || expired(io)) {
|
|
70997
71500
|
return parsed;
|
|
71501
|
+
}
|
|
70998
71502
|
}
|
|
70999
71503
|
}
|
|
71504
|
+
async function openPickerWithRetry(io) {
|
|
71505
|
+
const remaining = io.timeoutMs - (Date.now() - io.startedAt);
|
|
71506
|
+
const firstDeadline = Date.now() + Math.max(io.stepMs * 2, Math.floor(remaining / 2));
|
|
71507
|
+
const parsed = await openPicker(io, firstDeadline);
|
|
71508
|
+
if (parsed?.footerSeen)
|
|
71509
|
+
return parsed;
|
|
71510
|
+
if (expired(io))
|
|
71511
|
+
return parsed;
|
|
71512
|
+
await dismissPicker(io);
|
|
71513
|
+
if (expired(io))
|
|
71514
|
+
return parsed;
|
|
71515
|
+
return openPicker(io);
|
|
71516
|
+
}
|
|
71000
71517
|
async function dismissPicker(io) {
|
|
71001
71518
|
for (let attempt = 0;attempt < 2; attempt++) {
|
|
71002
71519
|
try {
|
|
@@ -71026,7 +71543,7 @@ async function discoverModels(agentName3, opts = {}) {
|
|
|
71026
71543
|
let parsed = null;
|
|
71027
71544
|
let dismissed = true;
|
|
71028
71545
|
try {
|
|
71029
|
-
parsed = await
|
|
71546
|
+
parsed = await openPickerWithRetry(io);
|
|
71030
71547
|
} finally {
|
|
71031
71548
|
dismissed = await dismissOrWarn(io, "discover");
|
|
71032
71549
|
}
|
|
@@ -71051,7 +71568,7 @@ async function selectModel(agentName3, targetLabel, opts = {}) {
|
|
|
71051
71568
|
io.startedAt = Date.now();
|
|
71052
71569
|
let selected = false;
|
|
71053
71570
|
try {
|
|
71054
|
-
const parsed = await
|
|
71571
|
+
const parsed = await openPickerWithRetry(io);
|
|
71055
71572
|
if (!parsed || !parsed.footerSeen) {
|
|
71056
71573
|
return { ok: false, reason: "picker did not render \u2014 agent may be mid-turn" };
|
|
71057
71574
|
}
|
|
@@ -71108,7 +71625,7 @@ function extractConfirmation(pane) {
|
|
|
71108
71625
|
}
|
|
71109
71626
|
|
|
71110
71627
|
// ../src/agents/scaffold.ts
|
|
71111
|
-
import { join as
|
|
71628
|
+
import { join as join33, resolve as resolve6 } from "node:path";
|
|
71112
71629
|
init_atomic();
|
|
71113
71630
|
|
|
71114
71631
|
// ../src/agents/agent-uid.ts
|
|
@@ -71132,8 +71649,8 @@ var CONTAINER_DEFAULT_UTC_ZONES = new Set([
|
|
|
71132
71649
|
]);
|
|
71133
71650
|
|
|
71134
71651
|
// ../src/cli/agent-config.ts
|
|
71135
|
-
import { join as
|
|
71136
|
-
import { homedir as
|
|
71652
|
+
import { join as join31 } from "node:path";
|
|
71653
|
+
import { homedir as homedir10 } from "node:os";
|
|
71137
71654
|
|
|
71138
71655
|
// ../src/cli/helpers.ts
|
|
71139
71656
|
init_loader();
|
|
@@ -71153,12 +71670,12 @@ var WEBKITE_VAULT_KEYS = new Set([
|
|
|
71153
71670
|
init_overlay_loader();
|
|
71154
71671
|
|
|
71155
71672
|
// ../src/cli/agent-config.ts
|
|
71156
|
-
var AUDIT_ROOT =
|
|
71673
|
+
var AUDIT_ROOT = join31(homedir10(), ".switchroom", "audit");
|
|
71157
71674
|
|
|
71158
71675
|
// ../src/agents/profiles.ts
|
|
71159
71676
|
var import_handlebars = __toESM(require_lib(), 1);
|
|
71160
71677
|
import { readFileSync as readFileSync26, writeFileSync as writeFileSync22, existsSync as existsSync26, readdirSync as readdirSync5, statSync as statSync8, copyFileSync, mkdirSync as mkdirSync22, realpathSync as realpathSync2 } from "node:fs";
|
|
71161
|
-
import { resolve as resolve5, join as
|
|
71678
|
+
import { resolve as resolve5, join as join32, sep as pathSep } from "node:path";
|
|
71162
71679
|
var PROFILES_ROOT = resolve5(import.meta.dirname, "../../profiles");
|
|
71163
71680
|
import_handlebars.default.registerHelper("json", (value) => {
|
|
71164
71681
|
return new import_handlebars.default.SafeString(JSON.stringify(value, null, 2));
|
|
@@ -71169,7 +71686,7 @@ import_handlebars.default.registerHelper("isNumber", (value) => {
|
|
|
71169
71686
|
var SHARED_FRAGMENTS_DIR = resolve5(PROFILES_ROOT, "_shared");
|
|
71170
71687
|
var SHARED_FRAGMENTS = ["vault-protocol", "agent-self-service", "execution-discipline", "reply-discipline", "dev-protocol"];
|
|
71171
71688
|
for (const name of SHARED_FRAGMENTS) {
|
|
71172
|
-
const fragPath =
|
|
71689
|
+
const fragPath = join32(SHARED_FRAGMENTS_DIR, `${name}.md.hbs`);
|
|
71173
71690
|
if (existsSync26(fragPath)) {
|
|
71174
71691
|
import_handlebars.default.registerPartial(name, readFileSync26(fragPath, "utf-8"));
|
|
71175
71692
|
}
|
|
@@ -71881,7 +72398,7 @@ init_overlay_loader();
|
|
|
71881
72398
|
init_merge();
|
|
71882
72399
|
var import_yaml4 = __toESM(require_dist(), 1);
|
|
71883
72400
|
import { readFileSync as readFileSync27, existsSync as existsSync27 } from "node:fs";
|
|
71884
|
-
import { homedir as
|
|
72401
|
+
import { homedir as homedir11 } from "node:os";
|
|
71885
72402
|
import { resolve as resolve7 } from "node:path";
|
|
71886
72403
|
|
|
71887
72404
|
class ConfigError2 extends Error {
|
|
@@ -71938,7 +72455,7 @@ function coerceLegacyGoogleWorkspaceKeys2(parsed, filePath) {
|
|
|
71938
72455
|
}
|
|
71939
72456
|
function findConfigFile2(startDir) {
|
|
71940
72457
|
const envPath = process.env.SWITCHROOM_CONFIG;
|
|
71941
|
-
const home2 =
|
|
72458
|
+
const home2 = homedir11();
|
|
71942
72459
|
const userDir = resolve7(home2, ".switchroom");
|
|
71943
72460
|
const searchPaths = [
|
|
71944
72461
|
envPath ? resolve7(envPath) : null,
|
|
@@ -72577,7 +73094,7 @@ function numField(obj, key) {
|
|
|
72577
73094
|
|
|
72578
73095
|
// gateway/context-occupancy.ts
|
|
72579
73096
|
import { mkdirSync as mkdirSync23, writeFileSync as writeFileSync23 } from "node:fs";
|
|
72580
|
-
import { join as
|
|
73097
|
+
import { join as join34 } from "node:path";
|
|
72581
73098
|
var CONTEXT_OCCUPANCY_FILENAME = "context-occupancy.json";
|
|
72582
73099
|
var TIGHT_FRACTION = 0.8;
|
|
72583
73100
|
function buildContextOccupancy(occupancy, cap, now) {
|
|
@@ -72600,7 +73117,7 @@ function buildContextOccupancy(occupancy, cap, now) {
|
|
|
72600
73117
|
}
|
|
72601
73118
|
function writeContextOccupancySnapshot(stateDir, snapshot, deps) {
|
|
72602
73119
|
try {
|
|
72603
|
-
const path2 =
|
|
73120
|
+
const path2 = join34(stateDir, CONTEXT_OCCUPANCY_FILENAME);
|
|
72604
73121
|
(deps?.mkdir ?? ((p, o) => mkdirSync23(p, o)))(stateDir, { recursive: true });
|
|
72605
73122
|
(deps?.writeFile ?? ((p, d) => writeFileSync23(p, d)))(path2, JSON.stringify(snapshot, null, 2) + `
|
|
72606
73123
|
`);
|
|
@@ -73095,12 +73612,12 @@ function startWebhookIngestServer(opts) {
|
|
|
73095
73612
|
|
|
73096
73613
|
// ../src/web/webhook-gateway-record.ts
|
|
73097
73614
|
import { appendFileSync as appendFileSync5, mkdirSync as mkdirSync26 } from "fs";
|
|
73098
|
-
import { join as
|
|
73099
|
-
import { homedir as
|
|
73615
|
+
import { join as join37 } from "path";
|
|
73616
|
+
import { homedir as homedir13 } from "os";
|
|
73100
73617
|
|
|
73101
73618
|
// ../src/web/webhook-handler.ts
|
|
73102
73619
|
import { appendFileSync as appendFileSync4, existsSync as existsSync31, mkdirSync as mkdirSync24, readFileSync as readFileSync29, writeFileSync as writeFileSync24 } from "fs";
|
|
73103
|
-
import { join as
|
|
73620
|
+
import { join as join35 } from "path";
|
|
73104
73621
|
var DEDUP_MAX = 1000;
|
|
73105
73622
|
var DEDUP_TTL_MS = 24 * 60 * 60 * 1000;
|
|
73106
73623
|
function loadDedupFile(path2) {
|
|
@@ -73129,8 +73646,8 @@ var agentDedupCache = new Map;
|
|
|
73129
73646
|
function createFileDedupStore(resolveAgentDir) {
|
|
73130
73647
|
return {
|
|
73131
73648
|
check(agent, deliveryId, now) {
|
|
73132
|
-
const telegramDir =
|
|
73133
|
-
const filePath =
|
|
73649
|
+
const telegramDir = join35(resolveAgentDir(agent), "telegram");
|
|
73650
|
+
const filePath = join35(telegramDir, "webhook-dedup.json");
|
|
73134
73651
|
if (!agentDedupCache.has(agent)) {
|
|
73135
73652
|
agentDedupCache.set(agent, loadDedupFile(filePath));
|
|
73136
73653
|
}
|
|
@@ -73152,8 +73669,8 @@ var throttleIssueWindow = new Map;
|
|
|
73152
73669
|
|
|
73153
73670
|
// ../src/web/webhook-dispatch.ts
|
|
73154
73671
|
import { existsSync as existsSync32, mkdirSync as mkdirSync25, readFileSync as readFileSync30, writeFileSync as writeFileSync25 } from "fs";
|
|
73155
|
-
import { join as
|
|
73156
|
-
import { homedir as
|
|
73672
|
+
import { join as join36 } from "path";
|
|
73673
|
+
import { homedir as homedir12 } from "os";
|
|
73157
73674
|
|
|
73158
73675
|
// ../src/agent-scheduler/ipc-client.ts
|
|
73159
73676
|
import { createConnection as createConnection2 } from "node:net";
|
|
@@ -73472,8 +73989,8 @@ function createFileCooldownStore(resolveAgentDir) {
|
|
|
73472
73989
|
isCoolingDown(agent, key, cooldownMs, now) {
|
|
73473
73990
|
if (cooldownMs <= 0)
|
|
73474
73991
|
return false;
|
|
73475
|
-
const telegramDir =
|
|
73476
|
-
const filePath =
|
|
73992
|
+
const telegramDir = join36(resolveAgentDir(agent), "telegram");
|
|
73993
|
+
const filePath = join36(telegramDir, "webhook-cooldown.json");
|
|
73477
73994
|
if (!cache.has(agent)) {
|
|
73478
73995
|
cache.set(agent, loadCooldownFile(filePath));
|
|
73479
73996
|
}
|
|
@@ -73530,9 +74047,9 @@ async function defaultInject(socketPath, agentName3, inbound) {
|
|
|
73530
74047
|
}
|
|
73531
74048
|
function injectWebhookInbound(agent, prompt, ctx, deps = {}) {
|
|
73532
74049
|
const log = deps.log ?? ((s) => process.stderr.write(s));
|
|
73533
|
-
const resolveAgentDir = deps.resolveAgentDir ?? ((a) =>
|
|
74050
|
+
const resolveAgentDir = deps.resolveAgentDir ?? ((a) => join36(homedir12(), ".switchroom", "agents", a));
|
|
73534
74051
|
const now = (deps.now ?? Date.now)();
|
|
73535
|
-
const socketPath =
|
|
74052
|
+
const socketPath = join36(resolveAgentDir(agent), "telegram", "gateway.sock");
|
|
73536
74053
|
const inbound = {
|
|
73537
74054
|
type: "inbound",
|
|
73538
74055
|
chatId: ctx.chatId,
|
|
@@ -73604,7 +74121,7 @@ function evaluateDispatch(args, deps = {}) {
|
|
|
73604
74121
|
const log = deps.log ?? ((s) => process.stderr.write(s));
|
|
73605
74122
|
const now = (deps.now ?? Date.now)();
|
|
73606
74123
|
const nowDate = deps.nowDate ?? (() => new Date(now));
|
|
73607
|
-
const resolveAgentDir = deps.resolveAgentDir ?? ((a) =>
|
|
74124
|
+
const resolveAgentDir = deps.resolveAgentDir ?? ((a) => join36(homedir12(), ".switchroom", "agents", a));
|
|
73608
74125
|
const cooldownStore = deps.cooldownStore ?? createFileCooldownStore(resolveAgentDir);
|
|
73609
74126
|
if (!DISPATCH_SOURCES.includes(args.source))
|
|
73610
74127
|
return 0;
|
|
@@ -73682,10 +74199,10 @@ var CONSOLIDATION_COMPLETED_EVENT = "consolidation.completed";
|
|
|
73682
74199
|
function recordWebhookEvent(rec, deps = {}) {
|
|
73683
74200
|
const log = deps.log ?? ((s) => process.stderr.write(s));
|
|
73684
74201
|
const now = rec.ts || (deps.now ?? Date.now)();
|
|
73685
|
-
const resolveAgentDir = deps.resolveAgentDir ?? ((a) =>
|
|
74202
|
+
const resolveAgentDir = deps.resolveAgentDir ?? ((a) => join37(homedir13(), ".switchroom", "agents", a));
|
|
73686
74203
|
const dedupStore = deps.dedupStore ?? createFileDedupStore(resolveAgentDir);
|
|
73687
74204
|
const agent = rec.agent;
|
|
73688
|
-
const telegramDir =
|
|
74205
|
+
const telegramDir = join37(resolveAgentDir(agent), "telegram");
|
|
73689
74206
|
if (rec.source === "github" && rec.delivery_id) {
|
|
73690
74207
|
const originalTs = dedupStore.check(agent, rec.delivery_id, now);
|
|
73691
74208
|
if (originalTs !== undefined) {
|
|
@@ -73694,7 +74211,7 @@ function recordWebhookEvent(rec, deps = {}) {
|
|
|
73694
74211
|
return { status: "deduped", ts: originalTs };
|
|
73695
74212
|
}
|
|
73696
74213
|
}
|
|
73697
|
-
const logPath =
|
|
74214
|
+
const logPath = join37(telegramDir, "webhook-events.jsonl");
|
|
73698
74215
|
try {
|
|
73699
74216
|
mkdirSync26(telegramDir, { recursive: true });
|
|
73700
74217
|
const record = {
|
|
@@ -77142,17 +77659,17 @@ import {
|
|
|
77142
77659
|
readFileSync as readFileSync31,
|
|
77143
77660
|
writeSync as writeSync5
|
|
77144
77661
|
} from "node:fs";
|
|
77145
|
-
import { join as
|
|
77662
|
+
import { join as join38 } from "node:path";
|
|
77146
77663
|
import { randomUUID as randomUUID5 } from "node:crypto";
|
|
77147
77664
|
var PROPOSALS_FILE2 = "skill-proposals.jsonl";
|
|
77148
77665
|
var REJECTED_FILE2 = "skill-proposals-rejected.jsonl";
|
|
77149
77666
|
var REJECTION_TTL_MS2 = 90 * 24 * 60 * 60 * 1000;
|
|
77150
77667
|
var PROPOSAL_SIM_THRESHOLD = 0.5;
|
|
77151
77668
|
function proposalsPath2(stateDir) {
|
|
77152
|
-
return
|
|
77669
|
+
return join38(stateDir, PROPOSALS_FILE2);
|
|
77153
77670
|
}
|
|
77154
77671
|
function rejectedPath2(stateDir) {
|
|
77155
|
-
return
|
|
77672
|
+
return join38(stateDir, REJECTED_FILE2);
|
|
77156
77673
|
}
|
|
77157
77674
|
function ensureDir3(stateDir) {
|
|
77158
77675
|
if (!existsSync33(stateDir)) {
|
|
@@ -78647,17 +79164,17 @@ import {
|
|
|
78647
79164
|
readdirSync as readdirSync6,
|
|
78648
79165
|
readFileSync as readFileSync37
|
|
78649
79166
|
} from "fs";
|
|
78650
|
-
import { join as
|
|
79167
|
+
import { join as join40 } from "path";
|
|
78651
79168
|
|
|
78652
79169
|
// session-tail.ts
|
|
78653
|
-
function
|
|
79170
|
+
function sanitizeCwdToProjectName2(cwd) {
|
|
78654
79171
|
return cwd.replace(/[^a-zA-Z0-9]/g, "-");
|
|
78655
79172
|
}
|
|
78656
|
-
var
|
|
78657
|
-
var
|
|
78658
|
-
function
|
|
79173
|
+
var MAX_JSONL_LINE_BYTES2 = 2 * 1024 * 1024;
|
|
79174
|
+
var MAX_ERROR_TEXT_CHARS2 = 500;
|
|
79175
|
+
function extractToolResultErrorText2(content3) {
|
|
78659
79176
|
if (typeof content3 === "string") {
|
|
78660
|
-
return content3.slice(0,
|
|
79177
|
+
return content3.slice(0, MAX_ERROR_TEXT_CHARS2);
|
|
78661
79178
|
}
|
|
78662
79179
|
if (Array.isArray(content3)) {
|
|
78663
79180
|
const parts = [];
|
|
@@ -78670,11 +79187,11 @@ function extractToolResultErrorText(content3) {
|
|
|
78670
79187
|
}
|
|
78671
79188
|
}
|
|
78672
79189
|
return parts.join(`
|
|
78673
|
-
`).slice(0,
|
|
79190
|
+
`).slice(0, MAX_ERROR_TEXT_CHARS2);
|
|
78674
79191
|
}
|
|
78675
79192
|
return "";
|
|
78676
79193
|
}
|
|
78677
|
-
function
|
|
79194
|
+
function projectAssistantTextBlocks2(content3, make) {
|
|
78678
79195
|
const out = new Map;
|
|
78679
79196
|
let lastToolUseIdx = -1;
|
|
78680
79197
|
content3.forEach((c, i) => {
|
|
@@ -78691,6 +79208,13 @@ function projectAssistantTextBlocks(content3, make) {
|
|
|
78691
79208
|
});
|
|
78692
79209
|
return out;
|
|
78693
79210
|
}
|
|
79211
|
+
function sumUsageTokens2(usage) {
|
|
79212
|
+
if (usage == null || typeof usage !== "object")
|
|
79213
|
+
return 0;
|
|
79214
|
+
const u = usage;
|
|
79215
|
+
const n = (v) => typeof v === "number" && Number.isFinite(v) ? v : 0;
|
|
79216
|
+
return n(u.input_tokens) + n(u.output_tokens) + n(u.cache_creation_input_tokens);
|
|
79217
|
+
}
|
|
78694
79218
|
function assistantLineCarriesAnswerSurface(content3) {
|
|
78695
79219
|
if (!Array.isArray(content3))
|
|
78696
79220
|
return false;
|
|
@@ -78750,7 +79274,7 @@ function projectSubagentLine(line, agentId, state4) {
|
|
|
78750
79274
|
agentId,
|
|
78751
79275
|
toolUseId: cc.tool_use_id ?? "",
|
|
78752
79276
|
isError: isError2,
|
|
78753
|
-
errorText: isError2 ?
|
|
79277
|
+
errorText: isError2 ? extractToolResultErrorText2(cc.content) : undefined
|
|
78754
79278
|
});
|
|
78755
79279
|
}
|
|
78756
79280
|
}
|
|
@@ -78766,7 +79290,17 @@ function projectSubagentLine(line, agentId, state4) {
|
|
|
78766
79290
|
if (typeof subModel === "string" && !isModelSentinel(subModel)) {
|
|
78767
79291
|
events.push({ kind: "sub_agent_model", agentId, model: subModel });
|
|
78768
79292
|
}
|
|
78769
|
-
const
|
|
79293
|
+
const subUsageTotal = sumUsageTokens2(message?.usage);
|
|
79294
|
+
if (subUsageTotal > 0) {
|
|
79295
|
+
const subMsgId = message?.id;
|
|
79296
|
+
events.push({
|
|
79297
|
+
kind: "sub_agent_usage",
|
|
79298
|
+
agentId,
|
|
79299
|
+
messageId: typeof subMsgId === "string" ? subMsgId : null,
|
|
79300
|
+
totalTokens: subUsageTotal
|
|
79301
|
+
});
|
|
79302
|
+
}
|
|
79303
|
+
const textEvents = projectAssistantTextBlocks2(content3, (text4, blockIndex, lastInMessage) => ({
|
|
78770
79304
|
kind: "sub_agent_text",
|
|
78771
79305
|
agentId,
|
|
78772
79306
|
text: text4,
|
|
@@ -78890,7 +79424,7 @@ function sanitiseToolArg(name, raw) {
|
|
|
78890
79424
|
case "NotebookEdit": {
|
|
78891
79425
|
const fp = raw.file_path;
|
|
78892
79426
|
if (typeof fp === "string" && fp.length > 0)
|
|
78893
|
-
out =
|
|
79427
|
+
out = basename6(fp);
|
|
78894
79428
|
break;
|
|
78895
79429
|
}
|
|
78896
79430
|
case "Bash": {
|
|
@@ -78926,7 +79460,7 @@ function sanitiseToolArg(name, raw) {
|
|
|
78926
79460
|
out = out.slice(0, SANITISE_MAX_LEN - 1) + "\u2026";
|
|
78927
79461
|
return out;
|
|
78928
79462
|
}
|
|
78929
|
-
function
|
|
79463
|
+
function basename6(p) {
|
|
78930
79464
|
const idx = p.lastIndexOf("/");
|
|
78931
79465
|
return idx === -1 ? p : p.slice(idx + 1);
|
|
78932
79466
|
}
|
|
@@ -79130,10 +79664,10 @@ import {
|
|
|
79130
79664
|
utimesSync,
|
|
79131
79665
|
writeFileSync as writeFileSync29
|
|
79132
79666
|
} from "node:fs";
|
|
79133
|
-
import { join as
|
|
79667
|
+
import { join as join39 } from "node:path";
|
|
79134
79668
|
var TURN_ACTIVE_MARKER_FILE = "turn-active.json";
|
|
79135
79669
|
function touchTurnActiveMarker(stateDir) {
|
|
79136
|
-
const path2 =
|
|
79670
|
+
const path2 = join39(stateDir, TURN_ACTIVE_MARKER_FILE);
|
|
79137
79671
|
if (!existsSync34(path2))
|
|
79138
79672
|
return;
|
|
79139
79673
|
const now = new Date;
|
|
@@ -79300,6 +79834,7 @@ function readSubTail(entry, tail, now, onDescriptionUpdate, fs2, log, db2, paren
|
|
|
79300
79834
|
},
|
|
79301
79835
|
lastTool: entry.lastTool,
|
|
79302
79836
|
toolCount: entry.toolCount,
|
|
79837
|
+
totalTokens: entry.totalTokens,
|
|
79303
79838
|
model: entry.currentModel,
|
|
79304
79839
|
skeleton: true
|
|
79305
79840
|
});
|
|
@@ -79381,6 +79916,7 @@ function readSubTail(entry, tail, now, onDescriptionUpdate, fs2, log, db2, paren
|
|
|
79381
79916
|
},
|
|
79382
79917
|
lastTool: entry.lastTool,
|
|
79383
79918
|
toolCount: entry.toolCount,
|
|
79919
|
+
totalTokens: entry.totalTokens,
|
|
79384
79920
|
model: entry.currentModel
|
|
79385
79921
|
});
|
|
79386
79922
|
return true;
|
|
@@ -79491,6 +80027,15 @@ function readSubTail(entry, tail, now, onDescriptionUpdate, fs2, log, db2, paren
|
|
|
79491
80027
|
}
|
|
79492
80028
|
continue;
|
|
79493
80029
|
}
|
|
80030
|
+
if (ev.kind === "sub_agent_usage") {
|
|
80031
|
+
if (ev.messageId == null) {
|
|
80032
|
+
entry.totalTokens += ev.totalTokens;
|
|
80033
|
+
} else if (!entry.seenUsageMessageIds.has(ev.messageId)) {
|
|
80034
|
+
entry.seenUsageMessageIds.add(ev.messageId);
|
|
80035
|
+
entry.totalTokens += ev.totalTokens;
|
|
80036
|
+
}
|
|
80037
|
+
continue;
|
|
80038
|
+
}
|
|
79494
80039
|
if (ev.kind === "sub_agent_tool_use") {
|
|
79495
80040
|
const narrativeJustFired = resolvePendingSubNarrative(ev.toolName, ev.input);
|
|
79496
80041
|
if (REPLY_TOOLS2.has(ev.toolName) && typeof ev.input?.text === "string") {
|
|
@@ -79519,6 +80064,7 @@ function readSubTail(entry, tail, now, onDescriptionUpdate, fs2, log, db2, paren
|
|
|
79519
80064
|
},
|
|
79520
80065
|
lastTool: entry.lastTool,
|
|
79521
80066
|
toolCount: entry.toolCount,
|
|
80067
|
+
totalTokens: entry.totalTokens,
|
|
79522
80068
|
progressLine: toolLine,
|
|
79523
80069
|
model: entry.currentModel
|
|
79524
80070
|
});
|
|
@@ -79593,7 +80139,7 @@ function readSubTail(entry, tail, now, onDescriptionUpdate, fs2, log, db2, paren
|
|
|
79593
80139
|
}
|
|
79594
80140
|
function startSubagentWatcher(config) {
|
|
79595
80141
|
const agentDir = config.agentDir;
|
|
79596
|
-
const expectedProjectSlug = config.agentCwd != null ?
|
|
80142
|
+
const expectedProjectSlug = config.agentCwd != null ? sanitizeCwdToProjectName2(config.agentCwd) : null;
|
|
79597
80143
|
const extraWatchCwdsProvider = config.extraWatchCwdsProvider ?? null;
|
|
79598
80144
|
const warnedForeignSlugs = new Set;
|
|
79599
80145
|
const stallThresholdMs = config.stallThresholdMs ?? parseEnvMs("SWITCHROOM_SUBAGENT_STALL_MS") ?? DEFAULT_STALL_THRESHOLD_MS;
|
|
@@ -79658,6 +80204,8 @@ function startSubagentWatcher(config) {
|
|
|
79658
80204
|
dispatchedAt: n,
|
|
79659
80205
|
lastActivityAt: n,
|
|
79660
80206
|
toolCount: 0,
|
|
80207
|
+
totalTokens: 0,
|
|
80208
|
+
seenUsageMessageIds: new Set,
|
|
79661
80209
|
stallNotified: false,
|
|
79662
80210
|
stalledAt: null,
|
|
79663
80211
|
completionNotified: false,
|
|
@@ -79794,6 +80342,7 @@ function startSubagentWatcher(config) {
|
|
|
79794
80342
|
state: entry.state,
|
|
79795
80343
|
outcome: entry.errored ? "failed" : entry.historical ? "orphan" : "completed",
|
|
79796
80344
|
toolCount: entry.toolCount,
|
|
80345
|
+
totalTokens: entry.totalTokens,
|
|
79797
80346
|
durationMs: nowFn() - entry.dispatchedAt,
|
|
79798
80347
|
description: entry.description,
|
|
79799
80348
|
resultText: entry.errored ? entry.lastResultText || entry.errorDetail || "" : entry.lastResultText,
|
|
@@ -79814,6 +80363,7 @@ function startSubagentWatcher(config) {
|
|
|
79814
80363
|
state: entry.state,
|
|
79815
80364
|
outcome: "failed",
|
|
79816
80365
|
toolCount: entry.toolCount,
|
|
80366
|
+
totalTokens: entry.totalTokens,
|
|
79817
80367
|
durationMs: nowFn() - entry.dispatchedAt,
|
|
79818
80368
|
description: entry.description,
|
|
79819
80369
|
resultText: entry.lastResultText,
|
|
@@ -80070,8 +80620,8 @@ function startSubagentWatcher(config) {
|
|
|
80070
80620
|
if (stopped)
|
|
80071
80621
|
return;
|
|
80072
80622
|
pruneVanishedDirWatchers();
|
|
80073
|
-
const claudeHome =
|
|
80074
|
-
const projectsRoot =
|
|
80623
|
+
const claudeHome = join40(agentDir, ".claude");
|
|
80624
|
+
const projectsRoot = join40(claudeHome, "projects");
|
|
80075
80625
|
if (!fs2.existsSync(projectsRoot))
|
|
80076
80626
|
return;
|
|
80077
80627
|
let projectDirs;
|
|
@@ -80087,7 +80637,7 @@ function startSubagentWatcher(config) {
|
|
|
80087
80637
|
if (extraWatchCwdsProvider != null) {
|
|
80088
80638
|
try {
|
|
80089
80639
|
for (const cwd of extraWatchCwdsProvider()) {
|
|
80090
|
-
allowedSlugs.add(
|
|
80640
|
+
allowedSlugs.add(sanitizeCwdToProjectName2(cwd));
|
|
80091
80641
|
}
|
|
80092
80642
|
} catch (err) {
|
|
80093
80643
|
providerOk = false;
|
|
@@ -80105,7 +80655,7 @@ function startSubagentWatcher(config) {
|
|
|
80105
80655
|
continue;
|
|
80106
80656
|
}
|
|
80107
80657
|
warnedForeignSlugs.delete(pDir);
|
|
80108
|
-
const projectPath =
|
|
80658
|
+
const projectPath = join40(projectsRoot, pDir);
|
|
80109
80659
|
let sessionDirs;
|
|
80110
80660
|
try {
|
|
80111
80661
|
sessionDirs = fs2.readdirSync(projectPath);
|
|
@@ -80115,7 +80665,7 @@ function startSubagentWatcher(config) {
|
|
|
80115
80665
|
for (const sDir of sessionDirs) {
|
|
80116
80666
|
if (sDir.endsWith(".jsonl"))
|
|
80117
80667
|
continue;
|
|
80118
|
-
const subagentsPath =
|
|
80668
|
+
const subagentsPath = join40(projectPath, sDir, "subagents");
|
|
80119
80669
|
if (!fs2.existsSync(subagentsPath))
|
|
80120
80670
|
continue;
|
|
80121
80671
|
const watchAndScan = (dirPath) => {
|
|
@@ -80124,7 +80674,7 @@ function startSubagentWatcher(config) {
|
|
|
80124
80674
|
const w = fs2.watch(dirPath, (_event, filename) => {
|
|
80125
80675
|
if (!filename || !filename.toString().startsWith("agent-") || !filename.toString().endsWith(".jsonl"))
|
|
80126
80676
|
return;
|
|
80127
|
-
const filePath =
|
|
80677
|
+
const filePath = join40(dirPath, filename.toString());
|
|
80128
80678
|
if (!knownFiles.has(filePath)) {
|
|
80129
80679
|
scanSubagentsDir(dirPath);
|
|
80130
80680
|
}
|
|
@@ -80138,7 +80688,7 @@ function startSubagentWatcher(config) {
|
|
|
80138
80688
|
scanSubagentsDir(dirPath);
|
|
80139
80689
|
};
|
|
80140
80690
|
watchAndScan(subagentsPath);
|
|
80141
|
-
const workflowsPath =
|
|
80691
|
+
const workflowsPath = join40(subagentsPath, "workflows");
|
|
80142
80692
|
if (fs2.existsSync(workflowsPath)) {
|
|
80143
80693
|
let wfDirs;
|
|
80144
80694
|
try {
|
|
@@ -80148,7 +80698,7 @@ function startSubagentWatcher(config) {
|
|
|
80148
80698
|
}
|
|
80149
80699
|
for (const wfDir of wfDirs) {
|
|
80150
80700
|
try {
|
|
80151
|
-
const wfPath =
|
|
80701
|
+
const wfPath = join40(workflowsPath, wfDir);
|
|
80152
80702
|
if (!fs2.statSync(wfPath).isDirectory())
|
|
80153
80703
|
continue;
|
|
80154
80704
|
watchAndScan(wfPath);
|
|
@@ -80168,7 +80718,7 @@ function startSubagentWatcher(config) {
|
|
|
80168
80718
|
for (const e of entries) {
|
|
80169
80719
|
if (!e.startsWith("agent-") || !e.endsWith(".jsonl"))
|
|
80170
80720
|
continue;
|
|
80171
|
-
const filePath =
|
|
80721
|
+
const filePath = join40(subagentsPath, e);
|
|
80172
80722
|
if (knownFiles.has(filePath))
|
|
80173
80723
|
continue;
|
|
80174
80724
|
const agentId = e.slice("agent-".length, -".jsonl".length);
|
|
@@ -80280,13 +80830,13 @@ import {
|
|
|
80280
80830
|
existsSync as existsSync36,
|
|
80281
80831
|
renameSync as renameSync15
|
|
80282
80832
|
} from "node:fs";
|
|
80283
|
-
import { join as
|
|
80284
|
-
import { homedir as
|
|
80833
|
+
import { join as join41, resolve as resolve8 } from "node:path";
|
|
80834
|
+
import { homedir as homedir14 } from "node:os";
|
|
80285
80835
|
function registryDir() {
|
|
80286
|
-
return resolve8(process.env.SWITCHROOM_WORKTREE_DIR ??
|
|
80836
|
+
return resolve8(process.env.SWITCHROOM_WORKTREE_DIR ?? join41(homedir14(), ".switchroom", "worktrees"));
|
|
80287
80837
|
}
|
|
80288
80838
|
function recordPath(id) {
|
|
80289
|
-
return
|
|
80839
|
+
return join41(registryDir(), `${id}.json`);
|
|
80290
80840
|
}
|
|
80291
80841
|
function ensureDir4() {
|
|
80292
80842
|
mkdirSync29(registryDir(), { recursive: true });
|
|
@@ -80337,12 +80887,12 @@ function recordExists(id) {
|
|
|
80337
80887
|
|
|
80338
80888
|
// worktree-watch-cwds.ts
|
|
80339
80889
|
import { realpathSync as realpathSync3 } from "node:fs";
|
|
80340
|
-
import { basename as
|
|
80890
|
+
import { basename as basename8 } from "node:path";
|
|
80341
80891
|
var identityEscalated = false;
|
|
80342
80892
|
function defaultDeriveName(agentDir) {
|
|
80343
80893
|
if (!agentDir || agentDir.trim().length === 0)
|
|
80344
80894
|
return "";
|
|
80345
|
-
const leaf =
|
|
80895
|
+
const leaf = basename8(agentDir).trim();
|
|
80346
80896
|
return leaf;
|
|
80347
80897
|
}
|
|
80348
80898
|
function resolveOwnerIdentity(self, agentDir, deriveName) {
|
|
@@ -80465,14 +81015,14 @@ init_boot_card();
|
|
|
80465
81015
|
|
|
80466
81016
|
// gateway/update-announce.ts
|
|
80467
81017
|
import { existsSync as existsSync41, mkdirSync as mkdirSync33, openSync as openSync9, closeSync as closeSync9, readFileSync as readFileSync44 } from "node:fs";
|
|
80468
|
-
import { join as
|
|
80469
|
-
import { homedir as
|
|
81018
|
+
import { join as join46 } from "node:path";
|
|
81019
|
+
import { homedir as homedir16 } from "node:os";
|
|
80470
81020
|
|
|
80471
81021
|
// ../src/host-control/audit-reader.ts
|
|
80472
|
-
import { homedir as
|
|
80473
|
-
import { join as
|
|
80474
|
-
function defaultAuditLogPath(home2 =
|
|
80475
|
-
return
|
|
81022
|
+
import { homedir as homedir15 } from "node:os";
|
|
81023
|
+
import { join as join45 } from "node:path";
|
|
81024
|
+
function defaultAuditLogPath(home2 = homedir15()) {
|
|
81025
|
+
return join45(home2, ".switchroom", "host-control-audit.log");
|
|
80476
81026
|
}
|
|
80477
81027
|
function parseAuditLine(line) {
|
|
80478
81028
|
const trimmed = line.trim();
|
|
@@ -80657,15 +81207,15 @@ function renderUpdateOutcomeLine(entry) {
|
|
|
80657
81207
|
`);
|
|
80658
81208
|
}
|
|
80659
81209
|
function claimUpdateAnnouncement(requestId, opts = {}) {
|
|
80660
|
-
const stateDir = opts.stateDir ?? process.env.TELEGRAM_STATE_DIR ??
|
|
80661
|
-
const dir =
|
|
81210
|
+
const stateDir = opts.stateDir ?? process.env.TELEGRAM_STATE_DIR ?? join46(homedir16(), ".switchroom");
|
|
81211
|
+
const dir = join46(stateDir, "update-announced");
|
|
80662
81212
|
try {
|
|
80663
81213
|
mkdirSync33(dir, { recursive: true });
|
|
80664
81214
|
} catch {
|
|
80665
81215
|
return false;
|
|
80666
81216
|
}
|
|
80667
81217
|
const safeId = requestId.replace(/[^A-Za-z0-9_.-]/g, "_").slice(0, 200);
|
|
80668
|
-
const path2 =
|
|
81218
|
+
const path2 = join46(dir, safeId);
|
|
80669
81219
|
try {
|
|
80670
81220
|
const fd = openSync9(path2, "wx");
|
|
80671
81221
|
closeSync9(fd);
|
|
@@ -80887,7 +81437,7 @@ function createIssuesCardHandle(opts) {
|
|
|
80887
81437
|
|
|
80888
81438
|
// issues-watcher.ts
|
|
80889
81439
|
import { existsSync as existsSync43, statSync as statSync12 } from "node:fs";
|
|
80890
|
-
import { join as
|
|
81440
|
+
import { join as join48 } from "node:path";
|
|
80891
81441
|
|
|
80892
81442
|
// ../src/issues/store.ts
|
|
80893
81443
|
import {
|
|
@@ -80903,7 +81453,7 @@ import {
|
|
|
80903
81453
|
writeFileSync as writeFileSync36,
|
|
80904
81454
|
writeSync as writeSync6
|
|
80905
81455
|
} from "node:fs";
|
|
80906
|
-
import { join as
|
|
81456
|
+
import { join as join47 } from "node:path";
|
|
80907
81457
|
import { randomBytes as randomBytes7 } from "node:crypto";
|
|
80908
81458
|
import { execSync } from "node:child_process";
|
|
80909
81459
|
|
|
@@ -80922,7 +81472,7 @@ init_redact();
|
|
|
80922
81472
|
var ISSUES_FILE = "issues.jsonl";
|
|
80923
81473
|
var ISSUES_LOCK = "issues.lock";
|
|
80924
81474
|
function readAll(stateDir) {
|
|
80925
|
-
const path2 =
|
|
81475
|
+
const path2 = join47(stateDir, ISSUES_FILE);
|
|
80926
81476
|
if (!existsSync42(path2))
|
|
80927
81477
|
return [];
|
|
80928
81478
|
let raw;
|
|
@@ -80959,7 +81509,7 @@ function list2(stateDir, opts = {}) {
|
|
|
80959
81509
|
});
|
|
80960
81510
|
}
|
|
80961
81511
|
function resolve9(stateDir, fingerprint, nowFn = Date.now) {
|
|
80962
|
-
if (!existsSync42(
|
|
81512
|
+
if (!existsSync42(join47(stateDir, ISSUES_FILE)))
|
|
80963
81513
|
return 0;
|
|
80964
81514
|
return withLock(stateDir, () => {
|
|
80965
81515
|
const all2 = readAll(stateDir);
|
|
@@ -80977,7 +81527,7 @@ function resolve9(stateDir, fingerprint, nowFn = Date.now) {
|
|
|
80977
81527
|
});
|
|
80978
81528
|
}
|
|
80979
81529
|
function writeAll(stateDir, events) {
|
|
80980
|
-
const path2 =
|
|
81530
|
+
const path2 = join47(stateDir, ISSUES_FILE);
|
|
80981
81531
|
sweepOrphanTmpFiles(stateDir);
|
|
80982
81532
|
const tmp = `${path2}.tmp-${process.pid}-${randomBytes7(4).toString("hex")}`;
|
|
80983
81533
|
const body = events.length === 0 ? "" : events.map((e) => JSON.stringify(e)).join(`
|
|
@@ -80999,7 +81549,7 @@ function sweepOrphanTmpFiles(stateDir) {
|
|
|
80999
81549
|
for (const entry of entries) {
|
|
81000
81550
|
if (!entry.startsWith(TMP_PREFIX))
|
|
81001
81551
|
continue;
|
|
81002
|
-
const tmpPath2 =
|
|
81552
|
+
const tmpPath2 = join47(stateDir, entry);
|
|
81003
81553
|
try {
|
|
81004
81554
|
const stat = statSync11(tmpPath2);
|
|
81005
81555
|
if (stat.mtimeMs < cutoff) {
|
|
@@ -81011,7 +81561,7 @@ function sweepOrphanTmpFiles(stateDir) {
|
|
|
81011
81561
|
var LOCK_RETRY_MS = 25;
|
|
81012
81562
|
var LOCK_TIMEOUT_MS = 1e4;
|
|
81013
81563
|
function withLock(stateDir, fn) {
|
|
81014
|
-
const lockPath =
|
|
81564
|
+
const lockPath = join47(stateDir, ISSUES_LOCK);
|
|
81015
81565
|
const startedAt = Date.now();
|
|
81016
81566
|
let fd = null;
|
|
81017
81567
|
while (fd === null) {
|
|
@@ -81096,7 +81646,7 @@ function isIssueEvent(v) {
|
|
|
81096
81646
|
// issues-watcher.ts
|
|
81097
81647
|
var DEFAULT_POLL_INTERVAL_MS2 = 2000;
|
|
81098
81648
|
function startIssuesWatcher(opts) {
|
|
81099
|
-
const path2 =
|
|
81649
|
+
const path2 = join48(opts.stateDir, ISSUES_FILE);
|
|
81100
81650
|
const log = opts.log ?? (() => {});
|
|
81101
81651
|
const intervalMs = opts.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS2;
|
|
81102
81652
|
const setIntervalFn = opts.setInterval ?? setInterval;
|
|
@@ -81158,10 +81708,10 @@ function defaultReadEvents(stateDir) {
|
|
|
81158
81708
|
}
|
|
81159
81709
|
// permission-title.ts
|
|
81160
81710
|
init_card_format();
|
|
81161
|
-
import { basename as
|
|
81711
|
+
import { basename as basename10 } from "node:path";
|
|
81162
81712
|
|
|
81163
81713
|
// permission-rule.ts
|
|
81164
|
-
import { basename as
|
|
81714
|
+
import { basename as basename9 } from "node:path";
|
|
81165
81715
|
var FILE_TOOLS = new Set([
|
|
81166
81716
|
"Edit",
|
|
81167
81717
|
"Write",
|
|
@@ -81239,7 +81789,7 @@ function skillBasenameFromPath(input) {
|
|
|
81239
81789
|
if (!path2)
|
|
81240
81790
|
return null;
|
|
81241
81791
|
const trimmed = path2.replace(/\/SKILL\.md$/i, "").replace(/\/$/, "");
|
|
81242
|
-
return
|
|
81792
|
+
return basename9(trimmed) || null;
|
|
81243
81793
|
}
|
|
81244
81794
|
function matchesAllowRule(rule, toolName, inputPreview) {
|
|
81245
81795
|
if (!rule || !toolName)
|
|
@@ -81531,11 +82081,11 @@ function describeGrant(toolName, inputPreview, option) {
|
|
|
81531
82081
|
return m ? `run ${m[1]} commands` : "run that command";
|
|
81532
82082
|
}
|
|
81533
82083
|
if (t === "Edit" || t === "MultiEdit" || t === "NotebookEdit")
|
|
81534
|
-
return `edit ${
|
|
82084
|
+
return `edit ${basename10(arg)}`;
|
|
81535
82085
|
if (t === "Write")
|
|
81536
|
-
return `write ${
|
|
82086
|
+
return `write ${basename10(arg)}`;
|
|
81537
82087
|
if (t === "Read")
|
|
81538
|
-
return `read ${
|
|
82088
|
+
return `read ${basename10(arg)}`;
|
|
81539
82089
|
return naturalAction(toolName, inputPreview);
|
|
81540
82090
|
}
|
|
81541
82091
|
switch (rule) {
|
|
@@ -81574,12 +82124,12 @@ function fileBase(input, rawPreview) {
|
|
|
81574
82124
|
if (input) {
|
|
81575
82125
|
const p = readString2(input, "file_path") ?? readString2(input, "notebook_path");
|
|
81576
82126
|
if (p)
|
|
81577
|
-
return
|
|
82127
|
+
return basename10(p);
|
|
81578
82128
|
}
|
|
81579
82129
|
if (rawPreview) {
|
|
81580
82130
|
const p = extractFilePathFromRaw2(rawPreview);
|
|
81581
82131
|
if (p)
|
|
81582
|
-
return
|
|
82132
|
+
return basename10(p);
|
|
81583
82133
|
}
|
|
81584
82134
|
return null;
|
|
81585
82135
|
}
|
|
@@ -81710,7 +82260,7 @@ function truncate6(text4, max) {
|
|
|
81710
82260
|
}
|
|
81711
82261
|
|
|
81712
82262
|
// permission-rule.ts
|
|
81713
|
-
import { basename as
|
|
82263
|
+
import { basename as basename11 } from "node:path";
|
|
81714
82264
|
var FILE_TOOLS2 = new Set([
|
|
81715
82265
|
"Edit",
|
|
81716
82266
|
"Write",
|
|
@@ -81842,14 +82392,14 @@ function skillBasenameFromPath3(input) {
|
|
|
81842
82392
|
if (!path2)
|
|
81843
82393
|
return null;
|
|
81844
82394
|
const trimmed = path2.replace(/\/SKILL\.md$/i, "").replace(/\/$/, "");
|
|
81845
|
-
return
|
|
82395
|
+
return basename11(trimmed) || null;
|
|
81846
82396
|
}
|
|
81847
82397
|
function isRulePersisted(resolvedAllow, ruleRule) {
|
|
81848
82398
|
return resolvedAllow.includes(ruleRule);
|
|
81849
82399
|
}
|
|
81850
82400
|
|
|
81851
82401
|
// scoped-approval.ts
|
|
81852
|
-
import { basename as
|
|
82402
|
+
import { basename as basename12 } from "node:path";
|
|
81853
82403
|
var SCOPED_APPROVAL_DEFAULT_TTL_MS = 30 * 60 * 1000;
|
|
81854
82404
|
function scopedApprovalTtlMs(env = process.env) {
|
|
81855
82405
|
const raw = env.SWITCHROOM_SCOPED_APPROVAL_TTL_MS;
|
|
@@ -81874,7 +82424,7 @@ function resolveTimeBox(toolName, inputPreview, choices) {
|
|
|
81874
82424
|
const fileMatch = FILE_RULE.exec(rule);
|
|
81875
82425
|
if (fileMatch) {
|
|
81876
82426
|
const verb = fileMatch[1] === "Read" ? "reads of" : "edits to";
|
|
81877
|
-
return { rule, breadth: `${verb} ${
|
|
82427
|
+
return { rule, breadth: `${verb} ${basename12(fileMatch[2])}` };
|
|
81878
82428
|
}
|
|
81879
82429
|
const bashMatch = BASH_FAMILY_RULE.exec(rule);
|
|
81880
82430
|
if (bashMatch) {
|
|
@@ -81975,7 +82525,7 @@ function readBashCommand(inputPreview) {
|
|
|
81975
82525
|
|
|
81976
82526
|
// gateway/scoped-grant-store.ts
|
|
81977
82527
|
import { readFileSync as readFileSync47, writeFileSync as writeFileSync37 } from "node:fs";
|
|
81978
|
-
import { join as
|
|
82528
|
+
import { join as join49 } from "node:path";
|
|
81979
82529
|
|
|
81980
82530
|
// scoped-approval.ts
|
|
81981
82531
|
var SCOPED_APPROVAL_DEFAULT_TTL_MS2 = 30 * 60 * 1000;
|
|
@@ -82017,7 +82567,7 @@ function scopedGrantPersistEnabled(env = process.env) {
|
|
|
82017
82567
|
return env.SWITCHROOM_SCOPED_GRANT_PERSIST !== "0";
|
|
82018
82568
|
}
|
|
82019
82569
|
function createScopedGrantStore(stateDir, env = process.env) {
|
|
82020
|
-
const filePath =
|
|
82570
|
+
const filePath = join49(stateDir, "scoped-grants.json");
|
|
82021
82571
|
const enabled8 = scopedGrantPersistEnabled(env);
|
|
82022
82572
|
function read() {
|
|
82023
82573
|
try {
|
|
@@ -82391,7 +82941,7 @@ function isDiffPreApproved(agentName3, unifiedDiff, deps) {
|
|
|
82391
82941
|
// credits-watch.ts
|
|
82392
82942
|
init_card_format();
|
|
82393
82943
|
import { readFileSync as readFileSync48, writeFileSync as writeFileSync38, existsSync as existsSync44, mkdirSync as mkdirSync35 } from "fs";
|
|
82394
|
-
import { join as
|
|
82944
|
+
import { join as join50 } from "path";
|
|
82395
82945
|
var STATE_FILE = "credits-watch.json";
|
|
82396
82946
|
var DEFAULT_CREDIT_FATAL_REASONS = new Set;
|
|
82397
82947
|
var KNOWN_CREDIT_REASONS = [
|
|
@@ -82413,7 +82963,7 @@ function emptyCreditState() {
|
|
|
82413
82963
|
return { lastNotifiedReason: null, lastNotifiedAt: 0 };
|
|
82414
82964
|
}
|
|
82415
82965
|
function readClaudeJsonOverage(claudeConfigDir) {
|
|
82416
|
-
const path2 =
|
|
82966
|
+
const path2 = join50(claudeConfigDir, ".claude.json");
|
|
82417
82967
|
if (!existsSync44(path2))
|
|
82418
82968
|
return null;
|
|
82419
82969
|
let raw;
|
|
@@ -82496,7 +83046,7 @@ function humanizeReason(reason) {
|
|
|
82496
83046
|
}
|
|
82497
83047
|
}
|
|
82498
83048
|
function loadCreditState(stateDir) {
|
|
82499
|
-
const path2 =
|
|
83049
|
+
const path2 = join50(stateDir, STATE_FILE);
|
|
82500
83050
|
if (!existsSync44(path2))
|
|
82501
83051
|
return emptyCreditState();
|
|
82502
83052
|
try {
|
|
@@ -82513,7 +83063,7 @@ function loadCreditState(stateDir) {
|
|
|
82513
83063
|
}
|
|
82514
83064
|
function saveCreditState(stateDir, state4) {
|
|
82515
83065
|
mkdirSync35(stateDir, { recursive: true });
|
|
82516
|
-
const path2 =
|
|
83066
|
+
const path2 = join50(stateDir, STATE_FILE);
|
|
82517
83067
|
writeFileSync38(path2, JSON.stringify(state4, null, 2) + `
|
|
82518
83068
|
`, { mode: 384 });
|
|
82519
83069
|
}
|
|
@@ -82522,7 +83072,7 @@ function saveCreditState(stateDir, state4) {
|
|
|
82522
83072
|
init_auth_snapshot_format();
|
|
82523
83073
|
init_card_format();
|
|
82524
83074
|
import { readFileSync as readFileSync49, writeFileSync as writeFileSync39, existsSync as existsSync45, mkdirSync as mkdirSync36 } from "fs";
|
|
82525
|
-
import { join as
|
|
83075
|
+
import { join as join51 } from "path";
|
|
82526
83076
|
var STATE_FILE2 = "quota-watch.json";
|
|
82527
83077
|
function emptyQuotaWatchState() {
|
|
82528
83078
|
return {};
|
|
@@ -82760,7 +83310,7 @@ function buildRecoveryMessage(agentName3, snap) {
|
|
|
82760
83310
|
`);
|
|
82761
83311
|
}
|
|
82762
83312
|
function loadQuotaWatchState(stateDir) {
|
|
82763
|
-
const path2 =
|
|
83313
|
+
const path2 = join51(stateDir, STATE_FILE2);
|
|
82764
83314
|
if (!existsSync45(path2))
|
|
82765
83315
|
return emptyQuotaWatchState();
|
|
82766
83316
|
try {
|
|
@@ -82782,7 +83332,7 @@ function loadQuotaWatchState(stateDir) {
|
|
|
82782
83332
|
}
|
|
82783
83333
|
function saveQuotaWatchState(stateDir, state4) {
|
|
82784
83334
|
mkdirSync36(stateDir, { recursive: true });
|
|
82785
|
-
const path2 =
|
|
83335
|
+
const path2 = join51(stateDir, STATE_FILE2);
|
|
82786
83336
|
writeFileSync39(path2, JSON.stringify(state4, null, 2) + `
|
|
82787
83337
|
`, { mode: 384 });
|
|
82788
83338
|
}
|
|
@@ -82840,17 +83390,17 @@ import {
|
|
|
82840
83390
|
utimesSync as utimesSync2,
|
|
82841
83391
|
writeFileSync as writeFileSync40
|
|
82842
83392
|
} from "node:fs";
|
|
82843
|
-
import { join as
|
|
83393
|
+
import { join as join52 } from "node:path";
|
|
82844
83394
|
var TURN_ACTIVE_MARKER_FILE2 = "turn-active.json";
|
|
82845
83395
|
function writeTurnActiveMarker(stateDir, marker) {
|
|
82846
83396
|
try {
|
|
82847
83397
|
mkdirSync37(stateDir, { recursive: true });
|
|
82848
|
-
writeFileSync40(
|
|
83398
|
+
writeFileSync40(join52(stateDir, TURN_ACTIVE_MARKER_FILE2), JSON.stringify(marker, null, 2) + `
|
|
82849
83399
|
`, { mode: 384 });
|
|
82850
83400
|
} catch {}
|
|
82851
83401
|
}
|
|
82852
83402
|
function touchTurnActiveMarker2(stateDir) {
|
|
82853
|
-
const path2 =
|
|
83403
|
+
const path2 = join52(stateDir, TURN_ACTIVE_MARKER_FILE2);
|
|
82854
83404
|
if (!existsSync46(path2))
|
|
82855
83405
|
return;
|
|
82856
83406
|
const now = new Date;
|
|
@@ -82865,11 +83415,11 @@ function touchTurnActiveMarker2(stateDir) {
|
|
|
82865
83415
|
}
|
|
82866
83416
|
function removeTurnActiveMarker(stateDir) {
|
|
82867
83417
|
try {
|
|
82868
|
-
unlinkSync21(
|
|
83418
|
+
unlinkSync21(join52(stateDir, TURN_ACTIVE_MARKER_FILE2));
|
|
82869
83419
|
} catch {}
|
|
82870
83420
|
}
|
|
82871
83421
|
function sweepStaleTurnActiveMarker(stateDir, opts) {
|
|
82872
|
-
const path2 =
|
|
83422
|
+
const path2 = join52(stateDir, TURN_ACTIVE_MARKER_FILE2);
|
|
82873
83423
|
if (!existsSync46(path2))
|
|
82874
83424
|
return false;
|
|
82875
83425
|
const now = opts.now ?? Date.now();
|
|
@@ -82900,7 +83450,7 @@ function sweepStaleTurnActiveMarker(stateDir, opts) {
|
|
|
82900
83450
|
}
|
|
82901
83451
|
}
|
|
82902
83452
|
function readTurnActiveMarkerAgeMs(stateDir, now) {
|
|
82903
|
-
const path2 =
|
|
83453
|
+
const path2 = join52(stateDir, TURN_ACTIVE_MARKER_FILE2);
|
|
82904
83454
|
try {
|
|
82905
83455
|
const st = statSync13(path2);
|
|
82906
83456
|
return (now ?? Date.now()) - st.mtimeMs;
|
|
@@ -82910,10 +83460,10 @@ function readTurnActiveMarkerAgeMs(stateDir, now) {
|
|
|
82910
83460
|
}
|
|
82911
83461
|
|
|
82912
83462
|
// ../src/build-info.ts
|
|
82913
|
-
var VERSION = "0.18.
|
|
82914
|
-
var COMMIT_SHA = "
|
|
82915
|
-
var COMMIT_DATE = "2026-07-
|
|
82916
|
-
var LATEST_PR =
|
|
83463
|
+
var VERSION = "0.18.25";
|
|
83464
|
+
var COMMIT_SHA = "01803cff";
|
|
83465
|
+
var COMMIT_DATE = "2026-07-15T16:26:26+10:00";
|
|
83466
|
+
var LATEST_PR = 3254;
|
|
82917
83467
|
var COMMITS_AHEAD_OF_TAG = 0;
|
|
82918
83468
|
|
|
82919
83469
|
// gateway/boot-version.ts
|
|
@@ -82990,12 +83540,12 @@ init_protocol();
|
|
|
82990
83540
|
init_peercred();
|
|
82991
83541
|
import * as net5 from "node:net";
|
|
82992
83542
|
import * as fs2 from "node:fs";
|
|
82993
|
-
import { homedir as
|
|
82994
|
-
import { join as
|
|
83543
|
+
import { homedir as homedir17 } from "node:os";
|
|
83544
|
+
import { join as join53 } from "node:path";
|
|
82995
83545
|
var DEFAULT_TIMEOUT_MS4 = 2000;
|
|
82996
83546
|
var UNLOCK_TIMEOUT_MS = 30000;
|
|
82997
|
-
var LEGACY_SOCKET_PATH2 =
|
|
82998
|
-
var OPERATOR_SOCKET_PATH2 =
|
|
83547
|
+
var LEGACY_SOCKET_PATH2 = join53(homedir17(), ".switchroom", "vault-broker.sock");
|
|
83548
|
+
var OPERATOR_SOCKET_PATH2 = join53(homedir17(), ".switchroom", "broker-operator", "sock");
|
|
82999
83549
|
function defaultBrokerSocketPath2() {
|
|
83000
83550
|
if (fs2.existsSync(OPERATOR_SOCKET_PATH2))
|
|
83001
83551
|
return OPERATOR_SOCKET_PATH2;
|
|
@@ -83878,7 +84428,7 @@ function resolveVaultApprovalPosture(broker) {
|
|
|
83878
84428
|
|
|
83879
84429
|
// registry/turns-schema.ts
|
|
83880
84430
|
import { chmodSync as chmodSync10, mkdirSync as mkdirSync38 } from "fs";
|
|
83881
|
-
import { join as
|
|
84431
|
+
import { join as join54 } from "path";
|
|
83882
84432
|
var DatabaseClass2 = null;
|
|
83883
84433
|
function loadDatabaseClass2() {
|
|
83884
84434
|
if (DatabaseClass2 != null)
|
|
@@ -83929,12 +84479,16 @@ var PHASE2_MIGRATIONS = [
|
|
|
83929
84479
|
var PHASE3_MIGRATIONS = [
|
|
83930
84480
|
`ALTER TABLE turns ADD COLUMN resumed_at INTEGER`
|
|
83931
84481
|
];
|
|
84482
|
+
var PHASE4_MIGRATIONS = [
|
|
84483
|
+
`ALTER TABLE turns ADD COLUMN session_id TEXT`,
|
|
84484
|
+
`ALTER TABLE turns ADD COLUMN answer_redelivered_at INTEGER`
|
|
84485
|
+
];
|
|
83932
84486
|
function applySchema(db2) {
|
|
83933
84487
|
db2.exec("PRAGMA journal_mode = WAL");
|
|
83934
84488
|
db2.exec("PRAGMA synchronous = NORMAL");
|
|
83935
84489
|
db2.exec("PRAGMA busy_timeout = 5000");
|
|
83936
84490
|
db2.exec(SCHEMA_SQL);
|
|
83937
|
-
for (const sql of [...PHASE1_MIGRATIONS, ...PHASE2_MIGRATIONS, ...PHASE3_MIGRATIONS]) {
|
|
84491
|
+
for (const sql of [...PHASE1_MIGRATIONS, ...PHASE2_MIGRATIONS, ...PHASE3_MIGRATIONS, ...PHASE4_MIGRATIONS]) {
|
|
83938
84492
|
try {
|
|
83939
84493
|
db2.exec(sql);
|
|
83940
84494
|
} catch (err) {
|
|
@@ -83946,9 +84500,9 @@ function applySchema(db2) {
|
|
|
83946
84500
|
}
|
|
83947
84501
|
function openTurnsDb(agentDir) {
|
|
83948
84502
|
const Database = loadDatabaseClass2();
|
|
83949
|
-
const dir =
|
|
84503
|
+
const dir = join54(agentDir, "telegram");
|
|
83950
84504
|
mkdirSync38(dir, { recursive: true, mode: 448 });
|
|
83951
|
-
const path2 =
|
|
84505
|
+
const path2 = join54(dir, "registry.db");
|
|
83952
84506
|
const db2 = new Database(path2, { create: true });
|
|
83953
84507
|
applySchema(db2);
|
|
83954
84508
|
try {
|
|
@@ -83977,6 +84531,8 @@ function mapRow(row) {
|
|
|
83977
84531
|
tool_call_count: row.tool_call_count,
|
|
83978
84532
|
interrupt_reason: row.interrupt_reason,
|
|
83979
84533
|
resumed_at: row.resumed_at,
|
|
84534
|
+
session_id: row.session_id ?? null,
|
|
84535
|
+
answer_redelivered_at: row.answer_redelivered_at ?? null,
|
|
83980
84536
|
created_at: row.created_at,
|
|
83981
84537
|
updated_at: row.updated_at
|
|
83982
84538
|
};
|
|
@@ -84072,6 +84628,24 @@ function markTurnResumed(db2, turnKey2, now = Date.now()) {
|
|
|
84072
84628
|
WHERE turn_key = ? AND resumed_at IS NULL
|
|
84073
84629
|
`).run(now, now, turnKey2);
|
|
84074
84630
|
}
|
|
84631
|
+
function stampTurnSessionId(db2, turnKey2, sessionId, now = Date.now()) {
|
|
84632
|
+
if (!sessionId)
|
|
84633
|
+
return;
|
|
84634
|
+
db2.prepare(`
|
|
84635
|
+
UPDATE turns
|
|
84636
|
+
SET session_id = ?,
|
|
84637
|
+
updated_at = ?
|
|
84638
|
+
WHERE turn_key = ? AND session_id IS NULL
|
|
84639
|
+
`).run(sessionId, now, turnKey2);
|
|
84640
|
+
}
|
|
84641
|
+
function markAnswerRedelivered(db2, turnKey2, now = Date.now()) {
|
|
84642
|
+
db2.prepare(`
|
|
84643
|
+
UPDATE turns
|
|
84644
|
+
SET answer_redelivered_at = ?,
|
|
84645
|
+
updated_at = ?
|
|
84646
|
+
WHERE turn_key = ? AND answer_redelivered_at IS NULL
|
|
84647
|
+
`).run(now, now, turnKey2);
|
|
84648
|
+
}
|
|
84075
84649
|
function findLatestTurnIfInterrupted(db2) {
|
|
84076
84650
|
const row = db2.prepare(`
|
|
84077
84651
|
SELECT * FROM turns
|
|
@@ -84701,7 +85275,7 @@ installGlobalErrorHandlers();
|
|
|
84701
85275
|
process.on("beforeExit", () => {
|
|
84702
85276
|
shutdownAnalytics();
|
|
84703
85277
|
});
|
|
84704
|
-
var STATE_DIR = process.env.TELEGRAM_STATE_DIR ??
|
|
85278
|
+
var STATE_DIR = process.env.TELEGRAM_STATE_DIR ?? join55(homedir18(), ".claude", "channels", "telegram");
|
|
84705
85279
|
var permCardStore = createPermissionCardStore(STATE_DIR);
|
|
84706
85280
|
var BLOCKED_APPROVALS_DIR = process.env.SWITCHROOM_BLOCKED_APPROVALS_DIR ?? "/state/blocked-approvals";
|
|
84707
85281
|
var AGENT_NAME = process.env.SWITCHROOM_AGENT_NAME ?? "agent";
|
|
@@ -84801,11 +85375,11 @@ function scheduleAlwaysAllowPersistDrain() {
|
|
|
84801
85375
|
}, ALWAYS_ALLOW_DRAIN_INTERVAL_MS);
|
|
84802
85376
|
timer3.unref?.();
|
|
84803
85377
|
}
|
|
84804
|
-
var ACCESS_FILE =
|
|
84805
|
-
var APPROVED_DIR =
|
|
84806
|
-
var ENV_FILE =
|
|
84807
|
-
var INBOX_DIR =
|
|
84808
|
-
var PEOPLE_FILE =
|
|
85378
|
+
var ACCESS_FILE = join55(STATE_DIR, "access.json");
|
|
85379
|
+
var APPROVED_DIR = join55(STATE_DIR, "approved");
|
|
85380
|
+
var ENV_FILE = join55(STATE_DIR, ".env");
|
|
85381
|
+
var INBOX_DIR = join55(STATE_DIR, "inbox");
|
|
85382
|
+
var PEOPLE_FILE = join55(STATE_DIR, "people.json");
|
|
84809
85383
|
function triggerSelfRestart(targetAgent, reason, delayMs = 300) {
|
|
84810
85384
|
const isDocker = process.env.SWITCHROOM_RUNTIME === "docker";
|
|
84811
85385
|
const selfAgent = process.env.SWITCHROOM_AGENT_NAME;
|
|
@@ -85002,7 +85576,7 @@ function assertSendable(f) {
|
|
|
85002
85576
|
} catch {
|
|
85003
85577
|
throw new Error(`refusing to send file \u2014 cannot resolve real path: ${f}`);
|
|
85004
85578
|
}
|
|
85005
|
-
const inbox =
|
|
85579
|
+
const inbox = join55(stateReal, "inbox");
|
|
85006
85580
|
if (real.startsWith(stateReal + sep3) && !real.startsWith(inbox + sep3)) {
|
|
85007
85581
|
throw new Error(`refusing to send channel state: ${f}`);
|
|
85008
85582
|
}
|
|
@@ -85127,7 +85701,7 @@ var HISTORY_ENABLED = HISTORY_ACCESS.historyEnabled !== false;
|
|
|
85127
85701
|
if (HISTORY_ENABLED) {
|
|
85128
85702
|
try {
|
|
85129
85703
|
initHistory(STATE_DIR, HISTORY_ACCESS.historyRetentionDays ?? 30);
|
|
85130
|
-
process.stderr.write(`telegram gateway: history capture enabled at ${
|
|
85704
|
+
process.stderr.write(`telegram gateway: history capture enabled at ${join55(STATE_DIR, "history.db")}
|
|
85131
85705
|
`);
|
|
85132
85706
|
} catch (err) {
|
|
85133
85707
|
process.stderr.write(`telegram gateway: history init failed (${err.message}) \u2014 capture disabled
|
|
@@ -85136,6 +85710,7 @@ if (HISTORY_ENABLED) {
|
|
|
85136
85710
|
}
|
|
85137
85711
|
var turnsDb = null;
|
|
85138
85712
|
var bootResumeInbound = null;
|
|
85713
|
+
var pendingRedelivery = null;
|
|
85139
85714
|
var bridgeDeadPriorStreak = 0;
|
|
85140
85715
|
try {
|
|
85141
85716
|
const agentDir = STATE_DIR.endsWith("/telegram") ? STATE_DIR.slice(0, -"/telegram".length) : STATE_DIR;
|
|
@@ -85144,7 +85719,7 @@ try {
|
|
|
85144
85719
|
let markerTurnKey = null;
|
|
85145
85720
|
let markerAgeMs = null;
|
|
85146
85721
|
try {
|
|
85147
|
-
const markerPath =
|
|
85722
|
+
const markerPath = join55(STATE_DIR, TURN_ACTIVE_MARKER_FILE2);
|
|
85148
85723
|
if (existsSync50(markerPath)) {
|
|
85149
85724
|
const st = statSync16(markerPath);
|
|
85150
85725
|
markerAgeMs = Date.now() - st.mtimeMs;
|
|
@@ -85169,10 +85744,10 @@ try {
|
|
|
85169
85744
|
process.stderr.write(`telegram gateway: turn-registry boot-reaper stamped ${reaped} orphaned turn(s)${timeoutTurnKey ? ` (turnKey=${timeoutTurnKey} as 'timeout', markerAgeMs=${markerAgeMs})` : " as 'restart'"}
|
|
85170
85745
|
`);
|
|
85171
85746
|
} else {
|
|
85172
|
-
process.stderr.write(`telegram gateway: turn-registry initialized at ${
|
|
85747
|
+
process.stderr.write(`telegram gateway: turn-registry initialized at ${join55(agentDir, "telegram", "registry.db")}
|
|
85173
85748
|
`);
|
|
85174
85749
|
}
|
|
85175
|
-
const bridgeDeadMarker = consumeBridgeDeadEscalationMarker(
|
|
85750
|
+
const bridgeDeadMarker = consumeBridgeDeadEscalationMarker(join55(STATE_DIR, "bridge-dead-escalation.json"));
|
|
85176
85751
|
if (bridgeDeadMarker != null) {
|
|
85177
85752
|
bridgeDeadPriorStreak = bridgeDeadMarker.count ?? 1;
|
|
85178
85753
|
process.stderr.write(`telegram gateway: boot: prior restart was a bridge-dead escalation (reason=${bridgeDeadMarker.reason}, consecutive=${bridgeDeadPriorStreak}${bridgeDeadMarker.crashTail ? `, crashTail=${bridgeDeadMarker.crashTail}` : ""})
|
|
@@ -85185,7 +85760,7 @@ try {
|
|
|
85185
85760
|
const pending2 = findLatestTurnIfInterrupted(turnsDb);
|
|
85186
85761
|
const selfAgent = process.env.SWITCHROOM_AGENT_NAME ?? "";
|
|
85187
85762
|
if (pending2 != null && selfAgent) {
|
|
85188
|
-
const bootResumeMarkerPath = process.env.SWITCHROOM_GATEWAY_CLEAN_SHUTDOWN_MARKER ??
|
|
85763
|
+
const bootResumeMarkerPath = process.env.SWITCHROOM_GATEWAY_CLEAN_SHUTDOWN_MARKER ?? join55(STATE_DIR, "clean-shutdown.json");
|
|
85189
85764
|
const bootResumeCleanMarker = readCleanShutdownMarker(bootResumeMarkerPath);
|
|
85190
85765
|
const bootResumeForceAlways = process.env.SWITCHROOM_BOOT_RESUME_ALWAYS === "1";
|
|
85191
85766
|
const bootResumeMode = parseBootResumeMode(process.env.SWITCHROOM_BOOT_RESUME);
|
|
@@ -85203,6 +85778,19 @@ try {
|
|
|
85203
85778
|
ageMs: Math.max(0, Date.now() - pending2.started_at),
|
|
85204
85779
|
maxAgeMs: RESUME_MAX_AGE_MS
|
|
85205
85780
|
});
|
|
85781
|
+
const redeliverCapture = decideRedeliverCapture({
|
|
85782
|
+
willBeResumed: bootResumeKind === "resume",
|
|
85783
|
+
hasSessionId: Boolean(pending2.session_id)
|
|
85784
|
+
});
|
|
85785
|
+
if (redeliverCapture.capture) {
|
|
85786
|
+
pendingRedelivery = { turn: pending2, maxAgeMs: RESUME_MAX_AGE_MS };
|
|
85787
|
+
} else if (redeliverCapture.skipReason === "will-be-resumed") {
|
|
85788
|
+
process.stderr.write(`telegram gateway: crash-redelivery suppressed \u2014 interrupted turnKey=${pending2.turn_key} will be RESUMED (bootResumeKind=resume); the fresh re-answer supersedes the recovered draft (no double-send)
|
|
85789
|
+
`);
|
|
85790
|
+
} else {
|
|
85791
|
+
process.stderr.write(`telegram gateway: crash-redelivery skipped \u2014 interrupted turnKey=${pending2.turn_key} has no pinned session_id (pre-feature turn or no session event seen); cannot resolve exact transcript
|
|
85792
|
+
`);
|
|
85793
|
+
}
|
|
85206
85794
|
let interruptedSubagents = [];
|
|
85207
85795
|
try {
|
|
85208
85796
|
interruptedSubagents = listNonTerminalSubagentsForTurn(turnsDb, pending2.turn_key).map((s) => ({ agentType: s.agent_type, description: s.description, status: s.status }));
|
|
@@ -85285,7 +85873,7 @@ try {
|
|
|
85285
85873
|
`);
|
|
85286
85874
|
}
|
|
85287
85875
|
}
|
|
85288
|
-
const pendingEnvPath =
|
|
85876
|
+
const pendingEnvPath = join55(agentDir, ".pending-turn.env");
|
|
85289
85877
|
try {
|
|
85290
85878
|
if (pending2 != null) {
|
|
85291
85879
|
const lines = [
|
|
@@ -85340,6 +85928,11 @@ function resolveSubagentOriginChat(agentId) {
|
|
|
85340
85928
|
}
|
|
85341
85929
|
var WORKER_FEED_FALLBACK_LOG_CAP = 256;
|
|
85342
85930
|
var WORKER_FEED_STALE_TTL_MARGIN_MS = 300000;
|
|
85931
|
+
var WORKER_FEED_ABSOLUTE_ROW_LIFETIME_CAP_MULTIPLE = 4;
|
|
85932
|
+
var WORKER_FEED_GROUP_MESSAGE_LIFETIME_CAP_MS = (() => {
|
|
85933
|
+
const v = Number(process.env.SWITCHROOM_WORKER_FEED_GROUP_MESSAGE_LIFETIME_CAP_MS);
|
|
85934
|
+
return Number.isFinite(v) && v > 0 ? v : 3600000;
|
|
85935
|
+
})();
|
|
85343
85936
|
var workerFeedOwnerDmFallbackLogged = new Set;
|
|
85344
85937
|
function resolveWorkerFeedChat(agentId, fleetChatId) {
|
|
85345
85938
|
const origin = resolveSubagentOriginChat(agentId);
|
|
@@ -85402,7 +85995,7 @@ function checkApprovals() {
|
|
|
85402
85995
|
return;
|
|
85403
85996
|
}
|
|
85404
85997
|
for (const senderId of files) {
|
|
85405
|
-
const file =
|
|
85998
|
+
const file = join55(APPROVED_DIR, senderId);
|
|
85406
85999
|
bot.api.sendMessage(senderId, "Paired! Say hi to Claude.").then(() => rmSync6(file, { force: true }), (err) => {
|
|
85407
86000
|
process.stderr.write(`telegram gateway: failed to send approval confirm: ${err}
|
|
85408
86001
|
`);
|
|
@@ -85507,7 +86100,7 @@ function noteAgentOutputAt(key, ts) {
|
|
|
85507
86100
|
lastAgentOutputAt.delete(oldest);
|
|
85508
86101
|
}
|
|
85509
86102
|
}
|
|
85510
|
-
var OBLIGATION_STORE_PATH =
|
|
86103
|
+
var OBLIGATION_STORE_PATH = join55(STATE_DIR, "obligations.json");
|
|
85511
86104
|
var obligationStoreFs = {
|
|
85512
86105
|
readFileSync: (p) => readFileSync54(p, "utf8"),
|
|
85513
86106
|
writeFileSync: (p, d) => writeFileSync43(p, d),
|
|
@@ -85622,6 +86215,7 @@ var pendingRestarts = new Map;
|
|
|
85622
86215
|
var pendingSessionCommand = createPendingSessionCommandSlots();
|
|
85623
86216
|
var PENDING_CMD_DRAIN_CAP_MS = 60000;
|
|
85624
86217
|
var lastSessionActiveFile = null;
|
|
86218
|
+
var lastSessionStampedTurnKey = null;
|
|
85625
86219
|
var compactState = initialCompactState();
|
|
85626
86220
|
var compactDispatching = false;
|
|
85627
86221
|
var COMPACT_CARD_TIMEOUT_MS = 900000;
|
|
@@ -85748,6 +86342,26 @@ function findLatestEndedTurnForChat(chatId) {
|
|
|
85748
86342
|
}
|
|
85749
86343
|
return latest;
|
|
85750
86344
|
}
|
|
86345
|
+
function resolveReplyOwnerTurn(liveTurn, chatId, args) {
|
|
86346
|
+
const origin = findTurnByOriginId(args.origin_turn_id);
|
|
86347
|
+
const quoted = findTurnByQuotedMessageId(chatId, args.reply_to);
|
|
86348
|
+
const latestEnded = findLatestEndedTurnForChat(chatId);
|
|
86349
|
+
const byId = new Map;
|
|
86350
|
+
for (const t of [latestEnded, quoted, origin, liveTurn]) {
|
|
86351
|
+
if (t != null)
|
|
86352
|
+
byId.set(t.turnId, t);
|
|
86353
|
+
}
|
|
86354
|
+
const latestEndedAgeMs = latestEnded?.endedAt != null ? Date.now() - latestEnded.endedAt : null;
|
|
86355
|
+
const winnerId = resolveReplyOwnerTurnId({
|
|
86356
|
+
liveTurnId: liveTurn?.turnId ?? null,
|
|
86357
|
+
originTurnId: origin?.turnId ?? null,
|
|
86358
|
+
quotedTurnId: quoted?.turnId ?? null,
|
|
86359
|
+
latestEndedTurnId: latestEnded?.turnId ?? null,
|
|
86360
|
+
latestEndedAgeMs,
|
|
86361
|
+
latestEndedTtlMs: DEFAULT_SUPERSEDE_TTL_MS
|
|
86362
|
+
});
|
|
86363
|
+
return winnerId != null ? byId.get(winnerId) ?? null : null;
|
|
86364
|
+
}
|
|
85751
86365
|
function resolveAnswerThreadWithLog(chatId, explicitThreadId, originTurn, originVia, liveTurn, surface) {
|
|
85752
86366
|
const recovered = LATE_REPLY_TOPIC_RECOVERY_ENABLED && explicitThreadId == null && originTurn == null && liveTurn == null ? findLatestEndedTurnForChat(chatId) : null;
|
|
85753
86367
|
const threadId = resolveAnswerThreadId({
|
|
@@ -86268,6 +86882,7 @@ function endCurrentTurnAtomic(turn, opts) {
|
|
|
86268
86882
|
clearAnswerReadyFlushTimeout(turn);
|
|
86269
86883
|
endCurrentTurnForKey(turn, key);
|
|
86270
86884
|
const turnEndedAt = Date.now();
|
|
86885
|
+
turn.endedAt = turnEndedAt;
|
|
86271
86886
|
process.stderr.write(`telegram gateway: ${formatTurnLifecycle("clear", "turn_end", turn, turnEndedAt)}
|
|
86272
86887
|
`);
|
|
86273
86888
|
if (opts?.deferRecord !== true) {
|
|
@@ -87814,7 +88429,7 @@ var statusPinState = new Map;
|
|
|
87814
88429
|
var statusPinChatIds = new Map;
|
|
87815
88430
|
var statusPinPinnedAt = new Map;
|
|
87816
88431
|
var statusPinRightsCache = new PinRightsCache2;
|
|
87817
|
-
var STATUS_PIN_STORE_PATH =
|
|
88432
|
+
var STATUS_PIN_STORE_PATH = join55(STATE_DIR, "status-pins.json");
|
|
87818
88433
|
var statusPinStoreFs = {
|
|
87819
88434
|
readFileSync: (p) => readFileSync54(p, "utf8"),
|
|
87820
88435
|
writeFileSync: (p, d) => writeFileSync43(p, d),
|
|
@@ -87822,7 +88437,7 @@ var statusPinStoreFs = {
|
|
|
87822
88437
|
existsSync: (p) => existsSync50(p)
|
|
87823
88438
|
};
|
|
87824
88439
|
var statusPinPersistEnabled = !STATIC && PIN_STATUS_WHILE_WORKING;
|
|
87825
|
-
var ACTIVITY_CARD_STORE_PATH =
|
|
88440
|
+
var ACTIVITY_CARD_STORE_PATH = join55(STATE_DIR, "activity-cards-pending.json");
|
|
87826
88441
|
var activityCardStoreFs = {
|
|
87827
88442
|
readFileSync: (p) => readFileSync54(p, "utf8"),
|
|
87828
88443
|
writeFileSync: (p, d) => writeFileSync43(p, d),
|
|
@@ -87830,7 +88445,7 @@ var activityCardStoreFs = {
|
|
|
87830
88445
|
existsSync: (p) => existsSync50(p)
|
|
87831
88446
|
};
|
|
87832
88447
|
var activityCardPersistEnabled = !STATIC;
|
|
87833
|
-
var QUEUED_CARD_STORE_PATH =
|
|
88448
|
+
var QUEUED_CARD_STORE_PATH = join55(STATE_DIR, "queued-cards-pending.json");
|
|
87834
88449
|
var queuedCardStoreFs = {
|
|
87835
88450
|
readFileSync: (p) => readFileSync54(p, "utf8"),
|
|
87836
88451
|
writeFileSync: (p, d) => writeFileSync43(p, d),
|
|
@@ -88184,11 +88799,11 @@ var getPinnedProgressCardMessageId = null;
|
|
|
88184
88799
|
var completeProgressCardTurn = null;
|
|
88185
88800
|
var subagentWatcher = null;
|
|
88186
88801
|
var workerActivityFeed = null;
|
|
88187
|
-
var SOCKET_PATH = process.env.SWITCHROOM_GATEWAY_SOCKET ??
|
|
88802
|
+
var SOCKET_PATH = process.env.SWITCHROOM_GATEWAY_SOCKET ?? join55(STATE_DIR, "gateway.sock");
|
|
88188
88803
|
mkdirSync40(STATE_DIR, { recursive: true, mode: 448 });
|
|
88189
|
-
var GATEWAY_PID_PATH = process.env.SWITCHROOM_GATEWAY_PID_FILE ??
|
|
88190
|
-
var GATEWAY_SESSION_MARKER_PATH = process.env.SWITCHROOM_GATEWAY_SESSION_MARKER ??
|
|
88191
|
-
var GATEWAY_CLEAN_SHUTDOWN_MARKER_PATH = process.env.SWITCHROOM_GATEWAY_CLEAN_SHUTDOWN_MARKER ??
|
|
88804
|
+
var GATEWAY_PID_PATH = process.env.SWITCHROOM_GATEWAY_PID_FILE ?? join55(STATE_DIR, "gateway.pid.json");
|
|
88805
|
+
var GATEWAY_SESSION_MARKER_PATH = process.env.SWITCHROOM_GATEWAY_SESSION_MARKER ?? join55(STATE_DIR, "gateway-session.json");
|
|
88806
|
+
var GATEWAY_CLEAN_SHUTDOWN_MARKER_PATH = process.env.SWITCHROOM_GATEWAY_CLEAN_SHUTDOWN_MARKER ?? join55(STATE_DIR, "clean-shutdown.json");
|
|
88192
88807
|
var GATEWAY_STARTED_AT_MS = Date.now();
|
|
88193
88808
|
var BOOT_CARD_ENABLED = process.env.SWITCHROOM_BOOT_CARD !== "false";
|
|
88194
88809
|
var activeBootCard = null;
|
|
@@ -88217,7 +88832,7 @@ function ensureIssuesCard(chatId, threadId) {
|
|
|
88217
88832
|
bot: botApi,
|
|
88218
88833
|
log: (msg) => process.stderr.write(`telegram gateway: ${msg}
|
|
88219
88834
|
`),
|
|
88220
|
-
persistPath:
|
|
88835
|
+
persistPath: join55(stateDir, "issues-card.json")
|
|
88221
88836
|
});
|
|
88222
88837
|
activeIssuesWatcher = startIssuesWatcher({
|
|
88223
88838
|
stateDir,
|
|
@@ -88548,7 +89163,7 @@ startTimer2({
|
|
|
88548
89163
|
}
|
|
88549
89164
|
});
|
|
88550
89165
|
var inboundSpool = STATIC ? undefined : createInboundSpool({
|
|
88551
|
-
path:
|
|
89166
|
+
path: join55(STATE_DIR, "inbound-spool.jsonl"),
|
|
88552
89167
|
fs: {
|
|
88553
89168
|
appendFileSync: (p, d) => appendFileSync6(p, d),
|
|
88554
89169
|
readFileSync: (p) => readFileSync54(p, "utf8"),
|
|
@@ -88683,6 +89298,88 @@ async function deliverCapturedProse(args) {
|
|
|
88683
89298
|
}
|
|
88684
89299
|
}
|
|
88685
89300
|
}
|
|
89301
|
+
async function maybeRedeliverUndeliveredAnswer() {
|
|
89302
|
+
const candidate = pendingRedelivery;
|
|
89303
|
+
pendingRedelivery = null;
|
|
89304
|
+
if (candidate == null || turnsDb == null)
|
|
89305
|
+
return;
|
|
89306
|
+
const { turn, maxAgeMs } = candidate;
|
|
89307
|
+
const sessionId = turn.session_id;
|
|
89308
|
+
if (!sessionId)
|
|
89309
|
+
return;
|
|
89310
|
+
let transcriptText;
|
|
89311
|
+
try {
|
|
89312
|
+
const projectsDir = getProjectsDirForCwd();
|
|
89313
|
+
const path2 = join55(projectsDir, `${sessionId}.jsonl`);
|
|
89314
|
+
if (!existsSync50(path2)) {
|
|
89315
|
+
process.stderr.write(`telegram gateway: crash-redelivery \u2014 transcript not found for turnKey=${turn.turn_key} session=${sessionId} (${path2}); skipping
|
|
89316
|
+
`);
|
|
89317
|
+
return;
|
|
89318
|
+
}
|
|
89319
|
+
transcriptText = readFileSync54(path2, "utf8");
|
|
89320
|
+
} catch (err) {
|
|
89321
|
+
process.stderr.write(`telegram gateway: crash-redelivery \u2014 transcript read failed turnKey=${turn.turn_key}: ${err.message}
|
|
89322
|
+
`);
|
|
89323
|
+
return;
|
|
89324
|
+
}
|
|
89325
|
+
const projected = projectTrailingAnswerFromTranscript(transcriptText);
|
|
89326
|
+
const threadIdNum2 = turn.thread_id != null && turn.thread_id !== "" ? Number(turn.thread_id) : undefined;
|
|
89327
|
+
const threadIdForOracle = threadIdNum2 != null && Number.isFinite(threadIdNum2) ? threadIdNum2 : null;
|
|
89328
|
+
const decision = decideRedeliver({
|
|
89329
|
+
capturedText: projected.text,
|
|
89330
|
+
trailingIsText: projected.trailingIsText,
|
|
89331
|
+
hasDeliveredText: HISTORY_ENABLED ? hasOutboundWithText(turn.chat_id, projected.text, threadIdForOracle, turn.started_at) : false,
|
|
89332
|
+
alreadyRedelivered: turn.answer_redelivered_at != null,
|
|
89333
|
+
ageMs: Math.max(0, Date.now() - turn.started_at),
|
|
89334
|
+
maxAgeMs
|
|
89335
|
+
});
|
|
89336
|
+
if (!decision.redeliver || decision.framedText == null) {
|
|
89337
|
+
process.stderr.write(`telegram gateway: crash-redelivery skipped turnKey=${turn.turn_key} reason=${decision.skipReason ?? "unknown"}
|
|
89338
|
+
`);
|
|
89339
|
+
return;
|
|
89340
|
+
}
|
|
89341
|
+
const chatId = turn.chat_id;
|
|
89342
|
+
const out = redactOutboundText(decision.framedText, "crash_redelivery");
|
|
89343
|
+
const chunks = splitMarkdownChunks2(out, RICH_MESSAGE_MAX_CHARS2);
|
|
89344
|
+
const sentIds = [];
|
|
89345
|
+
try {
|
|
89346
|
+
let liveThreadId = threadIdNum2 != null && Number.isFinite(threadIdNum2) ? threadIdNum2 : undefined;
|
|
89347
|
+
for (const c of chunks) {
|
|
89348
|
+
const sent = await retryWithThreadFallback2(robustApiCall, (tid) => {
|
|
89349
|
+
const opts = {
|
|
89350
|
+
link_preview_options: { is_disabled: true },
|
|
89351
|
+
...tid != null ? { message_thread_id: tid } : {}
|
|
89352
|
+
};
|
|
89353
|
+
return bot.api.sendRichMessage(chatId, richMessage2(c), opts);
|
|
89354
|
+
}, { threadId: liveThreadId, chat_id: chatId, verb: "crash-redelivery.sendMessage" });
|
|
89355
|
+
if (liveThreadId != null && sent.message_thread_id == null) {
|
|
89356
|
+
liveThreadId = undefined;
|
|
89357
|
+
}
|
|
89358
|
+
sentIds.push(sent.message_id);
|
|
89359
|
+
}
|
|
89360
|
+
if (HISTORY_ENABLED && sentIds.length > 0) {
|
|
89361
|
+
try {
|
|
89362
|
+
recordOutbound({
|
|
89363
|
+
chat_id: chatId,
|
|
89364
|
+
thread_id: threadIdForOracle,
|
|
89365
|
+
message_ids: sentIds,
|
|
89366
|
+
texts: chunks
|
|
89367
|
+
});
|
|
89368
|
+
} catch {}
|
|
89369
|
+
}
|
|
89370
|
+
try {
|
|
89371
|
+
markAnswerRedelivered(turnsDb, turn.turn_key);
|
|
89372
|
+
} catch (err) {
|
|
89373
|
+
process.stderr.write(`telegram gateway: crash-redelivery markAnswerRedelivered failed turnKey=${turn.turn_key}: ${err.message}
|
|
89374
|
+
`);
|
|
89375
|
+
}
|
|
89376
|
+
process.stderr.write(`telegram gateway: crash-redelivery \u2014 delivered recovered answer (${out.length} chars, ${chunks.length} chunk(s)) for turnKey=${turn.turn_key} chat=${chatId}
|
|
89377
|
+
`);
|
|
89378
|
+
} catch (err) {
|
|
89379
|
+
process.stderr.write(`telegram gateway: crash-redelivery send failed turnKey=${turn.turn_key}: ${err.message} ` + `\u2014 left un-stamped for a later retry
|
|
89380
|
+
`);
|
|
89381
|
+
}
|
|
89382
|
+
}
|
|
88686
89383
|
function obligationSweep() {
|
|
88687
89384
|
if (!OBLIGATION_LEDGER_ENABLED)
|
|
88688
89385
|
return;
|
|
@@ -88842,8 +89539,8 @@ var bridgeDeadWatchdog = createBridgeDeadWatchdog({
|
|
|
88842
89539
|
isSessionAlive: () => findAgentProcessInContainer2()?.comm === "claude",
|
|
88843
89540
|
isShuttingDown: () => shuttingDown,
|
|
88844
89541
|
escalate: (reason) => triggerSelfRestart(process.env.SWITCHROOM_AGENT_NAME ?? "", reason, 1500),
|
|
88845
|
-
crashLogPath:
|
|
88846
|
-
markerPath:
|
|
89542
|
+
crashLogPath: join55(STATE_DIR, "bridge-crash.log"),
|
|
89543
|
+
markerPath: join55(STATE_DIR, "bridge-dead-escalation.json"),
|
|
88847
89544
|
log: (line) => process.stderr.write(`${line}
|
|
88848
89545
|
`),
|
|
88849
89546
|
priorStreak: bridgeDeadPriorStreak,
|
|
@@ -88970,8 +89667,8 @@ var ipcServer = createIpcServer({
|
|
|
88970
89667
|
probeQuotaViaBroker: (t) => probeQuotaForBootCard(agentSlug, t),
|
|
88971
89668
|
tmuxSupervisor: process.env.SWITCHROOM_TMUX_SUPERVISOR === "1",
|
|
88972
89669
|
dockerMode: process.env.SWITCHROOM_RUNTIME === "docker",
|
|
88973
|
-
configSnapshotPath:
|
|
88974
|
-
bootCardStatePath:
|
|
89670
|
+
configSnapshotPath: join55(resolvedAgentDirForCard, ".config-snapshot.json"),
|
|
89671
|
+
bootCardStatePath: join55(resolvedAgentDirForCard, ".boot-card-msgid.json"),
|
|
88975
89672
|
floodStatePath: FLOOD_STATE_PATH,
|
|
88976
89673
|
...updateOutcomeLine ? { updateOutcomeLine } : {}
|
|
88977
89674
|
}, ackMsgId).then((handle) => {
|
|
@@ -89056,6 +89753,21 @@ var ipcServer = createIpcServer({
|
|
|
89056
89753
|
return;
|
|
89057
89754
|
if (msg.activeFile)
|
|
89058
89755
|
lastSessionActiveFile = msg.activeFile;
|
|
89756
|
+
if (turnsDb != null && msg.activeFile != null) {
|
|
89757
|
+
const stampKey = currentTurn?.registryKey ?? null;
|
|
89758
|
+
if (stampKey != null && stampKey !== lastSessionStampedTurnKey) {
|
|
89759
|
+
const sessionId = basename13(msg.activeFile).replace(/\.jsonl$/, "");
|
|
89760
|
+
if (sessionId) {
|
|
89761
|
+
try {
|
|
89762
|
+
stampTurnSessionId(turnsDb, stampKey, sessionId);
|
|
89763
|
+
lastSessionStampedTurnKey = stampKey;
|
|
89764
|
+
} catch (err) {
|
|
89765
|
+
process.stderr.write(`telegram gateway: stampTurnSessionId failed turnKey=${stampKey}: ${err.message}
|
|
89766
|
+
`);
|
|
89767
|
+
}
|
|
89768
|
+
}
|
|
89769
|
+
}
|
|
89770
|
+
}
|
|
89059
89771
|
const ev = msg.event;
|
|
89060
89772
|
handleSessionEvent(ev);
|
|
89061
89773
|
toolFlightTracker.onEvent(ev);
|
|
@@ -89638,7 +90350,7 @@ var ipcServer = createIpcServer({
|
|
|
89638
90350
|
const receiverUid = receiverUidRaw ? Number(receiverUidRaw) : NaN;
|
|
89639
90351
|
if (Number.isInteger(receiverUid))
|
|
89640
90352
|
allowedUids.push(receiverUid);
|
|
89641
|
-
const socketPath =
|
|
90353
|
+
const socketPath = join55(STATE_DIR, "webhook.sock");
|
|
89642
90354
|
const webhookInject = (agentName3, inbound) => {
|
|
89643
90355
|
const msg = inbound;
|
|
89644
90356
|
const delivered = ipcServer.sendToAgent(agentName3, msg);
|
|
@@ -89873,9 +90585,9 @@ function redactOutboundText(text5, site) {
|
|
|
89873
90585
|
var VOICE_OUT_DEFAULT_CHUNK_CHARS = 600;
|
|
89874
90586
|
var VOICE_OUT_HARD_CHUNK_CAP = 4096;
|
|
89875
90587
|
var voiceOnDemandCache = new VoiceOnDemandCache({
|
|
89876
|
-
persistPath:
|
|
90588
|
+
persistPath: join55(STATE_DIR, "voice-ondemand.json")
|
|
89877
90589
|
});
|
|
89878
|
-
var VOICE_CACHE_DIR =
|
|
90590
|
+
var VOICE_CACHE_DIR = join55(STATE_DIR, "voice-cache");
|
|
89879
90591
|
var voicePreSynthQueue = new PreSynthQueue({
|
|
89880
90592
|
runJob: async (job) => {
|
|
89881
90593
|
const sidecarToken = await materializeSidecarToken();
|
|
@@ -90044,7 +90756,8 @@ async function executeReply(args) {
|
|
|
90044
90756
|
}
|
|
90045
90757
|
{
|
|
90046
90758
|
const replyThreadId = args.message_thread_id != null ? Number(args.message_thread_id) : undefined;
|
|
90047
|
-
const
|
|
90759
|
+
const ownerTurn = resolveReplyOwnerTurn(turn, chat_id, args);
|
|
90760
|
+
const resolvedTurnId = ownerTurn?.turnId ?? null;
|
|
90048
90761
|
const decision = flushedTurnSupersede.take(chat_id, replyThreadId, { liveTurnId: resolvedTurnId, now: Date.now() });
|
|
90049
90762
|
if (decision.supersede) {
|
|
90050
90763
|
process.stderr.write(`telegram gateway: reply: superseding flushed turn message(s) chatId=${chat_id} ids=${JSON.stringify(decision.deleteMessageIds)}
|
|
@@ -90052,6 +90765,25 @@ async function executeReply(args) {
|
|
|
90052
90765
|
for (const id of decision.deleteMessageIds) {
|
|
90053
90766
|
await swallowingApiCall(() => lockedBot.api.deleteMessage(chat_id, id), { chat_id, verb: "reply.supersedeFlushed" });
|
|
90054
90767
|
}
|
|
90768
|
+
} else {
|
|
90769
|
+
const replySubstantive = isSubstantiveFinalReply({
|
|
90770
|
+
text: rawText,
|
|
90771
|
+
disableNotification: args.disable_notification === true
|
|
90772
|
+
});
|
|
90773
|
+
const suppressByLatch = decideAnswerLatchSuppression({
|
|
90774
|
+
superseded: false,
|
|
90775
|
+
replySubstantive,
|
|
90776
|
+
isLateReply: turn == null,
|
|
90777
|
+
ownerAnswerDelivered: ownerTurn?.answerDelivered ?? false
|
|
90778
|
+
});
|
|
90779
|
+
if (suppressByLatch) {
|
|
90780
|
+
process.stderr.write(`telegram gateway: reply: suppressed by answer-delivered latch (flush already delivered this turn's answer) chatId=${chat_id} ownerTurnId=${JSON.stringify(resolvedTurnId)}
|
|
90781
|
+
`);
|
|
90782
|
+
return { content: [{ type: "text", text: "sent (deduped \u2014 answer already delivered via turn-flush)" }] };
|
|
90783
|
+
}
|
|
90784
|
+
if (replySubstantive && ownerTurn != null) {
|
|
90785
|
+
ownerTurn.answerDelivered = true;
|
|
90786
|
+
}
|
|
90055
90787
|
}
|
|
90056
90788
|
}
|
|
90057
90789
|
const files = args.files ?? [];
|
|
@@ -90834,7 +91566,7 @@ async function executeSendGif(rawArgs) {
|
|
|
90834
91566
|
};
|
|
90835
91567
|
}
|
|
90836
91568
|
async function publishToTelegraph(text5, shortName, authorName) {
|
|
90837
|
-
const accountPath =
|
|
91569
|
+
const accountPath = join55(STATE_DIR, "telegraph-account.json");
|
|
90838
91570
|
let account = null;
|
|
90839
91571
|
try {
|
|
90840
91572
|
if (existsSync50(accountPath)) {
|
|
@@ -91757,7 +92489,8 @@ function composeTurnActivity(turn, final = false, liveSuffix = "") {
|
|
|
91757
92489
|
elapsedMs: turn.startedAt > 0 ? Date.now() - turn.startedAt : 0,
|
|
91758
92490
|
toolCount: turn.labeledToolCount,
|
|
91759
92491
|
state: final ? "done" : "running",
|
|
91760
|
-
model: turn.currentModel
|
|
92492
|
+
model: turn.currentModel,
|
|
92493
|
+
totalTokens: turn.totalTokens
|
|
91761
92494
|
};
|
|
91762
92495
|
return renderActivityFeedWithNested2(turn.mirrorLines, childLines, final, liveSuffix, stepCount, header);
|
|
91763
92496
|
}
|
|
@@ -92201,6 +92934,8 @@ function handleSessionEvent(ev) {
|
|
|
92201
92934
|
finalAnswerDelivered: false,
|
|
92202
92935
|
finalAnswerSubstantive: false,
|
|
92203
92936
|
finalAnswerEverDelivered: false,
|
|
92937
|
+
answerDelivered: false,
|
|
92938
|
+
endedAt: null,
|
|
92204
92939
|
firstPingAt: null,
|
|
92205
92940
|
firstPingWasSubstantive: false,
|
|
92206
92941
|
silentAnchorMessageId: null,
|
|
@@ -92216,6 +92951,8 @@ function handleSessionEvent(ev) {
|
|
|
92216
92951
|
lastAssistantDone: false,
|
|
92217
92952
|
toolCallCount: 0,
|
|
92218
92953
|
labeledToolCount: 0,
|
|
92954
|
+
totalTokens: 0,
|
|
92955
|
+
seenUsageMessageIds: new Set,
|
|
92219
92956
|
activityMessageId: null,
|
|
92220
92957
|
activityInFlight: null,
|
|
92221
92958
|
activityPendingRender: null,
|
|
@@ -92289,6 +93026,18 @@ function handleSessionEvent(ev) {
|
|
|
92289
93026
|
sessionModelSource.noteTranscriptModel(ev.model);
|
|
92290
93027
|
return;
|
|
92291
93028
|
}
|
|
93029
|
+
case "usage": {
|
|
93030
|
+
const turn = currentTurn;
|
|
93031
|
+
if (turn == null)
|
|
93032
|
+
return;
|
|
93033
|
+
if (ev.messageId != null) {
|
|
93034
|
+
if (turn.seenUsageMessageIds.has(ev.messageId))
|
|
93035
|
+
return;
|
|
93036
|
+
turn.seenUsageMessageIds.add(ev.messageId);
|
|
93037
|
+
}
|
|
93038
|
+
turn.totalTokens += ev.totalTokens;
|
|
93039
|
+
return;
|
|
93040
|
+
}
|
|
92292
93041
|
case "thinking": {
|
|
92293
93042
|
const turn = currentTurn;
|
|
92294
93043
|
if (turn == null)
|
|
@@ -92677,6 +93426,9 @@ function handleSessionEvent(ev) {
|
|
|
92677
93426
|
}
|
|
92678
93427
|
turn.finalAnswerDelivered = true;
|
|
92679
93428
|
turn.finalAnswerSubstantive = true;
|
|
93429
|
+
if (capturedText.trim().length >= FLUSH_SUBSTANTIVE_MIN_CHARS) {
|
|
93430
|
+
turn.answerDelivered = true;
|
|
93431
|
+
}
|
|
92680
93432
|
const cardTakeover = progressDriver?.takeOverCard({
|
|
92681
93433
|
chatId: backstopChatId,
|
|
92682
93434
|
threadId: backstopThreadId != null ? String(backstopThreadId) : undefined
|
|
@@ -92789,6 +93541,7 @@ function handleSessionEvent(ev) {
|
|
|
92789
93541
|
sendThrew = true;
|
|
92790
93542
|
process.stderr.write(`telegram gateway: turn-flush send failed: ${err.message}
|
|
92791
93543
|
`);
|
|
93544
|
+
turn.answerDelivered = false;
|
|
92792
93545
|
if (backstopCtrl)
|
|
92793
93546
|
backstopCtrl.finalize("error");
|
|
92794
93547
|
} finally {
|
|
@@ -94155,7 +94908,7 @@ function getMyAgentName() {
|
|
|
94155
94908
|
const fromEnv = process.env.SWITCHROOM_AGENT_NAME;
|
|
94156
94909
|
if (fromEnv && fromEnv.trim().length > 0)
|
|
94157
94910
|
return fromEnv.trim();
|
|
94158
|
-
return
|
|
94911
|
+
return basename13(process.cwd());
|
|
94159
94912
|
}
|
|
94160
94913
|
function isSelfTargetingCommand(name) {
|
|
94161
94914
|
if (name === "all")
|
|
@@ -94168,7 +94921,7 @@ function restartMarkerPath() {
|
|
|
94168
94921
|
const agentDir = resolveAgentDirFromEnv();
|
|
94169
94922
|
if (!agentDir)
|
|
94170
94923
|
return null;
|
|
94171
|
-
return
|
|
94924
|
+
return join55(agentDir, "restart-pending.json");
|
|
94172
94925
|
}
|
|
94173
94926
|
function writeRestartMarker(marker) {
|
|
94174
94927
|
const p = restartMarkerPath();
|
|
@@ -94360,7 +95113,7 @@ function _resetDockerReachableCache() {
|
|
|
94360
95113
|
}
|
|
94361
95114
|
function spawnSwitchroomDetached(args, onFailure) {
|
|
94362
95115
|
const fullArgs = SWITCHROOM_CONFIG ? ["--config", SWITCHROOM_CONFIG, ...args] : args;
|
|
94363
|
-
const logPath =
|
|
95116
|
+
const logPath = join55(STATE_DIR, "detached-spawn.log");
|
|
94364
95117
|
let outFd = null;
|
|
94365
95118
|
try {
|
|
94366
95119
|
mkdirSync40(STATE_DIR, { recursive: true });
|
|
@@ -94758,7 +95511,7 @@ bot.use(async (ctx, next) => {
|
|
|
94758
95511
|
});
|
|
94759
95512
|
function readRecentDenialsForAgent(agentName3, windowMs, limit) {
|
|
94760
95513
|
try {
|
|
94761
|
-
const auditPath =
|
|
95514
|
+
const auditPath = join55(homedir18(), ".switchroom", "vault-audit.log");
|
|
94762
95515
|
if (!existsSync50(auditPath))
|
|
94763
95516
|
return [];
|
|
94764
95517
|
const raw = readFileSync54(auditPath, "utf8");
|
|
@@ -94812,7 +95565,7 @@ async function buildAgentMetadata(agentName3) {
|
|
|
94812
95565
|
try {
|
|
94813
95566
|
const agentDir = resolveAgentDirFromEnv();
|
|
94814
95567
|
if (agentDir) {
|
|
94815
|
-
const raw = readFileSync54(
|
|
95568
|
+
const raw = readFileSync54(join55(agentDir, ".claude", ".claude.json"), "utf8");
|
|
94816
95569
|
claudeJson = JSON.parse(raw);
|
|
94817
95570
|
}
|
|
94818
95571
|
} catch {}
|
|
@@ -95008,7 +95761,7 @@ function buildModelDeps(restartCtx) {
|
|
|
95008
95761
|
try {
|
|
95009
95762
|
const agentDir = resolveAgentDirFromEnv();
|
|
95010
95763
|
if (agentDir) {
|
|
95011
|
-
const local = await fetchQuota2({ claudeConfigDir:
|
|
95764
|
+
const local = await fetchQuota2({ claudeConfigDir: join55(agentDir, ".claude") });
|
|
95012
95765
|
if (local.ok)
|
|
95013
95766
|
return formatQuotaLine2(local.data);
|
|
95014
95767
|
}
|
|
@@ -95480,7 +96233,7 @@ bot.command("restart", async (ctx) => {
|
|
|
95480
96233
|
function flushAgentHandoff(agentDir) {
|
|
95481
96234
|
let removed = 0;
|
|
95482
96235
|
for (const fname of [".handoff.md", ".handoff-topic"]) {
|
|
95483
|
-
const p =
|
|
96236
|
+
const p = join55(agentDir, fname);
|
|
95484
96237
|
try {
|
|
95485
96238
|
if (existsSync50(p)) {
|
|
95486
96239
|
unlinkSync24(p);
|
|
@@ -95538,7 +96291,7 @@ async function handleNewCommand(ctx) {
|
|
|
95538
96291
|
writeRestartMarker({ chat_id: chatId, thread_id: threadId ?? null, ack_message_id: ackId, ts: Date.now() });
|
|
95539
96292
|
if (agentDir != null) {
|
|
95540
96293
|
try {
|
|
95541
|
-
writeFileSync43(
|
|
96294
|
+
writeFileSync43(join55(agentDir, ".force-fresh-session"), `${kind} at ${new Date().toISOString()}
|
|
95542
96295
|
`, "utf8");
|
|
95543
96296
|
} catch (err) {
|
|
95544
96297
|
process.stderr.write(`telegram gateway: failed to write force-fresh marker: ${err}
|
|
@@ -95908,7 +96661,7 @@ var lockoutOps = {
|
|
|
95908
96661
|
writeFileSync: (p, data, opts) => writeFileSync43(p, data, opts),
|
|
95909
96662
|
existsSync: (p) => existsSync50(p),
|
|
95910
96663
|
mkdirSync: (p, opts) => mkdirSync40(p, opts),
|
|
95911
|
-
joinPath: (...parts) =>
|
|
96664
|
+
joinPath: (...parts) => join55(...parts)
|
|
95912
96665
|
};
|
|
95913
96666
|
var FLEET_FALLBACK_DEDUP_MS = 30000;
|
|
95914
96667
|
function isAuthBrokerSocketReachable() {
|
|
@@ -96168,7 +96921,7 @@ async function runCreditWatch() {
|
|
|
96168
96921
|
if (!agentDir)
|
|
96169
96922
|
return;
|
|
96170
96923
|
const agentName3 = getMyAgentName();
|
|
96171
|
-
const claudeConfigDir =
|
|
96924
|
+
const claudeConfigDir = join55(agentDir, ".claude");
|
|
96172
96925
|
const stateDir = STATE_DIR;
|
|
96173
96926
|
const reason = readClaudeJsonOverage(claudeConfigDir);
|
|
96174
96927
|
const prev = loadCreditState(stateDir);
|
|
@@ -97180,7 +97933,7 @@ bot.command("usage", async (ctx) => {
|
|
|
97180
97933
|
await switchroomReply(ctx, "**/usage:** cannot resolve agent dir.", { html: true });
|
|
97181
97934
|
return;
|
|
97182
97935
|
}
|
|
97183
|
-
const result = await fetchQuota2({ claudeConfigDir:
|
|
97936
|
+
const result = await fetchQuota2({ claudeConfigDir: join55(agentDir, ".claude") });
|
|
97184
97937
|
if (!result.ok) {
|
|
97185
97938
|
await switchroomReply(ctx, `**/usage:** ${escapeHtmlForTg2(result.reason)}`, { html: true });
|
|
97186
97939
|
return;
|
|
@@ -99068,6 +99821,10 @@ var didOneTimeSetup = false;
|
|
|
99068
99821
|
process.stderr.write(`telegram gateway: blocked-approval boot reconcile failed: ${err.message}
|
|
99069
99822
|
`);
|
|
99070
99823
|
}
|
|
99824
|
+
maybeRedeliverUndeliveredAnswer().catch((err) => {
|
|
99825
|
+
process.stderr.write(`telegram gateway: crash-redelivery boot send errored: ${err.message}
|
|
99826
|
+
`);
|
|
99827
|
+
});
|
|
99071
99828
|
try {
|
|
99072
99829
|
const bootAccess = loadAccess();
|
|
99073
99830
|
const chatSet = new Set(bootAccess.allowFrom);
|
|
@@ -99165,7 +99922,7 @@ var didOneTimeSetup = false;
|
|
|
99165
99922
|
return;
|
|
99166
99923
|
}
|
|
99167
99924
|
})();
|
|
99168
|
-
const resolvedAgentDirForBootCard = agentDir ??
|
|
99925
|
+
const resolvedAgentDirForBootCard = agentDir ?? join55(homedir18(), ".switchroom", "agents", agentSlug);
|
|
99169
99926
|
const handle = await startBootCard(chatId, threadId, botApiForCard, {
|
|
99170
99927
|
agentName: agentDisplayName,
|
|
99171
99928
|
agentSlug,
|
|
@@ -99179,8 +99936,8 @@ var didOneTimeSetup = false;
|
|
|
99179
99936
|
probeQuotaViaBroker: (t) => probeQuotaForBootCard(agentSlug, t),
|
|
99180
99937
|
tmuxSupervisor: process.env.SWITCHROOM_TMUX_SUPERVISOR === "1",
|
|
99181
99938
|
dockerMode: process.env.SWITCHROOM_RUNTIME === "docker",
|
|
99182
|
-
configSnapshotPath:
|
|
99183
|
-
bootCardStatePath:
|
|
99939
|
+
configSnapshotPath: join55(resolvedAgentDirForBootCard, ".config-snapshot.json"),
|
|
99940
|
+
bootCardStatePath: join55(resolvedAgentDirForBootCard, ".boot-card-msgid.json"),
|
|
99184
99941
|
floodStatePath: FLOOD_STATE_PATH,
|
|
99185
99942
|
...updateOutcomeLine ? { updateOutcomeLine } : {}
|
|
99186
99943
|
}, ackMsgId);
|
|
@@ -99211,7 +99968,7 @@ var didOneTimeSetup = false;
|
|
|
99211
99968
|
try {
|
|
99212
99969
|
const smAgentDir = resolveAgentDirFromEnv();
|
|
99213
99970
|
if (smAgentDir) {
|
|
99214
|
-
const activePath =
|
|
99971
|
+
const activePath = join55(smAgentDir, ".active-session-model");
|
|
99215
99972
|
if (existsSync50(activePath)) {
|
|
99216
99973
|
try {
|
|
99217
99974
|
const launched = readFileSync54(activePath, "utf8").trim();
|
|
@@ -99223,7 +99980,7 @@ var didOneTimeSetup = false;
|
|
|
99223
99980
|
sessionModelSource.setOverride(launched.length > 0 && launched !== configured ? launched : null);
|
|
99224
99981
|
} catch {}
|
|
99225
99982
|
}
|
|
99226
|
-
const activeEffortPath =
|
|
99983
|
+
const activeEffortPath = join55(smAgentDir, ".active-session-effort");
|
|
99227
99984
|
if (existsSync50(activeEffortPath)) {
|
|
99228
99985
|
try {
|
|
99229
99986
|
const launchedEffort = readFileSync54(activeEffortPath, "utf8").trim();
|
|
@@ -99231,7 +99988,7 @@ var didOneTimeSetup = false;
|
|
|
99231
99988
|
sessionEffortOverride = launchedEffort.length > 0 && launchedEffort !== configuredEffort ? launchedEffort : null;
|
|
99232
99989
|
} catch {}
|
|
99233
99990
|
}
|
|
99234
|
-
const alertPath =
|
|
99991
|
+
const alertPath = join55(smAgentDir, ".session-model-alert");
|
|
99235
99992
|
if (existsSync50(alertPath)) {
|
|
99236
99993
|
let alertText = null;
|
|
99237
99994
|
try {
|
|
@@ -99338,7 +100095,10 @@ var didOneTimeSetup = false;
|
|
|
99338
100095
|
})();
|
|
99339
100096
|
const foregroundNestingEnabled = process.env.SWITCHROOM_FOREGROUND_SUBAGENT_NESTING !== "0";
|
|
99340
100097
|
const orphanStatusEnabled = isOrphanSubagentStatusEnabled(process.env.SWITCHROOM_ORPHAN_SUBAGENT_STATUS);
|
|
99341
|
-
workerActivityFeed
|
|
100098
|
+
if (workerActivityFeed != null) {
|
|
100099
|
+
workerActivityFeed.purgeAllOnBoot();
|
|
100100
|
+
workerActivityFeed.stop();
|
|
100101
|
+
}
|
|
99342
100102
|
workerActivityFeed = createWorkerActivityFeed({
|
|
99343
100103
|
bot: {
|
|
99344
100104
|
sendMessage: async (cid, text5, sendOpts) => {
|
|
@@ -99356,6 +100116,8 @@ var didOneTimeSetup = false;
|
|
|
99356
100116
|
floodWaitRemainingMs: probeFloodWaitRemainingMs,
|
|
99357
100117
|
maxRows: workerFeedMaxRows,
|
|
99358
100118
|
staleWorkerTtlMs: resolveInflightTerminalCapMs() + WORKER_FEED_STALE_TTL_MARGIN_MS,
|
|
100119
|
+
absoluteRowLifetimeCapMs: resolveInflightTerminalCapMs() * WORKER_FEED_ABSOLUTE_ROW_LIFETIME_CAP_MULTIPLE,
|
|
100120
|
+
groupMessageLifetimeCapMs: WORKER_FEED_GROUP_MESSAGE_LIFETIME_CAP_MS,
|
|
99359
100121
|
reconcilePin: ({ feedKey, chatId, messageId }) => {
|
|
99360
100122
|
if (!PIN_STATUS_WHILE_WORKING)
|
|
99361
100123
|
return;
|
|
@@ -99409,7 +100171,7 @@ var didOneTimeSetup = false;
|
|
|
99409
100171
|
`);
|
|
99410
100172
|
}
|
|
99411
100173
|
},
|
|
99412
|
-
onFinish: ({ agentId, outcome, description, resultText, toolCount, durationMs, background: entryBackground }) => {
|
|
100174
|
+
onFinish: ({ agentId, outcome, description, resultText, toolCount, totalTokens, durationMs, background: entryBackground }) => {
|
|
99413
100175
|
deferredDoneReactions.promote();
|
|
99414
100176
|
let fleetChatId = "";
|
|
99415
100177
|
try {
|
|
@@ -99437,6 +100199,7 @@ var didOneTimeSetup = false;
|
|
|
99437
100199
|
description: dispatch.feedDescription,
|
|
99438
100200
|
lastTool: null,
|
|
99439
100201
|
toolCount,
|
|
100202
|
+
totalTokens,
|
|
99440
100203
|
latestSummary: resultText,
|
|
99441
100204
|
elapsedMs: durationMs,
|
|
99442
100205
|
state: outcome === "failed" ? "failed" : "done",
|
|
@@ -99484,6 +100247,7 @@ var didOneTimeSetup = false;
|
|
|
99484
100247
|
description: dispatch.feedDescription,
|
|
99485
100248
|
lastTool: null,
|
|
99486
100249
|
toolCount,
|
|
100250
|
+
totalTokens,
|
|
99487
100251
|
latestSummary: resultText,
|
|
99488
100252
|
elapsedMs: durationMs,
|
|
99489
100253
|
state: outcome === "failed" ? "failed" : "done",
|
|
@@ -99497,6 +100261,7 @@ var didOneTimeSetup = false;
|
|
|
99497
100261
|
description: dispatch.feedDescription,
|
|
99498
100262
|
lastTool: null,
|
|
99499
100263
|
toolCount,
|
|
100264
|
+
totalTokens,
|
|
99500
100265
|
latestSummary: resultText,
|
|
99501
100266
|
elapsedMs: durationMs,
|
|
99502
100267
|
state: outcome === "failed" ? "failed" : "done",
|
|
@@ -99537,7 +100302,7 @@ var didOneTimeSetup = false;
|
|
|
99537
100302
|
process.stderr.write(`telegram gateway: subagent-handback queued agent=${agentId} outcome=${outcome} chat=${decision.chatId} resultChars=${resultText.length}
|
|
99538
100303
|
`);
|
|
99539
100304
|
},
|
|
99540
|
-
onProgress: ({ agentId, description, latestSummary, elapsedMs, prevBucketIdx, setBucketIdx, lastTool, toolCount, progressLine, model, skeleton }) => {
|
|
100305
|
+
onProgress: ({ agentId, description, latestSummary, elapsedMs, prevBucketIdx, setBucketIdx, lastTool, toolCount, totalTokens, progressLine, model, skeleton }) => {
|
|
99541
100306
|
let fleetChatId = "";
|
|
99542
100307
|
try {
|
|
99543
100308
|
const fleets = progressDriver?.peekAllFleets() ?? [];
|
|
@@ -99574,7 +100339,8 @@ var didOneTimeSetup = false;
|
|
|
99574
100339
|
latestSummary: stepLine,
|
|
99575
100340
|
elapsedMs,
|
|
99576
100341
|
state: "running",
|
|
99577
|
-
model: feedModel
|
|
100342
|
+
model: feedModel,
|
|
100343
|
+
totalTokens
|
|
99578
100344
|
}, wk.threadId);
|
|
99579
100345
|
return;
|
|
99580
100346
|
}
|
|
@@ -99635,7 +100401,8 @@ var didOneTimeSetup = false;
|
|
|
99635
100401
|
latestSummary: stepLine,
|
|
99636
100402
|
elapsedMs,
|
|
99637
100403
|
state: "running",
|
|
99638
|
-
model: feedModel
|
|
100404
|
+
model: feedModel,
|
|
100405
|
+
totalTokens
|
|
99639
100406
|
}, wk.threadId);
|
|
99640
100407
|
return;
|
|
99641
100408
|
}
|