switchroom 0.18.24 → 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 +1489 -829
- package/telegram-plugin/dist/server.js +26 -0
- package/telegram-plugin/gateway/callback-query-handlers.ts +7 -0
- package/telegram-plugin/gateway/gateway.ts +314 -3
- 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/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/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-pin-persistence.test.ts +306 -0
- package/telegram-plugin/tool-activity-summary.ts +54 -3
- package/telegram-plugin/worker-activity-feed.ts +104 -0
- 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;
|
|
@@ -40570,6 +40643,7 @@ function createWorkerActivityFeed(opts) {
|
|
|
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));
|
|
40572
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));
|
|
40573
40647
|
const reconcilePinFn = opts.reconcilePin ?? (() => {});
|
|
40574
40648
|
const setIntervalFn = opts.setInterval ?? ((cb, ms) => {
|
|
40575
40649
|
const t = setInterval(cb, ms);
|
|
@@ -40670,6 +40744,7 @@ function createWorkerActivityFeed(opts) {
|
|
|
40670
40744
|
description: v.description,
|
|
40671
40745
|
elapsedMs: elapsedFor(r),
|
|
40672
40746
|
toolCount: v.toolCount,
|
|
40747
|
+
totalTokens: v.totalTokens,
|
|
40673
40748
|
currentStep,
|
|
40674
40749
|
historyLines: r.narrative.length > 0 ? [...r.narrative] : undefined,
|
|
40675
40750
|
model: v.model
|
|
@@ -40747,6 +40822,7 @@ function createWorkerActivityFeed(opts) {
|
|
|
40747
40822
|
return;
|
|
40748
40823
|
}
|
|
40749
40824
|
g.messageId = sent.message_id;
|
|
40825
|
+
g.messageCreatedAtMs = now;
|
|
40750
40826
|
g.lastBody = body;
|
|
40751
40827
|
g.lastEditAt = now;
|
|
40752
40828
|
g.terminalPainted = false;
|
|
@@ -40777,6 +40853,7 @@ function createWorkerActivityFeed(opts) {
|
|
|
40777
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}`);
|
|
40778
40854
|
} else {
|
|
40779
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);
|
|
40780
40857
|
}
|
|
40781
40858
|
if (isTerminal)
|
|
40782
40859
|
clearStaged();
|
|
@@ -40795,6 +40872,7 @@ function createWorkerActivityFeed(opts) {
|
|
|
40795
40872
|
}
|
|
40796
40873
|
if (outcome === "gone") {
|
|
40797
40874
|
g.messageId = null;
|
|
40875
|
+
g.messageCreatedAtMs = 0;
|
|
40798
40876
|
g.lastBody = null;
|
|
40799
40877
|
if (isTerminal)
|
|
40800
40878
|
clearStaged();
|
|
@@ -40838,6 +40916,7 @@ function createWorkerActivityFeed(opts) {
|
|
|
40838
40916
|
description: lv?.description ?? "background task",
|
|
40839
40917
|
lastTool: null,
|
|
40840
40918
|
toolCount: lv?.toolCount ?? 0,
|
|
40919
|
+
totalTokens: lv?.totalTokens,
|
|
40841
40920
|
latestSummary: "",
|
|
40842
40921
|
elapsedMs: liveElapsed(row, nowFn()),
|
|
40843
40922
|
state: "incomplete",
|
|
@@ -40897,6 +40976,16 @@ function createWorkerActivityFeed(opts) {
|
|
|
40897
40976
|
const running = runningRows(g);
|
|
40898
40977
|
if (running.length === 0)
|
|
40899
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
|
+
}
|
|
40900
40989
|
if (g.messageId == null) {
|
|
40901
40990
|
const maxElapsed = Math.max(0, ...running.map((r) => liveElapsed(r, now)));
|
|
40902
40991
|
if (maxElapsed < firstPaintMin)
|
|
@@ -40951,6 +41040,7 @@ function createWorkerActivityFeed(opts) {
|
|
|
40951
41040
|
chatId,
|
|
40952
41041
|
threadId,
|
|
40953
41042
|
messageId: null,
|
|
41043
|
+
messageCreatedAtMs: 0,
|
|
40954
41044
|
lastBody: null,
|
|
40955
41045
|
lastEditAt: 0,
|
|
40956
41046
|
cooldownUntil: 0,
|
|
@@ -40963,6 +41053,7 @@ function createWorkerActivityFeed(opts) {
|
|
|
40963
41053
|
}
|
|
40964
41054
|
if (!g.workers.has(agentId) && g.terminalPainted && !hasLiveWorker(g)) {
|
|
40965
41055
|
g.messageId = null;
|
|
41056
|
+
g.messageCreatedAtMs = 0;
|
|
40966
41057
|
g.lastBody = null;
|
|
40967
41058
|
g.pendingFinalize.clear();
|
|
40968
41059
|
g.terminalPainted = false;
|
|
@@ -41889,8 +41980,18 @@ function buildVaultGrantDeniedInbound(opts) {
|
|
|
41889
41980
|
}
|
|
41890
41981
|
};
|
|
41891
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
|
+
}
|
|
41892
41992
|
function buildVaultGrantApprovedCardText(opts) {
|
|
41893
|
-
|
|
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 ?? "");
|
|
41894
41995
|
}
|
|
41895
41996
|
function buildVaultSaveCompletedInbound(opts) {
|
|
41896
41997
|
const ts = opts.nowMs ?? Date.now();
|
|
@@ -43163,6 +43264,7 @@ function createCallbackQueryHandlers(deps) {
|
|
|
43163
43264
|
pendingCardStore.remove(stageId);
|
|
43164
43265
|
if (pending.card_message_id != null) {
|
|
43165
43266
|
const days = Math.round(pending.ttl_seconds / 86400);
|
|
43267
|
+
const reasonNormalized = normalizeGrantReason(pending.reason);
|
|
43166
43268
|
const footer = getVaultApprovalAuthMode() === "telegram-id" ? `
|
|
43167
43269
|
_Approver verified by Telegram identity \u2014 broker auto-unlocked at startup._` : "";
|
|
43168
43270
|
await ctx.api.editMessageText(pending.chat_id, pending.card_message_id, richMessage(buildVaultGrantApprovedCardText({
|
|
@@ -43171,6 +43273,7 @@ _Approver verified by Telegram identity \u2014 broker auto-unlocked at startup._
|
|
|
43171
43273
|
key: pending.key,
|
|
43172
43274
|
days,
|
|
43173
43275
|
grantId: id,
|
|
43276
|
+
reasonEscaped: reasonNormalized.length > 0 ? escapeHtmlForTg2(reasonNormalized) : undefined,
|
|
43174
43277
|
footer
|
|
43175
43278
|
})), { reply_markup: { inline_keyboard: [] } }).catch(() => {});
|
|
43176
43279
|
}
|
|
@@ -45035,16 +45138,34 @@ function clipNarrative2(s) {
|
|
|
45035
45138
|
return s.split(`
|
|
45036
45139
|
`)[0].trim().slice(0, STATUS_LINE_MAX);
|
|
45037
45140
|
}
|
|
45038
|
-
function renderActivityHeader2(emoji, label, description, elapsedMs, toolCount, state, model) {
|
|
45141
|
+
function renderActivityHeader2(emoji, label, description, elapsedMs, toolCount, state, model, totalTokens) {
|
|
45039
45142
|
const toolWord = toolCount === 1 ? "tool" : "tools";
|
|
45040
45143
|
const elapsed = formatFeedElapsed2(elapsedMs);
|
|
45041
45144
|
const descPart = description.length > 0 ? ` \u00b7 _${escapeMarkdown(description)}_` : "";
|
|
45042
45145
|
const line1 = `${emoji} **${escapeMarkdown(label)}**${descPart}`;
|
|
45146
|
+
const tokPart = tokenSegment2(totalTokens);
|
|
45043
45147
|
const modelLabel = formatModelLabel(model);
|
|
45044
45148
|
const modelPart = modelLabel != null ? ` \u00b7 ${escapeMarkdown(modelLabel)}` : "";
|
|
45045
|
-
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}_`;
|
|
45046
45150
|
return [line1, line2];
|
|
45047
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
|
+
}
|
|
45048
45169
|
function formatFeedElapsed2(ms) {
|
|
45049
45170
|
const s = Math.floor(ms / 1000);
|
|
45050
45171
|
if (s < 60)
|
|
@@ -45081,7 +45202,7 @@ function renderStatusCard2(opts) {
|
|
|
45081
45202
|
const hasChildren = rawChildren.length > 0;
|
|
45082
45203
|
const steps = rawSteps.map(escapeStepLine2);
|
|
45083
45204
|
const children = rawChildren.map(escapeStepLine2);
|
|
45084
|
-
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) : [];
|
|
45085
45206
|
const out = [...headerLines];
|
|
45086
45207
|
if (hasChildren) {
|
|
45087
45208
|
const shownParent = steps.slice(-STATUS_ROLLING_LINES);
|
|
@@ -45178,7 +45299,8 @@ function renderActivityFeed2(lines, final = false, liveSuffix = "", stepCount, h
|
|
|
45178
45299
|
elapsedMs: header.elapsedMs,
|
|
45179
45300
|
toolCount: header.toolCount,
|
|
45180
45301
|
state: header.state,
|
|
45181
|
-
model: header.model
|
|
45302
|
+
model: header.model,
|
|
45303
|
+
totalTokens: header.totalTokens
|
|
45182
45304
|
} : undefined,
|
|
45183
45305
|
steps: lines,
|
|
45184
45306
|
final,
|
|
@@ -45197,7 +45319,8 @@ function renderActivityFeedWithNested2(lines, childLines, final = false, liveSuf
|
|
|
45197
45319
|
elapsedMs: header.elapsedMs,
|
|
45198
45320
|
toolCount: header.toolCount,
|
|
45199
45321
|
state: header.state,
|
|
45200
|
-
model: header.model
|
|
45322
|
+
model: header.model,
|
|
45323
|
+
totalTokens: header.totalTokens
|
|
45201
45324
|
} : undefined,
|
|
45202
45325
|
steps: lines,
|
|
45203
45326
|
childSteps: children,
|
|
@@ -62872,6 +62995,597 @@ function resolveAnswerLaneConfig(input) {
|
|
|
62872
62995
|
};
|
|
62873
62996
|
}
|
|
62874
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
|
+
|
|
62875
63589
|
// pty-tail.ts
|
|
62876
63590
|
var import_headless = __toESM(require_xterm_headless(), 1);
|
|
62877
63591
|
var PTY_DEBUG = process.env.SWITCHROOM_PTY_DEBUG === "1";
|
|
@@ -62956,7 +63670,7 @@ async function gatewayStartupRetry(fn, opts = {}) {
|
|
|
62956
63670
|
|
|
62957
63671
|
// gateway/quarantine.ts
|
|
62958
63672
|
import { mkdirSync as mkdirSync15, writeFileSync as writeFileSync15 } from "node:fs";
|
|
62959
|
-
import { join as
|
|
63673
|
+
import { join as join22 } from "node:path";
|
|
62960
63674
|
var QUARANTINE_FILENAME = "quarantine.json";
|
|
62961
63675
|
function writeQuarantineMarker(telegramStateDir, reason, detail, nowFn = Date.now) {
|
|
62962
63676
|
mkdirSync15(telegramStateDir, { recursive: true, mode: 448 });
|
|
@@ -62966,7 +63680,7 @@ function writeQuarantineMarker(telegramStateDir, reason, detail, nowFn = Date.no
|
|
|
62966
63680
|
ts: nowFn(),
|
|
62967
63681
|
detail
|
|
62968
63682
|
};
|
|
62969
|
-
writeFileSync15(
|
|
63683
|
+
writeFileSync15(join22(telegramStateDir, QUARANTINE_FILENAME), JSON.stringify(marker) + `
|
|
62970
63684
|
`, "utf-8");
|
|
62971
63685
|
}
|
|
62972
63686
|
|
|
@@ -63985,9 +64699,9 @@ function defaultAddAccount(label, credentials, opts) {
|
|
|
63985
64699
|
// ../src/auth/broker/client.ts
|
|
63986
64700
|
init_protocol2();
|
|
63987
64701
|
import * as net3 from "node:net";
|
|
63988
|
-
import { homedir as
|
|
64702
|
+
import { homedir as homedir8 } from "node:os";
|
|
63989
64703
|
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
63990
|
-
import { join as
|
|
64704
|
+
import { join as join24 } from "node:path";
|
|
63991
64705
|
var DEFAULT_TIMEOUT_MS3 = 5000;
|
|
63992
64706
|
function reviveDate2(v) {
|
|
63993
64707
|
if (v == null)
|
|
@@ -63997,8 +64711,8 @@ function reviveDate2(v) {
|
|
|
63997
64711
|
const d = new Date(v);
|
|
63998
64712
|
return Number.isNaN(d.getTime()) ? null : d;
|
|
63999
64713
|
}
|
|
64000
|
-
function operatorSocketPath2(home2 =
|
|
64001
|
-
return
|
|
64714
|
+
function operatorSocketPath2(home2 = homedir8()) {
|
|
64715
|
+
return join24(home2, ".switchroom", "state", "auth-broker-operator", "sock");
|
|
64002
64716
|
}
|
|
64003
64717
|
function resolveAuthBrokerSocketPath2(opts) {
|
|
64004
64718
|
if (opts?.socket)
|
|
@@ -64312,13 +65026,13 @@ class AuthBrokerClient2 {
|
|
|
64312
65026
|
init_loader();
|
|
64313
65027
|
init_resolver();
|
|
64314
65028
|
init_vault();
|
|
64315
|
-
import { existsSync as
|
|
65029
|
+
import { existsSync as existsSync17 } from "node:fs";
|
|
64316
65030
|
var DEFAULT_VOICE_API_KEY_REF = "vault:openai/api-key";
|
|
64317
65031
|
function tryDirectVaultRead(ref, config, passphrase) {
|
|
64318
65032
|
if (!passphrase)
|
|
64319
65033
|
return null;
|
|
64320
65034
|
const vaultPath = resolvePath(config.vault?.path ?? "~/.switchroom/vault.enc");
|
|
64321
|
-
if (!
|
|
65035
|
+
if (!existsSync17(vaultPath))
|
|
64322
65036
|
return null;
|
|
64323
65037
|
try {
|
|
64324
65038
|
const secrets = openVault(passphrase, vaultPath);
|
|
@@ -64374,14 +65088,14 @@ async function materializeVoiceKey(opts = {}, logger2 = (line) => process.stderr
|
|
|
64374
65088
|
init_loader();
|
|
64375
65089
|
init_resolver();
|
|
64376
65090
|
init_vault();
|
|
64377
|
-
import { existsSync as
|
|
65091
|
+
import { existsSync as existsSync18 } from "node:fs";
|
|
64378
65092
|
var VOICE_SIDECAR_TOKEN_KEY = "voice/sidecar-token";
|
|
64379
65093
|
var DEFAULT_VOICE_SIDECAR_TOKEN_REF = `vault:${VOICE_SIDECAR_TOKEN_KEY}`;
|
|
64380
65094
|
function tryDirectVaultRead2(ref, config, passphrase) {
|
|
64381
65095
|
if (!passphrase)
|
|
64382
65096
|
return null;
|
|
64383
65097
|
const vaultPath = resolvePath(config.vault?.path ?? "~/.switchroom/vault.enc");
|
|
64384
|
-
if (!
|
|
65098
|
+
if (!existsSync18(vaultPath))
|
|
64385
65099
|
return null;
|
|
64386
65100
|
try {
|
|
64387
65101
|
const secrets = openVault(passphrase, vaultPath);
|
|
@@ -64434,17 +65148,17 @@ async function materializeSidecarToken(opts = {}, logger2 = (line) => process.st
|
|
|
64434
65148
|
}
|
|
64435
65149
|
|
|
64436
65150
|
// ../src/setup/host-capabilities.ts
|
|
64437
|
-
import { existsSync as
|
|
65151
|
+
import { existsSync as existsSync19, mkdirSync as mkdirSync18, readFileSync as readFileSync19, writeFileSync as writeFileSync16 } from "node:fs";
|
|
64438
65152
|
init_paths();
|
|
64439
65153
|
function hostCapabilitiesPath() {
|
|
64440
65154
|
return resolveStatePath("host-capabilities.json");
|
|
64441
65155
|
}
|
|
64442
65156
|
function loadHostCapabilities() {
|
|
64443
65157
|
const path2 = hostCapabilitiesPath();
|
|
64444
|
-
if (!
|
|
65158
|
+
if (!existsSync19(path2))
|
|
64445
65159
|
return null;
|
|
64446
65160
|
try {
|
|
64447
|
-
const parsed = JSON.parse(
|
|
65161
|
+
const parsed = JSON.parse(readFileSync19(path2, "utf-8"));
|
|
64448
65162
|
if (parsed && typeof parsed === "object" && "voice" in parsed && typeof parsed.voice === "object") {
|
|
64449
65163
|
return parsed;
|
|
64450
65164
|
}
|
|
@@ -64552,16 +65266,16 @@ function resolveExhaustUntil(resetAtMs, now = Date.now()) {
|
|
|
64552
65266
|
|
|
64553
65267
|
// gateway/auth-add-flow.ts
|
|
64554
65268
|
import { execFileSync as execFileSync3 } from "node:child_process";
|
|
64555
|
-
import { existsSync as
|
|
64556
|
-
import { homedir as
|
|
64557
|
-
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";
|
|
64558
65272
|
import { randomBytes as randomBytes5 } from "node:crypto";
|
|
64559
65273
|
|
|
64560
65274
|
// ../src/auth/manager.ts
|
|
64561
65275
|
import {
|
|
64562
|
-
readFileSync as
|
|
65276
|
+
readFileSync as readFileSync20,
|
|
64563
65277
|
readdirSync as readdirSync3,
|
|
64564
|
-
existsSync as
|
|
65278
|
+
existsSync as existsSync20,
|
|
64565
65279
|
writeFileSync as writeFileSync17,
|
|
64566
65280
|
mkdirSync as mkdirSync19,
|
|
64567
65281
|
mkdtempSync as mkdtempSync2,
|
|
@@ -64588,9 +65302,9 @@ function parseSetupTokenUrl(output) {
|
|
|
64588
65302
|
}
|
|
64589
65303
|
function readTokenFromCredentialsFile(credentialsFilePath) {
|
|
64590
65304
|
try {
|
|
64591
|
-
if (!
|
|
65305
|
+
if (!existsSync20(credentialsFilePath))
|
|
64592
65306
|
return null;
|
|
64593
|
-
const raw =
|
|
65307
|
+
const raw = readFileSync20(credentialsFilePath, "utf-8");
|
|
64594
65308
|
const parsed = JSON.parse(raw);
|
|
64595
65309
|
const token = parsed?.claudeAiOauth?.accessToken;
|
|
64596
65310
|
if (typeof token !== "string")
|
|
@@ -64648,9 +65362,9 @@ function makeAuthAddTmuxOps(tmuxBin = "tmux") {
|
|
|
64648
65362
|
};
|
|
64649
65363
|
}
|
|
64650
65364
|
var pendingAuthAddFlows = new Map;
|
|
64651
|
-
function pickScratchDir(label, home2 =
|
|
65365
|
+
function pickScratchDir(label, home2 = homedir9()) {
|
|
64652
65366
|
const suffix = randomBytes5(8).toString("hex");
|
|
64653
|
-
return
|
|
65367
|
+
return join25(home2, ".switchroom", "accounts", ".in-progress", `${label}-${suffix}`);
|
|
64654
65368
|
}
|
|
64655
65369
|
function cleanScratchDir(scratchDir) {
|
|
64656
65370
|
try {
|
|
@@ -64659,8 +65373,8 @@ function cleanScratchDir(scratchDir) {
|
|
|
64659
65373
|
}
|
|
64660
65374
|
var AUTH_TMUX_SESSION_FILE = ".auth-tmux-session";
|
|
64661
65375
|
function sweepOrphanSessions(home2, tmux, nowMs2 = Date.now()) {
|
|
64662
|
-
const inProgressDir =
|
|
64663
|
-
if (!
|
|
65376
|
+
const inProgressDir = join25(home2, ".switchroom", "accounts", ".in-progress");
|
|
65377
|
+
if (!existsSync21(inProgressDir))
|
|
64664
65378
|
return;
|
|
64665
65379
|
let entries;
|
|
64666
65380
|
try {
|
|
@@ -64670,16 +65384,16 @@ function sweepOrphanSessions(home2, tmux, nowMs2 = Date.now()) {
|
|
|
64670
65384
|
}
|
|
64671
65385
|
const tenMinMs = 10 * 60000;
|
|
64672
65386
|
for (const entry of entries) {
|
|
64673
|
-
const dir =
|
|
64674
|
-
const sessionFile =
|
|
64675
|
-
if (!
|
|
65387
|
+
const dir = join25(inProgressDir, entry);
|
|
65388
|
+
const sessionFile = join25(dir, AUTH_TMUX_SESSION_FILE);
|
|
65389
|
+
if (!existsSync21(sessionFile))
|
|
64676
65390
|
continue;
|
|
64677
65391
|
let fileContents;
|
|
64678
65392
|
let fileMtime;
|
|
64679
65393
|
try {
|
|
64680
65394
|
const stat = statSync7(sessionFile);
|
|
64681
65395
|
fileMtime = stat.mtimeMs;
|
|
64682
|
-
fileContents =
|
|
65396
|
+
fileContents = readFileSync21(sessionFile, "utf8").trim();
|
|
64683
65397
|
} catch {
|
|
64684
65398
|
continue;
|
|
64685
65399
|
}
|
|
@@ -64699,7 +65413,7 @@ async function startAccountAuthSession(label, opts = {}) {
|
|
|
64699
65413
|
if (process.env.SWITCHROOM_TMUX_SUPERVISOR !== "1" && !opts.tmuxOps) {
|
|
64700
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).");
|
|
64701
65415
|
}
|
|
64702
|
-
const home2 = opts.home ??
|
|
65416
|
+
const home2 = opts.home ?? homedir9();
|
|
64703
65417
|
const urlTimeoutMs = opts.urlTimeoutMs ?? 12000;
|
|
64704
65418
|
const agentName3 = opts.agentName ?? process.env.SWITCHROOM_AGENT_NAME ?? "gateway";
|
|
64705
65419
|
const tmux = opts.tmuxOps ?? makeAuthAddTmuxOps(opts.tmuxBin);
|
|
@@ -64711,7 +65425,7 @@ async function startAccountAuthSession(label, opts = {}) {
|
|
|
64711
65425
|
const tmuxSocket = `switchroom-${agentName3}`;
|
|
64712
65426
|
const tmuxSession = `auth-add-${label}-${hexSuffix}`.slice(0, 64);
|
|
64713
65427
|
try {
|
|
64714
|
-
writeFileSync18(
|
|
65428
|
+
writeFileSync18(join25(scratchDir, AUTH_TMUX_SESSION_FILE), `${tmuxSocket}
|
|
64715
65429
|
${tmuxSession}`, "utf8");
|
|
64716
65430
|
} catch {}
|
|
64717
65431
|
const sessionEnv = {
|
|
@@ -64760,7 +65474,7 @@ async function submitAccountAuthCode(flow3, code2, opts = {}) {
|
|
|
64760
65474
|
const pollIntervalMs = opts.pollIntervalMs ?? 250;
|
|
64761
65475
|
const pollTimeoutMs = opts.pollTimeoutMs ?? 300000;
|
|
64762
65476
|
const tmux = opts.tmuxOps ?? makeAuthAddTmuxOps();
|
|
64763
|
-
const credentialsPath =
|
|
65477
|
+
const credentialsPath = join25(flow3.scratchDir, ".credentials.json");
|
|
64764
65478
|
try {
|
|
64765
65479
|
tmux.send(flow3.tmuxSocket, flow3.tmuxSession, code2);
|
|
64766
65480
|
} catch (err) {
|
|
@@ -64770,11 +65484,11 @@ async function submitAccountAuthCode(flow3, code2, opts = {}) {
|
|
|
64770
65484
|
const deadline = Date.now() + pollTimeoutMs;
|
|
64771
65485
|
while (Date.now() < deadline) {
|
|
64772
65486
|
await new Promise((r) => setTimeout(r, pollIntervalMs));
|
|
64773
|
-
if (
|
|
65487
|
+
if (existsSync21(credentialsPath)) {
|
|
64774
65488
|
const token = readTokenFromCredentialsFile(credentialsPath);
|
|
64775
65489
|
if (token) {
|
|
64776
65490
|
try {
|
|
64777
|
-
const raw =
|
|
65491
|
+
const raw = readFileSync21(credentialsPath, "utf-8");
|
|
64778
65492
|
const parsed = JSON.parse(raw);
|
|
64779
65493
|
if (parsed.claudeAiOauth?.accessToken) {
|
|
64780
65494
|
tmux.killSession(flow3.tmuxSocket, flow3.tmuxSession);
|
|
@@ -64784,7 +65498,7 @@ async function submitAccountAuthCode(flow3, code2, opts = {}) {
|
|
|
64784
65498
|
}
|
|
64785
65499
|
}
|
|
64786
65500
|
if (!tmux.hasSession(flow3.tmuxSocket, flow3.tmuxSession)) {
|
|
64787
|
-
if (!
|
|
65501
|
+
if (!existsSync21(credentialsPath)) {
|
|
64788
65502
|
cleanScratchDir(flow3.scratchDir);
|
|
64789
65503
|
throw new Error("claude setup-token exited without writing credentials \u2014 the code may be invalid or expired");
|
|
64790
65504
|
}
|
|
@@ -65236,28 +65950,6 @@ function autoClassifyMidTurnInbound(i) {
|
|
|
65236
65950
|
|
|
65237
65951
|
// operator-events.ts
|
|
65238
65952
|
init_format();
|
|
65239
|
-
|
|
65240
|
-
// raw-error-scrub.ts
|
|
65241
|
-
function stripRawErrorBytes(raw) {
|
|
65242
|
-
if (typeof raw !== "string" || raw.length === 0)
|
|
65243
|
-
return "";
|
|
65244
|
-
let s = raw;
|
|
65245
|
-
s = s.replace(/API Error:?\s*\d*\s*/gi, " ");
|
|
65246
|
-
s = s.replace(/\bb'[^']*'/g, " ");
|
|
65247
|
-
s = s.replace(/\bb"[^"]*"/g, " ");
|
|
65248
|
-
s = s.replace(/[\u00b7\-\s]*\{[\s\S]*?["']type["']\s*:\s*["']error["'][\s\S]*$/i, " ");
|
|
65249
|
-
s = s.replace(/^\s*\{[\s\S]*\}\s*$/g, " ");
|
|
65250
|
-
s = s.replace(/\s+/g, " ").replace(/[\u00b7:\-\s]+$/g, "").replace(/^[\u00b7:\-\s]+/g, "").trim();
|
|
65251
|
-
return s;
|
|
65252
|
-
}
|
|
65253
|
-
function extractRequestId(raw) {
|
|
65254
|
-
if (typeof raw !== "string" || raw.length === 0)
|
|
65255
|
-
return;
|
|
65256
|
-
const m = raw.match(/["']request[_-]?id["']\s*:\s*["']([A-Za-z0-9._-]+)["']/i) ?? raw.match(/\brequest[_-]?id[=:]\s*([A-Za-z0-9._-]+)/i);
|
|
65257
|
-
return m ? m[1] : undefined;
|
|
65258
|
-
}
|
|
65259
|
-
|
|
65260
|
-
// operator-events.ts
|
|
65261
65953
|
function renderOperatorEvent(ev) {
|
|
65262
65954
|
const agent = escapeMarkdown(ev.agent);
|
|
65263
65955
|
const detail = escapeMarkdown(stripRawErrorBytes(ev.detail));
|
|
@@ -65445,15 +66137,15 @@ function renderOperatorEvent(ev) {
|
|
|
65445
66137
|
};
|
|
65446
66138
|
}
|
|
65447
66139
|
}
|
|
65448
|
-
var
|
|
65449
|
-
var
|
|
65450
|
-
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) {
|
|
65451
66143
|
const key = `${agent}:${kind}`;
|
|
65452
|
-
const last =
|
|
66144
|
+
const last = cooldownMap2.get(key);
|
|
65453
66145
|
if (last != null && now - last < cooldownMs) {
|
|
65454
66146
|
return false;
|
|
65455
66147
|
}
|
|
65456
|
-
|
|
66148
|
+
cooldownMap2.set(key, now);
|
|
65457
66149
|
return true;
|
|
65458
66150
|
}
|
|
65459
66151
|
|
|
@@ -65464,290 +66156,6 @@ function recordOperatorEvent(event, now = Date.now()) {
|
|
|
65464
66156
|
store.set(event.agent, { event, storedAt: now });
|
|
65465
66157
|
}
|
|
65466
66158
|
|
|
65467
|
-
// model-unavailable.ts
|
|
65468
|
-
init_quota_check();
|
|
65469
|
-
init_card_format();
|
|
65470
|
-
var transientUpstreamSignals = [
|
|
65471
|
-
"not your usage limit",
|
|
65472
|
-
"not your account",
|
|
65473
|
-
"not your account's",
|
|
65474
|
-
"temporarily limiting requests",
|
|
65475
|
-
"temporarily rate",
|
|
65476
|
-
"server is temporarily",
|
|
65477
|
-
"would exceed your account\u2019s rate limit",
|
|
65478
|
-
"would exceed your account's rate limit"
|
|
65479
|
-
];
|
|
65480
|
-
function isTransientUpstreamSignal(text4) {
|
|
65481
|
-
if (typeof text4 !== "string" || text4.length === 0)
|
|
65482
|
-
return false;
|
|
65483
|
-
const sample = text4.length > 16384 ? text4.slice(0, 16384) : text4;
|
|
65484
|
-
const lower = sample.toLowerCase();
|
|
65485
|
-
return transientUpstreamSignals.some((s) => lower.includes(s));
|
|
65486
|
-
}
|
|
65487
|
-
var litellmProxyLocal429Signals = [
|
|
65488
|
-
"deployment over user-defined ratelimit",
|
|
65489
|
-
"model rate limit exceeded. tpm limit",
|
|
65490
|
-
"model rate limit exceeded. rpm limit",
|
|
65491
|
-
"deployment over defined rpm limit",
|
|
65492
|
-
"no deployments available for selected model",
|
|
65493
|
-
"litellm rate limit handler",
|
|
65494
|
-
"crossed tpm / rpm",
|
|
65495
|
-
"max parallel request limit reached"
|
|
65496
|
-
];
|
|
65497
|
-
var litellmV3LimiterSignalPair = ["rate limit exceeded for ", "limit type:"];
|
|
65498
|
-
function isLitellmProxyLocal429(text4) {
|
|
65499
|
-
if (typeof text4 !== "string" || text4.length === 0)
|
|
65500
|
-
return false;
|
|
65501
|
-
const sample = text4.length > 16384 ? text4.slice(0, 16384) : text4;
|
|
65502
|
-
const lower = sample.toLowerCase();
|
|
65503
|
-
if (litellmProxyLocal429Signals.some((s) => lower.includes(s)))
|
|
65504
|
-
return true;
|
|
65505
|
-
return litellmV3LimiterSignalPair.every((s) => lower.includes(s));
|
|
65506
|
-
}
|
|
65507
|
-
function parseLitellmLimitDetail(text4, parseTimeNow = new Date) {
|
|
65508
|
-
const empty2 = { limitType: null, limit: null, currentUsage: null, resetAtMs: null };
|
|
65509
|
-
if (typeof text4 !== "string" || text4.length === 0)
|
|
65510
|
-
return empty2;
|
|
65511
|
-
const sample = text4.length > 16384 ? text4.slice(0, 16384) : text4;
|
|
65512
|
-
const lower = sample.toLowerCase();
|
|
65513
|
-
let limitType = null;
|
|
65514
|
-
let limit = null;
|
|
65515
|
-
const eqLimit = lower.match(/\b([tr]pm)[ _]limit[=:]\s*(\d+)/);
|
|
65516
|
-
if (eqLimit) {
|
|
65517
|
-
limitType = eqLimit[1];
|
|
65518
|
-
limit = Number(eqLimit[2]);
|
|
65519
|
-
}
|
|
65520
|
-
if (limitType == null) {
|
|
65521
|
-
const v3Type = lower.match(/limit type:\s*(tokens|requests|max_parallel_requests)/);
|
|
65522
|
-
if (v3Type)
|
|
65523
|
-
limitType = v3Type[1];
|
|
65524
|
-
}
|
|
65525
|
-
if (limit == null) {
|
|
65526
|
-
const v3Limit = lower.match(/current limit:\s*(\d+)/);
|
|
65527
|
-
if (v3Limit)
|
|
65528
|
-
limit = Number(v3Limit[1]);
|
|
65529
|
-
}
|
|
65530
|
-
let currentUsage = null;
|
|
65531
|
-
const usage = lower.match(/current usage=(\d+)/);
|
|
65532
|
-
if (usage)
|
|
65533
|
-
currentUsage = Number(usage[1]);
|
|
65534
|
-
let resetAtMs = null;
|
|
65535
|
-
const resetsAt = sample.match(/limit resets at:\s*(\d{4}-\d{2}-\d{2}) (\d{2}:\d{2}:\d{2}) UTC/i);
|
|
65536
|
-
if (resetsAt) {
|
|
65537
|
-
const d = new Date(`${resetsAt[1]}T${resetsAt[2]}Z`);
|
|
65538
|
-
if (!Number.isNaN(d.getTime()))
|
|
65539
|
-
resetAtMs = d.getTime();
|
|
65540
|
-
}
|
|
65541
|
-
if (resetAtMs == null) {
|
|
65542
|
-
const tryAgain = lower.match(/try again in\s+(\d+(?:\.\d+)?)\s*seconds/);
|
|
65543
|
-
if (tryAgain) {
|
|
65544
|
-
const secs = Number(tryAgain[1]);
|
|
65545
|
-
if (Number.isFinite(secs) && secs > 0 && secs < 7 * 24 * 3600) {
|
|
65546
|
-
resetAtMs = parseTimeNow.getTime() + Math.round(secs * 1000);
|
|
65547
|
-
}
|
|
65548
|
-
}
|
|
65549
|
-
}
|
|
65550
|
-
return {
|
|
65551
|
-
limitType,
|
|
65552
|
-
limit: limit != null && Number.isFinite(limit) ? limit : null,
|
|
65553
|
-
currentUsage: currentUsage != null && Number.isFinite(currentUsage) ? currentUsage : null,
|
|
65554
|
-
resetAtMs
|
|
65555
|
-
};
|
|
65556
|
-
}
|
|
65557
|
-
function detectModelUnavailable(stderr) {
|
|
65558
|
-
if (typeof stderr !== "string" || stderr.length === 0)
|
|
65559
|
-
return null;
|
|
65560
|
-
const sample = stderr.length > 16384 ? stderr.slice(0, 16384) : stderr;
|
|
65561
|
-
const lower = sample.toLowerCase();
|
|
65562
|
-
if (transientUpstreamSignals.some((s) => lower.includes(s))) {
|
|
65563
|
-
const resetAt = parseResetTime(sample);
|
|
65564
|
-
return resetAt !== undefined ? { kind: "overload", resetAt, raw: stderr } : { kind: "overload", raw: stderr };
|
|
65565
|
-
}
|
|
65566
|
-
if (isLitellmProxyLocal429(sample)) {
|
|
65567
|
-
const resetAt = parseResetTime(sample);
|
|
65568
|
-
return resetAt !== undefined ? { kind: "overload", resetAt, raw: stderr } : { kind: "overload", raw: stderr };
|
|
65569
|
-
}
|
|
65570
|
-
const quotaSignals = [
|
|
65571
|
-
"out of extra usage",
|
|
65572
|
-
"extra usage",
|
|
65573
|
-
"credit_balance_too_low",
|
|
65574
|
-
"credit balance too low",
|
|
65575
|
-
"usage limit",
|
|
65576
|
-
"usage_limit",
|
|
65577
|
-
"quota exhausted",
|
|
65578
|
-
"quota_exhausted",
|
|
65579
|
-
"plan limit",
|
|
65580
|
-
"subscription limit",
|
|
65581
|
-
"hit your limit",
|
|
65582
|
-
"hit the limit",
|
|
65583
|
-
"session limit",
|
|
65584
|
-
"session cap"
|
|
65585
|
-
];
|
|
65586
|
-
if (quotaSignals.some((s) => lower.includes(s))) {
|
|
65587
|
-
const resetAt = parseResetTime(sample);
|
|
65588
|
-
return resetAt !== undefined ? { kind: "quota_exhausted", resetAt, raw: stderr } : { kind: "quota_exhausted", raw: stderr };
|
|
65589
|
-
}
|
|
65590
|
-
const overloadSignals = [
|
|
65591
|
-
"overloaded_error",
|
|
65592
|
-
"overloaded",
|
|
65593
|
-
"rate_limit_error",
|
|
65594
|
-
"rate limit",
|
|
65595
|
-
"rate-limited",
|
|
65596
|
-
"http 429",
|
|
65597
|
-
'"status":429',
|
|
65598
|
-
"status: 429",
|
|
65599
|
-
" 429 ",
|
|
65600
|
-
"503 service",
|
|
65601
|
-
"service unavailable",
|
|
65602
|
-
'"status":529',
|
|
65603
|
-
"http 529",
|
|
65604
|
-
" 529 "
|
|
65605
|
-
];
|
|
65606
|
-
if (overloadSignals.some((s) => lower.includes(s))) {
|
|
65607
|
-
const resetAt = parseResetTime(sample);
|
|
65608
|
-
return resetAt !== undefined ? { kind: "overload", resetAt, raw: stderr } : { kind: "overload", raw: stderr };
|
|
65609
|
-
}
|
|
65610
|
-
const networkSignals = [
|
|
65611
|
-
"econnrefused",
|
|
65612
|
-
"econnreset",
|
|
65613
|
-
"etimedout",
|
|
65614
|
-
"enotfound",
|
|
65615
|
-
"eai_again",
|
|
65616
|
-
"fetch failed",
|
|
65617
|
-
"network error",
|
|
65618
|
-
"socket hang up",
|
|
65619
|
-
"request timed out",
|
|
65620
|
-
"connection refused",
|
|
65621
|
-
"getaddrinfo"
|
|
65622
|
-
];
|
|
65623
|
-
if (networkSignals.some((s) => lower.includes(s))) {
|
|
65624
|
-
return { kind: "network", raw: stderr };
|
|
65625
|
-
}
|
|
65626
|
-
return null;
|
|
65627
|
-
}
|
|
65628
|
-
function parseResetTime(text4, parseTimeNow = new Date) {
|
|
65629
|
-
const lower = text4.toLowerCase();
|
|
65630
|
-
const retryAfter = lower.match(/retry[\s-]*after[:\s]+(\d+)\s*(seconds?|s\b|minutes?|m\b|hours?|h\b)?/);
|
|
65631
|
-
if (retryAfter) {
|
|
65632
|
-
const n = Number(retryAfter[1]);
|
|
65633
|
-
if (Number.isFinite(n) && n > 0 && n < 7 * 24 * 3600) {
|
|
65634
|
-
const unit = (retryAfter[2] ?? "seconds").toLowerCase();
|
|
65635
|
-
const ms = unit.startsWith("h") ? n * 3600000 : unit.startsWith("m") ? n * 60000 : n * 1000;
|
|
65636
|
-
return new Date(parseTimeNow.getTime() + ms);
|
|
65637
|
-
}
|
|
65638
|
-
}
|
|
65639
|
-
const relReset = lower.match(/resets?\s+in\s+([0-9hms\s]+)/);
|
|
65640
|
-
if (relReset) {
|
|
65641
|
-
const ms = parseRelativeDuration(relReset[1]);
|
|
65642
|
-
if (ms != null)
|
|
65643
|
-
return new Date(parseTimeNow.getTime() + ms);
|
|
65644
|
-
}
|
|
65645
|
-
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/);
|
|
65646
|
-
if (iso) {
|
|
65647
|
-
const d = new Date(iso[0]);
|
|
65648
|
-
if (!Number.isNaN(d.getTime()))
|
|
65649
|
-
return d;
|
|
65650
|
-
}
|
|
65651
|
-
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)?))?)/);
|
|
65652
|
-
if (calReset) {
|
|
65653
|
-
const candidate = `${calReset[1]} ${parseTimeNow.getUTCFullYear()}`;
|
|
65654
|
-
const d = new Date(candidate);
|
|
65655
|
-
if (!Number.isNaN(d.getTime()))
|
|
65656
|
-
return d;
|
|
65657
|
-
}
|
|
65658
|
-
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);
|
|
65659
|
-
if (timeOnly) {
|
|
65660
|
-
const d = resolveNextWallClock(Number(timeOnly[1]), timeOnly[2] ? Number(timeOnly[2]) : 0, timeOnly[3]?.toLowerCase(), timeOnly[4]?.trim(), parseTimeNow);
|
|
65661
|
-
if (d != null)
|
|
65662
|
-
return d;
|
|
65663
|
-
}
|
|
65664
|
-
return;
|
|
65665
|
-
}
|
|
65666
|
-
function resolveNextWallClock(hour12or24, minute, ampm, tz, nowDate) {
|
|
65667
|
-
let hour = hour12or24;
|
|
65668
|
-
if (ampm === "pm" && hour < 12)
|
|
65669
|
-
hour += 12;
|
|
65670
|
-
if (ampm === "am" && hour === 12)
|
|
65671
|
-
hour = 0;
|
|
65672
|
-
if (!Number.isFinite(hour) || hour > 23 || hour < 0)
|
|
65673
|
-
return;
|
|
65674
|
-
if (!Number.isFinite(minute) || minute > 59 || minute < 0)
|
|
65675
|
-
return;
|
|
65676
|
-
const nowMs2 = nowDate.getTime();
|
|
65677
|
-
const base = new Date(nowMs2);
|
|
65678
|
-
for (let dayOffset = 0;dayOffset <= 2; dayOffset++) {
|
|
65679
|
-
const dateParts = tzDateParts(new Date(nowMs2 + dayOffset * 86400000), tz);
|
|
65680
|
-
if (dateParts == null)
|
|
65681
|
-
return;
|
|
65682
|
-
const epoch = wallClockToEpoch(dateParts.year, dateParts.month, dateParts.day, hour, minute, tz);
|
|
65683
|
-
if (epoch != null && epoch > nowMs2)
|
|
65684
|
-
return new Date(epoch);
|
|
65685
|
-
}
|
|
65686
|
-
return;
|
|
65687
|
-
}
|
|
65688
|
-
function tzDateParts(d, tz) {
|
|
65689
|
-
if (!tz) {
|
|
65690
|
-
return { year: d.getUTCFullYear(), month: d.getUTCMonth(), day: d.getUTCDate() };
|
|
65691
|
-
}
|
|
65692
|
-
try {
|
|
65693
|
-
const fmt = new Intl.DateTimeFormat("en-US", {
|
|
65694
|
-
timeZone: tz,
|
|
65695
|
-
year: "numeric",
|
|
65696
|
-
month: "2-digit",
|
|
65697
|
-
day: "2-digit"
|
|
65698
|
-
});
|
|
65699
|
-
const parts = Object.fromEntries(fmt.formatToParts(d).filter((p) => p.type !== "literal").map((p) => [p.type, p.value]));
|
|
65700
|
-
return {
|
|
65701
|
-
year: Number(parts.year),
|
|
65702
|
-
month: Number(parts.month) - 1,
|
|
65703
|
-
day: Number(parts.day)
|
|
65704
|
-
};
|
|
65705
|
-
} catch {
|
|
65706
|
-
return null;
|
|
65707
|
-
}
|
|
65708
|
-
}
|
|
65709
|
-
function wallClockToEpoch(year, month, day, hour, minute, tz) {
|
|
65710
|
-
const asUtc = Date.UTC(year, month, day, hour, minute, 0);
|
|
65711
|
-
if (!tz)
|
|
65712
|
-
return asUtc;
|
|
65713
|
-
try {
|
|
65714
|
-
const fmt = new Intl.DateTimeFormat("en-US", {
|
|
65715
|
-
timeZone: tz,
|
|
65716
|
-
year: "numeric",
|
|
65717
|
-
month: "2-digit",
|
|
65718
|
-
day: "2-digit",
|
|
65719
|
-
hour: "2-digit",
|
|
65720
|
-
minute: "2-digit",
|
|
65721
|
-
second: "2-digit",
|
|
65722
|
-
hour12: false
|
|
65723
|
-
});
|
|
65724
|
-
const parts = Object.fromEntries(fmt.formatToParts(new Date(asUtc)).filter((p) => p.type !== "literal").map((p) => [p.type, p.value]));
|
|
65725
|
-
const shown = Date.UTC(Number(parts.year), Number(parts.month) - 1, Number(parts.day), Number(parts.hour) % 24, Number(parts.minute), Number(parts.second));
|
|
65726
|
-
const offset = shown - asUtc;
|
|
65727
|
-
return asUtc - offset;
|
|
65728
|
-
} catch {
|
|
65729
|
-
return null;
|
|
65730
|
-
}
|
|
65731
|
-
}
|
|
65732
|
-
function parseRelativeDuration(s) {
|
|
65733
|
-
let total = 0;
|
|
65734
|
-
let matched = false;
|
|
65735
|
-
const re = /(\d+)\s*(h|hours?|m|minutes?|s|seconds?)/g;
|
|
65736
|
-
let m;
|
|
65737
|
-
while ((m = re.exec(s)) != null) {
|
|
65738
|
-
matched = true;
|
|
65739
|
-
const n = Number(m[1]);
|
|
65740
|
-
const unit = m[2].toLowerCase();
|
|
65741
|
-
if (unit.startsWith("h"))
|
|
65742
|
-
total += n * 3600000;
|
|
65743
|
-
else if (unit.startsWith("m"))
|
|
65744
|
-
total += n * 60000;
|
|
65745
|
-
else
|
|
65746
|
-
total += n * 1000;
|
|
65747
|
-
}
|
|
65748
|
-
return matched && total > 0 ? total : null;
|
|
65749
|
-
}
|
|
65750
|
-
|
|
65751
66159
|
// throttle-tier.ts
|
|
65752
66160
|
init_card_format();
|
|
65753
66161
|
init_quota_check();
|
|
@@ -65807,72 +66215,6 @@ function renderThrottleEscalationNotice(opts) {
|
|
|
65807
66215
|
${tail}`;
|
|
65808
66216
|
}
|
|
65809
66217
|
|
|
65810
|
-
// operator-events.ts
|
|
65811
|
-
init_format();
|
|
65812
|
-
function classifyClaudeError(raw) {
|
|
65813
|
-
try {
|
|
65814
|
-
return classifyInner(raw);
|
|
65815
|
-
} catch {
|
|
65816
|
-
return "unknown-4xx";
|
|
65817
|
-
}
|
|
65818
|
-
}
|
|
65819
|
-
function classifyInner(raw) {
|
|
65820
|
-
if (raw == null)
|
|
65821
|
-
return "unknown-4xx";
|
|
65822
|
-
const obj = typeof raw === "object" ? raw : {};
|
|
65823
|
-
const errorType = extractString(obj, "error_type") ?? extractString(obj, "type") ?? extractString(getNestedObj(obj, "error"), "type") ?? "";
|
|
65824
|
-
const errorCode = extractString(obj, "code") ?? extractString(getNestedObj(obj, "error"), "code") ?? "";
|
|
65825
|
-
const message = extractString(obj, "message") ?? extractString(getNestedObj(obj, "error"), "message") ?? (typeof raw === "string" ? raw : "") ?? "";
|
|
65826
|
-
const status = extractNumber(obj, "status") ?? extractNumber(obj, "statusCode") ?? extractNumber(obj, "status_code") ?? null;
|
|
65827
|
-
const sdkCode = extractString(obj, "error_code") ?? "";
|
|
65828
|
-
if (errorType === "authentication_error" || errorCode === "authentication_error" || sdkCode === "authentication_error" || message.toLowerCase().includes("authentication_error")) {
|
|
65829
|
-
const msg = message.toLowerCase();
|
|
65830
|
-
if (msg.includes("expired") || msg.includes("refresh")) {
|
|
65831
|
-
return "credentials-expired";
|
|
65832
|
-
}
|
|
65833
|
-
return "credentials-invalid";
|
|
65834
|
-
}
|
|
65835
|
-
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")) {
|
|
65836
|
-
return "credentials-invalid";
|
|
65837
|
-
}
|
|
65838
|
-
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")) {
|
|
65839
|
-
return "credit-exhausted";
|
|
65840
|
-
}
|
|
65841
|
-
if (errorType === "rate_limit_error" || errorCode === "rate_limit_error" || sdkCode === "rate_limit_error" || message.toLowerCase().includes("rate_limit_error") || message.toLowerCase().includes("rate limit")) {
|
|
65842
|
-
return "rate-limited";
|
|
65843
|
-
}
|
|
65844
|
-
if (errorType === "overloaded_error" || errorCode === "overloaded_error" || sdkCode === "overloaded_error" || message.toLowerCase().includes("overloaded_error") || message.toLowerCase().includes("overloaded")) {
|
|
65845
|
-
return "rate-limited";
|
|
65846
|
-
}
|
|
65847
|
-
if (errorType === "agent-crashed" || errorCode === "agent-crashed") {
|
|
65848
|
-
return "agent-crashed";
|
|
65849
|
-
}
|
|
65850
|
-
if (errorType === "agent-restarted-unexpectedly" || errorCode === "agent-restarted-unexpectedly") {
|
|
65851
|
-
return "agent-restarted-unexpectedly";
|
|
65852
|
-
}
|
|
65853
|
-
if (status != null) {
|
|
65854
|
-
if (status >= 400 && status < 500)
|
|
65855
|
-
return "unknown-4xx";
|
|
65856
|
-
if (status >= 500 && status < 600)
|
|
65857
|
-
return "unknown-5xx";
|
|
65858
|
-
}
|
|
65859
|
-
return "unknown-4xx";
|
|
65860
|
-
}
|
|
65861
|
-
function extractString(obj, key) {
|
|
65862
|
-
const v = obj[key];
|
|
65863
|
-
return typeof v === "string" && v.length > 0 ? v : null;
|
|
65864
|
-
}
|
|
65865
|
-
function extractNumber(obj, key) {
|
|
65866
|
-
const v = obj[key];
|
|
65867
|
-
return typeof v === "number" ? v : null;
|
|
65868
|
-
}
|
|
65869
|
-
function getNestedObj(obj, key) {
|
|
65870
|
-
const v = obj[key];
|
|
65871
|
-
return typeof v === "object" && v != null ? v : {};
|
|
65872
|
-
}
|
|
65873
|
-
var DEFAULT_OPERATOR_EVENT_COOLDOWN_MS2 = 5 * 60000;
|
|
65874
|
-
var cooldownMap2 = new Map;
|
|
65875
|
-
|
|
65876
66218
|
// llm-error-present.ts
|
|
65877
66219
|
function extractModel(raw) {
|
|
65878
66220
|
const m = raw.match(/["']?model["']?\s*[=:]\s*["']?((?:claude|sr)[A-Za-z0-9._-]+)/i);
|
|
@@ -67373,6 +67715,35 @@ function richMessage2(markdown) {
|
|
|
67373
67715
|
return { markdown };
|
|
67374
67716
|
}
|
|
67375
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
|
+
|
|
67376
67747
|
// text-voice-scrub.ts
|
|
67377
67748
|
var NULL3 = "\x00";
|
|
67378
67749
|
var FENCE_PH2 = `${NULL3}VS_FENCE`;
|
|
@@ -68360,10 +68731,10 @@ function resolveAgentDirFromEnv() {
|
|
|
68360
68731
|
|
|
68361
68732
|
// active-reactions.ts
|
|
68362
68733
|
import { readFileSync as readFileSync22, writeFileSync as writeFileSync19, renameSync as renameSync7, existsSync as existsSync23, unlinkSync as unlinkSync11 } from "node:fs";
|
|
68363
|
-
import { join as
|
|
68734
|
+
import { join as join27 } from "node:path";
|
|
68364
68735
|
var ACTIVE_REACTIONS_FILENAME = ".active-reactions.json";
|
|
68365
68736
|
function reactionsPath(agentDir) {
|
|
68366
|
-
return
|
|
68737
|
+
return join27(agentDir, ACTIVE_REACTIONS_FILENAME);
|
|
68367
68738
|
}
|
|
68368
68739
|
function readActiveReactions(agentDir) {
|
|
68369
68740
|
const p = reactionsPath(agentDir);
|
|
@@ -68428,10 +68799,10 @@ function clearActiveReactions(agentDir) {
|
|
|
68428
68799
|
|
|
68429
68800
|
// active-reactions.ts
|
|
68430
68801
|
import { readFileSync as readFileSync23, writeFileSync as writeFileSync20, renameSync as renameSync8, existsSync as existsSync24, unlinkSync as unlinkSync12 } from "node:fs";
|
|
68431
|
-
import { join as
|
|
68802
|
+
import { join as join28 } from "node:path";
|
|
68432
68803
|
var ACTIVE_REACTIONS_FILENAME2 = ".active-reactions.json";
|
|
68433
68804
|
function reactionsPath2(agentDir) {
|
|
68434
|
-
return
|
|
68805
|
+
return join28(agentDir, ACTIVE_REACTIONS_FILENAME2);
|
|
68435
68806
|
}
|
|
68436
68807
|
function readActiveReactions2(agentDir) {
|
|
68437
68808
|
const p = reactionsPath2(agentDir);
|
|
@@ -69349,12 +69720,12 @@ async function approvalRecord(args, opts) {
|
|
|
69349
69720
|
|
|
69350
69721
|
// quota-check.ts
|
|
69351
69722
|
import { readFileSync as readFileSync24, existsSync as existsSync25 } from "fs";
|
|
69352
|
-
import { join as
|
|
69723
|
+
import { join as join29 } from "path";
|
|
69353
69724
|
var OAUTH_BETA2 = "oauth-2025-04-20";
|
|
69354
69725
|
var DEFAULT_USER_AGENT2 = "claude-cli/1.0.0 (external, cli)";
|
|
69355
69726
|
var DEFAULT_PROBE_MODEL2 = "claude-haiku-4-5-20251001";
|
|
69356
69727
|
function readOauthToken2(claudeConfigDir) {
|
|
69357
|
-
const tokenFile =
|
|
69728
|
+
const tokenFile = join29(claudeConfigDir, ".oauth-token");
|
|
69358
69729
|
if (!existsSync25(tokenFile))
|
|
69359
69730
|
return null;
|
|
69360
69731
|
try {
|
|
@@ -69754,18 +70125,32 @@ async function injectSlashCommand(agentName3, command, opts = {}) {
|
|
|
69754
70125
|
const socket = opts.socketName ?? defaultSocketName(agentName3);
|
|
69755
70126
|
const session = opts.sessionName ?? agentName3;
|
|
69756
70127
|
const settleMs = opts.settleMs ?? 2000;
|
|
69757
|
-
const
|
|
70128
|
+
const signalMode = !!(opts.successPattern || opts.errorPattern);
|
|
70129
|
+
const timeoutMs = opts.timeoutMs ?? (signalMode ? 8000 : 5000);
|
|
69758
70130
|
return withPaneLock(`${socket}:${session}`, () => injectSlashCommandWith(makeTmuxRunner(tmuxBin), {
|
|
69759
70131
|
socket,
|
|
69760
70132
|
session,
|
|
69761
70133
|
command: command.trim(),
|
|
69762
70134
|
settleMs,
|
|
69763
70135
|
timeoutMs,
|
|
69764
|
-
precondition: opts.precondition
|
|
70136
|
+
precondition: opts.precondition,
|
|
70137
|
+
successPattern: opts.successPattern,
|
|
70138
|
+
errorPattern: opts.errorPattern,
|
|
70139
|
+
settleBeforeSendMs: opts.settleBeforeSendMs
|
|
69765
70140
|
}));
|
|
69766
70141
|
}
|
|
69767
70142
|
async function injectSlashCommandWith(runner, args) {
|
|
69768
|
-
const {
|
|
70143
|
+
const {
|
|
70144
|
+
socket,
|
|
70145
|
+
session,
|
|
70146
|
+
command,
|
|
70147
|
+
settleMs,
|
|
70148
|
+
timeoutMs,
|
|
70149
|
+
precondition,
|
|
70150
|
+
successPattern,
|
|
70151
|
+
errorPattern,
|
|
70152
|
+
settleBeforeSendMs
|
|
70153
|
+
} = args;
|
|
69769
70154
|
let bareVerb;
|
|
69770
70155
|
try {
|
|
69771
70156
|
bareVerb = validateInjectCommand(command);
|
|
@@ -69806,6 +70191,17 @@ async function injectSlashCommandWith(runner, args) {
|
|
|
69806
70191
|
errorMessage: "inject precondition returned false at write time; send aborted " + "(no keys sent)."
|
|
69807
70192
|
};
|
|
69808
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
|
+
}
|
|
69809
70205
|
const before = runner.capture(socket, session) ?? "";
|
|
69810
70206
|
try {
|
|
69811
70207
|
runner.send(socket, session, ["send-keys", "-l", command]);
|
|
@@ -69824,9 +70220,23 @@ async function injectSlashCommandWith(runner, args) {
|
|
|
69824
70220
|
const start = Date.now();
|
|
69825
70221
|
let last = before;
|
|
69826
70222
|
let stableSince = null;
|
|
70223
|
+
const signalMode = !!(successPattern || errorPattern);
|
|
69827
70224
|
while (Date.now() - start < timeoutMs) {
|
|
69828
70225
|
await sleep(POLL_INTERVAL_MS2);
|
|
69829
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
|
+
}
|
|
69830
70240
|
if (cur === last && cur !== before) {
|
|
69831
70241
|
if (stableSince === null) {
|
|
69832
70242
|
stableSince = Date.now();
|
|
@@ -70261,7 +70671,11 @@ async function handleModelCommand(parsed, deps) {
|
|
|
70261
70671
|
const verbHtml = `\`/model ${deps.escapeHtml(model)}\``;
|
|
70262
70672
|
let result;
|
|
70263
70673
|
try {
|
|
70264
|
-
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
|
+
});
|
|
70265
70679
|
} catch (err) {
|
|
70266
70680
|
const msg = err instanceof Error ? err.message : String(err);
|
|
70267
70681
|
return {
|
|
@@ -70269,21 +70683,21 @@ async function handleModelCommand(parsed, deps) {
|
|
|
70269
70683
|
html: true
|
|
70270
70684
|
};
|
|
70271
70685
|
}
|
|
70272
|
-
if (result.outcome === "ok") {
|
|
70273
|
-
const
|
|
70274
|
-
if (errLine) {
|
|
70275
|
-
return {
|
|
70276
|
-
text: [
|
|
70277
|
-
`\u274c ${verbHtml} \u2014 the switch did not take:`,
|
|
70278
|
-
deps.preBlock(errLine),
|
|
70279
|
-
"Check `/model` for valid model names."
|
|
70280
|
-
].join(`
|
|
70281
|
-
`),
|
|
70282
|
-
html: true
|
|
70283
|
-
};
|
|
70284
|
-
}
|
|
70285
|
-
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;
|
|
70286
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
|
+
}
|
|
70287
70701
|
const confirmed = sessionModelFromConfirmation(confirmation) ?? model;
|
|
70288
70702
|
return {
|
|
70289
70703
|
text: [
|
|
@@ -70294,26 +70708,31 @@ async function handleModelCommand(parsed, deps) {
|
|
|
70294
70708
|
].join(`
|
|
70295
70709
|
`),
|
|
70296
70710
|
html: true,
|
|
70297
|
-
|
|
70711
|
+
selectedModel: confirmed
|
|
70298
70712
|
};
|
|
70299
70713
|
}
|
|
70300
|
-
|
|
70301
|
-
|
|
70302
|
-
|
|
70303
|
-
|
|
70304
|
-
|
|
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(`
|
|
70305
70722
|
`),
|
|
70306
|
-
|
|
70307
|
-
|
|
70308
|
-
|
|
70309
|
-
|
|
70723
|
+
html: true
|
|
70724
|
+
};
|
|
70725
|
+
}
|
|
70726
|
+
const optimisticLabel = optimisticModelRecordLabel(model);
|
|
70310
70727
|
return {
|
|
70311
70728
|
text: [
|
|
70312
|
-
`${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.`,
|
|
70313
70730
|
PERSIST_NOTE
|
|
70314
70731
|
].join(`
|
|
70315
70732
|
`),
|
|
70316
|
-
html: true
|
|
70733
|
+
html: true,
|
|
70734
|
+
selectedModel: optimisticLabel,
|
|
70735
|
+
optimistic: true
|
|
70317
70736
|
};
|
|
70318
70737
|
}
|
|
70319
70738
|
if (result.errorCode === "session_missing") {
|
|
@@ -70376,6 +70795,15 @@ function expandSrAlias(arg) {
|
|
|
70376
70795
|
function srFriendlyLabel(srName) {
|
|
70377
70796
|
return SR_MODEL_LABELS[srName] ?? srName.replace(/^sr-/, "").replace(/-/g, " ");
|
|
70378
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
|
+
}
|
|
70379
70807
|
function classifyDiscoveredOptions(options) {
|
|
70380
70808
|
return {
|
|
70381
70809
|
claude: options.filter((o) => !o.label.startsWith("sr-") && !o.label.includes("/") && (/^[A-Z]/.test(o.label) || o.label.startsWith("claude-"))),
|
|
@@ -70548,7 +70976,11 @@ async function handleModelMenuCallback(data, deps) {
|
|
|
70548
70976
|
}
|
|
70549
70977
|
let aliasResult;
|
|
70550
70978
|
try {
|
|
70551
|
-
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
|
+
});
|
|
70552
70984
|
} catch (err) {
|
|
70553
70985
|
const msg = err instanceof Error ? err.message : String(err);
|
|
70554
70986
|
return {
|
|
@@ -70556,16 +70988,32 @@ async function handleModelMenuCallback(data, deps) {
|
|
|
70556
70988
|
reply: await menuWithBanner(deps, `\u274c Switch to **${deps.escapeHtml(alias)}** failed: ${deps.escapeHtml(msg)}`)
|
|
70557
70989
|
};
|
|
70558
70990
|
}
|
|
70559
|
-
if (aliasResult.outcome === "ok") {
|
|
70560
|
-
const confirmation = modelSwitchConfirmationLine(aliasResult.output)
|
|
70561
|
-
|
|
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);
|
|
70562
71012
|
return {
|
|
70563
|
-
answer:
|
|
70564
|
-
reply: await menuWithBannerStatic(deps,
|
|
70565
|
-
|
|
70566
|
-
|
|
70567
|
-
selectedModelToken: alias
|
|
70568
|
-
}
|
|
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
|
|
70569
71017
|
};
|
|
70570
71018
|
}
|
|
70571
71019
|
return {
|
|
@@ -70662,7 +71110,7 @@ function modelSwitchConfirmationLine(output) {
|
|
|
70662
71110
|
`).map((l) => l.trim()).find((l) => MODEL_SWITCH_CONFIRMATION_PREFIX.test(l));
|
|
70663
71111
|
return line && line.length > 0 ? line : null;
|
|
70664
71112
|
}
|
|
70665
|
-
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;
|
|
70666
71114
|
function modelSwitchErrorLine(output) {
|
|
70667
71115
|
const line = output.split(`
|
|
70668
71116
|
`).map((l) => l.trim()).find((l) => MODEL_SWITCH_ERROR_RE.test(l));
|
|
@@ -70711,7 +71159,7 @@ async function menuWithBannerStatic(deps, banner) {
|
|
|
70711
71159
|
|
|
70712
71160
|
// gateway/session-model-file.ts
|
|
70713
71161
|
import { readFileSync as readFileSync25, writeFileSync as writeFileSync21, renameSync as renameSync9, rmSync as rmSync4 } from "node:fs";
|
|
70714
|
-
import { join as
|
|
71162
|
+
import { join as join30 } from "node:path";
|
|
70715
71163
|
|
|
70716
71164
|
// gateway/model-command.ts
|
|
70717
71165
|
var MODEL_ARG_RE2 = /^[A-Za-z0-9][A-Za-z0-9._/\[\]-]{0,99}$/;
|
|
@@ -70740,18 +71188,18 @@ function writeSessionModelFile(agentDir, model, configuredDefaultAtWrite) {
|
|
|
70740
71188
|
if (!isValidModelArg2(model)) {
|
|
70741
71189
|
throw new Error(`refusing to persist non-canonical session model token: ${JSON.stringify(model)}`);
|
|
70742
71190
|
}
|
|
70743
|
-
atomicWrite(
|
|
71191
|
+
atomicWrite(join30(agentDir, SESSION_MODEL_FILE), serializeSessionModel({ model, configuredDefaultAtWrite, ts: Date.now() }));
|
|
70744
71192
|
}
|
|
70745
71193
|
function readSessionModelFileRaw(agentDir) {
|
|
70746
71194
|
try {
|
|
70747
|
-
return readFileSync25(
|
|
71195
|
+
return readFileSync25(join30(agentDir, SESSION_MODEL_FILE), "utf8");
|
|
70748
71196
|
} catch {
|
|
70749
71197
|
return null;
|
|
70750
71198
|
}
|
|
70751
71199
|
}
|
|
70752
71200
|
function clearSessionModelFile(agentDir) {
|
|
70753
71201
|
try {
|
|
70754
|
-
rmSync4(
|
|
71202
|
+
rmSync4(join30(agentDir, SESSION_MODEL_FILE), { force: true });
|
|
70755
71203
|
} catch {}
|
|
70756
71204
|
}
|
|
70757
71205
|
function restoreSessionModelFileRaw(agentDir, raw) {
|
|
@@ -70760,12 +71208,12 @@ function restoreSessionModelFileRaw(agentDir, raw) {
|
|
|
70760
71208
|
return;
|
|
70761
71209
|
}
|
|
70762
71210
|
try {
|
|
70763
|
-
atomicWrite(
|
|
71211
|
+
atomicWrite(join30(agentDir, SESSION_MODEL_FILE), raw);
|
|
70764
71212
|
} catch {}
|
|
70765
71213
|
}
|
|
70766
71214
|
function readConfiguredDefaultModel(agentDir) {
|
|
70767
71215
|
try {
|
|
70768
|
-
const v = readFileSync25(
|
|
71216
|
+
const v = readFileSync25(join30(agentDir, CONFIGURED_DEFAULT_MODEL_FILE), "utf8").trim();
|
|
70769
71217
|
return v.length > 0 ? v : null;
|
|
70770
71218
|
} catch {
|
|
70771
71219
|
return null;
|
|
@@ -70777,12 +71225,12 @@ function writeSessionEffortFile(agentDir, level, configuredDefaultAtWrite) {
|
|
|
70777
71225
|
if (!EFFORT_LEVEL_RE.test(level)) {
|
|
70778
71226
|
throw new Error(`refusing to persist non-allowlisted effort level: ${JSON.stringify(level)}`);
|
|
70779
71227
|
}
|
|
70780
|
-
atomicWrite(
|
|
71228
|
+
atomicWrite(join30(agentDir, SESSION_EFFORT_FILE), `${JSON.stringify({ level, configuredDefaultAtWrite: configuredDefaultAtWrite ?? "", ts: Date.now() })}
|
|
70781
71229
|
`);
|
|
70782
71230
|
}
|
|
70783
71231
|
function clearSessionEffortFile(agentDir) {
|
|
70784
71232
|
try {
|
|
70785
|
-
rmSync4(
|
|
71233
|
+
rmSync4(join30(agentDir, SESSION_EFFORT_FILE), { force: true });
|
|
70786
71234
|
} catch {}
|
|
70787
71235
|
}
|
|
70788
71236
|
var PREMIUM_RECOVERY_FILE = ".premium-recovery";
|
|
@@ -70805,13 +71253,13 @@ function writePremiumRecoveryFile(agentDir, premiumModel, chats) {
|
|
|
70805
71253
|
if (clean.length === 0) {
|
|
70806
71254
|
throw new Error("refusing to persist premium-recovery marker with no chats to notify");
|
|
70807
71255
|
}
|
|
70808
|
-
atomicWrite(
|
|
71256
|
+
atomicWrite(join30(agentDir, PREMIUM_RECOVERY_FILE), `${JSON.stringify({ premiumModel, chats: clean, ts: Date.now() })}
|
|
70809
71257
|
`);
|
|
70810
71258
|
}
|
|
70811
71259
|
function readPremiumRecoveryFile(agentDir) {
|
|
70812
71260
|
let raw;
|
|
70813
71261
|
try {
|
|
70814
|
-
raw = readFileSync25(
|
|
71262
|
+
raw = readFileSync25(join30(agentDir, PREMIUM_RECOVERY_FILE), "utf8");
|
|
70815
71263
|
} catch {
|
|
70816
71264
|
return null;
|
|
70817
71265
|
}
|
|
@@ -70824,7 +71272,7 @@ function readPremiumRecoveryFile(agentDir) {
|
|
|
70824
71272
|
}
|
|
70825
71273
|
function clearPremiumRecoveryFile(agentDir) {
|
|
70826
71274
|
try {
|
|
70827
|
-
rmSync4(
|
|
71275
|
+
rmSync4(join30(agentDir, PREMIUM_RECOVERY_FILE), { force: true });
|
|
70828
71276
|
} catch {}
|
|
70829
71277
|
}
|
|
70830
71278
|
|
|
@@ -71016,7 +71464,7 @@ function makeIo(agentName3, opts) {
|
|
|
71016
71464
|
socket: opts.socketName ?? `switchroom-${agentName3}`,
|
|
71017
71465
|
session: opts.sessionName ?? agentName3,
|
|
71018
71466
|
stepMs: opts.stepMs ?? 600,
|
|
71019
|
-
timeoutMs: opts.timeoutMs ??
|
|
71467
|
+
timeoutMs: opts.timeoutMs ?? 12000,
|
|
71020
71468
|
sleep: opts._sleep ?? realSleep,
|
|
71021
71469
|
log: opts._log ?? ((line) => process.stderr.write(`${line}
|
|
71022
71470
|
`)),
|
|
@@ -71039,7 +71487,7 @@ function sendLiteral(io, text4) {
|
|
|
71039
71487
|
function sendKey(io, key) {
|
|
71040
71488
|
io.runner.send(io.socket, io.session, ["send-keys", key]);
|
|
71041
71489
|
}
|
|
71042
|
-
async function openPicker(io) {
|
|
71490
|
+
async function openPicker(io, deadlineMs) {
|
|
71043
71491
|
sendLiteral(io, "/model");
|
|
71044
71492
|
sendKey(io, "Enter");
|
|
71045
71493
|
for (;; ) {
|
|
@@ -71048,10 +71496,24 @@ async function openPicker(io) {
|
|
|
71048
71496
|
const parsed = parseModelPicker(pane);
|
|
71049
71497
|
if (parsed?.footerSeen)
|
|
71050
71498
|
return parsed;
|
|
71051
|
-
if (expired(io))
|
|
71499
|
+
if (deadlineMs != null && Date.now() >= deadlineMs || expired(io)) {
|
|
71052
71500
|
return parsed;
|
|
71501
|
+
}
|
|
71053
71502
|
}
|
|
71054
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
|
+
}
|
|
71055
71517
|
async function dismissPicker(io) {
|
|
71056
71518
|
for (let attempt = 0;attempt < 2; attempt++) {
|
|
71057
71519
|
try {
|
|
@@ -71081,7 +71543,7 @@ async function discoverModels(agentName3, opts = {}) {
|
|
|
71081
71543
|
let parsed = null;
|
|
71082
71544
|
let dismissed = true;
|
|
71083
71545
|
try {
|
|
71084
|
-
parsed = await
|
|
71546
|
+
parsed = await openPickerWithRetry(io);
|
|
71085
71547
|
} finally {
|
|
71086
71548
|
dismissed = await dismissOrWarn(io, "discover");
|
|
71087
71549
|
}
|
|
@@ -71106,7 +71568,7 @@ async function selectModel(agentName3, targetLabel, opts = {}) {
|
|
|
71106
71568
|
io.startedAt = Date.now();
|
|
71107
71569
|
let selected = false;
|
|
71108
71570
|
try {
|
|
71109
|
-
const parsed = await
|
|
71571
|
+
const parsed = await openPickerWithRetry(io);
|
|
71110
71572
|
if (!parsed || !parsed.footerSeen) {
|
|
71111
71573
|
return { ok: false, reason: "picker did not render \u2014 agent may be mid-turn" };
|
|
71112
71574
|
}
|
|
@@ -71163,7 +71625,7 @@ function extractConfirmation(pane) {
|
|
|
71163
71625
|
}
|
|
71164
71626
|
|
|
71165
71627
|
// ../src/agents/scaffold.ts
|
|
71166
|
-
import { join as
|
|
71628
|
+
import { join as join33, resolve as resolve6 } from "node:path";
|
|
71167
71629
|
init_atomic();
|
|
71168
71630
|
|
|
71169
71631
|
// ../src/agents/agent-uid.ts
|
|
@@ -71187,8 +71649,8 @@ var CONTAINER_DEFAULT_UTC_ZONES = new Set([
|
|
|
71187
71649
|
]);
|
|
71188
71650
|
|
|
71189
71651
|
// ../src/cli/agent-config.ts
|
|
71190
|
-
import { join as
|
|
71191
|
-
import { homedir as
|
|
71652
|
+
import { join as join31 } from "node:path";
|
|
71653
|
+
import { homedir as homedir10 } from "node:os";
|
|
71192
71654
|
|
|
71193
71655
|
// ../src/cli/helpers.ts
|
|
71194
71656
|
init_loader();
|
|
@@ -71208,12 +71670,12 @@ var WEBKITE_VAULT_KEYS = new Set([
|
|
|
71208
71670
|
init_overlay_loader();
|
|
71209
71671
|
|
|
71210
71672
|
// ../src/cli/agent-config.ts
|
|
71211
|
-
var AUDIT_ROOT =
|
|
71673
|
+
var AUDIT_ROOT = join31(homedir10(), ".switchroom", "audit");
|
|
71212
71674
|
|
|
71213
71675
|
// ../src/agents/profiles.ts
|
|
71214
71676
|
var import_handlebars = __toESM(require_lib(), 1);
|
|
71215
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";
|
|
71216
|
-
import { resolve as resolve5, join as
|
|
71678
|
+
import { resolve as resolve5, join as join32, sep as pathSep } from "node:path";
|
|
71217
71679
|
var PROFILES_ROOT = resolve5(import.meta.dirname, "../../profiles");
|
|
71218
71680
|
import_handlebars.default.registerHelper("json", (value) => {
|
|
71219
71681
|
return new import_handlebars.default.SafeString(JSON.stringify(value, null, 2));
|
|
@@ -71224,7 +71686,7 @@ import_handlebars.default.registerHelper("isNumber", (value) => {
|
|
|
71224
71686
|
var SHARED_FRAGMENTS_DIR = resolve5(PROFILES_ROOT, "_shared");
|
|
71225
71687
|
var SHARED_FRAGMENTS = ["vault-protocol", "agent-self-service", "execution-discipline", "reply-discipline", "dev-protocol"];
|
|
71226
71688
|
for (const name of SHARED_FRAGMENTS) {
|
|
71227
|
-
const fragPath =
|
|
71689
|
+
const fragPath = join32(SHARED_FRAGMENTS_DIR, `${name}.md.hbs`);
|
|
71228
71690
|
if (existsSync26(fragPath)) {
|
|
71229
71691
|
import_handlebars.default.registerPartial(name, readFileSync26(fragPath, "utf-8"));
|
|
71230
71692
|
}
|
|
@@ -71936,7 +72398,7 @@ init_overlay_loader();
|
|
|
71936
72398
|
init_merge();
|
|
71937
72399
|
var import_yaml4 = __toESM(require_dist(), 1);
|
|
71938
72400
|
import { readFileSync as readFileSync27, existsSync as existsSync27 } from "node:fs";
|
|
71939
|
-
import { homedir as
|
|
72401
|
+
import { homedir as homedir11 } from "node:os";
|
|
71940
72402
|
import { resolve as resolve7 } from "node:path";
|
|
71941
72403
|
|
|
71942
72404
|
class ConfigError2 extends Error {
|
|
@@ -71993,7 +72455,7 @@ function coerceLegacyGoogleWorkspaceKeys2(parsed, filePath) {
|
|
|
71993
72455
|
}
|
|
71994
72456
|
function findConfigFile2(startDir) {
|
|
71995
72457
|
const envPath = process.env.SWITCHROOM_CONFIG;
|
|
71996
|
-
const home2 =
|
|
72458
|
+
const home2 = homedir11();
|
|
71997
72459
|
const userDir = resolve7(home2, ".switchroom");
|
|
71998
72460
|
const searchPaths = [
|
|
71999
72461
|
envPath ? resolve7(envPath) : null,
|
|
@@ -72632,7 +73094,7 @@ function numField(obj, key) {
|
|
|
72632
73094
|
|
|
72633
73095
|
// gateway/context-occupancy.ts
|
|
72634
73096
|
import { mkdirSync as mkdirSync23, writeFileSync as writeFileSync23 } from "node:fs";
|
|
72635
|
-
import { join as
|
|
73097
|
+
import { join as join34 } from "node:path";
|
|
72636
73098
|
var CONTEXT_OCCUPANCY_FILENAME = "context-occupancy.json";
|
|
72637
73099
|
var TIGHT_FRACTION = 0.8;
|
|
72638
73100
|
function buildContextOccupancy(occupancy, cap, now) {
|
|
@@ -72655,7 +73117,7 @@ function buildContextOccupancy(occupancy, cap, now) {
|
|
|
72655
73117
|
}
|
|
72656
73118
|
function writeContextOccupancySnapshot(stateDir, snapshot, deps) {
|
|
72657
73119
|
try {
|
|
72658
|
-
const path2 =
|
|
73120
|
+
const path2 = join34(stateDir, CONTEXT_OCCUPANCY_FILENAME);
|
|
72659
73121
|
(deps?.mkdir ?? ((p, o) => mkdirSync23(p, o)))(stateDir, { recursive: true });
|
|
72660
73122
|
(deps?.writeFile ?? ((p, d) => writeFileSync23(p, d)))(path2, JSON.stringify(snapshot, null, 2) + `
|
|
72661
73123
|
`);
|
|
@@ -73150,12 +73612,12 @@ function startWebhookIngestServer(opts) {
|
|
|
73150
73612
|
|
|
73151
73613
|
// ../src/web/webhook-gateway-record.ts
|
|
73152
73614
|
import { appendFileSync as appendFileSync5, mkdirSync as mkdirSync26 } from "fs";
|
|
73153
|
-
import { join as
|
|
73154
|
-
import { homedir as
|
|
73615
|
+
import { join as join37 } from "path";
|
|
73616
|
+
import { homedir as homedir13 } from "os";
|
|
73155
73617
|
|
|
73156
73618
|
// ../src/web/webhook-handler.ts
|
|
73157
73619
|
import { appendFileSync as appendFileSync4, existsSync as existsSync31, mkdirSync as mkdirSync24, readFileSync as readFileSync29, writeFileSync as writeFileSync24 } from "fs";
|
|
73158
|
-
import { join as
|
|
73620
|
+
import { join as join35 } from "path";
|
|
73159
73621
|
var DEDUP_MAX = 1000;
|
|
73160
73622
|
var DEDUP_TTL_MS = 24 * 60 * 60 * 1000;
|
|
73161
73623
|
function loadDedupFile(path2) {
|
|
@@ -73184,8 +73646,8 @@ var agentDedupCache = new Map;
|
|
|
73184
73646
|
function createFileDedupStore(resolveAgentDir) {
|
|
73185
73647
|
return {
|
|
73186
73648
|
check(agent, deliveryId, now) {
|
|
73187
|
-
const telegramDir =
|
|
73188
|
-
const filePath =
|
|
73649
|
+
const telegramDir = join35(resolveAgentDir(agent), "telegram");
|
|
73650
|
+
const filePath = join35(telegramDir, "webhook-dedup.json");
|
|
73189
73651
|
if (!agentDedupCache.has(agent)) {
|
|
73190
73652
|
agentDedupCache.set(agent, loadDedupFile(filePath));
|
|
73191
73653
|
}
|
|
@@ -73207,8 +73669,8 @@ var throttleIssueWindow = new Map;
|
|
|
73207
73669
|
|
|
73208
73670
|
// ../src/web/webhook-dispatch.ts
|
|
73209
73671
|
import { existsSync as existsSync32, mkdirSync as mkdirSync25, readFileSync as readFileSync30, writeFileSync as writeFileSync25 } from "fs";
|
|
73210
|
-
import { join as
|
|
73211
|
-
import { homedir as
|
|
73672
|
+
import { join as join36 } from "path";
|
|
73673
|
+
import { homedir as homedir12 } from "os";
|
|
73212
73674
|
|
|
73213
73675
|
// ../src/agent-scheduler/ipc-client.ts
|
|
73214
73676
|
import { createConnection as createConnection2 } from "node:net";
|
|
@@ -73527,8 +73989,8 @@ function createFileCooldownStore(resolveAgentDir) {
|
|
|
73527
73989
|
isCoolingDown(agent, key, cooldownMs, now) {
|
|
73528
73990
|
if (cooldownMs <= 0)
|
|
73529
73991
|
return false;
|
|
73530
|
-
const telegramDir =
|
|
73531
|
-
const filePath =
|
|
73992
|
+
const telegramDir = join36(resolveAgentDir(agent), "telegram");
|
|
73993
|
+
const filePath = join36(telegramDir, "webhook-cooldown.json");
|
|
73532
73994
|
if (!cache.has(agent)) {
|
|
73533
73995
|
cache.set(agent, loadCooldownFile(filePath));
|
|
73534
73996
|
}
|
|
@@ -73585,9 +74047,9 @@ async function defaultInject(socketPath, agentName3, inbound) {
|
|
|
73585
74047
|
}
|
|
73586
74048
|
function injectWebhookInbound(agent, prompt, ctx, deps = {}) {
|
|
73587
74049
|
const log = deps.log ?? ((s) => process.stderr.write(s));
|
|
73588
|
-
const resolveAgentDir = deps.resolveAgentDir ?? ((a) =>
|
|
74050
|
+
const resolveAgentDir = deps.resolveAgentDir ?? ((a) => join36(homedir12(), ".switchroom", "agents", a));
|
|
73589
74051
|
const now = (deps.now ?? Date.now)();
|
|
73590
|
-
const socketPath =
|
|
74052
|
+
const socketPath = join36(resolveAgentDir(agent), "telegram", "gateway.sock");
|
|
73591
74053
|
const inbound = {
|
|
73592
74054
|
type: "inbound",
|
|
73593
74055
|
chatId: ctx.chatId,
|
|
@@ -73659,7 +74121,7 @@ function evaluateDispatch(args, deps = {}) {
|
|
|
73659
74121
|
const log = deps.log ?? ((s) => process.stderr.write(s));
|
|
73660
74122
|
const now = (deps.now ?? Date.now)();
|
|
73661
74123
|
const nowDate = deps.nowDate ?? (() => new Date(now));
|
|
73662
|
-
const resolveAgentDir = deps.resolveAgentDir ?? ((a) =>
|
|
74124
|
+
const resolveAgentDir = deps.resolveAgentDir ?? ((a) => join36(homedir12(), ".switchroom", "agents", a));
|
|
73663
74125
|
const cooldownStore = deps.cooldownStore ?? createFileCooldownStore(resolveAgentDir);
|
|
73664
74126
|
if (!DISPATCH_SOURCES.includes(args.source))
|
|
73665
74127
|
return 0;
|
|
@@ -73737,10 +74199,10 @@ var CONSOLIDATION_COMPLETED_EVENT = "consolidation.completed";
|
|
|
73737
74199
|
function recordWebhookEvent(rec, deps = {}) {
|
|
73738
74200
|
const log = deps.log ?? ((s) => process.stderr.write(s));
|
|
73739
74201
|
const now = rec.ts || (deps.now ?? Date.now)();
|
|
73740
|
-
const resolveAgentDir = deps.resolveAgentDir ?? ((a) =>
|
|
74202
|
+
const resolveAgentDir = deps.resolveAgentDir ?? ((a) => join37(homedir13(), ".switchroom", "agents", a));
|
|
73741
74203
|
const dedupStore = deps.dedupStore ?? createFileDedupStore(resolveAgentDir);
|
|
73742
74204
|
const agent = rec.agent;
|
|
73743
|
-
const telegramDir =
|
|
74205
|
+
const telegramDir = join37(resolveAgentDir(agent), "telegram");
|
|
73744
74206
|
if (rec.source === "github" && rec.delivery_id) {
|
|
73745
74207
|
const originalTs = dedupStore.check(agent, rec.delivery_id, now);
|
|
73746
74208
|
if (originalTs !== undefined) {
|
|
@@ -73749,7 +74211,7 @@ function recordWebhookEvent(rec, deps = {}) {
|
|
|
73749
74211
|
return { status: "deduped", ts: originalTs };
|
|
73750
74212
|
}
|
|
73751
74213
|
}
|
|
73752
|
-
const logPath =
|
|
74214
|
+
const logPath = join37(telegramDir, "webhook-events.jsonl");
|
|
73753
74215
|
try {
|
|
73754
74216
|
mkdirSync26(telegramDir, { recursive: true });
|
|
73755
74217
|
const record = {
|
|
@@ -77197,17 +77659,17 @@ import {
|
|
|
77197
77659
|
readFileSync as readFileSync31,
|
|
77198
77660
|
writeSync as writeSync5
|
|
77199
77661
|
} from "node:fs";
|
|
77200
|
-
import { join as
|
|
77662
|
+
import { join as join38 } from "node:path";
|
|
77201
77663
|
import { randomUUID as randomUUID5 } from "node:crypto";
|
|
77202
77664
|
var PROPOSALS_FILE2 = "skill-proposals.jsonl";
|
|
77203
77665
|
var REJECTED_FILE2 = "skill-proposals-rejected.jsonl";
|
|
77204
77666
|
var REJECTION_TTL_MS2 = 90 * 24 * 60 * 60 * 1000;
|
|
77205
77667
|
var PROPOSAL_SIM_THRESHOLD = 0.5;
|
|
77206
77668
|
function proposalsPath2(stateDir) {
|
|
77207
|
-
return
|
|
77669
|
+
return join38(stateDir, PROPOSALS_FILE2);
|
|
77208
77670
|
}
|
|
77209
77671
|
function rejectedPath2(stateDir) {
|
|
77210
|
-
return
|
|
77672
|
+
return join38(stateDir, REJECTED_FILE2);
|
|
77211
77673
|
}
|
|
77212
77674
|
function ensureDir3(stateDir) {
|
|
77213
77675
|
if (!existsSync33(stateDir)) {
|
|
@@ -78702,17 +79164,17 @@ import {
|
|
|
78702
79164
|
readdirSync as readdirSync6,
|
|
78703
79165
|
readFileSync as readFileSync37
|
|
78704
79166
|
} from "fs";
|
|
78705
|
-
import { join as
|
|
79167
|
+
import { join as join40 } from "path";
|
|
78706
79168
|
|
|
78707
79169
|
// session-tail.ts
|
|
78708
|
-
function
|
|
79170
|
+
function sanitizeCwdToProjectName2(cwd) {
|
|
78709
79171
|
return cwd.replace(/[^a-zA-Z0-9]/g, "-");
|
|
78710
79172
|
}
|
|
78711
|
-
var
|
|
78712
|
-
var
|
|
78713
|
-
function
|
|
79173
|
+
var MAX_JSONL_LINE_BYTES2 = 2 * 1024 * 1024;
|
|
79174
|
+
var MAX_ERROR_TEXT_CHARS2 = 500;
|
|
79175
|
+
function extractToolResultErrorText2(content3) {
|
|
78714
79176
|
if (typeof content3 === "string") {
|
|
78715
|
-
return content3.slice(0,
|
|
79177
|
+
return content3.slice(0, MAX_ERROR_TEXT_CHARS2);
|
|
78716
79178
|
}
|
|
78717
79179
|
if (Array.isArray(content3)) {
|
|
78718
79180
|
const parts = [];
|
|
@@ -78725,11 +79187,11 @@ function extractToolResultErrorText(content3) {
|
|
|
78725
79187
|
}
|
|
78726
79188
|
}
|
|
78727
79189
|
return parts.join(`
|
|
78728
|
-
`).slice(0,
|
|
79190
|
+
`).slice(0, MAX_ERROR_TEXT_CHARS2);
|
|
78729
79191
|
}
|
|
78730
79192
|
return "";
|
|
78731
79193
|
}
|
|
78732
|
-
function
|
|
79194
|
+
function projectAssistantTextBlocks2(content3, make) {
|
|
78733
79195
|
const out = new Map;
|
|
78734
79196
|
let lastToolUseIdx = -1;
|
|
78735
79197
|
content3.forEach((c, i) => {
|
|
@@ -78746,6 +79208,13 @@ function projectAssistantTextBlocks(content3, make) {
|
|
|
78746
79208
|
});
|
|
78747
79209
|
return out;
|
|
78748
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
|
+
}
|
|
78749
79218
|
function assistantLineCarriesAnswerSurface(content3) {
|
|
78750
79219
|
if (!Array.isArray(content3))
|
|
78751
79220
|
return false;
|
|
@@ -78805,7 +79274,7 @@ function projectSubagentLine(line, agentId, state4) {
|
|
|
78805
79274
|
agentId,
|
|
78806
79275
|
toolUseId: cc.tool_use_id ?? "",
|
|
78807
79276
|
isError: isError2,
|
|
78808
|
-
errorText: isError2 ?
|
|
79277
|
+
errorText: isError2 ? extractToolResultErrorText2(cc.content) : undefined
|
|
78809
79278
|
});
|
|
78810
79279
|
}
|
|
78811
79280
|
}
|
|
@@ -78821,7 +79290,17 @@ function projectSubagentLine(line, agentId, state4) {
|
|
|
78821
79290
|
if (typeof subModel === "string" && !isModelSentinel(subModel)) {
|
|
78822
79291
|
events.push({ kind: "sub_agent_model", agentId, model: subModel });
|
|
78823
79292
|
}
|
|
78824
|
-
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) => ({
|
|
78825
79304
|
kind: "sub_agent_text",
|
|
78826
79305
|
agentId,
|
|
78827
79306
|
text: text4,
|
|
@@ -78945,7 +79424,7 @@ function sanitiseToolArg(name, raw) {
|
|
|
78945
79424
|
case "NotebookEdit": {
|
|
78946
79425
|
const fp = raw.file_path;
|
|
78947
79426
|
if (typeof fp === "string" && fp.length > 0)
|
|
78948
|
-
out =
|
|
79427
|
+
out = basename6(fp);
|
|
78949
79428
|
break;
|
|
78950
79429
|
}
|
|
78951
79430
|
case "Bash": {
|
|
@@ -78981,7 +79460,7 @@ function sanitiseToolArg(name, raw) {
|
|
|
78981
79460
|
out = out.slice(0, SANITISE_MAX_LEN - 1) + "\u2026";
|
|
78982
79461
|
return out;
|
|
78983
79462
|
}
|
|
78984
|
-
function
|
|
79463
|
+
function basename6(p) {
|
|
78985
79464
|
const idx = p.lastIndexOf("/");
|
|
78986
79465
|
return idx === -1 ? p : p.slice(idx + 1);
|
|
78987
79466
|
}
|
|
@@ -79185,10 +79664,10 @@ import {
|
|
|
79185
79664
|
utimesSync,
|
|
79186
79665
|
writeFileSync as writeFileSync29
|
|
79187
79666
|
} from "node:fs";
|
|
79188
|
-
import { join as
|
|
79667
|
+
import { join as join39 } from "node:path";
|
|
79189
79668
|
var TURN_ACTIVE_MARKER_FILE = "turn-active.json";
|
|
79190
79669
|
function touchTurnActiveMarker(stateDir) {
|
|
79191
|
-
const path2 =
|
|
79670
|
+
const path2 = join39(stateDir, TURN_ACTIVE_MARKER_FILE);
|
|
79192
79671
|
if (!existsSync34(path2))
|
|
79193
79672
|
return;
|
|
79194
79673
|
const now = new Date;
|
|
@@ -79355,6 +79834,7 @@ function readSubTail(entry, tail, now, onDescriptionUpdate, fs2, log, db2, paren
|
|
|
79355
79834
|
},
|
|
79356
79835
|
lastTool: entry.lastTool,
|
|
79357
79836
|
toolCount: entry.toolCount,
|
|
79837
|
+
totalTokens: entry.totalTokens,
|
|
79358
79838
|
model: entry.currentModel,
|
|
79359
79839
|
skeleton: true
|
|
79360
79840
|
});
|
|
@@ -79436,6 +79916,7 @@ function readSubTail(entry, tail, now, onDescriptionUpdate, fs2, log, db2, paren
|
|
|
79436
79916
|
},
|
|
79437
79917
|
lastTool: entry.lastTool,
|
|
79438
79918
|
toolCount: entry.toolCount,
|
|
79919
|
+
totalTokens: entry.totalTokens,
|
|
79439
79920
|
model: entry.currentModel
|
|
79440
79921
|
});
|
|
79441
79922
|
return true;
|
|
@@ -79546,6 +80027,15 @@ function readSubTail(entry, tail, now, onDescriptionUpdate, fs2, log, db2, paren
|
|
|
79546
80027
|
}
|
|
79547
80028
|
continue;
|
|
79548
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
|
+
}
|
|
79549
80039
|
if (ev.kind === "sub_agent_tool_use") {
|
|
79550
80040
|
const narrativeJustFired = resolvePendingSubNarrative(ev.toolName, ev.input);
|
|
79551
80041
|
if (REPLY_TOOLS2.has(ev.toolName) && typeof ev.input?.text === "string") {
|
|
@@ -79574,6 +80064,7 @@ function readSubTail(entry, tail, now, onDescriptionUpdate, fs2, log, db2, paren
|
|
|
79574
80064
|
},
|
|
79575
80065
|
lastTool: entry.lastTool,
|
|
79576
80066
|
toolCount: entry.toolCount,
|
|
80067
|
+
totalTokens: entry.totalTokens,
|
|
79577
80068
|
progressLine: toolLine,
|
|
79578
80069
|
model: entry.currentModel
|
|
79579
80070
|
});
|
|
@@ -79648,7 +80139,7 @@ function readSubTail(entry, tail, now, onDescriptionUpdate, fs2, log, db2, paren
|
|
|
79648
80139
|
}
|
|
79649
80140
|
function startSubagentWatcher(config) {
|
|
79650
80141
|
const agentDir = config.agentDir;
|
|
79651
|
-
const expectedProjectSlug = config.agentCwd != null ?
|
|
80142
|
+
const expectedProjectSlug = config.agentCwd != null ? sanitizeCwdToProjectName2(config.agentCwd) : null;
|
|
79652
80143
|
const extraWatchCwdsProvider = config.extraWatchCwdsProvider ?? null;
|
|
79653
80144
|
const warnedForeignSlugs = new Set;
|
|
79654
80145
|
const stallThresholdMs = config.stallThresholdMs ?? parseEnvMs("SWITCHROOM_SUBAGENT_STALL_MS") ?? DEFAULT_STALL_THRESHOLD_MS;
|
|
@@ -79713,6 +80204,8 @@ function startSubagentWatcher(config) {
|
|
|
79713
80204
|
dispatchedAt: n,
|
|
79714
80205
|
lastActivityAt: n,
|
|
79715
80206
|
toolCount: 0,
|
|
80207
|
+
totalTokens: 0,
|
|
80208
|
+
seenUsageMessageIds: new Set,
|
|
79716
80209
|
stallNotified: false,
|
|
79717
80210
|
stalledAt: null,
|
|
79718
80211
|
completionNotified: false,
|
|
@@ -79849,6 +80342,7 @@ function startSubagentWatcher(config) {
|
|
|
79849
80342
|
state: entry.state,
|
|
79850
80343
|
outcome: entry.errored ? "failed" : entry.historical ? "orphan" : "completed",
|
|
79851
80344
|
toolCount: entry.toolCount,
|
|
80345
|
+
totalTokens: entry.totalTokens,
|
|
79852
80346
|
durationMs: nowFn() - entry.dispatchedAt,
|
|
79853
80347
|
description: entry.description,
|
|
79854
80348
|
resultText: entry.errored ? entry.lastResultText || entry.errorDetail || "" : entry.lastResultText,
|
|
@@ -79869,6 +80363,7 @@ function startSubagentWatcher(config) {
|
|
|
79869
80363
|
state: entry.state,
|
|
79870
80364
|
outcome: "failed",
|
|
79871
80365
|
toolCount: entry.toolCount,
|
|
80366
|
+
totalTokens: entry.totalTokens,
|
|
79872
80367
|
durationMs: nowFn() - entry.dispatchedAt,
|
|
79873
80368
|
description: entry.description,
|
|
79874
80369
|
resultText: entry.lastResultText,
|
|
@@ -80125,8 +80620,8 @@ function startSubagentWatcher(config) {
|
|
|
80125
80620
|
if (stopped)
|
|
80126
80621
|
return;
|
|
80127
80622
|
pruneVanishedDirWatchers();
|
|
80128
|
-
const claudeHome =
|
|
80129
|
-
const projectsRoot =
|
|
80623
|
+
const claudeHome = join40(agentDir, ".claude");
|
|
80624
|
+
const projectsRoot = join40(claudeHome, "projects");
|
|
80130
80625
|
if (!fs2.existsSync(projectsRoot))
|
|
80131
80626
|
return;
|
|
80132
80627
|
let projectDirs;
|
|
@@ -80142,7 +80637,7 @@ function startSubagentWatcher(config) {
|
|
|
80142
80637
|
if (extraWatchCwdsProvider != null) {
|
|
80143
80638
|
try {
|
|
80144
80639
|
for (const cwd of extraWatchCwdsProvider()) {
|
|
80145
|
-
allowedSlugs.add(
|
|
80640
|
+
allowedSlugs.add(sanitizeCwdToProjectName2(cwd));
|
|
80146
80641
|
}
|
|
80147
80642
|
} catch (err) {
|
|
80148
80643
|
providerOk = false;
|
|
@@ -80160,7 +80655,7 @@ function startSubagentWatcher(config) {
|
|
|
80160
80655
|
continue;
|
|
80161
80656
|
}
|
|
80162
80657
|
warnedForeignSlugs.delete(pDir);
|
|
80163
|
-
const projectPath =
|
|
80658
|
+
const projectPath = join40(projectsRoot, pDir);
|
|
80164
80659
|
let sessionDirs;
|
|
80165
80660
|
try {
|
|
80166
80661
|
sessionDirs = fs2.readdirSync(projectPath);
|
|
@@ -80170,7 +80665,7 @@ function startSubagentWatcher(config) {
|
|
|
80170
80665
|
for (const sDir of sessionDirs) {
|
|
80171
80666
|
if (sDir.endsWith(".jsonl"))
|
|
80172
80667
|
continue;
|
|
80173
|
-
const subagentsPath =
|
|
80668
|
+
const subagentsPath = join40(projectPath, sDir, "subagents");
|
|
80174
80669
|
if (!fs2.existsSync(subagentsPath))
|
|
80175
80670
|
continue;
|
|
80176
80671
|
const watchAndScan = (dirPath) => {
|
|
@@ -80179,7 +80674,7 @@ function startSubagentWatcher(config) {
|
|
|
80179
80674
|
const w = fs2.watch(dirPath, (_event, filename) => {
|
|
80180
80675
|
if (!filename || !filename.toString().startsWith("agent-") || !filename.toString().endsWith(".jsonl"))
|
|
80181
80676
|
return;
|
|
80182
|
-
const filePath =
|
|
80677
|
+
const filePath = join40(dirPath, filename.toString());
|
|
80183
80678
|
if (!knownFiles.has(filePath)) {
|
|
80184
80679
|
scanSubagentsDir(dirPath);
|
|
80185
80680
|
}
|
|
@@ -80193,7 +80688,7 @@ function startSubagentWatcher(config) {
|
|
|
80193
80688
|
scanSubagentsDir(dirPath);
|
|
80194
80689
|
};
|
|
80195
80690
|
watchAndScan(subagentsPath);
|
|
80196
|
-
const workflowsPath =
|
|
80691
|
+
const workflowsPath = join40(subagentsPath, "workflows");
|
|
80197
80692
|
if (fs2.existsSync(workflowsPath)) {
|
|
80198
80693
|
let wfDirs;
|
|
80199
80694
|
try {
|
|
@@ -80203,7 +80698,7 @@ function startSubagentWatcher(config) {
|
|
|
80203
80698
|
}
|
|
80204
80699
|
for (const wfDir of wfDirs) {
|
|
80205
80700
|
try {
|
|
80206
|
-
const wfPath =
|
|
80701
|
+
const wfPath = join40(workflowsPath, wfDir);
|
|
80207
80702
|
if (!fs2.statSync(wfPath).isDirectory())
|
|
80208
80703
|
continue;
|
|
80209
80704
|
watchAndScan(wfPath);
|
|
@@ -80223,7 +80718,7 @@ function startSubagentWatcher(config) {
|
|
|
80223
80718
|
for (const e of entries) {
|
|
80224
80719
|
if (!e.startsWith("agent-") || !e.endsWith(".jsonl"))
|
|
80225
80720
|
continue;
|
|
80226
|
-
const filePath =
|
|
80721
|
+
const filePath = join40(subagentsPath, e);
|
|
80227
80722
|
if (knownFiles.has(filePath))
|
|
80228
80723
|
continue;
|
|
80229
80724
|
const agentId = e.slice("agent-".length, -".jsonl".length);
|
|
@@ -80335,13 +80830,13 @@ import {
|
|
|
80335
80830
|
existsSync as existsSync36,
|
|
80336
80831
|
renameSync as renameSync15
|
|
80337
80832
|
} from "node:fs";
|
|
80338
|
-
import { join as
|
|
80339
|
-
import { homedir as
|
|
80833
|
+
import { join as join41, resolve as resolve8 } from "node:path";
|
|
80834
|
+
import { homedir as homedir14 } from "node:os";
|
|
80340
80835
|
function registryDir() {
|
|
80341
|
-
return resolve8(process.env.SWITCHROOM_WORKTREE_DIR ??
|
|
80836
|
+
return resolve8(process.env.SWITCHROOM_WORKTREE_DIR ?? join41(homedir14(), ".switchroom", "worktrees"));
|
|
80342
80837
|
}
|
|
80343
80838
|
function recordPath(id) {
|
|
80344
|
-
return
|
|
80839
|
+
return join41(registryDir(), `${id}.json`);
|
|
80345
80840
|
}
|
|
80346
80841
|
function ensureDir4() {
|
|
80347
80842
|
mkdirSync29(registryDir(), { recursive: true });
|
|
@@ -80392,12 +80887,12 @@ function recordExists(id) {
|
|
|
80392
80887
|
|
|
80393
80888
|
// worktree-watch-cwds.ts
|
|
80394
80889
|
import { realpathSync as realpathSync3 } from "node:fs";
|
|
80395
|
-
import { basename as
|
|
80890
|
+
import { basename as basename8 } from "node:path";
|
|
80396
80891
|
var identityEscalated = false;
|
|
80397
80892
|
function defaultDeriveName(agentDir) {
|
|
80398
80893
|
if (!agentDir || agentDir.trim().length === 0)
|
|
80399
80894
|
return "";
|
|
80400
|
-
const leaf =
|
|
80895
|
+
const leaf = basename8(agentDir).trim();
|
|
80401
80896
|
return leaf;
|
|
80402
80897
|
}
|
|
80403
80898
|
function resolveOwnerIdentity(self, agentDir, deriveName) {
|
|
@@ -80520,14 +81015,14 @@ init_boot_card();
|
|
|
80520
81015
|
|
|
80521
81016
|
// gateway/update-announce.ts
|
|
80522
81017
|
import { existsSync as existsSync41, mkdirSync as mkdirSync33, openSync as openSync9, closeSync as closeSync9, readFileSync as readFileSync44 } from "node:fs";
|
|
80523
|
-
import { join as
|
|
80524
|
-
import { homedir as
|
|
81018
|
+
import { join as join46 } from "node:path";
|
|
81019
|
+
import { homedir as homedir16 } from "node:os";
|
|
80525
81020
|
|
|
80526
81021
|
// ../src/host-control/audit-reader.ts
|
|
80527
|
-
import { homedir as
|
|
80528
|
-
import { join as
|
|
80529
|
-
function defaultAuditLogPath(home2 =
|
|
80530
|
-
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");
|
|
80531
81026
|
}
|
|
80532
81027
|
function parseAuditLine(line) {
|
|
80533
81028
|
const trimmed = line.trim();
|
|
@@ -80712,15 +81207,15 @@ function renderUpdateOutcomeLine(entry) {
|
|
|
80712
81207
|
`);
|
|
80713
81208
|
}
|
|
80714
81209
|
function claimUpdateAnnouncement(requestId, opts = {}) {
|
|
80715
|
-
const stateDir = opts.stateDir ?? process.env.TELEGRAM_STATE_DIR ??
|
|
80716
|
-
const dir =
|
|
81210
|
+
const stateDir = opts.stateDir ?? process.env.TELEGRAM_STATE_DIR ?? join46(homedir16(), ".switchroom");
|
|
81211
|
+
const dir = join46(stateDir, "update-announced");
|
|
80717
81212
|
try {
|
|
80718
81213
|
mkdirSync33(dir, { recursive: true });
|
|
80719
81214
|
} catch {
|
|
80720
81215
|
return false;
|
|
80721
81216
|
}
|
|
80722
81217
|
const safeId = requestId.replace(/[^A-Za-z0-9_.-]/g, "_").slice(0, 200);
|
|
80723
|
-
const path2 =
|
|
81218
|
+
const path2 = join46(dir, safeId);
|
|
80724
81219
|
try {
|
|
80725
81220
|
const fd = openSync9(path2, "wx");
|
|
80726
81221
|
closeSync9(fd);
|
|
@@ -80942,7 +81437,7 @@ function createIssuesCardHandle(opts) {
|
|
|
80942
81437
|
|
|
80943
81438
|
// issues-watcher.ts
|
|
80944
81439
|
import { existsSync as existsSync43, statSync as statSync12 } from "node:fs";
|
|
80945
|
-
import { join as
|
|
81440
|
+
import { join as join48 } from "node:path";
|
|
80946
81441
|
|
|
80947
81442
|
// ../src/issues/store.ts
|
|
80948
81443
|
import {
|
|
@@ -80958,7 +81453,7 @@ import {
|
|
|
80958
81453
|
writeFileSync as writeFileSync36,
|
|
80959
81454
|
writeSync as writeSync6
|
|
80960
81455
|
} from "node:fs";
|
|
80961
|
-
import { join as
|
|
81456
|
+
import { join as join47 } from "node:path";
|
|
80962
81457
|
import { randomBytes as randomBytes7 } from "node:crypto";
|
|
80963
81458
|
import { execSync } from "node:child_process";
|
|
80964
81459
|
|
|
@@ -80977,7 +81472,7 @@ init_redact();
|
|
|
80977
81472
|
var ISSUES_FILE = "issues.jsonl";
|
|
80978
81473
|
var ISSUES_LOCK = "issues.lock";
|
|
80979
81474
|
function readAll(stateDir) {
|
|
80980
|
-
const path2 =
|
|
81475
|
+
const path2 = join47(stateDir, ISSUES_FILE);
|
|
80981
81476
|
if (!existsSync42(path2))
|
|
80982
81477
|
return [];
|
|
80983
81478
|
let raw;
|
|
@@ -81014,7 +81509,7 @@ function list2(stateDir, opts = {}) {
|
|
|
81014
81509
|
});
|
|
81015
81510
|
}
|
|
81016
81511
|
function resolve9(stateDir, fingerprint, nowFn = Date.now) {
|
|
81017
|
-
if (!existsSync42(
|
|
81512
|
+
if (!existsSync42(join47(stateDir, ISSUES_FILE)))
|
|
81018
81513
|
return 0;
|
|
81019
81514
|
return withLock(stateDir, () => {
|
|
81020
81515
|
const all2 = readAll(stateDir);
|
|
@@ -81032,7 +81527,7 @@ function resolve9(stateDir, fingerprint, nowFn = Date.now) {
|
|
|
81032
81527
|
});
|
|
81033
81528
|
}
|
|
81034
81529
|
function writeAll(stateDir, events) {
|
|
81035
|
-
const path2 =
|
|
81530
|
+
const path2 = join47(stateDir, ISSUES_FILE);
|
|
81036
81531
|
sweepOrphanTmpFiles(stateDir);
|
|
81037
81532
|
const tmp = `${path2}.tmp-${process.pid}-${randomBytes7(4).toString("hex")}`;
|
|
81038
81533
|
const body = events.length === 0 ? "" : events.map((e) => JSON.stringify(e)).join(`
|
|
@@ -81054,7 +81549,7 @@ function sweepOrphanTmpFiles(stateDir) {
|
|
|
81054
81549
|
for (const entry of entries) {
|
|
81055
81550
|
if (!entry.startsWith(TMP_PREFIX))
|
|
81056
81551
|
continue;
|
|
81057
|
-
const tmpPath2 =
|
|
81552
|
+
const tmpPath2 = join47(stateDir, entry);
|
|
81058
81553
|
try {
|
|
81059
81554
|
const stat = statSync11(tmpPath2);
|
|
81060
81555
|
if (stat.mtimeMs < cutoff) {
|
|
@@ -81066,7 +81561,7 @@ function sweepOrphanTmpFiles(stateDir) {
|
|
|
81066
81561
|
var LOCK_RETRY_MS = 25;
|
|
81067
81562
|
var LOCK_TIMEOUT_MS = 1e4;
|
|
81068
81563
|
function withLock(stateDir, fn) {
|
|
81069
|
-
const lockPath =
|
|
81564
|
+
const lockPath = join47(stateDir, ISSUES_LOCK);
|
|
81070
81565
|
const startedAt = Date.now();
|
|
81071
81566
|
let fd = null;
|
|
81072
81567
|
while (fd === null) {
|
|
@@ -81151,7 +81646,7 @@ function isIssueEvent(v) {
|
|
|
81151
81646
|
// issues-watcher.ts
|
|
81152
81647
|
var DEFAULT_POLL_INTERVAL_MS2 = 2000;
|
|
81153
81648
|
function startIssuesWatcher(opts) {
|
|
81154
|
-
const path2 =
|
|
81649
|
+
const path2 = join48(opts.stateDir, ISSUES_FILE);
|
|
81155
81650
|
const log = opts.log ?? (() => {});
|
|
81156
81651
|
const intervalMs = opts.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS2;
|
|
81157
81652
|
const setIntervalFn = opts.setInterval ?? setInterval;
|
|
@@ -81213,10 +81708,10 @@ function defaultReadEvents(stateDir) {
|
|
|
81213
81708
|
}
|
|
81214
81709
|
// permission-title.ts
|
|
81215
81710
|
init_card_format();
|
|
81216
|
-
import { basename as
|
|
81711
|
+
import { basename as basename10 } from "node:path";
|
|
81217
81712
|
|
|
81218
81713
|
// permission-rule.ts
|
|
81219
|
-
import { basename as
|
|
81714
|
+
import { basename as basename9 } from "node:path";
|
|
81220
81715
|
var FILE_TOOLS = new Set([
|
|
81221
81716
|
"Edit",
|
|
81222
81717
|
"Write",
|
|
@@ -81294,7 +81789,7 @@ function skillBasenameFromPath(input) {
|
|
|
81294
81789
|
if (!path2)
|
|
81295
81790
|
return null;
|
|
81296
81791
|
const trimmed = path2.replace(/\/SKILL\.md$/i, "").replace(/\/$/, "");
|
|
81297
|
-
return
|
|
81792
|
+
return basename9(trimmed) || null;
|
|
81298
81793
|
}
|
|
81299
81794
|
function matchesAllowRule(rule, toolName, inputPreview) {
|
|
81300
81795
|
if (!rule || !toolName)
|
|
@@ -81586,11 +82081,11 @@ function describeGrant(toolName, inputPreview, option) {
|
|
|
81586
82081
|
return m ? `run ${m[1]} commands` : "run that command";
|
|
81587
82082
|
}
|
|
81588
82083
|
if (t === "Edit" || t === "MultiEdit" || t === "NotebookEdit")
|
|
81589
|
-
return `edit ${
|
|
82084
|
+
return `edit ${basename10(arg)}`;
|
|
81590
82085
|
if (t === "Write")
|
|
81591
|
-
return `write ${
|
|
82086
|
+
return `write ${basename10(arg)}`;
|
|
81592
82087
|
if (t === "Read")
|
|
81593
|
-
return `read ${
|
|
82088
|
+
return `read ${basename10(arg)}`;
|
|
81594
82089
|
return naturalAction(toolName, inputPreview);
|
|
81595
82090
|
}
|
|
81596
82091
|
switch (rule) {
|
|
@@ -81629,12 +82124,12 @@ function fileBase(input, rawPreview) {
|
|
|
81629
82124
|
if (input) {
|
|
81630
82125
|
const p = readString2(input, "file_path") ?? readString2(input, "notebook_path");
|
|
81631
82126
|
if (p)
|
|
81632
|
-
return
|
|
82127
|
+
return basename10(p);
|
|
81633
82128
|
}
|
|
81634
82129
|
if (rawPreview) {
|
|
81635
82130
|
const p = extractFilePathFromRaw2(rawPreview);
|
|
81636
82131
|
if (p)
|
|
81637
|
-
return
|
|
82132
|
+
return basename10(p);
|
|
81638
82133
|
}
|
|
81639
82134
|
return null;
|
|
81640
82135
|
}
|
|
@@ -81765,7 +82260,7 @@ function truncate6(text4, max) {
|
|
|
81765
82260
|
}
|
|
81766
82261
|
|
|
81767
82262
|
// permission-rule.ts
|
|
81768
|
-
import { basename as
|
|
82263
|
+
import { basename as basename11 } from "node:path";
|
|
81769
82264
|
var FILE_TOOLS2 = new Set([
|
|
81770
82265
|
"Edit",
|
|
81771
82266
|
"Write",
|
|
@@ -81897,14 +82392,14 @@ function skillBasenameFromPath3(input) {
|
|
|
81897
82392
|
if (!path2)
|
|
81898
82393
|
return null;
|
|
81899
82394
|
const trimmed = path2.replace(/\/SKILL\.md$/i, "").replace(/\/$/, "");
|
|
81900
|
-
return
|
|
82395
|
+
return basename11(trimmed) || null;
|
|
81901
82396
|
}
|
|
81902
82397
|
function isRulePersisted(resolvedAllow, ruleRule) {
|
|
81903
82398
|
return resolvedAllow.includes(ruleRule);
|
|
81904
82399
|
}
|
|
81905
82400
|
|
|
81906
82401
|
// scoped-approval.ts
|
|
81907
|
-
import { basename as
|
|
82402
|
+
import { basename as basename12 } from "node:path";
|
|
81908
82403
|
var SCOPED_APPROVAL_DEFAULT_TTL_MS = 30 * 60 * 1000;
|
|
81909
82404
|
function scopedApprovalTtlMs(env = process.env) {
|
|
81910
82405
|
const raw = env.SWITCHROOM_SCOPED_APPROVAL_TTL_MS;
|
|
@@ -81929,7 +82424,7 @@ function resolveTimeBox(toolName, inputPreview, choices) {
|
|
|
81929
82424
|
const fileMatch = FILE_RULE.exec(rule);
|
|
81930
82425
|
if (fileMatch) {
|
|
81931
82426
|
const verb = fileMatch[1] === "Read" ? "reads of" : "edits to";
|
|
81932
|
-
return { rule, breadth: `${verb} ${
|
|
82427
|
+
return { rule, breadth: `${verb} ${basename12(fileMatch[2])}` };
|
|
81933
82428
|
}
|
|
81934
82429
|
const bashMatch = BASH_FAMILY_RULE.exec(rule);
|
|
81935
82430
|
if (bashMatch) {
|
|
@@ -82030,7 +82525,7 @@ function readBashCommand(inputPreview) {
|
|
|
82030
82525
|
|
|
82031
82526
|
// gateway/scoped-grant-store.ts
|
|
82032
82527
|
import { readFileSync as readFileSync47, writeFileSync as writeFileSync37 } from "node:fs";
|
|
82033
|
-
import { join as
|
|
82528
|
+
import { join as join49 } from "node:path";
|
|
82034
82529
|
|
|
82035
82530
|
// scoped-approval.ts
|
|
82036
82531
|
var SCOPED_APPROVAL_DEFAULT_TTL_MS2 = 30 * 60 * 1000;
|
|
@@ -82072,7 +82567,7 @@ function scopedGrantPersistEnabled(env = process.env) {
|
|
|
82072
82567
|
return env.SWITCHROOM_SCOPED_GRANT_PERSIST !== "0";
|
|
82073
82568
|
}
|
|
82074
82569
|
function createScopedGrantStore(stateDir, env = process.env) {
|
|
82075
|
-
const filePath =
|
|
82570
|
+
const filePath = join49(stateDir, "scoped-grants.json");
|
|
82076
82571
|
const enabled8 = scopedGrantPersistEnabled(env);
|
|
82077
82572
|
function read() {
|
|
82078
82573
|
try {
|
|
@@ -82446,7 +82941,7 @@ function isDiffPreApproved(agentName3, unifiedDiff, deps) {
|
|
|
82446
82941
|
// credits-watch.ts
|
|
82447
82942
|
init_card_format();
|
|
82448
82943
|
import { readFileSync as readFileSync48, writeFileSync as writeFileSync38, existsSync as existsSync44, mkdirSync as mkdirSync35 } from "fs";
|
|
82449
|
-
import { join as
|
|
82944
|
+
import { join as join50 } from "path";
|
|
82450
82945
|
var STATE_FILE = "credits-watch.json";
|
|
82451
82946
|
var DEFAULT_CREDIT_FATAL_REASONS = new Set;
|
|
82452
82947
|
var KNOWN_CREDIT_REASONS = [
|
|
@@ -82468,7 +82963,7 @@ function emptyCreditState() {
|
|
|
82468
82963
|
return { lastNotifiedReason: null, lastNotifiedAt: 0 };
|
|
82469
82964
|
}
|
|
82470
82965
|
function readClaudeJsonOverage(claudeConfigDir) {
|
|
82471
|
-
const path2 =
|
|
82966
|
+
const path2 = join50(claudeConfigDir, ".claude.json");
|
|
82472
82967
|
if (!existsSync44(path2))
|
|
82473
82968
|
return null;
|
|
82474
82969
|
let raw;
|
|
@@ -82551,7 +83046,7 @@ function humanizeReason(reason) {
|
|
|
82551
83046
|
}
|
|
82552
83047
|
}
|
|
82553
83048
|
function loadCreditState(stateDir) {
|
|
82554
|
-
const path2 =
|
|
83049
|
+
const path2 = join50(stateDir, STATE_FILE);
|
|
82555
83050
|
if (!existsSync44(path2))
|
|
82556
83051
|
return emptyCreditState();
|
|
82557
83052
|
try {
|
|
@@ -82568,7 +83063,7 @@ function loadCreditState(stateDir) {
|
|
|
82568
83063
|
}
|
|
82569
83064
|
function saveCreditState(stateDir, state4) {
|
|
82570
83065
|
mkdirSync35(stateDir, { recursive: true });
|
|
82571
|
-
const path2 =
|
|
83066
|
+
const path2 = join50(stateDir, STATE_FILE);
|
|
82572
83067
|
writeFileSync38(path2, JSON.stringify(state4, null, 2) + `
|
|
82573
83068
|
`, { mode: 384 });
|
|
82574
83069
|
}
|
|
@@ -82577,7 +83072,7 @@ function saveCreditState(stateDir, state4) {
|
|
|
82577
83072
|
init_auth_snapshot_format();
|
|
82578
83073
|
init_card_format();
|
|
82579
83074
|
import { readFileSync as readFileSync49, writeFileSync as writeFileSync39, existsSync as existsSync45, mkdirSync as mkdirSync36 } from "fs";
|
|
82580
|
-
import { join as
|
|
83075
|
+
import { join as join51 } from "path";
|
|
82581
83076
|
var STATE_FILE2 = "quota-watch.json";
|
|
82582
83077
|
function emptyQuotaWatchState() {
|
|
82583
83078
|
return {};
|
|
@@ -82815,7 +83310,7 @@ function buildRecoveryMessage(agentName3, snap) {
|
|
|
82815
83310
|
`);
|
|
82816
83311
|
}
|
|
82817
83312
|
function loadQuotaWatchState(stateDir) {
|
|
82818
|
-
const path2 =
|
|
83313
|
+
const path2 = join51(stateDir, STATE_FILE2);
|
|
82819
83314
|
if (!existsSync45(path2))
|
|
82820
83315
|
return emptyQuotaWatchState();
|
|
82821
83316
|
try {
|
|
@@ -82837,7 +83332,7 @@ function loadQuotaWatchState(stateDir) {
|
|
|
82837
83332
|
}
|
|
82838
83333
|
function saveQuotaWatchState(stateDir, state4) {
|
|
82839
83334
|
mkdirSync36(stateDir, { recursive: true });
|
|
82840
|
-
const path2 =
|
|
83335
|
+
const path2 = join51(stateDir, STATE_FILE2);
|
|
82841
83336
|
writeFileSync39(path2, JSON.stringify(state4, null, 2) + `
|
|
82842
83337
|
`, { mode: 384 });
|
|
82843
83338
|
}
|
|
@@ -82895,17 +83390,17 @@ import {
|
|
|
82895
83390
|
utimesSync as utimesSync2,
|
|
82896
83391
|
writeFileSync as writeFileSync40
|
|
82897
83392
|
} from "node:fs";
|
|
82898
|
-
import { join as
|
|
83393
|
+
import { join as join52 } from "node:path";
|
|
82899
83394
|
var TURN_ACTIVE_MARKER_FILE2 = "turn-active.json";
|
|
82900
83395
|
function writeTurnActiveMarker(stateDir, marker) {
|
|
82901
83396
|
try {
|
|
82902
83397
|
mkdirSync37(stateDir, { recursive: true });
|
|
82903
|
-
writeFileSync40(
|
|
83398
|
+
writeFileSync40(join52(stateDir, TURN_ACTIVE_MARKER_FILE2), JSON.stringify(marker, null, 2) + `
|
|
82904
83399
|
`, { mode: 384 });
|
|
82905
83400
|
} catch {}
|
|
82906
83401
|
}
|
|
82907
83402
|
function touchTurnActiveMarker2(stateDir) {
|
|
82908
|
-
const path2 =
|
|
83403
|
+
const path2 = join52(stateDir, TURN_ACTIVE_MARKER_FILE2);
|
|
82909
83404
|
if (!existsSync46(path2))
|
|
82910
83405
|
return;
|
|
82911
83406
|
const now = new Date;
|
|
@@ -82920,11 +83415,11 @@ function touchTurnActiveMarker2(stateDir) {
|
|
|
82920
83415
|
}
|
|
82921
83416
|
function removeTurnActiveMarker(stateDir) {
|
|
82922
83417
|
try {
|
|
82923
|
-
unlinkSync21(
|
|
83418
|
+
unlinkSync21(join52(stateDir, TURN_ACTIVE_MARKER_FILE2));
|
|
82924
83419
|
} catch {}
|
|
82925
83420
|
}
|
|
82926
83421
|
function sweepStaleTurnActiveMarker(stateDir, opts) {
|
|
82927
|
-
const path2 =
|
|
83422
|
+
const path2 = join52(stateDir, TURN_ACTIVE_MARKER_FILE2);
|
|
82928
83423
|
if (!existsSync46(path2))
|
|
82929
83424
|
return false;
|
|
82930
83425
|
const now = opts.now ?? Date.now();
|
|
@@ -82955,7 +83450,7 @@ function sweepStaleTurnActiveMarker(stateDir, opts) {
|
|
|
82955
83450
|
}
|
|
82956
83451
|
}
|
|
82957
83452
|
function readTurnActiveMarkerAgeMs(stateDir, now) {
|
|
82958
|
-
const path2 =
|
|
83453
|
+
const path2 = join52(stateDir, TURN_ACTIVE_MARKER_FILE2);
|
|
82959
83454
|
try {
|
|
82960
83455
|
const st = statSync13(path2);
|
|
82961
83456
|
return (now ?? Date.now()) - st.mtimeMs;
|
|
@@ -82965,10 +83460,10 @@ function readTurnActiveMarkerAgeMs(stateDir, now) {
|
|
|
82965
83460
|
}
|
|
82966
83461
|
|
|
82967
83462
|
// ../src/build-info.ts
|
|
82968
|
-
var VERSION = "0.18.
|
|
82969
|
-
var COMMIT_SHA = "
|
|
82970
|
-
var COMMIT_DATE = "2026-07-
|
|
82971
|
-
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;
|
|
82972
83467
|
var COMMITS_AHEAD_OF_TAG = 0;
|
|
82973
83468
|
|
|
82974
83469
|
// gateway/boot-version.ts
|
|
@@ -83045,12 +83540,12 @@ init_protocol();
|
|
|
83045
83540
|
init_peercred();
|
|
83046
83541
|
import * as net5 from "node:net";
|
|
83047
83542
|
import * as fs2 from "node:fs";
|
|
83048
|
-
import { homedir as
|
|
83049
|
-
import { join as
|
|
83543
|
+
import { homedir as homedir17 } from "node:os";
|
|
83544
|
+
import { join as join53 } from "node:path";
|
|
83050
83545
|
var DEFAULT_TIMEOUT_MS4 = 2000;
|
|
83051
83546
|
var UNLOCK_TIMEOUT_MS = 30000;
|
|
83052
|
-
var LEGACY_SOCKET_PATH2 =
|
|
83053
|
-
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");
|
|
83054
83549
|
function defaultBrokerSocketPath2() {
|
|
83055
83550
|
if (fs2.existsSync(OPERATOR_SOCKET_PATH2))
|
|
83056
83551
|
return OPERATOR_SOCKET_PATH2;
|
|
@@ -83933,7 +84428,7 @@ function resolveVaultApprovalPosture(broker) {
|
|
|
83933
84428
|
|
|
83934
84429
|
// registry/turns-schema.ts
|
|
83935
84430
|
import { chmodSync as chmodSync10, mkdirSync as mkdirSync38 } from "fs";
|
|
83936
|
-
import { join as
|
|
84431
|
+
import { join as join54 } from "path";
|
|
83937
84432
|
var DatabaseClass2 = null;
|
|
83938
84433
|
function loadDatabaseClass2() {
|
|
83939
84434
|
if (DatabaseClass2 != null)
|
|
@@ -83984,12 +84479,16 @@ var PHASE2_MIGRATIONS = [
|
|
|
83984
84479
|
var PHASE3_MIGRATIONS = [
|
|
83985
84480
|
`ALTER TABLE turns ADD COLUMN resumed_at INTEGER`
|
|
83986
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
|
+
];
|
|
83987
84486
|
function applySchema(db2) {
|
|
83988
84487
|
db2.exec("PRAGMA journal_mode = WAL");
|
|
83989
84488
|
db2.exec("PRAGMA synchronous = NORMAL");
|
|
83990
84489
|
db2.exec("PRAGMA busy_timeout = 5000");
|
|
83991
84490
|
db2.exec(SCHEMA_SQL);
|
|
83992
|
-
for (const sql of [...PHASE1_MIGRATIONS, ...PHASE2_MIGRATIONS, ...PHASE3_MIGRATIONS]) {
|
|
84491
|
+
for (const sql of [...PHASE1_MIGRATIONS, ...PHASE2_MIGRATIONS, ...PHASE3_MIGRATIONS, ...PHASE4_MIGRATIONS]) {
|
|
83993
84492
|
try {
|
|
83994
84493
|
db2.exec(sql);
|
|
83995
84494
|
} catch (err) {
|
|
@@ -84001,9 +84500,9 @@ function applySchema(db2) {
|
|
|
84001
84500
|
}
|
|
84002
84501
|
function openTurnsDb(agentDir) {
|
|
84003
84502
|
const Database = loadDatabaseClass2();
|
|
84004
|
-
const dir =
|
|
84503
|
+
const dir = join54(agentDir, "telegram");
|
|
84005
84504
|
mkdirSync38(dir, { recursive: true, mode: 448 });
|
|
84006
|
-
const path2 =
|
|
84505
|
+
const path2 = join54(dir, "registry.db");
|
|
84007
84506
|
const db2 = new Database(path2, { create: true });
|
|
84008
84507
|
applySchema(db2);
|
|
84009
84508
|
try {
|
|
@@ -84032,6 +84531,8 @@ function mapRow(row) {
|
|
|
84032
84531
|
tool_call_count: row.tool_call_count,
|
|
84033
84532
|
interrupt_reason: row.interrupt_reason,
|
|
84034
84533
|
resumed_at: row.resumed_at,
|
|
84534
|
+
session_id: row.session_id ?? null,
|
|
84535
|
+
answer_redelivered_at: row.answer_redelivered_at ?? null,
|
|
84035
84536
|
created_at: row.created_at,
|
|
84036
84537
|
updated_at: row.updated_at
|
|
84037
84538
|
};
|
|
@@ -84127,6 +84628,24 @@ function markTurnResumed(db2, turnKey2, now = Date.now()) {
|
|
|
84127
84628
|
WHERE turn_key = ? AND resumed_at IS NULL
|
|
84128
84629
|
`).run(now, now, turnKey2);
|
|
84129
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
|
+
}
|
|
84130
84649
|
function findLatestTurnIfInterrupted(db2) {
|
|
84131
84650
|
const row = db2.prepare(`
|
|
84132
84651
|
SELECT * FROM turns
|
|
@@ -84756,7 +85275,7 @@ installGlobalErrorHandlers();
|
|
|
84756
85275
|
process.on("beforeExit", () => {
|
|
84757
85276
|
shutdownAnalytics();
|
|
84758
85277
|
});
|
|
84759
|
-
var STATE_DIR = process.env.TELEGRAM_STATE_DIR ??
|
|
85278
|
+
var STATE_DIR = process.env.TELEGRAM_STATE_DIR ?? join55(homedir18(), ".claude", "channels", "telegram");
|
|
84760
85279
|
var permCardStore = createPermissionCardStore(STATE_DIR);
|
|
84761
85280
|
var BLOCKED_APPROVALS_DIR = process.env.SWITCHROOM_BLOCKED_APPROVALS_DIR ?? "/state/blocked-approvals";
|
|
84762
85281
|
var AGENT_NAME = process.env.SWITCHROOM_AGENT_NAME ?? "agent";
|
|
@@ -84856,11 +85375,11 @@ function scheduleAlwaysAllowPersistDrain() {
|
|
|
84856
85375
|
}, ALWAYS_ALLOW_DRAIN_INTERVAL_MS);
|
|
84857
85376
|
timer3.unref?.();
|
|
84858
85377
|
}
|
|
84859
|
-
var ACCESS_FILE =
|
|
84860
|
-
var APPROVED_DIR =
|
|
84861
|
-
var ENV_FILE =
|
|
84862
|
-
var INBOX_DIR =
|
|
84863
|
-
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");
|
|
84864
85383
|
function triggerSelfRestart(targetAgent, reason, delayMs = 300) {
|
|
84865
85384
|
const isDocker = process.env.SWITCHROOM_RUNTIME === "docker";
|
|
84866
85385
|
const selfAgent = process.env.SWITCHROOM_AGENT_NAME;
|
|
@@ -85057,7 +85576,7 @@ function assertSendable(f) {
|
|
|
85057
85576
|
} catch {
|
|
85058
85577
|
throw new Error(`refusing to send file \u2014 cannot resolve real path: ${f}`);
|
|
85059
85578
|
}
|
|
85060
|
-
const inbox =
|
|
85579
|
+
const inbox = join55(stateReal, "inbox");
|
|
85061
85580
|
if (real.startsWith(stateReal + sep3) && !real.startsWith(inbox + sep3)) {
|
|
85062
85581
|
throw new Error(`refusing to send channel state: ${f}`);
|
|
85063
85582
|
}
|
|
@@ -85182,7 +85701,7 @@ var HISTORY_ENABLED = HISTORY_ACCESS.historyEnabled !== false;
|
|
|
85182
85701
|
if (HISTORY_ENABLED) {
|
|
85183
85702
|
try {
|
|
85184
85703
|
initHistory(STATE_DIR, HISTORY_ACCESS.historyRetentionDays ?? 30);
|
|
85185
|
-
process.stderr.write(`telegram gateway: history capture enabled at ${
|
|
85704
|
+
process.stderr.write(`telegram gateway: history capture enabled at ${join55(STATE_DIR, "history.db")}
|
|
85186
85705
|
`);
|
|
85187
85706
|
} catch (err) {
|
|
85188
85707
|
process.stderr.write(`telegram gateway: history init failed (${err.message}) \u2014 capture disabled
|
|
@@ -85191,6 +85710,7 @@ if (HISTORY_ENABLED) {
|
|
|
85191
85710
|
}
|
|
85192
85711
|
var turnsDb = null;
|
|
85193
85712
|
var bootResumeInbound = null;
|
|
85713
|
+
var pendingRedelivery = null;
|
|
85194
85714
|
var bridgeDeadPriorStreak = 0;
|
|
85195
85715
|
try {
|
|
85196
85716
|
const agentDir = STATE_DIR.endsWith("/telegram") ? STATE_DIR.slice(0, -"/telegram".length) : STATE_DIR;
|
|
@@ -85199,7 +85719,7 @@ try {
|
|
|
85199
85719
|
let markerTurnKey = null;
|
|
85200
85720
|
let markerAgeMs = null;
|
|
85201
85721
|
try {
|
|
85202
|
-
const markerPath =
|
|
85722
|
+
const markerPath = join55(STATE_DIR, TURN_ACTIVE_MARKER_FILE2);
|
|
85203
85723
|
if (existsSync50(markerPath)) {
|
|
85204
85724
|
const st = statSync16(markerPath);
|
|
85205
85725
|
markerAgeMs = Date.now() - st.mtimeMs;
|
|
@@ -85224,10 +85744,10 @@ try {
|
|
|
85224
85744
|
process.stderr.write(`telegram gateway: turn-registry boot-reaper stamped ${reaped} orphaned turn(s)${timeoutTurnKey ? ` (turnKey=${timeoutTurnKey} as 'timeout', markerAgeMs=${markerAgeMs})` : " as 'restart'"}
|
|
85225
85745
|
`);
|
|
85226
85746
|
} else {
|
|
85227
|
-
process.stderr.write(`telegram gateway: turn-registry initialized at ${
|
|
85747
|
+
process.stderr.write(`telegram gateway: turn-registry initialized at ${join55(agentDir, "telegram", "registry.db")}
|
|
85228
85748
|
`);
|
|
85229
85749
|
}
|
|
85230
|
-
const bridgeDeadMarker = consumeBridgeDeadEscalationMarker(
|
|
85750
|
+
const bridgeDeadMarker = consumeBridgeDeadEscalationMarker(join55(STATE_DIR, "bridge-dead-escalation.json"));
|
|
85231
85751
|
if (bridgeDeadMarker != null) {
|
|
85232
85752
|
bridgeDeadPriorStreak = bridgeDeadMarker.count ?? 1;
|
|
85233
85753
|
process.stderr.write(`telegram gateway: boot: prior restart was a bridge-dead escalation (reason=${bridgeDeadMarker.reason}, consecutive=${bridgeDeadPriorStreak}${bridgeDeadMarker.crashTail ? `, crashTail=${bridgeDeadMarker.crashTail}` : ""})
|
|
@@ -85240,7 +85760,7 @@ try {
|
|
|
85240
85760
|
const pending2 = findLatestTurnIfInterrupted(turnsDb);
|
|
85241
85761
|
const selfAgent = process.env.SWITCHROOM_AGENT_NAME ?? "";
|
|
85242
85762
|
if (pending2 != null && selfAgent) {
|
|
85243
|
-
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");
|
|
85244
85764
|
const bootResumeCleanMarker = readCleanShutdownMarker(bootResumeMarkerPath);
|
|
85245
85765
|
const bootResumeForceAlways = process.env.SWITCHROOM_BOOT_RESUME_ALWAYS === "1";
|
|
85246
85766
|
const bootResumeMode = parseBootResumeMode(process.env.SWITCHROOM_BOOT_RESUME);
|
|
@@ -85258,6 +85778,19 @@ try {
|
|
|
85258
85778
|
ageMs: Math.max(0, Date.now() - pending2.started_at),
|
|
85259
85779
|
maxAgeMs: RESUME_MAX_AGE_MS
|
|
85260
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
|
+
}
|
|
85261
85794
|
let interruptedSubagents = [];
|
|
85262
85795
|
try {
|
|
85263
85796
|
interruptedSubagents = listNonTerminalSubagentsForTurn(turnsDb, pending2.turn_key).map((s) => ({ agentType: s.agent_type, description: s.description, status: s.status }));
|
|
@@ -85340,7 +85873,7 @@ try {
|
|
|
85340
85873
|
`);
|
|
85341
85874
|
}
|
|
85342
85875
|
}
|
|
85343
|
-
const pendingEnvPath =
|
|
85876
|
+
const pendingEnvPath = join55(agentDir, ".pending-turn.env");
|
|
85344
85877
|
try {
|
|
85345
85878
|
if (pending2 != null) {
|
|
85346
85879
|
const lines = [
|
|
@@ -85396,6 +85929,10 @@ function resolveSubagentOriginChat(agentId) {
|
|
|
85396
85929
|
var WORKER_FEED_FALLBACK_LOG_CAP = 256;
|
|
85397
85930
|
var WORKER_FEED_STALE_TTL_MARGIN_MS = 300000;
|
|
85398
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
|
+
})();
|
|
85399
85936
|
var workerFeedOwnerDmFallbackLogged = new Set;
|
|
85400
85937
|
function resolveWorkerFeedChat(agentId, fleetChatId) {
|
|
85401
85938
|
const origin = resolveSubagentOriginChat(agentId);
|
|
@@ -85458,7 +85995,7 @@ function checkApprovals() {
|
|
|
85458
85995
|
return;
|
|
85459
85996
|
}
|
|
85460
85997
|
for (const senderId of files) {
|
|
85461
|
-
const file =
|
|
85998
|
+
const file = join55(APPROVED_DIR, senderId);
|
|
85462
85999
|
bot.api.sendMessage(senderId, "Paired! Say hi to Claude.").then(() => rmSync6(file, { force: true }), (err) => {
|
|
85463
86000
|
process.stderr.write(`telegram gateway: failed to send approval confirm: ${err}
|
|
85464
86001
|
`);
|
|
@@ -85563,7 +86100,7 @@ function noteAgentOutputAt(key, ts) {
|
|
|
85563
86100
|
lastAgentOutputAt.delete(oldest);
|
|
85564
86101
|
}
|
|
85565
86102
|
}
|
|
85566
|
-
var OBLIGATION_STORE_PATH =
|
|
86103
|
+
var OBLIGATION_STORE_PATH = join55(STATE_DIR, "obligations.json");
|
|
85567
86104
|
var obligationStoreFs = {
|
|
85568
86105
|
readFileSync: (p) => readFileSync54(p, "utf8"),
|
|
85569
86106
|
writeFileSync: (p, d) => writeFileSync43(p, d),
|
|
@@ -85678,6 +86215,7 @@ var pendingRestarts = new Map;
|
|
|
85678
86215
|
var pendingSessionCommand = createPendingSessionCommandSlots();
|
|
85679
86216
|
var PENDING_CMD_DRAIN_CAP_MS = 60000;
|
|
85680
86217
|
var lastSessionActiveFile = null;
|
|
86218
|
+
var lastSessionStampedTurnKey = null;
|
|
85681
86219
|
var compactState = initialCompactState();
|
|
85682
86220
|
var compactDispatching = false;
|
|
85683
86221
|
var COMPACT_CARD_TIMEOUT_MS = 900000;
|
|
@@ -87891,7 +88429,7 @@ var statusPinState = new Map;
|
|
|
87891
88429
|
var statusPinChatIds = new Map;
|
|
87892
88430
|
var statusPinPinnedAt = new Map;
|
|
87893
88431
|
var statusPinRightsCache = new PinRightsCache2;
|
|
87894
|
-
var STATUS_PIN_STORE_PATH =
|
|
88432
|
+
var STATUS_PIN_STORE_PATH = join55(STATE_DIR, "status-pins.json");
|
|
87895
88433
|
var statusPinStoreFs = {
|
|
87896
88434
|
readFileSync: (p) => readFileSync54(p, "utf8"),
|
|
87897
88435
|
writeFileSync: (p, d) => writeFileSync43(p, d),
|
|
@@ -87899,7 +88437,7 @@ var statusPinStoreFs = {
|
|
|
87899
88437
|
existsSync: (p) => existsSync50(p)
|
|
87900
88438
|
};
|
|
87901
88439
|
var statusPinPersistEnabled = !STATIC && PIN_STATUS_WHILE_WORKING;
|
|
87902
|
-
var ACTIVITY_CARD_STORE_PATH =
|
|
88440
|
+
var ACTIVITY_CARD_STORE_PATH = join55(STATE_DIR, "activity-cards-pending.json");
|
|
87903
88441
|
var activityCardStoreFs = {
|
|
87904
88442
|
readFileSync: (p) => readFileSync54(p, "utf8"),
|
|
87905
88443
|
writeFileSync: (p, d) => writeFileSync43(p, d),
|
|
@@ -87907,7 +88445,7 @@ var activityCardStoreFs = {
|
|
|
87907
88445
|
existsSync: (p) => existsSync50(p)
|
|
87908
88446
|
};
|
|
87909
88447
|
var activityCardPersistEnabled = !STATIC;
|
|
87910
|
-
var QUEUED_CARD_STORE_PATH =
|
|
88448
|
+
var QUEUED_CARD_STORE_PATH = join55(STATE_DIR, "queued-cards-pending.json");
|
|
87911
88449
|
var queuedCardStoreFs = {
|
|
87912
88450
|
readFileSync: (p) => readFileSync54(p, "utf8"),
|
|
87913
88451
|
writeFileSync: (p, d) => writeFileSync43(p, d),
|
|
@@ -88261,11 +88799,11 @@ var getPinnedProgressCardMessageId = null;
|
|
|
88261
88799
|
var completeProgressCardTurn = null;
|
|
88262
88800
|
var subagentWatcher = null;
|
|
88263
88801
|
var workerActivityFeed = null;
|
|
88264
|
-
var SOCKET_PATH = process.env.SWITCHROOM_GATEWAY_SOCKET ??
|
|
88802
|
+
var SOCKET_PATH = process.env.SWITCHROOM_GATEWAY_SOCKET ?? join55(STATE_DIR, "gateway.sock");
|
|
88265
88803
|
mkdirSync40(STATE_DIR, { recursive: true, mode: 448 });
|
|
88266
|
-
var GATEWAY_PID_PATH = process.env.SWITCHROOM_GATEWAY_PID_FILE ??
|
|
88267
|
-
var GATEWAY_SESSION_MARKER_PATH = process.env.SWITCHROOM_GATEWAY_SESSION_MARKER ??
|
|
88268
|
-
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");
|
|
88269
88807
|
var GATEWAY_STARTED_AT_MS = Date.now();
|
|
88270
88808
|
var BOOT_CARD_ENABLED = process.env.SWITCHROOM_BOOT_CARD !== "false";
|
|
88271
88809
|
var activeBootCard = null;
|
|
@@ -88294,7 +88832,7 @@ function ensureIssuesCard(chatId, threadId) {
|
|
|
88294
88832
|
bot: botApi,
|
|
88295
88833
|
log: (msg) => process.stderr.write(`telegram gateway: ${msg}
|
|
88296
88834
|
`),
|
|
88297
|
-
persistPath:
|
|
88835
|
+
persistPath: join55(stateDir, "issues-card.json")
|
|
88298
88836
|
});
|
|
88299
88837
|
activeIssuesWatcher = startIssuesWatcher({
|
|
88300
88838
|
stateDir,
|
|
@@ -88625,7 +89163,7 @@ startTimer2({
|
|
|
88625
89163
|
}
|
|
88626
89164
|
});
|
|
88627
89165
|
var inboundSpool = STATIC ? undefined : createInboundSpool({
|
|
88628
|
-
path:
|
|
89166
|
+
path: join55(STATE_DIR, "inbound-spool.jsonl"),
|
|
88629
89167
|
fs: {
|
|
88630
89168
|
appendFileSync: (p, d) => appendFileSync6(p, d),
|
|
88631
89169
|
readFileSync: (p) => readFileSync54(p, "utf8"),
|
|
@@ -88760,6 +89298,88 @@ async function deliverCapturedProse(args) {
|
|
|
88760
89298
|
}
|
|
88761
89299
|
}
|
|
88762
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
|
+
}
|
|
88763
89383
|
function obligationSweep() {
|
|
88764
89384
|
if (!OBLIGATION_LEDGER_ENABLED)
|
|
88765
89385
|
return;
|
|
@@ -88919,8 +89539,8 @@ var bridgeDeadWatchdog = createBridgeDeadWatchdog({
|
|
|
88919
89539
|
isSessionAlive: () => findAgentProcessInContainer2()?.comm === "claude",
|
|
88920
89540
|
isShuttingDown: () => shuttingDown,
|
|
88921
89541
|
escalate: (reason) => triggerSelfRestart(process.env.SWITCHROOM_AGENT_NAME ?? "", reason, 1500),
|
|
88922
|
-
crashLogPath:
|
|
88923
|
-
markerPath:
|
|
89542
|
+
crashLogPath: join55(STATE_DIR, "bridge-crash.log"),
|
|
89543
|
+
markerPath: join55(STATE_DIR, "bridge-dead-escalation.json"),
|
|
88924
89544
|
log: (line) => process.stderr.write(`${line}
|
|
88925
89545
|
`),
|
|
88926
89546
|
priorStreak: bridgeDeadPriorStreak,
|
|
@@ -89047,8 +89667,8 @@ var ipcServer = createIpcServer({
|
|
|
89047
89667
|
probeQuotaViaBroker: (t) => probeQuotaForBootCard(agentSlug, t),
|
|
89048
89668
|
tmuxSupervisor: process.env.SWITCHROOM_TMUX_SUPERVISOR === "1",
|
|
89049
89669
|
dockerMode: process.env.SWITCHROOM_RUNTIME === "docker",
|
|
89050
|
-
configSnapshotPath:
|
|
89051
|
-
bootCardStatePath:
|
|
89670
|
+
configSnapshotPath: join55(resolvedAgentDirForCard, ".config-snapshot.json"),
|
|
89671
|
+
bootCardStatePath: join55(resolvedAgentDirForCard, ".boot-card-msgid.json"),
|
|
89052
89672
|
floodStatePath: FLOOD_STATE_PATH,
|
|
89053
89673
|
...updateOutcomeLine ? { updateOutcomeLine } : {}
|
|
89054
89674
|
}, ackMsgId).then((handle) => {
|
|
@@ -89133,6 +89753,21 @@ var ipcServer = createIpcServer({
|
|
|
89133
89753
|
return;
|
|
89134
89754
|
if (msg.activeFile)
|
|
89135
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
|
+
}
|
|
89136
89771
|
const ev = msg.event;
|
|
89137
89772
|
handleSessionEvent(ev);
|
|
89138
89773
|
toolFlightTracker.onEvent(ev);
|
|
@@ -89715,7 +90350,7 @@ var ipcServer = createIpcServer({
|
|
|
89715
90350
|
const receiverUid = receiverUidRaw ? Number(receiverUidRaw) : NaN;
|
|
89716
90351
|
if (Number.isInteger(receiverUid))
|
|
89717
90352
|
allowedUids.push(receiverUid);
|
|
89718
|
-
const socketPath =
|
|
90353
|
+
const socketPath = join55(STATE_DIR, "webhook.sock");
|
|
89719
90354
|
const webhookInject = (agentName3, inbound) => {
|
|
89720
90355
|
const msg = inbound;
|
|
89721
90356
|
const delivered = ipcServer.sendToAgent(agentName3, msg);
|
|
@@ -89950,9 +90585,9 @@ function redactOutboundText(text5, site) {
|
|
|
89950
90585
|
var VOICE_OUT_DEFAULT_CHUNK_CHARS = 600;
|
|
89951
90586
|
var VOICE_OUT_HARD_CHUNK_CAP = 4096;
|
|
89952
90587
|
var voiceOnDemandCache = new VoiceOnDemandCache({
|
|
89953
|
-
persistPath:
|
|
90588
|
+
persistPath: join55(STATE_DIR, "voice-ondemand.json")
|
|
89954
90589
|
});
|
|
89955
|
-
var VOICE_CACHE_DIR =
|
|
90590
|
+
var VOICE_CACHE_DIR = join55(STATE_DIR, "voice-cache");
|
|
89956
90591
|
var voicePreSynthQueue = new PreSynthQueue({
|
|
89957
90592
|
runJob: async (job) => {
|
|
89958
90593
|
const sidecarToken = await materializeSidecarToken();
|
|
@@ -90931,7 +91566,7 @@ async function executeSendGif(rawArgs) {
|
|
|
90931
91566
|
};
|
|
90932
91567
|
}
|
|
90933
91568
|
async function publishToTelegraph(text5, shortName, authorName) {
|
|
90934
|
-
const accountPath =
|
|
91569
|
+
const accountPath = join55(STATE_DIR, "telegraph-account.json");
|
|
90935
91570
|
let account = null;
|
|
90936
91571
|
try {
|
|
90937
91572
|
if (existsSync50(accountPath)) {
|
|
@@ -91854,7 +92489,8 @@ function composeTurnActivity(turn, final = false, liveSuffix = "") {
|
|
|
91854
92489
|
elapsedMs: turn.startedAt > 0 ? Date.now() - turn.startedAt : 0,
|
|
91855
92490
|
toolCount: turn.labeledToolCount,
|
|
91856
92491
|
state: final ? "done" : "running",
|
|
91857
|
-
model: turn.currentModel
|
|
92492
|
+
model: turn.currentModel,
|
|
92493
|
+
totalTokens: turn.totalTokens
|
|
91858
92494
|
};
|
|
91859
92495
|
return renderActivityFeedWithNested2(turn.mirrorLines, childLines, final, liveSuffix, stepCount, header);
|
|
91860
92496
|
}
|
|
@@ -92315,6 +92951,8 @@ function handleSessionEvent(ev) {
|
|
|
92315
92951
|
lastAssistantDone: false,
|
|
92316
92952
|
toolCallCount: 0,
|
|
92317
92953
|
labeledToolCount: 0,
|
|
92954
|
+
totalTokens: 0,
|
|
92955
|
+
seenUsageMessageIds: new Set,
|
|
92318
92956
|
activityMessageId: null,
|
|
92319
92957
|
activityInFlight: null,
|
|
92320
92958
|
activityPendingRender: null,
|
|
@@ -92388,6 +93026,18 @@ function handleSessionEvent(ev) {
|
|
|
92388
93026
|
sessionModelSource.noteTranscriptModel(ev.model);
|
|
92389
93027
|
return;
|
|
92390
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
|
+
}
|
|
92391
93041
|
case "thinking": {
|
|
92392
93042
|
const turn = currentTurn;
|
|
92393
93043
|
if (turn == null)
|
|
@@ -94258,7 +94908,7 @@ function getMyAgentName() {
|
|
|
94258
94908
|
const fromEnv = process.env.SWITCHROOM_AGENT_NAME;
|
|
94259
94909
|
if (fromEnv && fromEnv.trim().length > 0)
|
|
94260
94910
|
return fromEnv.trim();
|
|
94261
|
-
return
|
|
94911
|
+
return basename13(process.cwd());
|
|
94262
94912
|
}
|
|
94263
94913
|
function isSelfTargetingCommand(name) {
|
|
94264
94914
|
if (name === "all")
|
|
@@ -94271,7 +94921,7 @@ function restartMarkerPath() {
|
|
|
94271
94921
|
const agentDir = resolveAgentDirFromEnv();
|
|
94272
94922
|
if (!agentDir)
|
|
94273
94923
|
return null;
|
|
94274
|
-
return
|
|
94924
|
+
return join55(agentDir, "restart-pending.json");
|
|
94275
94925
|
}
|
|
94276
94926
|
function writeRestartMarker(marker) {
|
|
94277
94927
|
const p = restartMarkerPath();
|
|
@@ -94463,7 +95113,7 @@ function _resetDockerReachableCache() {
|
|
|
94463
95113
|
}
|
|
94464
95114
|
function spawnSwitchroomDetached(args, onFailure) {
|
|
94465
95115
|
const fullArgs = SWITCHROOM_CONFIG ? ["--config", SWITCHROOM_CONFIG, ...args] : args;
|
|
94466
|
-
const logPath =
|
|
95116
|
+
const logPath = join55(STATE_DIR, "detached-spawn.log");
|
|
94467
95117
|
let outFd = null;
|
|
94468
95118
|
try {
|
|
94469
95119
|
mkdirSync40(STATE_DIR, { recursive: true });
|
|
@@ -94861,7 +95511,7 @@ bot.use(async (ctx, next) => {
|
|
|
94861
95511
|
});
|
|
94862
95512
|
function readRecentDenialsForAgent(agentName3, windowMs, limit) {
|
|
94863
95513
|
try {
|
|
94864
|
-
const auditPath =
|
|
95514
|
+
const auditPath = join55(homedir18(), ".switchroom", "vault-audit.log");
|
|
94865
95515
|
if (!existsSync50(auditPath))
|
|
94866
95516
|
return [];
|
|
94867
95517
|
const raw = readFileSync54(auditPath, "utf8");
|
|
@@ -94915,7 +95565,7 @@ async function buildAgentMetadata(agentName3) {
|
|
|
94915
95565
|
try {
|
|
94916
95566
|
const agentDir = resolveAgentDirFromEnv();
|
|
94917
95567
|
if (agentDir) {
|
|
94918
|
-
const raw = readFileSync54(
|
|
95568
|
+
const raw = readFileSync54(join55(agentDir, ".claude", ".claude.json"), "utf8");
|
|
94919
95569
|
claudeJson = JSON.parse(raw);
|
|
94920
95570
|
}
|
|
94921
95571
|
} catch {}
|
|
@@ -95111,7 +95761,7 @@ function buildModelDeps(restartCtx) {
|
|
|
95111
95761
|
try {
|
|
95112
95762
|
const agentDir = resolveAgentDirFromEnv();
|
|
95113
95763
|
if (agentDir) {
|
|
95114
|
-
const local = await fetchQuota2({ claudeConfigDir:
|
|
95764
|
+
const local = await fetchQuota2({ claudeConfigDir: join55(agentDir, ".claude") });
|
|
95115
95765
|
if (local.ok)
|
|
95116
95766
|
return formatQuotaLine2(local.data);
|
|
95117
95767
|
}
|
|
@@ -95583,7 +96233,7 @@ bot.command("restart", async (ctx) => {
|
|
|
95583
96233
|
function flushAgentHandoff(agentDir) {
|
|
95584
96234
|
let removed = 0;
|
|
95585
96235
|
for (const fname of [".handoff.md", ".handoff-topic"]) {
|
|
95586
|
-
const p =
|
|
96236
|
+
const p = join55(agentDir, fname);
|
|
95587
96237
|
try {
|
|
95588
96238
|
if (existsSync50(p)) {
|
|
95589
96239
|
unlinkSync24(p);
|
|
@@ -95641,7 +96291,7 @@ async function handleNewCommand(ctx) {
|
|
|
95641
96291
|
writeRestartMarker({ chat_id: chatId, thread_id: threadId ?? null, ack_message_id: ackId, ts: Date.now() });
|
|
95642
96292
|
if (agentDir != null) {
|
|
95643
96293
|
try {
|
|
95644
|
-
writeFileSync43(
|
|
96294
|
+
writeFileSync43(join55(agentDir, ".force-fresh-session"), `${kind} at ${new Date().toISOString()}
|
|
95645
96295
|
`, "utf8");
|
|
95646
96296
|
} catch (err) {
|
|
95647
96297
|
process.stderr.write(`telegram gateway: failed to write force-fresh marker: ${err}
|
|
@@ -96011,7 +96661,7 @@ var lockoutOps = {
|
|
|
96011
96661
|
writeFileSync: (p, data, opts) => writeFileSync43(p, data, opts),
|
|
96012
96662
|
existsSync: (p) => existsSync50(p),
|
|
96013
96663
|
mkdirSync: (p, opts) => mkdirSync40(p, opts),
|
|
96014
|
-
joinPath: (...parts) =>
|
|
96664
|
+
joinPath: (...parts) => join55(...parts)
|
|
96015
96665
|
};
|
|
96016
96666
|
var FLEET_FALLBACK_DEDUP_MS = 30000;
|
|
96017
96667
|
function isAuthBrokerSocketReachable() {
|
|
@@ -96271,7 +96921,7 @@ async function runCreditWatch() {
|
|
|
96271
96921
|
if (!agentDir)
|
|
96272
96922
|
return;
|
|
96273
96923
|
const agentName3 = getMyAgentName();
|
|
96274
|
-
const claudeConfigDir =
|
|
96924
|
+
const claudeConfigDir = join55(agentDir, ".claude");
|
|
96275
96925
|
const stateDir = STATE_DIR;
|
|
96276
96926
|
const reason = readClaudeJsonOverage(claudeConfigDir);
|
|
96277
96927
|
const prev = loadCreditState(stateDir);
|
|
@@ -97283,7 +97933,7 @@ bot.command("usage", async (ctx) => {
|
|
|
97283
97933
|
await switchroomReply(ctx, "**/usage:** cannot resolve agent dir.", { html: true });
|
|
97284
97934
|
return;
|
|
97285
97935
|
}
|
|
97286
|
-
const result = await fetchQuota2({ claudeConfigDir:
|
|
97936
|
+
const result = await fetchQuota2({ claudeConfigDir: join55(agentDir, ".claude") });
|
|
97287
97937
|
if (!result.ok) {
|
|
97288
97938
|
await switchroomReply(ctx, `**/usage:** ${escapeHtmlForTg2(result.reason)}`, { html: true });
|
|
97289
97939
|
return;
|
|
@@ -99171,6 +99821,10 @@ var didOneTimeSetup = false;
|
|
|
99171
99821
|
process.stderr.write(`telegram gateway: blocked-approval boot reconcile failed: ${err.message}
|
|
99172
99822
|
`);
|
|
99173
99823
|
}
|
|
99824
|
+
maybeRedeliverUndeliveredAnswer().catch((err) => {
|
|
99825
|
+
process.stderr.write(`telegram gateway: crash-redelivery boot send errored: ${err.message}
|
|
99826
|
+
`);
|
|
99827
|
+
});
|
|
99174
99828
|
try {
|
|
99175
99829
|
const bootAccess = loadAccess();
|
|
99176
99830
|
const chatSet = new Set(bootAccess.allowFrom);
|
|
@@ -99268,7 +99922,7 @@ var didOneTimeSetup = false;
|
|
|
99268
99922
|
return;
|
|
99269
99923
|
}
|
|
99270
99924
|
})();
|
|
99271
|
-
const resolvedAgentDirForBootCard = agentDir ??
|
|
99925
|
+
const resolvedAgentDirForBootCard = agentDir ?? join55(homedir18(), ".switchroom", "agents", agentSlug);
|
|
99272
99926
|
const handle = await startBootCard(chatId, threadId, botApiForCard, {
|
|
99273
99927
|
agentName: agentDisplayName,
|
|
99274
99928
|
agentSlug,
|
|
@@ -99282,8 +99936,8 @@ var didOneTimeSetup = false;
|
|
|
99282
99936
|
probeQuotaViaBroker: (t) => probeQuotaForBootCard(agentSlug, t),
|
|
99283
99937
|
tmuxSupervisor: process.env.SWITCHROOM_TMUX_SUPERVISOR === "1",
|
|
99284
99938
|
dockerMode: process.env.SWITCHROOM_RUNTIME === "docker",
|
|
99285
|
-
configSnapshotPath:
|
|
99286
|
-
bootCardStatePath:
|
|
99939
|
+
configSnapshotPath: join55(resolvedAgentDirForBootCard, ".config-snapshot.json"),
|
|
99940
|
+
bootCardStatePath: join55(resolvedAgentDirForBootCard, ".boot-card-msgid.json"),
|
|
99287
99941
|
floodStatePath: FLOOD_STATE_PATH,
|
|
99288
99942
|
...updateOutcomeLine ? { updateOutcomeLine } : {}
|
|
99289
99943
|
}, ackMsgId);
|
|
@@ -99314,7 +99968,7 @@ var didOneTimeSetup = false;
|
|
|
99314
99968
|
try {
|
|
99315
99969
|
const smAgentDir = resolveAgentDirFromEnv();
|
|
99316
99970
|
if (smAgentDir) {
|
|
99317
|
-
const activePath =
|
|
99971
|
+
const activePath = join55(smAgentDir, ".active-session-model");
|
|
99318
99972
|
if (existsSync50(activePath)) {
|
|
99319
99973
|
try {
|
|
99320
99974
|
const launched = readFileSync54(activePath, "utf8").trim();
|
|
@@ -99326,7 +99980,7 @@ var didOneTimeSetup = false;
|
|
|
99326
99980
|
sessionModelSource.setOverride(launched.length > 0 && launched !== configured ? launched : null);
|
|
99327
99981
|
} catch {}
|
|
99328
99982
|
}
|
|
99329
|
-
const activeEffortPath =
|
|
99983
|
+
const activeEffortPath = join55(smAgentDir, ".active-session-effort");
|
|
99330
99984
|
if (existsSync50(activeEffortPath)) {
|
|
99331
99985
|
try {
|
|
99332
99986
|
const launchedEffort = readFileSync54(activeEffortPath, "utf8").trim();
|
|
@@ -99334,7 +99988,7 @@ var didOneTimeSetup = false;
|
|
|
99334
99988
|
sessionEffortOverride = launchedEffort.length > 0 && launchedEffort !== configuredEffort ? launchedEffort : null;
|
|
99335
99989
|
} catch {}
|
|
99336
99990
|
}
|
|
99337
|
-
const alertPath =
|
|
99991
|
+
const alertPath = join55(smAgentDir, ".session-model-alert");
|
|
99338
99992
|
if (existsSync50(alertPath)) {
|
|
99339
99993
|
let alertText = null;
|
|
99340
99994
|
try {
|
|
@@ -99463,6 +100117,7 @@ var didOneTimeSetup = false;
|
|
|
99463
100117
|
maxRows: workerFeedMaxRows,
|
|
99464
100118
|
staleWorkerTtlMs: resolveInflightTerminalCapMs() + WORKER_FEED_STALE_TTL_MARGIN_MS,
|
|
99465
100119
|
absoluteRowLifetimeCapMs: resolveInflightTerminalCapMs() * WORKER_FEED_ABSOLUTE_ROW_LIFETIME_CAP_MULTIPLE,
|
|
100120
|
+
groupMessageLifetimeCapMs: WORKER_FEED_GROUP_MESSAGE_LIFETIME_CAP_MS,
|
|
99466
100121
|
reconcilePin: ({ feedKey, chatId, messageId }) => {
|
|
99467
100122
|
if (!PIN_STATUS_WHILE_WORKING)
|
|
99468
100123
|
return;
|
|
@@ -99516,7 +100171,7 @@ var didOneTimeSetup = false;
|
|
|
99516
100171
|
`);
|
|
99517
100172
|
}
|
|
99518
100173
|
},
|
|
99519
|
-
onFinish: ({ agentId, outcome, description, resultText, toolCount, durationMs, background: entryBackground }) => {
|
|
100174
|
+
onFinish: ({ agentId, outcome, description, resultText, toolCount, totalTokens, durationMs, background: entryBackground }) => {
|
|
99520
100175
|
deferredDoneReactions.promote();
|
|
99521
100176
|
let fleetChatId = "";
|
|
99522
100177
|
try {
|
|
@@ -99544,6 +100199,7 @@ var didOneTimeSetup = false;
|
|
|
99544
100199
|
description: dispatch.feedDescription,
|
|
99545
100200
|
lastTool: null,
|
|
99546
100201
|
toolCount,
|
|
100202
|
+
totalTokens,
|
|
99547
100203
|
latestSummary: resultText,
|
|
99548
100204
|
elapsedMs: durationMs,
|
|
99549
100205
|
state: outcome === "failed" ? "failed" : "done",
|
|
@@ -99591,6 +100247,7 @@ var didOneTimeSetup = false;
|
|
|
99591
100247
|
description: dispatch.feedDescription,
|
|
99592
100248
|
lastTool: null,
|
|
99593
100249
|
toolCount,
|
|
100250
|
+
totalTokens,
|
|
99594
100251
|
latestSummary: resultText,
|
|
99595
100252
|
elapsedMs: durationMs,
|
|
99596
100253
|
state: outcome === "failed" ? "failed" : "done",
|
|
@@ -99604,6 +100261,7 @@ var didOneTimeSetup = false;
|
|
|
99604
100261
|
description: dispatch.feedDescription,
|
|
99605
100262
|
lastTool: null,
|
|
99606
100263
|
toolCount,
|
|
100264
|
+
totalTokens,
|
|
99607
100265
|
latestSummary: resultText,
|
|
99608
100266
|
elapsedMs: durationMs,
|
|
99609
100267
|
state: outcome === "failed" ? "failed" : "done",
|
|
@@ -99644,7 +100302,7 @@ var didOneTimeSetup = false;
|
|
|
99644
100302
|
process.stderr.write(`telegram gateway: subagent-handback queued agent=${agentId} outcome=${outcome} chat=${decision.chatId} resultChars=${resultText.length}
|
|
99645
100303
|
`);
|
|
99646
100304
|
},
|
|
99647
|
-
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 }) => {
|
|
99648
100306
|
let fleetChatId = "";
|
|
99649
100307
|
try {
|
|
99650
100308
|
const fleets = progressDriver?.peekAllFleets() ?? [];
|
|
@@ -99681,7 +100339,8 @@ var didOneTimeSetup = false;
|
|
|
99681
100339
|
latestSummary: stepLine,
|
|
99682
100340
|
elapsedMs,
|
|
99683
100341
|
state: "running",
|
|
99684
|
-
model: feedModel
|
|
100342
|
+
model: feedModel,
|
|
100343
|
+
totalTokens
|
|
99685
100344
|
}, wk.threadId);
|
|
99686
100345
|
return;
|
|
99687
100346
|
}
|
|
@@ -99742,7 +100401,8 @@ var didOneTimeSetup = false;
|
|
|
99742
100401
|
latestSummary: stepLine,
|
|
99743
100402
|
elapsedMs,
|
|
99744
100403
|
state: "running",
|
|
99745
|
-
model: feedModel
|
|
100404
|
+
model: feedModel,
|
|
100405
|
+
totalTokens
|
|
99746
100406
|
}, wk.threadId);
|
|
99747
100407
|
return;
|
|
99748
100408
|
}
|