switchroom 0.19.11 → 0.19.13
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 +1 -1
- package/dist/host-control/main.js +1 -1
- package/package.json +1 -1
- package/profiles/_base/cron-session.sh.hbs +5 -0
- package/profiles/_base/start.sh.hbs +11 -0
- package/telegram-plugin/dist/gateway/gateway.js +1382 -1032
- package/telegram-plugin/final-answer-detect.ts +23 -0
- package/telegram-plugin/gateway/gateway.ts +29 -36
- package/telegram-plugin/gateway/outbound-send-path.ts +109 -8
- package/telegram-plugin/gateway/outbox-sweep.ts +282 -0
- package/telegram-plugin/gateway/stream-render.ts +26 -50
- package/telegram-plugin/hooks/silent-end-interrupt-stop.mjs +94 -2
- package/telegram-plugin/hooks/silent-end-scan.mjs +279 -0
- package/telegram-plugin/outbox.ts +472 -0
- package/telegram-plugin/scripts/bun-test-ci.sh +91 -0
- package/telegram-plugin/temporal-normalize.ts +347 -0
- package/telegram-plugin/tests/gateway-outbound-redact.test.ts +22 -12
- package/telegram-plugin/tests/outbound-send-path.test.ts +191 -0
- package/telegram-plugin/tests/outbox-capture-scan.test.ts +222 -0
- package/telegram-plugin/tests/outbox-delivery.test.ts +278 -0
- package/telegram-plugin/tests/outbox-hook-capture.test.ts +112 -0
- package/telegram-plugin/tests/silent-end-interrupt-stop-integration.test.ts +60 -63
- package/telegram-plugin/tests/silent-end.test.ts +21 -28
- package/telegram-plugin/tests/temporal-normalize.test.ts +222 -0
- package/telegram-plugin/tests/turn-flush-safety.test.ts +23 -18
|
@@ -29063,6 +29063,9 @@ function isSubstantiveFinalReply(input) {
|
|
|
29063
29063
|
return true;
|
|
29064
29064
|
return false;
|
|
29065
29065
|
}
|
|
29066
|
+
function shouldJournalReplySiteDelivery(input) {
|
|
29067
|
+
return isSubstantiveFinalReply(input);
|
|
29068
|
+
}
|
|
29066
29069
|
var FINAL_ANSWER_MIN_CHARS2 = 200;
|
|
29067
29070
|
|
|
29068
29071
|
// history.ts
|
|
@@ -29088,8 +29091,8 @@ __export(exports_history, {
|
|
|
29088
29091
|
_resetForTests: () => _resetForTests,
|
|
29089
29092
|
MIN_PREFIX_MATCH_CHARS: () => MIN_PREFIX_MATCH_CHARS2
|
|
29090
29093
|
});
|
|
29091
|
-
import { chmodSync as chmodSync9, existsSync as
|
|
29092
|
-
import { join as
|
|
29094
|
+
import { chmodSync as chmodSync9, existsSync as existsSync26, mkdirSync as mkdirSync27 } from "fs";
|
|
29095
|
+
import { join as join30 } from "path";
|
|
29093
29096
|
function loadDatabaseClass2() {
|
|
29094
29097
|
if (DatabaseClass2 != null)
|
|
29095
29098
|
return DatabaseClass2;
|
|
@@ -29120,8 +29123,8 @@ function initHistory2(stateDir, retentionDays = 30) {
|
|
|
29120
29123
|
if (db2 != null)
|
|
29121
29124
|
return;
|
|
29122
29125
|
const Database = loadDatabaseClass2();
|
|
29123
|
-
|
|
29124
|
-
const path2 =
|
|
29126
|
+
mkdirSync27(stateDir, { recursive: true, mode: 448 });
|
|
29127
|
+
const path2 = join30(stateDir, "history.db");
|
|
29125
29128
|
dbPath2 = path2;
|
|
29126
29129
|
db2 = new Database(path2, { create: true });
|
|
29127
29130
|
db2.exec("PRAGMA journal_mode = WAL");
|
|
@@ -29187,7 +29190,7 @@ function initHistory2(stateDir, retentionDays = 30) {
|
|
|
29187
29190
|
}
|
|
29188
29191
|
for (const suffix of ["", "-shm", "-wal"]) {
|
|
29189
29192
|
const f = path2 + suffix;
|
|
29190
|
-
if (
|
|
29193
|
+
if (existsSync26(f)) {
|
|
29191
29194
|
try {
|
|
29192
29195
|
chmodSync9(f, 420);
|
|
29193
29196
|
} catch {}
|
|
@@ -29239,7 +29242,7 @@ function checkpointWal2() {
|
|
|
29239
29242
|
if (dbPath2) {
|
|
29240
29243
|
for (const suffix of ["-shm", "-wal"]) {
|
|
29241
29244
|
const f = dbPath2 + suffix;
|
|
29242
|
-
if (
|
|
29245
|
+
if (existsSync26(f)) {
|
|
29243
29246
|
try {
|
|
29244
29247
|
chmodSync9(f, 420);
|
|
29245
29248
|
} catch {}
|
|
@@ -29486,7 +29489,7 @@ var FLUSH_SUPPRESSION_WINDOW_MS = 2000;
|
|
|
29486
29489
|
var init_turn_flush_suppression = () => {};
|
|
29487
29490
|
|
|
29488
29491
|
// ../src/util/atomic.ts
|
|
29489
|
-
import { closeSync as closeSync6, constants as constants2, fchmodSync, fchownSync, fsyncSync as fsyncSync2, openSync as openSync6, renameSync as
|
|
29492
|
+
import { closeSync as closeSync6, constants as constants2, fchmodSync, fchownSync, fsyncSync as fsyncSync2, openSync as openSync6, renameSync as renameSync12, rmSync as rmSync5, writeSync as writeSync4 } from "node:fs";
|
|
29490
29493
|
var TMP_OPEN_FLAGS;
|
|
29491
29494
|
var init_atomic = __esm(() => {
|
|
29492
29495
|
TMP_OPEN_FLAGS = constants2.O_WRONLY | constants2.O_CREAT | constants2.O_EXCL | (constants2.O_NOFOLLOW ?? 0);
|
|
@@ -32448,7 +32451,7 @@ var require_util = __commonJS((exports2) => {
|
|
|
32448
32451
|
return path2;
|
|
32449
32452
|
}
|
|
32450
32453
|
exports2.normalize = normalize;
|
|
32451
|
-
function
|
|
32454
|
+
function join38(aRoot, aPath) {
|
|
32452
32455
|
if (aRoot === "") {
|
|
32453
32456
|
aRoot = ".";
|
|
32454
32457
|
}
|
|
@@ -32480,7 +32483,7 @@ var require_util = __commonJS((exports2) => {
|
|
|
32480
32483
|
}
|
|
32481
32484
|
return joined;
|
|
32482
32485
|
}
|
|
32483
|
-
exports2.join =
|
|
32486
|
+
exports2.join = join38;
|
|
32484
32487
|
exports2.isAbsolute = function(aPath) {
|
|
32485
32488
|
return aPath.charAt(0) === "/" || urlRegexp.test(aPath);
|
|
32486
32489
|
};
|
|
@@ -32653,7 +32656,7 @@ var require_util = __commonJS((exports2) => {
|
|
|
32653
32656
|
parsed.path = parsed.path.substring(0, index2 + 1);
|
|
32654
32657
|
}
|
|
32655
32658
|
}
|
|
32656
|
-
sourceURL =
|
|
32659
|
+
sourceURL = join38(urlGenerate(parsed), sourceURL);
|
|
32657
32660
|
}
|
|
32658
32661
|
return normalize(sourceURL);
|
|
32659
32662
|
}
|
|
@@ -35258,19 +35261,19 @@ function renderAuthLine(state7, agentName3, now = Date.now()) {
|
|
|
35258
35261
|
}
|
|
35259
35262
|
|
|
35260
35263
|
// gateway/quota-cache.ts
|
|
35261
|
-
import { existsSync as
|
|
35262
|
-
import { join as
|
|
35264
|
+
import { existsSync as existsSync42, readFileSync as readFileSync43, writeFileSync as writeFileSync36, mkdirSync as mkdirSync37 } from "fs";
|
|
35265
|
+
import { join as join47, dirname as dirname15 } from "path";
|
|
35263
35266
|
function defaultCachePath() {
|
|
35264
|
-
return process.env.SWITCHROOM_QUOTA_CACHE_PATH ??
|
|
35267
|
+
return process.env.SWITCHROOM_QUOTA_CACHE_PATH ?? join47(process.env.HOME ?? "/tmp", ".switchroom", "quota-cache.json");
|
|
35265
35268
|
}
|
|
35266
35269
|
function readQuotaCache(opts = {}) {
|
|
35267
35270
|
const path2 = opts.path ?? defaultCachePath();
|
|
35268
35271
|
const now = opts.now ?? Date.now();
|
|
35269
|
-
if (!
|
|
35272
|
+
if (!existsSync42(path2))
|
|
35270
35273
|
return null;
|
|
35271
35274
|
let entry;
|
|
35272
35275
|
try {
|
|
35273
|
-
entry = JSON.parse(
|
|
35276
|
+
entry = JSON.parse(readFileSync43(path2, "utf8"));
|
|
35274
35277
|
} catch {
|
|
35275
35278
|
return null;
|
|
35276
35279
|
}
|
|
@@ -35296,8 +35299,8 @@ function writeQuotaCache(result, opts = {}) {
|
|
|
35296
35299
|
result
|
|
35297
35300
|
};
|
|
35298
35301
|
try {
|
|
35299
|
-
|
|
35300
|
-
|
|
35302
|
+
mkdirSync37(dirname15(path2), { recursive: true });
|
|
35303
|
+
writeFileSync36(path2, JSON.stringify(entry, null, 2), { mode: 384 });
|
|
35301
35304
|
} catch {}
|
|
35302
35305
|
}
|
|
35303
35306
|
var DEFAULT_TTL_MS4, RATE_LIMIT_TTL_MS;
|
|
@@ -35307,8 +35310,8 @@ var init_quota_cache = __esm(() => {
|
|
|
35307
35310
|
});
|
|
35308
35311
|
|
|
35309
35312
|
// gateway/boot-probes.ts
|
|
35310
|
-
import { readFileSync as
|
|
35311
|
-
import { join as
|
|
35313
|
+
import { readFileSync as readFileSync44, readdirSync as readdirSync10, existsSync as existsSync43 } from "fs";
|
|
35314
|
+
import { join as join48 } from "path";
|
|
35312
35315
|
import { execFile as execFileCb } from "child_process";
|
|
35313
35316
|
import { promisify } from "util";
|
|
35314
35317
|
async function withTimeout(label, p, timeoutMs = PROBE_TIMEOUT_MS) {
|
|
@@ -35350,11 +35353,11 @@ function mapPlan(billingType, hasExtra) {
|
|
|
35350
35353
|
}
|
|
35351
35354
|
async function probeAccount(agentDir) {
|
|
35352
35355
|
return withTimeout("Account", (async () => {
|
|
35353
|
-
const claudeDir =
|
|
35354
|
-
const claudeJsonPath =
|
|
35356
|
+
const claudeDir = join48(agentDir, ".claude");
|
|
35357
|
+
const claudeJsonPath = join48(claudeDir, ".claude.json");
|
|
35355
35358
|
let cfg = {};
|
|
35356
35359
|
try {
|
|
35357
|
-
const raw =
|
|
35360
|
+
const raw = readFileSync44(claudeJsonPath, "utf8");
|
|
35358
35361
|
cfg = JSON.parse(raw);
|
|
35359
35362
|
} catch {
|
|
35360
35363
|
return { status: "fail", label: "Account", detail: "no .claude.json" };
|
|
@@ -35372,12 +35375,12 @@ async function probeAccount(agentDir) {
|
|
|
35372
35375
|
let tokenStr = "";
|
|
35373
35376
|
let status = "ok";
|
|
35374
35377
|
for (const candidate of [
|
|
35375
|
-
|
|
35376
|
-
|
|
35378
|
+
join48(claudeDir, ".oauth-token.meta.json"),
|
|
35379
|
+
join48(claudeDir, "accounts", "default", ".oauth-token.meta.json")
|
|
35377
35380
|
]) {
|
|
35378
|
-
if (
|
|
35381
|
+
if (existsSync43(candidate)) {
|
|
35379
35382
|
try {
|
|
35380
|
-
const meta = JSON.parse(
|
|
35383
|
+
const meta = JSON.parse(readFileSync44(candidate, "utf8"));
|
|
35381
35384
|
if (meta.expiresAt) {
|
|
35382
35385
|
tokenStr = " \u00b7 " + formatDaysFromNow(meta.expiresAt);
|
|
35383
35386
|
const daysLeft = Math.round((meta.expiresAt - Date.now()) / 86400000);
|
|
@@ -35552,9 +35555,9 @@ async function resolveTmuxSupervisorPid(agentName3, execFileImpl) {
|
|
|
35552
35555
|
if (!cgroup)
|
|
35553
35556
|
return null;
|
|
35554
35557
|
const procsPath = `/sys/fs/cgroup${cgroup}/cgroup.procs`;
|
|
35555
|
-
if (!
|
|
35558
|
+
if (!existsSync43(procsPath))
|
|
35556
35559
|
return null;
|
|
35557
|
-
const pidsRaw =
|
|
35560
|
+
const pidsRaw = readFileSync44(procsPath, "utf-8");
|
|
35558
35561
|
const pids = pidsRaw.split(`
|
|
35559
35562
|
`).map((s) => s.trim()).filter(Boolean);
|
|
35560
35563
|
if (pids.length === 0)
|
|
@@ -35567,7 +35570,7 @@ async function resolveTmuxSupervisorPid(agentName3, execFileImpl) {
|
|
|
35567
35570
|
let rss = 0;
|
|
35568
35571
|
let comm = "";
|
|
35569
35572
|
try {
|
|
35570
|
-
const status =
|
|
35573
|
+
const status = readFileSync44(`/proc/${pid}/status`, "utf-8");
|
|
35571
35574
|
const rssLine = status.split(`
|
|
35572
35575
|
`).find((l) => l.startsWith("VmRSS:"));
|
|
35573
35576
|
if (rssLine) {
|
|
@@ -35579,7 +35582,7 @@ async function resolveTmuxSupervisorPid(agentName3, execFileImpl) {
|
|
|
35579
35582
|
continue;
|
|
35580
35583
|
}
|
|
35581
35584
|
try {
|
|
35582
|
-
comm =
|
|
35585
|
+
comm = readFileSync44(`/proc/${pid}/comm`, "utf-8").trim();
|
|
35583
35586
|
} catch {}
|
|
35584
35587
|
candidates.push({ pid, rss, comm });
|
|
35585
35588
|
}
|
|
@@ -35759,9 +35762,9 @@ async function probeQuota(claudeConfigDir, _agentDir, fetchImpl = fetch, opts =
|
|
|
35759
35762
|
let claudeDirForProbe = null;
|
|
35760
35763
|
for (const candidate of [
|
|
35761
35764
|
claudeConfigDir,
|
|
35762
|
-
|
|
35765
|
+
join48(claudeConfigDir, "accounts", "default")
|
|
35763
35766
|
]) {
|
|
35764
|
-
if (
|
|
35767
|
+
if (existsSync43(join48(candidate, ".oauth-token"))) {
|
|
35765
35768
|
claudeDirForProbe = candidate;
|
|
35766
35769
|
break;
|
|
35767
35770
|
}
|
|
@@ -35826,7 +35829,7 @@ async function probeHindsight(bankName, fetchImpl = fetch) {
|
|
|
35826
35829
|
}
|
|
35827
35830
|
function readContainerBootTimeMsForProbe() {
|
|
35828
35831
|
try {
|
|
35829
|
-
const stat1 =
|
|
35832
|
+
const stat1 = readFileSync44("/proc/1/stat", "utf8");
|
|
35830
35833
|
const lastParen = stat1.lastIndexOf(")");
|
|
35831
35834
|
if (lastParen < 0)
|
|
35832
35835
|
return null;
|
|
@@ -35834,7 +35837,7 @@ function readContainerBootTimeMsForProbe() {
|
|
|
35834
35837
|
const starttimeTicks = Number(after[19]);
|
|
35835
35838
|
if (!Number.isFinite(starttimeTicks))
|
|
35836
35839
|
return null;
|
|
35837
|
-
const procStat =
|
|
35840
|
+
const procStat = readFileSync44("/proc/stat", "utf8");
|
|
35838
35841
|
const btimeLine = procStat.split(`
|
|
35839
35842
|
`).find((l) => l.startsWith("btime "));
|
|
35840
35843
|
if (!btimeLine)
|
|
@@ -35932,7 +35935,7 @@ async function probeUds(label, socketPath, opts = {}) {
|
|
|
35932
35935
|
}
|
|
35933
35936
|
return withTimeout(label, (async () => {
|
|
35934
35937
|
if (!opts.connectImpl) {
|
|
35935
|
-
if (!
|
|
35938
|
+
if (!existsSync43(socketPath)) {
|
|
35936
35939
|
return {
|
|
35937
35940
|
status: "fail",
|
|
35938
35941
|
label,
|
|
@@ -35996,7 +35999,7 @@ async function probeSkills(agentDir, opts = {}) {
|
|
|
35996
35999
|
return withTimeout("Skills", (async () => {
|
|
35997
36000
|
const fs2 = opts.fs ?? realSkillsFs;
|
|
35998
36001
|
const max = opts.maxNamesShown ?? 3;
|
|
35999
|
-
const skillsDir =
|
|
36002
|
+
const skillsDir = join48(agentDir, ".claude", "skills");
|
|
36000
36003
|
if (!fs2.exists(skillsDir)) {
|
|
36001
36004
|
return { status: "ok", label: "Skills", detail: "no skills dir" };
|
|
36002
36005
|
}
|
|
@@ -36011,17 +36014,17 @@ async function probeSkills(agentDir, opts = {}) {
|
|
|
36011
36014
|
}
|
|
36012
36015
|
const dangling = [];
|
|
36013
36016
|
for (const name of entries) {
|
|
36014
|
-
const skillPath =
|
|
36017
|
+
const skillPath = join48(skillsDir, name);
|
|
36015
36018
|
if (!fs2.exists(skillPath)) {
|
|
36016
36019
|
dangling.push(name);
|
|
36017
36020
|
continue;
|
|
36018
36021
|
}
|
|
36019
|
-
const skillMd =
|
|
36022
|
+
const skillMd = join48(skillPath, "SKILL.md");
|
|
36020
36023
|
if (!fs2.exists(skillMd) && !fs2.exists(skillPath + ".md")) {
|
|
36021
36024
|
continue;
|
|
36022
36025
|
}
|
|
36023
36026
|
}
|
|
36024
|
-
const overlayDir = opts.overlaySkillsDir ??
|
|
36027
|
+
const overlayDir = opts.overlaySkillsDir ?? join48(agentDir, "skills.d");
|
|
36025
36028
|
const overlaySlugs = new Set;
|
|
36026
36029
|
if (fs2.exists(overlayDir)) {
|
|
36027
36030
|
let overlayEntries = [];
|
|
@@ -36063,8 +36066,8 @@ function renderBucketedSkills(switchroom, agent) {
|
|
|
36063
36066
|
}
|
|
36064
36067
|
async function probeConnections(agentDir, opts = {}) {
|
|
36065
36068
|
return withTimeout("Connections", (async () => {
|
|
36066
|
-
const path2 =
|
|
36067
|
-
const read = opts.readFileImpl ?? ((p) =>
|
|
36069
|
+
const path2 = join48(agentDir, ".claude", "connection-health.json");
|
|
36070
|
+
const read = opts.readFileImpl ?? ((p) => readFileSync44(p, "utf8"));
|
|
36068
36071
|
let issues = [];
|
|
36069
36072
|
try {
|
|
36070
36073
|
const parsed = JSON.parse(read(path2));
|
|
@@ -36094,25 +36097,25 @@ var init_boot_probes = __esm(() => {
|
|
|
36094
36097
|
init_quota_check();
|
|
36095
36098
|
execFile = promisify(execFileCb);
|
|
36096
36099
|
realProcFs = {
|
|
36097
|
-
readdir: (p) =>
|
|
36098
|
-
readFile: (p) =>
|
|
36100
|
+
readdir: (p) => readdirSync10(p),
|
|
36101
|
+
readFile: (p) => readFileSync44(p, "utf-8")
|
|
36099
36102
|
};
|
|
36100
36103
|
realSchedulerFs = {
|
|
36101
|
-
readFile: (p) =>
|
|
36104
|
+
readFile: (p) => readFileSync44(p, "utf-8"),
|
|
36102
36105
|
mtimeMs: (p) => {
|
|
36103
|
-
const { statSync:
|
|
36104
|
-
return
|
|
36106
|
+
const { statSync: statSync14 } = __require("fs");
|
|
36107
|
+
return statSync14(p).mtimeMs;
|
|
36105
36108
|
},
|
|
36106
|
-
exists: (p) =>
|
|
36109
|
+
exists: (p) => existsSync43(p)
|
|
36107
36110
|
};
|
|
36108
36111
|
realSkillsFs = {
|
|
36109
|
-
readdir: (p) =>
|
|
36110
|
-
exists: (p) =>
|
|
36112
|
+
readdir: (p) => readdirSync10(p),
|
|
36113
|
+
exists: (p) => existsSync43(p)
|
|
36111
36114
|
};
|
|
36112
36115
|
});
|
|
36113
36116
|
|
|
36114
36117
|
// gateway/boot-issue-cache.ts
|
|
36115
|
-
import { existsSync as
|
|
36118
|
+
import { existsSync as existsSync44, readFileSync as readFileSync45, writeFileSync as writeFileSync37, mkdirSync as mkdirSync38, renameSync as renameSync18 } from "fs";
|
|
36116
36119
|
import { dirname as dirname16 } from "path";
|
|
36117
36120
|
function fingerprintProbe(key, r) {
|
|
36118
36121
|
if (r.status === "ok")
|
|
@@ -36192,11 +36195,11 @@ function diffProbes(probes, cache, opts = {}) {
|
|
|
36192
36195
|
return out;
|
|
36193
36196
|
}
|
|
36194
36197
|
function loadCache(path2, now = Date.now) {
|
|
36195
|
-
if (!
|
|
36198
|
+
if (!existsSync44(path2))
|
|
36196
36199
|
return { ...EMPTY_CACHE, probes: {} };
|
|
36197
36200
|
let raw;
|
|
36198
36201
|
try {
|
|
36199
|
-
raw =
|
|
36202
|
+
raw = readFileSync45(path2, "utf-8");
|
|
36200
36203
|
} catch {
|
|
36201
36204
|
return { ...EMPTY_CACHE, probes: {} };
|
|
36202
36205
|
}
|
|
@@ -36205,7 +36208,7 @@ function loadCache(path2, now = Date.now) {
|
|
|
36205
36208
|
parsed = JSON.parse(raw);
|
|
36206
36209
|
} catch {
|
|
36207
36210
|
try {
|
|
36208
|
-
|
|
36211
|
+
renameSync18(path2, `${path2}.corrupt-${now()}`);
|
|
36209
36212
|
} catch {}
|
|
36210
36213
|
return { ...EMPTY_CACHE, probes: {} };
|
|
36211
36214
|
}
|
|
@@ -36239,10 +36242,10 @@ function applyAndSave(path2, cache, diff) {
|
|
|
36239
36242
|
}
|
|
36240
36243
|
}
|
|
36241
36244
|
try {
|
|
36242
|
-
|
|
36245
|
+
mkdirSync38(dirname16(path2), { recursive: true });
|
|
36243
36246
|
const tmp = `${path2}.tmp`;
|
|
36244
|
-
|
|
36245
|
-
|
|
36247
|
+
writeFileSync37(tmp, JSON.stringify(next), { mode: 384 });
|
|
36248
|
+
renameSync18(tmp, path2);
|
|
36246
36249
|
} catch {}
|
|
36247
36250
|
return next;
|
|
36248
36251
|
}
|
|
@@ -36254,14 +36257,14 @@ var init_boot_issue_cache = __esm(() => {
|
|
|
36254
36257
|
});
|
|
36255
36258
|
|
|
36256
36259
|
// gateway/config-snapshot.ts
|
|
36257
|
-
import { createHash as
|
|
36258
|
-
import { existsSync as
|
|
36260
|
+
import { createHash as createHash4 } from "crypto";
|
|
36261
|
+
import { existsSync as existsSync45, readFileSync as readFileSync46, writeFileSync as writeFileSync38, mkdirSync as mkdirSync39, renameSync as renameSync19 } from "fs";
|
|
36259
36262
|
import { dirname as dirname17 } from "path";
|
|
36260
36263
|
function hashStringArray(items) {
|
|
36261
36264
|
if (!items || items.length === 0)
|
|
36262
36265
|
return null;
|
|
36263
36266
|
const sorted = [...items].sort();
|
|
36264
|
-
const raw =
|
|
36267
|
+
const raw = createHash4("sha256").update(sorted.join("\x00")).digest("hex");
|
|
36265
36268
|
return raw.slice(0, 12);
|
|
36266
36269
|
}
|
|
36267
36270
|
function normalizeModel(model) {
|
|
@@ -36320,11 +36323,11 @@ function renderConfigChangeDim(dim) {
|
|
|
36320
36323
|
}
|
|
36321
36324
|
}
|
|
36322
36325
|
function loadSnapshot(path2, now = Date.now) {
|
|
36323
|
-
if (!
|
|
36326
|
+
if (!existsSync45(path2))
|
|
36324
36327
|
return null;
|
|
36325
36328
|
let raw;
|
|
36326
36329
|
try {
|
|
36327
|
-
raw =
|
|
36330
|
+
raw = readFileSync46(path2, "utf-8");
|
|
36328
36331
|
} catch {
|
|
36329
36332
|
return null;
|
|
36330
36333
|
}
|
|
@@ -36333,7 +36336,7 @@ function loadSnapshot(path2, now = Date.now) {
|
|
|
36333
36336
|
parsed = JSON.parse(raw);
|
|
36334
36337
|
} catch {
|
|
36335
36338
|
try {
|
|
36336
|
-
|
|
36339
|
+
renameSync19(path2, `${path2}.corrupt-${now()}`);
|
|
36337
36340
|
} catch {}
|
|
36338
36341
|
return null;
|
|
36339
36342
|
}
|
|
@@ -36354,10 +36357,10 @@ function loadSnapshot(path2, now = Date.now) {
|
|
|
36354
36357
|
}
|
|
36355
36358
|
function persistSnapshot(path2, snapshot) {
|
|
36356
36359
|
try {
|
|
36357
|
-
|
|
36360
|
+
mkdirSync39(dirname17(path2), { recursive: true });
|
|
36358
36361
|
const tmp = `${path2}.tmp`;
|
|
36359
|
-
|
|
36360
|
-
|
|
36362
|
+
writeFileSync38(tmp, JSON.stringify(snapshot), { mode: 384 });
|
|
36363
|
+
renameSync19(tmp, path2);
|
|
36361
36364
|
} catch {}
|
|
36362
36365
|
}
|
|
36363
36366
|
var init_config_snapshot = __esm(() => {
|
|
@@ -36365,13 +36368,13 @@ var init_config_snapshot = __esm(() => {
|
|
|
36365
36368
|
});
|
|
36366
36369
|
|
|
36367
36370
|
// gateway/boot-card-msgid.ts
|
|
36368
|
-
import { readFileSync as
|
|
36371
|
+
import { readFileSync as readFileSync47, writeFileSync as writeFileSync39 } from "node:fs";
|
|
36369
36372
|
function bootCardChatKey(chatId, threadId) {
|
|
36370
36373
|
return `${chatId}:${threadId ?? ""}`;
|
|
36371
36374
|
}
|
|
36372
36375
|
function readStore(path2) {
|
|
36373
36376
|
try {
|
|
36374
|
-
const parsed = JSON.parse(
|
|
36377
|
+
const parsed = JSON.parse(readFileSync47(path2, "utf8"));
|
|
36375
36378
|
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
36376
36379
|
return parsed;
|
|
36377
36380
|
}
|
|
@@ -36390,7 +36393,7 @@ function saveBootCardMsgId(path2, chatKey3, messageId) {
|
|
|
36390
36393
|
if (store2[chatKey3] === messageId)
|
|
36391
36394
|
return;
|
|
36392
36395
|
store2[chatKey3] = messageId;
|
|
36393
|
-
|
|
36396
|
+
writeFileSync39(path2, JSON.stringify(store2), "utf8");
|
|
36394
36397
|
} catch {}
|
|
36395
36398
|
}
|
|
36396
36399
|
var init_boot_card_msgid = () => {};
|
|
@@ -36408,8 +36411,8 @@ __export(exports_boot_card, {
|
|
|
36408
36411
|
readRoutingMode: () => readRoutingMode,
|
|
36409
36412
|
containerBootStartMs: () => containerBootStartMs
|
|
36410
36413
|
});
|
|
36411
|
-
import { join as
|
|
36412
|
-
import { readFileSync as
|
|
36414
|
+
import { join as join49 } from "path";
|
|
36415
|
+
import { readFileSync as readFileSync48, statSync as statSync14 } from "fs";
|
|
36413
36416
|
function resolvePersonaName(slug, loadConfig3) {
|
|
36414
36417
|
try {
|
|
36415
36418
|
const config = loadConfig3 ? loadConfig3() : loadConfig();
|
|
@@ -36431,7 +36434,7 @@ function shouldSkipDuplicateBootCard(gate, site) {
|
|
|
36431
36434
|
}
|
|
36432
36435
|
return { skip: false };
|
|
36433
36436
|
}
|
|
36434
|
-
function containerBootStartMs(fsImpl = { readFileSync:
|
|
36437
|
+
function containerBootStartMs(fsImpl = { readFileSync: readFileSync48 }) {
|
|
36435
36438
|
try {
|
|
36436
36439
|
const btimeLine = fsImpl.readFileSync("/proc/stat", "utf-8").split(`
|
|
36437
36440
|
`).find((l) => l.startsWith("btime "));
|
|
@@ -36452,8 +36455,8 @@ function containerBootStartMs(fsImpl = { readFileSync: readFileSync47 }) {
|
|
|
36452
36455
|
return null;
|
|
36453
36456
|
}
|
|
36454
36457
|
}
|
|
36455
|
-
function readRoutingMode(agentDir, bootStartMs, fsImpl = { readFileSync:
|
|
36456
|
-
const path2 =
|
|
36458
|
+
function readRoutingMode(agentDir, bootStartMs, fsImpl = { readFileSync: readFileSync48, statSync: statSync14 }) {
|
|
36459
|
+
const path2 = join49(agentDir, ".routing-mode");
|
|
36457
36460
|
try {
|
|
36458
36461
|
const raw = fsImpl.readFileSync(path2, "utf-8");
|
|
36459
36462
|
const line = raw.split(`
|
|
@@ -36560,7 +36563,7 @@ function renderBootCard(opts) {
|
|
|
36560
36563
|
return stackCardLines(flatLines);
|
|
36561
36564
|
}
|
|
36562
36565
|
async function runAllProbes(opts) {
|
|
36563
|
-
const claudeDir =
|
|
36566
|
+
const claudeDir = join49(opts.agentDir, ".claude");
|
|
36564
36567
|
const probes = {};
|
|
36565
36568
|
const slug = opts.agentSlug ?? opts.agentName;
|
|
36566
36569
|
await Promise.allSettled([
|
|
@@ -37414,7 +37417,7 @@ __export(exports_tmux2, {
|
|
|
37414
37417
|
captureAgentPane: () => captureAgentPane2
|
|
37415
37418
|
});
|
|
37416
37419
|
import { execFileSync as execFileSync8 } from "node:child_process";
|
|
37417
|
-
import { chmodSync as chmodSync13, mkdirSync as
|
|
37420
|
+
import { chmodSync as chmodSync13, mkdirSync as mkdirSync47, readdirSync as readdirSync13, statSync as statSync19, unlinkSync as unlinkSync26, writeFileSync as writeFileSync48 } from "node:fs";
|
|
37418
37421
|
import { resolve as resolve12 } from "node:path";
|
|
37419
37422
|
function captureAgentPane2(opts) {
|
|
37420
37423
|
const { agentName: agentName3, agentDir, reason } = opts;
|
|
@@ -37426,7 +37429,7 @@ function captureAgentPane2(opts) {
|
|
|
37426
37429
|
const reasonSlug = sanitizeReason2(reason);
|
|
37427
37430
|
const outPath = resolve12(outDir, `${ts}-${reasonSlug}.txt`);
|
|
37428
37431
|
try {
|
|
37429
|
-
|
|
37432
|
+
mkdirSync47(outDir, { recursive: true, mode: 448 });
|
|
37430
37433
|
} catch (err) {
|
|
37431
37434
|
const msg = `mkdir crash-reports failed: ${err.message}`;
|
|
37432
37435
|
console.error(`[tmux-capture] ${agentName3}: ${msg}`);
|
|
@@ -37465,7 +37468,7 @@ function captureAgentPane2(opts) {
|
|
|
37465
37468
|
` + `
|
|
37466
37469
|
`;
|
|
37467
37470
|
try {
|
|
37468
|
-
|
|
37471
|
+
writeFileSync48(outPath, Buffer.concat([Buffer.from(header, "utf8"), body]), {
|
|
37469
37472
|
mode: 384
|
|
37470
37473
|
});
|
|
37471
37474
|
} catch (err) {
|
|
@@ -37535,7 +37538,7 @@ function sleepSync3(ms) {
|
|
|
37535
37538
|
function pruneOldReports2(dir, retain) {
|
|
37536
37539
|
let entries;
|
|
37537
37540
|
try {
|
|
37538
|
-
entries =
|
|
37541
|
+
entries = readdirSync13(dir);
|
|
37539
37542
|
} catch {
|
|
37540
37543
|
return;
|
|
37541
37544
|
}
|
|
@@ -37543,13 +37546,13 @@ function pruneOldReports2(dir, retain) {
|
|
|
37543
37546
|
const full = resolve12(dir, n);
|
|
37544
37547
|
let mtimeMs = 0;
|
|
37545
37548
|
try {
|
|
37546
|
-
mtimeMs =
|
|
37549
|
+
mtimeMs = statSync19(full).mtimeMs;
|
|
37547
37550
|
} catch {}
|
|
37548
37551
|
return { full, mtimeMs };
|
|
37549
37552
|
}).sort((a, b) => b.mtimeMs - a.mtimeMs);
|
|
37550
37553
|
for (const stale of files.slice(retain)) {
|
|
37551
37554
|
try {
|
|
37552
|
-
|
|
37555
|
+
unlinkSync26(stale.full);
|
|
37553
37556
|
} catch {}
|
|
37554
37557
|
}
|
|
37555
37558
|
}
|
|
@@ -38286,7 +38289,7 @@ function buildGrantedKeyboard(scope) {
|
|
|
38286
38289
|
const btn = scopeToOpenInDriveButton(scope);
|
|
38287
38290
|
if (btn === null)
|
|
38288
38291
|
return;
|
|
38289
|
-
return new
|
|
38292
|
+
return new import_grammy15.InlineKeyboard().url(btn.text, btn.url);
|
|
38290
38293
|
}
|
|
38291
38294
|
async function handleApprovalCallback(ctx, data) {
|
|
38292
38295
|
const parsed = parseApprovalCallback(data);
|
|
@@ -38332,11 +38335,11 @@ async function handleApprovalCallback(ctx, data) {
|
|
|
38332
38335
|
} catch {}
|
|
38333
38336
|
await ctx.answerCallbackQuery({ text: granted ? "Approved" : "Denied" });
|
|
38334
38337
|
}
|
|
38335
|
-
var
|
|
38338
|
+
var import_grammy15;
|
|
38336
38339
|
var init_approval_callback = __esm(() => {
|
|
38337
38340
|
init_approval_card();
|
|
38338
38341
|
init_client3();
|
|
38339
|
-
|
|
38342
|
+
import_grammy15 = __toESM(require_mod2(), 1);
|
|
38340
38343
|
});
|
|
38341
38344
|
|
|
38342
38345
|
// ../src/telegram/materialize-bot-token.ts
|
|
@@ -38345,7 +38348,7 @@ __export(exports_materialize_bot_token, {
|
|
|
38345
38348
|
materializeBotToken: () => materializeBotToken,
|
|
38346
38349
|
BotTokenMaterializeError: () => BotTokenMaterializeError
|
|
38347
38350
|
});
|
|
38348
|
-
import { existsSync as
|
|
38351
|
+
import { existsSync as existsSync54 } from "node:fs";
|
|
38349
38352
|
function pickConfiguredToken(config, agentName3) {
|
|
38350
38353
|
if (agentName3) {
|
|
38351
38354
|
const agent = config.agents?.[agentName3];
|
|
@@ -38359,7 +38362,7 @@ function tryDirectVaultRead4(ref, config, passphrase) {
|
|
|
38359
38362
|
if (!passphrase)
|
|
38360
38363
|
return null;
|
|
38361
38364
|
const vaultPath = resolvePath(config.vault?.path ?? "~/.switchroom/vault.enc");
|
|
38362
|
-
if (!
|
|
38365
|
+
if (!existsSync54(vaultPath))
|
|
38363
38366
|
return null;
|
|
38364
38367
|
try {
|
|
38365
38368
|
const secrets = openVault(passphrase, vaultPath);
|
|
@@ -38509,28 +38512,28 @@ var init_approvals_commands = __esm(() => {
|
|
|
38509
38512
|
});
|
|
38510
38513
|
|
|
38511
38514
|
// gateway/gateway.ts
|
|
38512
|
-
var
|
|
38515
|
+
var import_grammy16 = __toESM(require_mod(), 1);
|
|
38513
38516
|
var import_runner3 = __toESM(require_mod3(), 1);
|
|
38514
|
-
import { randomBytes as randomBytes10, createHash as
|
|
38517
|
+
import { randomBytes as randomBytes10, createHash as createHash5 } from "crypto";
|
|
38515
38518
|
import { execFileSync as execFileSync9, execSync as execSync2, spawn as spawn2 } from "child_process";
|
|
38516
38519
|
import {
|
|
38517
|
-
readFileSync as
|
|
38518
|
-
writeFileSync as
|
|
38519
|
-
mkdirSync as
|
|
38520
|
-
readdirSync as
|
|
38520
|
+
readFileSync as readFileSync59,
|
|
38521
|
+
writeFileSync as writeFileSync49,
|
|
38522
|
+
mkdirSync as mkdirSync48,
|
|
38523
|
+
readdirSync as readdirSync14,
|
|
38521
38524
|
rmSync as rmSync6,
|
|
38522
|
-
statSync as
|
|
38523
|
-
renameSync as
|
|
38525
|
+
statSync as statSync20,
|
|
38526
|
+
renameSync as renameSync22,
|
|
38524
38527
|
realpathSync as realpathSync4,
|
|
38525
38528
|
chmodSync as chmodSync14,
|
|
38526
38529
|
openSync as openSync12,
|
|
38527
38530
|
closeSync as closeSync12,
|
|
38528
|
-
existsSync as
|
|
38529
|
-
unlinkSync as
|
|
38530
|
-
appendFileSync as
|
|
38531
|
+
existsSync as existsSync55,
|
|
38532
|
+
unlinkSync as unlinkSync27,
|
|
38533
|
+
appendFileSync as appendFileSync8
|
|
38531
38534
|
} from "fs";
|
|
38532
|
-
import { homedir as
|
|
38533
|
-
import { join as
|
|
38535
|
+
import { homedir as homedir20 } from "os";
|
|
38536
|
+
import { join as join61, sep as sep4, basename as basename15 } from "path";
|
|
38534
38537
|
|
|
38535
38538
|
// plugin-logger.ts
|
|
38536
38539
|
import { appendFileSync, mkdirSync, renameSync, statSync, existsSync } from "fs";
|
|
@@ -41573,7 +41576,16 @@ import { AsyncLocalStorage } from "async_hooks";
|
|
|
41573
41576
|
|
|
41574
41577
|
// retry-api-call.ts
|
|
41575
41578
|
var import_grammy2 = __toESM(require_mod2(), 1);
|
|
41579
|
+
function isLocalResourceError(err) {
|
|
41580
|
+
const code = err?.code;
|
|
41581
|
+
if (typeof code === "string" && ["ENOSPC", "EDQUOT", "EIO", "ENOMEM"].includes(code)) {
|
|
41582
|
+
return true;
|
|
41583
|
+
}
|
|
41584
|
+
const msg = err instanceof Error ? err.message : String(err ?? "");
|
|
41585
|
+
return /\b(ENOSPC|EDQUOT|EIO|ENOMEM)\b/.test(msg) || /no space left on device/i.test(msg) || /disk quota exceeded/i.test(msg);
|
|
41586
|
+
}
|
|
41576
41587
|
var LOCAL_RESOURCE_EXHAUSTED = "LOCAL_RESOURCE_EXHAUSTED";
|
|
41588
|
+
var GIVE_UP_MESSAGE = "retryApiCall: max retries exceeded";
|
|
41577
41589
|
var FLOOD_WAIT_ACTIVE = "FLOOD_WAIT_ACTIVE";
|
|
41578
41590
|
function makeFloodWaitActiveError(retryAfterSec, untilTs, original) {
|
|
41579
41591
|
return Object.assign(new Error(FLOOD_WAIT_ACTIVE), {
|
|
@@ -41587,6 +41599,99 @@ function makeFloodWaitActiveError(retryAfterSec, untilTs, original) {
|
|
|
41587
41599
|
function isFloodWaitActiveError(err) {
|
|
41588
41600
|
return err instanceof Error && err.message === FLOOD_WAIT_ACTIVE;
|
|
41589
41601
|
}
|
|
41602
|
+
var DEFAULT_MAX_FLOOD_SLEEP_MS = 120000;
|
|
41603
|
+
var DEFAULT_SLEEP = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
41604
|
+
function createRetryApiCall(config = {}) {
|
|
41605
|
+
const maxRetries = config.maxRetries ?? 3;
|
|
41606
|
+
const maxFloodSleepMs = config.maxFloodSleepMs ?? DEFAULT_MAX_FLOOD_SLEEP_MS;
|
|
41607
|
+
const sleep = config.sleep ?? DEFAULT_SLEEP;
|
|
41608
|
+
const observer = config.observer;
|
|
41609
|
+
const log = config.log;
|
|
41610
|
+
const onFloodWait = config.onFloodWait;
|
|
41611
|
+
const floodWaitRemainingMs = config.floodWaitRemainingMs;
|
|
41612
|
+
function openWindowMs() {
|
|
41613
|
+
if (!floodWaitRemainingMs)
|
|
41614
|
+
return 0;
|
|
41615
|
+
try {
|
|
41616
|
+
const ms = floodWaitRemainingMs();
|
|
41617
|
+
return Number.isFinite(ms) && ms > 0 ? ms : 0;
|
|
41618
|
+
} catch {
|
|
41619
|
+
return 0;
|
|
41620
|
+
}
|
|
41621
|
+
}
|
|
41622
|
+
return async function retryApiCall(fn, opts) {
|
|
41623
|
+
for (let attempt = 0;attempt < maxRetries; attempt++) {
|
|
41624
|
+
const remaining = openWindowMs();
|
|
41625
|
+
if (remaining > maxFloodSleepMs) {
|
|
41626
|
+
const retryAfterSec = Math.ceil(remaining / 1000);
|
|
41627
|
+
log?.(`telegram gateway: flood window still open for ${retryAfterSec}s \u2014 ` + `not issuing the ${opts?.verb ?? "api-call"} (would feed the ban)
|
|
41628
|
+
`);
|
|
41629
|
+
const gated = makeFloodWaitActiveError(retryAfterSec, Date.now() + remaining, null);
|
|
41630
|
+
observer?.onGiveUp?.({ attempts: attempt + 1, error: gated });
|
|
41631
|
+
throw gated;
|
|
41632
|
+
}
|
|
41633
|
+
try {
|
|
41634
|
+
return await fn();
|
|
41635
|
+
} catch (err) {
|
|
41636
|
+
const isGrammyErr = err instanceof import_grammy2.GrammyError;
|
|
41637
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
41638
|
+
const desc = isGrammyErr ? err.description : msg;
|
|
41639
|
+
if (isLocalResourceError(err)) {
|
|
41640
|
+
log?.(`telegram gateway: LOCAL resource exhaustion (${err.code ?? "disk/mem"}) \u2014 ` + `not retrying the send (would feed a flood ban); surfacing degraded state
|
|
41641
|
+
`);
|
|
41642
|
+
observer?.onGiveUp?.({ attempts: attempt + 1, error: err });
|
|
41643
|
+
throw Object.assign(new Error(LOCAL_RESOURCE_EXHAUSTED), { original: err });
|
|
41644
|
+
}
|
|
41645
|
+
if (isGrammyErr && err.error_code === 429) {
|
|
41646
|
+
const retryAfter = Number(err.parameters?.retry_after ?? 5);
|
|
41647
|
+
const delayMs = retryAfter * 1000;
|
|
41648
|
+
try {
|
|
41649
|
+
onFloodWait?.(retryAfter, opts);
|
|
41650
|
+
} catch {}
|
|
41651
|
+
if (delayMs > maxFloodSleepMs) {
|
|
41652
|
+
log?.(`telegram gateway: 429 flood ban of ${retryAfter}s exceeds the ` + `${Math.round(maxFloodSleepMs / 1000)}s in-process sleep ceiling \u2014 ` + `not sleeping it; surfacing degraded state
|
|
41653
|
+
`);
|
|
41654
|
+
observer?.onGiveUp?.({ attempts: attempt + 1, error: err });
|
|
41655
|
+
throw makeFloodWaitActiveError(retryAfter, Date.now() + delayMs, err);
|
|
41656
|
+
}
|
|
41657
|
+
log?.(`telegram gateway: 429 rate limited, waiting ${retryAfter}s
|
|
41658
|
+
`);
|
|
41659
|
+
observer?.onRetry?.({ attempt, reason: "flood_wait", delayMs });
|
|
41660
|
+
await sleep(delayMs);
|
|
41661
|
+
continue;
|
|
41662
|
+
}
|
|
41663
|
+
if (isGrammyErr && err.error_code === 400 && desc.includes("not modified")) {
|
|
41664
|
+
observer?.onBenign?.({ kind: "not_modified" });
|
|
41665
|
+
return;
|
|
41666
|
+
}
|
|
41667
|
+
if (isGrammyErr && err.error_code === 400 && (desc.includes("message to edit not found") || desc.includes("message to delete not found"))) {
|
|
41668
|
+
observer?.onBenign?.({
|
|
41669
|
+
kind: desc.includes("edit") ? "message_not_found" : "delete_not_found"
|
|
41670
|
+
});
|
|
41671
|
+
return;
|
|
41672
|
+
}
|
|
41673
|
+
if (isGrammyErr && err.error_code === 400 && desc.includes("thread not found") && opts?.threadId && opts?.chat_id) {
|
|
41674
|
+
throw Object.assign(new Error("THREAD_NOT_FOUND"), { original: err });
|
|
41675
|
+
}
|
|
41676
|
+
if (!isGrammyErr && (msg.includes("ECONNRESET") || msg.includes("ETIMEDOUT") || msg.includes("fetch failed") || msg.includes("ENOTFOUND"))) {
|
|
41677
|
+
if (attempt < maxRetries - 1) {
|
|
41678
|
+
const delayMs = Math.pow(2, attempt) * 1000;
|
|
41679
|
+
log?.(`telegram gateway: network error, retrying in ${delayMs / 1000}s: ${msg}
|
|
41680
|
+
`);
|
|
41681
|
+
observer?.onRetry?.({ attempt, reason: "network", delayMs });
|
|
41682
|
+
await sleep(delayMs);
|
|
41683
|
+
continue;
|
|
41684
|
+
}
|
|
41685
|
+
}
|
|
41686
|
+
observer?.onGiveUp?.({ attempts: attempt + 1, error: err });
|
|
41687
|
+
throw err;
|
|
41688
|
+
}
|
|
41689
|
+
}
|
|
41690
|
+
const giveUpErr = new Error(GIVE_UP_MESSAGE);
|
|
41691
|
+
observer?.onGiveUp?.({ attempts: maxRetries, error: giveUpErr });
|
|
41692
|
+
throw giveUpErr;
|
|
41693
|
+
};
|
|
41694
|
+
}
|
|
41590
41695
|
async function retryWithThreadFallback(retry, send, opts) {
|
|
41591
41696
|
try {
|
|
41592
41697
|
return await retry(() => send(opts.threadId), {
|
|
@@ -61646,7 +61751,7 @@ function createChatLock() {
|
|
|
61646
61751
|
|
|
61647
61752
|
// retry-api-call.ts
|
|
61648
61753
|
var import_grammy6 = __toESM(require_mod2(), 1);
|
|
61649
|
-
function
|
|
61754
|
+
function isLocalResourceError2(err) {
|
|
61650
61755
|
const code2 = err?.code;
|
|
61651
61756
|
if (typeof code2 === "string" && ["ENOSPC", "EDQUOT", "EIO", "ENOMEM"].includes(code2)) {
|
|
61652
61757
|
return true;
|
|
@@ -61655,7 +61760,7 @@ function isLocalResourceError(err) {
|
|
|
61655
61760
|
return /\b(ENOSPC|EDQUOT|EIO|ENOMEM)\b/.test(msg) || /no space left on device/i.test(msg) || /disk quota exceeded/i.test(msg);
|
|
61656
61761
|
}
|
|
61657
61762
|
var LOCAL_RESOURCE_EXHAUSTED2 = "LOCAL_RESOURCE_EXHAUSTED";
|
|
61658
|
-
var
|
|
61763
|
+
var GIVE_UP_MESSAGE2 = "retryApiCall: max retries exceeded";
|
|
61659
61764
|
var FLOOD_WAIT_ACTIVE2 = "FLOOD_WAIT_ACTIVE";
|
|
61660
61765
|
function makeFloodWaitActiveError2(retryAfterSec, untilTs, original) {
|
|
61661
61766
|
return Object.assign(new Error(FLOOD_WAIT_ACTIVE2), {
|
|
@@ -61669,12 +61774,12 @@ function makeFloodWaitActiveError2(retryAfterSec, untilTs, original) {
|
|
|
61669
61774
|
function isFloodWaitActiveError2(err) {
|
|
61670
61775
|
return err instanceof Error && err.message === FLOOD_WAIT_ACTIVE2;
|
|
61671
61776
|
}
|
|
61672
|
-
var
|
|
61673
|
-
var
|
|
61777
|
+
var DEFAULT_MAX_FLOOD_SLEEP_MS2 = 120000;
|
|
61778
|
+
var DEFAULT_SLEEP2 = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
61674
61779
|
function createRetryApiCall2(config = {}) {
|
|
61675
61780
|
const maxRetries = config.maxRetries ?? 3;
|
|
61676
|
-
const maxFloodSleepMs = config.maxFloodSleepMs ??
|
|
61677
|
-
const sleep = config.sleep ??
|
|
61781
|
+
const maxFloodSleepMs = config.maxFloodSleepMs ?? DEFAULT_MAX_FLOOD_SLEEP_MS2;
|
|
61782
|
+
const sleep = config.sleep ?? DEFAULT_SLEEP2;
|
|
61678
61783
|
const observer = config.observer;
|
|
61679
61784
|
const log = config.log;
|
|
61680
61785
|
const onFloodWait = config.onFloodWait;
|
|
@@ -61706,7 +61811,7 @@ function createRetryApiCall2(config = {}) {
|
|
|
61706
61811
|
const isGrammyErr = err instanceof import_grammy6.GrammyError;
|
|
61707
61812
|
const msg = err instanceof Error ? err.message : String(err);
|
|
61708
61813
|
const desc = isGrammyErr ? err.description : msg;
|
|
61709
|
-
if (
|
|
61814
|
+
if (isLocalResourceError2(err)) {
|
|
61710
61815
|
log?.(`telegram gateway: LOCAL resource exhaustion (${err.code ?? "disk/mem"}) \u2014 ` + `not retrying the send (would feed a flood ban); surfacing degraded state
|
|
61711
61816
|
`);
|
|
61712
61817
|
observer?.onGiveUp?.({ attempts: attempt + 1, error: err });
|
|
@@ -61757,7 +61862,7 @@ function createRetryApiCall2(config = {}) {
|
|
|
61757
61862
|
throw err;
|
|
61758
61863
|
}
|
|
61759
61864
|
}
|
|
61760
|
-
const giveUpErr = new Error(
|
|
61865
|
+
const giveUpErr = new Error(GIVE_UP_MESSAGE2);
|
|
61761
61866
|
observer?.onGiveUp?.({ attempts: maxRetries, error: giveUpErr });
|
|
61762
61867
|
throw giveUpErr;
|
|
61763
61868
|
};
|
|
@@ -69023,7 +69128,7 @@ var STARTUP_RETRY_DELAYS_MS = [
|
|
|
69023
69128
|
32000,
|
|
69024
69129
|
64000
|
|
69025
69130
|
];
|
|
69026
|
-
var
|
|
69131
|
+
var DEFAULT_SLEEP3 = (ms) => new Promise((resolve7) => setTimeout(resolve7, ms));
|
|
69027
69132
|
function classifyStartupError(err) {
|
|
69028
69133
|
if (!(err instanceof Error))
|
|
69029
69134
|
return "other";
|
|
@@ -69043,7 +69148,7 @@ function classifyStartupError(err) {
|
|
|
69043
69148
|
}
|
|
69044
69149
|
async function gatewayStartupRetry(fn, opts = {}) {
|
|
69045
69150
|
const delays = opts.delaysMs ?? STARTUP_RETRY_DELAYS_MS;
|
|
69046
|
-
const sleep = opts.sleep ??
|
|
69151
|
+
const sleep = opts.sleep ?? DEFAULT_SLEEP3;
|
|
69047
69152
|
const onExhausted = opts.onExhausted ?? ((err) => {
|
|
69048
69153
|
process.stderr.write(`telegram gateway: startup failed after ${delays.length + 1} attempts \u2014 exiting so systemd can restart: ${err}
|
|
69049
69154
|
`);
|
|
@@ -73012,16 +73117,6 @@ function validateStringArray(field, value, tag = "gateway") {
|
|
|
73012
73117
|
|
|
73013
73118
|
// format.ts
|
|
73014
73119
|
var RICH_MESSAGE_MAX_CHARS2 = 32768;
|
|
73015
|
-
function repairEscapedWhitespace2(text4) {
|
|
73016
|
-
if (!/\\[nrt"\\]/.test(text4))
|
|
73017
|
-
return text4;
|
|
73018
|
-
const nonce = Math.random().toString(36).slice(2);
|
|
73019
|
-
const BACKSLASH_PH = `\x00BK${nonce}_`;
|
|
73020
|
-
const { masked, restore: restore2 } = maskCodeRegions2(text4, nonce);
|
|
73021
|
-
const unescaped = masked.replace(/\\\\/g, BACKSLASH_PH).replace(/\\n/g, `
|
|
73022
|
-
`).replace(/\\r/g, "\r").replace(/\\t/g, "\t").replace(/\\"/g, '"').replace(new RegExp(BACKSLASH_PH.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "g"), "\\");
|
|
73023
|
-
return restore2(unescaped);
|
|
73024
|
-
}
|
|
73025
73120
|
function maskCodeRegions2(text4, nonce) {
|
|
73026
73121
|
const FENCE_MASK_PH = `\x00RMF${nonce}_`;
|
|
73027
73122
|
const INLINE_MASK_PH = `\x00RMI${nonce}_`;
|
|
@@ -73041,55 +73136,6 @@ function maskCodeRegions2(text4, nonce) {
|
|
|
73041
73136
|
const stripPlaceholders = (s) => s.replace(anyMaskRe, "");
|
|
73042
73137
|
return { masked, restore: restore2, placeholder: FENCE_MASK_PH, stripPlaceholders };
|
|
73043
73138
|
}
|
|
73044
|
-
function splitCollapsedInlineBullets2(text4) {
|
|
73045
|
-
if (!/[\u2022\u00b7]/.test(text4))
|
|
73046
|
-
return text4;
|
|
73047
|
-
const interiorSep = /[ \t]+([\u2022\u00b7])[ \t]+/g;
|
|
73048
|
-
return text4.split(`
|
|
73049
|
-
`).map((line) => {
|
|
73050
|
-
if (!/^[ \t]*[\u2022\u00b7*-] /.test(line))
|
|
73051
|
-
return line;
|
|
73052
|
-
if (!/[ \t][\u2022\u00b7][ \t]/.test(line))
|
|
73053
|
-
return line;
|
|
73054
|
-
return line.replace(interiorSep, `
|
|
73055
|
-
$1 `);
|
|
73056
|
-
}).join(`
|
|
73057
|
-
`);
|
|
73058
|
-
}
|
|
73059
|
-
function normalizeParagraphBreaks2(text4) {
|
|
73060
|
-
if (!text4.includes(`
|
|
73061
|
-
`) && !/[\u2022\u00b7]/.test(text4))
|
|
73062
|
-
return text4;
|
|
73063
|
-
const nonce = Math.random().toString(36).slice(2);
|
|
73064
|
-
const { masked: maskedRaw, restore: restore2, placeholder } = maskCodeRegions2(text4, nonce);
|
|
73065
|
-
const masked = splitCollapsedInlineBullets2(maskedRaw);
|
|
73066
|
-
let out = masked.replace(/\n[ \t\r]+\n(?:[ \t\r]*\n)*/g, `
|
|
73067
|
-
|
|
73068
|
-
`).replace(/\n{3,}/g, `
|
|
73069
|
-
|
|
73070
|
-
`);
|
|
73071
|
-
const lines = out.split(`
|
|
73072
|
-
`);
|
|
73073
|
-
const pieces = [];
|
|
73074
|
-
for (let i = 0;i < lines.length; i++) {
|
|
73075
|
-
let line = lines[i];
|
|
73076
|
-
const isLast = i === lines.length - 1;
|
|
73077
|
-
const next = isLast ? "" : lines[i + 1];
|
|
73078
|
-
const promote = !isLast && line.trim() !== "" && next.trim() !== "" && shouldPromoteBreak2(line, next, placeholder);
|
|
73079
|
-
if (promote) {
|
|
73080
|
-
line = line.replace(/[ \t\r]+$/, "");
|
|
73081
|
-
}
|
|
73082
|
-
pieces.push(line);
|
|
73083
|
-
if (isLast)
|
|
73084
|
-
break;
|
|
73085
|
-
pieces.push(promote ? `
|
|
73086
|
-
` : `
|
|
73087
|
-
`);
|
|
73088
|
-
}
|
|
73089
|
-
out = pieces.join("");
|
|
73090
|
-
out = ensureBlockBoundaries2(out, placeholder);
|
|
73091
|
-
return restore2(out);
|
|
73092
|
-
}
|
|
73093
73139
|
function hardenCardBreaks2(text4) {
|
|
73094
73140
|
if (!text4.includes(`
|
|
73095
73141
|
`))
|
|
@@ -73200,85 +73246,6 @@ function addParagraphSpacers2(text4) {
|
|
|
73200
73246
|
return restore2(out.join(`
|
|
73201
73247
|
`));
|
|
73202
73248
|
}
|
|
73203
|
-
function normalizePunctuation2(text4) {
|
|
73204
|
-
if (!/[\u2014\u2013\u2022\u00b7]/.test(text4))
|
|
73205
|
-
return text4;
|
|
73206
|
-
const nonce = Math.random().toString(36).slice(2);
|
|
73207
|
-
const { masked, restore: restore2 } = maskCodeRegions2(text4, nonce);
|
|
73208
|
-
const linkMasks = [];
|
|
73209
|
-
const LINK_MASK_PH = `\x00RML${nonce}_`;
|
|
73210
|
-
const maskedLinks = masked.replace(/(\]\()([^)\n]*)(\))/g, (_m, open, href, close) => {
|
|
73211
|
-
const idx = linkMasks.length;
|
|
73212
|
-
linkMasks.push(href);
|
|
73213
|
-
return `${open}${LINK_MASK_PH}${idx}\x00${close}`;
|
|
73214
|
-
});
|
|
73215
|
-
const maskedAutolinks = maskedLinks.replace(/(<)([a-zA-Z][a-zA-Z0-9+.-]*:[^>\s]*)(>)/g, (_m, lt, uri, gt) => {
|
|
73216
|
-
const idx = linkMasks.length;
|
|
73217
|
-
linkMasks.push(uri);
|
|
73218
|
-
return `${lt}${LINK_MASK_PH}${idx}\x00${gt}`;
|
|
73219
|
-
});
|
|
73220
|
-
const escNonce = nonce.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
73221
|
-
const linkRestoreRe = new RegExp(`\x00RML${escNonce}_(\\d+)\x00`, "g");
|
|
73222
|
-
const restoreLinks = (s) => s.replace(linkRestoreRe, (_m, idx) => linkMasks[Number(idx)] ?? _m);
|
|
73223
|
-
let out = maskedAutolinks.replace(/(\S)[ \t][\u2014\u2013][ \t](?=(\S))/g, (_m, a, b) => /\d/.test(a) && /\d/.test(b) ? `${a}-` : `${a}, `).replace(/(\w)\u2014(?=(\w))/g, (_m, a, b) => /\d/.test(a) && /\d/.test(b) ? `${a}-` : `${a}, `).replace(/(\w)\u2013(?=\w)/g, "$1-");
|
|
73224
|
-
out = restoreLinks(out);
|
|
73225
|
-
out = out.split(`
|
|
73226
|
-
`).map((line) => line.replace(/^([ \t]*)[\u2022\u00b7][ \t]+/, "$1- ")).join(`
|
|
73227
|
-
`);
|
|
73228
|
-
return restore2(out);
|
|
73229
|
-
}
|
|
73230
|
-
function listItemContent2(line) {
|
|
73231
|
-
return line.trimStart().replace(/^(?:[-*+]|\d+[.)])\s+/, "");
|
|
73232
|
-
}
|
|
73233
|
-
function isFullyBolded2(fragment) {
|
|
73234
|
-
return /^\*\*[^*]+\*\*[.,:;!?]?$/.test(fragment.trim());
|
|
73235
|
-
}
|
|
73236
|
-
function unbold2(fragment) {
|
|
73237
|
-
return fragment.replace(/\*\*([^*]+)\*\*/g, "$1");
|
|
73238
|
-
}
|
|
73239
|
-
function stripExcessBold2(text4) {
|
|
73240
|
-
if (!text4.includes("**"))
|
|
73241
|
-
return text4;
|
|
73242
|
-
const nonce = Math.random().toString(36).slice(2);
|
|
73243
|
-
const { masked, restore: restore2, placeholder, stripPlaceholders } = maskCodeRegions2(text4, nonce);
|
|
73244
|
-
const visible = stripPlaceholders(masked);
|
|
73245
|
-
if (visible.length < 100)
|
|
73246
|
-
return restore2(masked);
|
|
73247
|
-
let boldChars = 0;
|
|
73248
|
-
for (const m of visible.matchAll(/\*\*([^*]+)\*\*/g))
|
|
73249
|
-
boldChars += m[1].length;
|
|
73250
|
-
if (boldChars / visible.length > 0.3) {
|
|
73251
|
-
return restore2(unbold2(masked));
|
|
73252
|
-
}
|
|
73253
|
-
const blocks = masked.split(/\n{2,}/);
|
|
73254
|
-
const rebuilt = blocks.map((block) => {
|
|
73255
|
-
const lines = block.split(`
|
|
73256
|
-
`).filter((l) => l.trim() !== "");
|
|
73257
|
-
if (lines.length === 0 || !block.includes("**"))
|
|
73258
|
-
return block;
|
|
73259
|
-
const listLines = lines.filter((l) => isListItemLine2(l));
|
|
73260
|
-
if (listLines.length >= 2 && listLines.length === lines.length) {
|
|
73261
|
-
if (lines.every((l) => isFullyBolded2(listItemContent2(l))))
|
|
73262
|
-
return unbold2(block);
|
|
73263
|
-
return block;
|
|
73264
|
-
}
|
|
73265
|
-
const isProseBlock = lines.every((l) => !isMarkerLine2(l, placeholder));
|
|
73266
|
-
if (!isProseBlock)
|
|
73267
|
-
return block;
|
|
73268
|
-
if (!lines.every((l) => isFullyBolded2(l)))
|
|
73269
|
-
return block;
|
|
73270
|
-
if (lines.length === 1 && lines[0].trim().length <= 48)
|
|
73271
|
-
return block;
|
|
73272
|
-
return unbold2(block);
|
|
73273
|
-
});
|
|
73274
|
-
const seps = masked.match(/\n{2,}/g) ?? [];
|
|
73275
|
-
let out = rebuilt[0] ?? "";
|
|
73276
|
-
for (let i = 1;i < rebuilt.length; i++)
|
|
73277
|
-
out += (seps[i - 1] ?? `
|
|
73278
|
-
|
|
73279
|
-
`) + rebuilt[i];
|
|
73280
|
-
return restore2(out);
|
|
73281
|
-
}
|
|
73282
73249
|
function isListItemLine2(line) {
|
|
73283
73250
|
const t = line.trimStart();
|
|
73284
73251
|
return /^[-*+]\s/.test(t) || /^\d+[.)]\s/.test(t);
|
|
@@ -73301,82 +73268,6 @@ function isBlockquoteLine2(line) {
|
|
|
73301
73268
|
function isHeadingLine2(line) {
|
|
73302
73269
|
return /^#{1,6}\s/.test(line.trimStart());
|
|
73303
73270
|
}
|
|
73304
|
-
function ensureBlockBoundaries2(text4, placeholder) {
|
|
73305
|
-
if (!text4.includes(`
|
|
73306
|
-
`))
|
|
73307
|
-
return text4;
|
|
73308
|
-
const lines = text4.split(`
|
|
73309
|
-
`);
|
|
73310
|
-
const result = [];
|
|
73311
|
-
for (let i = 0;i < lines.length; i++) {
|
|
73312
|
-
const line = lines[i];
|
|
73313
|
-
const prev = result.length > 0 ? result[result.length - 1] : null;
|
|
73314
|
-
const prevNonBlank = prev != null && prev.trim() !== "";
|
|
73315
|
-
const curBlank = line.trim() === "";
|
|
73316
|
-
if (prevNonBlank && !curBlank) {
|
|
73317
|
-
const next = i + 1 < lines.length ? lines[i + 1] : "";
|
|
73318
|
-
const prevIsTable = isTableRowLine2(prev);
|
|
73319
|
-
const startsTableHere = !prevIsTable && (line.includes("|") && isTableDelimiterLine2(next) || isTableRowLine2(line) && isTableDelimiterLine2(next));
|
|
73320
|
-
const startsFence = isFenceOpenLine2(line, placeholder) && !isFenceOpenLine2(prev, placeholder);
|
|
73321
|
-
const startsQuote = isBlockquoteLine2(line) && !isBlockquoteLine2(prev);
|
|
73322
|
-
const startsHeading = isHeadingLine2(line) && !isHeadingLine2(prev);
|
|
73323
|
-
const startsList = isListItemLine2(line) && !isListItemLine2(prev);
|
|
73324
|
-
if (startsTableHere || startsFence || startsQuote || startsHeading || startsList) {
|
|
73325
|
-
result.push("");
|
|
73326
|
-
}
|
|
73327
|
-
}
|
|
73328
|
-
if (prevNonBlank && !curBlank && isFenceOpenLine2(prev, placeholder)) {
|
|
73329
|
-
const alreadySeparated = result.length > 0 && result[result.length - 1].trim() === "";
|
|
73330
|
-
const curIsBlockStart = isFenceOpenLine2(line, placeholder) || isBlockquoteLine2(line) || isHeadingLine2(line) || isTableRowLine2(line) || isTableDelimiterLine2(line);
|
|
73331
|
-
if (!alreadySeparated && !curIsBlockStart) {
|
|
73332
|
-
result.push("");
|
|
73333
|
-
}
|
|
73334
|
-
}
|
|
73335
|
-
if (prevNonBlank && !curBlank && isListItemLine2(prev) && !isListItemLine2(line)) {
|
|
73336
|
-
const isIndentedContinuation = /^(\t| {4,})\S/.test(line);
|
|
73337
|
-
const alreadySeparated = result.length > 0 && result[result.length - 1].trim() === "";
|
|
73338
|
-
if (!isIndentedContinuation && !alreadySeparated) {
|
|
73339
|
-
result.push("");
|
|
73340
|
-
}
|
|
73341
|
-
}
|
|
73342
|
-
result.push(line);
|
|
73343
|
-
}
|
|
73344
|
-
return result.join(`
|
|
73345
|
-
`);
|
|
73346
|
-
}
|
|
73347
|
-
function isMarkerLine2(line, placeholder) {
|
|
73348
|
-
if (/^ {4,}\S/.test(line))
|
|
73349
|
-
return true;
|
|
73350
|
-
const t = line.trimStart();
|
|
73351
|
-
if (placeholder != null && placeholder.length > 0 && t.startsWith(placeholder))
|
|
73352
|
-
return true;
|
|
73353
|
-
return /^[-*+]\s/.test(t) || /^\d+[.)]\s/.test(t) || t.startsWith(">") || /^#{1,6}\s/.test(t) || isTableRowLine2(line) || isTableDelimiterLine2(line) || t.startsWith("```") || /^(-{3,}|\*{3,}|_{3,})\s*$/.test(t);
|
|
73354
|
-
}
|
|
73355
|
-
function shouldPromoteBreak2(prev, next, placeholder) {
|
|
73356
|
-
if (isMarkerLine2(prev, placeholder) || isMarkerLine2(next, placeholder))
|
|
73357
|
-
return false;
|
|
73358
|
-
const nextTrimmed = next.trimStart();
|
|
73359
|
-
if (nextTrimmed.length === 0)
|
|
73360
|
-
return false;
|
|
73361
|
-
const prevTrimmed = prev.trimEnd();
|
|
73362
|
-
const unwrapped = prevTrimmed.replace(/[)"'\u2019\u201d\]]+$/, "");
|
|
73363
|
-
const terminator = unwrapped.slice(-1);
|
|
73364
|
-
if (terminator === "." || terminator === "!" || terminator === "?" || terminator === ":") {
|
|
73365
|
-
return true;
|
|
73366
|
-
}
|
|
73367
|
-
const statStripped = prevTrimmed.replace(/[*_`]/g, "");
|
|
73368
|
-
const colonIdx = statStripped.indexOf(":");
|
|
73369
|
-
if (colonIdx > 0) {
|
|
73370
|
-
const label = statStripped.slice(0, colonIdx).trim();
|
|
73371
|
-
const value = statStripped.slice(colonIdx + 1);
|
|
73372
|
-
const labelWords = label.length === 0 ? 0 : label.split(/\s+/).length;
|
|
73373
|
-
const isStatLine = /^[ \t]+\S/.test(value) && labelWords >= 1 && labelWords <= 3 && label.length <= 24;
|
|
73374
|
-
if (isStatLine && !/^[a-z]/.test(nextTrimmed)) {
|
|
73375
|
-
return true;
|
|
73376
|
-
}
|
|
73377
|
-
}
|
|
73378
|
-
return false;
|
|
73379
|
-
}
|
|
73380
73271
|
function hardSliceToCap2(text4, cap = RICH_MESSAGE_MAX_CHARS2) {
|
|
73381
73272
|
if (cap <= 0)
|
|
73382
73273
|
return [text4];
|
|
@@ -73666,121 +73557,221 @@ function decideRedeliver(input) {
|
|
|
73666
73557
|
return { redeliver: true, framedText: frameRedelivery(text4) };
|
|
73667
73558
|
}
|
|
73668
73559
|
|
|
73669
|
-
//
|
|
73670
|
-
|
|
73671
|
-
|
|
73672
|
-
|
|
73673
|
-
|
|
73674
|
-
|
|
73675
|
-
|
|
73676
|
-
|
|
73677
|
-
|
|
73678
|
-
const
|
|
73679
|
-
|
|
73560
|
+
// gateway/outbound-send-path.ts
|
|
73561
|
+
init_format();
|
|
73562
|
+
init_text_voice_scrub();
|
|
73563
|
+
|
|
73564
|
+
// temporal-normalize.ts
|
|
73565
|
+
function maskTemporalRegions(text4) {
|
|
73566
|
+
const nonce = Math.random().toString(36).slice(2);
|
|
73567
|
+
const PH = `\x00TN${nonce}_`;
|
|
73568
|
+
const masks = [];
|
|
73569
|
+
const push2 = (s) => {
|
|
73570
|
+
masks.push(s);
|
|
73571
|
+
return `${PH}${masks.length - 1}\x00`;
|
|
73572
|
+
};
|
|
73573
|
+
const masked = text4.replace(/```[\s\S]*?```/g, (m) => push2(m)).replace(/`[^`\n]+`/g, (m) => push2(m)).replace(/(\]\()([^)\n]*)(\))/g, (_m, open, href, close) => `${open}${push2(href)}${close}`).replace(/(<)([a-zA-Z][a-zA-Z0-9+.-]*:[^>\s]*)(>)/g, (_m, lt, uri, gt) => `${lt}${push2(uri)}${gt}`);
|
|
73574
|
+
const esc = nonce.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
73575
|
+
const re = new RegExp(`\x00TN${esc}_(\\d+)\x00`, "g");
|
|
73576
|
+
const restore2 = (s) => s.replace(re, (_m, idx) => masks[Number(idx)] ?? _m);
|
|
73577
|
+
return { masked, restore: restore2 };
|
|
73578
|
+
}
|
|
73579
|
+
function isBlockquoteLine3(line) {
|
|
73580
|
+
return line.trimStart().startsWith(">");
|
|
73680
73581
|
}
|
|
73681
|
-
function
|
|
73682
|
-
|
|
73683
|
-
|
|
73582
|
+
function perNonQuoteLine(masked, fn) {
|
|
73583
|
+
return masked.split(`
|
|
73584
|
+
`).map((line) => isBlockquoteLine3(line) ? line : fn(line)).join(`
|
|
73585
|
+
`);
|
|
73684
73586
|
}
|
|
73685
|
-
|
|
73686
|
-
|
|
73687
|
-
|
|
73688
|
-
|
|
73689
|
-
|
|
73690
|
-
|
|
73691
|
-
|
|
73692
|
-
|
|
73693
|
-
|
|
73694
|
-
const recapped = rest.replace(/^(\s*)([a-z])/, (_m, ws, ch) => ws + ch.toUpperCase());
|
|
73695
|
-
return { out: leadingWs + recapped, count: 1 };
|
|
73587
|
+
function renderLocalDate(ms, tz) {
|
|
73588
|
+
const parts = new Intl.DateTimeFormat("en-US", {
|
|
73589
|
+
timeZone: tz,
|
|
73590
|
+
weekday: "short",
|
|
73591
|
+
day: "numeric",
|
|
73592
|
+
month: "short"
|
|
73593
|
+
}).formatToParts(new Date(ms));
|
|
73594
|
+
const get = (t) => parts.find((p) => p.type === t)?.value ?? "";
|
|
73595
|
+
return `${get("weekday")} ${get("day")} ${get("month")}`;
|
|
73696
73596
|
}
|
|
73697
|
-
function
|
|
73698
|
-
|
|
73699
|
-
|
|
73700
|
-
|
|
73701
|
-
|
|
73702
|
-
|
|
73703
|
-
|
|
73704
|
-
});
|
|
73705
|
-
parked = parked.replace(/<pre>[\s\S]*?<\/pre>/gi, (m) => {
|
|
73706
|
-
const idx = parts.length;
|
|
73707
|
-
parts.push({ prefix: HTML_PRE_PH2, idx, raw: m });
|
|
73708
|
-
return `${HTML_PRE_PH2}${idx}${NULL4}`;
|
|
73709
|
-
});
|
|
73710
|
-
parked = parked.replace(/<code[^>]*>[\s\S]*?<\/code>/gi, (m) => {
|
|
73711
|
-
const idx = parts.length;
|
|
73712
|
-
parts.push({ prefix: HTML_CODE_PH2, idx, raw: m });
|
|
73713
|
-
return `${HTML_CODE_PH2}${idx}${NULL4}`;
|
|
73714
|
-
});
|
|
73715
|
-
parked = parked.replace(/`[^`\n]+`/g, (m) => {
|
|
73716
|
-
const idx = parts.length;
|
|
73717
|
-
parts.push({ prefix: INLINE_PH4, idx, raw: m });
|
|
73718
|
-
return `${INLINE_PH4}${idx}${NULL4}`;
|
|
73719
|
-
});
|
|
73720
|
-
parked = parked.replace(URL_RE3, (m) => {
|
|
73721
|
-
const idx = parts.length;
|
|
73722
|
-
parts.push({ prefix: URL_PH2, idx, raw: m });
|
|
73723
|
-
return `${URL_PH2}${idx}${NULL4}`;
|
|
73724
|
-
});
|
|
73725
|
-
return { parked, parts };
|
|
73597
|
+
function renderLocalClock(ms, tz) {
|
|
73598
|
+
return new Intl.DateTimeFormat("en-US", {
|
|
73599
|
+
timeZone: tz,
|
|
73600
|
+
hour: "numeric",
|
|
73601
|
+
minute: "2-digit",
|
|
73602
|
+
hour12: true
|
|
73603
|
+
}).format(new Date(ms)).replace(/\s([AP])M$/, (_m, p) => ` ${p.toLowerCase()}m`);
|
|
73726
73604
|
}
|
|
73727
|
-
function
|
|
73728
|
-
|
|
73729
|
-
for (let i = parts.length - 1;i >= 0; i--) {
|
|
73730
|
-
const p = parts[i];
|
|
73731
|
-
restored = restored.replace(`${p.prefix}${p.idx}${NULL4}`, () => p.raw);
|
|
73732
|
-
}
|
|
73733
|
-
return restored;
|
|
73605
|
+
function renderLocalDatetime(ms, tz) {
|
|
73606
|
+
return `${renderLocalDate(ms, tz)} ${renderLocalClock(ms, tz)} ${tzAbbrev(ms, tz)}`;
|
|
73734
73607
|
}
|
|
73735
|
-
|
|
73736
|
-
|
|
73737
|
-
|
|
73738
|
-
|
|
73739
|
-
|
|
73740
|
-
|
|
73741
|
-
return after.replace(/^([a-z])/, (_m, ch) => ch.toUpperCase());
|
|
73742
|
-
};
|
|
73743
|
-
out = out.replace(/(\S) [\u2014\u2013] (\S)(\S?)/g, (m, before, after, peek) => {
|
|
73744
|
-
if (/\d/.test(before) && /\d/.test(after))
|
|
73608
|
+
var ISO_UTC_RE = /\d{4}-\d{2}-\d{2}T\d{2}:\d{2}(?::\d{2}(?:\.\d+)?)?(?:Z|\+00:00)/g;
|
|
73609
|
+
var LABELLED_UTC_RE = /\b(\d{1,2})(?::(\d{2}))?\s?(am|pm)?\s?(UTC|GMT)\b/gi;
|
|
73610
|
+
function convertUtcInLine(line, tz, nowMs2) {
|
|
73611
|
+
let out = line.replace(ISO_UTC_RE, (m) => {
|
|
73612
|
+
const ms = Date.parse(m);
|
|
73613
|
+
if (Number.isNaN(ms))
|
|
73745
73614
|
return m;
|
|
73746
|
-
|
|
73747
|
-
return `${before}. ${capFirst(after + peek)}`;
|
|
73748
|
-
});
|
|
73749
|
-
out = out.replace(/ [\u2014\u2013](\s*\n)/g, (_m, ws) => {
|
|
73750
|
-
replaced++;
|
|
73751
|
-
return `.${ws}`;
|
|
73615
|
+
return renderLocalDatetime(ms, tz);
|
|
73752
73616
|
});
|
|
73753
|
-
out = out.replace(
|
|
73754
|
-
if (
|
|
73617
|
+
out = out.replace(LABELLED_UTC_RE, (m, hh, mm, ampm) => {
|
|
73618
|
+
if (mm == null && ampm == null)
|
|
73755
73619
|
return m;
|
|
73756
|
-
|
|
73757
|
-
|
|
73758
|
-
|
|
73759
|
-
|
|
73760
|
-
|
|
73761
|
-
|
|
73620
|
+
let h = Number(hh);
|
|
73621
|
+
const pm = ampm != null && /pm/i.test(ampm);
|
|
73622
|
+
if (ampm != null) {
|
|
73623
|
+
if (h > 12)
|
|
73624
|
+
return m;
|
|
73625
|
+
if (h === 12)
|
|
73626
|
+
h = pm ? 12 : 0;
|
|
73627
|
+
else if (pm)
|
|
73628
|
+
h += 12;
|
|
73629
|
+
}
|
|
73630
|
+
if (h > 23)
|
|
73631
|
+
return m;
|
|
73632
|
+
const minute = mm != null ? Number(mm) : 0;
|
|
73633
|
+
if (minute > 59)
|
|
73634
|
+
return m;
|
|
73635
|
+
const now = new Date(nowMs2);
|
|
73636
|
+
const ms = Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate(), h, minute);
|
|
73637
|
+
return `${renderLocalClock(ms, tz)} ${tzAbbrev(ms, tz)}`;
|
|
73762
73638
|
});
|
|
73763
|
-
return
|
|
73639
|
+
return out;
|
|
73764
73640
|
}
|
|
73765
|
-
function
|
|
73766
|
-
if (!
|
|
73767
|
-
return
|
|
73641
|
+
function normalizeUtcDatetimes(text4, tz, nowMs2) {
|
|
73642
|
+
if (!text4)
|
|
73643
|
+
return text4;
|
|
73644
|
+
try {
|
|
73645
|
+
if (!/\d{4}-\d{2}-\d{2}T|\b(?:UTC|GMT)\b/i.test(text4))
|
|
73646
|
+
return text4;
|
|
73647
|
+
const { masked, restore: restore2 } = maskTemporalRegions(text4);
|
|
73648
|
+
const out = perNonQuoteLine(masked, (line) => convertUtcInLine(line, tz, nowMs2));
|
|
73649
|
+
return restore2(out);
|
|
73650
|
+
} catch {
|
|
73651
|
+
return text4;
|
|
73768
73652
|
}
|
|
73769
|
-
|
|
73770
|
-
|
|
73771
|
-
|
|
73772
|
-
|
|
73773
|
-
|
|
73774
|
-
|
|
73653
|
+
}
|
|
73654
|
+
var REL = "(today|tonight|this\\s+morning|this\\s+afternoon|this\\s+evening|tomorrow|yesterday)";
|
|
73655
|
+
var SEP = "([\\s,]*(?:on\\s+)?\\(?\\s*)";
|
|
73656
|
+
var WD = "(?:mon|tue|wed|thu|fri|sat|sun)[a-z]*";
|
|
73657
|
+
var MON = "(?:jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec)[a-z]*";
|
|
73658
|
+
var WD_GROUP = `((?:${WD})[\\s,]+)?`;
|
|
73659
|
+
var DATE_CORE = `((?:\\d{1,2}\\s+${MON})|(?:${MON}\\s+\\d{1,2})|(?:\\d{4}-\\d{2}-\\d{2})|(?:\\d{1,2}\\/\\d{2}))`;
|
|
73660
|
+
var MONTHS4 = ["jan", "feb", "mar", "apr", "may", "jun", "jul", "aug", "sep", "oct", "nov", "dec"];
|
|
73661
|
+
var WEEKDAY_SHORT = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
|
|
73662
|
+
function buildRelRegex() {
|
|
73663
|
+
return new RegExp(REL + SEP + WD_GROUP + DATE_CORE, "gi");
|
|
73664
|
+
}
|
|
73665
|
+
function parseDateCore(core) {
|
|
73666
|
+
let m;
|
|
73667
|
+
if (m = /^(\d{4})-(\d{2})-(\d{2})$/.exec(core)) {
|
|
73668
|
+
return { year: Number(m[1]), month: Number(m[2]), day: Number(m[3]) };
|
|
73669
|
+
}
|
|
73670
|
+
if (m = /^(\d{1,2})\/(\d{2})$/.exec(core)) {
|
|
73671
|
+
return { year: null, month: Number(m[2]), day: Number(m[1]) };
|
|
73672
|
+
}
|
|
73673
|
+
if (m = /^(\d{1,2})\s+([a-z]+)$/i.exec(core)) {
|
|
73674
|
+
const mon = MONTHS4.indexOf(m[2].slice(0, 3).toLowerCase());
|
|
73675
|
+
if (mon < 0)
|
|
73676
|
+
return null;
|
|
73677
|
+
return { year: null, month: mon + 1, day: Number(m[1]) };
|
|
73678
|
+
}
|
|
73679
|
+
if (m = /^([a-z]+)\s+(\d{1,2})$/i.exec(core)) {
|
|
73680
|
+
const mon = MONTHS4.indexOf(m[1].slice(0, 3).toLowerCase());
|
|
73681
|
+
if (mon < 0)
|
|
73682
|
+
return null;
|
|
73683
|
+
return { year: null, month: mon + 1, day: Number(m[2]) };
|
|
73684
|
+
}
|
|
73685
|
+
return null;
|
|
73686
|
+
}
|
|
73687
|
+
function dayIndex(y, mo1, d) {
|
|
73688
|
+
return Math.floor(Date.UTC(y, mo1 - 1, d) / 86400000);
|
|
73689
|
+
}
|
|
73690
|
+
function resolveYear(parsed, todayY, todayIdx) {
|
|
73691
|
+
if (parsed.year != null)
|
|
73692
|
+
return parsed.year;
|
|
73693
|
+
let best = todayY;
|
|
73694
|
+
let bestAbs = Infinity;
|
|
73695
|
+
for (const y of [todayY - 1, todayY, todayY + 1]) {
|
|
73696
|
+
const diff = Math.abs(dayIndex(y, parsed.month, parsed.day) - todayIdx);
|
|
73697
|
+
if (diff < bestAbs) {
|
|
73698
|
+
bestAbs = diff;
|
|
73699
|
+
best = y;
|
|
73700
|
+
}
|
|
73701
|
+
}
|
|
73702
|
+
return best;
|
|
73703
|
+
}
|
|
73704
|
+
function matchCase(sample, replacement) {
|
|
73705
|
+
if (sample.length > 0 && sample[0] === sample[0].toUpperCase() && sample[0] !== sample[0].toLowerCase()) {
|
|
73706
|
+
return replacement.charAt(0).toUpperCase() + replacement.slice(1);
|
|
73707
|
+
}
|
|
73708
|
+
return replacement;
|
|
73709
|
+
}
|
|
73710
|
+
var TIME_OF_DAY_RE = /^(tonight|this\s+)/i;
|
|
73711
|
+
function rewriteRelativeInLine(line, tz, nowMs2) {
|
|
73712
|
+
const today = localDay(nowMs2, tz);
|
|
73713
|
+
const [ty, tm, td] = today.split("-").map(Number);
|
|
73714
|
+
const todayIdx = dayIndex(ty, tm, td);
|
|
73715
|
+
return line.replace(buildRelRegex(), (full, rel, sep4, wdGroup, core) => {
|
|
73716
|
+
const parsed = parseDateCore(core);
|
|
73717
|
+
if (parsed == null)
|
|
73718
|
+
return full;
|
|
73719
|
+
const year = resolveYear(parsed, ty, todayIdx);
|
|
73720
|
+
const targetIdx = dayIndex(year, parsed.month, parsed.day);
|
|
73721
|
+
const diff = targetIdx - todayIdx;
|
|
73722
|
+
if (diff < -1 || diff > 1)
|
|
73723
|
+
return full;
|
|
73724
|
+
const isTimeOfDay = TIME_OF_DAY_RE.test(rel);
|
|
73725
|
+
let newRel = rel;
|
|
73726
|
+
if (isTimeOfDay) {
|
|
73727
|
+
if (diff !== 0)
|
|
73728
|
+
return full;
|
|
73729
|
+
} else {
|
|
73730
|
+
const want = diff === 1 ? "tomorrow" : diff === 0 ? "today" : "yesterday";
|
|
73731
|
+
newRel = matchCase(rel, want);
|
|
73732
|
+
}
|
|
73733
|
+
let newWdGroup = wdGroup;
|
|
73734
|
+
if (wdGroup != null) {
|
|
73735
|
+
const trueDow = new Date(Date.UTC(year, parsed.month - 1, parsed.day)).getUTCDay();
|
|
73736
|
+
const trueShort = WEEKDAY_SHORT[trueDow];
|
|
73737
|
+
const wdMatch = /^([a-z]+)([\s,]+)$/i.exec(wdGroup);
|
|
73738
|
+
if (wdMatch != null) {
|
|
73739
|
+
const writtenShort = wdMatch[1].slice(0, 3).toLowerCase();
|
|
73740
|
+
if (writtenShort !== trueShort.toLowerCase()) {
|
|
73741
|
+
const wasLong = wdMatch[1].length > 3;
|
|
73742
|
+
const corrected = wasLong ? longWeekday(trueDow) : trueShort;
|
|
73743
|
+
newWdGroup = matchCase(wdMatch[1], corrected) + wdMatch[2];
|
|
73744
|
+
}
|
|
73745
|
+
}
|
|
73746
|
+
}
|
|
73747
|
+
return `${newRel}${sep4}${newWdGroup ?? ""}${core}`;
|
|
73748
|
+
});
|
|
73749
|
+
}
|
|
73750
|
+
var WEEKDAY_LONG = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
|
|
73751
|
+
function longWeekday(dow) {
|
|
73752
|
+
return WEEKDAY_LONG[dow];
|
|
73753
|
+
}
|
|
73754
|
+
function normalizeRelativeDays(text4, tz, nowMs2) {
|
|
73755
|
+
if (!text4)
|
|
73756
|
+
return text4;
|
|
73757
|
+
try {
|
|
73758
|
+
if (!/\b(today|tonight|this\s+(?:morning|afternoon|evening)|tomorrow|yesterday)\b/i.test(text4)) {
|
|
73759
|
+
return text4;
|
|
73760
|
+
}
|
|
73761
|
+
const { masked, restore: restore2 } = maskTemporalRegions(text4);
|
|
73762
|
+
const out = perNonQuoteLine(masked, (line) => rewriteRelativeInLine(line, tz, nowMs2));
|
|
73763
|
+
return restore2(out);
|
|
73764
|
+
} catch {
|
|
73765
|
+
return text4;
|
|
73775
73766
|
}
|
|
73776
|
-
|
|
73767
|
+
}
|
|
73768
|
+
function normalizeTemporal(text4, tz, nowMs2) {
|
|
73769
|
+
return normalizeRelativeDays(normalizeUtcDatetimes(text4, tz, nowMs2), tz, nowMs2);
|
|
73777
73770
|
}
|
|
73778
73771
|
|
|
73779
73772
|
// gateway/outbound-send-path.ts
|
|
73780
|
-
init_format();
|
|
73781
|
-
init_text_voice_scrub();
|
|
73782
73773
|
var import_grammy10 = __toESM(require_mod2(), 1);
|
|
73783
|
-
import { statSync as
|
|
73774
|
+
import { statSync as statSync10 } from "fs";
|
|
73784
73775
|
import { extname } from "path";
|
|
73785
73776
|
|
|
73786
73777
|
// gateway/backstop-delivery.ts
|
|
@@ -73911,6 +73902,314 @@ function combineReadBackResults(perId) {
|
|
|
73911
73902
|
init_format();
|
|
73912
73903
|
init_rich_send();
|
|
73913
73904
|
|
|
73905
|
+
// outbox.ts
|
|
73906
|
+
import { createHash as createHash3 } from "node:crypto";
|
|
73907
|
+
import {
|
|
73908
|
+
existsSync as existsSync24,
|
|
73909
|
+
mkdirSync as mkdirSync25,
|
|
73910
|
+
readFileSync as readFileSync24,
|
|
73911
|
+
readdirSync as readdirSync6,
|
|
73912
|
+
renameSync as renameSync8,
|
|
73913
|
+
statSync as statSync9,
|
|
73914
|
+
unlinkSync as unlinkSync12,
|
|
73915
|
+
writeFileSync as writeFileSync22,
|
|
73916
|
+
appendFileSync as appendFileSync4
|
|
73917
|
+
} from "node:fs";
|
|
73918
|
+
import { join as join28 } from "node:path";
|
|
73919
|
+
import { homedir as homedir10 } from "node:os";
|
|
73920
|
+
function sha256Hex(s) {
|
|
73921
|
+
return createHash3("sha256").update(s, "utf8").digest("hex");
|
|
73922
|
+
}
|
|
73923
|
+
function resolveStateDir2(explicit) {
|
|
73924
|
+
if (explicit != null && explicit !== "")
|
|
73925
|
+
return explicit;
|
|
73926
|
+
const env = process.env.TELEGRAM_STATE_DIR;
|
|
73927
|
+
if (env != null && env !== "")
|
|
73928
|
+
return env;
|
|
73929
|
+
const home2 = process.env.HOME ?? homedir10();
|
|
73930
|
+
return join28(home2, ".claude", "channels", "telegram");
|
|
73931
|
+
}
|
|
73932
|
+
function resolveOutboxDir(stateDir) {
|
|
73933
|
+
return join28(resolveStateDir2(stateDir), "outbox");
|
|
73934
|
+
}
|
|
73935
|
+
var JOURNAL_FILE = "delivered.jsonl";
|
|
73936
|
+
var OUTBOX_MAX_AGE_MS = 30 * 60000;
|
|
73937
|
+
var OUTBOX_QUIET_MS = 5000;
|
|
73938
|
+
var OUTBOX_SENDING_TIMEOUT_MS = 60000;
|
|
73939
|
+
function listPendingRecords(stateDir) {
|
|
73940
|
+
const dir = resolveOutboxDir(stateDir);
|
|
73941
|
+
try {
|
|
73942
|
+
return readdirSync6(dir).filter((f) => f.endsWith(".json") && f !== JOURNAL_FILE && !f.startsWith("."));
|
|
73943
|
+
} catch {
|
|
73944
|
+
return [];
|
|
73945
|
+
}
|
|
73946
|
+
}
|
|
73947
|
+
function readOutboxRecord(fileName, stateDir) {
|
|
73948
|
+
try {
|
|
73949
|
+
const raw = readFileSync24(join28(resolveOutboxDir(stateDir), fileName), "utf8");
|
|
73950
|
+
return JSON.parse(raw);
|
|
73951
|
+
} catch {
|
|
73952
|
+
return null;
|
|
73953
|
+
}
|
|
73954
|
+
}
|
|
73955
|
+
function claimRecord(nonce, stateDir) {
|
|
73956
|
+
const dir = resolveOutboxDir(stateDir);
|
|
73957
|
+
const from = join28(dir, `${nonce}.json`);
|
|
73958
|
+
const to = join28(dir, `${nonce}.sending`);
|
|
73959
|
+
try {
|
|
73960
|
+
renameSync8(from, to);
|
|
73961
|
+
return to;
|
|
73962
|
+
} catch {
|
|
73963
|
+
return null;
|
|
73964
|
+
}
|
|
73965
|
+
}
|
|
73966
|
+
function releaseClaim(nonce, stateDir) {
|
|
73967
|
+
const dir = resolveOutboxDir(stateDir);
|
|
73968
|
+
try {
|
|
73969
|
+
renameSync8(join28(dir, `${nonce}.sending`), join28(dir, `${nonce}.json`));
|
|
73970
|
+
} catch {}
|
|
73971
|
+
}
|
|
73972
|
+
function removeClaimed(nonce, stateDir) {
|
|
73973
|
+
try {
|
|
73974
|
+
unlinkSync12(join28(resolveOutboxDir(stateDir), `${nonce}.sending`));
|
|
73975
|
+
} catch {}
|
|
73976
|
+
}
|
|
73977
|
+
function reclaimStaleSending(stateDir, now = Date.now()) {
|
|
73978
|
+
const dir = resolveOutboxDir(stateDir);
|
|
73979
|
+
let files;
|
|
73980
|
+
try {
|
|
73981
|
+
files = readdirSync6(dir).filter((f) => f.endsWith(".sending"));
|
|
73982
|
+
} catch {
|
|
73983
|
+
return;
|
|
73984
|
+
}
|
|
73985
|
+
for (const f of files) {
|
|
73986
|
+
try {
|
|
73987
|
+
const st = statSync9(join28(dir, f));
|
|
73988
|
+
if (now - st.mtimeMs > OUTBOX_SENDING_TIMEOUT_MS) {
|
|
73989
|
+
renameSync8(join28(dir, f), join28(dir, f.replace(/\.sending$/, ".json")));
|
|
73990
|
+
}
|
|
73991
|
+
} catch {}
|
|
73992
|
+
}
|
|
73993
|
+
}
|
|
73994
|
+
function readDeliveredNonces(stateDir) {
|
|
73995
|
+
const set = new Set;
|
|
73996
|
+
const path2 = join28(resolveOutboxDir(stateDir), JOURNAL_FILE);
|
|
73997
|
+
if (!existsSync24(path2))
|
|
73998
|
+
return set;
|
|
73999
|
+
try {
|
|
74000
|
+
for (const line of readFileSync24(path2, "utf8").split(`
|
|
74001
|
+
`)) {
|
|
74002
|
+
if (!line)
|
|
74003
|
+
continue;
|
|
74004
|
+
try {
|
|
74005
|
+
const e = JSON.parse(line);
|
|
74006
|
+
if (e.turnNonce)
|
|
74007
|
+
set.add(e.turnNonce);
|
|
74008
|
+
} catch {}
|
|
74009
|
+
}
|
|
74010
|
+
} catch {}
|
|
74011
|
+
return set;
|
|
74012
|
+
}
|
|
74013
|
+
var JOURNAL_KEEP = 2000;
|
|
74014
|
+
var JOURNAL_ROTATE_AT = 4000;
|
|
74015
|
+
function appendDelivered(entry, stateDir) {
|
|
74016
|
+
const dir = resolveOutboxDir(stateDir);
|
|
74017
|
+
const path2 = join28(dir, JOURNAL_FILE);
|
|
74018
|
+
try {
|
|
74019
|
+
mkdirSync25(dir, { recursive: true });
|
|
74020
|
+
appendFileSync4(path2, JSON.stringify(entry) + `
|
|
74021
|
+
`, { mode: 384 });
|
|
74022
|
+
compactJournalIfLarge(path2);
|
|
74023
|
+
} catch {}
|
|
74024
|
+
}
|
|
74025
|
+
function compactJournalIfLarge(path2) {
|
|
74026
|
+
try {
|
|
74027
|
+
const lines = readFileSync24(path2, "utf8").split(`
|
|
74028
|
+
`).filter((l) => l.length > 0);
|
|
74029
|
+
if (lines.length <= JOURNAL_ROTATE_AT)
|
|
74030
|
+
return;
|
|
74031
|
+
const kept = lines.slice(lines.length - JOURNAL_KEEP);
|
|
74032
|
+
const tmp = `${path2}.${process.pid}.compact`;
|
|
74033
|
+
writeFileSync22(tmp, kept.join(`
|
|
74034
|
+
`) + `
|
|
74035
|
+
`, { mode: 384 });
|
|
74036
|
+
renameSync8(tmp, path2);
|
|
74037
|
+
} catch {}
|
|
74038
|
+
}
|
|
74039
|
+
function clearOutboxRecord(nonce, stateDir) {
|
|
74040
|
+
const dir = resolveOutboxDir(stateDir);
|
|
74041
|
+
for (const suffix of [".json", ".sending"]) {
|
|
74042
|
+
try {
|
|
74043
|
+
unlinkSync12(join28(dir, `${nonce}${suffix}`));
|
|
74044
|
+
} catch {}
|
|
74045
|
+
}
|
|
74046
|
+
}
|
|
74047
|
+
function decideOutboxSweep(input) {
|
|
74048
|
+
const {
|
|
74049
|
+
record,
|
|
74050
|
+
now,
|
|
74051
|
+
deliveredNonces,
|
|
74052
|
+
textAlreadyDelivered,
|
|
74053
|
+
routable,
|
|
74054
|
+
routePrefix = "",
|
|
74055
|
+
quietMs = OUTBOX_QUIET_MS,
|
|
74056
|
+
maxAgeMs = OUTBOX_MAX_AGE_MS
|
|
74057
|
+
} = input;
|
|
74058
|
+
if (deliveredNonces.has(record.turnNonce))
|
|
74059
|
+
return { action: "skip-journaled" };
|
|
74060
|
+
const age = now - record.createdAt;
|
|
74061
|
+
if (age < quietMs)
|
|
74062
|
+
return { action: "skip-quiet" };
|
|
74063
|
+
if (textAlreadyDelivered)
|
|
74064
|
+
return { action: "skip-dedup" };
|
|
74065
|
+
if (!routable)
|
|
74066
|
+
return { action: "skip-unroutable" };
|
|
74067
|
+
const delayed = age > maxAgeMs;
|
|
74068
|
+
const prefix = (delayed ? "(delayed) " : "") + routePrefix;
|
|
74069
|
+
return { action: delayed ? "send-delayed" : "send", text: prefix + record.text };
|
|
74070
|
+
}
|
|
74071
|
+
function resolveOutboxChat(record, deps) {
|
|
74072
|
+
if (record.chatId != null && record.chatId !== "") {
|
|
74073
|
+
return { chatId: record.chatId, threadId: record.threadId ?? null, via: "anchor" };
|
|
74074
|
+
}
|
|
74075
|
+
if (record.anchorContent && deps.registryChainLookup) {
|
|
74076
|
+
const hit = deps.registryChainLookup(record.anchorContent);
|
|
74077
|
+
if (hit != null)
|
|
74078
|
+
return { chatId: hit.chatId, threadId: hit.threadId, via: "registry" };
|
|
74079
|
+
}
|
|
74080
|
+
if (record.originChatId != null && record.originChatId !== "") {
|
|
74081
|
+
return { chatId: record.originChatId, threadId: record.originThreadId ?? null, via: "origin" };
|
|
74082
|
+
}
|
|
74083
|
+
return null;
|
|
74084
|
+
}
|
|
74085
|
+
function extractTaskId(anchorContent) {
|
|
74086
|
+
if (typeof anchorContent !== "string")
|
|
74087
|
+
return null;
|
|
74088
|
+
const m = anchorContent.match(/<task-id>\s*([^<\s]+)\s*<\/task-id>/) ?? anchorContent.match(/task[_-]?id="([^"]+)"/i) ?? anchorContent.match(/task[_-]?id:\s*([^\s,}"']+)/i);
|
|
74089
|
+
return m ? m[1] : null;
|
|
74090
|
+
}
|
|
74091
|
+
|
|
74092
|
+
// registry/subagents-schema.ts
|
|
74093
|
+
function countRunningBackgroundSubagents(db2) {
|
|
74094
|
+
const row = db2.prepare("SELECT count(*) AS n FROM subagents WHERE background = 1 AND status = 'running'").get();
|
|
74095
|
+
return row?.n ?? 0;
|
|
74096
|
+
}
|
|
74097
|
+
function recordSubagentEnd(db2, args) {
|
|
74098
|
+
db2.prepare(`
|
|
74099
|
+
UPDATE subagents
|
|
74100
|
+
SET
|
|
74101
|
+
ended_at = ?,
|
|
74102
|
+
status = ?,
|
|
74103
|
+
result_summary = COALESCE(?, result_summary)
|
|
74104
|
+
WHERE id = ?
|
|
74105
|
+
AND status NOT IN ('completed', 'failed')
|
|
74106
|
+
`).run(args.endedAt, args.status, args.resultSummary ?? null, args.id);
|
|
74107
|
+
}
|
|
74108
|
+
function recordSubagentStall(db2, args) {
|
|
74109
|
+
args.stalledAt;
|
|
74110
|
+
if (args.reason !== undefined) {
|
|
74111
|
+
db2.prepare(`
|
|
74112
|
+
UPDATE subagents
|
|
74113
|
+
SET status = 'stalled',
|
|
74114
|
+
result_summary = ?
|
|
74115
|
+
WHERE id = ?
|
|
74116
|
+
AND status NOT IN ('completed', 'failed')
|
|
74117
|
+
`).run(args.reason, args.id);
|
|
74118
|
+
return;
|
|
74119
|
+
}
|
|
74120
|
+
db2.prepare(`
|
|
74121
|
+
UPDATE subagents
|
|
74122
|
+
SET status = 'stalled'
|
|
74123
|
+
WHERE id = ?
|
|
74124
|
+
AND status NOT IN ('completed', 'failed')
|
|
74125
|
+
`).run(args.id);
|
|
74126
|
+
}
|
|
74127
|
+
function reapStuckRunningRows(db2, args) {
|
|
74128
|
+
const cutoff = args.now - args.ttlMs;
|
|
74129
|
+
const rows = db2.prepare(`
|
|
74130
|
+
SELECT id, jsonl_agent_id FROM subagents
|
|
74131
|
+
WHERE status = 'running'
|
|
74132
|
+
AND background = 1
|
|
74133
|
+
AND COALESCE(last_activity_at, started_at) < ?
|
|
74134
|
+
`).all(cutoff);
|
|
74135
|
+
const candidates = args.isLive ? rows.filter((r) => !args.isLive(r.jsonl_agent_id)) : rows;
|
|
74136
|
+
for (const row of candidates) {
|
|
74137
|
+
recordSubagentStall(db2, {
|
|
74138
|
+
id: row.id,
|
|
74139
|
+
stalledAt: args.now,
|
|
74140
|
+
reason: `reaped: stuck in running past ${Math.round(args.ttlMs / 60000)}min ttl (jsonl linkage likely missing)`
|
|
74141
|
+
});
|
|
74142
|
+
}
|
|
74143
|
+
return { reaped: candidates.length, ids: candidates.map((r) => r.id) };
|
|
74144
|
+
}
|
|
74145
|
+
function recordSubagentResume(db2, args) {
|
|
74146
|
+
args.resumedAt;
|
|
74147
|
+
db2.prepare(`
|
|
74148
|
+
UPDATE subagents
|
|
74149
|
+
SET status = 'running'
|
|
74150
|
+
WHERE id = ?
|
|
74151
|
+
AND status = 'stalled'
|
|
74152
|
+
`).run(args.id);
|
|
74153
|
+
}
|
|
74154
|
+
function bumpSubagentActivity(db2, args) {
|
|
74155
|
+
db2.prepare(`
|
|
74156
|
+
UPDATE subagents
|
|
74157
|
+
SET last_activity_at = ?
|
|
74158
|
+
WHERE id = ?
|
|
74159
|
+
`).run(args.ts, args.id);
|
|
74160
|
+
}
|
|
74161
|
+
function recordSubagentModel(db2, args) {
|
|
74162
|
+
db2.prepare(`
|
|
74163
|
+
UPDATE subagents
|
|
74164
|
+
SET model = ?
|
|
74165
|
+
WHERE id = ?
|
|
74166
|
+
`).run(args.model, args.id);
|
|
74167
|
+
}
|
|
74168
|
+
function recordNestedSubagentDispatch(db2, args) {
|
|
74169
|
+
db2.prepare(`
|
|
74170
|
+
INSERT OR IGNORE INTO subagents
|
|
74171
|
+
(id, parent_session_id, parent_turn_key, agent_type, description,
|
|
74172
|
+
background, started_at, last_activity_at, status, jsonl_agent_id,
|
|
74173
|
+
parent_agent_id)
|
|
74174
|
+
VALUES (?, NULL, NULL, ?, ?, ?, ?, ?, 'running', NULL, ?)
|
|
74175
|
+
`).run(args.toolUseId, args.agentType ?? null, args.description ?? null, args.background ? 1 : 0, args.now, args.now, args.parentJsonlAgentId);
|
|
74176
|
+
db2.prepare(`
|
|
74177
|
+
UPDATE subagents
|
|
74178
|
+
SET parent_agent_id = COALESCE(parent_agent_id, ?),
|
|
74179
|
+
parent_turn_key = COALESCE(
|
|
74180
|
+
parent_turn_key,
|
|
74181
|
+
(SELECT p.parent_turn_key FROM subagents p
|
|
74182
|
+
WHERE p.jsonl_agent_id = ? LIMIT 1)
|
|
74183
|
+
)
|
|
74184
|
+
WHERE id = ?
|
|
74185
|
+
`).run(args.parentJsonlAgentId, args.parentJsonlAgentId, args.toolUseId);
|
|
74186
|
+
}
|
|
74187
|
+
function resolveSubagentOriginTurnKey(db2, jsonlAgentId, maxHops = 5) {
|
|
74188
|
+
const seen = new Set;
|
|
74189
|
+
let currentJsonlId = jsonlAgentId;
|
|
74190
|
+
for (let hop = 0;hop <= maxHops && currentJsonlId != null; hop++) {
|
|
74191
|
+
if (seen.has(currentJsonlId))
|
|
74192
|
+
return null;
|
|
74193
|
+
seen.add(currentJsonlId);
|
|
74194
|
+
const row = db2.prepare("SELECT parent_turn_key, parent_agent_id FROM subagents WHERE jsonl_agent_id = ? LIMIT 1").get(currentJsonlId);
|
|
74195
|
+
if (row == null)
|
|
74196
|
+
return null;
|
|
74197
|
+
if (row.parent_turn_key != null && row.parent_turn_key.length > 0)
|
|
74198
|
+
return row.parent_turn_key;
|
|
74199
|
+
currentJsonlId = row.parent_agent_id ?? null;
|
|
74200
|
+
}
|
|
74201
|
+
return null;
|
|
74202
|
+
}
|
|
74203
|
+
|
|
74204
|
+
// gateway/outbox-sweep.ts
|
|
74205
|
+
function journalExternalDelivery(args, stateDir, now = Date.now()) {
|
|
74206
|
+
const nonce = args.turnNonce;
|
|
74207
|
+
if (nonce == null || nonce === "")
|
|
74208
|
+
return;
|
|
74209
|
+
appendDelivered({ turnNonce: nonce, textSha256: sha256Hex(args.text), tgMessageId: args.tgMessageId, ts: now }, stateDir);
|
|
74210
|
+
clearOutboxRecord(nonce, stateDir);
|
|
74211
|
+
}
|
|
74212
|
+
|
|
73914
74213
|
// gateway/chat-id-fallback.ts
|
|
73915
74214
|
function resolveChatIdFallback(rawChatId, access, turnSessionChatId, lastKnownChatId, hasLiveTurn) {
|
|
73916
74215
|
if (access.allowFrom.includes(rawChatId) || rawChatId in access.groups) {
|
|
@@ -73951,12 +74250,12 @@ function decideOverPing(input) {
|
|
|
73951
74250
|
// silent-reply-anchor.ts
|
|
73952
74251
|
init_format();
|
|
73953
74252
|
var TELEGRAM_MSG_CAP2 = RICH_MESSAGE_MAX_CHARS;
|
|
73954
|
-
function
|
|
74253
|
+
function enabled4() {
|
|
73955
74254
|
const v = process.env.SWITCHROOM_DISABLE_SILENT_REPLY_AUTOEDIT;
|
|
73956
74255
|
return !(v === "1" || v === "true");
|
|
73957
74256
|
}
|
|
73958
74257
|
function decideSilentReplyAnchor(input) {
|
|
73959
|
-
if (!
|
|
74258
|
+
if (!enabled4()) {
|
|
73960
74259
|
return { kind: "fresh", becomesAnchor: false };
|
|
73961
74260
|
}
|
|
73962
74261
|
if (!input.effectivelySilent) {
|
|
@@ -74279,11 +74578,11 @@ function classifyPhotoFile(path2) {
|
|
|
74279
74578
|
}
|
|
74280
74579
|
|
|
74281
74580
|
// streaming-metrics.ts
|
|
74282
|
-
function
|
|
74581
|
+
function enabled5() {
|
|
74283
74582
|
return process.env.SWITCHROOM_STREAMING_METRICS === "1";
|
|
74284
74583
|
}
|
|
74285
74584
|
function logStreamingEvent2(ev) {
|
|
74286
|
-
if (!
|
|
74585
|
+
if (!enabled5())
|
|
74287
74586
|
return;
|
|
74288
74587
|
const line = JSON.stringify({ ts: monotonicNowMs2(), ...ev });
|
|
74289
74588
|
try {
|
|
@@ -74300,26 +74599,26 @@ function monotonicNowMs2() {
|
|
|
74300
74599
|
}
|
|
74301
74600
|
|
|
74302
74601
|
// silent-end.ts
|
|
74303
|
-
import { existsSync as
|
|
74304
|
-
import { dirname as dirname11, join as
|
|
74305
|
-
import { homedir as
|
|
74602
|
+
import { existsSync as existsSync25, readFileSync as readFileSync25, writeFileSync as writeFileSync23, unlinkSync as unlinkSync13, mkdirSync as mkdirSync26 } from "node:fs";
|
|
74603
|
+
import { dirname as dirname11, join as join29 } from "node:path";
|
|
74604
|
+
import { homedir as homedir11 } from "node:os";
|
|
74306
74605
|
var SILENT_END_MAX_RETRIES2 = 2;
|
|
74307
74606
|
var SILENT_END_STALE_RECORD_MAX_AGE_MS2 = 30 * 60000;
|
|
74308
74607
|
function silentEndFallbackText(turnDurationMs) {
|
|
74309
74608
|
const elapsed = typeof turnDurationMs === "number" && Number.isFinite(turnDurationMs) && turnDurationMs > 0 ? ` (waited ${Math.round(turnDurationMs / 1000)}s)` : "";
|
|
74310
74609
|
return "\u26a0\ufe0f The agent finished working but didn\u2019t send a reply" + elapsed + " \u2014 your last message may not have been answered. Please try asking again.";
|
|
74311
74610
|
}
|
|
74312
|
-
function
|
|
74611
|
+
function resolveStateDir3(deps) {
|
|
74313
74612
|
if (deps?.stateDir != null)
|
|
74314
74613
|
return deps.stateDir;
|
|
74315
74614
|
const env = process.env.TELEGRAM_STATE_DIR;
|
|
74316
74615
|
if (env != null && env !== "")
|
|
74317
74616
|
return env;
|
|
74318
|
-
const home2 = process.env.HOME ??
|
|
74319
|
-
return
|
|
74617
|
+
const home2 = process.env.HOME ?? homedir11();
|
|
74618
|
+
return join29(home2, ".claude", "channels", "telegram");
|
|
74320
74619
|
}
|
|
74321
74620
|
function resolveStatePath3(deps) {
|
|
74322
|
-
return
|
|
74621
|
+
return join29(resolveStateDir3(deps), "silent-end-pending.json");
|
|
74323
74622
|
}
|
|
74324
74623
|
function emitLog2(deps, line) {
|
|
74325
74624
|
if (deps?.log != null)
|
|
@@ -74331,8 +74630,8 @@ function writeSilentEndState2(args, deps) {
|
|
|
74331
74630
|
const statePath = resolveStatePath3(deps);
|
|
74332
74631
|
let retryCount = 0;
|
|
74333
74632
|
try {
|
|
74334
|
-
if (
|
|
74335
|
-
const prev = JSON.parse(
|
|
74633
|
+
if (existsSync25(statePath)) {
|
|
74634
|
+
const prev = JSON.parse(readFileSync25(statePath, "utf8"));
|
|
74336
74635
|
if (prev.turnKey === args.turnKey && typeof prev.retryCount === "number") {
|
|
74337
74636
|
retryCount = prev.retryCount;
|
|
74338
74637
|
}
|
|
@@ -74348,8 +74647,8 @@ function writeSilentEndState2(args, deps) {
|
|
|
74348
74647
|
timestamp: Date.now()
|
|
74349
74648
|
};
|
|
74350
74649
|
try {
|
|
74351
|
-
|
|
74352
|
-
|
|
74650
|
+
mkdirSync26(dirname11(statePath), { recursive: true });
|
|
74651
|
+
writeFileSync23(statePath, JSON.stringify(state3), "utf8");
|
|
74353
74652
|
emitLog2(deps, `silent-end: wrote state file turnKey=${args.turnKey} retryCount=${retryCount}
|
|
74354
74653
|
`);
|
|
74355
74654
|
} catch (err) {
|
|
@@ -74359,13 +74658,13 @@ function writeSilentEndState2(args, deps) {
|
|
|
74359
74658
|
}
|
|
74360
74659
|
function clearSilentEndState2(turnKey3, deps) {
|
|
74361
74660
|
const statePath = resolveStatePath3(deps);
|
|
74362
|
-
if (!
|
|
74661
|
+
if (!existsSync25(statePath))
|
|
74363
74662
|
return;
|
|
74364
74663
|
try {
|
|
74365
|
-
const prev = JSON.parse(
|
|
74664
|
+
const prev = JSON.parse(readFileSync25(statePath, "utf8"));
|
|
74366
74665
|
if (prev.turnKey != null && prev.turnKey !== turnKey3)
|
|
74367
74666
|
return;
|
|
74368
|
-
|
|
74667
|
+
unlinkSync13(statePath);
|
|
74369
74668
|
emitLog2(deps, `silent-end: cleared state file turnKey=${turnKey3}
|
|
74370
74669
|
`);
|
|
74371
74670
|
} catch {}
|
|
@@ -74396,10 +74695,10 @@ function settleCapturedProseDelivery(outcome, effects) {
|
|
|
74396
74695
|
}
|
|
74397
74696
|
function readSilentEndState2(deps) {
|
|
74398
74697
|
const statePath = resolveStatePath3(deps);
|
|
74399
|
-
if (!
|
|
74698
|
+
if (!existsSync25(statePath))
|
|
74400
74699
|
return null;
|
|
74401
74700
|
try {
|
|
74402
|
-
return JSON.parse(
|
|
74701
|
+
return JSON.parse(readFileSync25(statePath, "utf8"));
|
|
74403
74702
|
} catch {
|
|
74404
74703
|
return null;
|
|
74405
74704
|
}
|
|
@@ -74428,10 +74727,21 @@ function recordSilentTurnEnd2(args, deps) {
|
|
|
74428
74727
|
var recordUndeliveredTurnEnd2 = recordSilentTurnEnd2;
|
|
74429
74728
|
|
|
74430
74729
|
// gateway/outbound-send-path.ts
|
|
74431
|
-
function normalizeOutboundBody(rawText, site, redact2) {
|
|
74432
|
-
|
|
74730
|
+
function normalizeOutboundBody(rawText, site, redact2, opts = {}) {
|
|
74731
|
+
const { literalText = false, addSpacers = false, tz, nowMs: nowMs2 } = opts;
|
|
74732
|
+
let text4 = repairEscapedWhitespace(rawText);
|
|
74733
|
+
if (!literalText)
|
|
74734
|
+
text4 = normalizeParagraphBreaks(text4);
|
|
74433
74735
|
text4 = redact2(text4, site);
|
|
74434
|
-
|
|
74736
|
+
if (!literalText) {
|
|
74737
|
+
let formatted = stripExcessBold(normalizePunctuation(text4));
|
|
74738
|
+
if (addSpacers)
|
|
74739
|
+
formatted = addParagraphSpacers(formatted);
|
|
74740
|
+
text4 = formatted;
|
|
74741
|
+
if (tz != null && nowMs2 != null) {
|
|
74742
|
+
text4 = normalizeTemporal(text4, tz, nowMs2);
|
|
74743
|
+
}
|
|
74744
|
+
}
|
|
74435
74745
|
let voiceReplaced = 0;
|
|
74436
74746
|
const scrub = scrubVoice(text4);
|
|
74437
74747
|
if (scrub.replaced > 0) {
|
|
@@ -74683,7 +74993,10 @@ async function sendReply(deps, req) {
|
|
|
74683
74993
|
const rawText = args.text;
|
|
74684
74994
|
if (rawText == null || rawText === "")
|
|
74685
74995
|
throw new Error("reply: text is required and cannot be empty");
|
|
74686
|
-
const _normalized = normalizeOutboundBody(rawText, "reply", redactOutboundText
|
|
74996
|
+
const _normalized = normalizeOutboundBody(rawText, "reply", redactOutboundText, {
|
|
74997
|
+
tz: resolveEnvTimezone(),
|
|
74998
|
+
nowMs: Date.now()
|
|
74999
|
+
});
|
|
74687
75000
|
let text4 = _normalized.text;
|
|
74688
75001
|
if (_normalized.voiceReplaced > 0) {
|
|
74689
75002
|
emitRuntimeMetric2({
|
|
@@ -74862,7 +75175,7 @@ ${url}`;
|
|
|
74862
75175
|
}
|
|
74863
75176
|
for (const f of files) {
|
|
74864
75177
|
assertSendable(f);
|
|
74865
|
-
const st =
|
|
75178
|
+
const st = statSync10(f);
|
|
74866
75179
|
if (st.size > MAX_ATTACHMENT_BYTES) {
|
|
74867
75180
|
throw new Error(`file too large: ${f} (${(st.size / 1024 / 1024).toFixed(1)}MB, max 50MB)`);
|
|
74868
75181
|
}
|
|
@@ -75063,6 +75376,9 @@ ${url}`;
|
|
|
75063
75376
|
closeObligationOnSubstantiveReply(args, turn2, replyRoutedOriginTurn);
|
|
75064
75377
|
}
|
|
75065
75378
|
outboundDedup.record(chat_id, threadId, decision.mergedText, Date.now(), turn2?.registryKey ?? null);
|
|
75379
|
+
if (isFinalAnswerReply({ text: decision.mergedText, disableNotification: modelDisableNotification })) {
|
|
75380
|
+
journalExternalDelivery({ turnNonce: turn2?.turnId ?? null, text: decision.mergedText, tgMessageId: decision.messageId });
|
|
75381
|
+
}
|
|
75066
75382
|
silentAnchorEditDone = true;
|
|
75067
75383
|
} catch (err) {
|
|
75068
75384
|
process.stderr.write(`telegram gateway: silent-reply auto-edit failed, ` + `falling back to fresh send: ${err instanceof Error ? err.message : String(err)}
|
|
@@ -75336,7 +75652,11 @@ ${url}`;
|
|
|
75336
75652
|
process.stderr.write(`telegram channel: reply: finalized chatId=${chat_id} messageIds=[${sentIds.join(",")}] chunks=${chunks.length}
|
|
75337
75653
|
`);
|
|
75338
75654
|
if (sentIds.length > 0) {
|
|
75339
|
-
|
|
75655
|
+
const t = getCurrentTurn();
|
|
75656
|
+
outboundDedup.record(chat_id, threadId, text4, Date.now(), t?.registryKey ?? null);
|
|
75657
|
+
if (shouldJournalReplySiteDelivery({ text: rawText, disableNotification: modelDisableNotification })) {
|
|
75658
|
+
journalExternalDelivery({ turnNonce: t?.turnId ?? null, text: text4, tgMessageId: sentIds[sentIds.length - 1] });
|
|
75659
|
+
}
|
|
75340
75660
|
}
|
|
75341
75661
|
return { content: [{ type: "text", text: result }] };
|
|
75342
75662
|
}
|
|
@@ -75389,6 +75709,7 @@ async function deliverCapturedProse(deps, args) {
|
|
|
75389
75709
|
} catch {}
|
|
75390
75710
|
}
|
|
75391
75711
|
outboundDedup.record(chatId, threadId, text4, now, registryKey);
|
|
75712
|
+
journalExternalDelivery({ turnNonce: originTurnId, text: text4, tgMessageId: sentIds[sentIds.length - 1] });
|
|
75392
75713
|
process.stderr.write(`telegram gateway: captured-prose delivery \u2014 sent ${out.length} chars recovered from ` + `transcript scan (chat=${chatId} origin=${originTurnId})
|
|
75393
75714
|
`);
|
|
75394
75715
|
outcome = "sent";
|
|
@@ -75841,8 +76162,37 @@ class LivenessTracker {
|
|
|
75841
76162
|
}
|
|
75842
76163
|
}
|
|
75843
76164
|
|
|
75844
|
-
// gateway/
|
|
76165
|
+
// gateway/outbound-send-path.ts
|
|
76166
|
+
init_format();
|
|
76167
|
+
init_text_voice_scrub();
|
|
76168
|
+
var import_grammy11 = __toESM(require_mod2(), 1);
|
|
75845
76169
|
init_format();
|
|
76170
|
+
init_rich_send();
|
|
76171
|
+
function normalizeOutboundBody2(rawText, site, redact2, opts = {}) {
|
|
76172
|
+
const { literalText = false, addSpacers = false, tz, nowMs: nowMs2 } = opts;
|
|
76173
|
+
let text4 = repairEscapedWhitespace(rawText);
|
|
76174
|
+
if (!literalText)
|
|
76175
|
+
text4 = normalizeParagraphBreaks(text4);
|
|
76176
|
+
text4 = redact2(text4, site);
|
|
76177
|
+
if (!literalText) {
|
|
76178
|
+
let formatted = stripExcessBold(normalizePunctuation(text4));
|
|
76179
|
+
if (addSpacers)
|
|
76180
|
+
formatted = addParagraphSpacers(formatted);
|
|
76181
|
+
text4 = formatted;
|
|
76182
|
+
if (tz != null && nowMs2 != null) {
|
|
76183
|
+
text4 = normalizeTemporal(text4, tz, nowMs2);
|
|
76184
|
+
}
|
|
76185
|
+
}
|
|
76186
|
+
let voiceReplaced = 0;
|
|
76187
|
+
const scrub = scrubVoice(text4);
|
|
76188
|
+
if (scrub.replaced > 0) {
|
|
76189
|
+
text4 = scrub.scrubbed;
|
|
76190
|
+
voiceReplaced = scrub.replaced;
|
|
76191
|
+
}
|
|
76192
|
+
return { text: text4, voiceReplaced };
|
|
76193
|
+
}
|
|
76194
|
+
|
|
76195
|
+
// gateway/stream-render.ts
|
|
75846
76196
|
init_history();
|
|
75847
76197
|
|
|
75848
76198
|
// narrative-dedup.ts
|
|
@@ -75908,20 +76258,20 @@ var INTERRUPTED_VIA = new Set([
|
|
|
75908
76258
|
init_rich_send();
|
|
75909
76259
|
|
|
75910
76260
|
// runtime-metrics.ts
|
|
75911
|
-
import { mkdirSync as
|
|
75912
|
-
import { dirname as dirname12, join as
|
|
76261
|
+
import { mkdirSync as mkdirSync28, appendFileSync as appendFileSync5 } from "node:fs";
|
|
76262
|
+
import { dirname as dirname12, join as join31 } from "node:path";
|
|
75913
76263
|
function resolveJsonlPath2() {
|
|
75914
76264
|
const override = process.env.SWITCHROOM_RUNTIME_METRICS_PATH;
|
|
75915
76265
|
if (override && override.trim() !== "")
|
|
75916
76266
|
return override.trim();
|
|
75917
76267
|
const base = process.env.SWITCHROOM_RUNTIME_STATE_DIR ?? "/state/agent";
|
|
75918
|
-
return
|
|
76268
|
+
return join31(base, "runtime-metrics.jsonl");
|
|
75919
76269
|
}
|
|
75920
76270
|
function appendJsonl2(line) {
|
|
75921
76271
|
const path2 = resolveJsonlPath2();
|
|
75922
76272
|
try {
|
|
75923
|
-
|
|
75924
|
-
|
|
76273
|
+
mkdirSync28(dirname12(path2), { recursive: true });
|
|
76274
|
+
appendFileSync5(path2, line + `
|
|
75925
76275
|
`, "utf-8");
|
|
75926
76276
|
} catch (err) {
|
|
75927
76277
|
process.stderr.write(`runtime-metrics: jsonl write failed: ${err.message}
|
|
@@ -75942,9 +76292,6 @@ function emitRuntimeMetric2(event) {
|
|
|
75942
76292
|
captureEvent(event.kind, { ...event, ts: wrapped.ts });
|
|
75943
76293
|
}
|
|
75944
76294
|
|
|
75945
|
-
// gateway/stream-render.ts
|
|
75946
|
-
init_text_voice_scrub();
|
|
75947
|
-
|
|
75948
76295
|
// gateway/feed-open-gate.ts
|
|
75949
76296
|
function shouldEarlyOpenLiveness(input) {
|
|
75950
76297
|
if (!input.enabled)
|
|
@@ -76414,28 +76761,28 @@ function detectStatusSurfaceDegraded(t) {
|
|
|
76414
76761
|
// gateway/turn-active-marker.ts
|
|
76415
76762
|
import {
|
|
76416
76763
|
closeSync as closeSync5,
|
|
76417
|
-
existsSync as
|
|
76418
|
-
mkdirSync as
|
|
76764
|
+
existsSync as existsSync28,
|
|
76765
|
+
mkdirSync as mkdirSync29,
|
|
76419
76766
|
openSync as openSync5,
|
|
76420
|
-
readFileSync as
|
|
76421
|
-
statSync as
|
|
76422
|
-
unlinkSync as
|
|
76767
|
+
readFileSync as readFileSync26,
|
|
76768
|
+
statSync as statSync11,
|
|
76769
|
+
unlinkSync as unlinkSync14,
|
|
76423
76770
|
utimesSync,
|
|
76424
|
-
writeFileSync as
|
|
76771
|
+
writeFileSync as writeFileSync24
|
|
76425
76772
|
} from "node:fs";
|
|
76426
|
-
import { join as
|
|
76773
|
+
import { join as join32 } from "node:path";
|
|
76427
76774
|
var TURN_ACTIVE_MARKER_FILE = "turn-active.json";
|
|
76428
76775
|
var TURN_ACTIVE_HARD_TTL_MS = 10 * 60000;
|
|
76429
76776
|
function writeTurnActiveMarker(stateDir, marker) {
|
|
76430
76777
|
try {
|
|
76431
|
-
|
|
76432
|
-
|
|
76778
|
+
mkdirSync29(stateDir, { recursive: true });
|
|
76779
|
+
writeFileSync24(join32(stateDir, TURN_ACTIVE_MARKER_FILE), JSON.stringify(marker, null, 2) + `
|
|
76433
76780
|
`, { mode: 384 });
|
|
76434
76781
|
} catch {}
|
|
76435
76782
|
}
|
|
76436
76783
|
function touchTurnActiveMarker(stateDir) {
|
|
76437
|
-
const path2 =
|
|
76438
|
-
if (!
|
|
76784
|
+
const path2 = join32(stateDir, TURN_ACTIVE_MARKER_FILE);
|
|
76785
|
+
if (!existsSync28(path2))
|
|
76439
76786
|
return;
|
|
76440
76787
|
const now = new Date;
|
|
76441
76788
|
try {
|
|
@@ -76449,13 +76796,13 @@ function touchTurnActiveMarker(stateDir) {
|
|
|
76449
76796
|
}
|
|
76450
76797
|
function removeTurnActiveMarker(stateDir) {
|
|
76451
76798
|
try {
|
|
76452
|
-
|
|
76799
|
+
unlinkSync14(join32(stateDir, TURN_ACTIVE_MARKER_FILE));
|
|
76453
76800
|
} catch {}
|
|
76454
76801
|
}
|
|
76455
76802
|
function readTurnActiveMarkerAgeMs(stateDir, now) {
|
|
76456
|
-
const path2 =
|
|
76803
|
+
const path2 = join32(stateDir, TURN_ACTIVE_MARKER_FILE);
|
|
76457
76804
|
try {
|
|
76458
|
-
const st =
|
|
76805
|
+
const st = statSync11(path2);
|
|
76459
76806
|
return (now ?? Date.now()) - st.mtimeMs;
|
|
76460
76807
|
} catch {
|
|
76461
76808
|
return null;
|
|
@@ -76512,7 +76859,7 @@ function finalizeBackstopSendGated(turn, send) {
|
|
|
76512
76859
|
var MAX_LIFETIME_MS2 = 30 * 60000;
|
|
76513
76860
|
var stateByKey2 = new Map;
|
|
76514
76861
|
var activeDeps3 = null;
|
|
76515
|
-
function
|
|
76862
|
+
function enabled6() {
|
|
76516
76863
|
const v = process.env.SWITCHROOM_DISABLE_PENDING_PROGRESS;
|
|
76517
76864
|
return !(v === "1" || v === "true");
|
|
76518
76865
|
}
|
|
@@ -76520,7 +76867,7 @@ function nowMs2() {
|
|
|
76520
76867
|
return activeDeps3?.nowMs ? activeDeps3.nowMs() : Date.now();
|
|
76521
76868
|
}
|
|
76522
76869
|
function noteTurnEnd2(key) {
|
|
76523
|
-
if (!
|
|
76870
|
+
if (!enabled6())
|
|
76524
76871
|
return;
|
|
76525
76872
|
const s = stateByKey2.get(key);
|
|
76526
76873
|
if (s == null)
|
|
@@ -77231,24 +77578,18 @@ function handleSessionEvent(deps, ev) {
|
|
|
77231
77578
|
return;
|
|
77232
77579
|
}
|
|
77233
77580
|
if (turnEndDecision === "flush" && flushDecision.kind === "flush") {
|
|
77234
|
-
let capturedText = flushDecision.text;
|
|
77235
|
-
capturedText = normalizeParagraphBreaks(repairEscapedWhitespace(capturedText));
|
|
77236
77581
|
const backstopChatId = chatId;
|
|
77237
77582
|
const backstopThreadId = threadId;
|
|
77238
77583
|
const backstopCtrl = ctrl;
|
|
77239
|
-
|
|
77240
|
-
capturedText =
|
|
77241
|
-
{
|
|
77242
|
-
|
|
77243
|
-
|
|
77244
|
-
|
|
77245
|
-
|
|
77246
|
-
|
|
77247
|
-
|
|
77248
|
-
replaced: scrub.replaced,
|
|
77249
|
-
site: "turn_flush"
|
|
77250
|
-
});
|
|
77251
|
-
}
|
|
77584
|
+
const _flushNorm = normalizeOutboundBody2(flushDecision.text, "turn_flush", redactOutboundText, { tz: resolveEnvTimezone(), nowMs: Date.now() });
|
|
77585
|
+
let capturedText = _flushNorm.text;
|
|
77586
|
+
if (_flushNorm.voiceReplaced > 0) {
|
|
77587
|
+
emitRuntimeMetric2({
|
|
77588
|
+
kind: "voice_scrub_applied",
|
|
77589
|
+
chatKey: statusKey(backstopChatId, backstopThreadId),
|
|
77590
|
+
replaced: _flushNorm.voiceReplaced,
|
|
77591
|
+
site: "turn_flush"
|
|
77592
|
+
});
|
|
77252
77593
|
}
|
|
77253
77594
|
turn.finalAnswerDelivered = true;
|
|
77254
77595
|
turn.finalAnswerSubstantive = true;
|
|
@@ -78346,19 +78687,19 @@ function resolveAgentDirFromEnv() {
|
|
|
78346
78687
|
}
|
|
78347
78688
|
|
|
78348
78689
|
// active-reactions.ts
|
|
78349
|
-
import { readFileSync as
|
|
78350
|
-
import { join as
|
|
78690
|
+
import { readFileSync as readFileSync27, writeFileSync as writeFileSync25, renameSync as renameSync9, existsSync as existsSync29, unlinkSync as unlinkSync15 } from "node:fs";
|
|
78691
|
+
import { join as join33 } from "node:path";
|
|
78351
78692
|
var ACTIVE_REACTIONS_FILENAME = ".active-reactions.json";
|
|
78352
78693
|
function reactionsPath(agentDir) {
|
|
78353
|
-
return
|
|
78694
|
+
return join33(agentDir, ACTIVE_REACTIONS_FILENAME);
|
|
78354
78695
|
}
|
|
78355
78696
|
function readActiveReactions(agentDir) {
|
|
78356
78697
|
const p = reactionsPath(agentDir);
|
|
78357
|
-
if (!
|
|
78698
|
+
if (!existsSync29(p))
|
|
78358
78699
|
return [];
|
|
78359
78700
|
let raw;
|
|
78360
78701
|
try {
|
|
78361
|
-
raw =
|
|
78702
|
+
raw = readFileSync27(p, "utf-8");
|
|
78362
78703
|
} catch {
|
|
78363
78704
|
return [];
|
|
78364
78705
|
}
|
|
@@ -78384,15 +78725,15 @@ function writeActiveReactions(agentDir, reactions) {
|
|
|
78384
78725
|
const p = reactionsPath(agentDir);
|
|
78385
78726
|
if (reactions.length === 0) {
|
|
78386
78727
|
try {
|
|
78387
|
-
|
|
78728
|
+
unlinkSync15(p);
|
|
78388
78729
|
} catch {}
|
|
78389
78730
|
return;
|
|
78390
78731
|
}
|
|
78391
78732
|
const tmp = `${p}.tmp-${process.pid}-${Date.now()}`;
|
|
78392
78733
|
try {
|
|
78393
|
-
|
|
78734
|
+
writeFileSync25(tmp, JSON.stringify(reactions) + `
|
|
78394
78735
|
`, "utf-8");
|
|
78395
|
-
|
|
78736
|
+
renameSync9(tmp, p);
|
|
78396
78737
|
} catch {}
|
|
78397
78738
|
}
|
|
78398
78739
|
function addActiveReaction(agentDir, reaction) {
|
|
@@ -78409,24 +78750,24 @@ function removeActiveReaction(agentDir, chatId, messageId) {
|
|
|
78409
78750
|
}
|
|
78410
78751
|
function clearActiveReactions(agentDir) {
|
|
78411
78752
|
try {
|
|
78412
|
-
|
|
78753
|
+
unlinkSync15(reactionsPath(agentDir));
|
|
78413
78754
|
} catch {}
|
|
78414
78755
|
}
|
|
78415
78756
|
|
|
78416
78757
|
// active-reactions.ts
|
|
78417
|
-
import { readFileSync as
|
|
78418
|
-
import { join as
|
|
78758
|
+
import { readFileSync as readFileSync28, writeFileSync as writeFileSync26, renameSync as renameSync10, existsSync as existsSync30, unlinkSync as unlinkSync16 } from "node:fs";
|
|
78759
|
+
import { join as join34 } from "node:path";
|
|
78419
78760
|
var ACTIVE_REACTIONS_FILENAME2 = ".active-reactions.json";
|
|
78420
78761
|
function reactionsPath2(agentDir) {
|
|
78421
|
-
return
|
|
78762
|
+
return join34(agentDir, ACTIVE_REACTIONS_FILENAME2);
|
|
78422
78763
|
}
|
|
78423
78764
|
function readActiveReactions2(agentDir) {
|
|
78424
78765
|
const p = reactionsPath2(agentDir);
|
|
78425
|
-
if (!
|
|
78766
|
+
if (!existsSync30(p))
|
|
78426
78767
|
return [];
|
|
78427
78768
|
let raw;
|
|
78428
78769
|
try {
|
|
78429
|
-
raw =
|
|
78770
|
+
raw = readFileSync28(p, "utf-8");
|
|
78430
78771
|
} catch {
|
|
78431
78772
|
return [];
|
|
78432
78773
|
}
|
|
@@ -78450,7 +78791,7 @@ function readActiveReactions2(agentDir) {
|
|
|
78450
78791
|
}
|
|
78451
78792
|
function clearActiveReactions2(agentDir) {
|
|
78452
78793
|
try {
|
|
78453
|
-
|
|
78794
|
+
unlinkSync16(reactionsPath2(agentDir));
|
|
78454
78795
|
} catch {}
|
|
78455
78796
|
}
|
|
78456
78797
|
|
|
@@ -78871,7 +79212,7 @@ async function loadFromAuthBroker(options = {}) {
|
|
|
78871
79212
|
}
|
|
78872
79213
|
|
|
78873
79214
|
// gateway/folder-picker-handler.ts
|
|
78874
|
-
var
|
|
79215
|
+
var import_grammy12 = __toESM(require_mod2(), 1);
|
|
78875
79216
|
|
|
78876
79217
|
// ../src/drive/folder-picker.ts
|
|
78877
79218
|
var DRIVE_ID_RE2 = /^[A-Za-z0-9_-]+$/;
|
|
@@ -79190,7 +79531,7 @@ async function recordGrantAndConfirm(ctx, deps, parsed) {
|
|
|
79190
79531
|
const confirmText = `\u2705 Granted ${deps.agentName} access to folder ${parsed.folder_id}
|
|
79191
79532
|
` + `Scope: \`${scope}\`
|
|
79192
79533
|
` + `Revoke with: \`/approvals revoke ${decisionId}\``;
|
|
79193
|
-
const kb = new
|
|
79534
|
+
const kb = new import_grammy12.InlineKeyboard().url("\uD83D\uDCD6 Open in Drive", `https://drive.google.com/drive/folders/${parsed.folder_id}`);
|
|
79194
79535
|
try {
|
|
79195
79536
|
await ctx.editMessageText({ markdown: confirmText }, {
|
|
79196
79537
|
reply_markup: kb
|
|
@@ -79199,7 +79540,7 @@ async function recordGrantAndConfirm(ctx, deps, parsed) {
|
|
|
79199
79540
|
await ctx.answerCallbackQuery({ text: "Allowed" });
|
|
79200
79541
|
}
|
|
79201
79542
|
function toKeyboard(spec) {
|
|
79202
|
-
const kb = new
|
|
79543
|
+
const kb = new import_grammy12.InlineKeyboard;
|
|
79203
79544
|
for (let r = 0;r < spec.rows.length; r++) {
|
|
79204
79545
|
const row = spec.rows[r];
|
|
79205
79546
|
for (const btn of row) {
|
|
@@ -79294,17 +79635,17 @@ async function approvalRecord(args, opts) {
|
|
|
79294
79635
|
}
|
|
79295
79636
|
|
|
79296
79637
|
// quota-check.ts
|
|
79297
|
-
import { readFileSync as
|
|
79298
|
-
import { join as
|
|
79638
|
+
import { readFileSync as readFileSync29, existsSync as existsSync31 } from "fs";
|
|
79639
|
+
import { join as join35 } from "path";
|
|
79299
79640
|
var OAUTH_BETA2 = "oauth-2025-04-20";
|
|
79300
79641
|
var DEFAULT_USER_AGENT2 = "claude-cli/1.0.0 (external, cli)";
|
|
79301
79642
|
var DEFAULT_PROBE_MODEL2 = "claude-haiku-4-5-20251001";
|
|
79302
79643
|
function readOauthToken2(claudeConfigDir) {
|
|
79303
|
-
const tokenFile =
|
|
79304
|
-
if (!
|
|
79644
|
+
const tokenFile = join35(claudeConfigDir, ".oauth-token");
|
|
79645
|
+
if (!existsSync31(tokenFile))
|
|
79305
79646
|
return null;
|
|
79306
79647
|
try {
|
|
79307
|
-
const raw =
|
|
79648
|
+
const raw = readFileSync29(tokenFile, "utf-8").trim();
|
|
79308
79649
|
return raw.length > 0 ? raw : null;
|
|
79309
79650
|
} catch {
|
|
79310
79651
|
return null;
|
|
@@ -80640,15 +80981,15 @@ async function menuWithBannerStatic(deps, banner) {
|
|
|
80640
80981
|
}
|
|
80641
80982
|
|
|
80642
80983
|
// gateway/session-model-file.ts
|
|
80643
|
-
import { readFileSync as
|
|
80644
|
-
import { join as
|
|
80984
|
+
import { readFileSync as readFileSync30, writeFileSync as writeFileSync27, renameSync as renameSync11, rmSync as rmSync4 } from "node:fs";
|
|
80985
|
+
import { join as join36 } from "node:path";
|
|
80645
80986
|
var SESSION_MODEL_FILE = ".session-model";
|
|
80646
80987
|
var CONFIGURED_DEFAULT_MODEL_FILE = ".configured-default-model";
|
|
80647
80988
|
var SESSION_MODEL_BOOT_ATTEMPTS_FILE = ".session-model-boot-attempts";
|
|
80648
80989
|
function atomicWrite(path2, content3) {
|
|
80649
80990
|
const tmp = `${path2}.tmp-${process.pid}-${Date.now()}`;
|
|
80650
|
-
|
|
80651
|
-
|
|
80991
|
+
writeFileSync27(tmp, content3, "utf8");
|
|
80992
|
+
renameSync11(tmp, path2);
|
|
80652
80993
|
}
|
|
80653
80994
|
function serializeSessionModel(rec) {
|
|
80654
80995
|
return `${JSON.stringify({
|
|
@@ -80663,28 +81004,28 @@ function writeSessionModelFile(agentDir, model, configuredDefaultAtWrite) {
|
|
|
80663
81004
|
throw new Error(`refusing to persist non-canonical session model token: ${JSON.stringify(model)}`);
|
|
80664
81005
|
}
|
|
80665
81006
|
try {
|
|
80666
|
-
rmSync4(
|
|
81007
|
+
rmSync4(join36(agentDir, SESSION_MODEL_BOOT_ATTEMPTS_FILE), { force: true });
|
|
80667
81008
|
} catch {}
|
|
80668
|
-
atomicWrite(
|
|
81009
|
+
atomicWrite(join36(agentDir, SESSION_MODEL_FILE), serializeSessionModel({ model, configuredDefaultAtWrite, ts: Date.now() }));
|
|
80669
81010
|
}
|
|
80670
81011
|
function readSessionModelFileRaw(agentDir) {
|
|
80671
81012
|
try {
|
|
80672
|
-
return
|
|
81013
|
+
return readFileSync30(join36(agentDir, SESSION_MODEL_FILE), "utf8");
|
|
80673
81014
|
} catch {
|
|
80674
81015
|
return null;
|
|
80675
81016
|
}
|
|
80676
81017
|
}
|
|
80677
81018
|
function clearSessionModelFile(agentDir) {
|
|
80678
81019
|
try {
|
|
80679
|
-
rmSync4(
|
|
81020
|
+
rmSync4(join36(agentDir, SESSION_MODEL_FILE), { force: true });
|
|
80680
81021
|
} catch {}
|
|
80681
81022
|
}
|
|
80682
81023
|
function consumeSessionModelCarrierOnHealthyBoot(agentDir) {
|
|
80683
81024
|
try {
|
|
80684
|
-
rmSync4(
|
|
81025
|
+
rmSync4(join36(agentDir, SESSION_MODEL_FILE), { force: true });
|
|
80685
81026
|
} catch {}
|
|
80686
81027
|
try {
|
|
80687
|
-
rmSync4(
|
|
81028
|
+
rmSync4(join36(agentDir, SESSION_MODEL_BOOT_ATTEMPTS_FILE), { force: true });
|
|
80688
81029
|
} catch {}
|
|
80689
81030
|
}
|
|
80690
81031
|
function restoreSessionModelFileRaw(agentDir, raw) {
|
|
@@ -80693,12 +81034,12 @@ function restoreSessionModelFileRaw(agentDir, raw) {
|
|
|
80693
81034
|
return;
|
|
80694
81035
|
}
|
|
80695
81036
|
try {
|
|
80696
|
-
atomicWrite(
|
|
81037
|
+
atomicWrite(join36(agentDir, SESSION_MODEL_FILE), raw);
|
|
80697
81038
|
} catch {}
|
|
80698
81039
|
}
|
|
80699
81040
|
function readConfiguredDefaultModel(agentDir) {
|
|
80700
81041
|
try {
|
|
80701
|
-
const v =
|
|
81042
|
+
const v = readFileSync30(join36(agentDir, CONFIGURED_DEFAULT_MODEL_FILE), "utf8").trim();
|
|
80702
81043
|
return v.length > 0 ? v : null;
|
|
80703
81044
|
} catch {
|
|
80704
81045
|
return null;
|
|
@@ -80710,12 +81051,12 @@ function writeSessionEffortFile(agentDir, level, configuredDefaultAtWrite) {
|
|
|
80710
81051
|
if (!EFFORT_LEVEL_RE.test(level)) {
|
|
80711
81052
|
throw new Error(`refusing to persist non-allowlisted effort level: ${JSON.stringify(level)}`);
|
|
80712
81053
|
}
|
|
80713
|
-
atomicWrite(
|
|
81054
|
+
atomicWrite(join36(agentDir, SESSION_EFFORT_FILE), `${JSON.stringify({ level, configuredDefaultAtWrite: configuredDefaultAtWrite ?? "", ts: Date.now() })}
|
|
80714
81055
|
`);
|
|
80715
81056
|
}
|
|
80716
81057
|
function clearSessionEffortFile(agentDir) {
|
|
80717
81058
|
try {
|
|
80718
|
-
rmSync4(
|
|
81059
|
+
rmSync4(join36(agentDir, SESSION_EFFORT_FILE), { force: true });
|
|
80719
81060
|
} catch {}
|
|
80720
81061
|
}
|
|
80721
81062
|
var PREMIUM_RECOVERY_FILE = ".premium-recovery";
|
|
@@ -80738,13 +81079,13 @@ function writePremiumRecoveryFile(agentDir, premiumModel, chats) {
|
|
|
80738
81079
|
if (clean.length === 0) {
|
|
80739
81080
|
throw new Error("refusing to persist premium-recovery marker with no chats to notify");
|
|
80740
81081
|
}
|
|
80741
|
-
atomicWrite(
|
|
81082
|
+
atomicWrite(join36(agentDir, PREMIUM_RECOVERY_FILE), `${JSON.stringify({ premiumModel, chats: clean, ts: Date.now() })}
|
|
80742
81083
|
`);
|
|
80743
81084
|
}
|
|
80744
81085
|
function readPremiumRecoveryFile(agentDir) {
|
|
80745
81086
|
let raw;
|
|
80746
81087
|
try {
|
|
80747
|
-
raw =
|
|
81088
|
+
raw = readFileSync30(join36(agentDir, PREMIUM_RECOVERY_FILE), "utf8");
|
|
80748
81089
|
} catch {
|
|
80749
81090
|
return null;
|
|
80750
81091
|
}
|
|
@@ -80757,7 +81098,7 @@ function readPremiumRecoveryFile(agentDir) {
|
|
|
80757
81098
|
}
|
|
80758
81099
|
function clearPremiumRecoveryFile(agentDir) {
|
|
80759
81100
|
try {
|
|
80760
|
-
rmSync4(
|
|
81101
|
+
rmSync4(join36(agentDir, PREMIUM_RECOVERY_FILE), { force: true });
|
|
80761
81102
|
} catch {}
|
|
80762
81103
|
}
|
|
80763
81104
|
|
|
@@ -81046,7 +81387,7 @@ async function discoverModels(agentName3, opts = {}) {
|
|
|
81046
81387
|
}
|
|
81047
81388
|
|
|
81048
81389
|
// ../src/agents/scaffold.ts
|
|
81049
|
-
import { dirname as dirname14, isAbsolute, join as
|
|
81390
|
+
import { dirname as dirname14, isAbsolute, join as join39, relative, resolve as resolve8 } from "node:path";
|
|
81050
81391
|
init_atomic();
|
|
81051
81392
|
|
|
81052
81393
|
// ../src/agents/agent-uid.ts
|
|
@@ -81063,8 +81404,8 @@ init_merge();
|
|
|
81063
81404
|
init_timezone();
|
|
81064
81405
|
|
|
81065
81406
|
// ../src/cli/agent-config.ts
|
|
81066
|
-
import { join as
|
|
81067
|
-
import { homedir as
|
|
81407
|
+
import { join as join37 } from "node:path";
|
|
81408
|
+
import { homedir as homedir12 } from "node:os";
|
|
81068
81409
|
|
|
81069
81410
|
// ../src/cli/helpers.ts
|
|
81070
81411
|
init_loader();
|
|
@@ -81084,12 +81425,12 @@ var WEBKITE_VAULT_KEYS = new Set([
|
|
|
81084
81425
|
init_overlay_loader();
|
|
81085
81426
|
|
|
81086
81427
|
// ../src/cli/agent-config.ts
|
|
81087
|
-
var AUDIT_ROOT =
|
|
81428
|
+
var AUDIT_ROOT = join37(homedir12(), ".switchroom", "audit");
|
|
81088
81429
|
|
|
81089
81430
|
// ../src/agents/profiles.ts
|
|
81090
81431
|
var import_handlebars = __toESM(require_lib(), 1);
|
|
81091
|
-
import { readFileSync as
|
|
81092
|
-
import { resolve as resolve7, join as
|
|
81432
|
+
import { readFileSync as readFileSync31, writeFileSync as writeFileSync28, existsSync as existsSync32, readdirSync as readdirSync7, statSync as statSync12, copyFileSync, mkdirSync as mkdirSync30, realpathSync as realpathSync2 } from "node:fs";
|
|
81433
|
+
import { resolve as resolve7, join as join38, sep as pathSep } from "node:path";
|
|
81093
81434
|
function resolveProfilesRoot() {
|
|
81094
81435
|
const envOverride = process.env.SWITCHROOM_PROFILES_ROOT?.trim();
|
|
81095
81436
|
if (envOverride) {
|
|
@@ -81100,7 +81441,7 @@ function resolveProfilesRoot() {
|
|
|
81100
81441
|
resolve7(import.meta.dirname, "profiles")
|
|
81101
81442
|
];
|
|
81102
81443
|
for (const candidate of candidates) {
|
|
81103
|
-
if (
|
|
81444
|
+
if (existsSync32(candidate)) {
|
|
81104
81445
|
return candidate;
|
|
81105
81446
|
}
|
|
81106
81447
|
}
|
|
@@ -81116,9 +81457,9 @@ import_handlebars.default.registerHelper("isNumber", (value) => {
|
|
|
81116
81457
|
var SHARED_FRAGMENTS_DIR = resolve7(PROFILES_ROOT, "_shared");
|
|
81117
81458
|
var SHARED_FRAGMENTS = ["vault-protocol", "agent-self-service", "execution-discipline", "reply-discipline", "dev-protocol"];
|
|
81118
81459
|
for (const name of SHARED_FRAGMENTS) {
|
|
81119
|
-
const fragPath =
|
|
81120
|
-
if (
|
|
81121
|
-
import_handlebars.default.registerPartial(name,
|
|
81460
|
+
const fragPath = join38(SHARED_FRAGMENTS_DIR, `${name}.md.hbs`);
|
|
81461
|
+
if (existsSync32(fragPath)) {
|
|
81462
|
+
import_handlebars.default.registerPartial(name, readFileSync31(fragPath, "utf-8"));
|
|
81122
81463
|
}
|
|
81123
81464
|
}
|
|
81124
81465
|
|
|
@@ -81725,8 +82066,8 @@ init_overlay_loader();
|
|
|
81725
82066
|
init_merge();
|
|
81726
82067
|
init_timezone();
|
|
81727
82068
|
var import_yaml4 = __toESM(require_dist(), 1);
|
|
81728
|
-
import { readFileSync as
|
|
81729
|
-
import { homedir as
|
|
82069
|
+
import { readFileSync as readFileSync32, existsSync as existsSync33 } from "node:fs";
|
|
82070
|
+
import { homedir as homedir13 } from "node:os";
|
|
81730
82071
|
import { resolve as resolve9 } from "node:path";
|
|
81731
82072
|
|
|
81732
82073
|
class ConfigError2 extends Error {
|
|
@@ -81783,7 +82124,7 @@ function coerceLegacyGoogleWorkspaceKeys2(parsed, filePath) {
|
|
|
81783
82124
|
}
|
|
81784
82125
|
function findConfigFile2(startDir) {
|
|
81785
82126
|
const envPath = process.env.SWITCHROOM_CONFIG;
|
|
81786
|
-
const home2 =
|
|
82127
|
+
const home2 = homedir13();
|
|
81787
82128
|
const userDir = resolve9(home2, ".switchroom");
|
|
81788
82129
|
const searchPaths = [
|
|
81789
82130
|
envPath ? resolve9(envPath) : null,
|
|
@@ -81801,7 +82142,7 @@ function findConfigFile2(startDir) {
|
|
|
81801
82142
|
resolve9(userDir, "clerk.yml")
|
|
81802
82143
|
].filter(Boolean);
|
|
81803
82144
|
for (const path2 of searchPaths) {
|
|
81804
|
-
if (
|
|
82145
|
+
if (existsSync33(path2)) {
|
|
81805
82146
|
return path2;
|
|
81806
82147
|
}
|
|
81807
82148
|
}
|
|
@@ -81809,12 +82150,12 @@ function findConfigFile2(startDir) {
|
|
|
81809
82150
|
}
|
|
81810
82151
|
function loadConfig2(configPath) {
|
|
81811
82152
|
const filePath = configPath ?? findConfigFile2();
|
|
81812
|
-
if (!
|
|
82153
|
+
if (!existsSync33(filePath)) {
|
|
81813
82154
|
throw new ConfigError2(`Config file not found: ${filePath}`);
|
|
81814
82155
|
}
|
|
81815
82156
|
let raw;
|
|
81816
82157
|
try {
|
|
81817
|
-
raw =
|
|
82158
|
+
raw = readFileSync32(filePath, "utf-8");
|
|
81818
82159
|
} catch (err) {
|
|
81819
82160
|
throw new ConfigError2(`Failed to read config file: ${filePath}`, [
|
|
81820
82161
|
` ${err.message}`
|
|
@@ -82344,15 +82685,15 @@ function topicForRecipient(args) {
|
|
|
82344
82685
|
}
|
|
82345
82686
|
|
|
82346
82687
|
// ../src/agents/perf.ts
|
|
82347
|
-
import { existsSync as
|
|
82688
|
+
import { existsSync as existsSync34, readFileSync as readFileSync33 } from "node:fs";
|
|
82348
82689
|
function readTurnUsages(jsonlPath, lastN) {
|
|
82349
|
-
if (!
|
|
82690
|
+
if (!existsSync34(jsonlPath))
|
|
82350
82691
|
return [];
|
|
82351
82692
|
if (lastN <= 0)
|
|
82352
82693
|
return [];
|
|
82353
82694
|
let raw;
|
|
82354
82695
|
try {
|
|
82355
|
-
raw =
|
|
82696
|
+
raw = readFileSync33(jsonlPath, "utf-8");
|
|
82356
82697
|
} catch {
|
|
82357
82698
|
return [];
|
|
82358
82699
|
}
|
|
@@ -82443,8 +82784,8 @@ function numField(obj, key) {
|
|
|
82443
82784
|
}
|
|
82444
82785
|
|
|
82445
82786
|
// gateway/context-occupancy.ts
|
|
82446
|
-
import { mkdirSync as
|
|
82447
|
-
import { join as
|
|
82787
|
+
import { mkdirSync as mkdirSync31, writeFileSync as writeFileSync29 } from "node:fs";
|
|
82788
|
+
import { join as join40 } from "node:path";
|
|
82448
82789
|
var CONTEXT_OCCUPANCY_FILENAME = "context-occupancy.json";
|
|
82449
82790
|
var TIGHT_FRACTION = 0.8;
|
|
82450
82791
|
function buildContextOccupancy(occupancy, cap, now) {
|
|
@@ -82467,9 +82808,9 @@ function buildContextOccupancy(occupancy, cap, now) {
|
|
|
82467
82808
|
}
|
|
82468
82809
|
function writeContextOccupancySnapshot(stateDir, snapshot, deps) {
|
|
82469
82810
|
try {
|
|
82470
|
-
const path2 =
|
|
82471
|
-
(deps?.mkdir ?? ((p, o) =>
|
|
82472
|
-
(deps?.writeFile ?? ((p, d) =>
|
|
82811
|
+
const path2 = join40(stateDir, CONTEXT_OCCUPANCY_FILENAME);
|
|
82812
|
+
(deps?.mkdir ?? ((p, o) => mkdirSync31(p, o)))(stateDir, { recursive: true });
|
|
82813
|
+
(deps?.writeFile ?? ((p, d) => writeFileSync29(p, d)))(path2, JSON.stringify(snapshot, null, 2) + `
|
|
82473
82814
|
`);
|
|
82474
82815
|
} catch {}
|
|
82475
82816
|
}
|
|
@@ -82623,7 +82964,7 @@ function nextCompactNotify(state6, ev) {
|
|
|
82623
82964
|
}
|
|
82624
82965
|
|
|
82625
82966
|
// gateway/hostd-dispatch.ts
|
|
82626
|
-
import { existsSync as
|
|
82967
|
+
import { existsSync as existsSync35 } from "node:fs";
|
|
82627
82968
|
import { randomBytes as randomBytes7 } from "node:crypto";
|
|
82628
82969
|
init_loader();
|
|
82629
82970
|
var _hostdEnabled2;
|
|
@@ -82647,13 +82988,13 @@ function hostdSocketPath2(agentName3) {
|
|
|
82647
82988
|
function hostdWillBeUsed2(agentName3) {
|
|
82648
82989
|
if (!isHostdEnabled2())
|
|
82649
82990
|
return false;
|
|
82650
|
-
return
|
|
82991
|
+
return existsSync35(hostdSocketPath2(agentName3));
|
|
82651
82992
|
}
|
|
82652
82993
|
async function tryHostdDispatch2(agentName3, req, timeoutMs = 5000) {
|
|
82653
82994
|
if (!isHostdEnabled2())
|
|
82654
82995
|
return "not-configured";
|
|
82655
82996
|
const sockPath = hostdSocketPath2(agentName3);
|
|
82656
|
-
if (!
|
|
82997
|
+
if (!existsSync35(sockPath))
|
|
82657
82998
|
return "not-configured";
|
|
82658
82999
|
try {
|
|
82659
83000
|
return await hostdRequest({ socketPath: sockPath, timeoutMs }, req);
|
|
@@ -82682,7 +83023,7 @@ async function pollHostdStatus(agentName3, targetRequestId, opts) {
|
|
|
82682
83023
|
if (!isHostdEnabled2())
|
|
82683
83024
|
return "not-configured";
|
|
82684
83025
|
const sockPath = hostdSocketPath2(agentName3);
|
|
82685
|
-
if (!
|
|
83026
|
+
if (!existsSync35(sockPath))
|
|
82686
83027
|
return "not-configured";
|
|
82687
83028
|
const now = opts.now ?? Date.now;
|
|
82688
83029
|
const sleep2 = opts.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
|
|
@@ -82801,7 +83142,7 @@ function createDmPinSweeper(deps) {
|
|
|
82801
83142
|
|
|
82802
83143
|
// gateway/webhook-ingest-server.ts
|
|
82803
83144
|
import net4 from "node:net";
|
|
82804
|
-
import { chmodSync as chmodSync10, existsSync as
|
|
83145
|
+
import { chmodSync as chmodSync10, existsSync as existsSync36, unlinkSync as unlinkSync17 } from "node:fs";
|
|
82805
83146
|
var MAX_REQUEST_BYTES = 1024 * 1024;
|
|
82806
83147
|
function fdOf(conn) {
|
|
82807
83148
|
const handle = conn._handle;
|
|
@@ -82813,8 +83154,8 @@ function startWebhookIngestServer(opts) {
|
|
|
82813
83154
|
const log = opts.log ?? ((s) => process.stderr.write(s));
|
|
82814
83155
|
const allowed = new Set(opts.allowedUids);
|
|
82815
83156
|
try {
|
|
82816
|
-
if (
|
|
82817
|
-
|
|
83157
|
+
if (existsSync36(opts.socketPath))
|
|
83158
|
+
unlinkSync17(opts.socketPath);
|
|
82818
83159
|
} catch (err) {
|
|
82819
83160
|
log(`webhook-ingest-server: could not unlink stale socket: ${err.message}
|
|
82820
83161
|
`);
|
|
@@ -82906,28 +83247,28 @@ function startWebhookIngestServer(opts) {
|
|
|
82906
83247
|
server.close();
|
|
82907
83248
|
} catch {}
|
|
82908
83249
|
try {
|
|
82909
|
-
if (
|
|
82910
|
-
|
|
83250
|
+
if (existsSync36(opts.socketPath))
|
|
83251
|
+
unlinkSync17(opts.socketPath);
|
|
82911
83252
|
} catch {}
|
|
82912
83253
|
}
|
|
82913
83254
|
};
|
|
82914
83255
|
}
|
|
82915
83256
|
|
|
82916
83257
|
// ../src/web/webhook-gateway-record.ts
|
|
82917
|
-
import { appendFileSync as
|
|
82918
|
-
import { join as
|
|
82919
|
-
import { homedir as
|
|
83258
|
+
import { appendFileSync as appendFileSync7, mkdirSync as mkdirSync34 } from "fs";
|
|
83259
|
+
import { join as join43 } from "path";
|
|
83260
|
+
import { homedir as homedir15 } from "os";
|
|
82920
83261
|
|
|
82921
83262
|
// ../src/web/webhook-handler.ts
|
|
82922
|
-
import { appendFileSync as
|
|
82923
|
-
import { join as
|
|
83263
|
+
import { appendFileSync as appendFileSync6, existsSync as existsSync37, mkdirSync as mkdirSync32, readFileSync as readFileSync34, writeFileSync as writeFileSync30 } from "fs";
|
|
83264
|
+
import { join as join41 } from "path";
|
|
82924
83265
|
var DEDUP_MAX = 1000;
|
|
82925
83266
|
var DEDUP_TTL_MS = 24 * 60 * 60 * 1000;
|
|
82926
83267
|
function loadDedupFile(path2) {
|
|
82927
83268
|
try {
|
|
82928
|
-
if (!
|
|
83269
|
+
if (!existsSync37(path2))
|
|
82929
83270
|
return {};
|
|
82930
|
-
const raw = JSON.parse(
|
|
83271
|
+
const raw = JSON.parse(readFileSync34(path2, "utf-8"));
|
|
82931
83272
|
return typeof raw.deliveries === "object" && raw.deliveries !== null ? raw.deliveries : {};
|
|
82932
83273
|
} catch {
|
|
82933
83274
|
return {};
|
|
@@ -82941,7 +83282,7 @@ function saveDedupFile(path2, deliveries, now) {
|
|
|
82941
83282
|
}
|
|
82942
83283
|
const sorted = Object.entries(pruned).sort((a, b) => b[1] - a[1]).slice(0, DEDUP_MAX);
|
|
82943
83284
|
const final = Object.fromEntries(sorted);
|
|
82944
|
-
|
|
83285
|
+
writeFileSync30(path2, JSON.stringify({ deliveries: final }), {
|
|
82945
83286
|
mode: 384
|
|
82946
83287
|
});
|
|
82947
83288
|
}
|
|
@@ -82949,8 +83290,8 @@ var agentDedupCache = new Map;
|
|
|
82949
83290
|
function createFileDedupStore(resolveAgentDir) {
|
|
82950
83291
|
return {
|
|
82951
83292
|
check(agent, deliveryId, now) {
|
|
82952
|
-
const telegramDir =
|
|
82953
|
-
const filePath =
|
|
83293
|
+
const telegramDir = join41(resolveAgentDir(agent), "telegram");
|
|
83294
|
+
const filePath = join41(telegramDir, "webhook-dedup.json");
|
|
82954
83295
|
if (!agentDedupCache.has(agent)) {
|
|
82955
83296
|
agentDedupCache.set(agent, loadDedupFile(filePath));
|
|
82956
83297
|
}
|
|
@@ -82960,7 +83301,7 @@ function createFileDedupStore(resolveAgentDir) {
|
|
|
82960
83301
|
}
|
|
82961
83302
|
deliveries[deliveryId] = now;
|
|
82962
83303
|
try {
|
|
82963
|
-
|
|
83304
|
+
mkdirSync32(telegramDir, { recursive: true });
|
|
82964
83305
|
saveDedupFile(filePath, deliveries, now);
|
|
82965
83306
|
} catch {}
|
|
82966
83307
|
return;
|
|
@@ -82971,9 +83312,9 @@ var tokenBuckets = new Map;
|
|
|
82971
83312
|
var throttleIssueWindow = new Map;
|
|
82972
83313
|
|
|
82973
83314
|
// ../src/web/webhook-dispatch.ts
|
|
82974
|
-
import { existsSync as
|
|
82975
|
-
import { join as
|
|
82976
|
-
import { homedir as
|
|
83315
|
+
import { existsSync as existsSync38, mkdirSync as mkdirSync33, readFileSync as readFileSync35, writeFileSync as writeFileSync31 } from "fs";
|
|
83316
|
+
import { join as join42 } from "path";
|
|
83317
|
+
import { homedir as homedir14 } from "os";
|
|
82977
83318
|
|
|
82978
83319
|
// ../src/agent-scheduler/ipc-client.ts
|
|
82979
83320
|
import { createConnection as createConnection2 } from "node:net";
|
|
@@ -83271,9 +83612,9 @@ function cooldownKey(source, eventType, repo, number, ruleIndex) {
|
|
|
83271
83612
|
}
|
|
83272
83613
|
function loadCooldownFile(path2) {
|
|
83273
83614
|
try {
|
|
83274
|
-
if (!
|
|
83615
|
+
if (!existsSync38(path2))
|
|
83275
83616
|
return {};
|
|
83276
|
-
const raw = JSON.parse(
|
|
83617
|
+
const raw = JSON.parse(readFileSync35(path2, "utf-8"));
|
|
83277
83618
|
return typeof raw.dispatches === "object" && raw.dispatches !== null ? raw.dispatches : {};
|
|
83278
83619
|
} catch {
|
|
83279
83620
|
return {};
|
|
@@ -83281,7 +83622,7 @@ function loadCooldownFile(path2) {
|
|
|
83281
83622
|
}
|
|
83282
83623
|
function saveCooldownFile(path2, dispatches) {
|
|
83283
83624
|
try {
|
|
83284
|
-
|
|
83625
|
+
writeFileSync31(path2, JSON.stringify({ dispatches }), {
|
|
83285
83626
|
mode: 384
|
|
83286
83627
|
});
|
|
83287
83628
|
} catch {}
|
|
@@ -83292,8 +83633,8 @@ function createFileCooldownStore(resolveAgentDir) {
|
|
|
83292
83633
|
isCoolingDown(agent, key, cooldownMs, now) {
|
|
83293
83634
|
if (cooldownMs <= 0)
|
|
83294
83635
|
return false;
|
|
83295
|
-
const telegramDir =
|
|
83296
|
-
const filePath =
|
|
83636
|
+
const telegramDir = join42(resolveAgentDir(agent), "telegram");
|
|
83637
|
+
const filePath = join42(telegramDir, "webhook-cooldown.json");
|
|
83297
83638
|
if (!cache.has(agent)) {
|
|
83298
83639
|
cache.set(agent, loadCooldownFile(filePath));
|
|
83299
83640
|
}
|
|
@@ -83304,7 +83645,7 @@ function createFileCooldownStore(resolveAgentDir) {
|
|
|
83304
83645
|
}
|
|
83305
83646
|
dispatches[key] = now;
|
|
83306
83647
|
try {
|
|
83307
|
-
|
|
83648
|
+
mkdirSync33(telegramDir, { recursive: true });
|
|
83308
83649
|
saveCooldownFile(filePath, dispatches);
|
|
83309
83650
|
} catch {}
|
|
83310
83651
|
return false;
|
|
@@ -83350,9 +83691,9 @@ async function defaultInject(socketPath, agentName3, inbound) {
|
|
|
83350
83691
|
}
|
|
83351
83692
|
function injectWebhookInbound(agent, prompt, ctx, deps = {}) {
|
|
83352
83693
|
const log = deps.log ?? ((s) => process.stderr.write(s));
|
|
83353
|
-
const resolveAgentDir = deps.resolveAgentDir ?? ((a) =>
|
|
83694
|
+
const resolveAgentDir = deps.resolveAgentDir ?? ((a) => join42(homedir14(), ".switchroom", "agents", a));
|
|
83354
83695
|
const now = (deps.now ?? Date.now)();
|
|
83355
|
-
const socketPath =
|
|
83696
|
+
const socketPath = join42(resolveAgentDir(agent), "telegram", "gateway.sock");
|
|
83356
83697
|
const inbound = {
|
|
83357
83698
|
type: "inbound",
|
|
83358
83699
|
chatId: ctx.chatId,
|
|
@@ -83424,7 +83765,7 @@ function evaluateDispatch(args, deps = {}) {
|
|
|
83424
83765
|
const log = deps.log ?? ((s) => process.stderr.write(s));
|
|
83425
83766
|
const now = (deps.now ?? Date.now)();
|
|
83426
83767
|
const nowDate = deps.nowDate ?? (() => new Date(now));
|
|
83427
|
-
const resolveAgentDir = deps.resolveAgentDir ?? ((a) =>
|
|
83768
|
+
const resolveAgentDir = deps.resolveAgentDir ?? ((a) => join42(homedir14(), ".switchroom", "agents", a));
|
|
83428
83769
|
const cooldownStore = deps.cooldownStore ?? createFileCooldownStore(resolveAgentDir);
|
|
83429
83770
|
if (!DISPATCH_SOURCES.includes(args.source))
|
|
83430
83771
|
return 0;
|
|
@@ -83502,10 +83843,10 @@ var CONSOLIDATION_COMPLETED_EVENT = "consolidation.completed";
|
|
|
83502
83843
|
function recordWebhookEvent(rec, deps = {}) {
|
|
83503
83844
|
const log = deps.log ?? ((s) => process.stderr.write(s));
|
|
83504
83845
|
const now = rec.ts || (deps.now ?? Date.now)();
|
|
83505
|
-
const resolveAgentDir = deps.resolveAgentDir ?? ((a) =>
|
|
83846
|
+
const resolveAgentDir = deps.resolveAgentDir ?? ((a) => join43(homedir15(), ".switchroom", "agents", a));
|
|
83506
83847
|
const dedupStore = deps.dedupStore ?? createFileDedupStore(resolveAgentDir);
|
|
83507
83848
|
const agent = rec.agent;
|
|
83508
|
-
const telegramDir =
|
|
83849
|
+
const telegramDir = join43(resolveAgentDir(agent), "telegram");
|
|
83509
83850
|
if (rec.source === "github" && rec.delivery_id) {
|
|
83510
83851
|
const originalTs = dedupStore.check(agent, rec.delivery_id, now);
|
|
83511
83852
|
if (originalTs !== undefined) {
|
|
@@ -83514,9 +83855,9 @@ function recordWebhookEvent(rec, deps = {}) {
|
|
|
83514
83855
|
return { status: "deduped", ts: originalTs };
|
|
83515
83856
|
}
|
|
83516
83857
|
}
|
|
83517
|
-
const logPath =
|
|
83858
|
+
const logPath = join43(telegramDir, "webhook-events.jsonl");
|
|
83518
83859
|
try {
|
|
83519
|
-
|
|
83860
|
+
mkdirSync34(telegramDir, { recursive: true });
|
|
83520
83861
|
const record = {
|
|
83521
83862
|
ts: now,
|
|
83522
83863
|
source: rec.source,
|
|
@@ -83524,7 +83865,7 @@ function recordWebhookEvent(rec, deps = {}) {
|
|
|
83524
83865
|
rendered_text: rec.rendered_text,
|
|
83525
83866
|
payload: rec.payload
|
|
83526
83867
|
};
|
|
83527
|
-
|
|
83868
|
+
appendFileSync7(logPath, JSON.stringify(record) + `
|
|
83528
83869
|
`, { mode: 384 });
|
|
83529
83870
|
} catch (err) {
|
|
83530
83871
|
log(`webhook-gateway: agent='${agent}' source='${rec.source}' write failed: ${err.message}
|
|
@@ -83626,7 +83967,7 @@ function recordWebhookEvent(rec, deps = {}) {
|
|
|
83626
83967
|
|
|
83627
83968
|
// gateway/ipc-server.ts
|
|
83628
83969
|
init_format();
|
|
83629
|
-
import { renameSync as
|
|
83970
|
+
import { renameSync as renameSync13, unlinkSync as unlinkSync18, chmodSync as chmodSync11 } from "fs";
|
|
83630
83971
|
var MAX_BUFFER_SIZE = 1024 * 1024;
|
|
83631
83972
|
var VALID_OPERATOR_KINDS = new Set([
|
|
83632
83973
|
"credentials-expired",
|
|
@@ -83835,10 +84176,10 @@ function createIpcServer(options) {
|
|
|
83835
84176
|
heartbeatTimeoutMs = 30000
|
|
83836
84177
|
} = options;
|
|
83837
84178
|
try {
|
|
83838
|
-
|
|
84179
|
+
renameSync13(socketPath, socketPath + ".bak");
|
|
83839
84180
|
} catch {}
|
|
83840
84181
|
try {
|
|
83841
|
-
|
|
84182
|
+
unlinkSync18(socketPath + ".bak");
|
|
83842
84183
|
} catch {}
|
|
83843
84184
|
const clients = new Set;
|
|
83844
84185
|
const agentIndex = new Map;
|
|
@@ -84225,7 +84566,7 @@ function createIpcServer(options) {
|
|
|
84225
84566
|
clientBySocketId.clear();
|
|
84226
84567
|
server.stop(true);
|
|
84227
84568
|
try {
|
|
84228
|
-
|
|
84569
|
+
renameSync13(socketPath, socketPath + ".bak");
|
|
84229
84570
|
} catch {}
|
|
84230
84571
|
}
|
|
84231
84572
|
};
|
|
@@ -84679,7 +85020,7 @@ async function handleRequestMs365Approval(client3, msg, deps) {
|
|
|
84679
85020
|
|
|
84680
85021
|
// gateway/diff-preview-card.ts
|
|
84681
85022
|
init_format();
|
|
84682
|
-
var
|
|
85023
|
+
var import_grammy13 = __toESM(require_mod2(), 1);
|
|
84683
85024
|
var REQUEST_ID_RE = /^[0-9a-f]{32}$/;
|
|
84684
85025
|
var PENDING_FILE_ID_SENTINEL = "pending-create";
|
|
84685
85026
|
function buildDiffPreviewCard(input) {
|
|
@@ -84697,7 +85038,7 @@ function buildDiffPreviewCard(input) {
|
|
|
84697
85038
|
}
|
|
84698
85039
|
const text4 = bodyLines.join(`
|
|
84699
85040
|
`);
|
|
84700
|
-
const kb = new
|
|
85041
|
+
const kb = new import_grammy13.InlineKeyboard;
|
|
84701
85042
|
const ROW_BREAK_AFTER = [
|
|
84702
85043
|
"apply_suggestion"
|
|
84703
85044
|
];
|
|
@@ -88160,27 +88501,27 @@ function skillProposalKeyboard(id) {
|
|
|
88160
88501
|
// ../src/self-improve/skill-proposals.ts
|
|
88161
88502
|
import {
|
|
88162
88503
|
closeSync as closeSync7,
|
|
88163
|
-
existsSync as
|
|
88164
|
-
mkdirSync as
|
|
88504
|
+
existsSync as existsSync39,
|
|
88505
|
+
mkdirSync as mkdirSync35,
|
|
88165
88506
|
openSync as openSync7,
|
|
88166
|
-
readFileSync as
|
|
88507
|
+
readFileSync as readFileSync36,
|
|
88167
88508
|
writeSync as writeSync5
|
|
88168
88509
|
} from "node:fs";
|
|
88169
|
-
import { join as
|
|
88510
|
+
import { join as join44 } from "node:path";
|
|
88170
88511
|
import { randomUUID as randomUUID5 } from "node:crypto";
|
|
88171
88512
|
var PROPOSALS_FILE2 = "skill-proposals.jsonl";
|
|
88172
88513
|
var REJECTED_FILE2 = "skill-proposals-rejected.jsonl";
|
|
88173
88514
|
var REJECTION_TTL_MS2 = 90 * 24 * 60 * 60 * 1000;
|
|
88174
88515
|
var PROPOSAL_SIM_THRESHOLD = 0.5;
|
|
88175
88516
|
function proposalsPath2(stateDir) {
|
|
88176
|
-
return
|
|
88517
|
+
return join44(stateDir, PROPOSALS_FILE2);
|
|
88177
88518
|
}
|
|
88178
88519
|
function rejectedPath2(stateDir) {
|
|
88179
|
-
return
|
|
88520
|
+
return join44(stateDir, REJECTED_FILE2);
|
|
88180
88521
|
}
|
|
88181
88522
|
function ensureDir3(stateDir) {
|
|
88182
|
-
if (!
|
|
88183
|
-
|
|
88523
|
+
if (!existsSync39(stateDir)) {
|
|
88524
|
+
mkdirSync35(stateDir, { recursive: true, mode: 493 });
|
|
88184
88525
|
}
|
|
88185
88526
|
}
|
|
88186
88527
|
function appendLine2(path2, obj) {
|
|
@@ -88193,11 +88534,11 @@ function appendLine2(path2, obj) {
|
|
|
88193
88534
|
}
|
|
88194
88535
|
}
|
|
88195
88536
|
function readLines2(path2, isValid2) {
|
|
88196
|
-
if (!
|
|
88537
|
+
if (!existsSync39(path2))
|
|
88197
88538
|
return [];
|
|
88198
88539
|
let raw;
|
|
88199
88540
|
try {
|
|
88200
|
-
raw =
|
|
88541
|
+
raw = readFileSync36(path2, "utf-8");
|
|
88201
88542
|
} catch {
|
|
88202
88543
|
return [];
|
|
88203
88544
|
}
|
|
@@ -88794,15 +89135,15 @@ function escapeBody2(s) {
|
|
|
88794
89135
|
}
|
|
88795
89136
|
|
|
88796
89137
|
// gateway/pid-file.ts
|
|
88797
|
-
import { writeFileSync as
|
|
89138
|
+
import { writeFileSync as writeFileSync32, readFileSync as readFileSync37, unlinkSync as unlinkSync19, renameSync as renameSync14 } from "node:fs";
|
|
88798
89139
|
function writePidFile(path2, record) {
|
|
88799
89140
|
const tmp = `${path2}.tmp-${process.pid}-${Date.now()}`;
|
|
88800
|
-
|
|
88801
|
-
|
|
89141
|
+
writeFileSync32(tmp, JSON.stringify(record), "utf-8");
|
|
89142
|
+
renameSync14(tmp, path2);
|
|
88802
89143
|
}
|
|
88803
89144
|
function clearPidFile(path2) {
|
|
88804
89145
|
try {
|
|
88805
|
-
|
|
89146
|
+
unlinkSync19(path2);
|
|
88806
89147
|
} catch {}
|
|
88807
89148
|
}
|
|
88808
89149
|
|
|
@@ -88813,10 +89154,10 @@ import {
|
|
|
88813
89154
|
writeFile as writeFileAsync,
|
|
88814
89155
|
readFile as readFileAsync
|
|
88815
89156
|
} from "node:fs/promises";
|
|
88816
|
-
import { readFileSync as
|
|
89157
|
+
import { readFileSync as readFileSync38 } from "node:fs";
|
|
88817
89158
|
function readCurrentBootId() {
|
|
88818
89159
|
try {
|
|
88819
|
-
const stat =
|
|
89160
|
+
const stat = readFileSync38("/proc/1/stat", "utf-8");
|
|
88820
89161
|
const lastParen = stat.lastIndexOf(")");
|
|
88821
89162
|
if (lastParen < 0)
|
|
88822
89163
|
return null;
|
|
@@ -88964,14 +89305,14 @@ var DEFAULT_LOG3 = (line) => {
|
|
|
88964
89305
|
`) ? line : line + `
|
|
88965
89306
|
`);
|
|
88966
89307
|
};
|
|
88967
|
-
var
|
|
89308
|
+
var DEFAULT_SLEEP4 = (ms) => new Promise((resolve10) => setTimeout(resolve10, ms));
|
|
88968
89309
|
var DEFAULT_NOW = () => typeof performance !== "undefined" && typeof performance.now === "function" ? performance.now() : Date.now();
|
|
88969
89310
|
function fmtAgent2(agentName3) {
|
|
88970
89311
|
return agentName3 ? ` agent=${agentName3}` : "";
|
|
88971
89312
|
}
|
|
88972
89313
|
async function drainShutdown(opts) {
|
|
88973
89314
|
const log = opts.log ?? DEFAULT_LOG3;
|
|
88974
|
-
const sleep2 = opts.sleep ??
|
|
89315
|
+
const sleep2 = opts.sleep ?? DEFAULT_SLEEP4;
|
|
88975
89316
|
const now = opts.now ?? DEFAULT_NOW;
|
|
88976
89317
|
const budgetMs = opts.budgetMs ?? 35000;
|
|
88977
89318
|
const pollIntervalMs = opts.pollIntervalMs ?? 100;
|
|
@@ -89019,15 +89360,15 @@ function safeCount(fn) {
|
|
|
89019
89360
|
}
|
|
89020
89361
|
|
|
89021
89362
|
// gateway/session-marker.ts
|
|
89022
|
-
import { writeFileSync as
|
|
89363
|
+
import { writeFileSync as writeFileSync33, readFileSync as readFileSync39, renameSync as renameSync15, unlinkSync as unlinkSync20 } from "node:fs";
|
|
89023
89364
|
function writeSessionMarker(path2, marker) {
|
|
89024
89365
|
const tmp = `${path2}.tmp-${process.pid}-${Date.now()}`;
|
|
89025
|
-
|
|
89026
|
-
|
|
89366
|
+
writeFileSync33(tmp, JSON.stringify(marker), "utf-8");
|
|
89367
|
+
renameSync15(tmp, path2);
|
|
89027
89368
|
}
|
|
89028
89369
|
function readSessionMarker(path2) {
|
|
89029
89370
|
try {
|
|
89030
|
-
const raw =
|
|
89371
|
+
const raw = readFileSync39(path2, "utf-8");
|
|
89031
89372
|
const parsed = JSON.parse(raw);
|
|
89032
89373
|
if (typeof parsed.pid === "number" && typeof parsed.startedAtMs === "number" && Number.isFinite(parsed.pid) && Number.isFinite(parsed.startedAtMs)) {
|
|
89033
89374
|
return { pid: parsed.pid, startedAtMs: parsed.startedAtMs };
|
|
@@ -89049,16 +89390,16 @@ function shouldFireRestartBanner(input) {
|
|
|
89049
89390
|
}
|
|
89050
89391
|
|
|
89051
89392
|
// gateway/clean-shutdown-marker.ts
|
|
89052
|
-
import { writeFileSync as
|
|
89393
|
+
import { writeFileSync as writeFileSync34, readFileSync as readFileSync40, renameSync as renameSync16, unlinkSync as unlinkSync21 } from "node:fs";
|
|
89053
89394
|
var DEFAULT_MAX_AGE_MS = 60000;
|
|
89054
89395
|
function writeCleanShutdownMarker(path2, marker) {
|
|
89055
89396
|
const tmp = `${path2}.tmp-${process.pid}-${Date.now()}`;
|
|
89056
|
-
|
|
89057
|
-
|
|
89397
|
+
writeFileSync34(tmp, JSON.stringify(marker), "utf-8");
|
|
89398
|
+
renameSync16(tmp, path2);
|
|
89058
89399
|
}
|
|
89059
89400
|
function readCleanShutdownMarker(path2) {
|
|
89060
89401
|
try {
|
|
89061
|
-
const raw =
|
|
89402
|
+
const raw = readFileSync40(path2, "utf-8");
|
|
89062
89403
|
const parsed = JSON.parse(raw);
|
|
89063
89404
|
if (typeof parsed.ts === "number" && Number.isFinite(parsed.ts) && typeof parsed.signal === "string" && parsed.signal.length > 0) {
|
|
89064
89405
|
const out = { ts: parsed.ts, signal: parsed.signal };
|
|
@@ -89073,7 +89414,7 @@ function readCleanShutdownMarker(path2) {
|
|
|
89073
89414
|
}
|
|
89074
89415
|
function clearCleanShutdownMarker(path2) {
|
|
89075
89416
|
try {
|
|
89076
|
-
|
|
89417
|
+
unlinkSync21(path2);
|
|
89077
89418
|
} catch {}
|
|
89078
89419
|
}
|
|
89079
89420
|
function shouldSuppressRecoveryBanner(marker, now, maxAgeMs = DEFAULT_MAX_AGE_MS) {
|
|
@@ -89430,16 +89771,16 @@ function classifyAdminGate(text4, myAgentName) {
|
|
|
89430
89771
|
|
|
89431
89772
|
// subagent-watcher.ts
|
|
89432
89773
|
import {
|
|
89433
|
-
existsSync as
|
|
89774
|
+
existsSync as existsSync40,
|
|
89434
89775
|
openSync as openSync8,
|
|
89435
89776
|
readSync as readSync2,
|
|
89436
|
-
statSync as
|
|
89777
|
+
statSync as statSync13,
|
|
89437
89778
|
closeSync as closeSync8,
|
|
89438
89779
|
watch,
|
|
89439
|
-
readdirSync as
|
|
89440
|
-
readFileSync as
|
|
89780
|
+
readdirSync as readdirSync8,
|
|
89781
|
+
readFileSync as readFileSync41
|
|
89441
89782
|
} from "fs";
|
|
89442
|
-
import { join as
|
|
89783
|
+
import { join as join45 } from "path";
|
|
89443
89784
|
|
|
89444
89785
|
// session-tail.ts
|
|
89445
89786
|
function sanitizeCwdToProjectName2(cwd) {
|
|
@@ -89753,104 +90094,6 @@ function redactSecrets(text4) {
|
|
|
89753
90094
|
|
|
89754
90095
|
// subagent-watcher.ts
|
|
89755
90096
|
init_card_format();
|
|
89756
|
-
|
|
89757
|
-
// registry/subagents-schema.ts
|
|
89758
|
-
function countRunningBackgroundSubagents(db3) {
|
|
89759
|
-
const row = db3.prepare("SELECT count(*) AS n FROM subagents WHERE background = 1 AND status = 'running'").get();
|
|
89760
|
-
return row?.n ?? 0;
|
|
89761
|
-
}
|
|
89762
|
-
function recordSubagentEnd(db3, args) {
|
|
89763
|
-
db3.prepare(`
|
|
89764
|
-
UPDATE subagents
|
|
89765
|
-
SET
|
|
89766
|
-
ended_at = ?,
|
|
89767
|
-
status = ?,
|
|
89768
|
-
result_summary = COALESCE(?, result_summary)
|
|
89769
|
-
WHERE id = ?
|
|
89770
|
-
AND status NOT IN ('completed', 'failed')
|
|
89771
|
-
`).run(args.endedAt, args.status, args.resultSummary ?? null, args.id);
|
|
89772
|
-
}
|
|
89773
|
-
function recordSubagentStall(db3, args) {
|
|
89774
|
-
args.stalledAt;
|
|
89775
|
-
if (args.reason !== undefined) {
|
|
89776
|
-
db3.prepare(`
|
|
89777
|
-
UPDATE subagents
|
|
89778
|
-
SET status = 'stalled',
|
|
89779
|
-
result_summary = ?
|
|
89780
|
-
WHERE id = ?
|
|
89781
|
-
AND status NOT IN ('completed', 'failed')
|
|
89782
|
-
`).run(args.reason, args.id);
|
|
89783
|
-
return;
|
|
89784
|
-
}
|
|
89785
|
-
db3.prepare(`
|
|
89786
|
-
UPDATE subagents
|
|
89787
|
-
SET status = 'stalled'
|
|
89788
|
-
WHERE id = ?
|
|
89789
|
-
AND status NOT IN ('completed', 'failed')
|
|
89790
|
-
`).run(args.id);
|
|
89791
|
-
}
|
|
89792
|
-
function reapStuckRunningRows(db3, args) {
|
|
89793
|
-
const cutoff = args.now - args.ttlMs;
|
|
89794
|
-
const rows = db3.prepare(`
|
|
89795
|
-
SELECT id, jsonl_agent_id FROM subagents
|
|
89796
|
-
WHERE status = 'running'
|
|
89797
|
-
AND background = 1
|
|
89798
|
-
AND COALESCE(last_activity_at, started_at) < ?
|
|
89799
|
-
`).all(cutoff);
|
|
89800
|
-
const candidates = args.isLive ? rows.filter((r) => !args.isLive(r.jsonl_agent_id)) : rows;
|
|
89801
|
-
for (const row of candidates) {
|
|
89802
|
-
recordSubagentStall(db3, {
|
|
89803
|
-
id: row.id,
|
|
89804
|
-
stalledAt: args.now,
|
|
89805
|
-
reason: `reaped: stuck in running past ${Math.round(args.ttlMs / 60000)}min ttl (jsonl linkage likely missing)`
|
|
89806
|
-
});
|
|
89807
|
-
}
|
|
89808
|
-
return { reaped: candidates.length, ids: candidates.map((r) => r.id) };
|
|
89809
|
-
}
|
|
89810
|
-
function recordSubagentResume(db3, args) {
|
|
89811
|
-
args.resumedAt;
|
|
89812
|
-
db3.prepare(`
|
|
89813
|
-
UPDATE subagents
|
|
89814
|
-
SET status = 'running'
|
|
89815
|
-
WHERE id = ?
|
|
89816
|
-
AND status = 'stalled'
|
|
89817
|
-
`).run(args.id);
|
|
89818
|
-
}
|
|
89819
|
-
function bumpSubagentActivity(db3, args) {
|
|
89820
|
-
db3.prepare(`
|
|
89821
|
-
UPDATE subagents
|
|
89822
|
-
SET last_activity_at = ?
|
|
89823
|
-
WHERE id = ?
|
|
89824
|
-
`).run(args.ts, args.id);
|
|
89825
|
-
}
|
|
89826
|
-
function recordSubagentModel(db3, args) {
|
|
89827
|
-
db3.prepare(`
|
|
89828
|
-
UPDATE subagents
|
|
89829
|
-
SET model = ?
|
|
89830
|
-
WHERE id = ?
|
|
89831
|
-
`).run(args.model, args.id);
|
|
89832
|
-
}
|
|
89833
|
-
function recordNestedSubagentDispatch(db3, args) {
|
|
89834
|
-
db3.prepare(`
|
|
89835
|
-
INSERT OR IGNORE INTO subagents
|
|
89836
|
-
(id, parent_session_id, parent_turn_key, agent_type, description,
|
|
89837
|
-
background, started_at, last_activity_at, status, jsonl_agent_id,
|
|
89838
|
-
parent_agent_id)
|
|
89839
|
-
VALUES (?, NULL, NULL, ?, ?, ?, ?, ?, 'running', NULL, ?)
|
|
89840
|
-
`).run(args.toolUseId, args.agentType ?? null, args.description ?? null, args.background ? 1 : 0, args.now, args.now, args.parentJsonlAgentId);
|
|
89841
|
-
db3.prepare(`
|
|
89842
|
-
UPDATE subagents
|
|
89843
|
-
SET parent_agent_id = COALESCE(parent_agent_id, ?),
|
|
89844
|
-
parent_turn_key = COALESCE(
|
|
89845
|
-
parent_turn_key,
|
|
89846
|
-
(SELECT p.parent_turn_key FROM subagents p
|
|
89847
|
-
WHERE p.jsonl_agent_id = ? LIMIT 1)
|
|
89848
|
-
)
|
|
89849
|
-
WHERE id = ?
|
|
89850
|
-
`).run(args.parentJsonlAgentId, args.parentJsonlAgentId, args.toolUseId);
|
|
89851
|
-
}
|
|
89852
|
-
|
|
89853
|
-
// subagent-watcher.ts
|
|
89854
90097
|
var DEFAULT_RESCAN_MS = 1000;
|
|
89855
90098
|
var DEFAULT_STALL_THRESHOLD_MS = 60000;
|
|
89856
90099
|
var DEFAULT_SILENT_SYNTHESIS_STALL_THRESHOLD_MS = 300000;
|
|
@@ -89887,7 +90130,7 @@ function backfillJsonlAgentId(db3, jsonlPath, agentId, log) {
|
|
|
89887
90130
|
const metaPath = jsonlPath.replace(/\.jsonl$/, ".meta.json");
|
|
89888
90131
|
let meta;
|
|
89889
90132
|
try {
|
|
89890
|
-
const raw =
|
|
90133
|
+
const raw = readFileSync41(metaPath, "utf8");
|
|
89891
90134
|
meta = JSON.parse(raw);
|
|
89892
90135
|
} catch {
|
|
89893
90136
|
log?.(`subagent-watcher: backfill skip ${agentId} \u2014 meta.json not readable at ${metaPath}`);
|
|
@@ -90342,9 +90585,9 @@ function startSubagentWatcher(config) {
|
|
|
90342
90585
|
clearTimeout(ref.ref);
|
|
90343
90586
|
});
|
|
90344
90587
|
const fs2 = config.fs ?? {
|
|
90345
|
-
existsSync:
|
|
90346
|
-
readdirSync:
|
|
90347
|
-
statSync:
|
|
90588
|
+
existsSync: existsSync40,
|
|
90589
|
+
readdirSync: readdirSync8,
|
|
90590
|
+
statSync: statSync13,
|
|
90348
90591
|
openSync: openSync8,
|
|
90349
90592
|
closeSync: closeSync8,
|
|
90350
90593
|
readSync: readSync2,
|
|
@@ -90860,8 +91103,8 @@ function startSubagentWatcher(config) {
|
|
|
90860
91103
|
if (stopped)
|
|
90861
91104
|
return;
|
|
90862
91105
|
pruneVanishedDirWatchers();
|
|
90863
|
-
const claudeHome =
|
|
90864
|
-
const projectsRoot =
|
|
91106
|
+
const claudeHome = join45(agentDir, ".claude");
|
|
91107
|
+
const projectsRoot = join45(claudeHome, "projects");
|
|
90865
91108
|
if (!fs2.existsSync(projectsRoot))
|
|
90866
91109
|
return;
|
|
90867
91110
|
let projectDirs;
|
|
@@ -90895,7 +91138,7 @@ function startSubagentWatcher(config) {
|
|
|
90895
91138
|
continue;
|
|
90896
91139
|
}
|
|
90897
91140
|
warnedForeignSlugs.delete(pDir);
|
|
90898
|
-
const projectPath =
|
|
91141
|
+
const projectPath = join45(projectsRoot, pDir);
|
|
90899
91142
|
let sessionDirs;
|
|
90900
91143
|
try {
|
|
90901
91144
|
sessionDirs = fs2.readdirSync(projectPath);
|
|
@@ -90905,7 +91148,7 @@ function startSubagentWatcher(config) {
|
|
|
90905
91148
|
for (const sDir of sessionDirs) {
|
|
90906
91149
|
if (sDir.endsWith(".jsonl"))
|
|
90907
91150
|
continue;
|
|
90908
|
-
const subagentsPath =
|
|
91151
|
+
const subagentsPath = join45(projectPath, sDir, "subagents");
|
|
90909
91152
|
if (!fs2.existsSync(subagentsPath))
|
|
90910
91153
|
continue;
|
|
90911
91154
|
const watchAndScan = (dirPath) => {
|
|
@@ -90914,7 +91157,7 @@ function startSubagentWatcher(config) {
|
|
|
90914
91157
|
const w = fs2.watch(dirPath, (_event, filename) => {
|
|
90915
91158
|
if (!filename || !filename.toString().startsWith("agent-") || !filename.toString().endsWith(".jsonl"))
|
|
90916
91159
|
return;
|
|
90917
|
-
const filePath =
|
|
91160
|
+
const filePath = join45(dirPath, filename.toString());
|
|
90918
91161
|
if (!knownFiles.has(filePath)) {
|
|
90919
91162
|
scanSubagentsDir(dirPath);
|
|
90920
91163
|
}
|
|
@@ -90928,7 +91171,7 @@ function startSubagentWatcher(config) {
|
|
|
90928
91171
|
scanSubagentsDir(dirPath);
|
|
90929
91172
|
};
|
|
90930
91173
|
watchAndScan(subagentsPath);
|
|
90931
|
-
const workflowsPath =
|
|
91174
|
+
const workflowsPath = join45(subagentsPath, "workflows");
|
|
90932
91175
|
if (fs2.existsSync(workflowsPath)) {
|
|
90933
91176
|
let wfDirs;
|
|
90934
91177
|
try {
|
|
@@ -90938,7 +91181,7 @@ function startSubagentWatcher(config) {
|
|
|
90938
91181
|
}
|
|
90939
91182
|
for (const wfDir of wfDirs) {
|
|
90940
91183
|
try {
|
|
90941
|
-
const wfPath =
|
|
91184
|
+
const wfPath = join45(workflowsPath, wfDir);
|
|
90942
91185
|
if (!fs2.statSync(wfPath).isDirectory())
|
|
90943
91186
|
continue;
|
|
90944
91187
|
watchAndScan(wfPath);
|
|
@@ -90958,7 +91201,7 @@ function startSubagentWatcher(config) {
|
|
|
90958
91201
|
for (const e of entries) {
|
|
90959
91202
|
if (!e.startsWith("agent-") || !e.endsWith(".jsonl"))
|
|
90960
91203
|
continue;
|
|
90961
|
-
const filePath =
|
|
91204
|
+
const filePath = join45(subagentsPath, e);
|
|
90962
91205
|
if (knownFiles.has(filePath))
|
|
90963
91206
|
continue;
|
|
90964
91207
|
const agentId = e.slice("agent-".length, -".jsonl".length);
|
|
@@ -91086,37 +91329,37 @@ function startSubagentWatcher(config) {
|
|
|
91086
91329
|
|
|
91087
91330
|
// ../src/worktree/registry.ts
|
|
91088
91331
|
import {
|
|
91089
|
-
mkdirSync as
|
|
91090
|
-
writeFileSync as
|
|
91091
|
-
readFileSync as
|
|
91092
|
-
readdirSync as
|
|
91093
|
-
unlinkSync as
|
|
91094
|
-
existsSync as
|
|
91095
|
-
renameSync as
|
|
91332
|
+
mkdirSync as mkdirSync36,
|
|
91333
|
+
writeFileSync as writeFileSync35,
|
|
91334
|
+
readFileSync as readFileSync42,
|
|
91335
|
+
readdirSync as readdirSync9,
|
|
91336
|
+
unlinkSync as unlinkSync22,
|
|
91337
|
+
existsSync as existsSync41,
|
|
91338
|
+
renameSync as renameSync17
|
|
91096
91339
|
} from "node:fs";
|
|
91097
|
-
import { join as
|
|
91098
|
-
import { homedir as
|
|
91340
|
+
import { join as join46, resolve as resolve10 } from "node:path";
|
|
91341
|
+
import { homedir as homedir16 } from "node:os";
|
|
91099
91342
|
function registryDir() {
|
|
91100
|
-
return resolve10(process.env.SWITCHROOM_WORKTREE_DIR ??
|
|
91343
|
+
return resolve10(process.env.SWITCHROOM_WORKTREE_DIR ?? join46(homedir16(), ".switchroom", "worktrees"));
|
|
91101
91344
|
}
|
|
91102
91345
|
function recordPath(id) {
|
|
91103
|
-
return
|
|
91346
|
+
return join46(registryDir(), `${id}.json`);
|
|
91104
91347
|
}
|
|
91105
91348
|
function ensureDir4() {
|
|
91106
|
-
|
|
91349
|
+
mkdirSync36(registryDir(), { recursive: true });
|
|
91107
91350
|
}
|
|
91108
91351
|
function writeRecord(record) {
|
|
91109
91352
|
ensureDir4();
|
|
91110
91353
|
const target = recordPath(record.id);
|
|
91111
91354
|
const tmp = `${target}.tmp${process.pid}`;
|
|
91112
|
-
|
|
91355
|
+
writeFileSync35(tmp, JSON.stringify(record, null, 2) + `
|
|
91113
91356
|
`, { mode: 384 });
|
|
91114
|
-
|
|
91357
|
+
renameSync17(tmp, target);
|
|
91115
91358
|
}
|
|
91116
91359
|
function readRecord(id) {
|
|
91117
91360
|
const path2 = recordPath(id);
|
|
91118
91361
|
try {
|
|
91119
|
-
const raw =
|
|
91362
|
+
const raw = readFileSync42(path2, "utf8");
|
|
91120
91363
|
return JSON.parse(raw);
|
|
91121
91364
|
} catch {
|
|
91122
91365
|
return null;
|
|
@@ -91126,7 +91369,7 @@ function listRecords() {
|
|
|
91126
91369
|
ensureDir4();
|
|
91127
91370
|
const dir = registryDir();
|
|
91128
91371
|
const records = [];
|
|
91129
|
-
for (const entry of
|
|
91372
|
+
for (const entry of readdirSync9(dir)) {
|
|
91130
91373
|
if (!entry.endsWith(".json"))
|
|
91131
91374
|
continue;
|
|
91132
91375
|
const id = entry.slice(0, -5);
|
|
@@ -91146,7 +91389,7 @@ function touchHeartbeat(id, onAfterRead) {
|
|
|
91146
91389
|
writeRecord({ ...rec, heartbeatAt: new Date().toISOString() });
|
|
91147
91390
|
}
|
|
91148
91391
|
function recordExists(id) {
|
|
91149
|
-
return
|
|
91392
|
+
return existsSync41(recordPath(id));
|
|
91150
91393
|
}
|
|
91151
91394
|
|
|
91152
91395
|
// worktree-watch-cwds.ts
|
|
@@ -91278,15 +91521,15 @@ function determineRestartReason(opts) {
|
|
|
91278
91521
|
init_boot_card();
|
|
91279
91522
|
|
|
91280
91523
|
// gateway/update-announce.ts
|
|
91281
|
-
import { existsSync as
|
|
91282
|
-
import { join as
|
|
91283
|
-
import { homedir as
|
|
91524
|
+
import { existsSync as existsSync46, mkdirSync as mkdirSync40, openSync as openSync9, closeSync as closeSync9, readFileSync as readFileSync49 } from "node:fs";
|
|
91525
|
+
import { join as join51 } from "node:path";
|
|
91526
|
+
import { homedir as homedir18 } from "node:os";
|
|
91284
91527
|
|
|
91285
91528
|
// ../src/host-control/audit-reader.ts
|
|
91286
|
-
import { homedir as
|
|
91287
|
-
import { join as
|
|
91288
|
-
function defaultAuditLogPath(home2 =
|
|
91289
|
-
return
|
|
91529
|
+
import { homedir as homedir17 } from "node:os";
|
|
91530
|
+
import { join as join50 } from "node:path";
|
|
91531
|
+
function defaultAuditLogPath(home2 = homedir17()) {
|
|
91532
|
+
return join50(home2, ".switchroom", "host-control-audit.log");
|
|
91290
91533
|
}
|
|
91291
91534
|
function parseAuditLine(line) {
|
|
91292
91535
|
const trimmed = line.trim();
|
|
@@ -91409,8 +91652,8 @@ function readAndFilter(raw, filters, limit) {
|
|
|
91409
91652
|
var DEFAULT_LOOKBACK_MS = 10 * 60 * 1000;
|
|
91410
91653
|
function readLastTerminalUpdateAudit(opts = {}) {
|
|
91411
91654
|
const path2 = opts.auditLogPath ?? defaultAuditLogPath();
|
|
91412
|
-
const exists = opts.exists ??
|
|
91413
|
-
const readFile = opts.readFile ?? ((p) =>
|
|
91655
|
+
const exists = opts.exists ?? existsSync46;
|
|
91656
|
+
const readFile = opts.readFile ?? ((p) => readFileSync49(p, "utf-8"));
|
|
91414
91657
|
if (!exists(path2))
|
|
91415
91658
|
return null;
|
|
91416
91659
|
let raw;
|
|
@@ -91471,15 +91714,15 @@ function renderUpdateOutcomeLine(entry) {
|
|
|
91471
91714
|
`);
|
|
91472
91715
|
}
|
|
91473
91716
|
function claimUpdateAnnouncement(requestId, opts = {}) {
|
|
91474
|
-
const stateDir = opts.stateDir ?? process.env.TELEGRAM_STATE_DIR ??
|
|
91475
|
-
const dir =
|
|
91717
|
+
const stateDir = opts.stateDir ?? process.env.TELEGRAM_STATE_DIR ?? join51(homedir18(), ".switchroom");
|
|
91718
|
+
const dir = join51(stateDir, "update-announced");
|
|
91476
91719
|
try {
|
|
91477
|
-
|
|
91720
|
+
mkdirSync40(dir, { recursive: true });
|
|
91478
91721
|
} catch {
|
|
91479
91722
|
return false;
|
|
91480
91723
|
}
|
|
91481
91724
|
const safeId = requestId.replace(/[^A-Za-z0-9_.-]/g, "_").slice(0, 200);
|
|
91482
|
-
const path2 =
|
|
91725
|
+
const path2 = join51(dir, safeId);
|
|
91483
91726
|
try {
|
|
91484
91727
|
const fd = openSync9(path2, "wx");
|
|
91485
91728
|
closeSync9(fd);
|
|
@@ -91499,7 +91742,7 @@ function maybeRenderUpdateAnnouncement(opts = {}) {
|
|
|
91499
91742
|
|
|
91500
91743
|
// issues-card.ts
|
|
91501
91744
|
init_card_format();
|
|
91502
|
-
import { readFileSync as
|
|
91745
|
+
import { readFileSync as readFileSync50, writeFileSync as writeFileSync40 } from "node:fs";
|
|
91503
91746
|
var SEVERITY_EMOJI = {
|
|
91504
91747
|
info: "\u2139\ufe0f",
|
|
91505
91748
|
warn: "\u26a0\ufe0f",
|
|
@@ -91591,7 +91834,7 @@ function extractRetryAfterSecs2(err) {
|
|
|
91591
91834
|
var COOLDOWN_JITTER_MS2 = 500;
|
|
91592
91835
|
function readPersistedMessageId(path2, log) {
|
|
91593
91836
|
try {
|
|
91594
|
-
const raw =
|
|
91837
|
+
const raw = readFileSync50(path2, "utf8");
|
|
91595
91838
|
const parsed = JSON.parse(raw);
|
|
91596
91839
|
const v = parsed.messageId;
|
|
91597
91840
|
if (typeof v === "number" && Number.isInteger(v) && v > 0)
|
|
@@ -91607,7 +91850,7 @@ function readPersistedMessageId(path2, log) {
|
|
|
91607
91850
|
}
|
|
91608
91851
|
function writePersistedMessageId(path2, messageId, log) {
|
|
91609
91852
|
try {
|
|
91610
|
-
|
|
91853
|
+
writeFileSync40(path2, JSON.stringify({ messageId }) + `
|
|
91611
91854
|
`, { mode: 384 });
|
|
91612
91855
|
} catch (err) {
|
|
91613
91856
|
log(`issues-card: persist write failed (${err.message})`);
|
|
@@ -91700,24 +91943,24 @@ function createIssuesCardHandle(opts) {
|
|
|
91700
91943
|
}
|
|
91701
91944
|
|
|
91702
91945
|
// issues-watcher.ts
|
|
91703
|
-
import { existsSync as
|
|
91704
|
-
import { join as
|
|
91946
|
+
import { existsSync as existsSync48, statSync as statSync16 } from "node:fs";
|
|
91947
|
+
import { join as join53 } from "node:path";
|
|
91705
91948
|
|
|
91706
91949
|
// ../src/issues/store.ts
|
|
91707
91950
|
import {
|
|
91708
91951
|
closeSync as closeSync10,
|
|
91709
|
-
existsSync as
|
|
91710
|
-
mkdirSync as
|
|
91952
|
+
existsSync as existsSync47,
|
|
91953
|
+
mkdirSync as mkdirSync41,
|
|
91711
91954
|
openSync as openSync10,
|
|
91712
|
-
readdirSync as
|
|
91713
|
-
readFileSync as
|
|
91714
|
-
renameSync as
|
|
91715
|
-
statSync as
|
|
91716
|
-
unlinkSync as
|
|
91717
|
-
writeFileSync as
|
|
91955
|
+
readdirSync as readdirSync11,
|
|
91956
|
+
readFileSync as readFileSync51,
|
|
91957
|
+
renameSync as renameSync20,
|
|
91958
|
+
statSync as statSync15,
|
|
91959
|
+
unlinkSync as unlinkSync23,
|
|
91960
|
+
writeFileSync as writeFileSync41,
|
|
91718
91961
|
writeSync as writeSync6
|
|
91719
91962
|
} from "node:fs";
|
|
91720
|
-
import { join as
|
|
91963
|
+
import { join as join52 } from "node:path";
|
|
91721
91964
|
import { randomBytes as randomBytes8 } from "node:crypto";
|
|
91722
91965
|
import { execSync } from "node:child_process";
|
|
91723
91966
|
|
|
@@ -91736,12 +91979,12 @@ init_redact();
|
|
|
91736
91979
|
var ISSUES_FILE = "issues.jsonl";
|
|
91737
91980
|
var ISSUES_LOCK = "issues.lock";
|
|
91738
91981
|
function readAll(stateDir) {
|
|
91739
|
-
const path2 =
|
|
91740
|
-
if (!
|
|
91982
|
+
const path2 = join52(stateDir, ISSUES_FILE);
|
|
91983
|
+
if (!existsSync47(path2))
|
|
91741
91984
|
return [];
|
|
91742
91985
|
let raw;
|
|
91743
91986
|
try {
|
|
91744
|
-
raw =
|
|
91987
|
+
raw = readFileSync51(path2, "utf-8");
|
|
91745
91988
|
} catch {
|
|
91746
91989
|
return [];
|
|
91747
91990
|
}
|
|
@@ -91773,7 +92016,7 @@ function list2(stateDir, opts = {}) {
|
|
|
91773
92016
|
});
|
|
91774
92017
|
}
|
|
91775
92018
|
function resolve11(stateDir, fingerprint, nowFn = Date.now) {
|
|
91776
|
-
if (!
|
|
92019
|
+
if (!existsSync47(join52(stateDir, ISSUES_FILE)))
|
|
91777
92020
|
return 0;
|
|
91778
92021
|
return withLock(stateDir, () => {
|
|
91779
92022
|
const all2 = readAll(stateDir);
|
|
@@ -91791,21 +92034,21 @@ function resolve11(stateDir, fingerprint, nowFn = Date.now) {
|
|
|
91791
92034
|
});
|
|
91792
92035
|
}
|
|
91793
92036
|
function writeAll(stateDir, events) {
|
|
91794
|
-
const path2 =
|
|
92037
|
+
const path2 = join52(stateDir, ISSUES_FILE);
|
|
91795
92038
|
sweepOrphanTmpFiles(stateDir);
|
|
91796
92039
|
const tmp = `${path2}.tmp-${process.pid}-${randomBytes8(4).toString("hex")}`;
|
|
91797
92040
|
const body = events.length === 0 ? "" : events.map((e) => JSON.stringify(e)).join(`
|
|
91798
92041
|
`) + `
|
|
91799
92042
|
`;
|
|
91800
|
-
|
|
91801
|
-
|
|
92043
|
+
writeFileSync41(tmp, body, "utf-8");
|
|
92044
|
+
renameSync20(tmp, path2);
|
|
91802
92045
|
}
|
|
91803
92046
|
var ORPHAN_TMP_TTL_MS = 60000;
|
|
91804
92047
|
var TMP_PREFIX = `${ISSUES_FILE}.tmp-`;
|
|
91805
92048
|
function sweepOrphanTmpFiles(stateDir) {
|
|
91806
92049
|
let entries;
|
|
91807
92050
|
try {
|
|
91808
|
-
entries =
|
|
92051
|
+
entries = readdirSync11(stateDir);
|
|
91809
92052
|
} catch {
|
|
91810
92053
|
return;
|
|
91811
92054
|
}
|
|
@@ -91813,11 +92056,11 @@ function sweepOrphanTmpFiles(stateDir) {
|
|
|
91813
92056
|
for (const entry of entries) {
|
|
91814
92057
|
if (!entry.startsWith(TMP_PREFIX))
|
|
91815
92058
|
continue;
|
|
91816
|
-
const tmpPath2 =
|
|
92059
|
+
const tmpPath2 = join52(stateDir, entry);
|
|
91817
92060
|
try {
|
|
91818
|
-
const stat =
|
|
92061
|
+
const stat = statSync15(tmpPath2);
|
|
91819
92062
|
if (stat.mtimeMs < cutoff) {
|
|
91820
|
-
|
|
92063
|
+
unlinkSync23(tmpPath2);
|
|
91821
92064
|
}
|
|
91822
92065
|
} catch {}
|
|
91823
92066
|
}
|
|
@@ -91825,7 +92068,7 @@ function sweepOrphanTmpFiles(stateDir) {
|
|
|
91825
92068
|
var LOCK_RETRY_MS = 25;
|
|
91826
92069
|
var LOCK_TIMEOUT_MS = 1e4;
|
|
91827
92070
|
function withLock(stateDir, fn) {
|
|
91828
|
-
const lockPath =
|
|
92071
|
+
const lockPath = join52(stateDir, ISSUES_LOCK);
|
|
91829
92072
|
const startedAt = Date.now();
|
|
91830
92073
|
let fd = null;
|
|
91831
92074
|
while (fd === null) {
|
|
@@ -91853,27 +92096,27 @@ function withLock(stateDir, fn) {
|
|
|
91853
92096
|
closeSync10(fd);
|
|
91854
92097
|
} catch {}
|
|
91855
92098
|
try {
|
|
91856
|
-
|
|
92099
|
+
unlinkSync23(lockPath);
|
|
91857
92100
|
} catch {}
|
|
91858
92101
|
}
|
|
91859
92102
|
}
|
|
91860
92103
|
function tryStealStaleLock(lockPath) {
|
|
91861
92104
|
let pidStr;
|
|
91862
92105
|
try {
|
|
91863
|
-
pidStr =
|
|
92106
|
+
pidStr = readFileSync51(lockPath, "utf-8").trim();
|
|
91864
92107
|
} catch {
|
|
91865
92108
|
return true;
|
|
91866
92109
|
}
|
|
91867
92110
|
const pid = Number(pidStr);
|
|
91868
92111
|
if (!Number.isFinite(pid) || pid <= 0) {
|
|
91869
92112
|
try {
|
|
91870
|
-
|
|
92113
|
+
unlinkSync23(lockPath);
|
|
91871
92114
|
} catch {}
|
|
91872
92115
|
return true;
|
|
91873
92116
|
}
|
|
91874
92117
|
if (pid === process.pid) {
|
|
91875
92118
|
try {
|
|
91876
|
-
|
|
92119
|
+
unlinkSync23(lockPath);
|
|
91877
92120
|
} catch {}
|
|
91878
92121
|
return true;
|
|
91879
92122
|
}
|
|
@@ -91888,7 +92131,7 @@ function tryStealStaleLock(lockPath) {
|
|
|
91888
92131
|
return false;
|
|
91889
92132
|
}
|
|
91890
92133
|
try {
|
|
91891
|
-
|
|
92134
|
+
unlinkSync23(lockPath);
|
|
91892
92135
|
} catch {}
|
|
91893
92136
|
return true;
|
|
91894
92137
|
}
|
|
@@ -91910,7 +92153,7 @@ function isIssueEvent(v) {
|
|
|
91910
92153
|
// issues-watcher.ts
|
|
91911
92154
|
var DEFAULT_POLL_INTERVAL_MS2 = 2000;
|
|
91912
92155
|
function startIssuesWatcher(opts) {
|
|
91913
|
-
const path2 =
|
|
92156
|
+
const path2 = join53(opts.stateDir, ISSUES_FILE);
|
|
91914
92157
|
const log = opts.log ?? (() => {});
|
|
91915
92158
|
const intervalMs = opts.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS2;
|
|
91916
92159
|
const setIntervalFn = opts.setInterval ?? setInterval;
|
|
@@ -91958,10 +92201,10 @@ function startIssuesWatcher(opts) {
|
|
|
91958
92201
|
};
|
|
91959
92202
|
}
|
|
91960
92203
|
function defaultSignatureProvider(path2) {
|
|
91961
|
-
if (!
|
|
92204
|
+
if (!existsSync48(path2))
|
|
91962
92205
|
return null;
|
|
91963
92206
|
try {
|
|
91964
|
-
const stat =
|
|
92207
|
+
const stat = statSync16(path2);
|
|
91965
92208
|
return `${stat.mtimeMs}:${stat.size}`;
|
|
91966
92209
|
} catch {
|
|
91967
92210
|
return null;
|
|
@@ -92669,8 +92912,8 @@ function readBashCommand(inputPreview) {
|
|
|
92669
92912
|
}
|
|
92670
92913
|
|
|
92671
92914
|
// gateway/scoped-grant-store.ts
|
|
92672
|
-
import { readFileSync as
|
|
92673
|
-
import { join as
|
|
92915
|
+
import { readFileSync as readFileSync52, writeFileSync as writeFileSync42 } from "node:fs";
|
|
92916
|
+
import { join as join54 } from "node:path";
|
|
92674
92917
|
|
|
92675
92918
|
// scoped-approval.ts
|
|
92676
92919
|
var SCOPED_APPROVAL_DEFAULT_TTL_MS2 = 30 * 60 * 1000;
|
|
@@ -92712,11 +92955,11 @@ function scopedGrantPersistEnabled(env = process.env) {
|
|
|
92712
92955
|
return env.SWITCHROOM_SCOPED_GRANT_PERSIST !== "0";
|
|
92713
92956
|
}
|
|
92714
92957
|
function createScopedGrantStore(stateDir, env = process.env) {
|
|
92715
|
-
const filePath =
|
|
92716
|
-
const
|
|
92958
|
+
const filePath = join54(stateDir, "scoped-grants.json");
|
|
92959
|
+
const enabled7 = scopedGrantPersistEnabled(env);
|
|
92717
92960
|
function read() {
|
|
92718
92961
|
try {
|
|
92719
|
-
const raw =
|
|
92962
|
+
const raw = readFileSync52(filePath, "utf-8");
|
|
92720
92963
|
const parsed = JSON.parse(raw);
|
|
92721
92964
|
return Array.isArray(parsed) ? parsed : [];
|
|
92722
92965
|
} catch {
|
|
@@ -92724,17 +92967,17 @@ function createScopedGrantStore(stateDir, env = process.env) {
|
|
|
92724
92967
|
}
|
|
92725
92968
|
}
|
|
92726
92969
|
return {
|
|
92727
|
-
enabled:
|
|
92970
|
+
enabled: enabled7,
|
|
92728
92971
|
load(now) {
|
|
92729
|
-
if (!
|
|
92972
|
+
if (!enabled7)
|
|
92730
92973
|
return new Map;
|
|
92731
92974
|
return deserializeScopedGrants(read(), now);
|
|
92732
92975
|
},
|
|
92733
92976
|
save(store2) {
|
|
92734
|
-
if (!
|
|
92977
|
+
if (!enabled7)
|
|
92735
92978
|
return;
|
|
92736
92979
|
try {
|
|
92737
|
-
|
|
92980
|
+
writeFileSync42(filePath, JSON.stringify(serializeScopedGrants(store2)), {
|
|
92738
92981
|
encoding: "utf-8",
|
|
92739
92982
|
mode: 384
|
|
92740
92983
|
});
|
|
@@ -93085,8 +93328,8 @@ function isDiffPreApproved(agentName3, unifiedDiff, deps) {
|
|
|
93085
93328
|
|
|
93086
93329
|
// credits-watch.ts
|
|
93087
93330
|
init_card_format();
|
|
93088
|
-
import { readFileSync as
|
|
93089
|
-
import { join as
|
|
93331
|
+
import { readFileSync as readFileSync53, writeFileSync as writeFileSync43, existsSync as existsSync49, mkdirSync as mkdirSync42 } from "fs";
|
|
93332
|
+
import { join as join55 } from "path";
|
|
93090
93333
|
var STATE_FILE = "credits-watch.json";
|
|
93091
93334
|
var DEFAULT_CREDIT_FATAL_REASONS = new Set;
|
|
93092
93335
|
var KNOWN_CREDIT_REASONS = [
|
|
@@ -93108,12 +93351,12 @@ function emptyCreditState() {
|
|
|
93108
93351
|
return { lastNotifiedReason: null, lastNotifiedAt: 0 };
|
|
93109
93352
|
}
|
|
93110
93353
|
function readClaudeJsonOverage(claudeConfigDir) {
|
|
93111
|
-
const path2 =
|
|
93112
|
-
if (!
|
|
93354
|
+
const path2 = join55(claudeConfigDir, ".claude.json");
|
|
93355
|
+
if (!existsSync49(path2))
|
|
93113
93356
|
return null;
|
|
93114
93357
|
let raw;
|
|
93115
93358
|
try {
|
|
93116
|
-
raw =
|
|
93359
|
+
raw = readFileSync53(path2, "utf-8");
|
|
93117
93360
|
} catch {
|
|
93118
93361
|
return null;
|
|
93119
93362
|
}
|
|
@@ -93191,11 +93434,11 @@ function humanizeReason(reason) {
|
|
|
93191
93434
|
}
|
|
93192
93435
|
}
|
|
93193
93436
|
function loadCreditState(stateDir) {
|
|
93194
|
-
const path2 =
|
|
93195
|
-
if (!
|
|
93437
|
+
const path2 = join55(stateDir, STATE_FILE);
|
|
93438
|
+
if (!existsSync49(path2))
|
|
93196
93439
|
return emptyCreditState();
|
|
93197
93440
|
try {
|
|
93198
|
-
const raw =
|
|
93441
|
+
const raw = readFileSync53(path2, "utf-8");
|
|
93199
93442
|
const parsed = JSON.parse(raw);
|
|
93200
93443
|
if (parsed && typeof parsed === "object" && (parsed.lastNotifiedReason === null || typeof parsed.lastNotifiedReason === "string") && typeof parsed.lastNotifiedAt === "number" && Number.isFinite(parsed.lastNotifiedAt)) {
|
|
93201
93444
|
return {
|
|
@@ -93207,17 +93450,17 @@ function loadCreditState(stateDir) {
|
|
|
93207
93450
|
return emptyCreditState();
|
|
93208
93451
|
}
|
|
93209
93452
|
function saveCreditState(stateDir, state7) {
|
|
93210
|
-
|
|
93211
|
-
const path2 =
|
|
93212
|
-
|
|
93453
|
+
mkdirSync42(stateDir, { recursive: true });
|
|
93454
|
+
const path2 = join55(stateDir, STATE_FILE);
|
|
93455
|
+
writeFileSync43(path2, JSON.stringify(state7, null, 2) + `
|
|
93213
93456
|
`, { mode: 384 });
|
|
93214
93457
|
}
|
|
93215
93458
|
|
|
93216
93459
|
// quota-watch.ts
|
|
93217
93460
|
init_auth_snapshot_format();
|
|
93218
93461
|
init_card_format();
|
|
93219
|
-
import { readFileSync as
|
|
93220
|
-
import { join as
|
|
93462
|
+
import { readFileSync as readFileSync54, writeFileSync as writeFileSync44, existsSync as existsSync50, mkdirSync as mkdirSync43 } from "fs";
|
|
93463
|
+
import { join as join56 } from "path";
|
|
93221
93464
|
var STATE_FILE2 = "quota-watch.json";
|
|
93222
93465
|
function emptyQuotaWatchState() {
|
|
93223
93466
|
return {};
|
|
@@ -93455,11 +93698,11 @@ function buildRecoveryMessage(agentName3, snap) {
|
|
|
93455
93698
|
`);
|
|
93456
93699
|
}
|
|
93457
93700
|
function loadQuotaWatchState(stateDir) {
|
|
93458
|
-
const path2 =
|
|
93459
|
-
if (!
|
|
93701
|
+
const path2 = join56(stateDir, STATE_FILE2);
|
|
93702
|
+
if (!existsSync50(path2))
|
|
93460
93703
|
return emptyQuotaWatchState();
|
|
93461
93704
|
try {
|
|
93462
|
-
const raw =
|
|
93705
|
+
const raw = readFileSync54(path2, "utf-8");
|
|
93463
93706
|
const parsed = JSON.parse(raw);
|
|
93464
93707
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
93465
93708
|
return emptyQuotaWatchState();
|
|
@@ -93476,9 +93719,9 @@ function loadQuotaWatchState(stateDir) {
|
|
|
93476
93719
|
}
|
|
93477
93720
|
}
|
|
93478
93721
|
function saveQuotaWatchState(stateDir, state7) {
|
|
93479
|
-
|
|
93480
|
-
const path2 =
|
|
93481
|
-
|
|
93722
|
+
mkdirSync43(stateDir, { recursive: true });
|
|
93723
|
+
const path2 = join56(stateDir, STATE_FILE2);
|
|
93724
|
+
writeFileSync44(path2, JSON.stringify(state7, null, 2) + `
|
|
93482
93725
|
`, { mode: 384 });
|
|
93483
93726
|
}
|
|
93484
93727
|
function patchQuotaWatchState(current, accountLabel, accountState) {
|
|
@@ -93526,31 +93769,31 @@ init_auth_snapshot_format2();
|
|
|
93526
93769
|
// gateway/turn-active-marker.ts
|
|
93527
93770
|
import {
|
|
93528
93771
|
closeSync as closeSync11,
|
|
93529
|
-
existsSync as
|
|
93530
|
-
mkdirSync as
|
|
93772
|
+
existsSync as existsSync51,
|
|
93773
|
+
mkdirSync as mkdirSync44,
|
|
93531
93774
|
openSync as openSync11,
|
|
93532
|
-
readFileSync as
|
|
93533
|
-
statSync as
|
|
93534
|
-
unlinkSync as
|
|
93775
|
+
readFileSync as readFileSync55,
|
|
93776
|
+
statSync as statSync17,
|
|
93777
|
+
unlinkSync as unlinkSync24,
|
|
93535
93778
|
utimesSync as utimesSync2,
|
|
93536
|
-
writeFileSync as
|
|
93779
|
+
writeFileSync as writeFileSync45
|
|
93537
93780
|
} from "node:fs";
|
|
93538
|
-
import { join as
|
|
93781
|
+
import { join as join57 } from "node:path";
|
|
93539
93782
|
var TURN_ACTIVE_MARKER_FILE2 = "turn-active.json";
|
|
93540
93783
|
var TURN_ACTIVE_HARD_TTL_MS2 = 10 * 60000;
|
|
93541
93784
|
var TURN_ACTIVE_IDLE_SWEEP_MS = 60000;
|
|
93542
93785
|
function removeTurnActiveMarker2(stateDir) {
|
|
93543
93786
|
try {
|
|
93544
|
-
|
|
93787
|
+
unlinkSync24(join57(stateDir, TURN_ACTIVE_MARKER_FILE2));
|
|
93545
93788
|
} catch {}
|
|
93546
93789
|
}
|
|
93547
93790
|
function sweepStaleTurnActiveMarker(stateDir, opts) {
|
|
93548
|
-
const path2 =
|
|
93549
|
-
if (!
|
|
93791
|
+
const path2 = join57(stateDir, TURN_ACTIVE_MARKER_FILE2);
|
|
93792
|
+
if (!existsSync51(path2))
|
|
93550
93793
|
return false;
|
|
93551
93794
|
const now = opts.now ?? Date.now();
|
|
93552
93795
|
try {
|
|
93553
|
-
const st =
|
|
93796
|
+
const st = statSync17(path2);
|
|
93554
93797
|
const ageMs = now - st.mtimeMs;
|
|
93555
93798
|
const hardExpired = ageMs > opts.hardTtlMs;
|
|
93556
93799
|
const idleExpired = !opts.turnInFlight && ageMs > opts.idleSweepMs;
|
|
@@ -93558,9 +93801,9 @@ function sweepStaleTurnActiveMarker(stateDir, opts) {
|
|
|
93558
93801
|
return false;
|
|
93559
93802
|
let payload = null;
|
|
93560
93803
|
try {
|
|
93561
|
-
payload =
|
|
93804
|
+
payload = readFileSync55(path2, "utf8");
|
|
93562
93805
|
} catch {}
|
|
93563
|
-
|
|
93806
|
+
unlinkSync24(path2);
|
|
93564
93807
|
if (opts.onRemove) {
|
|
93565
93808
|
try {
|
|
93566
93809
|
opts.onRemove({
|
|
@@ -93576,9 +93819,9 @@ function sweepStaleTurnActiveMarker(stateDir, opts) {
|
|
|
93576
93819
|
}
|
|
93577
93820
|
}
|
|
93578
93821
|
function readTurnActiveMarkerAgeMs2(stateDir, now) {
|
|
93579
|
-
const path2 =
|
|
93822
|
+
const path2 = join57(stateDir, TURN_ACTIVE_MARKER_FILE2);
|
|
93580
93823
|
try {
|
|
93581
|
-
const st =
|
|
93824
|
+
const st = statSync17(path2);
|
|
93582
93825
|
return (now ?? Date.now()) - st.mtimeMs;
|
|
93583
93826
|
} catch {
|
|
93584
93827
|
return null;
|
|
@@ -93589,19 +93832,19 @@ function effectiveTurnAgeMs(markerAgeMs, turnStartedAt, now) {
|
|
|
93589
93832
|
}
|
|
93590
93833
|
|
|
93591
93834
|
// gateway/gateway-heartbeat.ts
|
|
93592
|
-
import { mkdirSync as
|
|
93593
|
-
import { join as
|
|
93835
|
+
import { mkdirSync as mkdirSync45, utimesSync as utimesSync3, writeFileSync as writeFileSync46 } from "node:fs";
|
|
93836
|
+
import { join as join58 } from "node:path";
|
|
93594
93837
|
var GATEWAY_HEARTBEAT_FILE = "gateway-heartbeat";
|
|
93595
93838
|
var GATEWAY_HEARTBEAT_INTERVAL_MS = 15000;
|
|
93596
93839
|
function touchGatewayHeartbeat(stateDir) {
|
|
93597
|
-
const path2 =
|
|
93840
|
+
const path2 = join58(stateDir, GATEWAY_HEARTBEAT_FILE);
|
|
93598
93841
|
const now = new Date;
|
|
93599
93842
|
try {
|
|
93600
93843
|
utimesSync3(path2, now, now);
|
|
93601
93844
|
} catch {
|
|
93602
93845
|
try {
|
|
93603
|
-
|
|
93604
|
-
|
|
93846
|
+
mkdirSync45(stateDir, { recursive: true });
|
|
93847
|
+
writeFileSync46(path2, `${Date.now()}
|
|
93605
93848
|
`, { mode: 384 });
|
|
93606
93849
|
} catch {}
|
|
93607
93850
|
}
|
|
@@ -93613,11 +93856,120 @@ function startGatewayHeartbeat(stateDir, intervalMs = GATEWAY_HEARTBEAT_INTERVAL
|
|
|
93613
93856
|
return timer3;
|
|
93614
93857
|
}
|
|
93615
93858
|
|
|
93859
|
+
// gateway/outbox-sweep.ts
|
|
93860
|
+
async function sweepOutbox(deps) {
|
|
93861
|
+
const now = deps.now?.() ?? Date.now();
|
|
93862
|
+
const log = deps.log ?? (() => {});
|
|
93863
|
+
const summary = { scanned: 0, delivered: 0, skipped: 0 };
|
|
93864
|
+
reclaimStaleSending(deps.stateDir, now);
|
|
93865
|
+
const pending = listPendingRecords(deps.stateDir);
|
|
93866
|
+
if (pending.length === 0)
|
|
93867
|
+
return summary;
|
|
93868
|
+
const deliveredNonces = readDeliveredNonces(deps.stateDir);
|
|
93869
|
+
for (const fileName of pending) {
|
|
93870
|
+
const record2 = readOutboxRecord(fileName, deps.stateDir);
|
|
93871
|
+
if (record2 == null)
|
|
93872
|
+
continue;
|
|
93873
|
+
summary.scanned++;
|
|
93874
|
+
const resolved = resolveOutboxChat(record2, {
|
|
93875
|
+
registryChainLookup: (anchorContent) => {
|
|
93876
|
+
const taskId = extractTaskId(anchorContent);
|
|
93877
|
+
if (taskId == null || deps.registryChainLookup == null)
|
|
93878
|
+
return null;
|
|
93879
|
+
return deps.registryChainLookup(taskId);
|
|
93880
|
+
}
|
|
93881
|
+
});
|
|
93882
|
+
const routePrefix = resolved?.via === "origin" ? "(from background task) " : "";
|
|
93883
|
+
const decision = decideOutboxSweep({
|
|
93884
|
+
record: record2,
|
|
93885
|
+
now,
|
|
93886
|
+
deliveredNonces,
|
|
93887
|
+
textAlreadyDelivered: resolved != null && deps.textAlreadyDelivered(resolved.chatId, resolved.threadId, record2.text),
|
|
93888
|
+
routable: resolved != null,
|
|
93889
|
+
routePrefix,
|
|
93890
|
+
quietMs: deps.quietMs ?? OUTBOX_QUIET_MS
|
|
93891
|
+
});
|
|
93892
|
+
if (decision.action !== "send" && decision.action !== "send-delayed") {
|
|
93893
|
+
summary.skipped++;
|
|
93894
|
+
if (decision.action === "skip-journaled") {
|
|
93895
|
+
clearOutboxRecord(record2.turnNonce, deps.stateDir);
|
|
93896
|
+
} else if (decision.action === "skip-dedup") {
|
|
93897
|
+
appendDelivered({ turnNonce: record2.turnNonce, textSha256: record2.textSha256, ts: now }, deps.stateDir);
|
|
93898
|
+
clearOutboxRecord(record2.turnNonce, deps.stateDir);
|
|
93899
|
+
}
|
|
93900
|
+
continue;
|
|
93901
|
+
}
|
|
93902
|
+
const claimed = claimRecord(record2.turnNonce, deps.stateDir);
|
|
93903
|
+
if (claimed == null) {
|
|
93904
|
+
summary.skipped++;
|
|
93905
|
+
continue;
|
|
93906
|
+
}
|
|
93907
|
+
const resolvedChat = resolved;
|
|
93908
|
+
try {
|
|
93909
|
+
const messageId = await deps.send(resolvedChat.chatId, resolvedChat.threadId, decision.text ?? record2.text);
|
|
93910
|
+
appendDelivered({ turnNonce: record2.turnNonce, textSha256: record2.textSha256, tgMessageId: messageId, ts: now }, deps.stateDir);
|
|
93911
|
+
removeClaimed(record2.turnNonce, deps.stateDir);
|
|
93912
|
+
summary.delivered++;
|
|
93913
|
+
log(`outbox-sweep: delivered nonce=${record2.turnNonce} via=${resolvedChat.via} ` + `source=${record2.source} chars=${record2.text.length}${decision.action === "send-delayed" ? " (delayed)" : ""}
|
|
93914
|
+
`);
|
|
93915
|
+
} catch (err) {
|
|
93916
|
+
releaseClaim(record2.turnNonce, deps.stateDir);
|
|
93917
|
+
summary.skipped++;
|
|
93918
|
+
log(`outbox-sweep: send failed nonce=${record2.turnNonce}: ${err.message} \u2014 will retry
|
|
93919
|
+
`);
|
|
93920
|
+
}
|
|
93921
|
+
}
|
|
93922
|
+
return summary;
|
|
93923
|
+
}
|
|
93924
|
+
function chatFromTurnKey(turnKey3) {
|
|
93925
|
+
const idx = turnKey3.indexOf(":");
|
|
93926
|
+
const chatId = idx === -1 ? turnKey3 : turnKey3.slice(0, idx);
|
|
93927
|
+
const tRaw = idx === -1 ? "_" : turnKey3.slice(idx + 1);
|
|
93928
|
+
const t = tRaw === "_" || tRaw === "" ? null : Number(tRaw);
|
|
93929
|
+
return { chatId, threadId: Number.isFinite(t) ? t : null };
|
|
93930
|
+
}
|
|
93931
|
+
var OUTBOX_SWEEP_INTERVAL_MS = 5000;
|
|
93932
|
+
function startOutboxSweep(deps) {
|
|
93933
|
+
if (!deps.isGatewayMain || process.env.SWITCHROOM_TG_OUTBOX_DELIVERY === "0")
|
|
93934
|
+
return;
|
|
93935
|
+
const retry = createRetryApiCall({ log: deps.log });
|
|
93936
|
+
const tick3 = () => {
|
|
93937
|
+
const bot = deps.getBot();
|
|
93938
|
+
if (bot == null)
|
|
93939
|
+
return;
|
|
93940
|
+
sweepOutbox({
|
|
93941
|
+
stateDir: deps.stateDir,
|
|
93942
|
+
log: deps.log,
|
|
93943
|
+
send: async (chatId, threadId, text4) => {
|
|
93944
|
+
let lastId;
|
|
93945
|
+
for (let i = 0;i < text4.length; i += 4000) {
|
|
93946
|
+
const chunk2 = text4.slice(i, i + 4000);
|
|
93947
|
+
const res = await retryWithThreadFallback(retry, (tid) => bot.api.sendMessage(chatId, chunk2, tid != null ? { message_thread_id: tid } : {}), { threadId: threadId ?? undefined, chat_id: chatId, verb: "outbox-sweep.sendMessage" });
|
|
93948
|
+
lastId = res?.message_id;
|
|
93949
|
+
}
|
|
93950
|
+
return lastId;
|
|
93951
|
+
},
|
|
93952
|
+
textAlreadyDelivered: (chatId, threadId, text4) => deps.dedupCheck(chatId, threadId ?? undefined, text4),
|
|
93953
|
+
registryChainLookup: (taskId) => {
|
|
93954
|
+
const db3 = deps.getTurnsDb();
|
|
93955
|
+
if (db3 == null)
|
|
93956
|
+
return null;
|
|
93957
|
+
const turnKey3 = resolveSubagentOriginTurnKey(db3, taskId);
|
|
93958
|
+
return turnKey3 == null ? null : chatFromTurnKey(turnKey3);
|
|
93959
|
+
}
|
|
93960
|
+
}).catch((err) => deps.log?.(`outbox-sweep: tick failed: ${err.message}
|
|
93961
|
+
`));
|
|
93962
|
+
};
|
|
93963
|
+
const timer3 = setInterval(tick3, OUTBOX_SWEEP_INTERVAL_MS);
|
|
93964
|
+
timer3.unref?.();
|
|
93965
|
+
return timer3;
|
|
93966
|
+
}
|
|
93967
|
+
|
|
93616
93968
|
// ../src/build-info.ts
|
|
93617
|
-
var VERSION = "0.19.
|
|
93618
|
-
var COMMIT_SHA = "
|
|
93619
|
-
var COMMIT_DATE = "2026-07-
|
|
93620
|
-
var LATEST_PR =
|
|
93969
|
+
var VERSION = "0.19.13";
|
|
93970
|
+
var COMMIT_SHA = "fda10488";
|
|
93971
|
+
var COMMIT_DATE = "2026-07-23T04:34:01Z";
|
|
93972
|
+
var LATEST_PR = 3508;
|
|
93621
93973
|
var COMMITS_AHEAD_OF_TAG = 0;
|
|
93622
93974
|
|
|
93623
93975
|
// gateway/boot-version.ts
|
|
@@ -93662,10 +94014,10 @@ function composeBootVersionString(inputs) {
|
|
|
93662
94014
|
}
|
|
93663
94015
|
|
|
93664
94016
|
// gateway/unhandled-rejection-policy.ts
|
|
93665
|
-
var
|
|
94017
|
+
var import_grammy14 = __toESM(require_mod2(), 1);
|
|
93666
94018
|
function classifyRejection(err, opts = {}) {
|
|
93667
|
-
const isGrammy = opts.isGrammyError != null ? opts.isGrammyError(err) : err instanceof
|
|
93668
|
-
const isHttp = opts.isHttpError != null ? opts.isHttpError(err) : err instanceof
|
|
94019
|
+
const isGrammy = opts.isGrammyError != null ? opts.isGrammyError(err) : err instanceof import_grammy14.GrammyError;
|
|
94020
|
+
const isHttp = opts.isHttpError != null ? opts.isHttpError(err) : err instanceof import_grammy14.HttpError;
|
|
93669
94021
|
if (isHttp)
|
|
93670
94022
|
return "log_only";
|
|
93671
94023
|
if (err instanceof Error && err.message === FLOOD_WAIT_ACTIVE)
|
|
@@ -93694,12 +94046,12 @@ init_protocol();
|
|
|
93694
94046
|
init_peercred();
|
|
93695
94047
|
import * as net5 from "node:net";
|
|
93696
94048
|
import * as fs2 from "node:fs";
|
|
93697
|
-
import { homedir as
|
|
93698
|
-
import { join as
|
|
94049
|
+
import { homedir as homedir19 } from "node:os";
|
|
94050
|
+
import { join as join59 } from "node:path";
|
|
93699
94051
|
var DEFAULT_TIMEOUT_MS4 = 2000;
|
|
93700
94052
|
var UNLOCK_TIMEOUT_MS = 30000;
|
|
93701
|
-
var LEGACY_SOCKET_PATH2 =
|
|
93702
|
-
var OPERATOR_SOCKET_PATH2 =
|
|
94053
|
+
var LEGACY_SOCKET_PATH2 = join59(homedir19(), ".switchroom", "vault-broker.sock");
|
|
94054
|
+
var OPERATOR_SOCKET_PATH2 = join59(homedir19(), ".switchroom", "broker-operator", "sock");
|
|
93703
94055
|
function defaultBrokerSocketPath2() {
|
|
93704
94056
|
if (fs2.existsSync(OPERATOR_SOCKET_PATH2))
|
|
93705
94057
|
return OPERATOR_SOCKET_PATH2;
|
|
@@ -94581,8 +94933,8 @@ function resolveVaultApprovalPosture(broker) {
|
|
|
94581
94933
|
}
|
|
94582
94934
|
|
|
94583
94935
|
// registry/turns-schema.ts
|
|
94584
|
-
import { chmodSync as chmodSync12, mkdirSync as
|
|
94585
|
-
import { join as
|
|
94936
|
+
import { chmodSync as chmodSync12, mkdirSync as mkdirSync46 } from "fs";
|
|
94937
|
+
import { join as join60 } from "path";
|
|
94586
94938
|
var DatabaseClass3 = null;
|
|
94587
94939
|
function loadDatabaseClass3() {
|
|
94588
94940
|
if (DatabaseClass3 != null)
|
|
@@ -94654,9 +95006,9 @@ function applySchema(db3) {
|
|
|
94654
95006
|
}
|
|
94655
95007
|
function openTurnsDb(agentDir) {
|
|
94656
95008
|
const Database = loadDatabaseClass3();
|
|
94657
|
-
const dir =
|
|
94658
|
-
|
|
94659
|
-
const path2 =
|
|
95009
|
+
const dir = join60(agentDir, "telegram");
|
|
95010
|
+
mkdirSync46(dir, { recursive: true, mode: 448 });
|
|
95011
|
+
const path2 = join60(dir, "registry.db");
|
|
94660
95012
|
const db3 = new Database(path2, { create: true });
|
|
94661
95013
|
applySchema(db3);
|
|
94662
95014
|
try {
|
|
@@ -95000,7 +95352,7 @@ function selectResumeBuilder(endedVia, opts) {
|
|
|
95000
95352
|
}
|
|
95001
95353
|
|
|
95002
95354
|
// gateway/bridge-dead-watchdog.ts
|
|
95003
|
-
import { readFileSync as
|
|
95355
|
+
import { readFileSync as readFileSync57, writeFileSync as writeFileSync47, renameSync as renameSync21, unlinkSync as unlinkSync25 } from "node:fs";
|
|
95004
95356
|
|
|
95005
95357
|
// gateway/cron-session.ts
|
|
95006
95358
|
var CRON_IDENTITY_SUFFIX2 = "-cron";
|
|
@@ -95033,7 +95385,7 @@ function readFreshCrashLogTail(path2, opts = {}) {
|
|
|
95033
95385
|
const nowMs3 = opts.nowMs ?? Date.now();
|
|
95034
95386
|
const freshWindowMs = opts.freshWindowMs ?? CRASH_LOG_FRESH_WINDOW_MS;
|
|
95035
95387
|
const maxLines = opts.maxLines ?? CRASH_LOG_TAIL_LINES;
|
|
95036
|
-
const readFile = opts.readFile ?? ((p) =>
|
|
95388
|
+
const readFile = opts.readFile ?? ((p) => readFileSync57(p, "utf8"));
|
|
95037
95389
|
let raw;
|
|
95038
95390
|
try {
|
|
95039
95391
|
raw = readFile(path2);
|
|
@@ -95054,13 +95406,13 @@ function readFreshCrashLogTail(path2, opts = {}) {
|
|
|
95054
95406
|
}
|
|
95055
95407
|
function writeBridgeDeadEscalationMarker(path2, marker) {
|
|
95056
95408
|
const tmp = `${path2}.tmp-${process.pid}-${Date.now()}`;
|
|
95057
|
-
|
|
95058
|
-
|
|
95409
|
+
writeFileSync47(tmp, JSON.stringify(marker), "utf8");
|
|
95410
|
+
renameSync21(tmp, path2);
|
|
95059
95411
|
}
|
|
95060
95412
|
function consumeBridgeDeadEscalationMarker(path2, nowMs3 = Date.now(), maxAgeMs = ESCALATION_MARKER_MAX_AGE_MS) {
|
|
95061
95413
|
let marker = null;
|
|
95062
95414
|
try {
|
|
95063
|
-
const parsed = JSON.parse(
|
|
95415
|
+
const parsed = JSON.parse(readFileSync57(path2, "utf8"));
|
|
95064
95416
|
if (typeof parsed.ts === "number" && Number.isFinite(parsed.ts) && typeof parsed.reason === "string") {
|
|
95065
95417
|
const age = nowMs3 - parsed.ts;
|
|
95066
95418
|
if (age >= 0 && age < maxAgeMs) {
|
|
@@ -95072,7 +95424,7 @@ function consumeBridgeDeadEscalationMarker(path2, nowMs3 = Date.now(), maxAgeMs
|
|
|
95072
95424
|
}
|
|
95073
95425
|
} catch {}
|
|
95074
95426
|
try {
|
|
95075
|
-
|
|
95427
|
+
unlinkSync25(path2);
|
|
95076
95428
|
} catch {}
|
|
95077
95429
|
return marker;
|
|
95078
95430
|
}
|
|
@@ -95213,15 +95565,15 @@ function createBridgeDeadWatchdog(opts) {
|
|
|
95213
95565
|
}
|
|
95214
95566
|
|
|
95215
95567
|
// gateway/boot-probes.ts
|
|
95216
|
-
import { readFileSync as
|
|
95568
|
+
import { readFileSync as readFileSync58, readdirSync as readdirSync12, existsSync as existsSync53 } from "fs";
|
|
95217
95569
|
init_quota_cache();
|
|
95218
95570
|
init_quota_check();
|
|
95219
95571
|
import { execFile as execFileCb2 } from "child_process";
|
|
95220
95572
|
import { promisify as promisify2 } from "util";
|
|
95221
95573
|
var execFile2 = promisify2(execFileCb2);
|
|
95222
95574
|
var realProcFs2 = {
|
|
95223
|
-
readdir: (p) =>
|
|
95224
|
-
readFile: (p) =>
|
|
95575
|
+
readdir: (p) => readdirSync12(p),
|
|
95576
|
+
readFile: (p) => readFileSync58(p, "utf-8")
|
|
95225
95577
|
};
|
|
95226
95578
|
function findAgentProcessInContainer2(fs3 = realProcFs2) {
|
|
95227
95579
|
let entries;
|
|
@@ -95353,7 +95705,7 @@ function listNonTerminalSubagentsForTurn(db3, parentTurnKey) {
|
|
|
95353
95705
|
`).all(parentTurnKey);
|
|
95354
95706
|
return rows.map(mapSubagentRow);
|
|
95355
95707
|
}
|
|
95356
|
-
function
|
|
95708
|
+
function resolveSubagentOriginTurnKey2(db3, jsonlAgentId, maxHops = 5) {
|
|
95357
95709
|
const seen = new Set;
|
|
95358
95710
|
let currentJsonlId = jsonlAgentId;
|
|
95359
95711
|
for (let hop = 0;hop <= maxHops && currentJsonlId != null; hop++) {
|
|
@@ -95471,7 +95823,7 @@ if (isGatewayMain) {
|
|
|
95471
95823
|
shutdownAnalytics();
|
|
95472
95824
|
});
|
|
95473
95825
|
}
|
|
95474
|
-
var STATE_DIR = process.env.TELEGRAM_STATE_DIR ??
|
|
95826
|
+
var STATE_DIR = process.env.TELEGRAM_STATE_DIR ?? join61(homedir20(), ".claude", "channels", "telegram");
|
|
95475
95827
|
var permCardStore = createPermissionCardStore(STATE_DIR);
|
|
95476
95828
|
var BLOCKED_APPROVALS_DIR = process.env.SWITCHROOM_BLOCKED_APPROVALS_DIR ?? "/state/blocked-approvals";
|
|
95477
95829
|
var AGENT_NAME = process.env.SWITCHROOM_AGENT_NAME ?? "agent";
|
|
@@ -95510,7 +95862,7 @@ function alwaysAllowDrainDeps() {
|
|
|
95510
95862
|
return {
|
|
95511
95863
|
readConfigText: () => {
|
|
95512
95864
|
const cfgPath = process.env.SWITCHROOM_CONFIG ?? SWITCHROOM_CONFIG ?? findConfigFile2();
|
|
95513
|
-
return
|
|
95865
|
+
return readFileSync59(cfgPath, "utf8");
|
|
95514
95866
|
},
|
|
95515
95867
|
resolveAllowList: (_configText, agentName3) => {
|
|
95516
95868
|
const cfg = loadConfig2();
|
|
@@ -95571,11 +95923,11 @@ function scheduleAlwaysAllowPersistDrain() {
|
|
|
95571
95923
|
}, ALWAYS_ALLOW_DRAIN_INTERVAL_MS);
|
|
95572
95924
|
timer3.unref?.();
|
|
95573
95925
|
}
|
|
95574
|
-
var ACCESS_FILE =
|
|
95575
|
-
var APPROVED_DIR =
|
|
95576
|
-
var ENV_FILE =
|
|
95577
|
-
var INBOX_DIR =
|
|
95578
|
-
var PEOPLE_FILE =
|
|
95926
|
+
var ACCESS_FILE = join61(STATE_DIR, "access.json");
|
|
95927
|
+
var APPROVED_DIR = join61(STATE_DIR, "approved");
|
|
95928
|
+
var ENV_FILE = join61(STATE_DIR, ".env");
|
|
95929
|
+
var INBOX_DIR = join61(STATE_DIR, "inbox");
|
|
95930
|
+
var PEOPLE_FILE = join61(STATE_DIR, "people.json");
|
|
95579
95931
|
function triggerSelfRestart(targetAgent, reason, delayMs = 300) {
|
|
95580
95932
|
const isDocker = process.env.SWITCHROOM_RUNTIME === "docker";
|
|
95581
95933
|
const selfAgent = process.env.SWITCHROOM_AGENT_NAME;
|
|
@@ -95640,7 +95992,7 @@ function formatBootVersion() {
|
|
|
95640
95992
|
}
|
|
95641
95993
|
try {
|
|
95642
95994
|
chmodSync14(ENV_FILE, 384);
|
|
95643
|
-
for (const line of
|
|
95995
|
+
for (const line of readFileSync59(ENV_FILE, "utf8").split(`
|
|
95644
95996
|
`)) {
|
|
95645
95997
|
const m = line.match(/^(\w+)=(.*)$/);
|
|
95646
95998
|
if (m && process.env[m[1]] === undefined)
|
|
@@ -95661,7 +96013,7 @@ var bot;
|
|
|
95661
96013
|
var lastGetUpdatesHeartbeatMs = Date.now();
|
|
95662
96014
|
var GRAMMY_VERSION = (() => {
|
|
95663
96015
|
try {
|
|
95664
|
-
const raw =
|
|
96016
|
+
const raw = readFileSync59(new URL("../../node_modules/grammy/package.json", import.meta.url), "utf8");
|
|
95665
96017
|
return JSON.parse(raw).version ?? "unknown";
|
|
95666
96018
|
} catch {
|
|
95667
96019
|
return "unknown";
|
|
@@ -95724,7 +96076,7 @@ function assertSendable(f) {
|
|
|
95724
96076
|
} catch {
|
|
95725
96077
|
throw new Error(`refusing to send file \u2014 cannot resolve real path: ${f}`);
|
|
95726
96078
|
}
|
|
95727
|
-
const inbox =
|
|
96079
|
+
const inbox = join61(stateReal, "inbox");
|
|
95728
96080
|
if (real.startsWith(stateReal + sep4) && !real.startsWith(inbox + sep4)) {
|
|
95729
96081
|
throw new Error(`refusing to send channel state: ${f}`);
|
|
95730
96082
|
}
|
|
@@ -95743,7 +96095,7 @@ function assertSendable(f) {
|
|
|
95743
96095
|
}
|
|
95744
96096
|
function readAccessFile() {
|
|
95745
96097
|
try {
|
|
95746
|
-
const raw =
|
|
96098
|
+
const raw = readFileSync59(ACCESS_FILE, "utf8");
|
|
95747
96099
|
const parsed = JSON.parse(raw);
|
|
95748
96100
|
const allowFrom = validateStringArray("allowFrom", parsed.allowFrom ?? []);
|
|
95749
96101
|
const groups = {};
|
|
@@ -95783,7 +96135,7 @@ function readAccessFile() {
|
|
|
95783
96135
|
if (err.code === "ENOENT")
|
|
95784
96136
|
return defaultAccess();
|
|
95785
96137
|
try {
|
|
95786
|
-
|
|
96138
|
+
renameSync22(ACCESS_FILE, `${ACCESS_FILE}.corrupt-${Date.now()}`);
|
|
95787
96139
|
} catch {}
|
|
95788
96140
|
process.stderr.write(`telegram gateway: access.json is corrupt, moved aside. Starting fresh.
|
|
95789
96141
|
`);
|
|
@@ -95805,7 +96157,7 @@ function loadAccess() {
|
|
|
95805
96157
|
}
|
|
95806
96158
|
function readPeopleFile() {
|
|
95807
96159
|
try {
|
|
95808
|
-
const raw =
|
|
96160
|
+
const raw = readFileSync59(PEOPLE_FILE, "utf8");
|
|
95809
96161
|
const parsed = JSON.parse(raw);
|
|
95810
96162
|
if (!Array.isArray(parsed.entries))
|
|
95811
96163
|
return [];
|
|
@@ -95827,11 +96179,11 @@ function assertAllowedChat(chat_id) {
|
|
|
95827
96179
|
function saveAccess(a) {
|
|
95828
96180
|
if (STATIC)
|
|
95829
96181
|
return;
|
|
95830
|
-
|
|
96182
|
+
mkdirSync48(STATE_DIR, { recursive: true, mode: 448 });
|
|
95831
96183
|
const tmp = ACCESS_FILE + ".tmp";
|
|
95832
|
-
|
|
96184
|
+
writeFileSync49(tmp, JSON.stringify(a, null, 2) + `
|
|
95833
96185
|
`, { mode: 384 });
|
|
95834
|
-
|
|
96186
|
+
renameSync22(tmp, ACCESS_FILE);
|
|
95835
96187
|
}
|
|
95836
96188
|
function pruneExpired(a) {
|
|
95837
96189
|
const now = Date.now();
|
|
@@ -95849,7 +96201,7 @@ var HISTORY_ENABLED = HISTORY_ACCESS.historyEnabled !== false;
|
|
|
95849
96201
|
if (isGatewayMain && HISTORY_ENABLED) {
|
|
95850
96202
|
try {
|
|
95851
96203
|
initHistory(STATE_DIR, HISTORY_ACCESS.historyRetentionDays ?? 30);
|
|
95852
|
-
process.stderr.write(`telegram gateway: history capture enabled at ${
|
|
96204
|
+
process.stderr.write(`telegram gateway: history capture enabled at ${join61(STATE_DIR, "history.db")}
|
|
95853
96205
|
`);
|
|
95854
96206
|
} catch (err) {
|
|
95855
96207
|
process.stderr.write(`telegram gateway: history init failed (${err.message}) \u2014 capture disabled
|
|
@@ -95868,12 +96220,12 @@ if (isGatewayMain)
|
|
|
95868
96220
|
let markerTurnKey = null;
|
|
95869
96221
|
let markerAgeMs = null;
|
|
95870
96222
|
try {
|
|
95871
|
-
const markerPath =
|
|
95872
|
-
if (
|
|
95873
|
-
const st =
|
|
96223
|
+
const markerPath = join61(STATE_DIR, TURN_ACTIVE_MARKER_FILE2);
|
|
96224
|
+
if (existsSync55(markerPath)) {
|
|
96225
|
+
const st = statSync20(markerPath);
|
|
95874
96226
|
markerAgeMs = Date.now() - st.mtimeMs;
|
|
95875
96227
|
try {
|
|
95876
|
-
const payload = JSON.parse(
|
|
96228
|
+
const payload = JSON.parse(readFileSync59(markerPath, "utf8"));
|
|
95877
96229
|
if (typeof payload.turnKey === "string" && payload.turnKey.length > 0) {
|
|
95878
96230
|
markerTurnKey = payload.turnKey;
|
|
95879
96231
|
}
|
|
@@ -95893,10 +96245,10 @@ if (isGatewayMain)
|
|
|
95893
96245
|
process.stderr.write(`telegram gateway: turn-registry boot-reaper stamped ${reaped} orphaned turn(s)` + `${timeoutTurnKey ? ` (turnKey=${timeoutTurnKey} as 'timeout', markerAgeMs=${markerAgeMs})` : " as 'restart'"}
|
|
95894
96246
|
`);
|
|
95895
96247
|
} else {
|
|
95896
|
-
process.stderr.write(`telegram gateway: turn-registry initialized at ${
|
|
96248
|
+
process.stderr.write(`telegram gateway: turn-registry initialized at ${join61(agentDir, "telegram", "registry.db")}
|
|
95897
96249
|
`);
|
|
95898
96250
|
}
|
|
95899
|
-
const bridgeDeadMarker = consumeBridgeDeadEscalationMarker(
|
|
96251
|
+
const bridgeDeadMarker = consumeBridgeDeadEscalationMarker(join61(STATE_DIR, "bridge-dead-escalation.json"));
|
|
95900
96252
|
if (bridgeDeadMarker != null) {
|
|
95901
96253
|
bridgeDeadPriorStreak = bridgeDeadMarker.count ?? 1;
|
|
95902
96254
|
process.stderr.write(`telegram gateway: boot: prior restart was a bridge-dead escalation (reason=${bridgeDeadMarker.reason}` + `, consecutive=${bridgeDeadPriorStreak}` + `${bridgeDeadMarker.crashTail ? `, crashTail=${bridgeDeadMarker.crashTail}` : ""})
|
|
@@ -95909,7 +96261,7 @@ if (isGatewayMain)
|
|
|
95909
96261
|
const pending2 = findLatestTurnIfInterrupted(turnsDb);
|
|
95910
96262
|
const selfAgent = process.env.SWITCHROOM_AGENT_NAME ?? "";
|
|
95911
96263
|
if (pending2 != null && selfAgent) {
|
|
95912
|
-
const bootResumeMarkerPath = process.env.SWITCHROOM_GATEWAY_CLEAN_SHUTDOWN_MARKER ??
|
|
96264
|
+
const bootResumeMarkerPath = process.env.SWITCHROOM_GATEWAY_CLEAN_SHUTDOWN_MARKER ?? join61(STATE_DIR, "clean-shutdown.json");
|
|
95913
96265
|
const bootResumeCleanMarker = readCleanShutdownMarker(bootResumeMarkerPath);
|
|
95914
96266
|
const bootResumeForceAlways = process.env.SWITCHROOM_BOOT_RESUME_ALWAYS === "1";
|
|
95915
96267
|
const bootResumeMode = parseBootResumeMode(process.env.SWITCHROOM_BOOT_RESUME);
|
|
@@ -96022,7 +96374,7 @@ if (isGatewayMain)
|
|
|
96022
96374
|
`);
|
|
96023
96375
|
}
|
|
96024
96376
|
}
|
|
96025
|
-
const pendingEnvPath =
|
|
96377
|
+
const pendingEnvPath = join61(agentDir, ".pending-turn.env");
|
|
96026
96378
|
try {
|
|
96027
96379
|
if (pending2 != null) {
|
|
96028
96380
|
const lines = [
|
|
@@ -96036,13 +96388,13 @@ if (isGatewayMain)
|
|
|
96036
96388
|
pending2.interrupt_reason != null ? `SWITCHROOM_PENDING_INTERRUPT_REASON=${pending2.interrupt_reason}` : `SWITCHROOM_PENDING_INTERRUPT_REASON=`
|
|
96037
96389
|
];
|
|
96038
96390
|
const pendingEnvTmp = `${pendingEnvPath}.tmp-${process.pid}`;
|
|
96039
|
-
|
|
96391
|
+
writeFileSync49(pendingEnvTmp, lines.join(`
|
|
96040
96392
|
`) + `
|
|
96041
96393
|
`, { mode: 384 });
|
|
96042
|
-
|
|
96394
|
+
renameSync22(pendingEnvTmp, pendingEnvPath);
|
|
96043
96395
|
process.stderr.write(`telegram gateway: pending-turn env written to ${pendingEnvPath} turnKey=${pending2.turn_key} endedVia=${pending2.ended_via ?? "open"}
|
|
96044
96396
|
`);
|
|
96045
|
-
} else if (
|
|
96397
|
+
} else if (existsSync55(pendingEnvPath)) {
|
|
96046
96398
|
rmSync6(pendingEnvPath, { force: true });
|
|
96047
96399
|
process.stderr.write(`telegram gateway: pending-turn env cleared (clean previous shutdown)
|
|
96048
96400
|
`);
|
|
@@ -96060,7 +96412,7 @@ function resolveSubagentOriginChat(agentId) {
|
|
|
96060
96412
|
if (turnsDb == null)
|
|
96061
96413
|
return null;
|
|
96062
96414
|
try {
|
|
96063
|
-
const originKey =
|
|
96415
|
+
const originKey = resolveSubagentOriginTurnKey2(turnsDb, agentId);
|
|
96064
96416
|
if (originKey == null)
|
|
96065
96417
|
return null;
|
|
96066
96418
|
const turn = getTurnByKey(turnsDb, originKey);
|
|
@@ -96145,12 +96497,12 @@ if (isGatewayMain && !STATIC) {
|
|
|
96145
96497
|
function checkApprovals() {
|
|
96146
96498
|
let files;
|
|
96147
96499
|
try {
|
|
96148
|
-
files =
|
|
96500
|
+
files = readdirSync14(APPROVED_DIR);
|
|
96149
96501
|
} catch {
|
|
96150
96502
|
return;
|
|
96151
96503
|
}
|
|
96152
96504
|
for (const senderId of files) {
|
|
96153
|
-
const file =
|
|
96505
|
+
const file = join61(APPROVED_DIR, senderId);
|
|
96154
96506
|
bot.api.sendMessage(senderId, "Paired! Say hi to Claude.").then(() => rmSync6(file, { force: true }), (err) => {
|
|
96155
96507
|
process.stderr.write(`telegram gateway: failed to send approval confirm: ${err}
|
|
96156
96508
|
`);
|
|
@@ -96323,12 +96675,12 @@ function noteAgentOutputAt(key, ts) {
|
|
|
96323
96675
|
lastAgentOutputAt.delete(oldest);
|
|
96324
96676
|
}
|
|
96325
96677
|
}
|
|
96326
|
-
var OBLIGATION_STORE_PATH =
|
|
96678
|
+
var OBLIGATION_STORE_PATH = join61(STATE_DIR, "obligations.json");
|
|
96327
96679
|
var obligationStoreFs = {
|
|
96328
|
-
readFileSync: (p) =>
|
|
96329
|
-
writeFileSync: (p, d) =>
|
|
96330
|
-
renameSync: (a, b) =>
|
|
96331
|
-
existsSync: (p) =>
|
|
96680
|
+
readFileSync: (p) => readFileSync59(p, "utf8"),
|
|
96681
|
+
writeFileSync: (p, d) => writeFileSync49(p, d),
|
|
96682
|
+
renameSync: (a, b) => renameSync22(a, b),
|
|
96683
|
+
existsSync: (p) => existsSync55(p)
|
|
96332
96684
|
};
|
|
96333
96685
|
var obligationLedger = new ObligationLedger(OBLIGATION_REPRESENT_MAX, {
|
|
96334
96686
|
onChange: STATIC || !OBLIGATION_LEDGER_ENABLED ? undefined : (snapshot) => persistObligations(OBLIGATION_STORE_PATH, obligationStoreFs, snapshot)
|
|
@@ -97102,14 +97454,14 @@ function emitTurnRecord(turn, endedAt) {
|
|
|
97102
97454
|
maybeRotate(turnsPath, {
|
|
97103
97455
|
statSize: (p) => {
|
|
97104
97456
|
try {
|
|
97105
|
-
return
|
|
97457
|
+
return statSync20(p).size;
|
|
97106
97458
|
} catch {
|
|
97107
97459
|
return;
|
|
97108
97460
|
}
|
|
97109
97461
|
},
|
|
97110
|
-
rename: (from, to) =>
|
|
97462
|
+
rename: (from, to) => renameSync22(from, to)
|
|
97111
97463
|
});
|
|
97112
|
-
|
|
97464
|
+
appendFileSync8(turnsPath, rec);
|
|
97113
97465
|
} catch {}
|
|
97114
97466
|
}
|
|
97115
97467
|
function maybeProactiveCompact() {
|
|
@@ -97591,7 +97943,7 @@ function wrapBootCardApi(threadId) {
|
|
|
97591
97943
|
await lockedBot.api.editMessageText(cid, mid, richMessage2(text5), editOpts);
|
|
97592
97944
|
return "edited";
|
|
97593
97945
|
} catch (err) {
|
|
97594
|
-
const desc = err instanceof
|
|
97946
|
+
const desc = err instanceof import_grammy16.GrammyError ? err.description : err instanceof Error ? err.message : String(err);
|
|
97595
97947
|
if (typeof desc === "string" && desc.toLowerCase().includes("not modified"))
|
|
97596
97948
|
return "edited";
|
|
97597
97949
|
return "gone";
|
|
@@ -97735,7 +98087,7 @@ function sweepStaleAlwaysAllowCorrelations(now = Date.now()) {
|
|
|
97735
98087
|
var MENTAL_MODEL_CORRELATION_TTL_MS = 720000;
|
|
97736
98088
|
var pendingMentalModelCorrelations = createSweepableStore((entry, now) => now - entry.createdAt > MENTAL_MODEL_CORRELATION_TTL_MS);
|
|
97737
98089
|
function mentalModelCorrelationKey(agentName3, unifiedDiff) {
|
|
97738
|
-
return `${agentName3}::${
|
|
98090
|
+
return `${agentName3}::${createHash5("sha256").update(unifiedDiff).digest("hex")}`;
|
|
97739
98091
|
}
|
|
97740
98092
|
function sweepStaleMentalModelCorrelations(now = Date.now()) {
|
|
97741
98093
|
pendingMentalModelCorrelations.sweep(now);
|
|
@@ -98671,28 +99023,28 @@ var statusPinState = new Map;
|
|
|
98671
99023
|
var statusPinChatIds = new Map;
|
|
98672
99024
|
var statusPinPinnedAt = new Map;
|
|
98673
99025
|
var statusPinRightsCache = new PinRightsCache2;
|
|
98674
|
-
var STATUS_PIN_STORE_PATH =
|
|
99026
|
+
var STATUS_PIN_STORE_PATH = join61(STATE_DIR, "status-pins.json");
|
|
98675
99027
|
var statusPinStoreFs = {
|
|
98676
|
-
readFileSync: (p) =>
|
|
98677
|
-
writeFileSync: (p, d) =>
|
|
98678
|
-
renameSync: (a, b) =>
|
|
98679
|
-
existsSync: (p) =>
|
|
99028
|
+
readFileSync: (p) => readFileSync59(p, "utf8"),
|
|
99029
|
+
writeFileSync: (p, d) => writeFileSync49(p, d),
|
|
99030
|
+
renameSync: (a, b) => renameSync22(a, b),
|
|
99031
|
+
existsSync: (p) => existsSync55(p)
|
|
98680
99032
|
};
|
|
98681
99033
|
var statusPinPersistEnabled = !STATIC && PIN_STATUS_WHILE_WORKING;
|
|
98682
|
-
var ACTIVITY_CARD_STORE_PATH =
|
|
99034
|
+
var ACTIVITY_CARD_STORE_PATH = join61(STATE_DIR, "activity-cards-pending.json");
|
|
98683
99035
|
var activityCardStoreFs = {
|
|
98684
|
-
readFileSync: (p) =>
|
|
98685
|
-
writeFileSync: (p, d) =>
|
|
98686
|
-
renameSync: (a, b) =>
|
|
98687
|
-
existsSync: (p) =>
|
|
99036
|
+
readFileSync: (p) => readFileSync59(p, "utf8"),
|
|
99037
|
+
writeFileSync: (p, d) => writeFileSync49(p, d),
|
|
99038
|
+
renameSync: (a, b) => renameSync22(a, b),
|
|
99039
|
+
existsSync: (p) => existsSync55(p)
|
|
98688
99040
|
};
|
|
98689
99041
|
var activityCardPersistEnabled = !STATIC;
|
|
98690
|
-
var QUEUED_CARD_STORE_PATH =
|
|
99042
|
+
var QUEUED_CARD_STORE_PATH = join61(STATE_DIR, "queued-cards-pending.json");
|
|
98691
99043
|
var queuedCardStoreFs = {
|
|
98692
|
-
readFileSync: (p) =>
|
|
98693
|
-
writeFileSync: (p, d) =>
|
|
98694
|
-
renameSync: (a, b) =>
|
|
98695
|
-
existsSync: (p) =>
|
|
99044
|
+
readFileSync: (p) => readFileSync59(p, "utf8"),
|
|
99045
|
+
writeFileSync: (p, d) => writeFileSync49(p, d),
|
|
99046
|
+
renameSync: (a, b) => renameSync22(a, b),
|
|
99047
|
+
existsSync: (p) => existsSync55(p)
|
|
98696
99048
|
};
|
|
98697
99049
|
var queuedCardPersistEnabled = !STATIC;
|
|
98698
99050
|
function persistQueuedCard(key, chatId, threadId, messageId) {
|
|
@@ -99065,12 +99417,12 @@ var getPinnedProgressCardMessageId = null;
|
|
|
99065
99417
|
var completeProgressCardTurn = null;
|
|
99066
99418
|
var subagentWatcher = null;
|
|
99067
99419
|
var workerActivityFeed = null;
|
|
99068
|
-
var SOCKET_PATH = process.env.SWITCHROOM_GATEWAY_SOCKET ??
|
|
99420
|
+
var SOCKET_PATH = process.env.SWITCHROOM_GATEWAY_SOCKET ?? join61(STATE_DIR, "gateway.sock");
|
|
99069
99421
|
if (isGatewayMain)
|
|
99070
|
-
|
|
99071
|
-
var GATEWAY_PID_PATH = process.env.SWITCHROOM_GATEWAY_PID_FILE ??
|
|
99072
|
-
var GATEWAY_SESSION_MARKER_PATH = process.env.SWITCHROOM_GATEWAY_SESSION_MARKER ??
|
|
99073
|
-
var GATEWAY_CLEAN_SHUTDOWN_MARKER_PATH = process.env.SWITCHROOM_GATEWAY_CLEAN_SHUTDOWN_MARKER ??
|
|
99422
|
+
mkdirSync48(STATE_DIR, { recursive: true, mode: 448 });
|
|
99423
|
+
var GATEWAY_PID_PATH = process.env.SWITCHROOM_GATEWAY_PID_FILE ?? join61(STATE_DIR, "gateway.pid.json");
|
|
99424
|
+
var GATEWAY_SESSION_MARKER_PATH = process.env.SWITCHROOM_GATEWAY_SESSION_MARKER ?? join61(STATE_DIR, "gateway-session.json");
|
|
99425
|
+
var GATEWAY_CLEAN_SHUTDOWN_MARKER_PATH = process.env.SWITCHROOM_GATEWAY_CLEAN_SHUTDOWN_MARKER ?? join61(STATE_DIR, "clean-shutdown.json");
|
|
99074
99426
|
var GATEWAY_STARTED_AT_MS = Date.now();
|
|
99075
99427
|
var BOOT_CARD_ENABLED = process.env.SWITCHROOM_BOOT_CARD !== "false";
|
|
99076
99428
|
var activeBootCard = null;
|
|
@@ -99099,7 +99451,7 @@ function ensureIssuesCard(chatId, threadId) {
|
|
|
99099
99451
|
bot: botApi,
|
|
99100
99452
|
log: (msg) => process.stderr.write(`telegram gateway: ${msg}
|
|
99101
99453
|
`),
|
|
99102
|
-
persistPath:
|
|
99454
|
+
persistPath: join61(stateDir, "issues-card.json")
|
|
99103
99455
|
});
|
|
99104
99456
|
activeIssuesWatcher = startIssuesWatcher({
|
|
99105
99457
|
stateDir,
|
|
@@ -99275,6 +99627,7 @@ function runDeliveryConfirmSweep() {
|
|
|
99275
99627
|
}
|
|
99276
99628
|
var _deliveryConfirmSweep = isGatewayMain ? setInterval(runDeliveryConfirmSweep, DELIVERY_CONFIRM_SWEEP_MS) : undefined;
|
|
99277
99629
|
_deliveryConfirmSweep?.unref?.();
|
|
99630
|
+
startOutboxSweep({ isGatewayMain, stateDir: STATE_DIR, getBot: () => bot, getTurnsDb: () => turnsDb, dedupCheck: (c, t, x) => outboundDedup.check(c, t, x, Date.now()) != null, log: (l) => process.stderr.write(l) });
|
|
99278
99631
|
if (isGatewayMain)
|
|
99279
99632
|
startTimer2({
|
|
99280
99633
|
editMessage: async (ctx) => {
|
|
@@ -99296,14 +99649,14 @@ if (isGatewayMain)
|
|
|
99296
99649
|
var inboundSpool;
|
|
99297
99650
|
if (isGatewayMain)
|
|
99298
99651
|
inboundSpool = STATIC ? undefined : createInboundSpool({
|
|
99299
|
-
path:
|
|
99652
|
+
path: join61(STATE_DIR, "inbound-spool.jsonl"),
|
|
99300
99653
|
fs: {
|
|
99301
|
-
appendFileSync: (p, d) =>
|
|
99302
|
-
readFileSync: (p) =>
|
|
99303
|
-
writeFileSync: (p, d) =>
|
|
99304
|
-
renameSync: (a, b) =>
|
|
99305
|
-
existsSync: (p) =>
|
|
99306
|
-
statSizeSync: (p) =>
|
|
99654
|
+
appendFileSync: (p, d) => appendFileSync8(p, d),
|
|
99655
|
+
readFileSync: (p) => readFileSync59(p, "utf8"),
|
|
99656
|
+
writeFileSync: (p, d) => writeFileSync49(p, d),
|
|
99657
|
+
renameSync: (a, b) => renameSync22(a, b),
|
|
99658
|
+
existsSync: (p) => existsSync55(p),
|
|
99659
|
+
statSizeSync: (p) => statSync20(p).size
|
|
99307
99660
|
},
|
|
99308
99661
|
onDegraded: (info) => {
|
|
99309
99662
|
process.stderr.write(`telegram gateway: inbound-spool durability ${info.degraded ? "DEGRADED to in-memory-only" : "RECOVERED"} path=${info.path} consecutiveFailures=${info.consecutiveFailures}${info.error != null ? ` error=${info.error}` : ""}
|
|
@@ -99364,13 +99717,13 @@ async function maybeRedeliverUndeliveredAnswer() {
|
|
|
99364
99717
|
let transcriptText;
|
|
99365
99718
|
try {
|
|
99366
99719
|
const projectsDir = getProjectsDirForCwd();
|
|
99367
|
-
const path2 =
|
|
99368
|
-
if (!
|
|
99720
|
+
const path2 = join61(projectsDir, `${sessionId}.jsonl`);
|
|
99721
|
+
if (!existsSync55(path2)) {
|
|
99369
99722
|
process.stderr.write(`telegram gateway: crash-redelivery \u2014 transcript not found for turnKey=${turn.turn_key} session=${sessionId} (${path2}); skipping
|
|
99370
99723
|
`);
|
|
99371
99724
|
return;
|
|
99372
99725
|
}
|
|
99373
|
-
transcriptText =
|
|
99726
|
+
transcriptText = readFileSync59(path2, "utf8");
|
|
99374
99727
|
} catch (err) {
|
|
99375
99728
|
process.stderr.write(`telegram gateway: crash-redelivery \u2014 transcript read failed turnKey=${turn.turn_key}: ${err.message}
|
|
99376
99729
|
`);
|
|
@@ -99467,7 +99820,7 @@ if (isGatewayMain && inboundSpool != null) {
|
|
|
99467
99820
|
}
|
|
99468
99821
|
var pendingPermissionBuffer = createPendingPermissionBuffer();
|
|
99469
99822
|
function buildPermissionActionRow(requestId, showAlways) {
|
|
99470
|
-
const kb = new
|
|
99823
|
+
const kb = new import_grammy16.InlineKeyboard().text("\u274C Deny", `perm:deny:${requestId}`).text("\u2705 Allow", `perm:allow:${requestId}`);
|
|
99471
99824
|
if (showAlways)
|
|
99472
99825
|
kb.text("\uD83D\uDD01 Always\u2026", `perm:always:${requestId}`);
|
|
99473
99826
|
return kb;
|
|
@@ -99529,8 +99882,8 @@ var bridgeDeadWatchdog = createBridgeDeadWatchdog({
|
|
|
99529
99882
|
isSessionAlive: () => findAgentProcessInContainer2()?.comm === "claude",
|
|
99530
99883
|
isShuttingDown: () => shuttingDown,
|
|
99531
99884
|
escalate: (reason) => triggerSelfRestart(process.env.SWITCHROOM_AGENT_NAME ?? "", reason, 1500),
|
|
99532
|
-
crashLogPath:
|
|
99533
|
-
markerPath:
|
|
99885
|
+
crashLogPath: join61(STATE_DIR, "bridge-crash.log"),
|
|
99886
|
+
markerPath: join61(STATE_DIR, "bridge-dead-escalation.json"),
|
|
99534
99887
|
log: (line) => process.stderr.write(`${line}
|
|
99535
99888
|
`),
|
|
99536
99889
|
priorStreak: bridgeDeadPriorStreak,
|
|
@@ -99636,8 +99989,8 @@ if (isGatewayMain)
|
|
|
99636
99989
|
probeQuotaViaBroker: (t) => probeQuotaForBootCard(agentSlug, t),
|
|
99637
99990
|
tmuxSupervisor: process.env.SWITCHROOM_TMUX_SUPERVISOR === "1",
|
|
99638
99991
|
dockerMode: process.env.SWITCHROOM_RUNTIME === "docker",
|
|
99639
|
-
configSnapshotPath:
|
|
99640
|
-
bootCardStatePath:
|
|
99992
|
+
configSnapshotPath: join61(resolvedAgentDirForCard, ".config-snapshot.json"),
|
|
99993
|
+
bootCardStatePath: join61(resolvedAgentDirForCard, ".boot-card-msgid.json"),
|
|
99641
99994
|
floodStatePath: FLOOD_STATE_PATH,
|
|
99642
99995
|
...updateOutcomeLine ? { updateOutcomeLine } : {}
|
|
99643
99996
|
}, ackMsgId).then((handle) => {
|
|
@@ -100041,7 +100394,7 @@ if (isGatewayMain)
|
|
|
100041
100394
|
}
|
|
100042
100395
|
},
|
|
100043
100396
|
postAttachment: async (args) => {
|
|
100044
|
-
const input = new
|
|
100397
|
+
const input = new import_grammy16.InputFile(Buffer.from(args.content, "utf8"), args.filename);
|
|
100045
100398
|
await robustApiCall(() => bot.api.sendDocument(args.chatId, input, {
|
|
100046
100399
|
...args.threadId !== undefined ? { message_thread_id: args.threadId } : {}
|
|
100047
100400
|
}), {
|
|
@@ -100319,7 +100672,7 @@ if (isGatewayMain)
|
|
|
100319
100672
|
const receiverUid = receiverUidRaw ? Number(receiverUidRaw) : NaN;
|
|
100320
100673
|
if (Number.isInteger(receiverUid))
|
|
100321
100674
|
allowedUids.push(receiverUid);
|
|
100322
|
-
const socketPath =
|
|
100675
|
+
const socketPath = join61(STATE_DIR, "webhook.sock");
|
|
100323
100676
|
const webhookInject = (agentName3, inbound) => {
|
|
100324
100677
|
const msg = inbound;
|
|
100325
100678
|
const delivered = ipcServer.sendToAgent(agentName3, msg);
|
|
@@ -100547,9 +100900,9 @@ function redactOutboundText(text5, site) {
|
|
|
100547
100900
|
var VOICE_OUT_DEFAULT_CHUNK_CHARS = 600;
|
|
100548
100901
|
var VOICE_OUT_HARD_CHUNK_CAP = 4096;
|
|
100549
100902
|
var voiceOnDemandCache = new VoiceOnDemandCache({
|
|
100550
|
-
persistPath:
|
|
100903
|
+
persistPath: join61(STATE_DIR, "voice-ondemand.json")
|
|
100551
100904
|
});
|
|
100552
|
-
var VOICE_CACHE_DIR =
|
|
100905
|
+
var VOICE_CACHE_DIR = join61(STATE_DIR, "voice-cache");
|
|
100553
100906
|
var voicePreSynthQueue = new PreSynthQueue({
|
|
100554
100907
|
runJob: async (job) => {
|
|
100555
100908
|
const sidecarToken = await materializeSidecarToken2();
|
|
@@ -100816,7 +101169,7 @@ async function executeAskUser(rawArgs) {
|
|
|
100816
101169
|
}
|
|
100817
101170
|
}
|
|
100818
101171
|
const askId = generateAskId();
|
|
100819
|
-
const keyboard = new
|
|
101172
|
+
const keyboard = new import_grammy16.InlineKeyboard;
|
|
100820
101173
|
for (let i = 0;i < args.options.length; i++) {
|
|
100821
101174
|
keyboard.text(args.options[i], encodeAskCallback(askId, i));
|
|
100822
101175
|
if (i < args.options.length - 1)
|
|
@@ -100951,11 +101304,11 @@ async function executeSendGif(rawArgs) {
|
|
|
100951
101304
|
};
|
|
100952
101305
|
}
|
|
100953
101306
|
async function publishToTelegraph(text5, shortName, authorName) {
|
|
100954
|
-
const accountPath =
|
|
101307
|
+
const accountPath = join61(STATE_DIR, "telegraph-account.json");
|
|
100955
101308
|
let account = null;
|
|
100956
101309
|
try {
|
|
100957
|
-
if (
|
|
100958
|
-
const raw =
|
|
101310
|
+
if (existsSync55(accountPath)) {
|
|
101311
|
+
const raw = readFileSync59(accountPath, "utf-8");
|
|
100959
101312
|
const parsed = JSON.parse(raw);
|
|
100960
101313
|
if (parsed.shortName && parsed.accessToken) {
|
|
100961
101314
|
account = parsed;
|
|
@@ -100974,8 +101327,8 @@ async function publishToTelegraph(text5, shortName, authorName) {
|
|
|
100974
101327
|
}
|
|
100975
101328
|
account = created.value;
|
|
100976
101329
|
try {
|
|
100977
|
-
|
|
100978
|
-
|
|
101330
|
+
mkdirSync48(STATE_DIR, { recursive: true, mode: 448 });
|
|
101331
|
+
writeFileSync49(accountPath, JSON.stringify(account, null, 2), { mode: 384 });
|
|
100979
101332
|
} catch (err) {
|
|
100980
101333
|
process.stderr.write(`telegram gateway: telegraph cache write failed: ${err.message}
|
|
100981
101334
|
`);
|
|
@@ -101091,7 +101444,7 @@ _The secret was NOT saved. The agent can re-request with \`request_secret\`._`,
|
|
|
101091
101444
|
}
|
|
101092
101445
|
function readLiveSwitchroomConfigText() {
|
|
101093
101446
|
const cfgPath = process.env.SWITCHROOM_CONFIG ?? findConfigFile2();
|
|
101094
|
-
return
|
|
101447
|
+
return readFileSync59(cfgPath, "utf8");
|
|
101095
101448
|
}
|
|
101096
101449
|
async function executeReact(args) {
|
|
101097
101450
|
if (!args.chat_id)
|
|
@@ -101130,9 +101483,9 @@ async function executeDownloadAttachment(args) {
|
|
|
101130
101483
|
fileUniqueId: file.file_unique_id,
|
|
101131
101484
|
now: Date.now()
|
|
101132
101485
|
});
|
|
101133
|
-
|
|
101486
|
+
mkdirSync48(INBOX_DIR, { recursive: true, mode: 448 });
|
|
101134
101487
|
assertInsideInbox2(INBOX_DIR, dlPath);
|
|
101135
|
-
|
|
101488
|
+
writeFileSync49(dlPath, buf, { mode: 384 });
|
|
101136
101489
|
return { content: [{ type: "text", text: dlPath }] };
|
|
101137
101490
|
}
|
|
101138
101491
|
async function executeEditMessage(args) {
|
|
@@ -101146,23 +101499,20 @@ async function executeEditMessage(args) {
|
|
|
101146
101499
|
const editAccess = loadAccess();
|
|
101147
101500
|
const editFormat = args.format ?? (editAccess.parseMode ?? "html");
|
|
101148
101501
|
const editLiteralText = editFormat === "text";
|
|
101149
|
-
|
|
101150
|
-
|
|
101151
|
-
|
|
101152
|
-
|
|
101153
|
-
|
|
101154
|
-
|
|
101155
|
-
|
|
101156
|
-
|
|
101157
|
-
|
|
101158
|
-
|
|
101159
|
-
|
|
101160
|
-
|
|
101161
|
-
|
|
101162
|
-
|
|
101163
|
-
site: "edit_message"
|
|
101164
|
-
});
|
|
101165
|
-
}
|
|
101502
|
+
const _editNorm = normalizeOutboundBody(args.text, "edit_message", redactOutboundText, {
|
|
101503
|
+
literalText: editLiteralText,
|
|
101504
|
+
addSpacers: !editLiteralText,
|
|
101505
|
+
tz: resolveEnvTimezone2(),
|
|
101506
|
+
nowMs: Date.now()
|
|
101507
|
+
});
|
|
101508
|
+
let editRawText = _editNorm.text;
|
|
101509
|
+
if (_editNorm.voiceReplaced > 0) {
|
|
101510
|
+
emitRuntimeMetric({
|
|
101511
|
+
kind: "voice_scrub_applied",
|
|
101512
|
+
chatKey: statusKey(String(args.chat_id ?? ""), undefined),
|
|
101513
|
+
replaced: _editNorm.voiceReplaced,
|
|
101514
|
+
site: "edit_message"
|
|
101515
|
+
});
|
|
101166
101516
|
}
|
|
101167
101517
|
const edited = await robustApiCall(() => lockedBot.api.editMessageText(String(args.chat_id ?? ""), Number(args.message_id), editLiteralText ? editRawText : richMessage2(editRawText)));
|
|
101168
101518
|
const id = typeof edited === "object" && edited ? edited.message_id : args.message_id;
|
|
@@ -102495,14 +102845,14 @@ function restartMarkerPath() {
|
|
|
102495
102845
|
const agentDir = resolveAgentDirFromEnv();
|
|
102496
102846
|
if (!agentDir)
|
|
102497
102847
|
return null;
|
|
102498
|
-
return
|
|
102848
|
+
return join61(agentDir, "restart-pending.json");
|
|
102499
102849
|
}
|
|
102500
102850
|
function writeRestartMarker(marker) {
|
|
102501
102851
|
const p = restartMarkerPath();
|
|
102502
102852
|
if (!p)
|
|
102503
102853
|
return;
|
|
102504
102854
|
try {
|
|
102505
|
-
|
|
102855
|
+
writeFileSync49(p, JSON.stringify(marker));
|
|
102506
102856
|
lastPlannedRestartAt = Date.now();
|
|
102507
102857
|
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}
|
|
102508
102858
|
`);
|
|
@@ -102521,7 +102871,7 @@ function readRestartMarker() {
|
|
|
102521
102871
|
if (!p)
|
|
102522
102872
|
return null;
|
|
102523
102873
|
try {
|
|
102524
|
-
return JSON.parse(
|
|
102874
|
+
return JSON.parse(readFileSync59(p, "utf8"));
|
|
102525
102875
|
} catch {
|
|
102526
102876
|
return null;
|
|
102527
102877
|
}
|
|
@@ -102670,7 +103020,7 @@ var _dockerReachable;
|
|
|
102670
103020
|
function isDockerReachable() {
|
|
102671
103021
|
if (_dockerReachable !== undefined)
|
|
102672
103022
|
return _dockerReachable;
|
|
102673
|
-
if (!
|
|
103023
|
+
if (!existsSync55("/var/run/docker.sock")) {
|
|
102674
103024
|
_dockerReachable = false;
|
|
102675
103025
|
return _dockerReachable;
|
|
102676
103026
|
}
|
|
@@ -102687,12 +103037,12 @@ function _resetDockerReachableCache() {
|
|
|
102687
103037
|
}
|
|
102688
103038
|
function spawnSwitchroomDetached(args, onFailure) {
|
|
102689
103039
|
const fullArgs = SWITCHROOM_CONFIG ? ["--config", SWITCHROOM_CONFIG, ...args] : args;
|
|
102690
|
-
const logPath =
|
|
103040
|
+
const logPath = join61(STATE_DIR, "detached-spawn.log");
|
|
102691
103041
|
let outFd = null;
|
|
102692
103042
|
try {
|
|
102693
|
-
|
|
103043
|
+
mkdirSync48(STATE_DIR, { recursive: true });
|
|
102694
103044
|
outFd = openSync12(logPath, "a");
|
|
102695
|
-
|
|
103045
|
+
writeFileSync49(logPath, `
|
|
102696
103046
|
[${new Date().toISOString()}] spawn ${SWITCHROOM_CLI} ${fullArgs.join(" ")}
|
|
102697
103047
|
`, { flag: "a" });
|
|
102698
103048
|
} catch {}
|
|
@@ -102718,7 +103068,7 @@ function spawnSwitchroomDetached(args, onFailure) {
|
|
|
102718
103068
|
return;
|
|
102719
103069
|
let tail = "";
|
|
102720
103070
|
try {
|
|
102721
|
-
const full =
|
|
103071
|
+
const full = readFileSync59(logPath, "utf8");
|
|
102722
103072
|
tail = full.split(`
|
|
102723
103073
|
`).slice(-30).join(`
|
|
102724
103074
|
`).trim();
|
|
@@ -102980,10 +103330,10 @@ ${preBlock(formatSwitchroomOutput(detail))}`, { html: true });
|
|
|
102980
103330
|
}
|
|
102981
103331
|
function readRecentDenialsForAgent(agentName3, windowMs, limit) {
|
|
102982
103332
|
try {
|
|
102983
|
-
const auditPath =
|
|
102984
|
-
if (!
|
|
103333
|
+
const auditPath = join61(homedir20(), ".switchroom", "vault-audit.log");
|
|
103334
|
+
if (!existsSync55(auditPath))
|
|
102985
103335
|
return [];
|
|
102986
|
-
const raw =
|
|
103336
|
+
const raw = readFileSync59(auditPath, "utf8");
|
|
102987
103337
|
return recentDenialsFromAuditLog(raw, { agentName: agentName3, windowMs, limit });
|
|
102988
103338
|
} catch {
|
|
102989
103339
|
return [];
|
|
@@ -103034,7 +103384,7 @@ async function buildAgentMetadata(agentName3) {
|
|
|
103034
103384
|
try {
|
|
103035
103385
|
const agentDir = resolveAgentDirFromEnv();
|
|
103036
103386
|
if (agentDir) {
|
|
103037
|
-
const raw =
|
|
103387
|
+
const raw = readFileSync59(join61(agentDir, ".claude", ".claude.json"), "utf8");
|
|
103038
103388
|
claudeJson = JSON.parse(raw);
|
|
103039
103389
|
}
|
|
103040
103390
|
} catch {}
|
|
@@ -103163,7 +103513,7 @@ function buildModelDeps(restartCtx) {
|
|
|
103163
103513
|
try {
|
|
103164
103514
|
const agentDir = resolveAgentDirFromEnv();
|
|
103165
103515
|
if (agentDir) {
|
|
103166
|
-
const local = await fetchQuota2({ claudeConfigDir:
|
|
103516
|
+
const local = await fetchQuota2({ claudeConfigDir: join61(agentDir, ".claude") });
|
|
103167
103517
|
if (local.ok)
|
|
103168
103518
|
return formatQuotaLine2(local.data);
|
|
103169
103519
|
}
|
|
@@ -103258,7 +103608,7 @@ function buildModelDeps(restartCtx) {
|
|
|
103258
103608
|
function modelMenuReplyMarkup(reply) {
|
|
103259
103609
|
if (!reply.keyboard)
|
|
103260
103610
|
return;
|
|
103261
|
-
const kb = new
|
|
103611
|
+
const kb = new import_grammy16.InlineKeyboard;
|
|
103262
103612
|
for (const row of reply.keyboard) {
|
|
103263
103613
|
for (const btn of row)
|
|
103264
103614
|
kb.text(btn.text, btn.callback_data);
|
|
@@ -103398,7 +103748,7 @@ function buildEffortDeps() {
|
|
|
103398
103748
|
function effortMenuReplyMarkup(reply) {
|
|
103399
103749
|
if (!reply.keyboard)
|
|
103400
103750
|
return;
|
|
103401
|
-
const kb = new
|
|
103751
|
+
const kb = new import_grammy16.InlineKeyboard;
|
|
103402
103752
|
for (const row of reply.keyboard) {
|
|
103403
103753
|
for (const btn of row)
|
|
103404
103754
|
kb.text(btn.text, btn.callback_data);
|
|
@@ -103409,10 +103759,10 @@ function effortMenuReplyMarkup(reply) {
|
|
|
103409
103759
|
function flushAgentHandoff(agentDir) {
|
|
103410
103760
|
let removed = 0;
|
|
103411
103761
|
for (const fname of [".handoff.md", ".handoff-topic"]) {
|
|
103412
|
-
const p =
|
|
103762
|
+
const p = join61(agentDir, fname);
|
|
103413
103763
|
try {
|
|
103414
|
-
if (
|
|
103415
|
-
|
|
103764
|
+
if (existsSync55(p)) {
|
|
103765
|
+
unlinkSync27(p);
|
|
103416
103766
|
removed++;
|
|
103417
103767
|
}
|
|
103418
103768
|
} catch (err) {
|
|
@@ -103467,7 +103817,7 @@ async function handleNewCommand(ctx) {
|
|
|
103467
103817
|
writeRestartMarker({ chat_id: chatId, thread_id: threadId ?? null, ack_message_id: ackId, ts: Date.now() });
|
|
103468
103818
|
if (agentDir != null) {
|
|
103469
103819
|
try {
|
|
103470
|
-
|
|
103820
|
+
writeFileSync49(join61(agentDir, ".force-fresh-session"), `${kind} at ${new Date().toISOString()}
|
|
103471
103821
|
`, "utf8");
|
|
103472
103822
|
} catch (err) {
|
|
103473
103823
|
process.stderr.write(`telegram gateway: failed to write force-fresh marker: ${err}
|
|
@@ -103580,16 +103930,16 @@ function buildFolderPickerDeps() {
|
|
|
103580
103930
|
};
|
|
103581
103931
|
}
|
|
103582
103932
|
var lockoutOps = {
|
|
103583
|
-
readFileSync: (p, enc) =>
|
|
103584
|
-
writeFileSync: (p, data, opts) =>
|
|
103585
|
-
existsSync: (p) =>
|
|
103586
|
-
mkdirSync: (p, opts) =>
|
|
103587
|
-
joinPath: (...parts) =>
|
|
103933
|
+
readFileSync: (p, enc) => readFileSync59(p, enc),
|
|
103934
|
+
writeFileSync: (p, data, opts) => writeFileSync49(p, data, opts),
|
|
103935
|
+
existsSync: (p) => existsSync55(p),
|
|
103936
|
+
mkdirSync: (p, opts) => mkdirSync48(p, opts),
|
|
103937
|
+
joinPath: (...parts) => join61(...parts)
|
|
103588
103938
|
};
|
|
103589
103939
|
var FLEET_FALLBACK_DEDUP_MS = 30000;
|
|
103590
103940
|
function isAuthBrokerSocketReachable() {
|
|
103591
103941
|
try {
|
|
103592
|
-
return
|
|
103942
|
+
return existsSync55(resolveAuthBrokerSocketPath2());
|
|
103593
103943
|
} catch {
|
|
103594
103944
|
return false;
|
|
103595
103945
|
}
|
|
@@ -103844,7 +104194,7 @@ async function runCreditWatch() {
|
|
|
103844
104194
|
if (!agentDir)
|
|
103845
104195
|
return;
|
|
103846
104196
|
const agentName3 = getMyAgentName();
|
|
103847
|
-
const claudeConfigDir =
|
|
104197
|
+
const claudeConfigDir = join61(agentDir, ".claude");
|
|
103848
104198
|
const stateDir = STATE_DIR;
|
|
103849
104199
|
const reason = readClaudeJsonOverage(claudeConfigDir);
|
|
103850
104200
|
const prev = loadCreditState(stateDir);
|
|
@@ -105051,7 +105401,7 @@ _Google accounts are connected from the host CLI._`, { html: true });
|
|
|
105051
105401
|
return;
|
|
105052
105402
|
}
|
|
105053
105403
|
const appNote = started.source === "default" ? "_Using switchroom's shipped Microsoft app._" : "_Using your configured Microsoft app._";
|
|
105054
|
-
const keyboard = new
|
|
105404
|
+
const keyboard = new import_grammy16.InlineKeyboard().url("\uD83D\uDD10 Sign in to Microsoft", started.device.verification_uri).row().text("\u2716 Cancel", `cn:cancel:${key}`);
|
|
105055
105405
|
const sent = await ctx.replyWithRichMessage(richMessage2(`\uD83D\uDD17 **Connect a Microsoft account**
|
|
105056
105406
|
|
|
105057
105407
|
` + `1. Tap **Sign in to Microsoft** below.
|
|
@@ -105187,7 +105537,7 @@ Send \`/auth ${parsed.provider} cancel\` to abort.`, { html: true });
|
|
|
105187
105537
|
tz: process.env.SWITCHROOM_TIMEZONE ?? process.env.TZ
|
|
105188
105538
|
});
|
|
105189
105539
|
if (reply.keyboard && reply.keyboard.length > 0) {
|
|
105190
|
-
const kb = new
|
|
105540
|
+
const kb = new import_grammy16.InlineKeyboard;
|
|
105191
105541
|
for (let i = 0;i < reply.keyboard.length; i++) {
|
|
105192
105542
|
const row = reply.keyboard[i];
|
|
105193
105543
|
for (const b of row) {
|
|
@@ -105329,7 +105679,7 @@ Send \`/auth ${parsed.provider} cancel\` to abort.`, { html: true });
|
|
|
105329
105679
|
}
|
|
105330
105680
|
lines.push("");
|
|
105331
105681
|
const denials = readRecentDenialsForAgent(targetAgent, 604800000, 5);
|
|
105332
|
-
const denialKeyboard = new
|
|
105682
|
+
const denialKeyboard = new import_grammy16.InlineKeyboard;
|
|
105333
105683
|
if (denials.length > 0) {
|
|
105334
105684
|
lines.push("**Recent denials (last 7d):**");
|
|
105335
105685
|
for (const d of denials) {
|
|
@@ -105377,7 +105727,7 @@ Send \`/auth ${parsed.provider} cancel\` to abort.`, { html: true });
|
|
|
105377
105727
|
byAgent.set(g.agent_slug, list3);
|
|
105378
105728
|
}
|
|
105379
105729
|
const lines = ["**\uD83D\uDCDC Active grants**", ""];
|
|
105380
|
-
const keyboard = new
|
|
105730
|
+
const keyboard = new import_grammy16.InlineKeyboard;
|
|
105381
105731
|
for (const [agentName3, agentGrants] of byAgent) {
|
|
105382
105732
|
lines.push(`**${escapeHtmlForTg2(agentName3)}:**`);
|
|
105383
105733
|
for (const g of agentGrants) {
|
|
@@ -105574,7 +105924,7 @@ Send \`/auth ${parsed.provider} cancel\` to abort.`, { html: true });
|
|
|
105574
105924
|
if (ctx.chat?.type !== "private") {
|
|
105575
105925
|
kbRows = kbRows.filter((row) => !row.some((b) => b.callbackData?.startsWith("auth:use:")));
|
|
105576
105926
|
}
|
|
105577
|
-
const keyboard = new
|
|
105927
|
+
const keyboard = new import_grammy16.InlineKeyboard;
|
|
105578
105928
|
kbRows.forEach((row, ri) => {
|
|
105579
105929
|
if (ri > 0)
|
|
105580
105930
|
keyboard.row();
|
|
@@ -105600,7 +105950,7 @@ Send \`/auth ${parsed.provider} cancel\` to abort.`, { html: true });
|
|
|
105600
105950
|
await switchroomReply(ctx, "**/usage:** cannot resolve agent dir.", { html: true });
|
|
105601
105951
|
return;
|
|
105602
105952
|
}
|
|
105603
|
-
const result = await fetchQuota2({ claudeConfigDir:
|
|
105953
|
+
const result = await fetchQuota2({ claudeConfigDir: join61(agentDir, ".claude") });
|
|
105604
105954
|
if (!result.ok) {
|
|
105605
105955
|
await switchroomReply(ctx, `**/usage:** ${escapeHtmlForTg2(result.reason)}`, { html: true });
|
|
105606
105956
|
return;
|
|
@@ -105926,7 +106276,7 @@ ${preBlock(formatSwitchroomOutput(err.message ?? "unknown error"))}`, { html: tr
|
|
|
105926
106276
|
await ctx.answerCallbackQuery({ text: "No always-allow rule for this tool." }).catch(() => {});
|
|
105927
106277
|
return;
|
|
105928
106278
|
}
|
|
105929
|
-
keyboard = new
|
|
106279
|
+
keyboard = new import_grammy16.InlineKeyboard().text("\u2190 Back", `perm:back:${request_id}`);
|
|
105930
106280
|
if (choices.specific)
|
|
105931
106281
|
keyboard.text(choices.specific.buttonLabel, `perm:asn:${request_id}`);
|
|
105932
106282
|
keyboard.text(`${choices.broad.buttonLabel} \u26A0\uFE0F`, `perm:asb:${request_id}`);
|
|
@@ -105986,7 +106336,7 @@ ${interimLabel}` : interimLabel
|
|
|
105986
106336
|
const unifiedDiff = (() => {
|
|
105987
106337
|
try {
|
|
105988
106338
|
const cfgPath = process.env.SWITCHROOM_CONFIG ?? SWITCHROOM_CONFIG ?? findConfigFile2();
|
|
105989
|
-
const raw =
|
|
106339
|
+
const raw = readFileSync59(cfgPath, "utf8");
|
|
105990
106340
|
return synthesizeAllowRuleDiff({ agentName: agentName3, rule: chosen.rule, configText: raw });
|
|
105991
106341
|
} catch (err) {
|
|
105992
106342
|
process.stderr.write(`telegram gateway: always-allow diff synth failed: ${err.message}
|
|
@@ -106228,7 +106578,7 @@ ${labelWithResume}` : labelWithResume,
|
|
|
106228
106578
|
verb: "voice-ondemand.sendVoice",
|
|
106229
106579
|
...threadId != null ? { threadId } : {}
|
|
106230
106580
|
}),
|
|
106231
|
-
sendVoiceByUpload: (chatId, audio, sendOpts, threadId) => robustApiCall(() => bot2.api.sendVoice(chatId, new
|
|
106581
|
+
sendVoiceByUpload: (chatId, audio, sendOpts, threadId) => robustApiCall(() => bot2.api.sendVoice(chatId, new import_grammy16.InputFile(Buffer.from(audio)), sendOpts), {
|
|
106232
106582
|
chat_id: chatId,
|
|
106233
106583
|
verb: "voice-ondemand.sendVoice",
|
|
106234
106584
|
...threadId != null ? { threadId } : {}
|
|
@@ -106487,7 +106837,7 @@ async function initGatewayBot() {
|
|
|
106487
106837
|
`);
|
|
106488
106838
|
process.exit(1);
|
|
106489
106839
|
}
|
|
106490
|
-
bot = new
|
|
106840
|
+
bot = new import_grammy16.Bot(TOKEN);
|
|
106491
106841
|
installTgPostLogger(bot);
|
|
106492
106842
|
installRichMarkdownGuard(bot);
|
|
106493
106843
|
installUpdateTap(bot, (line) => process.stderr.write(line));
|
|
@@ -106842,7 +107192,7 @@ async function startGateway() {
|
|
|
106842
107192
|
return;
|
|
106843
107193
|
}
|
|
106844
107194
|
})();
|
|
106845
|
-
const resolvedAgentDirForBootCard = agentDir ??
|
|
107195
|
+
const resolvedAgentDirForBootCard = agentDir ?? join61(homedir20(), ".switchroom", "agents", agentSlug);
|
|
106846
107196
|
const handle = await startBootCard(chatId, threadId, botApiForCard, {
|
|
106847
107197
|
agentName: agentDisplayName,
|
|
106848
107198
|
agentSlug,
|
|
@@ -106856,8 +107206,8 @@ async function startGateway() {
|
|
|
106856
107206
|
probeQuotaViaBroker: (t) => probeQuotaForBootCard(agentSlug, t),
|
|
106857
107207
|
tmuxSupervisor: process.env.SWITCHROOM_TMUX_SUPERVISOR === "1",
|
|
106858
107208
|
dockerMode: process.env.SWITCHROOM_RUNTIME === "docker",
|
|
106859
|
-
configSnapshotPath:
|
|
106860
|
-
bootCardStatePath:
|
|
107209
|
+
configSnapshotPath: join61(resolvedAgentDirForBootCard, ".config-snapshot.json"),
|
|
107210
|
+
bootCardStatePath: join61(resolvedAgentDirForBootCard, ".boot-card-msgid.json"),
|
|
106861
107211
|
floodStatePath: FLOOD_STATE_PATH,
|
|
106862
107212
|
...updateOutcomeLine ? { updateOutcomeLine } : {}
|
|
106863
107213
|
}, ackMsgId);
|
|
@@ -106888,10 +107238,10 @@ async function startGateway() {
|
|
|
106888
107238
|
try {
|
|
106889
107239
|
const smAgentDir = resolveAgentDirFromEnv();
|
|
106890
107240
|
if (smAgentDir) {
|
|
106891
|
-
const activePath =
|
|
106892
|
-
if (
|
|
107241
|
+
const activePath = join61(smAgentDir, ".active-session-model");
|
|
107242
|
+
if (existsSync55(activePath)) {
|
|
106893
107243
|
try {
|
|
106894
|
-
const launched =
|
|
107244
|
+
const launched = readFileSync59(activePath, "utf8").trim();
|
|
106895
107245
|
const configured = (() => {
|
|
106896
107246
|
const d = switchroomExecJson(["agent", "list"]);
|
|
106897
107247
|
const raw = d?.agents?.find((a) => a.name === getMyAgentName())?.model ?? null;
|
|
@@ -106924,29 +107274,29 @@ async function startGateway() {
|
|
|
106924
107274
|
deliverModelSwitchBootNotice({
|
|
106925
107275
|
...modelBootCardDeps,
|
|
106926
107276
|
confirmation,
|
|
106927
|
-
hasSessionModelAlert:
|
|
107277
|
+
hasSessionModelAlert: existsSync55(join61(smAgentDir, ".session-model-alert"))
|
|
106928
107278
|
});
|
|
106929
107279
|
}
|
|
106930
107280
|
} catch {}
|
|
106931
107281
|
}
|
|
106932
|
-
const activeEffortPath =
|
|
106933
|
-
if (
|
|
107282
|
+
const activeEffortPath = join61(smAgentDir, ".active-session-effort");
|
|
107283
|
+
if (existsSync55(activeEffortPath)) {
|
|
106934
107284
|
try {
|
|
106935
|
-
const launchedEffort =
|
|
107285
|
+
const launchedEffort = readFileSync59(activeEffortPath, "utf8").trim();
|
|
106936
107286
|
const configuredEffort = getConfiguredEffortForPersist();
|
|
106937
107287
|
sessionEffortOverride = launchedEffort.length > 0 && launchedEffort !== configuredEffort ? launchedEffort : null;
|
|
106938
107288
|
} catch {}
|
|
106939
107289
|
}
|
|
106940
|
-
const alertPath =
|
|
106941
|
-
if (
|
|
107290
|
+
const alertPath = join61(smAgentDir, ".session-model-alert");
|
|
107291
|
+
if (existsSync55(alertPath)) {
|
|
106942
107292
|
let alertText = null;
|
|
106943
107293
|
try {
|
|
106944
|
-
alertText =
|
|
107294
|
+
alertText = readFileSync59(alertPath, "utf8").trim();
|
|
106945
107295
|
} catch {
|
|
106946
107296
|
alertText = null;
|
|
106947
107297
|
}
|
|
106948
107298
|
try {
|
|
106949
|
-
|
|
107299
|
+
unlinkSync27(alertPath);
|
|
106950
107300
|
} catch {}
|
|
106951
107301
|
if (alertText && alertText.length > 0) {
|
|
106952
107302
|
const operators = loadAccess().allowFrom;
|
|
@@ -107442,7 +107792,7 @@ async function startGateway() {
|
|
|
107442
107792
|
await runnerHandle.task();
|
|
107443
107793
|
return;
|
|
107444
107794
|
} catch (err) {
|
|
107445
|
-
if (err instanceof
|
|
107795
|
+
if (err instanceof import_grammy16.GrammyError && err.error_code === 409) {
|
|
107446
107796
|
const delay = Math.min(1000 * attempt, 15000);
|
|
107447
107797
|
const agentName3 = process.env.SWITCHROOM_AGENT_NAME ?? "-";
|
|
107448
107798
|
process.stderr.write(`telegram gateway: poll.409.detected attempt=${attempt} retry_in_ms=${delay} agent=${agentName3}
|