switchroom 0.18.7 → 0.18.8
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 +905 -758
- package/dist/host-control/main.js +1 -1
- package/package.json +1 -1
- package/profiles/_base/start.sh.hbs +111 -34
- package/skills/switchroom-runtime/SKILL.md +2 -0
- package/telegram-plugin/dist/gateway/gateway.js +1403 -657
- package/telegram-plugin/flood-circuit-breaker.ts +123 -0
- package/telegram-plugin/gateway/activity-card-store.ts +63 -18
- package/telegram-plugin/gateway/boot-card.ts +27 -0
- package/telegram-plugin/gateway/busy-ack.ts +106 -0
- package/telegram-plugin/gateway/gateway.ts +564 -85
- package/telegram-plugin/gateway/mental-model-propose-diff.ts +61 -5
- package/telegram-plugin/gateway/model-command.ts +23 -11
- package/telegram-plugin/gateway/session-model-file.ts +198 -0
- package/telegram-plugin/gateway/status-pin-store.ts +82 -22
- package/telegram-plugin/gateway/worker-pin-reaper.ts +114 -0
- package/telegram-plugin/hooks/hooks.json +10 -10
- package/telegram-plugin/hooks/run-hook.sh +84 -0
- package/telegram-plugin/model-unavailable.ts +26 -0
- package/telegram-plugin/pty-partial-handler.ts +39 -0
- package/telegram-plugin/render/rich-render.ts +79 -1
- package/telegram-plugin/retry-api-call.ts +62 -0
- package/telegram-plugin/shared/bot-runtime.ts +8 -1
- package/telegram-plugin/silence-poke.ts +14 -0
- package/telegram-plugin/stream-controller.ts +156 -38
- package/telegram-plugin/tests/activity-card-store.test.ts +47 -2
- package/telegram-plugin/tests/approval-card-restart-outcome.test.ts +218 -0
- package/telegram-plugin/tests/boot-card-flood-suppress.test.ts +111 -0
- package/telegram-plugin/tests/busy-ack-wiring.test.ts +118 -0
- package/telegram-plugin/tests/busy-ack.test.ts +121 -0
- package/telegram-plugin/tests/flood-circuit-breaker.test.ts +74 -0
- package/telegram-plugin/tests/gateway-session-model-relaunch.test.ts +177 -25
- package/telegram-plugin/tests/mental-model-name-entity-corruption.test.ts +119 -0
- package/telegram-plugin/tests/model-command.test.ts +2 -2
- package/telegram-plugin/tests/model-unavailable.test.ts +41 -0
- package/telegram-plugin/tests/pty-partial-handler.test.ts +56 -0
- package/telegram-plugin/tests/render/render-outbound-chunks.test.ts +98 -0
- package/telegram-plugin/tests/retry-api-call.test.ts +59 -0
- package/telegram-plugin/tests/run-hook-wrapper.test.ts +132 -0
- package/telegram-plugin/tests/session-model-file.test.ts +132 -0
- package/telegram-plugin/tests/slot-banner-boot-recovery.test.ts +3 -3
- package/telegram-plugin/tests/status-pin-boot-recovery.test.ts +3 -3
- package/telegram-plugin/tests/status-pin-store.test.ts +62 -6
- package/telegram-plugin/tests/stream-controller-chunk-cap.test.ts +122 -0
- package/telegram-plugin/tests/voice-send.test.ts +308 -0
- package/telegram-plugin/tests/worker-pin-reaper.test.ts +132 -0
- package/telegram-plugin/uat/scenarios/jtbd-deliberate-restart-resumes-dm.test.ts +118 -0
- package/telegram-plugin/uat/scenarios/jtbd-midflight-busy-ack-dm.test.ts +201 -0
- package/telegram-plugin/uat/scenarios/jtbd-worker-pin-lifecycle-dm.test.ts +208 -0
- package/telegram-plugin/uat/scenarios/vault-card-survives-gateway-restart-dm.test.ts +140 -0
- package/telegram-plugin/uat/scenarios/vault-deny-resumes-turn-dm.test.ts +84 -0
- package/telegram-plugin/uat/scenarios/vault-timeout-wakes-agent-dm.test.ts +91 -0
- package/telegram-plugin/voice-ondemand.ts +25 -1
- package/telegram-plugin/voice-send.ts +154 -0
|
@@ -6784,7 +6784,89 @@ function isBlockquoteLine(line) {
|
|
|
6784
6784
|
function isHeadingLine(line) {
|
|
6785
6785
|
return /^#{1,6}\s/.test(line.trimStart());
|
|
6786
6786
|
}
|
|
6787
|
-
|
|
6787
|
+
function hardSliceToCap(text, cap = RICH_MESSAGE_MAX_CHARS) {
|
|
6788
|
+
if (cap <= 0)
|
|
6789
|
+
return [text];
|
|
6790
|
+
if (text.length <= cap)
|
|
6791
|
+
return [text];
|
|
6792
|
+
const out = [];
|
|
6793
|
+
for (let i = 0;i < text.length; i += cap) {
|
|
6794
|
+
out.push(text.slice(i, i + cap));
|
|
6795
|
+
}
|
|
6796
|
+
return out;
|
|
6797
|
+
}
|
|
6798
|
+
function splitMarkdownChunks(text, maxLen = RICH_MESSAGE_MAX_CHARS) {
|
|
6799
|
+
if (text.length <= maxLen)
|
|
6800
|
+
return [text];
|
|
6801
|
+
const chunks = [];
|
|
6802
|
+
let rest = text;
|
|
6803
|
+
while (rest.length > 0) {
|
|
6804
|
+
if (rest.length <= maxLen) {
|
|
6805
|
+
chunks.push(rest);
|
|
6806
|
+
break;
|
|
6807
|
+
}
|
|
6808
|
+
let cut = maxLen;
|
|
6809
|
+
const paraIdx = rest.lastIndexOf(`
|
|
6810
|
+
|
|
6811
|
+
`, maxLen);
|
|
6812
|
+
const lineIdx = rest.lastIndexOf(`
|
|
6813
|
+
`, maxLen);
|
|
6814
|
+
const spaceIdx = rest.lastIndexOf(" ", maxLen);
|
|
6815
|
+
if (paraIdx > maxLen / 3) {
|
|
6816
|
+
cut = paraIdx;
|
|
6817
|
+
} else if (lineIdx > maxLen / 3) {
|
|
6818
|
+
cut = lineIdx;
|
|
6819
|
+
} else if (spaceIdx > 0) {
|
|
6820
|
+
cut = spaceIdx;
|
|
6821
|
+
}
|
|
6822
|
+
cut = backOffOpenFence(rest, cut);
|
|
6823
|
+
cut = backOffTableRow(rest, cut);
|
|
6824
|
+
if (cut <= 0) {
|
|
6825
|
+
const sliced = hardSliceToCap(rest, maxLen);
|
|
6826
|
+
chunks.push(stripBoundarySpacers(sliced[0], "trailing"));
|
|
6827
|
+
rest = stripBoundarySpacers(sliced.slice(1).join(""), "leading");
|
|
6828
|
+
continue;
|
|
6829
|
+
}
|
|
6830
|
+
chunks.push(stripBoundarySpacers(rest.slice(0, cut), "trailing"));
|
|
6831
|
+
rest = stripBoundarySpacers(rest.slice(cut), "leading");
|
|
6832
|
+
}
|
|
6833
|
+
return chunks.map((c) => stripBoundarySpacers(c, "trailing"));
|
|
6834
|
+
}
|
|
6835
|
+
function stripBoundarySpacers(chunk, side) {
|
|
6836
|
+
const sp = PARAGRAPH_SPACER;
|
|
6837
|
+
if (side === "leading") {
|
|
6838
|
+
return chunk.replace(new RegExp(`^(?:[ \\t]*${sp}?[ \\t]*\\n+)+`), "");
|
|
6839
|
+
}
|
|
6840
|
+
return chunk.replace(new RegExp(`(?:\\n+[ \\t]*${sp}?[ \\t]*)+$`), "");
|
|
6841
|
+
}
|
|
6842
|
+
function backOffOpenFence(text, cut) {
|
|
6843
|
+
if (cut <= 0 || cut >= text.length)
|
|
6844
|
+
return cut;
|
|
6845
|
+
const before = text.slice(0, cut);
|
|
6846
|
+
const fences = before.match(/^```/gm);
|
|
6847
|
+
if (fences == null || fences.length % 2 === 0)
|
|
6848
|
+
return cut;
|
|
6849
|
+
const lastFence = before.lastIndexOf("\n```");
|
|
6850
|
+
if (lastFence <= 0) {
|
|
6851
|
+
return 0;
|
|
6852
|
+
}
|
|
6853
|
+
return lastFence;
|
|
6854
|
+
}
|
|
6855
|
+
function backOffTableRow(text, cut) {
|
|
6856
|
+
if (cut <= 0 || cut >= text.length)
|
|
6857
|
+
return cut;
|
|
6858
|
+
const lineStart = text.lastIndexOf(`
|
|
6859
|
+
`, cut - 1) + 1;
|
|
6860
|
+
const nextNl = text.indexOf(`
|
|
6861
|
+
`, cut);
|
|
6862
|
+
const lineEnd = nextNl === -1 ? text.length : nextNl;
|
|
6863
|
+
const line = text.slice(lineStart, lineEnd);
|
|
6864
|
+
if (line.includes("|")) {
|
|
6865
|
+
return lineStart > 0 ? lineStart - 1 : 0;
|
|
6866
|
+
}
|
|
6867
|
+
return cut;
|
|
6868
|
+
}
|
|
6869
|
+
var RICH_MESSAGE_MAX_CHARS = 32768, PARAGRAPH_SPACER = "\u00a0";
|
|
6788
6870
|
|
|
6789
6871
|
// text-voice-scrub.ts
|
|
6790
6872
|
function enabled() {
|
|
@@ -6975,6 +7057,34 @@ var require_mod4 = __commonJS((exports) => {
|
|
|
6975
7057
|
__exportStar(require_worker(), exports);
|
|
6976
7058
|
});
|
|
6977
7059
|
|
|
7060
|
+
// flood-circuit-breaker.ts
|
|
7061
|
+
import { existsSync as existsSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync3, mkdirSync as mkdirSync5 } from "node:fs";
|
|
7062
|
+
function floodWaitRemainingMs(state, now) {
|
|
7063
|
+
if (!state)
|
|
7064
|
+
return 0;
|
|
7065
|
+
return Math.max(0, state.untilTs - now);
|
|
7066
|
+
}
|
|
7067
|
+
function readFloodState(path) {
|
|
7068
|
+
try {
|
|
7069
|
+
if (!existsSync3(path))
|
|
7070
|
+
return null;
|
|
7071
|
+
const raw = JSON.parse(readFileSync3(path, "utf-8"));
|
|
7072
|
+
if (typeof raw.untilTs !== "number")
|
|
7073
|
+
return null;
|
|
7074
|
+
return {
|
|
7075
|
+
untilTs: raw.untilTs,
|
|
7076
|
+
retryAfterSec: typeof raw.retryAfterSec === "number" ? raw.retryAfterSec : 0,
|
|
7077
|
+
recordedTs: typeof raw.recordedTs === "number" ? raw.recordedTs : 0
|
|
7078
|
+
};
|
|
7079
|
+
} catch {
|
|
7080
|
+
return null;
|
|
7081
|
+
}
|
|
7082
|
+
}
|
|
7083
|
+
function suppressNonEssentialSendMs(path, now) {
|
|
7084
|
+
return floodWaitRemainingMs(readFloodState(path), now);
|
|
7085
|
+
}
|
|
7086
|
+
var init_flood_circuit_breaker = () => {};
|
|
7087
|
+
|
|
6978
7088
|
// gateway/approval-card.ts
|
|
6979
7089
|
function parseApprovalCallback(data) {
|
|
6980
7090
|
if (!data.startsWith("apv:"))
|
|
@@ -17343,7 +17453,7 @@ __export(exports_client, {
|
|
|
17343
17453
|
import * as net from "node:net";
|
|
17344
17454
|
import { homedir as homedir3 } from "node:os";
|
|
17345
17455
|
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
17346
|
-
import { join as
|
|
17456
|
+
import { join as join15 } from "node:path";
|
|
17347
17457
|
function reviveDate(v) {
|
|
17348
17458
|
if (v == null)
|
|
17349
17459
|
return null;
|
|
@@ -17353,7 +17463,7 @@ function reviveDate(v) {
|
|
|
17353
17463
|
return Number.isNaN(d.getTime()) ? null : d;
|
|
17354
17464
|
}
|
|
17355
17465
|
function operatorSocketPath(home = homedir3()) {
|
|
17356
|
-
return
|
|
17466
|
+
return join15(home, ".switchroom", "state", "auth-broker-operator", "sock");
|
|
17357
17467
|
}
|
|
17358
17468
|
function resolveAuthBrokerSocketPath(opts) {
|
|
17359
17469
|
if (opts?.socket)
|
|
@@ -17670,11 +17780,11 @@ var init_flock = () => {};
|
|
|
17670
17780
|
// ../src/vault/vault.ts
|
|
17671
17781
|
import { randomBytes as randomBytes3, scryptSync, createCipheriv, createDecipheriv } from "node:crypto";
|
|
17672
17782
|
import {
|
|
17673
|
-
readFileSync as
|
|
17783
|
+
readFileSync as readFileSync12,
|
|
17674
17784
|
writeSync,
|
|
17675
|
-
existsSync as
|
|
17785
|
+
existsSync as existsSync9,
|
|
17676
17786
|
renameSync as renameSync4,
|
|
17677
|
-
mkdirSync as
|
|
17787
|
+
mkdirSync as mkdirSync12,
|
|
17678
17788
|
unlinkSync as unlinkSync7,
|
|
17679
17789
|
fsyncSync,
|
|
17680
17790
|
openSync,
|
|
@@ -17713,12 +17823,12 @@ function normalizeSecrets(raw) {
|
|
|
17713
17823
|
return out;
|
|
17714
17824
|
}
|
|
17715
17825
|
function openVault(passphrase, vaultPath) {
|
|
17716
|
-
if (!
|
|
17826
|
+
if (!existsSync9(vaultPath)) {
|
|
17717
17827
|
throw new VaultError(`Vault file not found: ${vaultPath}`);
|
|
17718
17828
|
}
|
|
17719
17829
|
let vaultFile;
|
|
17720
17830
|
try {
|
|
17721
|
-
vaultFile = JSON.parse(
|
|
17831
|
+
vaultFile = JSON.parse(readFileSync12(vaultPath, "utf8"));
|
|
17722
17832
|
} catch {
|
|
17723
17833
|
throw new VaultError(`Failed to read vault file: ${vaultPath}`);
|
|
17724
17834
|
}
|
|
@@ -25473,7 +25583,7 @@ var init_schema = __esm(() => {
|
|
|
25473
25583
|
});
|
|
25474
25584
|
|
|
25475
25585
|
// ../src/config/paths.ts
|
|
25476
|
-
import { existsSync as
|
|
25586
|
+
import { existsSync as existsSync10 } from "node:fs";
|
|
25477
25587
|
import { resolve as resolve2 } from "node:path";
|
|
25478
25588
|
function home() {
|
|
25479
25589
|
return process.env.HOME ?? "/root";
|
|
@@ -25489,7 +25599,7 @@ function resolveStatePath2(fragment) {
|
|
|
25489
25599
|
const h = home();
|
|
25490
25600
|
const primary = resolve2(h, DEFAULT_STATE_DIR, fragment);
|
|
25491
25601
|
const legacy = resolve2(h, LEGACY_STATE_DIR, fragment);
|
|
25492
|
-
if (!
|
|
25602
|
+
if (!existsSync10(primary) && existsSync10(legacy)) {
|
|
25493
25603
|
warnLegacyStateOnce(legacy);
|
|
25494
25604
|
return legacy;
|
|
25495
25605
|
}
|
|
@@ -25502,9 +25612,9 @@ function resolveDualPath(pathStr) {
|
|
|
25502
25612
|
const absolute = resolve2(h, rest);
|
|
25503
25613
|
if (rest.startsWith(`${DEFAULT_STATE_DIR}/`)) {
|
|
25504
25614
|
const frag = rest.slice(DEFAULT_STATE_DIR.length + 1);
|
|
25505
|
-
if (!
|
|
25615
|
+
if (!existsSync10(absolute)) {
|
|
25506
25616
|
const legacy = resolve2(h, LEGACY_STATE_DIR, frag);
|
|
25507
|
-
if (
|
|
25617
|
+
if (existsSync10(legacy)) {
|
|
25508
25618
|
warnLegacyStateOnce(legacy);
|
|
25509
25619
|
return legacy;
|
|
25510
25620
|
}
|
|
@@ -25529,7 +25639,7 @@ var init_overlay_schema = __esm(() => {
|
|
|
25529
25639
|
});
|
|
25530
25640
|
|
|
25531
25641
|
// ../src/config/overlay-loader.ts
|
|
25532
|
-
import { existsSync as
|
|
25642
|
+
import { existsSync as existsSync11, readFileSync as readFileSync13, readdirSync as readdirSync2, statSync as statSync3 } from "node:fs";
|
|
25533
25643
|
import { basename as basename4, resolve as resolve3 } from "node:path";
|
|
25534
25644
|
function deriveOverlayTitle(raw, fileName) {
|
|
25535
25645
|
const titleFromComment = raw.match(/^#[^\S\n]*name:[^\S\n]*(\S.*?)[^\S\n]*$/m)?.[1];
|
|
@@ -25545,7 +25655,7 @@ function overlayDirFor(agentName3, subdir) {
|
|
|
25545
25655
|
return resolve3(base);
|
|
25546
25656
|
}
|
|
25547
25657
|
function listYamlFiles(dir) {
|
|
25548
|
-
if (!
|
|
25658
|
+
if (!existsSync11(dir))
|
|
25549
25659
|
return [];
|
|
25550
25660
|
let entries;
|
|
25551
25661
|
try {
|
|
@@ -25593,7 +25703,7 @@ function applyAgentOverlays(config) {
|
|
|
25593
25703
|
const merged = [...agentCfg.schedule ?? []];
|
|
25594
25704
|
for (const file of files) {
|
|
25595
25705
|
try {
|
|
25596
|
-
const raw =
|
|
25706
|
+
const raw = readFileSync13(file, "utf-8");
|
|
25597
25707
|
const parsed = import_yaml.parse(raw);
|
|
25598
25708
|
const doc = OverlayDocSchema.parse(parsed);
|
|
25599
25709
|
const title = deriveOverlayTitle(raw, basename4(file));
|
|
@@ -25634,7 +25744,7 @@ function applyAgentOverlays(config) {
|
|
|
25634
25744
|
const seen = new Set(merged);
|
|
25635
25745
|
for (const file of skillFiles) {
|
|
25636
25746
|
try {
|
|
25637
|
-
const raw =
|
|
25747
|
+
const raw = readFileSync13(file, "utf-8");
|
|
25638
25748
|
const parsed = import_yaml.parse(raw);
|
|
25639
25749
|
const doc = OverlayDocSchema.parse(parsed);
|
|
25640
25750
|
for (const skillName of doc.skills ?? []) {
|
|
@@ -26080,7 +26190,7 @@ function validateNotionWorkspaceConfig(config) {
|
|
|
26080
26190
|
}
|
|
26081
26191
|
|
|
26082
26192
|
// ../src/config/loader.ts
|
|
26083
|
-
import { readFileSync as
|
|
26193
|
+
import { readFileSync as readFileSync14, existsSync as existsSync12 } from "node:fs";
|
|
26084
26194
|
import { homedir as homedir4 } from "node:os";
|
|
26085
26195
|
import { resolve as resolve4 } from "node:path";
|
|
26086
26196
|
function formatZodErrors(error) {
|
|
@@ -26147,7 +26257,7 @@ function findConfigFile(startDir) {
|
|
|
26147
26257
|
resolve4(userDir, "clerk.yml")
|
|
26148
26258
|
].filter(Boolean);
|
|
26149
26259
|
for (const path2 of searchPaths) {
|
|
26150
|
-
if (
|
|
26260
|
+
if (existsSync12(path2)) {
|
|
26151
26261
|
return path2;
|
|
26152
26262
|
}
|
|
26153
26263
|
}
|
|
@@ -26155,12 +26265,12 @@ function findConfigFile(startDir) {
|
|
|
26155
26265
|
}
|
|
26156
26266
|
function loadConfig(configPath) {
|
|
26157
26267
|
const filePath = configPath ?? findConfigFile();
|
|
26158
|
-
if (!
|
|
26268
|
+
if (!existsSync12(filePath)) {
|
|
26159
26269
|
throw new ConfigError(`Config file not found: ${filePath}`);
|
|
26160
26270
|
}
|
|
26161
26271
|
let raw;
|
|
26162
26272
|
try {
|
|
26163
|
-
raw =
|
|
26273
|
+
raw = readFileSync14(filePath, "utf-8");
|
|
26164
26274
|
} catch (err) {
|
|
26165
26275
|
throw new ConfigError(`Failed to read config file: ${filePath}`, [
|
|
26166
26276
|
` ${err.message}`
|
|
@@ -26651,7 +26761,7 @@ function isDockerRuntime() {
|
|
|
26651
26761
|
import * as net2 from "node:net";
|
|
26652
26762
|
import * as fs from "node:fs";
|
|
26653
26763
|
import { homedir as homedir5 } from "node:os";
|
|
26654
|
-
import { join as
|
|
26764
|
+
import { join as join16 } from "node:path";
|
|
26655
26765
|
function defaultBrokerSocketPath() {
|
|
26656
26766
|
if (fs.existsSync(OPERATOR_SOCKET_PATH))
|
|
26657
26767
|
return OPERATOR_SOCKET_PATH;
|
|
@@ -26660,8 +26770,8 @@ function defaultBrokerSocketPath() {
|
|
|
26660
26770
|
return LEGACY_SOCKET_PATH;
|
|
26661
26771
|
}
|
|
26662
26772
|
function vaultTokenFilePath(agentSlug) {
|
|
26663
|
-
const base = process.env.SWITCHROOM_AGENTS_DIR ||
|
|
26664
|
-
return
|
|
26773
|
+
const base = process.env.SWITCHROOM_AGENTS_DIR || join16(homedir5(), ".switchroom", "agents");
|
|
26774
|
+
return join16(base, agentSlug, ".vault-token");
|
|
26665
26775
|
}
|
|
26666
26776
|
function readVaultTokenFile(agentSlug) {
|
|
26667
26777
|
const filePath = vaultTokenFilePath(agentSlug);
|
|
@@ -26816,22 +26926,22 @@ var DEFAULT_TIMEOUT_MS2 = 2000, LEGACY_SOCKET_PATH, OPERATOR_SOCKET_PATH;
|
|
|
26816
26926
|
var init_client2 = __esm(() => {
|
|
26817
26927
|
init_protocol2();
|
|
26818
26928
|
init_peercred();
|
|
26819
|
-
LEGACY_SOCKET_PATH =
|
|
26820
|
-
OPERATOR_SOCKET_PATH =
|
|
26929
|
+
LEGACY_SOCKET_PATH = join16(homedir5(), ".switchroom", "vault-broker.sock");
|
|
26930
|
+
OPERATOR_SOCKET_PATH = join16(homedir5(), ".switchroom", "broker-operator", "sock");
|
|
26821
26931
|
});
|
|
26822
26932
|
|
|
26823
26933
|
// ../src/vault/resolver.ts
|
|
26824
26934
|
import {
|
|
26825
26935
|
chmodSync as chmodSync2,
|
|
26826
26936
|
closeSync as closeSync2,
|
|
26827
|
-
mkdirSync as
|
|
26937
|
+
mkdirSync as mkdirSync13,
|
|
26828
26938
|
mkdtempSync,
|
|
26829
26939
|
openSync as openSync2,
|
|
26830
26940
|
rmSync,
|
|
26831
26941
|
statSync as statSync5,
|
|
26832
26942
|
writeSync as writeSync2
|
|
26833
26943
|
} from "node:fs";
|
|
26834
|
-
import { join as
|
|
26944
|
+
import { join as join17 } from "node:path";
|
|
26835
26945
|
import { tmpdir } from "node:os";
|
|
26836
26946
|
import { constants as fsConstants } from "node:fs";
|
|
26837
26947
|
function isVaultReference(value) {
|
|
@@ -26883,11 +26993,11 @@ function materializationRoot() {
|
|
|
26883
26993
|
return cachedRoot;
|
|
26884
26994
|
const xdg = process.env.XDG_RUNTIME_DIR;
|
|
26885
26995
|
if (xdg) {
|
|
26886
|
-
const base =
|
|
26887
|
-
|
|
26888
|
-
cachedRoot = mkdtempSync(
|
|
26996
|
+
const base = join17(xdg, "switchroom", "vault");
|
|
26997
|
+
mkdirSync13(base, { recursive: true, mode: 448 });
|
|
26998
|
+
cachedRoot = mkdtempSync(join17(base, "run-"));
|
|
26889
26999
|
} else {
|
|
26890
|
-
cachedRoot = mkdtempSync(
|
|
27000
|
+
cachedRoot = mkdtempSync(join17(tmpdir(), "switchroom-vault-"));
|
|
26891
27001
|
}
|
|
26892
27002
|
chmodSync2(cachedRoot, 448);
|
|
26893
27003
|
return cachedRoot;
|
|
@@ -26902,13 +27012,13 @@ function writeFileExclusive(filePath, content3) {
|
|
|
26902
27012
|
}
|
|
26903
27013
|
}
|
|
26904
27014
|
function materializeFilesEntry(key, files) {
|
|
26905
|
-
const dir =
|
|
27015
|
+
const dir = join17(materializationRoot(), key);
|
|
26906
27016
|
if (materializedDirs.has(dir)) {
|
|
26907
27017
|
try {
|
|
26908
27018
|
rmSync(dir, { recursive: true, force: true });
|
|
26909
27019
|
} catch {}
|
|
26910
27020
|
}
|
|
26911
|
-
|
|
27021
|
+
mkdirSync13(dir, { recursive: true, mode: 448 });
|
|
26912
27022
|
chmodSync2(dir, 448);
|
|
26913
27023
|
const st = statSync5(dir);
|
|
26914
27024
|
if (typeof process.getuid === "function" && st.uid !== process.getuid()) {
|
|
@@ -26918,7 +27028,7 @@ function materializeFilesEntry(key, files) {
|
|
|
26918
27028
|
if (filename.includes("/") || filename.includes("\\") || filename === ".." || filename === "." || filename.includes("\x00")) {
|
|
26919
27029
|
throw new Error(`Refusing to materialize vault file with unsafe name: ${filename}`);
|
|
26920
27030
|
}
|
|
26921
|
-
const filePath =
|
|
27031
|
+
const filePath = join17(dir, filename);
|
|
26922
27032
|
const content3 = encoding === "base64" ? Buffer.from(value, "base64") : value;
|
|
26923
27033
|
writeFileExclusive(filePath, content3);
|
|
26924
27034
|
}
|
|
@@ -29432,8 +29542,8 @@ __export(exports_history, {
|
|
|
29432
29542
|
checkpointWal: () => checkpointWal,
|
|
29433
29543
|
_resetForTests: () => _resetForTests
|
|
29434
29544
|
});
|
|
29435
|
-
import { chmodSync as chmodSync4, existsSync as
|
|
29436
|
-
import { join as
|
|
29545
|
+
import { chmodSync as chmodSync4, existsSync as existsSync19, mkdirSync as mkdirSync17 } from "fs";
|
|
29546
|
+
import { join as join20 } from "path";
|
|
29437
29547
|
function loadDatabaseClass() {
|
|
29438
29548
|
if (DatabaseClass != null)
|
|
29439
29549
|
return DatabaseClass;
|
|
@@ -29455,8 +29565,8 @@ function initHistory(stateDir, retentionDays = 30) {
|
|
|
29455
29565
|
if (db != null)
|
|
29456
29566
|
return;
|
|
29457
29567
|
const Database = loadDatabaseClass();
|
|
29458
|
-
|
|
29459
|
-
const path2 =
|
|
29568
|
+
mkdirSync17(stateDir, { recursive: true, mode: 448 });
|
|
29569
|
+
const path2 = join20(stateDir, "history.db");
|
|
29460
29570
|
dbPath = path2;
|
|
29461
29571
|
db = new Database(path2, { create: true });
|
|
29462
29572
|
db.exec("PRAGMA journal_mode = WAL");
|
|
@@ -29494,7 +29604,7 @@ function initHistory(stateDir, retentionDays = 30) {
|
|
|
29494
29604
|
}
|
|
29495
29605
|
for (const suffix of ["", "-shm", "-wal"]) {
|
|
29496
29606
|
const f = path2 + suffix;
|
|
29497
|
-
if (
|
|
29607
|
+
if (existsSync19(f)) {
|
|
29498
29608
|
try {
|
|
29499
29609
|
chmodSync4(f, 420);
|
|
29500
29610
|
} catch {}
|
|
@@ -29519,7 +29629,7 @@ function checkpointWal() {
|
|
|
29519
29629
|
if (dbPath) {
|
|
29520
29630
|
for (const suffix of ["-shm", "-wal"]) {
|
|
29521
29631
|
const f = dbPath + suffix;
|
|
29522
|
-
if (
|
|
29632
|
+
if (existsSync19(f)) {
|
|
29523
29633
|
try {
|
|
29524
29634
|
chmodSync4(f, 420);
|
|
29525
29635
|
} catch {}
|
|
@@ -29686,14 +29796,14 @@ var init_history = __esm(() => {
|
|
|
29686
29796
|
});
|
|
29687
29797
|
|
|
29688
29798
|
// quota-check.ts
|
|
29689
|
-
import { readFileSync as
|
|
29690
|
-
import { join as
|
|
29799
|
+
import { readFileSync as readFileSync19, existsSync as existsSync20 } from "fs";
|
|
29800
|
+
import { join as join21 } from "path";
|
|
29691
29801
|
function readOauthToken(claudeConfigDir) {
|
|
29692
|
-
const tokenFile =
|
|
29693
|
-
if (!
|
|
29802
|
+
const tokenFile = join21(claudeConfigDir, ".oauth-token");
|
|
29803
|
+
if (!existsSync20(tokenFile))
|
|
29694
29804
|
return null;
|
|
29695
29805
|
try {
|
|
29696
|
-
const raw =
|
|
29806
|
+
const raw = readFileSync19(tokenFile, "utf-8").trim();
|
|
29697
29807
|
return raw.length > 0 ? raw : null;
|
|
29698
29808
|
} catch {
|
|
29699
29809
|
return null;
|
|
@@ -32777,7 +32887,7 @@ var require_util = __commonJS((exports2) => {
|
|
|
32777
32887
|
return path2;
|
|
32778
32888
|
}
|
|
32779
32889
|
exports2.normalize = normalize;
|
|
32780
|
-
function
|
|
32890
|
+
function join27(aRoot, aPath) {
|
|
32781
32891
|
if (aRoot === "") {
|
|
32782
32892
|
aRoot = ".";
|
|
32783
32893
|
}
|
|
@@ -32809,7 +32919,7 @@ var require_util = __commonJS((exports2) => {
|
|
|
32809
32919
|
}
|
|
32810
32920
|
return joined;
|
|
32811
32921
|
}
|
|
32812
|
-
exports2.join =
|
|
32922
|
+
exports2.join = join27;
|
|
32813
32923
|
exports2.isAbsolute = function(aPath) {
|
|
32814
32924
|
return aPath.charAt(0) === "/" || urlRegexp.test(aPath);
|
|
32815
32925
|
};
|
|
@@ -32982,7 +33092,7 @@ var require_util = __commonJS((exports2) => {
|
|
|
32982
33092
|
parsed.path = parsed.path.substring(0, index2 + 1);
|
|
32983
33093
|
}
|
|
32984
33094
|
}
|
|
32985
|
-
sourceURL =
|
|
33095
|
+
sourceURL = join27(urlGenerate(parsed), sourceURL);
|
|
32986
33096
|
}
|
|
32987
33097
|
return normalize(sourceURL);
|
|
32988
33098
|
}
|
|
@@ -35587,19 +35697,19 @@ function renderAuthLine(state4, agentName3, now = Date.now()) {
|
|
|
35587
35697
|
}
|
|
35588
35698
|
|
|
35589
35699
|
// gateway/quota-cache.ts
|
|
35590
|
-
import { existsSync as
|
|
35591
|
-
import { join as
|
|
35700
|
+
import { existsSync as existsSync35, readFileSync as readFileSync37, writeFileSync as writeFileSync29, mkdirSync as mkdirSync27 } from "fs";
|
|
35701
|
+
import { join as join38, dirname as dirname10 } from "path";
|
|
35592
35702
|
function defaultCachePath() {
|
|
35593
|
-
return process.env.SWITCHROOM_QUOTA_CACHE_PATH ??
|
|
35703
|
+
return process.env.SWITCHROOM_QUOTA_CACHE_PATH ?? join38(process.env.HOME ?? "/tmp", ".switchroom", "quota-cache.json");
|
|
35594
35704
|
}
|
|
35595
35705
|
function readQuotaCache(opts = {}) {
|
|
35596
35706
|
const path2 = opts.path ?? defaultCachePath();
|
|
35597
35707
|
const now = opts.now ?? Date.now();
|
|
35598
|
-
if (!
|
|
35708
|
+
if (!existsSync35(path2))
|
|
35599
35709
|
return null;
|
|
35600
35710
|
let entry;
|
|
35601
35711
|
try {
|
|
35602
|
-
entry = JSON.parse(
|
|
35712
|
+
entry = JSON.parse(readFileSync37(path2, "utf8"));
|
|
35603
35713
|
} catch {
|
|
35604
35714
|
return null;
|
|
35605
35715
|
}
|
|
@@ -35625,8 +35735,8 @@ function writeQuotaCache(result, opts = {}) {
|
|
|
35625
35735
|
result
|
|
35626
35736
|
};
|
|
35627
35737
|
try {
|
|
35628
|
-
|
|
35629
|
-
|
|
35738
|
+
mkdirSync27(dirname10(path2), { recursive: true });
|
|
35739
|
+
writeFileSync29(path2, JSON.stringify(entry, null, 2), { mode: 384 });
|
|
35630
35740
|
} catch {}
|
|
35631
35741
|
}
|
|
35632
35742
|
var DEFAULT_TTL_MS4, RATE_LIMIT_TTL_MS;
|
|
@@ -35636,8 +35746,8 @@ var init_quota_cache = __esm(() => {
|
|
|
35636
35746
|
});
|
|
35637
35747
|
|
|
35638
35748
|
// gateway/boot-probes.ts
|
|
35639
|
-
import { readFileSync as
|
|
35640
|
-
import { join as
|
|
35749
|
+
import { readFileSync as readFileSync38, readdirSync as readdirSync8, existsSync as existsSync36 } from "fs";
|
|
35750
|
+
import { join as join39 } from "path";
|
|
35641
35751
|
import { execFile as execFileCb } from "child_process";
|
|
35642
35752
|
import { promisify } from "util";
|
|
35643
35753
|
async function withTimeout(label, p, timeoutMs = PROBE_TIMEOUT_MS) {
|
|
@@ -35679,11 +35789,11 @@ function mapPlan(billingType, hasExtra) {
|
|
|
35679
35789
|
}
|
|
35680
35790
|
async function probeAccount(agentDir) {
|
|
35681
35791
|
return withTimeout("Account", (async () => {
|
|
35682
|
-
const claudeDir =
|
|
35683
|
-
const claudeJsonPath =
|
|
35792
|
+
const claudeDir = join39(agentDir, ".claude");
|
|
35793
|
+
const claudeJsonPath = join39(claudeDir, ".claude.json");
|
|
35684
35794
|
let cfg = {};
|
|
35685
35795
|
try {
|
|
35686
|
-
const raw =
|
|
35796
|
+
const raw = readFileSync38(claudeJsonPath, "utf8");
|
|
35687
35797
|
cfg = JSON.parse(raw);
|
|
35688
35798
|
} catch {
|
|
35689
35799
|
return { status: "fail", label: "Account", detail: "no .claude.json" };
|
|
@@ -35701,12 +35811,12 @@ async function probeAccount(agentDir) {
|
|
|
35701
35811
|
let tokenStr = "";
|
|
35702
35812
|
let status = "ok";
|
|
35703
35813
|
for (const candidate of [
|
|
35704
|
-
|
|
35705
|
-
|
|
35814
|
+
join39(claudeDir, ".oauth-token.meta.json"),
|
|
35815
|
+
join39(claudeDir, "accounts", "default", ".oauth-token.meta.json")
|
|
35706
35816
|
]) {
|
|
35707
|
-
if (
|
|
35817
|
+
if (existsSync36(candidate)) {
|
|
35708
35818
|
try {
|
|
35709
|
-
const meta = JSON.parse(
|
|
35819
|
+
const meta = JSON.parse(readFileSync38(candidate, "utf8"));
|
|
35710
35820
|
if (meta.expiresAt) {
|
|
35711
35821
|
tokenStr = " \u00b7 " + formatDaysFromNow(meta.expiresAt);
|
|
35712
35822
|
const daysLeft = Math.round((meta.expiresAt - Date.now()) / 86400000);
|
|
@@ -35881,9 +35991,9 @@ async function resolveTmuxSupervisorPid(agentName3, execFileImpl) {
|
|
|
35881
35991
|
if (!cgroup)
|
|
35882
35992
|
return null;
|
|
35883
35993
|
const procsPath = `/sys/fs/cgroup${cgroup}/cgroup.procs`;
|
|
35884
|
-
if (!
|
|
35994
|
+
if (!existsSync36(procsPath))
|
|
35885
35995
|
return null;
|
|
35886
|
-
const pidsRaw =
|
|
35996
|
+
const pidsRaw = readFileSync38(procsPath, "utf-8");
|
|
35887
35997
|
const pids = pidsRaw.split(`
|
|
35888
35998
|
`).map((s) => s.trim()).filter(Boolean);
|
|
35889
35999
|
if (pids.length === 0)
|
|
@@ -35896,7 +36006,7 @@ async function resolveTmuxSupervisorPid(agentName3, execFileImpl) {
|
|
|
35896
36006
|
let rss = 0;
|
|
35897
36007
|
let comm = "";
|
|
35898
36008
|
try {
|
|
35899
|
-
const status =
|
|
36009
|
+
const status = readFileSync38(`/proc/${pid}/status`, "utf-8");
|
|
35900
36010
|
const rssLine = status.split(`
|
|
35901
36011
|
`).find((l) => l.startsWith("VmRSS:"));
|
|
35902
36012
|
if (rssLine) {
|
|
@@ -35908,7 +36018,7 @@ async function resolveTmuxSupervisorPid(agentName3, execFileImpl) {
|
|
|
35908
36018
|
continue;
|
|
35909
36019
|
}
|
|
35910
36020
|
try {
|
|
35911
|
-
comm =
|
|
36021
|
+
comm = readFileSync38(`/proc/${pid}/comm`, "utf-8").trim();
|
|
35912
36022
|
} catch {}
|
|
35913
36023
|
candidates.push({ pid, rss, comm });
|
|
35914
36024
|
}
|
|
@@ -36088,9 +36198,9 @@ async function probeQuota(claudeConfigDir, _agentDir, fetchImpl = fetch, opts =
|
|
|
36088
36198
|
let claudeDirForProbe = null;
|
|
36089
36199
|
for (const candidate of [
|
|
36090
36200
|
claudeConfigDir,
|
|
36091
|
-
|
|
36201
|
+
join39(claudeConfigDir, "accounts", "default")
|
|
36092
36202
|
]) {
|
|
36093
|
-
if (
|
|
36203
|
+
if (existsSync36(join39(candidate, ".oauth-token"))) {
|
|
36094
36204
|
claudeDirForProbe = candidate;
|
|
36095
36205
|
break;
|
|
36096
36206
|
}
|
|
@@ -36155,7 +36265,7 @@ async function probeHindsight(bankName, fetchImpl = fetch) {
|
|
|
36155
36265
|
}
|
|
36156
36266
|
function readContainerBootTimeMsForProbe() {
|
|
36157
36267
|
try {
|
|
36158
|
-
const stat1 =
|
|
36268
|
+
const stat1 = readFileSync38("/proc/1/stat", "utf8");
|
|
36159
36269
|
const lastParen = stat1.lastIndexOf(")");
|
|
36160
36270
|
if (lastParen < 0)
|
|
36161
36271
|
return null;
|
|
@@ -36163,7 +36273,7 @@ function readContainerBootTimeMsForProbe() {
|
|
|
36163
36273
|
const starttimeTicks = Number(after[19]);
|
|
36164
36274
|
if (!Number.isFinite(starttimeTicks))
|
|
36165
36275
|
return null;
|
|
36166
|
-
const procStat =
|
|
36276
|
+
const procStat = readFileSync38("/proc/stat", "utf8");
|
|
36167
36277
|
const btimeLine = procStat.split(`
|
|
36168
36278
|
`).find((l) => l.startsWith("btime "));
|
|
36169
36279
|
if (!btimeLine)
|
|
@@ -36261,7 +36371,7 @@ async function probeUds(label, socketPath, opts = {}) {
|
|
|
36261
36371
|
}
|
|
36262
36372
|
return withTimeout(label, (async () => {
|
|
36263
36373
|
if (!opts.connectImpl) {
|
|
36264
|
-
if (!
|
|
36374
|
+
if (!existsSync36(socketPath)) {
|
|
36265
36375
|
return {
|
|
36266
36376
|
status: "fail",
|
|
36267
36377
|
label,
|
|
@@ -36325,7 +36435,7 @@ async function probeSkills(agentDir, opts = {}) {
|
|
|
36325
36435
|
return withTimeout("Skills", (async () => {
|
|
36326
36436
|
const fs2 = opts.fs ?? realSkillsFs;
|
|
36327
36437
|
const max = opts.maxNamesShown ?? 3;
|
|
36328
|
-
const skillsDir =
|
|
36438
|
+
const skillsDir = join39(agentDir, ".claude", "skills");
|
|
36329
36439
|
if (!fs2.exists(skillsDir)) {
|
|
36330
36440
|
return { status: "ok", label: "Skills", detail: "no skills dir" };
|
|
36331
36441
|
}
|
|
@@ -36340,17 +36450,17 @@ async function probeSkills(agentDir, opts = {}) {
|
|
|
36340
36450
|
}
|
|
36341
36451
|
const dangling = [];
|
|
36342
36452
|
for (const name of entries) {
|
|
36343
|
-
const skillPath =
|
|
36453
|
+
const skillPath = join39(skillsDir, name);
|
|
36344
36454
|
if (!fs2.exists(skillPath)) {
|
|
36345
36455
|
dangling.push(name);
|
|
36346
36456
|
continue;
|
|
36347
36457
|
}
|
|
36348
|
-
const skillMd =
|
|
36458
|
+
const skillMd = join39(skillPath, "SKILL.md");
|
|
36349
36459
|
if (!fs2.exists(skillMd) && !fs2.exists(skillPath + ".md")) {
|
|
36350
36460
|
continue;
|
|
36351
36461
|
}
|
|
36352
36462
|
}
|
|
36353
|
-
const overlayDir = opts.overlaySkillsDir ??
|
|
36463
|
+
const overlayDir = opts.overlaySkillsDir ?? join39(agentDir, "skills.d");
|
|
36354
36464
|
const overlaySlugs = new Set;
|
|
36355
36465
|
if (fs2.exists(overlayDir)) {
|
|
36356
36466
|
let overlayEntries = [];
|
|
@@ -36392,8 +36502,8 @@ function renderBucketedSkills(switchroom, agent) {
|
|
|
36392
36502
|
}
|
|
36393
36503
|
async function probeConnections(agentDir, opts = {}) {
|
|
36394
36504
|
return withTimeout("Connections", (async () => {
|
|
36395
|
-
const path2 =
|
|
36396
|
-
const read = opts.readFileImpl ?? ((p) =>
|
|
36505
|
+
const path2 = join39(agentDir, ".claude", "connection-health.json");
|
|
36506
|
+
const read = opts.readFileImpl ?? ((p) => readFileSync38(p, "utf8"));
|
|
36397
36507
|
let issues = [];
|
|
36398
36508
|
try {
|
|
36399
36509
|
const parsed = JSON.parse(read(path2));
|
|
@@ -36424,25 +36534,25 @@ var init_boot_probes = __esm(() => {
|
|
|
36424
36534
|
execFile = promisify(execFileCb);
|
|
36425
36535
|
realProcFs = {
|
|
36426
36536
|
readdir: (p) => readdirSync8(p),
|
|
36427
|
-
readFile: (p) =>
|
|
36537
|
+
readFile: (p) => readFileSync38(p, "utf-8")
|
|
36428
36538
|
};
|
|
36429
36539
|
realSchedulerFs = {
|
|
36430
|
-
readFile: (p) =>
|
|
36540
|
+
readFile: (p) => readFileSync38(p, "utf-8"),
|
|
36431
36541
|
mtimeMs: (p) => {
|
|
36432
36542
|
const { statSync: statSync11 } = __require("fs");
|
|
36433
36543
|
return statSync11(p).mtimeMs;
|
|
36434
36544
|
},
|
|
36435
|
-
exists: (p) =>
|
|
36545
|
+
exists: (p) => existsSync36(p)
|
|
36436
36546
|
};
|
|
36437
36547
|
realSkillsFs = {
|
|
36438
36548
|
readdir: (p) => readdirSync8(p),
|
|
36439
|
-
exists: (p) =>
|
|
36549
|
+
exists: (p) => existsSync36(p)
|
|
36440
36550
|
};
|
|
36441
36551
|
});
|
|
36442
36552
|
|
|
36443
36553
|
// gateway/boot-issue-cache.ts
|
|
36444
|
-
import { existsSync as
|
|
36445
|
-
import { dirname as
|
|
36554
|
+
import { existsSync as existsSync37, readFileSync as readFileSync39, writeFileSync as writeFileSync30, mkdirSync as mkdirSync28, renameSync as renameSync13 } from "fs";
|
|
36555
|
+
import { dirname as dirname11 } from "path";
|
|
36446
36556
|
function fingerprintProbe(key, r) {
|
|
36447
36557
|
if (r.status === "ok")
|
|
36448
36558
|
return `${key}:ok`;
|
|
@@ -36521,11 +36631,11 @@ function diffProbes(probes, cache, opts = {}) {
|
|
|
36521
36631
|
return out;
|
|
36522
36632
|
}
|
|
36523
36633
|
function loadCache(path2, now = Date.now) {
|
|
36524
|
-
if (!
|
|
36634
|
+
if (!existsSync37(path2))
|
|
36525
36635
|
return { ...EMPTY_CACHE, probes: {} };
|
|
36526
36636
|
let raw;
|
|
36527
36637
|
try {
|
|
36528
|
-
raw =
|
|
36638
|
+
raw = readFileSync39(path2, "utf-8");
|
|
36529
36639
|
} catch {
|
|
36530
36640
|
return { ...EMPTY_CACHE, probes: {} };
|
|
36531
36641
|
}
|
|
@@ -36534,7 +36644,7 @@ function loadCache(path2, now = Date.now) {
|
|
|
36534
36644
|
parsed = JSON.parse(raw);
|
|
36535
36645
|
} catch {
|
|
36536
36646
|
try {
|
|
36537
|
-
|
|
36647
|
+
renameSync13(path2, `${path2}.corrupt-${now()}`);
|
|
36538
36648
|
} catch {}
|
|
36539
36649
|
return { ...EMPTY_CACHE, probes: {} };
|
|
36540
36650
|
}
|
|
@@ -36568,10 +36678,10 @@ function applyAndSave(path2, cache, diff) {
|
|
|
36568
36678
|
}
|
|
36569
36679
|
}
|
|
36570
36680
|
try {
|
|
36571
|
-
|
|
36681
|
+
mkdirSync28(dirname11(path2), { recursive: true });
|
|
36572
36682
|
const tmp = `${path2}.tmp`;
|
|
36573
|
-
|
|
36574
|
-
|
|
36683
|
+
writeFileSync30(tmp, JSON.stringify(next), { mode: 384 });
|
|
36684
|
+
renameSync13(tmp, path2);
|
|
36575
36685
|
} catch {}
|
|
36576
36686
|
return next;
|
|
36577
36687
|
}
|
|
@@ -36584,8 +36694,8 @@ var init_boot_issue_cache = __esm(() => {
|
|
|
36584
36694
|
|
|
36585
36695
|
// gateway/config-snapshot.ts
|
|
36586
36696
|
import { createHash as createHash2 } from "crypto";
|
|
36587
|
-
import { existsSync as
|
|
36588
|
-
import { dirname as
|
|
36697
|
+
import { existsSync as existsSync38, readFileSync as readFileSync40, writeFileSync as writeFileSync31, mkdirSync as mkdirSync29, renameSync as renameSync14 } from "fs";
|
|
36698
|
+
import { dirname as dirname12 } from "path";
|
|
36589
36699
|
function hashStringArray(items) {
|
|
36590
36700
|
if (!items || items.length === 0)
|
|
36591
36701
|
return null;
|
|
@@ -36649,11 +36759,11 @@ function renderConfigChangeDim(dim) {
|
|
|
36649
36759
|
}
|
|
36650
36760
|
}
|
|
36651
36761
|
function loadSnapshot(path2, now = Date.now) {
|
|
36652
|
-
if (!
|
|
36762
|
+
if (!existsSync38(path2))
|
|
36653
36763
|
return null;
|
|
36654
36764
|
let raw;
|
|
36655
36765
|
try {
|
|
36656
|
-
raw =
|
|
36766
|
+
raw = readFileSync40(path2, "utf-8");
|
|
36657
36767
|
} catch {
|
|
36658
36768
|
return null;
|
|
36659
36769
|
}
|
|
@@ -36662,7 +36772,7 @@ function loadSnapshot(path2, now = Date.now) {
|
|
|
36662
36772
|
parsed = JSON.parse(raw);
|
|
36663
36773
|
} catch {
|
|
36664
36774
|
try {
|
|
36665
|
-
|
|
36775
|
+
renameSync14(path2, `${path2}.corrupt-${now()}`);
|
|
36666
36776
|
} catch {}
|
|
36667
36777
|
return null;
|
|
36668
36778
|
}
|
|
@@ -36683,10 +36793,10 @@ function loadSnapshot(path2, now = Date.now) {
|
|
|
36683
36793
|
}
|
|
36684
36794
|
function persistSnapshot(path2, snapshot) {
|
|
36685
36795
|
try {
|
|
36686
|
-
|
|
36796
|
+
mkdirSync29(dirname12(path2), { recursive: true });
|
|
36687
36797
|
const tmp = `${path2}.tmp`;
|
|
36688
|
-
|
|
36689
|
-
|
|
36798
|
+
writeFileSync31(tmp, JSON.stringify(snapshot), { mode: 384 });
|
|
36799
|
+
renameSync14(tmp, path2);
|
|
36690
36800
|
} catch {}
|
|
36691
36801
|
}
|
|
36692
36802
|
var init_config_snapshot = __esm(() => {
|
|
@@ -36694,13 +36804,13 @@ var init_config_snapshot = __esm(() => {
|
|
|
36694
36804
|
});
|
|
36695
36805
|
|
|
36696
36806
|
// gateway/boot-card-msgid.ts
|
|
36697
|
-
import { readFileSync as
|
|
36807
|
+
import { readFileSync as readFileSync41, writeFileSync as writeFileSync32 } from "node:fs";
|
|
36698
36808
|
function bootCardChatKey(chatId, threadId) {
|
|
36699
36809
|
return `${chatId}:${threadId ?? ""}`;
|
|
36700
36810
|
}
|
|
36701
36811
|
function readStore(path2) {
|
|
36702
36812
|
try {
|
|
36703
|
-
const parsed = JSON.parse(
|
|
36813
|
+
const parsed = JSON.parse(readFileSync41(path2, "utf8"));
|
|
36704
36814
|
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
36705
36815
|
return parsed;
|
|
36706
36816
|
}
|
|
@@ -36719,7 +36829,7 @@ function saveBootCardMsgId(path2, chatKey3, messageId) {
|
|
|
36719
36829
|
if (store2[chatKey3] === messageId)
|
|
36720
36830
|
return;
|
|
36721
36831
|
store2[chatKey3] = messageId;
|
|
36722
|
-
|
|
36832
|
+
writeFileSync32(path2, JSON.stringify(store2), "utf8");
|
|
36723
36833
|
} catch {}
|
|
36724
36834
|
}
|
|
36725
36835
|
var init_boot_card_msgid = () => {};
|
|
@@ -36734,7 +36844,7 @@ __export(exports_boot_card, {
|
|
|
36734
36844
|
renderBootCard: () => renderBootCard,
|
|
36735
36845
|
renderAccountRows: () => renderAuthLine
|
|
36736
36846
|
});
|
|
36737
|
-
import { join as
|
|
36847
|
+
import { join as join40 } from "path";
|
|
36738
36848
|
function resolvePersonaName(slug, loadConfig3) {
|
|
36739
36849
|
try {
|
|
36740
36850
|
const config = loadConfig3 ? loadConfig3() : loadConfig();
|
|
@@ -36825,7 +36935,7 @@ function renderBootCard(opts) {
|
|
|
36825
36935
|
return stackCardLines(flatLines);
|
|
36826
36936
|
}
|
|
36827
36937
|
async function runAllProbes(opts) {
|
|
36828
|
-
const claudeDir =
|
|
36938
|
+
const claudeDir = join40(opts.agentDir, ".claude");
|
|
36829
36939
|
const probes = {};
|
|
36830
36940
|
const slug = opts.agentSlug ?? opts.agentName;
|
|
36831
36941
|
await Promise.allSettled([
|
|
@@ -36866,6 +36976,15 @@ async function startBootCard(chatId, threadId, bot, opts, ackMessageId, log) {
|
|
|
36866
36976
|
const logger2 = log ?? ((l) => process.stderr.write(l));
|
|
36867
36977
|
const setTimeoutFn = opts.setTimeoutImpl ?? setTimeout;
|
|
36868
36978
|
const settleMs = opts.settleWindowMs ?? SETTLE_WINDOW_MS;
|
|
36979
|
+
if (opts.floodStatePath != null) {
|
|
36980
|
+
const now = (opts.nowMs ?? Date.now)();
|
|
36981
|
+
const remainingMs = suppressNonEssentialSendMs(opts.floodStatePath, now);
|
|
36982
|
+
if (remainingMs > 0) {
|
|
36983
|
+
logger2(`telegram gateway: boot-card: SUPPRESSED \u2014 Telegram flood-wait active for ~${Math.round(remainingMs / 1000)}s; ` + `not posting a restart card into the open ban window (issue #2923)
|
|
36984
|
+
`);
|
|
36985
|
+
return { messageId: -1, complete: () => {} };
|
|
36986
|
+
}
|
|
36987
|
+
}
|
|
36869
36988
|
const ackText = renderBootCard({
|
|
36870
36989
|
agentName: opts.agentName,
|
|
36871
36990
|
agentSlug: opts.agentSlug,
|
|
@@ -37079,6 +37198,7 @@ var init_boot_card = __esm(() => {
|
|
|
37079
37198
|
init_boot_issue_cache();
|
|
37080
37199
|
init_config_snapshot();
|
|
37081
37200
|
init_boot_card_msgid();
|
|
37201
|
+
init_flood_circuit_breaker();
|
|
37082
37202
|
init_loader();
|
|
37083
37203
|
init_merge();
|
|
37084
37204
|
DOT = {
|
|
@@ -37614,7 +37734,7 @@ __export(exports_materialize_bot_token, {
|
|
|
37614
37734
|
materializeBotToken: () => materializeBotToken,
|
|
37615
37735
|
BotTokenMaterializeError: () => BotTokenMaterializeError
|
|
37616
37736
|
});
|
|
37617
|
-
import { existsSync as
|
|
37737
|
+
import { existsSync as existsSync46 } from "node:fs";
|
|
37618
37738
|
function pickConfiguredToken(config, agentName3) {
|
|
37619
37739
|
if (agentName3) {
|
|
37620
37740
|
const agent = config.agents?.[agentName3];
|
|
@@ -37628,7 +37748,7 @@ function tryDirectVaultRead3(ref, config, passphrase) {
|
|
|
37628
37748
|
if (!passphrase)
|
|
37629
37749
|
return null;
|
|
37630
37750
|
const vaultPath = resolvePath(config.vault?.path ?? "~/.switchroom/vault.enc");
|
|
37631
|
-
if (!
|
|
37751
|
+
if (!existsSync46(vaultPath))
|
|
37632
37752
|
return null;
|
|
37633
37753
|
try {
|
|
37634
37754
|
const secrets = openVault(passphrase, vaultPath);
|
|
@@ -37720,7 +37840,7 @@ __export(exports_tmux, {
|
|
|
37720
37840
|
captureAgentPane: () => captureAgentPane
|
|
37721
37841
|
});
|
|
37722
37842
|
import { execFileSync as execFileSync5 } from "node:child_process";
|
|
37723
|
-
import { mkdirSync as
|
|
37843
|
+
import { mkdirSync as mkdirSync36, readdirSync as readdirSync10, statSync as statSync15, unlinkSync as unlinkSync19, writeFileSync as writeFileSync39 } from "node:fs";
|
|
37724
37844
|
import { resolve as resolve10 } from "node:path";
|
|
37725
37845
|
function captureAgentPane(opts) {
|
|
37726
37846
|
const { agentName: agentName3, agentDir, reason } = opts;
|
|
@@ -37732,7 +37852,7 @@ function captureAgentPane(opts) {
|
|
|
37732
37852
|
const reasonSlug = sanitizeReason(reason);
|
|
37733
37853
|
const outPath = resolve10(outDir, `${ts}-${reasonSlug}.txt`);
|
|
37734
37854
|
try {
|
|
37735
|
-
|
|
37855
|
+
mkdirSync36(outDir, { recursive: true, mode: 493 });
|
|
37736
37856
|
} catch (err) {
|
|
37737
37857
|
const msg = `mkdir crash-reports failed: ${err.message}`;
|
|
37738
37858
|
console.error(`[tmux-capture] ${agentName3}: ${msg}`);
|
|
@@ -37766,7 +37886,7 @@ function captureAgentPane(opts) {
|
|
|
37766
37886
|
` + `
|
|
37767
37887
|
`;
|
|
37768
37888
|
try {
|
|
37769
|
-
|
|
37889
|
+
writeFileSync39(outPath, Buffer.concat([Buffer.from(header, "utf8"), body]), {
|
|
37770
37890
|
mode: 420
|
|
37771
37891
|
});
|
|
37772
37892
|
} catch (err) {
|
|
@@ -38470,23 +38590,23 @@ var import_runner3 = __toESM(require_mod3(), 1);
|
|
|
38470
38590
|
import { randomBytes as randomBytes8, createHash as createHash3 } from "crypto";
|
|
38471
38591
|
import { execFileSync as execFileSync6, execSync as execSync2, spawn } from "child_process";
|
|
38472
38592
|
import {
|
|
38473
|
-
readFileSync as
|
|
38474
|
-
writeFileSync as
|
|
38475
|
-
mkdirSync as
|
|
38593
|
+
readFileSync as readFileSync50,
|
|
38594
|
+
writeFileSync as writeFileSync40,
|
|
38595
|
+
mkdirSync as mkdirSync37,
|
|
38476
38596
|
readdirSync as readdirSync11,
|
|
38477
|
-
rmSync as
|
|
38597
|
+
rmSync as rmSync5,
|
|
38478
38598
|
statSync as statSync16,
|
|
38479
|
-
renameSync as
|
|
38599
|
+
renameSync as renameSync16,
|
|
38480
38600
|
realpathSync as realpathSync4,
|
|
38481
38601
|
chmodSync as chmodSync8,
|
|
38482
38602
|
openSync as openSync9,
|
|
38483
38603
|
closeSync as closeSync9,
|
|
38484
|
-
existsSync as
|
|
38604
|
+
existsSync as existsSync47,
|
|
38485
38605
|
unlinkSync as unlinkSync20,
|
|
38486
38606
|
appendFileSync as appendFileSync6
|
|
38487
38607
|
} from "fs";
|
|
38488
38608
|
import { homedir as homedir16 } from "os";
|
|
38489
|
-
import { join as
|
|
38609
|
+
import { join as join51, extname, sep as sep3, basename as basename12 } from "path";
|
|
38490
38610
|
|
|
38491
38611
|
// plugin-logger.ts
|
|
38492
38612
|
import { appendFileSync, mkdirSync, renameSync, statSync, existsSync } from "fs";
|
|
@@ -38776,6 +38896,28 @@ function resolveSafeBoundaryEnabled(configured) {
|
|
|
38776
38896
|
return configured !== false;
|
|
38777
38897
|
}
|
|
38778
38898
|
|
|
38899
|
+
// gateway/busy-ack.ts
|
|
38900
|
+
var BUSY_ACK_STEP_AGE_THRESHOLD_MS = 12000;
|
|
38901
|
+
function shouldPostBusyAck(input) {
|
|
38902
|
+
if (input.gateDecision !== "buffer-until-idle" && input.gateDecision !== "steer")
|
|
38903
|
+
return false;
|
|
38904
|
+
if (!input.midToolCall)
|
|
38905
|
+
return false;
|
|
38906
|
+
if (input.stepAgeMs == null || input.stepAgeMs < BUSY_ACK_STEP_AGE_THRESHOLD_MS)
|
|
38907
|
+
return false;
|
|
38908
|
+
if (input.alreadyAcked)
|
|
38909
|
+
return false;
|
|
38910
|
+
return true;
|
|
38911
|
+
}
|
|
38912
|
+
function formatBusyAckText(input) {
|
|
38913
|
+
const name = input.toolName ?? "a long-running step";
|
|
38914
|
+
const activity = input.toolLabel != null && input.toolLabel.length > 0 ? `${name}: ${input.toolLabel}` : name;
|
|
38915
|
+
if (input.gateDecision === "steer") {
|
|
38916
|
+
return `\u23f3 Steer noted \u2014 currently inside \`${activity}\`; I'll fold it in when this step finishes.`;
|
|
38917
|
+
}
|
|
38918
|
+
return `\u23f3 Queued \u2014 currently inside \`${activity}\`; I'll answer when this step finishes.`;
|
|
38919
|
+
}
|
|
38920
|
+
|
|
38779
38921
|
// sticker-aliases.ts
|
|
38780
38922
|
function looksLikeFileId(s) {
|
|
38781
38923
|
return /^[A-Za-z0-9_-]{10,200}$/.test(s);
|
|
@@ -39229,12 +39371,13 @@ class VoiceOnDemandCache {
|
|
|
39229
39371
|
this.flush();
|
|
39230
39372
|
return null;
|
|
39231
39373
|
}
|
|
39232
|
-
const { text, voice, speed, filePath } = entry;
|
|
39374
|
+
const { text, voice, speed, filePath, telegramFileId } = entry;
|
|
39233
39375
|
return {
|
|
39234
39376
|
text,
|
|
39235
39377
|
speed,
|
|
39236
39378
|
...voice !== undefined ? { voice } : {},
|
|
39237
|
-
...filePath !== undefined ? { filePath } : {}
|
|
39379
|
+
...filePath !== undefined ? { filePath } : {},
|
|
39380
|
+
...telegramFileId !== undefined ? { telegramFileId } : {}
|
|
39238
39381
|
};
|
|
39239
39382
|
}
|
|
39240
39383
|
setFilePath(token, filePath) {
|
|
@@ -39244,6 +39387,15 @@ class VoiceOnDemandCache {
|
|
|
39244
39387
|
entry.filePath = filePath;
|
|
39245
39388
|
this.flush();
|
|
39246
39389
|
}
|
|
39390
|
+
setTelegramFileId(token, fileId) {
|
|
39391
|
+
if (fileId.length === 0)
|
|
39392
|
+
return;
|
|
39393
|
+
const entry = this.store.get(token);
|
|
39394
|
+
if (entry == null || entry.expiresAt <= this.now())
|
|
39395
|
+
return;
|
|
39396
|
+
entry.telegramFileId = fileId;
|
|
39397
|
+
this.flush();
|
|
39398
|
+
}
|
|
39247
39399
|
prune(tokens) {
|
|
39248
39400
|
let changed = false;
|
|
39249
39401
|
for (const token of tokens) {
|
|
@@ -39332,6 +39484,49 @@ function mayInjectListenButton(rawKeyboard) {
|
|
|
39332
39484
|
return !rawKeyboard.some((row) => Array.isArray(row) && row.length > 0);
|
|
39333
39485
|
}
|
|
39334
39486
|
|
|
39487
|
+
// voice-send.ts
|
|
39488
|
+
function extractVoiceFileId(sent) {
|
|
39489
|
+
const id = sent?.voice?.file_id;
|
|
39490
|
+
return typeof id === "string" && id.length > 0 ? id : undefined;
|
|
39491
|
+
}
|
|
39492
|
+
function isHttp400Error(err) {
|
|
39493
|
+
if (err == null || typeof err !== "object")
|
|
39494
|
+
return false;
|
|
39495
|
+
const e = err;
|
|
39496
|
+
return e.error_code === 400;
|
|
39497
|
+
}
|
|
39498
|
+
async function sendVoiceReusingFileId(deps) {
|
|
39499
|
+
const shouldReupload = deps.isInvalidFileIdError ?? isHttp400Error;
|
|
39500
|
+
if (deps.fileId != null && deps.fileId.length > 0) {
|
|
39501
|
+
try {
|
|
39502
|
+
const sent = await deps.sendByFileId(deps.fileId);
|
|
39503
|
+
const fresh = extractVoiceFileId(sent);
|
|
39504
|
+
if (fresh != null)
|
|
39505
|
+
deps.onFileId(fresh);
|
|
39506
|
+
return { ok: true, path: "file_id", refreshed: false };
|
|
39507
|
+
} catch (err) {
|
|
39508
|
+
if (!shouldReupload(err)) {
|
|
39509
|
+
return { ok: false, reason: "send-failed", error: err };
|
|
39510
|
+
}
|
|
39511
|
+
deps.log?.(`voice-ondemand: stored file_id rejected (${String(err?.description ?? err?.message ?? err)}) \u2014 re-uploading from disk
|
|
39512
|
+
`);
|
|
39513
|
+
}
|
|
39514
|
+
}
|
|
39515
|
+
const audio = await deps.loadAudio();
|
|
39516
|
+
if (audio == null)
|
|
39517
|
+
return { ok: false, reason: "no-audio" };
|
|
39518
|
+
const refreshed = deps.fileId != null && deps.fileId.length > 0;
|
|
39519
|
+
try {
|
|
39520
|
+
const sent = await deps.sendByUpload(audio);
|
|
39521
|
+
const fresh = extractVoiceFileId(sent);
|
|
39522
|
+
if (fresh != null)
|
|
39523
|
+
deps.onFileId(fresh);
|
|
39524
|
+
return { ok: true, path: "upload", refreshed };
|
|
39525
|
+
} catch (err) {
|
|
39526
|
+
return { ok: false, reason: "send-failed", error: err };
|
|
39527
|
+
}
|
|
39528
|
+
}
|
|
39529
|
+
|
|
39335
39530
|
// voice-presynth.ts
|
|
39336
39531
|
import { readdirSync, statSync as statSync2, unlinkSync, mkdirSync as mkdirSync3, writeFileSync as writeFileSync2, renameSync as renameSync3 } from "fs";
|
|
39337
39532
|
import { join as join2 } from "path";
|
|
@@ -41954,6 +42149,7 @@ import { AsyncLocalStorage } from "async_hooks";
|
|
|
41954
42149
|
var import_grammy = __toESM(require_mod2(), 1);
|
|
41955
42150
|
|
|
41956
42151
|
// shared/bot-runtime.ts
|
|
42152
|
+
init_flood_circuit_breaker();
|
|
41957
42153
|
var tgPostTagStore = new AsyncLocalStorage;
|
|
41958
42154
|
function escapeHtmlForTg(text) {
|
|
41959
42155
|
return text.replace(/([\\`*_~=\[\]|])/g, "\\$1");
|
|
@@ -41981,13 +42177,13 @@ function renderVaultRequestAccessCard(req) {
|
|
|
41981
42177
|
}
|
|
41982
42178
|
|
|
41983
42179
|
// gateway/permission-card-store.ts
|
|
41984
|
-
import { readFileSync as
|
|
42180
|
+
import { readFileSync as readFileSync4, writeFileSync as writeFileSync4, unlinkSync as unlinkSync2 } from "node:fs";
|
|
41985
42181
|
import { join as join4 } from "node:path";
|
|
41986
42182
|
function createPermissionCardStore(stateDir) {
|
|
41987
42183
|
const filePath = join4(stateDir, "pending-perm-cards.json");
|
|
41988
42184
|
function read() {
|
|
41989
42185
|
try {
|
|
41990
|
-
const raw =
|
|
42186
|
+
const raw = readFileSync4(filePath, "utf-8");
|
|
41991
42187
|
const parsed = JSON.parse(raw);
|
|
41992
42188
|
return Array.isArray(parsed) ? parsed : [];
|
|
41993
42189
|
} catch {
|
|
@@ -41996,7 +42192,7 @@ function createPermissionCardStore(stateDir) {
|
|
|
41996
42192
|
}
|
|
41997
42193
|
function write(entries) {
|
|
41998
42194
|
try {
|
|
41999
|
-
|
|
42195
|
+
writeFileSync4(filePath, JSON.stringify(entries), { encoding: "utf-8", mode: 384 });
|
|
42000
42196
|
} catch (err) {
|
|
42001
42197
|
process.stderr.write(`telegram gateway: permission-card-store write failed: ${err.message}
|
|
42002
42198
|
`);
|
|
@@ -42032,13 +42228,13 @@ function createPermissionCardStore(stateDir) {
|
|
|
42032
42228
|
}
|
|
42033
42229
|
|
|
42034
42230
|
// gateway/pending-card-store.ts
|
|
42035
|
-
import { readFileSync as
|
|
42231
|
+
import { readFileSync as readFileSync5, writeFileSync as writeFileSync5, unlinkSync as unlinkSync3, chmodSync } from "node:fs";
|
|
42036
42232
|
import { join as join5 } from "node:path";
|
|
42037
42233
|
function createPendingCardStore(stateDir) {
|
|
42038
42234
|
const filePath = join5(stateDir, "pending-approval-cards.json");
|
|
42039
42235
|
function read() {
|
|
42040
42236
|
try {
|
|
42041
|
-
const raw =
|
|
42237
|
+
const raw = readFileSync5(filePath, "utf-8");
|
|
42042
42238
|
const parsed = JSON.parse(raw);
|
|
42043
42239
|
return Array.isArray(parsed) ? parsed : [];
|
|
42044
42240
|
} catch {
|
|
@@ -42047,7 +42243,7 @@ function createPendingCardStore(stateDir) {
|
|
|
42047
42243
|
}
|
|
42048
42244
|
function write(entries) {
|
|
42049
42245
|
try {
|
|
42050
|
-
|
|
42246
|
+
writeFileSync5(filePath, JSON.stringify(entries), { encoding: "utf-8", mode: 384 });
|
|
42051
42247
|
chmodSync(filePath, 384);
|
|
42052
42248
|
} catch (err) {
|
|
42053
42249
|
process.stderr.write(`telegram gateway: pending-card-store write failed: ${err.message}
|
|
@@ -42216,7 +42412,7 @@ function distinctRequestIds(cards) {
|
|
|
42216
42412
|
}
|
|
42217
42413
|
|
|
42218
42414
|
// gateway/missed-approvals-store.ts
|
|
42219
|
-
import { readFileSync as
|
|
42415
|
+
import { readFileSync as readFileSync6, writeFileSync as writeFileSync6, unlinkSync as unlinkSync4 } from "node:fs";
|
|
42220
42416
|
import { join as join6 } from "node:path";
|
|
42221
42417
|
var MAX_PENDING = 50;
|
|
42222
42418
|
var MAX_DELIVERED = 20;
|
|
@@ -42224,7 +42420,7 @@ function createMissedApprovalsStore(stateDir) {
|
|
|
42224
42420
|
const filePath = join6(stateDir, "missed-approvals.json");
|
|
42225
42421
|
function read() {
|
|
42226
42422
|
try {
|
|
42227
|
-
const raw =
|
|
42423
|
+
const raw = readFileSync6(filePath, "utf-8");
|
|
42228
42424
|
const parsed = JSON.parse(raw);
|
|
42229
42425
|
return {
|
|
42230
42426
|
pending: Array.isArray(parsed?.pending) ? parsed.pending : [],
|
|
@@ -42236,7 +42432,7 @@ function createMissedApprovalsStore(stateDir) {
|
|
|
42236
42432
|
}
|
|
42237
42433
|
function write(f) {
|
|
42238
42434
|
try {
|
|
42239
|
-
|
|
42435
|
+
writeFileSync6(filePath, JSON.stringify(f), { encoding: "utf-8", mode: 384 });
|
|
42240
42436
|
} catch (err) {
|
|
42241
42437
|
process.stderr.write(`telegram gateway: missed-approvals-store write failed: ${err.message}
|
|
42242
42438
|
`);
|
|
@@ -42293,7 +42489,7 @@ function createMissedApprovalsStore(stateDir) {
|
|
|
42293
42489
|
}
|
|
42294
42490
|
|
|
42295
42491
|
// gateway/always-allow-persist-queue.ts
|
|
42296
|
-
import { readFileSync as
|
|
42492
|
+
import { readFileSync as readFileSync7, writeFileSync as writeFileSync7, unlinkSync as unlinkSync5 } from "node:fs";
|
|
42297
42493
|
import { join as join7 } from "node:path";
|
|
42298
42494
|
var MAX_ATTEMPTS = 5;
|
|
42299
42495
|
var MAX_AGE_MS = 24 * 60 * 60 * 1000;
|
|
@@ -42313,7 +42509,7 @@ function computeBackoffMs(attempts, retryAfterMs) {
|
|
|
42313
42509
|
}
|
|
42314
42510
|
return exp;
|
|
42315
42511
|
}
|
|
42316
|
-
function createAlwaysAllowPersistQueue(stateDir, writeFileSyncFn =
|
|
42512
|
+
function createAlwaysAllowPersistQueue(stateDir, writeFileSyncFn = writeFileSync7) {
|
|
42317
42513
|
const filePath = join7(stateDir, "always-allow-persist-queue.json");
|
|
42318
42514
|
let lock = Promise.resolve();
|
|
42319
42515
|
function withLock(fn) {
|
|
@@ -42323,7 +42519,7 @@ function createAlwaysAllowPersistQueue(stateDir, writeFileSyncFn = writeFileSync
|
|
|
42323
42519
|
}
|
|
42324
42520
|
function read() {
|
|
42325
42521
|
try {
|
|
42326
|
-
const raw =
|
|
42522
|
+
const raw = readFileSync7(filePath, "utf-8");
|
|
42327
42523
|
const parsed = JSON.parse(raw);
|
|
42328
42524
|
return { entries: Array.isArray(parsed?.entries) ? parsed.entries : [] };
|
|
42329
42525
|
} catch {
|
|
@@ -52753,6 +52949,7 @@ function renderSafe(doc, source, maxLen = RICH_MESSAGE_MAX_CHARS) {
|
|
|
52753
52949
|
}
|
|
52754
52950
|
|
|
52755
52951
|
// render/rich-render.ts
|
|
52952
|
+
var PLAIN_TEXT_MAX_CHARS = 4096;
|
|
52756
52953
|
function parseRichRenderEnabled(raw) {
|
|
52757
52954
|
if (raw == null)
|
|
52758
52955
|
return false;
|
|
@@ -52765,10 +52962,27 @@ function richRenderEnabled(env = process.env) {
|
|
|
52765
52962
|
function renderOutbound(text4, maxLen = RICH_MESSAGE_MAX_CHARS) {
|
|
52766
52963
|
return renderSafe(parse2(text4), text4, maxLen);
|
|
52767
52964
|
}
|
|
52768
|
-
function
|
|
52769
|
-
if (!richRenderEnabled(env))
|
|
52770
|
-
return { text: text4, mode: "markdown", degradations: [] };
|
|
52771
|
-
|
|
52965
|
+
function renderOutboundChunks(text4, env = process.env, maxLen = RICH_MESSAGE_MAX_CHARS, plainMax = PLAIN_TEXT_MAX_CHARS) {
|
|
52966
|
+
if (!richRenderEnabled(env)) {
|
|
52967
|
+
return [{ text: text4, mode: "markdown", degradations: [] }];
|
|
52968
|
+
}
|
|
52969
|
+
const whole = renderOutbound(text4, maxLen);
|
|
52970
|
+
if (whole.mode === "markdown" && whole.text.length <= maxLen)
|
|
52971
|
+
return [whole];
|
|
52972
|
+
if (whole.mode === "plain" && whole.text.length <= plainMax)
|
|
52973
|
+
return [whole];
|
|
52974
|
+
const out = [];
|
|
52975
|
+
for (const rawPiece of splitMarkdownChunks(text4, maxLen)) {
|
|
52976
|
+
const rendered = renderOutbound(rawPiece, maxLen);
|
|
52977
|
+
if (rendered.mode === "markdown" && rendered.text.length <= maxLen) {
|
|
52978
|
+
out.push(rendered);
|
|
52979
|
+
continue;
|
|
52980
|
+
}
|
|
52981
|
+
for (const plainPiece of splitMarkdownChunks(rendered.text, plainMax)) {
|
|
52982
|
+
out.push({ text: plainPiece, mode: "plain", degradations: rendered.degradations });
|
|
52983
|
+
}
|
|
52984
|
+
}
|
|
52985
|
+
return out.length > 0 ? out : [whole];
|
|
52772
52986
|
}
|
|
52773
52987
|
|
|
52774
52988
|
// stream-controller.ts
|
|
@@ -52809,50 +53023,103 @@ function createStreamController(cfg) {
|
|
|
52809
53023
|
...protectContent === true ? { protect_content: true } : {},
|
|
52810
53024
|
...cfg.disableNotification === true ? { disable_notification: true } : {}
|
|
52811
53025
|
};
|
|
52812
|
-
const
|
|
53026
|
+
const renderPieces = (text4) => {
|
|
52813
53027
|
if (literalText)
|
|
52814
|
-
return
|
|
52815
|
-
|
|
52816
|
-
|
|
52817
|
-
|
|
53028
|
+
return [{ text: text4, rich: false }];
|
|
53029
|
+
return renderOutboundChunks(text4).map((r) => ({ text: r.text, rich: r.mode !== "plain" }));
|
|
53030
|
+
};
|
|
53031
|
+
const sendPiece = (piece, opts) => {
|
|
53032
|
+
if (!piece.rich)
|
|
53033
|
+
return bot.api.sendMessage(chatId, piece.text, opts);
|
|
52818
53034
|
const richOpts = { ...opts };
|
|
52819
53035
|
delete richOpts.link_preview_options;
|
|
52820
|
-
return bot.api.sendRichMessage(chatId, richMessage(
|
|
52821
|
-
};
|
|
52822
|
-
const
|
|
52823
|
-
if (
|
|
52824
|
-
return bot.api.editMessageText(chatId, id,
|
|
52825
|
-
|
|
52826
|
-
|
|
52827
|
-
|
|
52828
|
-
|
|
53036
|
+
return bot.api.sendRichMessage(chatId, richMessage(piece.text), richOpts);
|
|
53037
|
+
};
|
|
53038
|
+
const editPiece = (id, piece, opts) => {
|
|
53039
|
+
if (!piece.rich)
|
|
53040
|
+
return bot.api.editMessageText(chatId, id, piece.text, opts);
|
|
53041
|
+
return bot.api.editMessageText(chatId, id, richMessage(piece.text), opts);
|
|
53042
|
+
};
|
|
53043
|
+
const tailIds = [];
|
|
53044
|
+
const tailLastText = [];
|
|
53045
|
+
const upsertTail = async (ti, piece) => {
|
|
53046
|
+
const existingId = tailIds[ti];
|
|
53047
|
+
if (existingId != null) {
|
|
53048
|
+
if (tailLastText[ti] === piece.text)
|
|
53049
|
+
return;
|
|
53050
|
+
try {
|
|
53051
|
+
await retry(() => editPiece(existingId, piece, baseOpts), { threadId, chat_id: chatId });
|
|
53052
|
+
tailLastText[ti] = piece.text;
|
|
53053
|
+
onEdit?.(existingId, piece.text.length);
|
|
53054
|
+
} catch (err) {
|
|
53055
|
+
if (!literalText && piece.rich && isParseEntitiesError(err)) {
|
|
53056
|
+
warn?.(`stream-controller: tail-piece #${ti + 1} edit parse-entities rejected \u2014 retrying same id=${existingId} as plain text (${err instanceof Error ? err.message : String(err)})`);
|
|
53057
|
+
await retry(() => bot.api.editMessageText(chatId, existingId, piece.text, baseOpts), { threadId, chat_id: chatId });
|
|
53058
|
+
tailLastText[ti] = piece.text;
|
|
53059
|
+
onEdit?.(existingId, piece.text.length);
|
|
53060
|
+
} else {
|
|
53061
|
+
warn?.(`stream-controller: tail-piece #${ti + 1} edit FAILED (id=${existingId}) \u2014 partial delivery, this piece may be stale (${err instanceof Error ? err.message : String(err)})`);
|
|
53062
|
+
}
|
|
53063
|
+
}
|
|
53064
|
+
return;
|
|
53065
|
+
}
|
|
53066
|
+
try {
|
|
53067
|
+
const sent = await retry(() => sendPiece(piece, sendOpts), { threadId, chat_id: chatId });
|
|
53068
|
+
tailIds[ti] = sent.message_id;
|
|
53069
|
+
tailLastText[ti] = piece.text;
|
|
53070
|
+
onSend?.(sent.message_id, piece.text.length);
|
|
53071
|
+
} catch (err) {
|
|
53072
|
+
if (!literalText && piece.rich && isParseEntitiesError(err)) {
|
|
53073
|
+
warn?.(`stream-controller: tail-piece #${ti + 1} send parse-entities rejected \u2014 sending as plain text (${err instanceof Error ? err.message : String(err)})`);
|
|
53074
|
+
const sent = await retry(() => bot.api.sendMessage(chatId, piece.text, sendOpts), { threadId, chat_id: chatId });
|
|
53075
|
+
tailIds[ti] = sent.message_id;
|
|
53076
|
+
tailLastText[ti] = piece.text;
|
|
53077
|
+
onSend?.(sent.message_id, piece.text.length);
|
|
53078
|
+
} else {
|
|
53079
|
+
warn?.(`stream-controller: tail-piece #${ti + 1} send FAILED \u2014 partial delivery, this and later pieces may be missing this flush (${err instanceof Error ? err.message : String(err)})`);
|
|
53080
|
+
}
|
|
53081
|
+
}
|
|
52829
53082
|
};
|
|
52830
53083
|
return createDraftStream(async (text4) => {
|
|
53084
|
+
const pieces = renderPieces(text4);
|
|
53085
|
+
const head = pieces[0];
|
|
53086
|
+
let anchorId;
|
|
52831
53087
|
try {
|
|
52832
|
-
const sent = await retry(() =>
|
|
52833
|
-
|
|
52834
|
-
return sent.message_id;
|
|
53088
|
+
const sent = await retry(() => sendPiece(head, sendOpts), { threadId, chat_id: chatId });
|
|
53089
|
+
anchorId = sent.message_id;
|
|
52835
53090
|
} catch (err) {
|
|
52836
|
-
if (!literalText && isParseEntitiesError(err)) {
|
|
53091
|
+
if (!literalText && head.rich && isParseEntitiesError(err)) {
|
|
52837
53092
|
warn?.(`stream-controller: send parse-entities rejected \u2014 retrying once as plain text (${err instanceof Error ? err.message : String(err)})`);
|
|
52838
|
-
const
|
|
52839
|
-
|
|
52840
|
-
|
|
53093
|
+
const fallbackBody = pieces.length === 1 ? text4 : head.text;
|
|
53094
|
+
const sent = await retry(() => bot.api.sendMessage(chatId, fallbackBody, sendOpts), { threadId, chat_id: chatId });
|
|
53095
|
+
anchorId = sent.message_id;
|
|
53096
|
+
} else {
|
|
53097
|
+
throw err;
|
|
52841
53098
|
}
|
|
52842
|
-
throw err;
|
|
52843
53099
|
}
|
|
53100
|
+
onSend?.(anchorId, head.text.length);
|
|
53101
|
+
for (let pi = 1;pi < pieces.length; pi++) {
|
|
53102
|
+
await upsertTail(pi - 1, pieces[pi]);
|
|
53103
|
+
}
|
|
53104
|
+
return anchorId;
|
|
52844
53105
|
}, async (id, text4) => {
|
|
53106
|
+
const pieces = renderPieces(text4);
|
|
53107
|
+
const head = pieces[0];
|
|
52845
53108
|
try {
|
|
52846
|
-
await retry(() =>
|
|
52847
|
-
onEdit?.(id,
|
|
53109
|
+
await retry(() => editPiece(id, head, baseOpts), { threadId, chat_id: chatId });
|
|
53110
|
+
onEdit?.(id, head.text.length);
|
|
52848
53111
|
} catch (err) {
|
|
52849
|
-
if (!literalText && isParseEntitiesError(err)) {
|
|
53112
|
+
if (!literalText && head.rich && isParseEntitiesError(err)) {
|
|
52850
53113
|
warn?.(`stream-controller: edit parse-entities rejected \u2014 retrying same id=${id} as plain text (${err instanceof Error ? err.message : String(err)})`);
|
|
52851
|
-
|
|
52852
|
-
|
|
52853
|
-
|
|
53114
|
+
const fallbackBody = pieces.length === 1 ? text4 : head.text;
|
|
53115
|
+
await retry(() => bot.api.editMessageText(chatId, id, fallbackBody, baseOpts), { threadId, chat_id: chatId });
|
|
53116
|
+
onEdit?.(id, head.text.length);
|
|
53117
|
+
} else {
|
|
53118
|
+
throw err;
|
|
52854
53119
|
}
|
|
52855
|
-
|
|
53120
|
+
}
|
|
53121
|
+
for (let pi = 1;pi < pieces.length; pi++) {
|
|
53122
|
+
await upsertTail(pi - 1, pieces[pi]);
|
|
52856
53123
|
}
|
|
52857
53124
|
}, {
|
|
52858
53125
|
...throttleMs != null ? { throttleMs } : {},
|
|
@@ -52866,6 +53133,12 @@ function createStreamController(cfg) {
|
|
|
52866
53133
|
}
|
|
52867
53134
|
|
|
52868
53135
|
// pty-partial-handler.ts
|
|
53136
|
+
function looksLikeRawApiError(text4) {
|
|
53137
|
+
if (typeof text4 !== "string" || text4.length === 0)
|
|
53138
|
+
return false;
|
|
53139
|
+
const lower = text4.toLowerCase();
|
|
53140
|
+
return lower.includes("api error:") || lower.includes('"type":"error"') || lower.includes("'type': 'error'") || lower.includes("rate_limit_error") || lower.includes("overloaded_error") || lower.includes('"is_error":true') || / b'\{/.test(text4);
|
|
53141
|
+
}
|
|
52869
53142
|
function streamKey(chatId, threadId) {
|
|
52870
53143
|
const t = threadId == null || threadId === 0 ? "_" : String(threadId);
|
|
52871
53144
|
return `${chatId}:${t}`;
|
|
@@ -52898,6 +53171,8 @@ function handlePtyPartialPure(text4, state, deps) {
|
|
|
52898
53171
|
});
|
|
52899
53172
|
if (suppressed)
|
|
52900
53173
|
return "suppressed";
|
|
53174
|
+
if (looksLikeRawApiError(text4))
|
|
53175
|
+
return "error-suppressed";
|
|
52901
53176
|
if (state.lastPtyPreviewByChat.get(sKey) === text4)
|
|
52902
53177
|
return "dedup-skip";
|
|
52903
53178
|
const isFirst = !state.lastPtyPreviewByChat.has(sKey);
|
|
@@ -52977,12 +53252,22 @@ function createChatLock() {
|
|
|
52977
53252
|
|
|
52978
53253
|
// retry-api-call.ts
|
|
52979
53254
|
var import_grammy5 = __toESM(require_mod2(), 1);
|
|
53255
|
+
function isLocalResourceError(err) {
|
|
53256
|
+
const code2 = err?.code;
|
|
53257
|
+
if (typeof code2 === "string" && ["ENOSPC", "EDQUOT", "EIO", "ENOMEM"].includes(code2)) {
|
|
53258
|
+
return true;
|
|
53259
|
+
}
|
|
53260
|
+
const msg = err instanceof Error ? err.message : String(err ?? "");
|
|
53261
|
+
return /\b(ENOSPC|EDQUOT|EIO|ENOMEM)\b/.test(msg) || /no space left on device/i.test(msg) || /disk quota exceeded/i.test(msg);
|
|
53262
|
+
}
|
|
53263
|
+
var LOCAL_RESOURCE_EXHAUSTED = "LOCAL_RESOURCE_EXHAUSTED";
|
|
52980
53264
|
var DEFAULT_SLEEP = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
52981
53265
|
function createRetryApiCall2(config = {}) {
|
|
52982
53266
|
const maxRetries = config.maxRetries ?? 3;
|
|
52983
53267
|
const sleep = config.sleep ?? DEFAULT_SLEEP;
|
|
52984
53268
|
const observer = config.observer;
|
|
52985
53269
|
const log = config.log;
|
|
53270
|
+
const onFloodWait = config.onFloodWait;
|
|
52986
53271
|
return async function retryApiCall(fn, opts) {
|
|
52987
53272
|
for (let attempt = 0;attempt < maxRetries; attempt++) {
|
|
52988
53273
|
try {
|
|
@@ -52991,9 +53276,18 @@ function createRetryApiCall2(config = {}) {
|
|
|
52991
53276
|
const isGrammyErr = err instanceof import_grammy5.GrammyError;
|
|
52992
53277
|
const msg = err instanceof Error ? err.message : String(err);
|
|
52993
53278
|
const desc = isGrammyErr ? err.description : msg;
|
|
53279
|
+
if (isLocalResourceError(err)) {
|
|
53280
|
+
log?.(`telegram gateway: LOCAL resource exhaustion (${err.code ?? "disk/mem"}) \u2014 ` + `not retrying the send (would feed a flood ban); surfacing degraded state
|
|
53281
|
+
`);
|
|
53282
|
+
observer?.onGiveUp?.({ attempts: attempt + 1, error: err });
|
|
53283
|
+
throw Object.assign(new Error(LOCAL_RESOURCE_EXHAUSTED), { original: err });
|
|
53284
|
+
}
|
|
52994
53285
|
if (isGrammyErr && err.error_code === 429) {
|
|
52995
53286
|
const retryAfter = Number(err.parameters?.retry_after ?? 5);
|
|
52996
53287
|
const delayMs = retryAfter * 1000;
|
|
53288
|
+
try {
|
|
53289
|
+
onFloodWait?.(retryAfter);
|
|
53290
|
+
} catch {}
|
|
52997
53291
|
log?.(`telegram gateway: 429 rate limited, waiting ${retryAfter}s
|
|
52998
53292
|
`);
|
|
52999
53293
|
observer?.onRetry?.({ attempt, reason: "flood_wait", delayMs });
|
|
@@ -53082,6 +53376,7 @@ var import_grammy6 = __toESM(require_mod2(), 1);
|
|
|
53082
53376
|
var import_runner2 = __toESM(require_mod4(), 1);
|
|
53083
53377
|
import { createHash } from "crypto";
|
|
53084
53378
|
import { AsyncLocalStorage as AsyncLocalStorage2 } from "async_hooks";
|
|
53379
|
+
init_flood_circuit_breaker();
|
|
53085
53380
|
var tgPostTagStore2 = new AsyncLocalStorage2;
|
|
53086
53381
|
function _getTgPostTags() {
|
|
53087
53382
|
return tgPostTagStore2.getStore();
|
|
@@ -53125,8 +53420,50 @@ function installTgPostLogger(bot) {
|
|
|
53125
53420
|
});
|
|
53126
53421
|
}
|
|
53127
53422
|
|
|
53423
|
+
// flood-circuit-breaker.ts
|
|
53424
|
+
import { existsSync as existsSync4, readFileSync as readFileSync8, writeFileSync as writeFileSync8, mkdirSync as mkdirSync6 } from "node:fs";
|
|
53425
|
+
import { dirname as dirname3, join as join8 } from "node:path";
|
|
53426
|
+
var FLOOD_STATE_FILE = "flood-wait.json";
|
|
53427
|
+
function floodStatePath(stateDir) {
|
|
53428
|
+
return join8(stateDir, FLOOD_STATE_FILE);
|
|
53429
|
+
}
|
|
53430
|
+
function computeFloodWait(prior, retryAfterSec, now) {
|
|
53431
|
+
const candidate = now + Math.max(0, retryAfterSec) * 1000;
|
|
53432
|
+
const untilTs = prior && prior.untilTs > candidate ? prior.untilTs : candidate;
|
|
53433
|
+
return { untilTs, retryAfterSec, recordedTs: now };
|
|
53434
|
+
}
|
|
53435
|
+
function readFloodState2(path2) {
|
|
53436
|
+
try {
|
|
53437
|
+
if (!existsSync4(path2))
|
|
53438
|
+
return null;
|
|
53439
|
+
const raw = JSON.parse(readFileSync8(path2, "utf-8"));
|
|
53440
|
+
if (typeof raw.untilTs !== "number")
|
|
53441
|
+
return null;
|
|
53442
|
+
return {
|
|
53443
|
+
untilTs: raw.untilTs,
|
|
53444
|
+
retryAfterSec: typeof raw.retryAfterSec === "number" ? raw.retryAfterSec : 0,
|
|
53445
|
+
recordedTs: typeof raw.recordedTs === "number" ? raw.recordedTs : 0
|
|
53446
|
+
};
|
|
53447
|
+
} catch {
|
|
53448
|
+
return null;
|
|
53449
|
+
}
|
|
53450
|
+
}
|
|
53451
|
+
function writeFloodState(path2, state) {
|
|
53452
|
+
try {
|
|
53453
|
+
mkdirSync6(dirname3(path2), { recursive: true });
|
|
53454
|
+
writeFileSync8(path2, JSON.stringify(state), { mode: 384 });
|
|
53455
|
+
} catch {}
|
|
53456
|
+
}
|
|
53457
|
+
function makeFloodWaitRecorder2(path2, now = Date.now) {
|
|
53458
|
+
return (retryAfterSec) => {
|
|
53459
|
+
const t = now();
|
|
53460
|
+
const next = computeFloodWait(readFloodState2(path2), retryAfterSec, t);
|
|
53461
|
+
writeFloodState(path2, next);
|
|
53462
|
+
};
|
|
53463
|
+
}
|
|
53464
|
+
|
|
53128
53465
|
// attachment-path.ts
|
|
53129
|
-
import { join as
|
|
53466
|
+
import { join as join9, basename as basename3, resolve, sep } from "node:path";
|
|
53130
53467
|
function sanitizeExtension(ext) {
|
|
53131
53468
|
if (ext == null)
|
|
53132
53469
|
return "bin";
|
|
@@ -53149,7 +53486,7 @@ function buildAttachmentPath(input) {
|
|
|
53149
53486
|
const ext = extractExtension(input.telegramFilePath);
|
|
53150
53487
|
const uid = sanitizeUniqueId(input.fileUniqueId);
|
|
53151
53488
|
const filename = `${input.now}-${uid}.${ext}`;
|
|
53152
|
-
return
|
|
53489
|
+
return join9(input.inboxDir, filename);
|
|
53153
53490
|
}
|
|
53154
53491
|
function assertInsideInbox(inboxDir, candidatePath) {
|
|
53155
53492
|
const inboxReal = resolve(inboxDir);
|
|
@@ -53236,7 +53573,7 @@ function clear(key) {
|
|
|
53236
53573
|
}
|
|
53237
53574
|
|
|
53238
53575
|
// ../node_modules/.bun/posthog-node@5.29.2/node_modules/posthog-node/dist/extensions/error-tracking/modifiers/module.node.mjs
|
|
53239
|
-
import { dirname as
|
|
53576
|
+
import { dirname as dirname4, posix, sep as sep2 } from "path";
|
|
53240
53577
|
function createModulerModifier() {
|
|
53241
53578
|
const getModuleFromFileName = createGetModuleFromFilename();
|
|
53242
53579
|
return async (frames) => {
|
|
@@ -53245,7 +53582,7 @@ function createModulerModifier() {
|
|
|
53245
53582
|
return frames;
|
|
53246
53583
|
};
|
|
53247
53584
|
}
|
|
53248
|
-
function createGetModuleFromFilename(basePath = process.argv[1] ?
|
|
53585
|
+
function createGetModuleFromFilename(basePath = process.argv[1] ? dirname4(process.argv[1]) : process.cwd(), isWindows = sep2 === "\\") {
|
|
53249
53586
|
const normalizedBase = isWindows ? normalizeWindowsPath(basePath) : basePath;
|
|
53250
53587
|
return (filename) => {
|
|
53251
53588
|
if (!filename)
|
|
@@ -57676,8 +58013,8 @@ class PostHog extends PostHogBackendClient {
|
|
|
57676
58013
|
}
|
|
57677
58014
|
|
|
57678
58015
|
// analytics-posthog.ts
|
|
57679
|
-
import { existsSync as
|
|
57680
|
-
import { dirname as
|
|
58016
|
+
import { existsSync as existsSync5, mkdirSync as mkdirSync7, readFileSync as readFileSync9, writeFileSync as writeFileSync9 } from "node:fs";
|
|
58017
|
+
import { dirname as dirname5, join as join10 } from "node:path";
|
|
57681
58018
|
import { randomUUID } from "node:crypto";
|
|
57682
58019
|
var DEFAULT_KEY = "phc_qKY87cKWZm6ZyCtk7LcRd2cU8Sg42u7Ywhui5stYCegd";
|
|
57683
58020
|
var DEFAULT_HOST = "https://us.i.posthog.com";
|
|
@@ -57703,10 +58040,10 @@ function getDistinctId() {
|
|
|
57703
58040
|
cachedDistinctId = envId.trim();
|
|
57704
58041
|
return cachedDistinctId;
|
|
57705
58042
|
}
|
|
57706
|
-
const fallbackPath =
|
|
58043
|
+
const fallbackPath = join10(process.env.SWITCHROOM_RUNTIME_STATE_DIR ?? "/state/agent", "analytics-id");
|
|
57707
58044
|
try {
|
|
57708
|
-
if (
|
|
57709
|
-
const existing =
|
|
58045
|
+
if (existsSync5(fallbackPath)) {
|
|
58046
|
+
const existing = readFileSync9(fallbackPath, "utf-8").trim();
|
|
57710
58047
|
if (existing) {
|
|
57711
58048
|
cachedDistinctId = existing;
|
|
57712
58049
|
return existing;
|
|
@@ -57716,8 +58053,8 @@ function getDistinctId() {
|
|
|
57716
58053
|
const id = randomUUID();
|
|
57717
58054
|
cachedDistinctId = id;
|
|
57718
58055
|
try {
|
|
57719
|
-
|
|
57720
|
-
|
|
58056
|
+
mkdirSync7(dirname5(fallbackPath), { recursive: true });
|
|
58057
|
+
writeFileSync9(fallbackPath, id, "utf-8");
|
|
57721
58058
|
} catch {}
|
|
57722
58059
|
return id;
|
|
57723
58060
|
}
|
|
@@ -57788,12 +58125,12 @@ function installGlobalErrorHandlers() {
|
|
|
57788
58125
|
}
|
|
57789
58126
|
|
|
57790
58127
|
// runtime-metrics.ts
|
|
57791
|
-
import { mkdirSync as
|
|
57792
|
-
import { dirname as
|
|
58128
|
+
import { mkdirSync as mkdirSync9, appendFileSync as appendFileSync3 } from "node:fs";
|
|
58129
|
+
import { dirname as dirname7, join as join12 } from "node:path";
|
|
57793
58130
|
|
|
57794
58131
|
// analytics-posthog.ts
|
|
57795
|
-
import { existsSync as
|
|
57796
|
-
import { dirname as
|
|
58132
|
+
import { existsSync as existsSync6, mkdirSync as mkdirSync8, readFileSync as readFileSync10, writeFileSync as writeFileSync10 } from "node:fs";
|
|
58133
|
+
import { dirname as dirname6, join as join11 } from "node:path";
|
|
57797
58134
|
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
57798
58135
|
var DEFAULT_KEY2 = "phc_qKY87cKWZm6ZyCtk7LcRd2cU8Sg42u7Ywhui5stYCegd";
|
|
57799
58136
|
var DEFAULT_HOST2 = "https://us.i.posthog.com";
|
|
@@ -57818,10 +58155,10 @@ function getDistinctId2() {
|
|
|
57818
58155
|
cachedDistinctId2 = envId.trim();
|
|
57819
58156
|
return cachedDistinctId2;
|
|
57820
58157
|
}
|
|
57821
|
-
const fallbackPath =
|
|
58158
|
+
const fallbackPath = join11(process.env.SWITCHROOM_RUNTIME_STATE_DIR ?? "/state/agent", "analytics-id");
|
|
57822
58159
|
try {
|
|
57823
|
-
if (
|
|
57824
|
-
const existing =
|
|
58160
|
+
if (existsSync6(fallbackPath)) {
|
|
58161
|
+
const existing = readFileSync10(fallbackPath, "utf-8").trim();
|
|
57825
58162
|
if (existing) {
|
|
57826
58163
|
cachedDistinctId2 = existing;
|
|
57827
58164
|
return existing;
|
|
@@ -57831,8 +58168,8 @@ function getDistinctId2() {
|
|
|
57831
58168
|
const id = randomUUID2();
|
|
57832
58169
|
cachedDistinctId2 = id;
|
|
57833
58170
|
try {
|
|
57834
|
-
|
|
57835
|
-
|
|
58171
|
+
mkdirSync8(dirname6(fallbackPath), { recursive: true });
|
|
58172
|
+
writeFileSync10(fallbackPath, id, "utf-8");
|
|
57836
58173
|
} catch {}
|
|
57837
58174
|
return id;
|
|
57838
58175
|
}
|
|
@@ -57881,12 +58218,12 @@ function resolveJsonlPath() {
|
|
|
57881
58218
|
if (override && override.trim() !== "")
|
|
57882
58219
|
return override.trim();
|
|
57883
58220
|
const base = process.env.SWITCHROOM_RUNTIME_STATE_DIR ?? "/state/agent";
|
|
57884
|
-
return
|
|
58221
|
+
return join12(base, "runtime-metrics.jsonl");
|
|
57885
58222
|
}
|
|
57886
58223
|
function appendJsonl(line) {
|
|
57887
58224
|
const path2 = resolveJsonlPath();
|
|
57888
58225
|
try {
|
|
57889
|
-
|
|
58226
|
+
mkdirSync9(dirname7(path2), { recursive: true });
|
|
57890
58227
|
appendFileSync3(path2, line + `
|
|
57891
58228
|
`, "utf-8");
|
|
57892
58229
|
} catch (err) {
|
|
@@ -58101,6 +58438,13 @@ function formatFrameworkFallbackText(fallbackKind, silenceMs, inFlightTools = []
|
|
|
58101
58438
|
}
|
|
58102
58439
|
return null;
|
|
58103
58440
|
}
|
|
58441
|
+
function longestInFlightTool(key, now) {
|
|
58442
|
+
const s = state2.get(key);
|
|
58443
|
+
if (s == null)
|
|
58444
|
+
return null;
|
|
58445
|
+
const snaps = snapshotInFlight(s, now);
|
|
58446
|
+
return snaps.length > 0 ? snaps[0] : null;
|
|
58447
|
+
}
|
|
58104
58448
|
function snapshotInFlight(s, now) {
|
|
58105
58449
|
return Array.from(s.inFlightTools.values()).sort((a, b) => a.startedAt - b.startedAt).map((t) => ({ name: t.name, label: t.label, durationMs: now - t.startedAt }));
|
|
58106
58450
|
}
|
|
@@ -58401,8 +58745,8 @@ _still working (${minutes}m) \u00b7 message me anytime, I'll keep you posted_`;
|
|
|
58401
58745
|
}
|
|
58402
58746
|
|
|
58403
58747
|
// silent-end.ts
|
|
58404
|
-
import { existsSync as
|
|
58405
|
-
import { dirname as
|
|
58748
|
+
import { existsSync as existsSync8, readFileSync as readFileSync11, writeFileSync as writeFileSync11, unlinkSync as unlinkSync6, mkdirSync as mkdirSync10 } from "node:fs";
|
|
58749
|
+
import { dirname as dirname8, join as join13 } from "node:path";
|
|
58406
58750
|
import { homedir as homedir2 } from "node:os";
|
|
58407
58751
|
var SILENT_END_MAX_RETRIES = 2;
|
|
58408
58752
|
var SILENT_END_STALE_RECORD_MAX_AGE_MS = 30 * 60000;
|
|
@@ -58417,10 +58761,10 @@ function resolveStateDir(deps) {
|
|
|
58417
58761
|
if (env != null && env !== "")
|
|
58418
58762
|
return env;
|
|
58419
58763
|
const home = process.env.HOME ?? homedir2();
|
|
58420
|
-
return
|
|
58764
|
+
return join13(home, ".claude", "channels", "telegram");
|
|
58421
58765
|
}
|
|
58422
58766
|
function resolveStatePath(deps) {
|
|
58423
|
-
return
|
|
58767
|
+
return join13(resolveStateDir(deps), "silent-end-pending.json");
|
|
58424
58768
|
}
|
|
58425
58769
|
function emitLog(deps, line) {
|
|
58426
58770
|
if (deps?.log != null)
|
|
@@ -58432,8 +58776,8 @@ function writeSilentEndState(args, deps) {
|
|
|
58432
58776
|
const statePath = resolveStatePath(deps);
|
|
58433
58777
|
let retryCount = 0;
|
|
58434
58778
|
try {
|
|
58435
|
-
if (
|
|
58436
|
-
const prev = JSON.parse(
|
|
58779
|
+
if (existsSync8(statePath)) {
|
|
58780
|
+
const prev = JSON.parse(readFileSync11(statePath, "utf8"));
|
|
58437
58781
|
if (prev.turnKey === args.turnKey && typeof prev.retryCount === "number") {
|
|
58438
58782
|
retryCount = prev.retryCount;
|
|
58439
58783
|
}
|
|
@@ -58449,8 +58793,8 @@ function writeSilentEndState(args, deps) {
|
|
|
58449
58793
|
timestamp: Date.now()
|
|
58450
58794
|
};
|
|
58451
58795
|
try {
|
|
58452
|
-
|
|
58453
|
-
|
|
58796
|
+
mkdirSync10(dirname8(statePath), { recursive: true });
|
|
58797
|
+
writeFileSync11(statePath, JSON.stringify(state3), "utf8");
|
|
58454
58798
|
emitLog(deps, `silent-end: wrote state file turnKey=${args.turnKey} retryCount=${retryCount}
|
|
58455
58799
|
`);
|
|
58456
58800
|
} catch (err) {
|
|
@@ -58460,10 +58804,10 @@ function writeSilentEndState(args, deps) {
|
|
|
58460
58804
|
}
|
|
58461
58805
|
function clearSilentEndState(turnKey, deps) {
|
|
58462
58806
|
const statePath = resolveStatePath(deps);
|
|
58463
|
-
if (!
|
|
58807
|
+
if (!existsSync8(statePath))
|
|
58464
58808
|
return;
|
|
58465
58809
|
try {
|
|
58466
|
-
const prev = JSON.parse(
|
|
58810
|
+
const prev = JSON.parse(readFileSync11(statePath, "utf8"));
|
|
58467
58811
|
if (prev.turnKey != null && prev.turnKey !== turnKey)
|
|
58468
58812
|
return;
|
|
58469
58813
|
unlinkSync6(statePath);
|
|
@@ -58473,10 +58817,10 @@ function clearSilentEndState(turnKey, deps) {
|
|
|
58473
58817
|
}
|
|
58474
58818
|
function readSilentEndState(deps) {
|
|
58475
58819
|
const statePath = resolveStatePath(deps);
|
|
58476
|
-
if (!
|
|
58820
|
+
if (!existsSync8(statePath))
|
|
58477
58821
|
return null;
|
|
58478
58822
|
try {
|
|
58479
|
-
return JSON.parse(
|
|
58823
|
+
return JSON.parse(readFileSync11(statePath, "utf8"));
|
|
58480
58824
|
} catch {
|
|
58481
58825
|
return null;
|
|
58482
58826
|
}
|
|
@@ -58937,18 +59281,18 @@ async function gatewayStartupRetry(fn, opts = {}) {
|
|
|
58937
59281
|
}
|
|
58938
59282
|
|
|
58939
59283
|
// gateway/quarantine.ts
|
|
58940
|
-
import { mkdirSync as
|
|
58941
|
-
import { join as
|
|
59284
|
+
import { mkdirSync as mkdirSync11, writeFileSync as writeFileSync12 } from "node:fs";
|
|
59285
|
+
import { join as join14 } from "node:path";
|
|
58942
59286
|
var QUARANTINE_FILENAME = "quarantine.json";
|
|
58943
59287
|
function writeQuarantineMarker(telegramStateDir, reason, detail, nowFn = Date.now) {
|
|
58944
|
-
|
|
59288
|
+
mkdirSync11(telegramStateDir, { recursive: true, mode: 448 });
|
|
58945
59289
|
const marker = {
|
|
58946
59290
|
v: 1,
|
|
58947
59291
|
reason,
|
|
58948
59292
|
ts: nowFn(),
|
|
58949
59293
|
detail
|
|
58950
59294
|
};
|
|
58951
|
-
|
|
59295
|
+
writeFileSync12(join14(telegramStateDir, QUARANTINE_FILENAME), JSON.stringify(marker) + `
|
|
58952
59296
|
`, "utf-8");
|
|
58953
59297
|
}
|
|
58954
59298
|
|
|
@@ -59928,7 +60272,7 @@ init_protocol();
|
|
|
59928
60272
|
import * as net3 from "node:net";
|
|
59929
60273
|
import { homedir as homedir6 } from "node:os";
|
|
59930
60274
|
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
59931
|
-
import { join as
|
|
60275
|
+
import { join as join18 } from "node:path";
|
|
59932
60276
|
var DEFAULT_TIMEOUT_MS3 = 5000;
|
|
59933
60277
|
function reviveDate2(v) {
|
|
59934
60278
|
if (v == null)
|
|
@@ -59939,7 +60283,7 @@ function reviveDate2(v) {
|
|
|
59939
60283
|
return Number.isNaN(d.getTime()) ? null : d;
|
|
59940
60284
|
}
|
|
59941
60285
|
function operatorSocketPath2(home2 = homedir6()) {
|
|
59942
|
-
return
|
|
60286
|
+
return join18(home2, ".switchroom", "state", "auth-broker-operator", "sock");
|
|
59943
60287
|
}
|
|
59944
60288
|
function resolveAuthBrokerSocketPath2(opts) {
|
|
59945
60289
|
if (opts?.socket)
|
|
@@ -60244,13 +60588,13 @@ class AuthBrokerClient2 {
|
|
|
60244
60588
|
init_loader();
|
|
60245
60589
|
init_resolver();
|
|
60246
60590
|
init_vault();
|
|
60247
|
-
import { existsSync as
|
|
60591
|
+
import { existsSync as existsSync14 } from "node:fs";
|
|
60248
60592
|
var DEFAULT_VOICE_API_KEY_REF = "vault:openai/api-key";
|
|
60249
60593
|
function tryDirectVaultRead(ref, config, passphrase) {
|
|
60250
60594
|
if (!passphrase)
|
|
60251
60595
|
return null;
|
|
60252
60596
|
const vaultPath = resolvePath(config.vault?.path ?? "~/.switchroom/vault.enc");
|
|
60253
|
-
if (!
|
|
60597
|
+
if (!existsSync14(vaultPath))
|
|
60254
60598
|
return null;
|
|
60255
60599
|
try {
|
|
60256
60600
|
const secrets = openVault(passphrase, vaultPath);
|
|
@@ -60306,14 +60650,14 @@ async function materializeVoiceKey(opts = {}, logger2 = (line) => process.stderr
|
|
|
60306
60650
|
init_loader();
|
|
60307
60651
|
init_resolver();
|
|
60308
60652
|
init_vault();
|
|
60309
|
-
import { existsSync as
|
|
60653
|
+
import { existsSync as existsSync15 } from "node:fs";
|
|
60310
60654
|
var VOICE_SIDECAR_TOKEN_KEY = "voice/sidecar-token";
|
|
60311
60655
|
var DEFAULT_VOICE_SIDECAR_TOKEN_REF = `vault:${VOICE_SIDECAR_TOKEN_KEY}`;
|
|
60312
60656
|
function tryDirectVaultRead2(ref, config, passphrase) {
|
|
60313
60657
|
if (!passphrase)
|
|
60314
60658
|
return null;
|
|
60315
60659
|
const vaultPath = resolvePath(config.vault?.path ?? "~/.switchroom/vault.enc");
|
|
60316
|
-
if (!
|
|
60660
|
+
if (!existsSync15(vaultPath))
|
|
60317
60661
|
return null;
|
|
60318
60662
|
try {
|
|
60319
60663
|
const secrets = openVault(passphrase, vaultPath);
|
|
@@ -60366,17 +60710,17 @@ async function materializeSidecarToken(opts = {}, logger2 = (line) => process.st
|
|
|
60366
60710
|
}
|
|
60367
60711
|
|
|
60368
60712
|
// ../src/setup/host-capabilities.ts
|
|
60369
|
-
import { existsSync as
|
|
60713
|
+
import { existsSync as existsSync16, mkdirSync as mkdirSync14, readFileSync as readFileSync16, writeFileSync as writeFileSync13 } from "node:fs";
|
|
60370
60714
|
init_paths();
|
|
60371
60715
|
function hostCapabilitiesPath() {
|
|
60372
60716
|
return resolveStatePath2("host-capabilities.json");
|
|
60373
60717
|
}
|
|
60374
60718
|
function loadHostCapabilities() {
|
|
60375
60719
|
const path2 = hostCapabilitiesPath();
|
|
60376
|
-
if (!
|
|
60720
|
+
if (!existsSync16(path2))
|
|
60377
60721
|
return null;
|
|
60378
60722
|
try {
|
|
60379
|
-
const parsed = JSON.parse(
|
|
60723
|
+
const parsed = JSON.parse(readFileSync16(path2, "utf-8"));
|
|
60380
60724
|
if (parsed && typeof parsed === "object" && "voice" in parsed && typeof parsed.voice === "object") {
|
|
60381
60725
|
return parsed;
|
|
60382
60726
|
}
|
|
@@ -60474,18 +60818,18 @@ function resolveExhaustUntil(resetAtMs, now = Date.now()) {
|
|
|
60474
60818
|
|
|
60475
60819
|
// gateway/auth-add-flow.ts
|
|
60476
60820
|
import { execFileSync } from "node:child_process";
|
|
60477
|
-
import { existsSync as
|
|
60821
|
+
import { existsSync as existsSync18, mkdirSync as mkdirSync16, readFileSync as readFileSync18, readdirSync as readdirSync4, rmSync as rmSync3, statSync as statSync7, writeFileSync as writeFileSync15 } from "node:fs";
|
|
60478
60822
|
import { homedir as homedir7 } from "node:os";
|
|
60479
|
-
import { join as
|
|
60823
|
+
import { join as join19 } from "node:path";
|
|
60480
60824
|
import { randomBytes as randomBytes4 } from "node:crypto";
|
|
60481
60825
|
|
|
60482
60826
|
// ../src/auth/manager.ts
|
|
60483
60827
|
import {
|
|
60484
|
-
readFileSync as
|
|
60828
|
+
readFileSync as readFileSync17,
|
|
60485
60829
|
readdirSync as readdirSync3,
|
|
60486
|
-
existsSync as
|
|
60487
|
-
writeFileSync as
|
|
60488
|
-
mkdirSync as
|
|
60830
|
+
existsSync as existsSync17,
|
|
60831
|
+
writeFileSync as writeFileSync14,
|
|
60832
|
+
mkdirSync as mkdirSync15,
|
|
60489
60833
|
mkdtempSync as mkdtempSync2,
|
|
60490
60834
|
rmSync as rmSync2,
|
|
60491
60835
|
chmodSync as chmodSync3,
|
|
@@ -60510,9 +60854,9 @@ function parseSetupTokenUrl(output) {
|
|
|
60510
60854
|
}
|
|
60511
60855
|
function readTokenFromCredentialsFile(credentialsFilePath) {
|
|
60512
60856
|
try {
|
|
60513
|
-
if (!
|
|
60857
|
+
if (!existsSync17(credentialsFilePath))
|
|
60514
60858
|
return null;
|
|
60515
|
-
const raw =
|
|
60859
|
+
const raw = readFileSync17(credentialsFilePath, "utf-8");
|
|
60516
60860
|
const parsed = JSON.parse(raw);
|
|
60517
60861
|
const token = parsed?.claudeAiOauth?.accessToken;
|
|
60518
60862
|
if (typeof token !== "string")
|
|
@@ -60572,7 +60916,7 @@ function makeAuthAddTmuxOps(tmuxBin = "tmux") {
|
|
|
60572
60916
|
var pendingAuthAddFlows = new Map;
|
|
60573
60917
|
function pickScratchDir(label, home2 = homedir7()) {
|
|
60574
60918
|
const suffix = randomBytes4(8).toString("hex");
|
|
60575
|
-
return
|
|
60919
|
+
return join19(home2, ".switchroom", "accounts", ".in-progress", `${label}-${suffix}`);
|
|
60576
60920
|
}
|
|
60577
60921
|
function cleanScratchDir(scratchDir) {
|
|
60578
60922
|
try {
|
|
@@ -60581,8 +60925,8 @@ function cleanScratchDir(scratchDir) {
|
|
|
60581
60925
|
}
|
|
60582
60926
|
var AUTH_TMUX_SESSION_FILE = ".auth-tmux-session";
|
|
60583
60927
|
function sweepOrphanSessions(home2, tmux, nowMs2 = Date.now()) {
|
|
60584
|
-
const inProgressDir =
|
|
60585
|
-
if (!
|
|
60928
|
+
const inProgressDir = join19(home2, ".switchroom", "accounts", ".in-progress");
|
|
60929
|
+
if (!existsSync18(inProgressDir))
|
|
60586
60930
|
return;
|
|
60587
60931
|
let entries;
|
|
60588
60932
|
try {
|
|
@@ -60592,16 +60936,16 @@ function sweepOrphanSessions(home2, tmux, nowMs2 = Date.now()) {
|
|
|
60592
60936
|
}
|
|
60593
60937
|
const tenMinMs = 10 * 60000;
|
|
60594
60938
|
for (const entry of entries) {
|
|
60595
|
-
const dir =
|
|
60596
|
-
const sessionFile =
|
|
60597
|
-
if (!
|
|
60939
|
+
const dir = join19(inProgressDir, entry);
|
|
60940
|
+
const sessionFile = join19(dir, AUTH_TMUX_SESSION_FILE);
|
|
60941
|
+
if (!existsSync18(sessionFile))
|
|
60598
60942
|
continue;
|
|
60599
60943
|
let fileContents;
|
|
60600
60944
|
let fileMtime;
|
|
60601
60945
|
try {
|
|
60602
60946
|
const stat = statSync7(sessionFile);
|
|
60603
60947
|
fileMtime = stat.mtimeMs;
|
|
60604
|
-
fileContents =
|
|
60948
|
+
fileContents = readFileSync18(sessionFile, "utf8").trim();
|
|
60605
60949
|
} catch {
|
|
60606
60950
|
continue;
|
|
60607
60951
|
}
|
|
@@ -60627,13 +60971,13 @@ async function startAccountAuthSession(label, opts = {}) {
|
|
|
60627
60971
|
const tmux = opts.tmuxOps ?? makeAuthAddTmuxOps(opts.tmuxBin);
|
|
60628
60972
|
const binary = opts.claudeBinary ?? "claude";
|
|
60629
60973
|
const scratchDir = pickScratchDir(label, home2);
|
|
60630
|
-
|
|
60974
|
+
mkdirSync16(scratchDir, { recursive: true, mode: 448 });
|
|
60631
60975
|
sweepOrphanSessions(home2, tmux);
|
|
60632
60976
|
const hexSuffix = scratchDir.slice(scratchDir.lastIndexOf("-") + 1);
|
|
60633
60977
|
const tmuxSocket = `switchroom-${agentName3}`;
|
|
60634
60978
|
const tmuxSession = `auth-add-${label}-${hexSuffix}`.slice(0, 64);
|
|
60635
60979
|
try {
|
|
60636
|
-
|
|
60980
|
+
writeFileSync15(join19(scratchDir, AUTH_TMUX_SESSION_FILE), `${tmuxSocket}
|
|
60637
60981
|
${tmuxSession}`, "utf8");
|
|
60638
60982
|
} catch {}
|
|
60639
60983
|
const sessionEnv = {
|
|
@@ -60682,7 +61026,7 @@ async function submitAccountAuthCode(flow3, code2, opts = {}) {
|
|
|
60682
61026
|
const pollIntervalMs = opts.pollIntervalMs ?? 250;
|
|
60683
61027
|
const pollTimeoutMs = opts.pollTimeoutMs ?? 300000;
|
|
60684
61028
|
const tmux = opts.tmuxOps ?? makeAuthAddTmuxOps();
|
|
60685
|
-
const credentialsPath =
|
|
61029
|
+
const credentialsPath = join19(flow3.scratchDir, ".credentials.json");
|
|
60686
61030
|
try {
|
|
60687
61031
|
tmux.send(flow3.tmuxSocket, flow3.tmuxSession, code2);
|
|
60688
61032
|
} catch (err) {
|
|
@@ -60692,11 +61036,11 @@ async function submitAccountAuthCode(flow3, code2, opts = {}) {
|
|
|
60692
61036
|
const deadline = Date.now() + pollTimeoutMs;
|
|
60693
61037
|
while (Date.now() < deadline) {
|
|
60694
61038
|
await new Promise((r) => setTimeout(r, pollIntervalMs));
|
|
60695
|
-
if (
|
|
61039
|
+
if (existsSync18(credentialsPath)) {
|
|
60696
61040
|
const token = readTokenFromCredentialsFile(credentialsPath);
|
|
60697
61041
|
if (token) {
|
|
60698
61042
|
try {
|
|
60699
|
-
const raw =
|
|
61043
|
+
const raw = readFileSync18(credentialsPath, "utf-8");
|
|
60700
61044
|
const parsed = JSON.parse(raw);
|
|
60701
61045
|
if (parsed.claudeAiOauth?.accessToken) {
|
|
60702
61046
|
tmux.killSession(flow3.tmuxSocket, flow3.tmuxSession);
|
|
@@ -60706,7 +61050,7 @@ async function submitAccountAuthCode(flow3, code2, opts = {}) {
|
|
|
60706
61050
|
}
|
|
60707
61051
|
}
|
|
60708
61052
|
if (!tmux.hasSession(flow3.tmuxSocket, flow3.tmuxSession)) {
|
|
60709
|
-
if (!
|
|
61053
|
+
if (!existsSync18(credentialsPath)) {
|
|
60710
61054
|
cleanScratchDir(flow3.scratchDir);
|
|
60711
61055
|
throw new Error("claude setup-token exited without writing credentials \u2014 the code may be invalid or expired");
|
|
60712
61056
|
}
|
|
@@ -61060,6 +61404,20 @@ function detectModelUnavailable(stderr) {
|
|
|
61060
61404
|
return null;
|
|
61061
61405
|
const sample = stderr.length > 16384 ? stderr.slice(0, 16384) : stderr;
|
|
61062
61406
|
const lower = sample.toLowerCase();
|
|
61407
|
+
const transientUpstreamSignals = [
|
|
61408
|
+
"not your usage limit",
|
|
61409
|
+
"not your account",
|
|
61410
|
+
"not your account's",
|
|
61411
|
+
"temporarily limiting requests",
|
|
61412
|
+
"temporarily rate",
|
|
61413
|
+
"server is temporarily",
|
|
61414
|
+
"would exceed your account\u2019s rate limit",
|
|
61415
|
+
"would exceed your account's rate limit"
|
|
61416
|
+
];
|
|
61417
|
+
if (transientUpstreamSignals.some((s) => lower.includes(s))) {
|
|
61418
|
+
const resetAt = parseResetTime(sample);
|
|
61419
|
+
return resetAt !== undefined ? { kind: "overload", resetAt, raw: stderr } : { kind: "overload", raw: stderr };
|
|
61420
|
+
}
|
|
61063
61421
|
const quotaSignals = [
|
|
61064
61422
|
"out of extra usage",
|
|
61065
61423
|
"extra usage",
|
|
@@ -61599,7 +61957,7 @@ function hardenCardBreaks2(text4) {
|
|
|
61599
61957
|
}
|
|
61600
61958
|
return restore2(pieces.join(""));
|
|
61601
61959
|
}
|
|
61602
|
-
var
|
|
61960
|
+
var PARAGRAPH_SPACER2 = "\u00a0";
|
|
61603
61961
|
function addParagraphSpacers(text4) {
|
|
61604
61962
|
if (!text4.includes(`
|
|
61605
61963
|
|
|
@@ -61611,7 +61969,7 @@ function addParagraphSpacers(text4) {
|
|
|
61611
61969
|
|
|
61612
61970
|
`))
|
|
61613
61971
|
return restore2(masked);
|
|
61614
|
-
const spacerLine =
|
|
61972
|
+
const spacerLine = PARAGRAPH_SPACER2;
|
|
61615
61973
|
const isBlankLine = (line) => /^[ \t\r\f\v]*$/.test(line);
|
|
61616
61974
|
const asciiTrim = (line) => line.replace(/^[ \t\r\f\v]+|[ \t\r\f\v]+$/g, "");
|
|
61617
61975
|
const blockKind = (line) => {
|
|
@@ -61848,7 +62206,7 @@ function shouldPromoteBreak(prev, next, placeholder) {
|
|
|
61848
62206
|
}
|
|
61849
62207
|
return false;
|
|
61850
62208
|
}
|
|
61851
|
-
function
|
|
62209
|
+
function hardSliceToCap2(text4, cap = RICH_MESSAGE_MAX_CHARS2) {
|
|
61852
62210
|
if (cap <= 0)
|
|
61853
62211
|
return [text4];
|
|
61854
62212
|
if (text4.length <= cap)
|
|
@@ -61859,7 +62217,7 @@ function hardSliceToCap(text4, cap = RICH_MESSAGE_MAX_CHARS2) {
|
|
|
61859
62217
|
}
|
|
61860
62218
|
return out;
|
|
61861
62219
|
}
|
|
61862
|
-
function
|
|
62220
|
+
function splitMarkdownChunks2(text4, maxLen = RICH_MESSAGE_MAX_CHARS2) {
|
|
61863
62221
|
if (text4.length <= maxLen)
|
|
61864
62222
|
return [text4];
|
|
61865
62223
|
const chunks = [];
|
|
@@ -61883,27 +62241,27 @@ function splitMarkdownChunks(text4, maxLen = RICH_MESSAGE_MAX_CHARS2) {
|
|
|
61883
62241
|
} else if (spaceIdx > 0) {
|
|
61884
62242
|
cut = spaceIdx;
|
|
61885
62243
|
}
|
|
61886
|
-
cut =
|
|
61887
|
-
cut =
|
|
62244
|
+
cut = backOffOpenFence2(rest, cut);
|
|
62245
|
+
cut = backOffTableRow2(rest, cut);
|
|
61888
62246
|
if (cut <= 0) {
|
|
61889
|
-
const sliced =
|
|
61890
|
-
chunks.push(
|
|
61891
|
-
rest =
|
|
62247
|
+
const sliced = hardSliceToCap2(rest, maxLen);
|
|
62248
|
+
chunks.push(stripBoundarySpacers2(sliced[0], "trailing"));
|
|
62249
|
+
rest = stripBoundarySpacers2(sliced.slice(1).join(""), "leading");
|
|
61892
62250
|
continue;
|
|
61893
62251
|
}
|
|
61894
|
-
chunks.push(
|
|
61895
|
-
rest =
|
|
62252
|
+
chunks.push(stripBoundarySpacers2(rest.slice(0, cut), "trailing"));
|
|
62253
|
+
rest = stripBoundarySpacers2(rest.slice(cut), "leading");
|
|
61896
62254
|
}
|
|
61897
|
-
return chunks.map((c) =>
|
|
62255
|
+
return chunks.map((c) => stripBoundarySpacers2(c, "trailing"));
|
|
61898
62256
|
}
|
|
61899
|
-
function
|
|
61900
|
-
const sp =
|
|
62257
|
+
function stripBoundarySpacers2(chunk2, side) {
|
|
62258
|
+
const sp = PARAGRAPH_SPACER2;
|
|
61901
62259
|
if (side === "leading") {
|
|
61902
62260
|
return chunk2.replace(new RegExp(`^(?:[ \\t]*${sp}?[ \\t]*\\n+)+`), "");
|
|
61903
62261
|
}
|
|
61904
62262
|
return chunk2.replace(new RegExp(`(?:\\n+[ \\t]*${sp}?[ \\t]*)+$`), "");
|
|
61905
62263
|
}
|
|
61906
|
-
function
|
|
62264
|
+
function backOffOpenFence2(text4, cut) {
|
|
61907
62265
|
if (cut <= 0 || cut >= text4.length)
|
|
61908
62266
|
return cut;
|
|
61909
62267
|
const before = text4.slice(0, cut);
|
|
@@ -61916,7 +62274,7 @@ function backOffOpenFence(text4, cut) {
|
|
|
61916
62274
|
}
|
|
61917
62275
|
return lastFence;
|
|
61918
62276
|
}
|
|
61919
|
-
function
|
|
62277
|
+
function backOffTableRow2(text4, cut) {
|
|
61920
62278
|
if (cut <= 0 || cut >= text4.length)
|
|
61921
62279
|
return cut;
|
|
61922
62280
|
const lineStart2 = text4.lastIndexOf(`
|
|
@@ -62588,28 +62946,28 @@ function isTurnFlushSafetyEnabled(env = process.env) {
|
|
|
62588
62946
|
}
|
|
62589
62947
|
|
|
62590
62948
|
// agent-dir.ts
|
|
62591
|
-
import { dirname as
|
|
62949
|
+
import { dirname as dirname9 } from "node:path";
|
|
62592
62950
|
function resolveAgentDirFromEnv() {
|
|
62593
62951
|
const state3 = process.env.TELEGRAM_STATE_DIR;
|
|
62594
62952
|
if (!state3 || state3.trim().length === 0)
|
|
62595
62953
|
return null;
|
|
62596
|
-
return
|
|
62954
|
+
return dirname9(state3);
|
|
62597
62955
|
}
|
|
62598
62956
|
|
|
62599
62957
|
// active-reactions.ts
|
|
62600
|
-
import { readFileSync as
|
|
62601
|
-
import { join as
|
|
62958
|
+
import { readFileSync as readFileSync20, writeFileSync as writeFileSync16, renameSync as renameSync5, existsSync as existsSync21, unlinkSync as unlinkSync8 } from "node:fs";
|
|
62959
|
+
import { join as join22 } from "node:path";
|
|
62602
62960
|
var ACTIVE_REACTIONS_FILENAME = ".active-reactions.json";
|
|
62603
62961
|
function reactionsPath(agentDir) {
|
|
62604
|
-
return
|
|
62962
|
+
return join22(agentDir, ACTIVE_REACTIONS_FILENAME);
|
|
62605
62963
|
}
|
|
62606
62964
|
function readActiveReactions(agentDir) {
|
|
62607
62965
|
const p = reactionsPath(agentDir);
|
|
62608
|
-
if (!
|
|
62966
|
+
if (!existsSync21(p))
|
|
62609
62967
|
return [];
|
|
62610
62968
|
let raw;
|
|
62611
62969
|
try {
|
|
62612
|
-
raw =
|
|
62970
|
+
raw = readFileSync20(p, "utf-8");
|
|
62613
62971
|
} catch {
|
|
62614
62972
|
return [];
|
|
62615
62973
|
}
|
|
@@ -62641,7 +62999,7 @@ function writeActiveReactions(agentDir, reactions) {
|
|
|
62641
62999
|
}
|
|
62642
63000
|
const tmp = `${p}.tmp-${process.pid}-${Date.now()}`;
|
|
62643
63001
|
try {
|
|
62644
|
-
|
|
63002
|
+
writeFileSync16(tmp, JSON.stringify(reactions) + `
|
|
62645
63003
|
`, "utf-8");
|
|
62646
63004
|
renameSync5(tmp, p);
|
|
62647
63005
|
} catch {}
|
|
@@ -62665,19 +63023,19 @@ function clearActiveReactions(agentDir) {
|
|
|
62665
63023
|
}
|
|
62666
63024
|
|
|
62667
63025
|
// active-reactions.ts
|
|
62668
|
-
import { readFileSync as
|
|
62669
|
-
import { join as
|
|
63026
|
+
import { readFileSync as readFileSync21, writeFileSync as writeFileSync17, renameSync as renameSync6, existsSync as existsSync22, unlinkSync as unlinkSync9 } from "node:fs";
|
|
63027
|
+
import { join as join23 } from "node:path";
|
|
62670
63028
|
var ACTIVE_REACTIONS_FILENAME2 = ".active-reactions.json";
|
|
62671
63029
|
function reactionsPath2(agentDir) {
|
|
62672
|
-
return
|
|
63030
|
+
return join23(agentDir, ACTIVE_REACTIONS_FILENAME2);
|
|
62673
63031
|
}
|
|
62674
63032
|
function readActiveReactions2(agentDir) {
|
|
62675
63033
|
const p = reactionsPath2(agentDir);
|
|
62676
|
-
if (!
|
|
63034
|
+
if (!existsSync22(p))
|
|
62677
63035
|
return [];
|
|
62678
63036
|
let raw;
|
|
62679
63037
|
try {
|
|
62680
|
-
raw =
|
|
63038
|
+
raw = readFileSync21(p, "utf-8");
|
|
62681
63039
|
} catch {
|
|
62682
63040
|
return [];
|
|
62683
63041
|
}
|
|
@@ -63584,17 +63942,17 @@ async function approvalRecord(args, opts) {
|
|
|
63584
63942
|
}
|
|
63585
63943
|
|
|
63586
63944
|
// quota-check.ts
|
|
63587
|
-
import { readFileSync as
|
|
63588
|
-
import { join as
|
|
63945
|
+
import { readFileSync as readFileSync22, existsSync as existsSync23 } from "fs";
|
|
63946
|
+
import { join as join24 } from "path";
|
|
63589
63947
|
var OAUTH_BETA2 = "oauth-2025-04-20";
|
|
63590
63948
|
var DEFAULT_USER_AGENT2 = "claude-cli/1.0.0 (external, cli)";
|
|
63591
63949
|
var DEFAULT_PROBE_MODEL2 = "claude-haiku-4-5-20251001";
|
|
63592
63950
|
function readOauthToken2(claudeConfigDir) {
|
|
63593
|
-
const tokenFile =
|
|
63594
|
-
if (!
|
|
63951
|
+
const tokenFile = join24(claudeConfigDir, ".oauth-token");
|
|
63952
|
+
if (!existsSync23(tokenFile))
|
|
63595
63953
|
return null;
|
|
63596
63954
|
try {
|
|
63597
|
-
const raw =
|
|
63955
|
+
const raw = readFileSync22(tokenFile, "utf-8").trim();
|
|
63598
63956
|
return raw.length > 0 ? raw : null;
|
|
63599
63957
|
} catch {
|
|
63600
63958
|
return null;
|
|
@@ -64329,7 +64687,7 @@ function parseModelCommand(text4) {
|
|
|
64329
64687
|
}
|
|
64330
64688
|
return { kind: "set", model: arg };
|
|
64331
64689
|
}
|
|
64332
|
-
var PERSIST_NOTE = "
|
|
64690
|
+
var PERSIST_NOTE = "_Sticky across switchroom-managed relaunches (`/new`, watchdog recovery); reverts on `/restart`, agent restart, crash, or external container restart. `/model default` clears it. To persist, set `model:` in switchroom.yaml._";
|
|
64333
64691
|
function helpText2(deps, reason) {
|
|
64334
64692
|
const srAliasExamples = Object.keys(SR_MODEL_ALIASES).map((a) => `\`${a}\``).join(" \u00b7 ");
|
|
64335
64693
|
const lines = [];
|
|
@@ -64414,7 +64772,7 @@ async function handleModelCommand(parsed, deps) {
|
|
|
64414
64772
|
return {
|
|
64415
64773
|
text: [
|
|
64416
64774
|
`Switching to \`${deps.escapeHtml(model)}\` \u2014 restarting session (~30s).`,
|
|
64417
|
-
|
|
64775
|
+
PERSIST_NOTE
|
|
64418
64776
|
].join(`
|
|
64419
64777
|
`),
|
|
64420
64778
|
html: true
|
|
@@ -64714,7 +65072,8 @@ async function handleModelMenuCallback(data, deps) {
|
|
|
64714
65072
|
}
|
|
64715
65073
|
return {
|
|
64716
65074
|
answer: `Switching to ${friendlyName} \u2014 restarting (~30s)`,
|
|
64717
|
-
reply: await menuWithBannerStatic(deps, `\uD83D\uDD04 Switching session to **${deps.escapeHtml(friendlyName)}** \u2014 restarting (~30s).
|
|
65075
|
+
reply: await menuWithBannerStatic(deps, `\uD83D\uDD04 Switching session to **${deps.escapeHtml(friendlyName)}** \u2014 restarting (~30s).
|
|
65076
|
+
${PERSIST_NOTE}`),
|
|
64718
65077
|
selectedModel: srName
|
|
64719
65078
|
};
|
|
64720
65079
|
}
|
|
@@ -64756,11 +65115,13 @@ async function handleModelMenuCallback(data, deps) {
|
|
|
64756
65115
|
}
|
|
64757
65116
|
const token = canonicalClaudeToken(target.label);
|
|
64758
65117
|
const selectedModel = sessionModelFromConfirmation(result.confirmation) ?? token ?? undefined;
|
|
65118
|
+
const clearedDefault = token == null && /^default\b/i.test(target.label.trim());
|
|
64759
65119
|
return {
|
|
64760
65120
|
answer: deps.escapeHtml(result.confirmation),
|
|
64761
65121
|
reply: await menuWithBanner(deps, `\u2705 ${deps.escapeHtml(result.confirmation)}`),
|
|
64762
65122
|
...selectedModel ? { selectedModel } : {},
|
|
64763
|
-
...token ? { selectedModelToken: token } : {}
|
|
65123
|
+
...token ? { selectedModelToken: token } : {},
|
|
65124
|
+
...clearedDefault ? { clearedDefault: true } : {}
|
|
64764
65125
|
};
|
|
64765
65126
|
}
|
|
64766
65127
|
function isSrToClaudeTransition(prevModel, nextModel) {
|
|
@@ -64818,6 +65179,87 @@ async function menuWithBannerStatic(deps, banner) {
|
|
|
64818
65179
|
};
|
|
64819
65180
|
}
|
|
64820
65181
|
|
|
65182
|
+
// gateway/session-model-file.ts
|
|
65183
|
+
import { readFileSync as readFileSync23, writeFileSync as writeFileSync18, renameSync as renameSync7, rmSync as rmSync4 } from "node:fs";
|
|
65184
|
+
import { join as join25 } from "node:path";
|
|
65185
|
+
|
|
65186
|
+
// gateway/model-command.ts
|
|
65187
|
+
var MODEL_ARG_RE2 = /^[A-Za-z0-9][A-Za-z0-9._/\[\]-]{0,99}$/;
|
|
65188
|
+
function isValidModelArg2(arg) {
|
|
65189
|
+
return MODEL_ARG_RE2.test(arg);
|
|
65190
|
+
}
|
|
65191
|
+
|
|
65192
|
+
// gateway/session-model-file.ts
|
|
65193
|
+
var SESSION_MODEL_FILE = ".session-model";
|
|
65194
|
+
var RELAUNCH_MODEL_INTENT_FILE = ".relaunch-model-intent";
|
|
65195
|
+
var CONFIGURED_DEFAULT_MODEL_FILE = ".configured-default-model";
|
|
65196
|
+
var REVERT_RESTART_REASONS = new Set(["inline-button-restart"]);
|
|
65197
|
+
function intentForRestartReason(reason) {
|
|
65198
|
+
return REVERT_RESTART_REASONS.has(reason) ? "revert" : "keep";
|
|
65199
|
+
}
|
|
65200
|
+
function atomicWrite(path2, content3) {
|
|
65201
|
+
const tmp = `${path2}.tmp-${process.pid}-${Date.now()}`;
|
|
65202
|
+
writeFileSync18(tmp, content3, "utf8");
|
|
65203
|
+
renameSync7(tmp, path2);
|
|
65204
|
+
}
|
|
65205
|
+
function serializeSessionModel(rec) {
|
|
65206
|
+
return `${JSON.stringify({
|
|
65207
|
+
model: rec.model,
|
|
65208
|
+
configuredDefaultAtWrite: rec.configuredDefaultAtWrite,
|
|
65209
|
+
ts: rec.ts
|
|
65210
|
+
})}
|
|
65211
|
+
`;
|
|
65212
|
+
}
|
|
65213
|
+
function writeSessionModelFile(agentDir, model, configuredDefaultAtWrite) {
|
|
65214
|
+
if (!isValidModelArg2(model)) {
|
|
65215
|
+
throw new Error(`refusing to persist non-canonical session model token: ${JSON.stringify(model)}`);
|
|
65216
|
+
}
|
|
65217
|
+
atomicWrite(join25(agentDir, SESSION_MODEL_FILE), serializeSessionModel({ model, configuredDefaultAtWrite, ts: Date.now() }));
|
|
65218
|
+
}
|
|
65219
|
+
function readSessionModelFileRaw(agentDir) {
|
|
65220
|
+
try {
|
|
65221
|
+
return readFileSync23(join25(agentDir, SESSION_MODEL_FILE), "utf8");
|
|
65222
|
+
} catch {
|
|
65223
|
+
return null;
|
|
65224
|
+
}
|
|
65225
|
+
}
|
|
65226
|
+
function clearSessionModelFile(agentDir) {
|
|
65227
|
+
try {
|
|
65228
|
+
rmSync4(join25(agentDir, SESSION_MODEL_FILE), { force: true });
|
|
65229
|
+
} catch {}
|
|
65230
|
+
}
|
|
65231
|
+
function restoreSessionModelFileRaw(agentDir, raw) {
|
|
65232
|
+
if (raw == null) {
|
|
65233
|
+
clearSessionModelFile(agentDir);
|
|
65234
|
+
return;
|
|
65235
|
+
}
|
|
65236
|
+
try {
|
|
65237
|
+
atomicWrite(join25(agentDir, SESSION_MODEL_FILE), raw);
|
|
65238
|
+
} catch {}
|
|
65239
|
+
}
|
|
65240
|
+
function writeRelaunchModelIntent(agentDir, intent, reason) {
|
|
65241
|
+
try {
|
|
65242
|
+
atomicWrite(join25(agentDir, RELAUNCH_MODEL_INTENT_FILE), `${JSON.stringify({ intent, reason, ts: Date.now() })}
|
|
65243
|
+
`);
|
|
65244
|
+
} catch (err) {
|
|
65245
|
+
process.stderr.write(`telegram gateway: relaunch-model-intent write failed (boot will revert): ${err?.message ?? String(err)}
|
|
65246
|
+
`);
|
|
65247
|
+
}
|
|
65248
|
+
}
|
|
65249
|
+
function clearRelaunchModelIntent(agentDir) {
|
|
65250
|
+
try {
|
|
65251
|
+
rmSync4(join25(agentDir, RELAUNCH_MODEL_INTENT_FILE), { force: true });
|
|
65252
|
+
} catch {}
|
|
65253
|
+
}
|
|
65254
|
+
function readConfiguredDefaultModel(agentDir) {
|
|
65255
|
+
try {
|
|
65256
|
+
const v = readFileSync23(join25(agentDir, CONFIGURED_DEFAULT_MODEL_FILE), "utf8").trim();
|
|
65257
|
+
return v.length > 0 ? v : null;
|
|
65258
|
+
} catch {
|
|
65259
|
+
return null;
|
|
65260
|
+
}
|
|
65261
|
+
}
|
|
65262
|
+
|
|
64821
65263
|
// ../src/agents/model-picker.ts
|
|
64822
65264
|
var HEADER_RE = /Select model/;
|
|
64823
65265
|
var OPTION_RE = /^\s*(\u276f)?\s*(\d+)\.\s+(.*)$/;
|
|
@@ -65029,7 +65471,7 @@ function extractConfirmation(pane) {
|
|
|
65029
65471
|
}
|
|
65030
65472
|
|
|
65031
65473
|
// ../src/agents/scaffold.ts
|
|
65032
|
-
import { join as
|
|
65474
|
+
import { join as join28, resolve as resolve6 } from "node:path";
|
|
65033
65475
|
init_schema();
|
|
65034
65476
|
|
|
65035
65477
|
// ../src/config/users.ts
|
|
@@ -65047,7 +65489,7 @@ var CONTAINER_DEFAULT_UTC_ZONES = new Set([
|
|
|
65047
65489
|
]);
|
|
65048
65490
|
|
|
65049
65491
|
// ../src/cli/agent-config.ts
|
|
65050
|
-
import { join as
|
|
65492
|
+
import { join as join26 } from "node:path";
|
|
65051
65493
|
import { homedir as homedir8 } from "node:os";
|
|
65052
65494
|
|
|
65053
65495
|
// ../src/cli/helpers.ts
|
|
@@ -65067,12 +65509,12 @@ var WEBKITE_VAULT_KEYS = new Set([
|
|
|
65067
65509
|
init_overlay_loader();
|
|
65068
65510
|
|
|
65069
65511
|
// ../src/cli/agent-config.ts
|
|
65070
|
-
var AUDIT_ROOT =
|
|
65512
|
+
var AUDIT_ROOT = join26(homedir8(), ".switchroom", "audit");
|
|
65071
65513
|
|
|
65072
65514
|
// ../src/agents/profiles.ts
|
|
65073
65515
|
var import_handlebars = __toESM(require_lib2(), 1);
|
|
65074
|
-
import { readFileSync as
|
|
65075
|
-
import { resolve as resolve5, join as
|
|
65516
|
+
import { readFileSync as readFileSync24, writeFileSync as writeFileSync19, existsSync as existsSync24, readdirSync as readdirSync5, statSync as statSync8, copyFileSync, mkdirSync as mkdirSync18, realpathSync as realpathSync2 } from "node:fs";
|
|
65517
|
+
import { resolve as resolve5, join as join27, sep as pathSep } from "node:path";
|
|
65076
65518
|
var PROFILES_ROOT = resolve5(import.meta.dirname, "../../profiles");
|
|
65077
65519
|
import_handlebars.default.registerHelper("json", (value) => {
|
|
65078
65520
|
return new import_handlebars.default.SafeString(JSON.stringify(value, null, 2));
|
|
@@ -65083,9 +65525,9 @@ import_handlebars.default.registerHelper("isNumber", (value) => {
|
|
|
65083
65525
|
var SHARED_FRAGMENTS_DIR = resolve5(PROFILES_ROOT, "_shared");
|
|
65084
65526
|
var SHARED_FRAGMENTS = ["vault-protocol", "agent-self-service", "execution-discipline", "reply-discipline"];
|
|
65085
65527
|
for (const name of SHARED_FRAGMENTS) {
|
|
65086
|
-
const fragPath =
|
|
65087
|
-
if (
|
|
65088
|
-
import_handlebars.default.registerPartial(name,
|
|
65528
|
+
const fragPath = join27(SHARED_FRAGMENTS_DIR, `${name}.md.hbs`);
|
|
65529
|
+
if (existsSync24(fragPath)) {
|
|
65530
|
+
import_handlebars.default.registerPartial(name, readFileSync24(fragPath, "utf-8"));
|
|
65089
65531
|
}
|
|
65090
65532
|
}
|
|
65091
65533
|
|
|
@@ -65377,7 +65819,7 @@ init_paths();
|
|
|
65377
65819
|
init_overlay_loader();
|
|
65378
65820
|
init_merge();
|
|
65379
65821
|
var import_yaml3 = __toESM(require_dist(), 1);
|
|
65380
|
-
import { readFileSync as
|
|
65822
|
+
import { readFileSync as readFileSync25, existsSync as existsSync25 } from "node:fs";
|
|
65381
65823
|
import { homedir as homedir9 } from "node:os";
|
|
65382
65824
|
import { resolve as resolve7 } from "node:path";
|
|
65383
65825
|
|
|
@@ -65453,7 +65895,7 @@ function findConfigFile2(startDir) {
|
|
|
65453
65895
|
resolve7(userDir, "clerk.yml")
|
|
65454
65896
|
].filter(Boolean);
|
|
65455
65897
|
for (const path2 of searchPaths) {
|
|
65456
|
-
if (
|
|
65898
|
+
if (existsSync25(path2)) {
|
|
65457
65899
|
return path2;
|
|
65458
65900
|
}
|
|
65459
65901
|
}
|
|
@@ -65461,12 +65903,12 @@ function findConfigFile2(startDir) {
|
|
|
65461
65903
|
}
|
|
65462
65904
|
function loadConfig2(configPath) {
|
|
65463
65905
|
const filePath = configPath ?? findConfigFile2();
|
|
65464
|
-
if (!
|
|
65906
|
+
if (!existsSync25(filePath)) {
|
|
65465
65907
|
throw new ConfigError2(`Config file not found: ${filePath}`);
|
|
65466
65908
|
}
|
|
65467
65909
|
let raw;
|
|
65468
65910
|
try {
|
|
65469
|
-
raw =
|
|
65911
|
+
raw = readFileSync25(filePath, "utf-8");
|
|
65470
65912
|
} catch (err) {
|
|
65471
65913
|
throw new ConfigError2(`Failed to read config file: ${filePath}`, [
|
|
65472
65914
|
` ${err.message}`
|
|
@@ -65969,15 +66411,15 @@ function topicForRecipient(args) {
|
|
|
65969
66411
|
}
|
|
65970
66412
|
|
|
65971
66413
|
// ../src/agents/perf.ts
|
|
65972
|
-
import { existsSync as
|
|
66414
|
+
import { existsSync as existsSync26, readFileSync as readFileSync26 } from "node:fs";
|
|
65973
66415
|
function readTurnUsages(jsonlPath, lastN) {
|
|
65974
|
-
if (!
|
|
66416
|
+
if (!existsSync26(jsonlPath))
|
|
65975
66417
|
return [];
|
|
65976
66418
|
if (lastN <= 0)
|
|
65977
66419
|
return [];
|
|
65978
66420
|
let raw;
|
|
65979
66421
|
try {
|
|
65980
|
-
raw =
|
|
66422
|
+
raw = readFileSync26(jsonlPath, "utf-8");
|
|
65981
66423
|
} catch {
|
|
65982
66424
|
return [];
|
|
65983
66425
|
}
|
|
@@ -66068,8 +66510,8 @@ function numField(obj, key) {
|
|
|
66068
66510
|
}
|
|
66069
66511
|
|
|
66070
66512
|
// gateway/context-occupancy.ts
|
|
66071
|
-
import { mkdirSync as
|
|
66072
|
-
import { join as
|
|
66513
|
+
import { mkdirSync as mkdirSync19, writeFileSync as writeFileSync20 } from "node:fs";
|
|
66514
|
+
import { join as join29 } from "node:path";
|
|
66073
66515
|
var CONTEXT_OCCUPANCY_FILENAME = "context-occupancy.json";
|
|
66074
66516
|
var TIGHT_FRACTION = 0.8;
|
|
66075
66517
|
function buildContextOccupancy(occupancy, cap, now) {
|
|
@@ -66092,9 +66534,9 @@ function buildContextOccupancy(occupancy, cap, now) {
|
|
|
66092
66534
|
}
|
|
66093
66535
|
function writeContextOccupancySnapshot(stateDir, snapshot, deps) {
|
|
66094
66536
|
try {
|
|
66095
|
-
const path2 =
|
|
66096
|
-
(deps?.mkdir ?? ((p, o) =>
|
|
66097
|
-
(deps?.writeFile ?? ((p, d) =>
|
|
66537
|
+
const path2 = join29(stateDir, CONTEXT_OCCUPANCY_FILENAME);
|
|
66538
|
+
(deps?.mkdir ?? ((p, o) => mkdirSync19(p, o)))(stateDir, { recursive: true });
|
|
66539
|
+
(deps?.writeFile ?? ((p, d) => writeFileSync20(p, d)))(path2, JSON.stringify(snapshot, null, 2) + `
|
|
66098
66540
|
`);
|
|
66099
66541
|
} catch {}
|
|
66100
66542
|
}
|
|
@@ -66177,7 +66619,7 @@ function nextCompactNotify(state3, ev) {
|
|
|
66177
66619
|
}
|
|
66178
66620
|
|
|
66179
66621
|
// gateway/hostd-dispatch.ts
|
|
66180
|
-
import { existsSync as
|
|
66622
|
+
import { existsSync as existsSync27 } from "node:fs";
|
|
66181
66623
|
import { randomBytes as randomBytes5 } from "node:crypto";
|
|
66182
66624
|
|
|
66183
66625
|
// ../src/host-control/client.ts
|
|
@@ -66502,13 +66944,13 @@ function hostdSocketPath(agentName3) {
|
|
|
66502
66944
|
function hostdWillBeUsed(agentName3) {
|
|
66503
66945
|
if (!isHostdEnabled())
|
|
66504
66946
|
return false;
|
|
66505
|
-
return
|
|
66947
|
+
return existsSync27(hostdSocketPath(agentName3));
|
|
66506
66948
|
}
|
|
66507
66949
|
async function tryHostdDispatch(agentName3, req, timeoutMs = 5000) {
|
|
66508
66950
|
if (!isHostdEnabled())
|
|
66509
66951
|
return "not-configured";
|
|
66510
66952
|
const sockPath = hostdSocketPath(agentName3);
|
|
66511
|
-
if (!
|
|
66953
|
+
if (!existsSync27(sockPath))
|
|
66512
66954
|
return "not-configured";
|
|
66513
66955
|
try {
|
|
66514
66956
|
return await hostdRequest({ socketPath: sockPath, timeoutMs }, req);
|
|
@@ -66532,7 +66974,7 @@ async function hostdGetStatusOnce(agentName3, targetRequestId) {
|
|
|
66532
66974
|
if (!isHostdEnabled())
|
|
66533
66975
|
return "not-configured";
|
|
66534
66976
|
const sockPath = hostdSocketPath(agentName3);
|
|
66535
|
-
if (!
|
|
66977
|
+
if (!existsSync27(sockPath))
|
|
66536
66978
|
return "not-configured";
|
|
66537
66979
|
try {
|
|
66538
66980
|
const resp = await hostdRequest({ socketPath: sockPath, timeoutMs: 3000 }, {
|
|
@@ -66553,7 +66995,7 @@ async function pollHostdStatus(agentName3, targetRequestId, opts) {
|
|
|
66553
66995
|
if (!isHostdEnabled())
|
|
66554
66996
|
return "not-configured";
|
|
66555
66997
|
const sockPath = hostdSocketPath(agentName3);
|
|
66556
|
-
if (!
|
|
66998
|
+
if (!existsSync27(sockPath))
|
|
66557
66999
|
return "not-configured";
|
|
66558
67000
|
const now = opts.now ?? Date.now;
|
|
66559
67001
|
const sleep2 = opts.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
|
|
@@ -66632,7 +67074,7 @@ function shouldSweepChatAtBoot(chatId) {
|
|
|
66632
67074
|
|
|
66633
67075
|
// gateway/webhook-ingest-server.ts
|
|
66634
67076
|
import net4 from "node:net";
|
|
66635
|
-
import { chmodSync as chmodSync5, existsSync as
|
|
67077
|
+
import { chmodSync as chmodSync5, existsSync as existsSync28, unlinkSync as unlinkSync10 } from "node:fs";
|
|
66636
67078
|
var MAX_REQUEST_BYTES = 1024 * 1024;
|
|
66637
67079
|
function fdOf(conn) {
|
|
66638
67080
|
const handle = conn._handle;
|
|
@@ -66644,7 +67086,7 @@ function startWebhookIngestServer(opts) {
|
|
|
66644
67086
|
const log = opts.log ?? ((s) => process.stderr.write(s));
|
|
66645
67087
|
const allowed = new Set(opts.allowedUids);
|
|
66646
67088
|
try {
|
|
66647
|
-
if (
|
|
67089
|
+
if (existsSync28(opts.socketPath))
|
|
66648
67090
|
unlinkSync10(opts.socketPath);
|
|
66649
67091
|
} catch (err) {
|
|
66650
67092
|
log(`webhook-ingest-server: could not unlink stale socket: ${err.message}
|
|
@@ -66737,7 +67179,7 @@ function startWebhookIngestServer(opts) {
|
|
|
66737
67179
|
server.close();
|
|
66738
67180
|
} catch {}
|
|
66739
67181
|
try {
|
|
66740
|
-
if (
|
|
67182
|
+
if (existsSync28(opts.socketPath))
|
|
66741
67183
|
unlinkSync10(opts.socketPath);
|
|
66742
67184
|
} catch {}
|
|
66743
67185
|
}
|
|
@@ -66745,20 +67187,20 @@ function startWebhookIngestServer(opts) {
|
|
|
66745
67187
|
}
|
|
66746
67188
|
|
|
66747
67189
|
// ../src/web/webhook-gateway-record.ts
|
|
66748
|
-
import { appendFileSync as appendFileSync5, mkdirSync as
|
|
66749
|
-
import { join as
|
|
67190
|
+
import { appendFileSync as appendFileSync5, mkdirSync as mkdirSync22 } from "fs";
|
|
67191
|
+
import { join as join32 } from "path";
|
|
66750
67192
|
import { homedir as homedir11 } from "os";
|
|
66751
67193
|
|
|
66752
67194
|
// ../src/web/webhook-handler.ts
|
|
66753
|
-
import { appendFileSync as appendFileSync4, existsSync as
|
|
66754
|
-
import { join as
|
|
67195
|
+
import { appendFileSync as appendFileSync4, existsSync as existsSync29, mkdirSync as mkdirSync20, readFileSync as readFileSync27, writeFileSync as writeFileSync21 } from "fs";
|
|
67196
|
+
import { join as join30 } from "path";
|
|
66755
67197
|
var DEDUP_MAX = 1000;
|
|
66756
67198
|
var DEDUP_TTL_MS = 24 * 60 * 60 * 1000;
|
|
66757
67199
|
function loadDedupFile(path2) {
|
|
66758
67200
|
try {
|
|
66759
|
-
if (!
|
|
67201
|
+
if (!existsSync29(path2))
|
|
66760
67202
|
return {};
|
|
66761
|
-
const raw = JSON.parse(
|
|
67203
|
+
const raw = JSON.parse(readFileSync27(path2, "utf-8"));
|
|
66762
67204
|
return typeof raw.deliveries === "object" && raw.deliveries !== null ? raw.deliveries : {};
|
|
66763
67205
|
} catch {
|
|
66764
67206
|
return {};
|
|
@@ -66772,7 +67214,7 @@ function saveDedupFile(path2, deliveries, now) {
|
|
|
66772
67214
|
}
|
|
66773
67215
|
const sorted = Object.entries(pruned).sort((a, b) => b[1] - a[1]).slice(0, DEDUP_MAX);
|
|
66774
67216
|
const final = Object.fromEntries(sorted);
|
|
66775
|
-
|
|
67217
|
+
writeFileSync21(path2, JSON.stringify({ deliveries: final }), {
|
|
66776
67218
|
mode: 384
|
|
66777
67219
|
});
|
|
66778
67220
|
}
|
|
@@ -66780,8 +67222,8 @@ var agentDedupCache = new Map;
|
|
|
66780
67222
|
function createFileDedupStore(resolveAgentDir) {
|
|
66781
67223
|
return {
|
|
66782
67224
|
check(agent, deliveryId, now) {
|
|
66783
|
-
const telegramDir =
|
|
66784
|
-
const filePath =
|
|
67225
|
+
const telegramDir = join30(resolveAgentDir(agent), "telegram");
|
|
67226
|
+
const filePath = join30(telegramDir, "webhook-dedup.json");
|
|
66785
67227
|
if (!agentDedupCache.has(agent)) {
|
|
66786
67228
|
agentDedupCache.set(agent, loadDedupFile(filePath));
|
|
66787
67229
|
}
|
|
@@ -66791,7 +67233,7 @@ function createFileDedupStore(resolveAgentDir) {
|
|
|
66791
67233
|
}
|
|
66792
67234
|
deliveries[deliveryId] = now;
|
|
66793
67235
|
try {
|
|
66794
|
-
|
|
67236
|
+
mkdirSync20(telegramDir, { recursive: true });
|
|
66795
67237
|
saveDedupFile(filePath, deliveries, now);
|
|
66796
67238
|
} catch {}
|
|
66797
67239
|
return;
|
|
@@ -66802,8 +67244,8 @@ var tokenBuckets = new Map;
|
|
|
66802
67244
|
var throttleIssueWindow = new Map;
|
|
66803
67245
|
|
|
66804
67246
|
// ../src/web/webhook-dispatch.ts
|
|
66805
|
-
import { existsSync as
|
|
66806
|
-
import { join as
|
|
67247
|
+
import { existsSync as existsSync30, mkdirSync as mkdirSync21, readFileSync as readFileSync28, writeFileSync as writeFileSync22 } from "fs";
|
|
67248
|
+
import { join as join31 } from "path";
|
|
66807
67249
|
import { homedir as homedir10 } from "os";
|
|
66808
67250
|
|
|
66809
67251
|
// ../src/agent-scheduler/ipc-client.ts
|
|
@@ -67102,9 +67544,9 @@ function cooldownKey(source, eventType, repo, number, ruleIndex) {
|
|
|
67102
67544
|
}
|
|
67103
67545
|
function loadCooldownFile(path2) {
|
|
67104
67546
|
try {
|
|
67105
|
-
if (!
|
|
67547
|
+
if (!existsSync30(path2))
|
|
67106
67548
|
return {};
|
|
67107
|
-
const raw = JSON.parse(
|
|
67549
|
+
const raw = JSON.parse(readFileSync28(path2, "utf-8"));
|
|
67108
67550
|
return typeof raw.dispatches === "object" && raw.dispatches !== null ? raw.dispatches : {};
|
|
67109
67551
|
} catch {
|
|
67110
67552
|
return {};
|
|
@@ -67112,7 +67554,7 @@ function loadCooldownFile(path2) {
|
|
|
67112
67554
|
}
|
|
67113
67555
|
function saveCooldownFile(path2, dispatches) {
|
|
67114
67556
|
try {
|
|
67115
|
-
|
|
67557
|
+
writeFileSync22(path2, JSON.stringify({ dispatches }), {
|
|
67116
67558
|
mode: 384
|
|
67117
67559
|
});
|
|
67118
67560
|
} catch {}
|
|
@@ -67123,8 +67565,8 @@ function createFileCooldownStore(resolveAgentDir) {
|
|
|
67123
67565
|
isCoolingDown(agent, key, cooldownMs, now) {
|
|
67124
67566
|
if (cooldownMs <= 0)
|
|
67125
67567
|
return false;
|
|
67126
|
-
const telegramDir =
|
|
67127
|
-
const filePath =
|
|
67568
|
+
const telegramDir = join31(resolveAgentDir(agent), "telegram");
|
|
67569
|
+
const filePath = join31(telegramDir, "webhook-cooldown.json");
|
|
67128
67570
|
if (!cache.has(agent)) {
|
|
67129
67571
|
cache.set(agent, loadCooldownFile(filePath));
|
|
67130
67572
|
}
|
|
@@ -67135,7 +67577,7 @@ function createFileCooldownStore(resolveAgentDir) {
|
|
|
67135
67577
|
}
|
|
67136
67578
|
dispatches[key] = now;
|
|
67137
67579
|
try {
|
|
67138
|
-
|
|
67580
|
+
mkdirSync21(telegramDir, { recursive: true });
|
|
67139
67581
|
saveCooldownFile(filePath, dispatches);
|
|
67140
67582
|
} catch {}
|
|
67141
67583
|
return false;
|
|
@@ -67181,9 +67623,9 @@ async function defaultInject(socketPath, agentName3, inbound) {
|
|
|
67181
67623
|
}
|
|
67182
67624
|
function injectWebhookInbound(agent, prompt, ctx, deps = {}) {
|
|
67183
67625
|
const log = deps.log ?? ((s) => process.stderr.write(s));
|
|
67184
|
-
const resolveAgentDir = deps.resolveAgentDir ?? ((a) =>
|
|
67626
|
+
const resolveAgentDir = deps.resolveAgentDir ?? ((a) => join31(homedir10(), ".switchroom", "agents", a));
|
|
67185
67627
|
const now = (deps.now ?? Date.now)();
|
|
67186
|
-
const socketPath =
|
|
67628
|
+
const socketPath = join31(resolveAgentDir(agent), "telegram", "gateway.sock");
|
|
67187
67629
|
const inbound = {
|
|
67188
67630
|
type: "inbound",
|
|
67189
67631
|
chatId: ctx.chatId,
|
|
@@ -67255,7 +67697,7 @@ function evaluateDispatch(args, deps = {}) {
|
|
|
67255
67697
|
const log = deps.log ?? ((s) => process.stderr.write(s));
|
|
67256
67698
|
const now = (deps.now ?? Date.now)();
|
|
67257
67699
|
const nowDate = deps.nowDate ?? (() => new Date(now));
|
|
67258
|
-
const resolveAgentDir = deps.resolveAgentDir ?? ((a) =>
|
|
67700
|
+
const resolveAgentDir = deps.resolveAgentDir ?? ((a) => join31(homedir10(), ".switchroom", "agents", a));
|
|
67259
67701
|
const cooldownStore = deps.cooldownStore ?? createFileCooldownStore(resolveAgentDir);
|
|
67260
67702
|
if (!DISPATCH_SOURCES.includes(args.source))
|
|
67261
67703
|
return 0;
|
|
@@ -67333,10 +67775,10 @@ var CONSOLIDATION_COMPLETED_EVENT = "consolidation.completed";
|
|
|
67333
67775
|
function recordWebhookEvent(rec, deps = {}) {
|
|
67334
67776
|
const log = deps.log ?? ((s) => process.stderr.write(s));
|
|
67335
67777
|
const now = rec.ts || (deps.now ?? Date.now)();
|
|
67336
|
-
const resolveAgentDir = deps.resolveAgentDir ?? ((a) =>
|
|
67778
|
+
const resolveAgentDir = deps.resolveAgentDir ?? ((a) => join32(homedir11(), ".switchroom", "agents", a));
|
|
67337
67779
|
const dedupStore = deps.dedupStore ?? createFileDedupStore(resolveAgentDir);
|
|
67338
67780
|
const agent = rec.agent;
|
|
67339
|
-
const telegramDir =
|
|
67781
|
+
const telegramDir = join32(resolveAgentDir(agent), "telegram");
|
|
67340
67782
|
if (rec.source === "github" && rec.delivery_id) {
|
|
67341
67783
|
const originalTs = dedupStore.check(agent, rec.delivery_id, now);
|
|
67342
67784
|
if (originalTs !== undefined) {
|
|
@@ -67345,9 +67787,9 @@ function recordWebhookEvent(rec, deps = {}) {
|
|
|
67345
67787
|
return { status: "deduped", ts: originalTs };
|
|
67346
67788
|
}
|
|
67347
67789
|
}
|
|
67348
|
-
const logPath =
|
|
67790
|
+
const logPath = join32(telegramDir, "webhook-events.jsonl");
|
|
67349
67791
|
try {
|
|
67350
|
-
|
|
67792
|
+
mkdirSync22(telegramDir, { recursive: true });
|
|
67351
67793
|
const record = {
|
|
67352
67794
|
ts: now,
|
|
67353
67795
|
source: rec.source,
|
|
@@ -67456,7 +67898,7 @@ function recordWebhookEvent(rec, deps = {}) {
|
|
|
67456
67898
|
}
|
|
67457
67899
|
|
|
67458
67900
|
// gateway/ipc-server.ts
|
|
67459
|
-
import { renameSync as
|
|
67901
|
+
import { renameSync as renameSync8, unlinkSync as unlinkSync11, chmodSync as chmodSync6 } from "fs";
|
|
67460
67902
|
var MAX_BUFFER_SIZE = 1024 * 1024;
|
|
67461
67903
|
var VALID_OPERATOR_KINDS = new Set([
|
|
67462
67904
|
"credentials-expired",
|
|
@@ -67655,7 +68097,7 @@ function createIpcServer(options) {
|
|
|
67655
68097
|
heartbeatTimeoutMs = 30000
|
|
67656
68098
|
} = options;
|
|
67657
68099
|
try {
|
|
67658
|
-
|
|
68100
|
+
renameSync8(socketPath, socketPath + ".bak");
|
|
67659
68101
|
} catch {}
|
|
67660
68102
|
try {
|
|
67661
68103
|
unlinkSync11(socketPath + ".bak");
|
|
@@ -68032,7 +68474,7 @@ function createIpcServer(options) {
|
|
|
68032
68474
|
clientBySocketId.clear();
|
|
68033
68475
|
server.stop(true);
|
|
68034
68476
|
try {
|
|
68035
|
-
|
|
68477
|
+
renameSync8(socketPath, socketPath + ".bak");
|
|
68036
68478
|
} catch {}
|
|
68037
68479
|
}
|
|
68038
68480
|
};
|
|
@@ -68856,11 +69298,12 @@ function persistObligations(path2, fs2, snapshot, log = (l) => process.stderr.wr
|
|
|
68856
69298
|
}
|
|
68857
69299
|
|
|
68858
69300
|
// gateway/status-pin-store.ts
|
|
69301
|
+
var BOOT_UNPIN_MAX_ATTEMPTS = 5;
|
|
68859
69302
|
function isPinRow(x) {
|
|
68860
69303
|
if (x == null || typeof x !== "object")
|
|
68861
69304
|
return false;
|
|
68862
69305
|
const o = x;
|
|
68863
|
-
return typeof o.pinKey === "string" && o.pinKey.length > 0 && typeof o.chatId === "string" && o.chatId.length > 0 && typeof o.messageId === "number" && (o.pending === undefined || typeof o.pending === "boolean");
|
|
69306
|
+
return typeof o.pinKey === "string" && o.pinKey.length > 0 && typeof o.chatId === "string" && o.chatId.length > 0 && typeof o.messageId === "number" && (o.pending === undefined || typeof o.pending === "boolean") && (o.expiresAt === undefined || typeof o.expiresAt === "number") && (o.attempts === undefined || typeof o.attempts === "number");
|
|
68864
69307
|
}
|
|
68865
69308
|
function loadStatusPins(path2, fs2) {
|
|
68866
69309
|
if (!fs2.existsSync(path2))
|
|
@@ -68904,21 +69347,38 @@ function pinnedMessageIsOurs(tracked, chatId, pinnedMessageId) {
|
|
|
68904
69347
|
}
|
|
68905
69348
|
async function runStatusPinBootCleanup(args) {
|
|
68906
69349
|
const log = args.log ?? ((l) => process.stderr.write(l));
|
|
69350
|
+
const now = args.now ?? Date.now();
|
|
68907
69351
|
const persisted = loadStatusPins(args.path, args.fs);
|
|
68908
69352
|
if (persisted.length === 0)
|
|
68909
|
-
return { cleared: 0, total: 0 };
|
|
69353
|
+
return { cleared: 0, retained: 0, kept: 0, total: 0 };
|
|
68910
69354
|
let cleared = 0;
|
|
69355
|
+
let retained = 0;
|
|
69356
|
+
let kept = 0;
|
|
69357
|
+
const next = [];
|
|
68911
69358
|
for (const pin of persisted) {
|
|
69359
|
+
if (pin.expiresAt != null && pin.expiresAt > now) {
|
|
69360
|
+
next.push(pin);
|
|
69361
|
+
kept++;
|
|
69362
|
+
continue;
|
|
69363
|
+
}
|
|
68912
69364
|
try {
|
|
68913
69365
|
await args.unpin(pin.chatId, pin.messageId);
|
|
68914
69366
|
cleared++;
|
|
68915
69367
|
} catch (err) {
|
|
68916
|
-
|
|
69368
|
+
const attempts = (pin.attempts ?? 0) + 1;
|
|
69369
|
+
log(`status-pin-store: boot cleanup unpin failed ` + `(chat=${pin.chatId} msg=${pin.messageId} attempt=${attempts}): ` + `${err.message}
|
|
68917
69370
|
`);
|
|
69371
|
+
if (attempts < BOOT_UNPIN_MAX_ATTEMPTS) {
|
|
69372
|
+
next.push({ ...pin, attempts });
|
|
69373
|
+
retained++;
|
|
69374
|
+
} else {
|
|
69375
|
+
log(`status-pin-store: boot cleanup FORFEITING pin after ` + `${attempts} failed unpin attempts ` + `(key=${pin.pinKey} chat=${pin.chatId} msg=${pin.messageId}) \u2014 ` + `will not retry again
|
|
69376
|
+
`);
|
|
69377
|
+
}
|
|
68918
69378
|
}
|
|
68919
69379
|
}
|
|
68920
|
-
persistStatusPins(args.path, args.fs,
|
|
68921
|
-
return { cleared, total: persisted.length };
|
|
69380
|
+
persistStatusPins(args.path, args.fs, next, log);
|
|
69381
|
+
return { cleared, retained, kept, total: persisted.length };
|
|
68922
69382
|
}
|
|
68923
69383
|
var storeLockTails = new Map;
|
|
68924
69384
|
function withStoreLock(path2, fn) {
|
|
@@ -68937,6 +69397,11 @@ function applyStatusPinRow(path2, fs2, pinKey, row, log) {
|
|
|
68937
69397
|
const next = row == null ? others : [...others, row];
|
|
68938
69398
|
persistStatusPins(path2, fs2, next, log);
|
|
68939
69399
|
}
|
|
69400
|
+
function mutateStatusPinRow(path2, fs2, pinKey, row, log = (l) => process.stderr.write(l)) {
|
|
69401
|
+
return withStoreLock(path2, async () => {
|
|
69402
|
+
applyStatusPinRow(path2, fs2, pinKey, row, log);
|
|
69403
|
+
});
|
|
69404
|
+
}
|
|
68940
69405
|
function reconcileAndPersistStatusPin(args) {
|
|
68941
69406
|
const { path: path2, fs: fs2, pinKey, chatId, op } = args;
|
|
68942
69407
|
const log = args.log ?? ((l) => process.stderr.write(l));
|
|
@@ -68958,11 +69423,12 @@ function reconcileAndPersistStatusPin(args) {
|
|
|
68958
69423
|
}
|
|
68959
69424
|
|
|
68960
69425
|
// gateway/activity-card-store.ts
|
|
69426
|
+
var BOOT_UNPIN_MAX_ATTEMPTS2 = 5;
|
|
68961
69427
|
function isCardRow(x) {
|
|
68962
69428
|
if (x == null || typeof x !== "object")
|
|
68963
69429
|
return false;
|
|
68964
69430
|
const o = x;
|
|
68965
|
-
return typeof o.turnKey === "string" && o.turnKey.length > 0 && typeof o.chatId === "string" && o.chatId.length > 0 && (o.threadId === null || typeof o.threadId === "number") && typeof o.activityMessageId === "number" && typeof o.startedAt === "number" && (o.pinned === undefined || typeof o.pinned === "boolean");
|
|
69431
|
+
return typeof o.turnKey === "string" && o.turnKey.length > 0 && typeof o.chatId === "string" && o.chatId.length > 0 && (o.threadId === null || typeof o.threadId === "number") && typeof o.activityMessageId === "number" && typeof o.startedAt === "number" && (o.pinned === undefined || typeof o.pinned === "boolean") && (o.finalizeAttempted === undefined || typeof o.finalizeAttempted === "boolean") && (o.unpinAttempts === undefined || typeof o.unpinAttempts === "number");
|
|
68966
69432
|
}
|
|
68967
69433
|
function loadActivityCards(path2, fs2) {
|
|
68968
69434
|
if (!fs2.existsSync(path2))
|
|
@@ -69021,23 +69487,32 @@ async function runActivityCardBootReaper(args) {
|
|
|
69021
69487
|
let unpinned = 0;
|
|
69022
69488
|
for (const record of persisted) {
|
|
69023
69489
|
clearActivityCardRecord(args.path, args.fs, record.turnKey, record.activityMessageId, log);
|
|
69024
|
-
|
|
69025
|
-
|
|
69026
|
-
|
|
69027
|
-
|
|
69028
|
-
|
|
69029
|
-
|
|
69030
|
-
|
|
69031
|
-
|
|
69490
|
+
if (!record.finalizeAttempted) {
|
|
69491
|
+
try {
|
|
69492
|
+
const res = await args.finalizeCard(record);
|
|
69493
|
+
if (res != null)
|
|
69494
|
+
finalized++;
|
|
69495
|
+
else
|
|
69496
|
+
vanished++;
|
|
69497
|
+
} catch (err) {
|
|
69498
|
+
log(`activity-card-store: boot reaper finalize failed ` + `(chat=${record.chatId} msg=${record.activityMessageId}): ` + `${err.message}
|
|
69032
69499
|
`);
|
|
69500
|
+
}
|
|
69033
69501
|
}
|
|
69034
69502
|
if (record.pinned) {
|
|
69035
69503
|
try {
|
|
69036
69504
|
await args.unpinCard(record);
|
|
69037
69505
|
unpinned++;
|
|
69038
69506
|
} catch (err) {
|
|
69039
|
-
|
|
69507
|
+
const attempts = (record.unpinAttempts ?? 0) + 1;
|
|
69508
|
+
log(`activity-card-store: boot reaper unpin failed ` + `(chat=${record.chatId} msg=${record.activityMessageId} ` + `attempt=${attempts}): ${err.message}
|
|
69040
69509
|
`);
|
|
69510
|
+
if (attempts < BOOT_UNPIN_MAX_ATTEMPTS2) {
|
|
69511
|
+
writeActivityCardRecord(args.path, args.fs, { ...record, finalizeAttempted: true, unpinAttempts: attempts }, log);
|
|
69512
|
+
} else {
|
|
69513
|
+
log(`activity-card-store: boot reaper FORFEITING card unpin after ` + `${attempts} failed attempts ` + `(chat=${record.chatId} msg=${record.activityMessageId}) \u2014 ` + `will not retry again
|
|
69514
|
+
`);
|
|
69515
|
+
}
|
|
69041
69516
|
}
|
|
69042
69517
|
}
|
|
69043
69518
|
}
|
|
@@ -69084,6 +69559,37 @@ function restartOrphanCardFinalizeText(startedAt) {
|
|
|
69084
69559
|
return `\u26a0\ufe0f Interrupted by a gateway restart${elapsed} \u2014 this turn did not finish.`;
|
|
69085
69560
|
}
|
|
69086
69561
|
|
|
69562
|
+
// gateway/worker-pin-reaper.ts
|
|
69563
|
+
var WORKER_PIN_TTL_MS_DEFAULT = 6 * 60 * 60000;
|
|
69564
|
+
var WORKER_PIN_KEY_PREFIX = "wk:";
|
|
69565
|
+
function workerAgentIdOfPinKey(pinKey) {
|
|
69566
|
+
if (!pinKey.startsWith(WORKER_PIN_KEY_PREFIX))
|
|
69567
|
+
return null;
|
|
69568
|
+
const agentId = pinKey.slice(WORKER_PIN_KEY_PREFIX.length);
|
|
69569
|
+
return agentId.length > 0 ? agentId : null;
|
|
69570
|
+
}
|
|
69571
|
+
function decideWorkerPinReaps(args) {
|
|
69572
|
+
const reaps = [];
|
|
69573
|
+
for (const pin of args.pins) {
|
|
69574
|
+
const agentId = workerAgentIdOfPinKey(pin.pinKey);
|
|
69575
|
+
if (agentId == null)
|
|
69576
|
+
continue;
|
|
69577
|
+
if (pin.chatId.length === 0)
|
|
69578
|
+
continue;
|
|
69579
|
+
const status = args.statusOf(agentId);
|
|
69580
|
+
if (status === "terminal") {
|
|
69581
|
+
reaps.push({ ...pin, reason: "terminal" });
|
|
69582
|
+
continue;
|
|
69583
|
+
}
|
|
69584
|
+
if (status === "running")
|
|
69585
|
+
continue;
|
|
69586
|
+
if (args.now - pin.pinnedAt >= args.ttlMs) {
|
|
69587
|
+
reaps.push({ ...pin, reason: "ttl" });
|
|
69588
|
+
}
|
|
69589
|
+
}
|
|
69590
|
+
return reaps;
|
|
69591
|
+
}
|
|
69592
|
+
|
|
69087
69593
|
// gateway/with-deadline.ts
|
|
69088
69594
|
function withDeadline(p, ms, timeoutMessage) {
|
|
69089
69595
|
p.catch(() => {});
|
|
@@ -70504,26 +71010,26 @@ var import_yaml4 = __toESM(require_dist(), 1);
|
|
|
70504
71010
|
|
|
70505
71011
|
// ../src/web/config-diff.ts
|
|
70506
71012
|
import {
|
|
70507
|
-
mkdirSync as
|
|
71013
|
+
mkdirSync as mkdirSync23,
|
|
70508
71014
|
mkdtempSync as mkdtemp,
|
|
70509
71015
|
rmSync as rmrf,
|
|
70510
|
-
writeFileSync as
|
|
71016
|
+
writeFileSync as writeFileSync23
|
|
70511
71017
|
} from "node:fs";
|
|
70512
71018
|
import { spawnSync } from "node:child_process";
|
|
70513
71019
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
70514
|
-
import { join as
|
|
71020
|
+
import { join as join33 } from "node:path";
|
|
70515
71021
|
|
|
70516
71022
|
class ConfigDiffError extends Error {
|
|
70517
71023
|
}
|
|
70518
71024
|
function generateUnifiedDiff(before, after, name = "switchroom.yaml", gitBin = "git") {
|
|
70519
71025
|
if (before === after)
|
|
70520
71026
|
return "";
|
|
70521
|
-
const dir = mkdtemp(
|
|
71027
|
+
const dir = mkdtemp(join33(tmpdir2(), "switchroom-config-diff-"));
|
|
70522
71028
|
try {
|
|
70523
|
-
|
|
70524
|
-
|
|
70525
|
-
|
|
70526
|
-
|
|
71029
|
+
mkdirSync23(join33(dir, "cur"), { recursive: true });
|
|
71030
|
+
mkdirSync23(join33(dir, "new"), { recursive: true });
|
|
71031
|
+
writeFileSync23(join33(dir, "cur", name), before);
|
|
71032
|
+
writeFileSync23(join33(dir, "new", name), after);
|
|
70527
71033
|
const r = spawnSync(gitBin, ["diff", "--no-index", "--no-color", "--", `cur/${name}`, `new/${name}`], { cwd: dir, encoding: "utf-8", timeout: 1e4 });
|
|
70528
71034
|
if (r.status === 0)
|
|
70529
71035
|
return "";
|
|
@@ -70554,6 +71060,9 @@ function generateUnifiedDiff(before, after, name = "switchroom.yaml", gitBin = "
|
|
|
70554
71060
|
}
|
|
70555
71061
|
|
|
70556
71062
|
// gateway/mental-model-propose-diff.ts
|
|
71063
|
+
function decodeCanonicalEntities(s) {
|
|
71064
|
+
return s.replace(/</g, "<").replace(/>/g, ">").replace(/"/g, '"').replace(/'/g, "'").replace(/'/g, "'").replace(/&/g, "&");
|
|
71065
|
+
}
|
|
70557
71066
|
function readDeclaredMentalModelNames(configText, agentName3) {
|
|
70558
71067
|
if (!configText || !agentName3)
|
|
70559
71068
|
return [];
|
|
@@ -70600,17 +71109,20 @@ function buildMentalModelAppendDiff(args) {
|
|
|
70600
71109
|
detail: `agents.${agentName3} not present in config`
|
|
70601
71110
|
};
|
|
70602
71111
|
}
|
|
71112
|
+
const decodedName = decodeCanonicalEntities(spec.name);
|
|
70603
71113
|
const existing = readDeclaredMentalModelNames(configText, agentName3);
|
|
70604
|
-
if (existing.includes(
|
|
71114
|
+
if (existing.includes(decodedName)) {
|
|
70605
71115
|
return {
|
|
70606
71116
|
ok: false,
|
|
70607
71117
|
error: "duplicate",
|
|
70608
|
-
detail: `mental model "${
|
|
71118
|
+
detail: `mental model "${decodedName}" is already declared for ${agentName3}`
|
|
70609
71119
|
};
|
|
70610
71120
|
}
|
|
71121
|
+
const name = decodedName;
|
|
71122
|
+
const source_query = decodeCanonicalEntities(spec.source_query);
|
|
70611
71123
|
const item = {
|
|
70612
|
-
name
|
|
70613
|
-
source_query
|
|
71124
|
+
name,
|
|
71125
|
+
source_query
|
|
70614
71126
|
};
|
|
70615
71127
|
if (spec.refresh_after_consolidation !== undefined) {
|
|
70616
71128
|
item.refresh_after_consolidation = spec.refresh_after_consolidation;
|
|
@@ -70897,27 +71409,27 @@ function buildSkillProposalApplyInbound(opts) {
|
|
|
70897
71409
|
// ../src/self-improve/skill-proposals.ts
|
|
70898
71410
|
import {
|
|
70899
71411
|
closeSync as closeSync3,
|
|
70900
|
-
existsSync as
|
|
70901
|
-
mkdirSync as
|
|
71412
|
+
existsSync as existsSync31,
|
|
71413
|
+
mkdirSync as mkdirSync24,
|
|
70902
71414
|
openSync as openSync3,
|
|
70903
|
-
readFileSync as
|
|
71415
|
+
readFileSync as readFileSync29,
|
|
70904
71416
|
writeSync as writeSync3
|
|
70905
71417
|
} from "node:fs";
|
|
70906
|
-
import { join as
|
|
71418
|
+
import { join as join34 } from "node:path";
|
|
70907
71419
|
import { randomUUID as randomUUID5 } from "node:crypto";
|
|
70908
71420
|
var PROPOSALS_FILE = "skill-proposals.jsonl";
|
|
70909
71421
|
var REJECTED_FILE = "skill-proposals-rejected.jsonl";
|
|
70910
71422
|
var REJECTION_TTL_MS = 90 * 24 * 60 * 60 * 1000;
|
|
70911
71423
|
var PROPOSAL_SIM_THRESHOLD = 0.5;
|
|
70912
71424
|
function proposalsPath(stateDir) {
|
|
70913
|
-
return
|
|
71425
|
+
return join34(stateDir, PROPOSALS_FILE);
|
|
70914
71426
|
}
|
|
70915
71427
|
function rejectedPath(stateDir) {
|
|
70916
|
-
return
|
|
71428
|
+
return join34(stateDir, REJECTED_FILE);
|
|
70917
71429
|
}
|
|
70918
71430
|
function ensureDir2(stateDir) {
|
|
70919
|
-
if (!
|
|
70920
|
-
|
|
71431
|
+
if (!existsSync31(stateDir)) {
|
|
71432
|
+
mkdirSync24(stateDir, { recursive: true, mode: 493 });
|
|
70921
71433
|
}
|
|
70922
71434
|
}
|
|
70923
71435
|
function appendLine(path2, obj) {
|
|
@@ -70930,11 +71442,11 @@ function appendLine(path2, obj) {
|
|
|
70930
71442
|
}
|
|
70931
71443
|
}
|
|
70932
71444
|
function readLines(path2, isValid2) {
|
|
70933
|
-
if (!
|
|
71445
|
+
if (!existsSync31(path2))
|
|
70934
71446
|
return [];
|
|
70935
71447
|
let raw;
|
|
70936
71448
|
try {
|
|
70937
|
-
raw =
|
|
71449
|
+
raw = readFileSync29(path2, "utf-8");
|
|
70938
71450
|
} catch {
|
|
70939
71451
|
return [];
|
|
70940
71452
|
}
|
|
@@ -71563,11 +72075,11 @@ function escapeBody2(s) {
|
|
|
71563
72075
|
}
|
|
71564
72076
|
|
|
71565
72077
|
// gateway/pid-file.ts
|
|
71566
|
-
import { writeFileSync as
|
|
72078
|
+
import { writeFileSync as writeFileSync24, readFileSync as readFileSync30, unlinkSync as unlinkSync12, renameSync as renameSync9 } from "node:fs";
|
|
71567
72079
|
function writePidFile(path2, record) {
|
|
71568
72080
|
const tmp = `${path2}.tmp-${process.pid}-${Date.now()}`;
|
|
71569
|
-
|
|
71570
|
-
|
|
72081
|
+
writeFileSync24(tmp, JSON.stringify(record), "utf-8");
|
|
72082
|
+
renameSync9(tmp, path2);
|
|
71571
72083
|
}
|
|
71572
72084
|
function clearPidFile(path2) {
|
|
71573
72085
|
try {
|
|
@@ -71582,10 +72094,10 @@ import {
|
|
|
71582
72094
|
writeFile as writeFileAsync,
|
|
71583
72095
|
readFile as readFileAsync
|
|
71584
72096
|
} from "node:fs/promises";
|
|
71585
|
-
import { readFileSync as
|
|
72097
|
+
import { readFileSync as readFileSync31 } from "node:fs";
|
|
71586
72098
|
function readCurrentBootId() {
|
|
71587
72099
|
try {
|
|
71588
|
-
const stat =
|
|
72100
|
+
const stat = readFileSync31("/proc/1/stat", "utf-8");
|
|
71589
72101
|
const lastParen = stat.lastIndexOf(")");
|
|
71590
72102
|
if (lastParen < 0)
|
|
71591
72103
|
return null;
|
|
@@ -71788,15 +72300,15 @@ function safeCount(fn) {
|
|
|
71788
72300
|
}
|
|
71789
72301
|
|
|
71790
72302
|
// gateway/session-marker.ts
|
|
71791
|
-
import { writeFileSync as
|
|
72303
|
+
import { writeFileSync as writeFileSync25, readFileSync as readFileSync32, renameSync as renameSync10, unlinkSync as unlinkSync13 } from "node:fs";
|
|
71792
72304
|
function writeSessionMarker(path2, marker) {
|
|
71793
72305
|
const tmp = `${path2}.tmp-${process.pid}-${Date.now()}`;
|
|
71794
|
-
|
|
71795
|
-
|
|
72306
|
+
writeFileSync25(tmp, JSON.stringify(marker), "utf-8");
|
|
72307
|
+
renameSync10(tmp, path2);
|
|
71796
72308
|
}
|
|
71797
72309
|
function readSessionMarker(path2) {
|
|
71798
72310
|
try {
|
|
71799
|
-
const raw =
|
|
72311
|
+
const raw = readFileSync32(path2, "utf-8");
|
|
71800
72312
|
const parsed = JSON.parse(raw);
|
|
71801
72313
|
if (typeof parsed.pid === "number" && typeof parsed.startedAtMs === "number" && Number.isFinite(parsed.pid) && Number.isFinite(parsed.startedAtMs)) {
|
|
71802
72314
|
return { pid: parsed.pid, startedAtMs: parsed.startedAtMs };
|
|
@@ -71818,16 +72330,16 @@ function shouldFireRestartBanner(input) {
|
|
|
71818
72330
|
}
|
|
71819
72331
|
|
|
71820
72332
|
// gateway/clean-shutdown-marker.ts
|
|
71821
|
-
import { writeFileSync as
|
|
72333
|
+
import { writeFileSync as writeFileSync26, readFileSync as readFileSync33, renameSync as renameSync11, unlinkSync as unlinkSync14 } from "node:fs";
|
|
71822
72334
|
var DEFAULT_MAX_AGE_MS = 60000;
|
|
71823
72335
|
function writeCleanShutdownMarker(path2, marker) {
|
|
71824
72336
|
const tmp = `${path2}.tmp-${process.pid}-${Date.now()}`;
|
|
71825
|
-
|
|
71826
|
-
|
|
72337
|
+
writeFileSync26(tmp, JSON.stringify(marker), "utf-8");
|
|
72338
|
+
renameSync11(tmp, path2);
|
|
71827
72339
|
}
|
|
71828
72340
|
function readCleanShutdownMarker(path2) {
|
|
71829
72341
|
try {
|
|
71830
|
-
const raw =
|
|
72342
|
+
const raw = readFileSync33(path2, "utf-8");
|
|
71831
72343
|
const parsed = JSON.parse(raw);
|
|
71832
72344
|
if (typeof parsed.ts === "number" && Number.isFinite(parsed.ts) && typeof parsed.signal === "string" && parsed.signal.length > 0) {
|
|
71833
72345
|
const out = { ts: parsed.ts, signal: parsed.signal };
|
|
@@ -72433,16 +72945,16 @@ function classifyAdminGate(text4, myAgentName) {
|
|
|
72433
72945
|
|
|
72434
72946
|
// subagent-watcher.ts
|
|
72435
72947
|
import {
|
|
72436
|
-
existsSync as
|
|
72948
|
+
existsSync as existsSync33,
|
|
72437
72949
|
openSync as openSync5,
|
|
72438
72950
|
readSync,
|
|
72439
72951
|
statSync as statSync10,
|
|
72440
72952
|
closeSync as closeSync5,
|
|
72441
72953
|
watch,
|
|
72442
72954
|
readdirSync as readdirSync6,
|
|
72443
|
-
readFileSync as
|
|
72955
|
+
readFileSync as readFileSync35
|
|
72444
72956
|
} from "fs";
|
|
72445
|
-
import { join as
|
|
72957
|
+
import { join as join36 } from "path";
|
|
72446
72958
|
|
|
72447
72959
|
// operator-events.ts
|
|
72448
72960
|
function classifyClaudeError(raw) {
|
|
@@ -72922,20 +73434,20 @@ function recordNestedSubagentDispatch(db2, args) {
|
|
|
72922
73434
|
// gateway/turn-active-marker.ts
|
|
72923
73435
|
import {
|
|
72924
73436
|
closeSync as closeSync4,
|
|
72925
|
-
existsSync as
|
|
72926
|
-
mkdirSync as
|
|
73437
|
+
existsSync as existsSync32,
|
|
73438
|
+
mkdirSync as mkdirSync25,
|
|
72927
73439
|
openSync as openSync4,
|
|
72928
|
-
readFileSync as
|
|
73440
|
+
readFileSync as readFileSync34,
|
|
72929
73441
|
statSync as statSync9,
|
|
72930
73442
|
unlinkSync as unlinkSync15,
|
|
72931
73443
|
utimesSync,
|
|
72932
|
-
writeFileSync as
|
|
73444
|
+
writeFileSync as writeFileSync27
|
|
72933
73445
|
} from "node:fs";
|
|
72934
|
-
import { join as
|
|
73446
|
+
import { join as join35 } from "node:path";
|
|
72935
73447
|
var TURN_ACTIVE_MARKER_FILE = "turn-active.json";
|
|
72936
73448
|
function touchTurnActiveMarker(stateDir) {
|
|
72937
|
-
const path2 =
|
|
72938
|
-
if (!
|
|
73449
|
+
const path2 = join35(stateDir, TURN_ACTIVE_MARKER_FILE);
|
|
73450
|
+
if (!existsSync32(path2))
|
|
72939
73451
|
return;
|
|
72940
73452
|
const now = new Date;
|
|
72941
73453
|
try {
|
|
@@ -72977,7 +73489,7 @@ function backfillJsonlAgentId(db2, jsonlPath, agentId, log) {
|
|
|
72977
73489
|
const metaPath = jsonlPath.replace(/\.jsonl$/, ".meta.json");
|
|
72978
73490
|
let meta;
|
|
72979
73491
|
try {
|
|
72980
|
-
const raw =
|
|
73492
|
+
const raw = readFileSync35(metaPath, "utf8");
|
|
72981
73493
|
meta = JSON.parse(raw);
|
|
72982
73494
|
} catch {
|
|
72983
73495
|
log?.(`subagent-watcher: backfill skip ${agentId} \u2014 meta.json not readable at ${metaPath}`);
|
|
@@ -73325,7 +73837,7 @@ function startSubagentWatcher(config) {
|
|
|
73325
73837
|
clearTimeout(ref.ref);
|
|
73326
73838
|
});
|
|
73327
73839
|
const fs2 = config.fs ?? {
|
|
73328
|
-
existsSync:
|
|
73840
|
+
existsSync: existsSync33,
|
|
73329
73841
|
readdirSync: readdirSync6,
|
|
73330
73842
|
statSync: statSync10,
|
|
73331
73843
|
openSync: openSync5,
|
|
@@ -73623,8 +74135,8 @@ function startSubagentWatcher(config) {
|
|
|
73623
74135
|
function rescanSubagentDirs() {
|
|
73624
74136
|
if (stopped)
|
|
73625
74137
|
return;
|
|
73626
|
-
const claudeHome =
|
|
73627
|
-
const projectsRoot =
|
|
74138
|
+
const claudeHome = join36(agentDir, ".claude");
|
|
74139
|
+
const projectsRoot = join36(claudeHome, "projects");
|
|
73628
74140
|
if (!fs2.existsSync(projectsRoot))
|
|
73629
74141
|
return;
|
|
73630
74142
|
let projectDirs;
|
|
@@ -73658,7 +74170,7 @@ function startSubagentWatcher(config) {
|
|
|
73658
74170
|
continue;
|
|
73659
74171
|
}
|
|
73660
74172
|
warnedForeignSlugs.delete(pDir);
|
|
73661
|
-
const projectPath =
|
|
74173
|
+
const projectPath = join36(projectsRoot, pDir);
|
|
73662
74174
|
let sessionDirs;
|
|
73663
74175
|
try {
|
|
73664
74176
|
sessionDirs = fs2.readdirSync(projectPath);
|
|
@@ -73668,7 +74180,7 @@ function startSubagentWatcher(config) {
|
|
|
73668
74180
|
for (const sDir of sessionDirs) {
|
|
73669
74181
|
if (sDir.endsWith(".jsonl"))
|
|
73670
74182
|
continue;
|
|
73671
|
-
const subagentsPath =
|
|
74183
|
+
const subagentsPath = join36(projectPath, sDir, "subagents");
|
|
73672
74184
|
if (!fs2.existsSync(subagentsPath))
|
|
73673
74185
|
continue;
|
|
73674
74186
|
const watchAndScan = (dirPath) => {
|
|
@@ -73677,7 +74189,7 @@ function startSubagentWatcher(config) {
|
|
|
73677
74189
|
const w = fs2.watch(dirPath, (_event, filename) => {
|
|
73678
74190
|
if (!filename || !filename.toString().startsWith("agent-") || !filename.toString().endsWith(".jsonl"))
|
|
73679
74191
|
return;
|
|
73680
|
-
const filePath =
|
|
74192
|
+
const filePath = join36(dirPath, filename.toString());
|
|
73681
74193
|
if (!knownFiles.has(filePath)) {
|
|
73682
74194
|
scanSubagentsDir(dirPath);
|
|
73683
74195
|
}
|
|
@@ -73691,7 +74203,7 @@ function startSubagentWatcher(config) {
|
|
|
73691
74203
|
scanSubagentsDir(dirPath);
|
|
73692
74204
|
};
|
|
73693
74205
|
watchAndScan(subagentsPath);
|
|
73694
|
-
const workflowsPath =
|
|
74206
|
+
const workflowsPath = join36(subagentsPath, "workflows");
|
|
73695
74207
|
if (fs2.existsSync(workflowsPath)) {
|
|
73696
74208
|
let wfDirs;
|
|
73697
74209
|
try {
|
|
@@ -73701,7 +74213,7 @@ function startSubagentWatcher(config) {
|
|
|
73701
74213
|
}
|
|
73702
74214
|
for (const wfDir of wfDirs) {
|
|
73703
74215
|
try {
|
|
73704
|
-
const wfPath =
|
|
74216
|
+
const wfPath = join36(workflowsPath, wfDir);
|
|
73705
74217
|
if (!fs2.statSync(wfPath).isDirectory())
|
|
73706
74218
|
continue;
|
|
73707
74219
|
watchAndScan(wfPath);
|
|
@@ -73721,7 +74233,7 @@ function startSubagentWatcher(config) {
|
|
|
73721
74233
|
for (const e of entries) {
|
|
73722
74234
|
if (!e.startsWith("agent-") || !e.endsWith(".jsonl"))
|
|
73723
74235
|
continue;
|
|
73724
|
-
const filePath =
|
|
74236
|
+
const filePath = join36(subagentsPath, e);
|
|
73725
74237
|
if (knownFiles.has(filePath))
|
|
73726
74238
|
continue;
|
|
73727
74239
|
const agentId = e.slice("agent-".length, -".jsonl".length);
|
|
@@ -73815,29 +74327,29 @@ function startSubagentWatcher(config) {
|
|
|
73815
74327
|
|
|
73816
74328
|
// ../src/worktree/registry.ts
|
|
73817
74329
|
import {
|
|
73818
|
-
mkdirSync as
|
|
73819
|
-
writeFileSync as
|
|
73820
|
-
readFileSync as
|
|
74330
|
+
mkdirSync as mkdirSync26,
|
|
74331
|
+
writeFileSync as writeFileSync28,
|
|
74332
|
+
readFileSync as readFileSync36,
|
|
73821
74333
|
readdirSync as readdirSync7,
|
|
73822
74334
|
unlinkSync as unlinkSync16,
|
|
73823
|
-
existsSync as
|
|
73824
|
-
renameSync as
|
|
74335
|
+
existsSync as existsSync34,
|
|
74336
|
+
renameSync as renameSync12
|
|
73825
74337
|
} from "node:fs";
|
|
73826
|
-
import { join as
|
|
74338
|
+
import { join as join37, resolve as resolve8 } from "node:path";
|
|
73827
74339
|
import { homedir as homedir12 } from "node:os";
|
|
73828
74340
|
function registryDir() {
|
|
73829
|
-
return resolve8(process.env.SWITCHROOM_WORKTREE_DIR ??
|
|
74341
|
+
return resolve8(process.env.SWITCHROOM_WORKTREE_DIR ?? join37(homedir12(), ".switchroom", "worktrees"));
|
|
73830
74342
|
}
|
|
73831
74343
|
function recordPath(id) {
|
|
73832
|
-
return
|
|
74344
|
+
return join37(registryDir(), `${id}.json`);
|
|
73833
74345
|
}
|
|
73834
74346
|
function ensureDir3() {
|
|
73835
|
-
|
|
74347
|
+
mkdirSync26(registryDir(), { recursive: true });
|
|
73836
74348
|
}
|
|
73837
74349
|
function readRecord(id) {
|
|
73838
74350
|
const path2 = recordPath(id);
|
|
73839
74351
|
try {
|
|
73840
|
-
const raw =
|
|
74352
|
+
const raw = readFileSync36(path2, "utf8");
|
|
73841
74353
|
return JSON.parse(raw);
|
|
73842
74354
|
} catch {
|
|
73843
74355
|
return null;
|
|
@@ -73930,15 +74442,15 @@ function determineRestartReason(opts) {
|
|
|
73930
74442
|
init_boot_card();
|
|
73931
74443
|
|
|
73932
74444
|
// gateway/update-announce.ts
|
|
73933
|
-
import { existsSync as
|
|
73934
|
-
import { join as
|
|
74445
|
+
import { existsSync as existsSync39, mkdirSync as mkdirSync30, openSync as openSync6, closeSync as closeSync6, readFileSync as readFileSync42 } from "node:fs";
|
|
74446
|
+
import { join as join42 } from "node:path";
|
|
73935
74447
|
import { homedir as homedir14 } from "node:os";
|
|
73936
74448
|
|
|
73937
74449
|
// ../src/host-control/audit-reader.ts
|
|
73938
74450
|
import { homedir as homedir13 } from "node:os";
|
|
73939
|
-
import { join as
|
|
74451
|
+
import { join as join41 } from "node:path";
|
|
73940
74452
|
function defaultAuditLogPath(home2 = homedir13()) {
|
|
73941
|
-
return
|
|
74453
|
+
return join41(home2, ".switchroom", "host-control-audit.log");
|
|
73942
74454
|
}
|
|
73943
74455
|
function parseAuditLine(line) {
|
|
73944
74456
|
const trimmed = line.trim();
|
|
@@ -74059,8 +74571,8 @@ function readAndFilter(raw, filters, limit) {
|
|
|
74059
74571
|
var DEFAULT_LOOKBACK_MS = 10 * 60 * 1000;
|
|
74060
74572
|
function readLastTerminalUpdateAudit(opts = {}) {
|
|
74061
74573
|
const path2 = opts.auditLogPath ?? defaultAuditLogPath();
|
|
74062
|
-
const exists = opts.exists ??
|
|
74063
|
-
const readFile = opts.readFile ?? ((p) =>
|
|
74574
|
+
const exists = opts.exists ?? existsSync39;
|
|
74575
|
+
const readFile = opts.readFile ?? ((p) => readFileSync42(p, "utf-8"));
|
|
74064
74576
|
if (!exists(path2))
|
|
74065
74577
|
return null;
|
|
74066
74578
|
let raw;
|
|
@@ -74121,15 +74633,15 @@ function renderUpdateOutcomeLine(entry) {
|
|
|
74121
74633
|
`);
|
|
74122
74634
|
}
|
|
74123
74635
|
function claimUpdateAnnouncement(requestId, opts = {}) {
|
|
74124
|
-
const stateDir = opts.stateDir ?? process.env.TELEGRAM_STATE_DIR ??
|
|
74125
|
-
const dir =
|
|
74636
|
+
const stateDir = opts.stateDir ?? process.env.TELEGRAM_STATE_DIR ?? join42(homedir14(), ".switchroom");
|
|
74637
|
+
const dir = join42(stateDir, "update-announced");
|
|
74126
74638
|
try {
|
|
74127
|
-
|
|
74639
|
+
mkdirSync30(dir, { recursive: true });
|
|
74128
74640
|
} catch {
|
|
74129
74641
|
return false;
|
|
74130
74642
|
}
|
|
74131
74643
|
const safeId = requestId.replace(/[^A-Za-z0-9_.-]/g, "_").slice(0, 200);
|
|
74132
|
-
const path2 =
|
|
74644
|
+
const path2 = join42(dir, safeId);
|
|
74133
74645
|
try {
|
|
74134
74646
|
const fd = openSync6(path2, "wx");
|
|
74135
74647
|
closeSync6(fd);
|
|
@@ -74149,7 +74661,7 @@ function maybeRenderUpdateAnnouncement(opts = {}) {
|
|
|
74149
74661
|
|
|
74150
74662
|
// issues-card.ts
|
|
74151
74663
|
init_card_format();
|
|
74152
|
-
import { readFileSync as
|
|
74664
|
+
import { readFileSync as readFileSync43, writeFileSync as writeFileSync33 } from "node:fs";
|
|
74153
74665
|
var SEVERITY_EMOJI = {
|
|
74154
74666
|
info: "\u2139\ufe0f",
|
|
74155
74667
|
warn: "\u26a0\ufe0f",
|
|
@@ -74241,7 +74753,7 @@ function extractRetryAfterSecs2(err) {
|
|
|
74241
74753
|
var COOLDOWN_JITTER_MS2 = 500;
|
|
74242
74754
|
function readPersistedMessageId(path2, log) {
|
|
74243
74755
|
try {
|
|
74244
|
-
const raw =
|
|
74756
|
+
const raw = readFileSync43(path2, "utf8");
|
|
74245
74757
|
const parsed = JSON.parse(raw);
|
|
74246
74758
|
const v = parsed.messageId;
|
|
74247
74759
|
if (typeof v === "number" && Number.isInteger(v) && v > 0)
|
|
@@ -74257,7 +74769,7 @@ function readPersistedMessageId(path2, log) {
|
|
|
74257
74769
|
}
|
|
74258
74770
|
function writePersistedMessageId(path2, messageId, log) {
|
|
74259
74771
|
try {
|
|
74260
|
-
|
|
74772
|
+
writeFileSync33(path2, JSON.stringify({ messageId }) + `
|
|
74261
74773
|
`, { mode: 384 });
|
|
74262
74774
|
} catch (err) {
|
|
74263
74775
|
log(`issues-card: persist write failed (${err.message})`);
|
|
@@ -74350,24 +74862,24 @@ function createIssuesCardHandle(opts) {
|
|
|
74350
74862
|
}
|
|
74351
74863
|
|
|
74352
74864
|
// issues-watcher.ts
|
|
74353
|
-
import { existsSync as
|
|
74354
|
-
import { join as
|
|
74865
|
+
import { existsSync as existsSync41, statSync as statSync12 } from "node:fs";
|
|
74866
|
+
import { join as join44 } from "node:path";
|
|
74355
74867
|
|
|
74356
74868
|
// ../src/issues/store.ts
|
|
74357
74869
|
import {
|
|
74358
74870
|
closeSync as closeSync7,
|
|
74359
|
-
existsSync as
|
|
74360
|
-
mkdirSync as
|
|
74871
|
+
existsSync as existsSync40,
|
|
74872
|
+
mkdirSync as mkdirSync31,
|
|
74361
74873
|
openSync as openSync7,
|
|
74362
74874
|
readdirSync as readdirSync9,
|
|
74363
|
-
readFileSync as
|
|
74364
|
-
renameSync as
|
|
74875
|
+
readFileSync as readFileSync44,
|
|
74876
|
+
renameSync as renameSync15,
|
|
74365
74877
|
statSync as statSync11,
|
|
74366
74878
|
unlinkSync as unlinkSync17,
|
|
74367
|
-
writeFileSync as
|
|
74879
|
+
writeFileSync as writeFileSync34,
|
|
74368
74880
|
writeSync as writeSync4
|
|
74369
74881
|
} from "node:fs";
|
|
74370
|
-
import { join as
|
|
74882
|
+
import { join as join43 } from "node:path";
|
|
74371
74883
|
import { randomBytes as randomBytes6 } from "node:crypto";
|
|
74372
74884
|
import { execSync } from "node:child_process";
|
|
74373
74885
|
|
|
@@ -74386,12 +74898,12 @@ init_redact();
|
|
|
74386
74898
|
var ISSUES_FILE = "issues.jsonl";
|
|
74387
74899
|
var ISSUES_LOCK = "issues.lock";
|
|
74388
74900
|
function readAll(stateDir) {
|
|
74389
|
-
const path2 =
|
|
74390
|
-
if (!
|
|
74901
|
+
const path2 = join43(stateDir, ISSUES_FILE);
|
|
74902
|
+
if (!existsSync40(path2))
|
|
74391
74903
|
return [];
|
|
74392
74904
|
let raw;
|
|
74393
74905
|
try {
|
|
74394
|
-
raw =
|
|
74906
|
+
raw = readFileSync44(path2, "utf-8");
|
|
74395
74907
|
} catch {
|
|
74396
74908
|
return [];
|
|
74397
74909
|
}
|
|
@@ -74423,7 +74935,7 @@ function list2(stateDir, opts = {}) {
|
|
|
74423
74935
|
});
|
|
74424
74936
|
}
|
|
74425
74937
|
function resolve9(stateDir, fingerprint, nowFn = Date.now) {
|
|
74426
|
-
if (!
|
|
74938
|
+
if (!existsSync40(join43(stateDir, ISSUES_FILE)))
|
|
74427
74939
|
return 0;
|
|
74428
74940
|
return withLock(stateDir, () => {
|
|
74429
74941
|
const all2 = readAll(stateDir);
|
|
@@ -74441,14 +74953,14 @@ function resolve9(stateDir, fingerprint, nowFn = Date.now) {
|
|
|
74441
74953
|
});
|
|
74442
74954
|
}
|
|
74443
74955
|
function writeAll(stateDir, events) {
|
|
74444
|
-
const path2 =
|
|
74956
|
+
const path2 = join43(stateDir, ISSUES_FILE);
|
|
74445
74957
|
sweepOrphanTmpFiles(stateDir);
|
|
74446
74958
|
const tmp = `${path2}.tmp-${process.pid}-${randomBytes6(4).toString("hex")}`;
|
|
74447
74959
|
const body = events.length === 0 ? "" : events.map((e) => JSON.stringify(e)).join(`
|
|
74448
74960
|
`) + `
|
|
74449
74961
|
`;
|
|
74450
|
-
|
|
74451
|
-
|
|
74962
|
+
writeFileSync34(tmp, body, "utf-8");
|
|
74963
|
+
renameSync15(tmp, path2);
|
|
74452
74964
|
}
|
|
74453
74965
|
var ORPHAN_TMP_TTL_MS = 60000;
|
|
74454
74966
|
var TMP_PREFIX = `${ISSUES_FILE}.tmp-`;
|
|
@@ -74463,7 +74975,7 @@ function sweepOrphanTmpFiles(stateDir) {
|
|
|
74463
74975
|
for (const entry of entries) {
|
|
74464
74976
|
if (!entry.startsWith(TMP_PREFIX))
|
|
74465
74977
|
continue;
|
|
74466
|
-
const tmpPath2 =
|
|
74978
|
+
const tmpPath2 = join43(stateDir, entry);
|
|
74467
74979
|
try {
|
|
74468
74980
|
const stat = statSync11(tmpPath2);
|
|
74469
74981
|
if (stat.mtimeMs < cutoff) {
|
|
@@ -74475,7 +74987,7 @@ function sweepOrphanTmpFiles(stateDir) {
|
|
|
74475
74987
|
var LOCK_RETRY_MS = 25;
|
|
74476
74988
|
var LOCK_TIMEOUT_MS = 1e4;
|
|
74477
74989
|
function withLock(stateDir, fn) {
|
|
74478
|
-
const lockPath =
|
|
74990
|
+
const lockPath = join43(stateDir, ISSUES_LOCK);
|
|
74479
74991
|
const startedAt = Date.now();
|
|
74480
74992
|
let fd = null;
|
|
74481
74993
|
while (fd === null) {
|
|
@@ -74510,7 +75022,7 @@ function withLock(stateDir, fn) {
|
|
|
74510
75022
|
function tryStealStaleLock(lockPath) {
|
|
74511
75023
|
let pidStr;
|
|
74512
75024
|
try {
|
|
74513
|
-
pidStr =
|
|
75025
|
+
pidStr = readFileSync44(lockPath, "utf-8").trim();
|
|
74514
75026
|
} catch {
|
|
74515
75027
|
return true;
|
|
74516
75028
|
}
|
|
@@ -74560,7 +75072,7 @@ function isIssueEvent(v) {
|
|
|
74560
75072
|
// issues-watcher.ts
|
|
74561
75073
|
var DEFAULT_POLL_INTERVAL_MS2 = 2000;
|
|
74562
75074
|
function startIssuesWatcher(opts) {
|
|
74563
|
-
const path2 =
|
|
75075
|
+
const path2 = join44(opts.stateDir, ISSUES_FILE);
|
|
74564
75076
|
const log = opts.log ?? (() => {});
|
|
74565
75077
|
const intervalMs = opts.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS2;
|
|
74566
75078
|
const setIntervalFn = opts.setInterval ?? setInterval;
|
|
@@ -74608,7 +75120,7 @@ function startIssuesWatcher(opts) {
|
|
|
74608
75120
|
};
|
|
74609
75121
|
}
|
|
74610
75122
|
function defaultSignatureProvider(path2) {
|
|
74611
|
-
if (!
|
|
75123
|
+
if (!existsSync41(path2))
|
|
74612
75124
|
return null;
|
|
74613
75125
|
try {
|
|
74614
75126
|
const stat = statSync12(path2);
|
|
@@ -75364,8 +75876,8 @@ function readBashCommand(inputPreview) {
|
|
|
75364
75876
|
}
|
|
75365
75877
|
|
|
75366
75878
|
// gateway/scoped-grant-store.ts
|
|
75367
|
-
import { readFileSync as
|
|
75368
|
-
import { join as
|
|
75879
|
+
import { readFileSync as readFileSync45, writeFileSync as writeFileSync35 } from "node:fs";
|
|
75880
|
+
import { join as join45 } from "node:path";
|
|
75369
75881
|
|
|
75370
75882
|
// scoped-approval.ts
|
|
75371
75883
|
var SCOPED_APPROVAL_DEFAULT_TTL_MS2 = 30 * 60 * 1000;
|
|
@@ -75407,11 +75919,11 @@ function scopedGrantPersistEnabled(env = process.env) {
|
|
|
75407
75919
|
return env.SWITCHROOM_SCOPED_GRANT_PERSIST !== "0";
|
|
75408
75920
|
}
|
|
75409
75921
|
function createScopedGrantStore(stateDir, env = process.env) {
|
|
75410
|
-
const filePath =
|
|
75922
|
+
const filePath = join45(stateDir, "scoped-grants.json");
|
|
75411
75923
|
const enabled8 = scopedGrantPersistEnabled(env);
|
|
75412
75924
|
function read() {
|
|
75413
75925
|
try {
|
|
75414
|
-
const raw =
|
|
75926
|
+
const raw = readFileSync45(filePath, "utf-8");
|
|
75415
75927
|
const parsed = JSON.parse(raw);
|
|
75416
75928
|
return Array.isArray(parsed) ? parsed : [];
|
|
75417
75929
|
} catch {
|
|
@@ -75429,7 +75941,7 @@ function createScopedGrantStore(stateDir, env = process.env) {
|
|
|
75429
75941
|
if (!enabled8)
|
|
75430
75942
|
return;
|
|
75431
75943
|
try {
|
|
75432
|
-
|
|
75944
|
+
writeFileSync35(filePath, JSON.stringify(serializeScopedGrants(store2)), {
|
|
75433
75945
|
encoding: "utf-8",
|
|
75434
75946
|
mode: 384
|
|
75435
75947
|
});
|
|
@@ -75763,8 +76275,8 @@ function extractFlowItems(line) {
|
|
|
75763
76275
|
|
|
75764
76276
|
// credits-watch.ts
|
|
75765
76277
|
init_card_format();
|
|
75766
|
-
import { readFileSync as
|
|
75767
|
-
import { join as
|
|
76278
|
+
import { readFileSync as readFileSync46, writeFileSync as writeFileSync36, existsSync as existsSync42, mkdirSync as mkdirSync32 } from "fs";
|
|
76279
|
+
import { join as join46 } from "path";
|
|
75768
76280
|
var STATE_FILE = "credits-watch.json";
|
|
75769
76281
|
var DEFAULT_CREDIT_FATAL_REASONS = new Set;
|
|
75770
76282
|
var KNOWN_CREDIT_REASONS = [
|
|
@@ -75786,12 +76298,12 @@ function emptyCreditState() {
|
|
|
75786
76298
|
return { lastNotifiedReason: null, lastNotifiedAt: 0 };
|
|
75787
76299
|
}
|
|
75788
76300
|
function readClaudeJsonOverage(claudeConfigDir) {
|
|
75789
|
-
const path2 =
|
|
75790
|
-
if (!
|
|
76301
|
+
const path2 = join46(claudeConfigDir, ".claude.json");
|
|
76302
|
+
if (!existsSync42(path2))
|
|
75791
76303
|
return null;
|
|
75792
76304
|
let raw;
|
|
75793
76305
|
try {
|
|
75794
|
-
raw =
|
|
76306
|
+
raw = readFileSync46(path2, "utf-8");
|
|
75795
76307
|
} catch {
|
|
75796
76308
|
return null;
|
|
75797
76309
|
}
|
|
@@ -75869,11 +76381,11 @@ function humanizeReason(reason) {
|
|
|
75869
76381
|
}
|
|
75870
76382
|
}
|
|
75871
76383
|
function loadCreditState(stateDir) {
|
|
75872
|
-
const path2 =
|
|
75873
|
-
if (!
|
|
76384
|
+
const path2 = join46(stateDir, STATE_FILE);
|
|
76385
|
+
if (!existsSync42(path2))
|
|
75874
76386
|
return emptyCreditState();
|
|
75875
76387
|
try {
|
|
75876
|
-
const raw =
|
|
76388
|
+
const raw = readFileSync46(path2, "utf-8");
|
|
75877
76389
|
const parsed = JSON.parse(raw);
|
|
75878
76390
|
if (parsed && typeof parsed === "object" && (parsed.lastNotifiedReason === null || typeof parsed.lastNotifiedReason === "string") && typeof parsed.lastNotifiedAt === "number" && Number.isFinite(parsed.lastNotifiedAt)) {
|
|
75879
76391
|
return {
|
|
@@ -75885,17 +76397,17 @@ function loadCreditState(stateDir) {
|
|
|
75885
76397
|
return emptyCreditState();
|
|
75886
76398
|
}
|
|
75887
76399
|
function saveCreditState(stateDir, state4) {
|
|
75888
|
-
|
|
75889
|
-
const path2 =
|
|
75890
|
-
|
|
76400
|
+
mkdirSync32(stateDir, { recursive: true });
|
|
76401
|
+
const path2 = join46(stateDir, STATE_FILE);
|
|
76402
|
+
writeFileSync36(path2, JSON.stringify(state4, null, 2) + `
|
|
75891
76403
|
`, { mode: 384 });
|
|
75892
76404
|
}
|
|
75893
76405
|
|
|
75894
76406
|
// quota-watch.ts
|
|
75895
76407
|
init_auth_snapshot_format();
|
|
75896
76408
|
init_card_format();
|
|
75897
|
-
import { readFileSync as
|
|
75898
|
-
import { join as
|
|
76409
|
+
import { readFileSync as readFileSync47, writeFileSync as writeFileSync37, existsSync as existsSync43, mkdirSync as mkdirSync33 } from "fs";
|
|
76410
|
+
import { join as join47 } from "path";
|
|
75899
76411
|
var STATE_FILE2 = "quota-watch.json";
|
|
75900
76412
|
function emptyQuotaWatchState() {
|
|
75901
76413
|
return {};
|
|
@@ -76088,11 +76600,11 @@ function buildRecoveryMessage(agentName3, snap) {
|
|
|
76088
76600
|
`);
|
|
76089
76601
|
}
|
|
76090
76602
|
function loadQuotaWatchState(stateDir) {
|
|
76091
|
-
const path2 =
|
|
76092
|
-
if (!
|
|
76603
|
+
const path2 = join47(stateDir, STATE_FILE2);
|
|
76604
|
+
if (!existsSync43(path2))
|
|
76093
76605
|
return emptyQuotaWatchState();
|
|
76094
76606
|
try {
|
|
76095
|
-
const raw =
|
|
76607
|
+
const raw = readFileSync47(path2, "utf-8");
|
|
76096
76608
|
const parsed = JSON.parse(raw);
|
|
76097
76609
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
76098
76610
|
return emptyQuotaWatchState();
|
|
@@ -76109,9 +76621,9 @@ function loadQuotaWatchState(stateDir) {
|
|
|
76109
76621
|
}
|
|
76110
76622
|
}
|
|
76111
76623
|
function saveQuotaWatchState(stateDir, state4) {
|
|
76112
|
-
|
|
76113
|
-
const path2 =
|
|
76114
|
-
|
|
76624
|
+
mkdirSync33(stateDir, { recursive: true });
|
|
76625
|
+
const path2 = join47(stateDir, STATE_FILE2);
|
|
76626
|
+
writeFileSync37(path2, JSON.stringify(state4, null, 2) + `
|
|
76115
76627
|
`, { mode: 384 });
|
|
76116
76628
|
}
|
|
76117
76629
|
function patchQuotaWatchState(current, accountLabel, accountState) {
|
|
@@ -76141,27 +76653,27 @@ function maskVaultKey(name) {
|
|
|
76141
76653
|
// gateway/turn-active-marker.ts
|
|
76142
76654
|
import {
|
|
76143
76655
|
closeSync as closeSync8,
|
|
76144
|
-
existsSync as
|
|
76145
|
-
mkdirSync as
|
|
76656
|
+
existsSync as existsSync44,
|
|
76657
|
+
mkdirSync as mkdirSync34,
|
|
76146
76658
|
openSync as openSync8,
|
|
76147
|
-
readFileSync as
|
|
76659
|
+
readFileSync as readFileSync48,
|
|
76148
76660
|
statSync as statSync13,
|
|
76149
76661
|
unlinkSync as unlinkSync18,
|
|
76150
76662
|
utimesSync as utimesSync2,
|
|
76151
|
-
writeFileSync as
|
|
76663
|
+
writeFileSync as writeFileSync38
|
|
76152
76664
|
} from "node:fs";
|
|
76153
|
-
import { join as
|
|
76665
|
+
import { join as join48 } from "node:path";
|
|
76154
76666
|
var TURN_ACTIVE_MARKER_FILE2 = "turn-active.json";
|
|
76155
76667
|
function writeTurnActiveMarker(stateDir, marker) {
|
|
76156
76668
|
try {
|
|
76157
|
-
|
|
76158
|
-
|
|
76669
|
+
mkdirSync34(stateDir, { recursive: true });
|
|
76670
|
+
writeFileSync38(join48(stateDir, TURN_ACTIVE_MARKER_FILE2), JSON.stringify(marker, null, 2) + `
|
|
76159
76671
|
`, { mode: 384 });
|
|
76160
76672
|
} catch {}
|
|
76161
76673
|
}
|
|
76162
76674
|
function touchTurnActiveMarker2(stateDir) {
|
|
76163
|
-
const path2 =
|
|
76164
|
-
if (!
|
|
76675
|
+
const path2 = join48(stateDir, TURN_ACTIVE_MARKER_FILE2);
|
|
76676
|
+
if (!existsSync44(path2))
|
|
76165
76677
|
return;
|
|
76166
76678
|
const now = new Date;
|
|
76167
76679
|
try {
|
|
@@ -76175,12 +76687,12 @@ function touchTurnActiveMarker2(stateDir) {
|
|
|
76175
76687
|
}
|
|
76176
76688
|
function removeTurnActiveMarker(stateDir) {
|
|
76177
76689
|
try {
|
|
76178
|
-
unlinkSync18(
|
|
76690
|
+
unlinkSync18(join48(stateDir, TURN_ACTIVE_MARKER_FILE2));
|
|
76179
76691
|
} catch {}
|
|
76180
76692
|
}
|
|
76181
76693
|
function sweepStaleTurnActiveMarker(stateDir, opts) {
|
|
76182
|
-
const path2 =
|
|
76183
|
-
if (!
|
|
76694
|
+
const path2 = join48(stateDir, TURN_ACTIVE_MARKER_FILE2);
|
|
76695
|
+
if (!existsSync44(path2))
|
|
76184
76696
|
return false;
|
|
76185
76697
|
const now = opts.now ?? Date.now();
|
|
76186
76698
|
try {
|
|
@@ -76192,7 +76704,7 @@ function sweepStaleTurnActiveMarker(stateDir, opts) {
|
|
|
76192
76704
|
return false;
|
|
76193
76705
|
let payload = null;
|
|
76194
76706
|
try {
|
|
76195
|
-
payload =
|
|
76707
|
+
payload = readFileSync48(path2, "utf8");
|
|
76196
76708
|
} catch {}
|
|
76197
76709
|
unlinkSync18(path2);
|
|
76198
76710
|
if (opts.onRemove) {
|
|
@@ -76210,7 +76722,7 @@ function sweepStaleTurnActiveMarker(stateDir, opts) {
|
|
|
76210
76722
|
}
|
|
76211
76723
|
}
|
|
76212
76724
|
function readTurnActiveMarkerAgeMs(stateDir, now) {
|
|
76213
|
-
const path2 =
|
|
76725
|
+
const path2 = join48(stateDir, TURN_ACTIVE_MARKER_FILE2);
|
|
76214
76726
|
try {
|
|
76215
76727
|
const st = statSync13(path2);
|
|
76216
76728
|
return (now ?? Date.now()) - st.mtimeMs;
|
|
@@ -76220,10 +76732,10 @@ function readTurnActiveMarkerAgeMs(stateDir, now) {
|
|
|
76220
76732
|
}
|
|
76221
76733
|
|
|
76222
76734
|
// ../src/build-info.ts
|
|
76223
|
-
var VERSION = "0.18.
|
|
76224
|
-
var COMMIT_SHA = "
|
|
76225
|
-
var COMMIT_DATE = "2026-07-
|
|
76226
|
-
var LATEST_PR =
|
|
76735
|
+
var VERSION = "0.18.8";
|
|
76736
|
+
var COMMIT_SHA = "9255fe22";
|
|
76737
|
+
var COMMIT_DATE = "2026-07-10T12:33:35Z";
|
|
76738
|
+
var LATEST_PR = 3004;
|
|
76227
76739
|
var COMMITS_AHEAD_OF_TAG = 0;
|
|
76228
76740
|
|
|
76229
76741
|
// gateway/boot-version.ts
|
|
@@ -76297,11 +76809,11 @@ init_peercred();
|
|
|
76297
76809
|
import * as net5 from "node:net";
|
|
76298
76810
|
import * as fs2 from "node:fs";
|
|
76299
76811
|
import { homedir as homedir15 } from "node:os";
|
|
76300
|
-
import { join as
|
|
76812
|
+
import { join as join49 } from "node:path";
|
|
76301
76813
|
var DEFAULT_TIMEOUT_MS4 = 2000;
|
|
76302
76814
|
var UNLOCK_TIMEOUT_MS = 30000;
|
|
76303
|
-
var LEGACY_SOCKET_PATH2 =
|
|
76304
|
-
var OPERATOR_SOCKET_PATH2 =
|
|
76815
|
+
var LEGACY_SOCKET_PATH2 = join49(homedir15(), ".switchroom", "vault-broker.sock");
|
|
76816
|
+
var OPERATOR_SOCKET_PATH2 = join49(homedir15(), ".switchroom", "broker-operator", "sock");
|
|
76305
76817
|
function defaultBrokerSocketPath2() {
|
|
76306
76818
|
if (fs2.existsSync(OPERATOR_SOCKET_PATH2))
|
|
76307
76819
|
return OPERATOR_SOCKET_PATH2;
|
|
@@ -77231,8 +77743,8 @@ function matchesAdminOnlyKey(key, patterns) {
|
|
|
77231
77743
|
}
|
|
77232
77744
|
|
|
77233
77745
|
// registry/turns-schema.ts
|
|
77234
|
-
import { chmodSync as chmodSync7, mkdirSync as
|
|
77235
|
-
import { join as
|
|
77746
|
+
import { chmodSync as chmodSync7, mkdirSync as mkdirSync35 } from "fs";
|
|
77747
|
+
import { join as join50 } from "path";
|
|
77236
77748
|
var DatabaseClass2 = null;
|
|
77237
77749
|
function loadDatabaseClass2() {
|
|
77238
77750
|
if (DatabaseClass2 != null)
|
|
@@ -77300,9 +77812,9 @@ function applySchema(db2) {
|
|
|
77300
77812
|
}
|
|
77301
77813
|
function openTurnsDb(agentDir) {
|
|
77302
77814
|
const Database = loadDatabaseClass2();
|
|
77303
|
-
const dir =
|
|
77304
|
-
|
|
77305
|
-
const path2 =
|
|
77815
|
+
const dir = join50(agentDir, "telegram");
|
|
77816
|
+
mkdirSync35(dir, { recursive: true, mode: 448 });
|
|
77817
|
+
const path2 = join50(dir, "registry.db");
|
|
77306
77818
|
const db2 = new Database(path2, { create: true });
|
|
77307
77819
|
applySchema(db2);
|
|
77308
77820
|
try {
|
|
@@ -77757,7 +78269,7 @@ installGlobalErrorHandlers();
|
|
|
77757
78269
|
process.on("beforeExit", () => {
|
|
77758
78270
|
shutdownAnalytics();
|
|
77759
78271
|
});
|
|
77760
|
-
var STATE_DIR = process.env.TELEGRAM_STATE_DIR ??
|
|
78272
|
+
var STATE_DIR = process.env.TELEGRAM_STATE_DIR ?? join51(homedir16(), ".claude", "channels", "telegram");
|
|
77761
78273
|
var permCardStore = createPermissionCardStore(STATE_DIR);
|
|
77762
78274
|
var pendingCardStore = createPendingCardStore(STATE_DIR);
|
|
77763
78275
|
var missedApprovalsStore = createMissedApprovalsStore(STATE_DIR);
|
|
@@ -77776,7 +78288,7 @@ function alwaysAllowDrainDeps() {
|
|
|
77776
78288
|
return {
|
|
77777
78289
|
readConfigText: () => {
|
|
77778
78290
|
const cfgPath = process.env.SWITCHROOM_CONFIG ?? SWITCHROOM_CONFIG ?? findConfigFile2();
|
|
77779
|
-
return
|
|
78291
|
+
return readFileSync50(cfgPath, "utf8");
|
|
77780
78292
|
},
|
|
77781
78293
|
resolveAllowList: (_configText, agentName3) => {
|
|
77782
78294
|
const cfg = loadConfig2();
|
|
@@ -77837,11 +78349,11 @@ function scheduleAlwaysAllowPersistDrain() {
|
|
|
77837
78349
|
}, ALWAYS_ALLOW_DRAIN_INTERVAL_MS);
|
|
77838
78350
|
timer3.unref?.();
|
|
77839
78351
|
}
|
|
77840
|
-
var ACCESS_FILE =
|
|
77841
|
-
var APPROVED_DIR =
|
|
77842
|
-
var ENV_FILE =
|
|
77843
|
-
var INBOX_DIR =
|
|
77844
|
-
var PEOPLE_FILE =
|
|
78352
|
+
var ACCESS_FILE = join51(STATE_DIR, "access.json");
|
|
78353
|
+
var APPROVED_DIR = join51(STATE_DIR, "approved");
|
|
78354
|
+
var ENV_FILE = join51(STATE_DIR, ".env");
|
|
78355
|
+
var INBOX_DIR = join51(STATE_DIR, "inbox");
|
|
78356
|
+
var PEOPLE_FILE = join51(STATE_DIR, "people.json");
|
|
77845
78357
|
function triggerSelfRestart(targetAgent, reason, delayMs = 300) {
|
|
77846
78358
|
const isDocker = process.env.SWITCHROOM_RUNTIME === "docker";
|
|
77847
78359
|
const selfAgent = process.env.SWITCHROOM_AGENT_NAME;
|
|
@@ -77851,6 +78363,11 @@ function triggerSelfRestart(targetAgent, reason, delayMs = 300) {
|
|
|
77851
78363
|
`);
|
|
77852
78364
|
return false;
|
|
77853
78365
|
}
|
|
78366
|
+
{
|
|
78367
|
+
const smDir = resolveAgentDirFromEnv();
|
|
78368
|
+
if (smDir)
|
|
78369
|
+
writeRelaunchModelIntent(smDir, intentForRestartReason(reason), reason);
|
|
78370
|
+
}
|
|
77854
78371
|
process.stderr.write(`telegram gateway: restart-via-SIGTERM-PID1 agent=${targetAgent} reason=${reason} (docker)
|
|
77855
78372
|
`);
|
|
77856
78373
|
setTimeout(() => {
|
|
@@ -77863,6 +78380,11 @@ function triggerSelfRestart(targetAgent, reason, delayMs = 300) {
|
|
|
77863
78380
|
}, delayMs).unref();
|
|
77864
78381
|
return true;
|
|
77865
78382
|
}
|
|
78383
|
+
if (targetAgent === selfAgent) {
|
|
78384
|
+
const smDir = resolveAgentDirFromEnv();
|
|
78385
|
+
if (smDir)
|
|
78386
|
+
writeRelaunchModelIntent(smDir, intentForRestartReason(reason), reason);
|
|
78387
|
+
}
|
|
77866
78388
|
process.stderr.write(`telegram gateway: restart-via-systemctl agent=${targetAgent} reason=${reason}
|
|
77867
78389
|
`);
|
|
77868
78390
|
try {
|
|
@@ -77906,7 +78428,7 @@ function formatBootVersion() {
|
|
|
77906
78428
|
}
|
|
77907
78429
|
try {
|
|
77908
78430
|
chmodSync8(ENV_FILE, 384);
|
|
77909
|
-
for (const line of
|
|
78431
|
+
for (const line of readFileSync50(ENV_FILE, "utf8").split(`
|
|
77910
78432
|
`)) {
|
|
77911
78433
|
const m = line.match(/^(\w+)=(.*)$/);
|
|
77912
78434
|
if (m && process.env[m[1]] === undefined)
|
|
@@ -77971,7 +78493,7 @@ bot.api.config.use(async (prev, method, payload, signal) => {
|
|
|
77971
78493
|
});
|
|
77972
78494
|
var GRAMMY_VERSION = (() => {
|
|
77973
78495
|
try {
|
|
77974
|
-
const raw =
|
|
78496
|
+
const raw = readFileSync50(new URL("../../node_modules/grammy/package.json", import.meta.url), "utf8");
|
|
77975
78497
|
return JSON.parse(raw).version ?? "unknown";
|
|
77976
78498
|
} catch {
|
|
77977
78499
|
return "unknown";
|
|
@@ -78038,7 +78560,7 @@ function assertSendable(f) {
|
|
|
78038
78560
|
} catch {
|
|
78039
78561
|
throw new Error(`refusing to send file \u2014 cannot resolve real path: ${f}`);
|
|
78040
78562
|
}
|
|
78041
|
-
const inbox =
|
|
78563
|
+
const inbox = join51(stateReal, "inbox");
|
|
78042
78564
|
if (real.startsWith(stateReal + sep3) && !real.startsWith(inbox + sep3)) {
|
|
78043
78565
|
throw new Error(`refusing to send channel state: ${f}`);
|
|
78044
78566
|
}
|
|
@@ -78057,7 +78579,7 @@ function assertSendable(f) {
|
|
|
78057
78579
|
}
|
|
78058
78580
|
function readAccessFile() {
|
|
78059
78581
|
try {
|
|
78060
|
-
const raw =
|
|
78582
|
+
const raw = readFileSync50(ACCESS_FILE, "utf8");
|
|
78061
78583
|
const parsed = JSON.parse(raw);
|
|
78062
78584
|
const allowFrom = validateStringArray("allowFrom", parsed.allowFrom ?? []);
|
|
78063
78585
|
const groups = {};
|
|
@@ -78095,7 +78617,7 @@ function readAccessFile() {
|
|
|
78095
78617
|
if (err.code === "ENOENT")
|
|
78096
78618
|
return defaultAccess();
|
|
78097
78619
|
try {
|
|
78098
|
-
|
|
78620
|
+
renameSync16(ACCESS_FILE, `${ACCESS_FILE}.corrupt-${Date.now()}`);
|
|
78099
78621
|
} catch {}
|
|
78100
78622
|
process.stderr.write(`telegram gateway: access.json is corrupt, moved aside. Starting fresh.
|
|
78101
78623
|
`);
|
|
@@ -78117,7 +78639,7 @@ function loadAccess() {
|
|
|
78117
78639
|
}
|
|
78118
78640
|
function readPeopleFile() {
|
|
78119
78641
|
try {
|
|
78120
|
-
const raw =
|
|
78642
|
+
const raw = readFileSync50(PEOPLE_FILE, "utf8");
|
|
78121
78643
|
const parsed = JSON.parse(raw);
|
|
78122
78644
|
if (!Array.isArray(parsed.entries))
|
|
78123
78645
|
return [];
|
|
@@ -78139,11 +78661,11 @@ function assertAllowedChat(chat_id) {
|
|
|
78139
78661
|
function saveAccess(a) {
|
|
78140
78662
|
if (STATIC)
|
|
78141
78663
|
return;
|
|
78142
|
-
|
|
78664
|
+
mkdirSync37(STATE_DIR, { recursive: true, mode: 448 });
|
|
78143
78665
|
const tmp = ACCESS_FILE + ".tmp";
|
|
78144
|
-
|
|
78666
|
+
writeFileSync40(tmp, JSON.stringify(a, null, 2) + `
|
|
78145
78667
|
`, { mode: 384 });
|
|
78146
|
-
|
|
78668
|
+
renameSync16(tmp, ACCESS_FILE);
|
|
78147
78669
|
}
|
|
78148
78670
|
function pruneExpired(a) {
|
|
78149
78671
|
const now = Date.now();
|
|
@@ -78161,7 +78683,7 @@ var HISTORY_ENABLED = HISTORY_ACCESS.historyEnabled !== false;
|
|
|
78161
78683
|
if (HISTORY_ENABLED) {
|
|
78162
78684
|
try {
|
|
78163
78685
|
initHistory(STATE_DIR, HISTORY_ACCESS.historyRetentionDays ?? 30);
|
|
78164
|
-
process.stderr.write(`telegram gateway: history capture enabled at ${
|
|
78686
|
+
process.stderr.write(`telegram gateway: history capture enabled at ${join51(STATE_DIR, "history.db")}
|
|
78165
78687
|
`);
|
|
78166
78688
|
} catch (err) {
|
|
78167
78689
|
process.stderr.write(`telegram gateway: history init failed (${err.message}) \u2014 capture disabled
|
|
@@ -78177,12 +78699,12 @@ try {
|
|
|
78177
78699
|
let markerTurnKey = null;
|
|
78178
78700
|
let markerAgeMs = null;
|
|
78179
78701
|
try {
|
|
78180
|
-
const markerPath =
|
|
78181
|
-
if (
|
|
78702
|
+
const markerPath = join51(STATE_DIR, TURN_ACTIVE_MARKER_FILE2);
|
|
78703
|
+
if (existsSync47(markerPath)) {
|
|
78182
78704
|
const st = statSync16(markerPath);
|
|
78183
78705
|
markerAgeMs = Date.now() - st.mtimeMs;
|
|
78184
78706
|
try {
|
|
78185
|
-
const payload = JSON.parse(
|
|
78707
|
+
const payload = JSON.parse(readFileSync50(markerPath, "utf8"));
|
|
78186
78708
|
if (typeof payload.turnKey === "string" && payload.turnKey.length > 0) {
|
|
78187
78709
|
markerTurnKey = payload.turnKey;
|
|
78188
78710
|
}
|
|
@@ -78202,13 +78724,13 @@ try {
|
|
|
78202
78724
|
process.stderr.write(`telegram gateway: turn-registry boot-reaper stamped ${reaped} orphaned turn(s)${timeoutTurnKey ? ` (turnKey=${timeoutTurnKey} as 'timeout', markerAgeMs=${markerAgeMs})` : " as 'restart'"}
|
|
78203
78725
|
`);
|
|
78204
78726
|
} else {
|
|
78205
|
-
process.stderr.write(`telegram gateway: turn-registry initialized at ${
|
|
78727
|
+
process.stderr.write(`telegram gateway: turn-registry initialized at ${join51(agentDir, "telegram", "registry.db")}
|
|
78206
78728
|
`);
|
|
78207
78729
|
}
|
|
78208
78730
|
const pending2 = findLatestTurnIfInterrupted(turnsDb);
|
|
78209
78731
|
const selfAgent = process.env.SWITCHROOM_AGENT_NAME ?? "";
|
|
78210
78732
|
if (pending2 != null && selfAgent) {
|
|
78211
|
-
const bootResumeMarkerPath = process.env.SWITCHROOM_GATEWAY_CLEAN_SHUTDOWN_MARKER ??
|
|
78733
|
+
const bootResumeMarkerPath = process.env.SWITCHROOM_GATEWAY_CLEAN_SHUTDOWN_MARKER ?? join51(STATE_DIR, "clean-shutdown.json");
|
|
78212
78734
|
const bootResumeCleanMarker = readCleanShutdownMarker(bootResumeMarkerPath);
|
|
78213
78735
|
const bootResumeForceAlways = process.env.SWITCHROOM_BOOT_RESUME_ALWAYS === "1";
|
|
78214
78736
|
const bootResumeMode = parseBootResumeMode(process.env.SWITCHROOM_BOOT_RESUME);
|
|
@@ -78275,7 +78797,7 @@ try {
|
|
|
78275
78797
|
`);
|
|
78276
78798
|
}
|
|
78277
78799
|
}
|
|
78278
|
-
const pendingEnvPath =
|
|
78800
|
+
const pendingEnvPath = join51(agentDir, ".pending-turn.env");
|
|
78279
78801
|
try {
|
|
78280
78802
|
if (pending2 != null) {
|
|
78281
78803
|
const lines = [
|
|
@@ -78289,14 +78811,14 @@ try {
|
|
|
78289
78811
|
pending2.interrupt_reason != null ? `SWITCHROOM_PENDING_INTERRUPT_REASON=${pending2.interrupt_reason}` : `SWITCHROOM_PENDING_INTERRUPT_REASON=`
|
|
78290
78812
|
];
|
|
78291
78813
|
const pendingEnvTmp = `${pendingEnvPath}.tmp-${process.pid}`;
|
|
78292
|
-
|
|
78814
|
+
writeFileSync40(pendingEnvTmp, lines.join(`
|
|
78293
78815
|
`) + `
|
|
78294
78816
|
`, { mode: 384 });
|
|
78295
|
-
|
|
78817
|
+
renameSync16(pendingEnvTmp, pendingEnvPath);
|
|
78296
78818
|
process.stderr.write(`telegram gateway: pending-turn env written to ${pendingEnvPath} turnKey=${pending2.turn_key} endedVia=${pending2.ended_via ?? "open"}
|
|
78297
78819
|
`);
|
|
78298
|
-
} else if (
|
|
78299
|
-
|
|
78820
|
+
} else if (existsSync47(pendingEnvPath)) {
|
|
78821
|
+
rmSync5(pendingEnvPath, { force: true });
|
|
78300
78822
|
process.stderr.write(`telegram gateway: pending-turn env cleared (clean previous shutdown)
|
|
78301
78823
|
`);
|
|
78302
78824
|
}
|
|
@@ -78391,11 +78913,11 @@ function checkApprovals() {
|
|
|
78391
78913
|
return;
|
|
78392
78914
|
}
|
|
78393
78915
|
for (const senderId of files) {
|
|
78394
|
-
const file =
|
|
78395
|
-
bot.api.sendMessage(senderId, "Paired! Say hi to Claude.").then(() =>
|
|
78916
|
+
const file = join51(APPROVED_DIR, senderId);
|
|
78917
|
+
bot.api.sendMessage(senderId, "Paired! Say hi to Claude.").then(() => rmSync5(file, { force: true }), (err) => {
|
|
78396
78918
|
process.stderr.write(`telegram gateway: failed to send approval confirm: ${err}
|
|
78397
78919
|
`);
|
|
78398
|
-
|
|
78920
|
+
rmSync5(file, { force: true });
|
|
78399
78921
|
});
|
|
78400
78922
|
}
|
|
78401
78923
|
}
|
|
@@ -78408,6 +78930,8 @@ var chatThreadMap = new Map;
|
|
|
78408
78930
|
var activeStatusReactions = new Map;
|
|
78409
78931
|
var activeReactionMsgIds = new Map;
|
|
78410
78932
|
var queuedStatusMsgIds = new Map;
|
|
78933
|
+
var busyAckPostedKeys = new Set;
|
|
78934
|
+
var busyAckRecheckTimers = new Map;
|
|
78411
78935
|
var deferredDoneReactions = new DeferredDoneReactions({
|
|
78412
78936
|
countRunningWorkers: () => countRunningWorkers(),
|
|
78413
78937
|
getActive: (key) => activeStatusReactions.get(key),
|
|
@@ -78493,12 +79017,12 @@ function noteAgentOutputAt(key, ts) {
|
|
|
78493
79017
|
lastAgentOutputAt.delete(oldest);
|
|
78494
79018
|
}
|
|
78495
79019
|
}
|
|
78496
|
-
var OBLIGATION_STORE_PATH =
|
|
79020
|
+
var OBLIGATION_STORE_PATH = join51(STATE_DIR, "obligations.json");
|
|
78497
79021
|
var obligationStoreFs = {
|
|
78498
|
-
readFileSync: (p) =>
|
|
78499
|
-
writeFileSync: (p, d) =>
|
|
78500
|
-
renameSync: (a, b) =>
|
|
78501
|
-
existsSync: (p) =>
|
|
79022
|
+
readFileSync: (p) => readFileSync50(p, "utf8"),
|
|
79023
|
+
writeFileSync: (p, d) => writeFileSync40(p, d),
|
|
79024
|
+
renameSync: (a, b) => renameSync16(a, b),
|
|
79025
|
+
existsSync: (p) => existsSync47(p)
|
|
78502
79026
|
};
|
|
78503
79027
|
var obligationLedger = new ObligationLedger(OBLIGATION_REPRESENT_MAX, {
|
|
78504
79028
|
onChange: STATIC || !OBLIGATION_LEDGER_ENABLED ? undefined : (snapshot) => persistObligations(OBLIGATION_STORE_PATH, obligationStoreFs, snapshot)
|
|
@@ -78522,6 +79046,7 @@ var FRAMEWORK_ORIGIN_ROUTING_ENABLED = process.env.SWITCHROOM_FRAMEWORK_ORIGIN_R
|
|
|
78522
79046
|
var REPLY_TOPIC_AUTHORITY_ENABLED = process.env.SWITCHROOM_REPLY_TOPIC_AUTHORITY !== "0";
|
|
78523
79047
|
var TOPIC_FRAMING_ENABLED = process.env.SWITCHROOM_TOPIC_FRAMING !== "0";
|
|
78524
79048
|
var QUEUED_STATUS_UX_ENABLED = process.env.SWITCHROOM_QUEUED_STATUS_UX !== "0";
|
|
79049
|
+
var MIDFLIGHT_BUSY_ACK_ENABLED = process.env.SWITCHROOM_MIDFLIGHT_BUSY_ACK !== "0";
|
|
78525
79050
|
var FEED_REOPEN_AFTER_ACK_ENABLED = process.env.SWITCHROOM_FEED_REOPEN_AFTER_ACK !== "0";
|
|
78526
79051
|
var FEED_HEARTBEAT_ENABLED = process.env.SWITCHROOM_FEED_HEARTBEAT !== "0";
|
|
78527
79052
|
var FEED_HEARTBEAT_TICK_MS = 6000;
|
|
@@ -78816,13 +79341,11 @@ function postQueuedStatus(chatId, bufferedThread, inFlightThread) {
|
|
|
78816
79341
|
function promoteQueuedStatus(chatId, thread) {
|
|
78817
79342
|
if (!QUEUED_STATUS_UX_ENABLED)
|
|
78818
79343
|
return;
|
|
78819
|
-
if (thread == null)
|
|
78820
|
-
return;
|
|
78821
79344
|
const key = statusKey(chatId, thread);
|
|
78822
79345
|
const entry = queuedStatusMsgIds.get(key);
|
|
78823
79346
|
if (entry == null)
|
|
78824
79347
|
return;
|
|
78825
|
-
swallowingApiCall(() => bot.api.editMessageText(chatId, entry.messageId, "\u270D\uFE0F On it \u2014 replying now.", {}), { chat_id: chatId, verb: "queued-status.promote", threadId: thread });
|
|
79348
|
+
swallowingApiCall(() => bot.api.editMessageText(chatId, entry.messageId, "\u270D\uFE0F On it \u2014 replying now.", {}), { chat_id: chatId, verb: "queued-status.promote", ...thread != null ? { threadId: thread } : {} });
|
|
78826
79349
|
}
|
|
78827
79350
|
function reapQueuedStatus(chatId, thread) {
|
|
78828
79351
|
const key = statusKey(chatId, thread ?? null);
|
|
@@ -78832,6 +79355,65 @@ function reapQueuedStatus(chatId, thread) {
|
|
|
78832
79355
|
queuedStatusMsgIds.delete(key);
|
|
78833
79356
|
swallowingApiCall(() => bot.api.deleteMessage(chatId, entry.messageId), { chat_id: chatId, verb: "queued-status.reap", ...entry.threadId != null ? { threadId: entry.threadId } : {} });
|
|
78834
79357
|
}
|
|
79358
|
+
function maybePostBusyAck(gateDecision, chatId, threadId) {
|
|
79359
|
+
if (!MIDFLIGHT_BUSY_ACK_ENABLED)
|
|
79360
|
+
return;
|
|
79361
|
+
const key = statusKey(chatId, threadId);
|
|
79362
|
+
const inFlight = currentTurn;
|
|
79363
|
+
const inFlightKey = inFlight != null ? statusKey(inFlight.sessionChatId, inFlight.sessionThreadId) : key;
|
|
79364
|
+
const now = Date.now();
|
|
79365
|
+
const step = longestInFlightTool(inFlightKey, now);
|
|
79366
|
+
const midToolCall = toolFlightTracker.isMidToolCall();
|
|
79367
|
+
const stepAgeMs = step?.durationMs ?? null;
|
|
79368
|
+
const alreadyAcked = queuedStatusMsgIds.has(key) || busyAckPostedKeys.has(key);
|
|
79369
|
+
const fire = shouldPostBusyAck({ gateDecision, midToolCall, stepAgeMs, alreadyAcked });
|
|
79370
|
+
if (!fire) {
|
|
79371
|
+
if (midToolCall && !alreadyAcked && stepAgeMs != null && stepAgeMs < BUSY_ACK_STEP_AGE_THRESHOLD_MS && !busyAckRecheckTimers.has(key)) {
|
|
79372
|
+
const turnIdAtSchedule = inFlight?.turnId ?? null;
|
|
79373
|
+
const delayMs = BUSY_ACK_STEP_AGE_THRESHOLD_MS - stepAgeMs + 250;
|
|
79374
|
+
busyAckRecheckTimers.set(key, setTimeout(() => {
|
|
79375
|
+
busyAckRecheckTimers.delete(key);
|
|
79376
|
+
if (turnIdAtSchedule == null || currentTurn?.turnId !== turnIdAtSchedule)
|
|
79377
|
+
return;
|
|
79378
|
+
maybePostBusyAck(gateDecision, chatId, threadId);
|
|
79379
|
+
}, delayMs));
|
|
79380
|
+
}
|
|
79381
|
+
return;
|
|
79382
|
+
}
|
|
79383
|
+
const pendingRecheck = busyAckRecheckTimers.get(key);
|
|
79384
|
+
if (pendingRecheck != null) {
|
|
79385
|
+
clearTimeout(pendingRecheck);
|
|
79386
|
+
busyAckRecheckTimers.delete(key);
|
|
79387
|
+
}
|
|
79388
|
+
busyAckPostedKeys.add(key);
|
|
79389
|
+
const text5 = formatBusyAckText({
|
|
79390
|
+
gateDecision,
|
|
79391
|
+
toolName: step?.name ?? null,
|
|
79392
|
+
toolLabel: step?.label ?? null
|
|
79393
|
+
});
|
|
79394
|
+
process.stderr.write(`telegram gateway: mid-flight busy ack chat=${chatId} thread=${threadId ?? "-"} decision=${gateDecision} step=${step?.name ?? "-"} step_age_ms=${step?.durationMs ?? "-"}
|
|
79395
|
+
`);
|
|
79396
|
+
postBusyAck(chatId, threadId, text5);
|
|
79397
|
+
}
|
|
79398
|
+
function postBusyAck(chatId, threadId, text5) {
|
|
79399
|
+
const key = statusKey(chatId, threadId);
|
|
79400
|
+
if (queuedStatusMsgIds.has(key))
|
|
79401
|
+
return;
|
|
79402
|
+
(async () => {
|
|
79403
|
+
const sent = await swallowingApiCall(() => bot.api.sendMessage(chatId, text5, {
|
|
79404
|
+
...threadId != null ? { message_thread_id: threadId } : {},
|
|
79405
|
+
disable_notification: true
|
|
79406
|
+
}), { chat_id: chatId, verb: "busy-ack.post", ...threadId != null ? { threadId } : {} });
|
|
79407
|
+
const messageId = sent?.message_id;
|
|
79408
|
+
if (typeof messageId !== "number")
|
|
79409
|
+
return;
|
|
79410
|
+
if (queuedStatusMsgIds.has(key)) {
|
|
79411
|
+
swallowingApiCall(() => bot.api.deleteMessage(chatId, messageId), { chat_id: chatId, verb: "busy-ack.post-race-cleanup", ...threadId != null ? { threadId } : {} });
|
|
79412
|
+
return;
|
|
79413
|
+
}
|
|
79414
|
+
queuedStatusMsgIds.set(key, { chatId, threadId: threadId ?? null, messageId });
|
|
79415
|
+
})();
|
|
79416
|
+
}
|
|
78835
79417
|
var toolFlightTracker = new ToolFlightTracker;
|
|
78836
79418
|
var pendingDeferredInterrupt = null;
|
|
78837
79419
|
function cancelInterruptedObligation() {
|
|
@@ -78970,6 +79552,12 @@ function purgeReactionTracking(key, endingTurn) {
|
|
|
78970
79552
|
const pqThread = pqThreadPart === "_" || pqThreadPart === "" ? null : Number(pqThreadPart);
|
|
78971
79553
|
reapQueuedStatus(pqChatId, Number.isFinite(pqThread) ? pqThread : undefined);
|
|
78972
79554
|
}
|
|
79555
|
+
busyAckPostedKeys.delete(key);
|
|
79556
|
+
const busyAckRecheck = busyAckRecheckTimers.get(key);
|
|
79557
|
+
if (busyAckRecheck != null) {
|
|
79558
|
+
clearTimeout(busyAckRecheck);
|
|
79559
|
+
busyAckRecheckTimers.delete(key);
|
|
79560
|
+
}
|
|
78973
79561
|
claudeBusyKeys.delete(key);
|
|
78974
79562
|
claudeBusyKeySince.delete(key);
|
|
78975
79563
|
reactionTransitionCounts.delete(key);
|
|
@@ -79058,7 +79646,7 @@ function emitTurnRecord(turn, endedAt) {
|
|
|
79058
79646
|
return;
|
|
79059
79647
|
}
|
|
79060
79648
|
},
|
|
79061
|
-
rename: (from, to) =>
|
|
79649
|
+
rename: (from, to) => renameSync16(from, to)
|
|
79062
79650
|
});
|
|
79063
79651
|
appendFileSync6(turnsPath, rec);
|
|
79064
79652
|
} catch {}
|
|
@@ -79480,8 +80068,10 @@ var typingWrapper = createTypingWrapper({
|
|
|
79480
80068
|
stopTypingLoop,
|
|
79481
80069
|
isSurfaceTool: isTelegramSurfaceTool2
|
|
79482
80070
|
});
|
|
80071
|
+
var FLOOD_STATE_PATH = floodStatePath(STATE_DIR);
|
|
79483
80072
|
var robustApiCall = createRetryApiCall2({
|
|
79484
|
-
log: (line) => process.stderr.write(line)
|
|
80073
|
+
log: (line) => process.stderr.write(line),
|
|
80074
|
+
onFloodWait: makeFloodWaitRecorder2(FLOOD_STATE_PATH)
|
|
79485
80075
|
});
|
|
79486
80076
|
var swallowingApiCall = createSwallowingRetryApiCall(robustApiCall, (line) => process.stderr.write(line));
|
|
79487
80077
|
function wrapBootCardApi(threadId) {
|
|
@@ -80348,23 +80938,29 @@ var PIN_STATUS_WHILE_WORKING = (() => {
|
|
|
80348
80938
|
})();
|
|
80349
80939
|
var statusPinState = new Map;
|
|
80350
80940
|
var statusPinChatIds = new Map;
|
|
80351
|
-
var
|
|
80941
|
+
var statusPinPinnedAt = new Map;
|
|
80942
|
+
var STATUS_PIN_STORE_PATH = join51(STATE_DIR, "status-pins.json");
|
|
80352
80943
|
var statusPinStoreFs = {
|
|
80353
|
-
readFileSync: (p) =>
|
|
80354
|
-
writeFileSync: (p, d) =>
|
|
80355
|
-
renameSync: (a, b) =>
|
|
80356
|
-
existsSync: (p) =>
|
|
80944
|
+
readFileSync: (p) => readFileSync50(p, "utf8"),
|
|
80945
|
+
writeFileSync: (p, d) => writeFileSync40(p, d),
|
|
80946
|
+
renameSync: (a, b) => renameSync16(a, b),
|
|
80947
|
+
existsSync: (p) => existsSync47(p)
|
|
80357
80948
|
};
|
|
80358
80949
|
var statusPinPersistEnabled = !STATIC && PIN_STATUS_WHILE_WORKING;
|
|
80359
|
-
var ACTIVITY_CARD_STORE_PATH =
|
|
80950
|
+
var ACTIVITY_CARD_STORE_PATH = join51(STATE_DIR, "activity-cards-pending.json");
|
|
80360
80951
|
var activityCardStoreFs = {
|
|
80361
|
-
readFileSync: (p) =>
|
|
80362
|
-
writeFileSync: (p, d) =>
|
|
80363
|
-
renameSync: (a, b) =>
|
|
80364
|
-
existsSync: (p) =>
|
|
80952
|
+
readFileSync: (p) => readFileSync50(p, "utf8"),
|
|
80953
|
+
writeFileSync: (p, d) => writeFileSync40(p, d),
|
|
80954
|
+
renameSync: (a, b) => renameSync16(a, b),
|
|
80955
|
+
existsSync: (p) => existsSync47(p)
|
|
80365
80956
|
};
|
|
80366
80957
|
var activityCardPersistEnabled = !STATIC;
|
|
80367
80958
|
var bannerPinPersistEnabled = !STATIC;
|
|
80959
|
+
var toolPinPersistEnabled = !STATIC;
|
|
80960
|
+
var TOOL_PIN_TTL_MS = (() => {
|
|
80961
|
+
const v = Number(process.env.SWITCHROOM_TOOL_PIN_TTL_MS);
|
|
80962
|
+
return Number.isFinite(v) && v > 0 ? v : 604800000;
|
|
80963
|
+
})();
|
|
80368
80964
|
function statusPinApi() {
|
|
80369
80965
|
return {
|
|
80370
80966
|
pinChatMessage: (chat_id, message_id, opts) => robustApiCall(() => lockedBot.api.pinChatMessage(chat_id, message_id, opts), { chat_id: String(chat_id), verb: "status-pin.pin" }),
|
|
@@ -80372,16 +80968,16 @@ function statusPinApi() {
|
|
|
80372
80968
|
};
|
|
80373
80969
|
}
|
|
80374
80970
|
async function statusPinBootCleanup() {
|
|
80375
|
-
if (!statusPinPersistEnabled && !bannerPinPersistEnabled)
|
|
80971
|
+
if (!statusPinPersistEnabled && !bannerPinPersistEnabled && !toolPinPersistEnabled)
|
|
80376
80972
|
return;
|
|
80377
80973
|
const api = statusPinApi();
|
|
80378
|
-
const { cleared, total } = await runStatusPinBootCleanup({
|
|
80974
|
+
const { cleared, retained, kept, total } = await runStatusPinBootCleanup({
|
|
80379
80975
|
path: STATUS_PIN_STORE_PATH,
|
|
80380
80976
|
fs: statusPinStoreFs,
|
|
80381
80977
|
unpin: (chatId, messageId) => api.unpinChatMessage(chatId, messageId)
|
|
80382
80978
|
});
|
|
80383
80979
|
if (total > 0) {
|
|
80384
|
-
process.stderr.write(`telegram gateway: status-pin: cleared ${cleared}/${total} orphaned pin(s) from a prior session
|
|
80980
|
+
process.stderr.write(`telegram gateway: status-pin: cleared ${cleared}/${total} orphaned pin(s) from a prior session (retained ${retained} for retry, kept ${kept} unexpired tool pin(s))
|
|
80385
80981
|
`);
|
|
80386
80982
|
}
|
|
80387
80983
|
}
|
|
@@ -80416,6 +81012,11 @@ var MID_SESSION_CARD_REAPER_INTERVAL_MS = (() => {
|
|
|
80416
81012
|
const v = Number(process.env.SWITCHROOM_MID_SESSION_CARD_REAPER_INTERVAL_MS);
|
|
80417
81013
|
return Number.isFinite(v) && v > 0 ? v : 300000;
|
|
80418
81014
|
})();
|
|
81015
|
+
var WORKER_PIN_REAPER_ENABLED = process.env.SWITCHROOM_WORKER_PIN_REAPER !== "0";
|
|
81016
|
+
var WORKER_PIN_REAPER_TTL_MS = (() => {
|
|
81017
|
+
const v = Number(process.env.SWITCHROOM_WORKER_PIN_REAPER_TTL_MS);
|
|
81018
|
+
return Number.isFinite(v) && v > 0 ? v : WORKER_PIN_TTL_MS_DEFAULT;
|
|
81019
|
+
})();
|
|
80419
81020
|
function liveTurnKeySets() {
|
|
80420
81021
|
const registryKeys = new Set;
|
|
80421
81022
|
const topicKeys = new Set;
|
|
@@ -80465,11 +81066,18 @@ async function runMidSessionCardReaper() {
|
|
|
80465
81066
|
...record2.threadId != null ? { threadId: record2.threadId } : {},
|
|
80466
81067
|
verb: "activity-card.mid-session-reap-finalize"
|
|
80467
81068
|
}),
|
|
80468
|
-
unpinCard: (record2) =>
|
|
80469
|
-
|
|
80470
|
-
|
|
80471
|
-
|
|
80472
|
-
|
|
81069
|
+
unpinCard: async (record2) => {
|
|
81070
|
+
const pinKey = `fg:${record2.turnKey}`;
|
|
81071
|
+
if (statusPinState.has(pinKey)) {
|
|
81072
|
+
await reconcileStatusPin(pinKey, record2.chatId, { pinned: false });
|
|
81073
|
+
return true;
|
|
81074
|
+
}
|
|
81075
|
+
return robustApiCall(() => lockedBot.api.unpinChatMessage(record2.chatId, record2.activityMessageId), {
|
|
81076
|
+
chat_id: record2.chatId,
|
|
81077
|
+
...record2.threadId != null ? { threadId: record2.threadId } : {},
|
|
81078
|
+
verb: "activity-card.mid-session-reap-unpin"
|
|
81079
|
+
});
|
|
81080
|
+
}
|
|
80473
81081
|
});
|
|
80474
81082
|
if (total > 0) {
|
|
80475
81083
|
process.stderr.write(`telegram gateway: activity-card: mid-session finalized ${finalized}/${total} (vanished ${vanished}/${total}) orphaned card(s) (at-most-once)
|
|
@@ -80477,6 +81085,44 @@ async function runMidSessionCardReaper() {
|
|
|
80477
81085
|
}
|
|
80478
81086
|
} catch (err) {
|
|
80479
81087
|
process.stderr.write(`telegram gateway: mid-session card reaper error: ${err.message}
|
|
81088
|
+
`);
|
|
81089
|
+
}
|
|
81090
|
+
}
|
|
81091
|
+
if (WORKER_PIN_REAPER_ENABLED && PIN_STATUS_WHILE_WORKING) {
|
|
81092
|
+
try {
|
|
81093
|
+
const candidates = [...statusPinState.keys()].filter((k) => k.startsWith("wk:")).map((k) => ({
|
|
81094
|
+
pinKey: k,
|
|
81095
|
+
chatId: statusPinChatIds.get(k) ?? "",
|
|
81096
|
+
pinnedAt: statusPinPinnedAt.get(k) ?? now
|
|
81097
|
+
}));
|
|
81098
|
+
const reaps = decideWorkerPinReaps({
|
|
81099
|
+
pins: candidates,
|
|
81100
|
+
statusOf: (agentId) => {
|
|
81101
|
+
if (turnsDb == null)
|
|
81102
|
+
return "unknown";
|
|
81103
|
+
try {
|
|
81104
|
+
const row = getSubagentByJsonlId(turnsDb, agentId);
|
|
81105
|
+
if (row == null)
|
|
81106
|
+
return "unknown";
|
|
81107
|
+
if (row.status === "completed" || row.status === "failed")
|
|
81108
|
+
return "terminal";
|
|
81109
|
+
if (row.status === "running")
|
|
81110
|
+
return "running";
|
|
81111
|
+
return "unknown";
|
|
81112
|
+
} catch {
|
|
81113
|
+
return "unknown";
|
|
81114
|
+
}
|
|
81115
|
+
},
|
|
81116
|
+
ttlMs: WORKER_PIN_REAPER_TTL_MS,
|
|
81117
|
+
now
|
|
81118
|
+
});
|
|
81119
|
+
for (const reap of reaps) {
|
|
81120
|
+
process.stderr.write(`telegram gateway: worker-pin reaper unpinning ${reap.pinKey} (chat=${reap.chatId} reason=${reap.reason})
|
|
81121
|
+
`);
|
|
81122
|
+
await reconcileStatusPin(reap.pinKey, reap.chatId, { pinned: false });
|
|
81123
|
+
}
|
|
81124
|
+
} catch (err) {
|
|
81125
|
+
process.stderr.write(`telegram gateway: worker-pin reaper error: ${err.message}
|
|
80480
81126
|
`);
|
|
80481
81127
|
}
|
|
80482
81128
|
}
|
|
@@ -80518,9 +81164,12 @@ async function reconcileStatusPinInner(pinKey, chatId, desired) {
|
|
|
80518
81164
|
if (next2 == null) {
|
|
80519
81165
|
statusPinState.delete(pinKey);
|
|
80520
81166
|
statusPinChatIds.delete(pinKey);
|
|
81167
|
+
statusPinPinnedAt.delete(pinKey);
|
|
80521
81168
|
} else {
|
|
80522
81169
|
statusPinState.set(pinKey, next2);
|
|
80523
81170
|
statusPinChatIds.set(pinKey, chatId);
|
|
81171
|
+
if (!statusPinPinnedAt.has(pinKey))
|
|
81172
|
+
statusPinPinnedAt.set(pinKey, Date.now());
|
|
80524
81173
|
}
|
|
80525
81174
|
return;
|
|
80526
81175
|
}
|
|
@@ -80535,9 +81184,12 @@ async function reconcileStatusPinInner(pinKey, chatId, desired) {
|
|
|
80535
81184
|
if (next == null) {
|
|
80536
81185
|
statusPinState.delete(pinKey);
|
|
80537
81186
|
statusPinChatIds.delete(pinKey);
|
|
81187
|
+
statusPinPinnedAt.delete(pinKey);
|
|
80538
81188
|
} else {
|
|
80539
81189
|
statusPinState.set(pinKey, next);
|
|
80540
81190
|
statusPinChatIds.set(pinKey, chatId);
|
|
81191
|
+
if (!statusPinPinnedAt.has(pinKey))
|
|
81192
|
+
statusPinPinnedAt.set(pinKey, Date.now());
|
|
80541
81193
|
}
|
|
80542
81194
|
}
|
|
80543
81195
|
function reconcileWorkerPin(agentId, chatId, running) {
|
|
@@ -80567,6 +81219,7 @@ async function unpinAllStatusPins() {
|
|
|
80567
81219
|
const chatId = statusPinChatIds.get(key);
|
|
80568
81220
|
if (chatId == null) {
|
|
80569
81221
|
statusPinState.delete(key);
|
|
81222
|
+
statusPinPinnedAt.delete(key);
|
|
80570
81223
|
continue;
|
|
80571
81224
|
}
|
|
80572
81225
|
await reconcileStatusPin(key, chatId, { pinned: false });
|
|
@@ -80578,11 +81231,11 @@ var getPinnedProgressCardMessageId = null;
|
|
|
80578
81231
|
var completeProgressCardTurn = null;
|
|
80579
81232
|
var subagentWatcher = null;
|
|
80580
81233
|
var workerActivityFeed = null;
|
|
80581
|
-
var SOCKET_PATH = process.env.SWITCHROOM_GATEWAY_SOCKET ??
|
|
80582
|
-
|
|
80583
|
-
var GATEWAY_PID_PATH = process.env.SWITCHROOM_GATEWAY_PID_FILE ??
|
|
80584
|
-
var GATEWAY_SESSION_MARKER_PATH = process.env.SWITCHROOM_GATEWAY_SESSION_MARKER ??
|
|
80585
|
-
var GATEWAY_CLEAN_SHUTDOWN_MARKER_PATH = process.env.SWITCHROOM_GATEWAY_CLEAN_SHUTDOWN_MARKER ??
|
|
81234
|
+
var SOCKET_PATH = process.env.SWITCHROOM_GATEWAY_SOCKET ?? join51(STATE_DIR, "gateway.sock");
|
|
81235
|
+
mkdirSync37(STATE_DIR, { recursive: true, mode: 448 });
|
|
81236
|
+
var GATEWAY_PID_PATH = process.env.SWITCHROOM_GATEWAY_PID_FILE ?? join51(STATE_DIR, "gateway.pid.json");
|
|
81237
|
+
var GATEWAY_SESSION_MARKER_PATH = process.env.SWITCHROOM_GATEWAY_SESSION_MARKER ?? join51(STATE_DIR, "gateway-session.json");
|
|
81238
|
+
var GATEWAY_CLEAN_SHUTDOWN_MARKER_PATH = process.env.SWITCHROOM_GATEWAY_CLEAN_SHUTDOWN_MARKER ?? join51(STATE_DIR, "clean-shutdown.json");
|
|
80586
81239
|
var GATEWAY_STARTED_AT_MS = Date.now();
|
|
80587
81240
|
var BOOT_CARD_ENABLED = process.env.SWITCHROOM_BOOT_CARD !== "false";
|
|
80588
81241
|
var activeBootCard = null;
|
|
@@ -80611,7 +81264,7 @@ function ensureIssuesCard(chatId, threadId) {
|
|
|
80611
81264
|
bot: botApi,
|
|
80612
81265
|
log: (msg) => process.stderr.write(`telegram gateway: ${msg}
|
|
80613
81266
|
`),
|
|
80614
|
-
persistPath:
|
|
81267
|
+
persistPath: join51(stateDir, "issues-card.json")
|
|
80615
81268
|
});
|
|
80616
81269
|
activeIssuesWatcher = startIssuesWatcher({
|
|
80617
81270
|
stateDir,
|
|
@@ -80940,13 +81593,13 @@ startTimer2({
|
|
|
80940
81593
|
}
|
|
80941
81594
|
});
|
|
80942
81595
|
var inboundSpool = STATIC ? undefined : createInboundSpool({
|
|
80943
|
-
path:
|
|
81596
|
+
path: join51(STATE_DIR, "inbound-spool.jsonl"),
|
|
80944
81597
|
fs: {
|
|
80945
81598
|
appendFileSync: (p, d) => appendFileSync6(p, d),
|
|
80946
|
-
readFileSync: (p) =>
|
|
80947
|
-
writeFileSync: (p, d) =>
|
|
80948
|
-
renameSync: (a, b) =>
|
|
80949
|
-
existsSync: (p) =>
|
|
81599
|
+
readFileSync: (p) => readFileSync50(p, "utf8"),
|
|
81600
|
+
writeFileSync: (p, d) => writeFileSync40(p, d),
|
|
81601
|
+
renameSync: (a, b) => renameSync16(a, b),
|
|
81602
|
+
existsSync: (p) => existsSync47(p),
|
|
80950
81603
|
statSizeSync: (p) => statSync16(p).size
|
|
80951
81604
|
},
|
|
80952
81605
|
onDegraded: (info) => {
|
|
@@ -81242,8 +81895,9 @@ var ipcServer = createIpcServer({
|
|
|
81242
81895
|
probeQuotaViaBroker: (t) => probeQuotaForBootCard(agentSlug, t),
|
|
81243
81896
|
tmuxSupervisor: process.env.SWITCHROOM_TMUX_SUPERVISOR === "1",
|
|
81244
81897
|
dockerMode: process.env.SWITCHROOM_RUNTIME === "docker",
|
|
81245
|
-
configSnapshotPath:
|
|
81246
|
-
bootCardStatePath:
|
|
81898
|
+
configSnapshotPath: join51(resolvedAgentDirForCard, ".config-snapshot.json"),
|
|
81899
|
+
bootCardStatePath: join51(resolvedAgentDirForCard, ".boot-card-msgid.json"),
|
|
81900
|
+
floodStatePath: FLOOD_STATE_PATH,
|
|
81247
81901
|
...updateOutcomeLine ? { updateOutcomeLine } : {}
|
|
81248
81902
|
}, ackMsgId).then((handle) => {
|
|
81249
81903
|
activeBootCard = handle;
|
|
@@ -81906,7 +82560,7 @@ var ipcServer = createIpcServer({
|
|
|
81906
82560
|
const receiverUid = receiverUidRaw ? Number(receiverUidRaw) : NaN;
|
|
81907
82561
|
if (Number.isInteger(receiverUid))
|
|
81908
82562
|
allowedUids.push(receiverUid);
|
|
81909
|
-
const socketPath =
|
|
82563
|
+
const socketPath = join51(STATE_DIR, "webhook.sock");
|
|
81910
82564
|
const webhookInject = (agentName3, inbound) => {
|
|
81911
82565
|
const msg = inbound;
|
|
81912
82566
|
const delivered = ipcServer.sendToAgent(agentName3, msg);
|
|
@@ -82139,9 +82793,9 @@ function redactOutboundText(text5, site) {
|
|
|
82139
82793
|
var VOICE_OUT_DEFAULT_CHUNK_CHARS = 600;
|
|
82140
82794
|
var VOICE_OUT_HARD_CHUNK_CAP = 4096;
|
|
82141
82795
|
var voiceOnDemandCache = new VoiceOnDemandCache({
|
|
82142
|
-
persistPath:
|
|
82796
|
+
persistPath: join51(STATE_DIR, "voice-ondemand.json")
|
|
82143
82797
|
});
|
|
82144
|
-
var VOICE_CACHE_DIR =
|
|
82798
|
+
var VOICE_CACHE_DIR = join51(STATE_DIR, "voice-cache");
|
|
82145
82799
|
var voicePreSynthQueue = new PreSynthQueue({
|
|
82146
82800
|
runJob: async (job) => {
|
|
82147
82801
|
const sidecarToken = await materializeSidecarToken();
|
|
@@ -82416,7 +83070,7 @@ ${url}`;
|
|
|
82416
83070
|
}
|
|
82417
83071
|
const limit = Math.max(1, Math.min(access.textChunkLimit ?? RICH_MESSAGE_MAX_CHARS2, MAX_CHUNK_LIMIT));
|
|
82418
83072
|
const replyMode = access.replyToMode ?? "first";
|
|
82419
|
-
const chunks = literalText ? chunk2(effectiveText, limit, access.chunkMode ?? "length") :
|
|
83073
|
+
const chunks = literalText ? chunk2(effectiveText, limit, access.chunkMode ?? "length") : splitMarkdownChunks2(effectiveText, limit);
|
|
82420
83074
|
const sentIds = [];
|
|
82421
83075
|
const useOnDemandButton = voiceOutPlan != null && voiceOutPlan.replyMode === "on-demand" && voiceOutPlan.engine === "kokoro";
|
|
82422
83076
|
const voiceOggs = [];
|
|
@@ -82665,8 +83319,8 @@ ${url}`;
|
|
|
82665
83319
|
return lockedBot.api.sendRichMessage(chat_id, richMessage2(chunks[i]), richOpts);
|
|
82666
83320
|
};
|
|
82667
83321
|
const sendChunkResplit = async (opts) => {
|
|
82668
|
-
const subPieces =
|
|
82669
|
-
const pieces = subPieces.length > 1 ? subPieces :
|
|
83322
|
+
const subPieces = splitMarkdownChunks2(chunks[i], RICH_MESSAGE_MAX_CHARS2);
|
|
83323
|
+
const pieces = subPieces.length > 1 ? subPieces : hardSliceToCap2(chunks[i], RICH_MESSAGE_MAX_CHARS2);
|
|
82670
83324
|
for (let p = 0;p < pieces.length; p++) {
|
|
82671
83325
|
let sent;
|
|
82672
83326
|
if (literalText) {
|
|
@@ -83092,11 +83746,11 @@ async function executeSendGif(rawArgs) {
|
|
|
83092
83746
|
};
|
|
83093
83747
|
}
|
|
83094
83748
|
async function publishToTelegraph(text5, shortName, authorName) {
|
|
83095
|
-
const accountPath =
|
|
83749
|
+
const accountPath = join51(STATE_DIR, "telegraph-account.json");
|
|
83096
83750
|
let account = null;
|
|
83097
83751
|
try {
|
|
83098
|
-
if (
|
|
83099
|
-
const raw =
|
|
83752
|
+
if (existsSync47(accountPath)) {
|
|
83753
|
+
const raw = readFileSync50(accountPath, "utf-8");
|
|
83100
83754
|
const parsed = JSON.parse(raw);
|
|
83101
83755
|
if (parsed.shortName && parsed.accessToken) {
|
|
83102
83756
|
account = parsed;
|
|
@@ -83115,8 +83769,8 @@ async function publishToTelegraph(text5, shortName, authorName) {
|
|
|
83115
83769
|
}
|
|
83116
83770
|
account = created.value;
|
|
83117
83771
|
try {
|
|
83118
|
-
|
|
83119
|
-
|
|
83772
|
+
mkdirSync37(STATE_DIR, { recursive: true, mode: 448 });
|
|
83773
|
+
writeFileSync40(accountPath, JSON.stringify(account, null, 2), { mode: 384 });
|
|
83120
83774
|
} catch (err) {
|
|
83121
83775
|
process.stderr.write(`telegram gateway: telegraph cache write failed: ${err.message}
|
|
83122
83776
|
`);
|
|
@@ -83555,7 +84209,7 @@ async function executeVaultRequestAccess(args) {
|
|
|
83555
84209
|
}
|
|
83556
84210
|
function readLiveSwitchroomConfigText() {
|
|
83557
84211
|
const cfgPath = process.env.SWITCHROOM_CONFIG ?? findConfigFile2();
|
|
83558
|
-
return
|
|
84212
|
+
return readFileSync50(cfgPath, "utf8");
|
|
83559
84213
|
}
|
|
83560
84214
|
var MENTAL_MODEL_NAME_REGEX = /^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$/;
|
|
83561
84215
|
function buildMentalModelProposeKeyboard(stageId) {
|
|
@@ -83724,9 +84378,9 @@ async function executeDownloadAttachment(args) {
|
|
|
83724
84378
|
fileUniqueId: file.file_unique_id,
|
|
83725
84379
|
now: Date.now()
|
|
83726
84380
|
});
|
|
83727
|
-
|
|
84381
|
+
mkdirSync37(INBOX_DIR, { recursive: true, mode: 448 });
|
|
83728
84382
|
assertInsideInbox(INBOX_DIR, dlPath);
|
|
83729
|
-
|
|
84383
|
+
writeFileSync40(dlPath, buf, { mode: 384 });
|
|
83730
84384
|
return { content: [{ type: "text", text: dlPath }] };
|
|
83731
84385
|
}
|
|
83732
84386
|
async function executeEditMessage(args) {
|
|
@@ -83799,7 +84453,17 @@ async function executePinMessage(args) {
|
|
|
83799
84453
|
throw new Error("pin_message: message_id is required");
|
|
83800
84454
|
const pinChatId = String(args.chat_id ?? "");
|
|
83801
84455
|
assertAllowedChat(pinChatId);
|
|
83802
|
-
|
|
84456
|
+
const pinMsgId = Number(args.message_id);
|
|
84457
|
+
await robustApiCall(() => lockedBot.api.pinChatMessage(pinChatId, pinMsgId), { chat_id: pinChatId, verb: "pin_message" });
|
|
84458
|
+
if (toolPinPersistEnabled) {
|
|
84459
|
+
const toolPinKey = `tool:${pinChatId}:${pinMsgId}`;
|
|
84460
|
+
mutateStatusPinRow(STATUS_PIN_STORE_PATH, statusPinStoreFs, toolPinKey, {
|
|
84461
|
+
pinKey: toolPinKey,
|
|
84462
|
+
chatId: pinChatId,
|
|
84463
|
+
messageId: pinMsgId,
|
|
84464
|
+
expiresAt: Date.now() + TOOL_PIN_TTL_MS
|
|
84465
|
+
});
|
|
84466
|
+
}
|
|
83803
84467
|
return { content: [{ type: "text", text: `pinned message ${args.message_id}` }] };
|
|
83804
84468
|
}
|
|
83805
84469
|
async function executeDeleteMessage(args) {
|
|
@@ -84894,7 +85558,7 @@ function handleSessionEvent(ev) {
|
|
|
84894
85558
|
};
|
|
84895
85559
|
const limit = RICH_MESSAGE_MAX_CHARS2;
|
|
84896
85560
|
const renderedText = addParagraphSpacers(capturedText);
|
|
84897
|
-
const htmlChunks =
|
|
85561
|
+
const htmlChunks = splitMarkdownChunks2(renderedText, limit);
|
|
84898
85562
|
const sentIds = [];
|
|
84899
85563
|
try {
|
|
84900
85564
|
let firstSendUsedEdit = false;
|
|
@@ -85999,8 +86663,11 @@ ${preBlock(write.output)}`;
|
|
|
85999
86663
|
process.stderr.write(`telegram gateway: inbound held mid-turn agent=${selfAgent} chat=${chat_id} msg=${msgId ?? "-"} \u2014 will flush on turn-complete
|
|
86000
86664
|
`);
|
|
86001
86665
|
const inFlightThread = currentTurn?.sessionThreadId;
|
|
86002
|
-
|
|
86666
|
+
const crossTopicQueuedCard = QUEUED_STATUS_UX_ENABLED && !isDmChatId(chat_id) && messageThreadId != null && messageThreadId !== inFlightThread;
|
|
86667
|
+
if (crossTopicQueuedCard) {
|
|
86003
86668
|
postQueuedStatus(chat_id, messageThreadId, inFlightThread);
|
|
86669
|
+
} else {
|
|
86670
|
+
maybePostBusyAck("buffer-until-idle", chat_id, messageThreadId ?? undefined);
|
|
86004
86671
|
}
|
|
86005
86672
|
return;
|
|
86006
86673
|
}
|
|
@@ -86023,6 +86690,9 @@ ${preBlock(write.output)}`;
|
|
|
86023
86690
|
}
|
|
86024
86691
|
const delivered = ipcServer.sendToAgent(selfAgent, inboundMsg);
|
|
86025
86692
|
if (delivered) {
|
|
86693
|
+
if (isSteering) {
|
|
86694
|
+
maybePostBusyAck("steer", chat_id, messageThreadId ?? undefined);
|
|
86695
|
+
}
|
|
86026
86696
|
const busyKey = reservedBusyKey ?? markClaudeBusyForInbound(inboundMsg);
|
|
86027
86697
|
if (DELIVERY_CONFIRM_ENABLED && shouldTrackDelivery({
|
|
86028
86698
|
isSteering,
|
|
@@ -86154,14 +86824,14 @@ function restartMarkerPath() {
|
|
|
86154
86824
|
const agentDir = resolveAgentDirFromEnv();
|
|
86155
86825
|
if (!agentDir)
|
|
86156
86826
|
return null;
|
|
86157
|
-
return
|
|
86827
|
+
return join51(agentDir, "restart-pending.json");
|
|
86158
86828
|
}
|
|
86159
86829
|
function writeRestartMarker(marker) {
|
|
86160
86830
|
const p = restartMarkerPath();
|
|
86161
86831
|
if (!p)
|
|
86162
86832
|
return;
|
|
86163
86833
|
try {
|
|
86164
|
-
|
|
86834
|
+
writeFileSync40(p, JSON.stringify(marker));
|
|
86165
86835
|
lastPlannedRestartAt = Date.now();
|
|
86166
86836
|
process.stderr.write(`telegram gateway: restart-marker: write chat_id=${marker.chat_id} thread_id=${marker.thread_id ?? "-"} ack=${marker.ack_message_id ?? "-"} path=${p}
|
|
86167
86837
|
`);
|
|
@@ -86180,7 +86850,7 @@ function readRestartMarker() {
|
|
|
86180
86850
|
if (!p)
|
|
86181
86851
|
return null;
|
|
86182
86852
|
try {
|
|
86183
|
-
return JSON.parse(
|
|
86853
|
+
return JSON.parse(readFileSync50(p, "utf8"));
|
|
86184
86854
|
} catch {
|
|
86185
86855
|
return null;
|
|
86186
86856
|
}
|
|
@@ -86190,7 +86860,7 @@ function clearRestartMarker() {
|
|
|
86190
86860
|
if (!p)
|
|
86191
86861
|
return;
|
|
86192
86862
|
try {
|
|
86193
|
-
|
|
86863
|
+
rmSync5(p, { force: true });
|
|
86194
86864
|
process.stderr.write(`telegram gateway: restart-marker: cleared path=${p}
|
|
86195
86865
|
`);
|
|
86196
86866
|
} catch {}
|
|
@@ -86329,7 +86999,7 @@ var _dockerReachable;
|
|
|
86329
86999
|
function isDockerReachable() {
|
|
86330
87000
|
if (_dockerReachable !== undefined)
|
|
86331
87001
|
return _dockerReachable;
|
|
86332
|
-
if (!
|
|
87002
|
+
if (!existsSync47("/var/run/docker.sock")) {
|
|
86333
87003
|
_dockerReachable = false;
|
|
86334
87004
|
return _dockerReachable;
|
|
86335
87005
|
}
|
|
@@ -86346,12 +87016,12 @@ function _resetDockerReachableCache() {
|
|
|
86346
87016
|
}
|
|
86347
87017
|
function spawnSwitchroomDetached(args, onFailure) {
|
|
86348
87018
|
const fullArgs = SWITCHROOM_CONFIG ? ["--config", SWITCHROOM_CONFIG, ...args] : args;
|
|
86349
|
-
const logPath =
|
|
87019
|
+
const logPath = join51(STATE_DIR, "detached-spawn.log");
|
|
86350
87020
|
let outFd = null;
|
|
86351
87021
|
try {
|
|
86352
|
-
|
|
87022
|
+
mkdirSync37(STATE_DIR, { recursive: true });
|
|
86353
87023
|
outFd = openSync9(logPath, "a");
|
|
86354
|
-
|
|
87024
|
+
writeFileSync40(logPath, `
|
|
86355
87025
|
[${new Date().toISOString()}] spawn ${SWITCHROOM_CLI} ${fullArgs.join(" ")}
|
|
86356
87026
|
`, { flag: "a" });
|
|
86357
87027
|
} catch {}
|
|
@@ -86377,7 +87047,7 @@ function spawnSwitchroomDetached(args, onFailure) {
|
|
|
86377
87047
|
return;
|
|
86378
87048
|
let tail = "";
|
|
86379
87049
|
try {
|
|
86380
|
-
const full =
|
|
87050
|
+
const full = readFileSync50(logPath, "utf8");
|
|
86381
87051
|
tail = full.split(`
|
|
86382
87052
|
`).slice(-30).join(`
|
|
86383
87053
|
`).trim();
|
|
@@ -86744,10 +87414,10 @@ bot.use(async (ctx, next) => {
|
|
|
86744
87414
|
});
|
|
86745
87415
|
function readRecentDenialsForAgent(agentName3, windowMs, limit) {
|
|
86746
87416
|
try {
|
|
86747
|
-
const auditPath =
|
|
86748
|
-
if (!
|
|
87417
|
+
const auditPath = join51(homedir16(), ".switchroom", "vault-audit.log");
|
|
87418
|
+
if (!existsSync47(auditPath))
|
|
86749
87419
|
return [];
|
|
86750
|
-
const raw =
|
|
87420
|
+
const raw = readFileSync50(auditPath, "utf8");
|
|
86751
87421
|
return recentDenialsFromAuditLog(raw, { agentName: agentName3, windowMs, limit });
|
|
86752
87422
|
} catch {
|
|
86753
87423
|
return [];
|
|
@@ -86798,7 +87468,7 @@ async function buildAgentMetadata(agentName3) {
|
|
|
86798
87468
|
try {
|
|
86799
87469
|
const agentDir = resolveAgentDirFromEnv();
|
|
86800
87470
|
if (agentDir) {
|
|
86801
|
-
const raw =
|
|
87471
|
+
const raw = readFileSync50(join51(agentDir, ".claude", ".claude.json"), "utf8");
|
|
86802
87472
|
claudeJson = JSON.parse(raw);
|
|
86803
87473
|
}
|
|
86804
87474
|
} catch {}
|
|
@@ -86976,7 +87646,7 @@ function buildModelDeps(restartCtx) {
|
|
|
86976
87646
|
try {
|
|
86977
87647
|
const agentDir = resolveAgentDirFromEnv();
|
|
86978
87648
|
if (agentDir) {
|
|
86979
|
-
const local = await fetchQuota2({ claudeConfigDir:
|
|
87649
|
+
const local = await fetchQuota2({ claudeConfigDir: join51(agentDir, ".claude") });
|
|
86980
87650
|
if (local.ok)
|
|
86981
87651
|
return formatQuotaLine2(local.data);
|
|
86982
87652
|
}
|
|
@@ -87008,6 +87678,11 @@ function buildModelDeps(restartCtx) {
|
|
|
87008
87678
|
});
|
|
87009
87679
|
}
|
|
87010
87680
|
stampUserRestartReason(reason);
|
|
87681
|
+
{
|
|
87682
|
+
const smDir = resolveAgentDirFromEnv();
|
|
87683
|
+
if (smDir)
|
|
87684
|
+
writeRelaunchModelIntent(smDir, "keep", reason);
|
|
87685
|
+
}
|
|
87011
87686
|
await sweepBeforeSelfRestart();
|
|
87012
87687
|
const hostdResp = await tryHostdDispatch(name, {
|
|
87013
87688
|
v: 1,
|
|
@@ -87022,26 +87697,29 @@ function buildModelDeps(restartCtx) {
|
|
|
87022
87697
|
}
|
|
87023
87698
|
if (hostdResp.result !== "started" && hostdResp.result !== "completed") {
|
|
87024
87699
|
clearRestartMarker();
|
|
87700
|
+
{
|
|
87701
|
+
const smDir = resolveAgentDirFromEnv();
|
|
87702
|
+
if (smDir)
|
|
87703
|
+
clearRelaunchModelIntent(smDir);
|
|
87704
|
+
}
|
|
87025
87705
|
throw new Error(`hostd restart failed (result=${hostdResp.result}): ${hostdResp.error ?? "(no details)"}`);
|
|
87026
87706
|
}
|
|
87027
87707
|
},
|
|
87028
87708
|
scheduleModelRelaunch: async (model, reason) => {
|
|
87029
87709
|
const agentDir = resolveAgentDirFromEnv();
|
|
87030
87710
|
if (!agentDir)
|
|
87031
|
-
throw new Error("agent dir unresolvable \u2014 cannot write session-model
|
|
87711
|
+
throw new Error("agent dir unresolvable \u2014 cannot write session-model file");
|
|
87032
87712
|
const prevOverride = sessionModelSource.getOverride();
|
|
87033
|
-
|
|
87034
|
-
|
|
87713
|
+
const prevFileRaw = readSessionModelFileRaw(agentDir);
|
|
87714
|
+
writeSessionModelFile(agentDir, model, readConfiguredDefaultModel(agentDir) ?? resolveMainModel(deps.getConfiguredModel() ?? undefined));
|
|
87035
87715
|
sessionModelSource.setOverride(model);
|
|
87036
87716
|
try {
|
|
87037
87717
|
await deps.scheduleRestart(reason);
|
|
87038
87718
|
} catch (err) {
|
|
87039
|
-
const carrierPath = join49(agentDir, ".session-model-override");
|
|
87040
87719
|
if (err?.code !== "restart_in_flight") {
|
|
87041
|
-
|
|
87042
|
-
rmSync4(carrierPath, { force: true });
|
|
87043
|
-
} catch {}
|
|
87720
|
+
restoreSessionModelFileRaw(agentDir, prevFileRaw);
|
|
87044
87721
|
sessionModelSource.setOverride(prevOverride);
|
|
87722
|
+
clearRelaunchModelIntent(agentDir);
|
|
87045
87723
|
}
|
|
87046
87724
|
throw err;
|
|
87047
87725
|
}
|
|
@@ -87074,10 +87752,29 @@ bot.command("model", async (ctx) => {
|
|
|
87074
87752
|
return;
|
|
87075
87753
|
}
|
|
87076
87754
|
const reply = await handleModelCommand(parsed, deps);
|
|
87077
|
-
|
|
87755
|
+
const requested = parsed.kind === "set" ? expandSrAlias(parsed.model) : null;
|
|
87756
|
+
let persistWarning = "";
|
|
87757
|
+
if (requested?.toLowerCase() === "default") {
|
|
87758
|
+
const smDir = resolveAgentDirFromEnv();
|
|
87759
|
+
if (smDir)
|
|
87760
|
+
clearSessionModelFile(smDir);
|
|
87761
|
+
if (reply.selectedModel)
|
|
87762
|
+
sessionModelSource.setOverride(null);
|
|
87763
|
+
} else if (reply.selectedModel) {
|
|
87078
87764
|
sessionModelSource.setOverride(reply.selectedModel);
|
|
87765
|
+
const smDir = resolveAgentDirFromEnv();
|
|
87766
|
+
if (smDir && requested && isValidModelArg(requested) && !isSrModel(requested)) {
|
|
87767
|
+
try {
|
|
87768
|
+
writeSessionModelFile(smDir, requested, readConfiguredDefaultModel(smDir) ?? resolveMainModel(deps.getConfiguredModel() ?? undefined));
|
|
87769
|
+
} catch (err) {
|
|
87770
|
+
persistWarning = `
|
|
87771
|
+
\u26A0\uFE0F Couldn\u2019t persist the sticky override \u2014 the switch is live now but won\u2019t survive a relaunch.`;
|
|
87772
|
+
process.stderr.write(`telegram gateway: session-model persist failed (typed /model): ${err?.message ?? String(err)}
|
|
87773
|
+
`);
|
|
87774
|
+
}
|
|
87775
|
+
}
|
|
87079
87776
|
}
|
|
87080
|
-
await switchroomReply(ctx, reply.text, { html: reply.html });
|
|
87777
|
+
await switchroomReply(ctx, reply.text + persistWarning, { html: reply.html });
|
|
87081
87778
|
});
|
|
87082
87779
|
function buildEffortDeps() {
|
|
87083
87780
|
return {
|
|
@@ -87173,6 +87870,11 @@ bot.command("restart", async (ctx) => {
|
|
|
87173
87870
|
} catch {}
|
|
87174
87871
|
writeRestartMarker({ chat_id: chatId, thread_id: threadId ?? null, ack_message_id: ackId, ts: Date.now() });
|
|
87175
87872
|
stampUserRestartReason("user: /restart from chat");
|
|
87873
|
+
{
|
|
87874
|
+
const smDir = resolveAgentDirFromEnv();
|
|
87875
|
+
if (smDir)
|
|
87876
|
+
writeRelaunchModelIntent(smDir, "revert", "user: /restart from chat");
|
|
87877
|
+
}
|
|
87176
87878
|
await sweepBeforeSelfRestart();
|
|
87177
87879
|
const hostdResp = await tryHostdDispatch(getMyAgentName(), {
|
|
87178
87880
|
v: 1,
|
|
@@ -87203,9 +87905,9 @@ bot.command("restart", async (ctx) => {
|
|
|
87203
87905
|
function flushAgentHandoff(agentDir) {
|
|
87204
87906
|
let removed = 0;
|
|
87205
87907
|
for (const fname of [".handoff.md", ".handoff-topic"]) {
|
|
87206
|
-
const p =
|
|
87908
|
+
const p = join51(agentDir, fname);
|
|
87207
87909
|
try {
|
|
87208
|
-
if (
|
|
87910
|
+
if (existsSync47(p)) {
|
|
87209
87911
|
unlinkSync20(p);
|
|
87210
87912
|
removed++;
|
|
87211
87913
|
}
|
|
@@ -87261,7 +87963,7 @@ async function handleNewCommand(ctx) {
|
|
|
87261
87963
|
writeRestartMarker({ chat_id: chatId, thread_id: threadId ?? null, ack_message_id: ackId, ts: Date.now() });
|
|
87262
87964
|
if (agentDir != null) {
|
|
87263
87965
|
try {
|
|
87264
|
-
|
|
87966
|
+
writeFileSync40(join51(agentDir, ".force-fresh-session"), `${kind} at ${new Date().toISOString()}
|
|
87265
87967
|
`, "utf8");
|
|
87266
87968
|
} catch (err) {
|
|
87267
87969
|
process.stderr.write(`telegram gateway: failed to write force-fresh marker: ${err}
|
|
@@ -87269,6 +87971,9 @@ async function handleNewCommand(ctx) {
|
|
|
87269
87971
|
}
|
|
87270
87972
|
}
|
|
87271
87973
|
stampUserRestartReason(`user: /${kind} from chat`);
|
|
87974
|
+
if (agentDir != null) {
|
|
87975
|
+
writeRelaunchModelIntent(agentDir, "keep", `user: /${kind} from chat`);
|
|
87976
|
+
}
|
|
87272
87977
|
await sweepBeforeSelfRestart();
|
|
87273
87978
|
const hostdResp = await tryHostdDispatch(getMyAgentName(), {
|
|
87274
87979
|
v: 1,
|
|
@@ -87625,16 +88330,16 @@ bot.command("interrupt", async (ctx) => {
|
|
|
87625
88330
|
await runSwitchroomCommand(ctx, ["agent", "interrupt", name], `interrupt ${name}`);
|
|
87626
88331
|
});
|
|
87627
88332
|
var lockoutOps = {
|
|
87628
|
-
readFileSync: (p, enc) =>
|
|
87629
|
-
writeFileSync: (p, data, opts) =>
|
|
87630
|
-
existsSync: (p) =>
|
|
87631
|
-
mkdirSync: (p, opts) =>
|
|
87632
|
-
joinPath: (...parts) =>
|
|
88333
|
+
readFileSync: (p, enc) => readFileSync50(p, enc),
|
|
88334
|
+
writeFileSync: (p, data, opts) => writeFileSync40(p, data, opts),
|
|
88335
|
+
existsSync: (p) => existsSync47(p),
|
|
88336
|
+
mkdirSync: (p, opts) => mkdirSync37(p, opts),
|
|
88337
|
+
joinPath: (...parts) => join51(...parts)
|
|
87633
88338
|
};
|
|
87634
88339
|
var FLEET_FALLBACK_DEDUP_MS = 30000;
|
|
87635
88340
|
function isAuthBrokerSocketReachable() {
|
|
87636
88341
|
try {
|
|
87637
|
-
return
|
|
88342
|
+
return existsSync47(resolveAuthBrokerSocketPath2());
|
|
87638
88343
|
} catch {
|
|
87639
88344
|
return false;
|
|
87640
88345
|
}
|
|
@@ -87757,7 +88462,7 @@ async function runCreditWatch() {
|
|
|
87757
88462
|
if (!agentDir)
|
|
87758
88463
|
return;
|
|
87759
88464
|
const agentName3 = getMyAgentName();
|
|
87760
|
-
const claudeConfigDir =
|
|
88465
|
+
const claudeConfigDir = join51(agentDir, ".claude");
|
|
87761
88466
|
const stateDir = STATE_DIR;
|
|
87762
88467
|
const reason = readClaudeJsonOverage(claudeConfigDir);
|
|
87763
88468
|
const prev = loadCreditState(stateDir);
|
|
@@ -88275,10 +88980,10 @@ async function handleVaultRecentDenialCallback(ctx, data) {
|
|
|
88275
88980
|
return;
|
|
88276
88981
|
}
|
|
88277
88982
|
const { token, id } = result;
|
|
88278
|
-
const tokenPath =
|
|
88983
|
+
const tokenPath = join51(homedir16(), ".switchroom", "agents", agentName3, ".vault-token");
|
|
88279
88984
|
try {
|
|
88280
|
-
|
|
88281
|
-
|
|
88985
|
+
mkdirSync37(join51(homedir16(), ".switchroom", "agents", agentName3), { recursive: true });
|
|
88986
|
+
writeFileSync40(tokenPath, token, { mode: 384 });
|
|
88282
88987
|
} catch (err) {
|
|
88283
88988
|
await switchroomReply(ctx, `**Grant created (${escapeHtmlForTg2(id)}) but token write failed:** ${escapeHtmlForTg2(String(err))}
|
|
88284
88989
|
_Recover with: \`switchroom vault grant ${escapeHtmlForTg2(agentName3)} --keys ${escapeHtmlForTg2(keyName)} --duration 30d\` on the host._`, { html: true });
|
|
@@ -88355,10 +89060,10 @@ async function performVaultAccessApproval(ctx, pending2, stageId, senderId, atte
|
|
|
88355
89060
|
return;
|
|
88356
89061
|
}
|
|
88357
89062
|
const { token, id } = result;
|
|
88358
|
-
const tokenPath =
|
|
89063
|
+
const tokenPath = join51(homedir16(), ".switchroom", "agents", pending2.agent, ".vault-token");
|
|
88359
89064
|
try {
|
|
88360
|
-
|
|
88361
|
-
|
|
89065
|
+
mkdirSync37(join51(homedir16(), ".switchroom", "agents", pending2.agent), { recursive: true });
|
|
89066
|
+
writeFileSync40(tokenPath, token, { mode: 384 });
|
|
88362
89067
|
} catch (err) {
|
|
88363
89068
|
await switchroomReply(ctx, `**Grant created (${escapeHtmlForTg2(id)}) but token write failed:** ${escapeHtmlForTg2(String(err))}
|
|
88364
89069
|
_Recover with: \`switchroom vault grant ${escapeHtmlForTg2(pending2.agent)} --keys ${escapeHtmlForTg2(pending2.key)} --duration ${Math.round(pending2.ttl_seconds / 86400)}d\` on the host._`, { html: true });
|
|
@@ -89064,10 +89769,10 @@ async function executeGrantWizard(ctx, chatId, state4) {
|
|
|
89064
89769
|
return;
|
|
89065
89770
|
}
|
|
89066
89771
|
const { token, id } = result;
|
|
89067
|
-
const tokenPath =
|
|
89772
|
+
const tokenPath = join51(homedir16(), ".switchroom", "agents", state4.agent, ".vault-token");
|
|
89068
89773
|
try {
|
|
89069
|
-
|
|
89070
|
-
|
|
89774
|
+
mkdirSync37(join51(homedir16(), ".switchroom", "agents", state4.agent), { recursive: true });
|
|
89775
|
+
writeFileSync40(tokenPath, token, { mode: 384 });
|
|
89071
89776
|
} catch (err) {
|
|
89072
89777
|
await switchroomReply(ctx, `**Grant created but token write failed:** ${escapeHtmlForTg2(String(err))}`, { html: true });
|
|
89073
89778
|
return;
|
|
@@ -89917,7 +90622,7 @@ bot.command("usage", async (ctx) => {
|
|
|
89917
90622
|
await switchroomReply(ctx, "**/usage:** cannot resolve agent dir.", { html: true });
|
|
89918
90623
|
return;
|
|
89919
90624
|
}
|
|
89920
|
-
const result = await fetchQuota2({ claudeConfigDir:
|
|
90625
|
+
const result = await fetchQuota2({ claudeConfigDir: join51(agentDir, ".claude") });
|
|
89921
90626
|
if (!result.ok) {
|
|
89922
90627
|
await switchroomReply(ctx, `**/usage:** ${escapeHtmlForTg2(result.reason)}`, { html: true });
|
|
89923
90628
|
return;
|
|
@@ -90197,6 +90902,22 @@ bot.on("callback_query:data", async (ctx) => {
|
|
|
90197
90902
|
const outcome = await handleModelMenuCallback(data, modelDeps);
|
|
90198
90903
|
if (outcome.selectedModel) {
|
|
90199
90904
|
sessionModelSource.setOverride(outcome.selectedModel);
|
|
90905
|
+
const smDir = resolveAgentDirFromEnv();
|
|
90906
|
+
if (smDir && outcome.selectedModelToken) {
|
|
90907
|
+
try {
|
|
90908
|
+
writeSessionModelFile(smDir, outcome.selectedModelToken, readConfiguredDefaultModel(smDir) ?? resolveMainModel(modelDeps.getConfiguredModel() ?? undefined));
|
|
90909
|
+
} catch (err) {
|
|
90910
|
+
outcome.reply.text += `
|
|
90911
|
+
\u26A0\uFE0F Couldn\u2019t persist the sticky override \u2014 the switch is live now but won\u2019t survive a relaunch.`;
|
|
90912
|
+
process.stderr.write(`telegram gateway: session-model persist failed (menu): ${err?.message ?? String(err)}
|
|
90913
|
+
`);
|
|
90914
|
+
}
|
|
90915
|
+
}
|
|
90916
|
+
}
|
|
90917
|
+
if (outcome.clearedDefault) {
|
|
90918
|
+
const smDir = resolveAgentDirFromEnv();
|
|
90919
|
+
if (smDir)
|
|
90920
|
+
clearSessionModelFile(smDir);
|
|
90200
90921
|
}
|
|
90201
90922
|
if (outcome.toastOnly && !didInterimSrEdit)
|
|
90202
90923
|
return;
|
|
@@ -90208,13 +90929,14 @@ bot.on("callback_query:data", async (ctx) => {
|
|
|
90208
90929
|
const token = outcome.selectedModelToken;
|
|
90209
90930
|
if (agentDir && token) {
|
|
90210
90931
|
try {
|
|
90211
|
-
|
|
90212
|
-
`, "utf8");
|
|
90932
|
+
writeSessionModelFile(agentDir, token, readConfiguredDefaultModel(agentDir) ?? resolveMainModel(modelDeps.getConfiguredModel() ?? undefined));
|
|
90213
90933
|
sessionModelSource.setOverride(token);
|
|
90214
90934
|
} catch (e) {
|
|
90215
|
-
process.stderr.write(`telegram gateway: sr-to-claude
|
|
90935
|
+
process.stderr.write(`telegram gateway: sr-to-claude session-model write failed: ${e?.message ?? String(e)}
|
|
90216
90936
|
`);
|
|
90217
90937
|
}
|
|
90938
|
+
} else if (agentDir) {
|
|
90939
|
+
clearSessionModelFile(agentDir);
|
|
90218
90940
|
}
|
|
90219
90941
|
}
|
|
90220
90942
|
writeRestartMarker({ chat_id: cbChatId, thread_id: cbThreadId ?? null, ack_message_id: null, ts: Date.now() });
|
|
@@ -90426,22 +91148,34 @@ ${preBlock(formatSwitchroomOutput(err.message ?? "unknown error"))}`, { html: tr
|
|
|
90426
91148
|
}
|
|
90427
91149
|
return;
|
|
90428
91150
|
})();
|
|
90429
|
-
|
|
90430
|
-
|
|
90431
|
-
|
|
90432
|
-
|
|
90433
|
-
|
|
90434
|
-
|
|
91151
|
+
const tok = token;
|
|
91152
|
+
const sendOpts = {
|
|
91153
|
+
...cbMessageId != null ? { reply_parameters: { message_id: cbMessageId } } : {},
|
|
91154
|
+
...cbThreadId != null ? { message_thread_id: cbThreadId } : {}
|
|
91155
|
+
};
|
|
91156
|
+
const sendVerbOpts = {
|
|
91157
|
+
chat_id: cbChatId,
|
|
91158
|
+
verb: "voice-ondemand.sendVoice",
|
|
91159
|
+
...cbThreadId != null ? { threadId: cbThreadId } : {}
|
|
91160
|
+
};
|
|
91161
|
+
const loadAudio = async () => {
|
|
91162
|
+
let audio = null;
|
|
91163
|
+
if (entry.filePath != null) {
|
|
91164
|
+
try {
|
|
91165
|
+
audio = readFileSync50(entry.filePath);
|
|
91166
|
+
} catch {
|
|
91167
|
+
audio = null;
|
|
91168
|
+
}
|
|
91169
|
+
}
|
|
91170
|
+
if (audio != null) {
|
|
91171
|
+
await ctx.answerCallbackQuery({ text: "\uD83D\uDD0A" }).catch(() => {});
|
|
91172
|
+
return audio;
|
|
90435
91173
|
}
|
|
90436
|
-
}
|
|
90437
|
-
if (audio != null) {
|
|
90438
|
-
await ctx.answerCallbackQuery({ text: "\uD83D\uDD0A" }).catch(() => {});
|
|
90439
|
-
} else {
|
|
90440
91174
|
await ctx.answerCallbackQuery({ text: "\uD83D\uDD0A Synthesizing\u2026" }).catch(() => {});
|
|
90441
91175
|
const sidecarToken = await materializeSidecarToken();
|
|
90442
91176
|
if (!sidecarToken) {
|
|
90443
91177
|
await ctx.answerCallbackQuery({ text: "Voice sidecar unavailable \u2014 try again later." }).catch(() => {});
|
|
90444
|
-
return;
|
|
91178
|
+
return null;
|
|
90445
91179
|
}
|
|
90446
91180
|
const result = await synthesizeViaSidecar({
|
|
90447
91181
|
token: sidecarToken,
|
|
@@ -90453,20 +91187,31 @@ ${preBlock(formatSwitchroomOutput(err.message ?? "unknown error"))}`, { html: tr
|
|
|
90453
91187
|
process.stderr.write(`telegram gateway: voice-out on-demand: synthesis failed reason=${result.reason}
|
|
90454
91188
|
`);
|
|
90455
91189
|
await ctx.answerCallbackQuery({ text: `Voice failed: ${result.reason}` }).catch(() => {});
|
|
90456
|
-
return;
|
|
91190
|
+
return null;
|
|
90457
91191
|
}
|
|
90458
|
-
|
|
91192
|
+
return result.audio;
|
|
91193
|
+
};
|
|
91194
|
+
if (entry.telegramFileId != null) {
|
|
91195
|
+
await ctx.answerCallbackQuery({ text: "\uD83D\uDD0A" }).catch(() => {});
|
|
91196
|
+
}
|
|
91197
|
+
const sendResult = await sendVoiceReusingFileId({
|
|
91198
|
+
fileId: entry.telegramFileId ?? null,
|
|
91199
|
+
sendByFileId: (fid) => robustApiCall(() => bot.api.sendVoice(cbChatId, fid, sendOpts), sendVerbOpts),
|
|
91200
|
+
loadAudio,
|
|
91201
|
+
sendByUpload: (audioOut) => robustApiCall(() => bot.api.sendVoice(cbChatId, new import_grammy12.InputFile(Buffer.from(audioOut)), sendOpts), sendVerbOpts),
|
|
91202
|
+
onFileId: (fid) => voiceOnDemandCache.setTelegramFileId(tok, fid),
|
|
91203
|
+
log: (line) => process.stderr.write(line)
|
|
91204
|
+
});
|
|
91205
|
+
if (!sendResult.ok) {
|
|
91206
|
+
if (sendResult.reason === "send-failed") {
|
|
91207
|
+
const err = sendResult.error;
|
|
91208
|
+
const msg2 = err instanceof Error ? err.message : String(err);
|
|
91209
|
+
process.stderr.write(`telegram gateway: voice-out on-demand: sendVoice failed (non-fatal): ${msg2}
|
|
91210
|
+
`);
|
|
91211
|
+
}
|
|
91212
|
+
return;
|
|
90459
91213
|
}
|
|
90460
|
-
const audioOut = audio;
|
|
90461
91214
|
try {
|
|
90462
|
-
await robustApiCall(() => bot.api.sendVoice(cbChatId, new import_grammy12.InputFile(Buffer.from(audioOut)), {
|
|
90463
|
-
...cbMessageId != null ? { reply_parameters: { message_id: cbMessageId } } : {},
|
|
90464
|
-
...cbThreadId != null ? { message_thread_id: cbThreadId } : {}
|
|
90465
|
-
}), {
|
|
90466
|
-
chat_id: cbChatId,
|
|
90467
|
-
verb: "voice-ondemand.sendVoice",
|
|
90468
|
-
...cbThreadId != null ? { threadId: cbThreadId } : {}
|
|
90469
|
-
});
|
|
90470
91215
|
if (cbMessageId != null) {
|
|
90471
91216
|
await robustApiCall(() => bot.api.editMessageReplyMarkup(cbChatId, cbMessageId, {
|
|
90472
91217
|
reply_markup: { inline_keyboard: [] }
|
|
@@ -90478,7 +91223,7 @@ ${preBlock(formatSwitchroomOutput(err.message ?? "unknown error"))}`, { html: tr
|
|
|
90478
91223
|
}
|
|
90479
91224
|
} catch (err) {
|
|
90480
91225
|
const msg2 = err instanceof Error ? err.message : String(err);
|
|
90481
|
-
process.stderr.write(`telegram gateway: voice-out on-demand:
|
|
91226
|
+
process.stderr.write(`telegram gateway: voice-out on-demand: strip-listen-keyboard failed (non-fatal): ${msg2}
|
|
90482
91227
|
`);
|
|
90483
91228
|
}
|
|
90484
91229
|
return;
|
|
@@ -90648,7 +91393,7 @@ ${interimLabel}` : interimLabel
|
|
|
90648
91393
|
const unifiedDiff = (() => {
|
|
90649
91394
|
try {
|
|
90650
91395
|
const cfgPath = process.env.SWITCHROOM_CONFIG ?? SWITCHROOM_CONFIG ?? findConfigFile2();
|
|
90651
|
-
const raw =
|
|
91396
|
+
const raw = readFileSync50(cfgPath, "utf8");
|
|
90652
91397
|
return synthesizeAllowRuleDiff({ agentName: agentName3, rule: chosen.rule, configText: raw });
|
|
90653
91398
|
} catch (err) {
|
|
90654
91399
|
process.stderr.write(`telegram gateway: always-allow diff synth failed: ${err.message}
|
|
@@ -90837,9 +91582,9 @@ bot.on("message:photo", async (ctx) => {
|
|
|
90837
91582
|
fileUniqueId: best.file_unique_id,
|
|
90838
91583
|
now: Date.now()
|
|
90839
91584
|
});
|
|
90840
|
-
|
|
91585
|
+
mkdirSync37(INBOX_DIR, { recursive: true, mode: 448 });
|
|
90841
91586
|
assertInsideInbox(INBOX_DIR, dlPath);
|
|
90842
|
-
|
|
91587
|
+
writeFileSync40(dlPath, buf, { mode: 384 });
|
|
90843
91588
|
return dlPath;
|
|
90844
91589
|
} catch (err) {
|
|
90845
91590
|
const msg = err instanceof Error ? err.message : "unknown error";
|
|
@@ -91932,7 +92677,7 @@ var didOneTimeSetup = false;
|
|
|
91932
92677
|
return;
|
|
91933
92678
|
}
|
|
91934
92679
|
})();
|
|
91935
|
-
const resolvedAgentDirForBootCard = agentDir ??
|
|
92680
|
+
const resolvedAgentDirForBootCard = agentDir ?? join51(homedir16(), ".switchroom", "agents", agentSlug);
|
|
91936
92681
|
const handle = await startBootCard(chatId, threadId, botApiForCard, {
|
|
91937
92682
|
agentName: agentDisplayName,
|
|
91938
92683
|
agentSlug,
|
|
@@ -91946,8 +92691,9 @@ var didOneTimeSetup = false;
|
|
|
91946
92691
|
probeQuotaViaBroker: (t) => probeQuotaForBootCard(agentSlug, t),
|
|
91947
92692
|
tmuxSupervisor: process.env.SWITCHROOM_TMUX_SUPERVISOR === "1",
|
|
91948
92693
|
dockerMode: process.env.SWITCHROOM_RUNTIME === "docker",
|
|
91949
|
-
configSnapshotPath:
|
|
91950
|
-
bootCardStatePath:
|
|
92694
|
+
configSnapshotPath: join51(resolvedAgentDirForBootCard, ".config-snapshot.json"),
|
|
92695
|
+
bootCardStatePath: join51(resolvedAgentDirForBootCard, ".boot-card-msgid.json"),
|
|
92696
|
+
floodStatePath: FLOOD_STATE_PATH,
|
|
91951
92697
|
...updateOutcomeLine ? { updateOutcomeLine } : {}
|
|
91952
92698
|
}, ackMsgId);
|
|
91953
92699
|
activeBootCard = handle;
|
|
@@ -91977,10 +92723,10 @@ var didOneTimeSetup = false;
|
|
|
91977
92723
|
try {
|
|
91978
92724
|
const smAgentDir = resolveAgentDirFromEnv();
|
|
91979
92725
|
if (smAgentDir) {
|
|
91980
|
-
const activePath =
|
|
91981
|
-
if (
|
|
92726
|
+
const activePath = join51(smAgentDir, ".active-session-model");
|
|
92727
|
+
if (existsSync47(activePath)) {
|
|
91982
92728
|
try {
|
|
91983
|
-
const launched =
|
|
92729
|
+
const launched = readFileSync50(activePath, "utf8").trim();
|
|
91984
92730
|
const configured = (() => {
|
|
91985
92731
|
const d = switchroomExecJson(["agent", "list"]);
|
|
91986
92732
|
const raw = d?.agents?.find((a) => a.name === getMyAgentName())?.model ?? null;
|
|
@@ -91989,11 +92735,11 @@ var didOneTimeSetup = false;
|
|
|
91989
92735
|
sessionModelSource.setOverride(launched.length > 0 && launched !== configured ? launched : null);
|
|
91990
92736
|
} catch {}
|
|
91991
92737
|
}
|
|
91992
|
-
const alertPath =
|
|
91993
|
-
if (
|
|
92738
|
+
const alertPath = join51(smAgentDir, ".session-model-alert");
|
|
92739
|
+
if (existsSync47(alertPath)) {
|
|
91994
92740
|
let alertText = null;
|
|
91995
92741
|
try {
|
|
91996
|
-
alertText =
|
|
92742
|
+
alertText = readFileSync50(alertPath, "utf8").trim();
|
|
91997
92743
|
} catch {
|
|
91998
92744
|
alertText = null;
|
|
91999
92745
|
}
|