switchroom 0.14.21 → 0.14.22
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/agent-scheduler/index.js +0 -1
- package/dist/auth-broker/index.js +0 -1
- package/dist/cli/notion-write-pretool.mjs +0 -1
- package/dist/cli/switchroom.js +14 -6
- package/dist/host-control/main.js +0 -1
- package/dist/vault/approvals/kernel-server.js +0 -1
- package/dist/vault/broker/server.js +0 -1
- package/package.json +3 -3
- package/profiles/_base/start.sh.hbs +11 -24
- package/profiles/_shared/telegram-style.md.hbs +2 -2
- package/profiles/default/CLAUDE.md.hbs +4 -1
- package/skills/switchroom-runtime/SKILL.md +6 -16
- package/telegram-plugin/agent-dir.ts +15 -0
- package/telegram-plugin/dist/gateway/gateway.js +640 -509
- package/telegram-plugin/gateway/gateway.ts +216 -61
- package/telegram-plugin/gateway/inbound-spool.ts +15 -0
- package/telegram-plugin/gateway/resume-inbound-builder.ts +180 -0
- package/telegram-plugin/registry/turns-schema.ts +138 -33
- package/telegram-plugin/stream-reply-handler.ts +1 -11
- package/telegram-plugin/tests/agent-dir.test.ts +25 -0
- package/telegram-plugin/tests/e2e.test.ts +2 -77
- package/telegram-plugin/tests/inbound-spool.test.ts +45 -0
- package/telegram-plugin/tests/multi-turn-continuity.test.ts +0 -1
- package/telegram-plugin/tests/outbound-ordering.test.ts +0 -1
- package/telegram-plugin/tests/parse-mode-rotation.test.ts +0 -1
- package/telegram-plugin/tests/races.test.ts +0 -26
- package/telegram-plugin/tests/registry-turns.test.ts +106 -29
- package/telegram-plugin/tests/resume-inbound-builder.test.ts +182 -0
- package/telegram-plugin/tests/status-accent.test.ts +0 -1
- package/telegram-plugin/tests/stream-reply-error-paths.test.ts +0 -1
- package/telegram-plugin/tests/stream-reply-handler.test.ts +0 -24
- package/telegram-plugin/tests/streaming-e2e.test.ts +0 -1
- package/telegram-plugin/tests/streaming-orchestration.test.ts +0 -1
- package/telegram-plugin/tests/tool-activity-summary.test.ts +44 -0
- package/telegram-plugin/tests/turns-writer.test.ts +16 -6
- package/telegram-plugin/tool-activity-summary.ts +55 -0
- package/telegram-plugin/uat/driver.ts +3 -1
- package/telegram-plugin/handoff-continuity.ts +0 -206
- package/telegram-plugin/tests/handoff-continuity.test.ts +0 -262
|
@@ -23707,7 +23707,6 @@ var init_schema = __esm(() => {
|
|
|
23707
23707
|
}).optional();
|
|
23708
23708
|
SessionContinuitySchema = exports_external.object({
|
|
23709
23709
|
enabled: exports_external.boolean().optional().describe("Master switch for the session-handoff briefing (default true)."),
|
|
23710
|
-
show_handoff_line: exports_external.boolean().optional().describe("Whether the telegram plugin prepends a visible '\u21a9\ufe0f Picked up\u2026' " + "line to the first assistant reply after a restart (default true)."),
|
|
23711
23710
|
max_turns_in_briefing: exports_external.number().int().positive().optional().describe("Cap on recent user/assistant turn pairs fed to the summarizer."),
|
|
23712
23711
|
resume_mode: exports_external.enum(["auto", "continue", "handoff", "none"]).optional().describe("How to resume the next session. 'handoff' (default as of #362) " + "never passes --continue; a fresh Claude starts each restart and " + "reads a briefing assembled from recent Telegram messages, Hindsight " + "recall, and today's daily memory file. 'auto' uses --continue when " + "the latest JSONL is smaller than resume_max_bytes, else falls back " + "to the handoff briefing. 'continue' always passes --continue. " + "'none' starts completely fresh every time."),
|
|
23713
23712
|
resume_max_bytes: exports_external.number().int().positive().optional().describe("Byte threshold above which 'auto' mode falls back to handoff " + "instead of --continue. Default 2_000_000 (~2MB). Large transcripts " + "can blow out the context window even with prefix caching, and " + "--continue replay is known-fragile at scale.")
|
|
@@ -25600,7 +25599,7 @@ function isDockerRuntime() {
|
|
|
25600
25599
|
import * as net3 from "node:net";
|
|
25601
25600
|
import * as fs from "node:fs";
|
|
25602
25601
|
import { homedir as homedir7 } from "node:os";
|
|
25603
|
-
import { join as
|
|
25602
|
+
import { join as join15 } from "node:path";
|
|
25604
25603
|
function defaultBrokerSocketPath() {
|
|
25605
25604
|
if (fs.existsSync(OPERATOR_SOCKET_PATH))
|
|
25606
25605
|
return OPERATOR_SOCKET_PATH;
|
|
@@ -25609,7 +25608,7 @@ function defaultBrokerSocketPath() {
|
|
|
25609
25608
|
return LEGACY_SOCKET_PATH;
|
|
25610
25609
|
}
|
|
25611
25610
|
function vaultTokenFilePath(agentSlug) {
|
|
25612
|
-
return
|
|
25611
|
+
return join15(homedir7(), ".switchroom", "agents", agentSlug, ".vault-token");
|
|
25613
25612
|
}
|
|
25614
25613
|
function readVaultTokenFile(agentSlug) {
|
|
25615
25614
|
const filePath = vaultTokenFilePath(agentSlug);
|
|
@@ -25764,8 +25763,8 @@ var DEFAULT_TIMEOUT_MS3 = 2000, LEGACY_SOCKET_PATH, OPERATOR_SOCKET_PATH;
|
|
|
25764
25763
|
var init_client2 = __esm(() => {
|
|
25765
25764
|
init_protocol2();
|
|
25766
25765
|
init_peercred();
|
|
25767
|
-
LEGACY_SOCKET_PATH =
|
|
25768
|
-
OPERATOR_SOCKET_PATH =
|
|
25766
|
+
LEGACY_SOCKET_PATH = join15(homedir7(), ".switchroom", "vault-broker.sock");
|
|
25767
|
+
OPERATOR_SOCKET_PATH = join15(homedir7(), ".switchroom", "broker-operator", "sock");
|
|
25769
25768
|
});
|
|
25770
25769
|
|
|
25771
25770
|
// ../src/drive/deep-links.ts
|
|
@@ -27878,7 +27877,7 @@ var init_secretlint_source = __esm(() => {
|
|
|
27878
27877
|
});
|
|
27879
27878
|
|
|
27880
27879
|
// gateway/auth-line.ts
|
|
27881
|
-
function
|
|
27880
|
+
function escapeHtml8(s) {
|
|
27882
27881
|
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
27883
27882
|
}
|
|
27884
27883
|
function formatRelativeMs2(ms) {
|
|
@@ -27938,7 +27937,7 @@ function renderAuthLine(state4, agentName3, now = Date.now()) {
|
|
|
27938
27937
|
if (!acc)
|
|
27939
27938
|
continue;
|
|
27940
27939
|
const marker = label === activeLabel ? "\u25b6" : "\u21b3";
|
|
27941
|
-
const labelHtml = `<code>${
|
|
27940
|
+
const labelHtml = `<code>${escapeHtml8(acc.label)}</code>`;
|
|
27942
27941
|
const quotaLine = formatAuthQuotaLine(acc, now);
|
|
27943
27942
|
rows.push(quotaLine ? `${marker} ${labelHtml} ${quotaLine}` : `${marker} ${labelHtml}`);
|
|
27944
27943
|
}
|
|
@@ -27946,19 +27945,19 @@ function renderAuthLine(state4, agentName3, now = Date.now()) {
|
|
|
27946
27945
|
}
|
|
27947
27946
|
|
|
27948
27947
|
// gateway/quota-cache.ts
|
|
27949
|
-
import { existsSync as
|
|
27950
|
-
import { join as
|
|
27948
|
+
import { existsSync as existsSync25, readFileSync as readFileSync23, writeFileSync as writeFileSync14, mkdirSync as mkdirSync14 } from "fs";
|
|
27949
|
+
import { join as join22, dirname as dirname8 } from "path";
|
|
27951
27950
|
function defaultCachePath() {
|
|
27952
|
-
return process.env.SWITCHROOM_QUOTA_CACHE_PATH ??
|
|
27951
|
+
return process.env.SWITCHROOM_QUOTA_CACHE_PATH ?? join22(process.env.HOME ?? "/tmp", ".switchroom", "quota-cache.json");
|
|
27953
27952
|
}
|
|
27954
27953
|
function readQuotaCache(opts = {}) {
|
|
27955
27954
|
const path = opts.path ?? defaultCachePath();
|
|
27956
27955
|
const now = opts.now ?? Date.now();
|
|
27957
|
-
if (!
|
|
27956
|
+
if (!existsSync25(path))
|
|
27958
27957
|
return null;
|
|
27959
27958
|
let entry;
|
|
27960
27959
|
try {
|
|
27961
|
-
entry = JSON.parse(
|
|
27960
|
+
entry = JSON.parse(readFileSync23(path, "utf8"));
|
|
27962
27961
|
} catch {
|
|
27963
27962
|
return null;
|
|
27964
27963
|
}
|
|
@@ -27985,7 +27984,7 @@ function writeQuotaCache(result, opts = {}) {
|
|
|
27985
27984
|
};
|
|
27986
27985
|
try {
|
|
27987
27986
|
mkdirSync14(dirname8(path), { recursive: true });
|
|
27988
|
-
|
|
27987
|
+
writeFileSync14(path, JSON.stringify(entry, null, 2), { mode: 384 });
|
|
27989
27988
|
} catch {}
|
|
27990
27989
|
}
|
|
27991
27990
|
var DEFAULT_TTL_MS4, RATE_LIMIT_TTL_MS;
|
|
@@ -27995,8 +27994,8 @@ var init_quota_cache = __esm(() => {
|
|
|
27995
27994
|
});
|
|
27996
27995
|
|
|
27997
27996
|
// gateway/boot-probes.ts
|
|
27998
|
-
import { readFileSync as
|
|
27999
|
-
import { join as
|
|
27997
|
+
import { readFileSync as readFileSync24, readdirSync as readdirSync4, existsSync as existsSync26 } from "fs";
|
|
27998
|
+
import { join as join23 } from "path";
|
|
28000
27999
|
import { execFile as execFileCb } from "child_process";
|
|
28001
28000
|
import { promisify as promisify3 } from "util";
|
|
28002
28001
|
async function withTimeout(label, p, timeoutMs = PROBE_TIMEOUT_MS) {
|
|
@@ -28038,11 +28037,11 @@ function mapPlan(billingType, hasExtra) {
|
|
|
28038
28037
|
}
|
|
28039
28038
|
async function probeAccount(agentDir) {
|
|
28040
28039
|
return withTimeout("Account", (async () => {
|
|
28041
|
-
const claudeDir =
|
|
28042
|
-
const claudeJsonPath =
|
|
28040
|
+
const claudeDir = join23(agentDir, ".claude");
|
|
28041
|
+
const claudeJsonPath = join23(claudeDir, ".claude.json");
|
|
28043
28042
|
let cfg = {};
|
|
28044
28043
|
try {
|
|
28045
|
-
const raw =
|
|
28044
|
+
const raw = readFileSync24(claudeJsonPath, "utf8");
|
|
28046
28045
|
cfg = JSON.parse(raw);
|
|
28047
28046
|
} catch {
|
|
28048
28047
|
return { status: "fail", label: "Account", detail: "no .claude.json" };
|
|
@@ -28060,12 +28059,12 @@ async function probeAccount(agentDir) {
|
|
|
28060
28059
|
let tokenStr = "";
|
|
28061
28060
|
let status = "ok";
|
|
28062
28061
|
for (const candidate of [
|
|
28063
|
-
|
|
28064
|
-
|
|
28062
|
+
join23(claudeDir, ".oauth-token.meta.json"),
|
|
28063
|
+
join23(claudeDir, "accounts", "default", ".oauth-token.meta.json")
|
|
28065
28064
|
]) {
|
|
28066
|
-
if (
|
|
28065
|
+
if (existsSync26(candidate)) {
|
|
28067
28066
|
try {
|
|
28068
|
-
const meta = JSON.parse(
|
|
28067
|
+
const meta = JSON.parse(readFileSync24(candidate, "utf8"));
|
|
28069
28068
|
if (meta.expiresAt) {
|
|
28070
28069
|
tokenStr = " \u00b7 " + formatDaysFromNow(meta.expiresAt);
|
|
28071
28070
|
const daysLeft = Math.round((meta.expiresAt - Date.now()) / 86400000);
|
|
@@ -28240,9 +28239,9 @@ async function resolveTmuxSupervisorPid(agentName3, execFileImpl) {
|
|
|
28240
28239
|
if (!cgroup)
|
|
28241
28240
|
return null;
|
|
28242
28241
|
const procsPath = `/sys/fs/cgroup${cgroup}/cgroup.procs`;
|
|
28243
|
-
if (!
|
|
28242
|
+
if (!existsSync26(procsPath))
|
|
28244
28243
|
return null;
|
|
28245
|
-
const pidsRaw =
|
|
28244
|
+
const pidsRaw = readFileSync24(procsPath, "utf-8");
|
|
28246
28245
|
const pids = pidsRaw.split(`
|
|
28247
28246
|
`).map((s) => s.trim()).filter(Boolean);
|
|
28248
28247
|
if (pids.length === 0)
|
|
@@ -28255,7 +28254,7 @@ async function resolveTmuxSupervisorPid(agentName3, execFileImpl) {
|
|
|
28255
28254
|
let rss = 0;
|
|
28256
28255
|
let comm = "";
|
|
28257
28256
|
try {
|
|
28258
|
-
const status =
|
|
28257
|
+
const status = readFileSync24(`/proc/${pid}/status`, "utf-8");
|
|
28259
28258
|
const rssLine = status.split(`
|
|
28260
28259
|
`).find((l) => l.startsWith("VmRSS:"));
|
|
28261
28260
|
if (rssLine) {
|
|
@@ -28267,7 +28266,7 @@ async function resolveTmuxSupervisorPid(agentName3, execFileImpl) {
|
|
|
28267
28266
|
continue;
|
|
28268
28267
|
}
|
|
28269
28268
|
try {
|
|
28270
|
-
comm =
|
|
28269
|
+
comm = readFileSync24(`/proc/${pid}/comm`, "utf-8").trim();
|
|
28271
28270
|
} catch {}
|
|
28272
28271
|
candidates.push({ pid, rss, comm });
|
|
28273
28272
|
}
|
|
@@ -28447,9 +28446,9 @@ async function probeQuota(claudeConfigDir, _agentDir, fetchImpl = fetch, opts =
|
|
|
28447
28446
|
let claudeDirForProbe = null;
|
|
28448
28447
|
for (const candidate of [
|
|
28449
28448
|
claudeConfigDir,
|
|
28450
|
-
|
|
28449
|
+
join23(claudeConfigDir, "accounts", "default")
|
|
28451
28450
|
]) {
|
|
28452
|
-
if (
|
|
28451
|
+
if (existsSync26(join23(candidate, ".oauth-token"))) {
|
|
28453
28452
|
claudeDirForProbe = candidate;
|
|
28454
28453
|
break;
|
|
28455
28454
|
}
|
|
@@ -28514,7 +28513,7 @@ async function probeHindsight(bankName, fetchImpl = fetch) {
|
|
|
28514
28513
|
}
|
|
28515
28514
|
function readContainerBootTimeMsForProbe() {
|
|
28516
28515
|
try {
|
|
28517
|
-
const stat1 =
|
|
28516
|
+
const stat1 = readFileSync24("/proc/1/stat", "utf8");
|
|
28518
28517
|
const lastParen = stat1.lastIndexOf(")");
|
|
28519
28518
|
if (lastParen < 0)
|
|
28520
28519
|
return null;
|
|
@@ -28522,7 +28521,7 @@ function readContainerBootTimeMsForProbe() {
|
|
|
28522
28521
|
const starttimeTicks = Number(after[19]);
|
|
28523
28522
|
if (!Number.isFinite(starttimeTicks))
|
|
28524
28523
|
return null;
|
|
28525
|
-
const procStat =
|
|
28524
|
+
const procStat = readFileSync24("/proc/stat", "utf8");
|
|
28526
28525
|
const btimeLine = procStat.split(`
|
|
28527
28526
|
`).find((l) => l.startsWith("btime "));
|
|
28528
28527
|
if (!btimeLine)
|
|
@@ -28620,7 +28619,7 @@ async function probeUds(label, socketPath, opts = {}) {
|
|
|
28620
28619
|
}
|
|
28621
28620
|
return withTimeout(label, (async () => {
|
|
28622
28621
|
if (!opts.connectImpl) {
|
|
28623
|
-
if (!
|
|
28622
|
+
if (!existsSync26(socketPath)) {
|
|
28624
28623
|
return {
|
|
28625
28624
|
status: "fail",
|
|
28626
28625
|
label,
|
|
@@ -28684,7 +28683,7 @@ async function probeSkills(agentDir, opts = {}) {
|
|
|
28684
28683
|
return withTimeout("Skills", (async () => {
|
|
28685
28684
|
const fs2 = opts.fs ?? realSkillsFs;
|
|
28686
28685
|
const max = opts.maxNamesShown ?? 3;
|
|
28687
|
-
const skillsDir =
|
|
28686
|
+
const skillsDir = join23(agentDir, ".claude", "skills");
|
|
28688
28687
|
if (!fs2.exists(skillsDir)) {
|
|
28689
28688
|
return { status: "ok", label: "Skills", detail: "no skills dir" };
|
|
28690
28689
|
}
|
|
@@ -28699,17 +28698,17 @@ async function probeSkills(agentDir, opts = {}) {
|
|
|
28699
28698
|
}
|
|
28700
28699
|
const dangling = [];
|
|
28701
28700
|
for (const name of entries) {
|
|
28702
|
-
const skillPath =
|
|
28701
|
+
const skillPath = join23(skillsDir, name);
|
|
28703
28702
|
if (!fs2.exists(skillPath)) {
|
|
28704
28703
|
dangling.push(name);
|
|
28705
28704
|
continue;
|
|
28706
28705
|
}
|
|
28707
|
-
const skillMd =
|
|
28706
|
+
const skillMd = join23(skillPath, "SKILL.md");
|
|
28708
28707
|
if (!fs2.exists(skillMd) && !fs2.exists(skillPath + ".md")) {
|
|
28709
28708
|
continue;
|
|
28710
28709
|
}
|
|
28711
28710
|
}
|
|
28712
|
-
const overlayDir = opts.overlaySkillsDir ??
|
|
28711
|
+
const overlayDir = opts.overlaySkillsDir ?? join23(agentDir, "skills.d");
|
|
28713
28712
|
const overlaySlugs = new Set;
|
|
28714
28713
|
if (fs2.exists(overlayDir)) {
|
|
28715
28714
|
let overlayEntries = [];
|
|
@@ -28756,24 +28755,24 @@ var init_boot_probes = __esm(() => {
|
|
|
28756
28755
|
execFile3 = promisify3(execFileCb);
|
|
28757
28756
|
realProcFs = {
|
|
28758
28757
|
readdir: (p) => readdirSync4(p),
|
|
28759
|
-
readFile: (p) =>
|
|
28758
|
+
readFile: (p) => readFileSync24(p, "utf-8")
|
|
28760
28759
|
};
|
|
28761
28760
|
realSchedulerFs = {
|
|
28762
|
-
readFile: (p) =>
|
|
28761
|
+
readFile: (p) => readFileSync24(p, "utf-8"),
|
|
28763
28762
|
mtimeMs: (p) => {
|
|
28764
28763
|
const { statSync: statSync7 } = __require("fs");
|
|
28765
28764
|
return statSync7(p).mtimeMs;
|
|
28766
28765
|
},
|
|
28767
|
-
exists: (p) =>
|
|
28766
|
+
exists: (p) => existsSync26(p)
|
|
28768
28767
|
};
|
|
28769
28768
|
realSkillsFs = {
|
|
28770
28769
|
readdir: (p) => readdirSync4(p),
|
|
28771
|
-
exists: (p) =>
|
|
28770
|
+
exists: (p) => existsSync26(p)
|
|
28772
28771
|
};
|
|
28773
28772
|
});
|
|
28774
28773
|
|
|
28775
28774
|
// gateway/boot-issue-cache.ts
|
|
28776
|
-
import { existsSync as
|
|
28775
|
+
import { existsSync as existsSync27, readFileSync as readFileSync25, writeFileSync as writeFileSync15, mkdirSync as mkdirSync15, renameSync as renameSync8 } from "fs";
|
|
28777
28776
|
import { dirname as dirname9 } from "path";
|
|
28778
28777
|
function fingerprintProbe(key, r) {
|
|
28779
28778
|
if (r.status === "ok")
|
|
@@ -28853,11 +28852,11 @@ function diffProbes(probes, cache, opts = {}) {
|
|
|
28853
28852
|
return out;
|
|
28854
28853
|
}
|
|
28855
28854
|
function loadCache(path, now = Date.now) {
|
|
28856
|
-
if (!
|
|
28855
|
+
if (!existsSync27(path))
|
|
28857
28856
|
return { ...EMPTY_CACHE, probes: {} };
|
|
28858
28857
|
let raw;
|
|
28859
28858
|
try {
|
|
28860
|
-
raw =
|
|
28859
|
+
raw = readFileSync25(path, "utf-8");
|
|
28861
28860
|
} catch {
|
|
28862
28861
|
return { ...EMPTY_CACHE, probes: {} };
|
|
28863
28862
|
}
|
|
@@ -28866,7 +28865,7 @@ function loadCache(path, now = Date.now) {
|
|
|
28866
28865
|
parsed = JSON.parse(raw);
|
|
28867
28866
|
} catch {
|
|
28868
28867
|
try {
|
|
28869
|
-
|
|
28868
|
+
renameSync8(path, `${path}.corrupt-${now()}`);
|
|
28870
28869
|
} catch {}
|
|
28871
28870
|
return { ...EMPTY_CACHE, probes: {} };
|
|
28872
28871
|
}
|
|
@@ -28902,8 +28901,8 @@ function applyAndSave(path, cache, diff) {
|
|
|
28902
28901
|
try {
|
|
28903
28902
|
mkdirSync15(dirname9(path), { recursive: true });
|
|
28904
28903
|
const tmp = `${path}.tmp`;
|
|
28905
|
-
|
|
28906
|
-
|
|
28904
|
+
writeFileSync15(tmp, JSON.stringify(next), { mode: 384 });
|
|
28905
|
+
renameSync8(tmp, path);
|
|
28907
28906
|
} catch {}
|
|
28908
28907
|
return next;
|
|
28909
28908
|
}
|
|
@@ -28916,7 +28915,7 @@ var init_boot_issue_cache = __esm(() => {
|
|
|
28916
28915
|
|
|
28917
28916
|
// gateway/config-snapshot.ts
|
|
28918
28917
|
import { createHash as createHash2 } from "crypto";
|
|
28919
|
-
import { existsSync as
|
|
28918
|
+
import { existsSync as existsSync28, readFileSync as readFileSync26, writeFileSync as writeFileSync16, mkdirSync as mkdirSync16, renameSync as renameSync9 } from "fs";
|
|
28920
28919
|
import { dirname as dirname10 } from "path";
|
|
28921
28920
|
function hashStringArray(items) {
|
|
28922
28921
|
if (!items || items.length === 0)
|
|
@@ -28981,11 +28980,11 @@ function renderConfigChangeDim(dim) {
|
|
|
28981
28980
|
}
|
|
28982
28981
|
}
|
|
28983
28982
|
function loadSnapshot(path, now = Date.now) {
|
|
28984
|
-
if (!
|
|
28983
|
+
if (!existsSync28(path))
|
|
28985
28984
|
return null;
|
|
28986
28985
|
let raw;
|
|
28987
28986
|
try {
|
|
28988
|
-
raw =
|
|
28987
|
+
raw = readFileSync26(path, "utf-8");
|
|
28989
28988
|
} catch {
|
|
28990
28989
|
return null;
|
|
28991
28990
|
}
|
|
@@ -28994,7 +28993,7 @@ function loadSnapshot(path, now = Date.now) {
|
|
|
28994
28993
|
parsed = JSON.parse(raw);
|
|
28995
28994
|
} catch {
|
|
28996
28995
|
try {
|
|
28997
|
-
|
|
28996
|
+
renameSync9(path, `${path}.corrupt-${now()}`);
|
|
28998
28997
|
} catch {}
|
|
28999
28998
|
return null;
|
|
29000
28999
|
}
|
|
@@ -29017,8 +29016,8 @@ function persistSnapshot(path, snapshot) {
|
|
|
29017
29016
|
try {
|
|
29018
29017
|
mkdirSync16(dirname10(path), { recursive: true });
|
|
29019
29018
|
const tmp = `${path}.tmp`;
|
|
29020
|
-
|
|
29021
|
-
|
|
29019
|
+
writeFileSync16(tmp, JSON.stringify(snapshot), { mode: 384 });
|
|
29020
|
+
renameSync9(tmp, path);
|
|
29022
29021
|
} catch {}
|
|
29023
29022
|
}
|
|
29024
29023
|
var init_config_snapshot = () => {};
|
|
@@ -29033,7 +29032,7 @@ __export(exports_boot_card, {
|
|
|
29033
29032
|
renderBootCard: () => renderBootCard,
|
|
29034
29033
|
renderAccountRows: () => renderAuthLine
|
|
29035
29034
|
});
|
|
29036
|
-
import { join as
|
|
29035
|
+
import { join as join24 } from "path";
|
|
29037
29036
|
function resolvePersonaName(slug, loadConfig3) {
|
|
29038
29037
|
try {
|
|
29039
29038
|
const config = loadConfig3 ? loadConfig3() : loadConfig();
|
|
@@ -29123,7 +29122,7 @@ function renderBootCard(opts) {
|
|
|
29123
29122
|
`);
|
|
29124
29123
|
}
|
|
29125
29124
|
async function runAllProbes(opts) {
|
|
29126
|
-
const claudeDir =
|
|
29125
|
+
const claudeDir = join24(opts.agentDir, ".claude");
|
|
29127
29126
|
const probes = {};
|
|
29128
29127
|
const slug = opts.agentSlug ?? opts.agentName;
|
|
29129
29128
|
await Promise.allSettled([
|
|
@@ -29472,16 +29471,16 @@ function renderAccountRow2(snap, opts) {
|
|
|
29472
29471
|
const lines = [];
|
|
29473
29472
|
const marker = snap.isActive ? "\u25cf " : "";
|
|
29474
29473
|
if (!snap.quota) {
|
|
29475
|
-
lines.push(`${marker}<code>${
|
|
29474
|
+
lines.push(`${marker}<code>${escapeHtml11(snap.label)}</code> <i>quota probe failed</i>`);
|
|
29476
29475
|
if (snap.quotaError) {
|
|
29477
|
-
lines.push(` <i>${
|
|
29476
|
+
lines.push(` <i>${escapeHtml11(snap.quotaError)}</i>`);
|
|
29478
29477
|
}
|
|
29479
29478
|
return lines;
|
|
29480
29479
|
}
|
|
29481
29480
|
const q = snap.quota;
|
|
29482
29481
|
const fiveStr = fmtPct2(q.fiveHourUtilizationPct);
|
|
29483
29482
|
const sevenStr = fmtPct2(q.sevenDayUtilizationPct);
|
|
29484
|
-
lines.push(`${marker}<code>${
|
|
29483
|
+
lines.push(`${marker}<code>${escapeHtml11(snap.label)}</code> ${fiveStr} / ${sevenStr}`);
|
|
29485
29484
|
const health = classifyHealth2(snap);
|
|
29486
29485
|
if (health === "blocked") {
|
|
29487
29486
|
const win = bindingWindow3(q);
|
|
@@ -29587,13 +29586,13 @@ function renderFallbackAnnouncement2(input) {
|
|
|
29587
29586
|
const limitWord = input.oldQuota ? limitWordFor2(input.oldQuota) : "quota";
|
|
29588
29587
|
const headerLimit = limitWord === "quota" ? "quota cap" : `${limitWord} limit`;
|
|
29589
29588
|
if (!input.newLabel) {
|
|
29590
|
-
lines.push(`\uD83D\uDD34 <b>All accounts blocked \u00b7 ${headerLimit} on ${
|
|
29589
|
+
lines.push(`\uD83D\uDD34 <b>All accounts blocked \u00b7 ${headerLimit} on ${escapeHtml11(input.oldLabel)}</b>`);
|
|
29591
29590
|
lines.push("");
|
|
29592
|
-
lines.push(`Triggered by: agent <b>${
|
|
29591
|
+
lines.push(`Triggered by: agent <b>${escapeHtml11(input.triggerAgent)}</b>`);
|
|
29593
29592
|
if (input.oldQuota) {
|
|
29594
29593
|
const recovery = recoveryAtFor2(input.oldQuota);
|
|
29595
29594
|
if (recovery) {
|
|
29596
|
-
lines.push(`${
|
|
29595
|
+
lines.push(`${escapeHtml11(input.oldLabel)} recovers ${formatAbsolute2(recovery, tz)} ` + `(in ${formatRelative2(recovery, now)})`);
|
|
29597
29596
|
}
|
|
29598
29597
|
}
|
|
29599
29598
|
lines.push("");
|
|
@@ -29601,15 +29600,15 @@ function renderFallbackAnnouncement2(input) {
|
|
|
29601
29600
|
return lines.join(`
|
|
29602
29601
|
`);
|
|
29603
29602
|
}
|
|
29604
|
-
lines.push(`\u2713 <b>Switched fleet \u00b7 ${headerLimit} on ${
|
|
29603
|
+
lines.push(`\u2713 <b>Switched fleet \u00b7 ${headerLimit} on ${escapeHtml11(input.oldLabel)}</b>`);
|
|
29605
29604
|
lines.push("");
|
|
29606
|
-
lines.push(`<code>${
|
|
29607
|
-
lines.push(`Triggered by: agent <b>${
|
|
29605
|
+
lines.push(`<code>${escapeHtml11(input.oldLabel)}</code> \u2192 <code>${escapeHtml11(input.newLabel)}</code>`);
|
|
29606
|
+
lines.push(`Triggered by: agent <b>${escapeHtml11(input.triggerAgent)}</b>`);
|
|
29608
29607
|
lines.push("");
|
|
29609
29608
|
if (input.oldQuota) {
|
|
29610
29609
|
const recovery = recoveryAtFor2(input.oldQuota);
|
|
29611
29610
|
if (recovery) {
|
|
29612
|
-
lines.push(`<code>${
|
|
29611
|
+
lines.push(`<code>${escapeHtml11(input.oldLabel)}</code> recovers ` + `${formatAbsolute2(recovery, tz)} (in ${formatRelative2(recovery, now)})`);
|
|
29613
29612
|
}
|
|
29614
29613
|
}
|
|
29615
29614
|
if (input.newQuota) {
|
|
@@ -29617,7 +29616,7 @@ function renderFallbackAnnouncement2(input) {
|
|
|
29617
29616
|
const sevenStr = fmtPct2(input.newQuota.sevenDayUtilizationPct);
|
|
29618
29617
|
const hasHeadroom = input.newQuota.fiveHourUtilizationPct < THROTTLING_THRESHOLD_PCT2 && input.newQuota.sevenDayUtilizationPct < THROTTLING_THRESHOLD_PCT2;
|
|
29619
29618
|
const headroomStr = hasHeadroom ? "<i>(plenty of headroom)</i>" : "<i>(near limit \u2014 watch this)</i>";
|
|
29620
|
-
lines.push(`<code>${
|
|
29619
|
+
lines.push(`<code>${escapeHtml11(input.newLabel)}</code> now: ${fiveStr} of 5h \u00b7 ${sevenStr} of 7d ${headroomStr}`);
|
|
29621
29620
|
} else {
|
|
29622
29621
|
lines.push(`<i>(quota probe for new account is pending \u2014 will reflect on next /auth)</i>`);
|
|
29623
29622
|
}
|
|
@@ -29676,7 +29675,7 @@ function switchPriority2(s) {
|
|
|
29676
29675
|
return 2;
|
|
29677
29676
|
return 3;
|
|
29678
29677
|
}
|
|
29679
|
-
function
|
|
29678
|
+
function escapeHtml11(s) {
|
|
29680
29679
|
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
29681
29680
|
}
|
|
29682
29681
|
function buildSnapshotsFromState2(state4, quotas) {
|
|
@@ -29741,12 +29740,12 @@ var init_flock = () => {};
|
|
|
29741
29740
|
// ../src/vault/vault.ts
|
|
29742
29741
|
import { randomBytes as randomBytes5, scryptSync, createCipheriv, createDecipheriv } from "node:crypto";
|
|
29743
29742
|
import {
|
|
29744
|
-
readFileSync as
|
|
29745
|
-
writeFileSync as
|
|
29746
|
-
existsSync as
|
|
29747
|
-
renameSync as
|
|
29743
|
+
readFileSync as readFileSync34,
|
|
29744
|
+
writeFileSync as writeFileSync22,
|
|
29745
|
+
existsSync as existsSync36,
|
|
29746
|
+
renameSync as renameSync11,
|
|
29748
29747
|
mkdirSync as mkdirSync23,
|
|
29749
|
-
unlinkSync as
|
|
29748
|
+
unlinkSync as unlinkSync12,
|
|
29750
29749
|
lstatSync,
|
|
29751
29750
|
realpathSync
|
|
29752
29751
|
} from "node:fs";
|
|
@@ -29781,12 +29780,12 @@ function normalizeSecrets(raw) {
|
|
|
29781
29780
|
return out;
|
|
29782
29781
|
}
|
|
29783
29782
|
function openVault(passphrase, vaultPath) {
|
|
29784
|
-
if (!
|
|
29783
|
+
if (!existsSync36(vaultPath)) {
|
|
29785
29784
|
throw new VaultError(`Vault file not found: ${vaultPath}`);
|
|
29786
29785
|
}
|
|
29787
29786
|
let vaultFile;
|
|
29788
29787
|
try {
|
|
29789
|
-
vaultFile = JSON.parse(
|
|
29788
|
+
vaultFile = JSON.parse(readFileSync34(vaultPath, "utf8"));
|
|
29790
29789
|
} catch {
|
|
29791
29790
|
throw new VaultError(`Failed to read vault file: ${vaultPath}`);
|
|
29792
29791
|
}
|
|
@@ -29840,7 +29839,7 @@ import {
|
|
|
29840
29839
|
statSync as statSync11,
|
|
29841
29840
|
writeSync as writeSync2
|
|
29842
29841
|
} from "node:fs";
|
|
29843
|
-
import { join as
|
|
29842
|
+
import { join as join34 } from "node:path";
|
|
29844
29843
|
import { tmpdir } from "node:os";
|
|
29845
29844
|
import { constants as fsConstants } from "node:fs";
|
|
29846
29845
|
function isVaultReference(value) {
|
|
@@ -29892,11 +29891,11 @@ function materializationRoot() {
|
|
|
29892
29891
|
return cachedRoot;
|
|
29893
29892
|
const xdg = process.env.XDG_RUNTIME_DIR;
|
|
29894
29893
|
if (xdg) {
|
|
29895
|
-
const base =
|
|
29894
|
+
const base = join34(xdg, "switchroom", "vault");
|
|
29896
29895
|
mkdirSync24(base, { recursive: true, mode: 448 });
|
|
29897
|
-
cachedRoot = mkdtempSync2(
|
|
29896
|
+
cachedRoot = mkdtempSync2(join34(base, "run-"));
|
|
29898
29897
|
} else {
|
|
29899
|
-
cachedRoot = mkdtempSync2(
|
|
29898
|
+
cachedRoot = mkdtempSync2(join34(tmpdir(), "switchroom-vault-"));
|
|
29900
29899
|
}
|
|
29901
29900
|
chmodSync5(cachedRoot, 448);
|
|
29902
29901
|
return cachedRoot;
|
|
@@ -29911,7 +29910,7 @@ function writeFileExclusive(filePath, content) {
|
|
|
29911
29910
|
}
|
|
29912
29911
|
}
|
|
29913
29912
|
function materializeFilesEntry(key, files) {
|
|
29914
|
-
const dir =
|
|
29913
|
+
const dir = join34(materializationRoot(), key);
|
|
29915
29914
|
if (materializedDirs.has(dir)) {
|
|
29916
29915
|
try {
|
|
29917
29916
|
rmSync3(dir, { recursive: true, force: true });
|
|
@@ -29927,7 +29926,7 @@ function materializeFilesEntry(key, files) {
|
|
|
29927
29926
|
if (filename.includes("/") || filename.includes("\\") || filename === ".." || filename === "." || filename.includes("\x00")) {
|
|
29928
29927
|
throw new Error(`Refusing to materialize vault file with unsafe name: ${filename}`);
|
|
29929
29928
|
}
|
|
29930
|
-
const filePath =
|
|
29929
|
+
const filePath = join34(dir, filename);
|
|
29931
29930
|
const content = encoding === "base64" ? Buffer.from(value, "base64") : value;
|
|
29932
29931
|
writeFileExclusive(filePath, content);
|
|
29933
29932
|
}
|
|
@@ -30060,7 +30059,7 @@ __export(exports_materialize_bot_token, {
|
|
|
30060
30059
|
materializeBotToken: () => materializeBotToken,
|
|
30061
30060
|
BotTokenMaterializeError: () => BotTokenMaterializeError
|
|
30062
30061
|
});
|
|
30063
|
-
import { existsSync as
|
|
30062
|
+
import { existsSync as existsSync37 } from "node:fs";
|
|
30064
30063
|
function pickConfiguredToken(config, agentName3) {
|
|
30065
30064
|
if (agentName3) {
|
|
30066
30065
|
const agent = config.agents?.[agentName3];
|
|
@@ -30074,7 +30073,7 @@ function tryDirectVaultRead(ref, config, passphrase) {
|
|
|
30074
30073
|
if (!passphrase)
|
|
30075
30074
|
return null;
|
|
30076
30075
|
const vaultPath = resolvePath(config.vault?.path ?? "~/.switchroom/vault.enc");
|
|
30077
|
-
if (!
|
|
30076
|
+
if (!existsSync37(vaultPath))
|
|
30078
30077
|
return null;
|
|
30079
30078
|
try {
|
|
30080
30079
|
const secrets = openVault(passphrase, vaultPath);
|
|
@@ -30166,7 +30165,7 @@ __export(exports_tmux, {
|
|
|
30166
30165
|
captureAgentPane: () => captureAgentPane
|
|
30167
30166
|
});
|
|
30168
30167
|
import { execFileSync as execFileSync4 } from "node:child_process";
|
|
30169
|
-
import { mkdirSync as mkdirSync25, readdirSync as readdirSync6, statSync as statSync12, unlinkSync as
|
|
30168
|
+
import { mkdirSync as mkdirSync25, readdirSync as readdirSync6, statSync as statSync12, unlinkSync as unlinkSync13, writeFileSync as writeFileSync23 } from "node:fs";
|
|
30170
30169
|
import { resolve as resolve7 } from "node:path";
|
|
30171
30170
|
function captureAgentPane(opts) {
|
|
30172
30171
|
const { agentName: agentName3, agentDir, reason } = opts;
|
|
@@ -30212,7 +30211,7 @@ function captureAgentPane(opts) {
|
|
|
30212
30211
|
` + `
|
|
30213
30212
|
`;
|
|
30214
30213
|
try {
|
|
30215
|
-
|
|
30214
|
+
writeFileSync23(outPath, Buffer.concat([Buffer.from(header, "utf8"), body]), {
|
|
30216
30215
|
mode: 420
|
|
30217
30216
|
});
|
|
30218
30217
|
} catch (err) {
|
|
@@ -30280,7 +30279,7 @@ function pruneOldReports(dir, retain) {
|
|
|
30280
30279
|
}).sort((a, b) => b.mtimeMs - a.mtimeMs);
|
|
30281
30280
|
for (const stale of files.slice(retain)) {
|
|
30282
30281
|
try {
|
|
30283
|
-
|
|
30282
|
+
unlinkSync13(stale.full);
|
|
30284
30283
|
} catch {}
|
|
30285
30284
|
}
|
|
30286
30285
|
}
|
|
@@ -30322,7 +30321,7 @@ function truncateDiffForCard(unifiedDiff, maxLines = 50, maxChars = 3000) {
|
|
|
30322
30321
|
}
|
|
30323
30322
|
return out === unifiedDiff ? out : out + sentinel;
|
|
30324
30323
|
}
|
|
30325
|
-
function
|
|
30324
|
+
function escapeHtml12(s) {
|
|
30326
30325
|
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
30327
30326
|
}
|
|
30328
30327
|
function clipReason(reason) {
|
|
@@ -30333,10 +30332,10 @@ function clipReason(reason) {
|
|
|
30333
30332
|
function buildConfigApprovalCardBody(args) {
|
|
30334
30333
|
const safeReason = clipReason(args.reason);
|
|
30335
30334
|
const render = (diff) => `\uD83D\uDEE0 <b>Config edit proposed</b>
|
|
30336
|
-
` + `Agent: <code>${
|
|
30337
|
-
` + `Reason: ${
|
|
30335
|
+
` + `Agent: <code>${escapeHtml12(args.agentName)}</code>
|
|
30336
|
+
` + `Reason: ${escapeHtml12(safeReason)}
|
|
30338
30337
|
|
|
30339
|
-
` + `<pre>${
|
|
30338
|
+
` + `<pre>${escapeHtml12(diff)}</pre>`;
|
|
30340
30339
|
return truncateRawToFit({
|
|
30341
30340
|
raw: args.unifiedDiff,
|
|
30342
30341
|
render,
|
|
@@ -30448,8 +30447,8 @@ async function handleRequestConfigFinalize(_client, msg, deps) {
|
|
|
30448
30447
|
}
|
|
30449
30448
|
pending.delete(msg.requestId);
|
|
30450
30449
|
const body = msg.outcome === "applied" ? `\u2705 <b>Applied</b>${msg.detail ? `
|
|
30451
|
-
${
|
|
30452
|
-
${
|
|
30450
|
+
${escapeHtml12(msg.detail)}` : ""}` : `\u26a0\ufe0f <b>Reconcile failed; rolled back</b>${msg.detail ? `
|
|
30451
|
+
${escapeHtml12(msg.detail)}` : ""}`;
|
|
30453
30452
|
try {
|
|
30454
30453
|
await deps.editCard({
|
|
30455
30454
|
chatId: entry.chatId,
|
|
@@ -30589,17 +30588,17 @@ function registerApprovalsCommands(bot, opts) {
|
|
|
30589
30588
|
return;
|
|
30590
30589
|
}
|
|
30591
30590
|
if (decisions.length === 0) {
|
|
30592
|
-
await ctx.reply(agentFilter ? `No active approvals for <code>${
|
|
30591
|
+
await ctx.reply(agentFilter ? `No active approvals for <code>${escapeHtml13(agentFilter)}</code>.` : "No active approvals.", { parse_mode: "HTML" });
|
|
30593
30592
|
return;
|
|
30594
30593
|
}
|
|
30595
30594
|
const byAgent = new Map;
|
|
30596
30595
|
for (const d of decisions)
|
|
30597
30596
|
byAgent.set(d.agent_unit, (byAgent.get(d.agent_unit) ?? 0) + 1);
|
|
30598
|
-
const summary = Array.from(byAgent.entries()).map(([a, n]) => `\u2022 <b>${
|
|
30597
|
+
const summary = Array.from(byAgent.entries()).map(([a, n]) => `\u2022 <b>${escapeHtml13(a)}</b>: ${n}`).join(`
|
|
30599
30598
|
`);
|
|
30600
30599
|
const detail = decisions.slice(0, 20).map((d) => {
|
|
30601
30600
|
const ttl = d.ttl_expires_at === null ? "always" : `until ${new Date(d.ttl_expires_at).toISOString().slice(0, 16).replace("T", " ")}`;
|
|
30602
|
-
return `<code>${
|
|
30601
|
+
return `<code>${escapeHtml13(d.id.slice(0, 8))}</code> ` + `${escapeHtml13(d.agent_unit)} \u2192 ` + `<code>${escapeHtml13(d.scope)}</code> ` + `(${escapeHtml13(d.action)}, ${ttl}) ` + `\u00b7 /approvals revoke ${escapeHtml13(d.id)}`;
|
|
30603
30602
|
}).join(`
|
|
30604
30603
|
`);
|
|
30605
30604
|
await ctx.reply(`<b>Active approvals</b>
|
|
@@ -30625,13 +30624,13 @@ ${detail}`, {
|
|
|
30625
30624
|
await ctx.reply("Approval kernel unreachable.");
|
|
30626
30625
|
return;
|
|
30627
30626
|
}
|
|
30628
|
-
await ctx.reply(ok ? `Revoked <code>${
|
|
30627
|
+
await ctx.reply(ok ? `Revoked <code>${escapeHtml13(id)}</code>.` : `No such active decision <code>${escapeHtml13(id)}</code>.`, { parse_mode: "HTML" });
|
|
30629
30628
|
return;
|
|
30630
30629
|
}
|
|
30631
|
-
await ctx.reply(`Unknown subcommand <code>${
|
|
30630
|
+
await ctx.reply(`Unknown subcommand <code>${escapeHtml13(sub)}</code>. ` + `Use <code>/approvals list</code> or <code>/approvals revoke <id></code>. ` + `(<code>add</code> and <code>stats</code> are coming in a follow-up.)`, { parse_mode: "HTML" });
|
|
30632
30631
|
});
|
|
30633
30632
|
}
|
|
30634
|
-
function
|
|
30633
|
+
function escapeHtml13(s) {
|
|
30635
30634
|
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
30636
30635
|
}
|
|
30637
30636
|
var init_approvals_commands = __esm(() => {
|
|
@@ -30772,23 +30771,23 @@ var import_runner2 = __toESM(require_mod3(), 1);
|
|
|
30772
30771
|
import { randomBytes as randomBytes6 } from "crypto";
|
|
30773
30772
|
import { execFileSync as execFileSync5, execSync as execSync2, spawn as spawn2 } from "child_process";
|
|
30774
30773
|
import {
|
|
30775
|
-
readFileSync as
|
|
30776
|
-
writeFileSync as
|
|
30774
|
+
readFileSync as readFileSync35,
|
|
30775
|
+
writeFileSync as writeFileSync24,
|
|
30777
30776
|
mkdirSync as mkdirSync26,
|
|
30778
30777
|
readdirSync as readdirSync7,
|
|
30779
30778
|
rmSync as rmSync4,
|
|
30780
30779
|
statSync as statSync13,
|
|
30781
|
-
renameSync as
|
|
30780
|
+
renameSync as renameSync12,
|
|
30782
30781
|
realpathSync as realpathSync2,
|
|
30783
30782
|
chmodSync as chmodSync6,
|
|
30784
30783
|
openSync as openSync8,
|
|
30785
30784
|
closeSync as closeSync8,
|
|
30786
|
-
existsSync as
|
|
30787
|
-
unlinkSync as
|
|
30785
|
+
existsSync as existsSync38,
|
|
30786
|
+
unlinkSync as unlinkSync14,
|
|
30788
30787
|
appendFileSync as appendFileSync5
|
|
30789
30788
|
} from "fs";
|
|
30790
30789
|
import { homedir as homedir14 } from "os";
|
|
30791
|
-
import { join as
|
|
30790
|
+
import { join as join35, extname, sep as sep3, basename as basename7 } from "path";
|
|
30792
30791
|
|
|
30793
30792
|
// plugin-logger.ts
|
|
30794
30793
|
import { appendFileSync, mkdirSync, renameSync, statSync, existsSync } from "fs";
|
|
@@ -32182,6 +32181,33 @@ function renderActivityFeed(lines) {
|
|
|
32182
32181
|
return out.join(`
|
|
32183
32182
|
`);
|
|
32184
32183
|
}
|
|
32184
|
+
var NESTED_MAX_LINES = 4;
|
|
32185
|
+
var NESTED_LINE_MAX = 90;
|
|
32186
|
+
var NESTED_PREFIX = " \u21b3 ";
|
|
32187
|
+
function renderActivityFeedWithNested(lines, childLines) {
|
|
32188
|
+
const children = childLines.map((s) => s.trim()).filter((s) => s.length > 0);
|
|
32189
|
+
if (children.length === 0)
|
|
32190
|
+
return renderActivityFeed(lines);
|
|
32191
|
+
const out = [];
|
|
32192
|
+
const shownParent = lines.slice(-MIRROR_MAX_LINES);
|
|
32193
|
+
const hiddenParent = lines.length - shownParent.length;
|
|
32194
|
+
if (hiddenParent > 0)
|
|
32195
|
+
out.push(`<i>\u2713 +${hiddenParent} earlier\u2026</i>`);
|
|
32196
|
+
for (const l of shownParent)
|
|
32197
|
+
out.push(`<i>\u2713 ${escapeFeedHtml(l)}</i>`);
|
|
32198
|
+
const shownChild = children.slice(-NESTED_MAX_LINES);
|
|
32199
|
+
const hiddenChild = children.length - shownChild.length;
|
|
32200
|
+
if (hiddenChild > 0)
|
|
32201
|
+
out.push(`${NESTED_PREFIX}<i>+${hiddenChild} earlier\u2026</i>`);
|
|
32202
|
+
const lastChildIdx = shownChild.length - 1;
|
|
32203
|
+
shownChild.forEach((l, i) => {
|
|
32204
|
+
const t = l.length > NESTED_LINE_MAX ? l.slice(0, NESTED_LINE_MAX - 1) + "\u2026" : l;
|
|
32205
|
+
const esc = escapeFeedHtml(t);
|
|
32206
|
+
out.push(i === lastChildIdx ? `${NESTED_PREFIX}<b>\u2192 ${esc}</b>` : `${NESTED_PREFIX}<i>${esc}</i>`);
|
|
32207
|
+
});
|
|
32208
|
+
return out.length > 0 ? out.join(`
|
|
32209
|
+
`) : null;
|
|
32210
|
+
}
|
|
32185
32211
|
function appendActivityLabel(lines, label) {
|
|
32186
32212
|
const l = (label ?? "").trim();
|
|
32187
32213
|
if (l.length === 0)
|
|
@@ -33198,11 +33224,6 @@ async function handleStreamReply(args, state, deps) {
|
|
|
33198
33224
|
done,
|
|
33199
33225
|
streamExisted
|
|
33200
33226
|
});
|
|
33201
|
-
if (!stream) {
|
|
33202
|
-
const prefix = deps.takeHandoffPrefix(format === "html" ? "html" : format === "markdownv2" ? "markdownv2" : "text");
|
|
33203
|
-
if (prefix.length > 0)
|
|
33204
|
-
effectiveText = prefix + effectiveText;
|
|
33205
|
-
}
|
|
33206
33227
|
if (!stream) {
|
|
33207
33228
|
let replyToMessageId;
|
|
33208
33229
|
if (args.reply_to != null) {
|
|
@@ -42129,125 +42150,29 @@ function isTurnFlushSafetyEnabled(env = process.env) {
|
|
|
42129
42150
|
return true;
|
|
42130
42151
|
}
|
|
42131
42152
|
|
|
42132
|
-
//
|
|
42133
|
-
import {
|
|
42134
|
-
import { dirname as dirname7, join as join13 } from "node:path";
|
|
42135
|
-
var TOPIC_DISPLAY_MAX = 117;
|
|
42136
|
-
var HANDOFF_TOPIC_FILENAME = ".handoff-topic";
|
|
42137
|
-
var LAST_TURN_SUMMARY_FILENAME = ".last-turn-summary";
|
|
42153
|
+
// agent-dir.ts
|
|
42154
|
+
import { dirname as dirname7 } from "node:path";
|
|
42138
42155
|
function resolveAgentDirFromEnv() {
|
|
42139
42156
|
const state3 = process.env.TELEGRAM_STATE_DIR;
|
|
42140
42157
|
if (!state3 || state3.trim().length === 0)
|
|
42141
42158
|
return null;
|
|
42142
42159
|
return dirname7(state3);
|
|
42143
42160
|
}
|
|
42144
|
-
function readHandoffTopic(agentDir) {
|
|
42145
|
-
const p = join13(agentDir, HANDOFF_TOPIC_FILENAME);
|
|
42146
|
-
if (!existsSync13(p))
|
|
42147
|
-
return null;
|
|
42148
|
-
let raw;
|
|
42149
|
-
try {
|
|
42150
|
-
raw = readFileSync9(p, "utf-8");
|
|
42151
|
-
} catch {
|
|
42152
|
-
return null;
|
|
42153
|
-
}
|
|
42154
|
-
const lines = raw.split(/\r?\n/).map((l) => l.trim()).filter((l) => l.length > 0);
|
|
42155
|
-
if (lines.length === 0)
|
|
42156
|
-
return null;
|
|
42157
|
-
let topic = lines[0];
|
|
42158
|
-
if (topic.length > TOPIC_DISPLAY_MAX) {
|
|
42159
|
-
topic = topic.slice(0, TOPIC_DISPLAY_MAX) + "\u2026";
|
|
42160
|
-
}
|
|
42161
|
-
return topic;
|
|
42162
|
-
}
|
|
42163
|
-
function readLastTurnSummary(agentDir) {
|
|
42164
|
-
const p = join13(agentDir, LAST_TURN_SUMMARY_FILENAME);
|
|
42165
|
-
if (!existsSync13(p))
|
|
42166
|
-
return null;
|
|
42167
|
-
let raw;
|
|
42168
|
-
try {
|
|
42169
|
-
raw = readFileSync9(p, "utf-8");
|
|
42170
|
-
} catch {
|
|
42171
|
-
return null;
|
|
42172
|
-
}
|
|
42173
|
-
const lines = raw.split(/\r?\n/).map((l) => l.trim()).filter((l) => l.length > 0);
|
|
42174
|
-
if (lines.length === 0)
|
|
42175
|
-
return null;
|
|
42176
|
-
let topic = lines[0];
|
|
42177
|
-
if (topic.length > TOPIC_DISPLAY_MAX) {
|
|
42178
|
-
topic = topic.slice(0, TOPIC_DISPLAY_MAX) + "\u2026";
|
|
42179
|
-
}
|
|
42180
|
-
return topic;
|
|
42181
|
-
}
|
|
42182
|
-
function consumeHandoffTopic(agentDir) {
|
|
42183
|
-
const primary = readHandoffTopic(agentDir);
|
|
42184
|
-
const primaryPath = join13(agentDir, HANDOFF_TOPIC_FILENAME);
|
|
42185
|
-
const fallbackPath = join13(agentDir, LAST_TURN_SUMMARY_FILENAME);
|
|
42186
|
-
const removeFallback = () => {
|
|
42187
|
-
try {
|
|
42188
|
-
unlinkSync2(fallbackPath);
|
|
42189
|
-
} catch {}
|
|
42190
|
-
};
|
|
42191
|
-
if (primary !== null) {
|
|
42192
|
-
try {
|
|
42193
|
-
unlinkSync2(primaryPath);
|
|
42194
|
-
} catch {}
|
|
42195
|
-
removeFallback();
|
|
42196
|
-
return primary;
|
|
42197
|
-
}
|
|
42198
|
-
const fallback = readLastTurnSummary(agentDir);
|
|
42199
|
-
if (fallback !== null) {
|
|
42200
|
-
removeFallback();
|
|
42201
|
-
return fallback;
|
|
42202
|
-
}
|
|
42203
|
-
return null;
|
|
42204
|
-
}
|
|
42205
|
-
function shouldShowHandoffLine() {
|
|
42206
|
-
const v = process.env.SWITCHROOM_HANDOFF_SHOW_LINE;
|
|
42207
|
-
if (v === undefined)
|
|
42208
|
-
return true;
|
|
42209
|
-
return v.toLowerCase() !== "false";
|
|
42210
|
-
}
|
|
42211
|
-
function formatHandoffLine(topic, format) {
|
|
42212
|
-
const prefix = "\u21a9\ufe0f Picked up where we left off, ";
|
|
42213
|
-
if (format === "html") {
|
|
42214
|
-
return `<i>${prefix}${escapeHtml7(topic)}</i>
|
|
42215
|
-
|
|
42216
|
-
`;
|
|
42217
|
-
}
|
|
42218
|
-
if (format === "markdownv2") {
|
|
42219
|
-
const escaped = escapeMarkdownV2(topic);
|
|
42220
|
-
const prefixEsc = escapeMarkdownV2(prefix);
|
|
42221
|
-
return `_${prefixEsc}${escaped}_
|
|
42222
|
-
|
|
42223
|
-
`;
|
|
42224
|
-
}
|
|
42225
|
-
return `${prefix}${topic}
|
|
42226
|
-
|
|
42227
|
-
`;
|
|
42228
|
-
}
|
|
42229
|
-
function escapeHtml7(s) {
|
|
42230
|
-
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
42231
|
-
}
|
|
42232
|
-
var MDV2_SPECIALS = /[_*\[\]()~`>#+\-=|{}.!\\]/g;
|
|
42233
|
-
function escapeMarkdownV2(s) {
|
|
42234
|
-
return s.replace(MDV2_SPECIALS, (m) => "\\" + m);
|
|
42235
|
-
}
|
|
42236
42161
|
|
|
42237
42162
|
// active-reactions.ts
|
|
42238
|
-
import { readFileSync as
|
|
42239
|
-
import { join as
|
|
42163
|
+
import { readFileSync as readFileSync9, writeFileSync as writeFileSync6, renameSync as renameSync2, existsSync as existsSync13, unlinkSync as unlinkSync2 } from "node:fs";
|
|
42164
|
+
import { join as join13 } from "node:path";
|
|
42240
42165
|
var ACTIVE_REACTIONS_FILENAME = ".active-reactions.json";
|
|
42241
42166
|
function reactionsPath(agentDir) {
|
|
42242
|
-
return
|
|
42167
|
+
return join13(agentDir, ACTIVE_REACTIONS_FILENAME);
|
|
42243
42168
|
}
|
|
42244
42169
|
function readActiveReactions(agentDir) {
|
|
42245
42170
|
const p = reactionsPath(agentDir);
|
|
42246
|
-
if (!
|
|
42171
|
+
if (!existsSync13(p))
|
|
42247
42172
|
return [];
|
|
42248
42173
|
let raw;
|
|
42249
42174
|
try {
|
|
42250
|
-
raw =
|
|
42175
|
+
raw = readFileSync9(p, "utf-8");
|
|
42251
42176
|
} catch {
|
|
42252
42177
|
return [];
|
|
42253
42178
|
}
|
|
@@ -42273,15 +42198,15 @@ function writeActiveReactions(agentDir, reactions) {
|
|
|
42273
42198
|
const p = reactionsPath(agentDir);
|
|
42274
42199
|
if (reactions.length === 0) {
|
|
42275
42200
|
try {
|
|
42276
|
-
|
|
42201
|
+
unlinkSync2(p);
|
|
42277
42202
|
} catch {}
|
|
42278
42203
|
return;
|
|
42279
42204
|
}
|
|
42280
42205
|
const tmp = `${p}.tmp-${process.pid}-${Date.now()}`;
|
|
42281
42206
|
try {
|
|
42282
|
-
|
|
42207
|
+
writeFileSync6(tmp, JSON.stringify(reactions) + `
|
|
42283
42208
|
`, "utf-8");
|
|
42284
|
-
|
|
42209
|
+
renameSync2(tmp, p);
|
|
42285
42210
|
} catch {}
|
|
42286
42211
|
}
|
|
42287
42212
|
function addActiveReaction(agentDir, reaction) {
|
|
@@ -42298,24 +42223,24 @@ function removeActiveReaction(agentDir, chatId, messageId) {
|
|
|
42298
42223
|
}
|
|
42299
42224
|
function clearActiveReactions(agentDir) {
|
|
42300
42225
|
try {
|
|
42301
|
-
|
|
42226
|
+
unlinkSync2(reactionsPath(agentDir));
|
|
42302
42227
|
} catch {}
|
|
42303
42228
|
}
|
|
42304
42229
|
|
|
42305
42230
|
// active-reactions.ts
|
|
42306
|
-
import { readFileSync as
|
|
42307
|
-
import { join as
|
|
42231
|
+
import { readFileSync as readFileSync10, writeFileSync as writeFileSync7, renameSync as renameSync3, existsSync as existsSync14, unlinkSync as unlinkSync3 } from "node:fs";
|
|
42232
|
+
import { join as join14 } from "node:path";
|
|
42308
42233
|
var ACTIVE_REACTIONS_FILENAME2 = ".active-reactions.json";
|
|
42309
42234
|
function reactionsPath2(agentDir) {
|
|
42310
|
-
return
|
|
42235
|
+
return join14(agentDir, ACTIVE_REACTIONS_FILENAME2);
|
|
42311
42236
|
}
|
|
42312
42237
|
function readActiveReactions2(agentDir) {
|
|
42313
42238
|
const p = reactionsPath2(agentDir);
|
|
42314
|
-
if (!
|
|
42239
|
+
if (!existsSync14(p))
|
|
42315
42240
|
return [];
|
|
42316
42241
|
let raw;
|
|
42317
42242
|
try {
|
|
42318
|
-
raw =
|
|
42243
|
+
raw = readFileSync10(p, "utf-8");
|
|
42319
42244
|
} catch {
|
|
42320
42245
|
return [];
|
|
42321
42246
|
}
|
|
@@ -42339,7 +42264,7 @@ function readActiveReactions2(agentDir) {
|
|
|
42339
42264
|
}
|
|
42340
42265
|
function clearActiveReactions2(agentDir) {
|
|
42341
42266
|
try {
|
|
42342
|
-
|
|
42267
|
+
unlinkSync3(reactionsPath2(agentDir));
|
|
42343
42268
|
} catch {}
|
|
42344
42269
|
}
|
|
42345
42270
|
|
|
@@ -43192,17 +43117,17 @@ async function approvalRecord(args, opts) {
|
|
|
43192
43117
|
}
|
|
43193
43118
|
|
|
43194
43119
|
// quota-check.ts
|
|
43195
|
-
import { readFileSync as
|
|
43196
|
-
import { join as
|
|
43120
|
+
import { readFileSync as readFileSync12, existsSync as existsSync16 } from "fs";
|
|
43121
|
+
import { join as join16 } from "path";
|
|
43197
43122
|
var OAUTH_BETA2 = "oauth-2025-04-20";
|
|
43198
43123
|
var DEFAULT_USER_AGENT2 = "claude-cli/1.0.0 (external, cli)";
|
|
43199
43124
|
var DEFAULT_PROBE_MODEL2 = "claude-haiku-4-5-20251001";
|
|
43200
43125
|
function readOauthToken2(claudeConfigDir) {
|
|
43201
|
-
const tokenFile =
|
|
43202
|
-
if (!
|
|
43126
|
+
const tokenFile = join16(claudeConfigDir, ".oauth-token");
|
|
43127
|
+
if (!existsSync16(tokenFile))
|
|
43203
43128
|
return null;
|
|
43204
43129
|
try {
|
|
43205
|
-
const raw =
|
|
43130
|
+
const raw = readFileSync12(tokenFile, "utf-8").trim();
|
|
43206
43131
|
return raw.length > 0 ? raw : null;
|
|
43207
43132
|
} catch {
|
|
43208
43133
|
return null;
|
|
@@ -43798,7 +43723,7 @@ init_schema();
|
|
|
43798
43723
|
init_paths();
|
|
43799
43724
|
init_overlay_loader();
|
|
43800
43725
|
init_merge();
|
|
43801
|
-
import { readFileSync as
|
|
43726
|
+
import { readFileSync as readFileSync13, existsSync as existsSync17 } from "node:fs";
|
|
43802
43727
|
import { homedir as homedir8 } from "node:os";
|
|
43803
43728
|
import { resolve as resolve5 } from "node:path";
|
|
43804
43729
|
|
|
@@ -43874,7 +43799,7 @@ function findConfigFile2(startDir) {
|
|
|
43874
43799
|
resolve5(userDir, "clerk.yml")
|
|
43875
43800
|
].filter(Boolean);
|
|
43876
43801
|
for (const path of searchPaths) {
|
|
43877
|
-
if (
|
|
43802
|
+
if (existsSync17(path)) {
|
|
43878
43803
|
return path;
|
|
43879
43804
|
}
|
|
43880
43805
|
}
|
|
@@ -43882,12 +43807,12 @@ function findConfigFile2(startDir) {
|
|
|
43882
43807
|
}
|
|
43883
43808
|
function loadConfig2(configPath) {
|
|
43884
43809
|
const filePath = configPath ?? findConfigFile2();
|
|
43885
|
-
if (!
|
|
43810
|
+
if (!existsSync17(filePath)) {
|
|
43886
43811
|
throw new ConfigError2(`Config file not found: ${filePath}`);
|
|
43887
43812
|
}
|
|
43888
43813
|
let raw;
|
|
43889
43814
|
try {
|
|
43890
|
-
raw =
|
|
43815
|
+
raw = readFileSync13(filePath, "utf-8");
|
|
43891
43816
|
} catch (err) {
|
|
43892
43817
|
throw new ConfigError2(`Failed to read config file: ${filePath}`, [
|
|
43893
43818
|
` ${err.message}`
|
|
@@ -44327,15 +44252,15 @@ function resolveOutboundTopic(config, event) {
|
|
|
44327
44252
|
}
|
|
44328
44253
|
|
|
44329
44254
|
// ../src/agents/perf.ts
|
|
44330
|
-
import { existsSync as
|
|
44255
|
+
import { existsSync as existsSync18, readFileSync as readFileSync14 } from "node:fs";
|
|
44331
44256
|
function readTurnUsages(jsonlPath, lastN) {
|
|
44332
|
-
if (!
|
|
44257
|
+
if (!existsSync18(jsonlPath))
|
|
44333
44258
|
return [];
|
|
44334
44259
|
if (lastN <= 0)
|
|
44335
44260
|
return [];
|
|
44336
44261
|
let raw;
|
|
44337
44262
|
try {
|
|
44338
|
-
raw =
|
|
44263
|
+
raw = readFileSync14(jsonlPath, "utf-8");
|
|
44339
44264
|
} catch {
|
|
44340
44265
|
return [];
|
|
44341
44266
|
}
|
|
@@ -44473,7 +44398,7 @@ function nextCompactNotify(state3, ev) {
|
|
|
44473
44398
|
}
|
|
44474
44399
|
|
|
44475
44400
|
// gateway/hostd-dispatch.ts
|
|
44476
|
-
import { existsSync as
|
|
44401
|
+
import { existsSync as existsSync19 } from "node:fs";
|
|
44477
44402
|
import { randomBytes as randomBytes3 } from "node:crypto";
|
|
44478
44403
|
|
|
44479
44404
|
// ../src/host-control/client.ts
|
|
@@ -44764,13 +44689,13 @@ function hostdSocketPath(agentName3) {
|
|
|
44764
44689
|
function hostdWillBeUsed(agentName3) {
|
|
44765
44690
|
if (!isHostdEnabled())
|
|
44766
44691
|
return false;
|
|
44767
|
-
return
|
|
44692
|
+
return existsSync19(hostdSocketPath(agentName3));
|
|
44768
44693
|
}
|
|
44769
44694
|
async function tryHostdDispatch(agentName3, req, timeoutMs = 5000) {
|
|
44770
44695
|
if (!isHostdEnabled())
|
|
44771
44696
|
return "not-configured";
|
|
44772
44697
|
const sockPath = hostdSocketPath(agentName3);
|
|
44773
|
-
if (!
|
|
44698
|
+
if (!existsSync19(sockPath))
|
|
44774
44699
|
return "not-configured";
|
|
44775
44700
|
try {
|
|
44776
44701
|
return await hostdRequest({ socketPath: sockPath, timeoutMs }, req);
|
|
@@ -44794,7 +44719,7 @@ async function hostdGetStatusOnce(agentName3, targetRequestId) {
|
|
|
44794
44719
|
if (!isHostdEnabled())
|
|
44795
44720
|
return "not-configured";
|
|
44796
44721
|
const sockPath = hostdSocketPath(agentName3);
|
|
44797
|
-
if (!
|
|
44722
|
+
if (!existsSync19(sockPath))
|
|
44798
44723
|
return "not-configured";
|
|
44799
44724
|
try {
|
|
44800
44725
|
const resp = await hostdRequest({ socketPath: sockPath, timeoutMs: 3000 }, {
|
|
@@ -44815,7 +44740,7 @@ async function pollHostdStatus(agentName3, targetRequestId, opts) {
|
|
|
44815
44740
|
if (!isHostdEnabled())
|
|
44816
44741
|
return "not-configured";
|
|
44817
44742
|
const sockPath = hostdSocketPath(agentName3);
|
|
44818
|
-
if (!
|
|
44743
|
+
if (!existsSync19(sockPath))
|
|
44819
44744
|
return "not-configured";
|
|
44820
44745
|
const now = opts.now ?? Date.now;
|
|
44821
44746
|
const sleep2 = opts.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
|
|
@@ -44894,7 +44819,7 @@ function shouldSweepChatAtBoot(chatId) {
|
|
|
44894
44819
|
|
|
44895
44820
|
// gateway/webhook-ingest-server.ts
|
|
44896
44821
|
import net4 from "node:net";
|
|
44897
|
-
import { chmodSync as chmodSync3, existsSync as
|
|
44822
|
+
import { chmodSync as chmodSync3, existsSync as existsSync20, unlinkSync as unlinkSync4 } from "node:fs";
|
|
44898
44823
|
var MAX_REQUEST_BYTES = 1024 * 1024;
|
|
44899
44824
|
function fdOf(conn) {
|
|
44900
44825
|
const handle = conn._handle;
|
|
@@ -44906,8 +44831,8 @@ function startWebhookIngestServer(opts) {
|
|
|
44906
44831
|
const log = opts.log ?? ((s) => process.stderr.write(s));
|
|
44907
44832
|
const allowed = new Set(opts.allowedUids);
|
|
44908
44833
|
try {
|
|
44909
|
-
if (
|
|
44910
|
-
|
|
44834
|
+
if (existsSync20(opts.socketPath))
|
|
44835
|
+
unlinkSync4(opts.socketPath);
|
|
44911
44836
|
} catch (err) {
|
|
44912
44837
|
log(`webhook-ingest-server: could not unlink stale socket: ${err.message}
|
|
44913
44838
|
`);
|
|
@@ -44999,8 +44924,8 @@ function startWebhookIngestServer(opts) {
|
|
|
44999
44924
|
server.close();
|
|
45000
44925
|
} catch {}
|
|
45001
44926
|
try {
|
|
45002
|
-
if (
|
|
45003
|
-
|
|
44927
|
+
if (existsSync20(opts.socketPath))
|
|
44928
|
+
unlinkSync4(opts.socketPath);
|
|
45004
44929
|
} catch {}
|
|
45005
44930
|
}
|
|
45006
44931
|
};
|
|
@@ -45008,19 +44933,19 @@ function startWebhookIngestServer(opts) {
|
|
|
45008
44933
|
|
|
45009
44934
|
// ../src/web/webhook-gateway-record.ts
|
|
45010
44935
|
import { appendFileSync as appendFileSync4, mkdirSync as mkdirSync12 } from "fs";
|
|
45011
|
-
import { join as
|
|
44936
|
+
import { join as join19 } from "path";
|
|
45012
44937
|
import { homedir as homedir10 } from "os";
|
|
45013
44938
|
|
|
45014
44939
|
// ../src/web/webhook-handler.ts
|
|
45015
|
-
import { appendFileSync as appendFileSync3, existsSync as
|
|
45016
|
-
import { join as
|
|
44940
|
+
import { appendFileSync as appendFileSync3, existsSync as existsSync21, mkdirSync as mkdirSync10, readFileSync as readFileSync15, writeFileSync as writeFileSync8 } from "fs";
|
|
44941
|
+
import { join as join17 } from "path";
|
|
45017
44942
|
var DEDUP_MAX = 1000;
|
|
45018
44943
|
var DEDUP_TTL_MS = 24 * 60 * 60 * 1000;
|
|
45019
44944
|
function loadDedupFile(path) {
|
|
45020
44945
|
try {
|
|
45021
|
-
if (!
|
|
44946
|
+
if (!existsSync21(path))
|
|
45022
44947
|
return {};
|
|
45023
|
-
const raw = JSON.parse(
|
|
44948
|
+
const raw = JSON.parse(readFileSync15(path, "utf-8"));
|
|
45024
44949
|
return typeof raw.deliveries === "object" && raw.deliveries !== null ? raw.deliveries : {};
|
|
45025
44950
|
} catch {
|
|
45026
44951
|
return {};
|
|
@@ -45034,7 +44959,7 @@ function saveDedupFile(path, deliveries, now) {
|
|
|
45034
44959
|
}
|
|
45035
44960
|
const sorted = Object.entries(pruned).sort((a, b) => b[1] - a[1]).slice(0, DEDUP_MAX);
|
|
45036
44961
|
const final = Object.fromEntries(sorted);
|
|
45037
|
-
|
|
44962
|
+
writeFileSync8(path, JSON.stringify({ deliveries: final }), {
|
|
45038
44963
|
mode: 384
|
|
45039
44964
|
});
|
|
45040
44965
|
}
|
|
@@ -45042,8 +44967,8 @@ var agentDedupCache = new Map;
|
|
|
45042
44967
|
function createFileDedupStore(resolveAgentDir) {
|
|
45043
44968
|
return {
|
|
45044
44969
|
check(agent, deliveryId, now) {
|
|
45045
|
-
const telegramDir =
|
|
45046
|
-
const filePath =
|
|
44970
|
+
const telegramDir = join17(resolveAgentDir(agent), "telegram");
|
|
44971
|
+
const filePath = join17(telegramDir, "webhook-dedup.json");
|
|
45047
44972
|
if (!agentDedupCache.has(agent)) {
|
|
45048
44973
|
agentDedupCache.set(agent, loadDedupFile(filePath));
|
|
45049
44974
|
}
|
|
@@ -45064,8 +44989,8 @@ var tokenBuckets = new Map;
|
|
|
45064
44989
|
var throttleIssueWindow = new Map;
|
|
45065
44990
|
|
|
45066
44991
|
// ../src/web/webhook-dispatch.ts
|
|
45067
|
-
import { existsSync as
|
|
45068
|
-
import { join as
|
|
44992
|
+
import { existsSync as existsSync22, mkdirSync as mkdirSync11, readFileSync as readFileSync16, writeFileSync as writeFileSync9 } from "fs";
|
|
44993
|
+
import { join as join18 } from "path";
|
|
45069
44994
|
import { homedir as homedir9 } from "os";
|
|
45070
44995
|
|
|
45071
44996
|
// ../src/agent-scheduler/ipc-client.ts
|
|
@@ -45276,9 +45201,9 @@ function cooldownKey(eventType, repo, number, ruleIndex) {
|
|
|
45276
45201
|
}
|
|
45277
45202
|
function loadCooldownFile(path) {
|
|
45278
45203
|
try {
|
|
45279
|
-
if (!
|
|
45204
|
+
if (!existsSync22(path))
|
|
45280
45205
|
return {};
|
|
45281
|
-
const raw = JSON.parse(
|
|
45206
|
+
const raw = JSON.parse(readFileSync16(path, "utf-8"));
|
|
45282
45207
|
return typeof raw.dispatches === "object" && raw.dispatches !== null ? raw.dispatches : {};
|
|
45283
45208
|
} catch {
|
|
45284
45209
|
return {};
|
|
@@ -45286,7 +45211,7 @@ function loadCooldownFile(path) {
|
|
|
45286
45211
|
}
|
|
45287
45212
|
function saveCooldownFile(path, dispatches) {
|
|
45288
45213
|
try {
|
|
45289
|
-
|
|
45214
|
+
writeFileSync9(path, JSON.stringify({ dispatches }), {
|
|
45290
45215
|
mode: 384
|
|
45291
45216
|
});
|
|
45292
45217
|
} catch {}
|
|
@@ -45297,8 +45222,8 @@ function createFileCooldownStore(resolveAgentDir) {
|
|
|
45297
45222
|
isCoolingDown(agent, key, cooldownMs, now) {
|
|
45298
45223
|
if (cooldownMs <= 0)
|
|
45299
45224
|
return false;
|
|
45300
|
-
const telegramDir =
|
|
45301
|
-
const filePath =
|
|
45225
|
+
const telegramDir = join18(resolveAgentDir(agent), "telegram");
|
|
45226
|
+
const filePath = join18(telegramDir, "webhook-cooldown.json");
|
|
45302
45227
|
if (!cache.has(agent)) {
|
|
45303
45228
|
cache.set(agent, loadCooldownFile(filePath));
|
|
45304
45229
|
}
|
|
@@ -45355,9 +45280,9 @@ async function defaultInject(socketPath, agentName3, inbound) {
|
|
|
45355
45280
|
}
|
|
45356
45281
|
function injectWebhookInbound(agent, prompt, ctx, deps = {}) {
|
|
45357
45282
|
const log = deps.log ?? ((s) => process.stderr.write(s));
|
|
45358
|
-
const resolveAgentDir = deps.resolveAgentDir ?? ((a) =>
|
|
45283
|
+
const resolveAgentDir = deps.resolveAgentDir ?? ((a) => join18(homedir9(), ".switchroom", "agents", a));
|
|
45359
45284
|
const now = (deps.now ?? Date.now)();
|
|
45360
|
-
const socketPath =
|
|
45285
|
+
const socketPath = join18(resolveAgentDir(agent), "telegram", "gateway.sock");
|
|
45361
45286
|
const inbound = {
|
|
45362
45287
|
type: "inbound",
|
|
45363
45288
|
chatId: ctx.chatId,
|
|
@@ -45382,7 +45307,7 @@ function evaluateDispatch(args, deps = {}) {
|
|
|
45382
45307
|
const log = deps.log ?? ((s) => process.stderr.write(s));
|
|
45383
45308
|
const now = (deps.now ?? Date.now)();
|
|
45384
45309
|
const nowDate = deps.nowDate ?? (() => new Date(now));
|
|
45385
|
-
const resolveAgentDir = deps.resolveAgentDir ?? ((a) =>
|
|
45310
|
+
const resolveAgentDir = deps.resolveAgentDir ?? ((a) => join18(homedir9(), ".switchroom", "agents", a));
|
|
45386
45311
|
const cooldownStore = deps.cooldownStore ?? createFileCooldownStore(resolveAgentDir);
|
|
45387
45312
|
if (args.source !== "github")
|
|
45388
45313
|
return 0;
|
|
@@ -45458,10 +45383,10 @@ function resolveChannelTarget(config, agentName3) {
|
|
|
45458
45383
|
function recordWebhookEvent(rec, deps = {}) {
|
|
45459
45384
|
const log = deps.log ?? ((s) => process.stderr.write(s));
|
|
45460
45385
|
const now = rec.ts || (deps.now ?? Date.now)();
|
|
45461
|
-
const resolveAgentDir = deps.resolveAgentDir ?? ((a) =>
|
|
45386
|
+
const resolveAgentDir = deps.resolveAgentDir ?? ((a) => join19(homedir10(), ".switchroom", "agents", a));
|
|
45462
45387
|
const dedupStore = deps.dedupStore ?? createFileDedupStore(resolveAgentDir);
|
|
45463
45388
|
const agent = rec.agent;
|
|
45464
|
-
const telegramDir =
|
|
45389
|
+
const telegramDir = join19(resolveAgentDir(agent), "telegram");
|
|
45465
45390
|
if (rec.source === "github" && rec.delivery_id) {
|
|
45466
45391
|
const originalTs = dedupStore.check(agent, rec.delivery_id, now);
|
|
45467
45392
|
if (originalTs !== undefined) {
|
|
@@ -45470,7 +45395,7 @@ function recordWebhookEvent(rec, deps = {}) {
|
|
|
45470
45395
|
return { status: "deduped", ts: originalTs };
|
|
45471
45396
|
}
|
|
45472
45397
|
}
|
|
45473
|
-
const logPath =
|
|
45398
|
+
const logPath = join19(telegramDir, "webhook-events.jsonl");
|
|
45474
45399
|
try {
|
|
45475
45400
|
mkdirSync12(telegramDir, { recursive: true });
|
|
45476
45401
|
const record = {
|
|
@@ -45528,7 +45453,7 @@ function recordWebhookEvent(rec, deps = {}) {
|
|
|
45528
45453
|
}
|
|
45529
45454
|
|
|
45530
45455
|
// gateway/ipc-server.ts
|
|
45531
|
-
import { renameSync as
|
|
45456
|
+
import { renameSync as renameSync4, unlinkSync as unlinkSync5 } from "fs";
|
|
45532
45457
|
var MAX_BUFFER_SIZE = 1024 * 1024;
|
|
45533
45458
|
var VALID_OPERATOR_KINDS = new Set([
|
|
45534
45459
|
"credentials-expired",
|
|
@@ -45643,10 +45568,10 @@ function createIpcServer(options) {
|
|
|
45643
45568
|
heartbeatTimeoutMs = 30000
|
|
45644
45569
|
} = options;
|
|
45645
45570
|
try {
|
|
45646
|
-
|
|
45571
|
+
renameSync4(socketPath, socketPath + ".bak");
|
|
45647
45572
|
} catch {}
|
|
45648
45573
|
try {
|
|
45649
|
-
|
|
45574
|
+
unlinkSync5(socketPath + ".bak");
|
|
45650
45575
|
} catch {}
|
|
45651
45576
|
const clients = new Set;
|
|
45652
45577
|
const agentIndex = new Map;
|
|
@@ -45968,7 +45893,7 @@ function createIpcServer(options) {
|
|
|
45968
45893
|
clientBySocketId.clear();
|
|
45969
45894
|
server.stop(true);
|
|
45970
45895
|
try {
|
|
45971
|
-
|
|
45896
|
+
renameSync4(socketPath, socketPath + ".bak");
|
|
45972
45897
|
} catch {}
|
|
45973
45898
|
}
|
|
45974
45899
|
};
|
|
@@ -46394,9 +46319,9 @@ function buildDiffPreviewCard(input) {
|
|
|
46394
46319
|
}
|
|
46395
46320
|
const preview = input.preview;
|
|
46396
46321
|
const bodyLines = [];
|
|
46397
|
-
bodyLines.push(`<b>${
|
|
46322
|
+
bodyLines.push(`<b>${escapeHtml7(preview.title)}</b>`);
|
|
46398
46323
|
for (const line of preview.lines) {
|
|
46399
|
-
bodyLines.push(
|
|
46324
|
+
bodyLines.push(escapeHtml7(line.text));
|
|
46400
46325
|
}
|
|
46401
46326
|
const text = bodyLines.join(`
|
|
46402
46327
|
`);
|
|
@@ -46449,7 +46374,7 @@ function buildDiffPreviewCard(input) {
|
|
|
46449
46374
|
}
|
|
46450
46375
|
return { text, reply_markup: kb };
|
|
46451
46376
|
}
|
|
46452
|
-
function
|
|
46377
|
+
function escapeHtml7(s) {
|
|
46453
46378
|
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
46454
46379
|
}
|
|
46455
46380
|
|
|
@@ -46595,6 +46520,9 @@ function spoolId(msg) {
|
|
|
46595
46520
|
if (msg.meta?.source === "subagent_progress" && typeof msg.meta?.subagent_jsonl_id === "string" && msg.meta.subagent_jsonl_id.length > 0 && typeof msg.meta?.bucket_idx === "string" && msg.meta.bucket_idx.length > 0) {
|
|
46596
46521
|
return `s:progress:${msg.meta.subagent_jsonl_id}:${msg.meta.bucket_idx}`;
|
|
46597
46522
|
}
|
|
46523
|
+
if ((msg.meta?.source === "resume_interrupted" || msg.meta?.source === "resume_watchdog_timeout") && typeof msg.meta?.resume_turn_key === "string" && msg.meta.resume_turn_key.length > 0) {
|
|
46524
|
+
return `s:resume:${msg.meta.resume_turn_key}`;
|
|
46525
|
+
}
|
|
46598
46526
|
if (typeof msg.messageId === "number" && msg.messageId > 0) {
|
|
46599
46527
|
return `m:${msg.chatId}:${msg.messageId}`;
|
|
46600
46528
|
}
|
|
@@ -47828,15 +47756,15 @@ function escapeBody(s) {
|
|
|
47828
47756
|
}
|
|
47829
47757
|
|
|
47830
47758
|
// gateway/pid-file.ts
|
|
47831
|
-
import { writeFileSync as
|
|
47759
|
+
import { writeFileSync as writeFileSync10, readFileSync as readFileSync17, unlinkSync as unlinkSync6, renameSync as renameSync5 } from "node:fs";
|
|
47832
47760
|
function writePidFile(path, record) {
|
|
47833
47761
|
const tmp = `${path}.tmp-${process.pid}-${Date.now()}`;
|
|
47834
|
-
|
|
47835
|
-
|
|
47762
|
+
writeFileSync10(tmp, JSON.stringify(record), "utf-8");
|
|
47763
|
+
renameSync5(tmp, path);
|
|
47836
47764
|
}
|
|
47837
47765
|
function clearPidFile(path) {
|
|
47838
47766
|
try {
|
|
47839
|
-
|
|
47767
|
+
unlinkSync6(path);
|
|
47840
47768
|
} catch {}
|
|
47841
47769
|
}
|
|
47842
47770
|
|
|
@@ -47847,10 +47775,10 @@ import {
|
|
|
47847
47775
|
writeFile as writeFileAsync,
|
|
47848
47776
|
readFile as readFileAsync
|
|
47849
47777
|
} from "node:fs/promises";
|
|
47850
|
-
import { readFileSync as
|
|
47778
|
+
import { readFileSync as readFileSync18 } from "node:fs";
|
|
47851
47779
|
function readCurrentBootId() {
|
|
47852
47780
|
try {
|
|
47853
|
-
const stat =
|
|
47781
|
+
const stat = readFileSync18("/proc/1/stat", "utf-8");
|
|
47854
47782
|
const lastParen = stat.lastIndexOf(")");
|
|
47855
47783
|
if (lastParen < 0)
|
|
47856
47784
|
return null;
|
|
@@ -48053,15 +47981,15 @@ function safeCount(fn) {
|
|
|
48053
47981
|
}
|
|
48054
47982
|
|
|
48055
47983
|
// gateway/session-marker.ts
|
|
48056
|
-
import { writeFileSync as
|
|
47984
|
+
import { writeFileSync as writeFileSync11, readFileSync as readFileSync19, renameSync as renameSync6, unlinkSync as unlinkSync7 } from "node:fs";
|
|
48057
47985
|
function writeSessionMarker(path, marker) {
|
|
48058
47986
|
const tmp = `${path}.tmp-${process.pid}-${Date.now()}`;
|
|
48059
|
-
|
|
48060
|
-
|
|
47987
|
+
writeFileSync11(tmp, JSON.stringify(marker), "utf-8");
|
|
47988
|
+
renameSync6(tmp, path);
|
|
48061
47989
|
}
|
|
48062
47990
|
function readSessionMarker(path) {
|
|
48063
47991
|
try {
|
|
48064
|
-
const raw =
|
|
47992
|
+
const raw = readFileSync19(path, "utf-8");
|
|
48065
47993
|
const parsed = JSON.parse(raw);
|
|
48066
47994
|
if (typeof parsed.pid === "number" && typeof parsed.startedAtMs === "number" && Number.isFinite(parsed.pid) && Number.isFinite(parsed.startedAtMs)) {
|
|
48067
47995
|
return { pid: parsed.pid, startedAtMs: parsed.startedAtMs };
|
|
@@ -48083,16 +48011,16 @@ function shouldFireRestartBanner(input) {
|
|
|
48083
48011
|
}
|
|
48084
48012
|
|
|
48085
48013
|
// gateway/clean-shutdown-marker.ts
|
|
48086
|
-
import { writeFileSync as
|
|
48014
|
+
import { writeFileSync as writeFileSync12, readFileSync as readFileSync20, renameSync as renameSync7, unlinkSync as unlinkSync8 } from "node:fs";
|
|
48087
48015
|
var DEFAULT_MAX_AGE_MS = 60000;
|
|
48088
48016
|
function writeCleanShutdownMarker(path, marker) {
|
|
48089
48017
|
const tmp = `${path}.tmp-${process.pid}-${Date.now()}`;
|
|
48090
|
-
|
|
48091
|
-
|
|
48018
|
+
writeFileSync12(tmp, JSON.stringify(marker), "utf-8");
|
|
48019
|
+
renameSync7(tmp, path);
|
|
48092
48020
|
}
|
|
48093
48021
|
function readCleanShutdownMarker(path) {
|
|
48094
48022
|
try {
|
|
48095
|
-
const raw =
|
|
48023
|
+
const raw = readFileSync20(path, "utf-8");
|
|
48096
48024
|
const parsed = JSON.parse(raw);
|
|
48097
48025
|
if (typeof parsed.ts === "number" && Number.isFinite(parsed.ts) && typeof parsed.signal === "string" && parsed.signal.length > 0) {
|
|
48098
48026
|
const out = { ts: parsed.ts, signal: parsed.signal };
|
|
@@ -48107,7 +48035,7 @@ function readCleanShutdownMarker(path) {
|
|
|
48107
48035
|
}
|
|
48108
48036
|
function clearCleanShutdownMarker(path) {
|
|
48109
48037
|
try {
|
|
48110
|
-
|
|
48038
|
+
unlinkSync8(path);
|
|
48111
48039
|
} catch {}
|
|
48112
48040
|
}
|
|
48113
48041
|
function shouldSuppressRecoveryBanner(marker, now, maxAgeMs = DEFAULT_MAX_AGE_MS) {
|
|
@@ -48887,16 +48815,16 @@ function classifyAdminGate(text, myAgentName) {
|
|
|
48887
48815
|
|
|
48888
48816
|
// subagent-watcher.ts
|
|
48889
48817
|
import {
|
|
48890
|
-
existsSync as
|
|
48818
|
+
existsSync as existsSync24,
|
|
48891
48819
|
openSync as openSync2,
|
|
48892
48820
|
readSync,
|
|
48893
48821
|
statSync as statSync6,
|
|
48894
48822
|
closeSync as closeSync2,
|
|
48895
48823
|
watch,
|
|
48896
48824
|
readdirSync as readdirSync3,
|
|
48897
|
-
readFileSync as
|
|
48825
|
+
readFileSync as readFileSync22
|
|
48898
48826
|
} from "fs";
|
|
48899
|
-
import { join as
|
|
48827
|
+
import { join as join21 } from "path";
|
|
48900
48828
|
|
|
48901
48829
|
// operator-events.ts
|
|
48902
48830
|
var DEFAULT_OPERATOR_EVENT_COOLDOWN_MS2 = 5 * 60000;
|
|
@@ -49139,20 +49067,20 @@ function bumpSubagentActivity(db2, args) {
|
|
|
49139
49067
|
// gateway/turn-active-marker.ts
|
|
49140
49068
|
import {
|
|
49141
49069
|
closeSync,
|
|
49142
|
-
existsSync as
|
|
49070
|
+
existsSync as existsSync23,
|
|
49143
49071
|
mkdirSync as mkdirSync13,
|
|
49144
49072
|
openSync,
|
|
49145
|
-
readFileSync as
|
|
49073
|
+
readFileSync as readFileSync21,
|
|
49146
49074
|
statSync as statSync5,
|
|
49147
|
-
unlinkSync as
|
|
49075
|
+
unlinkSync as unlinkSync9,
|
|
49148
49076
|
utimesSync,
|
|
49149
|
-
writeFileSync as
|
|
49077
|
+
writeFileSync as writeFileSync13
|
|
49150
49078
|
} from "node:fs";
|
|
49151
|
-
import { join as
|
|
49079
|
+
import { join as join20 } from "node:path";
|
|
49152
49080
|
var TURN_ACTIVE_MARKER_FILE = "turn-active.json";
|
|
49153
49081
|
function touchTurnActiveMarker(stateDir) {
|
|
49154
|
-
const path =
|
|
49155
|
-
if (!
|
|
49082
|
+
const path = join20(stateDir, TURN_ACTIVE_MARKER_FILE);
|
|
49083
|
+
if (!existsSync23(path))
|
|
49156
49084
|
return;
|
|
49157
49085
|
const now = new Date;
|
|
49158
49086
|
try {
|
|
@@ -49187,7 +49115,7 @@ function backfillJsonlAgentId(db2, jsonlPath, agentId, log) {
|
|
|
49187
49115
|
const metaPath = jsonlPath.replace(/\.jsonl$/, ".meta.json");
|
|
49188
49116
|
let meta;
|
|
49189
49117
|
try {
|
|
49190
|
-
const raw =
|
|
49118
|
+
const raw = readFileSync22(metaPath, "utf8");
|
|
49191
49119
|
meta = JSON.parse(raw);
|
|
49192
49120
|
} catch {
|
|
49193
49121
|
log?.(`subagent-watcher: backfill skip ${agentId} \u2014 meta.json not readable at ${metaPath}`);
|
|
@@ -49376,7 +49304,7 @@ function startSubagentWatcher(config) {
|
|
|
49376
49304
|
clearTimeout(ref.ref);
|
|
49377
49305
|
});
|
|
49378
49306
|
const fs2 = config.fs ?? {
|
|
49379
|
-
existsSync:
|
|
49307
|
+
existsSync: existsSync24,
|
|
49380
49308
|
readdirSync: readdirSync3,
|
|
49381
49309
|
statSync: statSync6,
|
|
49382
49310
|
openSync: openSync2,
|
|
@@ -49610,8 +49538,8 @@ function startSubagentWatcher(config) {
|
|
|
49610
49538
|
function rescanSubagentDirs() {
|
|
49611
49539
|
if (stopped)
|
|
49612
49540
|
return;
|
|
49613
|
-
const claudeHome =
|
|
49614
|
-
const projectsRoot =
|
|
49541
|
+
const claudeHome = join21(agentDir, ".claude");
|
|
49542
|
+
const projectsRoot = join21(claudeHome, "projects");
|
|
49615
49543
|
if (!fs2.existsSync(projectsRoot))
|
|
49616
49544
|
return;
|
|
49617
49545
|
let projectDirs;
|
|
@@ -49628,7 +49556,7 @@ function startSubagentWatcher(config) {
|
|
|
49628
49556
|
}
|
|
49629
49557
|
continue;
|
|
49630
49558
|
}
|
|
49631
|
-
const projectPath =
|
|
49559
|
+
const projectPath = join21(projectsRoot, pDir);
|
|
49632
49560
|
let sessionDirs;
|
|
49633
49561
|
try {
|
|
49634
49562
|
sessionDirs = fs2.readdirSync(projectPath);
|
|
@@ -49638,7 +49566,7 @@ function startSubagentWatcher(config) {
|
|
|
49638
49566
|
for (const sDir of sessionDirs) {
|
|
49639
49567
|
if (sDir.endsWith(".jsonl"))
|
|
49640
49568
|
continue;
|
|
49641
|
-
const subagentsPath =
|
|
49569
|
+
const subagentsPath = join21(projectPath, sDir, "subagents");
|
|
49642
49570
|
if (!fs2.existsSync(subagentsPath))
|
|
49643
49571
|
continue;
|
|
49644
49572
|
if (!dirWatchers.has(subagentsPath)) {
|
|
@@ -49646,7 +49574,7 @@ function startSubagentWatcher(config) {
|
|
|
49646
49574
|
const w = fs2.watch(subagentsPath, (_event, filename) => {
|
|
49647
49575
|
if (!filename || !filename.toString().startsWith("agent-") || !filename.toString().endsWith(".jsonl"))
|
|
49648
49576
|
return;
|
|
49649
|
-
const filePath =
|
|
49577
|
+
const filePath = join21(subagentsPath, filename.toString());
|
|
49650
49578
|
if (!knownFiles.has(filePath)) {
|
|
49651
49579
|
scanSubagentsDir(subagentsPath);
|
|
49652
49580
|
}
|
|
@@ -49671,7 +49599,7 @@ function startSubagentWatcher(config) {
|
|
|
49671
49599
|
for (const e of entries) {
|
|
49672
49600
|
if (!e.startsWith("agent-") || !e.endsWith(".jsonl"))
|
|
49673
49601
|
continue;
|
|
49674
|
-
const filePath =
|
|
49602
|
+
const filePath = join21(subagentsPath, e);
|
|
49675
49603
|
if (knownFiles.has(filePath))
|
|
49676
49604
|
continue;
|
|
49677
49605
|
const agentId = e.slice("agent-".length, -".jsonl".length);
|
|
@@ -49788,15 +49716,15 @@ function determineRestartReason(opts) {
|
|
|
49788
49716
|
init_boot_card();
|
|
49789
49717
|
|
|
49790
49718
|
// gateway/update-announce.ts
|
|
49791
|
-
import { existsSync as
|
|
49792
|
-
import { join as
|
|
49719
|
+
import { existsSync as existsSync29, mkdirSync as mkdirSync17, openSync as openSync3, closeSync as closeSync3, readFileSync as readFileSync27 } from "node:fs";
|
|
49720
|
+
import { join as join26 } from "node:path";
|
|
49793
49721
|
import { homedir as homedir12 } from "node:os";
|
|
49794
49722
|
|
|
49795
49723
|
// ../src/host-control/audit-reader.ts
|
|
49796
49724
|
import { homedir as homedir11 } from "node:os";
|
|
49797
|
-
import { join as
|
|
49725
|
+
import { join as join25 } from "node:path";
|
|
49798
49726
|
function defaultAuditLogPath(home2 = homedir11()) {
|
|
49799
|
-
return
|
|
49727
|
+
return join25(home2, ".switchroom", "host-control-audit.log");
|
|
49800
49728
|
}
|
|
49801
49729
|
function parseAuditLine(line) {
|
|
49802
49730
|
const trimmed = line.trim();
|
|
@@ -49902,8 +49830,8 @@ function readAndFilter(raw, filters, limit) {
|
|
|
49902
49830
|
var DEFAULT_LOOKBACK_MS = 10 * 60 * 1000;
|
|
49903
49831
|
function readLastTerminalUpdateAudit(opts = {}) {
|
|
49904
49832
|
const path = opts.auditLogPath ?? defaultAuditLogPath();
|
|
49905
|
-
const exists = opts.exists ??
|
|
49906
|
-
const readFile = opts.readFile ?? ((p) =>
|
|
49833
|
+
const exists = opts.exists ?? existsSync29;
|
|
49834
|
+
const readFile = opts.readFile ?? ((p) => readFileSync27(p, "utf-8"));
|
|
49907
49835
|
if (!exists(path))
|
|
49908
49836
|
return null;
|
|
49909
49837
|
let raw;
|
|
@@ -49964,15 +49892,15 @@ function renderUpdateOutcomeLine(entry) {
|
|
|
49964
49892
|
`);
|
|
49965
49893
|
}
|
|
49966
49894
|
function claimUpdateAnnouncement(requestId, opts = {}) {
|
|
49967
|
-
const stateDir = opts.stateDir ?? process.env.TELEGRAM_STATE_DIR ??
|
|
49968
|
-
const dir =
|
|
49895
|
+
const stateDir = opts.stateDir ?? process.env.TELEGRAM_STATE_DIR ?? join26(homedir12(), ".switchroom");
|
|
49896
|
+
const dir = join26(stateDir, "update-announced");
|
|
49969
49897
|
try {
|
|
49970
49898
|
mkdirSync17(dir, { recursive: true });
|
|
49971
49899
|
} catch {
|
|
49972
49900
|
return false;
|
|
49973
49901
|
}
|
|
49974
49902
|
const safeId = requestId.replace(/[^A-Za-z0-9_.-]/g, "_").slice(0, 200);
|
|
49975
|
-
const path =
|
|
49903
|
+
const path = join26(dir, safeId);
|
|
49976
49904
|
try {
|
|
49977
49905
|
const fd = openSync3(path, "wx");
|
|
49978
49906
|
closeSync3(fd);
|
|
@@ -49991,7 +49919,7 @@ function maybeRenderUpdateAnnouncement(opts = {}) {
|
|
|
49991
49919
|
}
|
|
49992
49920
|
|
|
49993
49921
|
// issues-card.ts
|
|
49994
|
-
import { readFileSync as
|
|
49922
|
+
import { readFileSync as readFileSync28, writeFileSync as writeFileSync17 } from "node:fs";
|
|
49995
49923
|
var SEVERITY_EMOJI = {
|
|
49996
49924
|
info: "\u2139\ufe0f",
|
|
49997
49925
|
warn: "\u26a0\ufe0f",
|
|
@@ -50083,7 +50011,7 @@ function extractRetryAfterSecs2(err) {
|
|
|
50083
50011
|
var COOLDOWN_JITTER_MS2 = 500;
|
|
50084
50012
|
function readPersistedMessageId(path, log) {
|
|
50085
50013
|
try {
|
|
50086
|
-
const raw =
|
|
50014
|
+
const raw = readFileSync28(path, "utf8");
|
|
50087
50015
|
const parsed = JSON.parse(raw);
|
|
50088
50016
|
const v = parsed.messageId;
|
|
50089
50017
|
if (typeof v === "number" && Number.isInteger(v) && v > 0)
|
|
@@ -50099,7 +50027,7 @@ function readPersistedMessageId(path, log) {
|
|
|
50099
50027
|
}
|
|
50100
50028
|
function writePersistedMessageId(path, messageId, log) {
|
|
50101
50029
|
try {
|
|
50102
|
-
|
|
50030
|
+
writeFileSync17(path, JSON.stringify({ messageId }) + `
|
|
50103
50031
|
`, { mode: 384 });
|
|
50104
50032
|
} catch (err) {
|
|
50105
50033
|
log(`issues-card: persist write failed (${err.message})`);
|
|
@@ -50192,24 +50120,24 @@ function createIssuesCardHandle(opts) {
|
|
|
50192
50120
|
}
|
|
50193
50121
|
|
|
50194
50122
|
// issues-watcher.ts
|
|
50195
|
-
import { existsSync as
|
|
50196
|
-
import { join as
|
|
50123
|
+
import { existsSync as existsSync31, statSync as statSync8 } from "node:fs";
|
|
50124
|
+
import { join as join28 } from "node:path";
|
|
50197
50125
|
|
|
50198
50126
|
// ../src/issues/store.ts
|
|
50199
50127
|
import {
|
|
50200
50128
|
closeSync as closeSync4,
|
|
50201
|
-
existsSync as
|
|
50129
|
+
existsSync as existsSync30,
|
|
50202
50130
|
mkdirSync as mkdirSync18,
|
|
50203
50131
|
openSync as openSync4,
|
|
50204
50132
|
readdirSync as readdirSync5,
|
|
50205
|
-
readFileSync as
|
|
50206
|
-
renameSync as
|
|
50133
|
+
readFileSync as readFileSync29,
|
|
50134
|
+
renameSync as renameSync10,
|
|
50207
50135
|
statSync as statSync7,
|
|
50208
|
-
unlinkSync as
|
|
50209
|
-
writeFileSync as
|
|
50136
|
+
unlinkSync as unlinkSync10,
|
|
50137
|
+
writeFileSync as writeFileSync18,
|
|
50210
50138
|
writeSync
|
|
50211
50139
|
} from "node:fs";
|
|
50212
|
-
import { join as
|
|
50140
|
+
import { join as join27 } from "node:path";
|
|
50213
50141
|
import { randomBytes as randomBytes4 } from "node:crypto";
|
|
50214
50142
|
import { execSync } from "node:child_process";
|
|
50215
50143
|
|
|
@@ -50225,12 +50153,12 @@ var SEVERITY_RANK2 = {
|
|
|
50225
50153
|
var ISSUES_FILE = "issues.jsonl";
|
|
50226
50154
|
var ISSUES_LOCK = "issues.lock";
|
|
50227
50155
|
function readAll(stateDir) {
|
|
50228
|
-
const path =
|
|
50229
|
-
if (!
|
|
50156
|
+
const path = join27(stateDir, ISSUES_FILE);
|
|
50157
|
+
if (!existsSync30(path))
|
|
50230
50158
|
return [];
|
|
50231
50159
|
let raw;
|
|
50232
50160
|
try {
|
|
50233
|
-
raw =
|
|
50161
|
+
raw = readFileSync29(path, "utf-8");
|
|
50234
50162
|
} catch {
|
|
50235
50163
|
return [];
|
|
50236
50164
|
}
|
|
@@ -50262,7 +50190,7 @@ function list(stateDir, opts = {}) {
|
|
|
50262
50190
|
});
|
|
50263
50191
|
}
|
|
50264
50192
|
function resolve6(stateDir, fingerprint, nowFn = Date.now) {
|
|
50265
|
-
if (!
|
|
50193
|
+
if (!existsSync30(join27(stateDir, ISSUES_FILE)))
|
|
50266
50194
|
return 0;
|
|
50267
50195
|
return withLock(stateDir, () => {
|
|
50268
50196
|
const all = readAll(stateDir);
|
|
@@ -50280,14 +50208,14 @@ function resolve6(stateDir, fingerprint, nowFn = Date.now) {
|
|
|
50280
50208
|
});
|
|
50281
50209
|
}
|
|
50282
50210
|
function writeAll(stateDir, events) {
|
|
50283
|
-
const path =
|
|
50211
|
+
const path = join27(stateDir, ISSUES_FILE);
|
|
50284
50212
|
sweepOrphanTmpFiles(stateDir);
|
|
50285
50213
|
const tmp = `${path}.tmp-${process.pid}-${randomBytes4(4).toString("hex")}`;
|
|
50286
50214
|
const body = events.length === 0 ? "" : events.map((e) => JSON.stringify(e)).join(`
|
|
50287
50215
|
`) + `
|
|
50288
50216
|
`;
|
|
50289
|
-
|
|
50290
|
-
|
|
50217
|
+
writeFileSync18(tmp, body, "utf-8");
|
|
50218
|
+
renameSync10(tmp, path);
|
|
50291
50219
|
}
|
|
50292
50220
|
var ORPHAN_TMP_TTL_MS = 60000;
|
|
50293
50221
|
var TMP_PREFIX = `${ISSUES_FILE}.tmp-`;
|
|
@@ -50302,11 +50230,11 @@ function sweepOrphanTmpFiles(stateDir) {
|
|
|
50302
50230
|
for (const entry of entries) {
|
|
50303
50231
|
if (!entry.startsWith(TMP_PREFIX))
|
|
50304
50232
|
continue;
|
|
50305
|
-
const tmpPath2 =
|
|
50233
|
+
const tmpPath2 = join27(stateDir, entry);
|
|
50306
50234
|
try {
|
|
50307
50235
|
const stat = statSync7(tmpPath2);
|
|
50308
50236
|
if (stat.mtimeMs < cutoff) {
|
|
50309
|
-
|
|
50237
|
+
unlinkSync10(tmpPath2);
|
|
50310
50238
|
}
|
|
50311
50239
|
} catch {}
|
|
50312
50240
|
}
|
|
@@ -50314,7 +50242,7 @@ function sweepOrphanTmpFiles(stateDir) {
|
|
|
50314
50242
|
var LOCK_RETRY_MS = 25;
|
|
50315
50243
|
var LOCK_TIMEOUT_MS = 1e4;
|
|
50316
50244
|
function withLock(stateDir, fn) {
|
|
50317
|
-
const lockPath =
|
|
50245
|
+
const lockPath = join27(stateDir, ISSUES_LOCK);
|
|
50318
50246
|
const startedAt = Date.now();
|
|
50319
50247
|
let fd = null;
|
|
50320
50248
|
while (fd === null) {
|
|
@@ -50342,27 +50270,27 @@ function withLock(stateDir, fn) {
|
|
|
50342
50270
|
closeSync4(fd);
|
|
50343
50271
|
} catch {}
|
|
50344
50272
|
try {
|
|
50345
|
-
|
|
50273
|
+
unlinkSync10(lockPath);
|
|
50346
50274
|
} catch {}
|
|
50347
50275
|
}
|
|
50348
50276
|
}
|
|
50349
50277
|
function tryStealStaleLock(lockPath) {
|
|
50350
50278
|
let pidStr;
|
|
50351
50279
|
try {
|
|
50352
|
-
pidStr =
|
|
50280
|
+
pidStr = readFileSync29(lockPath, "utf-8").trim();
|
|
50353
50281
|
} catch {
|
|
50354
50282
|
return true;
|
|
50355
50283
|
}
|
|
50356
50284
|
const pid = Number(pidStr);
|
|
50357
50285
|
if (!Number.isFinite(pid) || pid <= 0) {
|
|
50358
50286
|
try {
|
|
50359
|
-
|
|
50287
|
+
unlinkSync10(lockPath);
|
|
50360
50288
|
} catch {}
|
|
50361
50289
|
return true;
|
|
50362
50290
|
}
|
|
50363
50291
|
if (pid === process.pid) {
|
|
50364
50292
|
try {
|
|
50365
|
-
|
|
50293
|
+
unlinkSync10(lockPath);
|
|
50366
50294
|
} catch {}
|
|
50367
50295
|
return true;
|
|
50368
50296
|
}
|
|
@@ -50377,7 +50305,7 @@ function tryStealStaleLock(lockPath) {
|
|
|
50377
50305
|
return false;
|
|
50378
50306
|
}
|
|
50379
50307
|
try {
|
|
50380
|
-
|
|
50308
|
+
unlinkSync10(lockPath);
|
|
50381
50309
|
} catch {}
|
|
50382
50310
|
return true;
|
|
50383
50311
|
}
|
|
@@ -50399,7 +50327,7 @@ function isIssueEvent(v) {
|
|
|
50399
50327
|
// issues-watcher.ts
|
|
50400
50328
|
var DEFAULT_POLL_INTERVAL_MS2 = 2000;
|
|
50401
50329
|
function startIssuesWatcher(opts) {
|
|
50402
|
-
const path =
|
|
50330
|
+
const path = join28(opts.stateDir, ISSUES_FILE);
|
|
50403
50331
|
const log = opts.log ?? (() => {});
|
|
50404
50332
|
const intervalMs = opts.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS2;
|
|
50405
50333
|
const setIntervalFn = opts.setInterval ?? setInterval;
|
|
@@ -50447,7 +50375,7 @@ function startIssuesWatcher(opts) {
|
|
|
50447
50375
|
};
|
|
50448
50376
|
}
|
|
50449
50377
|
function defaultSignatureProvider(path) {
|
|
50450
|
-
if (!
|
|
50378
|
+
if (!existsSync31(path))
|
|
50451
50379
|
return null;
|
|
50452
50380
|
try {
|
|
50453
50381
|
const stat = statSync8(path);
|
|
@@ -51052,8 +50980,8 @@ function extractFlowItems(line) {
|
|
|
51052
50980
|
}
|
|
51053
50981
|
|
|
51054
50982
|
// credits-watch.ts
|
|
51055
|
-
import { readFileSync as
|
|
51056
|
-
import { join as
|
|
50983
|
+
import { readFileSync as readFileSync30, writeFileSync as writeFileSync19, existsSync as existsSync32, mkdirSync as mkdirSync19 } from "fs";
|
|
50984
|
+
import { join as join29 } from "path";
|
|
51057
50985
|
var STATE_FILE = "credits-watch.json";
|
|
51058
50986
|
var FATAL_REASONS = new Set([
|
|
51059
50987
|
"out_of_credits",
|
|
@@ -51065,12 +50993,12 @@ function emptyCreditState() {
|
|
|
51065
50993
|
return { lastNotifiedReason: null, lastNotifiedAt: 0 };
|
|
51066
50994
|
}
|
|
51067
50995
|
function readClaudeJsonOverage(claudeConfigDir) {
|
|
51068
|
-
const path =
|
|
51069
|
-
if (!
|
|
50996
|
+
const path = join29(claudeConfigDir, ".claude.json");
|
|
50997
|
+
if (!existsSync32(path))
|
|
51070
50998
|
return null;
|
|
51071
50999
|
let raw;
|
|
51072
51000
|
try {
|
|
51073
|
-
raw =
|
|
51001
|
+
raw = readFileSync30(path, "utf-8");
|
|
51074
51002
|
} catch {
|
|
51075
51003
|
return null;
|
|
51076
51004
|
}
|
|
@@ -51094,7 +51022,7 @@ function evaluateCreditState(args) {
|
|
|
51094
51022
|
if (!currentIsFatal && prevIsFatal) {
|
|
51095
51023
|
return {
|
|
51096
51024
|
kind: "notify",
|
|
51097
|
-
message: `\u2705 <b>${
|
|
51025
|
+
message: `\u2705 <b>${escapeHtml9(agentName3)}</b>: credits restored \u2014 agent should resume normal operation.`,
|
|
51098
51026
|
newState: { lastNotifiedReason: null, lastNotifiedAt: now },
|
|
51099
51027
|
transition: "exited"
|
|
51100
51028
|
};
|
|
@@ -51123,12 +51051,12 @@ function evaluateCreditState(args) {
|
|
|
51123
51051
|
function buildEntryMessage(agentName3, reason) {
|
|
51124
51052
|
const desc = humanizeReason(reason);
|
|
51125
51053
|
return [
|
|
51126
|
-
`\u26a0\ufe0f <b>${
|
|
51054
|
+
`\u26a0\ufe0f <b>${escapeHtml9(agentName3)}</b>: ${desc}`,
|
|
51127
51055
|
``,
|
|
51128
51056
|
`Cron tasks and inbound replies will fail until this is resolved. Check`,
|
|
51129
51057
|
`your subscription or pre-paid usage at <a href="https://console.anthropic.com">console.anthropic.com</a>.`,
|
|
51130
51058
|
``,
|
|
51131
|
-
`<i>Source: Claude CLI cache (cachedExtraUsageDisabledReason=${
|
|
51059
|
+
`<i>Source: Claude CLI cache (cachedExtraUsageDisabledReason=${escapeHtml9(reason)})</i>`
|
|
51132
51060
|
].join(`
|
|
51133
51061
|
`);
|
|
51134
51062
|
}
|
|
@@ -51146,15 +51074,15 @@ function humanizeReason(reason) {
|
|
|
51146
51074
|
return `usage disabled (${reason})`;
|
|
51147
51075
|
}
|
|
51148
51076
|
}
|
|
51149
|
-
function
|
|
51077
|
+
function escapeHtml9(s) {
|
|
51150
51078
|
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
51151
51079
|
}
|
|
51152
51080
|
function loadCreditState(stateDir) {
|
|
51153
|
-
const path =
|
|
51154
|
-
if (!
|
|
51081
|
+
const path = join29(stateDir, STATE_FILE);
|
|
51082
|
+
if (!existsSync32(path))
|
|
51155
51083
|
return emptyCreditState();
|
|
51156
51084
|
try {
|
|
51157
|
-
const raw =
|
|
51085
|
+
const raw = readFileSync30(path, "utf-8");
|
|
51158
51086
|
const parsed = JSON.parse(raw);
|
|
51159
51087
|
if (parsed && typeof parsed === "object" && (parsed.lastNotifiedReason === null || typeof parsed.lastNotifiedReason === "string") && typeof parsed.lastNotifiedAt === "number" && Number.isFinite(parsed.lastNotifiedAt)) {
|
|
51160
51088
|
return {
|
|
@@ -51167,14 +51095,14 @@ function loadCreditState(stateDir) {
|
|
|
51167
51095
|
}
|
|
51168
51096
|
function saveCreditState(stateDir, state4) {
|
|
51169
51097
|
mkdirSync19(stateDir, { recursive: true });
|
|
51170
|
-
const path =
|
|
51171
|
-
|
|
51098
|
+
const path = join29(stateDir, STATE_FILE);
|
|
51099
|
+
writeFileSync19(path, JSON.stringify(state4, null, 2) + `
|
|
51172
51100
|
`, { mode: 384 });
|
|
51173
51101
|
}
|
|
51174
51102
|
|
|
51175
51103
|
// quota-watch.ts
|
|
51176
|
-
import { readFileSync as
|
|
51177
|
-
import { join as
|
|
51104
|
+
import { readFileSync as readFileSync31, writeFileSync as writeFileSync20, existsSync as existsSync33, mkdirSync as mkdirSync20 } from "fs";
|
|
51105
|
+
import { join as join30 } from "path";
|
|
51178
51106
|
var STATE_FILE2 = "quota-watch.json";
|
|
51179
51107
|
function emptyQuotaWatchState() {
|
|
51180
51108
|
return {};
|
|
@@ -51231,11 +51159,11 @@ function buildThrottlingMessage(agentName3, snap) {
|
|
|
51231
51159
|
const resetAt = win === "5h" ? q.fiveHourResetAt : q.sevenDayResetAt;
|
|
51232
51160
|
const resetStr = resetAt ? ` \u00b7 refills in ${formatRelative(resetAt, new Date)}` : "";
|
|
51233
51161
|
const activeNote = snap.isActive ? "" : `
|
|
51234
|
-
This is a non-active account. Consider <code>/auth use ${
|
|
51162
|
+
This is a non-active account. Consider <code>/auth use ${escapeHtml10(snap.label)}</code> to switch, or keep it as a fallback reserve.`;
|
|
51235
51163
|
const altNote = snap.isActive ? `
|
|
51236
51164
|
Consider <code>/auth use <other-account></code> if you have a healthier account, or wait for the ${winLabel} window to refill${resetStr}.` : "";
|
|
51237
51165
|
return [
|
|
51238
|
-
`\uD83D\uDFE1 <b>Quota approaching limit</b> \u2014 <code>${
|
|
51166
|
+
`\uD83D\uDFE1 <b>Quota approaching limit</b> \u2014 <code>${escapeHtml10(snap.label)}</code>`,
|
|
51239
51167
|
``,
|
|
51240
51168
|
`${fiveStr} of 5h \u00b7 ${sevenStr} of 7d`,
|
|
51241
51169
|
`Binding window: ${winLabel}${resetStr}`,
|
|
@@ -51252,7 +51180,7 @@ function buildRecoveryMessage(agentName3, snap) {
|
|
|
51252
51180
|
const q = snap.quota;
|
|
51253
51181
|
const utilLine = q ? `Current: ${fmtPct(q.fiveHourUtilizationPct)} of 5h \u00b7 ${fmtPct(q.sevenDayUtilizationPct)} of 7d` : "Current quota data unavailable.";
|
|
51254
51182
|
return [
|
|
51255
|
-
`\uD83D\uDFE2 <b>Quota back in healthy range</b> \u2014 <code>${
|
|
51183
|
+
`\uD83D\uDFE2 <b>Quota back in healthy range</b> \u2014 <code>${escapeHtml10(snap.label)}</code>`,
|
|
51256
51184
|
``,
|
|
51257
51185
|
utilLine,
|
|
51258
51186
|
``,
|
|
@@ -51260,15 +51188,15 @@ function buildRecoveryMessage(agentName3, snap) {
|
|
|
51260
51188
|
].join(`
|
|
51261
51189
|
`);
|
|
51262
51190
|
}
|
|
51263
|
-
function
|
|
51191
|
+
function escapeHtml10(s) {
|
|
51264
51192
|
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
51265
51193
|
}
|
|
51266
51194
|
function loadQuotaWatchState(stateDir) {
|
|
51267
|
-
const path =
|
|
51268
|
-
if (!
|
|
51195
|
+
const path = join30(stateDir, STATE_FILE2);
|
|
51196
|
+
if (!existsSync33(path))
|
|
51269
51197
|
return emptyQuotaWatchState();
|
|
51270
51198
|
try {
|
|
51271
|
-
const raw =
|
|
51199
|
+
const raw = readFileSync31(path, "utf-8");
|
|
51272
51200
|
const parsed = JSON.parse(raw);
|
|
51273
51201
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
51274
51202
|
return emptyQuotaWatchState();
|
|
@@ -51286,8 +51214,8 @@ function loadQuotaWatchState(stateDir) {
|
|
|
51286
51214
|
}
|
|
51287
51215
|
function saveQuotaWatchState(stateDir, state4) {
|
|
51288
51216
|
mkdirSync20(stateDir, { recursive: true });
|
|
51289
|
-
const path =
|
|
51290
|
-
|
|
51217
|
+
const path = join30(stateDir, STATE_FILE2);
|
|
51218
|
+
writeFileSync20(path, JSON.stringify(state4, null, 2) + `
|
|
51291
51219
|
`, { mode: 384 });
|
|
51292
51220
|
}
|
|
51293
51221
|
function patchQuotaWatchState(current, accountLabel, accountState) {
|
|
@@ -51300,27 +51228,27 @@ init_auth_snapshot_format();
|
|
|
51300
51228
|
// gateway/turn-active-marker.ts
|
|
51301
51229
|
import {
|
|
51302
51230
|
closeSync as closeSync5,
|
|
51303
|
-
existsSync as
|
|
51231
|
+
existsSync as existsSync34,
|
|
51304
51232
|
mkdirSync as mkdirSync21,
|
|
51305
51233
|
openSync as openSync5,
|
|
51306
|
-
readFileSync as
|
|
51234
|
+
readFileSync as readFileSync32,
|
|
51307
51235
|
statSync as statSync9,
|
|
51308
|
-
unlinkSync as
|
|
51236
|
+
unlinkSync as unlinkSync11,
|
|
51309
51237
|
utimesSync as utimesSync2,
|
|
51310
|
-
writeFileSync as
|
|
51238
|
+
writeFileSync as writeFileSync21
|
|
51311
51239
|
} from "node:fs";
|
|
51312
|
-
import { join as
|
|
51240
|
+
import { join as join31 } from "node:path";
|
|
51313
51241
|
var TURN_ACTIVE_MARKER_FILE2 = "turn-active.json";
|
|
51314
51242
|
function writeTurnActiveMarker(stateDir, marker) {
|
|
51315
51243
|
try {
|
|
51316
51244
|
mkdirSync21(stateDir, { recursive: true });
|
|
51317
|
-
|
|
51245
|
+
writeFileSync21(join31(stateDir, TURN_ACTIVE_MARKER_FILE2), JSON.stringify(marker, null, 2) + `
|
|
51318
51246
|
`, { mode: 384 });
|
|
51319
51247
|
} catch {}
|
|
51320
51248
|
}
|
|
51321
51249
|
function touchTurnActiveMarker2(stateDir) {
|
|
51322
|
-
const path =
|
|
51323
|
-
if (!
|
|
51250
|
+
const path = join31(stateDir, TURN_ACTIVE_MARKER_FILE2);
|
|
51251
|
+
if (!existsSync34(path))
|
|
51324
51252
|
return;
|
|
51325
51253
|
const now = new Date;
|
|
51326
51254
|
try {
|
|
@@ -51334,12 +51262,12 @@ function touchTurnActiveMarker2(stateDir) {
|
|
|
51334
51262
|
}
|
|
51335
51263
|
function removeTurnActiveMarker(stateDir) {
|
|
51336
51264
|
try {
|
|
51337
|
-
|
|
51265
|
+
unlinkSync11(join31(stateDir, TURN_ACTIVE_MARKER_FILE2));
|
|
51338
51266
|
} catch {}
|
|
51339
51267
|
}
|
|
51340
51268
|
function sweepStaleTurnActiveMarker(stateDir, opts) {
|
|
51341
|
-
const path =
|
|
51342
|
-
if (!
|
|
51269
|
+
const path = join31(stateDir, TURN_ACTIVE_MARKER_FILE2);
|
|
51270
|
+
if (!existsSync34(path))
|
|
51343
51271
|
return false;
|
|
51344
51272
|
const now = opts.now ?? Date.now();
|
|
51345
51273
|
try {
|
|
@@ -51351,9 +51279,9 @@ function sweepStaleTurnActiveMarker(stateDir, opts) {
|
|
|
51351
51279
|
return false;
|
|
51352
51280
|
let payload = null;
|
|
51353
51281
|
try {
|
|
51354
|
-
payload =
|
|
51282
|
+
payload = readFileSync32(path, "utf8");
|
|
51355
51283
|
} catch {}
|
|
51356
|
-
|
|
51284
|
+
unlinkSync11(path);
|
|
51357
51285
|
if (opts.onRemove) {
|
|
51358
51286
|
try {
|
|
51359
51287
|
opts.onRemove({
|
|
@@ -51370,10 +51298,10 @@ function sweepStaleTurnActiveMarker(stateDir, opts) {
|
|
|
51370
51298
|
}
|
|
51371
51299
|
|
|
51372
51300
|
// ../src/build-info.ts
|
|
51373
|
-
var VERSION = "0.14.
|
|
51374
|
-
var COMMIT_SHA = "
|
|
51375
|
-
var COMMIT_DATE = "2026-05-
|
|
51376
|
-
var LATEST_PR =
|
|
51301
|
+
var VERSION = "0.14.22";
|
|
51302
|
+
var COMMIT_SHA = "ab2692b9";
|
|
51303
|
+
var COMMIT_DATE = "2026-05-31T06:26:06Z";
|
|
51304
|
+
var LATEST_PR = 2028;
|
|
51377
51305
|
var COMMITS_AHEAD_OF_TAG = 0;
|
|
51378
51306
|
|
|
51379
51307
|
// gateway/boot-version.ts
|
|
@@ -51447,11 +51375,11 @@ init_peercred();
|
|
|
51447
51375
|
import * as net5 from "node:net";
|
|
51448
51376
|
import * as fs2 from "node:fs";
|
|
51449
51377
|
import { homedir as homedir13 } from "node:os";
|
|
51450
|
-
import { join as
|
|
51378
|
+
import { join as join32 } from "node:path";
|
|
51451
51379
|
var DEFAULT_TIMEOUT_MS4 = 2000;
|
|
51452
51380
|
var UNLOCK_TIMEOUT_MS = 30000;
|
|
51453
|
-
var LEGACY_SOCKET_PATH2 =
|
|
51454
|
-
var OPERATOR_SOCKET_PATH2 =
|
|
51381
|
+
var LEGACY_SOCKET_PATH2 = join32(homedir13(), ".switchroom", "vault-broker.sock");
|
|
51382
|
+
var OPERATOR_SOCKET_PATH2 = join32(homedir13(), ".switchroom", "broker-operator", "sock");
|
|
51455
51383
|
function defaultBrokerSocketPath2() {
|
|
51456
51384
|
if (fs2.existsSync(OPERATOR_SOCKET_PATH2))
|
|
51457
51385
|
return OPERATOR_SOCKET_PATH2;
|
|
@@ -51674,7 +51602,7 @@ function resolveVaultApprovalPosture(broker) {
|
|
|
51674
51602
|
|
|
51675
51603
|
// registry/turns-schema.ts
|
|
51676
51604
|
import { chmodSync as chmodSync4, mkdirSync as mkdirSync22 } from "fs";
|
|
51677
|
-
import { join as
|
|
51605
|
+
import { join as join33 } from "path";
|
|
51678
51606
|
var DatabaseClass2 = null;
|
|
51679
51607
|
function loadDatabaseClass2() {
|
|
51680
51608
|
if (DatabaseClass2 != null)
|
|
@@ -51707,6 +51635,7 @@ var SCHEMA_SQL = `
|
|
|
51707
51635
|
user_prompt_preview TEXT,
|
|
51708
51636
|
assistant_reply_preview TEXT,
|
|
51709
51637
|
tool_call_count INTEGER,
|
|
51638
|
+
interrupt_reason TEXT,
|
|
51710
51639
|
created_at INTEGER NOT NULL,
|
|
51711
51640
|
updated_at INTEGER NOT NULL
|
|
51712
51641
|
);
|
|
@@ -51717,11 +51646,14 @@ var PHASE1_MIGRATIONS = [
|
|
|
51717
51646
|
`ALTER TABLE turns ADD COLUMN assistant_reply_preview TEXT`,
|
|
51718
51647
|
`ALTER TABLE turns ADD COLUMN tool_call_count INTEGER`
|
|
51719
51648
|
];
|
|
51649
|
+
var PHASE2_MIGRATIONS = [
|
|
51650
|
+
`ALTER TABLE turns ADD COLUMN interrupt_reason TEXT`
|
|
51651
|
+
];
|
|
51720
51652
|
function applySchema(db2) {
|
|
51721
51653
|
db2.exec("PRAGMA journal_mode = WAL");
|
|
51722
51654
|
db2.exec("PRAGMA synchronous = NORMAL");
|
|
51723
51655
|
db2.exec(SCHEMA_SQL);
|
|
51724
|
-
for (const sql of PHASE1_MIGRATIONS) {
|
|
51656
|
+
for (const sql of [...PHASE1_MIGRATIONS, ...PHASE2_MIGRATIONS]) {
|
|
51725
51657
|
try {
|
|
51726
51658
|
db2.exec(sql);
|
|
51727
51659
|
} catch (err) {
|
|
@@ -51733,9 +51665,9 @@ function applySchema(db2) {
|
|
|
51733
51665
|
}
|
|
51734
51666
|
function openTurnsDb(agentDir) {
|
|
51735
51667
|
const Database = loadDatabaseClass2();
|
|
51736
|
-
const dir =
|
|
51668
|
+
const dir = join33(agentDir, "telegram");
|
|
51737
51669
|
mkdirSync22(dir, { recursive: true, mode: 448 });
|
|
51738
|
-
const path =
|
|
51670
|
+
const path = join33(dir, "registry.db");
|
|
51739
51671
|
const db2 = new Database(path, { create: true });
|
|
51740
51672
|
applySchema(db2);
|
|
51741
51673
|
try {
|
|
@@ -51757,6 +51689,7 @@ function mapRow(row) {
|
|
|
51757
51689
|
user_prompt_preview: row.user_prompt_preview,
|
|
51758
51690
|
assistant_reply_preview: row.assistant_reply_preview,
|
|
51759
51691
|
tool_call_count: row.tool_call_count,
|
|
51692
|
+
interrupt_reason: row.interrupt_reason,
|
|
51760
51693
|
created_at: row.created_at,
|
|
51761
51694
|
updated_at: row.updated_at
|
|
51762
51695
|
};
|
|
@@ -51785,26 +51718,143 @@ function recordTurnEnd(db2, args) {
|
|
|
51785
51718
|
WHERE turn_key = ?
|
|
51786
51719
|
`).run(now, args.endedVia, args.lastAssistantMsgId ?? null, args.lastAssistantDone !== undefined ? args.lastAssistantDone ? 1 : 0 : null, args.assistantReplyPreview ?? null, args.toolCallCount !== undefined ? args.toolCallCount : null, now, args.turnKey);
|
|
51787
51720
|
}
|
|
51788
|
-
function
|
|
51789
|
-
const now = Date.now();
|
|
51790
|
-
const
|
|
51721
|
+
function markOrphanedWithTimeoutClassification(db2, opts) {
|
|
51722
|
+
const now = opts.now ?? Date.now();
|
|
51723
|
+
const isHang = opts.markerAgeMs != null && opts.markerAgeMs >= opts.hangThresholdMs && opts.markerTurnKey != null && opts.markerTurnKey.length > 0;
|
|
51724
|
+
let timeoutTurnKey = null;
|
|
51725
|
+
if (isHang) {
|
|
51726
|
+
const r = db2.prepare(`
|
|
51727
|
+
UPDATE turns
|
|
51728
|
+
SET ended_at = ?,
|
|
51729
|
+
ended_via = 'timeout',
|
|
51730
|
+
interrupt_reason = ?,
|
|
51731
|
+
updated_at = ?
|
|
51732
|
+
WHERE turn_key = ? AND ended_at IS NULL
|
|
51733
|
+
`).run(now, opts.reasonSnapshot ?? null, now, opts.markerTurnKey);
|
|
51734
|
+
if (r.changes > 0)
|
|
51735
|
+
timeoutTurnKey = opts.markerTurnKey ?? null;
|
|
51736
|
+
}
|
|
51737
|
+
const rest = db2.prepare(`
|
|
51791
51738
|
UPDATE turns
|
|
51792
51739
|
SET ended_at = ?,
|
|
51793
51740
|
ended_via = 'restart',
|
|
51794
51741
|
updated_at = ?
|
|
51795
51742
|
WHERE ended_at IS NULL
|
|
51796
51743
|
`).run(now, now);
|
|
51797
|
-
return
|
|
51744
|
+
return { reaped: (timeoutTurnKey ? 1 : 0) + rest.changes, timeoutTurnKey };
|
|
51798
51745
|
}
|
|
51799
|
-
|
|
51746
|
+
var INTERRUPTED_VIA = new Set([
|
|
51747
|
+
"restart",
|
|
51748
|
+
"sigterm",
|
|
51749
|
+
"timeout",
|
|
51750
|
+
"unknown"
|
|
51751
|
+
]);
|
|
51752
|
+
function findLatestTurnIfInterrupted(db2) {
|
|
51800
51753
|
const row = db2.prepare(`
|
|
51801
51754
|
SELECT * FROM turns
|
|
51802
|
-
WHERE ended_at IS NULL
|
|
51803
|
-
OR ended_via IN ('restart', 'sigterm', 'timeout')
|
|
51804
51755
|
ORDER BY started_at DESC
|
|
51805
51756
|
LIMIT 1
|
|
51806
51757
|
`).get();
|
|
51807
|
-
|
|
51758
|
+
if (!row)
|
|
51759
|
+
return null;
|
|
51760
|
+
const turn = mapRow(row);
|
|
51761
|
+
if (turn.ended_at == null)
|
|
51762
|
+
return turn;
|
|
51763
|
+
if (turn.ended_via != null && INTERRUPTED_VIA.has(turn.ended_via))
|
|
51764
|
+
return turn;
|
|
51765
|
+
return null;
|
|
51766
|
+
}
|
|
51767
|
+
|
|
51768
|
+
// gateway/resume-inbound-builder.ts
|
|
51769
|
+
function humanizeElapsed(ms) {
|
|
51770
|
+
if (!Number.isFinite(ms) || ms < 0)
|
|
51771
|
+
return "an unknown amount of time";
|
|
51772
|
+
const sec = Math.round(ms / 1000);
|
|
51773
|
+
if (sec < 45)
|
|
51774
|
+
return "moments";
|
|
51775
|
+
const min = Math.round(sec / 60);
|
|
51776
|
+
if (min < 60)
|
|
51777
|
+
return `~${min} min`;
|
|
51778
|
+
const hr = Math.round(min / 60);
|
|
51779
|
+
if (hr < 24)
|
|
51780
|
+
return `~${hr}h`;
|
|
51781
|
+
const days = Math.round(hr / 24);
|
|
51782
|
+
return `~${days} day${days === 1 ? "" : "s"}`;
|
|
51783
|
+
}
|
|
51784
|
+
function threadIdNum(turn) {
|
|
51785
|
+
if (turn.thread_id == null)
|
|
51786
|
+
return;
|
|
51787
|
+
const n = Number(turn.thread_id);
|
|
51788
|
+
return Number.isFinite(n) ? n : undefined;
|
|
51789
|
+
}
|
|
51790
|
+
function promptClause(turn) {
|
|
51791
|
+
const p = turn.user_prompt_preview?.trim();
|
|
51792
|
+
if (!p)
|
|
51793
|
+
return "";
|
|
51794
|
+
const snippet = p.length > 160 ? p.slice(0, 160) + "\u2026" : p;
|
|
51795
|
+
return ` The request was: "${snippet}".`;
|
|
51796
|
+
}
|
|
51797
|
+
function buildResumeInterruptedInbound(ctx) {
|
|
51798
|
+
const ts = ctx.nowMs ?? Date.now();
|
|
51799
|
+
const elapsed = humanizeElapsed(ts - ctx.turn.started_at);
|
|
51800
|
+
const meta = {
|
|
51801
|
+
source: "resume_interrupted",
|
|
51802
|
+
resume_turn_key: ctx.turn.turn_key,
|
|
51803
|
+
interrupted_via: ctx.turn.ended_via ?? "restart",
|
|
51804
|
+
started_at: String(ctx.turn.started_at)
|
|
51805
|
+
};
|
|
51806
|
+
if (ctx.turn.user_prompt_preview)
|
|
51807
|
+
meta.original_prompt = ctx.turn.user_prompt_preview;
|
|
51808
|
+
const threadId = threadIdNum(ctx.turn);
|
|
51809
|
+
return {
|
|
51810
|
+
type: "inbound",
|
|
51811
|
+
chatId: ctx.turn.chat_id,
|
|
51812
|
+
...threadId != null ? { threadId } : {},
|
|
51813
|
+
messageId: ts,
|
|
51814
|
+
user: "switchroom",
|
|
51815
|
+
userId: 0,
|
|
51816
|
+
ts,
|
|
51817
|
+
text: `You just restarted. Your previous turn was interrupted ${elapsed} ago, ` + `before it finished \u2014 it was cut off by a restart, not completed.` + promptClause(ctx.turn) + ` Pick that work back up now and continue it through to completion. ` + `In your first message, briefly let the user know you're resuming what ` + `was interrupted (mention roughly how long ago in plain language) so ` + `they're not left wondering \u2014 then carry on with the actual task. Do ` + `not ask whether to resume; just resume. If you genuinely can't tell ` + `what the work was, say so and ask.`,
|
|
51818
|
+
meta
|
|
51819
|
+
};
|
|
51820
|
+
}
|
|
51821
|
+
function buildResumeWatchdogReportInbound(ctx) {
|
|
51822
|
+
const ts = ctx.nowMs ?? Date.now();
|
|
51823
|
+
const idle = humanizeElapsed(ctx.idleMs);
|
|
51824
|
+
const since = humanizeElapsed(ts - ctx.turn.started_at);
|
|
51825
|
+
const toolClause = ctx.turn.tool_call_count != null && ctx.turn.tool_call_count > 0 ? ` You'd run ${ctx.turn.tool_call_count} tool call${ctx.turn.tool_call_count === 1 ? "" : "s"} before it stalled.` : "";
|
|
51826
|
+
const meta = {
|
|
51827
|
+
source: "resume_watchdog_timeout",
|
|
51828
|
+
resume_turn_key: ctx.turn.turn_key,
|
|
51829
|
+
interrupted_via: "timeout",
|
|
51830
|
+
idle_ms: String(ctx.idleMs),
|
|
51831
|
+
started_at: String(ctx.turn.started_at)
|
|
51832
|
+
};
|
|
51833
|
+
if (ctx.turn.tool_call_count != null)
|
|
51834
|
+
meta.tool_call_count = String(ctx.turn.tool_call_count);
|
|
51835
|
+
if (ctx.turn.user_prompt_preview)
|
|
51836
|
+
meta.original_prompt = ctx.turn.user_prompt_preview;
|
|
51837
|
+
const threadId = threadIdNum(ctx.turn);
|
|
51838
|
+
return {
|
|
51839
|
+
type: "inbound",
|
|
51840
|
+
chatId: ctx.turn.chat_id,
|
|
51841
|
+
...threadId != null ? { threadId } : {},
|
|
51842
|
+
messageId: ts,
|
|
51843
|
+
user: "switchroom",
|
|
51844
|
+
userId: 0,
|
|
51845
|
+
ts,
|
|
51846
|
+
text: `You just restarted. Your previous turn (started ${since} ago) was ` + `killed by the hang-watchdog: it made no observable progress for ${idle} ` + `and the watchdog restarts a turn that goes that long without activity.` + toolClause + promptClause(ctx.turn) + ` Do NOT silently resume it \u2014 it may hang again the same way. Instead, ` + `tell the user plainly what happened: that your last turn was killed ` + `after ${idle} of no progress, and roughly what it was doing. Then ask ` + `whether they want you to retry it or take a different angle. Report ` + `only the honest cause \u2014 no observable progress for that long \u2014 don't ` + `speculate about a deeper root cause you can't see.`,
|
|
51847
|
+
meta
|
|
51848
|
+
};
|
|
51849
|
+
}
|
|
51850
|
+
function selectResumeBuilder(endedVia) {
|
|
51851
|
+
if (endedVia === "timeout")
|
|
51852
|
+
return "report";
|
|
51853
|
+
if (endedVia === "restart" || endedVia === "sigterm" || endedVia === "unknown")
|
|
51854
|
+
return "resume";
|
|
51855
|
+
if (endedVia == null)
|
|
51856
|
+
return "resume";
|
|
51857
|
+
return null;
|
|
51808
51858
|
}
|
|
51809
51859
|
|
|
51810
51860
|
// registry/subagents-schema.ts
|
|
@@ -51904,11 +51954,11 @@ installGlobalErrorHandlers();
|
|
|
51904
51954
|
process.on("beforeExit", () => {
|
|
51905
51955
|
shutdownAnalytics();
|
|
51906
51956
|
});
|
|
51907
|
-
var STATE_DIR = process.env.TELEGRAM_STATE_DIR ??
|
|
51908
|
-
var ACCESS_FILE =
|
|
51909
|
-
var APPROVED_DIR =
|
|
51910
|
-
var ENV_FILE =
|
|
51911
|
-
var INBOX_DIR =
|
|
51957
|
+
var STATE_DIR = process.env.TELEGRAM_STATE_DIR ?? join35(homedir14(), ".claude", "channels", "telegram");
|
|
51958
|
+
var ACCESS_FILE = join35(STATE_DIR, "access.json");
|
|
51959
|
+
var APPROVED_DIR = join35(STATE_DIR, "approved");
|
|
51960
|
+
var ENV_FILE = join35(STATE_DIR, ".env");
|
|
51961
|
+
var INBOX_DIR = join35(STATE_DIR, "inbox");
|
|
51912
51962
|
function triggerSelfRestart(targetAgent, reason, delayMs = 300) {
|
|
51913
51963
|
const isDocker = process.env.SWITCHROOM_RUNTIME === "docker";
|
|
51914
51964
|
const selfAgent = process.env.SWITCHROOM_AGENT_NAME;
|
|
@@ -51973,7 +52023,7 @@ function formatBootVersion() {
|
|
|
51973
52023
|
}
|
|
51974
52024
|
try {
|
|
51975
52025
|
chmodSync6(ENV_FILE, 384);
|
|
51976
|
-
for (const line of
|
|
52026
|
+
for (const line of readFileSync35(ENV_FILE, "utf8").split(`
|
|
51977
52027
|
`)) {
|
|
51978
52028
|
const m = line.match(/^(\w+)=(.*)$/);
|
|
51979
52029
|
if (m && process.env[m[1]] === undefined)
|
|
@@ -52026,7 +52076,7 @@ installTgPostLogger(bot);
|
|
|
52026
52076
|
var _rawSendMessageDraft = bot.api.raw.sendMessageDraft;
|
|
52027
52077
|
var GRAMMY_VERSION = (() => {
|
|
52028
52078
|
try {
|
|
52029
|
-
const raw =
|
|
52079
|
+
const raw = readFileSync35(new URL("../../node_modules/grammy/package.json", import.meta.url), "utf8");
|
|
52030
52080
|
return JSON.parse(raw).version ?? "unknown";
|
|
52031
52081
|
} catch {
|
|
52032
52082
|
return "unknown";
|
|
@@ -52099,7 +52149,7 @@ function assertSendable(f) {
|
|
|
52099
52149
|
} catch {
|
|
52100
52150
|
throw new Error(`refusing to send file \u2014 cannot resolve real path: ${f}`);
|
|
52101
52151
|
}
|
|
52102
|
-
const inbox =
|
|
52152
|
+
const inbox = join35(stateReal, "inbox");
|
|
52103
52153
|
if (real.startsWith(stateReal + sep3) && !real.startsWith(inbox + sep3)) {
|
|
52104
52154
|
throw new Error(`refusing to send channel state: ${f}`);
|
|
52105
52155
|
}
|
|
@@ -52118,7 +52168,7 @@ function assertSendable(f) {
|
|
|
52118
52168
|
}
|
|
52119
52169
|
function readAccessFile() {
|
|
52120
52170
|
try {
|
|
52121
|
-
const raw =
|
|
52171
|
+
const raw = readFileSync35(ACCESS_FILE, "utf8");
|
|
52122
52172
|
const parsed = JSON.parse(raw);
|
|
52123
52173
|
const allowFrom = validateStringArray("allowFrom", parsed.allowFrom ?? []);
|
|
52124
52174
|
const groups = {};
|
|
@@ -52155,7 +52205,7 @@ function readAccessFile() {
|
|
|
52155
52205
|
if (err.code === "ENOENT")
|
|
52156
52206
|
return defaultAccess();
|
|
52157
52207
|
try {
|
|
52158
|
-
|
|
52208
|
+
renameSync12(ACCESS_FILE, `${ACCESS_FILE}.corrupt-${Date.now()}`);
|
|
52159
52209
|
} catch {}
|
|
52160
52210
|
process.stderr.write(`telegram gateway: access.json is corrupt, moved aside. Starting fresh.
|
|
52161
52211
|
`);
|
|
@@ -52188,9 +52238,9 @@ function saveAccess(a) {
|
|
|
52188
52238
|
return;
|
|
52189
52239
|
mkdirSync26(STATE_DIR, { recursive: true, mode: 448 });
|
|
52190
52240
|
const tmp = ACCESS_FILE + ".tmp";
|
|
52191
|
-
|
|
52241
|
+
writeFileSync24(tmp, JSON.stringify(a, null, 2) + `
|
|
52192
52242
|
`, { mode: 384 });
|
|
52193
|
-
|
|
52243
|
+
renameSync12(tmp, ACCESS_FILE);
|
|
52194
52244
|
}
|
|
52195
52245
|
function pruneExpired(a) {
|
|
52196
52246
|
const now = Date.now();
|
|
@@ -52208,7 +52258,7 @@ var HISTORY_ENABLED = HISTORY_ACCESS.historyEnabled !== false;
|
|
|
52208
52258
|
if (HISTORY_ENABLED) {
|
|
52209
52259
|
try {
|
|
52210
52260
|
initHistory(STATE_DIR, HISTORY_ACCESS.historyRetentionDays ?? 30);
|
|
52211
|
-
process.stderr.write(`telegram gateway: history capture enabled at ${
|
|
52261
|
+
process.stderr.write(`telegram gateway: history capture enabled at ${join35(STATE_DIR, "history.db")}
|
|
52212
52262
|
`);
|
|
52213
52263
|
} catch (err) {
|
|
52214
52264
|
process.stderr.write(`telegram gateway: history init failed (${err.message}) \u2014 capture disabled
|
|
@@ -52216,21 +52266,71 @@ if (HISTORY_ENABLED) {
|
|
|
52216
52266
|
}
|
|
52217
52267
|
}
|
|
52218
52268
|
var turnsDb = null;
|
|
52269
|
+
var bootResumeInbound = null;
|
|
52219
52270
|
try {
|
|
52220
52271
|
const agentDir = STATE_DIR.endsWith("/telegram") ? STATE_DIR.slice(0, -"/telegram".length) : STATE_DIR;
|
|
52221
52272
|
turnsDb = openTurnsDb(agentDir);
|
|
52222
52273
|
applySubagentsSchema(turnsDb);
|
|
52223
|
-
|
|
52274
|
+
let markerTurnKey = null;
|
|
52275
|
+
let markerAgeMs = null;
|
|
52276
|
+
try {
|
|
52277
|
+
const markerPath = join35(STATE_DIR, TURN_ACTIVE_MARKER_FILE2);
|
|
52278
|
+
if (existsSync38(markerPath)) {
|
|
52279
|
+
const st = statSync13(markerPath);
|
|
52280
|
+
markerAgeMs = Date.now() - st.mtimeMs;
|
|
52281
|
+
try {
|
|
52282
|
+
const payload = JSON.parse(readFileSync35(markerPath, "utf8"));
|
|
52283
|
+
if (typeof payload.turnKey === "string" && payload.turnKey.length > 0) {
|
|
52284
|
+
markerTurnKey = payload.turnKey;
|
|
52285
|
+
}
|
|
52286
|
+
} catch {}
|
|
52287
|
+
}
|
|
52288
|
+
} catch {}
|
|
52289
|
+
const hangSecs = Number(process.env.TURN_HANG_SECS);
|
|
52290
|
+
const hangThresholdMs = (Number.isFinite(hangSecs) && hangSecs > 0 ? hangSecs : 300) * 1000;
|
|
52291
|
+
const reasonSnapshot = markerAgeMs != null ? JSON.stringify({ idleMs: Math.round(markerAgeMs) }) : null;
|
|
52292
|
+
const { reaped, timeoutTurnKey } = markOrphanedWithTimeoutClassification(turnsDb, {
|
|
52293
|
+
markerTurnKey,
|
|
52294
|
+
markerAgeMs,
|
|
52295
|
+
hangThresholdMs,
|
|
52296
|
+
reasonSnapshot
|
|
52297
|
+
});
|
|
52224
52298
|
if (reaped > 0) {
|
|
52225
|
-
process.stderr.write(`telegram gateway: turn-registry boot-reaper stamped ${reaped} orphaned turn(s) as
|
|
52299
|
+
process.stderr.write(`telegram gateway: turn-registry boot-reaper stamped ${reaped} orphaned turn(s)${timeoutTurnKey ? ` (turnKey=${timeoutTurnKey} as 'timeout', markerAgeMs=${markerAgeMs})` : " as 'restart'"}
|
|
52226
52300
|
`);
|
|
52227
52301
|
} else {
|
|
52228
|
-
process.stderr.write(`telegram gateway: turn-registry initialized at ${
|
|
52302
|
+
process.stderr.write(`telegram gateway: turn-registry initialized at ${join35(agentDir, "telegram", "registry.db")}
|
|
52303
|
+
`);
|
|
52304
|
+
}
|
|
52305
|
+
const pending2 = findLatestTurnIfInterrupted(turnsDb);
|
|
52306
|
+
const selfAgent = process.env.SWITCHROOM_AGENT_NAME ?? "";
|
|
52307
|
+
if (pending2 != null && selfAgent) {
|
|
52308
|
+
const kind = selectResumeBuilder(pending2.ended_via);
|
|
52309
|
+
if (kind === "resume") {
|
|
52310
|
+
bootResumeInbound = { agent: selfAgent, msg: buildResumeInterruptedInbound({ turn: pending2 }) };
|
|
52311
|
+
} else if (kind === "report") {
|
|
52312
|
+
let idleMs = pending2.turn_key === timeoutTurnKey && markerAgeMs != null ? markerAgeMs : null;
|
|
52313
|
+
if (idleMs == null && pending2.interrupt_reason) {
|
|
52314
|
+
try {
|
|
52315
|
+
const parsed = JSON.parse(pending2.interrupt_reason);
|
|
52316
|
+
if (typeof parsed.idleMs === "number" && Number.isFinite(parsed.idleMs))
|
|
52317
|
+
idleMs = parsed.idleMs;
|
|
52318
|
+
} catch {}
|
|
52319
|
+
}
|
|
52320
|
+
if (idleMs == null)
|
|
52321
|
+
idleMs = Math.max(0, Date.now() - pending2.started_at);
|
|
52322
|
+
bootResumeInbound = {
|
|
52323
|
+
agent: selfAgent,
|
|
52324
|
+
msg: buildResumeWatchdogReportInbound({ turn: pending2, idleMs })
|
|
52325
|
+
};
|
|
52326
|
+
}
|
|
52327
|
+
if (bootResumeInbound != null) {
|
|
52328
|
+
process.stderr.write(`telegram gateway: boot-resume queued kind=${kind} turnKey=${pending2.turn_key} endedVia=${pending2.ended_via ?? "open"} chat=${pending2.chat_id}
|
|
52229
52329
|
`);
|
|
52330
|
+
}
|
|
52230
52331
|
}
|
|
52231
|
-
const pendingEnvPath =
|
|
52332
|
+
const pendingEnvPath = join35(agentDir, ".pending-turn.env");
|
|
52232
52333
|
try {
|
|
52233
|
-
const pending2 = findMostRecentInterruptedTurn(turnsDb);
|
|
52234
52334
|
if (pending2 != null) {
|
|
52235
52335
|
const lines = [
|
|
52236
52336
|
`SWITCHROOM_PENDING_TURN=true`,
|
|
@@ -52239,22 +52339,23 @@ try {
|
|
|
52239
52339
|
pending2.thread_id != null ? `SWITCHROOM_PENDING_THREAD_ID=${pending2.thread_id}` : `SWITCHROOM_PENDING_THREAD_ID=`,
|
|
52240
52340
|
pending2.last_user_msg_id != null ? `SWITCHROOM_PENDING_USER_MSG_ID=${pending2.last_user_msg_id}` : `SWITCHROOM_PENDING_USER_MSG_ID=`,
|
|
52241
52341
|
`SWITCHROOM_PENDING_ENDED_VIA=${pending2.ended_via ?? "unknown"}`,
|
|
52242
|
-
`SWITCHROOM_PENDING_STARTED_AT=${pending2.started_at}
|
|
52342
|
+
`SWITCHROOM_PENDING_STARTED_AT=${pending2.started_at}`,
|
|
52343
|
+
pending2.interrupt_reason != null ? `SWITCHROOM_PENDING_INTERRUPT_REASON=${pending2.interrupt_reason}` : `SWITCHROOM_PENDING_INTERRUPT_REASON=`
|
|
52243
52344
|
];
|
|
52244
52345
|
const pendingEnvTmp = `${pendingEnvPath}.tmp-${process.pid}`;
|
|
52245
|
-
|
|
52346
|
+
writeFileSync24(pendingEnvTmp, lines.join(`
|
|
52246
52347
|
`) + `
|
|
52247
52348
|
`, { mode: 384 });
|
|
52248
|
-
|
|
52349
|
+
renameSync12(pendingEnvTmp, pendingEnvPath);
|
|
52249
52350
|
process.stderr.write(`telegram gateway: pending-turn env written to ${pendingEnvPath} turnKey=${pending2.turn_key} endedVia=${pending2.ended_via ?? "open"}
|
|
52250
52351
|
`);
|
|
52251
|
-
} else if (
|
|
52352
|
+
} else if (existsSync38(pendingEnvPath)) {
|
|
52252
52353
|
rmSync4(pendingEnvPath, { force: true });
|
|
52253
52354
|
process.stderr.write(`telegram gateway: pending-turn env cleared (clean previous shutdown)
|
|
52254
52355
|
`);
|
|
52255
52356
|
}
|
|
52256
52357
|
} catch (err) {
|
|
52257
|
-
process.stderr.write(`telegram gateway: pending-turn env write failed (${err.message})
|
|
52358
|
+
process.stderr.write(`telegram gateway: pending-turn env write failed (${err.message})
|
|
52258
52359
|
`);
|
|
52259
52360
|
}
|
|
52260
52361
|
} catch (err) {
|
|
@@ -52302,7 +52403,7 @@ function checkApprovals() {
|
|
|
52302
52403
|
return;
|
|
52303
52404
|
}
|
|
52304
52405
|
for (const senderId of files) {
|
|
52305
|
-
const file =
|
|
52406
|
+
const file = join35(APPROVED_DIR, senderId);
|
|
52306
52407
|
bot.api.sendMessage(senderId, "Paired! Say hi to Claude.").then(() => rmSync4(file, { force: true }), (err) => {
|
|
52307
52408
|
process.stderr.write(`telegram gateway: failed to send approval confirm: ${err}
|
|
52308
52409
|
`);
|
|
@@ -52669,26 +52770,6 @@ function probeAvailableReactions(chatId) {
|
|
|
52669
52770
|
}
|
|
52670
52771
|
})();
|
|
52671
52772
|
}
|
|
52672
|
-
var pendingHandoffTopic = null;
|
|
52673
|
-
function initHandoffContinuity() {
|
|
52674
|
-
if (!shouldShowHandoffLine()) {
|
|
52675
|
-
pendingHandoffTopic = null;
|
|
52676
|
-
return;
|
|
52677
|
-
}
|
|
52678
|
-
const agentDir = resolveAgentDirFromEnv();
|
|
52679
|
-
if (agentDir == null) {
|
|
52680
|
-
pendingHandoffTopic = null;
|
|
52681
|
-
return;
|
|
52682
|
-
}
|
|
52683
|
-
pendingHandoffTopic = consumeHandoffTopic(agentDir);
|
|
52684
|
-
}
|
|
52685
|
-
function takeHandoffPrefix(format) {
|
|
52686
|
-
if (pendingHandoffTopic == null)
|
|
52687
|
-
return "";
|
|
52688
|
-
const line = formatHandoffLine(pendingHandoffTopic, format);
|
|
52689
|
-
pendingHandoffTopic = null;
|
|
52690
|
-
return line;
|
|
52691
|
-
}
|
|
52692
52773
|
var PHOTO_EXTS = new Set([".jpg", ".jpeg", ".png", ".gif", ".webp"]);
|
|
52693
52774
|
function chunk2(text, limit, mode) {
|
|
52694
52775
|
if (text.length <= limit)
|
|
@@ -52713,7 +52794,7 @@ function chunk2(text, limit, mode) {
|
|
|
52713
52794
|
out.push(rest);
|
|
52714
52795
|
return out;
|
|
52715
52796
|
}
|
|
52716
|
-
function
|
|
52797
|
+
function escapeMarkdownV2(text) {
|
|
52717
52798
|
const specialChars = /[_*\[\]()~`>#+\-=|{}.!\\]/g;
|
|
52718
52799
|
const parts = [];
|
|
52719
52800
|
let last = 0;
|
|
@@ -53258,11 +53339,11 @@ var unpinProgressCardForChat = null;
|
|
|
53258
53339
|
var getPinnedProgressCardMessageId = null;
|
|
53259
53340
|
var completeProgressCardTurn = null;
|
|
53260
53341
|
var subagentWatcher = null;
|
|
53261
|
-
var SOCKET_PATH = process.env.SWITCHROOM_GATEWAY_SOCKET ??
|
|
53342
|
+
var SOCKET_PATH = process.env.SWITCHROOM_GATEWAY_SOCKET ?? join35(STATE_DIR, "gateway.sock");
|
|
53262
53343
|
mkdirSync26(STATE_DIR, { recursive: true, mode: 448 });
|
|
53263
|
-
var GATEWAY_PID_PATH = process.env.SWITCHROOM_GATEWAY_PID_FILE ??
|
|
53264
|
-
var GATEWAY_SESSION_MARKER_PATH = process.env.SWITCHROOM_GATEWAY_SESSION_MARKER ??
|
|
53265
|
-
var GATEWAY_CLEAN_SHUTDOWN_MARKER_PATH = process.env.SWITCHROOM_GATEWAY_CLEAN_SHUTDOWN_MARKER ??
|
|
53344
|
+
var GATEWAY_PID_PATH = process.env.SWITCHROOM_GATEWAY_PID_FILE ?? join35(STATE_DIR, "gateway.pid.json");
|
|
53345
|
+
var GATEWAY_SESSION_MARKER_PATH = process.env.SWITCHROOM_GATEWAY_SESSION_MARKER ?? join35(STATE_DIR, "gateway-session.json");
|
|
53346
|
+
var GATEWAY_CLEAN_SHUTDOWN_MARKER_PATH = process.env.SWITCHROOM_GATEWAY_CLEAN_SHUTDOWN_MARKER ?? join35(STATE_DIR, "clean-shutdown.json");
|
|
53266
53347
|
var GATEWAY_STARTED_AT_MS = Date.now();
|
|
53267
53348
|
var BOOT_CARD_ENABLED = process.env.SWITCHROOM_BOOT_CARD !== "false";
|
|
53268
53349
|
var activeBootCard = null;
|
|
@@ -53291,7 +53372,7 @@ function ensureIssuesCard(chatId, threadId) {
|
|
|
53291
53372
|
bot: botApi,
|
|
53292
53373
|
log: (msg) => process.stderr.write(`telegram gateway: ${msg}
|
|
53293
53374
|
`),
|
|
53294
|
-
persistPath:
|
|
53375
|
+
persistPath: join35(stateDir, "issues-card.json")
|
|
53295
53376
|
});
|
|
53296
53377
|
activeIssuesWatcher = startIssuesWatcher({
|
|
53297
53378
|
stateDir,
|
|
@@ -53450,17 +53531,24 @@ startTimer2({
|
|
|
53450
53531
|
}
|
|
53451
53532
|
});
|
|
53452
53533
|
var inboundSpool = STATIC ? undefined : createInboundSpool({
|
|
53453
|
-
path:
|
|
53534
|
+
path: join35(STATE_DIR, "inbound-spool.jsonl"),
|
|
53454
53535
|
fs: {
|
|
53455
53536
|
appendFileSync: (p, d) => appendFileSync5(p, d),
|
|
53456
|
-
readFileSync: (p) =>
|
|
53457
|
-
writeFileSync: (p, d) =>
|
|
53458
|
-
renameSync: (a, b) =>
|
|
53459
|
-
existsSync: (p) =>
|
|
53537
|
+
readFileSync: (p) => readFileSync35(p, "utf8"),
|
|
53538
|
+
writeFileSync: (p, d) => writeFileSync24(p, d),
|
|
53539
|
+
renameSync: (a, b) => renameSync12(a, b),
|
|
53540
|
+
existsSync: (p) => existsSync38(p),
|
|
53460
53541
|
statSizeSync: (p) => statSync13(p).size
|
|
53461
53542
|
}
|
|
53462
53543
|
});
|
|
53463
53544
|
var pendingInboundBuffer = createPendingInboundBuffer({ spool: inboundSpool });
|
|
53545
|
+
if (bootResumeInbound != null) {
|
|
53546
|
+
if (inboundSpool != null) {
|
|
53547
|
+
inboundSpool.put(bootResumeInbound.agent, bootResumeInbound.msg);
|
|
53548
|
+
} else {
|
|
53549
|
+
pendingInboundBuffer.push(bootResumeInbound.agent, bootResumeInbound.msg);
|
|
53550
|
+
}
|
|
53551
|
+
}
|
|
53464
53552
|
if (inboundSpool != null) {
|
|
53465
53553
|
const replay = inboundSpool.liveEntries();
|
|
53466
53554
|
for (const e of replay)
|
|
@@ -53589,7 +53677,7 @@ var ipcServer = createIpcServer({
|
|
|
53589
53677
|
probeQuotaViaBroker: (t) => probeQuotaForBootCard(agentSlug, t),
|
|
53590
53678
|
tmuxSupervisor: process.env.SWITCHROOM_TMUX_SUPERVISOR === "1",
|
|
53591
53679
|
dockerMode: process.env.SWITCHROOM_RUNTIME === "docker",
|
|
53592
|
-
configSnapshotPath:
|
|
53680
|
+
configSnapshotPath: join35(resolvedAgentDirForCard, ".config-snapshot.json"),
|
|
53593
53681
|
...updateOutcomeLine ? { updateOutcomeLine } : {}
|
|
53594
53682
|
}, ackMsgId).then((handle) => {
|
|
53595
53683
|
activeBootCard = handle;
|
|
@@ -54011,7 +54099,7 @@ var ipcServer = createIpcServer({
|
|
|
54011
54099
|
const receiverUid = receiverUidRaw ? Number(receiverUidRaw) : NaN;
|
|
54012
54100
|
if (Number.isInteger(receiverUid))
|
|
54013
54101
|
allowedUids.push(receiverUid);
|
|
54014
|
-
const socketPath =
|
|
54102
|
+
const socketPath = join35(STATE_DIR, "webhook.sock");
|
|
54015
54103
|
const webhookInject = (agentName3, inbound) => {
|
|
54016
54104
|
const msg = inbound;
|
|
54017
54105
|
const delivered = ipcServer.sendToAgent(agentName3, msg);
|
|
@@ -54261,16 +54349,11 @@ ${url}`;
|
|
|
54261
54349
|
effectiveText = markdownToHtml(text);
|
|
54262
54350
|
} else if (format === "markdownv2") {
|
|
54263
54351
|
parseMode = "MarkdownV2";
|
|
54264
|
-
effectiveText =
|
|
54352
|
+
effectiveText = escapeMarkdownV2(text);
|
|
54265
54353
|
} else {
|
|
54266
54354
|
parseMode = undefined;
|
|
54267
54355
|
effectiveText = text;
|
|
54268
54356
|
}
|
|
54269
|
-
{
|
|
54270
|
-
const prefix = takeHandoffPrefix(format === "html" ? "html" : format === "markdownv2" ? "markdownv2" : "text");
|
|
54271
|
-
if (prefix.length > 0)
|
|
54272
|
-
effectiveText = prefix + effectiveText;
|
|
54273
|
-
}
|
|
54274
54357
|
assertAllowedChat(chat_id);
|
|
54275
54358
|
let threadId = resolveThreadId(chat_id, args.message_thread_id);
|
|
54276
54359
|
if (reply_to == null && quoteOptIn && HISTORY_ENABLED) {
|
|
@@ -54683,9 +54766,8 @@ async function executeStreamReply(args) {
|
|
|
54683
54766
|
bot: lockedBot,
|
|
54684
54767
|
retry: robustApiCall,
|
|
54685
54768
|
markdownToHtml,
|
|
54686
|
-
escapeMarkdownV2
|
|
54769
|
+
escapeMarkdownV2,
|
|
54687
54770
|
repairEscapedWhitespace,
|
|
54688
|
-
takeHandoffPrefix,
|
|
54689
54771
|
assertAllowedChat,
|
|
54690
54772
|
resolveThreadId,
|
|
54691
54773
|
disableLinkPreview: access.disableLinkPreview !== false,
|
|
@@ -54715,8 +54797,8 @@ async function executeStreamReply(args) {
|
|
|
54715
54797
|
progressDriver?.recordOutboundDelivered(args.chat_id, args.message_thread_id);
|
|
54716
54798
|
} catch {}
|
|
54717
54799
|
try {
|
|
54718
|
-
const
|
|
54719
|
-
noteSignal(statusKey(args.chat_id,
|
|
54800
|
+
const threadIdNum2 = args.message_thread_id != null ? Number(args.message_thread_id) : undefined;
|
|
54801
|
+
noteSignal(statusKey(args.chat_id, threadIdNum2), Date.now());
|
|
54720
54802
|
} catch {}
|
|
54721
54803
|
}
|
|
54722
54804
|
if (args.done === true && result.messageId != null && streamButtonMeta != null && streamButtonMeta.size > 0) {
|
|
@@ -55008,11 +55090,11 @@ async function executeSendGif(rawArgs) {
|
|
|
55008
55090
|
};
|
|
55009
55091
|
}
|
|
55010
55092
|
async function publishToTelegraph(text, shortName, authorName) {
|
|
55011
|
-
const accountPath =
|
|
55093
|
+
const accountPath = join35(STATE_DIR, "telegraph-account.json");
|
|
55012
55094
|
let account = null;
|
|
55013
55095
|
try {
|
|
55014
|
-
if (
|
|
55015
|
-
const raw =
|
|
55096
|
+
if (existsSync38(accountPath)) {
|
|
55097
|
+
const raw = readFileSync35(accountPath, "utf-8");
|
|
55016
55098
|
const parsed = JSON.parse(raw);
|
|
55017
55099
|
if (parsed.shortName && parsed.accessToken) {
|
|
55018
55100
|
account = parsed;
|
|
@@ -55032,7 +55114,7 @@ async function publishToTelegraph(text, shortName, authorName) {
|
|
|
55032
55114
|
account = created.value;
|
|
55033
55115
|
try {
|
|
55034
55116
|
mkdirSync26(STATE_DIR, { recursive: true, mode: 448 });
|
|
55035
|
-
|
|
55117
|
+
writeFileSync24(accountPath, JSON.stringify(account, null, 2), { mode: 384 });
|
|
55036
55118
|
} catch (err) {
|
|
55037
55119
|
process.stderr.write(`telegram gateway: telegraph cache write failed: ${err.message}
|
|
55038
55120
|
`);
|
|
@@ -55276,7 +55358,7 @@ async function executeDownloadAttachment(args) {
|
|
|
55276
55358
|
});
|
|
55277
55359
|
mkdirSync26(INBOX_DIR, { recursive: true, mode: 448 });
|
|
55278
55360
|
assertInsideInbox(INBOX_DIR, dlPath);
|
|
55279
|
-
|
|
55361
|
+
writeFileSync24(dlPath, buf, { mode: 384 });
|
|
55280
55362
|
return { content: [{ type: "text", text: dlPath }] };
|
|
55281
55363
|
}
|
|
55282
55364
|
async function executeEditMessage(args) {
|
|
@@ -55310,7 +55392,7 @@ async function executeEditMessage(args) {
|
|
|
55310
55392
|
editText = markdownToHtml(editRawText);
|
|
55311
55393
|
} else if (editFormat === "markdownv2") {
|
|
55312
55394
|
editParseMode = "MarkdownV2";
|
|
55313
|
-
editText =
|
|
55395
|
+
editText = escapeMarkdownV2(editRawText);
|
|
55314
55396
|
} else {
|
|
55315
55397
|
editParseMode = undefined;
|
|
55316
55398
|
editText = editRawText;
|
|
@@ -55497,6 +55579,14 @@ function closeProgressLane(chatId, threadId) {
|
|
|
55497
55579
|
}
|
|
55498
55580
|
}
|
|
55499
55581
|
}
|
|
55582
|
+
var FOREGROUND_SUBAGENT_ACCUM_MAX = 12;
|
|
55583
|
+
function composeTurnActivity(turn) {
|
|
55584
|
+
const childLines = [];
|
|
55585
|
+
for (const narrative of turn.foregroundSubAgents.values()) {
|
|
55586
|
+
childLines.push(...narrative);
|
|
55587
|
+
}
|
|
55588
|
+
return renderActivityFeedWithNested(turn.mirrorLines, childLines);
|
|
55589
|
+
}
|
|
55500
55590
|
async function drainActivitySummary(turn) {
|
|
55501
55591
|
try {
|
|
55502
55592
|
while (turn.activityPendingRender !== turn.activityLastSentRender) {
|
|
@@ -55589,6 +55679,7 @@ function handleSessionEvent(ev) {
|
|
|
55589
55679
|
activityPendingRender: null,
|
|
55590
55680
|
activityLastSentRender: null,
|
|
55591
55681
|
mirrorLines: [],
|
|
55682
|
+
foregroundSubAgents: new Map,
|
|
55592
55683
|
answerStream: null,
|
|
55593
55684
|
isDm: isDmChatId(ev.chatId)
|
|
55594
55685
|
};
|
|
@@ -55683,7 +55774,7 @@ function handleSessionEvent(ev) {
|
|
|
55683
55774
|
return;
|
|
55684
55775
|
const rendered = appendActivityLabel(turn.mirrorLines, ev.label);
|
|
55685
55776
|
if (rendered != null) {
|
|
55686
|
-
turn.activityPendingRender = rendered;
|
|
55777
|
+
turn.activityPendingRender = composeTurnActivity(turn) ?? rendered;
|
|
55687
55778
|
if (turn.activityInFlight == null) {
|
|
55688
55779
|
turn.activityInFlight = drainActivitySummary(turn);
|
|
55689
55780
|
}
|
|
@@ -57106,14 +57197,14 @@ function restartMarkerPath() {
|
|
|
57106
57197
|
const agentDir = resolveAgentDirFromEnv();
|
|
57107
57198
|
if (!agentDir)
|
|
57108
57199
|
return null;
|
|
57109
|
-
return
|
|
57200
|
+
return join35(agentDir, "restart-pending.json");
|
|
57110
57201
|
}
|
|
57111
57202
|
function writeRestartMarker(marker) {
|
|
57112
57203
|
const p = restartMarkerPath();
|
|
57113
57204
|
if (!p)
|
|
57114
57205
|
return;
|
|
57115
57206
|
try {
|
|
57116
|
-
|
|
57207
|
+
writeFileSync24(p, JSON.stringify(marker));
|
|
57117
57208
|
lastPlannedRestartAt = Date.now();
|
|
57118
57209
|
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}
|
|
57119
57210
|
`);
|
|
@@ -57132,7 +57223,7 @@ function readRestartMarker() {
|
|
|
57132
57223
|
if (!p)
|
|
57133
57224
|
return null;
|
|
57134
57225
|
try {
|
|
57135
|
-
return JSON.parse(
|
|
57226
|
+
return JSON.parse(readFileSync35(p, "utf8"));
|
|
57136
57227
|
} catch {
|
|
57137
57228
|
return null;
|
|
57138
57229
|
}
|
|
@@ -57230,7 +57321,7 @@ var _dockerReachable;
|
|
|
57230
57321
|
function isDockerReachable() {
|
|
57231
57322
|
if (_dockerReachable !== undefined)
|
|
57232
57323
|
return _dockerReachable;
|
|
57233
|
-
if (!
|
|
57324
|
+
if (!existsSync38("/var/run/docker.sock")) {
|
|
57234
57325
|
_dockerReachable = false;
|
|
57235
57326
|
return _dockerReachable;
|
|
57236
57327
|
}
|
|
@@ -57247,12 +57338,12 @@ function _resetDockerReachableCache() {
|
|
|
57247
57338
|
}
|
|
57248
57339
|
function spawnSwitchroomDetached(args, onFailure) {
|
|
57249
57340
|
const fullArgs = SWITCHROOM_CONFIG ? ["--config", SWITCHROOM_CONFIG, ...args] : args;
|
|
57250
|
-
const logPath =
|
|
57341
|
+
const logPath = join35(STATE_DIR, "detached-spawn.log");
|
|
57251
57342
|
let outFd = null;
|
|
57252
57343
|
try {
|
|
57253
57344
|
mkdirSync26(STATE_DIR, { recursive: true });
|
|
57254
57345
|
outFd = openSync8(logPath, "a");
|
|
57255
|
-
|
|
57346
|
+
writeFileSync24(logPath, `
|
|
57256
57347
|
[${new Date().toISOString()}] spawn ${SWITCHROOM_CLI} ${fullArgs.join(" ")}
|
|
57257
57348
|
`, { flag: "a" });
|
|
57258
57349
|
} catch {}
|
|
@@ -57278,7 +57369,7 @@ function spawnSwitchroomDetached(args, onFailure) {
|
|
|
57278
57369
|
return;
|
|
57279
57370
|
let tail = "";
|
|
57280
57371
|
try {
|
|
57281
|
-
const full =
|
|
57372
|
+
const full = readFileSync35(logPath, "utf8");
|
|
57282
57373
|
tail = full.split(`
|
|
57283
57374
|
`).slice(-30).join(`
|
|
57284
57375
|
`).trim();
|
|
@@ -57620,10 +57711,10 @@ bot.use(async (ctx, next) => {
|
|
|
57620
57711
|
});
|
|
57621
57712
|
function readRecentDenialsForAgent(agentName3, windowMs, limit) {
|
|
57622
57713
|
try {
|
|
57623
|
-
const auditPath =
|
|
57624
|
-
if (!
|
|
57714
|
+
const auditPath = join35(homedir14(), ".switchroom", "vault-audit.log");
|
|
57715
|
+
if (!existsSync38(auditPath))
|
|
57625
57716
|
return [];
|
|
57626
|
-
const raw =
|
|
57717
|
+
const raw = readFileSync35(auditPath, "utf8");
|
|
57627
57718
|
return recentDenialsFromAuditLog(raw, { agentName: agentName3, windowMs, limit });
|
|
57628
57719
|
} catch {
|
|
57629
57720
|
return [];
|
|
@@ -57674,7 +57765,7 @@ async function buildAgentMetadata(agentName3) {
|
|
|
57674
57765
|
try {
|
|
57675
57766
|
const agentDir = resolveAgentDirFromEnv();
|
|
57676
57767
|
if (agentDir) {
|
|
57677
|
-
const raw =
|
|
57768
|
+
const raw = readFileSync35(join35(agentDir, ".claude", ".claude.json"), "utf8");
|
|
57678
57769
|
claudeJson = JSON.parse(raw);
|
|
57679
57770
|
}
|
|
57680
57771
|
} catch {}
|
|
@@ -57888,10 +57979,10 @@ bot.command("restart", async (ctx) => {
|
|
|
57888
57979
|
function flushAgentHandoff(agentDir) {
|
|
57889
57980
|
let removed = 0;
|
|
57890
57981
|
for (const fname of [".handoff.md", ".handoff-topic"]) {
|
|
57891
|
-
const p =
|
|
57982
|
+
const p = join35(agentDir, fname);
|
|
57892
57983
|
try {
|
|
57893
|
-
if (
|
|
57894
|
-
|
|
57984
|
+
if (existsSync38(p)) {
|
|
57985
|
+
unlinkSync14(p);
|
|
57895
57986
|
removed++;
|
|
57896
57987
|
}
|
|
57897
57988
|
} catch (err) {
|
|
@@ -57946,7 +58037,7 @@ async function handleNewOrResetCommand(ctx, kind) {
|
|
|
57946
58037
|
writeRestartMarker({ chat_id: chatId, thread_id: threadId ?? null, ack_message_id: ackId, ts: Date.now() });
|
|
57947
58038
|
if (agentDir != null) {
|
|
57948
58039
|
try {
|
|
57949
|
-
|
|
58040
|
+
writeFileSync24(join35(agentDir, ".force-fresh-session"), `${kind} at ${new Date().toISOString()}
|
|
57950
58041
|
`, "utf8");
|
|
57951
58042
|
} catch (err) {
|
|
57952
58043
|
process.stderr.write(`telegram gateway: failed to write force-fresh marker: ${err}
|
|
@@ -58306,16 +58397,16 @@ bot.command("interrupt", async (ctx) => {
|
|
|
58306
58397
|
await runSwitchroomCommand(ctx, ["agent", "interrupt", name], `interrupt ${name}`);
|
|
58307
58398
|
});
|
|
58308
58399
|
var lockoutOps = {
|
|
58309
|
-
readFileSync: (p, enc) =>
|
|
58310
|
-
writeFileSync: (p, data, opts) =>
|
|
58311
|
-
existsSync: (p) =>
|
|
58400
|
+
readFileSync: (p, enc) => readFileSync35(p, enc),
|
|
58401
|
+
writeFileSync: (p, data, opts) => writeFileSync24(p, data, opts),
|
|
58402
|
+
existsSync: (p) => existsSync38(p),
|
|
58312
58403
|
mkdirSync: (p, opts) => mkdirSync26(p, opts),
|
|
58313
|
-
joinPath: (...parts) =>
|
|
58404
|
+
joinPath: (...parts) => join35(...parts)
|
|
58314
58405
|
};
|
|
58315
58406
|
var FLEET_FALLBACK_DEDUP_MS = 30000;
|
|
58316
58407
|
function isAuthBrokerSocketReachable() {
|
|
58317
58408
|
try {
|
|
58318
|
-
return
|
|
58409
|
+
return existsSync38(resolveAuthBrokerSocketPath2());
|
|
58319
58410
|
} catch {
|
|
58320
58411
|
return false;
|
|
58321
58412
|
}
|
|
@@ -58376,7 +58467,7 @@ async function runCreditWatch() {
|
|
|
58376
58467
|
if (!agentDir)
|
|
58377
58468
|
return;
|
|
58378
58469
|
const agentName3 = getMyAgentName();
|
|
58379
|
-
const claudeConfigDir =
|
|
58470
|
+
const claudeConfigDir = join35(agentDir, ".claude");
|
|
58380
58471
|
const stateDir = STATE_DIR;
|
|
58381
58472
|
const reason = readClaudeJsonOverage(claudeConfigDir);
|
|
58382
58473
|
const prev = loadCreditState(stateDir);
|
|
@@ -58679,10 +58770,10 @@ async function handleVaultRecentDenialCallback(ctx, data) {
|
|
|
58679
58770
|
return;
|
|
58680
58771
|
}
|
|
58681
58772
|
const { token, id } = result;
|
|
58682
|
-
const tokenPath =
|
|
58773
|
+
const tokenPath = join35(homedir14(), ".switchroom", "agents", agentName3, ".vault-token");
|
|
58683
58774
|
try {
|
|
58684
|
-
mkdirSync26(
|
|
58685
|
-
|
|
58775
|
+
mkdirSync26(join35(homedir14(), ".switchroom", "agents", agentName3), { recursive: true });
|
|
58776
|
+
writeFileSync24(tokenPath, token, { mode: 384 });
|
|
58686
58777
|
} catch (err) {
|
|
58687
58778
|
await switchroomReply(ctx, `<b>Grant created (${escapeHtmlForTg(id)}) but token write failed:</b> ${escapeHtmlForTg(String(err))}
|
|
58688
58779
|
<i>Recover with: <code>switchroom vault grant ${escapeHtmlForTg(agentName3)} --keys ${escapeHtmlForTg(keyName)} --duration 30d</code> on the host.</i>`, { html: true });
|
|
@@ -58758,10 +58849,10 @@ async function performVaultAccessApproval(ctx, pending2, stageId, senderId, atte
|
|
|
58758
58849
|
return;
|
|
58759
58850
|
}
|
|
58760
58851
|
const { token, id } = result;
|
|
58761
|
-
const tokenPath =
|
|
58852
|
+
const tokenPath = join35(homedir14(), ".switchroom", "agents", pending2.agent, ".vault-token");
|
|
58762
58853
|
try {
|
|
58763
|
-
mkdirSync26(
|
|
58764
|
-
|
|
58854
|
+
mkdirSync26(join35(homedir14(), ".switchroom", "agents", pending2.agent), { recursive: true });
|
|
58855
|
+
writeFileSync24(tokenPath, token, { mode: 384 });
|
|
58765
58856
|
} catch (err) {
|
|
58766
58857
|
await switchroomReply(ctx, `<b>Grant created (${escapeHtmlForTg(id)}) but token write failed:</b> ${escapeHtmlForTg(String(err))}
|
|
58767
58858
|
<i>Recover with: <code>switchroom vault grant ${escapeHtmlForTg(pending2.agent)} --keys ${escapeHtmlForTg(pending2.key)} --duration ${Math.round(pending2.ttl_seconds / 86400)}d</code> on the host.</i>`, { html: true });
|
|
@@ -59236,10 +59327,10 @@ async function executeGrantWizard(ctx, chatId, state4) {
|
|
|
59236
59327
|
return;
|
|
59237
59328
|
}
|
|
59238
59329
|
const { token, id } = result;
|
|
59239
|
-
const tokenPath =
|
|
59330
|
+
const tokenPath = join35(homedir14(), ".switchroom", "agents", state4.agent, ".vault-token");
|
|
59240
59331
|
try {
|
|
59241
|
-
mkdirSync26(
|
|
59242
|
-
|
|
59332
|
+
mkdirSync26(join35(homedir14(), ".switchroom", "agents", state4.agent), { recursive: true });
|
|
59333
|
+
writeFileSync24(tokenPath, token, { mode: 384 });
|
|
59243
59334
|
} catch (err) {
|
|
59244
59335
|
await switchroomReply(ctx, `<b>Grant created but token write failed:</b> ${escapeHtmlForTg(String(err))}`, { html: true });
|
|
59245
59336
|
return;
|
|
@@ -60087,7 +60178,7 @@ bot.command("usage", async (ctx) => {
|
|
|
60087
60178
|
await switchroomReply(ctx, "<b>/usage:</b> cannot resolve agent dir.", { html: true });
|
|
60088
60179
|
return;
|
|
60089
60180
|
}
|
|
60090
|
-
const result = await fetchQuota2({ claudeConfigDir:
|
|
60181
|
+
const result = await fetchQuota2({ claudeConfigDir: join35(agentDir, ".claude") });
|
|
60091
60182
|
if (!result.ok) {
|
|
60092
60183
|
await switchroomReply(ctx, `<b>/usage:</b> ${escapeHtmlForTg(result.reason)}`, { html: true });
|
|
60093
60184
|
return;
|
|
@@ -60527,7 +60618,7 @@ ${preBlock(formatSwitchroomOutput(err.message ?? "unknown error"))}`, { html: tr
|
|
|
60527
60618
|
const unifiedDiff = (() => {
|
|
60528
60619
|
try {
|
|
60529
60620
|
const cfgPath = process.env.SWITCHROOM_CONFIG ?? SWITCHROOM_CONFIG ?? findConfigFile2();
|
|
60530
|
-
const raw =
|
|
60621
|
+
const raw = readFileSync35(cfgPath, "utf8");
|
|
60531
60622
|
return synthesizeAllowRuleDiff({ agentName: agentName3, rule: chosen.rule, configText: raw });
|
|
60532
60623
|
} catch (err) {
|
|
60533
60624
|
process.stderr.write(`telegram gateway: always-allow diff synth failed: ${err.message}
|
|
@@ -60669,7 +60760,7 @@ bot.on("message:photo", async (ctx) => {
|
|
|
60669
60760
|
});
|
|
60670
60761
|
mkdirSync26(INBOX_DIR, { recursive: true, mode: 448 });
|
|
60671
60762
|
assertInsideInbox(INBOX_DIR, dlPath);
|
|
60672
|
-
|
|
60763
|
+
writeFileSync24(dlPath, buf, { mode: 384 });
|
|
60673
60764
|
return dlPath;
|
|
60674
60765
|
} catch (err) {
|
|
60675
60766
|
const msg = err instanceof Error ? err.message : "unknown error";
|
|
@@ -60709,8 +60800,8 @@ async function maybeTranscribeVoice(fileId, mimeType, language) {
|
|
|
60709
60800
|
let apiKey = null;
|
|
60710
60801
|
try {
|
|
60711
60802
|
const path = __require("path").join(__require("os").homedir(), ".switchroom", "openai-api-key");
|
|
60712
|
-
if (
|
|
60713
|
-
apiKey =
|
|
60803
|
+
if (existsSync38(path)) {
|
|
60804
|
+
apiKey = readFileSync35(path, "utf-8").trim();
|
|
60714
60805
|
}
|
|
60715
60806
|
} catch (err) {
|
|
60716
60807
|
process.stderr.write(`telegram gateway: voice-in: failed to read api key: ${err.message}
|
|
@@ -61345,7 +61436,6 @@ process.on("SIGINT", () => void shutdown("SIGINT"));
|
|
|
61345
61436
|
`) });
|
|
61346
61437
|
}
|
|
61347
61438
|
}
|
|
61348
|
-
initHandoffContinuity();
|
|
61349
61439
|
process.on("unhandledRejection", (err) => {
|
|
61350
61440
|
const action = classifyRejection(err);
|
|
61351
61441
|
process.stderr.write(`telegram gateway: unhandled rejection (${action}): ${err}
|
|
@@ -61566,7 +61656,7 @@ var didOneTimeSetup = false;
|
|
|
61566
61656
|
return;
|
|
61567
61657
|
}
|
|
61568
61658
|
})();
|
|
61569
|
-
const resolvedAgentDirForBootCard = agentDir ??
|
|
61659
|
+
const resolvedAgentDirForBootCard = agentDir ?? join35(homedir14(), ".switchroom", "agents", agentSlug);
|
|
61570
61660
|
const handle = await startBootCard(chatId, threadId, botApiForCard, {
|
|
61571
61661
|
agentName: agentDisplayName,
|
|
61572
61662
|
agentSlug,
|
|
@@ -61580,7 +61670,7 @@ var didOneTimeSetup = false;
|
|
|
61580
61670
|
probeQuotaViaBroker: (t) => probeQuotaForBootCard(agentSlug, t),
|
|
61581
61671
|
tmuxSupervisor: process.env.SWITCHROOM_TMUX_SUPERVISOR === "1",
|
|
61582
61672
|
dockerMode: process.env.SWITCHROOM_RUNTIME === "docker",
|
|
61583
|
-
configSnapshotPath:
|
|
61673
|
+
configSnapshotPath: join35(resolvedAgentDirForBootCard, ".config-snapshot.json"),
|
|
61584
61674
|
...updateOutcomeLine ? { updateOutcomeLine } : {}
|
|
61585
61675
|
}, ackMsgId);
|
|
61586
61676
|
activeBootCard = handle;
|
|
@@ -61665,6 +61755,7 @@ var didOneTimeSetup = false;
|
|
|
61665
61755
|
const watcherAgentDir = resolveAgentDirFromEnv();
|
|
61666
61756
|
if (watcherAgentDir != null) {
|
|
61667
61757
|
const workerFeedEnabled = isWorkerActivityFeedEnabled(process.env.SWITCHROOM_WORKER_ACTIVITY_FEED);
|
|
61758
|
+
const foregroundNestingEnabled = process.env.SWITCHROOM_FOREGROUND_SUBAGENT_NESTING !== "0";
|
|
61668
61759
|
const workerActivityFeed = createWorkerActivityFeed({
|
|
61669
61760
|
bot: {
|
|
61670
61761
|
sendMessage: async (cid, text, sendOpts) => {
|
|
@@ -61724,6 +61815,19 @@ var didOneTimeSetup = false;
|
|
|
61724
61815
|
} catch {}
|
|
61725
61816
|
}
|
|
61726
61817
|
const isBackground = dispatch.isBackground;
|
|
61818
|
+
if (!isBackground) {
|
|
61819
|
+
const turn = currentTurn;
|
|
61820
|
+
if (turn != null && turn.foregroundSubAgents.delete(agentId) && !turn.replyCalled) {
|
|
61821
|
+
const rendered = composeTurnActivity(turn);
|
|
61822
|
+
if (rendered != null) {
|
|
61823
|
+
turn.activityPendingRender = rendered;
|
|
61824
|
+
if (turn.activityInFlight == null) {
|
|
61825
|
+
turn.activityInFlight = drainActivitySummary(turn);
|
|
61826
|
+
}
|
|
61827
|
+
}
|
|
61828
|
+
}
|
|
61829
|
+
return;
|
|
61830
|
+
}
|
|
61727
61831
|
if (workerFeedEnabled) {
|
|
61728
61832
|
workerActivityFeed.finish(agentId, {
|
|
61729
61833
|
description: dispatch.feedDescription,
|
|
@@ -61784,8 +61888,35 @@ var didOneTimeSetup = false;
|
|
|
61784
61888
|
} catch {}
|
|
61785
61889
|
}
|
|
61786
61890
|
const isBackground = dispatch.isBackground;
|
|
61787
|
-
if (!isBackground)
|
|
61891
|
+
if (!isBackground) {
|
|
61892
|
+
if (!foregroundNestingEnabled)
|
|
61893
|
+
return;
|
|
61894
|
+
const turn = currentTurn;
|
|
61895
|
+
if (turn == null || turn.replyCalled)
|
|
61896
|
+
return;
|
|
61897
|
+
const child = latestSummary.trim().slice(0, 120);
|
|
61898
|
+
if (child.length === 0)
|
|
61899
|
+
return;
|
|
61900
|
+
let narrative = turn.foregroundSubAgents.get(agentId);
|
|
61901
|
+
if (narrative == null) {
|
|
61902
|
+
narrative = [];
|
|
61903
|
+
turn.foregroundSubAgents.set(agentId, narrative);
|
|
61904
|
+
}
|
|
61905
|
+
if (narrative[narrative.length - 1] !== child) {
|
|
61906
|
+
narrative.push(child);
|
|
61907
|
+
if (narrative.length > FOREGROUND_SUBAGENT_ACCUM_MAX) {
|
|
61908
|
+
narrative.splice(0, narrative.length - FOREGROUND_SUBAGENT_ACCUM_MAX);
|
|
61909
|
+
}
|
|
61910
|
+
}
|
|
61911
|
+
const rendered = composeTurnActivity(turn);
|
|
61912
|
+
if (rendered != null) {
|
|
61913
|
+
turn.activityPendingRender = rendered;
|
|
61914
|
+
if (turn.activityInFlight == null) {
|
|
61915
|
+
turn.activityInFlight = drainActivitySummary(turn);
|
|
61916
|
+
}
|
|
61917
|
+
}
|
|
61788
61918
|
return;
|
|
61919
|
+
}
|
|
61789
61920
|
if (workerFeedEnabled) {
|
|
61790
61921
|
workerActivityFeed.update(agentId, fleetChatId || (loadAccess().allowFrom[0] ?? ""), {
|
|
61791
61922
|
description: dispatch.feedDescription,
|