switchroom 0.15.41 → 0.15.43
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 +2 -1
- package/dist/auth-broker/index.js +2 -1
- package/dist/cli/notion-write-pretool.mjs +2 -1
- package/dist/cli/switchroom.js +157 -13
- package/dist/cli/ui/index.html +31 -0
- package/dist/host-control/main.js +2 -1
- package/dist/vault/approvals/kernel-server.js +2 -1
- package/dist/vault/broker/server.js +2 -1
- package/package.json +1 -1
- package/telegram-plugin/dist/gateway/gateway.js +397 -226
- package/telegram-plugin/gateway/context-occupancy.ts +91 -0
- package/telegram-plugin/gateway/gateway.ts +204 -63
- package/telegram-plugin/gateway/hostd-dispatch.ts +1 -1
- package/telegram-plugin/gateway/idle-clear.ts +72 -0
- package/telegram-plugin/gateway/poll-health.ts +9 -4
- package/telegram-plugin/gateway/poll-stall-recovery.ts +59 -0
- package/telegram-plugin/tests/context-occupancy.test.ts +55 -0
- package/telegram-plugin/tests/idle-clear.test.ts +62 -0
- package/telegram-plugin/tests/poll-stall-recovery.test.ts +32 -0
- package/telegram-plugin/tests/welcome-text.test.ts +10 -11
- package/telegram-plugin/welcome-text.ts +11 -12
|
@@ -23960,7 +23960,8 @@ var init_schema = __esm(() => {
|
|
|
23960
23960
|
SessionSchema = exports_external.object({
|
|
23961
23961
|
max_idle: exports_external.string().regex(/^\d+[smh]$/, "Duration must be a number followed by s, m, or h (e.g. '2h', '30m')").optional().describe("Start a fresh session if the previous one has been idle " + "longer than this duration. Examples: '2h', '30m', '7200s'."),
|
|
23962
23962
|
max_turns: exports_external.number().int().positive().optional().describe("Start a fresh session if the previous one has more user " + "turns than this. Useful for preventing context bloat on " + "long-running agents."),
|
|
23963
|
-
max_context_tokens: exports_external.number().int().positive().optional().describe("Proactively run /compact when the live context window " + "occupancy (latest assistant turn input + cache-read + " + "cache-creation tokens) reaches this many tokens. Opt-in: " + "unset means rely on Claude Code's native auto-compaction. " + "Useful on large-window models (e.g. 1M Opus) to hold a " + "deliberately lean working context.")
|
|
23963
|
+
max_context_tokens: exports_external.number().int().positive().optional().describe("Proactively run /compact when the live context window " + "occupancy (latest assistant turn input + cache-read + " + "cache-creation tokens) reaches this many tokens. Opt-in: " + "unset means rely on Claude Code's native auto-compaction. " + "Useful on large-window models (e.g. 1M Opus) to hold a " + "deliberately lean working context."),
|
|
23964
|
+
idle_clear_after: exports_external.string().regex(/^\d+[smh]$/, "Duration must be a number followed by s, m, or h (e.g. '3h', '90m')").optional().describe("Auto-run /clear (wipe the working context) after the live " + "session has been idle this long. Defaults to '3h' when unset " + "(on by default); set '0s' to disable. Long-term memory lives " + "in Hindsight, so a clear loses only the in-session thread.")
|
|
23964
23965
|
}).optional();
|
|
23965
23966
|
SessionContinuitySchema = exports_external.object({
|
|
23966
23967
|
enabled: exports_external.boolean().optional().describe("Master switch for the session-handoff briefing (default true)."),
|
|
@@ -28928,10 +28929,10 @@ function renderAuthLine(state4, agentName3, now = Date.now()) {
|
|
|
28928
28929
|
}
|
|
28929
28930
|
|
|
28930
28931
|
// gateway/quota-cache.ts
|
|
28931
|
-
import { existsSync as existsSync26, readFileSync as readFileSync24, writeFileSync as
|
|
28932
|
-
import { join as
|
|
28932
|
+
import { existsSync as existsSync26, readFileSync as readFileSync24, writeFileSync as writeFileSync16, mkdirSync as mkdirSync17 } from "fs";
|
|
28933
|
+
import { join as join24, dirname as dirname8 } from "path";
|
|
28933
28934
|
function defaultCachePath() {
|
|
28934
|
-
return process.env.SWITCHROOM_QUOTA_CACHE_PATH ??
|
|
28935
|
+
return process.env.SWITCHROOM_QUOTA_CACHE_PATH ?? join24(process.env.HOME ?? "/tmp", ".switchroom", "quota-cache.json");
|
|
28935
28936
|
}
|
|
28936
28937
|
function readQuotaCache(opts = {}) {
|
|
28937
28938
|
const path = opts.path ?? defaultCachePath();
|
|
@@ -28966,8 +28967,8 @@ function writeQuotaCache(result, opts = {}) {
|
|
|
28966
28967
|
result
|
|
28967
28968
|
};
|
|
28968
28969
|
try {
|
|
28969
|
-
|
|
28970
|
-
|
|
28970
|
+
mkdirSync17(dirname8(path), { recursive: true });
|
|
28971
|
+
writeFileSync16(path, JSON.stringify(entry, null, 2), { mode: 384 });
|
|
28971
28972
|
} catch {}
|
|
28972
28973
|
}
|
|
28973
28974
|
var DEFAULT_TTL_MS4, RATE_LIMIT_TTL_MS;
|
|
@@ -28978,7 +28979,7 @@ var init_quota_cache = __esm(() => {
|
|
|
28978
28979
|
|
|
28979
28980
|
// gateway/boot-probes.ts
|
|
28980
28981
|
import { readFileSync as readFileSync25, readdirSync as readdirSync4, existsSync as existsSync27 } from "fs";
|
|
28981
|
-
import { join as
|
|
28982
|
+
import { join as join25 } from "path";
|
|
28982
28983
|
import { execFile as execFileCb } from "child_process";
|
|
28983
28984
|
import { promisify as promisify3 } from "util";
|
|
28984
28985
|
async function withTimeout(label, p, timeoutMs = PROBE_TIMEOUT_MS) {
|
|
@@ -29020,8 +29021,8 @@ function mapPlan(billingType, hasExtra) {
|
|
|
29020
29021
|
}
|
|
29021
29022
|
async function probeAccount(agentDir) {
|
|
29022
29023
|
return withTimeout("Account", (async () => {
|
|
29023
|
-
const claudeDir =
|
|
29024
|
-
const claudeJsonPath =
|
|
29024
|
+
const claudeDir = join25(agentDir, ".claude");
|
|
29025
|
+
const claudeJsonPath = join25(claudeDir, ".claude.json");
|
|
29025
29026
|
let cfg = {};
|
|
29026
29027
|
try {
|
|
29027
29028
|
const raw = readFileSync25(claudeJsonPath, "utf8");
|
|
@@ -29042,8 +29043,8 @@ async function probeAccount(agentDir) {
|
|
|
29042
29043
|
let tokenStr = "";
|
|
29043
29044
|
let status = "ok";
|
|
29044
29045
|
for (const candidate of [
|
|
29045
|
-
|
|
29046
|
-
|
|
29046
|
+
join25(claudeDir, ".oauth-token.meta.json"),
|
|
29047
|
+
join25(claudeDir, "accounts", "default", ".oauth-token.meta.json")
|
|
29047
29048
|
]) {
|
|
29048
29049
|
if (existsSync27(candidate)) {
|
|
29049
29050
|
try {
|
|
@@ -29429,9 +29430,9 @@ async function probeQuota(claudeConfigDir, _agentDir, fetchImpl = fetch, opts =
|
|
|
29429
29430
|
let claudeDirForProbe = null;
|
|
29430
29431
|
for (const candidate of [
|
|
29431
29432
|
claudeConfigDir,
|
|
29432
|
-
|
|
29433
|
+
join25(claudeConfigDir, "accounts", "default")
|
|
29433
29434
|
]) {
|
|
29434
|
-
if (existsSync27(
|
|
29435
|
+
if (existsSync27(join25(candidate, ".oauth-token"))) {
|
|
29435
29436
|
claudeDirForProbe = candidate;
|
|
29436
29437
|
break;
|
|
29437
29438
|
}
|
|
@@ -29666,7 +29667,7 @@ async function probeSkills(agentDir, opts = {}) {
|
|
|
29666
29667
|
return withTimeout("Skills", (async () => {
|
|
29667
29668
|
const fs2 = opts.fs ?? realSkillsFs;
|
|
29668
29669
|
const max = opts.maxNamesShown ?? 3;
|
|
29669
|
-
const skillsDir =
|
|
29670
|
+
const skillsDir = join25(agentDir, ".claude", "skills");
|
|
29670
29671
|
if (!fs2.exists(skillsDir)) {
|
|
29671
29672
|
return { status: "ok", label: "Skills", detail: "no skills dir" };
|
|
29672
29673
|
}
|
|
@@ -29681,17 +29682,17 @@ async function probeSkills(agentDir, opts = {}) {
|
|
|
29681
29682
|
}
|
|
29682
29683
|
const dangling = [];
|
|
29683
29684
|
for (const name of entries) {
|
|
29684
|
-
const skillPath =
|
|
29685
|
+
const skillPath = join25(skillsDir, name);
|
|
29685
29686
|
if (!fs2.exists(skillPath)) {
|
|
29686
29687
|
dangling.push(name);
|
|
29687
29688
|
continue;
|
|
29688
29689
|
}
|
|
29689
|
-
const skillMd =
|
|
29690
|
+
const skillMd = join25(skillPath, "SKILL.md");
|
|
29690
29691
|
if (!fs2.exists(skillMd) && !fs2.exists(skillPath + ".md")) {
|
|
29691
29692
|
continue;
|
|
29692
29693
|
}
|
|
29693
29694
|
}
|
|
29694
|
-
const overlayDir = opts.overlaySkillsDir ??
|
|
29695
|
+
const overlayDir = opts.overlaySkillsDir ?? join25(agentDir, "skills.d");
|
|
29695
29696
|
const overlaySlugs = new Set;
|
|
29696
29697
|
if (fs2.exists(overlayDir)) {
|
|
29697
29698
|
let overlayEntries = [];
|
|
@@ -29733,7 +29734,7 @@ function renderBucketedSkills(switchroom, agent) {
|
|
|
29733
29734
|
}
|
|
29734
29735
|
async function probeConnections(agentDir, opts = {}) {
|
|
29735
29736
|
return withTimeout("Connections", (async () => {
|
|
29736
|
-
const path =
|
|
29737
|
+
const path = join25(agentDir, ".claude", "connection-health.json");
|
|
29737
29738
|
const read = opts.readFileImpl ?? ((p) => readFileSync25(p, "utf8"));
|
|
29738
29739
|
let issues = [];
|
|
29739
29740
|
try {
|
|
@@ -29782,7 +29783,7 @@ var init_boot_probes = __esm(() => {
|
|
|
29782
29783
|
});
|
|
29783
29784
|
|
|
29784
29785
|
// gateway/boot-issue-cache.ts
|
|
29785
|
-
import { existsSync as existsSync28, readFileSync as readFileSync26, writeFileSync as
|
|
29786
|
+
import { existsSync as existsSync28, readFileSync as readFileSync26, writeFileSync as writeFileSync17, mkdirSync as mkdirSync18, renameSync as renameSync9 } from "fs";
|
|
29786
29787
|
import { dirname as dirname9 } from "path";
|
|
29787
29788
|
function fingerprintProbe(key, r) {
|
|
29788
29789
|
if (r.status === "ok")
|
|
@@ -29909,9 +29910,9 @@ function applyAndSave(path, cache, diff) {
|
|
|
29909
29910
|
}
|
|
29910
29911
|
}
|
|
29911
29912
|
try {
|
|
29912
|
-
|
|
29913
|
+
mkdirSync18(dirname9(path), { recursive: true });
|
|
29913
29914
|
const tmp = `${path}.tmp`;
|
|
29914
|
-
|
|
29915
|
+
writeFileSync17(tmp, JSON.stringify(next), { mode: 384 });
|
|
29915
29916
|
renameSync9(tmp, path);
|
|
29916
29917
|
} catch {}
|
|
29917
29918
|
return next;
|
|
@@ -29925,7 +29926,7 @@ var init_boot_issue_cache = __esm(() => {
|
|
|
29925
29926
|
|
|
29926
29927
|
// gateway/config-snapshot.ts
|
|
29927
29928
|
import { createHash as createHash2 } from "crypto";
|
|
29928
|
-
import { existsSync as existsSync29, readFileSync as readFileSync27, writeFileSync as
|
|
29929
|
+
import { existsSync as existsSync29, readFileSync as readFileSync27, writeFileSync as writeFileSync18, mkdirSync as mkdirSync19, renameSync as renameSync10 } from "fs";
|
|
29929
29930
|
import { dirname as dirname10 } from "path";
|
|
29930
29931
|
function hashStringArray(items) {
|
|
29931
29932
|
if (!items || items.length === 0)
|
|
@@ -30024,16 +30025,16 @@ function loadSnapshot(path, now = Date.now) {
|
|
|
30024
30025
|
}
|
|
30025
30026
|
function persistSnapshot(path, snapshot) {
|
|
30026
30027
|
try {
|
|
30027
|
-
|
|
30028
|
+
mkdirSync19(dirname10(path), { recursive: true });
|
|
30028
30029
|
const tmp = `${path}.tmp`;
|
|
30029
|
-
|
|
30030
|
+
writeFileSync18(tmp, JSON.stringify(snapshot), { mode: 384 });
|
|
30030
30031
|
renameSync10(tmp, path);
|
|
30031
30032
|
} catch {}
|
|
30032
30033
|
}
|
|
30033
30034
|
var init_config_snapshot = () => {};
|
|
30034
30035
|
|
|
30035
30036
|
// gateway/boot-card-msgid.ts
|
|
30036
|
-
import { readFileSync as readFileSync28, writeFileSync as
|
|
30037
|
+
import { readFileSync as readFileSync28, writeFileSync as writeFileSync19 } from "node:fs";
|
|
30037
30038
|
function bootCardChatKey(chatId, threadId) {
|
|
30038
30039
|
return `${chatId}:${threadId ?? ""}`;
|
|
30039
30040
|
}
|
|
@@ -30058,7 +30059,7 @@ function saveBootCardMsgId(path, chatKey3, messageId) {
|
|
|
30058
30059
|
if (store2[chatKey3] === messageId)
|
|
30059
30060
|
return;
|
|
30060
30061
|
store2[chatKey3] = messageId;
|
|
30061
|
-
|
|
30062
|
+
writeFileSync19(path, JSON.stringify(store2), "utf8");
|
|
30062
30063
|
} catch {}
|
|
30063
30064
|
}
|
|
30064
30065
|
var init_boot_card_msgid = () => {};
|
|
@@ -30073,7 +30074,7 @@ __export(exports_boot_card, {
|
|
|
30073
30074
|
renderBootCard: () => renderBootCard,
|
|
30074
30075
|
renderAccountRows: () => renderAuthLine
|
|
30075
30076
|
});
|
|
30076
|
-
import { join as
|
|
30077
|
+
import { join as join26 } from "path";
|
|
30077
30078
|
function resolvePersonaName(slug, loadConfig3) {
|
|
30078
30079
|
try {
|
|
30079
30080
|
const config = loadConfig3 ? loadConfig3() : loadConfig();
|
|
@@ -30163,7 +30164,7 @@ function renderBootCard(opts) {
|
|
|
30163
30164
|
`);
|
|
30164
30165
|
}
|
|
30165
30166
|
async function runAllProbes(opts) {
|
|
30166
|
-
const claudeDir =
|
|
30167
|
+
const claudeDir = join26(opts.agentDir, ".claude");
|
|
30167
30168
|
const probes = {};
|
|
30168
30169
|
const slug = opts.agentSlug ?? opts.agentName;
|
|
30169
30170
|
await Promise.allSettled([
|
|
@@ -30919,7 +30920,7 @@ __export(exports_tmux, {
|
|
|
30919
30920
|
captureAgentPane: () => captureAgentPane
|
|
30920
30921
|
});
|
|
30921
30922
|
import { execFileSync as execFileSync4 } from "node:child_process";
|
|
30922
|
-
import { mkdirSync as
|
|
30923
|
+
import { mkdirSync as mkdirSync26, readdirSync as readdirSync6, statSync as statSync12, unlinkSync as unlinkSync13, writeFileSync as writeFileSync25 } from "node:fs";
|
|
30923
30924
|
import { resolve as resolve7 } from "node:path";
|
|
30924
30925
|
function captureAgentPane(opts) {
|
|
30925
30926
|
const { agentName: agentName3, agentDir, reason } = opts;
|
|
@@ -30931,7 +30932,7 @@ function captureAgentPane(opts) {
|
|
|
30931
30932
|
const reasonSlug = sanitizeReason(reason);
|
|
30932
30933
|
const outPath = resolve7(outDir, `${ts}-${reasonSlug}.txt`);
|
|
30933
30934
|
try {
|
|
30934
|
-
|
|
30935
|
+
mkdirSync26(outDir, { recursive: true, mode: 493 });
|
|
30935
30936
|
} catch (err) {
|
|
30936
30937
|
const msg = `mkdir crash-reports failed: ${err.message}`;
|
|
30937
30938
|
console.error(`[tmux-capture] ${agentName3}: ${msg}`);
|
|
@@ -30965,7 +30966,7 @@ function captureAgentPane(opts) {
|
|
|
30965
30966
|
` + `
|
|
30966
30967
|
`;
|
|
30967
30968
|
try {
|
|
30968
|
-
|
|
30969
|
+
writeFileSync25(outPath, Buffer.concat([Buffer.from(header, "utf8"), body]), {
|
|
30969
30970
|
mode: 420
|
|
30970
30971
|
});
|
|
30971
30972
|
} catch (err) {
|
|
@@ -31560,8 +31561,8 @@ import { randomBytes as randomBytes6 } from "crypto";
|
|
|
31560
31561
|
import { execFileSync as execFileSync5, execSync as execSync2, spawn as spawn2 } from "child_process";
|
|
31561
31562
|
import {
|
|
31562
31563
|
readFileSync as readFileSync36,
|
|
31563
|
-
writeFileSync as
|
|
31564
|
-
mkdirSync as
|
|
31564
|
+
writeFileSync as writeFileSync26,
|
|
31565
|
+
mkdirSync as mkdirSync27,
|
|
31565
31566
|
readdirSync as readdirSync7,
|
|
31566
31567
|
rmSync as rmSync4,
|
|
31567
31568
|
statSync as statSync13,
|
|
@@ -31575,7 +31576,7 @@ import {
|
|
|
31575
31576
|
appendFileSync as appendFileSync5
|
|
31576
31577
|
} from "fs";
|
|
31577
31578
|
import { homedir as homedir14 } from "os";
|
|
31578
|
-
import { join as
|
|
31579
|
+
import { join as join36, extname, sep as sep3, basename as basename10 } from "path";
|
|
31579
31580
|
|
|
31580
31581
|
// plugin-logger.ts
|
|
31581
31582
|
import { appendFileSync, mkdirSync, renameSync, statSync, existsSync } from "fs";
|
|
@@ -43163,7 +43164,7 @@ function helpText(agentName3) {
|
|
|
43163
43164
|
``,
|
|
43164
43165
|
`This bot is the <b>${escapeHtml6(agentName3)}</b> agent. Text and photos route through to it; replies, reactions and progress cards come back.`,
|
|
43165
43166
|
``,
|
|
43166
|
-
`Tool approvals surface as inline buttons (\u2705 / \u274c) or via <code>/approve</code>, <code>/deny</code>, <code>/pending</code>. Start a fresh session with <code>/new</code
|
|
43167
|
+
`Tool approvals surface as inline buttons (\u2705 / \u274c) or via <code>/approve</code>, <code>/deny</code>, <code>/pending</code>. Start a fresh session with <code>/new</code>, or trim/clear context with <code>/compact</code> / <code>/clear</code>.`,
|
|
43167
43168
|
``,
|
|
43168
43169
|
`<code>/start</code> \u2014 pairing instructions`,
|
|
43169
43170
|
`<code>/status</code> \u2014 agent, model, auth`,
|
|
@@ -43231,7 +43232,8 @@ var TELEGRAM_MENU_COMMANDS = [
|
|
|
43231
43232
|
{ command: "help", description: "What this bot can do" },
|
|
43232
43233
|
{ command: "status", description: "Agent, model, auth" },
|
|
43233
43234
|
{ command: "new", description: "Fresh session (flush handoff, restart)" },
|
|
43234
|
-
{ command: "
|
|
43235
|
+
{ command: "compact", description: "Compact context (summarize, keep the thread)" },
|
|
43236
|
+
{ command: "clear", description: "Clear context (fresh slate; memory in Hindsight)" },
|
|
43235
43237
|
{ command: "approve", description: "Approve pending tool permission" },
|
|
43236
43238
|
{ command: "deny", description: "Deny pending tool permission" },
|
|
43237
43239
|
{ command: "pending", description: "List pending permission prompts" },
|
|
@@ -43239,7 +43241,6 @@ var TELEGRAM_MENU_COMMANDS = [
|
|
|
43239
43241
|
{ command: "restart", description: "Restart the agent (drain by default)" },
|
|
43240
43242
|
{ command: "version", description: "Show versions + running agent health" },
|
|
43241
43243
|
{ command: "logs", description: "Show recent agent logs" },
|
|
43242
|
-
{ command: "inject", description: "Inject a Claude Code slash command (e.g. /cost)" },
|
|
43243
43244
|
{ command: "model", description: "Show or switch the Claude model" },
|
|
43244
43245
|
{ command: "effort", description: "Show or switch the reasoning effort" },
|
|
43245
43246
|
{ command: "doctor", description: "Health check (deps, services, MCP)" },
|
|
@@ -43256,7 +43257,8 @@ function switchroomHelpText(agentName3) {
|
|
|
43256
43257
|
``,
|
|
43257
43258
|
`<b>Session & approvals</b>`,
|
|
43258
43259
|
`<code>/new</code> \u2014 fresh session (flush handoff, restart)`,
|
|
43259
|
-
`<code>/
|
|
43260
|
+
`<code>/compact</code> \u2014 compact context (summarize, keep the thread)`,
|
|
43261
|
+
`<code>/clear</code> \u2014 clear context (fresh slate; memory in Hindsight)`,
|
|
43260
43262
|
`<code>/approve [id]</code> \u2014 approve pending tool permission`,
|
|
43261
43263
|
`<code>/deny [id]</code> \u2014 deny pending tool permission`,
|
|
43262
43264
|
`<code>/pending</code> \u2014 list pending permission prompts`,
|
|
@@ -43310,10 +43312,6 @@ function newSessionAckText(agentName3, flushedHandoff) {
|
|
|
43310
43312
|
const tail = flushedHandoff ? " \u00b7 flushed handoff" : "";
|
|
43311
43313
|
return `\uD83C\uDD95 Started fresh session for <b>${escapeHtml6(agentName3)}</b>${tail} \u00b7 restarting\u2026`;
|
|
43312
43314
|
}
|
|
43313
|
-
function resetSessionAckText(agentName3, flushedHandoff) {
|
|
43314
|
-
const tail = flushedHandoff ? " \u00b7 flushed handoff" : "";
|
|
43315
|
-
return `\uD83D\uDD04 Reset session for <b>${escapeHtml6(agentName3)}</b>${tail} \u00b7 restarting\u2026`;
|
|
43316
|
-
}
|
|
43317
43315
|
|
|
43318
43316
|
// gateway/auth-status-adapter.ts
|
|
43319
43317
|
function formatExpiresInRelative(expiresAt, now = Date.now()) {
|
|
@@ -46383,6 +46381,38 @@ function numField(obj, key) {
|
|
|
46383
46381
|
return 0;
|
|
46384
46382
|
}
|
|
46385
46383
|
|
|
46384
|
+
// gateway/context-occupancy.ts
|
|
46385
|
+
import { mkdirSync as mkdirSync12, writeFileSync as writeFileSync9 } from "node:fs";
|
|
46386
|
+
import { join as join18 } from "node:path";
|
|
46387
|
+
var CONTEXT_OCCUPANCY_FILENAME = "context-occupancy.json";
|
|
46388
|
+
var TIGHT_FRACTION = 0.8;
|
|
46389
|
+
function buildContextOccupancy(occupancy, cap, now) {
|
|
46390
|
+
if (!Number.isFinite(occupancy) || occupancy < 0) {
|
|
46391
|
+
return { occupancy: 0, cap: cap ?? null, headroom: null, pct: null, state: "unknown", computedAt: now };
|
|
46392
|
+
}
|
|
46393
|
+
const c = cap != null && cap > 0 ? cap : null;
|
|
46394
|
+
if (c == null) {
|
|
46395
|
+
return { occupancy, cap: null, headroom: null, pct: null, state: "ok", computedAt: now };
|
|
46396
|
+
}
|
|
46397
|
+
const pct = occupancy / c;
|
|
46398
|
+
return {
|
|
46399
|
+
occupancy,
|
|
46400
|
+
cap: c,
|
|
46401
|
+
headroom: c - occupancy,
|
|
46402
|
+
pct,
|
|
46403
|
+
state: pct >= TIGHT_FRACTION ? "tight" : "ok",
|
|
46404
|
+
computedAt: now
|
|
46405
|
+
};
|
|
46406
|
+
}
|
|
46407
|
+
function writeContextOccupancySnapshot(stateDir, snapshot, deps) {
|
|
46408
|
+
try {
|
|
46409
|
+
const path = join18(stateDir, CONTEXT_OCCUPANCY_FILENAME);
|
|
46410
|
+
(deps?.mkdir ?? ((p, o) => mkdirSync12(p, o)))(stateDir, { recursive: true });
|
|
46411
|
+
(deps?.writeFile ?? ((p, d) => writeFileSync9(p, d)))(path, JSON.stringify(snapshot, null, 2) + `
|
|
46412
|
+
`);
|
|
46413
|
+
} catch {}
|
|
46414
|
+
}
|
|
46415
|
+
|
|
46386
46416
|
// gateway/proactive-compact.ts
|
|
46387
46417
|
var COMPACT_REARM_FRACTION = 0.6;
|
|
46388
46418
|
var COMPACT_COOLDOWN_TURNS = 3;
|
|
@@ -46409,6 +46439,36 @@ function decideProactiveCompact(state3, occupancy, cap) {
|
|
|
46409
46439
|
};
|
|
46410
46440
|
}
|
|
46411
46441
|
|
|
46442
|
+
// gateway/idle-clear.ts
|
|
46443
|
+
var DEFAULT_IDLE_CLEAR_MS = 3 * 60 * 60 * 1000;
|
|
46444
|
+
function decideIdleClear(state3, now) {
|
|
46445
|
+
if (state3.idleClearMs <= 0)
|
|
46446
|
+
return { clear: false };
|
|
46447
|
+
if (state3.turnInFlight)
|
|
46448
|
+
return { clear: false };
|
|
46449
|
+
if (state3.alreadyCleared)
|
|
46450
|
+
return { clear: false };
|
|
46451
|
+
if (now - state3.lastActivityAt < state3.idleClearMs)
|
|
46452
|
+
return { clear: false };
|
|
46453
|
+
return { clear: true };
|
|
46454
|
+
}
|
|
46455
|
+
function idleDurationToMs(raw) {
|
|
46456
|
+
const m = /^(\d+)([smh])$/.exec(raw.trim());
|
|
46457
|
+
if (!m)
|
|
46458
|
+
return null;
|
|
46459
|
+
const n = Number(m[1]);
|
|
46460
|
+
switch (m[2]) {
|
|
46461
|
+
case "s":
|
|
46462
|
+
return n * 1000;
|
|
46463
|
+
case "m":
|
|
46464
|
+
return n * 60000;
|
|
46465
|
+
case "h":
|
|
46466
|
+
return n * 3600000;
|
|
46467
|
+
default:
|
|
46468
|
+
return null;
|
|
46469
|
+
}
|
|
46470
|
+
}
|
|
46471
|
+
|
|
46412
46472
|
// gateway/compact-notify.ts
|
|
46413
46473
|
function idleCompactNotifyState() {
|
|
46414
46474
|
return { phase: "idle", fileAtStart: null };
|
|
@@ -46982,13 +47042,13 @@ function startWebhookIngestServer(opts) {
|
|
|
46982
47042
|
}
|
|
46983
47043
|
|
|
46984
47044
|
// ../src/web/webhook-gateway-record.ts
|
|
46985
|
-
import { appendFileSync as appendFileSync4, mkdirSync as
|
|
46986
|
-
import { join as
|
|
47045
|
+
import { appendFileSync as appendFileSync4, mkdirSync as mkdirSync15 } from "fs";
|
|
47046
|
+
import { join as join21 } from "path";
|
|
46987
47047
|
import { homedir as homedir10 } from "os";
|
|
46988
47048
|
|
|
46989
47049
|
// ../src/web/webhook-handler.ts
|
|
46990
|
-
import { appendFileSync as appendFileSync3, existsSync as existsSync22, mkdirSync as
|
|
46991
|
-
import { join as
|
|
47050
|
+
import { appendFileSync as appendFileSync3, existsSync as existsSync22, mkdirSync as mkdirSync13, readFileSync as readFileSync16, writeFileSync as writeFileSync10 } from "fs";
|
|
47051
|
+
import { join as join19 } from "path";
|
|
46992
47052
|
var DEDUP_MAX = 1000;
|
|
46993
47053
|
var DEDUP_TTL_MS = 24 * 60 * 60 * 1000;
|
|
46994
47054
|
function loadDedupFile(path) {
|
|
@@ -47009,7 +47069,7 @@ function saveDedupFile(path, deliveries, now) {
|
|
|
47009
47069
|
}
|
|
47010
47070
|
const sorted = Object.entries(pruned).sort((a, b) => b[1] - a[1]).slice(0, DEDUP_MAX);
|
|
47011
47071
|
const final = Object.fromEntries(sorted);
|
|
47012
|
-
|
|
47072
|
+
writeFileSync10(path, JSON.stringify({ deliveries: final }), {
|
|
47013
47073
|
mode: 384
|
|
47014
47074
|
});
|
|
47015
47075
|
}
|
|
@@ -47017,8 +47077,8 @@ var agentDedupCache = new Map;
|
|
|
47017
47077
|
function createFileDedupStore(resolveAgentDir) {
|
|
47018
47078
|
return {
|
|
47019
47079
|
check(agent, deliveryId, now) {
|
|
47020
|
-
const telegramDir =
|
|
47021
|
-
const filePath =
|
|
47080
|
+
const telegramDir = join19(resolveAgentDir(agent), "telegram");
|
|
47081
|
+
const filePath = join19(telegramDir, "webhook-dedup.json");
|
|
47022
47082
|
if (!agentDedupCache.has(agent)) {
|
|
47023
47083
|
agentDedupCache.set(agent, loadDedupFile(filePath));
|
|
47024
47084
|
}
|
|
@@ -47028,7 +47088,7 @@ function createFileDedupStore(resolveAgentDir) {
|
|
|
47028
47088
|
}
|
|
47029
47089
|
deliveries[deliveryId] = now;
|
|
47030
47090
|
try {
|
|
47031
|
-
|
|
47091
|
+
mkdirSync13(telegramDir, { recursive: true });
|
|
47032
47092
|
saveDedupFile(filePath, deliveries, now);
|
|
47033
47093
|
} catch {}
|
|
47034
47094
|
return;
|
|
@@ -47039,8 +47099,8 @@ var tokenBuckets = new Map;
|
|
|
47039
47099
|
var throttleIssueWindow = new Map;
|
|
47040
47100
|
|
|
47041
47101
|
// ../src/web/webhook-dispatch.ts
|
|
47042
|
-
import { existsSync as existsSync23, mkdirSync as
|
|
47043
|
-
import { join as
|
|
47102
|
+
import { existsSync as existsSync23, mkdirSync as mkdirSync14, readFileSync as readFileSync17, writeFileSync as writeFileSync11 } from "fs";
|
|
47103
|
+
import { join as join20 } from "path";
|
|
47044
47104
|
import { homedir as homedir9 } from "os";
|
|
47045
47105
|
|
|
47046
47106
|
// ../src/agent-scheduler/ipc-client.ts
|
|
@@ -47349,7 +47409,7 @@ function loadCooldownFile(path) {
|
|
|
47349
47409
|
}
|
|
47350
47410
|
function saveCooldownFile(path, dispatches) {
|
|
47351
47411
|
try {
|
|
47352
|
-
|
|
47412
|
+
writeFileSync11(path, JSON.stringify({ dispatches }), {
|
|
47353
47413
|
mode: 384
|
|
47354
47414
|
});
|
|
47355
47415
|
} catch {}
|
|
@@ -47360,8 +47420,8 @@ function createFileCooldownStore(resolveAgentDir) {
|
|
|
47360
47420
|
isCoolingDown(agent, key, cooldownMs, now) {
|
|
47361
47421
|
if (cooldownMs <= 0)
|
|
47362
47422
|
return false;
|
|
47363
|
-
const telegramDir =
|
|
47364
|
-
const filePath =
|
|
47423
|
+
const telegramDir = join20(resolveAgentDir(agent), "telegram");
|
|
47424
|
+
const filePath = join20(telegramDir, "webhook-cooldown.json");
|
|
47365
47425
|
if (!cache.has(agent)) {
|
|
47366
47426
|
cache.set(agent, loadCooldownFile(filePath));
|
|
47367
47427
|
}
|
|
@@ -47372,7 +47432,7 @@ function createFileCooldownStore(resolveAgentDir) {
|
|
|
47372
47432
|
}
|
|
47373
47433
|
dispatches[key] = now;
|
|
47374
47434
|
try {
|
|
47375
|
-
|
|
47435
|
+
mkdirSync14(telegramDir, { recursive: true });
|
|
47376
47436
|
saveCooldownFile(filePath, dispatches);
|
|
47377
47437
|
} catch {}
|
|
47378
47438
|
return false;
|
|
@@ -47418,9 +47478,9 @@ async function defaultInject(socketPath, agentName3, inbound) {
|
|
|
47418
47478
|
}
|
|
47419
47479
|
function injectWebhookInbound(agent, prompt, ctx, deps = {}) {
|
|
47420
47480
|
const log = deps.log ?? ((s) => process.stderr.write(s));
|
|
47421
|
-
const resolveAgentDir = deps.resolveAgentDir ?? ((a) =>
|
|
47481
|
+
const resolveAgentDir = deps.resolveAgentDir ?? ((a) => join20(homedir9(), ".switchroom", "agents", a));
|
|
47422
47482
|
const now = (deps.now ?? Date.now)();
|
|
47423
|
-
const socketPath =
|
|
47483
|
+
const socketPath = join20(resolveAgentDir(agent), "telegram", "gateway.sock");
|
|
47424
47484
|
const inbound = {
|
|
47425
47485
|
type: "inbound",
|
|
47426
47486
|
chatId: ctx.chatId,
|
|
@@ -47492,7 +47552,7 @@ function evaluateDispatch(args, deps = {}) {
|
|
|
47492
47552
|
const log = deps.log ?? ((s) => process.stderr.write(s));
|
|
47493
47553
|
const now = (deps.now ?? Date.now)();
|
|
47494
47554
|
const nowDate = deps.nowDate ?? (() => new Date(now));
|
|
47495
|
-
const resolveAgentDir = deps.resolveAgentDir ?? ((a) =>
|
|
47555
|
+
const resolveAgentDir = deps.resolveAgentDir ?? ((a) => join20(homedir9(), ".switchroom", "agents", a));
|
|
47496
47556
|
const cooldownStore = deps.cooldownStore ?? createFileCooldownStore(resolveAgentDir);
|
|
47497
47557
|
if (!DISPATCH_SOURCES.includes(args.source))
|
|
47498
47558
|
return 0;
|
|
@@ -47568,10 +47628,10 @@ function resolveChannelTarget(config, agentName3) {
|
|
|
47568
47628
|
function recordWebhookEvent(rec, deps = {}) {
|
|
47569
47629
|
const log = deps.log ?? ((s) => process.stderr.write(s));
|
|
47570
47630
|
const now = rec.ts || (deps.now ?? Date.now)();
|
|
47571
|
-
const resolveAgentDir = deps.resolveAgentDir ?? ((a) =>
|
|
47631
|
+
const resolveAgentDir = deps.resolveAgentDir ?? ((a) => join21(homedir10(), ".switchroom", "agents", a));
|
|
47572
47632
|
const dedupStore = deps.dedupStore ?? createFileDedupStore(resolveAgentDir);
|
|
47573
47633
|
const agent = rec.agent;
|
|
47574
|
-
const telegramDir =
|
|
47634
|
+
const telegramDir = join21(resolveAgentDir(agent), "telegram");
|
|
47575
47635
|
if (rec.source === "github" && rec.delivery_id) {
|
|
47576
47636
|
const originalTs = dedupStore.check(agent, rec.delivery_id, now);
|
|
47577
47637
|
if (originalTs !== undefined) {
|
|
@@ -47580,9 +47640,9 @@ function recordWebhookEvent(rec, deps = {}) {
|
|
|
47580
47640
|
return { status: "deduped", ts: originalTs };
|
|
47581
47641
|
}
|
|
47582
47642
|
}
|
|
47583
|
-
const logPath =
|
|
47643
|
+
const logPath = join21(telegramDir, "webhook-events.jsonl");
|
|
47584
47644
|
try {
|
|
47585
|
-
|
|
47645
|
+
mkdirSync15(telegramDir, { recursive: true });
|
|
47586
47646
|
const record = {
|
|
47587
47647
|
ts: now,
|
|
47588
47648
|
source: rec.source,
|
|
@@ -50257,6 +50317,20 @@ function createPollHealthCheck(options) {
|
|
|
50257
50317
|
};
|
|
50258
50318
|
}
|
|
50259
50319
|
|
|
50320
|
+
// gateway/poll-stall-recovery.ts
|
|
50321
|
+
var POLL_STALL_EXIT_CODE = 1;
|
|
50322
|
+
function recoverFromPollStall(deps = {}) {
|
|
50323
|
+
const exit = deps.exit ?? ((code) => process.exit(code));
|
|
50324
|
+
const log = deps.log ?? ((msg) => {
|
|
50325
|
+
process.stderr.write(msg.endsWith(`
|
|
50326
|
+
`) ? msg : msg + `
|
|
50327
|
+
`);
|
|
50328
|
+
});
|
|
50329
|
+
const agentName3 = deps.agentName ?? "-";
|
|
50330
|
+
log(`telegram gateway: poll.health_check.stall_recovery exiting code=${POLL_STALL_EXIT_CODE} ` + `pid=${process.pid} agent=${agentName3} \u2014 supervisor will restart the gateway with a fresh runner ` + `(not awaiting runnerHandle.stop(): grammy stop() blocks on a non-abortable getUpdates retry backoff during an outage)`);
|
|
50331
|
+
exit(POLL_STALL_EXIT_CODE);
|
|
50332
|
+
}
|
|
50333
|
+
|
|
50260
50334
|
// gateway/reaction-trigger.ts
|
|
50261
50335
|
var REACTIONS_DEFAULTS = Object.freeze({
|
|
50262
50336
|
enabled: true,
|
|
@@ -50481,10 +50555,10 @@ function escapeBody2(s) {
|
|
|
50481
50555
|
}
|
|
50482
50556
|
|
|
50483
50557
|
// gateway/pid-file.ts
|
|
50484
|
-
import { writeFileSync as
|
|
50558
|
+
import { writeFileSync as writeFileSync12, readFileSync as readFileSync18, unlinkSync as unlinkSync7, renameSync as renameSync6 } from "node:fs";
|
|
50485
50559
|
function writePidFile(path, record) {
|
|
50486
50560
|
const tmp = `${path}.tmp-${process.pid}-${Date.now()}`;
|
|
50487
|
-
|
|
50561
|
+
writeFileSync12(tmp, JSON.stringify(record), "utf-8");
|
|
50488
50562
|
renameSync6(tmp, path);
|
|
50489
50563
|
}
|
|
50490
50564
|
function clearPidFile(path) {
|
|
@@ -50706,10 +50780,10 @@ function safeCount(fn) {
|
|
|
50706
50780
|
}
|
|
50707
50781
|
|
|
50708
50782
|
// gateway/session-marker.ts
|
|
50709
|
-
import { writeFileSync as
|
|
50783
|
+
import { writeFileSync as writeFileSync13, readFileSync as readFileSync20, renameSync as renameSync7, unlinkSync as unlinkSync8 } from "node:fs";
|
|
50710
50784
|
function writeSessionMarker(path, marker) {
|
|
50711
50785
|
const tmp = `${path}.tmp-${process.pid}-${Date.now()}`;
|
|
50712
|
-
|
|
50786
|
+
writeFileSync13(tmp, JSON.stringify(marker), "utf-8");
|
|
50713
50787
|
renameSync7(tmp, path);
|
|
50714
50788
|
}
|
|
50715
50789
|
function readSessionMarker(path) {
|
|
@@ -50736,11 +50810,11 @@ function shouldFireRestartBanner(input) {
|
|
|
50736
50810
|
}
|
|
50737
50811
|
|
|
50738
50812
|
// gateway/clean-shutdown-marker.ts
|
|
50739
|
-
import { writeFileSync as
|
|
50813
|
+
import { writeFileSync as writeFileSync14, readFileSync as readFileSync21, renameSync as renameSync8, unlinkSync as unlinkSync9 } from "node:fs";
|
|
50740
50814
|
var DEFAULT_MAX_AGE_MS = 60000;
|
|
50741
50815
|
function writeCleanShutdownMarker(path, marker) {
|
|
50742
50816
|
const tmp = `${path}.tmp-${process.pid}-${Date.now()}`;
|
|
50743
|
-
|
|
50817
|
+
writeFileSync14(tmp, JSON.stringify(marker), "utf-8");
|
|
50744
50818
|
renameSync8(tmp, path);
|
|
50745
50819
|
}
|
|
50746
50820
|
function readCleanShutdownMarker(path) {
|
|
@@ -51333,7 +51407,7 @@ import {
|
|
|
51333
51407
|
readdirSync as readdirSync3,
|
|
51334
51408
|
readFileSync as readFileSync23
|
|
51335
51409
|
} from "fs";
|
|
51336
|
-
import { join as
|
|
51410
|
+
import { join as join23 } from "path";
|
|
51337
51411
|
|
|
51338
51412
|
// operator-events.ts
|
|
51339
51413
|
function classifyClaudeError(raw) {
|
|
@@ -51819,18 +51893,18 @@ function bumpSubagentActivity(db2, args) {
|
|
|
51819
51893
|
import {
|
|
51820
51894
|
closeSync as closeSync3,
|
|
51821
51895
|
existsSync as existsSync24,
|
|
51822
|
-
mkdirSync as
|
|
51896
|
+
mkdirSync as mkdirSync16,
|
|
51823
51897
|
openSync as openSync3,
|
|
51824
51898
|
readFileSync as readFileSync22,
|
|
51825
51899
|
statSync as statSync6,
|
|
51826
51900
|
unlinkSync as unlinkSync10,
|
|
51827
51901
|
utimesSync,
|
|
51828
|
-
writeFileSync as
|
|
51902
|
+
writeFileSync as writeFileSync15
|
|
51829
51903
|
} from "node:fs";
|
|
51830
|
-
import { join as
|
|
51904
|
+
import { join as join22 } from "node:path";
|
|
51831
51905
|
var TURN_ACTIVE_MARKER_FILE = "turn-active.json";
|
|
51832
51906
|
function touchTurnActiveMarker(stateDir) {
|
|
51833
|
-
const path =
|
|
51907
|
+
const path = join22(stateDir, TURN_ACTIVE_MARKER_FILE);
|
|
51834
51908
|
if (!existsSync24(path))
|
|
51835
51909
|
return;
|
|
51836
51910
|
const now = new Date;
|
|
@@ -52357,8 +52431,8 @@ function startSubagentWatcher(config) {
|
|
|
52357
52431
|
function rescanSubagentDirs() {
|
|
52358
52432
|
if (stopped)
|
|
52359
52433
|
return;
|
|
52360
|
-
const claudeHome =
|
|
52361
|
-
const projectsRoot =
|
|
52434
|
+
const claudeHome = join23(agentDir, ".claude");
|
|
52435
|
+
const projectsRoot = join23(claudeHome, "projects");
|
|
52362
52436
|
if (!fs2.existsSync(projectsRoot))
|
|
52363
52437
|
return;
|
|
52364
52438
|
let projectDirs;
|
|
@@ -52375,7 +52449,7 @@ function startSubagentWatcher(config) {
|
|
|
52375
52449
|
}
|
|
52376
52450
|
continue;
|
|
52377
52451
|
}
|
|
52378
|
-
const projectPath =
|
|
52452
|
+
const projectPath = join23(projectsRoot, pDir);
|
|
52379
52453
|
let sessionDirs;
|
|
52380
52454
|
try {
|
|
52381
52455
|
sessionDirs = fs2.readdirSync(projectPath);
|
|
@@ -52385,7 +52459,7 @@ function startSubagentWatcher(config) {
|
|
|
52385
52459
|
for (const sDir of sessionDirs) {
|
|
52386
52460
|
if (sDir.endsWith(".jsonl"))
|
|
52387
52461
|
continue;
|
|
52388
|
-
const subagentsPath =
|
|
52462
|
+
const subagentsPath = join23(projectPath, sDir, "subagents");
|
|
52389
52463
|
if (!fs2.existsSync(subagentsPath))
|
|
52390
52464
|
continue;
|
|
52391
52465
|
if (!dirWatchers.has(subagentsPath)) {
|
|
@@ -52393,7 +52467,7 @@ function startSubagentWatcher(config) {
|
|
|
52393
52467
|
const w = fs2.watch(subagentsPath, (_event, filename) => {
|
|
52394
52468
|
if (!filename || !filename.toString().startsWith("agent-") || !filename.toString().endsWith(".jsonl"))
|
|
52395
52469
|
return;
|
|
52396
|
-
const filePath =
|
|
52470
|
+
const filePath = join23(subagentsPath, filename.toString());
|
|
52397
52471
|
if (!knownFiles.has(filePath)) {
|
|
52398
52472
|
scanSubagentsDir(subagentsPath);
|
|
52399
52473
|
}
|
|
@@ -52418,7 +52492,7 @@ function startSubagentWatcher(config) {
|
|
|
52418
52492
|
for (const e of entries) {
|
|
52419
52493
|
if (!e.startsWith("agent-") || !e.endsWith(".jsonl"))
|
|
52420
52494
|
continue;
|
|
52421
|
-
const filePath =
|
|
52495
|
+
const filePath = join23(subagentsPath, e);
|
|
52422
52496
|
if (knownFiles.has(filePath))
|
|
52423
52497
|
continue;
|
|
52424
52498
|
const agentId = e.slice("agent-".length, -".jsonl".length);
|
|
@@ -52544,15 +52618,15 @@ function determineRestartReason(opts) {
|
|
|
52544
52618
|
init_boot_card();
|
|
52545
52619
|
|
|
52546
52620
|
// gateway/update-announce.ts
|
|
52547
|
-
import { existsSync as existsSync30, mkdirSync as
|
|
52548
|
-
import { join as
|
|
52621
|
+
import { existsSync as existsSync30, mkdirSync as mkdirSync20, openSync as openSync5, closeSync as closeSync5, readFileSync as readFileSync29 } from "node:fs";
|
|
52622
|
+
import { join as join28 } from "node:path";
|
|
52549
52623
|
import { homedir as homedir12 } from "node:os";
|
|
52550
52624
|
|
|
52551
52625
|
// ../src/host-control/audit-reader.ts
|
|
52552
52626
|
import { homedir as homedir11 } from "node:os";
|
|
52553
|
-
import { join as
|
|
52627
|
+
import { join as join27 } from "node:path";
|
|
52554
52628
|
function defaultAuditLogPath(home2 = homedir11()) {
|
|
52555
|
-
return
|
|
52629
|
+
return join27(home2, ".switchroom", "host-control-audit.log");
|
|
52556
52630
|
}
|
|
52557
52631
|
function parseAuditLine(line) {
|
|
52558
52632
|
const trimmed = line.trim();
|
|
@@ -52720,15 +52794,15 @@ function renderUpdateOutcomeLine(entry) {
|
|
|
52720
52794
|
`);
|
|
52721
52795
|
}
|
|
52722
52796
|
function claimUpdateAnnouncement(requestId, opts = {}) {
|
|
52723
|
-
const stateDir = opts.stateDir ?? process.env.TELEGRAM_STATE_DIR ??
|
|
52724
|
-
const dir =
|
|
52797
|
+
const stateDir = opts.stateDir ?? process.env.TELEGRAM_STATE_DIR ?? join28(homedir12(), ".switchroom");
|
|
52798
|
+
const dir = join28(stateDir, "update-announced");
|
|
52725
52799
|
try {
|
|
52726
|
-
|
|
52800
|
+
mkdirSync20(dir, { recursive: true });
|
|
52727
52801
|
} catch {
|
|
52728
52802
|
return false;
|
|
52729
52803
|
}
|
|
52730
52804
|
const safeId = requestId.replace(/[^A-Za-z0-9_.-]/g, "_").slice(0, 200);
|
|
52731
|
-
const path =
|
|
52805
|
+
const path = join28(dir, safeId);
|
|
52732
52806
|
try {
|
|
52733
52807
|
const fd = openSync5(path, "wx");
|
|
52734
52808
|
closeSync5(fd);
|
|
@@ -52747,7 +52821,7 @@ function maybeRenderUpdateAnnouncement(opts = {}) {
|
|
|
52747
52821
|
}
|
|
52748
52822
|
|
|
52749
52823
|
// issues-card.ts
|
|
52750
|
-
import { readFileSync as readFileSync30, writeFileSync as
|
|
52824
|
+
import { readFileSync as readFileSync30, writeFileSync as writeFileSync20 } from "node:fs";
|
|
52751
52825
|
var SEVERITY_EMOJI = {
|
|
52752
52826
|
info: "\u2139\ufe0f",
|
|
52753
52827
|
warn: "\u26a0\ufe0f",
|
|
@@ -52855,7 +52929,7 @@ function readPersistedMessageId(path, log) {
|
|
|
52855
52929
|
}
|
|
52856
52930
|
function writePersistedMessageId(path, messageId, log) {
|
|
52857
52931
|
try {
|
|
52858
|
-
|
|
52932
|
+
writeFileSync20(path, JSON.stringify({ messageId }) + `
|
|
52859
52933
|
`, { mode: 384 });
|
|
52860
52934
|
} catch (err) {
|
|
52861
52935
|
log(`issues-card: persist write failed (${err.message})`);
|
|
@@ -52949,23 +53023,23 @@ function createIssuesCardHandle(opts) {
|
|
|
52949
53023
|
|
|
52950
53024
|
// issues-watcher.ts
|
|
52951
53025
|
import { existsSync as existsSync32, statSync as statSync9 } from "node:fs";
|
|
52952
|
-
import { join as
|
|
53026
|
+
import { join as join30 } from "node:path";
|
|
52953
53027
|
|
|
52954
53028
|
// ../src/issues/store.ts
|
|
52955
53029
|
import {
|
|
52956
53030
|
closeSync as closeSync6,
|
|
52957
53031
|
existsSync as existsSync31,
|
|
52958
|
-
mkdirSync as
|
|
53032
|
+
mkdirSync as mkdirSync21,
|
|
52959
53033
|
openSync as openSync6,
|
|
52960
53034
|
readdirSync as readdirSync5,
|
|
52961
53035
|
readFileSync as readFileSync31,
|
|
52962
53036
|
renameSync as renameSync11,
|
|
52963
53037
|
statSync as statSync8,
|
|
52964
53038
|
unlinkSync as unlinkSync11,
|
|
52965
|
-
writeFileSync as
|
|
53039
|
+
writeFileSync as writeFileSync21,
|
|
52966
53040
|
writeSync as writeSync2
|
|
52967
53041
|
} from "node:fs";
|
|
52968
|
-
import { join as
|
|
53042
|
+
import { join as join29 } from "node:path";
|
|
52969
53043
|
import { randomBytes as randomBytes5 } from "node:crypto";
|
|
52970
53044
|
import { execSync } from "node:child_process";
|
|
52971
53045
|
|
|
@@ -52984,7 +53058,7 @@ init_redact();
|
|
|
52984
53058
|
var ISSUES_FILE = "issues.jsonl";
|
|
52985
53059
|
var ISSUES_LOCK = "issues.lock";
|
|
52986
53060
|
function readAll(stateDir) {
|
|
52987
|
-
const path =
|
|
53061
|
+
const path = join29(stateDir, ISSUES_FILE);
|
|
52988
53062
|
if (!existsSync31(path))
|
|
52989
53063
|
return [];
|
|
52990
53064
|
let raw;
|
|
@@ -53021,7 +53095,7 @@ function list(stateDir, opts = {}) {
|
|
|
53021
53095
|
});
|
|
53022
53096
|
}
|
|
53023
53097
|
function resolve6(stateDir, fingerprint, nowFn = Date.now) {
|
|
53024
|
-
if (!existsSync31(
|
|
53098
|
+
if (!existsSync31(join29(stateDir, ISSUES_FILE)))
|
|
53025
53099
|
return 0;
|
|
53026
53100
|
return withLock(stateDir, () => {
|
|
53027
53101
|
const all = readAll(stateDir);
|
|
@@ -53039,13 +53113,13 @@ function resolve6(stateDir, fingerprint, nowFn = Date.now) {
|
|
|
53039
53113
|
});
|
|
53040
53114
|
}
|
|
53041
53115
|
function writeAll(stateDir, events) {
|
|
53042
|
-
const path =
|
|
53116
|
+
const path = join29(stateDir, ISSUES_FILE);
|
|
53043
53117
|
sweepOrphanTmpFiles(stateDir);
|
|
53044
53118
|
const tmp = `${path}.tmp-${process.pid}-${randomBytes5(4).toString("hex")}`;
|
|
53045
53119
|
const body = events.length === 0 ? "" : events.map((e) => JSON.stringify(e)).join(`
|
|
53046
53120
|
`) + `
|
|
53047
53121
|
`;
|
|
53048
|
-
|
|
53122
|
+
writeFileSync21(tmp, body, "utf-8");
|
|
53049
53123
|
renameSync11(tmp, path);
|
|
53050
53124
|
}
|
|
53051
53125
|
var ORPHAN_TMP_TTL_MS = 60000;
|
|
@@ -53061,7 +53135,7 @@ function sweepOrphanTmpFiles(stateDir) {
|
|
|
53061
53135
|
for (const entry of entries) {
|
|
53062
53136
|
if (!entry.startsWith(TMP_PREFIX))
|
|
53063
53137
|
continue;
|
|
53064
|
-
const tmpPath2 =
|
|
53138
|
+
const tmpPath2 = join29(stateDir, entry);
|
|
53065
53139
|
try {
|
|
53066
53140
|
const stat = statSync8(tmpPath2);
|
|
53067
53141
|
if (stat.mtimeMs < cutoff) {
|
|
@@ -53073,7 +53147,7 @@ function sweepOrphanTmpFiles(stateDir) {
|
|
|
53073
53147
|
var LOCK_RETRY_MS = 25;
|
|
53074
53148
|
var LOCK_TIMEOUT_MS = 1e4;
|
|
53075
53149
|
function withLock(stateDir, fn) {
|
|
53076
|
-
const lockPath =
|
|
53150
|
+
const lockPath = join29(stateDir, ISSUES_LOCK);
|
|
53077
53151
|
const startedAt = Date.now();
|
|
53078
53152
|
let fd = null;
|
|
53079
53153
|
while (fd === null) {
|
|
@@ -53158,7 +53232,7 @@ function isIssueEvent(v) {
|
|
|
53158
53232
|
// issues-watcher.ts
|
|
53159
53233
|
var DEFAULT_POLL_INTERVAL_MS2 = 2000;
|
|
53160
53234
|
function startIssuesWatcher(opts) {
|
|
53161
|
-
const path =
|
|
53235
|
+
const path = join30(opts.stateDir, ISSUES_FILE);
|
|
53162
53236
|
const log = opts.log ?? (() => {});
|
|
53163
53237
|
const intervalMs = opts.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS2;
|
|
53164
53238
|
const setIntervalFn = opts.setInterval ?? setInterval;
|
|
@@ -54092,8 +54166,8 @@ function extractFlowItems(line) {
|
|
|
54092
54166
|
}
|
|
54093
54167
|
|
|
54094
54168
|
// credits-watch.ts
|
|
54095
|
-
import { readFileSync as readFileSync32, writeFileSync as
|
|
54096
|
-
import { join as
|
|
54169
|
+
import { readFileSync as readFileSync32, writeFileSync as writeFileSync22, existsSync as existsSync33, mkdirSync as mkdirSync22 } from "fs";
|
|
54170
|
+
import { join as join31 } from "path";
|
|
54097
54171
|
var STATE_FILE = "credits-watch.json";
|
|
54098
54172
|
var DEFAULT_CREDIT_FATAL_REASONS = new Set;
|
|
54099
54173
|
var KNOWN_CREDIT_REASONS = [
|
|
@@ -54115,7 +54189,7 @@ function emptyCreditState() {
|
|
|
54115
54189
|
return { lastNotifiedReason: null, lastNotifiedAt: 0 };
|
|
54116
54190
|
}
|
|
54117
54191
|
function readClaudeJsonOverage(claudeConfigDir) {
|
|
54118
|
-
const path =
|
|
54192
|
+
const path = join31(claudeConfigDir, ".claude.json");
|
|
54119
54193
|
if (!existsSync33(path))
|
|
54120
54194
|
return null;
|
|
54121
54195
|
let raw;
|
|
@@ -54201,7 +54275,7 @@ function escapeHtml9(s) {
|
|
|
54201
54275
|
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
54202
54276
|
}
|
|
54203
54277
|
function loadCreditState(stateDir) {
|
|
54204
|
-
const path =
|
|
54278
|
+
const path = join31(stateDir, STATE_FILE);
|
|
54205
54279
|
if (!existsSync33(path))
|
|
54206
54280
|
return emptyCreditState();
|
|
54207
54281
|
try {
|
|
@@ -54217,15 +54291,15 @@ function loadCreditState(stateDir) {
|
|
|
54217
54291
|
return emptyCreditState();
|
|
54218
54292
|
}
|
|
54219
54293
|
function saveCreditState(stateDir, state4) {
|
|
54220
|
-
|
|
54221
|
-
const path =
|
|
54222
|
-
|
|
54294
|
+
mkdirSync22(stateDir, { recursive: true });
|
|
54295
|
+
const path = join31(stateDir, STATE_FILE);
|
|
54296
|
+
writeFileSync22(path, JSON.stringify(state4, null, 2) + `
|
|
54223
54297
|
`, { mode: 384 });
|
|
54224
54298
|
}
|
|
54225
54299
|
|
|
54226
54300
|
// quota-watch.ts
|
|
54227
|
-
import { readFileSync as readFileSync33, writeFileSync as
|
|
54228
|
-
import { join as
|
|
54301
|
+
import { readFileSync as readFileSync33, writeFileSync as writeFileSync23, existsSync as existsSync34, mkdirSync as mkdirSync23 } from "fs";
|
|
54302
|
+
import { join as join32 } from "path";
|
|
54229
54303
|
var STATE_FILE2 = "quota-watch.json";
|
|
54230
54304
|
function emptyQuotaWatchState() {
|
|
54231
54305
|
return {};
|
|
@@ -54406,7 +54480,7 @@ function escapeHtml10(s) {
|
|
|
54406
54480
|
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
54407
54481
|
}
|
|
54408
54482
|
function loadQuotaWatchState(stateDir) {
|
|
54409
|
-
const path =
|
|
54483
|
+
const path = join32(stateDir, STATE_FILE2);
|
|
54410
54484
|
if (!existsSync34(path))
|
|
54411
54485
|
return emptyQuotaWatchState();
|
|
54412
54486
|
try {
|
|
@@ -54427,9 +54501,9 @@ function loadQuotaWatchState(stateDir) {
|
|
|
54427
54501
|
}
|
|
54428
54502
|
}
|
|
54429
54503
|
function saveQuotaWatchState(stateDir, state4) {
|
|
54430
|
-
|
|
54431
|
-
const path =
|
|
54432
|
-
|
|
54504
|
+
mkdirSync23(stateDir, { recursive: true });
|
|
54505
|
+
const path = join32(stateDir, STATE_FILE2);
|
|
54506
|
+
writeFileSync23(path, JSON.stringify(state4, null, 2) + `
|
|
54433
54507
|
`, { mode: 384 });
|
|
54434
54508
|
}
|
|
54435
54509
|
function patchQuotaWatchState(current, accountLabel, accountState) {
|
|
@@ -54443,25 +54517,25 @@ init_auth_snapshot_format();
|
|
|
54443
54517
|
import {
|
|
54444
54518
|
closeSync as closeSync7,
|
|
54445
54519
|
existsSync as existsSync35,
|
|
54446
|
-
mkdirSync as
|
|
54520
|
+
mkdirSync as mkdirSync24,
|
|
54447
54521
|
openSync as openSync7,
|
|
54448
54522
|
readFileSync as readFileSync34,
|
|
54449
54523
|
statSync as statSync10,
|
|
54450
54524
|
unlinkSync as unlinkSync12,
|
|
54451
54525
|
utimesSync as utimesSync2,
|
|
54452
|
-
writeFileSync as
|
|
54526
|
+
writeFileSync as writeFileSync24
|
|
54453
54527
|
} from "node:fs";
|
|
54454
|
-
import { join as
|
|
54528
|
+
import { join as join33 } from "node:path";
|
|
54455
54529
|
var TURN_ACTIVE_MARKER_FILE2 = "turn-active.json";
|
|
54456
54530
|
function writeTurnActiveMarker(stateDir, marker) {
|
|
54457
54531
|
try {
|
|
54458
|
-
|
|
54459
|
-
|
|
54532
|
+
mkdirSync24(stateDir, { recursive: true });
|
|
54533
|
+
writeFileSync24(join33(stateDir, TURN_ACTIVE_MARKER_FILE2), JSON.stringify(marker, null, 2) + `
|
|
54460
54534
|
`, { mode: 384 });
|
|
54461
54535
|
} catch {}
|
|
54462
54536
|
}
|
|
54463
54537
|
function touchTurnActiveMarker2(stateDir) {
|
|
54464
|
-
const path =
|
|
54538
|
+
const path = join33(stateDir, TURN_ACTIVE_MARKER_FILE2);
|
|
54465
54539
|
if (!existsSync35(path))
|
|
54466
54540
|
return;
|
|
54467
54541
|
const now = new Date;
|
|
@@ -54476,11 +54550,11 @@ function touchTurnActiveMarker2(stateDir) {
|
|
|
54476
54550
|
}
|
|
54477
54551
|
function removeTurnActiveMarker(stateDir) {
|
|
54478
54552
|
try {
|
|
54479
|
-
unlinkSync12(
|
|
54553
|
+
unlinkSync12(join33(stateDir, TURN_ACTIVE_MARKER_FILE2));
|
|
54480
54554
|
} catch {}
|
|
54481
54555
|
}
|
|
54482
54556
|
function sweepStaleTurnActiveMarker(stateDir, opts) {
|
|
54483
|
-
const path =
|
|
54557
|
+
const path = join33(stateDir, TURN_ACTIVE_MARKER_FILE2);
|
|
54484
54558
|
if (!existsSync35(path))
|
|
54485
54559
|
return false;
|
|
54486
54560
|
const now = opts.now ?? Date.now();
|
|
@@ -54511,7 +54585,7 @@ function sweepStaleTurnActiveMarker(stateDir, opts) {
|
|
|
54511
54585
|
}
|
|
54512
54586
|
}
|
|
54513
54587
|
function readTurnActiveMarkerAgeMs(stateDir, now) {
|
|
54514
|
-
const path =
|
|
54588
|
+
const path = join33(stateDir, TURN_ACTIVE_MARKER_FILE2);
|
|
54515
54589
|
try {
|
|
54516
54590
|
const st = statSync10(path);
|
|
54517
54591
|
return (now ?? Date.now()) - st.mtimeMs;
|
|
@@ -54521,11 +54595,11 @@ function readTurnActiveMarkerAgeMs(stateDir, now) {
|
|
|
54521
54595
|
}
|
|
54522
54596
|
|
|
54523
54597
|
// ../src/build-info.ts
|
|
54524
|
-
var VERSION = "0.15.
|
|
54525
|
-
var COMMIT_SHA = "
|
|
54526
|
-
var COMMIT_DATE = "2026-06-
|
|
54598
|
+
var VERSION = "0.15.43";
|
|
54599
|
+
var COMMIT_SHA = "5480a4c3";
|
|
54600
|
+
var COMMIT_DATE = "2026-06-19T12:01:56+10:00";
|
|
54527
54601
|
var LATEST_PR = null;
|
|
54528
|
-
var COMMITS_AHEAD_OF_TAG =
|
|
54602
|
+
var COMMITS_AHEAD_OF_TAG = 2;
|
|
54529
54603
|
|
|
54530
54604
|
// gateway/boot-version.ts
|
|
54531
54605
|
function formatRelativeAgo(iso) {
|
|
@@ -54598,11 +54672,11 @@ init_peercred();
|
|
|
54598
54672
|
import * as net5 from "node:net";
|
|
54599
54673
|
import * as fs2 from "node:fs";
|
|
54600
54674
|
import { homedir as homedir13 } from "node:os";
|
|
54601
|
-
import { join as
|
|
54675
|
+
import { join as join34 } from "node:path";
|
|
54602
54676
|
var DEFAULT_TIMEOUT_MS4 = 2000;
|
|
54603
54677
|
var UNLOCK_TIMEOUT_MS = 30000;
|
|
54604
|
-
var LEGACY_SOCKET_PATH2 =
|
|
54605
|
-
var OPERATOR_SOCKET_PATH2 =
|
|
54678
|
+
var LEGACY_SOCKET_PATH2 = join34(homedir13(), ".switchroom", "vault-broker.sock");
|
|
54679
|
+
var OPERATOR_SOCKET_PATH2 = join34(homedir13(), ".switchroom", "broker-operator", "sock");
|
|
54606
54680
|
function defaultBrokerSocketPath2() {
|
|
54607
54681
|
if (fs2.existsSync(OPERATOR_SOCKET_PATH2))
|
|
54608
54682
|
return OPERATOR_SOCKET_PATH2;
|
|
@@ -55535,8 +55609,8 @@ function matchesAdminOnlyKey(key, patterns) {
|
|
|
55535
55609
|
}
|
|
55536
55610
|
|
|
55537
55611
|
// registry/turns-schema.ts
|
|
55538
|
-
import { chmodSync as chmodSync5, mkdirSync as
|
|
55539
|
-
import { join as
|
|
55612
|
+
import { chmodSync as chmodSync5, mkdirSync as mkdirSync25 } from "fs";
|
|
55613
|
+
import { join as join35 } from "path";
|
|
55540
55614
|
var DatabaseClass2 = null;
|
|
55541
55615
|
function loadDatabaseClass2() {
|
|
55542
55616
|
if (DatabaseClass2 != null)
|
|
@@ -55599,9 +55673,9 @@ function applySchema(db2) {
|
|
|
55599
55673
|
}
|
|
55600
55674
|
function openTurnsDb(agentDir) {
|
|
55601
55675
|
const Database = loadDatabaseClass2();
|
|
55602
|
-
const dir =
|
|
55603
|
-
|
|
55604
|
-
const path =
|
|
55676
|
+
const dir = join35(agentDir, "telegram");
|
|
55677
|
+
mkdirSync25(dir, { recursive: true, mode: 448 });
|
|
55678
|
+
const path = join35(dir, "registry.db");
|
|
55605
55679
|
const db2 = new Database(path, { create: true });
|
|
55606
55680
|
applySchema(db2);
|
|
55607
55681
|
try {
|
|
@@ -55917,11 +55991,11 @@ installGlobalErrorHandlers();
|
|
|
55917
55991
|
process.on("beforeExit", () => {
|
|
55918
55992
|
shutdownAnalytics();
|
|
55919
55993
|
});
|
|
55920
|
-
var STATE_DIR = process.env.TELEGRAM_STATE_DIR ??
|
|
55921
|
-
var ACCESS_FILE =
|
|
55922
|
-
var APPROVED_DIR =
|
|
55923
|
-
var ENV_FILE =
|
|
55924
|
-
var INBOX_DIR =
|
|
55994
|
+
var STATE_DIR = process.env.TELEGRAM_STATE_DIR ?? join36(homedir14(), ".claude", "channels", "telegram");
|
|
55995
|
+
var ACCESS_FILE = join36(STATE_DIR, "access.json");
|
|
55996
|
+
var APPROVED_DIR = join36(STATE_DIR, "approved");
|
|
55997
|
+
var ENV_FILE = join36(STATE_DIR, ".env");
|
|
55998
|
+
var INBOX_DIR = join36(STATE_DIR, "inbox");
|
|
55925
55999
|
function triggerSelfRestart(targetAgent, reason, delayMs = 300) {
|
|
55926
56000
|
const isDocker = process.env.SWITCHROOM_RUNTIME === "docker";
|
|
55927
56001
|
const selfAgent = process.env.SWITCHROOM_AGENT_NAME;
|
|
@@ -56113,7 +56187,7 @@ function assertSendable(f) {
|
|
|
56113
56187
|
} catch {
|
|
56114
56188
|
throw new Error(`refusing to send file \u2014 cannot resolve real path: ${f}`);
|
|
56115
56189
|
}
|
|
56116
|
-
const inbox =
|
|
56190
|
+
const inbox = join36(stateReal, "inbox");
|
|
56117
56191
|
if (real.startsWith(stateReal + sep3) && !real.startsWith(inbox + sep3)) {
|
|
56118
56192
|
throw new Error(`refusing to send channel state: ${f}`);
|
|
56119
56193
|
}
|
|
@@ -56200,9 +56274,9 @@ function assertAllowedChat(chat_id) {
|
|
|
56200
56274
|
function saveAccess(a) {
|
|
56201
56275
|
if (STATIC)
|
|
56202
56276
|
return;
|
|
56203
|
-
|
|
56277
|
+
mkdirSync27(STATE_DIR, { recursive: true, mode: 448 });
|
|
56204
56278
|
const tmp = ACCESS_FILE + ".tmp";
|
|
56205
|
-
|
|
56279
|
+
writeFileSync26(tmp, JSON.stringify(a, null, 2) + `
|
|
56206
56280
|
`, { mode: 384 });
|
|
56207
56281
|
renameSync12(tmp, ACCESS_FILE);
|
|
56208
56282
|
}
|
|
@@ -56222,7 +56296,7 @@ var HISTORY_ENABLED = HISTORY_ACCESS.historyEnabled !== false;
|
|
|
56222
56296
|
if (HISTORY_ENABLED) {
|
|
56223
56297
|
try {
|
|
56224
56298
|
initHistory(STATE_DIR, HISTORY_ACCESS.historyRetentionDays ?? 30);
|
|
56225
|
-
process.stderr.write(`telegram gateway: history capture enabled at ${
|
|
56299
|
+
process.stderr.write(`telegram gateway: history capture enabled at ${join36(STATE_DIR, "history.db")}
|
|
56226
56300
|
`);
|
|
56227
56301
|
} catch (err) {
|
|
56228
56302
|
process.stderr.write(`telegram gateway: history init failed (${err.message}) \u2014 capture disabled
|
|
@@ -56238,7 +56312,7 @@ try {
|
|
|
56238
56312
|
let markerTurnKey = null;
|
|
56239
56313
|
let markerAgeMs = null;
|
|
56240
56314
|
try {
|
|
56241
|
-
const markerPath =
|
|
56315
|
+
const markerPath = join36(STATE_DIR, TURN_ACTIVE_MARKER_FILE2);
|
|
56242
56316
|
if (existsSync38(markerPath)) {
|
|
56243
56317
|
const st = statSync13(markerPath);
|
|
56244
56318
|
markerAgeMs = Date.now() - st.mtimeMs;
|
|
@@ -56263,7 +56337,7 @@ try {
|
|
|
56263
56337
|
process.stderr.write(`telegram gateway: turn-registry boot-reaper stamped ${reaped} orphaned turn(s)${timeoutTurnKey ? ` (turnKey=${timeoutTurnKey} as 'timeout', markerAgeMs=${markerAgeMs})` : " as 'restart'"}
|
|
56264
56338
|
`);
|
|
56265
56339
|
} else {
|
|
56266
|
-
process.stderr.write(`telegram gateway: turn-registry initialized at ${
|
|
56340
|
+
process.stderr.write(`telegram gateway: turn-registry initialized at ${join36(agentDir, "telegram", "registry.db")}
|
|
56267
56341
|
`);
|
|
56268
56342
|
}
|
|
56269
56343
|
const pending2 = findLatestTurnIfInterrupted(turnsDb);
|
|
@@ -56300,7 +56374,7 @@ try {
|
|
|
56300
56374
|
`);
|
|
56301
56375
|
}
|
|
56302
56376
|
}
|
|
56303
|
-
const pendingEnvPath =
|
|
56377
|
+
const pendingEnvPath = join36(agentDir, ".pending-turn.env");
|
|
56304
56378
|
try {
|
|
56305
56379
|
if (pending2 != null) {
|
|
56306
56380
|
const lines = [
|
|
@@ -56314,7 +56388,7 @@ try {
|
|
|
56314
56388
|
pending2.interrupt_reason != null ? `SWITCHROOM_PENDING_INTERRUPT_REASON=${pending2.interrupt_reason}` : `SWITCHROOM_PENDING_INTERRUPT_REASON=`
|
|
56315
56389
|
];
|
|
56316
56390
|
const pendingEnvTmp = `${pendingEnvPath}.tmp-${process.pid}`;
|
|
56317
|
-
|
|
56391
|
+
writeFileSync26(pendingEnvTmp, lines.join(`
|
|
56318
56392
|
`) + `
|
|
56319
56393
|
`, { mode: 384 });
|
|
56320
56394
|
renameSync12(pendingEnvTmp, pendingEnvPath);
|
|
@@ -56393,7 +56467,7 @@ function checkApprovals() {
|
|
|
56393
56467
|
return;
|
|
56394
56468
|
}
|
|
56395
56469
|
for (const senderId of files) {
|
|
56396
|
-
const file =
|
|
56470
|
+
const file = join36(APPROVED_DIR, senderId);
|
|
56397
56471
|
bot.api.sendMessage(senderId, "Paired! Say hi to Claude.").then(() => rmSync4(file, { force: true }), (err) => {
|
|
56398
56472
|
process.stderr.write(`telegram gateway: failed to send approval confirm: ${err}
|
|
56399
56473
|
`);
|
|
@@ -56403,6 +56477,9 @@ function checkApprovals() {
|
|
|
56403
56477
|
}
|
|
56404
56478
|
if (!STATIC)
|
|
56405
56479
|
setInterval(checkApprovals, 5000).unref();
|
|
56480
|
+
var IDLE_CLEAR_CHECK_MS = Number(process.env.SWITCHROOM_IDLE_CLEAR_CHECK_MS ?? 60000);
|
|
56481
|
+
if (!STATIC && IDLE_CLEAR_CHECK_MS > 0)
|
|
56482
|
+
setInterval(maybeIdleClear, IDLE_CLEAR_CHECK_MS).unref();
|
|
56406
56483
|
var chatThreadMap = new Map;
|
|
56407
56484
|
var activeStatusReactions = new Map;
|
|
56408
56485
|
var activeReactionMsgIds = new Map;
|
|
@@ -56473,10 +56550,10 @@ function noteAgentOutputAt(key, ts) {
|
|
|
56473
56550
|
lastAgentOutputAt.delete(oldest);
|
|
56474
56551
|
}
|
|
56475
56552
|
}
|
|
56476
|
-
var OBLIGATION_STORE_PATH =
|
|
56553
|
+
var OBLIGATION_STORE_PATH = join36(STATE_DIR, "obligations.json");
|
|
56477
56554
|
var obligationStoreFs = {
|
|
56478
56555
|
readFileSync: (p) => readFileSync36(p, "utf8"),
|
|
56479
|
-
writeFileSync: (p, d) =>
|
|
56556
|
+
writeFileSync: (p, d) => writeFileSync26(p, d),
|
|
56480
56557
|
renameSync: (a, b) => renameSync12(a, b),
|
|
56481
56558
|
existsSync: (p) => existsSync38(p)
|
|
56482
56559
|
};
|
|
@@ -56864,8 +56941,33 @@ function purgeReactionTracking(key, endingTurn) {
|
|
|
56864
56941
|
} else {
|
|
56865
56942
|
maybeProactiveCompact();
|
|
56866
56943
|
}
|
|
56944
|
+
snapshotContextOccupancy();
|
|
56867
56945
|
}
|
|
56868
56946
|
}
|
|
56947
|
+
function snapshotContextOccupancy() {
|
|
56948
|
+
try {
|
|
56949
|
+
const agentName3 = process.env.SWITCHROOM_AGENT_NAME;
|
|
56950
|
+
const file = lastSessionActiveFile;
|
|
56951
|
+
if (!agentName3 || !file)
|
|
56952
|
+
return;
|
|
56953
|
+
const turns = readTurnUsages(file, 1);
|
|
56954
|
+
if (turns.length === 0)
|
|
56955
|
+
return;
|
|
56956
|
+
const t = turns[0];
|
|
56957
|
+
const occupancy = t.input + t.cacheRead + t.cacheCreate;
|
|
56958
|
+
let cap = null;
|
|
56959
|
+
try {
|
|
56960
|
+
const cfg = loadConfig2();
|
|
56961
|
+
const rawAgent = cfg.agents?.[agentName3] ?? {};
|
|
56962
|
+
const resolved = resolveAgentConfig2(cfg.defaults, cfg.profiles, rawAgent);
|
|
56963
|
+
cap = resolved.session?.max_context_tokens ?? null;
|
|
56964
|
+
} catch {
|
|
56965
|
+
cap = null;
|
|
56966
|
+
}
|
|
56967
|
+
const stateDir = process.env.SWITCHROOM_AGENT_STATE_DIR ?? "/state/agent";
|
|
56968
|
+
writeContextOccupancySnapshot(stateDir, buildContextOccupancy(occupancy, cap, Date.now()));
|
|
56969
|
+
} catch {}
|
|
56970
|
+
}
|
|
56869
56971
|
function releaseTurnBufferGate(key, endingTurn) {
|
|
56870
56972
|
if (!activeTurnStartedAt.has(key))
|
|
56871
56973
|
return;
|
|
@@ -56957,6 +57059,81 @@ function maybeProactiveCompact() {
|
|
|
56957
57059
|
compactDispatching = false;
|
|
56958
57060
|
});
|
|
56959
57061
|
}
|
|
57062
|
+
var lastIdleActivityAt = Date.now();
|
|
57063
|
+
var idleAutoCleared = false;
|
|
57064
|
+
var idleClearDispatching = false;
|
|
57065
|
+
function markIdleActivity() {
|
|
57066
|
+
lastIdleActivityAt = Date.now();
|
|
57067
|
+
idleAutoCleared = false;
|
|
57068
|
+
}
|
|
57069
|
+
function resolveIdleClearMs() {
|
|
57070
|
+
const env = process.env.SWITCHROOM_IDLE_CLEAR_MS;
|
|
57071
|
+
if (env != null && env !== "") {
|
|
57072
|
+
const n = Number(env);
|
|
57073
|
+
return Number.isFinite(n) && n >= 0 ? n : DEFAULT_IDLE_CLEAR_MS;
|
|
57074
|
+
}
|
|
57075
|
+
try {
|
|
57076
|
+
const agentName3 = process.env.SWITCHROOM_AGENT_NAME;
|
|
57077
|
+
if (!agentName3)
|
|
57078
|
+
return DEFAULT_IDLE_CLEAR_MS;
|
|
57079
|
+
const cfg = loadConfig2();
|
|
57080
|
+
const rawAgent = cfg.agents?.[agentName3] ?? {};
|
|
57081
|
+
const resolved = resolveAgentConfig2(cfg.defaults, cfg.profiles, rawAgent);
|
|
57082
|
+
const raw = resolved.session?.idle_clear_after;
|
|
57083
|
+
if (raw == null)
|
|
57084
|
+
return DEFAULT_IDLE_CLEAR_MS;
|
|
57085
|
+
const ms = idleDurationToMs(raw);
|
|
57086
|
+
return ms == null ? DEFAULT_IDLE_CLEAR_MS : ms;
|
|
57087
|
+
} catch {
|
|
57088
|
+
return DEFAULT_IDLE_CLEAR_MS;
|
|
57089
|
+
}
|
|
57090
|
+
}
|
|
57091
|
+
function maybeIdleClear() {
|
|
57092
|
+
if (idleClearDispatching)
|
|
57093
|
+
return;
|
|
57094
|
+
const agentName3 = process.env.SWITCHROOM_AGENT_NAME;
|
|
57095
|
+
if (!agentName3)
|
|
57096
|
+
return;
|
|
57097
|
+
const idleClearMs = resolveIdleClearMs();
|
|
57098
|
+
const decision = decideIdleClear({
|
|
57099
|
+
lastActivityAt: lastIdleActivityAt,
|
|
57100
|
+
idleClearMs,
|
|
57101
|
+
alreadyCleared: idleAutoCleared,
|
|
57102
|
+
turnInFlight: turnInFlightForGate()
|
|
57103
|
+
}, Date.now());
|
|
57104
|
+
if (!decision.clear)
|
|
57105
|
+
return;
|
|
57106
|
+
idleAutoCleared = true;
|
|
57107
|
+
idleClearDispatching = true;
|
|
57108
|
+
process.stderr.write(`telegram gateway: idle auto-/clear for ${agentName3} (idle >= ${Math.round(idleClearMs / 60000)}m)
|
|
57109
|
+
`);
|
|
57110
|
+
injectSlashCommand(agentName3, "/clear").then(() => {
|
|
57111
|
+
postIdleClearNotice(idleClearMs);
|
|
57112
|
+
}).catch((err) => {
|
|
57113
|
+
process.stderr.write(`telegram gateway: idle /clear inject failed for ${agentName3}: ${err instanceof Error ? err.message : String(err)}
|
|
57114
|
+
`);
|
|
57115
|
+
}).finally(() => {
|
|
57116
|
+
idleClearDispatching = false;
|
|
57117
|
+
});
|
|
57118
|
+
}
|
|
57119
|
+
async function postIdleClearNotice(idleClearMs) {
|
|
57120
|
+
try {
|
|
57121
|
+
const chatId = loadAccess().allowFrom[0];
|
|
57122
|
+
if (!chatId)
|
|
57123
|
+
return;
|
|
57124
|
+
const threadId = topicForRecipient({
|
|
57125
|
+
recipientChatId: chatId,
|
|
57126
|
+
resolvedTopic: resolveAgentOutboundTopic({ kind: "compact-watchdog" }) ?? chatThreadMap.get(chatId),
|
|
57127
|
+
supergroupChatId: resolveAgentSupergroupChatId()
|
|
57128
|
+
});
|
|
57129
|
+
const hrs = Math.round(idleClearMs / 3600000 * 10) / 10;
|
|
57130
|
+
const text2 = `\uD83E\uDDF9 <b>Cleared after ${hrs}h idle</b> \u2014 fresh slate next message; ` + `long-term memory is in Hindsight.`;
|
|
57131
|
+
await swallowingApiCall(() => bot.api.sendMessage(chatId, text2, {
|
|
57132
|
+
parse_mode: "HTML",
|
|
57133
|
+
...threadId != null ? { message_thread_id: threadId } : {}
|
|
57134
|
+
}), { chat_id: chatId, verb: "idleAutoClear.notice" });
|
|
57135
|
+
} catch {}
|
|
57136
|
+
}
|
|
56960
57137
|
async function postCompactCard(occ, cap) {
|
|
56961
57138
|
try {
|
|
56962
57139
|
const chatId = loadAccess().allowFrom[0];
|
|
@@ -57760,11 +57937,11 @@ var unpinProgressCardForChat = null;
|
|
|
57760
57937
|
var getPinnedProgressCardMessageId = null;
|
|
57761
57938
|
var completeProgressCardTurn = null;
|
|
57762
57939
|
var subagentWatcher = null;
|
|
57763
|
-
var SOCKET_PATH = process.env.SWITCHROOM_GATEWAY_SOCKET ??
|
|
57764
|
-
|
|
57765
|
-
var GATEWAY_PID_PATH = process.env.SWITCHROOM_GATEWAY_PID_FILE ??
|
|
57766
|
-
var GATEWAY_SESSION_MARKER_PATH = process.env.SWITCHROOM_GATEWAY_SESSION_MARKER ??
|
|
57767
|
-
var GATEWAY_CLEAN_SHUTDOWN_MARKER_PATH = process.env.SWITCHROOM_GATEWAY_CLEAN_SHUTDOWN_MARKER ??
|
|
57940
|
+
var SOCKET_PATH = process.env.SWITCHROOM_GATEWAY_SOCKET ?? join36(STATE_DIR, "gateway.sock");
|
|
57941
|
+
mkdirSync27(STATE_DIR, { recursive: true, mode: 448 });
|
|
57942
|
+
var GATEWAY_PID_PATH = process.env.SWITCHROOM_GATEWAY_PID_FILE ?? join36(STATE_DIR, "gateway.pid.json");
|
|
57943
|
+
var GATEWAY_SESSION_MARKER_PATH = process.env.SWITCHROOM_GATEWAY_SESSION_MARKER ?? join36(STATE_DIR, "gateway-session.json");
|
|
57944
|
+
var GATEWAY_CLEAN_SHUTDOWN_MARKER_PATH = process.env.SWITCHROOM_GATEWAY_CLEAN_SHUTDOWN_MARKER ?? join36(STATE_DIR, "clean-shutdown.json");
|
|
57768
57945
|
var GATEWAY_STARTED_AT_MS = Date.now();
|
|
57769
57946
|
var BOOT_CARD_ENABLED = process.env.SWITCHROOM_BOOT_CARD !== "false";
|
|
57770
57947
|
var activeBootCard = null;
|
|
@@ -57793,7 +57970,7 @@ function ensureIssuesCard(chatId, threadId) {
|
|
|
57793
57970
|
bot: botApi,
|
|
57794
57971
|
log: (msg) => process.stderr.write(`telegram gateway: ${msg}
|
|
57795
57972
|
`),
|
|
57796
|
-
persistPath:
|
|
57973
|
+
persistPath: join36(stateDir, "issues-card.json")
|
|
57797
57974
|
});
|
|
57798
57975
|
activeIssuesWatcher = startIssuesWatcher({
|
|
57799
57976
|
stateDir,
|
|
@@ -58022,11 +58199,11 @@ startTimer2({
|
|
|
58022
58199
|
}
|
|
58023
58200
|
});
|
|
58024
58201
|
var inboundSpool = STATIC ? undefined : createInboundSpool({
|
|
58025
|
-
path:
|
|
58202
|
+
path: join36(STATE_DIR, "inbound-spool.jsonl"),
|
|
58026
58203
|
fs: {
|
|
58027
58204
|
appendFileSync: (p, d) => appendFileSync5(p, d),
|
|
58028
58205
|
readFileSync: (p) => readFileSync36(p, "utf8"),
|
|
58029
|
-
writeFileSync: (p, d) =>
|
|
58206
|
+
writeFileSync: (p, d) => writeFileSync26(p, d),
|
|
58030
58207
|
renameSync: (a, b) => renameSync12(a, b),
|
|
58031
58208
|
existsSync: (p) => existsSync38(p),
|
|
58032
58209
|
statSizeSync: (p) => statSync13(p).size
|
|
@@ -58242,8 +58419,8 @@ var ipcServer = createIpcServer({
|
|
|
58242
58419
|
probeQuotaViaBroker: (t) => probeQuotaForBootCard(agentSlug, t),
|
|
58243
58420
|
tmuxSupervisor: process.env.SWITCHROOM_TMUX_SUPERVISOR === "1",
|
|
58244
58421
|
dockerMode: process.env.SWITCHROOM_RUNTIME === "docker",
|
|
58245
|
-
configSnapshotPath:
|
|
58246
|
-
bootCardStatePath:
|
|
58422
|
+
configSnapshotPath: join36(resolvedAgentDirForCard, ".config-snapshot.json"),
|
|
58423
|
+
bootCardStatePath: join36(resolvedAgentDirForCard, ".boot-card-msgid.json"),
|
|
58247
58424
|
...updateOutcomeLine ? { updateOutcomeLine } : {}
|
|
58248
58425
|
}, ackMsgId).then((handle) => {
|
|
58249
58426
|
activeBootCard = handle;
|
|
@@ -58667,6 +58844,7 @@ var ipcServer = createIpcServer({
|
|
|
58667
58844
|
});
|
|
58668
58845
|
},
|
|
58669
58846
|
onInjectInbound(_client, msg) {
|
|
58847
|
+
markIdleActivity();
|
|
58670
58848
|
const promptKey = typeof msg.inbound.meta?.prompt_key === "string" ? msg.inbound.meta.prompt_key : "unknown";
|
|
58671
58849
|
const source = typeof msg.inbound.meta?.source === "string" ? msg.inbound.meta.source : "unknown";
|
|
58672
58850
|
const { target, delivered, fellBackToMain } = deliverInjectWithFallback(msg.agentName, msg.inbound.meta, (t) => ipcServer.sendToAgent(t, msg.inbound));
|
|
@@ -58739,7 +58917,7 @@ var ipcServer = createIpcServer({
|
|
|
58739
58917
|
const receiverUid = receiverUidRaw ? Number(receiverUidRaw) : NaN;
|
|
58740
58918
|
if (Number.isInteger(receiverUid))
|
|
58741
58919
|
allowedUids.push(receiverUid);
|
|
58742
|
-
const socketPath =
|
|
58920
|
+
const socketPath = join36(STATE_DIR, "webhook.sock");
|
|
58743
58921
|
const webhookInject = (agentName3, inbound) => {
|
|
58744
58922
|
const msg = inbound;
|
|
58745
58923
|
const delivered = ipcServer.sendToAgent(agentName3, msg);
|
|
@@ -59850,7 +60028,7 @@ async function executeSendGif(rawArgs) {
|
|
|
59850
60028
|
};
|
|
59851
60029
|
}
|
|
59852
60030
|
async function publishToTelegraph(text2, shortName, authorName) {
|
|
59853
|
-
const accountPath =
|
|
60031
|
+
const accountPath = join36(STATE_DIR, "telegraph-account.json");
|
|
59854
60032
|
let account = null;
|
|
59855
60033
|
try {
|
|
59856
60034
|
if (existsSync38(accountPath)) {
|
|
@@ -59873,8 +60051,8 @@ async function publishToTelegraph(text2, shortName, authorName) {
|
|
|
59873
60051
|
}
|
|
59874
60052
|
account = created.value;
|
|
59875
60053
|
try {
|
|
59876
|
-
|
|
59877
|
-
|
|
60054
|
+
mkdirSync27(STATE_DIR, { recursive: true, mode: 448 });
|
|
60055
|
+
writeFileSync26(accountPath, JSON.stringify(account, null, 2), { mode: 384 });
|
|
59878
60056
|
} catch (err) {
|
|
59879
60057
|
process.stderr.write(`telegram gateway: telegraph cache write failed: ${err.message}
|
|
59880
60058
|
`);
|
|
@@ -60334,9 +60512,9 @@ async function executeDownloadAttachment(args) {
|
|
|
60334
60512
|
fileUniqueId: file.file_unique_id,
|
|
60335
60513
|
now: Date.now()
|
|
60336
60514
|
});
|
|
60337
|
-
|
|
60515
|
+
mkdirSync27(INBOX_DIR, { recursive: true, mode: 448 });
|
|
60338
60516
|
assertInsideInbox(INBOX_DIR, dlPath);
|
|
60339
|
-
|
|
60517
|
+
writeFileSync26(dlPath, buf, { mode: 384 });
|
|
60340
60518
|
return { content: [{ type: "text", text: dlPath }] };
|
|
60341
60519
|
}
|
|
60342
60520
|
async function executeEditMessage(args) {
|
|
@@ -60711,6 +60889,7 @@ function handleSessionEvent(ev) {
|
|
|
60711
60889
|
isDm: isDmChatId(ev.chatId)
|
|
60712
60890
|
};
|
|
60713
60891
|
currentTurn = next;
|
|
60892
|
+
markIdleActivity();
|
|
60714
60893
|
process.stderr.write(`telegram gateway: ${formatTurnLifecycle("set", "enqueue", next, startedAt)}
|
|
60715
60894
|
`);
|
|
60716
60895
|
rememberRecentTurn(next);
|
|
@@ -61497,6 +61676,7 @@ function maybeEarlyAckReaction(ctx, from) {
|
|
|
61497
61676
|
bot.api.sendChatAction(chatId, "typing").catch(() => {});
|
|
61498
61677
|
}
|
|
61499
61678
|
async function handleInbound(ctx, text2, downloadImage, attachment, extraAttachments) {
|
|
61679
|
+
markIdleActivity();
|
|
61500
61680
|
const isTopicMessage = ctx.message?.is_topic_message ?? false;
|
|
61501
61681
|
const messageThreadId = ctx.message?.message_thread_id;
|
|
61502
61682
|
if (TOPIC_ID != null) {
|
|
@@ -62321,14 +62501,14 @@ function restartMarkerPath() {
|
|
|
62321
62501
|
const agentDir = resolveAgentDirFromEnv();
|
|
62322
62502
|
if (!agentDir)
|
|
62323
62503
|
return null;
|
|
62324
|
-
return
|
|
62504
|
+
return join36(agentDir, "restart-pending.json");
|
|
62325
62505
|
}
|
|
62326
62506
|
function writeRestartMarker(marker) {
|
|
62327
62507
|
const p = restartMarkerPath();
|
|
62328
62508
|
if (!p)
|
|
62329
62509
|
return;
|
|
62330
62510
|
try {
|
|
62331
|
-
|
|
62511
|
+
writeFileSync26(p, JSON.stringify(marker));
|
|
62332
62512
|
lastPlannedRestartAt = Date.now();
|
|
62333
62513
|
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}
|
|
62334
62514
|
`);
|
|
@@ -62513,12 +62693,12 @@ function _resetDockerReachableCache() {
|
|
|
62513
62693
|
}
|
|
62514
62694
|
function spawnSwitchroomDetached(args, onFailure) {
|
|
62515
62695
|
const fullArgs = SWITCHROOM_CONFIG ? ["--config", SWITCHROOM_CONFIG, ...args] : args;
|
|
62516
|
-
const logPath =
|
|
62696
|
+
const logPath = join36(STATE_DIR, "detached-spawn.log");
|
|
62517
62697
|
let outFd = null;
|
|
62518
62698
|
try {
|
|
62519
|
-
|
|
62699
|
+
mkdirSync27(STATE_DIR, { recursive: true });
|
|
62520
62700
|
outFd = openSync8(logPath, "a");
|
|
62521
|
-
|
|
62701
|
+
writeFileSync26(logPath, `
|
|
62522
62702
|
[${new Date().toISOString()}] spawn ${SWITCHROOM_CLI} ${fullArgs.join(" ")}
|
|
62523
62703
|
`, { flag: "a" });
|
|
62524
62704
|
} catch {}
|
|
@@ -62906,7 +63086,7 @@ bot.use(async (ctx, next) => {
|
|
|
62906
63086
|
});
|
|
62907
63087
|
function readRecentDenialsForAgent(agentName3, windowMs, limit) {
|
|
62908
63088
|
try {
|
|
62909
|
-
const auditPath =
|
|
63089
|
+
const auditPath = join36(homedir14(), ".switchroom", "vault-audit.log");
|
|
62910
63090
|
if (!existsSync38(auditPath))
|
|
62911
63091
|
return [];
|
|
62912
63092
|
const raw = readFileSync36(auditPath, "utf8");
|
|
@@ -62961,7 +63141,7 @@ async function buildAgentMetadata(agentName3) {
|
|
|
62961
63141
|
try {
|
|
62962
63142
|
const agentDir = resolveAgentDirFromEnv();
|
|
62963
63143
|
if (agentDir) {
|
|
62964
|
-
const raw = readFileSync36(
|
|
63144
|
+
const raw = readFileSync36(join36(agentDir, ".claude", ".claude.json"), "utf8");
|
|
62965
63145
|
claudeJson = JSON.parse(raw);
|
|
62966
63146
|
}
|
|
62967
63147
|
} catch {}
|
|
@@ -63076,17 +63256,26 @@ bot.command("agents", async (ctx) => {
|
|
|
63076
63256
|
`);
|
|
63077
63257
|
});
|
|
63078
63258
|
});
|
|
63079
|
-
|
|
63080
|
-
|
|
63081
|
-
isAuthorized: isAuthorizedSender,
|
|
63259
|
+
function buildInjectDeps(opts) {
|
|
63260
|
+
return {
|
|
63261
|
+
isAuthorized: opts?.open ? () => true : isAuthorizedSender,
|
|
63082
63262
|
inject: injectSlashCommand,
|
|
63083
|
-
reply: async (
|
|
63263
|
+
reply: async (ctx, text2, replyOpts) => switchroomReply(ctx, text2, { html: replyOpts?.html }),
|
|
63084
63264
|
getAgentName: getMyAgentName,
|
|
63085
|
-
getArgs: getCommandArgs,
|
|
63265
|
+
getArgs: opts?.fixedVerb != null ? () => opts.fixedVerb : getCommandArgs,
|
|
63086
63266
|
escapeHtml: escapeHtmlForTg,
|
|
63087
63267
|
preBlock,
|
|
63088
63268
|
formatOutput: formatSwitchroomOutput
|
|
63089
|
-
}
|
|
63269
|
+
};
|
|
63270
|
+
}
|
|
63271
|
+
bot.command("inject", async (ctx) => {
|
|
63272
|
+
await handleInjectCommand(ctx, buildInjectDeps());
|
|
63273
|
+
});
|
|
63274
|
+
bot.command("compact", async (ctx) => {
|
|
63275
|
+
await handleInjectCommand(ctx, buildInjectDeps({ open: true, fixedVerb: "/compact" }));
|
|
63276
|
+
});
|
|
63277
|
+
bot.command("clear", async (ctx) => {
|
|
63278
|
+
await handleInjectCommand(ctx, buildInjectDeps({ open: true, fixedVerb: "/clear" }));
|
|
63090
63279
|
});
|
|
63091
63280
|
function buildModelDeps() {
|
|
63092
63281
|
return {
|
|
@@ -63103,7 +63292,7 @@ function buildModelDeps() {
|
|
|
63103
63292
|
try {
|
|
63104
63293
|
const agentDir = resolveAgentDirFromEnv();
|
|
63105
63294
|
if (agentDir) {
|
|
63106
|
-
const local = await fetchQuota2({ claudeConfigDir:
|
|
63295
|
+
const local = await fetchQuota2({ claudeConfigDir: join36(agentDir, ".claude") });
|
|
63107
63296
|
if (local.ok)
|
|
63108
63297
|
return formatQuotaLine2(local.data);
|
|
63109
63298
|
}
|
|
@@ -63269,7 +63458,7 @@ bot.command("restart", async (ctx) => {
|
|
|
63269
63458
|
function flushAgentHandoff(agentDir) {
|
|
63270
63459
|
let removed = 0;
|
|
63271
63460
|
for (const fname of [".handoff.md", ".handoff-topic"]) {
|
|
63272
|
-
const p =
|
|
63461
|
+
const p = join36(agentDir, fname);
|
|
63273
63462
|
try {
|
|
63274
63463
|
if (existsSync38(p)) {
|
|
63275
63464
|
unlinkSync14(p);
|
|
@@ -63282,7 +63471,8 @@ function flushAgentHandoff(agentDir) {
|
|
|
63282
63471
|
}
|
|
63283
63472
|
return removed;
|
|
63284
63473
|
}
|
|
63285
|
-
async function
|
|
63474
|
+
async function handleNewCommand(ctx) {
|
|
63475
|
+
const kind = "new";
|
|
63286
63476
|
if (!isAuthorizedSender(ctx))
|
|
63287
63477
|
return;
|
|
63288
63478
|
const name = (typeof ctx.match === "string" ? ctx.match : "").trim() || getMyAgentName();
|
|
@@ -63309,7 +63499,7 @@ async function handleNewOrResetCommand(ctx, kind) {
|
|
|
63309
63499
|
const flushed = agentDir != null ? flushAgentHandoff(agentDir) : 0;
|
|
63310
63500
|
const chatId = String(ctx.chat.id);
|
|
63311
63501
|
const threadId = resolveThreadId(chatId, ctx.message?.message_thread_id);
|
|
63312
|
-
const ackText =
|
|
63502
|
+
const ackText = newSessionAckText(name, flushed > 0);
|
|
63313
63503
|
let ackId = null;
|
|
63314
63504
|
try {
|
|
63315
63505
|
const sent = await retryWithThreadFallback(robustApiCall, (tid) => lockedBot.api.sendMessage(chatId, ackText, {
|
|
@@ -63327,7 +63517,7 @@ async function handleNewOrResetCommand(ctx, kind) {
|
|
|
63327
63517
|
writeRestartMarker({ chat_id: chatId, thread_id: threadId ?? null, ack_message_id: ackId, ts: Date.now() });
|
|
63328
63518
|
if (agentDir != null) {
|
|
63329
63519
|
try {
|
|
63330
|
-
|
|
63520
|
+
writeFileSync26(join36(agentDir, ".force-fresh-session"), `${kind} at ${new Date().toISOString()}
|
|
63331
63521
|
`, "utf8");
|
|
63332
63522
|
} catch (err) {
|
|
63333
63523
|
process.stderr.write(`telegram gateway: failed to write force-fresh marker: ${err}
|
|
@@ -63354,8 +63544,7 @@ async function handleNewOrResetCommand(ctx, kind) {
|
|
|
63354
63544
|
await switchroomReply(ctx, `\u274C <b>${escapeHtmlForTg(kind)} ${escapeHtmlForTg(name)} failed via hostd</b> (result=${escapeHtmlForTg(hostdResp.result)}):
|
|
63355
63545
|
` + preBlock(hostdResp.error ?? "(no error message)"), { html: true });
|
|
63356
63546
|
}
|
|
63357
|
-
bot.command("new", async (ctx) =>
|
|
63358
|
-
bot.command("reset", async (ctx) => handleNewOrResetCommand(ctx, "reset"));
|
|
63547
|
+
bot.command("new", async (ctx) => handleNewCommand(ctx));
|
|
63359
63548
|
bot.command("update", async (ctx) => {
|
|
63360
63549
|
if (!isAuthorizedSender(ctx))
|
|
63361
63550
|
return;
|
|
@@ -63693,10 +63882,10 @@ bot.command("interrupt", async (ctx) => {
|
|
|
63693
63882
|
});
|
|
63694
63883
|
var lockoutOps = {
|
|
63695
63884
|
readFileSync: (p, enc) => readFileSync36(p, enc),
|
|
63696
|
-
writeFileSync: (p, data, opts) =>
|
|
63885
|
+
writeFileSync: (p, data, opts) => writeFileSync26(p, data, opts),
|
|
63697
63886
|
existsSync: (p) => existsSync38(p),
|
|
63698
|
-
mkdirSync: (p, opts) =>
|
|
63699
|
-
joinPath: (...parts) =>
|
|
63887
|
+
mkdirSync: (p, opts) => mkdirSync27(p, opts),
|
|
63888
|
+
joinPath: (...parts) => join36(...parts)
|
|
63700
63889
|
};
|
|
63701
63890
|
var FLEET_FALLBACK_DEDUP_MS = 30000;
|
|
63702
63891
|
function isAuthBrokerSocketReachable() {
|
|
@@ -63786,7 +63975,7 @@ async function runCreditWatch() {
|
|
|
63786
63975
|
if (!agentDir)
|
|
63787
63976
|
return;
|
|
63788
63977
|
const agentName3 = getMyAgentName();
|
|
63789
|
-
const claudeConfigDir =
|
|
63978
|
+
const claudeConfigDir = join36(agentDir, ".claude");
|
|
63790
63979
|
const stateDir = STATE_DIR;
|
|
63791
63980
|
const reason = readClaudeJsonOverage(claudeConfigDir);
|
|
63792
63981
|
const prev = loadCreditState(stateDir);
|
|
@@ -64312,10 +64501,10 @@ async function handleVaultRecentDenialCallback(ctx, data) {
|
|
|
64312
64501
|
return;
|
|
64313
64502
|
}
|
|
64314
64503
|
const { token, id } = result;
|
|
64315
|
-
const tokenPath =
|
|
64504
|
+
const tokenPath = join36(homedir14(), ".switchroom", "agents", agentName3, ".vault-token");
|
|
64316
64505
|
try {
|
|
64317
|
-
|
|
64318
|
-
|
|
64506
|
+
mkdirSync27(join36(homedir14(), ".switchroom", "agents", agentName3), { recursive: true });
|
|
64507
|
+
writeFileSync26(tokenPath, token, { mode: 384 });
|
|
64319
64508
|
} catch (err) {
|
|
64320
64509
|
await switchroomReply(ctx, `<b>Grant created (${escapeHtmlForTg(id)}) but token write failed:</b> ${escapeHtmlForTg(String(err))}
|
|
64321
64510
|
<i>Recover with: <code>switchroom vault grant ${escapeHtmlForTg(agentName3)} --keys ${escapeHtmlForTg(keyName)} --duration 30d</code> on the host.</i>`, { html: true });
|
|
@@ -64391,10 +64580,10 @@ async function performVaultAccessApproval(ctx, pending2, stageId, senderId, atte
|
|
|
64391
64580
|
return;
|
|
64392
64581
|
}
|
|
64393
64582
|
const { token, id } = result;
|
|
64394
|
-
const tokenPath =
|
|
64583
|
+
const tokenPath = join36(homedir14(), ".switchroom", "agents", pending2.agent, ".vault-token");
|
|
64395
64584
|
try {
|
|
64396
|
-
|
|
64397
|
-
|
|
64585
|
+
mkdirSync27(join36(homedir14(), ".switchroom", "agents", pending2.agent), { recursive: true });
|
|
64586
|
+
writeFileSync26(tokenPath, token, { mode: 384 });
|
|
64398
64587
|
} catch (err) {
|
|
64399
64588
|
await switchroomReply(ctx, `<b>Grant created (${escapeHtmlForTg(id)}) but token write failed:</b> ${escapeHtmlForTg(String(err))}
|
|
64400
64589
|
<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 });
|
|
@@ -64873,10 +65062,10 @@ async function executeGrantWizard(ctx, chatId, state4) {
|
|
|
64873
65062
|
return;
|
|
64874
65063
|
}
|
|
64875
65064
|
const { token, id } = result;
|
|
64876
|
-
const tokenPath =
|
|
65065
|
+
const tokenPath = join36(homedir14(), ".switchroom", "agents", state4.agent, ".vault-token");
|
|
64877
65066
|
try {
|
|
64878
|
-
|
|
64879
|
-
|
|
65067
|
+
mkdirSync27(join36(homedir14(), ".switchroom", "agents", state4.agent), { recursive: true });
|
|
65068
|
+
writeFileSync26(tokenPath, token, { mode: 384 });
|
|
64880
65069
|
} catch (err) {
|
|
64881
65070
|
await switchroomReply(ctx, `<b>Grant created but token write failed:</b> ${escapeHtmlForTg(String(err))}`, { html: true });
|
|
64882
65071
|
return;
|
|
@@ -65724,7 +65913,7 @@ bot.command("usage", async (ctx) => {
|
|
|
65724
65913
|
await switchroomReply(ctx, "<b>/usage:</b> cannot resolve agent dir.", { html: true });
|
|
65725
65914
|
return;
|
|
65726
65915
|
}
|
|
65727
|
-
const result = await fetchQuota2({ claudeConfigDir:
|
|
65916
|
+
const result = await fetchQuota2({ claudeConfigDir: join36(agentDir, ".claude") });
|
|
65728
65917
|
if (!result.ok) {
|
|
65729
65918
|
await switchroomReply(ctx, `<b>/usage:</b> ${escapeHtmlForTg(result.reason)}`, { html: true });
|
|
65730
65919
|
return;
|
|
@@ -66464,9 +66653,9 @@ bot.on("message:photo", async (ctx) => {
|
|
|
66464
66653
|
fileUniqueId: best.file_unique_id,
|
|
66465
66654
|
now: Date.now()
|
|
66466
66655
|
});
|
|
66467
|
-
|
|
66656
|
+
mkdirSync27(INBOX_DIR, { recursive: true, mode: 448 });
|
|
66468
66657
|
assertInsideInbox(INBOX_DIR, dlPath);
|
|
66469
|
-
|
|
66658
|
+
writeFileSync26(dlPath, buf, { mode: 384 });
|
|
66470
66659
|
return dlPath;
|
|
66471
66660
|
} catch (err) {
|
|
66472
66661
|
const msg = err instanceof Error ? err.message : "unknown error";
|
|
@@ -67245,25 +67434,14 @@ process.on("uncaughtException", (err) => {
|
|
|
67245
67434
|
shutdown("uncaughtException");
|
|
67246
67435
|
});
|
|
67247
67436
|
var runnerHandle = null;
|
|
67248
|
-
var POLL_HEALTH_INTERVAL_MS = Number(process.env.SWITCHROOM_POLL_HEALTH_INTERVAL_MS ??
|
|
67437
|
+
var POLL_HEALTH_INTERVAL_MS = Number(process.env.SWITCHROOM_POLL_HEALTH_INTERVAL_MS ?? 60000);
|
|
67249
67438
|
var POLL_HEALTH_THRESHOLD = Number(process.env.SWITCHROOM_POLL_HEALTH_THRESHOLD ?? 3);
|
|
67250
67439
|
var pollHealthCheck = null;
|
|
67251
67440
|
if (POLL_HEALTH_INTERVAL_MS > 0) {
|
|
67252
67441
|
pollHealthCheck = createPollHealthCheck({
|
|
67253
67442
|
ping: () => bot.api.getMe(),
|
|
67254
67443
|
onStall: async () => {
|
|
67255
|
-
|
|
67256
|
-
process.stderr.write(`telegram gateway: poll.health_check.stall_recovery stopping runner agent=${agentName3}
|
|
67257
|
-
`);
|
|
67258
|
-
if (runnerHandle != null && runnerHandle.isRunning()) {
|
|
67259
|
-
try {
|
|
67260
|
-
await runnerHandle.stop();
|
|
67261
|
-
} catch (err) {
|
|
67262
|
-
process.stderr.write(`telegram gateway: poll.health_check.stall_recovery runner.stop error: ${err.message}
|
|
67263
|
-
`);
|
|
67264
|
-
}
|
|
67265
|
-
}
|
|
67266
|
-
runnerHandle = null;
|
|
67444
|
+
recoverFromPollStall({ agentName: process.env.SWITCHROOM_AGENT_NAME ?? "-" });
|
|
67267
67445
|
},
|
|
67268
67446
|
intervalMs: POLL_HEALTH_INTERVAL_MS,
|
|
67269
67447
|
failureThreshold: POLL_HEALTH_THRESHOLD,
|
|
@@ -67451,7 +67629,7 @@ var didOneTimeSetup = false;
|
|
|
67451
67629
|
return;
|
|
67452
67630
|
}
|
|
67453
67631
|
})();
|
|
67454
|
-
const resolvedAgentDirForBootCard = agentDir ??
|
|
67632
|
+
const resolvedAgentDirForBootCard = agentDir ?? join36(homedir14(), ".switchroom", "agents", agentSlug);
|
|
67455
67633
|
const handle = await startBootCard(chatId, threadId, botApiForCard, {
|
|
67456
67634
|
agentName: agentDisplayName,
|
|
67457
67635
|
agentSlug,
|
|
@@ -67465,8 +67643,8 @@ var didOneTimeSetup = false;
|
|
|
67465
67643
|
probeQuotaViaBroker: (t) => probeQuotaForBootCard(agentSlug, t),
|
|
67466
67644
|
tmuxSupervisor: process.env.SWITCHROOM_TMUX_SUPERVISOR === "1",
|
|
67467
67645
|
dockerMode: process.env.SWITCHROOM_RUNTIME === "docker",
|
|
67468
|
-
configSnapshotPath:
|
|
67469
|
-
bootCardStatePath:
|
|
67646
|
+
configSnapshotPath: join36(resolvedAgentDirForBootCard, ".config-snapshot.json"),
|
|
67647
|
+
bootCardStatePath: join36(resolvedAgentDirForBootCard, ".boot-card-msgid.json"),
|
|
67470
67648
|
...updateOutcomeLine ? { updateOutcomeLine } : {}
|
|
67471
67649
|
}, ackMsgId);
|
|
67472
67650
|
activeBootCard = handle;
|
|
@@ -67853,13 +68031,6 @@ var didOneTimeSetup = false;
|
|
|
67853
68031
|
pollHealthCheck?.stop();
|
|
67854
68032
|
pollHealthCheck?.start();
|
|
67855
68033
|
await runnerHandle.task();
|
|
67856
|
-
if (runnerHandle === null) {
|
|
67857
|
-
const agentName3 = process.env.SWITCHROOM_AGENT_NAME ?? "-";
|
|
67858
|
-
process.stderr.write(`telegram gateway: poll.health_check.stall_recovery restarting runner agent=${agentName3}
|
|
67859
|
-
`);
|
|
67860
|
-
await new Promise((r) => setTimeout(r, 2000));
|
|
67861
|
-
continue;
|
|
67862
|
-
}
|
|
67863
68034
|
return;
|
|
67864
68035
|
} catch (err) {
|
|
67865
68036
|
if (err instanceof import_grammy9.GrammyError && err.error_code === 409) {
|