switchroom 0.18.24 → 0.18.26
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 +2 -2
- package/telegram-plugin/dist/bridge/bridge.js +26 -0
- package/telegram-plugin/dist/gateway/gateway.js +1827 -831
- 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/render/code-segments.ts +210 -0
- package/telegram-plugin/render/dollar-math-guard.ts +126 -0
- package/telegram-plugin/render/emphasis-guard.ts +158 -0
- package/telegram-plugin/render/inline-pairs-guard.ts +171 -0
- package/telegram-plugin/render/line-start-guard.ts +167 -0
- package/telegram-plugin/render/rich-render.ts +7 -0
- package/telegram-plugin/rich-send.ts +48 -2
- 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/render/dollar-math-guard.test.ts +162 -0
- package/telegram-plugin/tests/render/emphasis-guard.test.ts +205 -0
- package/telegram-plugin/tests/render/guard-composition.test.ts +138 -0
- package/telegram-plugin/tests/render/inline-pairs-guard.test.ts +171 -0
- package/telegram-plugin/tests/render/line-start-guard.test.ts +164 -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
|
@@ -7525,9 +7525,329 @@ var init_approval_card = __esm(() => {
|
|
|
7525
7525
|
import_grammy3 = __toESM(require_mod2(), 1);
|
|
7526
7526
|
});
|
|
7527
7527
|
|
|
7528
|
+
// render/code-segments.ts
|
|
7529
|
+
function findClosingBackticks(text, from, runLen) {
|
|
7530
|
+
let i = from;
|
|
7531
|
+
while (i < text.length) {
|
|
7532
|
+
if (text[i] === "`") {
|
|
7533
|
+
let j = i;
|
|
7534
|
+
while (j < text.length && text[j] === "`")
|
|
7535
|
+
j++;
|
|
7536
|
+
if (j - i === runLen)
|
|
7537
|
+
return j;
|
|
7538
|
+
i = j;
|
|
7539
|
+
} else {
|
|
7540
|
+
i++;
|
|
7541
|
+
}
|
|
7542
|
+
}
|
|
7543
|
+
return -1;
|
|
7544
|
+
}
|
|
7545
|
+
function splitCodeSegments(text) {
|
|
7546
|
+
const out = [];
|
|
7547
|
+
let i = 0;
|
|
7548
|
+
let plainStart = 0;
|
|
7549
|
+
while (i < text.length) {
|
|
7550
|
+
if (text[i] === "`") {
|
|
7551
|
+
let j = i;
|
|
7552
|
+
while (j < text.length && text[j] === "`")
|
|
7553
|
+
j++;
|
|
7554
|
+
const runLen = j - i;
|
|
7555
|
+
const close = findClosingBackticks(text, j, runLen);
|
|
7556
|
+
if (close !== -1) {
|
|
7557
|
+
if (plainStart < i)
|
|
7558
|
+
out.push({ code: false, text: text.slice(plainStart, i) });
|
|
7559
|
+
out.push({ code: true, text: text.slice(i, close) });
|
|
7560
|
+
i = close;
|
|
7561
|
+
plainStart = close;
|
|
7562
|
+
continue;
|
|
7563
|
+
}
|
|
7564
|
+
}
|
|
7565
|
+
i++;
|
|
7566
|
+
}
|
|
7567
|
+
if (plainStart < text.length)
|
|
7568
|
+
out.push({ code: false, text: text.slice(plainStart) });
|
|
7569
|
+
return out;
|
|
7570
|
+
}
|
|
7571
|
+
function isTableDelimiterRow(line) {
|
|
7572
|
+
const t = line.trim();
|
|
7573
|
+
return t.length > 0 && /^[\s|:-]+$/.test(t) && t.includes("-") && t.includes("|");
|
|
7574
|
+
}
|
|
7575
|
+
function isTableCandidateLine(line) {
|
|
7576
|
+
return /^\s*\|/.test(line);
|
|
7577
|
+
}
|
|
7578
|
+
function findTableRanges(text) {
|
|
7579
|
+
const ranges = [];
|
|
7580
|
+
const lines = text.split(`
|
|
7581
|
+
`);
|
|
7582
|
+
let offset = 0;
|
|
7583
|
+
let runStart = -1;
|
|
7584
|
+
let runEnd = -1;
|
|
7585
|
+
let runHasDelim = false;
|
|
7586
|
+
let runLineCount = 0;
|
|
7587
|
+
const flush = () => {
|
|
7588
|
+
if (runStart !== -1 && runLineCount >= 2 && runHasDelim) {
|
|
7589
|
+
ranges.push([runStart, runEnd]);
|
|
7590
|
+
}
|
|
7591
|
+
runStart = -1;
|
|
7592
|
+
runEnd = -1;
|
|
7593
|
+
runHasDelim = false;
|
|
7594
|
+
runLineCount = 0;
|
|
7595
|
+
};
|
|
7596
|
+
for (let k = 0;k < lines.length; k++) {
|
|
7597
|
+
const line = lines[k];
|
|
7598
|
+
const lineLen = line.length + (k < lines.length - 1 ? 1 : 0);
|
|
7599
|
+
if (isTableCandidateLine(line)) {
|
|
7600
|
+
if (runStart === -1)
|
|
7601
|
+
runStart = offset;
|
|
7602
|
+
runEnd = offset + lineLen;
|
|
7603
|
+
runLineCount += 1;
|
|
7604
|
+
if (isTableDelimiterRow(line))
|
|
7605
|
+
runHasDelim = true;
|
|
7606
|
+
} else {
|
|
7607
|
+
flush();
|
|
7608
|
+
}
|
|
7609
|
+
offset += lineLen;
|
|
7610
|
+
}
|
|
7611
|
+
flush();
|
|
7612
|
+
return ranges;
|
|
7613
|
+
}
|
|
7614
|
+
function splitProseProtected(text) {
|
|
7615
|
+
const out = [];
|
|
7616
|
+
const tables = findTableRanges(text);
|
|
7617
|
+
let tIdx = 0;
|
|
7618
|
+
let i = 0;
|
|
7619
|
+
let plainStart = 0;
|
|
7620
|
+
const pushProtected = (from, to) => {
|
|
7621
|
+
if (plainStart < from)
|
|
7622
|
+
out.push({ code: false, text: text.slice(plainStart, from) });
|
|
7623
|
+
out.push({ code: true, text: text.slice(from, to) });
|
|
7624
|
+
plainStart = to;
|
|
7625
|
+
};
|
|
7626
|
+
while (i < text.length) {
|
|
7627
|
+
while (tIdx < tables.length && tables[tIdx][1] <= i)
|
|
7628
|
+
tIdx++;
|
|
7629
|
+
if (tIdx < tables.length && tables[tIdx][0] === i) {
|
|
7630
|
+
const [, end] = tables[tIdx];
|
|
7631
|
+
pushProtected(i, end);
|
|
7632
|
+
i = end;
|
|
7633
|
+
continue;
|
|
7634
|
+
}
|
|
7635
|
+
const ch = text[i];
|
|
7636
|
+
if (ch === "[") {
|
|
7637
|
+
const close = text.indexOf("]", i + 1);
|
|
7638
|
+
if (close !== -1 && text[close + 1] === "(") {
|
|
7639
|
+
const destClose = text.indexOf(")", close + 2);
|
|
7640
|
+
if (destClose !== -1) {
|
|
7641
|
+
pushProtected(close + 1, destClose + 1);
|
|
7642
|
+
i = destClose + 1;
|
|
7643
|
+
continue;
|
|
7644
|
+
}
|
|
7645
|
+
}
|
|
7646
|
+
i++;
|
|
7647
|
+
continue;
|
|
7648
|
+
}
|
|
7649
|
+
if ((ch === "h" || ch === "w") && (i === 0 || !/[A-Za-z0-9]/.test(text[i - 1]))) {
|
|
7650
|
+
const m = /^(?:https?:\/\/|www\.)[^\s<>()\[\]]+/.exec(text.slice(i));
|
|
7651
|
+
if (m) {
|
|
7652
|
+
const end = i + m[0].length;
|
|
7653
|
+
pushProtected(i, end);
|
|
7654
|
+
i = end;
|
|
7655
|
+
continue;
|
|
7656
|
+
}
|
|
7657
|
+
}
|
|
7658
|
+
i++;
|
|
7659
|
+
}
|
|
7660
|
+
if (plainStart < text.length)
|
|
7661
|
+
out.push({ code: false, text: text.slice(plainStart) });
|
|
7662
|
+
return out;
|
|
7663
|
+
}
|
|
7664
|
+
function splitProtectedSegments(text) {
|
|
7665
|
+
const out = [];
|
|
7666
|
+
for (const seg of splitCodeSegments(text)) {
|
|
7667
|
+
if (seg.code) {
|
|
7668
|
+
out.push(seg);
|
|
7669
|
+
continue;
|
|
7670
|
+
}
|
|
7671
|
+
for (const sub of splitProseProtected(seg.text))
|
|
7672
|
+
out.push(sub);
|
|
7673
|
+
}
|
|
7674
|
+
return out;
|
|
7675
|
+
}
|
|
7676
|
+
|
|
7677
|
+
// render/dollar-math-guard.ts
|
|
7678
|
+
function guardDollarMath(text) {
|
|
7679
|
+
if (!text.includes("$"))
|
|
7680
|
+
return text;
|
|
7681
|
+
const segments = splitProtectedSegments(text);
|
|
7682
|
+
let total = 0;
|
|
7683
|
+
let hasCurrencySignal = false;
|
|
7684
|
+
for (const seg of segments) {
|
|
7685
|
+
if (seg.code)
|
|
7686
|
+
continue;
|
|
7687
|
+
total += seg.text.match(ANY_DOLLAR)?.length ?? 0;
|
|
7688
|
+
if (!hasCurrencySignal && CURRENCY_SIGNAL.test(seg.text))
|
|
7689
|
+
hasCurrencySignal = true;
|
|
7690
|
+
}
|
|
7691
|
+
if (total < 2 || !hasCurrencySignal)
|
|
7692
|
+
return text;
|
|
7693
|
+
return segments.map((seg) => seg.code ? seg.text : seg.text.replace(UNESCAPED_DOLLAR, () => "\\$")).join("");
|
|
7694
|
+
}
|
|
7695
|
+
var ANY_DOLLAR, CURRENCY_SIGNAL, UNESCAPED_DOLLAR;
|
|
7696
|
+
var init_dollar_math_guard = __esm(() => {
|
|
7697
|
+
ANY_DOLLAR = /\$/g;
|
|
7698
|
+
CURRENCY_SIGNAL = /\$\.?\d|\d\s?\$/;
|
|
7699
|
+
UNESCAPED_DOLLAR = /(?<!\\)\$/g;
|
|
7700
|
+
});
|
|
7701
|
+
|
|
7702
|
+
// render/emphasis-guard.ts
|
|
7703
|
+
function guardAccidentalEmphasis(text) {
|
|
7704
|
+
if (!text.includes("_") && !text.includes("*"))
|
|
7705
|
+
return text;
|
|
7706
|
+
const segments = splitProtectedSegments(text);
|
|
7707
|
+
let hasIntraUnderscore = false;
|
|
7708
|
+
let hasIntraAsterisk = false;
|
|
7709
|
+
let underscoreCount = 0;
|
|
7710
|
+
let asteriskCount = 0;
|
|
7711
|
+
for (const seg of segments) {
|
|
7712
|
+
if (seg.code)
|
|
7713
|
+
continue;
|
|
7714
|
+
if (INTRA_WORD_UNDERSCORE.test(seg.text))
|
|
7715
|
+
hasIntraUnderscore = true;
|
|
7716
|
+
if (INTRA_WORD_ASTERISK.test(seg.text))
|
|
7717
|
+
hasIntraAsterisk = true;
|
|
7718
|
+
underscoreCount += seg.text.match(ANY_UNDERSCORE)?.length ?? 0;
|
|
7719
|
+
asteriskCount += seg.text.match(ANY_ASTERISK)?.length ?? 0;
|
|
7720
|
+
}
|
|
7721
|
+
INTRA_WORD_UNDERSCORE.lastIndex = 0;
|
|
7722
|
+
INTRA_WORD_ASTERISK.lastIndex = 0;
|
|
7723
|
+
const armUnderscore = hasIntraUnderscore && underscoreCount >= 2;
|
|
7724
|
+
const armAsterisk = hasIntraAsterisk && asteriskCount >= 2;
|
|
7725
|
+
if (!armUnderscore && !armAsterisk)
|
|
7726
|
+
return text;
|
|
7727
|
+
return segments.map((seg) => {
|
|
7728
|
+
if (seg.code)
|
|
7729
|
+
return seg.text;
|
|
7730
|
+
let out = seg.text;
|
|
7731
|
+
if (armUnderscore)
|
|
7732
|
+
out = out.replace(INTRA_WORD_UNDERSCORE, "\\_");
|
|
7733
|
+
if (armAsterisk)
|
|
7734
|
+
out = out.replace(INTRA_WORD_ASTERISK, "\\*");
|
|
7735
|
+
return out;
|
|
7736
|
+
}).join("");
|
|
7737
|
+
}
|
|
7738
|
+
var INTRA_WORD_UNDERSCORE, INTRA_WORD_ASTERISK, ANY_UNDERSCORE, ANY_ASTERISK;
|
|
7739
|
+
var init_emphasis_guard = __esm(() => {
|
|
7740
|
+
INTRA_WORD_UNDERSCORE = /(?<=[A-Za-z0-9])_(?=[A-Za-z0-9])/g;
|
|
7741
|
+
INTRA_WORD_ASTERISK = /(?<=[A-Za-z0-9])\*(?=[A-Za-z0-9])/g;
|
|
7742
|
+
ANY_UNDERSCORE = /(?<!\\)_/g;
|
|
7743
|
+
ANY_ASTERISK = /(?<!\\)\*/g;
|
|
7744
|
+
});
|
|
7745
|
+
|
|
7746
|
+
// render/line-start-guard.ts
|
|
7747
|
+
function escapeAccidentalLineStart(line) {
|
|
7748
|
+
const indent = /^ */.exec(line)[0];
|
|
7749
|
+
if (indent.length >= 4)
|
|
7750
|
+
return line;
|
|
7751
|
+
const rest = line.slice(indent.length);
|
|
7752
|
+
if (ACCIDENTAL_BLOCKQUOTE.test(rest)) {
|
|
7753
|
+
return indent + "\\" + rest;
|
|
7754
|
+
}
|
|
7755
|
+
const ol = ACCIDENTAL_ORDERED_LIST.exec(rest);
|
|
7756
|
+
if (ol) {
|
|
7757
|
+
const digits = ol[1];
|
|
7758
|
+
const delim = ol[2];
|
|
7759
|
+
return indent + digits + "\\" + delim + rest.slice(digits.length + 1);
|
|
7760
|
+
}
|
|
7761
|
+
return line;
|
|
7762
|
+
}
|
|
7763
|
+
function guardAccidentalBlockConstructs(text) {
|
|
7764
|
+
if (!text.includes(">") && !/\d{4,}[.)]/.test(text))
|
|
7765
|
+
return text;
|
|
7766
|
+
const segments = splitProtectedSegments(text);
|
|
7767
|
+
let out = "";
|
|
7768
|
+
let atLineStart = true;
|
|
7769
|
+
for (const seg of segments) {
|
|
7770
|
+
if (seg.code) {
|
|
7771
|
+
out += seg.text;
|
|
7772
|
+
atLineStart = seg.text.endsWith(`
|
|
7773
|
+
`);
|
|
7774
|
+
continue;
|
|
7775
|
+
}
|
|
7776
|
+
const lines = seg.text.split(`
|
|
7777
|
+
`);
|
|
7778
|
+
for (let k = 0;k < lines.length; k++) {
|
|
7779
|
+
const lineIsAtStart = k === 0 ? atLineStart : true;
|
|
7780
|
+
const processed = lineIsAtStart ? escapeAccidentalLineStart(lines[k]) : lines[k];
|
|
7781
|
+
out += processed;
|
|
7782
|
+
if (k < lines.length - 1)
|
|
7783
|
+
out += `
|
|
7784
|
+
`;
|
|
7785
|
+
}
|
|
7786
|
+
atLineStart = seg.text.endsWith(`
|
|
7787
|
+
`);
|
|
7788
|
+
}
|
|
7789
|
+
return out;
|
|
7790
|
+
}
|
|
7791
|
+
var ACCIDENTAL_BLOCKQUOTE, ACCIDENTAL_ORDERED_LIST;
|
|
7792
|
+
var init_line_start_guard = __esm(() => {
|
|
7793
|
+
ACCIDENTAL_BLOCKQUOTE = /^>[0-9=]/;
|
|
7794
|
+
ACCIDENTAL_ORDERED_LIST = /^(\d{4,})([.)])(\s|$)/;
|
|
7795
|
+
});
|
|
7796
|
+
|
|
7797
|
+
// render/inline-pairs-guard.ts
|
|
7798
|
+
function countMatches(text, re) {
|
|
7799
|
+
return text.match(re)?.length ?? 0;
|
|
7800
|
+
}
|
|
7801
|
+
function guardAccidentalInlinePairs(text) {
|
|
7802
|
+
if (!/[~=|]/.test(text))
|
|
7803
|
+
return text;
|
|
7804
|
+
const segments = splitProtectedSegments(text);
|
|
7805
|
+
let tildes = 0;
|
|
7806
|
+
let marks = 0;
|
|
7807
|
+
let spoilers = 0;
|
|
7808
|
+
for (const seg of segments) {
|
|
7809
|
+
if (seg.code)
|
|
7810
|
+
continue;
|
|
7811
|
+
tildes += countMatches(seg.text, TILDE_APPROX);
|
|
7812
|
+
marks += countMatches(seg.text, MARK_OP);
|
|
7813
|
+
spoilers += countMatches(seg.text, SPOILER_OP);
|
|
7814
|
+
}
|
|
7815
|
+
const armTilde = tildes >= 2;
|
|
7816
|
+
const armMark = marks >= 2;
|
|
7817
|
+
const armSpoiler = spoilers >= 2;
|
|
7818
|
+
if (!armTilde && !armMark && !armSpoiler)
|
|
7819
|
+
return text;
|
|
7820
|
+
return segments.map((seg) => {
|
|
7821
|
+
if (seg.code)
|
|
7822
|
+
return seg.text;
|
|
7823
|
+
let out = seg.text;
|
|
7824
|
+
if (armTilde)
|
|
7825
|
+
out = out.replace(TILDE_APPROX, () => "\\~");
|
|
7826
|
+
if (armMark)
|
|
7827
|
+
out = out.replace(MARK_OP, () => "\\=\\=");
|
|
7828
|
+
if (armSpoiler)
|
|
7829
|
+
out = out.replace(SPOILER_OP, () => "\\|\\|");
|
|
7830
|
+
return out;
|
|
7831
|
+
}).join("");
|
|
7832
|
+
}
|
|
7833
|
+
var TILDE_APPROX, MARK_OP, SPOILER_OP;
|
|
7834
|
+
var init_inline_pairs_guard = __esm(() => {
|
|
7835
|
+
TILDE_APPROX = /(?<!\\)~(?=\$?\.?\d)/g;
|
|
7836
|
+
MARK_OP = /(?<=\w)==(?=\w)/g;
|
|
7837
|
+
SPOILER_OP = /(?<=\w)\|\|(?=\w)/g;
|
|
7838
|
+
});
|
|
7839
|
+
|
|
7528
7840
|
// rich-send.ts
|
|
7841
|
+
function guardAccidentalFormatting(markdown) {
|
|
7842
|
+
let out = markdown;
|
|
7843
|
+
out = guardAccidentalEmphasis(out);
|
|
7844
|
+
out = guardAccidentalBlockConstructs(out);
|
|
7845
|
+
out = guardAccidentalInlinePairs(out);
|
|
7846
|
+
out = guardDollarMath(out);
|
|
7847
|
+
return out;
|
|
7848
|
+
}
|
|
7529
7849
|
function richMessage(markdown) {
|
|
7530
|
-
return { markdown };
|
|
7850
|
+
return { markdown: guardAccidentalFormatting(markdown) };
|
|
7531
7851
|
}
|
|
7532
7852
|
function isParseEntitiesError(err) {
|
|
7533
7853
|
if (!(err instanceof import_grammy4.GrammyError) || err.error_code !== 400)
|
|
@@ -7545,6 +7865,10 @@ function isLengthError(err) {
|
|
|
7545
7865
|
}
|
|
7546
7866
|
var import_grammy4;
|
|
7547
7867
|
var init_rich_send = __esm(() => {
|
|
7868
|
+
init_dollar_math_guard();
|
|
7869
|
+
init_emphasis_guard();
|
|
7870
|
+
init_line_start_guard();
|
|
7871
|
+
init_inline_pairs_guard();
|
|
7548
7872
|
import_grammy4 = __toESM(require_mod2(), 1);
|
|
7549
7873
|
});
|
|
7550
7874
|
|
|
@@ -21866,6 +22190,142 @@ var init_loader = __esm(() => {
|
|
|
21866
22190
|
};
|
|
21867
22191
|
});
|
|
21868
22192
|
|
|
22193
|
+
// quota-check.ts
|
|
22194
|
+
import { readFileSync as readFileSync17, existsSync as existsSync15 } from "fs";
|
|
22195
|
+
import { join as join20 } from "path";
|
|
22196
|
+
function readOauthToken(claudeConfigDir) {
|
|
22197
|
+
const tokenFile = join20(claudeConfigDir, ".oauth-token");
|
|
22198
|
+
if (!existsSync15(tokenFile))
|
|
22199
|
+
return null;
|
|
22200
|
+
try {
|
|
22201
|
+
const raw = readFileSync17(tokenFile, "utf-8").trim();
|
|
22202
|
+
return raw.length > 0 ? raw : null;
|
|
22203
|
+
} catch {
|
|
22204
|
+
return null;
|
|
22205
|
+
}
|
|
22206
|
+
}
|
|
22207
|
+
function parseFloatHeader(headers, name) {
|
|
22208
|
+
const v = headers.get(name);
|
|
22209
|
+
if (v == null || v.trim().length === 0)
|
|
22210
|
+
return null;
|
|
22211
|
+
const n = Number(v);
|
|
22212
|
+
return Number.isFinite(n) ? n : null;
|
|
22213
|
+
}
|
|
22214
|
+
function parseEpochHeader(headers, name) {
|
|
22215
|
+
const v = headers.get(name);
|
|
22216
|
+
if (v == null)
|
|
22217
|
+
return null;
|
|
22218
|
+
const n = Number(v);
|
|
22219
|
+
if (!Number.isFinite(n) || n <= 0)
|
|
22220
|
+
return null;
|
|
22221
|
+
return new Date(n * 1000);
|
|
22222
|
+
}
|
|
22223
|
+
function parseQuotaHeaders(headers) {
|
|
22224
|
+
const fiveHour = parseFloatHeader(headers, "anthropic-ratelimit-unified-5h-utilization");
|
|
22225
|
+
const sevenDay = parseFloatHeader(headers, "anthropic-ratelimit-unified-7d-utilization");
|
|
22226
|
+
if (fiveHour == null && sevenDay == null) {
|
|
22227
|
+
return {
|
|
22228
|
+
ok: false,
|
|
22229
|
+
reason: "no unified rate-limit headers in response (API token, not OAuth?)"
|
|
22230
|
+
};
|
|
22231
|
+
}
|
|
22232
|
+
return {
|
|
22233
|
+
ok: true,
|
|
22234
|
+
data: {
|
|
22235
|
+
fiveHourUtilizationPct: (fiveHour ?? 0) * 100,
|
|
22236
|
+
sevenDayUtilizationPct: (sevenDay ?? 0) * 100,
|
|
22237
|
+
fiveHourUtilPresent: fiveHour != null,
|
|
22238
|
+
sevenDayUtilPresent: sevenDay != null,
|
|
22239
|
+
fiveHourResetAt: parseEpochHeader(headers, "anthropic-ratelimit-unified-5h-reset"),
|
|
22240
|
+
sevenDayResetAt: parseEpochHeader(headers, "anthropic-ratelimit-unified-7d-reset"),
|
|
22241
|
+
representativeClaim: headers.get("anthropic-ratelimit-unified-representative-claim"),
|
|
22242
|
+
overageStatus: headers.get("anthropic-ratelimit-unified-overage-status"),
|
|
22243
|
+
overageDisabledReason: headers.get("anthropic-ratelimit-unified-overage-disabled-reason")
|
|
22244
|
+
}
|
|
22245
|
+
};
|
|
22246
|
+
}
|
|
22247
|
+
async function fetchQuota(opts) {
|
|
22248
|
+
let token;
|
|
22249
|
+
if (opts.accessToken && opts.claudeConfigDir) {
|
|
22250
|
+
return {
|
|
22251
|
+
ok: false,
|
|
22252
|
+
reason: "pass only one of `accessToken` or `claudeConfigDir`, not both"
|
|
22253
|
+
};
|
|
22254
|
+
}
|
|
22255
|
+
if (opts.accessToken) {
|
|
22256
|
+
token = opts.accessToken.trim().length > 0 ? opts.accessToken : null;
|
|
22257
|
+
} else if (opts.claudeConfigDir) {
|
|
22258
|
+
token = readOauthToken(opts.claudeConfigDir);
|
|
22259
|
+
} else {
|
|
22260
|
+
return {
|
|
22261
|
+
ok: false,
|
|
22262
|
+
reason: "fetchQuota requires `accessToken` or `claudeConfigDir`"
|
|
22263
|
+
};
|
|
22264
|
+
}
|
|
22265
|
+
if (!token) {
|
|
22266
|
+
return { ok: false, reason: "no OAuth token at .oauth-token" };
|
|
22267
|
+
}
|
|
22268
|
+
const controller = new AbortController;
|
|
22269
|
+
const timeout = setTimeout(() => controller.abort(), opts.timeoutMs ?? 1e4);
|
|
22270
|
+
const fetchFn = opts.fetchImpl ?? fetch;
|
|
22271
|
+
let resp;
|
|
22272
|
+
try {
|
|
22273
|
+
resp = await fetchFn("https://api.anthropic.com/v1/messages", {
|
|
22274
|
+
method: "POST",
|
|
22275
|
+
headers: {
|
|
22276
|
+
"anthropic-version": "2023-06-01",
|
|
22277
|
+
"anthropic-beta": OAUTH_BETA,
|
|
22278
|
+
authorization: `Bearer ${token}`,
|
|
22279
|
+
"x-app": "cli",
|
|
22280
|
+
"user-agent": DEFAULT_USER_AGENT,
|
|
22281
|
+
"content-type": "application/json"
|
|
22282
|
+
},
|
|
22283
|
+
body: JSON.stringify({
|
|
22284
|
+
model: opts.model ?? DEFAULT_PROBE_MODEL,
|
|
22285
|
+
max_tokens: 1,
|
|
22286
|
+
messages: [{ role: "user", content: "hi" }]
|
|
22287
|
+
}),
|
|
22288
|
+
signal: controller.signal
|
|
22289
|
+
});
|
|
22290
|
+
} catch (err) {
|
|
22291
|
+
const msg = err?.message ?? String(err);
|
|
22292
|
+
return { ok: false, reason: `request failed: ${msg}` };
|
|
22293
|
+
} finally {
|
|
22294
|
+
clearTimeout(timeout);
|
|
22295
|
+
}
|
|
22296
|
+
if (resp.status === 401 || resp.status === 403) {
|
|
22297
|
+
return { ok: false, reason: `auth rejected (HTTP ${resp.status})` };
|
|
22298
|
+
}
|
|
22299
|
+
const parsed = parseQuotaHeaders(resp.headers);
|
|
22300
|
+
if (!parsed.ok && resp.status >= 400) {
|
|
22301
|
+
return { ok: false, reason: `HTTP ${resp.status}, ${parsed.reason}` };
|
|
22302
|
+
}
|
|
22303
|
+
return parsed;
|
|
22304
|
+
}
|
|
22305
|
+
function formatQuotaLine(q) {
|
|
22306
|
+
const fmt = (n) => `${Math.round(n)}%`;
|
|
22307
|
+
return `${fmt(q.fiveHourUtilizationPct)} / 5h \u00b7 ${fmt(q.sevenDayUtilizationPct)} / 7d`;
|
|
22308
|
+
}
|
|
22309
|
+
function formatResetRelative(target, now = new Date) {
|
|
22310
|
+
if (!target)
|
|
22311
|
+
return "\u2014";
|
|
22312
|
+
const deltaMs = target.getTime() - now.getTime();
|
|
22313
|
+
if (deltaMs <= 0)
|
|
22314
|
+
return "resets now";
|
|
22315
|
+
const totalMin = Math.round(deltaMs / 60000);
|
|
22316
|
+
if (totalMin < 60)
|
|
22317
|
+
return `resets in ${totalMin}m`;
|
|
22318
|
+
const hours = Math.floor(totalMin / 60);
|
|
22319
|
+
const mins = totalMin % 60;
|
|
22320
|
+
if (hours < 24)
|
|
22321
|
+
return mins > 0 ? `resets in ${hours}h ${mins}m` : `resets in ${hours}h`;
|
|
22322
|
+
const days = Math.floor(hours / 24);
|
|
22323
|
+
const remH = hours % 24;
|
|
22324
|
+
return remH > 0 ? `resets in ${days}d ${remH}h` : `resets in ${days}d`;
|
|
22325
|
+
}
|
|
22326
|
+
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";
|
|
22327
|
+
var init_quota_check = () => {};
|
|
22328
|
+
|
|
21869
22329
|
// ../node_modules/.bun/@xterm+headless@6.0.0/node_modules/@xterm/headless/lib-headless/xterm-headless.js
|
|
21870
22330
|
var require_xterm_headless = __commonJS((exports2) => {
|
|
21871
22331
|
(() => {
|
|
@@ -27427,9 +27887,9 @@ var init_flock = () => {};
|
|
|
27427
27887
|
// ../src/vault/vault.ts
|
|
27428
27888
|
import { randomBytes as randomBytes4, scryptSync, createCipheriv, createDecipheriv } from "node:crypto";
|
|
27429
27889
|
import {
|
|
27430
|
-
readFileSync as
|
|
27890
|
+
readFileSync as readFileSync18,
|
|
27431
27891
|
writeSync as writeSync2,
|
|
27432
|
-
existsSync as
|
|
27892
|
+
existsSync as existsSync16,
|
|
27433
27893
|
renameSync as renameSync6,
|
|
27434
27894
|
mkdirSync as mkdirSync16,
|
|
27435
27895
|
unlinkSync as unlinkSync10,
|
|
@@ -27470,12 +27930,12 @@ function normalizeSecrets(raw) {
|
|
|
27470
27930
|
return out;
|
|
27471
27931
|
}
|
|
27472
27932
|
function openVault(passphrase, vaultPath) {
|
|
27473
|
-
if (!
|
|
27933
|
+
if (!existsSync16(vaultPath)) {
|
|
27474
27934
|
throw new VaultError(`Vault file not found: ${vaultPath}`);
|
|
27475
27935
|
}
|
|
27476
27936
|
let vaultFile;
|
|
27477
27937
|
try {
|
|
27478
|
-
vaultFile = JSON.parse(
|
|
27938
|
+
vaultFile = JSON.parse(readFileSync18(vaultPath, "utf8"));
|
|
27479
27939
|
} catch {
|
|
27480
27940
|
throw new VaultError(`Failed to read vault file: ${vaultPath}`);
|
|
27481
27941
|
}
|
|
@@ -27529,7 +27989,7 @@ import {
|
|
|
27529
27989
|
statSync as statSync5,
|
|
27530
27990
|
writeSync as writeSync3
|
|
27531
27991
|
} from "node:fs";
|
|
27532
|
-
import { join as
|
|
27992
|
+
import { join as join23 } from "node:path";
|
|
27533
27993
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
27534
27994
|
import { constants as fsConstants } from "node:fs";
|
|
27535
27995
|
function isVaultReference(value) {
|
|
@@ -27581,11 +28041,11 @@ function materializationRoot() {
|
|
|
27581
28041
|
return cachedRoot;
|
|
27582
28042
|
const xdg = process.env.XDG_RUNTIME_DIR;
|
|
27583
28043
|
if (xdg) {
|
|
27584
|
-
const base =
|
|
28044
|
+
const base = join23(xdg, "switchroom", "vault");
|
|
27585
28045
|
mkdirSync17(base, { recursive: true, mode: 448 });
|
|
27586
|
-
cachedRoot = mkdtempSync(
|
|
28046
|
+
cachedRoot = mkdtempSync(join23(base, "run-"));
|
|
27587
28047
|
} else {
|
|
27588
|
-
cachedRoot = mkdtempSync(
|
|
28048
|
+
cachedRoot = mkdtempSync(join23(tmpdir2(), "switchroom-vault-"));
|
|
27589
28049
|
}
|
|
27590
28050
|
chmodSync5(cachedRoot, 448);
|
|
27591
28051
|
return cachedRoot;
|
|
@@ -27600,7 +28060,7 @@ function writeFileExclusive(filePath, content3) {
|
|
|
27600
28060
|
}
|
|
27601
28061
|
}
|
|
27602
28062
|
function materializeFilesEntry(key, files) {
|
|
27603
|
-
const dir =
|
|
28063
|
+
const dir = join23(materializationRoot(), key);
|
|
27604
28064
|
if (materializedDirs.has(dir)) {
|
|
27605
28065
|
try {
|
|
27606
28066
|
rmSync(dir, { recursive: true, force: true });
|
|
@@ -27616,7 +28076,7 @@ function materializeFilesEntry(key, files) {
|
|
|
27616
28076
|
if (filename.includes("/") || filename.includes("\\") || filename === ".." || filename === "." || filename.includes("\x00")) {
|
|
27617
28077
|
throw new Error(`Refusing to materialize vault file with unsafe name: ${filename}`);
|
|
27618
28078
|
}
|
|
27619
|
-
const filePath =
|
|
28079
|
+
const filePath = join23(dir, filename);
|
|
27620
28080
|
const content3 = encoding === "base64" ? Buffer.from(value, "base64") : value;
|
|
27621
28081
|
writeFileExclusive(filePath, content3);
|
|
27622
28082
|
}
|
|
@@ -28184,17 +28644,21 @@ __export(exports_history, {
|
|
|
28184
28644
|
recordEdit: () => recordEdit,
|
|
28185
28645
|
query: () => query,
|
|
28186
28646
|
pruneMessagesOlderThanDays: () => pruneMessagesOlderThanDays,
|
|
28647
|
+
normalizeDeliveryText: () => normalizeDeliveryText,
|
|
28187
28648
|
lookupMessageRoleAndText: () => lookupMessageRoleAndText,
|
|
28188
28649
|
initHistory: () => initHistory,
|
|
28650
|
+
hasOutboundWithText: () => hasOutboundWithText,
|
|
28189
28651
|
hasOutboundDeliveredSince: () => hasOutboundDeliveredSince,
|
|
28190
28652
|
getRecentOutboundCount: () => getRecentOutboundCount,
|
|
28191
28653
|
getLatestInboundMessageId: () => getLatestInboundMessageId,
|
|
28654
|
+
deliveryTextMatch: () => deliveryTextMatch,
|
|
28192
28655
|
deleteFromHistory: () => deleteFromHistory,
|
|
28193
28656
|
checkpointWal: () => checkpointWal,
|
|
28194
|
-
_resetForTests: () => _resetForTests
|
|
28657
|
+
_resetForTests: () => _resetForTests,
|
|
28658
|
+
MIN_PREFIX_MATCH_CHARS: () => MIN_PREFIX_MATCH_CHARS
|
|
28195
28659
|
});
|
|
28196
|
-
import { chmodSync as chmodSync7, existsSync as
|
|
28197
|
-
import { join as
|
|
28660
|
+
import { chmodSync as chmodSync7, existsSync as existsSync22, mkdirSync as mkdirSync21 } from "fs";
|
|
28661
|
+
import { join as join26 } from "path";
|
|
28198
28662
|
function loadDatabaseClass() {
|
|
28199
28663
|
if (DatabaseClass != null)
|
|
28200
28664
|
return DatabaseClass;
|
|
@@ -28217,7 +28681,7 @@ function initHistory(stateDir, retentionDays = 30) {
|
|
|
28217
28681
|
return;
|
|
28218
28682
|
const Database = loadDatabaseClass();
|
|
28219
28683
|
mkdirSync21(stateDir, { recursive: true, mode: 448 });
|
|
28220
|
-
const path2 =
|
|
28684
|
+
const path2 = join26(stateDir, "history.db");
|
|
28221
28685
|
dbPath = path2;
|
|
28222
28686
|
db = new Database(path2, { create: true });
|
|
28223
28687
|
db.exec("PRAGMA journal_mode = WAL");
|
|
@@ -28283,7 +28747,7 @@ function initHistory(stateDir, retentionDays = 30) {
|
|
|
28283
28747
|
}
|
|
28284
28748
|
for (const suffix of ["", "-shm", "-wal"]) {
|
|
28285
28749
|
const f = path2 + suffix;
|
|
28286
|
-
if (
|
|
28750
|
+
if (existsSync22(f)) {
|
|
28287
28751
|
try {
|
|
28288
28752
|
chmodSync7(f, 420);
|
|
28289
28753
|
} catch {}
|
|
@@ -28308,7 +28772,7 @@ function checkpointWal() {
|
|
|
28308
28772
|
if (dbPath) {
|
|
28309
28773
|
for (const suffix of ["-shm", "-wal"]) {
|
|
28310
28774
|
const f = dbPath + suffix;
|
|
28311
|
-
if (
|
|
28775
|
+
if (existsSync22(f)) {
|
|
28312
28776
|
try {
|
|
28313
28777
|
chmodSync7(f, 420);
|
|
28314
28778
|
} catch {}
|
|
@@ -28447,6 +28911,50 @@ function hasOutboundDeliveredSince(chatId, sinceMs, threadId, minChars = 200) {
|
|
|
28447
28911
|
return false;
|
|
28448
28912
|
}
|
|
28449
28913
|
}
|
|
28914
|
+
function hasOutboundWithText(chatId, text4, threadId, sinceMs) {
|
|
28915
|
+
const needle = normalizeDeliveryText(text4);
|
|
28916
|
+
if (needle.length === 0)
|
|
28917
|
+
return false;
|
|
28918
|
+
try {
|
|
28919
|
+
const params = [chatId];
|
|
28920
|
+
let sql = "SELECT text FROM messages WHERE chat_id = ? AND role = 'assistant'";
|
|
28921
|
+
if (threadId !== undefined) {
|
|
28922
|
+
if (threadId === null) {
|
|
28923
|
+
sql += " AND thread_id IS NULL";
|
|
28924
|
+
} else {
|
|
28925
|
+
sql += " AND thread_id = ?";
|
|
28926
|
+
params.push(threadId);
|
|
28927
|
+
}
|
|
28928
|
+
}
|
|
28929
|
+
if (sinceMs != null && Number.isFinite(sinceMs)) {
|
|
28930
|
+
sql += " AND ts >= ?";
|
|
28931
|
+
params.push(Math.floor(sinceMs / 1000));
|
|
28932
|
+
}
|
|
28933
|
+
sql += " ORDER BY ts DESC LIMIT 500";
|
|
28934
|
+
const rows = requireDb().prepare(sql).all(...params);
|
|
28935
|
+
for (const r of rows) {
|
|
28936
|
+
const hay = normalizeDeliveryText(r.text ?? "");
|
|
28937
|
+
if (hay.length === 0)
|
|
28938
|
+
continue;
|
|
28939
|
+
if (deliveryTextMatch(hay, needle))
|
|
28940
|
+
return true;
|
|
28941
|
+
}
|
|
28942
|
+
return false;
|
|
28943
|
+
} catch {
|
|
28944
|
+
return false;
|
|
28945
|
+
}
|
|
28946
|
+
}
|
|
28947
|
+
function normalizeDeliveryText(text4) {
|
|
28948
|
+
return text4.replace(/\s+/g, " ").trim();
|
|
28949
|
+
}
|
|
28950
|
+
function deliveryTextMatch(hay, needle) {
|
|
28951
|
+
if (hay === needle)
|
|
28952
|
+
return true;
|
|
28953
|
+
const shorter = Math.min(hay.length, needle.length);
|
|
28954
|
+
if (shorter < MIN_PREFIX_MATCH_CHARS)
|
|
28955
|
+
return false;
|
|
28956
|
+
return hay.startsWith(needle) || needle.startsWith(hay);
|
|
28957
|
+
}
|
|
28450
28958
|
function query(opts) {
|
|
28451
28959
|
const limit = Math.min(MAX_LIMIT, Math.max(1, opts.limit ?? DEFAULT_LIMIT));
|
|
28452
28960
|
const params = [opts.chat_id];
|
|
@@ -28469,147 +28977,11 @@ function query(opts) {
|
|
|
28469
28977
|
rows.reverse();
|
|
28470
28978
|
return rows;
|
|
28471
28979
|
}
|
|
28472
|
-
var DatabaseClass = null, DEFAULT_LIMIT = 10, MAX_LIMIT = 50, db = null, dbPath = null;
|
|
28980
|
+
var DatabaseClass = null, DEFAULT_LIMIT = 10, MAX_LIMIT = 50, db = null, dbPath = null, MIN_PREFIX_MATCH_CHARS = 40;
|
|
28473
28981
|
var init_history = __esm(() => {
|
|
28474
28982
|
init_redact();
|
|
28475
28983
|
});
|
|
28476
28984
|
|
|
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
28985
|
// ../src/util/atomic.ts
|
|
28614
28986
|
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
28987
|
var TMP_OPEN_FLAGS;
|
|
@@ -31573,7 +31945,7 @@ var require_util = __commonJS((exports2) => {
|
|
|
31573
31945
|
return path2;
|
|
31574
31946
|
}
|
|
31575
31947
|
exports2.normalize = normalize;
|
|
31576
|
-
function
|
|
31948
|
+
function join32(aRoot, aPath) {
|
|
31577
31949
|
if (aRoot === "") {
|
|
31578
31950
|
aRoot = ".";
|
|
31579
31951
|
}
|
|
@@ -31605,7 +31977,7 @@ var require_util = __commonJS((exports2) => {
|
|
|
31605
31977
|
}
|
|
31606
31978
|
return joined;
|
|
31607
31979
|
}
|
|
31608
|
-
exports2.join =
|
|
31980
|
+
exports2.join = join32;
|
|
31609
31981
|
exports2.isAbsolute = function(aPath) {
|
|
31610
31982
|
return aPath.charAt(0) === "/" || urlRegexp.test(aPath);
|
|
31611
31983
|
};
|
|
@@ -31778,7 +32150,7 @@ var require_util = __commonJS((exports2) => {
|
|
|
31778
32150
|
parsed.path = parsed.path.substring(0, index2 + 1);
|
|
31779
32151
|
}
|
|
31780
32152
|
}
|
|
31781
|
-
sourceURL =
|
|
32153
|
+
sourceURL = join32(urlGenerate(parsed), sourceURL);
|
|
31782
32154
|
}
|
|
31783
32155
|
return normalize(sourceURL);
|
|
31784
32156
|
}
|
|
@@ -34384,9 +34756,9 @@ function renderAuthLine(state4, agentName3, now = Date.now()) {
|
|
|
34384
34756
|
|
|
34385
34757
|
// gateway/quota-cache.ts
|
|
34386
34758
|
import { existsSync as existsSync37, readFileSync as readFileSync39, writeFileSync as writeFileSync31, mkdirSync as mkdirSync30 } from "fs";
|
|
34387
|
-
import { join as
|
|
34759
|
+
import { join as join42, dirname as dirname11 } from "path";
|
|
34388
34760
|
function defaultCachePath() {
|
|
34389
|
-
return process.env.SWITCHROOM_QUOTA_CACHE_PATH ??
|
|
34761
|
+
return process.env.SWITCHROOM_QUOTA_CACHE_PATH ?? join42(process.env.HOME ?? "/tmp", ".switchroom", "quota-cache.json");
|
|
34390
34762
|
}
|
|
34391
34763
|
function readQuotaCache(opts = {}) {
|
|
34392
34764
|
const path2 = opts.path ?? defaultCachePath();
|
|
@@ -34433,7 +34805,7 @@ var init_quota_cache = __esm(() => {
|
|
|
34433
34805
|
|
|
34434
34806
|
// gateway/boot-probes.ts
|
|
34435
34807
|
import { readFileSync as readFileSync40, readdirSync as readdirSync8, existsSync as existsSync38 } from "fs";
|
|
34436
|
-
import { join as
|
|
34808
|
+
import { join as join43 } from "path";
|
|
34437
34809
|
import { execFile as execFileCb } from "child_process";
|
|
34438
34810
|
import { promisify } from "util";
|
|
34439
34811
|
async function withTimeout(label, p, timeoutMs = PROBE_TIMEOUT_MS) {
|
|
@@ -34475,8 +34847,8 @@ function mapPlan(billingType, hasExtra) {
|
|
|
34475
34847
|
}
|
|
34476
34848
|
async function probeAccount(agentDir) {
|
|
34477
34849
|
return withTimeout("Account", (async () => {
|
|
34478
|
-
const claudeDir =
|
|
34479
|
-
const claudeJsonPath =
|
|
34850
|
+
const claudeDir = join43(agentDir, ".claude");
|
|
34851
|
+
const claudeJsonPath = join43(claudeDir, ".claude.json");
|
|
34480
34852
|
let cfg = {};
|
|
34481
34853
|
try {
|
|
34482
34854
|
const raw = readFileSync40(claudeJsonPath, "utf8");
|
|
@@ -34497,8 +34869,8 @@ async function probeAccount(agentDir) {
|
|
|
34497
34869
|
let tokenStr = "";
|
|
34498
34870
|
let status = "ok";
|
|
34499
34871
|
for (const candidate of [
|
|
34500
|
-
|
|
34501
|
-
|
|
34872
|
+
join43(claudeDir, ".oauth-token.meta.json"),
|
|
34873
|
+
join43(claudeDir, "accounts", "default", ".oauth-token.meta.json")
|
|
34502
34874
|
]) {
|
|
34503
34875
|
if (existsSync38(candidate)) {
|
|
34504
34876
|
try {
|
|
@@ -34884,9 +35256,9 @@ async function probeQuota(claudeConfigDir, _agentDir, fetchImpl = fetch, opts =
|
|
|
34884
35256
|
let claudeDirForProbe = null;
|
|
34885
35257
|
for (const candidate of [
|
|
34886
35258
|
claudeConfigDir,
|
|
34887
|
-
|
|
35259
|
+
join43(claudeConfigDir, "accounts", "default")
|
|
34888
35260
|
]) {
|
|
34889
|
-
if (existsSync38(
|
|
35261
|
+
if (existsSync38(join43(candidate, ".oauth-token"))) {
|
|
34890
35262
|
claudeDirForProbe = candidate;
|
|
34891
35263
|
break;
|
|
34892
35264
|
}
|
|
@@ -35121,7 +35493,7 @@ async function probeSkills(agentDir, opts = {}) {
|
|
|
35121
35493
|
return withTimeout("Skills", (async () => {
|
|
35122
35494
|
const fs2 = opts.fs ?? realSkillsFs;
|
|
35123
35495
|
const max = opts.maxNamesShown ?? 3;
|
|
35124
|
-
const skillsDir =
|
|
35496
|
+
const skillsDir = join43(agentDir, ".claude", "skills");
|
|
35125
35497
|
if (!fs2.exists(skillsDir)) {
|
|
35126
35498
|
return { status: "ok", label: "Skills", detail: "no skills dir" };
|
|
35127
35499
|
}
|
|
@@ -35136,17 +35508,17 @@ async function probeSkills(agentDir, opts = {}) {
|
|
|
35136
35508
|
}
|
|
35137
35509
|
const dangling = [];
|
|
35138
35510
|
for (const name of entries) {
|
|
35139
|
-
const skillPath =
|
|
35511
|
+
const skillPath = join43(skillsDir, name);
|
|
35140
35512
|
if (!fs2.exists(skillPath)) {
|
|
35141
35513
|
dangling.push(name);
|
|
35142
35514
|
continue;
|
|
35143
35515
|
}
|
|
35144
|
-
const skillMd =
|
|
35516
|
+
const skillMd = join43(skillPath, "SKILL.md");
|
|
35145
35517
|
if (!fs2.exists(skillMd) && !fs2.exists(skillPath + ".md")) {
|
|
35146
35518
|
continue;
|
|
35147
35519
|
}
|
|
35148
35520
|
}
|
|
35149
|
-
const overlayDir = opts.overlaySkillsDir ??
|
|
35521
|
+
const overlayDir = opts.overlaySkillsDir ?? join43(agentDir, "skills.d");
|
|
35150
35522
|
const overlaySlugs = new Set;
|
|
35151
35523
|
if (fs2.exists(overlayDir)) {
|
|
35152
35524
|
let overlayEntries = [];
|
|
@@ -35188,7 +35560,7 @@ function renderBucketedSkills(switchroom, agent) {
|
|
|
35188
35560
|
}
|
|
35189
35561
|
async function probeConnections(agentDir, opts = {}) {
|
|
35190
35562
|
return withTimeout("Connections", (async () => {
|
|
35191
|
-
const path2 =
|
|
35563
|
+
const path2 = join43(agentDir, ".claude", "connection-health.json");
|
|
35192
35564
|
const read = opts.readFileImpl ?? ((p) => readFileSync40(p, "utf8"));
|
|
35193
35565
|
let issues = [];
|
|
35194
35566
|
try {
|
|
@@ -35530,7 +35902,7 @@ __export(exports_boot_card, {
|
|
|
35530
35902
|
renderBootCard: () => renderBootCard,
|
|
35531
35903
|
renderAccountRows: () => renderAuthLine
|
|
35532
35904
|
});
|
|
35533
|
-
import { join as
|
|
35905
|
+
import { join as join44 } from "path";
|
|
35534
35906
|
function resolvePersonaName(slug, loadConfig3) {
|
|
35535
35907
|
try {
|
|
35536
35908
|
const config = loadConfig3 ? loadConfig3() : loadConfig();
|
|
@@ -35621,7 +35993,7 @@ function renderBootCard(opts) {
|
|
|
35621
35993
|
return stackCardLines(flatLines);
|
|
35622
35994
|
}
|
|
35623
35995
|
async function runAllProbes(opts) {
|
|
35624
|
-
const claudeDir =
|
|
35996
|
+
const claudeDir = join44(opts.agentDir, ".claude");
|
|
35625
35997
|
const probes = {};
|
|
35626
35998
|
const slug = opts.agentSlug ?? opts.agentName;
|
|
35627
35999
|
await Promise.allSettled([
|
|
@@ -37300,8 +37672,8 @@ import {
|
|
|
37300
37672
|
unlinkSync as unlinkSync24,
|
|
37301
37673
|
appendFileSync as appendFileSync6
|
|
37302
37674
|
} from "fs";
|
|
37303
|
-
import { homedir as
|
|
37304
|
-
import { join as
|
|
37675
|
+
import { homedir as homedir18 } from "os";
|
|
37676
|
+
import { join as join55, extname, sep as sep3, basename as basename13 } from "path";
|
|
37305
37677
|
|
|
37306
37678
|
// plugin-logger.ts
|
|
37307
37679
|
import { appendFileSync, mkdirSync, renameSync, statSync, existsSync } from "fs";
|
|
@@ -40211,16 +40583,34 @@ function clipNarrative(s) {
|
|
|
40211
40583
|
return s.split(`
|
|
40212
40584
|
`)[0].trim().slice(0, STATUS_LINE_MAX);
|
|
40213
40585
|
}
|
|
40214
|
-
function renderActivityHeader(emoji, label, description, elapsedMs, toolCount, state, model) {
|
|
40586
|
+
function renderActivityHeader(emoji, label, description, elapsedMs, toolCount, state, model, totalTokens) {
|
|
40215
40587
|
const toolWord = toolCount === 1 ? "tool" : "tools";
|
|
40216
40588
|
const elapsed = formatFeedElapsed(elapsedMs);
|
|
40217
40589
|
const descPart = description.length > 0 ? ` \u00b7 _${escapeMarkdown(description)}_` : "";
|
|
40218
40590
|
const line1 = `${emoji} **${escapeMarkdown(label)}**${descPart}`;
|
|
40591
|
+
const tokPart = tokenSegment(totalTokens);
|
|
40219
40592
|
const modelLabel = formatModelLabel(model);
|
|
40220
40593
|
const modelPart = modelLabel != null ? ` \u00b7 ${escapeMarkdown(modelLabel)}` : "";
|
|
40221
|
-
const line2 = state === "running" ? `_${elapsed} \u00b7 ${toolCount} ${toolWord}${modelPart}_` : `_${state} \u00b7 ${toolCount} ${toolWord} \u00b7 ${elapsed}${modelPart}_`;
|
|
40594
|
+
const line2 = state === "running" ? `_${elapsed} \u00b7 ${toolCount} ${toolWord}${tokPart}${modelPart}_` : `_${state} \u00b7 ${toolCount} ${toolWord}${tokPart} \u00b7 ${elapsed}${modelPart}_`;
|
|
40222
40595
|
return [line1, line2];
|
|
40223
40596
|
}
|
|
40597
|
+
function formatTokenCount(n) {
|
|
40598
|
+
if (!Number.isFinite(n) || n <= 0)
|
|
40599
|
+
return "0";
|
|
40600
|
+
if (n < 1000)
|
|
40601
|
+
return String(Math.floor(n));
|
|
40602
|
+
if (n < 1e6) {
|
|
40603
|
+
const k = Number((n / 1000).toFixed(1));
|
|
40604
|
+
if (k < 1000)
|
|
40605
|
+
return `${k.toFixed(1)}k`;
|
|
40606
|
+
}
|
|
40607
|
+
return `${(n / 1e6).toFixed(1)}M`;
|
|
40608
|
+
}
|
|
40609
|
+
function tokenSegment(totalTokens) {
|
|
40610
|
+
if (totalTokens == null || totalTokens <= 0)
|
|
40611
|
+
return "";
|
|
40612
|
+
return ` \u00b7 ${formatTokenCount(totalTokens)} tok`;
|
|
40613
|
+
}
|
|
40224
40614
|
function formatFeedElapsed(ms) {
|
|
40225
40615
|
const s = Math.floor(ms / 1000);
|
|
40226
40616
|
if (s < 60)
|
|
@@ -40257,7 +40647,7 @@ function renderStatusCard(opts) {
|
|
|
40257
40647
|
const hasChildren = rawChildren.length > 0;
|
|
40258
40648
|
const steps = rawSteps.map(escapeStepLine);
|
|
40259
40649
|
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) : [];
|
|
40650
|
+
const headerLines = header != null ? renderActivityHeader(header.emoji, header.label, header.description ?? "", header.elapsedMs, header.toolCount, header.state, header.model, header.totalTokens) : [];
|
|
40261
40651
|
const out = [...headerLines];
|
|
40262
40652
|
if (hasChildren) {
|
|
40263
40653
|
const shownParent = steps.slice(-STATUS_ROLLING_LINES);
|
|
@@ -40354,7 +40744,8 @@ function renderActivityFeed(lines, final = false, liveSuffix = "", stepCount, he
|
|
|
40354
40744
|
elapsedMs: header.elapsedMs,
|
|
40355
40745
|
toolCount: header.toolCount,
|
|
40356
40746
|
state: header.state,
|
|
40357
|
-
model: header.model
|
|
40747
|
+
model: header.model,
|
|
40748
|
+
totalTokens: header.totalTokens
|
|
40358
40749
|
} : undefined,
|
|
40359
40750
|
steps: lines,
|
|
40360
40751
|
final,
|
|
@@ -40373,7 +40764,8 @@ function renderActivityFeedWithNested(lines, childLines, final = false, liveSuff
|
|
|
40373
40764
|
elapsedMs: header.elapsedMs,
|
|
40374
40765
|
toolCount: header.toolCount,
|
|
40375
40766
|
state: header.state,
|
|
40376
|
-
model: header.model
|
|
40767
|
+
model: header.model,
|
|
40768
|
+
totalTokens: header.totalTokens
|
|
40377
40769
|
} : undefined,
|
|
40378
40770
|
steps: lines,
|
|
40379
40771
|
childSteps: children,
|
|
@@ -40398,9 +40790,10 @@ function renderCombinedWorkerFeed(rows, opts) {
|
|
|
40398
40790
|
const rowHeader = (r) => {
|
|
40399
40791
|
const desc = escapeMarkdown(truncate(stripMarkdown(r.description).replace(/\s+/g, " ").trim() || "background task", COMBINED_ROW_DESC_MAX));
|
|
40400
40792
|
const toolWord = r.toolCount === 1 ? "tool" : "tools";
|
|
40793
|
+
const tokPart = tokenSegment(r.totalTokens);
|
|
40401
40794
|
const modelLabel = formatModelLabel(r.model);
|
|
40402
40795
|
const modelPart = modelLabel != null ? ` \u00b7 ${escapeMarkdown(modelLabel)}` : "";
|
|
40403
|
-
return `**${desc}** _\u00b7 ${formatFeedElapsed(r.elapsedMs)} \u00b7 ${r.toolCount} ${toolWord}${modelPart}_`;
|
|
40796
|
+
return `**${desc}** _\u00b7 ${formatFeedElapsed(r.elapsedMs)} \u00b7 ${r.toolCount} ${toolWord}${tokPart}${modelPart}_`;
|
|
40404
40797
|
};
|
|
40405
40798
|
const rowHistory = (r) => {
|
|
40406
40799
|
const src = r.historyLines != null && r.historyLines.length > 0 ? r.historyLines : [r.currentStep];
|
|
@@ -40511,7 +40904,8 @@ function renderWorkerActivity(v, liveSuffix = "") {
|
|
|
40511
40904
|
elapsedMs: v.elapsedMs,
|
|
40512
40905
|
toolCount: v.toolCount,
|
|
40513
40906
|
state: v.state,
|
|
40514
|
-
model: v.model
|
|
40907
|
+
model: v.model,
|
|
40908
|
+
totalTokens: v.totalTokens
|
|
40515
40909
|
};
|
|
40516
40910
|
let result;
|
|
40517
40911
|
if (finished && v.state !== "incomplete") {
|
|
@@ -40536,6 +40930,9 @@ _starting\u2026_`;
|
|
|
40536
40930
|
return card;
|
|
40537
40931
|
}
|
|
40538
40932
|
var COOLDOWN_JITTER_MS = 500;
|
|
40933
|
+
var WORKER_CARD_SUPERSEDED_BODY = `\uD83D\uDEE0 **Worker** \u00b7 _continued_
|
|
40934
|
+
|
|
40935
|
+
_Live progress moved to a fresh card to stay pinned._`;
|
|
40539
40936
|
function extractRetryAfterSecs(err) {
|
|
40540
40937
|
if (err == null || typeof err !== "object")
|
|
40541
40938
|
return null;
|
|
@@ -40570,6 +40967,7 @@ function createWorkerActivityFeed(opts) {
|
|
|
40570
40967
|
const maxRows = Math.max(1, Math.floor(opts.maxRows ?? 8));
|
|
40571
40968
|
const staleWorkerTtlMs = Math.max(1, Math.floor(opts.staleWorkerTtlMs ?? 50 * 60000));
|
|
40572
40969
|
const absoluteRowLifetimeCapMs = Math.max(1, Math.floor(opts.absoluteRowLifetimeCapMs ?? 6 * 60 * 60000));
|
|
40970
|
+
const groupMessageLifetimeCapMs = Math.max(1, Math.floor(opts.groupMessageLifetimeCapMs ?? 60 * 60000));
|
|
40573
40971
|
const reconcilePinFn = opts.reconcilePin ?? (() => {});
|
|
40574
40972
|
const setIntervalFn = opts.setInterval ?? ((cb, ms) => {
|
|
40575
40973
|
const t = setInterval(cb, ms);
|
|
@@ -40670,6 +41068,7 @@ function createWorkerActivityFeed(opts) {
|
|
|
40670
41068
|
description: v.description,
|
|
40671
41069
|
elapsedMs: elapsedFor(r),
|
|
40672
41070
|
toolCount: v.toolCount,
|
|
41071
|
+
totalTokens: v.totalTokens,
|
|
40673
41072
|
currentStep,
|
|
40674
41073
|
historyLines: r.narrative.length > 0 ? [...r.narrative] : undefined,
|
|
40675
41074
|
model: v.model
|
|
@@ -40747,6 +41146,7 @@ function createWorkerActivityFeed(opts) {
|
|
|
40747
41146
|
return;
|
|
40748
41147
|
}
|
|
40749
41148
|
g.messageId = sent.message_id;
|
|
41149
|
+
g.messageCreatedAtMs = now;
|
|
40750
41150
|
g.lastBody = body;
|
|
40751
41151
|
g.lastEditAt = now;
|
|
40752
41152
|
g.terminalPainted = false;
|
|
@@ -40777,6 +41177,7 @@ function createWorkerActivityFeed(opts) {
|
|
|
40777
41177
|
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
41178
|
} else {
|
|
40779
41179
|
log(`worker-feed: edit feed=${g.feedKey} chat=${g.chatId} ` + `thread=${g.threadId ?? "-"} msgId=${g.messageId} workers=${g.workers.size} bytes=${body.length}`);
|
|
41180
|
+
syncPin(g);
|
|
40780
41181
|
}
|
|
40781
41182
|
if (isTerminal)
|
|
40782
41183
|
clearStaged();
|
|
@@ -40795,6 +41196,7 @@ function createWorkerActivityFeed(opts) {
|
|
|
40795
41196
|
}
|
|
40796
41197
|
if (outcome === "gone") {
|
|
40797
41198
|
g.messageId = null;
|
|
41199
|
+
g.messageCreatedAtMs = 0;
|
|
40798
41200
|
g.lastBody = null;
|
|
40799
41201
|
if (isTerminal)
|
|
40800
41202
|
clearStaged();
|
|
@@ -40838,6 +41240,7 @@ function createWorkerActivityFeed(opts) {
|
|
|
40838
41240
|
description: lv?.description ?? "background task",
|
|
40839
41241
|
lastTool: null,
|
|
40840
41242
|
toolCount: lv?.toolCount ?? 0,
|
|
41243
|
+
totalTokens: lv?.totalTokens,
|
|
40841
41244
|
latestSummary: "",
|
|
40842
41245
|
elapsedMs: liveElapsed(row, nowFn()),
|
|
40843
41246
|
state: "incomplete",
|
|
@@ -40897,6 +41300,16 @@ function createWorkerActivityFeed(opts) {
|
|
|
40897
41300
|
const running = runningRows(g);
|
|
40898
41301
|
if (running.length === 0)
|
|
40899
41302
|
continue;
|
|
41303
|
+
if (g.messageId != null && now - g.messageCreatedAtMs >= groupMessageLifetimeCapMs) {
|
|
41304
|
+
const age = Math.floor((now - g.messageCreatedAtMs) / 1000);
|
|
41305
|
+
const retiredId = g.messageId;
|
|
41306
|
+
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`);
|
|
41307
|
+
g.messageId = null;
|
|
41308
|
+
g.messageCreatedAtMs = 0;
|
|
41309
|
+
g.lastBody = null;
|
|
41310
|
+
syncPin(g);
|
|
41311
|
+
opts.bot.editMessageText(g.chatId, retiredId, WORKER_CARD_SUPERSEDED_BODY, sendOptsFor(g)).catch(() => {});
|
|
41312
|
+
}
|
|
40900
41313
|
if (g.messageId == null) {
|
|
40901
41314
|
const maxElapsed = Math.max(0, ...running.map((r) => liveElapsed(r, now)));
|
|
40902
41315
|
if (maxElapsed < firstPaintMin)
|
|
@@ -40951,6 +41364,7 @@ function createWorkerActivityFeed(opts) {
|
|
|
40951
41364
|
chatId,
|
|
40952
41365
|
threadId,
|
|
40953
41366
|
messageId: null,
|
|
41367
|
+
messageCreatedAtMs: 0,
|
|
40954
41368
|
lastBody: null,
|
|
40955
41369
|
lastEditAt: 0,
|
|
40956
41370
|
cooldownUntil: 0,
|
|
@@ -40963,6 +41377,7 @@ function createWorkerActivityFeed(opts) {
|
|
|
40963
41377
|
}
|
|
40964
41378
|
if (!g.workers.has(agentId) && g.terminalPainted && !hasLiveWorker(g)) {
|
|
40965
41379
|
g.messageId = null;
|
|
41380
|
+
g.messageCreatedAtMs = 0;
|
|
40966
41381
|
g.lastBody = null;
|
|
40967
41382
|
g.pendingFinalize.clear();
|
|
40968
41383
|
g.terminalPainted = false;
|
|
@@ -41889,8 +42304,18 @@ function buildVaultGrantDeniedInbound(opts) {
|
|
|
41889
42304
|
}
|
|
41890
42305
|
};
|
|
41891
42306
|
}
|
|
42307
|
+
var MAX_GRANT_REASON_CHARS = 300;
|
|
42308
|
+
function normalizeGrantReason(raw) {
|
|
42309
|
+
if (raw == null)
|
|
42310
|
+
return "";
|
|
42311
|
+
const collapsed = raw.replace(/\s+/g, " ").trim();
|
|
42312
|
+
if (collapsed.length === 0)
|
|
42313
|
+
return "";
|
|
42314
|
+
return collapsed.length > MAX_GRANT_REASON_CHARS ? collapsed.slice(0, MAX_GRANT_REASON_CHARS - 1) + "\u2026" : collapsed;
|
|
42315
|
+
}
|
|
41892
42316
|
function buildVaultGrantApprovedCardText(opts) {
|
|
41893
|
-
|
|
42317
|
+
const reasonClause = opts.reasonEscaped != null && opts.reasonEscaped.length > 0 ? ` _Reason: ${opts.reasonEscaped}_` : "";
|
|
42318
|
+
return `\u2705 Granted **${opts.agentEscaped}** ${opts.scope} access to ` + `\`${opts.key}\` for ${opts.days}d. ` + `(grant \`${opts.grantId}\`)` + reasonClause + (opts.footer ?? "");
|
|
41894
42319
|
}
|
|
41895
42320
|
function buildVaultSaveCompletedInbound(opts) {
|
|
41896
42321
|
const ts = opts.nowMs ?? Date.now();
|
|
@@ -43163,6 +43588,7 @@ function createCallbackQueryHandlers(deps) {
|
|
|
43163
43588
|
pendingCardStore.remove(stageId);
|
|
43164
43589
|
if (pending.card_message_id != null) {
|
|
43165
43590
|
const days = Math.round(pending.ttl_seconds / 86400);
|
|
43591
|
+
const reasonNormalized = normalizeGrantReason(pending.reason);
|
|
43166
43592
|
const footer = getVaultApprovalAuthMode() === "telegram-id" ? `
|
|
43167
43593
|
_Approver verified by Telegram identity \u2014 broker auto-unlocked at startup._` : "";
|
|
43168
43594
|
await ctx.api.editMessageText(pending.chat_id, pending.card_message_id, richMessage(buildVaultGrantApprovedCardText({
|
|
@@ -43171,6 +43597,7 @@ _Approver verified by Telegram identity \u2014 broker auto-unlocked at startup._
|
|
|
43171
43597
|
key: pending.key,
|
|
43172
43598
|
days,
|
|
43173
43599
|
grantId: id,
|
|
43600
|
+
reasonEscaped: reasonNormalized.length > 0 ? escapeHtmlForTg2(reasonNormalized) : undefined,
|
|
43174
43601
|
footer
|
|
43175
43602
|
})), { reply_markup: { inline_keyboard: [] } }).catch(() => {});
|
|
43176
43603
|
}
|
|
@@ -45035,16 +45462,34 @@ function clipNarrative2(s) {
|
|
|
45035
45462
|
return s.split(`
|
|
45036
45463
|
`)[0].trim().slice(0, STATUS_LINE_MAX);
|
|
45037
45464
|
}
|
|
45038
|
-
function renderActivityHeader2(emoji, label, description, elapsedMs, toolCount, state, model) {
|
|
45465
|
+
function renderActivityHeader2(emoji, label, description, elapsedMs, toolCount, state, model, totalTokens) {
|
|
45039
45466
|
const toolWord = toolCount === 1 ? "tool" : "tools";
|
|
45040
45467
|
const elapsed = formatFeedElapsed2(elapsedMs);
|
|
45041
45468
|
const descPart = description.length > 0 ? ` \u00b7 _${escapeMarkdown(description)}_` : "";
|
|
45042
45469
|
const line1 = `${emoji} **${escapeMarkdown(label)}**${descPart}`;
|
|
45470
|
+
const tokPart = tokenSegment2(totalTokens);
|
|
45043
45471
|
const modelLabel = formatModelLabel(model);
|
|
45044
45472
|
const modelPart = modelLabel != null ? ` \u00b7 ${escapeMarkdown(modelLabel)}` : "";
|
|
45045
|
-
const line2 = state === "running" ? `_${elapsed} \u00b7 ${toolCount} ${toolWord}${modelPart}_` : `_${state} \u00b7 ${toolCount} ${toolWord} \u00b7 ${elapsed}${modelPart}_`;
|
|
45473
|
+
const line2 = state === "running" ? `_${elapsed} \u00b7 ${toolCount} ${toolWord}${tokPart}${modelPart}_` : `_${state} \u00b7 ${toolCount} ${toolWord}${tokPart} \u00b7 ${elapsed}${modelPart}_`;
|
|
45046
45474
|
return [line1, line2];
|
|
45047
45475
|
}
|
|
45476
|
+
function formatTokenCount2(n) {
|
|
45477
|
+
if (!Number.isFinite(n) || n <= 0)
|
|
45478
|
+
return "0";
|
|
45479
|
+
if (n < 1000)
|
|
45480
|
+
return String(Math.floor(n));
|
|
45481
|
+
if (n < 1e6) {
|
|
45482
|
+
const k = Number((n / 1000).toFixed(1));
|
|
45483
|
+
if (k < 1000)
|
|
45484
|
+
return `${k.toFixed(1)}k`;
|
|
45485
|
+
}
|
|
45486
|
+
return `${(n / 1e6).toFixed(1)}M`;
|
|
45487
|
+
}
|
|
45488
|
+
function tokenSegment2(totalTokens) {
|
|
45489
|
+
if (totalTokens == null || totalTokens <= 0)
|
|
45490
|
+
return "";
|
|
45491
|
+
return ` \u00b7 ${formatTokenCount2(totalTokens)} tok`;
|
|
45492
|
+
}
|
|
45048
45493
|
function formatFeedElapsed2(ms) {
|
|
45049
45494
|
const s = Math.floor(ms / 1000);
|
|
45050
45495
|
if (s < 60)
|
|
@@ -45081,7 +45526,7 @@ function renderStatusCard2(opts) {
|
|
|
45081
45526
|
const hasChildren = rawChildren.length > 0;
|
|
45082
45527
|
const steps = rawSteps.map(escapeStepLine2);
|
|
45083
45528
|
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) : [];
|
|
45529
|
+
const headerLines = header != null ? renderActivityHeader2(header.emoji, header.label, header.description ?? "", header.elapsedMs, header.toolCount, header.state, header.model, header.totalTokens) : [];
|
|
45085
45530
|
const out = [...headerLines];
|
|
45086
45531
|
if (hasChildren) {
|
|
45087
45532
|
const shownParent = steps.slice(-STATUS_ROLLING_LINES);
|
|
@@ -45178,7 +45623,8 @@ function renderActivityFeed2(lines, final = false, liveSuffix = "", stepCount, h
|
|
|
45178
45623
|
elapsedMs: header.elapsedMs,
|
|
45179
45624
|
toolCount: header.toolCount,
|
|
45180
45625
|
state: header.state,
|
|
45181
|
-
model: header.model
|
|
45626
|
+
model: header.model,
|
|
45627
|
+
totalTokens: header.totalTokens
|
|
45182
45628
|
} : undefined,
|
|
45183
45629
|
steps: lines,
|
|
45184
45630
|
final,
|
|
@@ -45197,7 +45643,8 @@ function renderActivityFeedWithNested2(lines, childLines, final = false, liveSuf
|
|
|
45197
45643
|
elapsedMs: header.elapsedMs,
|
|
45198
45644
|
toolCount: header.toolCount,
|
|
45199
45645
|
state: header.state,
|
|
45200
|
-
model: header.model
|
|
45646
|
+
model: header.model,
|
|
45647
|
+
totalTokens: header.totalTokens
|
|
45201
45648
|
} : undefined,
|
|
45202
45649
|
steps: lines,
|
|
45203
45650
|
childSteps: children,
|
|
@@ -62872,6 +63319,597 @@ function resolveAnswerLaneConfig(input) {
|
|
|
62872
63319
|
};
|
|
62873
63320
|
}
|
|
62874
63321
|
|
|
63322
|
+
// session-tail.ts
|
|
63323
|
+
import { homedir as homedir7 } from "os";
|
|
63324
|
+
import { basename as basename5, join as join21 } from "path";
|
|
63325
|
+
|
|
63326
|
+
// operator-events.ts
|
|
63327
|
+
init_format();
|
|
63328
|
+
|
|
63329
|
+
// raw-error-scrub.ts
|
|
63330
|
+
function stripRawErrorBytes(raw) {
|
|
63331
|
+
if (typeof raw !== "string" || raw.length === 0)
|
|
63332
|
+
return "";
|
|
63333
|
+
let s = raw;
|
|
63334
|
+
s = s.replace(/API Error:?\s*\d*\s*/gi, " ");
|
|
63335
|
+
s = s.replace(/\bb'[^']*'/g, " ");
|
|
63336
|
+
s = s.replace(/\bb"[^"]*"/g, " ");
|
|
63337
|
+
s = s.replace(/[\u00b7\-\s]*\{[\s\S]*?["']type["']\s*:\s*["']error["'][\s\S]*$/i, " ");
|
|
63338
|
+
s = s.replace(/^\s*\{[\s\S]*\}\s*$/g, " ");
|
|
63339
|
+
s = s.replace(/\s+/g, " ").replace(/[\u00b7:\-\s]+$/g, "").replace(/^[\u00b7:\-\s]+/g, "").trim();
|
|
63340
|
+
return s;
|
|
63341
|
+
}
|
|
63342
|
+
function extractRequestId(raw) {
|
|
63343
|
+
if (typeof raw !== "string" || raw.length === 0)
|
|
63344
|
+
return;
|
|
63345
|
+
const m = raw.match(/["']request[_-]?id["']\s*:\s*["']([A-Za-z0-9._-]+)["']/i) ?? raw.match(/\brequest[_-]?id[=:]\s*([A-Za-z0-9._-]+)/i);
|
|
63346
|
+
return m ? m[1] : undefined;
|
|
63347
|
+
}
|
|
63348
|
+
|
|
63349
|
+
// operator-events.ts
|
|
63350
|
+
function classifyClaudeError(raw) {
|
|
63351
|
+
try {
|
|
63352
|
+
return classifyInner(raw);
|
|
63353
|
+
} catch {
|
|
63354
|
+
return "unknown-4xx";
|
|
63355
|
+
}
|
|
63356
|
+
}
|
|
63357
|
+
function classifyInner(raw) {
|
|
63358
|
+
if (raw == null)
|
|
63359
|
+
return "unknown-4xx";
|
|
63360
|
+
const obj = typeof raw === "object" ? raw : {};
|
|
63361
|
+
const errorType = extractString(obj, "error_type") ?? extractString(obj, "type") ?? extractString(getNestedObj(obj, "error"), "type") ?? "";
|
|
63362
|
+
const errorCode = extractString(obj, "code") ?? extractString(getNestedObj(obj, "error"), "code") ?? "";
|
|
63363
|
+
const message = extractString(obj, "message") ?? extractString(getNestedObj(obj, "error"), "message") ?? (typeof raw === "string" ? raw : "") ?? "";
|
|
63364
|
+
const status = extractNumber(obj, "status") ?? extractNumber(obj, "statusCode") ?? extractNumber(obj, "status_code") ?? null;
|
|
63365
|
+
const sdkCode = extractString(obj, "error_code") ?? "";
|
|
63366
|
+
if (errorType === "authentication_error" || errorCode === "authentication_error" || sdkCode === "authentication_error" || message.toLowerCase().includes("authentication_error")) {
|
|
63367
|
+
const msg = message.toLowerCase();
|
|
63368
|
+
if (msg.includes("expired") || msg.includes("refresh")) {
|
|
63369
|
+
return "credentials-expired";
|
|
63370
|
+
}
|
|
63371
|
+
return "credentials-invalid";
|
|
63372
|
+
}
|
|
63373
|
+
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")) {
|
|
63374
|
+
return "credentials-invalid";
|
|
63375
|
+
}
|
|
63376
|
+
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")) {
|
|
63377
|
+
return "credit-exhausted";
|
|
63378
|
+
}
|
|
63379
|
+
if (errorType === "rate_limit_error" || errorCode === "rate_limit_error" || sdkCode === "rate_limit_error" || message.toLowerCase().includes("rate_limit_error") || message.toLowerCase().includes("rate limit")) {
|
|
63380
|
+
return "rate-limited";
|
|
63381
|
+
}
|
|
63382
|
+
if (errorType === "overloaded_error" || errorCode === "overloaded_error" || sdkCode === "overloaded_error" || message.toLowerCase().includes("overloaded_error") || message.toLowerCase().includes("overloaded")) {
|
|
63383
|
+
return "rate-limited";
|
|
63384
|
+
}
|
|
63385
|
+
if (errorType === "agent-crashed" || errorCode === "agent-crashed") {
|
|
63386
|
+
return "agent-crashed";
|
|
63387
|
+
}
|
|
63388
|
+
if (errorType === "agent-restarted-unexpectedly" || errorCode === "agent-restarted-unexpectedly") {
|
|
63389
|
+
return "agent-restarted-unexpectedly";
|
|
63390
|
+
}
|
|
63391
|
+
if (status != null) {
|
|
63392
|
+
if (status >= 400 && status < 500)
|
|
63393
|
+
return "unknown-4xx";
|
|
63394
|
+
if (status >= 500 && status < 600)
|
|
63395
|
+
return "unknown-5xx";
|
|
63396
|
+
}
|
|
63397
|
+
return "unknown-4xx";
|
|
63398
|
+
}
|
|
63399
|
+
function extractString(obj, key) {
|
|
63400
|
+
const v = obj[key];
|
|
63401
|
+
return typeof v === "string" && v.length > 0 ? v : null;
|
|
63402
|
+
}
|
|
63403
|
+
function extractNumber(obj, key) {
|
|
63404
|
+
const v = obj[key];
|
|
63405
|
+
return typeof v === "number" ? v : null;
|
|
63406
|
+
}
|
|
63407
|
+
function getNestedObj(obj, key) {
|
|
63408
|
+
const v = obj[key];
|
|
63409
|
+
return typeof v === "object" && v != null ? v : {};
|
|
63410
|
+
}
|
|
63411
|
+
var DEFAULT_OPERATOR_EVENT_COOLDOWN_MS = 5 * 60000;
|
|
63412
|
+
var cooldownMap = new Map;
|
|
63413
|
+
|
|
63414
|
+
// model-unavailable.ts
|
|
63415
|
+
init_quota_check();
|
|
63416
|
+
init_card_format();
|
|
63417
|
+
var transientUpstreamSignals = [
|
|
63418
|
+
"not your usage limit",
|
|
63419
|
+
"not your account",
|
|
63420
|
+
"not your account's",
|
|
63421
|
+
"temporarily limiting requests",
|
|
63422
|
+
"temporarily rate",
|
|
63423
|
+
"server is temporarily",
|
|
63424
|
+
"would exceed your account\u2019s rate limit",
|
|
63425
|
+
"would exceed your account's rate limit"
|
|
63426
|
+
];
|
|
63427
|
+
function isTransientUpstreamSignal(text4) {
|
|
63428
|
+
if (typeof text4 !== "string" || text4.length === 0)
|
|
63429
|
+
return false;
|
|
63430
|
+
const sample = text4.length > 16384 ? text4.slice(0, 16384) : text4;
|
|
63431
|
+
const lower = sample.toLowerCase();
|
|
63432
|
+
return transientUpstreamSignals.some((s) => lower.includes(s));
|
|
63433
|
+
}
|
|
63434
|
+
var litellmProxyLocal429Signals = [
|
|
63435
|
+
"deployment over user-defined ratelimit",
|
|
63436
|
+
"model rate limit exceeded. tpm limit",
|
|
63437
|
+
"model rate limit exceeded. rpm limit",
|
|
63438
|
+
"deployment over defined rpm limit",
|
|
63439
|
+
"no deployments available for selected model",
|
|
63440
|
+
"litellm rate limit handler",
|
|
63441
|
+
"crossed tpm / rpm",
|
|
63442
|
+
"max parallel request limit reached"
|
|
63443
|
+
];
|
|
63444
|
+
var litellmV3LimiterSignalPair = ["rate limit exceeded for ", "limit type:"];
|
|
63445
|
+
function isLitellmProxyLocal429(text4) {
|
|
63446
|
+
if (typeof text4 !== "string" || text4.length === 0)
|
|
63447
|
+
return false;
|
|
63448
|
+
const sample = text4.length > 16384 ? text4.slice(0, 16384) : text4;
|
|
63449
|
+
const lower = sample.toLowerCase();
|
|
63450
|
+
if (litellmProxyLocal429Signals.some((s) => lower.includes(s)))
|
|
63451
|
+
return true;
|
|
63452
|
+
return litellmV3LimiterSignalPair.every((s) => lower.includes(s));
|
|
63453
|
+
}
|
|
63454
|
+
function parseLitellmLimitDetail(text4, parseTimeNow = new Date) {
|
|
63455
|
+
const empty2 = { limitType: null, limit: null, currentUsage: null, resetAtMs: null };
|
|
63456
|
+
if (typeof text4 !== "string" || text4.length === 0)
|
|
63457
|
+
return empty2;
|
|
63458
|
+
const sample = text4.length > 16384 ? text4.slice(0, 16384) : text4;
|
|
63459
|
+
const lower = sample.toLowerCase();
|
|
63460
|
+
let limitType = null;
|
|
63461
|
+
let limit = null;
|
|
63462
|
+
const eqLimit = lower.match(/\b([tr]pm)[ _]limit[=:]\s*(\d+)/);
|
|
63463
|
+
if (eqLimit) {
|
|
63464
|
+
limitType = eqLimit[1];
|
|
63465
|
+
limit = Number(eqLimit[2]);
|
|
63466
|
+
}
|
|
63467
|
+
if (limitType == null) {
|
|
63468
|
+
const v3Type = lower.match(/limit type:\s*(tokens|requests|max_parallel_requests)/);
|
|
63469
|
+
if (v3Type)
|
|
63470
|
+
limitType = v3Type[1];
|
|
63471
|
+
}
|
|
63472
|
+
if (limit == null) {
|
|
63473
|
+
const v3Limit = lower.match(/current limit:\s*(\d+)/);
|
|
63474
|
+
if (v3Limit)
|
|
63475
|
+
limit = Number(v3Limit[1]);
|
|
63476
|
+
}
|
|
63477
|
+
let currentUsage = null;
|
|
63478
|
+
const usage = lower.match(/current usage=(\d+)/);
|
|
63479
|
+
if (usage)
|
|
63480
|
+
currentUsage = Number(usage[1]);
|
|
63481
|
+
let resetAtMs = null;
|
|
63482
|
+
const resetsAt = sample.match(/limit resets at:\s*(\d{4}-\d{2}-\d{2}) (\d{2}:\d{2}:\d{2}) UTC/i);
|
|
63483
|
+
if (resetsAt) {
|
|
63484
|
+
const d = new Date(`${resetsAt[1]}T${resetsAt[2]}Z`);
|
|
63485
|
+
if (!Number.isNaN(d.getTime()))
|
|
63486
|
+
resetAtMs = d.getTime();
|
|
63487
|
+
}
|
|
63488
|
+
if (resetAtMs == null) {
|
|
63489
|
+
const tryAgain = lower.match(/try again in\s+(\d+(?:\.\d+)?)\s*seconds/);
|
|
63490
|
+
if (tryAgain) {
|
|
63491
|
+
const secs = Number(tryAgain[1]);
|
|
63492
|
+
if (Number.isFinite(secs) && secs > 0 && secs < 7 * 24 * 3600) {
|
|
63493
|
+
resetAtMs = parseTimeNow.getTime() + Math.round(secs * 1000);
|
|
63494
|
+
}
|
|
63495
|
+
}
|
|
63496
|
+
}
|
|
63497
|
+
return {
|
|
63498
|
+
limitType,
|
|
63499
|
+
limit: limit != null && Number.isFinite(limit) ? limit : null,
|
|
63500
|
+
currentUsage: currentUsage != null && Number.isFinite(currentUsage) ? currentUsage : null,
|
|
63501
|
+
resetAtMs
|
|
63502
|
+
};
|
|
63503
|
+
}
|
|
63504
|
+
function detectModelUnavailable(stderr) {
|
|
63505
|
+
if (typeof stderr !== "string" || stderr.length === 0)
|
|
63506
|
+
return null;
|
|
63507
|
+
const sample = stderr.length > 16384 ? stderr.slice(0, 16384) : stderr;
|
|
63508
|
+
const lower = sample.toLowerCase();
|
|
63509
|
+
if (transientUpstreamSignals.some((s) => lower.includes(s))) {
|
|
63510
|
+
const resetAt = parseResetTime(sample);
|
|
63511
|
+
return resetAt !== undefined ? { kind: "overload", resetAt, raw: stderr } : { kind: "overload", raw: stderr };
|
|
63512
|
+
}
|
|
63513
|
+
if (isLitellmProxyLocal429(sample)) {
|
|
63514
|
+
const resetAt = parseResetTime(sample);
|
|
63515
|
+
return resetAt !== undefined ? { kind: "overload", resetAt, raw: stderr } : { kind: "overload", raw: stderr };
|
|
63516
|
+
}
|
|
63517
|
+
const quotaSignals = [
|
|
63518
|
+
"out of extra usage",
|
|
63519
|
+
"extra usage",
|
|
63520
|
+
"credit_balance_too_low",
|
|
63521
|
+
"credit balance too low",
|
|
63522
|
+
"usage limit",
|
|
63523
|
+
"usage_limit",
|
|
63524
|
+
"quota exhausted",
|
|
63525
|
+
"quota_exhausted",
|
|
63526
|
+
"plan limit",
|
|
63527
|
+
"subscription limit",
|
|
63528
|
+
"hit your limit",
|
|
63529
|
+
"hit the limit",
|
|
63530
|
+
"session limit",
|
|
63531
|
+
"session cap"
|
|
63532
|
+
];
|
|
63533
|
+
if (quotaSignals.some((s) => lower.includes(s))) {
|
|
63534
|
+
const resetAt = parseResetTime(sample);
|
|
63535
|
+
return resetAt !== undefined ? { kind: "quota_exhausted", resetAt, raw: stderr } : { kind: "quota_exhausted", raw: stderr };
|
|
63536
|
+
}
|
|
63537
|
+
const overloadSignals = [
|
|
63538
|
+
"overloaded_error",
|
|
63539
|
+
"overloaded",
|
|
63540
|
+
"rate_limit_error",
|
|
63541
|
+
"rate limit",
|
|
63542
|
+
"rate-limited",
|
|
63543
|
+
"http 429",
|
|
63544
|
+
'"status":429',
|
|
63545
|
+
"status: 429",
|
|
63546
|
+
" 429 ",
|
|
63547
|
+
"503 service",
|
|
63548
|
+
"service unavailable",
|
|
63549
|
+
'"status":529',
|
|
63550
|
+
"http 529",
|
|
63551
|
+
" 529 "
|
|
63552
|
+
];
|
|
63553
|
+
if (overloadSignals.some((s) => lower.includes(s))) {
|
|
63554
|
+
const resetAt = parseResetTime(sample);
|
|
63555
|
+
return resetAt !== undefined ? { kind: "overload", resetAt, raw: stderr } : { kind: "overload", raw: stderr };
|
|
63556
|
+
}
|
|
63557
|
+
const networkSignals = [
|
|
63558
|
+
"econnrefused",
|
|
63559
|
+
"econnreset",
|
|
63560
|
+
"etimedout",
|
|
63561
|
+
"enotfound",
|
|
63562
|
+
"eai_again",
|
|
63563
|
+
"fetch failed",
|
|
63564
|
+
"network error",
|
|
63565
|
+
"socket hang up",
|
|
63566
|
+
"request timed out",
|
|
63567
|
+
"connection refused",
|
|
63568
|
+
"getaddrinfo"
|
|
63569
|
+
];
|
|
63570
|
+
if (networkSignals.some((s) => lower.includes(s))) {
|
|
63571
|
+
return { kind: "network", raw: stderr };
|
|
63572
|
+
}
|
|
63573
|
+
return null;
|
|
63574
|
+
}
|
|
63575
|
+
function parseResetTime(text4, parseTimeNow = new Date) {
|
|
63576
|
+
const lower = text4.toLowerCase();
|
|
63577
|
+
const retryAfter = lower.match(/retry[\s-]*after[:\s]+(\d+)\s*(seconds?|s\b|minutes?|m\b|hours?|h\b)?/);
|
|
63578
|
+
if (retryAfter) {
|
|
63579
|
+
const n = Number(retryAfter[1]);
|
|
63580
|
+
if (Number.isFinite(n) && n > 0 && n < 7 * 24 * 3600) {
|
|
63581
|
+
const unit = (retryAfter[2] ?? "seconds").toLowerCase();
|
|
63582
|
+
const ms = unit.startsWith("h") ? n * 3600000 : unit.startsWith("m") ? n * 60000 : n * 1000;
|
|
63583
|
+
return new Date(parseTimeNow.getTime() + ms);
|
|
63584
|
+
}
|
|
63585
|
+
}
|
|
63586
|
+
const relReset = lower.match(/resets?\s+in\s+([0-9hms\s]+)/);
|
|
63587
|
+
if (relReset) {
|
|
63588
|
+
const ms = parseRelativeDuration(relReset[1]);
|
|
63589
|
+
if (ms != null)
|
|
63590
|
+
return new Date(parseTimeNow.getTime() + ms);
|
|
63591
|
+
}
|
|
63592
|
+
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/);
|
|
63593
|
+
if (iso) {
|
|
63594
|
+
const d = new Date(iso[0]);
|
|
63595
|
+
if (!Number.isNaN(d.getTime()))
|
|
63596
|
+
return d;
|
|
63597
|
+
}
|
|
63598
|
+
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)?))?)/);
|
|
63599
|
+
if (calReset) {
|
|
63600
|
+
const candidate = `${calReset[1]} ${parseTimeNow.getUTCFullYear()}`;
|
|
63601
|
+
const d = new Date(candidate);
|
|
63602
|
+
if (!Number.isNaN(d.getTime()))
|
|
63603
|
+
return d;
|
|
63604
|
+
}
|
|
63605
|
+
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);
|
|
63606
|
+
if (timeOnly) {
|
|
63607
|
+
const d = resolveNextWallClock(Number(timeOnly[1]), timeOnly[2] ? Number(timeOnly[2]) : 0, timeOnly[3]?.toLowerCase(), timeOnly[4]?.trim(), parseTimeNow);
|
|
63608
|
+
if (d != null)
|
|
63609
|
+
return d;
|
|
63610
|
+
}
|
|
63611
|
+
return;
|
|
63612
|
+
}
|
|
63613
|
+
function resolveNextWallClock(hour12or24, minute, ampm, tz, nowDate) {
|
|
63614
|
+
let hour = hour12or24;
|
|
63615
|
+
if (ampm === "pm" && hour < 12)
|
|
63616
|
+
hour += 12;
|
|
63617
|
+
if (ampm === "am" && hour === 12)
|
|
63618
|
+
hour = 0;
|
|
63619
|
+
if (!Number.isFinite(hour) || hour > 23 || hour < 0)
|
|
63620
|
+
return;
|
|
63621
|
+
if (!Number.isFinite(minute) || minute > 59 || minute < 0)
|
|
63622
|
+
return;
|
|
63623
|
+
const nowMs2 = nowDate.getTime();
|
|
63624
|
+
const base = new Date(nowMs2);
|
|
63625
|
+
for (let dayOffset = 0;dayOffset <= 2; dayOffset++) {
|
|
63626
|
+
const dateParts = tzDateParts(new Date(nowMs2 + dayOffset * 86400000), tz);
|
|
63627
|
+
if (dateParts == null)
|
|
63628
|
+
return;
|
|
63629
|
+
const epoch = wallClockToEpoch(dateParts.year, dateParts.month, dateParts.day, hour, minute, tz);
|
|
63630
|
+
if (epoch != null && epoch > nowMs2)
|
|
63631
|
+
return new Date(epoch);
|
|
63632
|
+
}
|
|
63633
|
+
return;
|
|
63634
|
+
}
|
|
63635
|
+
function tzDateParts(d, tz) {
|
|
63636
|
+
if (!tz) {
|
|
63637
|
+
return { year: d.getUTCFullYear(), month: d.getUTCMonth(), day: d.getUTCDate() };
|
|
63638
|
+
}
|
|
63639
|
+
try {
|
|
63640
|
+
const fmt = new Intl.DateTimeFormat("en-US", {
|
|
63641
|
+
timeZone: tz,
|
|
63642
|
+
year: "numeric",
|
|
63643
|
+
month: "2-digit",
|
|
63644
|
+
day: "2-digit"
|
|
63645
|
+
});
|
|
63646
|
+
const parts = Object.fromEntries(fmt.formatToParts(d).filter((p) => p.type !== "literal").map((p) => [p.type, p.value]));
|
|
63647
|
+
return {
|
|
63648
|
+
year: Number(parts.year),
|
|
63649
|
+
month: Number(parts.month) - 1,
|
|
63650
|
+
day: Number(parts.day)
|
|
63651
|
+
};
|
|
63652
|
+
} catch {
|
|
63653
|
+
return null;
|
|
63654
|
+
}
|
|
63655
|
+
}
|
|
63656
|
+
function wallClockToEpoch(year, month, day, hour, minute, tz) {
|
|
63657
|
+
const asUtc = Date.UTC(year, month, day, hour, minute, 0);
|
|
63658
|
+
if (!tz)
|
|
63659
|
+
return asUtc;
|
|
63660
|
+
try {
|
|
63661
|
+
const fmt = new Intl.DateTimeFormat("en-US", {
|
|
63662
|
+
timeZone: tz,
|
|
63663
|
+
year: "numeric",
|
|
63664
|
+
month: "2-digit",
|
|
63665
|
+
day: "2-digit",
|
|
63666
|
+
hour: "2-digit",
|
|
63667
|
+
minute: "2-digit",
|
|
63668
|
+
second: "2-digit",
|
|
63669
|
+
hour12: false
|
|
63670
|
+
});
|
|
63671
|
+
const parts = Object.fromEntries(fmt.formatToParts(new Date(asUtc)).filter((p) => p.type !== "literal").map((p) => [p.type, p.value]));
|
|
63672
|
+
const shown = Date.UTC(Number(parts.year), Number(parts.month) - 1, Number(parts.day), Number(parts.hour) % 24, Number(parts.minute), Number(parts.second));
|
|
63673
|
+
const offset = shown - asUtc;
|
|
63674
|
+
return asUtc - offset;
|
|
63675
|
+
} catch {
|
|
63676
|
+
return null;
|
|
63677
|
+
}
|
|
63678
|
+
}
|
|
63679
|
+
function parseRelativeDuration(s) {
|
|
63680
|
+
let total = 0;
|
|
63681
|
+
let matched = false;
|
|
63682
|
+
const re = /(\d+)\s*(h|hours?|m|minutes?|s|seconds?)/g;
|
|
63683
|
+
let m;
|
|
63684
|
+
while ((m = re.exec(s)) != null) {
|
|
63685
|
+
matched = true;
|
|
63686
|
+
const n = Number(m[1]);
|
|
63687
|
+
const unit = m[2].toLowerCase();
|
|
63688
|
+
if (unit.startsWith("h"))
|
|
63689
|
+
total += n * 3600000;
|
|
63690
|
+
else if (unit.startsWith("m"))
|
|
63691
|
+
total += n * 60000;
|
|
63692
|
+
else
|
|
63693
|
+
total += n * 1000;
|
|
63694
|
+
}
|
|
63695
|
+
return matched && total > 0 ? total : null;
|
|
63696
|
+
}
|
|
63697
|
+
|
|
63698
|
+
// session-tail.ts
|
|
63699
|
+
function sanitizeCwdToProjectName(cwd) {
|
|
63700
|
+
return cwd.replace(/[^a-zA-Z0-9]/g, "-");
|
|
63701
|
+
}
|
|
63702
|
+
function getProjectsDirForCwd(cwd = process.cwd(), claudeHome = process.env.CLAUDE_CONFIG_DIR ?? join21(homedir7(), ".claude")) {
|
|
63703
|
+
return join21(claudeHome, "projects", sanitizeCwdToProjectName(cwd));
|
|
63704
|
+
}
|
|
63705
|
+
function parseChannelMeta(content3) {
|
|
63706
|
+
const grab = (key) => {
|
|
63707
|
+
const m = content3.match(new RegExp(`(?:^|[\\s"'])${key}="([^"]+)"`));
|
|
63708
|
+
return m ? m[1] : null;
|
|
63709
|
+
};
|
|
63710
|
+
return {
|
|
63711
|
+
chatId: grab("chat_id"),
|
|
63712
|
+
messageId: grab("message_id"),
|
|
63713
|
+
threadId: grab("message_thread_id")
|
|
63714
|
+
};
|
|
63715
|
+
}
|
|
63716
|
+
var MAX_JSONL_LINE_BYTES = 2 * 1024 * 1024;
|
|
63717
|
+
var MAX_ERROR_TEXT_CHARS = 500;
|
|
63718
|
+
function extractToolResultErrorText(content3) {
|
|
63719
|
+
if (typeof content3 === "string") {
|
|
63720
|
+
return content3.slice(0, MAX_ERROR_TEXT_CHARS);
|
|
63721
|
+
}
|
|
63722
|
+
if (Array.isArray(content3)) {
|
|
63723
|
+
const parts = [];
|
|
63724
|
+
for (const block of content3) {
|
|
63725
|
+
if (typeof block === "object" && block != null) {
|
|
63726
|
+
const b = block;
|
|
63727
|
+
if (b.type === "text" && typeof b.text === "string") {
|
|
63728
|
+
parts.push(b.text);
|
|
63729
|
+
}
|
|
63730
|
+
}
|
|
63731
|
+
}
|
|
63732
|
+
return parts.join(`
|
|
63733
|
+
`).slice(0, MAX_ERROR_TEXT_CHARS);
|
|
63734
|
+
}
|
|
63735
|
+
return "";
|
|
63736
|
+
}
|
|
63737
|
+
function projectAssistantTextBlocks(content3, make) {
|
|
63738
|
+
const out = new Map;
|
|
63739
|
+
let lastToolUseIdx = -1;
|
|
63740
|
+
content3.forEach((c, i) => {
|
|
63741
|
+
if (c.type === "tool_use")
|
|
63742
|
+
lastToolUseIdx = i;
|
|
63743
|
+
});
|
|
63744
|
+
content3.forEach((c, i) => {
|
|
63745
|
+
if (c.type !== "text")
|
|
63746
|
+
return;
|
|
63747
|
+
const text4 = c.text ?? "";
|
|
63748
|
+
if (text4.trim().length === 0)
|
|
63749
|
+
return;
|
|
63750
|
+
out.set(i, make(text4, i, i > lastToolUseIdx));
|
|
63751
|
+
});
|
|
63752
|
+
return out;
|
|
63753
|
+
}
|
|
63754
|
+
function sumUsageTokens(usage) {
|
|
63755
|
+
if (usage == null || typeof usage !== "object")
|
|
63756
|
+
return 0;
|
|
63757
|
+
const u = usage;
|
|
63758
|
+
const n = (v) => typeof v === "number" && Number.isFinite(v) ? v : 0;
|
|
63759
|
+
return n(u.input_tokens) + n(u.output_tokens) + n(u.cache_creation_input_tokens);
|
|
63760
|
+
}
|
|
63761
|
+
function projectTrailingAnswerFromTranscript(transcriptText) {
|
|
63762
|
+
const buf = [];
|
|
63763
|
+
let lastMeaningful = null;
|
|
63764
|
+
for (const rawLine of transcriptText.split(`
|
|
63765
|
+
`)) {
|
|
63766
|
+
const line = rawLine.trim();
|
|
63767
|
+
if (!line)
|
|
63768
|
+
continue;
|
|
63769
|
+
if (isRealUserTurnBoundary(line)) {
|
|
63770
|
+
buf.length = 0;
|
|
63771
|
+
lastMeaningful = null;
|
|
63772
|
+
continue;
|
|
63773
|
+
}
|
|
63774
|
+
for (const ev of projectTranscriptLine(line)) {
|
|
63775
|
+
if (ev.kind === "enqueue") {
|
|
63776
|
+
buf.length = 0;
|
|
63777
|
+
lastMeaningful = null;
|
|
63778
|
+
} else if (ev.kind === "tool_use") {
|
|
63779
|
+
buf.length = 0;
|
|
63780
|
+
lastMeaningful = "tool_use";
|
|
63781
|
+
} else if (ev.kind === "text") {
|
|
63782
|
+
const t = ev.text ?? "";
|
|
63783
|
+
if (t.trim().length > 0) {
|
|
63784
|
+
buf.push(t);
|
|
63785
|
+
lastMeaningful = "text";
|
|
63786
|
+
}
|
|
63787
|
+
}
|
|
63788
|
+
}
|
|
63789
|
+
}
|
|
63790
|
+
const text4 = buf.join("").trim();
|
|
63791
|
+
return { text: text4, trailingIsText: lastMeaningful === "text" && text4.length > 0 };
|
|
63792
|
+
}
|
|
63793
|
+
function isRealUserTurnBoundary(line) {
|
|
63794
|
+
let obj;
|
|
63795
|
+
try {
|
|
63796
|
+
obj = JSON.parse(line);
|
|
63797
|
+
} catch {
|
|
63798
|
+
return false;
|
|
63799
|
+
}
|
|
63800
|
+
if (obj.type !== "user")
|
|
63801
|
+
return false;
|
|
63802
|
+
const message = obj.message;
|
|
63803
|
+
const content3 = message?.content;
|
|
63804
|
+
if (typeof content3 === "string")
|
|
63805
|
+
return content3.trim().length > 0;
|
|
63806
|
+
if (Array.isArray(content3)) {
|
|
63807
|
+
for (const c of content3) {
|
|
63808
|
+
if (typeof c === "object" && c != null && c.type === "text") {
|
|
63809
|
+
const t = String(c.text ?? "");
|
|
63810
|
+
if (t.trim().length > 0)
|
|
63811
|
+
return true;
|
|
63812
|
+
}
|
|
63813
|
+
}
|
|
63814
|
+
}
|
|
63815
|
+
return false;
|
|
63816
|
+
}
|
|
63817
|
+
function projectTranscriptLine(line) {
|
|
63818
|
+
if (line.length > MAX_JSONL_LINE_BYTES)
|
|
63819
|
+
return [];
|
|
63820
|
+
let obj;
|
|
63821
|
+
try {
|
|
63822
|
+
obj = JSON.parse(line);
|
|
63823
|
+
} catch {
|
|
63824
|
+
return [];
|
|
63825
|
+
}
|
|
63826
|
+
const type = obj.type;
|
|
63827
|
+
if (!type)
|
|
63828
|
+
return [];
|
|
63829
|
+
if (type === "queue-operation") {
|
|
63830
|
+
const op = obj.operation;
|
|
63831
|
+
if (op === "enqueue") {
|
|
63832
|
+
const content3 = obj.content ?? "";
|
|
63833
|
+
const { chatId, messageId, threadId } = parseChannelMeta(content3);
|
|
63834
|
+
return [{ kind: "enqueue", chatId, messageId, threadId, rawContent: content3 }];
|
|
63835
|
+
}
|
|
63836
|
+
if (op === "dequeue") {
|
|
63837
|
+
return [{ kind: "dequeue" }];
|
|
63838
|
+
}
|
|
63839
|
+
return [];
|
|
63840
|
+
}
|
|
63841
|
+
if (type === "assistant") {
|
|
63842
|
+
const message = obj.message;
|
|
63843
|
+
const content3 = message?.content;
|
|
63844
|
+
if (!Array.isArray(content3))
|
|
63845
|
+
return [];
|
|
63846
|
+
if (obj.isApiErrorMessage === true) {
|
|
63847
|
+
const mainModel2 = message?.model;
|
|
63848
|
+
return typeof mainModel2 === "string" && !isModelSentinel(mainModel2) ? [{ kind: "model", model: mainModel2 }] : [];
|
|
63849
|
+
}
|
|
63850
|
+
const events = [];
|
|
63851
|
+
const mainModel = message?.model;
|
|
63852
|
+
if (typeof mainModel === "string" && !isModelSentinel(mainModel)) {
|
|
63853
|
+
events.push({ kind: "model", model: mainModel });
|
|
63854
|
+
}
|
|
63855
|
+
const mainUsageTotal = sumUsageTokens(message?.usage);
|
|
63856
|
+
if (mainUsageTotal > 0) {
|
|
63857
|
+
const mainMsgId = message?.id;
|
|
63858
|
+
events.push({
|
|
63859
|
+
kind: "usage",
|
|
63860
|
+
messageId: typeof mainMsgId === "string" ? mainMsgId : null,
|
|
63861
|
+
totalTokens: mainUsageTotal
|
|
63862
|
+
});
|
|
63863
|
+
}
|
|
63864
|
+
const textEvents = projectAssistantTextBlocks(content3, (text4, blockIndex, lastInMessage) => ({ kind: "text", text: text4, blockIndex, lastInMessage }));
|
|
63865
|
+
content3.forEach((c, i) => {
|
|
63866
|
+
const ct = c.type;
|
|
63867
|
+
if (ct === "thinking") {
|
|
63868
|
+
events.push({ kind: "thinking" });
|
|
63869
|
+
} else if (ct === "tool_use") {
|
|
63870
|
+
const input = c.input;
|
|
63871
|
+
events.push({
|
|
63872
|
+
kind: "tool_use",
|
|
63873
|
+
toolName: c.name ?? "",
|
|
63874
|
+
toolUseId: c.id ?? null,
|
|
63875
|
+
input: input && typeof input === "object" ? input : undefined
|
|
63876
|
+
});
|
|
63877
|
+
} else if (ct === "text") {
|
|
63878
|
+
const ev = textEvents.get(i);
|
|
63879
|
+
if (ev != null)
|
|
63880
|
+
events.push(ev);
|
|
63881
|
+
}
|
|
63882
|
+
});
|
|
63883
|
+
return events;
|
|
63884
|
+
}
|
|
63885
|
+
if (type === "user") {
|
|
63886
|
+
const message = obj.message;
|
|
63887
|
+
const content3 = message?.content;
|
|
63888
|
+
if (!Array.isArray(content3))
|
|
63889
|
+
return [];
|
|
63890
|
+
const events = [];
|
|
63891
|
+
for (const c of content3) {
|
|
63892
|
+
if (c.type === "tool_result") {
|
|
63893
|
+
const isError2 = c.is_error === true ? true : undefined;
|
|
63894
|
+
events.push({
|
|
63895
|
+
kind: "tool_result",
|
|
63896
|
+
toolUseId: c.tool_use_id ?? "",
|
|
63897
|
+
toolName: null,
|
|
63898
|
+
isError: isError2,
|
|
63899
|
+
errorText: isError2 ? extractToolResultErrorText(c.content) : undefined
|
|
63900
|
+
});
|
|
63901
|
+
}
|
|
63902
|
+
}
|
|
63903
|
+
return events;
|
|
63904
|
+
}
|
|
63905
|
+
if (type === "system" && obj.subtype === "turn_duration") {
|
|
63906
|
+
return [
|
|
63907
|
+
{ kind: "turn_end", durationMs: obj.durationMs ?? 0 }
|
|
63908
|
+
];
|
|
63909
|
+
}
|
|
63910
|
+
return [];
|
|
63911
|
+
}
|
|
63912
|
+
|
|
62875
63913
|
// pty-tail.ts
|
|
62876
63914
|
var import_headless = __toESM(require_xterm_headless(), 1);
|
|
62877
63915
|
var PTY_DEBUG = process.env.SWITCHROOM_PTY_DEBUG === "1";
|
|
@@ -62956,7 +63994,7 @@ async function gatewayStartupRetry(fn, opts = {}) {
|
|
|
62956
63994
|
|
|
62957
63995
|
// gateway/quarantine.ts
|
|
62958
63996
|
import { mkdirSync as mkdirSync15, writeFileSync as writeFileSync15 } from "node:fs";
|
|
62959
|
-
import { join as
|
|
63997
|
+
import { join as join22 } from "node:path";
|
|
62960
63998
|
var QUARANTINE_FILENAME = "quarantine.json";
|
|
62961
63999
|
function writeQuarantineMarker(telegramStateDir, reason, detail, nowFn = Date.now) {
|
|
62962
64000
|
mkdirSync15(telegramStateDir, { recursive: true, mode: 448 });
|
|
@@ -62966,7 +64004,7 @@ function writeQuarantineMarker(telegramStateDir, reason, detail, nowFn = Date.no
|
|
|
62966
64004
|
ts: nowFn(),
|
|
62967
64005
|
detail
|
|
62968
64006
|
};
|
|
62969
|
-
writeFileSync15(
|
|
64007
|
+
writeFileSync15(join22(telegramStateDir, QUARANTINE_FILENAME), JSON.stringify(marker) + `
|
|
62970
64008
|
`, "utf-8");
|
|
62971
64009
|
}
|
|
62972
64010
|
|
|
@@ -63985,9 +65023,9 @@ function defaultAddAccount(label, credentials, opts) {
|
|
|
63985
65023
|
// ../src/auth/broker/client.ts
|
|
63986
65024
|
init_protocol2();
|
|
63987
65025
|
import * as net3 from "node:net";
|
|
63988
|
-
import { homedir as
|
|
65026
|
+
import { homedir as homedir8 } from "node:os";
|
|
63989
65027
|
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
63990
|
-
import { join as
|
|
65028
|
+
import { join as join24 } from "node:path";
|
|
63991
65029
|
var DEFAULT_TIMEOUT_MS3 = 5000;
|
|
63992
65030
|
function reviveDate2(v) {
|
|
63993
65031
|
if (v == null)
|
|
@@ -63997,8 +65035,8 @@ function reviveDate2(v) {
|
|
|
63997
65035
|
const d = new Date(v);
|
|
63998
65036
|
return Number.isNaN(d.getTime()) ? null : d;
|
|
63999
65037
|
}
|
|
64000
|
-
function operatorSocketPath2(home2 =
|
|
64001
|
-
return
|
|
65038
|
+
function operatorSocketPath2(home2 = homedir8()) {
|
|
65039
|
+
return join24(home2, ".switchroom", "state", "auth-broker-operator", "sock");
|
|
64002
65040
|
}
|
|
64003
65041
|
function resolveAuthBrokerSocketPath2(opts) {
|
|
64004
65042
|
if (opts?.socket)
|
|
@@ -64312,13 +65350,13 @@ class AuthBrokerClient2 {
|
|
|
64312
65350
|
init_loader();
|
|
64313
65351
|
init_resolver();
|
|
64314
65352
|
init_vault();
|
|
64315
|
-
import { existsSync as
|
|
65353
|
+
import { existsSync as existsSync17 } from "node:fs";
|
|
64316
65354
|
var DEFAULT_VOICE_API_KEY_REF = "vault:openai/api-key";
|
|
64317
65355
|
function tryDirectVaultRead(ref, config, passphrase) {
|
|
64318
65356
|
if (!passphrase)
|
|
64319
65357
|
return null;
|
|
64320
65358
|
const vaultPath = resolvePath(config.vault?.path ?? "~/.switchroom/vault.enc");
|
|
64321
|
-
if (!
|
|
65359
|
+
if (!existsSync17(vaultPath))
|
|
64322
65360
|
return null;
|
|
64323
65361
|
try {
|
|
64324
65362
|
const secrets = openVault(passphrase, vaultPath);
|
|
@@ -64374,14 +65412,14 @@ async function materializeVoiceKey(opts = {}, logger2 = (line) => process.stderr
|
|
|
64374
65412
|
init_loader();
|
|
64375
65413
|
init_resolver();
|
|
64376
65414
|
init_vault();
|
|
64377
|
-
import { existsSync as
|
|
65415
|
+
import { existsSync as existsSync18 } from "node:fs";
|
|
64378
65416
|
var VOICE_SIDECAR_TOKEN_KEY = "voice/sidecar-token";
|
|
64379
65417
|
var DEFAULT_VOICE_SIDECAR_TOKEN_REF = `vault:${VOICE_SIDECAR_TOKEN_KEY}`;
|
|
64380
65418
|
function tryDirectVaultRead2(ref, config, passphrase) {
|
|
64381
65419
|
if (!passphrase)
|
|
64382
65420
|
return null;
|
|
64383
65421
|
const vaultPath = resolvePath(config.vault?.path ?? "~/.switchroom/vault.enc");
|
|
64384
|
-
if (!
|
|
65422
|
+
if (!existsSync18(vaultPath))
|
|
64385
65423
|
return null;
|
|
64386
65424
|
try {
|
|
64387
65425
|
const secrets = openVault(passphrase, vaultPath);
|
|
@@ -64434,17 +65472,17 @@ async function materializeSidecarToken(opts = {}, logger2 = (line) => process.st
|
|
|
64434
65472
|
}
|
|
64435
65473
|
|
|
64436
65474
|
// ../src/setup/host-capabilities.ts
|
|
64437
|
-
import { existsSync as
|
|
65475
|
+
import { existsSync as existsSync19, mkdirSync as mkdirSync18, readFileSync as readFileSync19, writeFileSync as writeFileSync16 } from "node:fs";
|
|
64438
65476
|
init_paths();
|
|
64439
65477
|
function hostCapabilitiesPath() {
|
|
64440
65478
|
return resolveStatePath("host-capabilities.json");
|
|
64441
65479
|
}
|
|
64442
65480
|
function loadHostCapabilities() {
|
|
64443
65481
|
const path2 = hostCapabilitiesPath();
|
|
64444
|
-
if (!
|
|
65482
|
+
if (!existsSync19(path2))
|
|
64445
65483
|
return null;
|
|
64446
65484
|
try {
|
|
64447
|
-
const parsed = JSON.parse(
|
|
65485
|
+
const parsed = JSON.parse(readFileSync19(path2, "utf-8"));
|
|
64448
65486
|
if (parsed && typeof parsed === "object" && "voice" in parsed && typeof parsed.voice === "object") {
|
|
64449
65487
|
return parsed;
|
|
64450
65488
|
}
|
|
@@ -64552,16 +65590,16 @@ function resolveExhaustUntil(resetAtMs, now = Date.now()) {
|
|
|
64552
65590
|
|
|
64553
65591
|
// gateway/auth-add-flow.ts
|
|
64554
65592
|
import { execFileSync as execFileSync3 } from "node:child_process";
|
|
64555
|
-
import { existsSync as
|
|
64556
|
-
import { homedir as
|
|
64557
|
-
import { join as
|
|
65593
|
+
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";
|
|
65594
|
+
import { homedir as homedir9 } from "node:os";
|
|
65595
|
+
import { join as join25 } from "node:path";
|
|
64558
65596
|
import { randomBytes as randomBytes5 } from "node:crypto";
|
|
64559
65597
|
|
|
64560
65598
|
// ../src/auth/manager.ts
|
|
64561
65599
|
import {
|
|
64562
|
-
readFileSync as
|
|
65600
|
+
readFileSync as readFileSync20,
|
|
64563
65601
|
readdirSync as readdirSync3,
|
|
64564
|
-
existsSync as
|
|
65602
|
+
existsSync as existsSync20,
|
|
64565
65603
|
writeFileSync as writeFileSync17,
|
|
64566
65604
|
mkdirSync as mkdirSync19,
|
|
64567
65605
|
mkdtempSync as mkdtempSync2,
|
|
@@ -64588,9 +65626,9 @@ function parseSetupTokenUrl(output) {
|
|
|
64588
65626
|
}
|
|
64589
65627
|
function readTokenFromCredentialsFile(credentialsFilePath) {
|
|
64590
65628
|
try {
|
|
64591
|
-
if (!
|
|
65629
|
+
if (!existsSync20(credentialsFilePath))
|
|
64592
65630
|
return null;
|
|
64593
|
-
const raw =
|
|
65631
|
+
const raw = readFileSync20(credentialsFilePath, "utf-8");
|
|
64594
65632
|
const parsed = JSON.parse(raw);
|
|
64595
65633
|
const token = parsed?.claudeAiOauth?.accessToken;
|
|
64596
65634
|
if (typeof token !== "string")
|
|
@@ -64648,9 +65686,9 @@ function makeAuthAddTmuxOps(tmuxBin = "tmux") {
|
|
|
64648
65686
|
};
|
|
64649
65687
|
}
|
|
64650
65688
|
var pendingAuthAddFlows = new Map;
|
|
64651
|
-
function pickScratchDir(label, home2 =
|
|
65689
|
+
function pickScratchDir(label, home2 = homedir9()) {
|
|
64652
65690
|
const suffix = randomBytes5(8).toString("hex");
|
|
64653
|
-
return
|
|
65691
|
+
return join25(home2, ".switchroom", "accounts", ".in-progress", `${label}-${suffix}`);
|
|
64654
65692
|
}
|
|
64655
65693
|
function cleanScratchDir(scratchDir) {
|
|
64656
65694
|
try {
|
|
@@ -64659,8 +65697,8 @@ function cleanScratchDir(scratchDir) {
|
|
|
64659
65697
|
}
|
|
64660
65698
|
var AUTH_TMUX_SESSION_FILE = ".auth-tmux-session";
|
|
64661
65699
|
function sweepOrphanSessions(home2, tmux, nowMs2 = Date.now()) {
|
|
64662
|
-
const inProgressDir =
|
|
64663
|
-
if (!
|
|
65700
|
+
const inProgressDir = join25(home2, ".switchroom", "accounts", ".in-progress");
|
|
65701
|
+
if (!existsSync21(inProgressDir))
|
|
64664
65702
|
return;
|
|
64665
65703
|
let entries;
|
|
64666
65704
|
try {
|
|
@@ -64670,16 +65708,16 @@ function sweepOrphanSessions(home2, tmux, nowMs2 = Date.now()) {
|
|
|
64670
65708
|
}
|
|
64671
65709
|
const tenMinMs = 10 * 60000;
|
|
64672
65710
|
for (const entry of entries) {
|
|
64673
|
-
const dir =
|
|
64674
|
-
const sessionFile =
|
|
64675
|
-
if (!
|
|
65711
|
+
const dir = join25(inProgressDir, entry);
|
|
65712
|
+
const sessionFile = join25(dir, AUTH_TMUX_SESSION_FILE);
|
|
65713
|
+
if (!existsSync21(sessionFile))
|
|
64676
65714
|
continue;
|
|
64677
65715
|
let fileContents;
|
|
64678
65716
|
let fileMtime;
|
|
64679
65717
|
try {
|
|
64680
65718
|
const stat = statSync7(sessionFile);
|
|
64681
65719
|
fileMtime = stat.mtimeMs;
|
|
64682
|
-
fileContents =
|
|
65720
|
+
fileContents = readFileSync21(sessionFile, "utf8").trim();
|
|
64683
65721
|
} catch {
|
|
64684
65722
|
continue;
|
|
64685
65723
|
}
|
|
@@ -64699,7 +65737,7 @@ async function startAccountAuthSession(label, opts = {}) {
|
|
|
64699
65737
|
if (process.env.SWITCHROOM_TMUX_SUPERVISOR !== "1" && !opts.tmuxOps) {
|
|
64700
65738
|
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
65739
|
}
|
|
64702
|
-
const home2 = opts.home ??
|
|
65740
|
+
const home2 = opts.home ?? homedir9();
|
|
64703
65741
|
const urlTimeoutMs = opts.urlTimeoutMs ?? 12000;
|
|
64704
65742
|
const agentName3 = opts.agentName ?? process.env.SWITCHROOM_AGENT_NAME ?? "gateway";
|
|
64705
65743
|
const tmux = opts.tmuxOps ?? makeAuthAddTmuxOps(opts.tmuxBin);
|
|
@@ -64711,7 +65749,7 @@ async function startAccountAuthSession(label, opts = {}) {
|
|
|
64711
65749
|
const tmuxSocket = `switchroom-${agentName3}`;
|
|
64712
65750
|
const tmuxSession = `auth-add-${label}-${hexSuffix}`.slice(0, 64);
|
|
64713
65751
|
try {
|
|
64714
|
-
writeFileSync18(
|
|
65752
|
+
writeFileSync18(join25(scratchDir, AUTH_TMUX_SESSION_FILE), `${tmuxSocket}
|
|
64715
65753
|
${tmuxSession}`, "utf8");
|
|
64716
65754
|
} catch {}
|
|
64717
65755
|
const sessionEnv = {
|
|
@@ -64760,7 +65798,7 @@ async function submitAccountAuthCode(flow3, code2, opts = {}) {
|
|
|
64760
65798
|
const pollIntervalMs = opts.pollIntervalMs ?? 250;
|
|
64761
65799
|
const pollTimeoutMs = opts.pollTimeoutMs ?? 300000;
|
|
64762
65800
|
const tmux = opts.tmuxOps ?? makeAuthAddTmuxOps();
|
|
64763
|
-
const credentialsPath =
|
|
65801
|
+
const credentialsPath = join25(flow3.scratchDir, ".credentials.json");
|
|
64764
65802
|
try {
|
|
64765
65803
|
tmux.send(flow3.tmuxSocket, flow3.tmuxSession, code2);
|
|
64766
65804
|
} catch (err) {
|
|
@@ -64770,11 +65808,11 @@ async function submitAccountAuthCode(flow3, code2, opts = {}) {
|
|
|
64770
65808
|
const deadline = Date.now() + pollTimeoutMs;
|
|
64771
65809
|
while (Date.now() < deadline) {
|
|
64772
65810
|
await new Promise((r) => setTimeout(r, pollIntervalMs));
|
|
64773
|
-
if (
|
|
65811
|
+
if (existsSync21(credentialsPath)) {
|
|
64774
65812
|
const token = readTokenFromCredentialsFile(credentialsPath);
|
|
64775
65813
|
if (token) {
|
|
64776
65814
|
try {
|
|
64777
|
-
const raw =
|
|
65815
|
+
const raw = readFileSync21(credentialsPath, "utf-8");
|
|
64778
65816
|
const parsed = JSON.parse(raw);
|
|
64779
65817
|
if (parsed.claudeAiOauth?.accessToken) {
|
|
64780
65818
|
tmux.killSession(flow3.tmuxSocket, flow3.tmuxSession);
|
|
@@ -64784,7 +65822,7 @@ async function submitAccountAuthCode(flow3, code2, opts = {}) {
|
|
|
64784
65822
|
}
|
|
64785
65823
|
}
|
|
64786
65824
|
if (!tmux.hasSession(flow3.tmuxSocket, flow3.tmuxSession)) {
|
|
64787
|
-
if (!
|
|
65825
|
+
if (!existsSync21(credentialsPath)) {
|
|
64788
65826
|
cleanScratchDir(flow3.scratchDir);
|
|
64789
65827
|
throw new Error("claude setup-token exited without writing credentials \u2014 the code may be invalid or expired");
|
|
64790
65828
|
}
|
|
@@ -65236,28 +66274,6 @@ function autoClassifyMidTurnInbound(i) {
|
|
|
65236
66274
|
|
|
65237
66275
|
// operator-events.ts
|
|
65238
66276
|
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
66277
|
function renderOperatorEvent(ev) {
|
|
65262
66278
|
const agent = escapeMarkdown(ev.agent);
|
|
65263
66279
|
const detail = escapeMarkdown(stripRawErrorBytes(ev.detail));
|
|
@@ -65445,15 +66461,15 @@ function renderOperatorEvent(ev) {
|
|
|
65445
66461
|
};
|
|
65446
66462
|
}
|
|
65447
66463
|
}
|
|
65448
|
-
var
|
|
65449
|
-
var
|
|
65450
|
-
function shouldEmitOperatorEvent(agent, kind, now = Date.now(), cooldownMs =
|
|
66464
|
+
var DEFAULT_OPERATOR_EVENT_COOLDOWN_MS2 = 5 * 60000;
|
|
66465
|
+
var cooldownMap2 = new Map;
|
|
66466
|
+
function shouldEmitOperatorEvent(agent, kind, now = Date.now(), cooldownMs = DEFAULT_OPERATOR_EVENT_COOLDOWN_MS2) {
|
|
65451
66467
|
const key = `${agent}:${kind}`;
|
|
65452
|
-
const last =
|
|
66468
|
+
const last = cooldownMap2.get(key);
|
|
65453
66469
|
if (last != null && now - last < cooldownMs) {
|
|
65454
66470
|
return false;
|
|
65455
66471
|
}
|
|
65456
|
-
|
|
66472
|
+
cooldownMap2.set(key, now);
|
|
65457
66473
|
return true;
|
|
65458
66474
|
}
|
|
65459
66475
|
|
|
@@ -65464,290 +66480,6 @@ function recordOperatorEvent(event, now = Date.now()) {
|
|
|
65464
66480
|
store.set(event.agent, { event, storedAt: now });
|
|
65465
66481
|
}
|
|
65466
66482
|
|
|
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
66483
|
// throttle-tier.ts
|
|
65752
66484
|
init_card_format();
|
|
65753
66485
|
init_quota_check();
|
|
@@ -65807,72 +66539,6 @@ function renderThrottleEscalationNotice(opts) {
|
|
|
65807
66539
|
${tail}`;
|
|
65808
66540
|
}
|
|
65809
66541
|
|
|
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
66542
|
// llm-error-present.ts
|
|
65877
66543
|
function extractModel(raw) {
|
|
65878
66544
|
const m = raw.match(/["']?model["']?\s*[=:]\s*["']?((?:claude|sr)[A-Za-z0-9._-]+)/i);
|
|
@@ -67368,9 +68034,50 @@ function backOffOpenInline2(text4, cut) {
|
|
|
67368
68034
|
}
|
|
67369
68035
|
|
|
67370
68036
|
// rich-send.ts
|
|
68037
|
+
init_dollar_math_guard();
|
|
68038
|
+
init_emphasis_guard();
|
|
68039
|
+
init_line_start_guard();
|
|
68040
|
+
init_inline_pairs_guard();
|
|
67371
68041
|
var import_grammy8 = __toESM(require_mod2(), 1);
|
|
68042
|
+
function guardAccidentalFormatting2(markdown) {
|
|
68043
|
+
let out = markdown;
|
|
68044
|
+
out = guardAccidentalEmphasis(out);
|
|
68045
|
+
out = guardAccidentalBlockConstructs(out);
|
|
68046
|
+
out = guardAccidentalInlinePairs(out);
|
|
68047
|
+
out = guardDollarMath(out);
|
|
68048
|
+
return out;
|
|
68049
|
+
}
|
|
67372
68050
|
function richMessage2(markdown) {
|
|
67373
|
-
return { markdown };
|
|
68051
|
+
return { markdown: guardAccidentalFormatting2(markdown) };
|
|
68052
|
+
}
|
|
68053
|
+
|
|
68054
|
+
// gateway/redelivery-decision.ts
|
|
68055
|
+
var REDELIVERY_PREFIX = "Recovered from an interrupted turn:";
|
|
68056
|
+
function decideRedeliverCapture(input) {
|
|
68057
|
+
if (input.willBeResumed)
|
|
68058
|
+
return { capture: false, skipReason: "will-be-resumed" };
|
|
68059
|
+
if (!input.hasSessionId)
|
|
68060
|
+
return { capture: false, skipReason: "no-session-id" };
|
|
68061
|
+
return { capture: true };
|
|
68062
|
+
}
|
|
68063
|
+
function frameRedelivery(capturedText) {
|
|
68064
|
+
return `${REDELIVERY_PREFIX}
|
|
68065
|
+
|
|
68066
|
+
${capturedText.trim()}`;
|
|
68067
|
+
}
|
|
68068
|
+
function decideRedeliver(input) {
|
|
68069
|
+
const text4 = input.capturedText.trim();
|
|
68070
|
+
if (text4.length === 0)
|
|
68071
|
+
return { redeliver: false, skipReason: "empty-text" };
|
|
68072
|
+
if (!input.trailingIsText)
|
|
68073
|
+
return { redeliver: false, skipReason: "trailing-not-text" };
|
|
68074
|
+
if (input.alreadyRedelivered)
|
|
68075
|
+
return { redeliver: false, skipReason: "already-redelivered" };
|
|
68076
|
+
if (input.hasDeliveredText)
|
|
68077
|
+
return { redeliver: false, skipReason: "already-delivered" };
|
|
68078
|
+
if (input.ageMs > input.maxAgeMs)
|
|
68079
|
+
return { redeliver: false, skipReason: "stale" };
|
|
68080
|
+
return { redeliver: true, framedText: frameRedelivery(text4) };
|
|
67374
68081
|
}
|
|
67375
68082
|
|
|
67376
68083
|
// text-voice-scrub.ts
|
|
@@ -68360,10 +69067,10 @@ function resolveAgentDirFromEnv() {
|
|
|
68360
69067
|
|
|
68361
69068
|
// active-reactions.ts
|
|
68362
69069
|
import { readFileSync as readFileSync22, writeFileSync as writeFileSync19, renameSync as renameSync7, existsSync as existsSync23, unlinkSync as unlinkSync11 } from "node:fs";
|
|
68363
|
-
import { join as
|
|
69070
|
+
import { join as join27 } from "node:path";
|
|
68364
69071
|
var ACTIVE_REACTIONS_FILENAME = ".active-reactions.json";
|
|
68365
69072
|
function reactionsPath(agentDir) {
|
|
68366
|
-
return
|
|
69073
|
+
return join27(agentDir, ACTIVE_REACTIONS_FILENAME);
|
|
68367
69074
|
}
|
|
68368
69075
|
function readActiveReactions(agentDir) {
|
|
68369
69076
|
const p = reactionsPath(agentDir);
|
|
@@ -68428,10 +69135,10 @@ function clearActiveReactions(agentDir) {
|
|
|
68428
69135
|
|
|
68429
69136
|
// active-reactions.ts
|
|
68430
69137
|
import { readFileSync as readFileSync23, writeFileSync as writeFileSync20, renameSync as renameSync8, existsSync as existsSync24, unlinkSync as unlinkSync12 } from "node:fs";
|
|
68431
|
-
import { join as
|
|
69138
|
+
import { join as join28 } from "node:path";
|
|
68432
69139
|
var ACTIVE_REACTIONS_FILENAME2 = ".active-reactions.json";
|
|
68433
69140
|
function reactionsPath2(agentDir) {
|
|
68434
|
-
return
|
|
69141
|
+
return join28(agentDir, ACTIVE_REACTIONS_FILENAME2);
|
|
68435
69142
|
}
|
|
68436
69143
|
function readActiveReactions2(agentDir) {
|
|
68437
69144
|
const p = reactionsPath2(agentDir);
|
|
@@ -69349,12 +70056,12 @@ async function approvalRecord(args, opts) {
|
|
|
69349
70056
|
|
|
69350
70057
|
// quota-check.ts
|
|
69351
70058
|
import { readFileSync as readFileSync24, existsSync as existsSync25 } from "fs";
|
|
69352
|
-
import { join as
|
|
70059
|
+
import { join as join29 } from "path";
|
|
69353
70060
|
var OAUTH_BETA2 = "oauth-2025-04-20";
|
|
69354
70061
|
var DEFAULT_USER_AGENT2 = "claude-cli/1.0.0 (external, cli)";
|
|
69355
70062
|
var DEFAULT_PROBE_MODEL2 = "claude-haiku-4-5-20251001";
|
|
69356
70063
|
function readOauthToken2(claudeConfigDir) {
|
|
69357
|
-
const tokenFile =
|
|
70064
|
+
const tokenFile = join29(claudeConfigDir, ".oauth-token");
|
|
69358
70065
|
if (!existsSync25(tokenFile))
|
|
69359
70066
|
return null;
|
|
69360
70067
|
try {
|
|
@@ -69754,18 +70461,32 @@ async function injectSlashCommand(agentName3, command, opts = {}) {
|
|
|
69754
70461
|
const socket = opts.socketName ?? defaultSocketName(agentName3);
|
|
69755
70462
|
const session = opts.sessionName ?? agentName3;
|
|
69756
70463
|
const settleMs = opts.settleMs ?? 2000;
|
|
69757
|
-
const
|
|
70464
|
+
const signalMode = !!(opts.successPattern || opts.errorPattern);
|
|
70465
|
+
const timeoutMs = opts.timeoutMs ?? (signalMode ? 8000 : 5000);
|
|
69758
70466
|
return withPaneLock(`${socket}:${session}`, () => injectSlashCommandWith(makeTmuxRunner(tmuxBin), {
|
|
69759
70467
|
socket,
|
|
69760
70468
|
session,
|
|
69761
70469
|
command: command.trim(),
|
|
69762
70470
|
settleMs,
|
|
69763
70471
|
timeoutMs,
|
|
69764
|
-
precondition: opts.precondition
|
|
70472
|
+
precondition: opts.precondition,
|
|
70473
|
+
successPattern: opts.successPattern,
|
|
70474
|
+
errorPattern: opts.errorPattern,
|
|
70475
|
+
settleBeforeSendMs: opts.settleBeforeSendMs
|
|
69765
70476
|
}));
|
|
69766
70477
|
}
|
|
69767
70478
|
async function injectSlashCommandWith(runner, args) {
|
|
69768
|
-
const {
|
|
70479
|
+
const {
|
|
70480
|
+
socket,
|
|
70481
|
+
session,
|
|
70482
|
+
command,
|
|
70483
|
+
settleMs,
|
|
70484
|
+
timeoutMs,
|
|
70485
|
+
precondition,
|
|
70486
|
+
successPattern,
|
|
70487
|
+
errorPattern,
|
|
70488
|
+
settleBeforeSendMs
|
|
70489
|
+
} = args;
|
|
69769
70490
|
let bareVerb;
|
|
69770
70491
|
try {
|
|
69771
70492
|
bareVerb = validateInjectCommand(command);
|
|
@@ -69806,6 +70527,17 @@ async function injectSlashCommandWith(runner, args) {
|
|
|
69806
70527
|
errorMessage: "inject precondition returned false at write time; send aborted " + "(no keys sent)."
|
|
69807
70528
|
};
|
|
69808
70529
|
}
|
|
70530
|
+
if (settleBeforeSendMs && settleBeforeSendMs > 0) {
|
|
70531
|
+
const settleStart = Date.now();
|
|
70532
|
+
let prevSettle = runner.capture(socket, session) ?? "";
|
|
70533
|
+
while (Date.now() - settleStart < settleBeforeSendMs) {
|
|
70534
|
+
await sleep(POLL_INTERVAL_MS2);
|
|
70535
|
+
const cur = runner.capture(socket, session) ?? "";
|
|
70536
|
+
if (cur === prevSettle)
|
|
70537
|
+
break;
|
|
70538
|
+
prevSettle = cur;
|
|
70539
|
+
}
|
|
70540
|
+
}
|
|
69809
70541
|
const before = runner.capture(socket, session) ?? "";
|
|
69810
70542
|
try {
|
|
69811
70543
|
runner.send(socket, session, ["send-keys", "-l", command]);
|
|
@@ -69824,9 +70556,23 @@ async function injectSlashCommandWith(runner, args) {
|
|
|
69824
70556
|
const start = Date.now();
|
|
69825
70557
|
let last = before;
|
|
69826
70558
|
let stableSince = null;
|
|
70559
|
+
const signalMode = !!(successPattern || errorPattern);
|
|
69827
70560
|
while (Date.now() - start < timeoutMs) {
|
|
69828
70561
|
await sleep(POLL_INTERVAL_MS2);
|
|
69829
70562
|
const cur = runner.capture(socket, session) ?? "";
|
|
70563
|
+
if (signalMode) {
|
|
70564
|
+
last = cur;
|
|
70565
|
+
if (cur !== before) {
|
|
70566
|
+
const { output: region } = diffPane(before, cur, command);
|
|
70567
|
+
const regionLines = region.split(`
|
|
70568
|
+
`).map((l) => l.trim());
|
|
70569
|
+
if (errorPattern && regionLines.some((l) => errorPattern.test(l)))
|
|
70570
|
+
break;
|
|
70571
|
+
if (successPattern && regionLines.some((l) => successPattern.test(l)))
|
|
70572
|
+
break;
|
|
70573
|
+
}
|
|
70574
|
+
continue;
|
|
70575
|
+
}
|
|
69830
70576
|
if (cur === last && cur !== before) {
|
|
69831
70577
|
if (stableSince === null) {
|
|
69832
70578
|
stableSince = Date.now();
|
|
@@ -70261,7 +71007,11 @@ async function handleModelCommand(parsed, deps) {
|
|
|
70261
71007
|
const verbHtml = `\`/model ${deps.escapeHtml(model)}\``;
|
|
70262
71008
|
let result;
|
|
70263
71009
|
try {
|
|
70264
|
-
result = await deps.inject(deps.getAgentName(), `/model ${model}
|
|
71010
|
+
result = await deps.inject(deps.getAgentName(), `/model ${model}`, {
|
|
71011
|
+
successPattern: MODEL_SWITCH_CONFIRMATION_PREFIX,
|
|
71012
|
+
errorPattern: MODEL_SWITCH_ERROR_RE,
|
|
71013
|
+
settleBeforeSendMs: 1500
|
|
71014
|
+
});
|
|
70265
71015
|
} catch (err) {
|
|
70266
71016
|
const msg = err instanceof Error ? err.message : String(err);
|
|
70267
71017
|
return {
|
|
@@ -70269,21 +71019,21 @@ async function handleModelCommand(parsed, deps) {
|
|
|
70269
71019
|
html: true
|
|
70270
71020
|
};
|
|
70271
71021
|
}
|
|
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);
|
|
71022
|
+
if (result.outcome === "ok" || result.outcome === "ok_no_output") {
|
|
71023
|
+
const confirmation = result.outcome === "ok" ? modelSwitchConfirmationLine(result.output) : null;
|
|
70286
71024
|
if (confirmation) {
|
|
71025
|
+
if (isKeptModelConfirmation(confirmation)) {
|
|
71026
|
+
return {
|
|
71027
|
+
text: [
|
|
71028
|
+
`${verbHtml}`,
|
|
71029
|
+
deps.preBlock(confirmation),
|
|
71030
|
+
...result.truncated ? ["_truncated_"] : [],
|
|
71031
|
+
PERSIST_NOTE
|
|
71032
|
+
].join(`
|
|
71033
|
+
`),
|
|
71034
|
+
html: true
|
|
71035
|
+
};
|
|
71036
|
+
}
|
|
70287
71037
|
const confirmed = sessionModelFromConfirmation(confirmation) ?? model;
|
|
70288
71038
|
return {
|
|
70289
71039
|
text: [
|
|
@@ -70294,26 +71044,31 @@ async function handleModelCommand(parsed, deps) {
|
|
|
70294
71044
|
].join(`
|
|
70295
71045
|
`),
|
|
70296
71046
|
html: true,
|
|
70297
|
-
|
|
71047
|
+
selectedModel: confirmed
|
|
70298
71048
|
};
|
|
70299
71049
|
}
|
|
70300
|
-
|
|
70301
|
-
|
|
70302
|
-
|
|
70303
|
-
|
|
70304
|
-
|
|
71050
|
+
const errLine = result.outcome === "ok" ? modelSwitchErrorLine(result.output) : null;
|
|
71051
|
+
if (errLine) {
|
|
71052
|
+
return {
|
|
71053
|
+
text: [
|
|
71054
|
+
`\u274c ${verbHtml} \u2014 the switch did not take:`,
|
|
71055
|
+
deps.preBlock(errLine),
|
|
71056
|
+
"Check `/model` for a valid, available model."
|
|
71057
|
+
].join(`
|
|
70305
71058
|
`),
|
|
70306
|
-
|
|
70307
|
-
|
|
70308
|
-
|
|
70309
|
-
|
|
71059
|
+
html: true
|
|
71060
|
+
};
|
|
71061
|
+
}
|
|
71062
|
+
const optimisticLabel = optimisticModelRecordLabel(model);
|
|
70310
71063
|
return {
|
|
70311
71064
|
text: [
|
|
70312
|
-
`${verbHtml} \u2014 sent, but
|
|
71065
|
+
`${verbHtml} \u2014 sent, but couldn't read a confirmation line. \`/status\` will show the live model once it's confirmed.`,
|
|
70313
71066
|
PERSIST_NOTE
|
|
70314
71067
|
].join(`
|
|
70315
71068
|
`),
|
|
70316
|
-
html: true
|
|
71069
|
+
html: true,
|
|
71070
|
+
selectedModel: optimisticLabel,
|
|
71071
|
+
optimistic: true
|
|
70317
71072
|
};
|
|
70318
71073
|
}
|
|
70319
71074
|
if (result.errorCode === "session_missing") {
|
|
@@ -70376,6 +71131,15 @@ function expandSrAlias(arg) {
|
|
|
70376
71131
|
function srFriendlyLabel(srName) {
|
|
70377
71132
|
return SR_MODEL_LABELS[srName] ?? srName.replace(/^sr-/, "").replace(/-/g, " ");
|
|
70378
71133
|
}
|
|
71134
|
+
function optimisticModelRecordLabel(token) {
|
|
71135
|
+
if (isSrModel(token))
|
|
71136
|
+
return token;
|
|
71137
|
+
const lower = token.toLowerCase();
|
|
71138
|
+
if (MODEL_ALIASES.includes(lower)) {
|
|
71139
|
+
return lower.charAt(0).toUpperCase() + lower.slice(1);
|
|
71140
|
+
}
|
|
71141
|
+
return token;
|
|
71142
|
+
}
|
|
70379
71143
|
function classifyDiscoveredOptions(options) {
|
|
70380
71144
|
return {
|
|
70381
71145
|
claude: options.filter((o) => !o.label.startsWith("sr-") && !o.label.includes("/") && (/^[A-Z]/.test(o.label) || o.label.startsWith("claude-"))),
|
|
@@ -70548,7 +71312,11 @@ async function handleModelMenuCallback(data, deps) {
|
|
|
70548
71312
|
}
|
|
70549
71313
|
let aliasResult;
|
|
70550
71314
|
try {
|
|
70551
|
-
aliasResult = await deps.inject(deps.getAgentName(), `/model ${alias}
|
|
71315
|
+
aliasResult = await deps.inject(deps.getAgentName(), `/model ${alias}`, {
|
|
71316
|
+
successPattern: MODEL_SWITCH_CONFIRMATION_PREFIX,
|
|
71317
|
+
errorPattern: MODEL_SWITCH_ERROR_RE,
|
|
71318
|
+
settleBeforeSendMs: 1500
|
|
71319
|
+
});
|
|
70552
71320
|
} catch (err) {
|
|
70553
71321
|
const msg = err instanceof Error ? err.message : String(err);
|
|
70554
71322
|
return {
|
|
@@ -70556,16 +71324,32 @@ async function handleModelMenuCallback(data, deps) {
|
|
|
70556
71324
|
reply: await menuWithBanner(deps, `\u274c Switch to **${deps.escapeHtml(alias)}** failed: ${deps.escapeHtml(msg)}`)
|
|
70557
71325
|
};
|
|
70558
71326
|
}
|
|
70559
|
-
if (aliasResult.outcome === "ok") {
|
|
70560
|
-
const confirmation = modelSwitchConfirmationLine(aliasResult.output)
|
|
70561
|
-
|
|
71327
|
+
if (aliasResult.outcome === "ok" || aliasResult.outcome === "ok_no_output") {
|
|
71328
|
+
const confirmation = aliasResult.outcome === "ok" ? modelSwitchConfirmationLine(aliasResult.output) : null;
|
|
71329
|
+
if (confirmation) {
|
|
71330
|
+
const kept = isKeptModelConfirmation(confirmation);
|
|
71331
|
+
return {
|
|
71332
|
+
answer: confirmation,
|
|
71333
|
+
reply: await menuWithBannerStatic(deps, `\u2705 ${deps.escapeHtml(confirmation)}`),
|
|
71334
|
+
...kept ? {} : {
|
|
71335
|
+
selectedModel: sessionModelFromConfirmation(confirmation) ?? optimisticModelRecordLabel(alias),
|
|
71336
|
+
selectedModelToken: alias
|
|
71337
|
+
}
|
|
71338
|
+
};
|
|
71339
|
+
}
|
|
71340
|
+
const aliasErr = aliasResult.outcome === "ok" ? modelSwitchErrorLine(aliasResult.output) : null;
|
|
71341
|
+
if (aliasErr) {
|
|
71342
|
+
return {
|
|
71343
|
+
answer: "Switch failed",
|
|
71344
|
+
reply: await menuWithBanner(deps, `\u274c Switch to **${deps.escapeHtml(alias)}** did not take: ${deps.escapeHtml(aliasErr)}`)
|
|
71345
|
+
};
|
|
71346
|
+
}
|
|
71347
|
+
const optimisticLabel = optimisticModelRecordLabel(alias);
|
|
70562
71348
|
return {
|
|
70563
|
-
answer:
|
|
70564
|
-
reply: await menuWithBannerStatic(deps,
|
|
70565
|
-
|
|
70566
|
-
|
|
70567
|
-
selectedModelToken: alias
|
|
70568
|
-
}
|
|
71349
|
+
answer: `Sent /model ${alias} \u2014 check /status`,
|
|
71350
|
+
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.`),
|
|
71351
|
+
selectedModel: optimisticLabel,
|
|
71352
|
+
selectedModelToken: alias
|
|
70569
71353
|
};
|
|
70570
71354
|
}
|
|
70571
71355
|
return {
|
|
@@ -70662,7 +71446,7 @@ function modelSwitchConfirmationLine(output) {
|
|
|
70662
71446
|
`).map((l) => l.trim()).find((l) => MODEL_SWITCH_CONFIRMATION_PREFIX.test(l));
|
|
70663
71447
|
return line && line.length > 0 ? line : null;
|
|
70664
71448
|
}
|
|
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;
|
|
71449
|
+
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
71450
|
function modelSwitchErrorLine(output) {
|
|
70667
71451
|
const line = output.split(`
|
|
70668
71452
|
`).map((l) => l.trim()).find((l) => MODEL_SWITCH_ERROR_RE.test(l));
|
|
@@ -70711,7 +71495,7 @@ async function menuWithBannerStatic(deps, banner) {
|
|
|
70711
71495
|
|
|
70712
71496
|
// gateway/session-model-file.ts
|
|
70713
71497
|
import { readFileSync as readFileSync25, writeFileSync as writeFileSync21, renameSync as renameSync9, rmSync as rmSync4 } from "node:fs";
|
|
70714
|
-
import { join as
|
|
71498
|
+
import { join as join30 } from "node:path";
|
|
70715
71499
|
|
|
70716
71500
|
// gateway/model-command.ts
|
|
70717
71501
|
var MODEL_ARG_RE2 = /^[A-Za-z0-9][A-Za-z0-9._/\[\]-]{0,99}$/;
|
|
@@ -70740,18 +71524,18 @@ function writeSessionModelFile(agentDir, model, configuredDefaultAtWrite) {
|
|
|
70740
71524
|
if (!isValidModelArg2(model)) {
|
|
70741
71525
|
throw new Error(`refusing to persist non-canonical session model token: ${JSON.stringify(model)}`);
|
|
70742
71526
|
}
|
|
70743
|
-
atomicWrite(
|
|
71527
|
+
atomicWrite(join30(agentDir, SESSION_MODEL_FILE), serializeSessionModel({ model, configuredDefaultAtWrite, ts: Date.now() }));
|
|
70744
71528
|
}
|
|
70745
71529
|
function readSessionModelFileRaw(agentDir) {
|
|
70746
71530
|
try {
|
|
70747
|
-
return readFileSync25(
|
|
71531
|
+
return readFileSync25(join30(agentDir, SESSION_MODEL_FILE), "utf8");
|
|
70748
71532
|
} catch {
|
|
70749
71533
|
return null;
|
|
70750
71534
|
}
|
|
70751
71535
|
}
|
|
70752
71536
|
function clearSessionModelFile(agentDir) {
|
|
70753
71537
|
try {
|
|
70754
|
-
rmSync4(
|
|
71538
|
+
rmSync4(join30(agentDir, SESSION_MODEL_FILE), { force: true });
|
|
70755
71539
|
} catch {}
|
|
70756
71540
|
}
|
|
70757
71541
|
function restoreSessionModelFileRaw(agentDir, raw) {
|
|
@@ -70760,12 +71544,12 @@ function restoreSessionModelFileRaw(agentDir, raw) {
|
|
|
70760
71544
|
return;
|
|
70761
71545
|
}
|
|
70762
71546
|
try {
|
|
70763
|
-
atomicWrite(
|
|
71547
|
+
atomicWrite(join30(agentDir, SESSION_MODEL_FILE), raw);
|
|
70764
71548
|
} catch {}
|
|
70765
71549
|
}
|
|
70766
71550
|
function readConfiguredDefaultModel(agentDir) {
|
|
70767
71551
|
try {
|
|
70768
|
-
const v = readFileSync25(
|
|
71552
|
+
const v = readFileSync25(join30(agentDir, CONFIGURED_DEFAULT_MODEL_FILE), "utf8").trim();
|
|
70769
71553
|
return v.length > 0 ? v : null;
|
|
70770
71554
|
} catch {
|
|
70771
71555
|
return null;
|
|
@@ -70777,12 +71561,12 @@ function writeSessionEffortFile(agentDir, level, configuredDefaultAtWrite) {
|
|
|
70777
71561
|
if (!EFFORT_LEVEL_RE.test(level)) {
|
|
70778
71562
|
throw new Error(`refusing to persist non-allowlisted effort level: ${JSON.stringify(level)}`);
|
|
70779
71563
|
}
|
|
70780
|
-
atomicWrite(
|
|
71564
|
+
atomicWrite(join30(agentDir, SESSION_EFFORT_FILE), `${JSON.stringify({ level, configuredDefaultAtWrite: configuredDefaultAtWrite ?? "", ts: Date.now() })}
|
|
70781
71565
|
`);
|
|
70782
71566
|
}
|
|
70783
71567
|
function clearSessionEffortFile(agentDir) {
|
|
70784
71568
|
try {
|
|
70785
|
-
rmSync4(
|
|
71569
|
+
rmSync4(join30(agentDir, SESSION_EFFORT_FILE), { force: true });
|
|
70786
71570
|
} catch {}
|
|
70787
71571
|
}
|
|
70788
71572
|
var PREMIUM_RECOVERY_FILE = ".premium-recovery";
|
|
@@ -70805,13 +71589,13 @@ function writePremiumRecoveryFile(agentDir, premiumModel, chats) {
|
|
|
70805
71589
|
if (clean.length === 0) {
|
|
70806
71590
|
throw new Error("refusing to persist premium-recovery marker with no chats to notify");
|
|
70807
71591
|
}
|
|
70808
|
-
atomicWrite(
|
|
71592
|
+
atomicWrite(join30(agentDir, PREMIUM_RECOVERY_FILE), `${JSON.stringify({ premiumModel, chats: clean, ts: Date.now() })}
|
|
70809
71593
|
`);
|
|
70810
71594
|
}
|
|
70811
71595
|
function readPremiumRecoveryFile(agentDir) {
|
|
70812
71596
|
let raw;
|
|
70813
71597
|
try {
|
|
70814
|
-
raw = readFileSync25(
|
|
71598
|
+
raw = readFileSync25(join30(agentDir, PREMIUM_RECOVERY_FILE), "utf8");
|
|
70815
71599
|
} catch {
|
|
70816
71600
|
return null;
|
|
70817
71601
|
}
|
|
@@ -70824,7 +71608,7 @@ function readPremiumRecoveryFile(agentDir) {
|
|
|
70824
71608
|
}
|
|
70825
71609
|
function clearPremiumRecoveryFile(agentDir) {
|
|
70826
71610
|
try {
|
|
70827
|
-
rmSync4(
|
|
71611
|
+
rmSync4(join30(agentDir, PREMIUM_RECOVERY_FILE), { force: true });
|
|
70828
71612
|
} catch {}
|
|
70829
71613
|
}
|
|
70830
71614
|
|
|
@@ -71016,7 +71800,7 @@ function makeIo(agentName3, opts) {
|
|
|
71016
71800
|
socket: opts.socketName ?? `switchroom-${agentName3}`,
|
|
71017
71801
|
session: opts.sessionName ?? agentName3,
|
|
71018
71802
|
stepMs: opts.stepMs ?? 600,
|
|
71019
|
-
timeoutMs: opts.timeoutMs ??
|
|
71803
|
+
timeoutMs: opts.timeoutMs ?? 12000,
|
|
71020
71804
|
sleep: opts._sleep ?? realSleep,
|
|
71021
71805
|
log: opts._log ?? ((line) => process.stderr.write(`${line}
|
|
71022
71806
|
`)),
|
|
@@ -71039,7 +71823,7 @@ function sendLiteral(io, text4) {
|
|
|
71039
71823
|
function sendKey(io, key) {
|
|
71040
71824
|
io.runner.send(io.socket, io.session, ["send-keys", key]);
|
|
71041
71825
|
}
|
|
71042
|
-
async function openPicker(io) {
|
|
71826
|
+
async function openPicker(io, deadlineMs) {
|
|
71043
71827
|
sendLiteral(io, "/model");
|
|
71044
71828
|
sendKey(io, "Enter");
|
|
71045
71829
|
for (;; ) {
|
|
@@ -71048,10 +71832,24 @@ async function openPicker(io) {
|
|
|
71048
71832
|
const parsed = parseModelPicker(pane);
|
|
71049
71833
|
if (parsed?.footerSeen)
|
|
71050
71834
|
return parsed;
|
|
71051
|
-
if (expired(io))
|
|
71835
|
+
if (deadlineMs != null && Date.now() >= deadlineMs || expired(io)) {
|
|
71052
71836
|
return parsed;
|
|
71837
|
+
}
|
|
71053
71838
|
}
|
|
71054
71839
|
}
|
|
71840
|
+
async function openPickerWithRetry(io) {
|
|
71841
|
+
const remaining = io.timeoutMs - (Date.now() - io.startedAt);
|
|
71842
|
+
const firstDeadline = Date.now() + Math.max(io.stepMs * 2, Math.floor(remaining / 2));
|
|
71843
|
+
const parsed = await openPicker(io, firstDeadline);
|
|
71844
|
+
if (parsed?.footerSeen)
|
|
71845
|
+
return parsed;
|
|
71846
|
+
if (expired(io))
|
|
71847
|
+
return parsed;
|
|
71848
|
+
await dismissPicker(io);
|
|
71849
|
+
if (expired(io))
|
|
71850
|
+
return parsed;
|
|
71851
|
+
return openPicker(io);
|
|
71852
|
+
}
|
|
71055
71853
|
async function dismissPicker(io) {
|
|
71056
71854
|
for (let attempt = 0;attempt < 2; attempt++) {
|
|
71057
71855
|
try {
|
|
@@ -71081,7 +71879,7 @@ async function discoverModels(agentName3, opts = {}) {
|
|
|
71081
71879
|
let parsed = null;
|
|
71082
71880
|
let dismissed = true;
|
|
71083
71881
|
try {
|
|
71084
|
-
parsed = await
|
|
71882
|
+
parsed = await openPickerWithRetry(io);
|
|
71085
71883
|
} finally {
|
|
71086
71884
|
dismissed = await dismissOrWarn(io, "discover");
|
|
71087
71885
|
}
|
|
@@ -71106,7 +71904,7 @@ async function selectModel(agentName3, targetLabel, opts = {}) {
|
|
|
71106
71904
|
io.startedAt = Date.now();
|
|
71107
71905
|
let selected = false;
|
|
71108
71906
|
try {
|
|
71109
|
-
const parsed = await
|
|
71907
|
+
const parsed = await openPickerWithRetry(io);
|
|
71110
71908
|
if (!parsed || !parsed.footerSeen) {
|
|
71111
71909
|
return { ok: false, reason: "picker did not render \u2014 agent may be mid-turn" };
|
|
71112
71910
|
}
|
|
@@ -71163,7 +71961,7 @@ function extractConfirmation(pane) {
|
|
|
71163
71961
|
}
|
|
71164
71962
|
|
|
71165
71963
|
// ../src/agents/scaffold.ts
|
|
71166
|
-
import { join as
|
|
71964
|
+
import { join as join33, resolve as resolve6 } from "node:path";
|
|
71167
71965
|
init_atomic();
|
|
71168
71966
|
|
|
71169
71967
|
// ../src/agents/agent-uid.ts
|
|
@@ -71187,8 +71985,8 @@ var CONTAINER_DEFAULT_UTC_ZONES = new Set([
|
|
|
71187
71985
|
]);
|
|
71188
71986
|
|
|
71189
71987
|
// ../src/cli/agent-config.ts
|
|
71190
|
-
import { join as
|
|
71191
|
-
import { homedir as
|
|
71988
|
+
import { join as join31 } from "node:path";
|
|
71989
|
+
import { homedir as homedir10 } from "node:os";
|
|
71192
71990
|
|
|
71193
71991
|
// ../src/cli/helpers.ts
|
|
71194
71992
|
init_loader();
|
|
@@ -71208,12 +72006,12 @@ var WEBKITE_VAULT_KEYS = new Set([
|
|
|
71208
72006
|
init_overlay_loader();
|
|
71209
72007
|
|
|
71210
72008
|
// ../src/cli/agent-config.ts
|
|
71211
|
-
var AUDIT_ROOT =
|
|
72009
|
+
var AUDIT_ROOT = join31(homedir10(), ".switchroom", "audit");
|
|
71212
72010
|
|
|
71213
72011
|
// ../src/agents/profiles.ts
|
|
71214
72012
|
var import_handlebars = __toESM(require_lib(), 1);
|
|
71215
72013
|
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
|
|
72014
|
+
import { resolve as resolve5, join as join32, sep as pathSep } from "node:path";
|
|
71217
72015
|
var PROFILES_ROOT = resolve5(import.meta.dirname, "../../profiles");
|
|
71218
72016
|
import_handlebars.default.registerHelper("json", (value) => {
|
|
71219
72017
|
return new import_handlebars.default.SafeString(JSON.stringify(value, null, 2));
|
|
@@ -71224,7 +72022,7 @@ import_handlebars.default.registerHelper("isNumber", (value) => {
|
|
|
71224
72022
|
var SHARED_FRAGMENTS_DIR = resolve5(PROFILES_ROOT, "_shared");
|
|
71225
72023
|
var SHARED_FRAGMENTS = ["vault-protocol", "agent-self-service", "execution-discipline", "reply-discipline", "dev-protocol"];
|
|
71226
72024
|
for (const name of SHARED_FRAGMENTS) {
|
|
71227
|
-
const fragPath =
|
|
72025
|
+
const fragPath = join32(SHARED_FRAGMENTS_DIR, `${name}.md.hbs`);
|
|
71228
72026
|
if (existsSync26(fragPath)) {
|
|
71229
72027
|
import_handlebars.default.registerPartial(name, readFileSync26(fragPath, "utf-8"));
|
|
71230
72028
|
}
|
|
@@ -71936,7 +72734,7 @@ init_overlay_loader();
|
|
|
71936
72734
|
init_merge();
|
|
71937
72735
|
var import_yaml4 = __toESM(require_dist(), 1);
|
|
71938
72736
|
import { readFileSync as readFileSync27, existsSync as existsSync27 } from "node:fs";
|
|
71939
|
-
import { homedir as
|
|
72737
|
+
import { homedir as homedir11 } from "node:os";
|
|
71940
72738
|
import { resolve as resolve7 } from "node:path";
|
|
71941
72739
|
|
|
71942
72740
|
class ConfigError2 extends Error {
|
|
@@ -71993,7 +72791,7 @@ function coerceLegacyGoogleWorkspaceKeys2(parsed, filePath) {
|
|
|
71993
72791
|
}
|
|
71994
72792
|
function findConfigFile2(startDir) {
|
|
71995
72793
|
const envPath = process.env.SWITCHROOM_CONFIG;
|
|
71996
|
-
const home2 =
|
|
72794
|
+
const home2 = homedir11();
|
|
71997
72795
|
const userDir = resolve7(home2, ".switchroom");
|
|
71998
72796
|
const searchPaths = [
|
|
71999
72797
|
envPath ? resolve7(envPath) : null,
|
|
@@ -72632,7 +73430,7 @@ function numField(obj, key) {
|
|
|
72632
73430
|
|
|
72633
73431
|
// gateway/context-occupancy.ts
|
|
72634
73432
|
import { mkdirSync as mkdirSync23, writeFileSync as writeFileSync23 } from "node:fs";
|
|
72635
|
-
import { join as
|
|
73433
|
+
import { join as join34 } from "node:path";
|
|
72636
73434
|
var CONTEXT_OCCUPANCY_FILENAME = "context-occupancy.json";
|
|
72637
73435
|
var TIGHT_FRACTION = 0.8;
|
|
72638
73436
|
function buildContextOccupancy(occupancy, cap, now) {
|
|
@@ -72655,7 +73453,7 @@ function buildContextOccupancy(occupancy, cap, now) {
|
|
|
72655
73453
|
}
|
|
72656
73454
|
function writeContextOccupancySnapshot(stateDir, snapshot, deps) {
|
|
72657
73455
|
try {
|
|
72658
|
-
const path2 =
|
|
73456
|
+
const path2 = join34(stateDir, CONTEXT_OCCUPANCY_FILENAME);
|
|
72659
73457
|
(deps?.mkdir ?? ((p, o) => mkdirSync23(p, o)))(stateDir, { recursive: true });
|
|
72660
73458
|
(deps?.writeFile ?? ((p, d) => writeFileSync23(p, d)))(path2, JSON.stringify(snapshot, null, 2) + `
|
|
72661
73459
|
`);
|
|
@@ -73150,12 +73948,12 @@ function startWebhookIngestServer(opts) {
|
|
|
73150
73948
|
|
|
73151
73949
|
// ../src/web/webhook-gateway-record.ts
|
|
73152
73950
|
import { appendFileSync as appendFileSync5, mkdirSync as mkdirSync26 } from "fs";
|
|
73153
|
-
import { join as
|
|
73154
|
-
import { homedir as
|
|
73951
|
+
import { join as join37 } from "path";
|
|
73952
|
+
import { homedir as homedir13 } from "os";
|
|
73155
73953
|
|
|
73156
73954
|
// ../src/web/webhook-handler.ts
|
|
73157
73955
|
import { appendFileSync as appendFileSync4, existsSync as existsSync31, mkdirSync as mkdirSync24, readFileSync as readFileSync29, writeFileSync as writeFileSync24 } from "fs";
|
|
73158
|
-
import { join as
|
|
73956
|
+
import { join as join35 } from "path";
|
|
73159
73957
|
var DEDUP_MAX = 1000;
|
|
73160
73958
|
var DEDUP_TTL_MS = 24 * 60 * 60 * 1000;
|
|
73161
73959
|
function loadDedupFile(path2) {
|
|
@@ -73184,8 +73982,8 @@ var agentDedupCache = new Map;
|
|
|
73184
73982
|
function createFileDedupStore(resolveAgentDir) {
|
|
73185
73983
|
return {
|
|
73186
73984
|
check(agent, deliveryId, now) {
|
|
73187
|
-
const telegramDir =
|
|
73188
|
-
const filePath =
|
|
73985
|
+
const telegramDir = join35(resolveAgentDir(agent), "telegram");
|
|
73986
|
+
const filePath = join35(telegramDir, "webhook-dedup.json");
|
|
73189
73987
|
if (!agentDedupCache.has(agent)) {
|
|
73190
73988
|
agentDedupCache.set(agent, loadDedupFile(filePath));
|
|
73191
73989
|
}
|
|
@@ -73207,8 +74005,8 @@ var throttleIssueWindow = new Map;
|
|
|
73207
74005
|
|
|
73208
74006
|
// ../src/web/webhook-dispatch.ts
|
|
73209
74007
|
import { existsSync as existsSync32, mkdirSync as mkdirSync25, readFileSync as readFileSync30, writeFileSync as writeFileSync25 } from "fs";
|
|
73210
|
-
import { join as
|
|
73211
|
-
import { homedir as
|
|
74008
|
+
import { join as join36 } from "path";
|
|
74009
|
+
import { homedir as homedir12 } from "os";
|
|
73212
74010
|
|
|
73213
74011
|
// ../src/agent-scheduler/ipc-client.ts
|
|
73214
74012
|
import { createConnection as createConnection2 } from "node:net";
|
|
@@ -73527,8 +74325,8 @@ function createFileCooldownStore(resolveAgentDir) {
|
|
|
73527
74325
|
isCoolingDown(agent, key, cooldownMs, now) {
|
|
73528
74326
|
if (cooldownMs <= 0)
|
|
73529
74327
|
return false;
|
|
73530
|
-
const telegramDir =
|
|
73531
|
-
const filePath =
|
|
74328
|
+
const telegramDir = join36(resolveAgentDir(agent), "telegram");
|
|
74329
|
+
const filePath = join36(telegramDir, "webhook-cooldown.json");
|
|
73532
74330
|
if (!cache.has(agent)) {
|
|
73533
74331
|
cache.set(agent, loadCooldownFile(filePath));
|
|
73534
74332
|
}
|
|
@@ -73585,9 +74383,9 @@ async function defaultInject(socketPath, agentName3, inbound) {
|
|
|
73585
74383
|
}
|
|
73586
74384
|
function injectWebhookInbound(agent, prompt, ctx, deps = {}) {
|
|
73587
74385
|
const log = deps.log ?? ((s) => process.stderr.write(s));
|
|
73588
|
-
const resolveAgentDir = deps.resolveAgentDir ?? ((a) =>
|
|
74386
|
+
const resolveAgentDir = deps.resolveAgentDir ?? ((a) => join36(homedir12(), ".switchroom", "agents", a));
|
|
73589
74387
|
const now = (deps.now ?? Date.now)();
|
|
73590
|
-
const socketPath =
|
|
74388
|
+
const socketPath = join36(resolveAgentDir(agent), "telegram", "gateway.sock");
|
|
73591
74389
|
const inbound = {
|
|
73592
74390
|
type: "inbound",
|
|
73593
74391
|
chatId: ctx.chatId,
|
|
@@ -73659,7 +74457,7 @@ function evaluateDispatch(args, deps = {}) {
|
|
|
73659
74457
|
const log = deps.log ?? ((s) => process.stderr.write(s));
|
|
73660
74458
|
const now = (deps.now ?? Date.now)();
|
|
73661
74459
|
const nowDate = deps.nowDate ?? (() => new Date(now));
|
|
73662
|
-
const resolveAgentDir = deps.resolveAgentDir ?? ((a) =>
|
|
74460
|
+
const resolveAgentDir = deps.resolveAgentDir ?? ((a) => join36(homedir12(), ".switchroom", "agents", a));
|
|
73663
74461
|
const cooldownStore = deps.cooldownStore ?? createFileCooldownStore(resolveAgentDir);
|
|
73664
74462
|
if (!DISPATCH_SOURCES.includes(args.source))
|
|
73665
74463
|
return 0;
|
|
@@ -73737,10 +74535,10 @@ var CONSOLIDATION_COMPLETED_EVENT = "consolidation.completed";
|
|
|
73737
74535
|
function recordWebhookEvent(rec, deps = {}) {
|
|
73738
74536
|
const log = deps.log ?? ((s) => process.stderr.write(s));
|
|
73739
74537
|
const now = rec.ts || (deps.now ?? Date.now)();
|
|
73740
|
-
const resolveAgentDir = deps.resolveAgentDir ?? ((a) =>
|
|
74538
|
+
const resolveAgentDir = deps.resolveAgentDir ?? ((a) => join37(homedir13(), ".switchroom", "agents", a));
|
|
73741
74539
|
const dedupStore = deps.dedupStore ?? createFileDedupStore(resolveAgentDir);
|
|
73742
74540
|
const agent = rec.agent;
|
|
73743
|
-
const telegramDir =
|
|
74541
|
+
const telegramDir = join37(resolveAgentDir(agent), "telegram");
|
|
73744
74542
|
if (rec.source === "github" && rec.delivery_id) {
|
|
73745
74543
|
const originalTs = dedupStore.check(agent, rec.delivery_id, now);
|
|
73746
74544
|
if (originalTs !== undefined) {
|
|
@@ -73749,7 +74547,7 @@ function recordWebhookEvent(rec, deps = {}) {
|
|
|
73749
74547
|
return { status: "deduped", ts: originalTs };
|
|
73750
74548
|
}
|
|
73751
74549
|
}
|
|
73752
|
-
const logPath =
|
|
74550
|
+
const logPath = join37(telegramDir, "webhook-events.jsonl");
|
|
73753
74551
|
try {
|
|
73754
74552
|
mkdirSync26(telegramDir, { recursive: true });
|
|
73755
74553
|
const record = {
|
|
@@ -77197,17 +77995,17 @@ import {
|
|
|
77197
77995
|
readFileSync as readFileSync31,
|
|
77198
77996
|
writeSync as writeSync5
|
|
77199
77997
|
} from "node:fs";
|
|
77200
|
-
import { join as
|
|
77998
|
+
import { join as join38 } from "node:path";
|
|
77201
77999
|
import { randomUUID as randomUUID5 } from "node:crypto";
|
|
77202
78000
|
var PROPOSALS_FILE2 = "skill-proposals.jsonl";
|
|
77203
78001
|
var REJECTED_FILE2 = "skill-proposals-rejected.jsonl";
|
|
77204
78002
|
var REJECTION_TTL_MS2 = 90 * 24 * 60 * 60 * 1000;
|
|
77205
78003
|
var PROPOSAL_SIM_THRESHOLD = 0.5;
|
|
77206
78004
|
function proposalsPath2(stateDir) {
|
|
77207
|
-
return
|
|
78005
|
+
return join38(stateDir, PROPOSALS_FILE2);
|
|
77208
78006
|
}
|
|
77209
78007
|
function rejectedPath2(stateDir) {
|
|
77210
|
-
return
|
|
78008
|
+
return join38(stateDir, REJECTED_FILE2);
|
|
77211
78009
|
}
|
|
77212
78010
|
function ensureDir3(stateDir) {
|
|
77213
78011
|
if (!existsSync33(stateDir)) {
|
|
@@ -78702,17 +79500,17 @@ import {
|
|
|
78702
79500
|
readdirSync as readdirSync6,
|
|
78703
79501
|
readFileSync as readFileSync37
|
|
78704
79502
|
} from "fs";
|
|
78705
|
-
import { join as
|
|
79503
|
+
import { join as join40 } from "path";
|
|
78706
79504
|
|
|
78707
79505
|
// session-tail.ts
|
|
78708
|
-
function
|
|
79506
|
+
function sanitizeCwdToProjectName2(cwd) {
|
|
78709
79507
|
return cwd.replace(/[^a-zA-Z0-9]/g, "-");
|
|
78710
79508
|
}
|
|
78711
|
-
var
|
|
78712
|
-
var
|
|
78713
|
-
function
|
|
79509
|
+
var MAX_JSONL_LINE_BYTES2 = 2 * 1024 * 1024;
|
|
79510
|
+
var MAX_ERROR_TEXT_CHARS2 = 500;
|
|
79511
|
+
function extractToolResultErrorText2(content3) {
|
|
78714
79512
|
if (typeof content3 === "string") {
|
|
78715
|
-
return content3.slice(0,
|
|
79513
|
+
return content3.slice(0, MAX_ERROR_TEXT_CHARS2);
|
|
78716
79514
|
}
|
|
78717
79515
|
if (Array.isArray(content3)) {
|
|
78718
79516
|
const parts = [];
|
|
@@ -78725,11 +79523,11 @@ function extractToolResultErrorText(content3) {
|
|
|
78725
79523
|
}
|
|
78726
79524
|
}
|
|
78727
79525
|
return parts.join(`
|
|
78728
|
-
`).slice(0,
|
|
79526
|
+
`).slice(0, MAX_ERROR_TEXT_CHARS2);
|
|
78729
79527
|
}
|
|
78730
79528
|
return "";
|
|
78731
79529
|
}
|
|
78732
|
-
function
|
|
79530
|
+
function projectAssistantTextBlocks2(content3, make) {
|
|
78733
79531
|
const out = new Map;
|
|
78734
79532
|
let lastToolUseIdx = -1;
|
|
78735
79533
|
content3.forEach((c, i) => {
|
|
@@ -78746,6 +79544,13 @@ function projectAssistantTextBlocks(content3, make) {
|
|
|
78746
79544
|
});
|
|
78747
79545
|
return out;
|
|
78748
79546
|
}
|
|
79547
|
+
function sumUsageTokens2(usage) {
|
|
79548
|
+
if (usage == null || typeof usage !== "object")
|
|
79549
|
+
return 0;
|
|
79550
|
+
const u = usage;
|
|
79551
|
+
const n = (v) => typeof v === "number" && Number.isFinite(v) ? v : 0;
|
|
79552
|
+
return n(u.input_tokens) + n(u.output_tokens) + n(u.cache_creation_input_tokens);
|
|
79553
|
+
}
|
|
78749
79554
|
function assistantLineCarriesAnswerSurface(content3) {
|
|
78750
79555
|
if (!Array.isArray(content3))
|
|
78751
79556
|
return false;
|
|
@@ -78805,7 +79610,7 @@ function projectSubagentLine(line, agentId, state4) {
|
|
|
78805
79610
|
agentId,
|
|
78806
79611
|
toolUseId: cc.tool_use_id ?? "",
|
|
78807
79612
|
isError: isError2,
|
|
78808
|
-
errorText: isError2 ?
|
|
79613
|
+
errorText: isError2 ? extractToolResultErrorText2(cc.content) : undefined
|
|
78809
79614
|
});
|
|
78810
79615
|
}
|
|
78811
79616
|
}
|
|
@@ -78821,7 +79626,17 @@ function projectSubagentLine(line, agentId, state4) {
|
|
|
78821
79626
|
if (typeof subModel === "string" && !isModelSentinel(subModel)) {
|
|
78822
79627
|
events.push({ kind: "sub_agent_model", agentId, model: subModel });
|
|
78823
79628
|
}
|
|
78824
|
-
const
|
|
79629
|
+
const subUsageTotal = sumUsageTokens2(message?.usage);
|
|
79630
|
+
if (subUsageTotal > 0) {
|
|
79631
|
+
const subMsgId = message?.id;
|
|
79632
|
+
events.push({
|
|
79633
|
+
kind: "sub_agent_usage",
|
|
79634
|
+
agentId,
|
|
79635
|
+
messageId: typeof subMsgId === "string" ? subMsgId : null,
|
|
79636
|
+
totalTokens: subUsageTotal
|
|
79637
|
+
});
|
|
79638
|
+
}
|
|
79639
|
+
const textEvents = projectAssistantTextBlocks2(content3, (text4, blockIndex, lastInMessage) => ({
|
|
78825
79640
|
kind: "sub_agent_text",
|
|
78826
79641
|
agentId,
|
|
78827
79642
|
text: text4,
|
|
@@ -78945,7 +79760,7 @@ function sanitiseToolArg(name, raw) {
|
|
|
78945
79760
|
case "NotebookEdit": {
|
|
78946
79761
|
const fp = raw.file_path;
|
|
78947
79762
|
if (typeof fp === "string" && fp.length > 0)
|
|
78948
|
-
out =
|
|
79763
|
+
out = basename6(fp);
|
|
78949
79764
|
break;
|
|
78950
79765
|
}
|
|
78951
79766
|
case "Bash": {
|
|
@@ -78981,7 +79796,7 @@ function sanitiseToolArg(name, raw) {
|
|
|
78981
79796
|
out = out.slice(0, SANITISE_MAX_LEN - 1) + "\u2026";
|
|
78982
79797
|
return out;
|
|
78983
79798
|
}
|
|
78984
|
-
function
|
|
79799
|
+
function basename6(p) {
|
|
78985
79800
|
const idx = p.lastIndexOf("/");
|
|
78986
79801
|
return idx === -1 ? p : p.slice(idx + 1);
|
|
78987
79802
|
}
|
|
@@ -79185,10 +80000,10 @@ import {
|
|
|
79185
80000
|
utimesSync,
|
|
79186
80001
|
writeFileSync as writeFileSync29
|
|
79187
80002
|
} from "node:fs";
|
|
79188
|
-
import { join as
|
|
80003
|
+
import { join as join39 } from "node:path";
|
|
79189
80004
|
var TURN_ACTIVE_MARKER_FILE = "turn-active.json";
|
|
79190
80005
|
function touchTurnActiveMarker(stateDir) {
|
|
79191
|
-
const path2 =
|
|
80006
|
+
const path2 = join39(stateDir, TURN_ACTIVE_MARKER_FILE);
|
|
79192
80007
|
if (!existsSync34(path2))
|
|
79193
80008
|
return;
|
|
79194
80009
|
const now = new Date;
|
|
@@ -79355,6 +80170,7 @@ function readSubTail(entry, tail, now, onDescriptionUpdate, fs2, log, db2, paren
|
|
|
79355
80170
|
},
|
|
79356
80171
|
lastTool: entry.lastTool,
|
|
79357
80172
|
toolCount: entry.toolCount,
|
|
80173
|
+
totalTokens: entry.totalTokens,
|
|
79358
80174
|
model: entry.currentModel,
|
|
79359
80175
|
skeleton: true
|
|
79360
80176
|
});
|
|
@@ -79436,6 +80252,7 @@ function readSubTail(entry, tail, now, onDescriptionUpdate, fs2, log, db2, paren
|
|
|
79436
80252
|
},
|
|
79437
80253
|
lastTool: entry.lastTool,
|
|
79438
80254
|
toolCount: entry.toolCount,
|
|
80255
|
+
totalTokens: entry.totalTokens,
|
|
79439
80256
|
model: entry.currentModel
|
|
79440
80257
|
});
|
|
79441
80258
|
return true;
|
|
@@ -79546,6 +80363,15 @@ function readSubTail(entry, tail, now, onDescriptionUpdate, fs2, log, db2, paren
|
|
|
79546
80363
|
}
|
|
79547
80364
|
continue;
|
|
79548
80365
|
}
|
|
80366
|
+
if (ev.kind === "sub_agent_usage") {
|
|
80367
|
+
if (ev.messageId == null) {
|
|
80368
|
+
entry.totalTokens += ev.totalTokens;
|
|
80369
|
+
} else if (!entry.seenUsageMessageIds.has(ev.messageId)) {
|
|
80370
|
+
entry.seenUsageMessageIds.add(ev.messageId);
|
|
80371
|
+
entry.totalTokens += ev.totalTokens;
|
|
80372
|
+
}
|
|
80373
|
+
continue;
|
|
80374
|
+
}
|
|
79549
80375
|
if (ev.kind === "sub_agent_tool_use") {
|
|
79550
80376
|
const narrativeJustFired = resolvePendingSubNarrative(ev.toolName, ev.input);
|
|
79551
80377
|
if (REPLY_TOOLS2.has(ev.toolName) && typeof ev.input?.text === "string") {
|
|
@@ -79574,6 +80400,7 @@ function readSubTail(entry, tail, now, onDescriptionUpdate, fs2, log, db2, paren
|
|
|
79574
80400
|
},
|
|
79575
80401
|
lastTool: entry.lastTool,
|
|
79576
80402
|
toolCount: entry.toolCount,
|
|
80403
|
+
totalTokens: entry.totalTokens,
|
|
79577
80404
|
progressLine: toolLine,
|
|
79578
80405
|
model: entry.currentModel
|
|
79579
80406
|
});
|
|
@@ -79648,7 +80475,7 @@ function readSubTail(entry, tail, now, onDescriptionUpdate, fs2, log, db2, paren
|
|
|
79648
80475
|
}
|
|
79649
80476
|
function startSubagentWatcher(config) {
|
|
79650
80477
|
const agentDir = config.agentDir;
|
|
79651
|
-
const expectedProjectSlug = config.agentCwd != null ?
|
|
80478
|
+
const expectedProjectSlug = config.agentCwd != null ? sanitizeCwdToProjectName2(config.agentCwd) : null;
|
|
79652
80479
|
const extraWatchCwdsProvider = config.extraWatchCwdsProvider ?? null;
|
|
79653
80480
|
const warnedForeignSlugs = new Set;
|
|
79654
80481
|
const stallThresholdMs = config.stallThresholdMs ?? parseEnvMs("SWITCHROOM_SUBAGENT_STALL_MS") ?? DEFAULT_STALL_THRESHOLD_MS;
|
|
@@ -79713,6 +80540,8 @@ function startSubagentWatcher(config) {
|
|
|
79713
80540
|
dispatchedAt: n,
|
|
79714
80541
|
lastActivityAt: n,
|
|
79715
80542
|
toolCount: 0,
|
|
80543
|
+
totalTokens: 0,
|
|
80544
|
+
seenUsageMessageIds: new Set,
|
|
79716
80545
|
stallNotified: false,
|
|
79717
80546
|
stalledAt: null,
|
|
79718
80547
|
completionNotified: false,
|
|
@@ -79849,6 +80678,7 @@ function startSubagentWatcher(config) {
|
|
|
79849
80678
|
state: entry.state,
|
|
79850
80679
|
outcome: entry.errored ? "failed" : entry.historical ? "orphan" : "completed",
|
|
79851
80680
|
toolCount: entry.toolCount,
|
|
80681
|
+
totalTokens: entry.totalTokens,
|
|
79852
80682
|
durationMs: nowFn() - entry.dispatchedAt,
|
|
79853
80683
|
description: entry.description,
|
|
79854
80684
|
resultText: entry.errored ? entry.lastResultText || entry.errorDetail || "" : entry.lastResultText,
|
|
@@ -79869,6 +80699,7 @@ function startSubagentWatcher(config) {
|
|
|
79869
80699
|
state: entry.state,
|
|
79870
80700
|
outcome: "failed",
|
|
79871
80701
|
toolCount: entry.toolCount,
|
|
80702
|
+
totalTokens: entry.totalTokens,
|
|
79872
80703
|
durationMs: nowFn() - entry.dispatchedAt,
|
|
79873
80704
|
description: entry.description,
|
|
79874
80705
|
resultText: entry.lastResultText,
|
|
@@ -80125,8 +80956,8 @@ function startSubagentWatcher(config) {
|
|
|
80125
80956
|
if (stopped)
|
|
80126
80957
|
return;
|
|
80127
80958
|
pruneVanishedDirWatchers();
|
|
80128
|
-
const claudeHome =
|
|
80129
|
-
const projectsRoot =
|
|
80959
|
+
const claudeHome = join40(agentDir, ".claude");
|
|
80960
|
+
const projectsRoot = join40(claudeHome, "projects");
|
|
80130
80961
|
if (!fs2.existsSync(projectsRoot))
|
|
80131
80962
|
return;
|
|
80132
80963
|
let projectDirs;
|
|
@@ -80142,7 +80973,7 @@ function startSubagentWatcher(config) {
|
|
|
80142
80973
|
if (extraWatchCwdsProvider != null) {
|
|
80143
80974
|
try {
|
|
80144
80975
|
for (const cwd of extraWatchCwdsProvider()) {
|
|
80145
|
-
allowedSlugs.add(
|
|
80976
|
+
allowedSlugs.add(sanitizeCwdToProjectName2(cwd));
|
|
80146
80977
|
}
|
|
80147
80978
|
} catch (err) {
|
|
80148
80979
|
providerOk = false;
|
|
@@ -80160,7 +80991,7 @@ function startSubagentWatcher(config) {
|
|
|
80160
80991
|
continue;
|
|
80161
80992
|
}
|
|
80162
80993
|
warnedForeignSlugs.delete(pDir);
|
|
80163
|
-
const projectPath =
|
|
80994
|
+
const projectPath = join40(projectsRoot, pDir);
|
|
80164
80995
|
let sessionDirs;
|
|
80165
80996
|
try {
|
|
80166
80997
|
sessionDirs = fs2.readdirSync(projectPath);
|
|
@@ -80170,7 +81001,7 @@ function startSubagentWatcher(config) {
|
|
|
80170
81001
|
for (const sDir of sessionDirs) {
|
|
80171
81002
|
if (sDir.endsWith(".jsonl"))
|
|
80172
81003
|
continue;
|
|
80173
|
-
const subagentsPath =
|
|
81004
|
+
const subagentsPath = join40(projectPath, sDir, "subagents");
|
|
80174
81005
|
if (!fs2.existsSync(subagentsPath))
|
|
80175
81006
|
continue;
|
|
80176
81007
|
const watchAndScan = (dirPath) => {
|
|
@@ -80179,7 +81010,7 @@ function startSubagentWatcher(config) {
|
|
|
80179
81010
|
const w = fs2.watch(dirPath, (_event, filename) => {
|
|
80180
81011
|
if (!filename || !filename.toString().startsWith("agent-") || !filename.toString().endsWith(".jsonl"))
|
|
80181
81012
|
return;
|
|
80182
|
-
const filePath =
|
|
81013
|
+
const filePath = join40(dirPath, filename.toString());
|
|
80183
81014
|
if (!knownFiles.has(filePath)) {
|
|
80184
81015
|
scanSubagentsDir(dirPath);
|
|
80185
81016
|
}
|
|
@@ -80193,7 +81024,7 @@ function startSubagentWatcher(config) {
|
|
|
80193
81024
|
scanSubagentsDir(dirPath);
|
|
80194
81025
|
};
|
|
80195
81026
|
watchAndScan(subagentsPath);
|
|
80196
|
-
const workflowsPath =
|
|
81027
|
+
const workflowsPath = join40(subagentsPath, "workflows");
|
|
80197
81028
|
if (fs2.existsSync(workflowsPath)) {
|
|
80198
81029
|
let wfDirs;
|
|
80199
81030
|
try {
|
|
@@ -80203,7 +81034,7 @@ function startSubagentWatcher(config) {
|
|
|
80203
81034
|
}
|
|
80204
81035
|
for (const wfDir of wfDirs) {
|
|
80205
81036
|
try {
|
|
80206
|
-
const wfPath =
|
|
81037
|
+
const wfPath = join40(workflowsPath, wfDir);
|
|
80207
81038
|
if (!fs2.statSync(wfPath).isDirectory())
|
|
80208
81039
|
continue;
|
|
80209
81040
|
watchAndScan(wfPath);
|
|
@@ -80223,7 +81054,7 @@ function startSubagentWatcher(config) {
|
|
|
80223
81054
|
for (const e of entries) {
|
|
80224
81055
|
if (!e.startsWith("agent-") || !e.endsWith(".jsonl"))
|
|
80225
81056
|
continue;
|
|
80226
|
-
const filePath =
|
|
81057
|
+
const filePath = join40(subagentsPath, e);
|
|
80227
81058
|
if (knownFiles.has(filePath))
|
|
80228
81059
|
continue;
|
|
80229
81060
|
const agentId = e.slice("agent-".length, -".jsonl".length);
|
|
@@ -80335,13 +81166,13 @@ import {
|
|
|
80335
81166
|
existsSync as existsSync36,
|
|
80336
81167
|
renameSync as renameSync15
|
|
80337
81168
|
} from "node:fs";
|
|
80338
|
-
import { join as
|
|
80339
|
-
import { homedir as
|
|
81169
|
+
import { join as join41, resolve as resolve8 } from "node:path";
|
|
81170
|
+
import { homedir as homedir14 } from "node:os";
|
|
80340
81171
|
function registryDir() {
|
|
80341
|
-
return resolve8(process.env.SWITCHROOM_WORKTREE_DIR ??
|
|
81172
|
+
return resolve8(process.env.SWITCHROOM_WORKTREE_DIR ?? join41(homedir14(), ".switchroom", "worktrees"));
|
|
80342
81173
|
}
|
|
80343
81174
|
function recordPath(id) {
|
|
80344
|
-
return
|
|
81175
|
+
return join41(registryDir(), `${id}.json`);
|
|
80345
81176
|
}
|
|
80346
81177
|
function ensureDir4() {
|
|
80347
81178
|
mkdirSync29(registryDir(), { recursive: true });
|
|
@@ -80392,12 +81223,12 @@ function recordExists(id) {
|
|
|
80392
81223
|
|
|
80393
81224
|
// worktree-watch-cwds.ts
|
|
80394
81225
|
import { realpathSync as realpathSync3 } from "node:fs";
|
|
80395
|
-
import { basename as
|
|
81226
|
+
import { basename as basename8 } from "node:path";
|
|
80396
81227
|
var identityEscalated = false;
|
|
80397
81228
|
function defaultDeriveName(agentDir) {
|
|
80398
81229
|
if (!agentDir || agentDir.trim().length === 0)
|
|
80399
81230
|
return "";
|
|
80400
|
-
const leaf =
|
|
81231
|
+
const leaf = basename8(agentDir).trim();
|
|
80401
81232
|
return leaf;
|
|
80402
81233
|
}
|
|
80403
81234
|
function resolveOwnerIdentity(self, agentDir, deriveName) {
|
|
@@ -80520,14 +81351,14 @@ init_boot_card();
|
|
|
80520
81351
|
|
|
80521
81352
|
// gateway/update-announce.ts
|
|
80522
81353
|
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
|
|
81354
|
+
import { join as join46 } from "node:path";
|
|
81355
|
+
import { homedir as homedir16 } from "node:os";
|
|
80525
81356
|
|
|
80526
81357
|
// ../src/host-control/audit-reader.ts
|
|
80527
|
-
import { homedir as
|
|
80528
|
-
import { join as
|
|
80529
|
-
function defaultAuditLogPath(home2 =
|
|
80530
|
-
return
|
|
81358
|
+
import { homedir as homedir15 } from "node:os";
|
|
81359
|
+
import { join as join45 } from "node:path";
|
|
81360
|
+
function defaultAuditLogPath(home2 = homedir15()) {
|
|
81361
|
+
return join45(home2, ".switchroom", "host-control-audit.log");
|
|
80531
81362
|
}
|
|
80532
81363
|
function parseAuditLine(line) {
|
|
80533
81364
|
const trimmed = line.trim();
|
|
@@ -80712,15 +81543,15 @@ function renderUpdateOutcomeLine(entry) {
|
|
|
80712
81543
|
`);
|
|
80713
81544
|
}
|
|
80714
81545
|
function claimUpdateAnnouncement(requestId, opts = {}) {
|
|
80715
|
-
const stateDir = opts.stateDir ?? process.env.TELEGRAM_STATE_DIR ??
|
|
80716
|
-
const dir =
|
|
81546
|
+
const stateDir = opts.stateDir ?? process.env.TELEGRAM_STATE_DIR ?? join46(homedir16(), ".switchroom");
|
|
81547
|
+
const dir = join46(stateDir, "update-announced");
|
|
80717
81548
|
try {
|
|
80718
81549
|
mkdirSync33(dir, { recursive: true });
|
|
80719
81550
|
} catch {
|
|
80720
81551
|
return false;
|
|
80721
81552
|
}
|
|
80722
81553
|
const safeId = requestId.replace(/[^A-Za-z0-9_.-]/g, "_").slice(0, 200);
|
|
80723
|
-
const path2 =
|
|
81554
|
+
const path2 = join46(dir, safeId);
|
|
80724
81555
|
try {
|
|
80725
81556
|
const fd = openSync9(path2, "wx");
|
|
80726
81557
|
closeSync9(fd);
|
|
@@ -80942,7 +81773,7 @@ function createIssuesCardHandle(opts) {
|
|
|
80942
81773
|
|
|
80943
81774
|
// issues-watcher.ts
|
|
80944
81775
|
import { existsSync as existsSync43, statSync as statSync12 } from "node:fs";
|
|
80945
|
-
import { join as
|
|
81776
|
+
import { join as join48 } from "node:path";
|
|
80946
81777
|
|
|
80947
81778
|
// ../src/issues/store.ts
|
|
80948
81779
|
import {
|
|
@@ -80958,7 +81789,7 @@ import {
|
|
|
80958
81789
|
writeFileSync as writeFileSync36,
|
|
80959
81790
|
writeSync as writeSync6
|
|
80960
81791
|
} from "node:fs";
|
|
80961
|
-
import { join as
|
|
81792
|
+
import { join as join47 } from "node:path";
|
|
80962
81793
|
import { randomBytes as randomBytes7 } from "node:crypto";
|
|
80963
81794
|
import { execSync } from "node:child_process";
|
|
80964
81795
|
|
|
@@ -80977,7 +81808,7 @@ init_redact();
|
|
|
80977
81808
|
var ISSUES_FILE = "issues.jsonl";
|
|
80978
81809
|
var ISSUES_LOCK = "issues.lock";
|
|
80979
81810
|
function readAll(stateDir) {
|
|
80980
|
-
const path2 =
|
|
81811
|
+
const path2 = join47(stateDir, ISSUES_FILE);
|
|
80981
81812
|
if (!existsSync42(path2))
|
|
80982
81813
|
return [];
|
|
80983
81814
|
let raw;
|
|
@@ -81014,7 +81845,7 @@ function list2(stateDir, opts = {}) {
|
|
|
81014
81845
|
});
|
|
81015
81846
|
}
|
|
81016
81847
|
function resolve9(stateDir, fingerprint, nowFn = Date.now) {
|
|
81017
|
-
if (!existsSync42(
|
|
81848
|
+
if (!existsSync42(join47(stateDir, ISSUES_FILE)))
|
|
81018
81849
|
return 0;
|
|
81019
81850
|
return withLock(stateDir, () => {
|
|
81020
81851
|
const all2 = readAll(stateDir);
|
|
@@ -81032,7 +81863,7 @@ function resolve9(stateDir, fingerprint, nowFn = Date.now) {
|
|
|
81032
81863
|
});
|
|
81033
81864
|
}
|
|
81034
81865
|
function writeAll(stateDir, events) {
|
|
81035
|
-
const path2 =
|
|
81866
|
+
const path2 = join47(stateDir, ISSUES_FILE);
|
|
81036
81867
|
sweepOrphanTmpFiles(stateDir);
|
|
81037
81868
|
const tmp = `${path2}.tmp-${process.pid}-${randomBytes7(4).toString("hex")}`;
|
|
81038
81869
|
const body = events.length === 0 ? "" : events.map((e) => JSON.stringify(e)).join(`
|
|
@@ -81054,7 +81885,7 @@ function sweepOrphanTmpFiles(stateDir) {
|
|
|
81054
81885
|
for (const entry of entries) {
|
|
81055
81886
|
if (!entry.startsWith(TMP_PREFIX))
|
|
81056
81887
|
continue;
|
|
81057
|
-
const tmpPath2 =
|
|
81888
|
+
const tmpPath2 = join47(stateDir, entry);
|
|
81058
81889
|
try {
|
|
81059
81890
|
const stat = statSync11(tmpPath2);
|
|
81060
81891
|
if (stat.mtimeMs < cutoff) {
|
|
@@ -81066,7 +81897,7 @@ function sweepOrphanTmpFiles(stateDir) {
|
|
|
81066
81897
|
var LOCK_RETRY_MS = 25;
|
|
81067
81898
|
var LOCK_TIMEOUT_MS = 1e4;
|
|
81068
81899
|
function withLock(stateDir, fn) {
|
|
81069
|
-
const lockPath =
|
|
81900
|
+
const lockPath = join47(stateDir, ISSUES_LOCK);
|
|
81070
81901
|
const startedAt = Date.now();
|
|
81071
81902
|
let fd = null;
|
|
81072
81903
|
while (fd === null) {
|
|
@@ -81151,7 +81982,7 @@ function isIssueEvent(v) {
|
|
|
81151
81982
|
// issues-watcher.ts
|
|
81152
81983
|
var DEFAULT_POLL_INTERVAL_MS2 = 2000;
|
|
81153
81984
|
function startIssuesWatcher(opts) {
|
|
81154
|
-
const path2 =
|
|
81985
|
+
const path2 = join48(opts.stateDir, ISSUES_FILE);
|
|
81155
81986
|
const log = opts.log ?? (() => {});
|
|
81156
81987
|
const intervalMs = opts.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS2;
|
|
81157
81988
|
const setIntervalFn = opts.setInterval ?? setInterval;
|
|
@@ -81213,10 +82044,10 @@ function defaultReadEvents(stateDir) {
|
|
|
81213
82044
|
}
|
|
81214
82045
|
// permission-title.ts
|
|
81215
82046
|
init_card_format();
|
|
81216
|
-
import { basename as
|
|
82047
|
+
import { basename as basename10 } from "node:path";
|
|
81217
82048
|
|
|
81218
82049
|
// permission-rule.ts
|
|
81219
|
-
import { basename as
|
|
82050
|
+
import { basename as basename9 } from "node:path";
|
|
81220
82051
|
var FILE_TOOLS = new Set([
|
|
81221
82052
|
"Edit",
|
|
81222
82053
|
"Write",
|
|
@@ -81294,7 +82125,7 @@ function skillBasenameFromPath(input) {
|
|
|
81294
82125
|
if (!path2)
|
|
81295
82126
|
return null;
|
|
81296
82127
|
const trimmed = path2.replace(/\/SKILL\.md$/i, "").replace(/\/$/, "");
|
|
81297
|
-
return
|
|
82128
|
+
return basename9(trimmed) || null;
|
|
81298
82129
|
}
|
|
81299
82130
|
function matchesAllowRule(rule, toolName, inputPreview) {
|
|
81300
82131
|
if (!rule || !toolName)
|
|
@@ -81586,11 +82417,11 @@ function describeGrant(toolName, inputPreview, option) {
|
|
|
81586
82417
|
return m ? `run ${m[1]} commands` : "run that command";
|
|
81587
82418
|
}
|
|
81588
82419
|
if (t === "Edit" || t === "MultiEdit" || t === "NotebookEdit")
|
|
81589
|
-
return `edit ${
|
|
82420
|
+
return `edit ${basename10(arg)}`;
|
|
81590
82421
|
if (t === "Write")
|
|
81591
|
-
return `write ${
|
|
82422
|
+
return `write ${basename10(arg)}`;
|
|
81592
82423
|
if (t === "Read")
|
|
81593
|
-
return `read ${
|
|
82424
|
+
return `read ${basename10(arg)}`;
|
|
81594
82425
|
return naturalAction(toolName, inputPreview);
|
|
81595
82426
|
}
|
|
81596
82427
|
switch (rule) {
|
|
@@ -81629,12 +82460,12 @@ function fileBase(input, rawPreview) {
|
|
|
81629
82460
|
if (input) {
|
|
81630
82461
|
const p = readString2(input, "file_path") ?? readString2(input, "notebook_path");
|
|
81631
82462
|
if (p)
|
|
81632
|
-
return
|
|
82463
|
+
return basename10(p);
|
|
81633
82464
|
}
|
|
81634
82465
|
if (rawPreview) {
|
|
81635
82466
|
const p = extractFilePathFromRaw2(rawPreview);
|
|
81636
82467
|
if (p)
|
|
81637
|
-
return
|
|
82468
|
+
return basename10(p);
|
|
81638
82469
|
}
|
|
81639
82470
|
return null;
|
|
81640
82471
|
}
|
|
@@ -81765,7 +82596,7 @@ function truncate6(text4, max) {
|
|
|
81765
82596
|
}
|
|
81766
82597
|
|
|
81767
82598
|
// permission-rule.ts
|
|
81768
|
-
import { basename as
|
|
82599
|
+
import { basename as basename11 } from "node:path";
|
|
81769
82600
|
var FILE_TOOLS2 = new Set([
|
|
81770
82601
|
"Edit",
|
|
81771
82602
|
"Write",
|
|
@@ -81897,14 +82728,14 @@ function skillBasenameFromPath3(input) {
|
|
|
81897
82728
|
if (!path2)
|
|
81898
82729
|
return null;
|
|
81899
82730
|
const trimmed = path2.replace(/\/SKILL\.md$/i, "").replace(/\/$/, "");
|
|
81900
|
-
return
|
|
82731
|
+
return basename11(trimmed) || null;
|
|
81901
82732
|
}
|
|
81902
82733
|
function isRulePersisted(resolvedAllow, ruleRule) {
|
|
81903
82734
|
return resolvedAllow.includes(ruleRule);
|
|
81904
82735
|
}
|
|
81905
82736
|
|
|
81906
82737
|
// scoped-approval.ts
|
|
81907
|
-
import { basename as
|
|
82738
|
+
import { basename as basename12 } from "node:path";
|
|
81908
82739
|
var SCOPED_APPROVAL_DEFAULT_TTL_MS = 30 * 60 * 1000;
|
|
81909
82740
|
function scopedApprovalTtlMs(env = process.env) {
|
|
81910
82741
|
const raw = env.SWITCHROOM_SCOPED_APPROVAL_TTL_MS;
|
|
@@ -81929,7 +82760,7 @@ function resolveTimeBox(toolName, inputPreview, choices) {
|
|
|
81929
82760
|
const fileMatch = FILE_RULE.exec(rule);
|
|
81930
82761
|
if (fileMatch) {
|
|
81931
82762
|
const verb = fileMatch[1] === "Read" ? "reads of" : "edits to";
|
|
81932
|
-
return { rule, breadth: `${verb} ${
|
|
82763
|
+
return { rule, breadth: `${verb} ${basename12(fileMatch[2])}` };
|
|
81933
82764
|
}
|
|
81934
82765
|
const bashMatch = BASH_FAMILY_RULE.exec(rule);
|
|
81935
82766
|
if (bashMatch) {
|
|
@@ -82030,7 +82861,7 @@ function readBashCommand(inputPreview) {
|
|
|
82030
82861
|
|
|
82031
82862
|
// gateway/scoped-grant-store.ts
|
|
82032
82863
|
import { readFileSync as readFileSync47, writeFileSync as writeFileSync37 } from "node:fs";
|
|
82033
|
-
import { join as
|
|
82864
|
+
import { join as join49 } from "node:path";
|
|
82034
82865
|
|
|
82035
82866
|
// scoped-approval.ts
|
|
82036
82867
|
var SCOPED_APPROVAL_DEFAULT_TTL_MS2 = 30 * 60 * 1000;
|
|
@@ -82072,7 +82903,7 @@ function scopedGrantPersistEnabled(env = process.env) {
|
|
|
82072
82903
|
return env.SWITCHROOM_SCOPED_GRANT_PERSIST !== "0";
|
|
82073
82904
|
}
|
|
82074
82905
|
function createScopedGrantStore(stateDir, env = process.env) {
|
|
82075
|
-
const filePath =
|
|
82906
|
+
const filePath = join49(stateDir, "scoped-grants.json");
|
|
82076
82907
|
const enabled8 = scopedGrantPersistEnabled(env);
|
|
82077
82908
|
function read() {
|
|
82078
82909
|
try {
|
|
@@ -82446,7 +83277,7 @@ function isDiffPreApproved(agentName3, unifiedDiff, deps) {
|
|
|
82446
83277
|
// credits-watch.ts
|
|
82447
83278
|
init_card_format();
|
|
82448
83279
|
import { readFileSync as readFileSync48, writeFileSync as writeFileSync38, existsSync as existsSync44, mkdirSync as mkdirSync35 } from "fs";
|
|
82449
|
-
import { join as
|
|
83280
|
+
import { join as join50 } from "path";
|
|
82450
83281
|
var STATE_FILE = "credits-watch.json";
|
|
82451
83282
|
var DEFAULT_CREDIT_FATAL_REASONS = new Set;
|
|
82452
83283
|
var KNOWN_CREDIT_REASONS = [
|
|
@@ -82468,7 +83299,7 @@ function emptyCreditState() {
|
|
|
82468
83299
|
return { lastNotifiedReason: null, lastNotifiedAt: 0 };
|
|
82469
83300
|
}
|
|
82470
83301
|
function readClaudeJsonOverage(claudeConfigDir) {
|
|
82471
|
-
const path2 =
|
|
83302
|
+
const path2 = join50(claudeConfigDir, ".claude.json");
|
|
82472
83303
|
if (!existsSync44(path2))
|
|
82473
83304
|
return null;
|
|
82474
83305
|
let raw;
|
|
@@ -82551,7 +83382,7 @@ function humanizeReason(reason) {
|
|
|
82551
83382
|
}
|
|
82552
83383
|
}
|
|
82553
83384
|
function loadCreditState(stateDir) {
|
|
82554
|
-
const path2 =
|
|
83385
|
+
const path2 = join50(stateDir, STATE_FILE);
|
|
82555
83386
|
if (!existsSync44(path2))
|
|
82556
83387
|
return emptyCreditState();
|
|
82557
83388
|
try {
|
|
@@ -82568,7 +83399,7 @@ function loadCreditState(stateDir) {
|
|
|
82568
83399
|
}
|
|
82569
83400
|
function saveCreditState(stateDir, state4) {
|
|
82570
83401
|
mkdirSync35(stateDir, { recursive: true });
|
|
82571
|
-
const path2 =
|
|
83402
|
+
const path2 = join50(stateDir, STATE_FILE);
|
|
82572
83403
|
writeFileSync38(path2, JSON.stringify(state4, null, 2) + `
|
|
82573
83404
|
`, { mode: 384 });
|
|
82574
83405
|
}
|
|
@@ -82577,7 +83408,7 @@ function saveCreditState(stateDir, state4) {
|
|
|
82577
83408
|
init_auth_snapshot_format();
|
|
82578
83409
|
init_card_format();
|
|
82579
83410
|
import { readFileSync as readFileSync49, writeFileSync as writeFileSync39, existsSync as existsSync45, mkdirSync as mkdirSync36 } from "fs";
|
|
82580
|
-
import { join as
|
|
83411
|
+
import { join as join51 } from "path";
|
|
82581
83412
|
var STATE_FILE2 = "quota-watch.json";
|
|
82582
83413
|
function emptyQuotaWatchState() {
|
|
82583
83414
|
return {};
|
|
@@ -82815,7 +83646,7 @@ function buildRecoveryMessage(agentName3, snap) {
|
|
|
82815
83646
|
`);
|
|
82816
83647
|
}
|
|
82817
83648
|
function loadQuotaWatchState(stateDir) {
|
|
82818
|
-
const path2 =
|
|
83649
|
+
const path2 = join51(stateDir, STATE_FILE2);
|
|
82819
83650
|
if (!existsSync45(path2))
|
|
82820
83651
|
return emptyQuotaWatchState();
|
|
82821
83652
|
try {
|
|
@@ -82837,7 +83668,7 @@ function loadQuotaWatchState(stateDir) {
|
|
|
82837
83668
|
}
|
|
82838
83669
|
function saveQuotaWatchState(stateDir, state4) {
|
|
82839
83670
|
mkdirSync36(stateDir, { recursive: true });
|
|
82840
|
-
const path2 =
|
|
83671
|
+
const path2 = join51(stateDir, STATE_FILE2);
|
|
82841
83672
|
writeFileSync39(path2, JSON.stringify(state4, null, 2) + `
|
|
82842
83673
|
`, { mode: 384 });
|
|
82843
83674
|
}
|
|
@@ -82895,17 +83726,17 @@ import {
|
|
|
82895
83726
|
utimesSync as utimesSync2,
|
|
82896
83727
|
writeFileSync as writeFileSync40
|
|
82897
83728
|
} from "node:fs";
|
|
82898
|
-
import { join as
|
|
83729
|
+
import { join as join52 } from "node:path";
|
|
82899
83730
|
var TURN_ACTIVE_MARKER_FILE2 = "turn-active.json";
|
|
82900
83731
|
function writeTurnActiveMarker(stateDir, marker) {
|
|
82901
83732
|
try {
|
|
82902
83733
|
mkdirSync37(stateDir, { recursive: true });
|
|
82903
|
-
writeFileSync40(
|
|
83734
|
+
writeFileSync40(join52(stateDir, TURN_ACTIVE_MARKER_FILE2), JSON.stringify(marker, null, 2) + `
|
|
82904
83735
|
`, { mode: 384 });
|
|
82905
83736
|
} catch {}
|
|
82906
83737
|
}
|
|
82907
83738
|
function touchTurnActiveMarker2(stateDir) {
|
|
82908
|
-
const path2 =
|
|
83739
|
+
const path2 = join52(stateDir, TURN_ACTIVE_MARKER_FILE2);
|
|
82909
83740
|
if (!existsSync46(path2))
|
|
82910
83741
|
return;
|
|
82911
83742
|
const now = new Date;
|
|
@@ -82920,11 +83751,11 @@ function touchTurnActiveMarker2(stateDir) {
|
|
|
82920
83751
|
}
|
|
82921
83752
|
function removeTurnActiveMarker(stateDir) {
|
|
82922
83753
|
try {
|
|
82923
|
-
unlinkSync21(
|
|
83754
|
+
unlinkSync21(join52(stateDir, TURN_ACTIVE_MARKER_FILE2));
|
|
82924
83755
|
} catch {}
|
|
82925
83756
|
}
|
|
82926
83757
|
function sweepStaleTurnActiveMarker(stateDir, opts) {
|
|
82927
|
-
const path2 =
|
|
83758
|
+
const path2 = join52(stateDir, TURN_ACTIVE_MARKER_FILE2);
|
|
82928
83759
|
if (!existsSync46(path2))
|
|
82929
83760
|
return false;
|
|
82930
83761
|
const now = opts.now ?? Date.now();
|
|
@@ -82955,7 +83786,7 @@ function sweepStaleTurnActiveMarker(stateDir, opts) {
|
|
|
82955
83786
|
}
|
|
82956
83787
|
}
|
|
82957
83788
|
function readTurnActiveMarkerAgeMs(stateDir, now) {
|
|
82958
|
-
const path2 =
|
|
83789
|
+
const path2 = join52(stateDir, TURN_ACTIVE_MARKER_FILE2);
|
|
82959
83790
|
try {
|
|
82960
83791
|
const st = statSync13(path2);
|
|
82961
83792
|
return (now ?? Date.now()) - st.mtimeMs;
|
|
@@ -82965,10 +83796,10 @@ function readTurnActiveMarkerAgeMs(stateDir, now) {
|
|
|
82965
83796
|
}
|
|
82966
83797
|
|
|
82967
83798
|
// ../src/build-info.ts
|
|
82968
|
-
var VERSION = "0.18.
|
|
82969
|
-
var COMMIT_SHA = "
|
|
82970
|
-
var COMMIT_DATE = "2026-07-
|
|
82971
|
-
var LATEST_PR =
|
|
83799
|
+
var VERSION = "0.18.26";
|
|
83800
|
+
var COMMIT_SHA = "ceb7d1a9";
|
|
83801
|
+
var COMMIT_DATE = "2026-07-15T11:05:52Z";
|
|
83802
|
+
var LATEST_PR = 3258;
|
|
82972
83803
|
var COMMITS_AHEAD_OF_TAG = 0;
|
|
82973
83804
|
|
|
82974
83805
|
// gateway/boot-version.ts
|
|
@@ -83045,12 +83876,12 @@ init_protocol();
|
|
|
83045
83876
|
init_peercred();
|
|
83046
83877
|
import * as net5 from "node:net";
|
|
83047
83878
|
import * as fs2 from "node:fs";
|
|
83048
|
-
import { homedir as
|
|
83049
|
-
import { join as
|
|
83879
|
+
import { homedir as homedir17 } from "node:os";
|
|
83880
|
+
import { join as join53 } from "node:path";
|
|
83050
83881
|
var DEFAULT_TIMEOUT_MS4 = 2000;
|
|
83051
83882
|
var UNLOCK_TIMEOUT_MS = 30000;
|
|
83052
|
-
var LEGACY_SOCKET_PATH2 =
|
|
83053
|
-
var OPERATOR_SOCKET_PATH2 =
|
|
83883
|
+
var LEGACY_SOCKET_PATH2 = join53(homedir17(), ".switchroom", "vault-broker.sock");
|
|
83884
|
+
var OPERATOR_SOCKET_PATH2 = join53(homedir17(), ".switchroom", "broker-operator", "sock");
|
|
83054
83885
|
function defaultBrokerSocketPath2() {
|
|
83055
83886
|
if (fs2.existsSync(OPERATOR_SOCKET_PATH2))
|
|
83056
83887
|
return OPERATOR_SOCKET_PATH2;
|
|
@@ -83933,7 +84764,7 @@ function resolveVaultApprovalPosture(broker) {
|
|
|
83933
84764
|
|
|
83934
84765
|
// registry/turns-schema.ts
|
|
83935
84766
|
import { chmodSync as chmodSync10, mkdirSync as mkdirSync38 } from "fs";
|
|
83936
|
-
import { join as
|
|
84767
|
+
import { join as join54 } from "path";
|
|
83937
84768
|
var DatabaseClass2 = null;
|
|
83938
84769
|
function loadDatabaseClass2() {
|
|
83939
84770
|
if (DatabaseClass2 != null)
|
|
@@ -83984,12 +84815,16 @@ var PHASE2_MIGRATIONS = [
|
|
|
83984
84815
|
var PHASE3_MIGRATIONS = [
|
|
83985
84816
|
`ALTER TABLE turns ADD COLUMN resumed_at INTEGER`
|
|
83986
84817
|
];
|
|
84818
|
+
var PHASE4_MIGRATIONS = [
|
|
84819
|
+
`ALTER TABLE turns ADD COLUMN session_id TEXT`,
|
|
84820
|
+
`ALTER TABLE turns ADD COLUMN answer_redelivered_at INTEGER`
|
|
84821
|
+
];
|
|
83987
84822
|
function applySchema(db2) {
|
|
83988
84823
|
db2.exec("PRAGMA journal_mode = WAL");
|
|
83989
84824
|
db2.exec("PRAGMA synchronous = NORMAL");
|
|
83990
84825
|
db2.exec("PRAGMA busy_timeout = 5000");
|
|
83991
84826
|
db2.exec(SCHEMA_SQL);
|
|
83992
|
-
for (const sql of [...PHASE1_MIGRATIONS, ...PHASE2_MIGRATIONS, ...PHASE3_MIGRATIONS]) {
|
|
84827
|
+
for (const sql of [...PHASE1_MIGRATIONS, ...PHASE2_MIGRATIONS, ...PHASE3_MIGRATIONS, ...PHASE4_MIGRATIONS]) {
|
|
83993
84828
|
try {
|
|
83994
84829
|
db2.exec(sql);
|
|
83995
84830
|
} catch (err) {
|
|
@@ -84001,9 +84836,9 @@ function applySchema(db2) {
|
|
|
84001
84836
|
}
|
|
84002
84837
|
function openTurnsDb(agentDir) {
|
|
84003
84838
|
const Database = loadDatabaseClass2();
|
|
84004
|
-
const dir =
|
|
84839
|
+
const dir = join54(agentDir, "telegram");
|
|
84005
84840
|
mkdirSync38(dir, { recursive: true, mode: 448 });
|
|
84006
|
-
const path2 =
|
|
84841
|
+
const path2 = join54(dir, "registry.db");
|
|
84007
84842
|
const db2 = new Database(path2, { create: true });
|
|
84008
84843
|
applySchema(db2);
|
|
84009
84844
|
try {
|
|
@@ -84032,6 +84867,8 @@ function mapRow(row) {
|
|
|
84032
84867
|
tool_call_count: row.tool_call_count,
|
|
84033
84868
|
interrupt_reason: row.interrupt_reason,
|
|
84034
84869
|
resumed_at: row.resumed_at,
|
|
84870
|
+
session_id: row.session_id ?? null,
|
|
84871
|
+
answer_redelivered_at: row.answer_redelivered_at ?? null,
|
|
84035
84872
|
created_at: row.created_at,
|
|
84036
84873
|
updated_at: row.updated_at
|
|
84037
84874
|
};
|
|
@@ -84127,6 +84964,24 @@ function markTurnResumed(db2, turnKey2, now = Date.now()) {
|
|
|
84127
84964
|
WHERE turn_key = ? AND resumed_at IS NULL
|
|
84128
84965
|
`).run(now, now, turnKey2);
|
|
84129
84966
|
}
|
|
84967
|
+
function stampTurnSessionId(db2, turnKey2, sessionId, now = Date.now()) {
|
|
84968
|
+
if (!sessionId)
|
|
84969
|
+
return;
|
|
84970
|
+
db2.prepare(`
|
|
84971
|
+
UPDATE turns
|
|
84972
|
+
SET session_id = ?,
|
|
84973
|
+
updated_at = ?
|
|
84974
|
+
WHERE turn_key = ? AND session_id IS NULL
|
|
84975
|
+
`).run(sessionId, now, turnKey2);
|
|
84976
|
+
}
|
|
84977
|
+
function markAnswerRedelivered(db2, turnKey2, now = Date.now()) {
|
|
84978
|
+
db2.prepare(`
|
|
84979
|
+
UPDATE turns
|
|
84980
|
+
SET answer_redelivered_at = ?,
|
|
84981
|
+
updated_at = ?
|
|
84982
|
+
WHERE turn_key = ? AND answer_redelivered_at IS NULL
|
|
84983
|
+
`).run(now, now, turnKey2);
|
|
84984
|
+
}
|
|
84130
84985
|
function findLatestTurnIfInterrupted(db2) {
|
|
84131
84986
|
const row = db2.prepare(`
|
|
84132
84987
|
SELECT * FROM turns
|
|
@@ -84756,7 +85611,7 @@ installGlobalErrorHandlers();
|
|
|
84756
85611
|
process.on("beforeExit", () => {
|
|
84757
85612
|
shutdownAnalytics();
|
|
84758
85613
|
});
|
|
84759
|
-
var STATE_DIR = process.env.TELEGRAM_STATE_DIR ??
|
|
85614
|
+
var STATE_DIR = process.env.TELEGRAM_STATE_DIR ?? join55(homedir18(), ".claude", "channels", "telegram");
|
|
84760
85615
|
var permCardStore = createPermissionCardStore(STATE_DIR);
|
|
84761
85616
|
var BLOCKED_APPROVALS_DIR = process.env.SWITCHROOM_BLOCKED_APPROVALS_DIR ?? "/state/blocked-approvals";
|
|
84762
85617
|
var AGENT_NAME = process.env.SWITCHROOM_AGENT_NAME ?? "agent";
|
|
@@ -84856,11 +85711,11 @@ function scheduleAlwaysAllowPersistDrain() {
|
|
|
84856
85711
|
}, ALWAYS_ALLOW_DRAIN_INTERVAL_MS);
|
|
84857
85712
|
timer3.unref?.();
|
|
84858
85713
|
}
|
|
84859
|
-
var ACCESS_FILE =
|
|
84860
|
-
var APPROVED_DIR =
|
|
84861
|
-
var ENV_FILE =
|
|
84862
|
-
var INBOX_DIR =
|
|
84863
|
-
var PEOPLE_FILE =
|
|
85714
|
+
var ACCESS_FILE = join55(STATE_DIR, "access.json");
|
|
85715
|
+
var APPROVED_DIR = join55(STATE_DIR, "approved");
|
|
85716
|
+
var ENV_FILE = join55(STATE_DIR, ".env");
|
|
85717
|
+
var INBOX_DIR = join55(STATE_DIR, "inbox");
|
|
85718
|
+
var PEOPLE_FILE = join55(STATE_DIR, "people.json");
|
|
84864
85719
|
function triggerSelfRestart(targetAgent, reason, delayMs = 300) {
|
|
84865
85720
|
const isDocker = process.env.SWITCHROOM_RUNTIME === "docker";
|
|
84866
85721
|
const selfAgent = process.env.SWITCHROOM_AGENT_NAME;
|
|
@@ -85057,7 +85912,7 @@ function assertSendable(f) {
|
|
|
85057
85912
|
} catch {
|
|
85058
85913
|
throw new Error(`refusing to send file \u2014 cannot resolve real path: ${f}`);
|
|
85059
85914
|
}
|
|
85060
|
-
const inbox =
|
|
85915
|
+
const inbox = join55(stateReal, "inbox");
|
|
85061
85916
|
if (real.startsWith(stateReal + sep3) && !real.startsWith(inbox + sep3)) {
|
|
85062
85917
|
throw new Error(`refusing to send channel state: ${f}`);
|
|
85063
85918
|
}
|
|
@@ -85182,7 +86037,7 @@ var HISTORY_ENABLED = HISTORY_ACCESS.historyEnabled !== false;
|
|
|
85182
86037
|
if (HISTORY_ENABLED) {
|
|
85183
86038
|
try {
|
|
85184
86039
|
initHistory(STATE_DIR, HISTORY_ACCESS.historyRetentionDays ?? 30);
|
|
85185
|
-
process.stderr.write(`telegram gateway: history capture enabled at ${
|
|
86040
|
+
process.stderr.write(`telegram gateway: history capture enabled at ${join55(STATE_DIR, "history.db")}
|
|
85186
86041
|
`);
|
|
85187
86042
|
} catch (err) {
|
|
85188
86043
|
process.stderr.write(`telegram gateway: history init failed (${err.message}) \u2014 capture disabled
|
|
@@ -85191,6 +86046,7 @@ if (HISTORY_ENABLED) {
|
|
|
85191
86046
|
}
|
|
85192
86047
|
var turnsDb = null;
|
|
85193
86048
|
var bootResumeInbound = null;
|
|
86049
|
+
var pendingRedelivery = null;
|
|
85194
86050
|
var bridgeDeadPriorStreak = 0;
|
|
85195
86051
|
try {
|
|
85196
86052
|
const agentDir = STATE_DIR.endsWith("/telegram") ? STATE_DIR.slice(0, -"/telegram".length) : STATE_DIR;
|
|
@@ -85199,7 +86055,7 @@ try {
|
|
|
85199
86055
|
let markerTurnKey = null;
|
|
85200
86056
|
let markerAgeMs = null;
|
|
85201
86057
|
try {
|
|
85202
|
-
const markerPath =
|
|
86058
|
+
const markerPath = join55(STATE_DIR, TURN_ACTIVE_MARKER_FILE2);
|
|
85203
86059
|
if (existsSync50(markerPath)) {
|
|
85204
86060
|
const st = statSync16(markerPath);
|
|
85205
86061
|
markerAgeMs = Date.now() - st.mtimeMs;
|
|
@@ -85224,10 +86080,10 @@ try {
|
|
|
85224
86080
|
process.stderr.write(`telegram gateway: turn-registry boot-reaper stamped ${reaped} orphaned turn(s)${timeoutTurnKey ? ` (turnKey=${timeoutTurnKey} as 'timeout', markerAgeMs=${markerAgeMs})` : " as 'restart'"}
|
|
85225
86081
|
`);
|
|
85226
86082
|
} else {
|
|
85227
|
-
process.stderr.write(`telegram gateway: turn-registry initialized at ${
|
|
86083
|
+
process.stderr.write(`telegram gateway: turn-registry initialized at ${join55(agentDir, "telegram", "registry.db")}
|
|
85228
86084
|
`);
|
|
85229
86085
|
}
|
|
85230
|
-
const bridgeDeadMarker = consumeBridgeDeadEscalationMarker(
|
|
86086
|
+
const bridgeDeadMarker = consumeBridgeDeadEscalationMarker(join55(STATE_DIR, "bridge-dead-escalation.json"));
|
|
85231
86087
|
if (bridgeDeadMarker != null) {
|
|
85232
86088
|
bridgeDeadPriorStreak = bridgeDeadMarker.count ?? 1;
|
|
85233
86089
|
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 +86096,7 @@ try {
|
|
|
85240
86096
|
const pending2 = findLatestTurnIfInterrupted(turnsDb);
|
|
85241
86097
|
const selfAgent = process.env.SWITCHROOM_AGENT_NAME ?? "";
|
|
85242
86098
|
if (pending2 != null && selfAgent) {
|
|
85243
|
-
const bootResumeMarkerPath = process.env.SWITCHROOM_GATEWAY_CLEAN_SHUTDOWN_MARKER ??
|
|
86099
|
+
const bootResumeMarkerPath = process.env.SWITCHROOM_GATEWAY_CLEAN_SHUTDOWN_MARKER ?? join55(STATE_DIR, "clean-shutdown.json");
|
|
85244
86100
|
const bootResumeCleanMarker = readCleanShutdownMarker(bootResumeMarkerPath);
|
|
85245
86101
|
const bootResumeForceAlways = process.env.SWITCHROOM_BOOT_RESUME_ALWAYS === "1";
|
|
85246
86102
|
const bootResumeMode = parseBootResumeMode(process.env.SWITCHROOM_BOOT_RESUME);
|
|
@@ -85258,6 +86114,19 @@ try {
|
|
|
85258
86114
|
ageMs: Math.max(0, Date.now() - pending2.started_at),
|
|
85259
86115
|
maxAgeMs: RESUME_MAX_AGE_MS
|
|
85260
86116
|
});
|
|
86117
|
+
const redeliverCapture = decideRedeliverCapture({
|
|
86118
|
+
willBeResumed: bootResumeKind === "resume",
|
|
86119
|
+
hasSessionId: Boolean(pending2.session_id)
|
|
86120
|
+
});
|
|
86121
|
+
if (redeliverCapture.capture) {
|
|
86122
|
+
pendingRedelivery = { turn: pending2, maxAgeMs: RESUME_MAX_AGE_MS };
|
|
86123
|
+
} else if (redeliverCapture.skipReason === "will-be-resumed") {
|
|
86124
|
+
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)
|
|
86125
|
+
`);
|
|
86126
|
+
} else {
|
|
86127
|
+
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
|
|
86128
|
+
`);
|
|
86129
|
+
}
|
|
85261
86130
|
let interruptedSubagents = [];
|
|
85262
86131
|
try {
|
|
85263
86132
|
interruptedSubagents = listNonTerminalSubagentsForTurn(turnsDb, pending2.turn_key).map((s) => ({ agentType: s.agent_type, description: s.description, status: s.status }));
|
|
@@ -85340,7 +86209,7 @@ try {
|
|
|
85340
86209
|
`);
|
|
85341
86210
|
}
|
|
85342
86211
|
}
|
|
85343
|
-
const pendingEnvPath =
|
|
86212
|
+
const pendingEnvPath = join55(agentDir, ".pending-turn.env");
|
|
85344
86213
|
try {
|
|
85345
86214
|
if (pending2 != null) {
|
|
85346
86215
|
const lines = [
|
|
@@ -85396,6 +86265,10 @@ function resolveSubagentOriginChat(agentId) {
|
|
|
85396
86265
|
var WORKER_FEED_FALLBACK_LOG_CAP = 256;
|
|
85397
86266
|
var WORKER_FEED_STALE_TTL_MARGIN_MS = 300000;
|
|
85398
86267
|
var WORKER_FEED_ABSOLUTE_ROW_LIFETIME_CAP_MULTIPLE = 4;
|
|
86268
|
+
var WORKER_FEED_GROUP_MESSAGE_LIFETIME_CAP_MS = (() => {
|
|
86269
|
+
const v = Number(process.env.SWITCHROOM_WORKER_FEED_GROUP_MESSAGE_LIFETIME_CAP_MS);
|
|
86270
|
+
return Number.isFinite(v) && v > 0 ? v : 3600000;
|
|
86271
|
+
})();
|
|
85399
86272
|
var workerFeedOwnerDmFallbackLogged = new Set;
|
|
85400
86273
|
function resolveWorkerFeedChat(agentId, fleetChatId) {
|
|
85401
86274
|
const origin = resolveSubagentOriginChat(agentId);
|
|
@@ -85458,7 +86331,7 @@ function checkApprovals() {
|
|
|
85458
86331
|
return;
|
|
85459
86332
|
}
|
|
85460
86333
|
for (const senderId of files) {
|
|
85461
|
-
const file =
|
|
86334
|
+
const file = join55(APPROVED_DIR, senderId);
|
|
85462
86335
|
bot.api.sendMessage(senderId, "Paired! Say hi to Claude.").then(() => rmSync6(file, { force: true }), (err) => {
|
|
85463
86336
|
process.stderr.write(`telegram gateway: failed to send approval confirm: ${err}
|
|
85464
86337
|
`);
|
|
@@ -85563,7 +86436,7 @@ function noteAgentOutputAt(key, ts) {
|
|
|
85563
86436
|
lastAgentOutputAt.delete(oldest);
|
|
85564
86437
|
}
|
|
85565
86438
|
}
|
|
85566
|
-
var OBLIGATION_STORE_PATH =
|
|
86439
|
+
var OBLIGATION_STORE_PATH = join55(STATE_DIR, "obligations.json");
|
|
85567
86440
|
var obligationStoreFs = {
|
|
85568
86441
|
readFileSync: (p) => readFileSync54(p, "utf8"),
|
|
85569
86442
|
writeFileSync: (p, d) => writeFileSync43(p, d),
|
|
@@ -85678,6 +86551,7 @@ var pendingRestarts = new Map;
|
|
|
85678
86551
|
var pendingSessionCommand = createPendingSessionCommandSlots();
|
|
85679
86552
|
var PENDING_CMD_DRAIN_CAP_MS = 60000;
|
|
85680
86553
|
var lastSessionActiveFile = null;
|
|
86554
|
+
var lastSessionStampedTurnKey = null;
|
|
85681
86555
|
var compactState = initialCompactState();
|
|
85682
86556
|
var compactDispatching = false;
|
|
85683
86557
|
var COMPACT_CARD_TIMEOUT_MS = 900000;
|
|
@@ -87891,7 +88765,7 @@ var statusPinState = new Map;
|
|
|
87891
88765
|
var statusPinChatIds = new Map;
|
|
87892
88766
|
var statusPinPinnedAt = new Map;
|
|
87893
88767
|
var statusPinRightsCache = new PinRightsCache2;
|
|
87894
|
-
var STATUS_PIN_STORE_PATH =
|
|
88768
|
+
var STATUS_PIN_STORE_PATH = join55(STATE_DIR, "status-pins.json");
|
|
87895
88769
|
var statusPinStoreFs = {
|
|
87896
88770
|
readFileSync: (p) => readFileSync54(p, "utf8"),
|
|
87897
88771
|
writeFileSync: (p, d) => writeFileSync43(p, d),
|
|
@@ -87899,7 +88773,7 @@ var statusPinStoreFs = {
|
|
|
87899
88773
|
existsSync: (p) => existsSync50(p)
|
|
87900
88774
|
};
|
|
87901
88775
|
var statusPinPersistEnabled = !STATIC && PIN_STATUS_WHILE_WORKING;
|
|
87902
|
-
var ACTIVITY_CARD_STORE_PATH =
|
|
88776
|
+
var ACTIVITY_CARD_STORE_PATH = join55(STATE_DIR, "activity-cards-pending.json");
|
|
87903
88777
|
var activityCardStoreFs = {
|
|
87904
88778
|
readFileSync: (p) => readFileSync54(p, "utf8"),
|
|
87905
88779
|
writeFileSync: (p, d) => writeFileSync43(p, d),
|
|
@@ -87907,7 +88781,7 @@ var activityCardStoreFs = {
|
|
|
87907
88781
|
existsSync: (p) => existsSync50(p)
|
|
87908
88782
|
};
|
|
87909
88783
|
var activityCardPersistEnabled = !STATIC;
|
|
87910
|
-
var QUEUED_CARD_STORE_PATH =
|
|
88784
|
+
var QUEUED_CARD_STORE_PATH = join55(STATE_DIR, "queued-cards-pending.json");
|
|
87911
88785
|
var queuedCardStoreFs = {
|
|
87912
88786
|
readFileSync: (p) => readFileSync54(p, "utf8"),
|
|
87913
88787
|
writeFileSync: (p, d) => writeFileSync43(p, d),
|
|
@@ -88261,11 +89135,11 @@ var getPinnedProgressCardMessageId = null;
|
|
|
88261
89135
|
var completeProgressCardTurn = null;
|
|
88262
89136
|
var subagentWatcher = null;
|
|
88263
89137
|
var workerActivityFeed = null;
|
|
88264
|
-
var SOCKET_PATH = process.env.SWITCHROOM_GATEWAY_SOCKET ??
|
|
89138
|
+
var SOCKET_PATH = process.env.SWITCHROOM_GATEWAY_SOCKET ?? join55(STATE_DIR, "gateway.sock");
|
|
88265
89139
|
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 ??
|
|
89140
|
+
var GATEWAY_PID_PATH = process.env.SWITCHROOM_GATEWAY_PID_FILE ?? join55(STATE_DIR, "gateway.pid.json");
|
|
89141
|
+
var GATEWAY_SESSION_MARKER_PATH = process.env.SWITCHROOM_GATEWAY_SESSION_MARKER ?? join55(STATE_DIR, "gateway-session.json");
|
|
89142
|
+
var GATEWAY_CLEAN_SHUTDOWN_MARKER_PATH = process.env.SWITCHROOM_GATEWAY_CLEAN_SHUTDOWN_MARKER ?? join55(STATE_DIR, "clean-shutdown.json");
|
|
88269
89143
|
var GATEWAY_STARTED_AT_MS = Date.now();
|
|
88270
89144
|
var BOOT_CARD_ENABLED = process.env.SWITCHROOM_BOOT_CARD !== "false";
|
|
88271
89145
|
var activeBootCard = null;
|
|
@@ -88294,7 +89168,7 @@ function ensureIssuesCard(chatId, threadId) {
|
|
|
88294
89168
|
bot: botApi,
|
|
88295
89169
|
log: (msg) => process.stderr.write(`telegram gateway: ${msg}
|
|
88296
89170
|
`),
|
|
88297
|
-
persistPath:
|
|
89171
|
+
persistPath: join55(stateDir, "issues-card.json")
|
|
88298
89172
|
});
|
|
88299
89173
|
activeIssuesWatcher = startIssuesWatcher({
|
|
88300
89174
|
stateDir,
|
|
@@ -88625,7 +89499,7 @@ startTimer2({
|
|
|
88625
89499
|
}
|
|
88626
89500
|
});
|
|
88627
89501
|
var inboundSpool = STATIC ? undefined : createInboundSpool({
|
|
88628
|
-
path:
|
|
89502
|
+
path: join55(STATE_DIR, "inbound-spool.jsonl"),
|
|
88629
89503
|
fs: {
|
|
88630
89504
|
appendFileSync: (p, d) => appendFileSync6(p, d),
|
|
88631
89505
|
readFileSync: (p) => readFileSync54(p, "utf8"),
|
|
@@ -88760,6 +89634,88 @@ async function deliverCapturedProse(args) {
|
|
|
88760
89634
|
}
|
|
88761
89635
|
}
|
|
88762
89636
|
}
|
|
89637
|
+
async function maybeRedeliverUndeliveredAnswer() {
|
|
89638
|
+
const candidate = pendingRedelivery;
|
|
89639
|
+
pendingRedelivery = null;
|
|
89640
|
+
if (candidate == null || turnsDb == null)
|
|
89641
|
+
return;
|
|
89642
|
+
const { turn, maxAgeMs } = candidate;
|
|
89643
|
+
const sessionId = turn.session_id;
|
|
89644
|
+
if (!sessionId)
|
|
89645
|
+
return;
|
|
89646
|
+
let transcriptText;
|
|
89647
|
+
try {
|
|
89648
|
+
const projectsDir = getProjectsDirForCwd();
|
|
89649
|
+
const path2 = join55(projectsDir, `${sessionId}.jsonl`);
|
|
89650
|
+
if (!existsSync50(path2)) {
|
|
89651
|
+
process.stderr.write(`telegram gateway: crash-redelivery \u2014 transcript not found for turnKey=${turn.turn_key} session=${sessionId} (${path2}); skipping
|
|
89652
|
+
`);
|
|
89653
|
+
return;
|
|
89654
|
+
}
|
|
89655
|
+
transcriptText = readFileSync54(path2, "utf8");
|
|
89656
|
+
} catch (err) {
|
|
89657
|
+
process.stderr.write(`telegram gateway: crash-redelivery \u2014 transcript read failed turnKey=${turn.turn_key}: ${err.message}
|
|
89658
|
+
`);
|
|
89659
|
+
return;
|
|
89660
|
+
}
|
|
89661
|
+
const projected = projectTrailingAnswerFromTranscript(transcriptText);
|
|
89662
|
+
const threadIdNum2 = turn.thread_id != null && turn.thread_id !== "" ? Number(turn.thread_id) : undefined;
|
|
89663
|
+
const threadIdForOracle = threadIdNum2 != null && Number.isFinite(threadIdNum2) ? threadIdNum2 : null;
|
|
89664
|
+
const decision = decideRedeliver({
|
|
89665
|
+
capturedText: projected.text,
|
|
89666
|
+
trailingIsText: projected.trailingIsText,
|
|
89667
|
+
hasDeliveredText: HISTORY_ENABLED ? hasOutboundWithText(turn.chat_id, projected.text, threadIdForOracle, turn.started_at) : false,
|
|
89668
|
+
alreadyRedelivered: turn.answer_redelivered_at != null,
|
|
89669
|
+
ageMs: Math.max(0, Date.now() - turn.started_at),
|
|
89670
|
+
maxAgeMs
|
|
89671
|
+
});
|
|
89672
|
+
if (!decision.redeliver || decision.framedText == null) {
|
|
89673
|
+
process.stderr.write(`telegram gateway: crash-redelivery skipped turnKey=${turn.turn_key} reason=${decision.skipReason ?? "unknown"}
|
|
89674
|
+
`);
|
|
89675
|
+
return;
|
|
89676
|
+
}
|
|
89677
|
+
const chatId = turn.chat_id;
|
|
89678
|
+
const out = redactOutboundText(decision.framedText, "crash_redelivery");
|
|
89679
|
+
const chunks = splitMarkdownChunks2(out, RICH_MESSAGE_MAX_CHARS2);
|
|
89680
|
+
const sentIds = [];
|
|
89681
|
+
try {
|
|
89682
|
+
let liveThreadId = threadIdNum2 != null && Number.isFinite(threadIdNum2) ? threadIdNum2 : undefined;
|
|
89683
|
+
for (const c of chunks) {
|
|
89684
|
+
const sent = await retryWithThreadFallback2(robustApiCall, (tid) => {
|
|
89685
|
+
const opts = {
|
|
89686
|
+
link_preview_options: { is_disabled: true },
|
|
89687
|
+
...tid != null ? { message_thread_id: tid } : {}
|
|
89688
|
+
};
|
|
89689
|
+
return bot.api.sendRichMessage(chatId, richMessage2(c), opts);
|
|
89690
|
+
}, { threadId: liveThreadId, chat_id: chatId, verb: "crash-redelivery.sendMessage" });
|
|
89691
|
+
if (liveThreadId != null && sent.message_thread_id == null) {
|
|
89692
|
+
liveThreadId = undefined;
|
|
89693
|
+
}
|
|
89694
|
+
sentIds.push(sent.message_id);
|
|
89695
|
+
}
|
|
89696
|
+
if (HISTORY_ENABLED && sentIds.length > 0) {
|
|
89697
|
+
try {
|
|
89698
|
+
recordOutbound({
|
|
89699
|
+
chat_id: chatId,
|
|
89700
|
+
thread_id: threadIdForOracle,
|
|
89701
|
+
message_ids: sentIds,
|
|
89702
|
+
texts: chunks
|
|
89703
|
+
});
|
|
89704
|
+
} catch {}
|
|
89705
|
+
}
|
|
89706
|
+
try {
|
|
89707
|
+
markAnswerRedelivered(turnsDb, turn.turn_key);
|
|
89708
|
+
} catch (err) {
|
|
89709
|
+
process.stderr.write(`telegram gateway: crash-redelivery markAnswerRedelivered failed turnKey=${turn.turn_key}: ${err.message}
|
|
89710
|
+
`);
|
|
89711
|
+
}
|
|
89712
|
+
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}
|
|
89713
|
+
`);
|
|
89714
|
+
} catch (err) {
|
|
89715
|
+
process.stderr.write(`telegram gateway: crash-redelivery send failed turnKey=${turn.turn_key}: ${err.message} ` + `\u2014 left un-stamped for a later retry
|
|
89716
|
+
`);
|
|
89717
|
+
}
|
|
89718
|
+
}
|
|
88763
89719
|
function obligationSweep() {
|
|
88764
89720
|
if (!OBLIGATION_LEDGER_ENABLED)
|
|
88765
89721
|
return;
|
|
@@ -88919,8 +89875,8 @@ var bridgeDeadWatchdog = createBridgeDeadWatchdog({
|
|
|
88919
89875
|
isSessionAlive: () => findAgentProcessInContainer2()?.comm === "claude",
|
|
88920
89876
|
isShuttingDown: () => shuttingDown,
|
|
88921
89877
|
escalate: (reason) => triggerSelfRestart(process.env.SWITCHROOM_AGENT_NAME ?? "", reason, 1500),
|
|
88922
|
-
crashLogPath:
|
|
88923
|
-
markerPath:
|
|
89878
|
+
crashLogPath: join55(STATE_DIR, "bridge-crash.log"),
|
|
89879
|
+
markerPath: join55(STATE_DIR, "bridge-dead-escalation.json"),
|
|
88924
89880
|
log: (line) => process.stderr.write(`${line}
|
|
88925
89881
|
`),
|
|
88926
89882
|
priorStreak: bridgeDeadPriorStreak,
|
|
@@ -89047,8 +90003,8 @@ var ipcServer = createIpcServer({
|
|
|
89047
90003
|
probeQuotaViaBroker: (t) => probeQuotaForBootCard(agentSlug, t),
|
|
89048
90004
|
tmuxSupervisor: process.env.SWITCHROOM_TMUX_SUPERVISOR === "1",
|
|
89049
90005
|
dockerMode: process.env.SWITCHROOM_RUNTIME === "docker",
|
|
89050
|
-
configSnapshotPath:
|
|
89051
|
-
bootCardStatePath:
|
|
90006
|
+
configSnapshotPath: join55(resolvedAgentDirForCard, ".config-snapshot.json"),
|
|
90007
|
+
bootCardStatePath: join55(resolvedAgentDirForCard, ".boot-card-msgid.json"),
|
|
89052
90008
|
floodStatePath: FLOOD_STATE_PATH,
|
|
89053
90009
|
...updateOutcomeLine ? { updateOutcomeLine } : {}
|
|
89054
90010
|
}, ackMsgId).then((handle) => {
|
|
@@ -89133,6 +90089,21 @@ var ipcServer = createIpcServer({
|
|
|
89133
90089
|
return;
|
|
89134
90090
|
if (msg.activeFile)
|
|
89135
90091
|
lastSessionActiveFile = msg.activeFile;
|
|
90092
|
+
if (turnsDb != null && msg.activeFile != null) {
|
|
90093
|
+
const stampKey = currentTurn?.registryKey ?? null;
|
|
90094
|
+
if (stampKey != null && stampKey !== lastSessionStampedTurnKey) {
|
|
90095
|
+
const sessionId = basename13(msg.activeFile).replace(/\.jsonl$/, "");
|
|
90096
|
+
if (sessionId) {
|
|
90097
|
+
try {
|
|
90098
|
+
stampTurnSessionId(turnsDb, stampKey, sessionId);
|
|
90099
|
+
lastSessionStampedTurnKey = stampKey;
|
|
90100
|
+
} catch (err) {
|
|
90101
|
+
process.stderr.write(`telegram gateway: stampTurnSessionId failed turnKey=${stampKey}: ${err.message}
|
|
90102
|
+
`);
|
|
90103
|
+
}
|
|
90104
|
+
}
|
|
90105
|
+
}
|
|
90106
|
+
}
|
|
89136
90107
|
const ev = msg.event;
|
|
89137
90108
|
handleSessionEvent(ev);
|
|
89138
90109
|
toolFlightTracker.onEvent(ev);
|
|
@@ -89715,7 +90686,7 @@ var ipcServer = createIpcServer({
|
|
|
89715
90686
|
const receiverUid = receiverUidRaw ? Number(receiverUidRaw) : NaN;
|
|
89716
90687
|
if (Number.isInteger(receiverUid))
|
|
89717
90688
|
allowedUids.push(receiverUid);
|
|
89718
|
-
const socketPath =
|
|
90689
|
+
const socketPath = join55(STATE_DIR, "webhook.sock");
|
|
89719
90690
|
const webhookInject = (agentName3, inbound) => {
|
|
89720
90691
|
const msg = inbound;
|
|
89721
90692
|
const delivered = ipcServer.sendToAgent(agentName3, msg);
|
|
@@ -89950,9 +90921,9 @@ function redactOutboundText(text5, site) {
|
|
|
89950
90921
|
var VOICE_OUT_DEFAULT_CHUNK_CHARS = 600;
|
|
89951
90922
|
var VOICE_OUT_HARD_CHUNK_CAP = 4096;
|
|
89952
90923
|
var voiceOnDemandCache = new VoiceOnDemandCache({
|
|
89953
|
-
persistPath:
|
|
90924
|
+
persistPath: join55(STATE_DIR, "voice-ondemand.json")
|
|
89954
90925
|
});
|
|
89955
|
-
var VOICE_CACHE_DIR =
|
|
90926
|
+
var VOICE_CACHE_DIR = join55(STATE_DIR, "voice-cache");
|
|
89956
90927
|
var voicePreSynthQueue = new PreSynthQueue({
|
|
89957
90928
|
runJob: async (job) => {
|
|
89958
90929
|
const sidecarToken = await materializeSidecarToken();
|
|
@@ -90931,7 +91902,7 @@ async function executeSendGif(rawArgs) {
|
|
|
90931
91902
|
};
|
|
90932
91903
|
}
|
|
90933
91904
|
async function publishToTelegraph(text5, shortName, authorName) {
|
|
90934
|
-
const accountPath =
|
|
91905
|
+
const accountPath = join55(STATE_DIR, "telegraph-account.json");
|
|
90935
91906
|
let account = null;
|
|
90936
91907
|
try {
|
|
90937
91908
|
if (existsSync50(accountPath)) {
|
|
@@ -91854,7 +92825,8 @@ function composeTurnActivity(turn, final = false, liveSuffix = "") {
|
|
|
91854
92825
|
elapsedMs: turn.startedAt > 0 ? Date.now() - turn.startedAt : 0,
|
|
91855
92826
|
toolCount: turn.labeledToolCount,
|
|
91856
92827
|
state: final ? "done" : "running",
|
|
91857
|
-
model: turn.currentModel
|
|
92828
|
+
model: turn.currentModel,
|
|
92829
|
+
totalTokens: turn.totalTokens
|
|
91858
92830
|
};
|
|
91859
92831
|
return renderActivityFeedWithNested2(turn.mirrorLines, childLines, final, liveSuffix, stepCount, header);
|
|
91860
92832
|
}
|
|
@@ -92315,6 +93287,8 @@ function handleSessionEvent(ev) {
|
|
|
92315
93287
|
lastAssistantDone: false,
|
|
92316
93288
|
toolCallCount: 0,
|
|
92317
93289
|
labeledToolCount: 0,
|
|
93290
|
+
totalTokens: 0,
|
|
93291
|
+
seenUsageMessageIds: new Set,
|
|
92318
93292
|
activityMessageId: null,
|
|
92319
93293
|
activityInFlight: null,
|
|
92320
93294
|
activityPendingRender: null,
|
|
@@ -92388,6 +93362,18 @@ function handleSessionEvent(ev) {
|
|
|
92388
93362
|
sessionModelSource.noteTranscriptModel(ev.model);
|
|
92389
93363
|
return;
|
|
92390
93364
|
}
|
|
93365
|
+
case "usage": {
|
|
93366
|
+
const turn = currentTurn;
|
|
93367
|
+
if (turn == null)
|
|
93368
|
+
return;
|
|
93369
|
+
if (ev.messageId != null) {
|
|
93370
|
+
if (turn.seenUsageMessageIds.has(ev.messageId))
|
|
93371
|
+
return;
|
|
93372
|
+
turn.seenUsageMessageIds.add(ev.messageId);
|
|
93373
|
+
}
|
|
93374
|
+
turn.totalTokens += ev.totalTokens;
|
|
93375
|
+
return;
|
|
93376
|
+
}
|
|
92391
93377
|
case "thinking": {
|
|
92392
93378
|
const turn = currentTurn;
|
|
92393
93379
|
if (turn == null)
|
|
@@ -94258,7 +95244,7 @@ function getMyAgentName() {
|
|
|
94258
95244
|
const fromEnv = process.env.SWITCHROOM_AGENT_NAME;
|
|
94259
95245
|
if (fromEnv && fromEnv.trim().length > 0)
|
|
94260
95246
|
return fromEnv.trim();
|
|
94261
|
-
return
|
|
95247
|
+
return basename13(process.cwd());
|
|
94262
95248
|
}
|
|
94263
95249
|
function isSelfTargetingCommand(name) {
|
|
94264
95250
|
if (name === "all")
|
|
@@ -94271,7 +95257,7 @@ function restartMarkerPath() {
|
|
|
94271
95257
|
const agentDir = resolveAgentDirFromEnv();
|
|
94272
95258
|
if (!agentDir)
|
|
94273
95259
|
return null;
|
|
94274
|
-
return
|
|
95260
|
+
return join55(agentDir, "restart-pending.json");
|
|
94275
95261
|
}
|
|
94276
95262
|
function writeRestartMarker(marker) {
|
|
94277
95263
|
const p = restartMarkerPath();
|
|
@@ -94463,7 +95449,7 @@ function _resetDockerReachableCache() {
|
|
|
94463
95449
|
}
|
|
94464
95450
|
function spawnSwitchroomDetached(args, onFailure) {
|
|
94465
95451
|
const fullArgs = SWITCHROOM_CONFIG ? ["--config", SWITCHROOM_CONFIG, ...args] : args;
|
|
94466
|
-
const logPath =
|
|
95452
|
+
const logPath = join55(STATE_DIR, "detached-spawn.log");
|
|
94467
95453
|
let outFd = null;
|
|
94468
95454
|
try {
|
|
94469
95455
|
mkdirSync40(STATE_DIR, { recursive: true });
|
|
@@ -94861,7 +95847,7 @@ bot.use(async (ctx, next) => {
|
|
|
94861
95847
|
});
|
|
94862
95848
|
function readRecentDenialsForAgent(agentName3, windowMs, limit) {
|
|
94863
95849
|
try {
|
|
94864
|
-
const auditPath =
|
|
95850
|
+
const auditPath = join55(homedir18(), ".switchroom", "vault-audit.log");
|
|
94865
95851
|
if (!existsSync50(auditPath))
|
|
94866
95852
|
return [];
|
|
94867
95853
|
const raw = readFileSync54(auditPath, "utf8");
|
|
@@ -94915,7 +95901,7 @@ async function buildAgentMetadata(agentName3) {
|
|
|
94915
95901
|
try {
|
|
94916
95902
|
const agentDir = resolveAgentDirFromEnv();
|
|
94917
95903
|
if (agentDir) {
|
|
94918
|
-
const raw = readFileSync54(
|
|
95904
|
+
const raw = readFileSync54(join55(agentDir, ".claude", ".claude.json"), "utf8");
|
|
94919
95905
|
claudeJson = JSON.parse(raw);
|
|
94920
95906
|
}
|
|
94921
95907
|
} catch {}
|
|
@@ -95111,7 +96097,7 @@ function buildModelDeps(restartCtx) {
|
|
|
95111
96097
|
try {
|
|
95112
96098
|
const agentDir = resolveAgentDirFromEnv();
|
|
95113
96099
|
if (agentDir) {
|
|
95114
|
-
const local = await fetchQuota2({ claudeConfigDir:
|
|
96100
|
+
const local = await fetchQuota2({ claudeConfigDir: join55(agentDir, ".claude") });
|
|
95115
96101
|
if (local.ok)
|
|
95116
96102
|
return formatQuotaLine2(local.data);
|
|
95117
96103
|
}
|
|
@@ -95583,7 +96569,7 @@ bot.command("restart", async (ctx) => {
|
|
|
95583
96569
|
function flushAgentHandoff(agentDir) {
|
|
95584
96570
|
let removed = 0;
|
|
95585
96571
|
for (const fname of [".handoff.md", ".handoff-topic"]) {
|
|
95586
|
-
const p =
|
|
96572
|
+
const p = join55(agentDir, fname);
|
|
95587
96573
|
try {
|
|
95588
96574
|
if (existsSync50(p)) {
|
|
95589
96575
|
unlinkSync24(p);
|
|
@@ -95641,7 +96627,7 @@ async function handleNewCommand(ctx) {
|
|
|
95641
96627
|
writeRestartMarker({ chat_id: chatId, thread_id: threadId ?? null, ack_message_id: ackId, ts: Date.now() });
|
|
95642
96628
|
if (agentDir != null) {
|
|
95643
96629
|
try {
|
|
95644
|
-
writeFileSync43(
|
|
96630
|
+
writeFileSync43(join55(agentDir, ".force-fresh-session"), `${kind} at ${new Date().toISOString()}
|
|
95645
96631
|
`, "utf8");
|
|
95646
96632
|
} catch (err) {
|
|
95647
96633
|
process.stderr.write(`telegram gateway: failed to write force-fresh marker: ${err}
|
|
@@ -96011,7 +96997,7 @@ var lockoutOps = {
|
|
|
96011
96997
|
writeFileSync: (p, data, opts) => writeFileSync43(p, data, opts),
|
|
96012
96998
|
existsSync: (p) => existsSync50(p),
|
|
96013
96999
|
mkdirSync: (p, opts) => mkdirSync40(p, opts),
|
|
96014
|
-
joinPath: (...parts) =>
|
|
97000
|
+
joinPath: (...parts) => join55(...parts)
|
|
96015
97001
|
};
|
|
96016
97002
|
var FLEET_FALLBACK_DEDUP_MS = 30000;
|
|
96017
97003
|
function isAuthBrokerSocketReachable() {
|
|
@@ -96271,7 +97257,7 @@ async function runCreditWatch() {
|
|
|
96271
97257
|
if (!agentDir)
|
|
96272
97258
|
return;
|
|
96273
97259
|
const agentName3 = getMyAgentName();
|
|
96274
|
-
const claudeConfigDir =
|
|
97260
|
+
const claudeConfigDir = join55(agentDir, ".claude");
|
|
96275
97261
|
const stateDir = STATE_DIR;
|
|
96276
97262
|
const reason = readClaudeJsonOverage(claudeConfigDir);
|
|
96277
97263
|
const prev = loadCreditState(stateDir);
|
|
@@ -97283,7 +98269,7 @@ bot.command("usage", async (ctx) => {
|
|
|
97283
98269
|
await switchroomReply(ctx, "**/usage:** cannot resolve agent dir.", { html: true });
|
|
97284
98270
|
return;
|
|
97285
98271
|
}
|
|
97286
|
-
const result = await fetchQuota2({ claudeConfigDir:
|
|
98272
|
+
const result = await fetchQuota2({ claudeConfigDir: join55(agentDir, ".claude") });
|
|
97287
98273
|
if (!result.ok) {
|
|
97288
98274
|
await switchroomReply(ctx, `**/usage:** ${escapeHtmlForTg2(result.reason)}`, { html: true });
|
|
97289
98275
|
return;
|
|
@@ -99171,6 +100157,10 @@ var didOneTimeSetup = false;
|
|
|
99171
100157
|
process.stderr.write(`telegram gateway: blocked-approval boot reconcile failed: ${err.message}
|
|
99172
100158
|
`);
|
|
99173
100159
|
}
|
|
100160
|
+
maybeRedeliverUndeliveredAnswer().catch((err) => {
|
|
100161
|
+
process.stderr.write(`telegram gateway: crash-redelivery boot send errored: ${err.message}
|
|
100162
|
+
`);
|
|
100163
|
+
});
|
|
99174
100164
|
try {
|
|
99175
100165
|
const bootAccess = loadAccess();
|
|
99176
100166
|
const chatSet = new Set(bootAccess.allowFrom);
|
|
@@ -99268,7 +100258,7 @@ var didOneTimeSetup = false;
|
|
|
99268
100258
|
return;
|
|
99269
100259
|
}
|
|
99270
100260
|
})();
|
|
99271
|
-
const resolvedAgentDirForBootCard = agentDir ??
|
|
100261
|
+
const resolvedAgentDirForBootCard = agentDir ?? join55(homedir18(), ".switchroom", "agents", agentSlug);
|
|
99272
100262
|
const handle = await startBootCard(chatId, threadId, botApiForCard, {
|
|
99273
100263
|
agentName: agentDisplayName,
|
|
99274
100264
|
agentSlug,
|
|
@@ -99282,8 +100272,8 @@ var didOneTimeSetup = false;
|
|
|
99282
100272
|
probeQuotaViaBroker: (t) => probeQuotaForBootCard(agentSlug, t),
|
|
99283
100273
|
tmuxSupervisor: process.env.SWITCHROOM_TMUX_SUPERVISOR === "1",
|
|
99284
100274
|
dockerMode: process.env.SWITCHROOM_RUNTIME === "docker",
|
|
99285
|
-
configSnapshotPath:
|
|
99286
|
-
bootCardStatePath:
|
|
100275
|
+
configSnapshotPath: join55(resolvedAgentDirForBootCard, ".config-snapshot.json"),
|
|
100276
|
+
bootCardStatePath: join55(resolvedAgentDirForBootCard, ".boot-card-msgid.json"),
|
|
99287
100277
|
floodStatePath: FLOOD_STATE_PATH,
|
|
99288
100278
|
...updateOutcomeLine ? { updateOutcomeLine } : {}
|
|
99289
100279
|
}, ackMsgId);
|
|
@@ -99314,7 +100304,7 @@ var didOneTimeSetup = false;
|
|
|
99314
100304
|
try {
|
|
99315
100305
|
const smAgentDir = resolveAgentDirFromEnv();
|
|
99316
100306
|
if (smAgentDir) {
|
|
99317
|
-
const activePath =
|
|
100307
|
+
const activePath = join55(smAgentDir, ".active-session-model");
|
|
99318
100308
|
if (existsSync50(activePath)) {
|
|
99319
100309
|
try {
|
|
99320
100310
|
const launched = readFileSync54(activePath, "utf8").trim();
|
|
@@ -99326,7 +100316,7 @@ var didOneTimeSetup = false;
|
|
|
99326
100316
|
sessionModelSource.setOverride(launched.length > 0 && launched !== configured ? launched : null);
|
|
99327
100317
|
} catch {}
|
|
99328
100318
|
}
|
|
99329
|
-
const activeEffortPath =
|
|
100319
|
+
const activeEffortPath = join55(smAgentDir, ".active-session-effort");
|
|
99330
100320
|
if (existsSync50(activeEffortPath)) {
|
|
99331
100321
|
try {
|
|
99332
100322
|
const launchedEffort = readFileSync54(activeEffortPath, "utf8").trim();
|
|
@@ -99334,7 +100324,7 @@ var didOneTimeSetup = false;
|
|
|
99334
100324
|
sessionEffortOverride = launchedEffort.length > 0 && launchedEffort !== configuredEffort ? launchedEffort : null;
|
|
99335
100325
|
} catch {}
|
|
99336
100326
|
}
|
|
99337
|
-
const alertPath =
|
|
100327
|
+
const alertPath = join55(smAgentDir, ".session-model-alert");
|
|
99338
100328
|
if (existsSync50(alertPath)) {
|
|
99339
100329
|
let alertText = null;
|
|
99340
100330
|
try {
|
|
@@ -99463,6 +100453,7 @@ var didOneTimeSetup = false;
|
|
|
99463
100453
|
maxRows: workerFeedMaxRows,
|
|
99464
100454
|
staleWorkerTtlMs: resolveInflightTerminalCapMs() + WORKER_FEED_STALE_TTL_MARGIN_MS,
|
|
99465
100455
|
absoluteRowLifetimeCapMs: resolveInflightTerminalCapMs() * WORKER_FEED_ABSOLUTE_ROW_LIFETIME_CAP_MULTIPLE,
|
|
100456
|
+
groupMessageLifetimeCapMs: WORKER_FEED_GROUP_MESSAGE_LIFETIME_CAP_MS,
|
|
99466
100457
|
reconcilePin: ({ feedKey, chatId, messageId }) => {
|
|
99467
100458
|
if (!PIN_STATUS_WHILE_WORKING)
|
|
99468
100459
|
return;
|
|
@@ -99516,7 +100507,7 @@ var didOneTimeSetup = false;
|
|
|
99516
100507
|
`);
|
|
99517
100508
|
}
|
|
99518
100509
|
},
|
|
99519
|
-
onFinish: ({ agentId, outcome, description, resultText, toolCount, durationMs, background: entryBackground }) => {
|
|
100510
|
+
onFinish: ({ agentId, outcome, description, resultText, toolCount, totalTokens, durationMs, background: entryBackground }) => {
|
|
99520
100511
|
deferredDoneReactions.promote();
|
|
99521
100512
|
let fleetChatId = "";
|
|
99522
100513
|
try {
|
|
@@ -99544,6 +100535,7 @@ var didOneTimeSetup = false;
|
|
|
99544
100535
|
description: dispatch.feedDescription,
|
|
99545
100536
|
lastTool: null,
|
|
99546
100537
|
toolCount,
|
|
100538
|
+
totalTokens,
|
|
99547
100539
|
latestSummary: resultText,
|
|
99548
100540
|
elapsedMs: durationMs,
|
|
99549
100541
|
state: outcome === "failed" ? "failed" : "done",
|
|
@@ -99591,6 +100583,7 @@ var didOneTimeSetup = false;
|
|
|
99591
100583
|
description: dispatch.feedDescription,
|
|
99592
100584
|
lastTool: null,
|
|
99593
100585
|
toolCount,
|
|
100586
|
+
totalTokens,
|
|
99594
100587
|
latestSummary: resultText,
|
|
99595
100588
|
elapsedMs: durationMs,
|
|
99596
100589
|
state: outcome === "failed" ? "failed" : "done",
|
|
@@ -99604,6 +100597,7 @@ var didOneTimeSetup = false;
|
|
|
99604
100597
|
description: dispatch.feedDescription,
|
|
99605
100598
|
lastTool: null,
|
|
99606
100599
|
toolCount,
|
|
100600
|
+
totalTokens,
|
|
99607
100601
|
latestSummary: resultText,
|
|
99608
100602
|
elapsedMs: durationMs,
|
|
99609
100603
|
state: outcome === "failed" ? "failed" : "done",
|
|
@@ -99644,7 +100638,7 @@ var didOneTimeSetup = false;
|
|
|
99644
100638
|
process.stderr.write(`telegram gateway: subagent-handback queued agent=${agentId} outcome=${outcome} chat=${decision.chatId} resultChars=${resultText.length}
|
|
99645
100639
|
`);
|
|
99646
100640
|
},
|
|
99647
|
-
onProgress: ({ agentId, description, latestSummary, elapsedMs, prevBucketIdx, setBucketIdx, lastTool, toolCount, progressLine, model, skeleton }) => {
|
|
100641
|
+
onProgress: ({ agentId, description, latestSummary, elapsedMs, prevBucketIdx, setBucketIdx, lastTool, toolCount, totalTokens, progressLine, model, skeleton }) => {
|
|
99648
100642
|
let fleetChatId = "";
|
|
99649
100643
|
try {
|
|
99650
100644
|
const fleets = progressDriver?.peekAllFleets() ?? [];
|
|
@@ -99681,7 +100675,8 @@ var didOneTimeSetup = false;
|
|
|
99681
100675
|
latestSummary: stepLine,
|
|
99682
100676
|
elapsedMs,
|
|
99683
100677
|
state: "running",
|
|
99684
|
-
model: feedModel
|
|
100678
|
+
model: feedModel,
|
|
100679
|
+
totalTokens
|
|
99685
100680
|
}, wk.threadId);
|
|
99686
100681
|
return;
|
|
99687
100682
|
}
|
|
@@ -99742,7 +100737,8 @@ var didOneTimeSetup = false;
|
|
|
99742
100737
|
latestSummary: stepLine,
|
|
99743
100738
|
elapsedMs,
|
|
99744
100739
|
state: "running",
|
|
99745
|
-
model: feedModel
|
|
100740
|
+
model: feedModel,
|
|
100741
|
+
totalTokens
|
|
99746
100742
|
}, wk.threadId);
|
|
99747
100743
|
return;
|
|
99748
100744
|
}
|