switchroom 0.14.21 → 0.14.23
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 +788 -513
- 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/subagent-watcher.ts +79 -5
- 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/subagent-registry-bugs.test.ts +7 -3
- package/telegram-plugin/tests/subagent-watcher-handback-gaps.test.ts +293 -0
- package/telegram-plugin/tests/subagent-watcher.test.ts +23 -15
- 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,18 +48815,79 @@ 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
|
|
48830
|
+
function classifyClaudeError(raw) {
|
|
48831
|
+
try {
|
|
48832
|
+
return classifyInner(raw);
|
|
48833
|
+
} catch {
|
|
48834
|
+
return "unknown-4xx";
|
|
48835
|
+
}
|
|
48836
|
+
}
|
|
48837
|
+
function classifyInner(raw) {
|
|
48838
|
+
if (raw == null)
|
|
48839
|
+
return "unknown-4xx";
|
|
48840
|
+
const obj = typeof raw === "object" ? raw : {};
|
|
48841
|
+
const errorType = extractString(obj, "error_type") ?? extractString(obj, "type") ?? extractString(getNestedObj(obj, "error"), "type") ?? "";
|
|
48842
|
+
const errorCode = extractString(obj, "code") ?? extractString(getNestedObj(obj, "error"), "code") ?? "";
|
|
48843
|
+
const message = extractString(obj, "message") ?? extractString(getNestedObj(obj, "error"), "message") ?? (typeof raw === "string" ? raw : "") ?? "";
|
|
48844
|
+
const status = extractNumber(obj, "status") ?? extractNumber(obj, "statusCode") ?? extractNumber(obj, "status_code") ?? null;
|
|
48845
|
+
const sdkCode = extractString(obj, "error_code") ?? "";
|
|
48846
|
+
if (errorType === "authentication_error" || errorCode === "authentication_error" || sdkCode === "authentication_error" || message.toLowerCase().includes("authentication_error")) {
|
|
48847
|
+
const msg = message.toLowerCase();
|
|
48848
|
+
if (msg.includes("expired") || msg.includes("refresh")) {
|
|
48849
|
+
return "credentials-expired";
|
|
48850
|
+
}
|
|
48851
|
+
return "credentials-invalid";
|
|
48852
|
+
}
|
|
48853
|
+
if (errorType === "invalid_api_key" || errorCode === "invalid_api_key" || sdkCode === "invalid_api_key" || message.toLowerCase().includes("invalid_api_key") || message.toLowerCase().includes("invalid api key")) {
|
|
48854
|
+
return "credentials-invalid";
|
|
48855
|
+
}
|
|
48856
|
+
if (errorType === "credit_balance_too_low" || errorCode === "credit_balance_too_low" || sdkCode === "credit_balance_too_low" || message.toLowerCase().includes("credit_balance_too_low") || message.toLowerCase().includes("credit balance")) {
|
|
48857
|
+
return "credit-exhausted";
|
|
48858
|
+
}
|
|
48859
|
+
if (errorType === "rate_limit_error" || errorCode === "rate_limit_error" || sdkCode === "rate_limit_error" || message.toLowerCase().includes("rate_limit_error") || message.toLowerCase().includes("rate limit")) {
|
|
48860
|
+
return "rate-limited";
|
|
48861
|
+
}
|
|
48862
|
+
if (errorType === "overloaded_error" || errorCode === "overloaded_error" || sdkCode === "overloaded_error" || message.toLowerCase().includes("overloaded_error") || message.toLowerCase().includes("overloaded")) {
|
|
48863
|
+
return "rate-limited";
|
|
48864
|
+
}
|
|
48865
|
+
if (errorType === "agent-crashed" || errorCode === "agent-crashed") {
|
|
48866
|
+
return "agent-crashed";
|
|
48867
|
+
}
|
|
48868
|
+
if (errorType === "agent-restarted-unexpectedly" || errorCode === "agent-restarted-unexpectedly") {
|
|
48869
|
+
return "agent-restarted-unexpectedly";
|
|
48870
|
+
}
|
|
48871
|
+
if (status != null) {
|
|
48872
|
+
if (status >= 400 && status < 500)
|
|
48873
|
+
return "unknown-4xx";
|
|
48874
|
+
if (status >= 500 && status < 600)
|
|
48875
|
+
return "unknown-5xx";
|
|
48876
|
+
}
|
|
48877
|
+
return "unknown-4xx";
|
|
48878
|
+
}
|
|
48879
|
+
function extractString(obj, key) {
|
|
48880
|
+
const v = obj[key];
|
|
48881
|
+
return typeof v === "string" && v.length > 0 ? v : null;
|
|
48882
|
+
}
|
|
48883
|
+
function extractNumber(obj, key) {
|
|
48884
|
+
const v = obj[key];
|
|
48885
|
+
return typeof v === "number" ? v : null;
|
|
48886
|
+
}
|
|
48887
|
+
function getNestedObj(obj, key) {
|
|
48888
|
+
const v = obj[key];
|
|
48889
|
+
return typeof v === "object" && v != null ? v : {};
|
|
48890
|
+
}
|
|
48902
48891
|
var DEFAULT_OPERATOR_EVENT_COOLDOWN_MS2 = 5 * 60000;
|
|
48903
48892
|
var cooldownMap2 = new Map;
|
|
48904
48893
|
|
|
@@ -49008,6 +48997,72 @@ function projectSubagentLine(line, agentId, state4) {
|
|
|
49008
48997
|
}
|
|
49009
48998
|
return [];
|
|
49010
48999
|
}
|
|
49000
|
+
function extractRetryState(obj) {
|
|
49001
|
+
return {
|
|
49002
|
+
retryAttempt: typeof obj.retryAttempt === "number" ? obj.retryAttempt : null,
|
|
49003
|
+
maxRetries: typeof obj.maxRetries === "number" ? obj.maxRetries : null
|
|
49004
|
+
};
|
|
49005
|
+
}
|
|
49006
|
+
function detectErrorInTranscriptLine(line) {
|
|
49007
|
+
if (!line || line.length > 2 * 1024 * 1024)
|
|
49008
|
+
return null;
|
|
49009
|
+
let obj;
|
|
49010
|
+
try {
|
|
49011
|
+
obj = JSON.parse(line);
|
|
49012
|
+
} catch {
|
|
49013
|
+
return null;
|
|
49014
|
+
}
|
|
49015
|
+
if (typeof obj !== "object" || obj == null)
|
|
49016
|
+
return null;
|
|
49017
|
+
const type = obj.type;
|
|
49018
|
+
if (obj.isApiErrorMessage === true) {
|
|
49019
|
+
const status = typeof obj.apiErrorStatus === "number" ? obj.apiErrorStatus : null;
|
|
49020
|
+
const errStr = typeof obj.error === "string" ? obj.error : "";
|
|
49021
|
+
const text = extractAssistantText(obj);
|
|
49022
|
+
const kind2 = status === 429 ? "quota-exhausted" : classifyClaudeError({ type: errStr, status, message: text });
|
|
49023
|
+
return {
|
|
49024
|
+
kind: kind2,
|
|
49025
|
+
raw: obj,
|
|
49026
|
+
detail: text || errStr || "api error",
|
|
49027
|
+
transient: kind2 === "rate-limited",
|
|
49028
|
+
terminal: true
|
|
49029
|
+
};
|
|
49030
|
+
}
|
|
49031
|
+
const isErrorLine = type === "api_error" || type === "error";
|
|
49032
|
+
const embeddedError = typeof obj.error === "object" && obj.error != null ? obj.error : null;
|
|
49033
|
+
if (!isErrorLine && !embeddedError)
|
|
49034
|
+
return null;
|
|
49035
|
+
const raw = embeddedError ?? obj;
|
|
49036
|
+
const kind = classifyClaudeError(embeddedError ?? obj);
|
|
49037
|
+
const detail = extractDetailMessage(embeddedError) ?? extractDetailMessage(obj) ?? String(type ?? "");
|
|
49038
|
+
const transient = kind === "rate-limited";
|
|
49039
|
+
const retry = extractRetryState(obj);
|
|
49040
|
+
const terminal = !transient ? true : retry.retryAttempt != null && retry.maxRetries != null ? retry.retryAttempt >= retry.maxRetries : isErrorLine;
|
|
49041
|
+
return { kind, raw, detail, transient, terminal };
|
|
49042
|
+
}
|
|
49043
|
+
function extractDetailMessage(obj) {
|
|
49044
|
+
if (!obj)
|
|
49045
|
+
return null;
|
|
49046
|
+
const msg = obj.message;
|
|
49047
|
+
return typeof msg === "string" && msg.length > 0 ? msg : null;
|
|
49048
|
+
}
|
|
49049
|
+
function extractAssistantText(obj) {
|
|
49050
|
+
const message = obj.message;
|
|
49051
|
+
if (typeof message !== "object" || message == null)
|
|
49052
|
+
return "";
|
|
49053
|
+
const content = message.content;
|
|
49054
|
+
if (!Array.isArray(content))
|
|
49055
|
+
return "";
|
|
49056
|
+
const parts = [];
|
|
49057
|
+
for (const block of content) {
|
|
49058
|
+
if (typeof block === "object" && block != null && block.type === "text") {
|
|
49059
|
+
const t = block.text;
|
|
49060
|
+
if (typeof t === "string")
|
|
49061
|
+
parts.push(t);
|
|
49062
|
+
}
|
|
49063
|
+
}
|
|
49064
|
+
return parts.join(" ").trim();
|
|
49065
|
+
}
|
|
49011
49066
|
|
|
49012
49067
|
// fleet-state.ts
|
|
49013
49068
|
var SANITISE_MAX_LEN = 120;
|
|
@@ -49139,20 +49194,20 @@ function bumpSubagentActivity(db2, args) {
|
|
|
49139
49194
|
// gateway/turn-active-marker.ts
|
|
49140
49195
|
import {
|
|
49141
49196
|
closeSync,
|
|
49142
|
-
existsSync as
|
|
49197
|
+
existsSync as existsSync23,
|
|
49143
49198
|
mkdirSync as mkdirSync13,
|
|
49144
49199
|
openSync,
|
|
49145
|
-
readFileSync as
|
|
49200
|
+
readFileSync as readFileSync21,
|
|
49146
49201
|
statSync as statSync5,
|
|
49147
|
-
unlinkSync as
|
|
49202
|
+
unlinkSync as unlinkSync9,
|
|
49148
49203
|
utimesSync,
|
|
49149
|
-
writeFileSync as
|
|
49204
|
+
writeFileSync as writeFileSync13
|
|
49150
49205
|
} from "node:fs";
|
|
49151
|
-
import { join as
|
|
49206
|
+
import { join as join20 } from "node:path";
|
|
49152
49207
|
var TURN_ACTIVE_MARKER_FILE = "turn-active.json";
|
|
49153
49208
|
function touchTurnActiveMarker(stateDir) {
|
|
49154
|
-
const path =
|
|
49155
|
-
if (!
|
|
49209
|
+
const path = join20(stateDir, TURN_ACTIVE_MARKER_FILE);
|
|
49210
|
+
if (!existsSync23(path))
|
|
49156
49211
|
return;
|
|
49157
49212
|
const now = new Date;
|
|
49158
49213
|
try {
|
|
@@ -49187,7 +49242,7 @@ function backfillJsonlAgentId(db2, jsonlPath, agentId, log) {
|
|
|
49187
49242
|
const metaPath = jsonlPath.replace(/\.jsonl$/, ".meta.json");
|
|
49188
49243
|
let meta;
|
|
49189
49244
|
try {
|
|
49190
|
-
const raw =
|
|
49245
|
+
const raw = readFileSync22(metaPath, "utf8");
|
|
49191
49246
|
meta = JSON.parse(raw);
|
|
49192
49247
|
} catch {
|
|
49193
49248
|
log?.(`subagent-watcher: backfill skip ${agentId} \u2014 meta.json not readable at ${metaPath}`);
|
|
@@ -49261,6 +49316,12 @@ function readSubTail(entry, tail, now, onDescriptionUpdate, fs2, log, db2, paren
|
|
|
49261
49316
|
for (const line of lines) {
|
|
49262
49317
|
if (!line)
|
|
49263
49318
|
continue;
|
|
49319
|
+
const errInfo = detectErrorInTranscriptLine(line);
|
|
49320
|
+
if (errInfo?.terminal) {
|
|
49321
|
+
entry.errored = true;
|
|
49322
|
+
if (errInfo.detail)
|
|
49323
|
+
entry.errorDetail = errInfo.detail.slice(0, SUBAGENT_RESULT_TEXT_MAX);
|
|
49324
|
+
}
|
|
49264
49325
|
const events = projectSubagentLine(line, entry.agentId, startState);
|
|
49265
49326
|
for (const ev of events) {
|
|
49266
49327
|
const idleSecBeforeBump = Math.round((now - entry.lastActivityAt) / 1000);
|
|
@@ -49325,7 +49386,7 @@ function readSubTail(entry, tail, now, onDescriptionUpdate, fs2, log, db2, paren
|
|
|
49325
49386
|
recordSubagentEnd(db2, {
|
|
49326
49387
|
id: rowRef.id,
|
|
49327
49388
|
endedAt: now,
|
|
49328
|
-
status: "completed"
|
|
49389
|
+
status: entry.errored ? "failed" : "completed"
|
|
49329
49390
|
});
|
|
49330
49391
|
}
|
|
49331
49392
|
} catch (dbErr) {
|
|
@@ -49376,7 +49437,7 @@ function startSubagentWatcher(config) {
|
|
|
49376
49437
|
clearTimeout(ref.ref);
|
|
49377
49438
|
});
|
|
49378
49439
|
const fs2 = config.fs ?? {
|
|
49379
|
-
existsSync:
|
|
49440
|
+
existsSync: existsSync24,
|
|
49380
49441
|
readdirSync: readdirSync3,
|
|
49381
49442
|
statSync: statSync6,
|
|
49382
49443
|
openSync: openSync2,
|
|
@@ -49435,6 +49496,17 @@ function startSubagentWatcher(config) {
|
|
|
49435
49496
|
readSubTail(entry, tail, n, (desc) => {
|
|
49436
49497
|
log?.(`subagent-watcher: description updated for ${agentId}: ${desc}`);
|
|
49437
49498
|
}, fs2, log, db2, parentStateDir, config.onUnstall, undefined, config.onProgress);
|
|
49499
|
+
if (isHistorical && entry.state === "running") {
|
|
49500
|
+
entry.historical = false;
|
|
49501
|
+
log?.(`subagent-watcher: ${agentId} was in-flight at boot \u2014 promoting to live (predates watcher; user still awaiting handback)`);
|
|
49502
|
+
if (db2 != null) {
|
|
49503
|
+
try {
|
|
49504
|
+
backfillJsonlAgentId(db2, filePath, agentId, log);
|
|
49505
|
+
} catch (err) {
|
|
49506
|
+
log?.(`subagent-watcher: backfill error for ${agentId}: ${err.message}`);
|
|
49507
|
+
}
|
|
49508
|
+
}
|
|
49509
|
+
}
|
|
49438
49510
|
if (isHistorical && entry.state === "done") {
|
|
49439
49511
|
entry.completionNotified = true;
|
|
49440
49512
|
scheduleTerminalCleanup(agentId);
|
|
@@ -49469,11 +49541,11 @@ function startSubagentWatcher(config) {
|
|
|
49469
49541
|
config.onFinish({
|
|
49470
49542
|
agentId,
|
|
49471
49543
|
state: entry.state,
|
|
49472
|
-
outcome: entry.historical ? "orphan" : "completed",
|
|
49544
|
+
outcome: entry.errored ? "failed" : entry.historical ? "orphan" : "completed",
|
|
49473
49545
|
toolCount: entry.toolCount,
|
|
49474
49546
|
durationMs: nowFn() - entry.dispatchedAt,
|
|
49475
49547
|
description: entry.description,
|
|
49476
|
-
resultText: entry.lastResultText
|
|
49548
|
+
resultText: entry.errored ? entry.lastResultText || entry.errorDetail || "" : entry.lastResultText
|
|
49477
49549
|
});
|
|
49478
49550
|
} catch (cbErr) {
|
|
49479
49551
|
log?.(`subagent-watcher: onFinish callback error ${agentId}: ${cbErr.message}`);
|
|
@@ -49590,7 +49662,7 @@ function startSubagentWatcher(config) {
|
|
|
49590
49662
|
recordSubagentEnd(db2, {
|
|
49591
49663
|
id: rowRef.id,
|
|
49592
49664
|
endedAt: n,
|
|
49593
|
-
status: "completed"
|
|
49665
|
+
status: entry.errored ? "failed" : "completed"
|
|
49594
49666
|
});
|
|
49595
49667
|
}
|
|
49596
49668
|
} catch (dbErr) {
|
|
@@ -49610,8 +49682,8 @@ function startSubagentWatcher(config) {
|
|
|
49610
49682
|
function rescanSubagentDirs() {
|
|
49611
49683
|
if (stopped)
|
|
49612
49684
|
return;
|
|
49613
|
-
const claudeHome =
|
|
49614
|
-
const projectsRoot =
|
|
49685
|
+
const claudeHome = join21(agentDir, ".claude");
|
|
49686
|
+
const projectsRoot = join21(claudeHome, "projects");
|
|
49615
49687
|
if (!fs2.existsSync(projectsRoot))
|
|
49616
49688
|
return;
|
|
49617
49689
|
let projectDirs;
|
|
@@ -49628,7 +49700,7 @@ function startSubagentWatcher(config) {
|
|
|
49628
49700
|
}
|
|
49629
49701
|
continue;
|
|
49630
49702
|
}
|
|
49631
|
-
const projectPath =
|
|
49703
|
+
const projectPath = join21(projectsRoot, pDir);
|
|
49632
49704
|
let sessionDirs;
|
|
49633
49705
|
try {
|
|
49634
49706
|
sessionDirs = fs2.readdirSync(projectPath);
|
|
@@ -49638,7 +49710,7 @@ function startSubagentWatcher(config) {
|
|
|
49638
49710
|
for (const sDir of sessionDirs) {
|
|
49639
49711
|
if (sDir.endsWith(".jsonl"))
|
|
49640
49712
|
continue;
|
|
49641
|
-
const subagentsPath =
|
|
49713
|
+
const subagentsPath = join21(projectPath, sDir, "subagents");
|
|
49642
49714
|
if (!fs2.existsSync(subagentsPath))
|
|
49643
49715
|
continue;
|
|
49644
49716
|
if (!dirWatchers.has(subagentsPath)) {
|
|
@@ -49646,7 +49718,7 @@ function startSubagentWatcher(config) {
|
|
|
49646
49718
|
const w = fs2.watch(subagentsPath, (_event, filename) => {
|
|
49647
49719
|
if (!filename || !filename.toString().startsWith("agent-") || !filename.toString().endsWith(".jsonl"))
|
|
49648
49720
|
return;
|
|
49649
|
-
const filePath =
|
|
49721
|
+
const filePath = join21(subagentsPath, filename.toString());
|
|
49650
49722
|
if (!knownFiles.has(filePath)) {
|
|
49651
49723
|
scanSubagentsDir(subagentsPath);
|
|
49652
49724
|
}
|
|
@@ -49671,7 +49743,7 @@ function startSubagentWatcher(config) {
|
|
|
49671
49743
|
for (const e of entries) {
|
|
49672
49744
|
if (!e.startsWith("agent-") || !e.endsWith(".jsonl"))
|
|
49673
49745
|
continue;
|
|
49674
|
-
const filePath =
|
|
49746
|
+
const filePath = join21(subagentsPath, e);
|
|
49675
49747
|
if (knownFiles.has(filePath))
|
|
49676
49748
|
continue;
|
|
49677
49749
|
const agentId = e.slice("agent-".length, -".jsonl".length);
|
|
@@ -49788,15 +49860,15 @@ function determineRestartReason(opts) {
|
|
|
49788
49860
|
init_boot_card();
|
|
49789
49861
|
|
|
49790
49862
|
// gateway/update-announce.ts
|
|
49791
|
-
import { existsSync as
|
|
49792
|
-
import { join as
|
|
49863
|
+
import { existsSync as existsSync29, mkdirSync as mkdirSync17, openSync as openSync3, closeSync as closeSync3, readFileSync as readFileSync27 } from "node:fs";
|
|
49864
|
+
import { join as join26 } from "node:path";
|
|
49793
49865
|
import { homedir as homedir12 } from "node:os";
|
|
49794
49866
|
|
|
49795
49867
|
// ../src/host-control/audit-reader.ts
|
|
49796
49868
|
import { homedir as homedir11 } from "node:os";
|
|
49797
|
-
import { join as
|
|
49869
|
+
import { join as join25 } from "node:path";
|
|
49798
49870
|
function defaultAuditLogPath(home2 = homedir11()) {
|
|
49799
|
-
return
|
|
49871
|
+
return join25(home2, ".switchroom", "host-control-audit.log");
|
|
49800
49872
|
}
|
|
49801
49873
|
function parseAuditLine(line) {
|
|
49802
49874
|
const trimmed = line.trim();
|
|
@@ -49902,8 +49974,8 @@ function readAndFilter(raw, filters, limit) {
|
|
|
49902
49974
|
var DEFAULT_LOOKBACK_MS = 10 * 60 * 1000;
|
|
49903
49975
|
function readLastTerminalUpdateAudit(opts = {}) {
|
|
49904
49976
|
const path = opts.auditLogPath ?? defaultAuditLogPath();
|
|
49905
|
-
const exists = opts.exists ??
|
|
49906
|
-
const readFile = opts.readFile ?? ((p) =>
|
|
49977
|
+
const exists = opts.exists ?? existsSync29;
|
|
49978
|
+
const readFile = opts.readFile ?? ((p) => readFileSync27(p, "utf-8"));
|
|
49907
49979
|
if (!exists(path))
|
|
49908
49980
|
return null;
|
|
49909
49981
|
let raw;
|
|
@@ -49964,15 +50036,15 @@ function renderUpdateOutcomeLine(entry) {
|
|
|
49964
50036
|
`);
|
|
49965
50037
|
}
|
|
49966
50038
|
function claimUpdateAnnouncement(requestId, opts = {}) {
|
|
49967
|
-
const stateDir = opts.stateDir ?? process.env.TELEGRAM_STATE_DIR ??
|
|
49968
|
-
const dir =
|
|
50039
|
+
const stateDir = opts.stateDir ?? process.env.TELEGRAM_STATE_DIR ?? join26(homedir12(), ".switchroom");
|
|
50040
|
+
const dir = join26(stateDir, "update-announced");
|
|
49969
50041
|
try {
|
|
49970
50042
|
mkdirSync17(dir, { recursive: true });
|
|
49971
50043
|
} catch {
|
|
49972
50044
|
return false;
|
|
49973
50045
|
}
|
|
49974
50046
|
const safeId = requestId.replace(/[^A-Za-z0-9_.-]/g, "_").slice(0, 200);
|
|
49975
|
-
const path =
|
|
50047
|
+
const path = join26(dir, safeId);
|
|
49976
50048
|
try {
|
|
49977
50049
|
const fd = openSync3(path, "wx");
|
|
49978
50050
|
closeSync3(fd);
|
|
@@ -49991,7 +50063,7 @@ function maybeRenderUpdateAnnouncement(opts = {}) {
|
|
|
49991
50063
|
}
|
|
49992
50064
|
|
|
49993
50065
|
// issues-card.ts
|
|
49994
|
-
import { readFileSync as
|
|
50066
|
+
import { readFileSync as readFileSync28, writeFileSync as writeFileSync17 } from "node:fs";
|
|
49995
50067
|
var SEVERITY_EMOJI = {
|
|
49996
50068
|
info: "\u2139\ufe0f",
|
|
49997
50069
|
warn: "\u26a0\ufe0f",
|
|
@@ -50083,7 +50155,7 @@ function extractRetryAfterSecs2(err) {
|
|
|
50083
50155
|
var COOLDOWN_JITTER_MS2 = 500;
|
|
50084
50156
|
function readPersistedMessageId(path, log) {
|
|
50085
50157
|
try {
|
|
50086
|
-
const raw =
|
|
50158
|
+
const raw = readFileSync28(path, "utf8");
|
|
50087
50159
|
const parsed = JSON.parse(raw);
|
|
50088
50160
|
const v = parsed.messageId;
|
|
50089
50161
|
if (typeof v === "number" && Number.isInteger(v) && v > 0)
|
|
@@ -50099,7 +50171,7 @@ function readPersistedMessageId(path, log) {
|
|
|
50099
50171
|
}
|
|
50100
50172
|
function writePersistedMessageId(path, messageId, log) {
|
|
50101
50173
|
try {
|
|
50102
|
-
|
|
50174
|
+
writeFileSync17(path, JSON.stringify({ messageId }) + `
|
|
50103
50175
|
`, { mode: 384 });
|
|
50104
50176
|
} catch (err) {
|
|
50105
50177
|
log(`issues-card: persist write failed (${err.message})`);
|
|
@@ -50192,24 +50264,24 @@ function createIssuesCardHandle(opts) {
|
|
|
50192
50264
|
}
|
|
50193
50265
|
|
|
50194
50266
|
// issues-watcher.ts
|
|
50195
|
-
import { existsSync as
|
|
50196
|
-
import { join as
|
|
50267
|
+
import { existsSync as existsSync31, statSync as statSync8 } from "node:fs";
|
|
50268
|
+
import { join as join28 } from "node:path";
|
|
50197
50269
|
|
|
50198
50270
|
// ../src/issues/store.ts
|
|
50199
50271
|
import {
|
|
50200
50272
|
closeSync as closeSync4,
|
|
50201
|
-
existsSync as
|
|
50273
|
+
existsSync as existsSync30,
|
|
50202
50274
|
mkdirSync as mkdirSync18,
|
|
50203
50275
|
openSync as openSync4,
|
|
50204
50276
|
readdirSync as readdirSync5,
|
|
50205
|
-
readFileSync as
|
|
50206
|
-
renameSync as
|
|
50277
|
+
readFileSync as readFileSync29,
|
|
50278
|
+
renameSync as renameSync10,
|
|
50207
50279
|
statSync as statSync7,
|
|
50208
|
-
unlinkSync as
|
|
50209
|
-
writeFileSync as
|
|
50280
|
+
unlinkSync as unlinkSync10,
|
|
50281
|
+
writeFileSync as writeFileSync18,
|
|
50210
50282
|
writeSync
|
|
50211
50283
|
} from "node:fs";
|
|
50212
|
-
import { join as
|
|
50284
|
+
import { join as join27 } from "node:path";
|
|
50213
50285
|
import { randomBytes as randomBytes4 } from "node:crypto";
|
|
50214
50286
|
import { execSync } from "node:child_process";
|
|
50215
50287
|
|
|
@@ -50225,12 +50297,12 @@ var SEVERITY_RANK2 = {
|
|
|
50225
50297
|
var ISSUES_FILE = "issues.jsonl";
|
|
50226
50298
|
var ISSUES_LOCK = "issues.lock";
|
|
50227
50299
|
function readAll(stateDir) {
|
|
50228
|
-
const path =
|
|
50229
|
-
if (!
|
|
50300
|
+
const path = join27(stateDir, ISSUES_FILE);
|
|
50301
|
+
if (!existsSync30(path))
|
|
50230
50302
|
return [];
|
|
50231
50303
|
let raw;
|
|
50232
50304
|
try {
|
|
50233
|
-
raw =
|
|
50305
|
+
raw = readFileSync29(path, "utf-8");
|
|
50234
50306
|
} catch {
|
|
50235
50307
|
return [];
|
|
50236
50308
|
}
|
|
@@ -50262,7 +50334,7 @@ function list(stateDir, opts = {}) {
|
|
|
50262
50334
|
});
|
|
50263
50335
|
}
|
|
50264
50336
|
function resolve6(stateDir, fingerprint, nowFn = Date.now) {
|
|
50265
|
-
if (!
|
|
50337
|
+
if (!existsSync30(join27(stateDir, ISSUES_FILE)))
|
|
50266
50338
|
return 0;
|
|
50267
50339
|
return withLock(stateDir, () => {
|
|
50268
50340
|
const all = readAll(stateDir);
|
|
@@ -50280,14 +50352,14 @@ function resolve6(stateDir, fingerprint, nowFn = Date.now) {
|
|
|
50280
50352
|
});
|
|
50281
50353
|
}
|
|
50282
50354
|
function writeAll(stateDir, events) {
|
|
50283
|
-
const path =
|
|
50355
|
+
const path = join27(stateDir, ISSUES_FILE);
|
|
50284
50356
|
sweepOrphanTmpFiles(stateDir);
|
|
50285
50357
|
const tmp = `${path}.tmp-${process.pid}-${randomBytes4(4).toString("hex")}`;
|
|
50286
50358
|
const body = events.length === 0 ? "" : events.map((e) => JSON.stringify(e)).join(`
|
|
50287
50359
|
`) + `
|
|
50288
50360
|
`;
|
|
50289
|
-
|
|
50290
|
-
|
|
50361
|
+
writeFileSync18(tmp, body, "utf-8");
|
|
50362
|
+
renameSync10(tmp, path);
|
|
50291
50363
|
}
|
|
50292
50364
|
var ORPHAN_TMP_TTL_MS = 60000;
|
|
50293
50365
|
var TMP_PREFIX = `${ISSUES_FILE}.tmp-`;
|
|
@@ -50302,11 +50374,11 @@ function sweepOrphanTmpFiles(stateDir) {
|
|
|
50302
50374
|
for (const entry of entries) {
|
|
50303
50375
|
if (!entry.startsWith(TMP_PREFIX))
|
|
50304
50376
|
continue;
|
|
50305
|
-
const tmpPath2 =
|
|
50377
|
+
const tmpPath2 = join27(stateDir, entry);
|
|
50306
50378
|
try {
|
|
50307
50379
|
const stat = statSync7(tmpPath2);
|
|
50308
50380
|
if (stat.mtimeMs < cutoff) {
|
|
50309
|
-
|
|
50381
|
+
unlinkSync10(tmpPath2);
|
|
50310
50382
|
}
|
|
50311
50383
|
} catch {}
|
|
50312
50384
|
}
|
|
@@ -50314,7 +50386,7 @@ function sweepOrphanTmpFiles(stateDir) {
|
|
|
50314
50386
|
var LOCK_RETRY_MS = 25;
|
|
50315
50387
|
var LOCK_TIMEOUT_MS = 1e4;
|
|
50316
50388
|
function withLock(stateDir, fn) {
|
|
50317
|
-
const lockPath =
|
|
50389
|
+
const lockPath = join27(stateDir, ISSUES_LOCK);
|
|
50318
50390
|
const startedAt = Date.now();
|
|
50319
50391
|
let fd = null;
|
|
50320
50392
|
while (fd === null) {
|
|
@@ -50342,27 +50414,27 @@ function withLock(stateDir, fn) {
|
|
|
50342
50414
|
closeSync4(fd);
|
|
50343
50415
|
} catch {}
|
|
50344
50416
|
try {
|
|
50345
|
-
|
|
50417
|
+
unlinkSync10(lockPath);
|
|
50346
50418
|
} catch {}
|
|
50347
50419
|
}
|
|
50348
50420
|
}
|
|
50349
50421
|
function tryStealStaleLock(lockPath) {
|
|
50350
50422
|
let pidStr;
|
|
50351
50423
|
try {
|
|
50352
|
-
pidStr =
|
|
50424
|
+
pidStr = readFileSync29(lockPath, "utf-8").trim();
|
|
50353
50425
|
} catch {
|
|
50354
50426
|
return true;
|
|
50355
50427
|
}
|
|
50356
50428
|
const pid = Number(pidStr);
|
|
50357
50429
|
if (!Number.isFinite(pid) || pid <= 0) {
|
|
50358
50430
|
try {
|
|
50359
|
-
|
|
50431
|
+
unlinkSync10(lockPath);
|
|
50360
50432
|
} catch {}
|
|
50361
50433
|
return true;
|
|
50362
50434
|
}
|
|
50363
50435
|
if (pid === process.pid) {
|
|
50364
50436
|
try {
|
|
50365
|
-
|
|
50437
|
+
unlinkSync10(lockPath);
|
|
50366
50438
|
} catch {}
|
|
50367
50439
|
return true;
|
|
50368
50440
|
}
|
|
@@ -50377,7 +50449,7 @@ function tryStealStaleLock(lockPath) {
|
|
|
50377
50449
|
return false;
|
|
50378
50450
|
}
|
|
50379
50451
|
try {
|
|
50380
|
-
|
|
50452
|
+
unlinkSync10(lockPath);
|
|
50381
50453
|
} catch {}
|
|
50382
50454
|
return true;
|
|
50383
50455
|
}
|
|
@@ -50399,7 +50471,7 @@ function isIssueEvent(v) {
|
|
|
50399
50471
|
// issues-watcher.ts
|
|
50400
50472
|
var DEFAULT_POLL_INTERVAL_MS2 = 2000;
|
|
50401
50473
|
function startIssuesWatcher(opts) {
|
|
50402
|
-
const path =
|
|
50474
|
+
const path = join28(opts.stateDir, ISSUES_FILE);
|
|
50403
50475
|
const log = opts.log ?? (() => {});
|
|
50404
50476
|
const intervalMs = opts.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS2;
|
|
50405
50477
|
const setIntervalFn = opts.setInterval ?? setInterval;
|
|
@@ -50447,7 +50519,7 @@ function startIssuesWatcher(opts) {
|
|
|
50447
50519
|
};
|
|
50448
50520
|
}
|
|
50449
50521
|
function defaultSignatureProvider(path) {
|
|
50450
|
-
if (!
|
|
50522
|
+
if (!existsSync31(path))
|
|
50451
50523
|
return null;
|
|
50452
50524
|
try {
|
|
50453
50525
|
const stat = statSync8(path);
|
|
@@ -51052,8 +51124,8 @@ function extractFlowItems(line) {
|
|
|
51052
51124
|
}
|
|
51053
51125
|
|
|
51054
51126
|
// credits-watch.ts
|
|
51055
|
-
import { readFileSync as
|
|
51056
|
-
import { join as
|
|
51127
|
+
import { readFileSync as readFileSync30, writeFileSync as writeFileSync19, existsSync as existsSync32, mkdirSync as mkdirSync19 } from "fs";
|
|
51128
|
+
import { join as join29 } from "path";
|
|
51057
51129
|
var STATE_FILE = "credits-watch.json";
|
|
51058
51130
|
var FATAL_REASONS = new Set([
|
|
51059
51131
|
"out_of_credits",
|
|
@@ -51065,12 +51137,12 @@ function emptyCreditState() {
|
|
|
51065
51137
|
return { lastNotifiedReason: null, lastNotifiedAt: 0 };
|
|
51066
51138
|
}
|
|
51067
51139
|
function readClaudeJsonOverage(claudeConfigDir) {
|
|
51068
|
-
const path =
|
|
51069
|
-
if (!
|
|
51140
|
+
const path = join29(claudeConfigDir, ".claude.json");
|
|
51141
|
+
if (!existsSync32(path))
|
|
51070
51142
|
return null;
|
|
51071
51143
|
let raw;
|
|
51072
51144
|
try {
|
|
51073
|
-
raw =
|
|
51145
|
+
raw = readFileSync30(path, "utf-8");
|
|
51074
51146
|
} catch {
|
|
51075
51147
|
return null;
|
|
51076
51148
|
}
|
|
@@ -51094,7 +51166,7 @@ function evaluateCreditState(args) {
|
|
|
51094
51166
|
if (!currentIsFatal && prevIsFatal) {
|
|
51095
51167
|
return {
|
|
51096
51168
|
kind: "notify",
|
|
51097
|
-
message: `\u2705 <b>${
|
|
51169
|
+
message: `\u2705 <b>${escapeHtml9(agentName3)}</b>: credits restored \u2014 agent should resume normal operation.`,
|
|
51098
51170
|
newState: { lastNotifiedReason: null, lastNotifiedAt: now },
|
|
51099
51171
|
transition: "exited"
|
|
51100
51172
|
};
|
|
@@ -51123,12 +51195,12 @@ function evaluateCreditState(args) {
|
|
|
51123
51195
|
function buildEntryMessage(agentName3, reason) {
|
|
51124
51196
|
const desc = humanizeReason(reason);
|
|
51125
51197
|
return [
|
|
51126
|
-
`\u26a0\ufe0f <b>${
|
|
51198
|
+
`\u26a0\ufe0f <b>${escapeHtml9(agentName3)}</b>: ${desc}`,
|
|
51127
51199
|
``,
|
|
51128
51200
|
`Cron tasks and inbound replies will fail until this is resolved. Check`,
|
|
51129
51201
|
`your subscription or pre-paid usage at <a href="https://console.anthropic.com">console.anthropic.com</a>.`,
|
|
51130
51202
|
``,
|
|
51131
|
-
`<i>Source: Claude CLI cache (cachedExtraUsageDisabledReason=${
|
|
51203
|
+
`<i>Source: Claude CLI cache (cachedExtraUsageDisabledReason=${escapeHtml9(reason)})</i>`
|
|
51132
51204
|
].join(`
|
|
51133
51205
|
`);
|
|
51134
51206
|
}
|
|
@@ -51146,15 +51218,15 @@ function humanizeReason(reason) {
|
|
|
51146
51218
|
return `usage disabled (${reason})`;
|
|
51147
51219
|
}
|
|
51148
51220
|
}
|
|
51149
|
-
function
|
|
51221
|
+
function escapeHtml9(s) {
|
|
51150
51222
|
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
51151
51223
|
}
|
|
51152
51224
|
function loadCreditState(stateDir) {
|
|
51153
|
-
const path =
|
|
51154
|
-
if (!
|
|
51225
|
+
const path = join29(stateDir, STATE_FILE);
|
|
51226
|
+
if (!existsSync32(path))
|
|
51155
51227
|
return emptyCreditState();
|
|
51156
51228
|
try {
|
|
51157
|
-
const raw =
|
|
51229
|
+
const raw = readFileSync30(path, "utf-8");
|
|
51158
51230
|
const parsed = JSON.parse(raw);
|
|
51159
51231
|
if (parsed && typeof parsed === "object" && (parsed.lastNotifiedReason === null || typeof parsed.lastNotifiedReason === "string") && typeof parsed.lastNotifiedAt === "number" && Number.isFinite(parsed.lastNotifiedAt)) {
|
|
51160
51232
|
return {
|
|
@@ -51167,14 +51239,14 @@ function loadCreditState(stateDir) {
|
|
|
51167
51239
|
}
|
|
51168
51240
|
function saveCreditState(stateDir, state4) {
|
|
51169
51241
|
mkdirSync19(stateDir, { recursive: true });
|
|
51170
|
-
const path =
|
|
51171
|
-
|
|
51242
|
+
const path = join29(stateDir, STATE_FILE);
|
|
51243
|
+
writeFileSync19(path, JSON.stringify(state4, null, 2) + `
|
|
51172
51244
|
`, { mode: 384 });
|
|
51173
51245
|
}
|
|
51174
51246
|
|
|
51175
51247
|
// quota-watch.ts
|
|
51176
|
-
import { readFileSync as
|
|
51177
|
-
import { join as
|
|
51248
|
+
import { readFileSync as readFileSync31, writeFileSync as writeFileSync20, existsSync as existsSync33, mkdirSync as mkdirSync20 } from "fs";
|
|
51249
|
+
import { join as join30 } from "path";
|
|
51178
51250
|
var STATE_FILE2 = "quota-watch.json";
|
|
51179
51251
|
function emptyQuotaWatchState() {
|
|
51180
51252
|
return {};
|
|
@@ -51231,11 +51303,11 @@ function buildThrottlingMessage(agentName3, snap) {
|
|
|
51231
51303
|
const resetAt = win === "5h" ? q.fiveHourResetAt : q.sevenDayResetAt;
|
|
51232
51304
|
const resetStr = resetAt ? ` \u00b7 refills in ${formatRelative(resetAt, new Date)}` : "";
|
|
51233
51305
|
const activeNote = snap.isActive ? "" : `
|
|
51234
|
-
This is a non-active account. Consider <code>/auth use ${
|
|
51306
|
+
This is a non-active account. Consider <code>/auth use ${escapeHtml10(snap.label)}</code> to switch, or keep it as a fallback reserve.`;
|
|
51235
51307
|
const altNote = snap.isActive ? `
|
|
51236
51308
|
Consider <code>/auth use <other-account></code> if you have a healthier account, or wait for the ${winLabel} window to refill${resetStr}.` : "";
|
|
51237
51309
|
return [
|
|
51238
|
-
`\uD83D\uDFE1 <b>Quota approaching limit</b> \u2014 <code>${
|
|
51310
|
+
`\uD83D\uDFE1 <b>Quota approaching limit</b> \u2014 <code>${escapeHtml10(snap.label)}</code>`,
|
|
51239
51311
|
``,
|
|
51240
51312
|
`${fiveStr} of 5h \u00b7 ${sevenStr} of 7d`,
|
|
51241
51313
|
`Binding window: ${winLabel}${resetStr}`,
|
|
@@ -51252,7 +51324,7 @@ function buildRecoveryMessage(agentName3, snap) {
|
|
|
51252
51324
|
const q = snap.quota;
|
|
51253
51325
|
const utilLine = q ? `Current: ${fmtPct(q.fiveHourUtilizationPct)} of 5h \u00b7 ${fmtPct(q.sevenDayUtilizationPct)} of 7d` : "Current quota data unavailable.";
|
|
51254
51326
|
return [
|
|
51255
|
-
`\uD83D\uDFE2 <b>Quota back in healthy range</b> \u2014 <code>${
|
|
51327
|
+
`\uD83D\uDFE2 <b>Quota back in healthy range</b> \u2014 <code>${escapeHtml10(snap.label)}</code>`,
|
|
51256
51328
|
``,
|
|
51257
51329
|
utilLine,
|
|
51258
51330
|
``,
|
|
@@ -51260,15 +51332,15 @@ function buildRecoveryMessage(agentName3, snap) {
|
|
|
51260
51332
|
].join(`
|
|
51261
51333
|
`);
|
|
51262
51334
|
}
|
|
51263
|
-
function
|
|
51335
|
+
function escapeHtml10(s) {
|
|
51264
51336
|
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
51265
51337
|
}
|
|
51266
51338
|
function loadQuotaWatchState(stateDir) {
|
|
51267
|
-
const path =
|
|
51268
|
-
if (!
|
|
51339
|
+
const path = join30(stateDir, STATE_FILE2);
|
|
51340
|
+
if (!existsSync33(path))
|
|
51269
51341
|
return emptyQuotaWatchState();
|
|
51270
51342
|
try {
|
|
51271
|
-
const raw =
|
|
51343
|
+
const raw = readFileSync31(path, "utf-8");
|
|
51272
51344
|
const parsed = JSON.parse(raw);
|
|
51273
51345
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
51274
51346
|
return emptyQuotaWatchState();
|
|
@@ -51286,8 +51358,8 @@ function loadQuotaWatchState(stateDir) {
|
|
|
51286
51358
|
}
|
|
51287
51359
|
function saveQuotaWatchState(stateDir, state4) {
|
|
51288
51360
|
mkdirSync20(stateDir, { recursive: true });
|
|
51289
|
-
const path =
|
|
51290
|
-
|
|
51361
|
+
const path = join30(stateDir, STATE_FILE2);
|
|
51362
|
+
writeFileSync20(path, JSON.stringify(state4, null, 2) + `
|
|
51291
51363
|
`, { mode: 384 });
|
|
51292
51364
|
}
|
|
51293
51365
|
function patchQuotaWatchState(current, accountLabel, accountState) {
|
|
@@ -51300,27 +51372,27 @@ init_auth_snapshot_format();
|
|
|
51300
51372
|
// gateway/turn-active-marker.ts
|
|
51301
51373
|
import {
|
|
51302
51374
|
closeSync as closeSync5,
|
|
51303
|
-
existsSync as
|
|
51375
|
+
existsSync as existsSync34,
|
|
51304
51376
|
mkdirSync as mkdirSync21,
|
|
51305
51377
|
openSync as openSync5,
|
|
51306
|
-
readFileSync as
|
|
51378
|
+
readFileSync as readFileSync32,
|
|
51307
51379
|
statSync as statSync9,
|
|
51308
|
-
unlinkSync as
|
|
51380
|
+
unlinkSync as unlinkSync11,
|
|
51309
51381
|
utimesSync as utimesSync2,
|
|
51310
|
-
writeFileSync as
|
|
51382
|
+
writeFileSync as writeFileSync21
|
|
51311
51383
|
} from "node:fs";
|
|
51312
|
-
import { join as
|
|
51384
|
+
import { join as join31 } from "node:path";
|
|
51313
51385
|
var TURN_ACTIVE_MARKER_FILE2 = "turn-active.json";
|
|
51314
51386
|
function writeTurnActiveMarker(stateDir, marker) {
|
|
51315
51387
|
try {
|
|
51316
51388
|
mkdirSync21(stateDir, { recursive: true });
|
|
51317
|
-
|
|
51389
|
+
writeFileSync21(join31(stateDir, TURN_ACTIVE_MARKER_FILE2), JSON.stringify(marker, null, 2) + `
|
|
51318
51390
|
`, { mode: 384 });
|
|
51319
51391
|
} catch {}
|
|
51320
51392
|
}
|
|
51321
51393
|
function touchTurnActiveMarker2(stateDir) {
|
|
51322
|
-
const path =
|
|
51323
|
-
if (!
|
|
51394
|
+
const path = join31(stateDir, TURN_ACTIVE_MARKER_FILE2);
|
|
51395
|
+
if (!existsSync34(path))
|
|
51324
51396
|
return;
|
|
51325
51397
|
const now = new Date;
|
|
51326
51398
|
try {
|
|
@@ -51334,12 +51406,12 @@ function touchTurnActiveMarker2(stateDir) {
|
|
|
51334
51406
|
}
|
|
51335
51407
|
function removeTurnActiveMarker(stateDir) {
|
|
51336
51408
|
try {
|
|
51337
|
-
|
|
51409
|
+
unlinkSync11(join31(stateDir, TURN_ACTIVE_MARKER_FILE2));
|
|
51338
51410
|
} catch {}
|
|
51339
51411
|
}
|
|
51340
51412
|
function sweepStaleTurnActiveMarker(stateDir, opts) {
|
|
51341
|
-
const path =
|
|
51342
|
-
if (!
|
|
51413
|
+
const path = join31(stateDir, TURN_ACTIVE_MARKER_FILE2);
|
|
51414
|
+
if (!existsSync34(path))
|
|
51343
51415
|
return false;
|
|
51344
51416
|
const now = opts.now ?? Date.now();
|
|
51345
51417
|
try {
|
|
@@ -51351,9 +51423,9 @@ function sweepStaleTurnActiveMarker(stateDir, opts) {
|
|
|
51351
51423
|
return false;
|
|
51352
51424
|
let payload = null;
|
|
51353
51425
|
try {
|
|
51354
|
-
payload =
|
|
51426
|
+
payload = readFileSync32(path, "utf8");
|
|
51355
51427
|
} catch {}
|
|
51356
|
-
|
|
51428
|
+
unlinkSync11(path);
|
|
51357
51429
|
if (opts.onRemove) {
|
|
51358
51430
|
try {
|
|
51359
51431
|
opts.onRemove({
|
|
@@ -51370,10 +51442,10 @@ function sweepStaleTurnActiveMarker(stateDir, opts) {
|
|
|
51370
51442
|
}
|
|
51371
51443
|
|
|
51372
51444
|
// ../src/build-info.ts
|
|
51373
|
-
var VERSION = "0.14.
|
|
51374
|
-
var COMMIT_SHA = "
|
|
51375
|
-
var COMMIT_DATE = "2026-05-
|
|
51376
|
-
var LATEST_PR =
|
|
51445
|
+
var VERSION = "0.14.23";
|
|
51446
|
+
var COMMIT_SHA = "8ac2987a";
|
|
51447
|
+
var COMMIT_DATE = "2026-05-31T22:03:26Z";
|
|
51448
|
+
var LATEST_PR = 2031;
|
|
51377
51449
|
var COMMITS_AHEAD_OF_TAG = 0;
|
|
51378
51450
|
|
|
51379
51451
|
// gateway/boot-version.ts
|
|
@@ -51447,11 +51519,11 @@ init_peercred();
|
|
|
51447
51519
|
import * as net5 from "node:net";
|
|
51448
51520
|
import * as fs2 from "node:fs";
|
|
51449
51521
|
import { homedir as homedir13 } from "node:os";
|
|
51450
|
-
import { join as
|
|
51522
|
+
import { join as join32 } from "node:path";
|
|
51451
51523
|
var DEFAULT_TIMEOUT_MS4 = 2000;
|
|
51452
51524
|
var UNLOCK_TIMEOUT_MS = 30000;
|
|
51453
|
-
var LEGACY_SOCKET_PATH2 =
|
|
51454
|
-
var OPERATOR_SOCKET_PATH2 =
|
|
51525
|
+
var LEGACY_SOCKET_PATH2 = join32(homedir13(), ".switchroom", "vault-broker.sock");
|
|
51526
|
+
var OPERATOR_SOCKET_PATH2 = join32(homedir13(), ".switchroom", "broker-operator", "sock");
|
|
51455
51527
|
function defaultBrokerSocketPath2() {
|
|
51456
51528
|
if (fs2.existsSync(OPERATOR_SOCKET_PATH2))
|
|
51457
51529
|
return OPERATOR_SOCKET_PATH2;
|
|
@@ -51674,7 +51746,7 @@ function resolveVaultApprovalPosture(broker) {
|
|
|
51674
51746
|
|
|
51675
51747
|
// registry/turns-schema.ts
|
|
51676
51748
|
import { chmodSync as chmodSync4, mkdirSync as mkdirSync22 } from "fs";
|
|
51677
|
-
import { join as
|
|
51749
|
+
import { join as join33 } from "path";
|
|
51678
51750
|
var DatabaseClass2 = null;
|
|
51679
51751
|
function loadDatabaseClass2() {
|
|
51680
51752
|
if (DatabaseClass2 != null)
|
|
@@ -51707,6 +51779,7 @@ var SCHEMA_SQL = `
|
|
|
51707
51779
|
user_prompt_preview TEXT,
|
|
51708
51780
|
assistant_reply_preview TEXT,
|
|
51709
51781
|
tool_call_count INTEGER,
|
|
51782
|
+
interrupt_reason TEXT,
|
|
51710
51783
|
created_at INTEGER NOT NULL,
|
|
51711
51784
|
updated_at INTEGER NOT NULL
|
|
51712
51785
|
);
|
|
@@ -51717,11 +51790,14 @@ var PHASE1_MIGRATIONS = [
|
|
|
51717
51790
|
`ALTER TABLE turns ADD COLUMN assistant_reply_preview TEXT`,
|
|
51718
51791
|
`ALTER TABLE turns ADD COLUMN tool_call_count INTEGER`
|
|
51719
51792
|
];
|
|
51793
|
+
var PHASE2_MIGRATIONS = [
|
|
51794
|
+
`ALTER TABLE turns ADD COLUMN interrupt_reason TEXT`
|
|
51795
|
+
];
|
|
51720
51796
|
function applySchema(db2) {
|
|
51721
51797
|
db2.exec("PRAGMA journal_mode = WAL");
|
|
51722
51798
|
db2.exec("PRAGMA synchronous = NORMAL");
|
|
51723
51799
|
db2.exec(SCHEMA_SQL);
|
|
51724
|
-
for (const sql of PHASE1_MIGRATIONS) {
|
|
51800
|
+
for (const sql of [...PHASE1_MIGRATIONS, ...PHASE2_MIGRATIONS]) {
|
|
51725
51801
|
try {
|
|
51726
51802
|
db2.exec(sql);
|
|
51727
51803
|
} catch (err) {
|
|
@@ -51733,9 +51809,9 @@ function applySchema(db2) {
|
|
|
51733
51809
|
}
|
|
51734
51810
|
function openTurnsDb(agentDir) {
|
|
51735
51811
|
const Database = loadDatabaseClass2();
|
|
51736
|
-
const dir =
|
|
51812
|
+
const dir = join33(agentDir, "telegram");
|
|
51737
51813
|
mkdirSync22(dir, { recursive: true, mode: 448 });
|
|
51738
|
-
const path =
|
|
51814
|
+
const path = join33(dir, "registry.db");
|
|
51739
51815
|
const db2 = new Database(path, { create: true });
|
|
51740
51816
|
applySchema(db2);
|
|
51741
51817
|
try {
|
|
@@ -51757,6 +51833,7 @@ function mapRow(row) {
|
|
|
51757
51833
|
user_prompt_preview: row.user_prompt_preview,
|
|
51758
51834
|
assistant_reply_preview: row.assistant_reply_preview,
|
|
51759
51835
|
tool_call_count: row.tool_call_count,
|
|
51836
|
+
interrupt_reason: row.interrupt_reason,
|
|
51760
51837
|
created_at: row.created_at,
|
|
51761
51838
|
updated_at: row.updated_at
|
|
51762
51839
|
};
|
|
@@ -51785,26 +51862,143 @@ function recordTurnEnd(db2, args) {
|
|
|
51785
51862
|
WHERE turn_key = ?
|
|
51786
51863
|
`).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
51864
|
}
|
|
51788
|
-
function
|
|
51789
|
-
const now = Date.now();
|
|
51790
|
-
const
|
|
51865
|
+
function markOrphanedWithTimeoutClassification(db2, opts) {
|
|
51866
|
+
const now = opts.now ?? Date.now();
|
|
51867
|
+
const isHang = opts.markerAgeMs != null && opts.markerAgeMs >= opts.hangThresholdMs && opts.markerTurnKey != null && opts.markerTurnKey.length > 0;
|
|
51868
|
+
let timeoutTurnKey = null;
|
|
51869
|
+
if (isHang) {
|
|
51870
|
+
const r = db2.prepare(`
|
|
51871
|
+
UPDATE turns
|
|
51872
|
+
SET ended_at = ?,
|
|
51873
|
+
ended_via = 'timeout',
|
|
51874
|
+
interrupt_reason = ?,
|
|
51875
|
+
updated_at = ?
|
|
51876
|
+
WHERE turn_key = ? AND ended_at IS NULL
|
|
51877
|
+
`).run(now, opts.reasonSnapshot ?? null, now, opts.markerTurnKey);
|
|
51878
|
+
if (r.changes > 0)
|
|
51879
|
+
timeoutTurnKey = opts.markerTurnKey ?? null;
|
|
51880
|
+
}
|
|
51881
|
+
const rest = db2.prepare(`
|
|
51791
51882
|
UPDATE turns
|
|
51792
51883
|
SET ended_at = ?,
|
|
51793
51884
|
ended_via = 'restart',
|
|
51794
51885
|
updated_at = ?
|
|
51795
51886
|
WHERE ended_at IS NULL
|
|
51796
51887
|
`).run(now, now);
|
|
51797
|
-
return
|
|
51888
|
+
return { reaped: (timeoutTurnKey ? 1 : 0) + rest.changes, timeoutTurnKey };
|
|
51798
51889
|
}
|
|
51799
|
-
|
|
51890
|
+
var INTERRUPTED_VIA = new Set([
|
|
51891
|
+
"restart",
|
|
51892
|
+
"sigterm",
|
|
51893
|
+
"timeout",
|
|
51894
|
+
"unknown"
|
|
51895
|
+
]);
|
|
51896
|
+
function findLatestTurnIfInterrupted(db2) {
|
|
51800
51897
|
const row = db2.prepare(`
|
|
51801
51898
|
SELECT * FROM turns
|
|
51802
|
-
WHERE ended_at IS NULL
|
|
51803
|
-
OR ended_via IN ('restart', 'sigterm', 'timeout')
|
|
51804
51899
|
ORDER BY started_at DESC
|
|
51805
51900
|
LIMIT 1
|
|
51806
51901
|
`).get();
|
|
51807
|
-
|
|
51902
|
+
if (!row)
|
|
51903
|
+
return null;
|
|
51904
|
+
const turn = mapRow(row);
|
|
51905
|
+
if (turn.ended_at == null)
|
|
51906
|
+
return turn;
|
|
51907
|
+
if (turn.ended_via != null && INTERRUPTED_VIA.has(turn.ended_via))
|
|
51908
|
+
return turn;
|
|
51909
|
+
return null;
|
|
51910
|
+
}
|
|
51911
|
+
|
|
51912
|
+
// gateway/resume-inbound-builder.ts
|
|
51913
|
+
function humanizeElapsed(ms) {
|
|
51914
|
+
if (!Number.isFinite(ms) || ms < 0)
|
|
51915
|
+
return "an unknown amount of time";
|
|
51916
|
+
const sec = Math.round(ms / 1000);
|
|
51917
|
+
if (sec < 45)
|
|
51918
|
+
return "moments";
|
|
51919
|
+
const min = Math.round(sec / 60);
|
|
51920
|
+
if (min < 60)
|
|
51921
|
+
return `~${min} min`;
|
|
51922
|
+
const hr = Math.round(min / 60);
|
|
51923
|
+
if (hr < 24)
|
|
51924
|
+
return `~${hr}h`;
|
|
51925
|
+
const days = Math.round(hr / 24);
|
|
51926
|
+
return `~${days} day${days === 1 ? "" : "s"}`;
|
|
51927
|
+
}
|
|
51928
|
+
function threadIdNum(turn) {
|
|
51929
|
+
if (turn.thread_id == null)
|
|
51930
|
+
return;
|
|
51931
|
+
const n = Number(turn.thread_id);
|
|
51932
|
+
return Number.isFinite(n) ? n : undefined;
|
|
51933
|
+
}
|
|
51934
|
+
function promptClause(turn) {
|
|
51935
|
+
const p = turn.user_prompt_preview?.trim();
|
|
51936
|
+
if (!p)
|
|
51937
|
+
return "";
|
|
51938
|
+
const snippet = p.length > 160 ? p.slice(0, 160) + "\u2026" : p;
|
|
51939
|
+
return ` The request was: "${snippet}".`;
|
|
51940
|
+
}
|
|
51941
|
+
function buildResumeInterruptedInbound(ctx) {
|
|
51942
|
+
const ts = ctx.nowMs ?? Date.now();
|
|
51943
|
+
const elapsed = humanizeElapsed(ts - ctx.turn.started_at);
|
|
51944
|
+
const meta = {
|
|
51945
|
+
source: "resume_interrupted",
|
|
51946
|
+
resume_turn_key: ctx.turn.turn_key,
|
|
51947
|
+
interrupted_via: ctx.turn.ended_via ?? "restart",
|
|
51948
|
+
started_at: String(ctx.turn.started_at)
|
|
51949
|
+
};
|
|
51950
|
+
if (ctx.turn.user_prompt_preview)
|
|
51951
|
+
meta.original_prompt = ctx.turn.user_prompt_preview;
|
|
51952
|
+
const threadId = threadIdNum(ctx.turn);
|
|
51953
|
+
return {
|
|
51954
|
+
type: "inbound",
|
|
51955
|
+
chatId: ctx.turn.chat_id,
|
|
51956
|
+
...threadId != null ? { threadId } : {},
|
|
51957
|
+
messageId: ts,
|
|
51958
|
+
user: "switchroom",
|
|
51959
|
+
userId: 0,
|
|
51960
|
+
ts,
|
|
51961
|
+
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.`,
|
|
51962
|
+
meta
|
|
51963
|
+
};
|
|
51964
|
+
}
|
|
51965
|
+
function buildResumeWatchdogReportInbound(ctx) {
|
|
51966
|
+
const ts = ctx.nowMs ?? Date.now();
|
|
51967
|
+
const idle = humanizeElapsed(ctx.idleMs);
|
|
51968
|
+
const since = humanizeElapsed(ts - ctx.turn.started_at);
|
|
51969
|
+
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.` : "";
|
|
51970
|
+
const meta = {
|
|
51971
|
+
source: "resume_watchdog_timeout",
|
|
51972
|
+
resume_turn_key: ctx.turn.turn_key,
|
|
51973
|
+
interrupted_via: "timeout",
|
|
51974
|
+
idle_ms: String(ctx.idleMs),
|
|
51975
|
+
started_at: String(ctx.turn.started_at)
|
|
51976
|
+
};
|
|
51977
|
+
if (ctx.turn.tool_call_count != null)
|
|
51978
|
+
meta.tool_call_count = String(ctx.turn.tool_call_count);
|
|
51979
|
+
if (ctx.turn.user_prompt_preview)
|
|
51980
|
+
meta.original_prompt = ctx.turn.user_prompt_preview;
|
|
51981
|
+
const threadId = threadIdNum(ctx.turn);
|
|
51982
|
+
return {
|
|
51983
|
+
type: "inbound",
|
|
51984
|
+
chatId: ctx.turn.chat_id,
|
|
51985
|
+
...threadId != null ? { threadId } : {},
|
|
51986
|
+
messageId: ts,
|
|
51987
|
+
user: "switchroom",
|
|
51988
|
+
userId: 0,
|
|
51989
|
+
ts,
|
|
51990
|
+
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.`,
|
|
51991
|
+
meta
|
|
51992
|
+
};
|
|
51993
|
+
}
|
|
51994
|
+
function selectResumeBuilder(endedVia) {
|
|
51995
|
+
if (endedVia === "timeout")
|
|
51996
|
+
return "report";
|
|
51997
|
+
if (endedVia === "restart" || endedVia === "sigterm" || endedVia === "unknown")
|
|
51998
|
+
return "resume";
|
|
51999
|
+
if (endedVia == null)
|
|
52000
|
+
return "resume";
|
|
52001
|
+
return null;
|
|
51808
52002
|
}
|
|
51809
52003
|
|
|
51810
52004
|
// registry/subagents-schema.ts
|
|
@@ -51904,11 +52098,11 @@ installGlobalErrorHandlers();
|
|
|
51904
52098
|
process.on("beforeExit", () => {
|
|
51905
52099
|
shutdownAnalytics();
|
|
51906
52100
|
});
|
|
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 =
|
|
52101
|
+
var STATE_DIR = process.env.TELEGRAM_STATE_DIR ?? join35(homedir14(), ".claude", "channels", "telegram");
|
|
52102
|
+
var ACCESS_FILE = join35(STATE_DIR, "access.json");
|
|
52103
|
+
var APPROVED_DIR = join35(STATE_DIR, "approved");
|
|
52104
|
+
var ENV_FILE = join35(STATE_DIR, ".env");
|
|
52105
|
+
var INBOX_DIR = join35(STATE_DIR, "inbox");
|
|
51912
52106
|
function triggerSelfRestart(targetAgent, reason, delayMs = 300) {
|
|
51913
52107
|
const isDocker = process.env.SWITCHROOM_RUNTIME === "docker";
|
|
51914
52108
|
const selfAgent = process.env.SWITCHROOM_AGENT_NAME;
|
|
@@ -51973,7 +52167,7 @@ function formatBootVersion() {
|
|
|
51973
52167
|
}
|
|
51974
52168
|
try {
|
|
51975
52169
|
chmodSync6(ENV_FILE, 384);
|
|
51976
|
-
for (const line of
|
|
52170
|
+
for (const line of readFileSync35(ENV_FILE, "utf8").split(`
|
|
51977
52171
|
`)) {
|
|
51978
52172
|
const m = line.match(/^(\w+)=(.*)$/);
|
|
51979
52173
|
if (m && process.env[m[1]] === undefined)
|
|
@@ -52026,7 +52220,7 @@ installTgPostLogger(bot);
|
|
|
52026
52220
|
var _rawSendMessageDraft = bot.api.raw.sendMessageDraft;
|
|
52027
52221
|
var GRAMMY_VERSION = (() => {
|
|
52028
52222
|
try {
|
|
52029
|
-
const raw =
|
|
52223
|
+
const raw = readFileSync35(new URL("../../node_modules/grammy/package.json", import.meta.url), "utf8");
|
|
52030
52224
|
return JSON.parse(raw).version ?? "unknown";
|
|
52031
52225
|
} catch {
|
|
52032
52226
|
return "unknown";
|
|
@@ -52099,7 +52293,7 @@ function assertSendable(f) {
|
|
|
52099
52293
|
} catch {
|
|
52100
52294
|
throw new Error(`refusing to send file \u2014 cannot resolve real path: ${f}`);
|
|
52101
52295
|
}
|
|
52102
|
-
const inbox =
|
|
52296
|
+
const inbox = join35(stateReal, "inbox");
|
|
52103
52297
|
if (real.startsWith(stateReal + sep3) && !real.startsWith(inbox + sep3)) {
|
|
52104
52298
|
throw new Error(`refusing to send channel state: ${f}`);
|
|
52105
52299
|
}
|
|
@@ -52118,7 +52312,7 @@ function assertSendable(f) {
|
|
|
52118
52312
|
}
|
|
52119
52313
|
function readAccessFile() {
|
|
52120
52314
|
try {
|
|
52121
|
-
const raw =
|
|
52315
|
+
const raw = readFileSync35(ACCESS_FILE, "utf8");
|
|
52122
52316
|
const parsed = JSON.parse(raw);
|
|
52123
52317
|
const allowFrom = validateStringArray("allowFrom", parsed.allowFrom ?? []);
|
|
52124
52318
|
const groups = {};
|
|
@@ -52155,7 +52349,7 @@ function readAccessFile() {
|
|
|
52155
52349
|
if (err.code === "ENOENT")
|
|
52156
52350
|
return defaultAccess();
|
|
52157
52351
|
try {
|
|
52158
|
-
|
|
52352
|
+
renameSync12(ACCESS_FILE, `${ACCESS_FILE}.corrupt-${Date.now()}`);
|
|
52159
52353
|
} catch {}
|
|
52160
52354
|
process.stderr.write(`telegram gateway: access.json is corrupt, moved aside. Starting fresh.
|
|
52161
52355
|
`);
|
|
@@ -52188,9 +52382,9 @@ function saveAccess(a) {
|
|
|
52188
52382
|
return;
|
|
52189
52383
|
mkdirSync26(STATE_DIR, { recursive: true, mode: 448 });
|
|
52190
52384
|
const tmp = ACCESS_FILE + ".tmp";
|
|
52191
|
-
|
|
52385
|
+
writeFileSync24(tmp, JSON.stringify(a, null, 2) + `
|
|
52192
52386
|
`, { mode: 384 });
|
|
52193
|
-
|
|
52387
|
+
renameSync12(tmp, ACCESS_FILE);
|
|
52194
52388
|
}
|
|
52195
52389
|
function pruneExpired(a) {
|
|
52196
52390
|
const now = Date.now();
|
|
@@ -52208,7 +52402,7 @@ var HISTORY_ENABLED = HISTORY_ACCESS.historyEnabled !== false;
|
|
|
52208
52402
|
if (HISTORY_ENABLED) {
|
|
52209
52403
|
try {
|
|
52210
52404
|
initHistory(STATE_DIR, HISTORY_ACCESS.historyRetentionDays ?? 30);
|
|
52211
|
-
process.stderr.write(`telegram gateway: history capture enabled at ${
|
|
52405
|
+
process.stderr.write(`telegram gateway: history capture enabled at ${join35(STATE_DIR, "history.db")}
|
|
52212
52406
|
`);
|
|
52213
52407
|
} catch (err) {
|
|
52214
52408
|
process.stderr.write(`telegram gateway: history init failed (${err.message}) \u2014 capture disabled
|
|
@@ -52216,21 +52410,71 @@ if (HISTORY_ENABLED) {
|
|
|
52216
52410
|
}
|
|
52217
52411
|
}
|
|
52218
52412
|
var turnsDb = null;
|
|
52413
|
+
var bootResumeInbound = null;
|
|
52219
52414
|
try {
|
|
52220
52415
|
const agentDir = STATE_DIR.endsWith("/telegram") ? STATE_DIR.slice(0, -"/telegram".length) : STATE_DIR;
|
|
52221
52416
|
turnsDb = openTurnsDb(agentDir);
|
|
52222
52417
|
applySubagentsSchema(turnsDb);
|
|
52223
|
-
|
|
52418
|
+
let markerTurnKey = null;
|
|
52419
|
+
let markerAgeMs = null;
|
|
52420
|
+
try {
|
|
52421
|
+
const markerPath = join35(STATE_DIR, TURN_ACTIVE_MARKER_FILE2);
|
|
52422
|
+
if (existsSync38(markerPath)) {
|
|
52423
|
+
const st = statSync13(markerPath);
|
|
52424
|
+
markerAgeMs = Date.now() - st.mtimeMs;
|
|
52425
|
+
try {
|
|
52426
|
+
const payload = JSON.parse(readFileSync35(markerPath, "utf8"));
|
|
52427
|
+
if (typeof payload.turnKey === "string" && payload.turnKey.length > 0) {
|
|
52428
|
+
markerTurnKey = payload.turnKey;
|
|
52429
|
+
}
|
|
52430
|
+
} catch {}
|
|
52431
|
+
}
|
|
52432
|
+
} catch {}
|
|
52433
|
+
const hangSecs = Number(process.env.TURN_HANG_SECS);
|
|
52434
|
+
const hangThresholdMs = (Number.isFinite(hangSecs) && hangSecs > 0 ? hangSecs : 300) * 1000;
|
|
52435
|
+
const reasonSnapshot = markerAgeMs != null ? JSON.stringify({ idleMs: Math.round(markerAgeMs) }) : null;
|
|
52436
|
+
const { reaped, timeoutTurnKey } = markOrphanedWithTimeoutClassification(turnsDb, {
|
|
52437
|
+
markerTurnKey,
|
|
52438
|
+
markerAgeMs,
|
|
52439
|
+
hangThresholdMs,
|
|
52440
|
+
reasonSnapshot
|
|
52441
|
+
});
|
|
52224
52442
|
if (reaped > 0) {
|
|
52225
|
-
process.stderr.write(`telegram gateway: turn-registry boot-reaper stamped ${reaped} orphaned turn(s) as
|
|
52443
|
+
process.stderr.write(`telegram gateway: turn-registry boot-reaper stamped ${reaped} orphaned turn(s)${timeoutTurnKey ? ` (turnKey=${timeoutTurnKey} as 'timeout', markerAgeMs=${markerAgeMs})` : " as 'restart'"}
|
|
52226
52444
|
`);
|
|
52227
52445
|
} else {
|
|
52228
|
-
process.stderr.write(`telegram gateway: turn-registry initialized at ${
|
|
52446
|
+
process.stderr.write(`telegram gateway: turn-registry initialized at ${join35(agentDir, "telegram", "registry.db")}
|
|
52447
|
+
`);
|
|
52448
|
+
}
|
|
52449
|
+
const pending2 = findLatestTurnIfInterrupted(turnsDb);
|
|
52450
|
+
const selfAgent = process.env.SWITCHROOM_AGENT_NAME ?? "";
|
|
52451
|
+
if (pending2 != null && selfAgent) {
|
|
52452
|
+
const kind = selectResumeBuilder(pending2.ended_via);
|
|
52453
|
+
if (kind === "resume") {
|
|
52454
|
+
bootResumeInbound = { agent: selfAgent, msg: buildResumeInterruptedInbound({ turn: pending2 }) };
|
|
52455
|
+
} else if (kind === "report") {
|
|
52456
|
+
let idleMs = pending2.turn_key === timeoutTurnKey && markerAgeMs != null ? markerAgeMs : null;
|
|
52457
|
+
if (idleMs == null && pending2.interrupt_reason) {
|
|
52458
|
+
try {
|
|
52459
|
+
const parsed = JSON.parse(pending2.interrupt_reason);
|
|
52460
|
+
if (typeof parsed.idleMs === "number" && Number.isFinite(parsed.idleMs))
|
|
52461
|
+
idleMs = parsed.idleMs;
|
|
52462
|
+
} catch {}
|
|
52463
|
+
}
|
|
52464
|
+
if (idleMs == null)
|
|
52465
|
+
idleMs = Math.max(0, Date.now() - pending2.started_at);
|
|
52466
|
+
bootResumeInbound = {
|
|
52467
|
+
agent: selfAgent,
|
|
52468
|
+
msg: buildResumeWatchdogReportInbound({ turn: pending2, idleMs })
|
|
52469
|
+
};
|
|
52470
|
+
}
|
|
52471
|
+
if (bootResumeInbound != null) {
|
|
52472
|
+
process.stderr.write(`telegram gateway: boot-resume queued kind=${kind} turnKey=${pending2.turn_key} endedVia=${pending2.ended_via ?? "open"} chat=${pending2.chat_id}
|
|
52229
52473
|
`);
|
|
52474
|
+
}
|
|
52230
52475
|
}
|
|
52231
|
-
const pendingEnvPath =
|
|
52476
|
+
const pendingEnvPath = join35(agentDir, ".pending-turn.env");
|
|
52232
52477
|
try {
|
|
52233
|
-
const pending2 = findMostRecentInterruptedTurn(turnsDb);
|
|
52234
52478
|
if (pending2 != null) {
|
|
52235
52479
|
const lines = [
|
|
52236
52480
|
`SWITCHROOM_PENDING_TURN=true`,
|
|
@@ -52239,22 +52483,23 @@ try {
|
|
|
52239
52483
|
pending2.thread_id != null ? `SWITCHROOM_PENDING_THREAD_ID=${pending2.thread_id}` : `SWITCHROOM_PENDING_THREAD_ID=`,
|
|
52240
52484
|
pending2.last_user_msg_id != null ? `SWITCHROOM_PENDING_USER_MSG_ID=${pending2.last_user_msg_id}` : `SWITCHROOM_PENDING_USER_MSG_ID=`,
|
|
52241
52485
|
`SWITCHROOM_PENDING_ENDED_VIA=${pending2.ended_via ?? "unknown"}`,
|
|
52242
|
-
`SWITCHROOM_PENDING_STARTED_AT=${pending2.started_at}
|
|
52486
|
+
`SWITCHROOM_PENDING_STARTED_AT=${pending2.started_at}`,
|
|
52487
|
+
pending2.interrupt_reason != null ? `SWITCHROOM_PENDING_INTERRUPT_REASON=${pending2.interrupt_reason}` : `SWITCHROOM_PENDING_INTERRUPT_REASON=`
|
|
52243
52488
|
];
|
|
52244
52489
|
const pendingEnvTmp = `${pendingEnvPath}.tmp-${process.pid}`;
|
|
52245
|
-
|
|
52490
|
+
writeFileSync24(pendingEnvTmp, lines.join(`
|
|
52246
52491
|
`) + `
|
|
52247
52492
|
`, { mode: 384 });
|
|
52248
|
-
|
|
52493
|
+
renameSync12(pendingEnvTmp, pendingEnvPath);
|
|
52249
52494
|
process.stderr.write(`telegram gateway: pending-turn env written to ${pendingEnvPath} turnKey=${pending2.turn_key} endedVia=${pending2.ended_via ?? "open"}
|
|
52250
52495
|
`);
|
|
52251
|
-
} else if (
|
|
52496
|
+
} else if (existsSync38(pendingEnvPath)) {
|
|
52252
52497
|
rmSync4(pendingEnvPath, { force: true });
|
|
52253
52498
|
process.stderr.write(`telegram gateway: pending-turn env cleared (clean previous shutdown)
|
|
52254
52499
|
`);
|
|
52255
52500
|
}
|
|
52256
52501
|
} catch (err) {
|
|
52257
|
-
process.stderr.write(`telegram gateway: pending-turn env write failed (${err.message})
|
|
52502
|
+
process.stderr.write(`telegram gateway: pending-turn env write failed (${err.message})
|
|
52258
52503
|
`);
|
|
52259
52504
|
}
|
|
52260
52505
|
} catch (err) {
|
|
@@ -52302,7 +52547,7 @@ function checkApprovals() {
|
|
|
52302
52547
|
return;
|
|
52303
52548
|
}
|
|
52304
52549
|
for (const senderId of files) {
|
|
52305
|
-
const file =
|
|
52550
|
+
const file = join35(APPROVED_DIR, senderId);
|
|
52306
52551
|
bot.api.sendMessage(senderId, "Paired! Say hi to Claude.").then(() => rmSync4(file, { force: true }), (err) => {
|
|
52307
52552
|
process.stderr.write(`telegram gateway: failed to send approval confirm: ${err}
|
|
52308
52553
|
`);
|
|
@@ -52669,26 +52914,6 @@ function probeAvailableReactions(chatId) {
|
|
|
52669
52914
|
}
|
|
52670
52915
|
})();
|
|
52671
52916
|
}
|
|
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
52917
|
var PHOTO_EXTS = new Set([".jpg", ".jpeg", ".png", ".gif", ".webp"]);
|
|
52693
52918
|
function chunk2(text, limit, mode) {
|
|
52694
52919
|
if (text.length <= limit)
|
|
@@ -52713,7 +52938,7 @@ function chunk2(text, limit, mode) {
|
|
|
52713
52938
|
out.push(rest);
|
|
52714
52939
|
return out;
|
|
52715
52940
|
}
|
|
52716
|
-
function
|
|
52941
|
+
function escapeMarkdownV2(text) {
|
|
52717
52942
|
const specialChars = /[_*\[\]()~`>#+\-=|{}.!\\]/g;
|
|
52718
52943
|
const parts = [];
|
|
52719
52944
|
let last = 0;
|
|
@@ -53258,11 +53483,11 @@ var unpinProgressCardForChat = null;
|
|
|
53258
53483
|
var getPinnedProgressCardMessageId = null;
|
|
53259
53484
|
var completeProgressCardTurn = null;
|
|
53260
53485
|
var subagentWatcher = null;
|
|
53261
|
-
var SOCKET_PATH = process.env.SWITCHROOM_GATEWAY_SOCKET ??
|
|
53486
|
+
var SOCKET_PATH = process.env.SWITCHROOM_GATEWAY_SOCKET ?? join35(STATE_DIR, "gateway.sock");
|
|
53262
53487
|
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 ??
|
|
53488
|
+
var GATEWAY_PID_PATH = process.env.SWITCHROOM_GATEWAY_PID_FILE ?? join35(STATE_DIR, "gateway.pid.json");
|
|
53489
|
+
var GATEWAY_SESSION_MARKER_PATH = process.env.SWITCHROOM_GATEWAY_SESSION_MARKER ?? join35(STATE_DIR, "gateway-session.json");
|
|
53490
|
+
var GATEWAY_CLEAN_SHUTDOWN_MARKER_PATH = process.env.SWITCHROOM_GATEWAY_CLEAN_SHUTDOWN_MARKER ?? join35(STATE_DIR, "clean-shutdown.json");
|
|
53266
53491
|
var GATEWAY_STARTED_AT_MS = Date.now();
|
|
53267
53492
|
var BOOT_CARD_ENABLED = process.env.SWITCHROOM_BOOT_CARD !== "false";
|
|
53268
53493
|
var activeBootCard = null;
|
|
@@ -53291,7 +53516,7 @@ function ensureIssuesCard(chatId, threadId) {
|
|
|
53291
53516
|
bot: botApi,
|
|
53292
53517
|
log: (msg) => process.stderr.write(`telegram gateway: ${msg}
|
|
53293
53518
|
`),
|
|
53294
|
-
persistPath:
|
|
53519
|
+
persistPath: join35(stateDir, "issues-card.json")
|
|
53295
53520
|
});
|
|
53296
53521
|
activeIssuesWatcher = startIssuesWatcher({
|
|
53297
53522
|
stateDir,
|
|
@@ -53450,17 +53675,24 @@ startTimer2({
|
|
|
53450
53675
|
}
|
|
53451
53676
|
});
|
|
53452
53677
|
var inboundSpool = STATIC ? undefined : createInboundSpool({
|
|
53453
|
-
path:
|
|
53678
|
+
path: join35(STATE_DIR, "inbound-spool.jsonl"),
|
|
53454
53679
|
fs: {
|
|
53455
53680
|
appendFileSync: (p, d) => appendFileSync5(p, d),
|
|
53456
|
-
readFileSync: (p) =>
|
|
53457
|
-
writeFileSync: (p, d) =>
|
|
53458
|
-
renameSync: (a, b) =>
|
|
53459
|
-
existsSync: (p) =>
|
|
53681
|
+
readFileSync: (p) => readFileSync35(p, "utf8"),
|
|
53682
|
+
writeFileSync: (p, d) => writeFileSync24(p, d),
|
|
53683
|
+
renameSync: (a, b) => renameSync12(a, b),
|
|
53684
|
+
existsSync: (p) => existsSync38(p),
|
|
53460
53685
|
statSizeSync: (p) => statSync13(p).size
|
|
53461
53686
|
}
|
|
53462
53687
|
});
|
|
53463
53688
|
var pendingInboundBuffer = createPendingInboundBuffer({ spool: inboundSpool });
|
|
53689
|
+
if (bootResumeInbound != null) {
|
|
53690
|
+
if (inboundSpool != null) {
|
|
53691
|
+
inboundSpool.put(bootResumeInbound.agent, bootResumeInbound.msg);
|
|
53692
|
+
} else {
|
|
53693
|
+
pendingInboundBuffer.push(bootResumeInbound.agent, bootResumeInbound.msg);
|
|
53694
|
+
}
|
|
53695
|
+
}
|
|
53464
53696
|
if (inboundSpool != null) {
|
|
53465
53697
|
const replay = inboundSpool.liveEntries();
|
|
53466
53698
|
for (const e of replay)
|
|
@@ -53589,7 +53821,7 @@ var ipcServer = createIpcServer({
|
|
|
53589
53821
|
probeQuotaViaBroker: (t) => probeQuotaForBootCard(agentSlug, t),
|
|
53590
53822
|
tmuxSupervisor: process.env.SWITCHROOM_TMUX_SUPERVISOR === "1",
|
|
53591
53823
|
dockerMode: process.env.SWITCHROOM_RUNTIME === "docker",
|
|
53592
|
-
configSnapshotPath:
|
|
53824
|
+
configSnapshotPath: join35(resolvedAgentDirForCard, ".config-snapshot.json"),
|
|
53593
53825
|
...updateOutcomeLine ? { updateOutcomeLine } : {}
|
|
53594
53826
|
}, ackMsgId).then((handle) => {
|
|
53595
53827
|
activeBootCard = handle;
|
|
@@ -54011,7 +54243,7 @@ var ipcServer = createIpcServer({
|
|
|
54011
54243
|
const receiverUid = receiverUidRaw ? Number(receiverUidRaw) : NaN;
|
|
54012
54244
|
if (Number.isInteger(receiverUid))
|
|
54013
54245
|
allowedUids.push(receiverUid);
|
|
54014
|
-
const socketPath =
|
|
54246
|
+
const socketPath = join35(STATE_DIR, "webhook.sock");
|
|
54015
54247
|
const webhookInject = (agentName3, inbound) => {
|
|
54016
54248
|
const msg = inbound;
|
|
54017
54249
|
const delivered = ipcServer.sendToAgent(agentName3, msg);
|
|
@@ -54261,16 +54493,11 @@ ${url}`;
|
|
|
54261
54493
|
effectiveText = markdownToHtml(text);
|
|
54262
54494
|
} else if (format === "markdownv2") {
|
|
54263
54495
|
parseMode = "MarkdownV2";
|
|
54264
|
-
effectiveText =
|
|
54496
|
+
effectiveText = escapeMarkdownV2(text);
|
|
54265
54497
|
} else {
|
|
54266
54498
|
parseMode = undefined;
|
|
54267
54499
|
effectiveText = text;
|
|
54268
54500
|
}
|
|
54269
|
-
{
|
|
54270
|
-
const prefix = takeHandoffPrefix(format === "html" ? "html" : format === "markdownv2" ? "markdownv2" : "text");
|
|
54271
|
-
if (prefix.length > 0)
|
|
54272
|
-
effectiveText = prefix + effectiveText;
|
|
54273
|
-
}
|
|
54274
54501
|
assertAllowedChat(chat_id);
|
|
54275
54502
|
let threadId = resolveThreadId(chat_id, args.message_thread_id);
|
|
54276
54503
|
if (reply_to == null && quoteOptIn && HISTORY_ENABLED) {
|
|
@@ -54683,9 +54910,8 @@ async function executeStreamReply(args) {
|
|
|
54683
54910
|
bot: lockedBot,
|
|
54684
54911
|
retry: robustApiCall,
|
|
54685
54912
|
markdownToHtml,
|
|
54686
|
-
escapeMarkdownV2
|
|
54913
|
+
escapeMarkdownV2,
|
|
54687
54914
|
repairEscapedWhitespace,
|
|
54688
|
-
takeHandoffPrefix,
|
|
54689
54915
|
assertAllowedChat,
|
|
54690
54916
|
resolveThreadId,
|
|
54691
54917
|
disableLinkPreview: access.disableLinkPreview !== false,
|
|
@@ -54715,8 +54941,8 @@ async function executeStreamReply(args) {
|
|
|
54715
54941
|
progressDriver?.recordOutboundDelivered(args.chat_id, args.message_thread_id);
|
|
54716
54942
|
} catch {}
|
|
54717
54943
|
try {
|
|
54718
|
-
const
|
|
54719
|
-
noteSignal(statusKey(args.chat_id,
|
|
54944
|
+
const threadIdNum2 = args.message_thread_id != null ? Number(args.message_thread_id) : undefined;
|
|
54945
|
+
noteSignal(statusKey(args.chat_id, threadIdNum2), Date.now());
|
|
54720
54946
|
} catch {}
|
|
54721
54947
|
}
|
|
54722
54948
|
if (args.done === true && result.messageId != null && streamButtonMeta != null && streamButtonMeta.size > 0) {
|
|
@@ -55008,11 +55234,11 @@ async function executeSendGif(rawArgs) {
|
|
|
55008
55234
|
};
|
|
55009
55235
|
}
|
|
55010
55236
|
async function publishToTelegraph(text, shortName, authorName) {
|
|
55011
|
-
const accountPath =
|
|
55237
|
+
const accountPath = join35(STATE_DIR, "telegraph-account.json");
|
|
55012
55238
|
let account = null;
|
|
55013
55239
|
try {
|
|
55014
|
-
if (
|
|
55015
|
-
const raw =
|
|
55240
|
+
if (existsSync38(accountPath)) {
|
|
55241
|
+
const raw = readFileSync35(accountPath, "utf-8");
|
|
55016
55242
|
const parsed = JSON.parse(raw);
|
|
55017
55243
|
if (parsed.shortName && parsed.accessToken) {
|
|
55018
55244
|
account = parsed;
|
|
@@ -55032,7 +55258,7 @@ async function publishToTelegraph(text, shortName, authorName) {
|
|
|
55032
55258
|
account = created.value;
|
|
55033
55259
|
try {
|
|
55034
55260
|
mkdirSync26(STATE_DIR, { recursive: true, mode: 448 });
|
|
55035
|
-
|
|
55261
|
+
writeFileSync24(accountPath, JSON.stringify(account, null, 2), { mode: 384 });
|
|
55036
55262
|
} catch (err) {
|
|
55037
55263
|
process.stderr.write(`telegram gateway: telegraph cache write failed: ${err.message}
|
|
55038
55264
|
`);
|
|
@@ -55276,7 +55502,7 @@ async function executeDownloadAttachment(args) {
|
|
|
55276
55502
|
});
|
|
55277
55503
|
mkdirSync26(INBOX_DIR, { recursive: true, mode: 448 });
|
|
55278
55504
|
assertInsideInbox(INBOX_DIR, dlPath);
|
|
55279
|
-
|
|
55505
|
+
writeFileSync24(dlPath, buf, { mode: 384 });
|
|
55280
55506
|
return { content: [{ type: "text", text: dlPath }] };
|
|
55281
55507
|
}
|
|
55282
55508
|
async function executeEditMessage(args) {
|
|
@@ -55310,7 +55536,7 @@ async function executeEditMessage(args) {
|
|
|
55310
55536
|
editText = markdownToHtml(editRawText);
|
|
55311
55537
|
} else if (editFormat === "markdownv2") {
|
|
55312
55538
|
editParseMode = "MarkdownV2";
|
|
55313
|
-
editText =
|
|
55539
|
+
editText = escapeMarkdownV2(editRawText);
|
|
55314
55540
|
} else {
|
|
55315
55541
|
editParseMode = undefined;
|
|
55316
55542
|
editText = editRawText;
|
|
@@ -55497,6 +55723,14 @@ function closeProgressLane(chatId, threadId) {
|
|
|
55497
55723
|
}
|
|
55498
55724
|
}
|
|
55499
55725
|
}
|
|
55726
|
+
var FOREGROUND_SUBAGENT_ACCUM_MAX = 12;
|
|
55727
|
+
function composeTurnActivity(turn) {
|
|
55728
|
+
const childLines = [];
|
|
55729
|
+
for (const narrative of turn.foregroundSubAgents.values()) {
|
|
55730
|
+
childLines.push(...narrative);
|
|
55731
|
+
}
|
|
55732
|
+
return renderActivityFeedWithNested(turn.mirrorLines, childLines);
|
|
55733
|
+
}
|
|
55500
55734
|
async function drainActivitySummary(turn) {
|
|
55501
55735
|
try {
|
|
55502
55736
|
while (turn.activityPendingRender !== turn.activityLastSentRender) {
|
|
@@ -55589,6 +55823,7 @@ function handleSessionEvent(ev) {
|
|
|
55589
55823
|
activityPendingRender: null,
|
|
55590
55824
|
activityLastSentRender: null,
|
|
55591
55825
|
mirrorLines: [],
|
|
55826
|
+
foregroundSubAgents: new Map,
|
|
55592
55827
|
answerStream: null,
|
|
55593
55828
|
isDm: isDmChatId(ev.chatId)
|
|
55594
55829
|
};
|
|
@@ -55683,7 +55918,7 @@ function handleSessionEvent(ev) {
|
|
|
55683
55918
|
return;
|
|
55684
55919
|
const rendered = appendActivityLabel(turn.mirrorLines, ev.label);
|
|
55685
55920
|
if (rendered != null) {
|
|
55686
|
-
turn.activityPendingRender = rendered;
|
|
55921
|
+
turn.activityPendingRender = composeTurnActivity(turn) ?? rendered;
|
|
55687
55922
|
if (turn.activityInFlight == null) {
|
|
55688
55923
|
turn.activityInFlight = drainActivitySummary(turn);
|
|
55689
55924
|
}
|
|
@@ -57106,14 +57341,14 @@ function restartMarkerPath() {
|
|
|
57106
57341
|
const agentDir = resolveAgentDirFromEnv();
|
|
57107
57342
|
if (!agentDir)
|
|
57108
57343
|
return null;
|
|
57109
|
-
return
|
|
57344
|
+
return join35(agentDir, "restart-pending.json");
|
|
57110
57345
|
}
|
|
57111
57346
|
function writeRestartMarker(marker) {
|
|
57112
57347
|
const p = restartMarkerPath();
|
|
57113
57348
|
if (!p)
|
|
57114
57349
|
return;
|
|
57115
57350
|
try {
|
|
57116
|
-
|
|
57351
|
+
writeFileSync24(p, JSON.stringify(marker));
|
|
57117
57352
|
lastPlannedRestartAt = Date.now();
|
|
57118
57353
|
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
57354
|
`);
|
|
@@ -57132,7 +57367,7 @@ function readRestartMarker() {
|
|
|
57132
57367
|
if (!p)
|
|
57133
57368
|
return null;
|
|
57134
57369
|
try {
|
|
57135
|
-
return JSON.parse(
|
|
57370
|
+
return JSON.parse(readFileSync35(p, "utf8"));
|
|
57136
57371
|
} catch {
|
|
57137
57372
|
return null;
|
|
57138
57373
|
}
|
|
@@ -57230,7 +57465,7 @@ var _dockerReachable;
|
|
|
57230
57465
|
function isDockerReachable() {
|
|
57231
57466
|
if (_dockerReachable !== undefined)
|
|
57232
57467
|
return _dockerReachable;
|
|
57233
|
-
if (!
|
|
57468
|
+
if (!existsSync38("/var/run/docker.sock")) {
|
|
57234
57469
|
_dockerReachable = false;
|
|
57235
57470
|
return _dockerReachable;
|
|
57236
57471
|
}
|
|
@@ -57247,12 +57482,12 @@ function _resetDockerReachableCache() {
|
|
|
57247
57482
|
}
|
|
57248
57483
|
function spawnSwitchroomDetached(args, onFailure) {
|
|
57249
57484
|
const fullArgs = SWITCHROOM_CONFIG ? ["--config", SWITCHROOM_CONFIG, ...args] : args;
|
|
57250
|
-
const logPath =
|
|
57485
|
+
const logPath = join35(STATE_DIR, "detached-spawn.log");
|
|
57251
57486
|
let outFd = null;
|
|
57252
57487
|
try {
|
|
57253
57488
|
mkdirSync26(STATE_DIR, { recursive: true });
|
|
57254
57489
|
outFd = openSync8(logPath, "a");
|
|
57255
|
-
|
|
57490
|
+
writeFileSync24(logPath, `
|
|
57256
57491
|
[${new Date().toISOString()}] spawn ${SWITCHROOM_CLI} ${fullArgs.join(" ")}
|
|
57257
57492
|
`, { flag: "a" });
|
|
57258
57493
|
} catch {}
|
|
@@ -57278,7 +57513,7 @@ function spawnSwitchroomDetached(args, onFailure) {
|
|
|
57278
57513
|
return;
|
|
57279
57514
|
let tail = "";
|
|
57280
57515
|
try {
|
|
57281
|
-
const full =
|
|
57516
|
+
const full = readFileSync35(logPath, "utf8");
|
|
57282
57517
|
tail = full.split(`
|
|
57283
57518
|
`).slice(-30).join(`
|
|
57284
57519
|
`).trim();
|
|
@@ -57620,10 +57855,10 @@ bot.use(async (ctx, next) => {
|
|
|
57620
57855
|
});
|
|
57621
57856
|
function readRecentDenialsForAgent(agentName3, windowMs, limit) {
|
|
57622
57857
|
try {
|
|
57623
|
-
const auditPath =
|
|
57624
|
-
if (!
|
|
57858
|
+
const auditPath = join35(homedir14(), ".switchroom", "vault-audit.log");
|
|
57859
|
+
if (!existsSync38(auditPath))
|
|
57625
57860
|
return [];
|
|
57626
|
-
const raw =
|
|
57861
|
+
const raw = readFileSync35(auditPath, "utf8");
|
|
57627
57862
|
return recentDenialsFromAuditLog(raw, { agentName: agentName3, windowMs, limit });
|
|
57628
57863
|
} catch {
|
|
57629
57864
|
return [];
|
|
@@ -57674,7 +57909,7 @@ async function buildAgentMetadata(agentName3) {
|
|
|
57674
57909
|
try {
|
|
57675
57910
|
const agentDir = resolveAgentDirFromEnv();
|
|
57676
57911
|
if (agentDir) {
|
|
57677
|
-
const raw =
|
|
57912
|
+
const raw = readFileSync35(join35(agentDir, ".claude", ".claude.json"), "utf8");
|
|
57678
57913
|
claudeJson = JSON.parse(raw);
|
|
57679
57914
|
}
|
|
57680
57915
|
} catch {}
|
|
@@ -57888,10 +58123,10 @@ bot.command("restart", async (ctx) => {
|
|
|
57888
58123
|
function flushAgentHandoff(agentDir) {
|
|
57889
58124
|
let removed = 0;
|
|
57890
58125
|
for (const fname of [".handoff.md", ".handoff-topic"]) {
|
|
57891
|
-
const p =
|
|
58126
|
+
const p = join35(agentDir, fname);
|
|
57892
58127
|
try {
|
|
57893
|
-
if (
|
|
57894
|
-
|
|
58128
|
+
if (existsSync38(p)) {
|
|
58129
|
+
unlinkSync14(p);
|
|
57895
58130
|
removed++;
|
|
57896
58131
|
}
|
|
57897
58132
|
} catch (err) {
|
|
@@ -57946,7 +58181,7 @@ async function handleNewOrResetCommand(ctx, kind) {
|
|
|
57946
58181
|
writeRestartMarker({ chat_id: chatId, thread_id: threadId ?? null, ack_message_id: ackId, ts: Date.now() });
|
|
57947
58182
|
if (agentDir != null) {
|
|
57948
58183
|
try {
|
|
57949
|
-
|
|
58184
|
+
writeFileSync24(join35(agentDir, ".force-fresh-session"), `${kind} at ${new Date().toISOString()}
|
|
57950
58185
|
`, "utf8");
|
|
57951
58186
|
} catch (err) {
|
|
57952
58187
|
process.stderr.write(`telegram gateway: failed to write force-fresh marker: ${err}
|
|
@@ -58306,16 +58541,16 @@ bot.command("interrupt", async (ctx) => {
|
|
|
58306
58541
|
await runSwitchroomCommand(ctx, ["agent", "interrupt", name], `interrupt ${name}`);
|
|
58307
58542
|
});
|
|
58308
58543
|
var lockoutOps = {
|
|
58309
|
-
readFileSync: (p, enc) =>
|
|
58310
|
-
writeFileSync: (p, data, opts) =>
|
|
58311
|
-
existsSync: (p) =>
|
|
58544
|
+
readFileSync: (p, enc) => readFileSync35(p, enc),
|
|
58545
|
+
writeFileSync: (p, data, opts) => writeFileSync24(p, data, opts),
|
|
58546
|
+
existsSync: (p) => existsSync38(p),
|
|
58312
58547
|
mkdirSync: (p, opts) => mkdirSync26(p, opts),
|
|
58313
|
-
joinPath: (...parts) =>
|
|
58548
|
+
joinPath: (...parts) => join35(...parts)
|
|
58314
58549
|
};
|
|
58315
58550
|
var FLEET_FALLBACK_DEDUP_MS = 30000;
|
|
58316
58551
|
function isAuthBrokerSocketReachable() {
|
|
58317
58552
|
try {
|
|
58318
|
-
return
|
|
58553
|
+
return existsSync38(resolveAuthBrokerSocketPath2());
|
|
58319
58554
|
} catch {
|
|
58320
58555
|
return false;
|
|
58321
58556
|
}
|
|
@@ -58376,7 +58611,7 @@ async function runCreditWatch() {
|
|
|
58376
58611
|
if (!agentDir)
|
|
58377
58612
|
return;
|
|
58378
58613
|
const agentName3 = getMyAgentName();
|
|
58379
|
-
const claudeConfigDir =
|
|
58614
|
+
const claudeConfigDir = join35(agentDir, ".claude");
|
|
58380
58615
|
const stateDir = STATE_DIR;
|
|
58381
58616
|
const reason = readClaudeJsonOverage(claudeConfigDir);
|
|
58382
58617
|
const prev = loadCreditState(stateDir);
|
|
@@ -58679,10 +58914,10 @@ async function handleVaultRecentDenialCallback(ctx, data) {
|
|
|
58679
58914
|
return;
|
|
58680
58915
|
}
|
|
58681
58916
|
const { token, id } = result;
|
|
58682
|
-
const tokenPath =
|
|
58917
|
+
const tokenPath = join35(homedir14(), ".switchroom", "agents", agentName3, ".vault-token");
|
|
58683
58918
|
try {
|
|
58684
|
-
mkdirSync26(
|
|
58685
|
-
|
|
58919
|
+
mkdirSync26(join35(homedir14(), ".switchroom", "agents", agentName3), { recursive: true });
|
|
58920
|
+
writeFileSync24(tokenPath, token, { mode: 384 });
|
|
58686
58921
|
} catch (err) {
|
|
58687
58922
|
await switchroomReply(ctx, `<b>Grant created (${escapeHtmlForTg(id)}) but token write failed:</b> ${escapeHtmlForTg(String(err))}
|
|
58688
58923
|
<i>Recover with: <code>switchroom vault grant ${escapeHtmlForTg(agentName3)} --keys ${escapeHtmlForTg(keyName)} --duration 30d</code> on the host.</i>`, { html: true });
|
|
@@ -58758,10 +58993,10 @@ async function performVaultAccessApproval(ctx, pending2, stageId, senderId, atte
|
|
|
58758
58993
|
return;
|
|
58759
58994
|
}
|
|
58760
58995
|
const { token, id } = result;
|
|
58761
|
-
const tokenPath =
|
|
58996
|
+
const tokenPath = join35(homedir14(), ".switchroom", "agents", pending2.agent, ".vault-token");
|
|
58762
58997
|
try {
|
|
58763
|
-
mkdirSync26(
|
|
58764
|
-
|
|
58998
|
+
mkdirSync26(join35(homedir14(), ".switchroom", "agents", pending2.agent), { recursive: true });
|
|
58999
|
+
writeFileSync24(tokenPath, token, { mode: 384 });
|
|
58765
59000
|
} catch (err) {
|
|
58766
59001
|
await switchroomReply(ctx, `<b>Grant created (${escapeHtmlForTg(id)}) but token write failed:</b> ${escapeHtmlForTg(String(err))}
|
|
58767
59002
|
<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 +59471,10 @@ async function executeGrantWizard(ctx, chatId, state4) {
|
|
|
59236
59471
|
return;
|
|
59237
59472
|
}
|
|
59238
59473
|
const { token, id } = result;
|
|
59239
|
-
const tokenPath =
|
|
59474
|
+
const tokenPath = join35(homedir14(), ".switchroom", "agents", state4.agent, ".vault-token");
|
|
59240
59475
|
try {
|
|
59241
|
-
mkdirSync26(
|
|
59242
|
-
|
|
59476
|
+
mkdirSync26(join35(homedir14(), ".switchroom", "agents", state4.agent), { recursive: true });
|
|
59477
|
+
writeFileSync24(tokenPath, token, { mode: 384 });
|
|
59243
59478
|
} catch (err) {
|
|
59244
59479
|
await switchroomReply(ctx, `<b>Grant created but token write failed:</b> ${escapeHtmlForTg(String(err))}`, { html: true });
|
|
59245
59480
|
return;
|
|
@@ -60087,7 +60322,7 @@ bot.command("usage", async (ctx) => {
|
|
|
60087
60322
|
await switchroomReply(ctx, "<b>/usage:</b> cannot resolve agent dir.", { html: true });
|
|
60088
60323
|
return;
|
|
60089
60324
|
}
|
|
60090
|
-
const result = await fetchQuota2({ claudeConfigDir:
|
|
60325
|
+
const result = await fetchQuota2({ claudeConfigDir: join35(agentDir, ".claude") });
|
|
60091
60326
|
if (!result.ok) {
|
|
60092
60327
|
await switchroomReply(ctx, `<b>/usage:</b> ${escapeHtmlForTg(result.reason)}`, { html: true });
|
|
60093
60328
|
return;
|
|
@@ -60527,7 +60762,7 @@ ${preBlock(formatSwitchroomOutput(err.message ?? "unknown error"))}`, { html: tr
|
|
|
60527
60762
|
const unifiedDiff = (() => {
|
|
60528
60763
|
try {
|
|
60529
60764
|
const cfgPath = process.env.SWITCHROOM_CONFIG ?? SWITCHROOM_CONFIG ?? findConfigFile2();
|
|
60530
|
-
const raw =
|
|
60765
|
+
const raw = readFileSync35(cfgPath, "utf8");
|
|
60531
60766
|
return synthesizeAllowRuleDiff({ agentName: agentName3, rule: chosen.rule, configText: raw });
|
|
60532
60767
|
} catch (err) {
|
|
60533
60768
|
process.stderr.write(`telegram gateway: always-allow diff synth failed: ${err.message}
|
|
@@ -60669,7 +60904,7 @@ bot.on("message:photo", async (ctx) => {
|
|
|
60669
60904
|
});
|
|
60670
60905
|
mkdirSync26(INBOX_DIR, { recursive: true, mode: 448 });
|
|
60671
60906
|
assertInsideInbox(INBOX_DIR, dlPath);
|
|
60672
|
-
|
|
60907
|
+
writeFileSync24(dlPath, buf, { mode: 384 });
|
|
60673
60908
|
return dlPath;
|
|
60674
60909
|
} catch (err) {
|
|
60675
60910
|
const msg = err instanceof Error ? err.message : "unknown error";
|
|
@@ -60709,8 +60944,8 @@ async function maybeTranscribeVoice(fileId, mimeType, language) {
|
|
|
60709
60944
|
let apiKey = null;
|
|
60710
60945
|
try {
|
|
60711
60946
|
const path = __require("path").join(__require("os").homedir(), ".switchroom", "openai-api-key");
|
|
60712
|
-
if (
|
|
60713
|
-
apiKey =
|
|
60947
|
+
if (existsSync38(path)) {
|
|
60948
|
+
apiKey = readFileSync35(path, "utf-8").trim();
|
|
60714
60949
|
}
|
|
60715
60950
|
} catch (err) {
|
|
60716
60951
|
process.stderr.write(`telegram gateway: voice-in: failed to read api key: ${err.message}
|
|
@@ -61345,7 +61580,6 @@ process.on("SIGINT", () => void shutdown("SIGINT"));
|
|
|
61345
61580
|
`) });
|
|
61346
61581
|
}
|
|
61347
61582
|
}
|
|
61348
|
-
initHandoffContinuity();
|
|
61349
61583
|
process.on("unhandledRejection", (err) => {
|
|
61350
61584
|
const action = classifyRejection(err);
|
|
61351
61585
|
process.stderr.write(`telegram gateway: unhandled rejection (${action}): ${err}
|
|
@@ -61566,7 +61800,7 @@ var didOneTimeSetup = false;
|
|
|
61566
61800
|
return;
|
|
61567
61801
|
}
|
|
61568
61802
|
})();
|
|
61569
|
-
const resolvedAgentDirForBootCard = agentDir ??
|
|
61803
|
+
const resolvedAgentDirForBootCard = agentDir ?? join35(homedir14(), ".switchroom", "agents", agentSlug);
|
|
61570
61804
|
const handle = await startBootCard(chatId, threadId, botApiForCard, {
|
|
61571
61805
|
agentName: agentDisplayName,
|
|
61572
61806
|
agentSlug,
|
|
@@ -61580,7 +61814,7 @@ var didOneTimeSetup = false;
|
|
|
61580
61814
|
probeQuotaViaBroker: (t) => probeQuotaForBootCard(agentSlug, t),
|
|
61581
61815
|
tmuxSupervisor: process.env.SWITCHROOM_TMUX_SUPERVISOR === "1",
|
|
61582
61816
|
dockerMode: process.env.SWITCHROOM_RUNTIME === "docker",
|
|
61583
|
-
configSnapshotPath:
|
|
61817
|
+
configSnapshotPath: join35(resolvedAgentDirForBootCard, ".config-snapshot.json"),
|
|
61584
61818
|
...updateOutcomeLine ? { updateOutcomeLine } : {}
|
|
61585
61819
|
}, ackMsgId);
|
|
61586
61820
|
activeBootCard = handle;
|
|
@@ -61665,6 +61899,7 @@ var didOneTimeSetup = false;
|
|
|
61665
61899
|
const watcherAgentDir = resolveAgentDirFromEnv();
|
|
61666
61900
|
if (watcherAgentDir != null) {
|
|
61667
61901
|
const workerFeedEnabled = isWorkerActivityFeedEnabled(process.env.SWITCHROOM_WORKER_ACTIVITY_FEED);
|
|
61902
|
+
const foregroundNestingEnabled = process.env.SWITCHROOM_FOREGROUND_SUBAGENT_NESTING !== "0";
|
|
61668
61903
|
const workerActivityFeed = createWorkerActivityFeed({
|
|
61669
61904
|
bot: {
|
|
61670
61905
|
sendMessage: async (cid, text, sendOpts) => {
|
|
@@ -61724,6 +61959,19 @@ var didOneTimeSetup = false;
|
|
|
61724
61959
|
} catch {}
|
|
61725
61960
|
}
|
|
61726
61961
|
const isBackground = dispatch.isBackground;
|
|
61962
|
+
if (!isBackground) {
|
|
61963
|
+
const turn = currentTurn;
|
|
61964
|
+
if (turn != null && turn.foregroundSubAgents.delete(agentId) && !turn.replyCalled) {
|
|
61965
|
+
const rendered = composeTurnActivity(turn);
|
|
61966
|
+
if (rendered != null) {
|
|
61967
|
+
turn.activityPendingRender = rendered;
|
|
61968
|
+
if (turn.activityInFlight == null) {
|
|
61969
|
+
turn.activityInFlight = drainActivitySummary(turn);
|
|
61970
|
+
}
|
|
61971
|
+
}
|
|
61972
|
+
}
|
|
61973
|
+
return;
|
|
61974
|
+
}
|
|
61727
61975
|
if (workerFeedEnabled) {
|
|
61728
61976
|
workerActivityFeed.finish(agentId, {
|
|
61729
61977
|
description: dispatch.feedDescription,
|
|
@@ -61784,8 +62032,35 @@ var didOneTimeSetup = false;
|
|
|
61784
62032
|
} catch {}
|
|
61785
62033
|
}
|
|
61786
62034
|
const isBackground = dispatch.isBackground;
|
|
61787
|
-
if (!isBackground)
|
|
62035
|
+
if (!isBackground) {
|
|
62036
|
+
if (!foregroundNestingEnabled)
|
|
62037
|
+
return;
|
|
62038
|
+
const turn = currentTurn;
|
|
62039
|
+
if (turn == null || turn.replyCalled)
|
|
62040
|
+
return;
|
|
62041
|
+
const child = latestSummary.trim().slice(0, 120);
|
|
62042
|
+
if (child.length === 0)
|
|
62043
|
+
return;
|
|
62044
|
+
let narrative = turn.foregroundSubAgents.get(agentId);
|
|
62045
|
+
if (narrative == null) {
|
|
62046
|
+
narrative = [];
|
|
62047
|
+
turn.foregroundSubAgents.set(agentId, narrative);
|
|
62048
|
+
}
|
|
62049
|
+
if (narrative[narrative.length - 1] !== child) {
|
|
62050
|
+
narrative.push(child);
|
|
62051
|
+
if (narrative.length > FOREGROUND_SUBAGENT_ACCUM_MAX) {
|
|
62052
|
+
narrative.splice(0, narrative.length - FOREGROUND_SUBAGENT_ACCUM_MAX);
|
|
62053
|
+
}
|
|
62054
|
+
}
|
|
62055
|
+
const rendered = composeTurnActivity(turn);
|
|
62056
|
+
if (rendered != null) {
|
|
62057
|
+
turn.activityPendingRender = rendered;
|
|
62058
|
+
if (turn.activityInFlight == null) {
|
|
62059
|
+
turn.activityInFlight = drainActivitySummary(turn);
|
|
62060
|
+
}
|
|
62061
|
+
}
|
|
61788
62062
|
return;
|
|
62063
|
+
}
|
|
61789
62064
|
if (workerFeedEnabled) {
|
|
61790
62065
|
workerActivityFeed.update(agentId, fleetChatId || (loadAccess().allowFrom[0] ?? ""), {
|
|
61791
62066
|
description: dispatch.feedDescription,
|