switchroom 0.19.12 → 0.19.14
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 +4 -2
- 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 +1402 -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 +115 -8
- package/telegram-plugin/gateway/outbox-sweep.ts +320 -0
- package/telegram-plugin/gateway/stream-render.ts +26 -50
- package/telegram-plugin/hooks/silent-end-interrupt-stop.mjs +120 -2
- package/telegram-plugin/hooks/silent-end-scan.mjs +311 -0
- package/telegram-plugin/outbox.ts +489 -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/outbox-reply-then-recap-e2e.test.ts +600 -0
- package/telegram-plugin/tests/silent-end-interrupt-stop-integration.test.ts +68 -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,321 @@ 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({
|
|
74210
|
+
turnNonce: nonce,
|
|
74211
|
+
textSha256: sha256Hex(args.text),
|
|
74212
|
+
tgMessageId: args.tgMessageId,
|
|
74213
|
+
ts: now,
|
|
74214
|
+
deliverySource: "reply-tool",
|
|
74215
|
+
...args.replyAlreadyDeliveredThisTurn == null ? {} : { replyAlreadyDeliveredThisTurn: args.replyAlreadyDeliveredThisTurn }
|
|
74216
|
+
}, stateDir);
|
|
74217
|
+
clearOutboxRecord(nonce, stateDir);
|
|
74218
|
+
}
|
|
74219
|
+
|
|
73914
74220
|
// gateway/chat-id-fallback.ts
|
|
73915
74221
|
function resolveChatIdFallback(rawChatId, access, turnSessionChatId, lastKnownChatId, hasLiveTurn) {
|
|
73916
74222
|
if (access.allowFrom.includes(rawChatId) || rawChatId in access.groups) {
|
|
@@ -73951,12 +74257,12 @@ function decideOverPing(input) {
|
|
|
73951
74257
|
// silent-reply-anchor.ts
|
|
73952
74258
|
init_format();
|
|
73953
74259
|
var TELEGRAM_MSG_CAP2 = RICH_MESSAGE_MAX_CHARS;
|
|
73954
|
-
function
|
|
74260
|
+
function enabled4() {
|
|
73955
74261
|
const v = process.env.SWITCHROOM_DISABLE_SILENT_REPLY_AUTOEDIT;
|
|
73956
74262
|
return !(v === "1" || v === "true");
|
|
73957
74263
|
}
|
|
73958
74264
|
function decideSilentReplyAnchor(input) {
|
|
73959
|
-
if (!
|
|
74265
|
+
if (!enabled4()) {
|
|
73960
74266
|
return { kind: "fresh", becomesAnchor: false };
|
|
73961
74267
|
}
|
|
73962
74268
|
if (!input.effectivelySilent) {
|
|
@@ -74279,11 +74585,11 @@ function classifyPhotoFile(path2) {
|
|
|
74279
74585
|
}
|
|
74280
74586
|
|
|
74281
74587
|
// streaming-metrics.ts
|
|
74282
|
-
function
|
|
74588
|
+
function enabled5() {
|
|
74283
74589
|
return process.env.SWITCHROOM_STREAMING_METRICS === "1";
|
|
74284
74590
|
}
|
|
74285
74591
|
function logStreamingEvent2(ev) {
|
|
74286
|
-
if (!
|
|
74592
|
+
if (!enabled5())
|
|
74287
74593
|
return;
|
|
74288
74594
|
const line = JSON.stringify({ ts: monotonicNowMs2(), ...ev });
|
|
74289
74595
|
try {
|
|
@@ -74300,26 +74606,26 @@ function monotonicNowMs2() {
|
|
|
74300
74606
|
}
|
|
74301
74607
|
|
|
74302
74608
|
// silent-end.ts
|
|
74303
|
-
import { existsSync as
|
|
74304
|
-
import { dirname as dirname11, join as
|
|
74305
|
-
import { homedir as
|
|
74609
|
+
import { existsSync as existsSync25, readFileSync as readFileSync25, writeFileSync as writeFileSync23, unlinkSync as unlinkSync13, mkdirSync as mkdirSync26 } from "node:fs";
|
|
74610
|
+
import { dirname as dirname11, join as join29 } from "node:path";
|
|
74611
|
+
import { homedir as homedir11 } from "node:os";
|
|
74306
74612
|
var SILENT_END_MAX_RETRIES2 = 2;
|
|
74307
74613
|
var SILENT_END_STALE_RECORD_MAX_AGE_MS2 = 30 * 60000;
|
|
74308
74614
|
function silentEndFallbackText(turnDurationMs) {
|
|
74309
74615
|
const elapsed = typeof turnDurationMs === "number" && Number.isFinite(turnDurationMs) && turnDurationMs > 0 ? ` (waited ${Math.round(turnDurationMs / 1000)}s)` : "";
|
|
74310
74616
|
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
74617
|
}
|
|
74312
|
-
function
|
|
74618
|
+
function resolveStateDir3(deps) {
|
|
74313
74619
|
if (deps?.stateDir != null)
|
|
74314
74620
|
return deps.stateDir;
|
|
74315
74621
|
const env = process.env.TELEGRAM_STATE_DIR;
|
|
74316
74622
|
if (env != null && env !== "")
|
|
74317
74623
|
return env;
|
|
74318
|
-
const home2 = process.env.HOME ??
|
|
74319
|
-
return
|
|
74624
|
+
const home2 = process.env.HOME ?? homedir11();
|
|
74625
|
+
return join29(home2, ".claude", "channels", "telegram");
|
|
74320
74626
|
}
|
|
74321
74627
|
function resolveStatePath3(deps) {
|
|
74322
|
-
return
|
|
74628
|
+
return join29(resolveStateDir3(deps), "silent-end-pending.json");
|
|
74323
74629
|
}
|
|
74324
74630
|
function emitLog2(deps, line) {
|
|
74325
74631
|
if (deps?.log != null)
|
|
@@ -74331,8 +74637,8 @@ function writeSilentEndState2(args, deps) {
|
|
|
74331
74637
|
const statePath = resolveStatePath3(deps);
|
|
74332
74638
|
let retryCount = 0;
|
|
74333
74639
|
try {
|
|
74334
|
-
if (
|
|
74335
|
-
const prev = JSON.parse(
|
|
74640
|
+
if (existsSync25(statePath)) {
|
|
74641
|
+
const prev = JSON.parse(readFileSync25(statePath, "utf8"));
|
|
74336
74642
|
if (prev.turnKey === args.turnKey && typeof prev.retryCount === "number") {
|
|
74337
74643
|
retryCount = prev.retryCount;
|
|
74338
74644
|
}
|
|
@@ -74348,8 +74654,8 @@ function writeSilentEndState2(args, deps) {
|
|
|
74348
74654
|
timestamp: Date.now()
|
|
74349
74655
|
};
|
|
74350
74656
|
try {
|
|
74351
|
-
|
|
74352
|
-
|
|
74657
|
+
mkdirSync26(dirname11(statePath), { recursive: true });
|
|
74658
|
+
writeFileSync23(statePath, JSON.stringify(state3), "utf8");
|
|
74353
74659
|
emitLog2(deps, `silent-end: wrote state file turnKey=${args.turnKey} retryCount=${retryCount}
|
|
74354
74660
|
`);
|
|
74355
74661
|
} catch (err) {
|
|
@@ -74359,13 +74665,13 @@ function writeSilentEndState2(args, deps) {
|
|
|
74359
74665
|
}
|
|
74360
74666
|
function clearSilentEndState2(turnKey3, deps) {
|
|
74361
74667
|
const statePath = resolveStatePath3(deps);
|
|
74362
|
-
if (!
|
|
74668
|
+
if (!existsSync25(statePath))
|
|
74363
74669
|
return;
|
|
74364
74670
|
try {
|
|
74365
|
-
const prev = JSON.parse(
|
|
74671
|
+
const prev = JSON.parse(readFileSync25(statePath, "utf8"));
|
|
74366
74672
|
if (prev.turnKey != null && prev.turnKey !== turnKey3)
|
|
74367
74673
|
return;
|
|
74368
|
-
|
|
74674
|
+
unlinkSync13(statePath);
|
|
74369
74675
|
emitLog2(deps, `silent-end: cleared state file turnKey=${turnKey3}
|
|
74370
74676
|
`);
|
|
74371
74677
|
} catch {}
|
|
@@ -74396,10 +74702,10 @@ function settleCapturedProseDelivery(outcome, effects) {
|
|
|
74396
74702
|
}
|
|
74397
74703
|
function readSilentEndState2(deps) {
|
|
74398
74704
|
const statePath = resolveStatePath3(deps);
|
|
74399
|
-
if (!
|
|
74705
|
+
if (!existsSync25(statePath))
|
|
74400
74706
|
return null;
|
|
74401
74707
|
try {
|
|
74402
|
-
return JSON.parse(
|
|
74708
|
+
return JSON.parse(readFileSync25(statePath, "utf8"));
|
|
74403
74709
|
} catch {
|
|
74404
74710
|
return null;
|
|
74405
74711
|
}
|
|
@@ -74428,10 +74734,21 @@ function recordSilentTurnEnd2(args, deps) {
|
|
|
74428
74734
|
var recordUndeliveredTurnEnd2 = recordSilentTurnEnd2;
|
|
74429
74735
|
|
|
74430
74736
|
// gateway/outbound-send-path.ts
|
|
74431
|
-
function normalizeOutboundBody(rawText, site, redact2) {
|
|
74432
|
-
|
|
74737
|
+
function normalizeOutboundBody(rawText, site, redact2, opts = {}) {
|
|
74738
|
+
const { literalText = false, addSpacers = false, tz, nowMs: nowMs2 } = opts;
|
|
74739
|
+
let text4 = repairEscapedWhitespace(rawText);
|
|
74740
|
+
if (!literalText)
|
|
74741
|
+
text4 = normalizeParagraphBreaks(text4);
|
|
74433
74742
|
text4 = redact2(text4, site);
|
|
74434
|
-
|
|
74743
|
+
if (!literalText) {
|
|
74744
|
+
let formatted = stripExcessBold(normalizePunctuation(text4));
|
|
74745
|
+
if (addSpacers)
|
|
74746
|
+
formatted = addParagraphSpacers(formatted);
|
|
74747
|
+
text4 = formatted;
|
|
74748
|
+
if (tz != null && nowMs2 != null) {
|
|
74749
|
+
text4 = normalizeTemporal(text4, tz, nowMs2);
|
|
74750
|
+
}
|
|
74751
|
+
}
|
|
74435
74752
|
let voiceReplaced = 0;
|
|
74436
74753
|
const scrub = scrubVoice(text4);
|
|
74437
74754
|
if (scrub.replaced > 0) {
|
|
@@ -74683,7 +75000,10 @@ async function sendReply(deps, req) {
|
|
|
74683
75000
|
const rawText = args.text;
|
|
74684
75001
|
if (rawText == null || rawText === "")
|
|
74685
75002
|
throw new Error("reply: text is required and cannot be empty");
|
|
74686
|
-
const _normalized = normalizeOutboundBody(rawText, "reply", redactOutboundText
|
|
75003
|
+
const _normalized = normalizeOutboundBody(rawText, "reply", redactOutboundText, {
|
|
75004
|
+
tz: resolveEnvTimezone(),
|
|
75005
|
+
nowMs: Date.now()
|
|
75006
|
+
});
|
|
74687
75007
|
let text4 = _normalized.text;
|
|
74688
75008
|
if (_normalized.voiceReplaced > 0) {
|
|
74689
75009
|
emitRuntimeMetric2({
|
|
@@ -74862,7 +75182,7 @@ ${url}`;
|
|
|
74862
75182
|
}
|
|
74863
75183
|
for (const f of files) {
|
|
74864
75184
|
assertSendable(f);
|
|
74865
|
-
const st =
|
|
75185
|
+
const st = statSync10(f);
|
|
74866
75186
|
if (st.size > MAX_ATTACHMENT_BYTES) {
|
|
74867
75187
|
throw new Error(`file too large: ${f} (${(st.size / 1024 / 1024).toFixed(1)}MB, max 50MB)`);
|
|
74868
75188
|
}
|
|
@@ -75063,6 +75383,9 @@ ${url}`;
|
|
|
75063
75383
|
closeObligationOnSubstantiveReply(args, turn2, replyRoutedOriginTurn);
|
|
75064
75384
|
}
|
|
75065
75385
|
outboundDedup.record(chat_id, threadId, decision.mergedText, Date.now(), turn2?.registryKey ?? null);
|
|
75386
|
+
if (isFinalAnswerReply({ text: decision.mergedText, disableNotification: modelDisableNotification })) {
|
|
75387
|
+
journalExternalDelivery({ turnNonce: turn2?.turnId ?? null, text: decision.mergedText, tgMessageId: decision.messageId, replyAlreadyDeliveredThisTurn: true });
|
|
75388
|
+
}
|
|
75066
75389
|
silentAnchorEditDone = true;
|
|
75067
75390
|
} catch (err) {
|
|
75068
75391
|
process.stderr.write(`telegram gateway: silent-reply auto-edit failed, ` + `falling back to fresh send: ${err instanceof Error ? err.message : String(err)}
|
|
@@ -75336,7 +75659,11 @@ ${url}`;
|
|
|
75336
75659
|
process.stderr.write(`telegram channel: reply: finalized chatId=${chat_id} messageIds=[${sentIds.join(",")}] chunks=${chunks.length}
|
|
75337
75660
|
`);
|
|
75338
75661
|
if (sentIds.length > 0) {
|
|
75339
|
-
|
|
75662
|
+
const t = getCurrentTurn();
|
|
75663
|
+
outboundDedup.record(chat_id, threadId, text4, Date.now(), t?.registryKey ?? null);
|
|
75664
|
+
if (shouldJournalReplySiteDelivery({ text: rawText, disableNotification: modelDisableNotification })) {
|
|
75665
|
+
journalExternalDelivery({ turnNonce: t?.turnId ?? null, text: text4, tgMessageId: sentIds[sentIds.length - 1], replyAlreadyDeliveredThisTurn: true });
|
|
75666
|
+
}
|
|
75340
75667
|
}
|
|
75341
75668
|
return { content: [{ type: "text", text: result }] };
|
|
75342
75669
|
}
|
|
@@ -75389,6 +75716,7 @@ async function deliverCapturedProse(deps, args) {
|
|
|
75389
75716
|
} catch {}
|
|
75390
75717
|
}
|
|
75391
75718
|
outboundDedup.record(chatId, threadId, text4, now, registryKey);
|
|
75719
|
+
journalExternalDelivery({ turnNonce: originTurnId, text: text4, tgMessageId: sentIds[sentIds.length - 1], replyAlreadyDeliveredThisTurn: false });
|
|
75392
75720
|
process.stderr.write(`telegram gateway: captured-prose delivery \u2014 sent ${out.length} chars recovered from ` + `transcript scan (chat=${chatId} origin=${originTurnId})
|
|
75393
75721
|
`);
|
|
75394
75722
|
outcome = "sent";
|
|
@@ -75841,8 +76169,37 @@ class LivenessTracker {
|
|
|
75841
76169
|
}
|
|
75842
76170
|
}
|
|
75843
76171
|
|
|
75844
|
-
// gateway/
|
|
76172
|
+
// gateway/outbound-send-path.ts
|
|
76173
|
+
init_format();
|
|
76174
|
+
init_text_voice_scrub();
|
|
76175
|
+
var import_grammy11 = __toESM(require_mod2(), 1);
|
|
75845
76176
|
init_format();
|
|
76177
|
+
init_rich_send();
|
|
76178
|
+
function normalizeOutboundBody2(rawText, site, redact2, opts = {}) {
|
|
76179
|
+
const { literalText = false, addSpacers = false, tz, nowMs: nowMs2 } = opts;
|
|
76180
|
+
let text4 = repairEscapedWhitespace(rawText);
|
|
76181
|
+
if (!literalText)
|
|
76182
|
+
text4 = normalizeParagraphBreaks(text4);
|
|
76183
|
+
text4 = redact2(text4, site);
|
|
76184
|
+
if (!literalText) {
|
|
76185
|
+
let formatted = stripExcessBold(normalizePunctuation(text4));
|
|
76186
|
+
if (addSpacers)
|
|
76187
|
+
formatted = addParagraphSpacers(formatted);
|
|
76188
|
+
text4 = formatted;
|
|
76189
|
+
if (tz != null && nowMs2 != null) {
|
|
76190
|
+
text4 = normalizeTemporal(text4, tz, nowMs2);
|
|
76191
|
+
}
|
|
76192
|
+
}
|
|
76193
|
+
let voiceReplaced = 0;
|
|
76194
|
+
const scrub = scrubVoice(text4);
|
|
76195
|
+
if (scrub.replaced > 0) {
|
|
76196
|
+
text4 = scrub.scrubbed;
|
|
76197
|
+
voiceReplaced = scrub.replaced;
|
|
76198
|
+
}
|
|
76199
|
+
return { text: text4, voiceReplaced };
|
|
76200
|
+
}
|
|
76201
|
+
|
|
76202
|
+
// gateway/stream-render.ts
|
|
75846
76203
|
init_history();
|
|
75847
76204
|
|
|
75848
76205
|
// narrative-dedup.ts
|
|
@@ -75908,20 +76265,20 @@ var INTERRUPTED_VIA = new Set([
|
|
|
75908
76265
|
init_rich_send();
|
|
75909
76266
|
|
|
75910
76267
|
// runtime-metrics.ts
|
|
75911
|
-
import { mkdirSync as
|
|
75912
|
-
import { dirname as dirname12, join as
|
|
76268
|
+
import { mkdirSync as mkdirSync28, appendFileSync as appendFileSync5 } from "node:fs";
|
|
76269
|
+
import { dirname as dirname12, join as join31 } from "node:path";
|
|
75913
76270
|
function resolveJsonlPath2() {
|
|
75914
76271
|
const override = process.env.SWITCHROOM_RUNTIME_METRICS_PATH;
|
|
75915
76272
|
if (override && override.trim() !== "")
|
|
75916
76273
|
return override.trim();
|
|
75917
76274
|
const base = process.env.SWITCHROOM_RUNTIME_STATE_DIR ?? "/state/agent";
|
|
75918
|
-
return
|
|
76275
|
+
return join31(base, "runtime-metrics.jsonl");
|
|
75919
76276
|
}
|
|
75920
76277
|
function appendJsonl2(line) {
|
|
75921
76278
|
const path2 = resolveJsonlPath2();
|
|
75922
76279
|
try {
|
|
75923
|
-
|
|
75924
|
-
|
|
76280
|
+
mkdirSync28(dirname12(path2), { recursive: true });
|
|
76281
|
+
appendFileSync5(path2, line + `
|
|
75925
76282
|
`, "utf-8");
|
|
75926
76283
|
} catch (err) {
|
|
75927
76284
|
process.stderr.write(`runtime-metrics: jsonl write failed: ${err.message}
|
|
@@ -75942,9 +76299,6 @@ function emitRuntimeMetric2(event) {
|
|
|
75942
76299
|
captureEvent(event.kind, { ...event, ts: wrapped.ts });
|
|
75943
76300
|
}
|
|
75944
76301
|
|
|
75945
|
-
// gateway/stream-render.ts
|
|
75946
|
-
init_text_voice_scrub();
|
|
75947
|
-
|
|
75948
76302
|
// gateway/feed-open-gate.ts
|
|
75949
76303
|
function shouldEarlyOpenLiveness(input) {
|
|
75950
76304
|
if (!input.enabled)
|
|
@@ -76414,28 +76768,28 @@ function detectStatusSurfaceDegraded(t) {
|
|
|
76414
76768
|
// gateway/turn-active-marker.ts
|
|
76415
76769
|
import {
|
|
76416
76770
|
closeSync as closeSync5,
|
|
76417
|
-
existsSync as
|
|
76418
|
-
mkdirSync as
|
|
76771
|
+
existsSync as existsSync28,
|
|
76772
|
+
mkdirSync as mkdirSync29,
|
|
76419
76773
|
openSync as openSync5,
|
|
76420
|
-
readFileSync as
|
|
76421
|
-
statSync as
|
|
76422
|
-
unlinkSync as
|
|
76774
|
+
readFileSync as readFileSync26,
|
|
76775
|
+
statSync as statSync11,
|
|
76776
|
+
unlinkSync as unlinkSync14,
|
|
76423
76777
|
utimesSync,
|
|
76424
|
-
writeFileSync as
|
|
76778
|
+
writeFileSync as writeFileSync24
|
|
76425
76779
|
} from "node:fs";
|
|
76426
|
-
import { join as
|
|
76780
|
+
import { join as join32 } from "node:path";
|
|
76427
76781
|
var TURN_ACTIVE_MARKER_FILE = "turn-active.json";
|
|
76428
76782
|
var TURN_ACTIVE_HARD_TTL_MS = 10 * 60000;
|
|
76429
76783
|
function writeTurnActiveMarker(stateDir, marker) {
|
|
76430
76784
|
try {
|
|
76431
|
-
|
|
76432
|
-
|
|
76785
|
+
mkdirSync29(stateDir, { recursive: true });
|
|
76786
|
+
writeFileSync24(join32(stateDir, TURN_ACTIVE_MARKER_FILE), JSON.stringify(marker, null, 2) + `
|
|
76433
76787
|
`, { mode: 384 });
|
|
76434
76788
|
} catch {}
|
|
76435
76789
|
}
|
|
76436
76790
|
function touchTurnActiveMarker(stateDir) {
|
|
76437
|
-
const path2 =
|
|
76438
|
-
if (!
|
|
76791
|
+
const path2 = join32(stateDir, TURN_ACTIVE_MARKER_FILE);
|
|
76792
|
+
if (!existsSync28(path2))
|
|
76439
76793
|
return;
|
|
76440
76794
|
const now = new Date;
|
|
76441
76795
|
try {
|
|
@@ -76449,13 +76803,13 @@ function touchTurnActiveMarker(stateDir) {
|
|
|
76449
76803
|
}
|
|
76450
76804
|
function removeTurnActiveMarker(stateDir) {
|
|
76451
76805
|
try {
|
|
76452
|
-
|
|
76806
|
+
unlinkSync14(join32(stateDir, TURN_ACTIVE_MARKER_FILE));
|
|
76453
76807
|
} catch {}
|
|
76454
76808
|
}
|
|
76455
76809
|
function readTurnActiveMarkerAgeMs(stateDir, now) {
|
|
76456
|
-
const path2 =
|
|
76810
|
+
const path2 = join32(stateDir, TURN_ACTIVE_MARKER_FILE);
|
|
76457
76811
|
try {
|
|
76458
|
-
const st =
|
|
76812
|
+
const st = statSync11(path2);
|
|
76459
76813
|
return (now ?? Date.now()) - st.mtimeMs;
|
|
76460
76814
|
} catch {
|
|
76461
76815
|
return null;
|
|
@@ -76512,7 +76866,7 @@ function finalizeBackstopSendGated(turn, send) {
|
|
|
76512
76866
|
var MAX_LIFETIME_MS2 = 30 * 60000;
|
|
76513
76867
|
var stateByKey2 = new Map;
|
|
76514
76868
|
var activeDeps3 = null;
|
|
76515
|
-
function
|
|
76869
|
+
function enabled6() {
|
|
76516
76870
|
const v = process.env.SWITCHROOM_DISABLE_PENDING_PROGRESS;
|
|
76517
76871
|
return !(v === "1" || v === "true");
|
|
76518
76872
|
}
|
|
@@ -76520,7 +76874,7 @@ function nowMs2() {
|
|
|
76520
76874
|
return activeDeps3?.nowMs ? activeDeps3.nowMs() : Date.now();
|
|
76521
76875
|
}
|
|
76522
76876
|
function noteTurnEnd2(key) {
|
|
76523
|
-
if (!
|
|
76877
|
+
if (!enabled6())
|
|
76524
76878
|
return;
|
|
76525
76879
|
const s = stateByKey2.get(key);
|
|
76526
76880
|
if (s == null)
|
|
@@ -77231,24 +77585,18 @@ function handleSessionEvent(deps, ev) {
|
|
|
77231
77585
|
return;
|
|
77232
77586
|
}
|
|
77233
77587
|
if (turnEndDecision === "flush" && flushDecision.kind === "flush") {
|
|
77234
|
-
let capturedText = flushDecision.text;
|
|
77235
|
-
capturedText = normalizeParagraphBreaks(repairEscapedWhitespace(capturedText));
|
|
77236
77588
|
const backstopChatId = chatId;
|
|
77237
77589
|
const backstopThreadId = threadId;
|
|
77238
77590
|
const backstopCtrl = ctrl;
|
|
77239
|
-
|
|
77240
|
-
capturedText =
|
|
77241
|
-
{
|
|
77242
|
-
|
|
77243
|
-
|
|
77244
|
-
|
|
77245
|
-
|
|
77246
|
-
|
|
77247
|
-
|
|
77248
|
-
replaced: scrub.replaced,
|
|
77249
|
-
site: "turn_flush"
|
|
77250
|
-
});
|
|
77251
|
-
}
|
|
77591
|
+
const _flushNorm = normalizeOutboundBody2(flushDecision.text, "turn_flush", redactOutboundText, { tz: resolveEnvTimezone(), nowMs: Date.now() });
|
|
77592
|
+
let capturedText = _flushNorm.text;
|
|
77593
|
+
if (_flushNorm.voiceReplaced > 0) {
|
|
77594
|
+
emitRuntimeMetric2({
|
|
77595
|
+
kind: "voice_scrub_applied",
|
|
77596
|
+
chatKey: statusKey(backstopChatId, backstopThreadId),
|
|
77597
|
+
replaced: _flushNorm.voiceReplaced,
|
|
77598
|
+
site: "turn_flush"
|
|
77599
|
+
});
|
|
77252
77600
|
}
|
|
77253
77601
|
turn.finalAnswerDelivered = true;
|
|
77254
77602
|
turn.finalAnswerSubstantive = true;
|
|
@@ -78346,19 +78694,19 @@ function resolveAgentDirFromEnv() {
|
|
|
78346
78694
|
}
|
|
78347
78695
|
|
|
78348
78696
|
// active-reactions.ts
|
|
78349
|
-
import { readFileSync as
|
|
78350
|
-
import { join as
|
|
78697
|
+
import { readFileSync as readFileSync27, writeFileSync as writeFileSync25, renameSync as renameSync9, existsSync as existsSync29, unlinkSync as unlinkSync15 } from "node:fs";
|
|
78698
|
+
import { join as join33 } from "node:path";
|
|
78351
78699
|
var ACTIVE_REACTIONS_FILENAME = ".active-reactions.json";
|
|
78352
78700
|
function reactionsPath(agentDir) {
|
|
78353
|
-
return
|
|
78701
|
+
return join33(agentDir, ACTIVE_REACTIONS_FILENAME);
|
|
78354
78702
|
}
|
|
78355
78703
|
function readActiveReactions(agentDir) {
|
|
78356
78704
|
const p = reactionsPath(agentDir);
|
|
78357
|
-
if (!
|
|
78705
|
+
if (!existsSync29(p))
|
|
78358
78706
|
return [];
|
|
78359
78707
|
let raw;
|
|
78360
78708
|
try {
|
|
78361
|
-
raw =
|
|
78709
|
+
raw = readFileSync27(p, "utf-8");
|
|
78362
78710
|
} catch {
|
|
78363
78711
|
return [];
|
|
78364
78712
|
}
|
|
@@ -78384,15 +78732,15 @@ function writeActiveReactions(agentDir, reactions) {
|
|
|
78384
78732
|
const p = reactionsPath(agentDir);
|
|
78385
78733
|
if (reactions.length === 0) {
|
|
78386
78734
|
try {
|
|
78387
|
-
|
|
78735
|
+
unlinkSync15(p);
|
|
78388
78736
|
} catch {}
|
|
78389
78737
|
return;
|
|
78390
78738
|
}
|
|
78391
78739
|
const tmp = `${p}.tmp-${process.pid}-${Date.now()}`;
|
|
78392
78740
|
try {
|
|
78393
|
-
|
|
78741
|
+
writeFileSync25(tmp, JSON.stringify(reactions) + `
|
|
78394
78742
|
`, "utf-8");
|
|
78395
|
-
|
|
78743
|
+
renameSync9(tmp, p);
|
|
78396
78744
|
} catch {}
|
|
78397
78745
|
}
|
|
78398
78746
|
function addActiveReaction(agentDir, reaction) {
|
|
@@ -78409,24 +78757,24 @@ function removeActiveReaction(agentDir, chatId, messageId) {
|
|
|
78409
78757
|
}
|
|
78410
78758
|
function clearActiveReactions(agentDir) {
|
|
78411
78759
|
try {
|
|
78412
|
-
|
|
78760
|
+
unlinkSync15(reactionsPath(agentDir));
|
|
78413
78761
|
} catch {}
|
|
78414
78762
|
}
|
|
78415
78763
|
|
|
78416
78764
|
// active-reactions.ts
|
|
78417
|
-
import { readFileSync as
|
|
78418
|
-
import { join as
|
|
78765
|
+
import { readFileSync as readFileSync28, writeFileSync as writeFileSync26, renameSync as renameSync10, existsSync as existsSync30, unlinkSync as unlinkSync16 } from "node:fs";
|
|
78766
|
+
import { join as join34 } from "node:path";
|
|
78419
78767
|
var ACTIVE_REACTIONS_FILENAME2 = ".active-reactions.json";
|
|
78420
78768
|
function reactionsPath2(agentDir) {
|
|
78421
|
-
return
|
|
78769
|
+
return join34(agentDir, ACTIVE_REACTIONS_FILENAME2);
|
|
78422
78770
|
}
|
|
78423
78771
|
function readActiveReactions2(agentDir) {
|
|
78424
78772
|
const p = reactionsPath2(agentDir);
|
|
78425
|
-
if (!
|
|
78773
|
+
if (!existsSync30(p))
|
|
78426
78774
|
return [];
|
|
78427
78775
|
let raw;
|
|
78428
78776
|
try {
|
|
78429
|
-
raw =
|
|
78777
|
+
raw = readFileSync28(p, "utf-8");
|
|
78430
78778
|
} catch {
|
|
78431
78779
|
return [];
|
|
78432
78780
|
}
|
|
@@ -78450,7 +78798,7 @@ function readActiveReactions2(agentDir) {
|
|
|
78450
78798
|
}
|
|
78451
78799
|
function clearActiveReactions2(agentDir) {
|
|
78452
78800
|
try {
|
|
78453
|
-
|
|
78801
|
+
unlinkSync16(reactionsPath2(agentDir));
|
|
78454
78802
|
} catch {}
|
|
78455
78803
|
}
|
|
78456
78804
|
|
|
@@ -78871,7 +79219,7 @@ async function loadFromAuthBroker(options = {}) {
|
|
|
78871
79219
|
}
|
|
78872
79220
|
|
|
78873
79221
|
// gateway/folder-picker-handler.ts
|
|
78874
|
-
var
|
|
79222
|
+
var import_grammy12 = __toESM(require_mod2(), 1);
|
|
78875
79223
|
|
|
78876
79224
|
// ../src/drive/folder-picker.ts
|
|
78877
79225
|
var DRIVE_ID_RE2 = /^[A-Za-z0-9_-]+$/;
|
|
@@ -79190,7 +79538,7 @@ async function recordGrantAndConfirm(ctx, deps, parsed) {
|
|
|
79190
79538
|
const confirmText = `\u2705 Granted ${deps.agentName} access to folder ${parsed.folder_id}
|
|
79191
79539
|
` + `Scope: \`${scope}\`
|
|
79192
79540
|
` + `Revoke with: \`/approvals revoke ${decisionId}\``;
|
|
79193
|
-
const kb = new
|
|
79541
|
+
const kb = new import_grammy12.InlineKeyboard().url("\uD83D\uDCD6 Open in Drive", `https://drive.google.com/drive/folders/${parsed.folder_id}`);
|
|
79194
79542
|
try {
|
|
79195
79543
|
await ctx.editMessageText({ markdown: confirmText }, {
|
|
79196
79544
|
reply_markup: kb
|
|
@@ -79199,7 +79547,7 @@ async function recordGrantAndConfirm(ctx, deps, parsed) {
|
|
|
79199
79547
|
await ctx.answerCallbackQuery({ text: "Allowed" });
|
|
79200
79548
|
}
|
|
79201
79549
|
function toKeyboard(spec) {
|
|
79202
|
-
const kb = new
|
|
79550
|
+
const kb = new import_grammy12.InlineKeyboard;
|
|
79203
79551
|
for (let r = 0;r < spec.rows.length; r++) {
|
|
79204
79552
|
const row = spec.rows[r];
|
|
79205
79553
|
for (const btn of row) {
|
|
@@ -79294,17 +79642,17 @@ async function approvalRecord(args, opts) {
|
|
|
79294
79642
|
}
|
|
79295
79643
|
|
|
79296
79644
|
// quota-check.ts
|
|
79297
|
-
import { readFileSync as
|
|
79298
|
-
import { join as
|
|
79645
|
+
import { readFileSync as readFileSync29, existsSync as existsSync31 } from "fs";
|
|
79646
|
+
import { join as join35 } from "path";
|
|
79299
79647
|
var OAUTH_BETA2 = "oauth-2025-04-20";
|
|
79300
79648
|
var DEFAULT_USER_AGENT2 = "claude-cli/1.0.0 (external, cli)";
|
|
79301
79649
|
var DEFAULT_PROBE_MODEL2 = "claude-haiku-4-5-20251001";
|
|
79302
79650
|
function readOauthToken2(claudeConfigDir) {
|
|
79303
|
-
const tokenFile =
|
|
79304
|
-
if (!
|
|
79651
|
+
const tokenFile = join35(claudeConfigDir, ".oauth-token");
|
|
79652
|
+
if (!existsSync31(tokenFile))
|
|
79305
79653
|
return null;
|
|
79306
79654
|
try {
|
|
79307
|
-
const raw =
|
|
79655
|
+
const raw = readFileSync29(tokenFile, "utf-8").trim();
|
|
79308
79656
|
return raw.length > 0 ? raw : null;
|
|
79309
79657
|
} catch {
|
|
79310
79658
|
return null;
|
|
@@ -80640,15 +80988,15 @@ async function menuWithBannerStatic(deps, banner) {
|
|
|
80640
80988
|
}
|
|
80641
80989
|
|
|
80642
80990
|
// gateway/session-model-file.ts
|
|
80643
|
-
import { readFileSync as
|
|
80644
|
-
import { join as
|
|
80991
|
+
import { readFileSync as readFileSync30, writeFileSync as writeFileSync27, renameSync as renameSync11, rmSync as rmSync4 } from "node:fs";
|
|
80992
|
+
import { join as join36 } from "node:path";
|
|
80645
80993
|
var SESSION_MODEL_FILE = ".session-model";
|
|
80646
80994
|
var CONFIGURED_DEFAULT_MODEL_FILE = ".configured-default-model";
|
|
80647
80995
|
var SESSION_MODEL_BOOT_ATTEMPTS_FILE = ".session-model-boot-attempts";
|
|
80648
80996
|
function atomicWrite(path2, content3) {
|
|
80649
80997
|
const tmp = `${path2}.tmp-${process.pid}-${Date.now()}`;
|
|
80650
|
-
|
|
80651
|
-
|
|
80998
|
+
writeFileSync27(tmp, content3, "utf8");
|
|
80999
|
+
renameSync11(tmp, path2);
|
|
80652
81000
|
}
|
|
80653
81001
|
function serializeSessionModel(rec) {
|
|
80654
81002
|
return `${JSON.stringify({
|
|
@@ -80663,28 +81011,28 @@ function writeSessionModelFile(agentDir, model, configuredDefaultAtWrite) {
|
|
|
80663
81011
|
throw new Error(`refusing to persist non-canonical session model token: ${JSON.stringify(model)}`);
|
|
80664
81012
|
}
|
|
80665
81013
|
try {
|
|
80666
|
-
rmSync4(
|
|
81014
|
+
rmSync4(join36(agentDir, SESSION_MODEL_BOOT_ATTEMPTS_FILE), { force: true });
|
|
80667
81015
|
} catch {}
|
|
80668
|
-
atomicWrite(
|
|
81016
|
+
atomicWrite(join36(agentDir, SESSION_MODEL_FILE), serializeSessionModel({ model, configuredDefaultAtWrite, ts: Date.now() }));
|
|
80669
81017
|
}
|
|
80670
81018
|
function readSessionModelFileRaw(agentDir) {
|
|
80671
81019
|
try {
|
|
80672
|
-
return
|
|
81020
|
+
return readFileSync30(join36(agentDir, SESSION_MODEL_FILE), "utf8");
|
|
80673
81021
|
} catch {
|
|
80674
81022
|
return null;
|
|
80675
81023
|
}
|
|
80676
81024
|
}
|
|
80677
81025
|
function clearSessionModelFile(agentDir) {
|
|
80678
81026
|
try {
|
|
80679
|
-
rmSync4(
|
|
81027
|
+
rmSync4(join36(agentDir, SESSION_MODEL_FILE), { force: true });
|
|
80680
81028
|
} catch {}
|
|
80681
81029
|
}
|
|
80682
81030
|
function consumeSessionModelCarrierOnHealthyBoot(agentDir) {
|
|
80683
81031
|
try {
|
|
80684
|
-
rmSync4(
|
|
81032
|
+
rmSync4(join36(agentDir, SESSION_MODEL_FILE), { force: true });
|
|
80685
81033
|
} catch {}
|
|
80686
81034
|
try {
|
|
80687
|
-
rmSync4(
|
|
81035
|
+
rmSync4(join36(agentDir, SESSION_MODEL_BOOT_ATTEMPTS_FILE), { force: true });
|
|
80688
81036
|
} catch {}
|
|
80689
81037
|
}
|
|
80690
81038
|
function restoreSessionModelFileRaw(agentDir, raw) {
|
|
@@ -80693,12 +81041,12 @@ function restoreSessionModelFileRaw(agentDir, raw) {
|
|
|
80693
81041
|
return;
|
|
80694
81042
|
}
|
|
80695
81043
|
try {
|
|
80696
|
-
atomicWrite(
|
|
81044
|
+
atomicWrite(join36(agentDir, SESSION_MODEL_FILE), raw);
|
|
80697
81045
|
} catch {}
|
|
80698
81046
|
}
|
|
80699
81047
|
function readConfiguredDefaultModel(agentDir) {
|
|
80700
81048
|
try {
|
|
80701
|
-
const v =
|
|
81049
|
+
const v = readFileSync30(join36(agentDir, CONFIGURED_DEFAULT_MODEL_FILE), "utf8").trim();
|
|
80702
81050
|
return v.length > 0 ? v : null;
|
|
80703
81051
|
} catch {
|
|
80704
81052
|
return null;
|
|
@@ -80710,12 +81058,12 @@ function writeSessionEffortFile(agentDir, level, configuredDefaultAtWrite) {
|
|
|
80710
81058
|
if (!EFFORT_LEVEL_RE.test(level)) {
|
|
80711
81059
|
throw new Error(`refusing to persist non-allowlisted effort level: ${JSON.stringify(level)}`);
|
|
80712
81060
|
}
|
|
80713
|
-
atomicWrite(
|
|
81061
|
+
atomicWrite(join36(agentDir, SESSION_EFFORT_FILE), `${JSON.stringify({ level, configuredDefaultAtWrite: configuredDefaultAtWrite ?? "", ts: Date.now() })}
|
|
80714
81062
|
`);
|
|
80715
81063
|
}
|
|
80716
81064
|
function clearSessionEffortFile(agentDir) {
|
|
80717
81065
|
try {
|
|
80718
|
-
rmSync4(
|
|
81066
|
+
rmSync4(join36(agentDir, SESSION_EFFORT_FILE), { force: true });
|
|
80719
81067
|
} catch {}
|
|
80720
81068
|
}
|
|
80721
81069
|
var PREMIUM_RECOVERY_FILE = ".premium-recovery";
|
|
@@ -80738,13 +81086,13 @@ function writePremiumRecoveryFile(agentDir, premiumModel, chats) {
|
|
|
80738
81086
|
if (clean.length === 0) {
|
|
80739
81087
|
throw new Error("refusing to persist premium-recovery marker with no chats to notify");
|
|
80740
81088
|
}
|
|
80741
|
-
atomicWrite(
|
|
81089
|
+
atomicWrite(join36(agentDir, PREMIUM_RECOVERY_FILE), `${JSON.stringify({ premiumModel, chats: clean, ts: Date.now() })}
|
|
80742
81090
|
`);
|
|
80743
81091
|
}
|
|
80744
81092
|
function readPremiumRecoveryFile(agentDir) {
|
|
80745
81093
|
let raw;
|
|
80746
81094
|
try {
|
|
80747
|
-
raw =
|
|
81095
|
+
raw = readFileSync30(join36(agentDir, PREMIUM_RECOVERY_FILE), "utf8");
|
|
80748
81096
|
} catch {
|
|
80749
81097
|
return null;
|
|
80750
81098
|
}
|
|
@@ -80757,7 +81105,7 @@ function readPremiumRecoveryFile(agentDir) {
|
|
|
80757
81105
|
}
|
|
80758
81106
|
function clearPremiumRecoveryFile(agentDir) {
|
|
80759
81107
|
try {
|
|
80760
|
-
rmSync4(
|
|
81108
|
+
rmSync4(join36(agentDir, PREMIUM_RECOVERY_FILE), { force: true });
|
|
80761
81109
|
} catch {}
|
|
80762
81110
|
}
|
|
80763
81111
|
|
|
@@ -81046,7 +81394,7 @@ async function discoverModels(agentName3, opts = {}) {
|
|
|
81046
81394
|
}
|
|
81047
81395
|
|
|
81048
81396
|
// ../src/agents/scaffold.ts
|
|
81049
|
-
import { dirname as dirname14, isAbsolute, join as
|
|
81397
|
+
import { dirname as dirname14, isAbsolute, join as join39, relative, resolve as resolve8 } from "node:path";
|
|
81050
81398
|
init_atomic();
|
|
81051
81399
|
|
|
81052
81400
|
// ../src/agents/agent-uid.ts
|
|
@@ -81063,8 +81411,8 @@ init_merge();
|
|
|
81063
81411
|
init_timezone();
|
|
81064
81412
|
|
|
81065
81413
|
// ../src/cli/agent-config.ts
|
|
81066
|
-
import { join as
|
|
81067
|
-
import { homedir as
|
|
81414
|
+
import { join as join37 } from "node:path";
|
|
81415
|
+
import { homedir as homedir12 } from "node:os";
|
|
81068
81416
|
|
|
81069
81417
|
// ../src/cli/helpers.ts
|
|
81070
81418
|
init_loader();
|
|
@@ -81084,12 +81432,12 @@ var WEBKITE_VAULT_KEYS = new Set([
|
|
|
81084
81432
|
init_overlay_loader();
|
|
81085
81433
|
|
|
81086
81434
|
// ../src/cli/agent-config.ts
|
|
81087
|
-
var AUDIT_ROOT =
|
|
81435
|
+
var AUDIT_ROOT = join37(homedir12(), ".switchroom", "audit");
|
|
81088
81436
|
|
|
81089
81437
|
// ../src/agents/profiles.ts
|
|
81090
81438
|
var import_handlebars = __toESM(require_lib(), 1);
|
|
81091
|
-
import { readFileSync as
|
|
81092
|
-
import { resolve as resolve7, join as
|
|
81439
|
+
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";
|
|
81440
|
+
import { resolve as resolve7, join as join38, sep as pathSep } from "node:path";
|
|
81093
81441
|
function resolveProfilesRoot() {
|
|
81094
81442
|
const envOverride = process.env.SWITCHROOM_PROFILES_ROOT?.trim();
|
|
81095
81443
|
if (envOverride) {
|
|
@@ -81100,7 +81448,7 @@ function resolveProfilesRoot() {
|
|
|
81100
81448
|
resolve7(import.meta.dirname, "profiles")
|
|
81101
81449
|
];
|
|
81102
81450
|
for (const candidate of candidates) {
|
|
81103
|
-
if (
|
|
81451
|
+
if (existsSync32(candidate)) {
|
|
81104
81452
|
return candidate;
|
|
81105
81453
|
}
|
|
81106
81454
|
}
|
|
@@ -81116,9 +81464,9 @@ import_handlebars.default.registerHelper("isNumber", (value) => {
|
|
|
81116
81464
|
var SHARED_FRAGMENTS_DIR = resolve7(PROFILES_ROOT, "_shared");
|
|
81117
81465
|
var SHARED_FRAGMENTS = ["vault-protocol", "agent-self-service", "execution-discipline", "reply-discipline", "dev-protocol"];
|
|
81118
81466
|
for (const name of SHARED_FRAGMENTS) {
|
|
81119
|
-
const fragPath =
|
|
81120
|
-
if (
|
|
81121
|
-
import_handlebars.default.registerPartial(name,
|
|
81467
|
+
const fragPath = join38(SHARED_FRAGMENTS_DIR, `${name}.md.hbs`);
|
|
81468
|
+
if (existsSync32(fragPath)) {
|
|
81469
|
+
import_handlebars.default.registerPartial(name, readFileSync31(fragPath, "utf-8"));
|
|
81122
81470
|
}
|
|
81123
81471
|
}
|
|
81124
81472
|
|
|
@@ -81725,8 +82073,8 @@ init_overlay_loader();
|
|
|
81725
82073
|
init_merge();
|
|
81726
82074
|
init_timezone();
|
|
81727
82075
|
var import_yaml4 = __toESM(require_dist(), 1);
|
|
81728
|
-
import { readFileSync as
|
|
81729
|
-
import { homedir as
|
|
82076
|
+
import { readFileSync as readFileSync32, existsSync as existsSync33 } from "node:fs";
|
|
82077
|
+
import { homedir as homedir13 } from "node:os";
|
|
81730
82078
|
import { resolve as resolve9 } from "node:path";
|
|
81731
82079
|
|
|
81732
82080
|
class ConfigError2 extends Error {
|
|
@@ -81783,7 +82131,7 @@ function coerceLegacyGoogleWorkspaceKeys2(parsed, filePath) {
|
|
|
81783
82131
|
}
|
|
81784
82132
|
function findConfigFile2(startDir) {
|
|
81785
82133
|
const envPath = process.env.SWITCHROOM_CONFIG;
|
|
81786
|
-
const home2 =
|
|
82134
|
+
const home2 = homedir13();
|
|
81787
82135
|
const userDir = resolve9(home2, ".switchroom");
|
|
81788
82136
|
const searchPaths = [
|
|
81789
82137
|
envPath ? resolve9(envPath) : null,
|
|
@@ -81801,7 +82149,7 @@ function findConfigFile2(startDir) {
|
|
|
81801
82149
|
resolve9(userDir, "clerk.yml")
|
|
81802
82150
|
].filter(Boolean);
|
|
81803
82151
|
for (const path2 of searchPaths) {
|
|
81804
|
-
if (
|
|
82152
|
+
if (existsSync33(path2)) {
|
|
81805
82153
|
return path2;
|
|
81806
82154
|
}
|
|
81807
82155
|
}
|
|
@@ -81809,12 +82157,12 @@ function findConfigFile2(startDir) {
|
|
|
81809
82157
|
}
|
|
81810
82158
|
function loadConfig2(configPath) {
|
|
81811
82159
|
const filePath = configPath ?? findConfigFile2();
|
|
81812
|
-
if (!
|
|
82160
|
+
if (!existsSync33(filePath)) {
|
|
81813
82161
|
throw new ConfigError2(`Config file not found: ${filePath}`);
|
|
81814
82162
|
}
|
|
81815
82163
|
let raw;
|
|
81816
82164
|
try {
|
|
81817
|
-
raw =
|
|
82165
|
+
raw = readFileSync32(filePath, "utf-8");
|
|
81818
82166
|
} catch (err) {
|
|
81819
82167
|
throw new ConfigError2(`Failed to read config file: ${filePath}`, [
|
|
81820
82168
|
` ${err.message}`
|
|
@@ -82344,15 +82692,15 @@ function topicForRecipient(args) {
|
|
|
82344
82692
|
}
|
|
82345
82693
|
|
|
82346
82694
|
// ../src/agents/perf.ts
|
|
82347
|
-
import { existsSync as
|
|
82695
|
+
import { existsSync as existsSync34, readFileSync as readFileSync33 } from "node:fs";
|
|
82348
82696
|
function readTurnUsages(jsonlPath, lastN) {
|
|
82349
|
-
if (!
|
|
82697
|
+
if (!existsSync34(jsonlPath))
|
|
82350
82698
|
return [];
|
|
82351
82699
|
if (lastN <= 0)
|
|
82352
82700
|
return [];
|
|
82353
82701
|
let raw;
|
|
82354
82702
|
try {
|
|
82355
|
-
raw =
|
|
82703
|
+
raw = readFileSync33(jsonlPath, "utf-8");
|
|
82356
82704
|
} catch {
|
|
82357
82705
|
return [];
|
|
82358
82706
|
}
|
|
@@ -82443,8 +82791,8 @@ function numField(obj, key) {
|
|
|
82443
82791
|
}
|
|
82444
82792
|
|
|
82445
82793
|
// gateway/context-occupancy.ts
|
|
82446
|
-
import { mkdirSync as
|
|
82447
|
-
import { join as
|
|
82794
|
+
import { mkdirSync as mkdirSync31, writeFileSync as writeFileSync29 } from "node:fs";
|
|
82795
|
+
import { join as join40 } from "node:path";
|
|
82448
82796
|
var CONTEXT_OCCUPANCY_FILENAME = "context-occupancy.json";
|
|
82449
82797
|
var TIGHT_FRACTION = 0.8;
|
|
82450
82798
|
function buildContextOccupancy(occupancy, cap, now) {
|
|
@@ -82467,9 +82815,9 @@ function buildContextOccupancy(occupancy, cap, now) {
|
|
|
82467
82815
|
}
|
|
82468
82816
|
function writeContextOccupancySnapshot(stateDir, snapshot, deps) {
|
|
82469
82817
|
try {
|
|
82470
|
-
const path2 =
|
|
82471
|
-
(deps?.mkdir ?? ((p, o) =>
|
|
82472
|
-
(deps?.writeFile ?? ((p, d) =>
|
|
82818
|
+
const path2 = join40(stateDir, CONTEXT_OCCUPANCY_FILENAME);
|
|
82819
|
+
(deps?.mkdir ?? ((p, o) => mkdirSync31(p, o)))(stateDir, { recursive: true });
|
|
82820
|
+
(deps?.writeFile ?? ((p, d) => writeFileSync29(p, d)))(path2, JSON.stringify(snapshot, null, 2) + `
|
|
82473
82821
|
`);
|
|
82474
82822
|
} catch {}
|
|
82475
82823
|
}
|
|
@@ -82623,7 +82971,7 @@ function nextCompactNotify(state6, ev) {
|
|
|
82623
82971
|
}
|
|
82624
82972
|
|
|
82625
82973
|
// gateway/hostd-dispatch.ts
|
|
82626
|
-
import { existsSync as
|
|
82974
|
+
import { existsSync as existsSync35 } from "node:fs";
|
|
82627
82975
|
import { randomBytes as randomBytes7 } from "node:crypto";
|
|
82628
82976
|
init_loader();
|
|
82629
82977
|
var _hostdEnabled2;
|
|
@@ -82647,13 +82995,13 @@ function hostdSocketPath2(agentName3) {
|
|
|
82647
82995
|
function hostdWillBeUsed2(agentName3) {
|
|
82648
82996
|
if (!isHostdEnabled2())
|
|
82649
82997
|
return false;
|
|
82650
|
-
return
|
|
82998
|
+
return existsSync35(hostdSocketPath2(agentName3));
|
|
82651
82999
|
}
|
|
82652
83000
|
async function tryHostdDispatch2(agentName3, req, timeoutMs = 5000) {
|
|
82653
83001
|
if (!isHostdEnabled2())
|
|
82654
83002
|
return "not-configured";
|
|
82655
83003
|
const sockPath = hostdSocketPath2(agentName3);
|
|
82656
|
-
if (!
|
|
83004
|
+
if (!existsSync35(sockPath))
|
|
82657
83005
|
return "not-configured";
|
|
82658
83006
|
try {
|
|
82659
83007
|
return await hostdRequest({ socketPath: sockPath, timeoutMs }, req);
|
|
@@ -82682,7 +83030,7 @@ async function pollHostdStatus(agentName3, targetRequestId, opts) {
|
|
|
82682
83030
|
if (!isHostdEnabled2())
|
|
82683
83031
|
return "not-configured";
|
|
82684
83032
|
const sockPath = hostdSocketPath2(agentName3);
|
|
82685
|
-
if (!
|
|
83033
|
+
if (!existsSync35(sockPath))
|
|
82686
83034
|
return "not-configured";
|
|
82687
83035
|
const now = opts.now ?? Date.now;
|
|
82688
83036
|
const sleep2 = opts.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
|
|
@@ -82801,7 +83149,7 @@ function createDmPinSweeper(deps) {
|
|
|
82801
83149
|
|
|
82802
83150
|
// gateway/webhook-ingest-server.ts
|
|
82803
83151
|
import net4 from "node:net";
|
|
82804
|
-
import { chmodSync as chmodSync10, existsSync as
|
|
83152
|
+
import { chmodSync as chmodSync10, existsSync as existsSync36, unlinkSync as unlinkSync17 } from "node:fs";
|
|
82805
83153
|
var MAX_REQUEST_BYTES = 1024 * 1024;
|
|
82806
83154
|
function fdOf(conn) {
|
|
82807
83155
|
const handle = conn._handle;
|
|
@@ -82813,8 +83161,8 @@ function startWebhookIngestServer(opts) {
|
|
|
82813
83161
|
const log = opts.log ?? ((s) => process.stderr.write(s));
|
|
82814
83162
|
const allowed = new Set(opts.allowedUids);
|
|
82815
83163
|
try {
|
|
82816
|
-
if (
|
|
82817
|
-
|
|
83164
|
+
if (existsSync36(opts.socketPath))
|
|
83165
|
+
unlinkSync17(opts.socketPath);
|
|
82818
83166
|
} catch (err) {
|
|
82819
83167
|
log(`webhook-ingest-server: could not unlink stale socket: ${err.message}
|
|
82820
83168
|
`);
|
|
@@ -82906,28 +83254,28 @@ function startWebhookIngestServer(opts) {
|
|
|
82906
83254
|
server.close();
|
|
82907
83255
|
} catch {}
|
|
82908
83256
|
try {
|
|
82909
|
-
if (
|
|
82910
|
-
|
|
83257
|
+
if (existsSync36(opts.socketPath))
|
|
83258
|
+
unlinkSync17(opts.socketPath);
|
|
82911
83259
|
} catch {}
|
|
82912
83260
|
}
|
|
82913
83261
|
};
|
|
82914
83262
|
}
|
|
82915
83263
|
|
|
82916
83264
|
// ../src/web/webhook-gateway-record.ts
|
|
82917
|
-
import { appendFileSync as
|
|
82918
|
-
import { join as
|
|
82919
|
-
import { homedir as
|
|
83265
|
+
import { appendFileSync as appendFileSync7, mkdirSync as mkdirSync34 } from "fs";
|
|
83266
|
+
import { join as join43 } from "path";
|
|
83267
|
+
import { homedir as homedir15 } from "os";
|
|
82920
83268
|
|
|
82921
83269
|
// ../src/web/webhook-handler.ts
|
|
82922
|
-
import { appendFileSync as
|
|
82923
|
-
import { join as
|
|
83270
|
+
import { appendFileSync as appendFileSync6, existsSync as existsSync37, mkdirSync as mkdirSync32, readFileSync as readFileSync34, writeFileSync as writeFileSync30 } from "fs";
|
|
83271
|
+
import { join as join41 } from "path";
|
|
82924
83272
|
var DEDUP_MAX = 1000;
|
|
82925
83273
|
var DEDUP_TTL_MS = 24 * 60 * 60 * 1000;
|
|
82926
83274
|
function loadDedupFile(path2) {
|
|
82927
83275
|
try {
|
|
82928
|
-
if (!
|
|
83276
|
+
if (!existsSync37(path2))
|
|
82929
83277
|
return {};
|
|
82930
|
-
const raw = JSON.parse(
|
|
83278
|
+
const raw = JSON.parse(readFileSync34(path2, "utf-8"));
|
|
82931
83279
|
return typeof raw.deliveries === "object" && raw.deliveries !== null ? raw.deliveries : {};
|
|
82932
83280
|
} catch {
|
|
82933
83281
|
return {};
|
|
@@ -82941,7 +83289,7 @@ function saveDedupFile(path2, deliveries, now) {
|
|
|
82941
83289
|
}
|
|
82942
83290
|
const sorted = Object.entries(pruned).sort((a, b) => b[1] - a[1]).slice(0, DEDUP_MAX);
|
|
82943
83291
|
const final = Object.fromEntries(sorted);
|
|
82944
|
-
|
|
83292
|
+
writeFileSync30(path2, JSON.stringify({ deliveries: final }), {
|
|
82945
83293
|
mode: 384
|
|
82946
83294
|
});
|
|
82947
83295
|
}
|
|
@@ -82949,8 +83297,8 @@ var agentDedupCache = new Map;
|
|
|
82949
83297
|
function createFileDedupStore(resolveAgentDir) {
|
|
82950
83298
|
return {
|
|
82951
83299
|
check(agent, deliveryId, now) {
|
|
82952
|
-
const telegramDir =
|
|
82953
|
-
const filePath =
|
|
83300
|
+
const telegramDir = join41(resolveAgentDir(agent), "telegram");
|
|
83301
|
+
const filePath = join41(telegramDir, "webhook-dedup.json");
|
|
82954
83302
|
if (!agentDedupCache.has(agent)) {
|
|
82955
83303
|
agentDedupCache.set(agent, loadDedupFile(filePath));
|
|
82956
83304
|
}
|
|
@@ -82960,7 +83308,7 @@ function createFileDedupStore(resolveAgentDir) {
|
|
|
82960
83308
|
}
|
|
82961
83309
|
deliveries[deliveryId] = now;
|
|
82962
83310
|
try {
|
|
82963
|
-
|
|
83311
|
+
mkdirSync32(telegramDir, { recursive: true });
|
|
82964
83312
|
saveDedupFile(filePath, deliveries, now);
|
|
82965
83313
|
} catch {}
|
|
82966
83314
|
return;
|
|
@@ -82971,9 +83319,9 @@ var tokenBuckets = new Map;
|
|
|
82971
83319
|
var throttleIssueWindow = new Map;
|
|
82972
83320
|
|
|
82973
83321
|
// ../src/web/webhook-dispatch.ts
|
|
82974
|
-
import { existsSync as
|
|
82975
|
-
import { join as
|
|
82976
|
-
import { homedir as
|
|
83322
|
+
import { existsSync as existsSync38, mkdirSync as mkdirSync33, readFileSync as readFileSync35, writeFileSync as writeFileSync31 } from "fs";
|
|
83323
|
+
import { join as join42 } from "path";
|
|
83324
|
+
import { homedir as homedir14 } from "os";
|
|
82977
83325
|
|
|
82978
83326
|
// ../src/agent-scheduler/ipc-client.ts
|
|
82979
83327
|
import { createConnection as createConnection2 } from "node:net";
|
|
@@ -83271,9 +83619,9 @@ function cooldownKey(source, eventType, repo, number, ruleIndex) {
|
|
|
83271
83619
|
}
|
|
83272
83620
|
function loadCooldownFile(path2) {
|
|
83273
83621
|
try {
|
|
83274
|
-
if (!
|
|
83622
|
+
if (!existsSync38(path2))
|
|
83275
83623
|
return {};
|
|
83276
|
-
const raw = JSON.parse(
|
|
83624
|
+
const raw = JSON.parse(readFileSync35(path2, "utf-8"));
|
|
83277
83625
|
return typeof raw.dispatches === "object" && raw.dispatches !== null ? raw.dispatches : {};
|
|
83278
83626
|
} catch {
|
|
83279
83627
|
return {};
|
|
@@ -83281,7 +83629,7 @@ function loadCooldownFile(path2) {
|
|
|
83281
83629
|
}
|
|
83282
83630
|
function saveCooldownFile(path2, dispatches) {
|
|
83283
83631
|
try {
|
|
83284
|
-
|
|
83632
|
+
writeFileSync31(path2, JSON.stringify({ dispatches }), {
|
|
83285
83633
|
mode: 384
|
|
83286
83634
|
});
|
|
83287
83635
|
} catch {}
|
|
@@ -83292,8 +83640,8 @@ function createFileCooldownStore(resolveAgentDir) {
|
|
|
83292
83640
|
isCoolingDown(agent, key, cooldownMs, now) {
|
|
83293
83641
|
if (cooldownMs <= 0)
|
|
83294
83642
|
return false;
|
|
83295
|
-
const telegramDir =
|
|
83296
|
-
const filePath =
|
|
83643
|
+
const telegramDir = join42(resolveAgentDir(agent), "telegram");
|
|
83644
|
+
const filePath = join42(telegramDir, "webhook-cooldown.json");
|
|
83297
83645
|
if (!cache.has(agent)) {
|
|
83298
83646
|
cache.set(agent, loadCooldownFile(filePath));
|
|
83299
83647
|
}
|
|
@@ -83304,7 +83652,7 @@ function createFileCooldownStore(resolveAgentDir) {
|
|
|
83304
83652
|
}
|
|
83305
83653
|
dispatches[key] = now;
|
|
83306
83654
|
try {
|
|
83307
|
-
|
|
83655
|
+
mkdirSync33(telegramDir, { recursive: true });
|
|
83308
83656
|
saveCooldownFile(filePath, dispatches);
|
|
83309
83657
|
} catch {}
|
|
83310
83658
|
return false;
|
|
@@ -83350,9 +83698,9 @@ async function defaultInject(socketPath, agentName3, inbound) {
|
|
|
83350
83698
|
}
|
|
83351
83699
|
function injectWebhookInbound(agent, prompt, ctx, deps = {}) {
|
|
83352
83700
|
const log = deps.log ?? ((s) => process.stderr.write(s));
|
|
83353
|
-
const resolveAgentDir = deps.resolveAgentDir ?? ((a) =>
|
|
83701
|
+
const resolveAgentDir = deps.resolveAgentDir ?? ((a) => join42(homedir14(), ".switchroom", "agents", a));
|
|
83354
83702
|
const now = (deps.now ?? Date.now)();
|
|
83355
|
-
const socketPath =
|
|
83703
|
+
const socketPath = join42(resolveAgentDir(agent), "telegram", "gateway.sock");
|
|
83356
83704
|
const inbound = {
|
|
83357
83705
|
type: "inbound",
|
|
83358
83706
|
chatId: ctx.chatId,
|
|
@@ -83424,7 +83772,7 @@ function evaluateDispatch(args, deps = {}) {
|
|
|
83424
83772
|
const log = deps.log ?? ((s) => process.stderr.write(s));
|
|
83425
83773
|
const now = (deps.now ?? Date.now)();
|
|
83426
83774
|
const nowDate = deps.nowDate ?? (() => new Date(now));
|
|
83427
|
-
const resolveAgentDir = deps.resolveAgentDir ?? ((a) =>
|
|
83775
|
+
const resolveAgentDir = deps.resolveAgentDir ?? ((a) => join42(homedir14(), ".switchroom", "agents", a));
|
|
83428
83776
|
const cooldownStore = deps.cooldownStore ?? createFileCooldownStore(resolveAgentDir);
|
|
83429
83777
|
if (!DISPATCH_SOURCES.includes(args.source))
|
|
83430
83778
|
return 0;
|
|
@@ -83502,10 +83850,10 @@ var CONSOLIDATION_COMPLETED_EVENT = "consolidation.completed";
|
|
|
83502
83850
|
function recordWebhookEvent(rec, deps = {}) {
|
|
83503
83851
|
const log = deps.log ?? ((s) => process.stderr.write(s));
|
|
83504
83852
|
const now = rec.ts || (deps.now ?? Date.now)();
|
|
83505
|
-
const resolveAgentDir = deps.resolveAgentDir ?? ((a) =>
|
|
83853
|
+
const resolveAgentDir = deps.resolveAgentDir ?? ((a) => join43(homedir15(), ".switchroom", "agents", a));
|
|
83506
83854
|
const dedupStore = deps.dedupStore ?? createFileDedupStore(resolveAgentDir);
|
|
83507
83855
|
const agent = rec.agent;
|
|
83508
|
-
const telegramDir =
|
|
83856
|
+
const telegramDir = join43(resolveAgentDir(agent), "telegram");
|
|
83509
83857
|
if (rec.source === "github" && rec.delivery_id) {
|
|
83510
83858
|
const originalTs = dedupStore.check(agent, rec.delivery_id, now);
|
|
83511
83859
|
if (originalTs !== undefined) {
|
|
@@ -83514,9 +83862,9 @@ function recordWebhookEvent(rec, deps = {}) {
|
|
|
83514
83862
|
return { status: "deduped", ts: originalTs };
|
|
83515
83863
|
}
|
|
83516
83864
|
}
|
|
83517
|
-
const logPath =
|
|
83865
|
+
const logPath = join43(telegramDir, "webhook-events.jsonl");
|
|
83518
83866
|
try {
|
|
83519
|
-
|
|
83867
|
+
mkdirSync34(telegramDir, { recursive: true });
|
|
83520
83868
|
const record = {
|
|
83521
83869
|
ts: now,
|
|
83522
83870
|
source: rec.source,
|
|
@@ -83524,7 +83872,7 @@ function recordWebhookEvent(rec, deps = {}) {
|
|
|
83524
83872
|
rendered_text: rec.rendered_text,
|
|
83525
83873
|
payload: rec.payload
|
|
83526
83874
|
};
|
|
83527
|
-
|
|
83875
|
+
appendFileSync7(logPath, JSON.stringify(record) + `
|
|
83528
83876
|
`, { mode: 384 });
|
|
83529
83877
|
} catch (err) {
|
|
83530
83878
|
log(`webhook-gateway: agent='${agent}' source='${rec.source}' write failed: ${err.message}
|
|
@@ -83626,7 +83974,7 @@ function recordWebhookEvent(rec, deps = {}) {
|
|
|
83626
83974
|
|
|
83627
83975
|
// gateway/ipc-server.ts
|
|
83628
83976
|
init_format();
|
|
83629
|
-
import { renameSync as
|
|
83977
|
+
import { renameSync as renameSync13, unlinkSync as unlinkSync18, chmodSync as chmodSync11 } from "fs";
|
|
83630
83978
|
var MAX_BUFFER_SIZE = 1024 * 1024;
|
|
83631
83979
|
var VALID_OPERATOR_KINDS = new Set([
|
|
83632
83980
|
"credentials-expired",
|
|
@@ -83835,10 +84183,10 @@ function createIpcServer(options) {
|
|
|
83835
84183
|
heartbeatTimeoutMs = 30000
|
|
83836
84184
|
} = options;
|
|
83837
84185
|
try {
|
|
83838
|
-
|
|
84186
|
+
renameSync13(socketPath, socketPath + ".bak");
|
|
83839
84187
|
} catch {}
|
|
83840
84188
|
try {
|
|
83841
|
-
|
|
84189
|
+
unlinkSync18(socketPath + ".bak");
|
|
83842
84190
|
} catch {}
|
|
83843
84191
|
const clients = new Set;
|
|
83844
84192
|
const agentIndex = new Map;
|
|
@@ -84225,7 +84573,7 @@ function createIpcServer(options) {
|
|
|
84225
84573
|
clientBySocketId.clear();
|
|
84226
84574
|
server.stop(true);
|
|
84227
84575
|
try {
|
|
84228
|
-
|
|
84576
|
+
renameSync13(socketPath, socketPath + ".bak");
|
|
84229
84577
|
} catch {}
|
|
84230
84578
|
}
|
|
84231
84579
|
};
|
|
@@ -84679,7 +85027,7 @@ async function handleRequestMs365Approval(client3, msg, deps) {
|
|
|
84679
85027
|
|
|
84680
85028
|
// gateway/diff-preview-card.ts
|
|
84681
85029
|
init_format();
|
|
84682
|
-
var
|
|
85030
|
+
var import_grammy13 = __toESM(require_mod2(), 1);
|
|
84683
85031
|
var REQUEST_ID_RE = /^[0-9a-f]{32}$/;
|
|
84684
85032
|
var PENDING_FILE_ID_SENTINEL = "pending-create";
|
|
84685
85033
|
function buildDiffPreviewCard(input) {
|
|
@@ -84697,7 +85045,7 @@ function buildDiffPreviewCard(input) {
|
|
|
84697
85045
|
}
|
|
84698
85046
|
const text4 = bodyLines.join(`
|
|
84699
85047
|
`);
|
|
84700
|
-
const kb = new
|
|
85048
|
+
const kb = new import_grammy13.InlineKeyboard;
|
|
84701
85049
|
const ROW_BREAK_AFTER = [
|
|
84702
85050
|
"apply_suggestion"
|
|
84703
85051
|
];
|
|
@@ -88160,27 +88508,27 @@ function skillProposalKeyboard(id) {
|
|
|
88160
88508
|
// ../src/self-improve/skill-proposals.ts
|
|
88161
88509
|
import {
|
|
88162
88510
|
closeSync as closeSync7,
|
|
88163
|
-
existsSync as
|
|
88164
|
-
mkdirSync as
|
|
88511
|
+
existsSync as existsSync39,
|
|
88512
|
+
mkdirSync as mkdirSync35,
|
|
88165
88513
|
openSync as openSync7,
|
|
88166
|
-
readFileSync as
|
|
88514
|
+
readFileSync as readFileSync36,
|
|
88167
88515
|
writeSync as writeSync5
|
|
88168
88516
|
} from "node:fs";
|
|
88169
|
-
import { join as
|
|
88517
|
+
import { join as join44 } from "node:path";
|
|
88170
88518
|
import { randomUUID as randomUUID5 } from "node:crypto";
|
|
88171
88519
|
var PROPOSALS_FILE2 = "skill-proposals.jsonl";
|
|
88172
88520
|
var REJECTED_FILE2 = "skill-proposals-rejected.jsonl";
|
|
88173
88521
|
var REJECTION_TTL_MS2 = 90 * 24 * 60 * 60 * 1000;
|
|
88174
88522
|
var PROPOSAL_SIM_THRESHOLD = 0.5;
|
|
88175
88523
|
function proposalsPath2(stateDir) {
|
|
88176
|
-
return
|
|
88524
|
+
return join44(stateDir, PROPOSALS_FILE2);
|
|
88177
88525
|
}
|
|
88178
88526
|
function rejectedPath2(stateDir) {
|
|
88179
|
-
return
|
|
88527
|
+
return join44(stateDir, REJECTED_FILE2);
|
|
88180
88528
|
}
|
|
88181
88529
|
function ensureDir3(stateDir) {
|
|
88182
|
-
if (!
|
|
88183
|
-
|
|
88530
|
+
if (!existsSync39(stateDir)) {
|
|
88531
|
+
mkdirSync35(stateDir, { recursive: true, mode: 493 });
|
|
88184
88532
|
}
|
|
88185
88533
|
}
|
|
88186
88534
|
function appendLine2(path2, obj) {
|
|
@@ -88193,11 +88541,11 @@ function appendLine2(path2, obj) {
|
|
|
88193
88541
|
}
|
|
88194
88542
|
}
|
|
88195
88543
|
function readLines2(path2, isValid2) {
|
|
88196
|
-
if (!
|
|
88544
|
+
if (!existsSync39(path2))
|
|
88197
88545
|
return [];
|
|
88198
88546
|
let raw;
|
|
88199
88547
|
try {
|
|
88200
|
-
raw =
|
|
88548
|
+
raw = readFileSync36(path2, "utf-8");
|
|
88201
88549
|
} catch {
|
|
88202
88550
|
return [];
|
|
88203
88551
|
}
|
|
@@ -88794,15 +89142,15 @@ function escapeBody2(s) {
|
|
|
88794
89142
|
}
|
|
88795
89143
|
|
|
88796
89144
|
// gateway/pid-file.ts
|
|
88797
|
-
import { writeFileSync as
|
|
89145
|
+
import { writeFileSync as writeFileSync32, readFileSync as readFileSync37, unlinkSync as unlinkSync19, renameSync as renameSync14 } from "node:fs";
|
|
88798
89146
|
function writePidFile(path2, record) {
|
|
88799
89147
|
const tmp = `${path2}.tmp-${process.pid}-${Date.now()}`;
|
|
88800
|
-
|
|
88801
|
-
|
|
89148
|
+
writeFileSync32(tmp, JSON.stringify(record), "utf-8");
|
|
89149
|
+
renameSync14(tmp, path2);
|
|
88802
89150
|
}
|
|
88803
89151
|
function clearPidFile(path2) {
|
|
88804
89152
|
try {
|
|
88805
|
-
|
|
89153
|
+
unlinkSync19(path2);
|
|
88806
89154
|
} catch {}
|
|
88807
89155
|
}
|
|
88808
89156
|
|
|
@@ -88813,10 +89161,10 @@ import {
|
|
|
88813
89161
|
writeFile as writeFileAsync,
|
|
88814
89162
|
readFile as readFileAsync
|
|
88815
89163
|
} from "node:fs/promises";
|
|
88816
|
-
import { readFileSync as
|
|
89164
|
+
import { readFileSync as readFileSync38 } from "node:fs";
|
|
88817
89165
|
function readCurrentBootId() {
|
|
88818
89166
|
try {
|
|
88819
|
-
const stat =
|
|
89167
|
+
const stat = readFileSync38("/proc/1/stat", "utf-8");
|
|
88820
89168
|
const lastParen = stat.lastIndexOf(")");
|
|
88821
89169
|
if (lastParen < 0)
|
|
88822
89170
|
return null;
|
|
@@ -88964,14 +89312,14 @@ var DEFAULT_LOG3 = (line) => {
|
|
|
88964
89312
|
`) ? line : line + `
|
|
88965
89313
|
`);
|
|
88966
89314
|
};
|
|
88967
|
-
var
|
|
89315
|
+
var DEFAULT_SLEEP4 = (ms) => new Promise((resolve10) => setTimeout(resolve10, ms));
|
|
88968
89316
|
var DEFAULT_NOW = () => typeof performance !== "undefined" && typeof performance.now === "function" ? performance.now() : Date.now();
|
|
88969
89317
|
function fmtAgent2(agentName3) {
|
|
88970
89318
|
return agentName3 ? ` agent=${agentName3}` : "";
|
|
88971
89319
|
}
|
|
88972
89320
|
async function drainShutdown(opts) {
|
|
88973
89321
|
const log = opts.log ?? DEFAULT_LOG3;
|
|
88974
|
-
const sleep2 = opts.sleep ??
|
|
89322
|
+
const sleep2 = opts.sleep ?? DEFAULT_SLEEP4;
|
|
88975
89323
|
const now = opts.now ?? DEFAULT_NOW;
|
|
88976
89324
|
const budgetMs = opts.budgetMs ?? 35000;
|
|
88977
89325
|
const pollIntervalMs = opts.pollIntervalMs ?? 100;
|
|
@@ -89019,15 +89367,15 @@ function safeCount(fn) {
|
|
|
89019
89367
|
}
|
|
89020
89368
|
|
|
89021
89369
|
// gateway/session-marker.ts
|
|
89022
|
-
import { writeFileSync as
|
|
89370
|
+
import { writeFileSync as writeFileSync33, readFileSync as readFileSync39, renameSync as renameSync15, unlinkSync as unlinkSync20 } from "node:fs";
|
|
89023
89371
|
function writeSessionMarker(path2, marker) {
|
|
89024
89372
|
const tmp = `${path2}.tmp-${process.pid}-${Date.now()}`;
|
|
89025
|
-
|
|
89026
|
-
|
|
89373
|
+
writeFileSync33(tmp, JSON.stringify(marker), "utf-8");
|
|
89374
|
+
renameSync15(tmp, path2);
|
|
89027
89375
|
}
|
|
89028
89376
|
function readSessionMarker(path2) {
|
|
89029
89377
|
try {
|
|
89030
|
-
const raw =
|
|
89378
|
+
const raw = readFileSync39(path2, "utf-8");
|
|
89031
89379
|
const parsed = JSON.parse(raw);
|
|
89032
89380
|
if (typeof parsed.pid === "number" && typeof parsed.startedAtMs === "number" && Number.isFinite(parsed.pid) && Number.isFinite(parsed.startedAtMs)) {
|
|
89033
89381
|
return { pid: parsed.pid, startedAtMs: parsed.startedAtMs };
|
|
@@ -89049,16 +89397,16 @@ function shouldFireRestartBanner(input) {
|
|
|
89049
89397
|
}
|
|
89050
89398
|
|
|
89051
89399
|
// gateway/clean-shutdown-marker.ts
|
|
89052
|
-
import { writeFileSync as
|
|
89400
|
+
import { writeFileSync as writeFileSync34, readFileSync as readFileSync40, renameSync as renameSync16, unlinkSync as unlinkSync21 } from "node:fs";
|
|
89053
89401
|
var DEFAULT_MAX_AGE_MS = 60000;
|
|
89054
89402
|
function writeCleanShutdownMarker(path2, marker) {
|
|
89055
89403
|
const tmp = `${path2}.tmp-${process.pid}-${Date.now()}`;
|
|
89056
|
-
|
|
89057
|
-
|
|
89404
|
+
writeFileSync34(tmp, JSON.stringify(marker), "utf-8");
|
|
89405
|
+
renameSync16(tmp, path2);
|
|
89058
89406
|
}
|
|
89059
89407
|
function readCleanShutdownMarker(path2) {
|
|
89060
89408
|
try {
|
|
89061
|
-
const raw =
|
|
89409
|
+
const raw = readFileSync40(path2, "utf-8");
|
|
89062
89410
|
const parsed = JSON.parse(raw);
|
|
89063
89411
|
if (typeof parsed.ts === "number" && Number.isFinite(parsed.ts) && typeof parsed.signal === "string" && parsed.signal.length > 0) {
|
|
89064
89412
|
const out = { ts: parsed.ts, signal: parsed.signal };
|
|
@@ -89073,7 +89421,7 @@ function readCleanShutdownMarker(path2) {
|
|
|
89073
89421
|
}
|
|
89074
89422
|
function clearCleanShutdownMarker(path2) {
|
|
89075
89423
|
try {
|
|
89076
|
-
|
|
89424
|
+
unlinkSync21(path2);
|
|
89077
89425
|
} catch {}
|
|
89078
89426
|
}
|
|
89079
89427
|
function shouldSuppressRecoveryBanner(marker, now, maxAgeMs = DEFAULT_MAX_AGE_MS) {
|
|
@@ -89430,16 +89778,16 @@ function classifyAdminGate(text4, myAgentName) {
|
|
|
89430
89778
|
|
|
89431
89779
|
// subagent-watcher.ts
|
|
89432
89780
|
import {
|
|
89433
|
-
existsSync as
|
|
89781
|
+
existsSync as existsSync40,
|
|
89434
89782
|
openSync as openSync8,
|
|
89435
89783
|
readSync as readSync2,
|
|
89436
|
-
statSync as
|
|
89784
|
+
statSync as statSync13,
|
|
89437
89785
|
closeSync as closeSync8,
|
|
89438
89786
|
watch,
|
|
89439
|
-
readdirSync as
|
|
89440
|
-
readFileSync as
|
|
89787
|
+
readdirSync as readdirSync8,
|
|
89788
|
+
readFileSync as readFileSync41
|
|
89441
89789
|
} from "fs";
|
|
89442
|
-
import { join as
|
|
89790
|
+
import { join as join45 } from "path";
|
|
89443
89791
|
|
|
89444
89792
|
// session-tail.ts
|
|
89445
89793
|
function sanitizeCwdToProjectName2(cwd) {
|
|
@@ -89753,104 +90101,6 @@ function redactSecrets(text4) {
|
|
|
89753
90101
|
|
|
89754
90102
|
// subagent-watcher.ts
|
|
89755
90103
|
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
90104
|
var DEFAULT_RESCAN_MS = 1000;
|
|
89855
90105
|
var DEFAULT_STALL_THRESHOLD_MS = 60000;
|
|
89856
90106
|
var DEFAULT_SILENT_SYNTHESIS_STALL_THRESHOLD_MS = 300000;
|
|
@@ -89887,7 +90137,7 @@ function backfillJsonlAgentId(db3, jsonlPath, agentId, log) {
|
|
|
89887
90137
|
const metaPath = jsonlPath.replace(/\.jsonl$/, ".meta.json");
|
|
89888
90138
|
let meta;
|
|
89889
90139
|
try {
|
|
89890
|
-
const raw =
|
|
90140
|
+
const raw = readFileSync41(metaPath, "utf8");
|
|
89891
90141
|
meta = JSON.parse(raw);
|
|
89892
90142
|
} catch {
|
|
89893
90143
|
log?.(`subagent-watcher: backfill skip ${agentId} \u2014 meta.json not readable at ${metaPath}`);
|
|
@@ -90342,9 +90592,9 @@ function startSubagentWatcher(config) {
|
|
|
90342
90592
|
clearTimeout(ref.ref);
|
|
90343
90593
|
});
|
|
90344
90594
|
const fs2 = config.fs ?? {
|
|
90345
|
-
existsSync:
|
|
90346
|
-
readdirSync:
|
|
90347
|
-
statSync:
|
|
90595
|
+
existsSync: existsSync40,
|
|
90596
|
+
readdirSync: readdirSync8,
|
|
90597
|
+
statSync: statSync13,
|
|
90348
90598
|
openSync: openSync8,
|
|
90349
90599
|
closeSync: closeSync8,
|
|
90350
90600
|
readSync: readSync2,
|
|
@@ -90860,8 +91110,8 @@ function startSubagentWatcher(config) {
|
|
|
90860
91110
|
if (stopped)
|
|
90861
91111
|
return;
|
|
90862
91112
|
pruneVanishedDirWatchers();
|
|
90863
|
-
const claudeHome =
|
|
90864
|
-
const projectsRoot =
|
|
91113
|
+
const claudeHome = join45(agentDir, ".claude");
|
|
91114
|
+
const projectsRoot = join45(claudeHome, "projects");
|
|
90865
91115
|
if (!fs2.existsSync(projectsRoot))
|
|
90866
91116
|
return;
|
|
90867
91117
|
let projectDirs;
|
|
@@ -90895,7 +91145,7 @@ function startSubagentWatcher(config) {
|
|
|
90895
91145
|
continue;
|
|
90896
91146
|
}
|
|
90897
91147
|
warnedForeignSlugs.delete(pDir);
|
|
90898
|
-
const projectPath =
|
|
91148
|
+
const projectPath = join45(projectsRoot, pDir);
|
|
90899
91149
|
let sessionDirs;
|
|
90900
91150
|
try {
|
|
90901
91151
|
sessionDirs = fs2.readdirSync(projectPath);
|
|
@@ -90905,7 +91155,7 @@ function startSubagentWatcher(config) {
|
|
|
90905
91155
|
for (const sDir of sessionDirs) {
|
|
90906
91156
|
if (sDir.endsWith(".jsonl"))
|
|
90907
91157
|
continue;
|
|
90908
|
-
const subagentsPath =
|
|
91158
|
+
const subagentsPath = join45(projectPath, sDir, "subagents");
|
|
90909
91159
|
if (!fs2.existsSync(subagentsPath))
|
|
90910
91160
|
continue;
|
|
90911
91161
|
const watchAndScan = (dirPath) => {
|
|
@@ -90914,7 +91164,7 @@ function startSubagentWatcher(config) {
|
|
|
90914
91164
|
const w = fs2.watch(dirPath, (_event, filename) => {
|
|
90915
91165
|
if (!filename || !filename.toString().startsWith("agent-") || !filename.toString().endsWith(".jsonl"))
|
|
90916
91166
|
return;
|
|
90917
|
-
const filePath =
|
|
91167
|
+
const filePath = join45(dirPath, filename.toString());
|
|
90918
91168
|
if (!knownFiles.has(filePath)) {
|
|
90919
91169
|
scanSubagentsDir(dirPath);
|
|
90920
91170
|
}
|
|
@@ -90928,7 +91178,7 @@ function startSubagentWatcher(config) {
|
|
|
90928
91178
|
scanSubagentsDir(dirPath);
|
|
90929
91179
|
};
|
|
90930
91180
|
watchAndScan(subagentsPath);
|
|
90931
|
-
const workflowsPath =
|
|
91181
|
+
const workflowsPath = join45(subagentsPath, "workflows");
|
|
90932
91182
|
if (fs2.existsSync(workflowsPath)) {
|
|
90933
91183
|
let wfDirs;
|
|
90934
91184
|
try {
|
|
@@ -90938,7 +91188,7 @@ function startSubagentWatcher(config) {
|
|
|
90938
91188
|
}
|
|
90939
91189
|
for (const wfDir of wfDirs) {
|
|
90940
91190
|
try {
|
|
90941
|
-
const wfPath =
|
|
91191
|
+
const wfPath = join45(workflowsPath, wfDir);
|
|
90942
91192
|
if (!fs2.statSync(wfPath).isDirectory())
|
|
90943
91193
|
continue;
|
|
90944
91194
|
watchAndScan(wfPath);
|
|
@@ -90958,7 +91208,7 @@ function startSubagentWatcher(config) {
|
|
|
90958
91208
|
for (const e of entries) {
|
|
90959
91209
|
if (!e.startsWith("agent-") || !e.endsWith(".jsonl"))
|
|
90960
91210
|
continue;
|
|
90961
|
-
const filePath =
|
|
91211
|
+
const filePath = join45(subagentsPath, e);
|
|
90962
91212
|
if (knownFiles.has(filePath))
|
|
90963
91213
|
continue;
|
|
90964
91214
|
const agentId = e.slice("agent-".length, -".jsonl".length);
|
|
@@ -91086,37 +91336,37 @@ function startSubagentWatcher(config) {
|
|
|
91086
91336
|
|
|
91087
91337
|
// ../src/worktree/registry.ts
|
|
91088
91338
|
import {
|
|
91089
|
-
mkdirSync as
|
|
91090
|
-
writeFileSync as
|
|
91091
|
-
readFileSync as
|
|
91092
|
-
readdirSync as
|
|
91093
|
-
unlinkSync as
|
|
91094
|
-
existsSync as
|
|
91095
|
-
renameSync as
|
|
91339
|
+
mkdirSync as mkdirSync36,
|
|
91340
|
+
writeFileSync as writeFileSync35,
|
|
91341
|
+
readFileSync as readFileSync42,
|
|
91342
|
+
readdirSync as readdirSync9,
|
|
91343
|
+
unlinkSync as unlinkSync22,
|
|
91344
|
+
existsSync as existsSync41,
|
|
91345
|
+
renameSync as renameSync17
|
|
91096
91346
|
} from "node:fs";
|
|
91097
|
-
import { join as
|
|
91098
|
-
import { homedir as
|
|
91347
|
+
import { join as join46, resolve as resolve10 } from "node:path";
|
|
91348
|
+
import { homedir as homedir16 } from "node:os";
|
|
91099
91349
|
function registryDir() {
|
|
91100
|
-
return resolve10(process.env.SWITCHROOM_WORKTREE_DIR ??
|
|
91350
|
+
return resolve10(process.env.SWITCHROOM_WORKTREE_DIR ?? join46(homedir16(), ".switchroom", "worktrees"));
|
|
91101
91351
|
}
|
|
91102
91352
|
function recordPath(id) {
|
|
91103
|
-
return
|
|
91353
|
+
return join46(registryDir(), `${id}.json`);
|
|
91104
91354
|
}
|
|
91105
91355
|
function ensureDir4() {
|
|
91106
|
-
|
|
91356
|
+
mkdirSync36(registryDir(), { recursive: true });
|
|
91107
91357
|
}
|
|
91108
91358
|
function writeRecord(record) {
|
|
91109
91359
|
ensureDir4();
|
|
91110
91360
|
const target = recordPath(record.id);
|
|
91111
91361
|
const tmp = `${target}.tmp${process.pid}`;
|
|
91112
|
-
|
|
91362
|
+
writeFileSync35(tmp, JSON.stringify(record, null, 2) + `
|
|
91113
91363
|
`, { mode: 384 });
|
|
91114
|
-
|
|
91364
|
+
renameSync17(tmp, target);
|
|
91115
91365
|
}
|
|
91116
91366
|
function readRecord(id) {
|
|
91117
91367
|
const path2 = recordPath(id);
|
|
91118
91368
|
try {
|
|
91119
|
-
const raw =
|
|
91369
|
+
const raw = readFileSync42(path2, "utf8");
|
|
91120
91370
|
return JSON.parse(raw);
|
|
91121
91371
|
} catch {
|
|
91122
91372
|
return null;
|
|
@@ -91126,7 +91376,7 @@ function listRecords() {
|
|
|
91126
91376
|
ensureDir4();
|
|
91127
91377
|
const dir = registryDir();
|
|
91128
91378
|
const records = [];
|
|
91129
|
-
for (const entry of
|
|
91379
|
+
for (const entry of readdirSync9(dir)) {
|
|
91130
91380
|
if (!entry.endsWith(".json"))
|
|
91131
91381
|
continue;
|
|
91132
91382
|
const id = entry.slice(0, -5);
|
|
@@ -91146,7 +91396,7 @@ function touchHeartbeat(id, onAfterRead) {
|
|
|
91146
91396
|
writeRecord({ ...rec, heartbeatAt: new Date().toISOString() });
|
|
91147
91397
|
}
|
|
91148
91398
|
function recordExists(id) {
|
|
91149
|
-
return
|
|
91399
|
+
return existsSync41(recordPath(id));
|
|
91150
91400
|
}
|
|
91151
91401
|
|
|
91152
91402
|
// worktree-watch-cwds.ts
|
|
@@ -91278,15 +91528,15 @@ function determineRestartReason(opts) {
|
|
|
91278
91528
|
init_boot_card();
|
|
91279
91529
|
|
|
91280
91530
|
// gateway/update-announce.ts
|
|
91281
|
-
import { existsSync as
|
|
91282
|
-
import { join as
|
|
91283
|
-
import { homedir as
|
|
91531
|
+
import { existsSync as existsSync46, mkdirSync as mkdirSync40, openSync as openSync9, closeSync as closeSync9, readFileSync as readFileSync49 } from "node:fs";
|
|
91532
|
+
import { join as join51 } from "node:path";
|
|
91533
|
+
import { homedir as homedir18 } from "node:os";
|
|
91284
91534
|
|
|
91285
91535
|
// ../src/host-control/audit-reader.ts
|
|
91286
|
-
import { homedir as
|
|
91287
|
-
import { join as
|
|
91288
|
-
function defaultAuditLogPath(home2 =
|
|
91289
|
-
return
|
|
91536
|
+
import { homedir as homedir17 } from "node:os";
|
|
91537
|
+
import { join as join50 } from "node:path";
|
|
91538
|
+
function defaultAuditLogPath(home2 = homedir17()) {
|
|
91539
|
+
return join50(home2, ".switchroom", "host-control-audit.log");
|
|
91290
91540
|
}
|
|
91291
91541
|
function parseAuditLine(line) {
|
|
91292
91542
|
const trimmed = line.trim();
|
|
@@ -91409,8 +91659,8 @@ function readAndFilter(raw, filters, limit) {
|
|
|
91409
91659
|
var DEFAULT_LOOKBACK_MS = 10 * 60 * 1000;
|
|
91410
91660
|
function readLastTerminalUpdateAudit(opts = {}) {
|
|
91411
91661
|
const path2 = opts.auditLogPath ?? defaultAuditLogPath();
|
|
91412
|
-
const exists = opts.exists ??
|
|
91413
|
-
const readFile = opts.readFile ?? ((p) =>
|
|
91662
|
+
const exists = opts.exists ?? existsSync46;
|
|
91663
|
+
const readFile = opts.readFile ?? ((p) => readFileSync49(p, "utf-8"));
|
|
91414
91664
|
if (!exists(path2))
|
|
91415
91665
|
return null;
|
|
91416
91666
|
let raw;
|
|
@@ -91471,15 +91721,15 @@ function renderUpdateOutcomeLine(entry) {
|
|
|
91471
91721
|
`);
|
|
91472
91722
|
}
|
|
91473
91723
|
function claimUpdateAnnouncement(requestId, opts = {}) {
|
|
91474
|
-
const stateDir = opts.stateDir ?? process.env.TELEGRAM_STATE_DIR ??
|
|
91475
|
-
const dir =
|
|
91724
|
+
const stateDir = opts.stateDir ?? process.env.TELEGRAM_STATE_DIR ?? join51(homedir18(), ".switchroom");
|
|
91725
|
+
const dir = join51(stateDir, "update-announced");
|
|
91476
91726
|
try {
|
|
91477
|
-
|
|
91727
|
+
mkdirSync40(dir, { recursive: true });
|
|
91478
91728
|
} catch {
|
|
91479
91729
|
return false;
|
|
91480
91730
|
}
|
|
91481
91731
|
const safeId = requestId.replace(/[^A-Za-z0-9_.-]/g, "_").slice(0, 200);
|
|
91482
|
-
const path2 =
|
|
91732
|
+
const path2 = join51(dir, safeId);
|
|
91483
91733
|
try {
|
|
91484
91734
|
const fd = openSync9(path2, "wx");
|
|
91485
91735
|
closeSync9(fd);
|
|
@@ -91499,7 +91749,7 @@ function maybeRenderUpdateAnnouncement(opts = {}) {
|
|
|
91499
91749
|
|
|
91500
91750
|
// issues-card.ts
|
|
91501
91751
|
init_card_format();
|
|
91502
|
-
import { readFileSync as
|
|
91752
|
+
import { readFileSync as readFileSync50, writeFileSync as writeFileSync40 } from "node:fs";
|
|
91503
91753
|
var SEVERITY_EMOJI = {
|
|
91504
91754
|
info: "\u2139\ufe0f",
|
|
91505
91755
|
warn: "\u26a0\ufe0f",
|
|
@@ -91591,7 +91841,7 @@ function extractRetryAfterSecs2(err) {
|
|
|
91591
91841
|
var COOLDOWN_JITTER_MS2 = 500;
|
|
91592
91842
|
function readPersistedMessageId(path2, log) {
|
|
91593
91843
|
try {
|
|
91594
|
-
const raw =
|
|
91844
|
+
const raw = readFileSync50(path2, "utf8");
|
|
91595
91845
|
const parsed = JSON.parse(raw);
|
|
91596
91846
|
const v = parsed.messageId;
|
|
91597
91847
|
if (typeof v === "number" && Number.isInteger(v) && v > 0)
|
|
@@ -91607,7 +91857,7 @@ function readPersistedMessageId(path2, log) {
|
|
|
91607
91857
|
}
|
|
91608
91858
|
function writePersistedMessageId(path2, messageId, log) {
|
|
91609
91859
|
try {
|
|
91610
|
-
|
|
91860
|
+
writeFileSync40(path2, JSON.stringify({ messageId }) + `
|
|
91611
91861
|
`, { mode: 384 });
|
|
91612
91862
|
} catch (err) {
|
|
91613
91863
|
log(`issues-card: persist write failed (${err.message})`);
|
|
@@ -91700,24 +91950,24 @@ function createIssuesCardHandle(opts) {
|
|
|
91700
91950
|
}
|
|
91701
91951
|
|
|
91702
91952
|
// issues-watcher.ts
|
|
91703
|
-
import { existsSync as
|
|
91704
|
-
import { join as
|
|
91953
|
+
import { existsSync as existsSync48, statSync as statSync16 } from "node:fs";
|
|
91954
|
+
import { join as join53 } from "node:path";
|
|
91705
91955
|
|
|
91706
91956
|
// ../src/issues/store.ts
|
|
91707
91957
|
import {
|
|
91708
91958
|
closeSync as closeSync10,
|
|
91709
|
-
existsSync as
|
|
91710
|
-
mkdirSync as
|
|
91959
|
+
existsSync as existsSync47,
|
|
91960
|
+
mkdirSync as mkdirSync41,
|
|
91711
91961
|
openSync as openSync10,
|
|
91712
|
-
readdirSync as
|
|
91713
|
-
readFileSync as
|
|
91714
|
-
renameSync as
|
|
91715
|
-
statSync as
|
|
91716
|
-
unlinkSync as
|
|
91717
|
-
writeFileSync as
|
|
91962
|
+
readdirSync as readdirSync11,
|
|
91963
|
+
readFileSync as readFileSync51,
|
|
91964
|
+
renameSync as renameSync20,
|
|
91965
|
+
statSync as statSync15,
|
|
91966
|
+
unlinkSync as unlinkSync23,
|
|
91967
|
+
writeFileSync as writeFileSync41,
|
|
91718
91968
|
writeSync as writeSync6
|
|
91719
91969
|
} from "node:fs";
|
|
91720
|
-
import { join as
|
|
91970
|
+
import { join as join52 } from "node:path";
|
|
91721
91971
|
import { randomBytes as randomBytes8 } from "node:crypto";
|
|
91722
91972
|
import { execSync } from "node:child_process";
|
|
91723
91973
|
|
|
@@ -91736,12 +91986,12 @@ init_redact();
|
|
|
91736
91986
|
var ISSUES_FILE = "issues.jsonl";
|
|
91737
91987
|
var ISSUES_LOCK = "issues.lock";
|
|
91738
91988
|
function readAll(stateDir) {
|
|
91739
|
-
const path2 =
|
|
91740
|
-
if (!
|
|
91989
|
+
const path2 = join52(stateDir, ISSUES_FILE);
|
|
91990
|
+
if (!existsSync47(path2))
|
|
91741
91991
|
return [];
|
|
91742
91992
|
let raw;
|
|
91743
91993
|
try {
|
|
91744
|
-
raw =
|
|
91994
|
+
raw = readFileSync51(path2, "utf-8");
|
|
91745
91995
|
} catch {
|
|
91746
91996
|
return [];
|
|
91747
91997
|
}
|
|
@@ -91773,7 +92023,7 @@ function list2(stateDir, opts = {}) {
|
|
|
91773
92023
|
});
|
|
91774
92024
|
}
|
|
91775
92025
|
function resolve11(stateDir, fingerprint, nowFn = Date.now) {
|
|
91776
|
-
if (!
|
|
92026
|
+
if (!existsSync47(join52(stateDir, ISSUES_FILE)))
|
|
91777
92027
|
return 0;
|
|
91778
92028
|
return withLock(stateDir, () => {
|
|
91779
92029
|
const all2 = readAll(stateDir);
|
|
@@ -91791,21 +92041,21 @@ function resolve11(stateDir, fingerprint, nowFn = Date.now) {
|
|
|
91791
92041
|
});
|
|
91792
92042
|
}
|
|
91793
92043
|
function writeAll(stateDir, events) {
|
|
91794
|
-
const path2 =
|
|
92044
|
+
const path2 = join52(stateDir, ISSUES_FILE);
|
|
91795
92045
|
sweepOrphanTmpFiles(stateDir);
|
|
91796
92046
|
const tmp = `${path2}.tmp-${process.pid}-${randomBytes8(4).toString("hex")}`;
|
|
91797
92047
|
const body = events.length === 0 ? "" : events.map((e) => JSON.stringify(e)).join(`
|
|
91798
92048
|
`) + `
|
|
91799
92049
|
`;
|
|
91800
|
-
|
|
91801
|
-
|
|
92050
|
+
writeFileSync41(tmp, body, "utf-8");
|
|
92051
|
+
renameSync20(tmp, path2);
|
|
91802
92052
|
}
|
|
91803
92053
|
var ORPHAN_TMP_TTL_MS = 60000;
|
|
91804
92054
|
var TMP_PREFIX = `${ISSUES_FILE}.tmp-`;
|
|
91805
92055
|
function sweepOrphanTmpFiles(stateDir) {
|
|
91806
92056
|
let entries;
|
|
91807
92057
|
try {
|
|
91808
|
-
entries =
|
|
92058
|
+
entries = readdirSync11(stateDir);
|
|
91809
92059
|
} catch {
|
|
91810
92060
|
return;
|
|
91811
92061
|
}
|
|
@@ -91813,11 +92063,11 @@ function sweepOrphanTmpFiles(stateDir) {
|
|
|
91813
92063
|
for (const entry of entries) {
|
|
91814
92064
|
if (!entry.startsWith(TMP_PREFIX))
|
|
91815
92065
|
continue;
|
|
91816
|
-
const tmpPath2 =
|
|
92066
|
+
const tmpPath2 = join52(stateDir, entry);
|
|
91817
92067
|
try {
|
|
91818
|
-
const stat =
|
|
92068
|
+
const stat = statSync15(tmpPath2);
|
|
91819
92069
|
if (stat.mtimeMs < cutoff) {
|
|
91820
|
-
|
|
92070
|
+
unlinkSync23(tmpPath2);
|
|
91821
92071
|
}
|
|
91822
92072
|
} catch {}
|
|
91823
92073
|
}
|
|
@@ -91825,7 +92075,7 @@ function sweepOrphanTmpFiles(stateDir) {
|
|
|
91825
92075
|
var LOCK_RETRY_MS = 25;
|
|
91826
92076
|
var LOCK_TIMEOUT_MS = 1e4;
|
|
91827
92077
|
function withLock(stateDir, fn) {
|
|
91828
|
-
const lockPath =
|
|
92078
|
+
const lockPath = join52(stateDir, ISSUES_LOCK);
|
|
91829
92079
|
const startedAt = Date.now();
|
|
91830
92080
|
let fd = null;
|
|
91831
92081
|
while (fd === null) {
|
|
@@ -91853,27 +92103,27 @@ function withLock(stateDir, fn) {
|
|
|
91853
92103
|
closeSync10(fd);
|
|
91854
92104
|
} catch {}
|
|
91855
92105
|
try {
|
|
91856
|
-
|
|
92106
|
+
unlinkSync23(lockPath);
|
|
91857
92107
|
} catch {}
|
|
91858
92108
|
}
|
|
91859
92109
|
}
|
|
91860
92110
|
function tryStealStaleLock(lockPath) {
|
|
91861
92111
|
let pidStr;
|
|
91862
92112
|
try {
|
|
91863
|
-
pidStr =
|
|
92113
|
+
pidStr = readFileSync51(lockPath, "utf-8").trim();
|
|
91864
92114
|
} catch {
|
|
91865
92115
|
return true;
|
|
91866
92116
|
}
|
|
91867
92117
|
const pid = Number(pidStr);
|
|
91868
92118
|
if (!Number.isFinite(pid) || pid <= 0) {
|
|
91869
92119
|
try {
|
|
91870
|
-
|
|
92120
|
+
unlinkSync23(lockPath);
|
|
91871
92121
|
} catch {}
|
|
91872
92122
|
return true;
|
|
91873
92123
|
}
|
|
91874
92124
|
if (pid === process.pid) {
|
|
91875
92125
|
try {
|
|
91876
|
-
|
|
92126
|
+
unlinkSync23(lockPath);
|
|
91877
92127
|
} catch {}
|
|
91878
92128
|
return true;
|
|
91879
92129
|
}
|
|
@@ -91888,7 +92138,7 @@ function tryStealStaleLock(lockPath) {
|
|
|
91888
92138
|
return false;
|
|
91889
92139
|
}
|
|
91890
92140
|
try {
|
|
91891
|
-
|
|
92141
|
+
unlinkSync23(lockPath);
|
|
91892
92142
|
} catch {}
|
|
91893
92143
|
return true;
|
|
91894
92144
|
}
|
|
@@ -91910,7 +92160,7 @@ function isIssueEvent(v) {
|
|
|
91910
92160
|
// issues-watcher.ts
|
|
91911
92161
|
var DEFAULT_POLL_INTERVAL_MS2 = 2000;
|
|
91912
92162
|
function startIssuesWatcher(opts) {
|
|
91913
|
-
const path2 =
|
|
92163
|
+
const path2 = join53(opts.stateDir, ISSUES_FILE);
|
|
91914
92164
|
const log = opts.log ?? (() => {});
|
|
91915
92165
|
const intervalMs = opts.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS2;
|
|
91916
92166
|
const setIntervalFn = opts.setInterval ?? setInterval;
|
|
@@ -91958,10 +92208,10 @@ function startIssuesWatcher(opts) {
|
|
|
91958
92208
|
};
|
|
91959
92209
|
}
|
|
91960
92210
|
function defaultSignatureProvider(path2) {
|
|
91961
|
-
if (!
|
|
92211
|
+
if (!existsSync48(path2))
|
|
91962
92212
|
return null;
|
|
91963
92213
|
try {
|
|
91964
|
-
const stat =
|
|
92214
|
+
const stat = statSync16(path2);
|
|
91965
92215
|
return `${stat.mtimeMs}:${stat.size}`;
|
|
91966
92216
|
} catch {
|
|
91967
92217
|
return null;
|
|
@@ -92669,8 +92919,8 @@ function readBashCommand(inputPreview) {
|
|
|
92669
92919
|
}
|
|
92670
92920
|
|
|
92671
92921
|
// gateway/scoped-grant-store.ts
|
|
92672
|
-
import { readFileSync as
|
|
92673
|
-
import { join as
|
|
92922
|
+
import { readFileSync as readFileSync52, writeFileSync as writeFileSync42 } from "node:fs";
|
|
92923
|
+
import { join as join54 } from "node:path";
|
|
92674
92924
|
|
|
92675
92925
|
// scoped-approval.ts
|
|
92676
92926
|
var SCOPED_APPROVAL_DEFAULT_TTL_MS2 = 30 * 60 * 1000;
|
|
@@ -92712,11 +92962,11 @@ function scopedGrantPersistEnabled(env = process.env) {
|
|
|
92712
92962
|
return env.SWITCHROOM_SCOPED_GRANT_PERSIST !== "0";
|
|
92713
92963
|
}
|
|
92714
92964
|
function createScopedGrantStore(stateDir, env = process.env) {
|
|
92715
|
-
const filePath =
|
|
92716
|
-
const
|
|
92965
|
+
const filePath = join54(stateDir, "scoped-grants.json");
|
|
92966
|
+
const enabled7 = scopedGrantPersistEnabled(env);
|
|
92717
92967
|
function read() {
|
|
92718
92968
|
try {
|
|
92719
|
-
const raw =
|
|
92969
|
+
const raw = readFileSync52(filePath, "utf-8");
|
|
92720
92970
|
const parsed = JSON.parse(raw);
|
|
92721
92971
|
return Array.isArray(parsed) ? parsed : [];
|
|
92722
92972
|
} catch {
|
|
@@ -92724,17 +92974,17 @@ function createScopedGrantStore(stateDir, env = process.env) {
|
|
|
92724
92974
|
}
|
|
92725
92975
|
}
|
|
92726
92976
|
return {
|
|
92727
|
-
enabled:
|
|
92977
|
+
enabled: enabled7,
|
|
92728
92978
|
load(now) {
|
|
92729
|
-
if (!
|
|
92979
|
+
if (!enabled7)
|
|
92730
92980
|
return new Map;
|
|
92731
92981
|
return deserializeScopedGrants(read(), now);
|
|
92732
92982
|
},
|
|
92733
92983
|
save(store2) {
|
|
92734
|
-
if (!
|
|
92984
|
+
if (!enabled7)
|
|
92735
92985
|
return;
|
|
92736
92986
|
try {
|
|
92737
|
-
|
|
92987
|
+
writeFileSync42(filePath, JSON.stringify(serializeScopedGrants(store2)), {
|
|
92738
92988
|
encoding: "utf-8",
|
|
92739
92989
|
mode: 384
|
|
92740
92990
|
});
|
|
@@ -93085,8 +93335,8 @@ function isDiffPreApproved(agentName3, unifiedDiff, deps) {
|
|
|
93085
93335
|
|
|
93086
93336
|
// credits-watch.ts
|
|
93087
93337
|
init_card_format();
|
|
93088
|
-
import { readFileSync as
|
|
93089
|
-
import { join as
|
|
93338
|
+
import { readFileSync as readFileSync53, writeFileSync as writeFileSync43, existsSync as existsSync49, mkdirSync as mkdirSync42 } from "fs";
|
|
93339
|
+
import { join as join55 } from "path";
|
|
93090
93340
|
var STATE_FILE = "credits-watch.json";
|
|
93091
93341
|
var DEFAULT_CREDIT_FATAL_REASONS = new Set;
|
|
93092
93342
|
var KNOWN_CREDIT_REASONS = [
|
|
@@ -93108,12 +93358,12 @@ function emptyCreditState() {
|
|
|
93108
93358
|
return { lastNotifiedReason: null, lastNotifiedAt: 0 };
|
|
93109
93359
|
}
|
|
93110
93360
|
function readClaudeJsonOverage(claudeConfigDir) {
|
|
93111
|
-
const path2 =
|
|
93112
|
-
if (!
|
|
93361
|
+
const path2 = join55(claudeConfigDir, ".claude.json");
|
|
93362
|
+
if (!existsSync49(path2))
|
|
93113
93363
|
return null;
|
|
93114
93364
|
let raw;
|
|
93115
93365
|
try {
|
|
93116
|
-
raw =
|
|
93366
|
+
raw = readFileSync53(path2, "utf-8");
|
|
93117
93367
|
} catch {
|
|
93118
93368
|
return null;
|
|
93119
93369
|
}
|
|
@@ -93191,11 +93441,11 @@ function humanizeReason(reason) {
|
|
|
93191
93441
|
}
|
|
93192
93442
|
}
|
|
93193
93443
|
function loadCreditState(stateDir) {
|
|
93194
|
-
const path2 =
|
|
93195
|
-
if (!
|
|
93444
|
+
const path2 = join55(stateDir, STATE_FILE);
|
|
93445
|
+
if (!existsSync49(path2))
|
|
93196
93446
|
return emptyCreditState();
|
|
93197
93447
|
try {
|
|
93198
|
-
const raw =
|
|
93448
|
+
const raw = readFileSync53(path2, "utf-8");
|
|
93199
93449
|
const parsed = JSON.parse(raw);
|
|
93200
93450
|
if (parsed && typeof parsed === "object" && (parsed.lastNotifiedReason === null || typeof parsed.lastNotifiedReason === "string") && typeof parsed.lastNotifiedAt === "number" && Number.isFinite(parsed.lastNotifiedAt)) {
|
|
93201
93451
|
return {
|
|
@@ -93207,17 +93457,17 @@ function loadCreditState(stateDir) {
|
|
|
93207
93457
|
return emptyCreditState();
|
|
93208
93458
|
}
|
|
93209
93459
|
function saveCreditState(stateDir, state7) {
|
|
93210
|
-
|
|
93211
|
-
const path2 =
|
|
93212
|
-
|
|
93460
|
+
mkdirSync42(stateDir, { recursive: true });
|
|
93461
|
+
const path2 = join55(stateDir, STATE_FILE);
|
|
93462
|
+
writeFileSync43(path2, JSON.stringify(state7, null, 2) + `
|
|
93213
93463
|
`, { mode: 384 });
|
|
93214
93464
|
}
|
|
93215
93465
|
|
|
93216
93466
|
// quota-watch.ts
|
|
93217
93467
|
init_auth_snapshot_format();
|
|
93218
93468
|
init_card_format();
|
|
93219
|
-
import { readFileSync as
|
|
93220
|
-
import { join as
|
|
93469
|
+
import { readFileSync as readFileSync54, writeFileSync as writeFileSync44, existsSync as existsSync50, mkdirSync as mkdirSync43 } from "fs";
|
|
93470
|
+
import { join as join56 } from "path";
|
|
93221
93471
|
var STATE_FILE2 = "quota-watch.json";
|
|
93222
93472
|
function emptyQuotaWatchState() {
|
|
93223
93473
|
return {};
|
|
@@ -93455,11 +93705,11 @@ function buildRecoveryMessage(agentName3, snap) {
|
|
|
93455
93705
|
`);
|
|
93456
93706
|
}
|
|
93457
93707
|
function loadQuotaWatchState(stateDir) {
|
|
93458
|
-
const path2 =
|
|
93459
|
-
if (!
|
|
93708
|
+
const path2 = join56(stateDir, STATE_FILE2);
|
|
93709
|
+
if (!existsSync50(path2))
|
|
93460
93710
|
return emptyQuotaWatchState();
|
|
93461
93711
|
try {
|
|
93462
|
-
const raw =
|
|
93712
|
+
const raw = readFileSync54(path2, "utf-8");
|
|
93463
93713
|
const parsed = JSON.parse(raw);
|
|
93464
93714
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
93465
93715
|
return emptyQuotaWatchState();
|
|
@@ -93476,9 +93726,9 @@ function loadQuotaWatchState(stateDir) {
|
|
|
93476
93726
|
}
|
|
93477
93727
|
}
|
|
93478
93728
|
function saveQuotaWatchState(stateDir, state7) {
|
|
93479
|
-
|
|
93480
|
-
const path2 =
|
|
93481
|
-
|
|
93729
|
+
mkdirSync43(stateDir, { recursive: true });
|
|
93730
|
+
const path2 = join56(stateDir, STATE_FILE2);
|
|
93731
|
+
writeFileSync44(path2, JSON.stringify(state7, null, 2) + `
|
|
93482
93732
|
`, { mode: 384 });
|
|
93483
93733
|
}
|
|
93484
93734
|
function patchQuotaWatchState(current, accountLabel, accountState) {
|
|
@@ -93526,31 +93776,31 @@ init_auth_snapshot_format2();
|
|
|
93526
93776
|
// gateway/turn-active-marker.ts
|
|
93527
93777
|
import {
|
|
93528
93778
|
closeSync as closeSync11,
|
|
93529
|
-
existsSync as
|
|
93530
|
-
mkdirSync as
|
|
93779
|
+
existsSync as existsSync51,
|
|
93780
|
+
mkdirSync as mkdirSync44,
|
|
93531
93781
|
openSync as openSync11,
|
|
93532
|
-
readFileSync as
|
|
93533
|
-
statSync as
|
|
93534
|
-
unlinkSync as
|
|
93782
|
+
readFileSync as readFileSync55,
|
|
93783
|
+
statSync as statSync17,
|
|
93784
|
+
unlinkSync as unlinkSync24,
|
|
93535
93785
|
utimesSync as utimesSync2,
|
|
93536
|
-
writeFileSync as
|
|
93786
|
+
writeFileSync as writeFileSync45
|
|
93537
93787
|
} from "node:fs";
|
|
93538
|
-
import { join as
|
|
93788
|
+
import { join as join57 } from "node:path";
|
|
93539
93789
|
var TURN_ACTIVE_MARKER_FILE2 = "turn-active.json";
|
|
93540
93790
|
var TURN_ACTIVE_HARD_TTL_MS2 = 10 * 60000;
|
|
93541
93791
|
var TURN_ACTIVE_IDLE_SWEEP_MS = 60000;
|
|
93542
93792
|
function removeTurnActiveMarker2(stateDir) {
|
|
93543
93793
|
try {
|
|
93544
|
-
|
|
93794
|
+
unlinkSync24(join57(stateDir, TURN_ACTIVE_MARKER_FILE2));
|
|
93545
93795
|
} catch {}
|
|
93546
93796
|
}
|
|
93547
93797
|
function sweepStaleTurnActiveMarker(stateDir, opts) {
|
|
93548
|
-
const path2 =
|
|
93549
|
-
if (!
|
|
93798
|
+
const path2 = join57(stateDir, TURN_ACTIVE_MARKER_FILE2);
|
|
93799
|
+
if (!existsSync51(path2))
|
|
93550
93800
|
return false;
|
|
93551
93801
|
const now = opts.now ?? Date.now();
|
|
93552
93802
|
try {
|
|
93553
|
-
const st =
|
|
93803
|
+
const st = statSync17(path2);
|
|
93554
93804
|
const ageMs = now - st.mtimeMs;
|
|
93555
93805
|
const hardExpired = ageMs > opts.hardTtlMs;
|
|
93556
93806
|
const idleExpired = !opts.turnInFlight && ageMs > opts.idleSweepMs;
|
|
@@ -93558,9 +93808,9 @@ function sweepStaleTurnActiveMarker(stateDir, opts) {
|
|
|
93558
93808
|
return false;
|
|
93559
93809
|
let payload = null;
|
|
93560
93810
|
try {
|
|
93561
|
-
payload =
|
|
93811
|
+
payload = readFileSync55(path2, "utf8");
|
|
93562
93812
|
} catch {}
|
|
93563
|
-
|
|
93813
|
+
unlinkSync24(path2);
|
|
93564
93814
|
if (opts.onRemove) {
|
|
93565
93815
|
try {
|
|
93566
93816
|
opts.onRemove({
|
|
@@ -93576,9 +93826,9 @@ function sweepStaleTurnActiveMarker(stateDir, opts) {
|
|
|
93576
93826
|
}
|
|
93577
93827
|
}
|
|
93578
93828
|
function readTurnActiveMarkerAgeMs2(stateDir, now) {
|
|
93579
|
-
const path2 =
|
|
93829
|
+
const path2 = join57(stateDir, TURN_ACTIVE_MARKER_FILE2);
|
|
93580
93830
|
try {
|
|
93581
|
-
const st =
|
|
93831
|
+
const st = statSync17(path2);
|
|
93582
93832
|
return (now ?? Date.now()) - st.mtimeMs;
|
|
93583
93833
|
} catch {
|
|
93584
93834
|
return null;
|
|
@@ -93589,19 +93839,19 @@ function effectiveTurnAgeMs(markerAgeMs, turnStartedAt, now) {
|
|
|
93589
93839
|
}
|
|
93590
93840
|
|
|
93591
93841
|
// gateway/gateway-heartbeat.ts
|
|
93592
|
-
import { mkdirSync as
|
|
93593
|
-
import { join as
|
|
93842
|
+
import { mkdirSync as mkdirSync45, utimesSync as utimesSync3, writeFileSync as writeFileSync46 } from "node:fs";
|
|
93843
|
+
import { join as join58 } from "node:path";
|
|
93594
93844
|
var GATEWAY_HEARTBEAT_FILE = "gateway-heartbeat";
|
|
93595
93845
|
var GATEWAY_HEARTBEAT_INTERVAL_MS = 15000;
|
|
93596
93846
|
function touchGatewayHeartbeat(stateDir) {
|
|
93597
|
-
const path2 =
|
|
93847
|
+
const path2 = join58(stateDir, GATEWAY_HEARTBEAT_FILE);
|
|
93598
93848
|
const now = new Date;
|
|
93599
93849
|
try {
|
|
93600
93850
|
utimesSync3(path2, now, now);
|
|
93601
93851
|
} catch {
|
|
93602
93852
|
try {
|
|
93603
|
-
|
|
93604
|
-
|
|
93853
|
+
mkdirSync45(stateDir, { recursive: true });
|
|
93854
|
+
writeFileSync46(path2, `${Date.now()}
|
|
93605
93855
|
`, { mode: 384 });
|
|
93606
93856
|
} catch {}
|
|
93607
93857
|
}
|
|
@@ -93613,11 +93863,133 @@ function startGatewayHeartbeat(stateDir, intervalMs = GATEWAY_HEARTBEAT_INTERVAL
|
|
|
93613
93863
|
return timer3;
|
|
93614
93864
|
}
|
|
93615
93865
|
|
|
93866
|
+
// gateway/outbox-sweep.ts
|
|
93867
|
+
async function sweepOutbox(deps) {
|
|
93868
|
+
const now = deps.now?.() ?? Date.now();
|
|
93869
|
+
const log = deps.log ?? (() => {});
|
|
93870
|
+
const summary = { scanned: 0, delivered: 0, skipped: 0 };
|
|
93871
|
+
reclaimStaleSending(deps.stateDir, now);
|
|
93872
|
+
const pending = listPendingRecords(deps.stateDir);
|
|
93873
|
+
if (pending.length === 0)
|
|
93874
|
+
return summary;
|
|
93875
|
+
const deliveredNonces = readDeliveredNonces(deps.stateDir);
|
|
93876
|
+
for (const fileName of pending) {
|
|
93877
|
+
const record2 = readOutboxRecord(fileName, deps.stateDir);
|
|
93878
|
+
if (record2 == null)
|
|
93879
|
+
continue;
|
|
93880
|
+
summary.scanned++;
|
|
93881
|
+
const resolved = resolveOutboxChat(record2, {
|
|
93882
|
+
registryChainLookup: (anchorContent) => {
|
|
93883
|
+
const taskId = extractTaskId(anchorContent);
|
|
93884
|
+
if (taskId == null || deps.registryChainLookup == null)
|
|
93885
|
+
return null;
|
|
93886
|
+
return deps.registryChainLookup(taskId);
|
|
93887
|
+
}
|
|
93888
|
+
});
|
|
93889
|
+
const routePrefix = resolved?.via === "origin" ? "(from background task) " : "";
|
|
93890
|
+
const decision = decideOutboxSweep({
|
|
93891
|
+
record: record2,
|
|
93892
|
+
now,
|
|
93893
|
+
deliveredNonces,
|
|
93894
|
+
textAlreadyDelivered: resolved != null && deps.textAlreadyDelivered(resolved.chatId, resolved.threadId, record2.text),
|
|
93895
|
+
routable: resolved != null,
|
|
93896
|
+
routePrefix,
|
|
93897
|
+
quietMs: deps.quietMs ?? OUTBOX_QUIET_MS
|
|
93898
|
+
});
|
|
93899
|
+
if (decision.action !== "send" && decision.action !== "send-delayed") {
|
|
93900
|
+
summary.skipped++;
|
|
93901
|
+
if (decision.action === "skip-journaled") {
|
|
93902
|
+
clearOutboxRecord(record2.turnNonce, deps.stateDir);
|
|
93903
|
+
} else if (decision.action === "skip-dedup") {
|
|
93904
|
+
appendDelivered({
|
|
93905
|
+
turnNonce: record2.turnNonce,
|
|
93906
|
+
textSha256: record2.textSha256,
|
|
93907
|
+
ts: now,
|
|
93908
|
+
deliverySource: "sweep",
|
|
93909
|
+
replyAlreadyDeliveredThisTurn: record2.replyAlreadyDeliveredThisTurn === true
|
|
93910
|
+
}, deps.stateDir);
|
|
93911
|
+
clearOutboxRecord(record2.turnNonce, deps.stateDir);
|
|
93912
|
+
}
|
|
93913
|
+
continue;
|
|
93914
|
+
}
|
|
93915
|
+
const claimed = claimRecord(record2.turnNonce, deps.stateDir);
|
|
93916
|
+
if (claimed == null) {
|
|
93917
|
+
summary.skipped++;
|
|
93918
|
+
continue;
|
|
93919
|
+
}
|
|
93920
|
+
const resolvedChat = resolved;
|
|
93921
|
+
try {
|
|
93922
|
+
const messageId = await deps.send(resolvedChat.chatId, resolvedChat.threadId, decision.text ?? record2.text);
|
|
93923
|
+
appendDelivered({
|
|
93924
|
+
turnNonce: record2.turnNonce,
|
|
93925
|
+
textSha256: record2.textSha256,
|
|
93926
|
+
tgMessageId: messageId,
|
|
93927
|
+
ts: now,
|
|
93928
|
+
deliverySource: "sweep",
|
|
93929
|
+
replyAlreadyDeliveredThisTurn: record2.replyAlreadyDeliveredThisTurn === true
|
|
93930
|
+
}, deps.stateDir);
|
|
93931
|
+
removeClaimed(record2.turnNonce, deps.stateDir);
|
|
93932
|
+
summary.delivered++;
|
|
93933
|
+
log(`outbox-sweep: delivered nonce=${record2.turnNonce} via=${resolvedChat.via} ` + `source=${record2.source} chars=${record2.text.length}${decision.action === "send-delayed" ? " (delayed)" : ""}
|
|
93934
|
+
`);
|
|
93935
|
+
} catch (err) {
|
|
93936
|
+
releaseClaim(record2.turnNonce, deps.stateDir);
|
|
93937
|
+
summary.skipped++;
|
|
93938
|
+
log(`outbox-sweep: send failed nonce=${record2.turnNonce}: ${err.message} \u2014 will retry
|
|
93939
|
+
`);
|
|
93940
|
+
}
|
|
93941
|
+
}
|
|
93942
|
+
return summary;
|
|
93943
|
+
}
|
|
93944
|
+
function chatFromTurnKey(turnKey3) {
|
|
93945
|
+
const idx = turnKey3.indexOf(":");
|
|
93946
|
+
const chatId = idx === -1 ? turnKey3 : turnKey3.slice(0, idx);
|
|
93947
|
+
const tRaw = idx === -1 ? "_" : turnKey3.slice(idx + 1);
|
|
93948
|
+
const t = tRaw === "_" || tRaw === "" ? null : Number(tRaw);
|
|
93949
|
+
return { chatId, threadId: Number.isFinite(t) ? t : null };
|
|
93950
|
+
}
|
|
93951
|
+
var OUTBOX_SWEEP_INTERVAL_MS = 5000;
|
|
93952
|
+
function startOutboxSweep(deps) {
|
|
93953
|
+
if (!deps.isGatewayMain || process.env.SWITCHROOM_TG_OUTBOX_DELIVERY === "0")
|
|
93954
|
+
return;
|
|
93955
|
+
const retry = createRetryApiCall({ log: deps.log });
|
|
93956
|
+
const tick3 = () => {
|
|
93957
|
+
const bot = deps.getBot();
|
|
93958
|
+
if (bot == null)
|
|
93959
|
+
return;
|
|
93960
|
+
sweepOutbox({
|
|
93961
|
+
stateDir: deps.stateDir,
|
|
93962
|
+
log: deps.log,
|
|
93963
|
+
send: async (chatId, threadId, text4) => {
|
|
93964
|
+
let lastId;
|
|
93965
|
+
for (let i = 0;i < text4.length; i += 4000) {
|
|
93966
|
+
const chunk2 = text4.slice(i, i + 4000);
|
|
93967
|
+
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" });
|
|
93968
|
+
lastId = res?.message_id;
|
|
93969
|
+
}
|
|
93970
|
+
return lastId;
|
|
93971
|
+
},
|
|
93972
|
+
textAlreadyDelivered: (chatId, threadId, text4) => deps.dedupCheck(chatId, threadId ?? undefined, text4),
|
|
93973
|
+
registryChainLookup: (taskId) => {
|
|
93974
|
+
const db3 = deps.getTurnsDb();
|
|
93975
|
+
if (db3 == null)
|
|
93976
|
+
return null;
|
|
93977
|
+
const turnKey3 = resolveSubagentOriginTurnKey(db3, taskId);
|
|
93978
|
+
return turnKey3 == null ? null : chatFromTurnKey(turnKey3);
|
|
93979
|
+
}
|
|
93980
|
+
}).catch((err) => deps.log?.(`outbox-sweep: tick failed: ${err.message}
|
|
93981
|
+
`));
|
|
93982
|
+
};
|
|
93983
|
+
const timer3 = setInterval(tick3, OUTBOX_SWEEP_INTERVAL_MS);
|
|
93984
|
+
timer3.unref?.();
|
|
93985
|
+
return timer3;
|
|
93986
|
+
}
|
|
93987
|
+
|
|
93616
93988
|
// ../src/build-info.ts
|
|
93617
|
-
var VERSION = "0.19.
|
|
93618
|
-
var COMMIT_SHA = "
|
|
93619
|
-
var COMMIT_DATE = "2026-07-
|
|
93620
|
-
var LATEST_PR =
|
|
93989
|
+
var VERSION = "0.19.14";
|
|
93990
|
+
var COMMIT_SHA = "1f1f69f8";
|
|
93991
|
+
var COMMIT_DATE = "2026-07-24T07:19:40+10:00";
|
|
93992
|
+
var LATEST_PR = 3514;
|
|
93621
93993
|
var COMMITS_AHEAD_OF_TAG = 0;
|
|
93622
93994
|
|
|
93623
93995
|
// gateway/boot-version.ts
|
|
@@ -93662,10 +94034,10 @@ function composeBootVersionString(inputs) {
|
|
|
93662
94034
|
}
|
|
93663
94035
|
|
|
93664
94036
|
// gateway/unhandled-rejection-policy.ts
|
|
93665
|
-
var
|
|
94037
|
+
var import_grammy14 = __toESM(require_mod2(), 1);
|
|
93666
94038
|
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
|
|
94039
|
+
const isGrammy = opts.isGrammyError != null ? opts.isGrammyError(err) : err instanceof import_grammy14.GrammyError;
|
|
94040
|
+
const isHttp = opts.isHttpError != null ? opts.isHttpError(err) : err instanceof import_grammy14.HttpError;
|
|
93669
94041
|
if (isHttp)
|
|
93670
94042
|
return "log_only";
|
|
93671
94043
|
if (err instanceof Error && err.message === FLOOD_WAIT_ACTIVE)
|
|
@@ -93694,12 +94066,12 @@ init_protocol();
|
|
|
93694
94066
|
init_peercred();
|
|
93695
94067
|
import * as net5 from "node:net";
|
|
93696
94068
|
import * as fs2 from "node:fs";
|
|
93697
|
-
import { homedir as
|
|
93698
|
-
import { join as
|
|
94069
|
+
import { homedir as homedir19 } from "node:os";
|
|
94070
|
+
import { join as join59 } from "node:path";
|
|
93699
94071
|
var DEFAULT_TIMEOUT_MS4 = 2000;
|
|
93700
94072
|
var UNLOCK_TIMEOUT_MS = 30000;
|
|
93701
|
-
var LEGACY_SOCKET_PATH2 =
|
|
93702
|
-
var OPERATOR_SOCKET_PATH2 =
|
|
94073
|
+
var LEGACY_SOCKET_PATH2 = join59(homedir19(), ".switchroom", "vault-broker.sock");
|
|
94074
|
+
var OPERATOR_SOCKET_PATH2 = join59(homedir19(), ".switchroom", "broker-operator", "sock");
|
|
93703
94075
|
function defaultBrokerSocketPath2() {
|
|
93704
94076
|
if (fs2.existsSync(OPERATOR_SOCKET_PATH2))
|
|
93705
94077
|
return OPERATOR_SOCKET_PATH2;
|
|
@@ -94581,8 +94953,8 @@ function resolveVaultApprovalPosture(broker) {
|
|
|
94581
94953
|
}
|
|
94582
94954
|
|
|
94583
94955
|
// registry/turns-schema.ts
|
|
94584
|
-
import { chmodSync as chmodSync12, mkdirSync as
|
|
94585
|
-
import { join as
|
|
94956
|
+
import { chmodSync as chmodSync12, mkdirSync as mkdirSync46 } from "fs";
|
|
94957
|
+
import { join as join60 } from "path";
|
|
94586
94958
|
var DatabaseClass3 = null;
|
|
94587
94959
|
function loadDatabaseClass3() {
|
|
94588
94960
|
if (DatabaseClass3 != null)
|
|
@@ -94654,9 +95026,9 @@ function applySchema(db3) {
|
|
|
94654
95026
|
}
|
|
94655
95027
|
function openTurnsDb(agentDir) {
|
|
94656
95028
|
const Database = loadDatabaseClass3();
|
|
94657
|
-
const dir =
|
|
94658
|
-
|
|
94659
|
-
const path2 =
|
|
95029
|
+
const dir = join60(agentDir, "telegram");
|
|
95030
|
+
mkdirSync46(dir, { recursive: true, mode: 448 });
|
|
95031
|
+
const path2 = join60(dir, "registry.db");
|
|
94660
95032
|
const db3 = new Database(path2, { create: true });
|
|
94661
95033
|
applySchema(db3);
|
|
94662
95034
|
try {
|
|
@@ -95000,7 +95372,7 @@ function selectResumeBuilder(endedVia, opts) {
|
|
|
95000
95372
|
}
|
|
95001
95373
|
|
|
95002
95374
|
// gateway/bridge-dead-watchdog.ts
|
|
95003
|
-
import { readFileSync as
|
|
95375
|
+
import { readFileSync as readFileSync57, writeFileSync as writeFileSync47, renameSync as renameSync21, unlinkSync as unlinkSync25 } from "node:fs";
|
|
95004
95376
|
|
|
95005
95377
|
// gateway/cron-session.ts
|
|
95006
95378
|
var CRON_IDENTITY_SUFFIX2 = "-cron";
|
|
@@ -95033,7 +95405,7 @@ function readFreshCrashLogTail(path2, opts = {}) {
|
|
|
95033
95405
|
const nowMs3 = opts.nowMs ?? Date.now();
|
|
95034
95406
|
const freshWindowMs = opts.freshWindowMs ?? CRASH_LOG_FRESH_WINDOW_MS;
|
|
95035
95407
|
const maxLines = opts.maxLines ?? CRASH_LOG_TAIL_LINES;
|
|
95036
|
-
const readFile = opts.readFile ?? ((p) =>
|
|
95408
|
+
const readFile = opts.readFile ?? ((p) => readFileSync57(p, "utf8"));
|
|
95037
95409
|
let raw;
|
|
95038
95410
|
try {
|
|
95039
95411
|
raw = readFile(path2);
|
|
@@ -95054,13 +95426,13 @@ function readFreshCrashLogTail(path2, opts = {}) {
|
|
|
95054
95426
|
}
|
|
95055
95427
|
function writeBridgeDeadEscalationMarker(path2, marker) {
|
|
95056
95428
|
const tmp = `${path2}.tmp-${process.pid}-${Date.now()}`;
|
|
95057
|
-
|
|
95058
|
-
|
|
95429
|
+
writeFileSync47(tmp, JSON.stringify(marker), "utf8");
|
|
95430
|
+
renameSync21(tmp, path2);
|
|
95059
95431
|
}
|
|
95060
95432
|
function consumeBridgeDeadEscalationMarker(path2, nowMs3 = Date.now(), maxAgeMs = ESCALATION_MARKER_MAX_AGE_MS) {
|
|
95061
95433
|
let marker = null;
|
|
95062
95434
|
try {
|
|
95063
|
-
const parsed = JSON.parse(
|
|
95435
|
+
const parsed = JSON.parse(readFileSync57(path2, "utf8"));
|
|
95064
95436
|
if (typeof parsed.ts === "number" && Number.isFinite(parsed.ts) && typeof parsed.reason === "string") {
|
|
95065
95437
|
const age = nowMs3 - parsed.ts;
|
|
95066
95438
|
if (age >= 0 && age < maxAgeMs) {
|
|
@@ -95072,7 +95444,7 @@ function consumeBridgeDeadEscalationMarker(path2, nowMs3 = Date.now(), maxAgeMs
|
|
|
95072
95444
|
}
|
|
95073
95445
|
} catch {}
|
|
95074
95446
|
try {
|
|
95075
|
-
|
|
95447
|
+
unlinkSync25(path2);
|
|
95076
95448
|
} catch {}
|
|
95077
95449
|
return marker;
|
|
95078
95450
|
}
|
|
@@ -95213,15 +95585,15 @@ function createBridgeDeadWatchdog(opts) {
|
|
|
95213
95585
|
}
|
|
95214
95586
|
|
|
95215
95587
|
// gateway/boot-probes.ts
|
|
95216
|
-
import { readFileSync as
|
|
95588
|
+
import { readFileSync as readFileSync58, readdirSync as readdirSync12, existsSync as existsSync53 } from "fs";
|
|
95217
95589
|
init_quota_cache();
|
|
95218
95590
|
init_quota_check();
|
|
95219
95591
|
import { execFile as execFileCb2 } from "child_process";
|
|
95220
95592
|
import { promisify as promisify2 } from "util";
|
|
95221
95593
|
var execFile2 = promisify2(execFileCb2);
|
|
95222
95594
|
var realProcFs2 = {
|
|
95223
|
-
readdir: (p) =>
|
|
95224
|
-
readFile: (p) =>
|
|
95595
|
+
readdir: (p) => readdirSync12(p),
|
|
95596
|
+
readFile: (p) => readFileSync58(p, "utf-8")
|
|
95225
95597
|
};
|
|
95226
95598
|
function findAgentProcessInContainer2(fs3 = realProcFs2) {
|
|
95227
95599
|
let entries;
|
|
@@ -95353,7 +95725,7 @@ function listNonTerminalSubagentsForTurn(db3, parentTurnKey) {
|
|
|
95353
95725
|
`).all(parentTurnKey);
|
|
95354
95726
|
return rows.map(mapSubagentRow);
|
|
95355
95727
|
}
|
|
95356
|
-
function
|
|
95728
|
+
function resolveSubagentOriginTurnKey2(db3, jsonlAgentId, maxHops = 5) {
|
|
95357
95729
|
const seen = new Set;
|
|
95358
95730
|
let currentJsonlId = jsonlAgentId;
|
|
95359
95731
|
for (let hop = 0;hop <= maxHops && currentJsonlId != null; hop++) {
|
|
@@ -95471,7 +95843,7 @@ if (isGatewayMain) {
|
|
|
95471
95843
|
shutdownAnalytics();
|
|
95472
95844
|
});
|
|
95473
95845
|
}
|
|
95474
|
-
var STATE_DIR = process.env.TELEGRAM_STATE_DIR ??
|
|
95846
|
+
var STATE_DIR = process.env.TELEGRAM_STATE_DIR ?? join61(homedir20(), ".claude", "channels", "telegram");
|
|
95475
95847
|
var permCardStore = createPermissionCardStore(STATE_DIR);
|
|
95476
95848
|
var BLOCKED_APPROVALS_DIR = process.env.SWITCHROOM_BLOCKED_APPROVALS_DIR ?? "/state/blocked-approvals";
|
|
95477
95849
|
var AGENT_NAME = process.env.SWITCHROOM_AGENT_NAME ?? "agent";
|
|
@@ -95510,7 +95882,7 @@ function alwaysAllowDrainDeps() {
|
|
|
95510
95882
|
return {
|
|
95511
95883
|
readConfigText: () => {
|
|
95512
95884
|
const cfgPath = process.env.SWITCHROOM_CONFIG ?? SWITCHROOM_CONFIG ?? findConfigFile2();
|
|
95513
|
-
return
|
|
95885
|
+
return readFileSync59(cfgPath, "utf8");
|
|
95514
95886
|
},
|
|
95515
95887
|
resolveAllowList: (_configText, agentName3) => {
|
|
95516
95888
|
const cfg = loadConfig2();
|
|
@@ -95571,11 +95943,11 @@ function scheduleAlwaysAllowPersistDrain() {
|
|
|
95571
95943
|
}, ALWAYS_ALLOW_DRAIN_INTERVAL_MS);
|
|
95572
95944
|
timer3.unref?.();
|
|
95573
95945
|
}
|
|
95574
|
-
var ACCESS_FILE =
|
|
95575
|
-
var APPROVED_DIR =
|
|
95576
|
-
var ENV_FILE =
|
|
95577
|
-
var INBOX_DIR =
|
|
95578
|
-
var PEOPLE_FILE =
|
|
95946
|
+
var ACCESS_FILE = join61(STATE_DIR, "access.json");
|
|
95947
|
+
var APPROVED_DIR = join61(STATE_DIR, "approved");
|
|
95948
|
+
var ENV_FILE = join61(STATE_DIR, ".env");
|
|
95949
|
+
var INBOX_DIR = join61(STATE_DIR, "inbox");
|
|
95950
|
+
var PEOPLE_FILE = join61(STATE_DIR, "people.json");
|
|
95579
95951
|
function triggerSelfRestart(targetAgent, reason, delayMs = 300) {
|
|
95580
95952
|
const isDocker = process.env.SWITCHROOM_RUNTIME === "docker";
|
|
95581
95953
|
const selfAgent = process.env.SWITCHROOM_AGENT_NAME;
|
|
@@ -95640,7 +96012,7 @@ function formatBootVersion() {
|
|
|
95640
96012
|
}
|
|
95641
96013
|
try {
|
|
95642
96014
|
chmodSync14(ENV_FILE, 384);
|
|
95643
|
-
for (const line of
|
|
96015
|
+
for (const line of readFileSync59(ENV_FILE, "utf8").split(`
|
|
95644
96016
|
`)) {
|
|
95645
96017
|
const m = line.match(/^(\w+)=(.*)$/);
|
|
95646
96018
|
if (m && process.env[m[1]] === undefined)
|
|
@@ -95661,7 +96033,7 @@ var bot;
|
|
|
95661
96033
|
var lastGetUpdatesHeartbeatMs = Date.now();
|
|
95662
96034
|
var GRAMMY_VERSION = (() => {
|
|
95663
96035
|
try {
|
|
95664
|
-
const raw =
|
|
96036
|
+
const raw = readFileSync59(new URL("../../node_modules/grammy/package.json", import.meta.url), "utf8");
|
|
95665
96037
|
return JSON.parse(raw).version ?? "unknown";
|
|
95666
96038
|
} catch {
|
|
95667
96039
|
return "unknown";
|
|
@@ -95724,7 +96096,7 @@ function assertSendable(f) {
|
|
|
95724
96096
|
} catch {
|
|
95725
96097
|
throw new Error(`refusing to send file \u2014 cannot resolve real path: ${f}`);
|
|
95726
96098
|
}
|
|
95727
|
-
const inbox =
|
|
96099
|
+
const inbox = join61(stateReal, "inbox");
|
|
95728
96100
|
if (real.startsWith(stateReal + sep4) && !real.startsWith(inbox + sep4)) {
|
|
95729
96101
|
throw new Error(`refusing to send channel state: ${f}`);
|
|
95730
96102
|
}
|
|
@@ -95743,7 +96115,7 @@ function assertSendable(f) {
|
|
|
95743
96115
|
}
|
|
95744
96116
|
function readAccessFile() {
|
|
95745
96117
|
try {
|
|
95746
|
-
const raw =
|
|
96118
|
+
const raw = readFileSync59(ACCESS_FILE, "utf8");
|
|
95747
96119
|
const parsed = JSON.parse(raw);
|
|
95748
96120
|
const allowFrom = validateStringArray("allowFrom", parsed.allowFrom ?? []);
|
|
95749
96121
|
const groups = {};
|
|
@@ -95783,7 +96155,7 @@ function readAccessFile() {
|
|
|
95783
96155
|
if (err.code === "ENOENT")
|
|
95784
96156
|
return defaultAccess();
|
|
95785
96157
|
try {
|
|
95786
|
-
|
|
96158
|
+
renameSync22(ACCESS_FILE, `${ACCESS_FILE}.corrupt-${Date.now()}`);
|
|
95787
96159
|
} catch {}
|
|
95788
96160
|
process.stderr.write(`telegram gateway: access.json is corrupt, moved aside. Starting fresh.
|
|
95789
96161
|
`);
|
|
@@ -95805,7 +96177,7 @@ function loadAccess() {
|
|
|
95805
96177
|
}
|
|
95806
96178
|
function readPeopleFile() {
|
|
95807
96179
|
try {
|
|
95808
|
-
const raw =
|
|
96180
|
+
const raw = readFileSync59(PEOPLE_FILE, "utf8");
|
|
95809
96181
|
const parsed = JSON.parse(raw);
|
|
95810
96182
|
if (!Array.isArray(parsed.entries))
|
|
95811
96183
|
return [];
|
|
@@ -95827,11 +96199,11 @@ function assertAllowedChat(chat_id) {
|
|
|
95827
96199
|
function saveAccess(a) {
|
|
95828
96200
|
if (STATIC)
|
|
95829
96201
|
return;
|
|
95830
|
-
|
|
96202
|
+
mkdirSync48(STATE_DIR, { recursive: true, mode: 448 });
|
|
95831
96203
|
const tmp = ACCESS_FILE + ".tmp";
|
|
95832
|
-
|
|
96204
|
+
writeFileSync49(tmp, JSON.stringify(a, null, 2) + `
|
|
95833
96205
|
`, { mode: 384 });
|
|
95834
|
-
|
|
96206
|
+
renameSync22(tmp, ACCESS_FILE);
|
|
95835
96207
|
}
|
|
95836
96208
|
function pruneExpired(a) {
|
|
95837
96209
|
const now = Date.now();
|
|
@@ -95849,7 +96221,7 @@ var HISTORY_ENABLED = HISTORY_ACCESS.historyEnabled !== false;
|
|
|
95849
96221
|
if (isGatewayMain && HISTORY_ENABLED) {
|
|
95850
96222
|
try {
|
|
95851
96223
|
initHistory(STATE_DIR, HISTORY_ACCESS.historyRetentionDays ?? 30);
|
|
95852
|
-
process.stderr.write(`telegram gateway: history capture enabled at ${
|
|
96224
|
+
process.stderr.write(`telegram gateway: history capture enabled at ${join61(STATE_DIR, "history.db")}
|
|
95853
96225
|
`);
|
|
95854
96226
|
} catch (err) {
|
|
95855
96227
|
process.stderr.write(`telegram gateway: history init failed (${err.message}) \u2014 capture disabled
|
|
@@ -95868,12 +96240,12 @@ if (isGatewayMain)
|
|
|
95868
96240
|
let markerTurnKey = null;
|
|
95869
96241
|
let markerAgeMs = null;
|
|
95870
96242
|
try {
|
|
95871
|
-
const markerPath =
|
|
95872
|
-
if (
|
|
95873
|
-
const st =
|
|
96243
|
+
const markerPath = join61(STATE_DIR, TURN_ACTIVE_MARKER_FILE2);
|
|
96244
|
+
if (existsSync55(markerPath)) {
|
|
96245
|
+
const st = statSync20(markerPath);
|
|
95874
96246
|
markerAgeMs = Date.now() - st.mtimeMs;
|
|
95875
96247
|
try {
|
|
95876
|
-
const payload = JSON.parse(
|
|
96248
|
+
const payload = JSON.parse(readFileSync59(markerPath, "utf8"));
|
|
95877
96249
|
if (typeof payload.turnKey === "string" && payload.turnKey.length > 0) {
|
|
95878
96250
|
markerTurnKey = payload.turnKey;
|
|
95879
96251
|
}
|
|
@@ -95893,10 +96265,10 @@ if (isGatewayMain)
|
|
|
95893
96265
|
process.stderr.write(`telegram gateway: turn-registry boot-reaper stamped ${reaped} orphaned turn(s)` + `${timeoutTurnKey ? ` (turnKey=${timeoutTurnKey} as 'timeout', markerAgeMs=${markerAgeMs})` : " as 'restart'"}
|
|
95894
96266
|
`);
|
|
95895
96267
|
} else {
|
|
95896
|
-
process.stderr.write(`telegram gateway: turn-registry initialized at ${
|
|
96268
|
+
process.stderr.write(`telegram gateway: turn-registry initialized at ${join61(agentDir, "telegram", "registry.db")}
|
|
95897
96269
|
`);
|
|
95898
96270
|
}
|
|
95899
|
-
const bridgeDeadMarker = consumeBridgeDeadEscalationMarker(
|
|
96271
|
+
const bridgeDeadMarker = consumeBridgeDeadEscalationMarker(join61(STATE_DIR, "bridge-dead-escalation.json"));
|
|
95900
96272
|
if (bridgeDeadMarker != null) {
|
|
95901
96273
|
bridgeDeadPriorStreak = bridgeDeadMarker.count ?? 1;
|
|
95902
96274
|
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 +96281,7 @@ if (isGatewayMain)
|
|
|
95909
96281
|
const pending2 = findLatestTurnIfInterrupted(turnsDb);
|
|
95910
96282
|
const selfAgent = process.env.SWITCHROOM_AGENT_NAME ?? "";
|
|
95911
96283
|
if (pending2 != null && selfAgent) {
|
|
95912
|
-
const bootResumeMarkerPath = process.env.SWITCHROOM_GATEWAY_CLEAN_SHUTDOWN_MARKER ??
|
|
96284
|
+
const bootResumeMarkerPath = process.env.SWITCHROOM_GATEWAY_CLEAN_SHUTDOWN_MARKER ?? join61(STATE_DIR, "clean-shutdown.json");
|
|
95913
96285
|
const bootResumeCleanMarker = readCleanShutdownMarker(bootResumeMarkerPath);
|
|
95914
96286
|
const bootResumeForceAlways = process.env.SWITCHROOM_BOOT_RESUME_ALWAYS === "1";
|
|
95915
96287
|
const bootResumeMode = parseBootResumeMode(process.env.SWITCHROOM_BOOT_RESUME);
|
|
@@ -96022,7 +96394,7 @@ if (isGatewayMain)
|
|
|
96022
96394
|
`);
|
|
96023
96395
|
}
|
|
96024
96396
|
}
|
|
96025
|
-
const pendingEnvPath =
|
|
96397
|
+
const pendingEnvPath = join61(agentDir, ".pending-turn.env");
|
|
96026
96398
|
try {
|
|
96027
96399
|
if (pending2 != null) {
|
|
96028
96400
|
const lines = [
|
|
@@ -96036,13 +96408,13 @@ if (isGatewayMain)
|
|
|
96036
96408
|
pending2.interrupt_reason != null ? `SWITCHROOM_PENDING_INTERRUPT_REASON=${pending2.interrupt_reason}` : `SWITCHROOM_PENDING_INTERRUPT_REASON=`
|
|
96037
96409
|
];
|
|
96038
96410
|
const pendingEnvTmp = `${pendingEnvPath}.tmp-${process.pid}`;
|
|
96039
|
-
|
|
96411
|
+
writeFileSync49(pendingEnvTmp, lines.join(`
|
|
96040
96412
|
`) + `
|
|
96041
96413
|
`, { mode: 384 });
|
|
96042
|
-
|
|
96414
|
+
renameSync22(pendingEnvTmp, pendingEnvPath);
|
|
96043
96415
|
process.stderr.write(`telegram gateway: pending-turn env written to ${pendingEnvPath} turnKey=${pending2.turn_key} endedVia=${pending2.ended_via ?? "open"}
|
|
96044
96416
|
`);
|
|
96045
|
-
} else if (
|
|
96417
|
+
} else if (existsSync55(pendingEnvPath)) {
|
|
96046
96418
|
rmSync6(pendingEnvPath, { force: true });
|
|
96047
96419
|
process.stderr.write(`telegram gateway: pending-turn env cleared (clean previous shutdown)
|
|
96048
96420
|
`);
|
|
@@ -96060,7 +96432,7 @@ function resolveSubagentOriginChat(agentId) {
|
|
|
96060
96432
|
if (turnsDb == null)
|
|
96061
96433
|
return null;
|
|
96062
96434
|
try {
|
|
96063
|
-
const originKey =
|
|
96435
|
+
const originKey = resolveSubagentOriginTurnKey2(turnsDb, agentId);
|
|
96064
96436
|
if (originKey == null)
|
|
96065
96437
|
return null;
|
|
96066
96438
|
const turn = getTurnByKey(turnsDb, originKey);
|
|
@@ -96145,12 +96517,12 @@ if (isGatewayMain && !STATIC) {
|
|
|
96145
96517
|
function checkApprovals() {
|
|
96146
96518
|
let files;
|
|
96147
96519
|
try {
|
|
96148
|
-
files =
|
|
96520
|
+
files = readdirSync14(APPROVED_DIR);
|
|
96149
96521
|
} catch {
|
|
96150
96522
|
return;
|
|
96151
96523
|
}
|
|
96152
96524
|
for (const senderId of files) {
|
|
96153
|
-
const file =
|
|
96525
|
+
const file = join61(APPROVED_DIR, senderId);
|
|
96154
96526
|
bot.api.sendMessage(senderId, "Paired! Say hi to Claude.").then(() => rmSync6(file, { force: true }), (err) => {
|
|
96155
96527
|
process.stderr.write(`telegram gateway: failed to send approval confirm: ${err}
|
|
96156
96528
|
`);
|
|
@@ -96323,12 +96695,12 @@ function noteAgentOutputAt(key, ts) {
|
|
|
96323
96695
|
lastAgentOutputAt.delete(oldest);
|
|
96324
96696
|
}
|
|
96325
96697
|
}
|
|
96326
|
-
var OBLIGATION_STORE_PATH =
|
|
96698
|
+
var OBLIGATION_STORE_PATH = join61(STATE_DIR, "obligations.json");
|
|
96327
96699
|
var obligationStoreFs = {
|
|
96328
|
-
readFileSync: (p) =>
|
|
96329
|
-
writeFileSync: (p, d) =>
|
|
96330
|
-
renameSync: (a, b) =>
|
|
96331
|
-
existsSync: (p) =>
|
|
96700
|
+
readFileSync: (p) => readFileSync59(p, "utf8"),
|
|
96701
|
+
writeFileSync: (p, d) => writeFileSync49(p, d),
|
|
96702
|
+
renameSync: (a, b) => renameSync22(a, b),
|
|
96703
|
+
existsSync: (p) => existsSync55(p)
|
|
96332
96704
|
};
|
|
96333
96705
|
var obligationLedger = new ObligationLedger(OBLIGATION_REPRESENT_MAX, {
|
|
96334
96706
|
onChange: STATIC || !OBLIGATION_LEDGER_ENABLED ? undefined : (snapshot) => persistObligations(OBLIGATION_STORE_PATH, obligationStoreFs, snapshot)
|
|
@@ -97102,14 +97474,14 @@ function emitTurnRecord(turn, endedAt) {
|
|
|
97102
97474
|
maybeRotate(turnsPath, {
|
|
97103
97475
|
statSize: (p) => {
|
|
97104
97476
|
try {
|
|
97105
|
-
return
|
|
97477
|
+
return statSync20(p).size;
|
|
97106
97478
|
} catch {
|
|
97107
97479
|
return;
|
|
97108
97480
|
}
|
|
97109
97481
|
},
|
|
97110
|
-
rename: (from, to) =>
|
|
97482
|
+
rename: (from, to) => renameSync22(from, to)
|
|
97111
97483
|
});
|
|
97112
|
-
|
|
97484
|
+
appendFileSync8(turnsPath, rec);
|
|
97113
97485
|
} catch {}
|
|
97114
97486
|
}
|
|
97115
97487
|
function maybeProactiveCompact() {
|
|
@@ -97591,7 +97963,7 @@ function wrapBootCardApi(threadId) {
|
|
|
97591
97963
|
await lockedBot.api.editMessageText(cid, mid, richMessage2(text5), editOpts);
|
|
97592
97964
|
return "edited";
|
|
97593
97965
|
} catch (err) {
|
|
97594
|
-
const desc = err instanceof
|
|
97966
|
+
const desc = err instanceof import_grammy16.GrammyError ? err.description : err instanceof Error ? err.message : String(err);
|
|
97595
97967
|
if (typeof desc === "string" && desc.toLowerCase().includes("not modified"))
|
|
97596
97968
|
return "edited";
|
|
97597
97969
|
return "gone";
|
|
@@ -97735,7 +98107,7 @@ function sweepStaleAlwaysAllowCorrelations(now = Date.now()) {
|
|
|
97735
98107
|
var MENTAL_MODEL_CORRELATION_TTL_MS = 720000;
|
|
97736
98108
|
var pendingMentalModelCorrelations = createSweepableStore((entry, now) => now - entry.createdAt > MENTAL_MODEL_CORRELATION_TTL_MS);
|
|
97737
98109
|
function mentalModelCorrelationKey(agentName3, unifiedDiff) {
|
|
97738
|
-
return `${agentName3}::${
|
|
98110
|
+
return `${agentName3}::${createHash5("sha256").update(unifiedDiff).digest("hex")}`;
|
|
97739
98111
|
}
|
|
97740
98112
|
function sweepStaleMentalModelCorrelations(now = Date.now()) {
|
|
97741
98113
|
pendingMentalModelCorrelations.sweep(now);
|
|
@@ -98671,28 +99043,28 @@ var statusPinState = new Map;
|
|
|
98671
99043
|
var statusPinChatIds = new Map;
|
|
98672
99044
|
var statusPinPinnedAt = new Map;
|
|
98673
99045
|
var statusPinRightsCache = new PinRightsCache2;
|
|
98674
|
-
var STATUS_PIN_STORE_PATH =
|
|
99046
|
+
var STATUS_PIN_STORE_PATH = join61(STATE_DIR, "status-pins.json");
|
|
98675
99047
|
var statusPinStoreFs = {
|
|
98676
|
-
readFileSync: (p) =>
|
|
98677
|
-
writeFileSync: (p, d) =>
|
|
98678
|
-
renameSync: (a, b) =>
|
|
98679
|
-
existsSync: (p) =>
|
|
99048
|
+
readFileSync: (p) => readFileSync59(p, "utf8"),
|
|
99049
|
+
writeFileSync: (p, d) => writeFileSync49(p, d),
|
|
99050
|
+
renameSync: (a, b) => renameSync22(a, b),
|
|
99051
|
+
existsSync: (p) => existsSync55(p)
|
|
98680
99052
|
};
|
|
98681
99053
|
var statusPinPersistEnabled = !STATIC && PIN_STATUS_WHILE_WORKING;
|
|
98682
|
-
var ACTIVITY_CARD_STORE_PATH =
|
|
99054
|
+
var ACTIVITY_CARD_STORE_PATH = join61(STATE_DIR, "activity-cards-pending.json");
|
|
98683
99055
|
var activityCardStoreFs = {
|
|
98684
|
-
readFileSync: (p) =>
|
|
98685
|
-
writeFileSync: (p, d) =>
|
|
98686
|
-
renameSync: (a, b) =>
|
|
98687
|
-
existsSync: (p) =>
|
|
99056
|
+
readFileSync: (p) => readFileSync59(p, "utf8"),
|
|
99057
|
+
writeFileSync: (p, d) => writeFileSync49(p, d),
|
|
99058
|
+
renameSync: (a, b) => renameSync22(a, b),
|
|
99059
|
+
existsSync: (p) => existsSync55(p)
|
|
98688
99060
|
};
|
|
98689
99061
|
var activityCardPersistEnabled = !STATIC;
|
|
98690
|
-
var QUEUED_CARD_STORE_PATH =
|
|
99062
|
+
var QUEUED_CARD_STORE_PATH = join61(STATE_DIR, "queued-cards-pending.json");
|
|
98691
99063
|
var queuedCardStoreFs = {
|
|
98692
|
-
readFileSync: (p) =>
|
|
98693
|
-
writeFileSync: (p, d) =>
|
|
98694
|
-
renameSync: (a, b) =>
|
|
98695
|
-
existsSync: (p) =>
|
|
99064
|
+
readFileSync: (p) => readFileSync59(p, "utf8"),
|
|
99065
|
+
writeFileSync: (p, d) => writeFileSync49(p, d),
|
|
99066
|
+
renameSync: (a, b) => renameSync22(a, b),
|
|
99067
|
+
existsSync: (p) => existsSync55(p)
|
|
98696
99068
|
};
|
|
98697
99069
|
var queuedCardPersistEnabled = !STATIC;
|
|
98698
99070
|
function persistQueuedCard(key, chatId, threadId, messageId) {
|
|
@@ -99065,12 +99437,12 @@ var getPinnedProgressCardMessageId = null;
|
|
|
99065
99437
|
var completeProgressCardTurn = null;
|
|
99066
99438
|
var subagentWatcher = null;
|
|
99067
99439
|
var workerActivityFeed = null;
|
|
99068
|
-
var SOCKET_PATH = process.env.SWITCHROOM_GATEWAY_SOCKET ??
|
|
99440
|
+
var SOCKET_PATH = process.env.SWITCHROOM_GATEWAY_SOCKET ?? join61(STATE_DIR, "gateway.sock");
|
|
99069
99441
|
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 ??
|
|
99442
|
+
mkdirSync48(STATE_DIR, { recursive: true, mode: 448 });
|
|
99443
|
+
var GATEWAY_PID_PATH = process.env.SWITCHROOM_GATEWAY_PID_FILE ?? join61(STATE_DIR, "gateway.pid.json");
|
|
99444
|
+
var GATEWAY_SESSION_MARKER_PATH = process.env.SWITCHROOM_GATEWAY_SESSION_MARKER ?? join61(STATE_DIR, "gateway-session.json");
|
|
99445
|
+
var GATEWAY_CLEAN_SHUTDOWN_MARKER_PATH = process.env.SWITCHROOM_GATEWAY_CLEAN_SHUTDOWN_MARKER ?? join61(STATE_DIR, "clean-shutdown.json");
|
|
99074
99446
|
var GATEWAY_STARTED_AT_MS = Date.now();
|
|
99075
99447
|
var BOOT_CARD_ENABLED = process.env.SWITCHROOM_BOOT_CARD !== "false";
|
|
99076
99448
|
var activeBootCard = null;
|
|
@@ -99099,7 +99471,7 @@ function ensureIssuesCard(chatId, threadId) {
|
|
|
99099
99471
|
bot: botApi,
|
|
99100
99472
|
log: (msg) => process.stderr.write(`telegram gateway: ${msg}
|
|
99101
99473
|
`),
|
|
99102
|
-
persistPath:
|
|
99474
|
+
persistPath: join61(stateDir, "issues-card.json")
|
|
99103
99475
|
});
|
|
99104
99476
|
activeIssuesWatcher = startIssuesWatcher({
|
|
99105
99477
|
stateDir,
|
|
@@ -99275,6 +99647,7 @@ function runDeliveryConfirmSweep() {
|
|
|
99275
99647
|
}
|
|
99276
99648
|
var _deliveryConfirmSweep = isGatewayMain ? setInterval(runDeliveryConfirmSweep, DELIVERY_CONFIRM_SWEEP_MS) : undefined;
|
|
99277
99649
|
_deliveryConfirmSweep?.unref?.();
|
|
99650
|
+
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
99651
|
if (isGatewayMain)
|
|
99279
99652
|
startTimer2({
|
|
99280
99653
|
editMessage: async (ctx) => {
|
|
@@ -99296,14 +99669,14 @@ if (isGatewayMain)
|
|
|
99296
99669
|
var inboundSpool;
|
|
99297
99670
|
if (isGatewayMain)
|
|
99298
99671
|
inboundSpool = STATIC ? undefined : createInboundSpool({
|
|
99299
|
-
path:
|
|
99672
|
+
path: join61(STATE_DIR, "inbound-spool.jsonl"),
|
|
99300
99673
|
fs: {
|
|
99301
|
-
appendFileSync: (p, d) =>
|
|
99302
|
-
readFileSync: (p) =>
|
|
99303
|
-
writeFileSync: (p, d) =>
|
|
99304
|
-
renameSync: (a, b) =>
|
|
99305
|
-
existsSync: (p) =>
|
|
99306
|
-
statSizeSync: (p) =>
|
|
99674
|
+
appendFileSync: (p, d) => appendFileSync8(p, d),
|
|
99675
|
+
readFileSync: (p) => readFileSync59(p, "utf8"),
|
|
99676
|
+
writeFileSync: (p, d) => writeFileSync49(p, d),
|
|
99677
|
+
renameSync: (a, b) => renameSync22(a, b),
|
|
99678
|
+
existsSync: (p) => existsSync55(p),
|
|
99679
|
+
statSizeSync: (p) => statSync20(p).size
|
|
99307
99680
|
},
|
|
99308
99681
|
onDegraded: (info) => {
|
|
99309
99682
|
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 +99737,13 @@ async function maybeRedeliverUndeliveredAnswer() {
|
|
|
99364
99737
|
let transcriptText;
|
|
99365
99738
|
try {
|
|
99366
99739
|
const projectsDir = getProjectsDirForCwd();
|
|
99367
|
-
const path2 =
|
|
99368
|
-
if (!
|
|
99740
|
+
const path2 = join61(projectsDir, `${sessionId}.jsonl`);
|
|
99741
|
+
if (!existsSync55(path2)) {
|
|
99369
99742
|
process.stderr.write(`telegram gateway: crash-redelivery \u2014 transcript not found for turnKey=${turn.turn_key} session=${sessionId} (${path2}); skipping
|
|
99370
99743
|
`);
|
|
99371
99744
|
return;
|
|
99372
99745
|
}
|
|
99373
|
-
transcriptText =
|
|
99746
|
+
transcriptText = readFileSync59(path2, "utf8");
|
|
99374
99747
|
} catch (err) {
|
|
99375
99748
|
process.stderr.write(`telegram gateway: crash-redelivery \u2014 transcript read failed turnKey=${turn.turn_key}: ${err.message}
|
|
99376
99749
|
`);
|
|
@@ -99467,7 +99840,7 @@ if (isGatewayMain && inboundSpool != null) {
|
|
|
99467
99840
|
}
|
|
99468
99841
|
var pendingPermissionBuffer = createPendingPermissionBuffer();
|
|
99469
99842
|
function buildPermissionActionRow(requestId, showAlways) {
|
|
99470
|
-
const kb = new
|
|
99843
|
+
const kb = new import_grammy16.InlineKeyboard().text("\u274C Deny", `perm:deny:${requestId}`).text("\u2705 Allow", `perm:allow:${requestId}`);
|
|
99471
99844
|
if (showAlways)
|
|
99472
99845
|
kb.text("\uD83D\uDD01 Always\u2026", `perm:always:${requestId}`);
|
|
99473
99846
|
return kb;
|
|
@@ -99529,8 +99902,8 @@ var bridgeDeadWatchdog = createBridgeDeadWatchdog({
|
|
|
99529
99902
|
isSessionAlive: () => findAgentProcessInContainer2()?.comm === "claude",
|
|
99530
99903
|
isShuttingDown: () => shuttingDown,
|
|
99531
99904
|
escalate: (reason) => triggerSelfRestart(process.env.SWITCHROOM_AGENT_NAME ?? "", reason, 1500),
|
|
99532
|
-
crashLogPath:
|
|
99533
|
-
markerPath:
|
|
99905
|
+
crashLogPath: join61(STATE_DIR, "bridge-crash.log"),
|
|
99906
|
+
markerPath: join61(STATE_DIR, "bridge-dead-escalation.json"),
|
|
99534
99907
|
log: (line) => process.stderr.write(`${line}
|
|
99535
99908
|
`),
|
|
99536
99909
|
priorStreak: bridgeDeadPriorStreak,
|
|
@@ -99636,8 +100009,8 @@ if (isGatewayMain)
|
|
|
99636
100009
|
probeQuotaViaBroker: (t) => probeQuotaForBootCard(agentSlug, t),
|
|
99637
100010
|
tmuxSupervisor: process.env.SWITCHROOM_TMUX_SUPERVISOR === "1",
|
|
99638
100011
|
dockerMode: process.env.SWITCHROOM_RUNTIME === "docker",
|
|
99639
|
-
configSnapshotPath:
|
|
99640
|
-
bootCardStatePath:
|
|
100012
|
+
configSnapshotPath: join61(resolvedAgentDirForCard, ".config-snapshot.json"),
|
|
100013
|
+
bootCardStatePath: join61(resolvedAgentDirForCard, ".boot-card-msgid.json"),
|
|
99641
100014
|
floodStatePath: FLOOD_STATE_PATH,
|
|
99642
100015
|
...updateOutcomeLine ? { updateOutcomeLine } : {}
|
|
99643
100016
|
}, ackMsgId).then((handle) => {
|
|
@@ -100041,7 +100414,7 @@ if (isGatewayMain)
|
|
|
100041
100414
|
}
|
|
100042
100415
|
},
|
|
100043
100416
|
postAttachment: async (args) => {
|
|
100044
|
-
const input = new
|
|
100417
|
+
const input = new import_grammy16.InputFile(Buffer.from(args.content, "utf8"), args.filename);
|
|
100045
100418
|
await robustApiCall(() => bot.api.sendDocument(args.chatId, input, {
|
|
100046
100419
|
...args.threadId !== undefined ? { message_thread_id: args.threadId } : {}
|
|
100047
100420
|
}), {
|
|
@@ -100319,7 +100692,7 @@ if (isGatewayMain)
|
|
|
100319
100692
|
const receiverUid = receiverUidRaw ? Number(receiverUidRaw) : NaN;
|
|
100320
100693
|
if (Number.isInteger(receiverUid))
|
|
100321
100694
|
allowedUids.push(receiverUid);
|
|
100322
|
-
const socketPath =
|
|
100695
|
+
const socketPath = join61(STATE_DIR, "webhook.sock");
|
|
100323
100696
|
const webhookInject = (agentName3, inbound) => {
|
|
100324
100697
|
const msg = inbound;
|
|
100325
100698
|
const delivered = ipcServer.sendToAgent(agentName3, msg);
|
|
@@ -100547,9 +100920,9 @@ function redactOutboundText(text5, site) {
|
|
|
100547
100920
|
var VOICE_OUT_DEFAULT_CHUNK_CHARS = 600;
|
|
100548
100921
|
var VOICE_OUT_HARD_CHUNK_CAP = 4096;
|
|
100549
100922
|
var voiceOnDemandCache = new VoiceOnDemandCache({
|
|
100550
|
-
persistPath:
|
|
100923
|
+
persistPath: join61(STATE_DIR, "voice-ondemand.json")
|
|
100551
100924
|
});
|
|
100552
|
-
var VOICE_CACHE_DIR =
|
|
100925
|
+
var VOICE_CACHE_DIR = join61(STATE_DIR, "voice-cache");
|
|
100553
100926
|
var voicePreSynthQueue = new PreSynthQueue({
|
|
100554
100927
|
runJob: async (job) => {
|
|
100555
100928
|
const sidecarToken = await materializeSidecarToken2();
|
|
@@ -100816,7 +101189,7 @@ async function executeAskUser(rawArgs) {
|
|
|
100816
101189
|
}
|
|
100817
101190
|
}
|
|
100818
101191
|
const askId = generateAskId();
|
|
100819
|
-
const keyboard = new
|
|
101192
|
+
const keyboard = new import_grammy16.InlineKeyboard;
|
|
100820
101193
|
for (let i = 0;i < args.options.length; i++) {
|
|
100821
101194
|
keyboard.text(args.options[i], encodeAskCallback(askId, i));
|
|
100822
101195
|
if (i < args.options.length - 1)
|
|
@@ -100951,11 +101324,11 @@ async function executeSendGif(rawArgs) {
|
|
|
100951
101324
|
};
|
|
100952
101325
|
}
|
|
100953
101326
|
async function publishToTelegraph(text5, shortName, authorName) {
|
|
100954
|
-
const accountPath =
|
|
101327
|
+
const accountPath = join61(STATE_DIR, "telegraph-account.json");
|
|
100955
101328
|
let account = null;
|
|
100956
101329
|
try {
|
|
100957
|
-
if (
|
|
100958
|
-
const raw =
|
|
101330
|
+
if (existsSync55(accountPath)) {
|
|
101331
|
+
const raw = readFileSync59(accountPath, "utf-8");
|
|
100959
101332
|
const parsed = JSON.parse(raw);
|
|
100960
101333
|
if (parsed.shortName && parsed.accessToken) {
|
|
100961
101334
|
account = parsed;
|
|
@@ -100974,8 +101347,8 @@ async function publishToTelegraph(text5, shortName, authorName) {
|
|
|
100974
101347
|
}
|
|
100975
101348
|
account = created.value;
|
|
100976
101349
|
try {
|
|
100977
|
-
|
|
100978
|
-
|
|
101350
|
+
mkdirSync48(STATE_DIR, { recursive: true, mode: 448 });
|
|
101351
|
+
writeFileSync49(accountPath, JSON.stringify(account, null, 2), { mode: 384 });
|
|
100979
101352
|
} catch (err) {
|
|
100980
101353
|
process.stderr.write(`telegram gateway: telegraph cache write failed: ${err.message}
|
|
100981
101354
|
`);
|
|
@@ -101091,7 +101464,7 @@ _The secret was NOT saved. The agent can re-request with \`request_secret\`._`,
|
|
|
101091
101464
|
}
|
|
101092
101465
|
function readLiveSwitchroomConfigText() {
|
|
101093
101466
|
const cfgPath = process.env.SWITCHROOM_CONFIG ?? findConfigFile2();
|
|
101094
|
-
return
|
|
101467
|
+
return readFileSync59(cfgPath, "utf8");
|
|
101095
101468
|
}
|
|
101096
101469
|
async function executeReact(args) {
|
|
101097
101470
|
if (!args.chat_id)
|
|
@@ -101130,9 +101503,9 @@ async function executeDownloadAttachment(args) {
|
|
|
101130
101503
|
fileUniqueId: file.file_unique_id,
|
|
101131
101504
|
now: Date.now()
|
|
101132
101505
|
});
|
|
101133
|
-
|
|
101506
|
+
mkdirSync48(INBOX_DIR, { recursive: true, mode: 448 });
|
|
101134
101507
|
assertInsideInbox2(INBOX_DIR, dlPath);
|
|
101135
|
-
|
|
101508
|
+
writeFileSync49(dlPath, buf, { mode: 384 });
|
|
101136
101509
|
return { content: [{ type: "text", text: dlPath }] };
|
|
101137
101510
|
}
|
|
101138
101511
|
async function executeEditMessage(args) {
|
|
@@ -101146,23 +101519,20 @@ async function executeEditMessage(args) {
|
|
|
101146
101519
|
const editAccess = loadAccess();
|
|
101147
101520
|
const editFormat = args.format ?? (editAccess.parseMode ?? "html");
|
|
101148
101521
|
const editLiteralText = editFormat === "text";
|
|
101149
|
-
|
|
101150
|
-
|
|
101151
|
-
|
|
101152
|
-
|
|
101153
|
-
|
|
101154
|
-
|
|
101155
|
-
|
|
101156
|
-
|
|
101157
|
-
|
|
101158
|
-
|
|
101159
|
-
|
|
101160
|
-
|
|
101161
|
-
|
|
101162
|
-
|
|
101163
|
-
site: "edit_message"
|
|
101164
|
-
});
|
|
101165
|
-
}
|
|
101522
|
+
const _editNorm = normalizeOutboundBody(args.text, "edit_message", redactOutboundText, {
|
|
101523
|
+
literalText: editLiteralText,
|
|
101524
|
+
addSpacers: !editLiteralText,
|
|
101525
|
+
tz: resolveEnvTimezone2(),
|
|
101526
|
+
nowMs: Date.now()
|
|
101527
|
+
});
|
|
101528
|
+
let editRawText = _editNorm.text;
|
|
101529
|
+
if (_editNorm.voiceReplaced > 0) {
|
|
101530
|
+
emitRuntimeMetric({
|
|
101531
|
+
kind: "voice_scrub_applied",
|
|
101532
|
+
chatKey: statusKey(String(args.chat_id ?? ""), undefined),
|
|
101533
|
+
replaced: _editNorm.voiceReplaced,
|
|
101534
|
+
site: "edit_message"
|
|
101535
|
+
});
|
|
101166
101536
|
}
|
|
101167
101537
|
const edited = await robustApiCall(() => lockedBot.api.editMessageText(String(args.chat_id ?? ""), Number(args.message_id), editLiteralText ? editRawText : richMessage2(editRawText)));
|
|
101168
101538
|
const id = typeof edited === "object" && edited ? edited.message_id : args.message_id;
|
|
@@ -102495,14 +102865,14 @@ function restartMarkerPath() {
|
|
|
102495
102865
|
const agentDir = resolveAgentDirFromEnv();
|
|
102496
102866
|
if (!agentDir)
|
|
102497
102867
|
return null;
|
|
102498
|
-
return
|
|
102868
|
+
return join61(agentDir, "restart-pending.json");
|
|
102499
102869
|
}
|
|
102500
102870
|
function writeRestartMarker(marker) {
|
|
102501
102871
|
const p = restartMarkerPath();
|
|
102502
102872
|
if (!p)
|
|
102503
102873
|
return;
|
|
102504
102874
|
try {
|
|
102505
|
-
|
|
102875
|
+
writeFileSync49(p, JSON.stringify(marker));
|
|
102506
102876
|
lastPlannedRestartAt = Date.now();
|
|
102507
102877
|
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
102878
|
`);
|
|
@@ -102521,7 +102891,7 @@ function readRestartMarker() {
|
|
|
102521
102891
|
if (!p)
|
|
102522
102892
|
return null;
|
|
102523
102893
|
try {
|
|
102524
|
-
return JSON.parse(
|
|
102894
|
+
return JSON.parse(readFileSync59(p, "utf8"));
|
|
102525
102895
|
} catch {
|
|
102526
102896
|
return null;
|
|
102527
102897
|
}
|
|
@@ -102670,7 +103040,7 @@ var _dockerReachable;
|
|
|
102670
103040
|
function isDockerReachable() {
|
|
102671
103041
|
if (_dockerReachable !== undefined)
|
|
102672
103042
|
return _dockerReachable;
|
|
102673
|
-
if (!
|
|
103043
|
+
if (!existsSync55("/var/run/docker.sock")) {
|
|
102674
103044
|
_dockerReachable = false;
|
|
102675
103045
|
return _dockerReachable;
|
|
102676
103046
|
}
|
|
@@ -102687,12 +103057,12 @@ function _resetDockerReachableCache() {
|
|
|
102687
103057
|
}
|
|
102688
103058
|
function spawnSwitchroomDetached(args, onFailure) {
|
|
102689
103059
|
const fullArgs = SWITCHROOM_CONFIG ? ["--config", SWITCHROOM_CONFIG, ...args] : args;
|
|
102690
|
-
const logPath =
|
|
103060
|
+
const logPath = join61(STATE_DIR, "detached-spawn.log");
|
|
102691
103061
|
let outFd = null;
|
|
102692
103062
|
try {
|
|
102693
|
-
|
|
103063
|
+
mkdirSync48(STATE_DIR, { recursive: true });
|
|
102694
103064
|
outFd = openSync12(logPath, "a");
|
|
102695
|
-
|
|
103065
|
+
writeFileSync49(logPath, `
|
|
102696
103066
|
[${new Date().toISOString()}] spawn ${SWITCHROOM_CLI} ${fullArgs.join(" ")}
|
|
102697
103067
|
`, { flag: "a" });
|
|
102698
103068
|
} catch {}
|
|
@@ -102718,7 +103088,7 @@ function spawnSwitchroomDetached(args, onFailure) {
|
|
|
102718
103088
|
return;
|
|
102719
103089
|
let tail = "";
|
|
102720
103090
|
try {
|
|
102721
|
-
const full =
|
|
103091
|
+
const full = readFileSync59(logPath, "utf8");
|
|
102722
103092
|
tail = full.split(`
|
|
102723
103093
|
`).slice(-30).join(`
|
|
102724
103094
|
`).trim();
|
|
@@ -102980,10 +103350,10 @@ ${preBlock(formatSwitchroomOutput(detail))}`, { html: true });
|
|
|
102980
103350
|
}
|
|
102981
103351
|
function readRecentDenialsForAgent(agentName3, windowMs, limit) {
|
|
102982
103352
|
try {
|
|
102983
|
-
const auditPath =
|
|
102984
|
-
if (!
|
|
103353
|
+
const auditPath = join61(homedir20(), ".switchroom", "vault-audit.log");
|
|
103354
|
+
if (!existsSync55(auditPath))
|
|
102985
103355
|
return [];
|
|
102986
|
-
const raw =
|
|
103356
|
+
const raw = readFileSync59(auditPath, "utf8");
|
|
102987
103357
|
return recentDenialsFromAuditLog(raw, { agentName: agentName3, windowMs, limit });
|
|
102988
103358
|
} catch {
|
|
102989
103359
|
return [];
|
|
@@ -103034,7 +103404,7 @@ async function buildAgentMetadata(agentName3) {
|
|
|
103034
103404
|
try {
|
|
103035
103405
|
const agentDir = resolveAgentDirFromEnv();
|
|
103036
103406
|
if (agentDir) {
|
|
103037
|
-
const raw =
|
|
103407
|
+
const raw = readFileSync59(join61(agentDir, ".claude", ".claude.json"), "utf8");
|
|
103038
103408
|
claudeJson = JSON.parse(raw);
|
|
103039
103409
|
}
|
|
103040
103410
|
} catch {}
|
|
@@ -103163,7 +103533,7 @@ function buildModelDeps(restartCtx) {
|
|
|
103163
103533
|
try {
|
|
103164
103534
|
const agentDir = resolveAgentDirFromEnv();
|
|
103165
103535
|
if (agentDir) {
|
|
103166
|
-
const local = await fetchQuota2({ claudeConfigDir:
|
|
103536
|
+
const local = await fetchQuota2({ claudeConfigDir: join61(agentDir, ".claude") });
|
|
103167
103537
|
if (local.ok)
|
|
103168
103538
|
return formatQuotaLine2(local.data);
|
|
103169
103539
|
}
|
|
@@ -103258,7 +103628,7 @@ function buildModelDeps(restartCtx) {
|
|
|
103258
103628
|
function modelMenuReplyMarkup(reply) {
|
|
103259
103629
|
if (!reply.keyboard)
|
|
103260
103630
|
return;
|
|
103261
|
-
const kb = new
|
|
103631
|
+
const kb = new import_grammy16.InlineKeyboard;
|
|
103262
103632
|
for (const row of reply.keyboard) {
|
|
103263
103633
|
for (const btn of row)
|
|
103264
103634
|
kb.text(btn.text, btn.callback_data);
|
|
@@ -103398,7 +103768,7 @@ function buildEffortDeps() {
|
|
|
103398
103768
|
function effortMenuReplyMarkup(reply) {
|
|
103399
103769
|
if (!reply.keyboard)
|
|
103400
103770
|
return;
|
|
103401
|
-
const kb = new
|
|
103771
|
+
const kb = new import_grammy16.InlineKeyboard;
|
|
103402
103772
|
for (const row of reply.keyboard) {
|
|
103403
103773
|
for (const btn of row)
|
|
103404
103774
|
kb.text(btn.text, btn.callback_data);
|
|
@@ -103409,10 +103779,10 @@ function effortMenuReplyMarkup(reply) {
|
|
|
103409
103779
|
function flushAgentHandoff(agentDir) {
|
|
103410
103780
|
let removed = 0;
|
|
103411
103781
|
for (const fname of [".handoff.md", ".handoff-topic"]) {
|
|
103412
|
-
const p =
|
|
103782
|
+
const p = join61(agentDir, fname);
|
|
103413
103783
|
try {
|
|
103414
|
-
if (
|
|
103415
|
-
|
|
103784
|
+
if (existsSync55(p)) {
|
|
103785
|
+
unlinkSync27(p);
|
|
103416
103786
|
removed++;
|
|
103417
103787
|
}
|
|
103418
103788
|
} catch (err) {
|
|
@@ -103467,7 +103837,7 @@ async function handleNewCommand(ctx) {
|
|
|
103467
103837
|
writeRestartMarker({ chat_id: chatId, thread_id: threadId ?? null, ack_message_id: ackId, ts: Date.now() });
|
|
103468
103838
|
if (agentDir != null) {
|
|
103469
103839
|
try {
|
|
103470
|
-
|
|
103840
|
+
writeFileSync49(join61(agentDir, ".force-fresh-session"), `${kind} at ${new Date().toISOString()}
|
|
103471
103841
|
`, "utf8");
|
|
103472
103842
|
} catch (err) {
|
|
103473
103843
|
process.stderr.write(`telegram gateway: failed to write force-fresh marker: ${err}
|
|
@@ -103580,16 +103950,16 @@ function buildFolderPickerDeps() {
|
|
|
103580
103950
|
};
|
|
103581
103951
|
}
|
|
103582
103952
|
var lockoutOps = {
|
|
103583
|
-
readFileSync: (p, enc) =>
|
|
103584
|
-
writeFileSync: (p, data, opts) =>
|
|
103585
|
-
existsSync: (p) =>
|
|
103586
|
-
mkdirSync: (p, opts) =>
|
|
103587
|
-
joinPath: (...parts) =>
|
|
103953
|
+
readFileSync: (p, enc) => readFileSync59(p, enc),
|
|
103954
|
+
writeFileSync: (p, data, opts) => writeFileSync49(p, data, opts),
|
|
103955
|
+
existsSync: (p) => existsSync55(p),
|
|
103956
|
+
mkdirSync: (p, opts) => mkdirSync48(p, opts),
|
|
103957
|
+
joinPath: (...parts) => join61(...parts)
|
|
103588
103958
|
};
|
|
103589
103959
|
var FLEET_FALLBACK_DEDUP_MS = 30000;
|
|
103590
103960
|
function isAuthBrokerSocketReachable() {
|
|
103591
103961
|
try {
|
|
103592
|
-
return
|
|
103962
|
+
return existsSync55(resolveAuthBrokerSocketPath2());
|
|
103593
103963
|
} catch {
|
|
103594
103964
|
return false;
|
|
103595
103965
|
}
|
|
@@ -103844,7 +104214,7 @@ async function runCreditWatch() {
|
|
|
103844
104214
|
if (!agentDir)
|
|
103845
104215
|
return;
|
|
103846
104216
|
const agentName3 = getMyAgentName();
|
|
103847
|
-
const claudeConfigDir =
|
|
104217
|
+
const claudeConfigDir = join61(agentDir, ".claude");
|
|
103848
104218
|
const stateDir = STATE_DIR;
|
|
103849
104219
|
const reason = readClaudeJsonOverage(claudeConfigDir);
|
|
103850
104220
|
const prev = loadCreditState(stateDir);
|
|
@@ -105051,7 +105421,7 @@ _Google accounts are connected from the host CLI._`, { html: true });
|
|
|
105051
105421
|
return;
|
|
105052
105422
|
}
|
|
105053
105423
|
const appNote = started.source === "default" ? "_Using switchroom's shipped Microsoft app._" : "_Using your configured Microsoft app._";
|
|
105054
|
-
const keyboard = new
|
|
105424
|
+
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
105425
|
const sent = await ctx.replyWithRichMessage(richMessage2(`\uD83D\uDD17 **Connect a Microsoft account**
|
|
105056
105426
|
|
|
105057
105427
|
` + `1. Tap **Sign in to Microsoft** below.
|
|
@@ -105187,7 +105557,7 @@ Send \`/auth ${parsed.provider} cancel\` to abort.`, { html: true });
|
|
|
105187
105557
|
tz: process.env.SWITCHROOM_TIMEZONE ?? process.env.TZ
|
|
105188
105558
|
});
|
|
105189
105559
|
if (reply.keyboard && reply.keyboard.length > 0) {
|
|
105190
|
-
const kb = new
|
|
105560
|
+
const kb = new import_grammy16.InlineKeyboard;
|
|
105191
105561
|
for (let i = 0;i < reply.keyboard.length; i++) {
|
|
105192
105562
|
const row = reply.keyboard[i];
|
|
105193
105563
|
for (const b of row) {
|
|
@@ -105329,7 +105699,7 @@ Send \`/auth ${parsed.provider} cancel\` to abort.`, { html: true });
|
|
|
105329
105699
|
}
|
|
105330
105700
|
lines.push("");
|
|
105331
105701
|
const denials = readRecentDenialsForAgent(targetAgent, 604800000, 5);
|
|
105332
|
-
const denialKeyboard = new
|
|
105702
|
+
const denialKeyboard = new import_grammy16.InlineKeyboard;
|
|
105333
105703
|
if (denials.length > 0) {
|
|
105334
105704
|
lines.push("**Recent denials (last 7d):**");
|
|
105335
105705
|
for (const d of denials) {
|
|
@@ -105377,7 +105747,7 @@ Send \`/auth ${parsed.provider} cancel\` to abort.`, { html: true });
|
|
|
105377
105747
|
byAgent.set(g.agent_slug, list3);
|
|
105378
105748
|
}
|
|
105379
105749
|
const lines = ["**\uD83D\uDCDC Active grants**", ""];
|
|
105380
|
-
const keyboard = new
|
|
105750
|
+
const keyboard = new import_grammy16.InlineKeyboard;
|
|
105381
105751
|
for (const [agentName3, agentGrants] of byAgent) {
|
|
105382
105752
|
lines.push(`**${escapeHtmlForTg2(agentName3)}:**`);
|
|
105383
105753
|
for (const g of agentGrants) {
|
|
@@ -105574,7 +105944,7 @@ Send \`/auth ${parsed.provider} cancel\` to abort.`, { html: true });
|
|
|
105574
105944
|
if (ctx.chat?.type !== "private") {
|
|
105575
105945
|
kbRows = kbRows.filter((row) => !row.some((b) => b.callbackData?.startsWith("auth:use:")));
|
|
105576
105946
|
}
|
|
105577
|
-
const keyboard = new
|
|
105947
|
+
const keyboard = new import_grammy16.InlineKeyboard;
|
|
105578
105948
|
kbRows.forEach((row, ri) => {
|
|
105579
105949
|
if (ri > 0)
|
|
105580
105950
|
keyboard.row();
|
|
@@ -105600,7 +105970,7 @@ Send \`/auth ${parsed.provider} cancel\` to abort.`, { html: true });
|
|
|
105600
105970
|
await switchroomReply(ctx, "**/usage:** cannot resolve agent dir.", { html: true });
|
|
105601
105971
|
return;
|
|
105602
105972
|
}
|
|
105603
|
-
const result = await fetchQuota2({ claudeConfigDir:
|
|
105973
|
+
const result = await fetchQuota2({ claudeConfigDir: join61(agentDir, ".claude") });
|
|
105604
105974
|
if (!result.ok) {
|
|
105605
105975
|
await switchroomReply(ctx, `**/usage:** ${escapeHtmlForTg2(result.reason)}`, { html: true });
|
|
105606
105976
|
return;
|
|
@@ -105926,7 +106296,7 @@ ${preBlock(formatSwitchroomOutput(err.message ?? "unknown error"))}`, { html: tr
|
|
|
105926
106296
|
await ctx.answerCallbackQuery({ text: "No always-allow rule for this tool." }).catch(() => {});
|
|
105927
106297
|
return;
|
|
105928
106298
|
}
|
|
105929
|
-
keyboard = new
|
|
106299
|
+
keyboard = new import_grammy16.InlineKeyboard().text("\u2190 Back", `perm:back:${request_id}`);
|
|
105930
106300
|
if (choices.specific)
|
|
105931
106301
|
keyboard.text(choices.specific.buttonLabel, `perm:asn:${request_id}`);
|
|
105932
106302
|
keyboard.text(`${choices.broad.buttonLabel} \u26A0\uFE0F`, `perm:asb:${request_id}`);
|
|
@@ -105986,7 +106356,7 @@ ${interimLabel}` : interimLabel
|
|
|
105986
106356
|
const unifiedDiff = (() => {
|
|
105987
106357
|
try {
|
|
105988
106358
|
const cfgPath = process.env.SWITCHROOM_CONFIG ?? SWITCHROOM_CONFIG ?? findConfigFile2();
|
|
105989
|
-
const raw =
|
|
106359
|
+
const raw = readFileSync59(cfgPath, "utf8");
|
|
105990
106360
|
return synthesizeAllowRuleDiff({ agentName: agentName3, rule: chosen.rule, configText: raw });
|
|
105991
106361
|
} catch (err) {
|
|
105992
106362
|
process.stderr.write(`telegram gateway: always-allow diff synth failed: ${err.message}
|
|
@@ -106228,7 +106598,7 @@ ${labelWithResume}` : labelWithResume,
|
|
|
106228
106598
|
verb: "voice-ondemand.sendVoice",
|
|
106229
106599
|
...threadId != null ? { threadId } : {}
|
|
106230
106600
|
}),
|
|
106231
|
-
sendVoiceByUpload: (chatId, audio, sendOpts, threadId) => robustApiCall(() => bot2.api.sendVoice(chatId, new
|
|
106601
|
+
sendVoiceByUpload: (chatId, audio, sendOpts, threadId) => robustApiCall(() => bot2.api.sendVoice(chatId, new import_grammy16.InputFile(Buffer.from(audio)), sendOpts), {
|
|
106232
106602
|
chat_id: chatId,
|
|
106233
106603
|
verb: "voice-ondemand.sendVoice",
|
|
106234
106604
|
...threadId != null ? { threadId } : {}
|
|
@@ -106487,7 +106857,7 @@ async function initGatewayBot() {
|
|
|
106487
106857
|
`);
|
|
106488
106858
|
process.exit(1);
|
|
106489
106859
|
}
|
|
106490
|
-
bot = new
|
|
106860
|
+
bot = new import_grammy16.Bot(TOKEN);
|
|
106491
106861
|
installTgPostLogger(bot);
|
|
106492
106862
|
installRichMarkdownGuard(bot);
|
|
106493
106863
|
installUpdateTap(bot, (line) => process.stderr.write(line));
|
|
@@ -106842,7 +107212,7 @@ async function startGateway() {
|
|
|
106842
107212
|
return;
|
|
106843
107213
|
}
|
|
106844
107214
|
})();
|
|
106845
|
-
const resolvedAgentDirForBootCard = agentDir ??
|
|
107215
|
+
const resolvedAgentDirForBootCard = agentDir ?? join61(homedir20(), ".switchroom", "agents", agentSlug);
|
|
106846
107216
|
const handle = await startBootCard(chatId, threadId, botApiForCard, {
|
|
106847
107217
|
agentName: agentDisplayName,
|
|
106848
107218
|
agentSlug,
|
|
@@ -106856,8 +107226,8 @@ async function startGateway() {
|
|
|
106856
107226
|
probeQuotaViaBroker: (t) => probeQuotaForBootCard(agentSlug, t),
|
|
106857
107227
|
tmuxSupervisor: process.env.SWITCHROOM_TMUX_SUPERVISOR === "1",
|
|
106858
107228
|
dockerMode: process.env.SWITCHROOM_RUNTIME === "docker",
|
|
106859
|
-
configSnapshotPath:
|
|
106860
|
-
bootCardStatePath:
|
|
107229
|
+
configSnapshotPath: join61(resolvedAgentDirForBootCard, ".config-snapshot.json"),
|
|
107230
|
+
bootCardStatePath: join61(resolvedAgentDirForBootCard, ".boot-card-msgid.json"),
|
|
106861
107231
|
floodStatePath: FLOOD_STATE_PATH,
|
|
106862
107232
|
...updateOutcomeLine ? { updateOutcomeLine } : {}
|
|
106863
107233
|
}, ackMsgId);
|
|
@@ -106888,10 +107258,10 @@ async function startGateway() {
|
|
|
106888
107258
|
try {
|
|
106889
107259
|
const smAgentDir = resolveAgentDirFromEnv();
|
|
106890
107260
|
if (smAgentDir) {
|
|
106891
|
-
const activePath =
|
|
106892
|
-
if (
|
|
107261
|
+
const activePath = join61(smAgentDir, ".active-session-model");
|
|
107262
|
+
if (existsSync55(activePath)) {
|
|
106893
107263
|
try {
|
|
106894
|
-
const launched =
|
|
107264
|
+
const launched = readFileSync59(activePath, "utf8").trim();
|
|
106895
107265
|
const configured = (() => {
|
|
106896
107266
|
const d = switchroomExecJson(["agent", "list"]);
|
|
106897
107267
|
const raw = d?.agents?.find((a) => a.name === getMyAgentName())?.model ?? null;
|
|
@@ -106924,29 +107294,29 @@ async function startGateway() {
|
|
|
106924
107294
|
deliverModelSwitchBootNotice({
|
|
106925
107295
|
...modelBootCardDeps,
|
|
106926
107296
|
confirmation,
|
|
106927
|
-
hasSessionModelAlert:
|
|
107297
|
+
hasSessionModelAlert: existsSync55(join61(smAgentDir, ".session-model-alert"))
|
|
106928
107298
|
});
|
|
106929
107299
|
}
|
|
106930
107300
|
} catch {}
|
|
106931
107301
|
}
|
|
106932
|
-
const activeEffortPath =
|
|
106933
|
-
if (
|
|
107302
|
+
const activeEffortPath = join61(smAgentDir, ".active-session-effort");
|
|
107303
|
+
if (existsSync55(activeEffortPath)) {
|
|
106934
107304
|
try {
|
|
106935
|
-
const launchedEffort =
|
|
107305
|
+
const launchedEffort = readFileSync59(activeEffortPath, "utf8").trim();
|
|
106936
107306
|
const configuredEffort = getConfiguredEffortForPersist();
|
|
106937
107307
|
sessionEffortOverride = launchedEffort.length > 0 && launchedEffort !== configuredEffort ? launchedEffort : null;
|
|
106938
107308
|
} catch {}
|
|
106939
107309
|
}
|
|
106940
|
-
const alertPath =
|
|
106941
|
-
if (
|
|
107310
|
+
const alertPath = join61(smAgentDir, ".session-model-alert");
|
|
107311
|
+
if (existsSync55(alertPath)) {
|
|
106942
107312
|
let alertText = null;
|
|
106943
107313
|
try {
|
|
106944
|
-
alertText =
|
|
107314
|
+
alertText = readFileSync59(alertPath, "utf8").trim();
|
|
106945
107315
|
} catch {
|
|
106946
107316
|
alertText = null;
|
|
106947
107317
|
}
|
|
106948
107318
|
try {
|
|
106949
|
-
|
|
107319
|
+
unlinkSync27(alertPath);
|
|
106950
107320
|
} catch {}
|
|
106951
107321
|
if (alertText && alertText.length > 0) {
|
|
106952
107322
|
const operators = loadAccess().allowFrom;
|
|
@@ -107442,7 +107812,7 @@ async function startGateway() {
|
|
|
107442
107812
|
await runnerHandle.task();
|
|
107443
107813
|
return;
|
|
107444
107814
|
} catch (err) {
|
|
107445
|
-
if (err instanceof
|
|
107815
|
+
if (err instanceof import_grammy16.GrammyError && err.error_code === 409) {
|
|
107446
107816
|
const delay = Math.min(1000 * attempt, 15000);
|
|
107447
107817
|
const agentName3 = process.env.SWITCHROOM_AGENT_NAME ?? "-";
|
|
107448
107818
|
process.stderr.write(`telegram gateway: poll.409.detected attempt=${attempt} retry_in_ms=${delay} agent=${agentName3}
|