switchroom 0.15.40 → 0.15.42
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 +4 -1
- package/dist/auth-broker/index.js +4 -1
- package/dist/cli/notion-write-pretool.mjs +4 -1
- package/dist/cli/switchroom.js +1469 -1247
- package/dist/cli/ui/index.html +31 -0
- package/dist/host-control/main.js +4 -1
- package/dist/vault/approvals/kernel-server.js +4 -1
- package/dist/vault/broker/server.js +23 -1
- package/package.json +1 -1
- package/profiles/_base/start.sh.hbs +15 -0
- package/telegram-plugin/dist/gateway/gateway.js +400 -227
- 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
- package/vendor/hindsight-memory/scripts/lib/config.py +12 -0
- package/vendor/hindsight-memory/scripts/recall.py +64 -0
- package/vendor/hindsight-memory/scripts/tests/test_recall_trivial_skip.py +101 -0
- package/vendor/hindsight-memory/tests/test_config.py +3 -3
|
@@ -23922,6 +23922,8 @@ var init_schema = __esm(() => {
|
|
|
23922
23922
|
max_memories: exports_external.number().int().min(0).optional().describe("Cap on the number of memories injected into the prompt by " + "auto-recall, regardless of token budget. Plugin default is 12. " + "0 disables the cap (all memories Hindsight returns are injected)."),
|
|
23923
23923
|
cache_ttl_secs: exports_external.number().int().min(0).optional().describe("Per-session recall cache TTL in seconds. When > 0, identical " + "(prompt, bank) within the same session reuse the cached recall " + "result instead of round-tripping to Hindsight. 0 disables. " + "Default is 600 (10 min) for switchroom-managed agents."),
|
|
23924
23924
|
min_overlap: exports_external.number().min(0).max(1).optional().describe("Minimum Jaccard token overlap [0.0\u20131.0] between the user " + "prompt and a memory's text for the memory to be injected. " + "Drops low-relevance matches before the count cap so weak hits " + "don't fill the slot on real queries. 0.0 disables (default \u2014 " + "current behaviour). Try 0.10\u20130.20 to start; observe the " + "`overlap_dropped` field via `switchroom memory recall-log`."),
|
|
23925
|
+
types: exports_external.array(exports_external.string()).optional().describe("Hindsight fact types to recall. Switchroom default is " + '["world", "experience", "observation"] \u2014 the synthesized ' + "`observation` tier is on by default. Set to " + '["world", "experience"] to opt out of observation-backed ' + "recall for this agent (or fleet-wide under defaults)."),
|
|
23926
|
+
skip_trivial: exports_external.boolean().optional().describe("Skip recall on plausibly-stateless trivial turns (time/date/" + "greeting). Switchroom default true \u2014 saves the recall arm + " + "injected tokens on turns that never need memory, guarded so it " + "never skips a turn that references user/project/session state. " + "Set false to always run recall."),
|
|
23925
23927
|
topic_filter_mode: exports_external.enum(["soft-preamble", "hard-filter"]).optional().describe("Supergroup-mode cross-topic memory behaviour. Default " + "(unset) \u2192 soft-preamble: recall returns memories from all " + "topics, and a 'Current topic: \u2026' preamble tells the model " + "to self-scope. hard-filter: drop any recalled memory whose " + "metadata.thread_id differs from the active inbound's topic. " + "Flip to hard-filter when the recall_log shows binding " + "failures (model surfacing the right memory but applying " + "it to the wrong topic).")
|
|
23926
23928
|
}).optional().describe("Auto-recall tuning knobs")
|
|
23927
23929
|
}).optional();
|
|
@@ -23958,7 +23960,8 @@ var init_schema = __esm(() => {
|
|
|
23958
23960
|
SessionSchema = exports_external.object({
|
|
23959
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'."),
|
|
23960
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."),
|
|
23961
|
-
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.")
|
|
23962
23965
|
}).optional();
|
|
23963
23966
|
SessionContinuitySchema = exports_external.object({
|
|
23964
23967
|
enabled: exports_external.boolean().optional().describe("Master switch for the session-handoff briefing (default true)."),
|
|
@@ -28926,10 +28929,10 @@ function renderAuthLine(state4, agentName3, now = Date.now()) {
|
|
|
28926
28929
|
}
|
|
28927
28930
|
|
|
28928
28931
|
// gateway/quota-cache.ts
|
|
28929
|
-
import { existsSync as existsSync26, readFileSync as readFileSync24, writeFileSync as
|
|
28930
|
-
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";
|
|
28931
28934
|
function defaultCachePath() {
|
|
28932
|
-
return process.env.SWITCHROOM_QUOTA_CACHE_PATH ??
|
|
28935
|
+
return process.env.SWITCHROOM_QUOTA_CACHE_PATH ?? join24(process.env.HOME ?? "/tmp", ".switchroom", "quota-cache.json");
|
|
28933
28936
|
}
|
|
28934
28937
|
function readQuotaCache(opts = {}) {
|
|
28935
28938
|
const path = opts.path ?? defaultCachePath();
|
|
@@ -28964,8 +28967,8 @@ function writeQuotaCache(result, opts = {}) {
|
|
|
28964
28967
|
result
|
|
28965
28968
|
};
|
|
28966
28969
|
try {
|
|
28967
|
-
|
|
28968
|
-
|
|
28970
|
+
mkdirSync17(dirname8(path), { recursive: true });
|
|
28971
|
+
writeFileSync16(path, JSON.stringify(entry, null, 2), { mode: 384 });
|
|
28969
28972
|
} catch {}
|
|
28970
28973
|
}
|
|
28971
28974
|
var DEFAULT_TTL_MS4, RATE_LIMIT_TTL_MS;
|
|
@@ -28976,7 +28979,7 @@ var init_quota_cache = __esm(() => {
|
|
|
28976
28979
|
|
|
28977
28980
|
// gateway/boot-probes.ts
|
|
28978
28981
|
import { readFileSync as readFileSync25, readdirSync as readdirSync4, existsSync as existsSync27 } from "fs";
|
|
28979
|
-
import { join as
|
|
28982
|
+
import { join as join25 } from "path";
|
|
28980
28983
|
import { execFile as execFileCb } from "child_process";
|
|
28981
28984
|
import { promisify as promisify3 } from "util";
|
|
28982
28985
|
async function withTimeout(label, p, timeoutMs = PROBE_TIMEOUT_MS) {
|
|
@@ -29018,8 +29021,8 @@ function mapPlan(billingType, hasExtra) {
|
|
|
29018
29021
|
}
|
|
29019
29022
|
async function probeAccount(agentDir) {
|
|
29020
29023
|
return withTimeout("Account", (async () => {
|
|
29021
|
-
const claudeDir =
|
|
29022
|
-
const claudeJsonPath =
|
|
29024
|
+
const claudeDir = join25(agentDir, ".claude");
|
|
29025
|
+
const claudeJsonPath = join25(claudeDir, ".claude.json");
|
|
29023
29026
|
let cfg = {};
|
|
29024
29027
|
try {
|
|
29025
29028
|
const raw = readFileSync25(claudeJsonPath, "utf8");
|
|
@@ -29040,8 +29043,8 @@ async function probeAccount(agentDir) {
|
|
|
29040
29043
|
let tokenStr = "";
|
|
29041
29044
|
let status = "ok";
|
|
29042
29045
|
for (const candidate of [
|
|
29043
|
-
|
|
29044
|
-
|
|
29046
|
+
join25(claudeDir, ".oauth-token.meta.json"),
|
|
29047
|
+
join25(claudeDir, "accounts", "default", ".oauth-token.meta.json")
|
|
29045
29048
|
]) {
|
|
29046
29049
|
if (existsSync27(candidate)) {
|
|
29047
29050
|
try {
|
|
@@ -29427,9 +29430,9 @@ async function probeQuota(claudeConfigDir, _agentDir, fetchImpl = fetch, opts =
|
|
|
29427
29430
|
let claudeDirForProbe = null;
|
|
29428
29431
|
for (const candidate of [
|
|
29429
29432
|
claudeConfigDir,
|
|
29430
|
-
|
|
29433
|
+
join25(claudeConfigDir, "accounts", "default")
|
|
29431
29434
|
]) {
|
|
29432
|
-
if (existsSync27(
|
|
29435
|
+
if (existsSync27(join25(candidate, ".oauth-token"))) {
|
|
29433
29436
|
claudeDirForProbe = candidate;
|
|
29434
29437
|
break;
|
|
29435
29438
|
}
|
|
@@ -29664,7 +29667,7 @@ async function probeSkills(agentDir, opts = {}) {
|
|
|
29664
29667
|
return withTimeout("Skills", (async () => {
|
|
29665
29668
|
const fs2 = opts.fs ?? realSkillsFs;
|
|
29666
29669
|
const max = opts.maxNamesShown ?? 3;
|
|
29667
|
-
const skillsDir =
|
|
29670
|
+
const skillsDir = join25(agentDir, ".claude", "skills");
|
|
29668
29671
|
if (!fs2.exists(skillsDir)) {
|
|
29669
29672
|
return { status: "ok", label: "Skills", detail: "no skills dir" };
|
|
29670
29673
|
}
|
|
@@ -29679,17 +29682,17 @@ async function probeSkills(agentDir, opts = {}) {
|
|
|
29679
29682
|
}
|
|
29680
29683
|
const dangling = [];
|
|
29681
29684
|
for (const name of entries) {
|
|
29682
|
-
const skillPath =
|
|
29685
|
+
const skillPath = join25(skillsDir, name);
|
|
29683
29686
|
if (!fs2.exists(skillPath)) {
|
|
29684
29687
|
dangling.push(name);
|
|
29685
29688
|
continue;
|
|
29686
29689
|
}
|
|
29687
|
-
const skillMd =
|
|
29690
|
+
const skillMd = join25(skillPath, "SKILL.md");
|
|
29688
29691
|
if (!fs2.exists(skillMd) && !fs2.exists(skillPath + ".md")) {
|
|
29689
29692
|
continue;
|
|
29690
29693
|
}
|
|
29691
29694
|
}
|
|
29692
|
-
const overlayDir = opts.overlaySkillsDir ??
|
|
29695
|
+
const overlayDir = opts.overlaySkillsDir ?? join25(agentDir, "skills.d");
|
|
29693
29696
|
const overlaySlugs = new Set;
|
|
29694
29697
|
if (fs2.exists(overlayDir)) {
|
|
29695
29698
|
let overlayEntries = [];
|
|
@@ -29731,7 +29734,7 @@ function renderBucketedSkills(switchroom, agent) {
|
|
|
29731
29734
|
}
|
|
29732
29735
|
async function probeConnections(agentDir, opts = {}) {
|
|
29733
29736
|
return withTimeout("Connections", (async () => {
|
|
29734
|
-
const path =
|
|
29737
|
+
const path = join25(agentDir, ".claude", "connection-health.json");
|
|
29735
29738
|
const read = opts.readFileImpl ?? ((p) => readFileSync25(p, "utf8"));
|
|
29736
29739
|
let issues = [];
|
|
29737
29740
|
try {
|
|
@@ -29780,7 +29783,7 @@ var init_boot_probes = __esm(() => {
|
|
|
29780
29783
|
});
|
|
29781
29784
|
|
|
29782
29785
|
// gateway/boot-issue-cache.ts
|
|
29783
|
-
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";
|
|
29784
29787
|
import { dirname as dirname9 } from "path";
|
|
29785
29788
|
function fingerprintProbe(key, r) {
|
|
29786
29789
|
if (r.status === "ok")
|
|
@@ -29907,9 +29910,9 @@ function applyAndSave(path, cache, diff) {
|
|
|
29907
29910
|
}
|
|
29908
29911
|
}
|
|
29909
29912
|
try {
|
|
29910
|
-
|
|
29913
|
+
mkdirSync18(dirname9(path), { recursive: true });
|
|
29911
29914
|
const tmp = `${path}.tmp`;
|
|
29912
|
-
|
|
29915
|
+
writeFileSync17(tmp, JSON.stringify(next), { mode: 384 });
|
|
29913
29916
|
renameSync9(tmp, path);
|
|
29914
29917
|
} catch {}
|
|
29915
29918
|
return next;
|
|
@@ -29923,7 +29926,7 @@ var init_boot_issue_cache = __esm(() => {
|
|
|
29923
29926
|
|
|
29924
29927
|
// gateway/config-snapshot.ts
|
|
29925
29928
|
import { createHash as createHash2 } from "crypto";
|
|
29926
|
-
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";
|
|
29927
29930
|
import { dirname as dirname10 } from "path";
|
|
29928
29931
|
function hashStringArray(items) {
|
|
29929
29932
|
if (!items || items.length === 0)
|
|
@@ -30022,16 +30025,16 @@ function loadSnapshot(path, now = Date.now) {
|
|
|
30022
30025
|
}
|
|
30023
30026
|
function persistSnapshot(path, snapshot) {
|
|
30024
30027
|
try {
|
|
30025
|
-
|
|
30028
|
+
mkdirSync19(dirname10(path), { recursive: true });
|
|
30026
30029
|
const tmp = `${path}.tmp`;
|
|
30027
|
-
|
|
30030
|
+
writeFileSync18(tmp, JSON.stringify(snapshot), { mode: 384 });
|
|
30028
30031
|
renameSync10(tmp, path);
|
|
30029
30032
|
} catch {}
|
|
30030
30033
|
}
|
|
30031
30034
|
var init_config_snapshot = () => {};
|
|
30032
30035
|
|
|
30033
30036
|
// gateway/boot-card-msgid.ts
|
|
30034
|
-
import { readFileSync as readFileSync28, writeFileSync as
|
|
30037
|
+
import { readFileSync as readFileSync28, writeFileSync as writeFileSync19 } from "node:fs";
|
|
30035
30038
|
function bootCardChatKey(chatId, threadId) {
|
|
30036
30039
|
return `${chatId}:${threadId ?? ""}`;
|
|
30037
30040
|
}
|
|
@@ -30056,7 +30059,7 @@ function saveBootCardMsgId(path, chatKey3, messageId) {
|
|
|
30056
30059
|
if (store2[chatKey3] === messageId)
|
|
30057
30060
|
return;
|
|
30058
30061
|
store2[chatKey3] = messageId;
|
|
30059
|
-
|
|
30062
|
+
writeFileSync19(path, JSON.stringify(store2), "utf8");
|
|
30060
30063
|
} catch {}
|
|
30061
30064
|
}
|
|
30062
30065
|
var init_boot_card_msgid = () => {};
|
|
@@ -30071,7 +30074,7 @@ __export(exports_boot_card, {
|
|
|
30071
30074
|
renderBootCard: () => renderBootCard,
|
|
30072
30075
|
renderAccountRows: () => renderAuthLine
|
|
30073
30076
|
});
|
|
30074
|
-
import { join as
|
|
30077
|
+
import { join as join26 } from "path";
|
|
30075
30078
|
function resolvePersonaName(slug, loadConfig3) {
|
|
30076
30079
|
try {
|
|
30077
30080
|
const config = loadConfig3 ? loadConfig3() : loadConfig();
|
|
@@ -30161,7 +30164,7 @@ function renderBootCard(opts) {
|
|
|
30161
30164
|
`);
|
|
30162
30165
|
}
|
|
30163
30166
|
async function runAllProbes(opts) {
|
|
30164
|
-
const claudeDir =
|
|
30167
|
+
const claudeDir = join26(opts.agentDir, ".claude");
|
|
30165
30168
|
const probes = {};
|
|
30166
30169
|
const slug = opts.agentSlug ?? opts.agentName;
|
|
30167
30170
|
await Promise.allSettled([
|
|
@@ -30917,7 +30920,7 @@ __export(exports_tmux, {
|
|
|
30917
30920
|
captureAgentPane: () => captureAgentPane
|
|
30918
30921
|
});
|
|
30919
30922
|
import { execFileSync as execFileSync4 } from "node:child_process";
|
|
30920
|
-
import { mkdirSync as
|
|
30923
|
+
import { mkdirSync as mkdirSync26, readdirSync as readdirSync6, statSync as statSync12, unlinkSync as unlinkSync13, writeFileSync as writeFileSync25 } from "node:fs";
|
|
30921
30924
|
import { resolve as resolve7 } from "node:path";
|
|
30922
30925
|
function captureAgentPane(opts) {
|
|
30923
30926
|
const { agentName: agentName3, agentDir, reason } = opts;
|
|
@@ -30929,7 +30932,7 @@ function captureAgentPane(opts) {
|
|
|
30929
30932
|
const reasonSlug = sanitizeReason(reason);
|
|
30930
30933
|
const outPath = resolve7(outDir, `${ts}-${reasonSlug}.txt`);
|
|
30931
30934
|
try {
|
|
30932
|
-
|
|
30935
|
+
mkdirSync26(outDir, { recursive: true, mode: 493 });
|
|
30933
30936
|
} catch (err) {
|
|
30934
30937
|
const msg = `mkdir crash-reports failed: ${err.message}`;
|
|
30935
30938
|
console.error(`[tmux-capture] ${agentName3}: ${msg}`);
|
|
@@ -30963,7 +30966,7 @@ function captureAgentPane(opts) {
|
|
|
30963
30966
|
` + `
|
|
30964
30967
|
`;
|
|
30965
30968
|
try {
|
|
30966
|
-
|
|
30969
|
+
writeFileSync25(outPath, Buffer.concat([Buffer.from(header, "utf8"), body]), {
|
|
30967
30970
|
mode: 420
|
|
30968
30971
|
});
|
|
30969
30972
|
} catch (err) {
|
|
@@ -31558,8 +31561,8 @@ import { randomBytes as randomBytes6 } from "crypto";
|
|
|
31558
31561
|
import { execFileSync as execFileSync5, execSync as execSync2, spawn as spawn2 } from "child_process";
|
|
31559
31562
|
import {
|
|
31560
31563
|
readFileSync as readFileSync36,
|
|
31561
|
-
writeFileSync as
|
|
31562
|
-
mkdirSync as
|
|
31564
|
+
writeFileSync as writeFileSync26,
|
|
31565
|
+
mkdirSync as mkdirSync27,
|
|
31563
31566
|
readdirSync as readdirSync7,
|
|
31564
31567
|
rmSync as rmSync4,
|
|
31565
31568
|
statSync as statSync13,
|
|
@@ -31573,7 +31576,7 @@ import {
|
|
|
31573
31576
|
appendFileSync as appendFileSync5
|
|
31574
31577
|
} from "fs";
|
|
31575
31578
|
import { homedir as homedir14 } from "os";
|
|
31576
|
-
import { join as
|
|
31579
|
+
import { join as join36, extname, sep as sep3, basename as basename10 } from "path";
|
|
31577
31580
|
|
|
31578
31581
|
// plugin-logger.ts
|
|
31579
31582
|
import { appendFileSync, mkdirSync, renameSync, statSync, existsSync } from "fs";
|
|
@@ -43161,7 +43164,7 @@ function helpText(agentName3) {
|
|
|
43161
43164
|
``,
|
|
43162
43165
|
`This bot is the <b>${escapeHtml6(agentName3)}</b> agent. Text and photos route through to it; replies, reactions and progress cards come back.`,
|
|
43163
43166
|
``,
|
|
43164
|
-
`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>.`,
|
|
43165
43168
|
``,
|
|
43166
43169
|
`<code>/start</code> \u2014 pairing instructions`,
|
|
43167
43170
|
`<code>/status</code> \u2014 agent, model, auth`,
|
|
@@ -43229,7 +43232,8 @@ var TELEGRAM_MENU_COMMANDS = [
|
|
|
43229
43232
|
{ command: "help", description: "What this bot can do" },
|
|
43230
43233
|
{ command: "status", description: "Agent, model, auth" },
|
|
43231
43234
|
{ command: "new", description: "Fresh session (flush handoff, restart)" },
|
|
43232
|
-
{ command: "
|
|
43235
|
+
{ command: "compact", description: "Compact context (summarize, keep the thread)" },
|
|
43236
|
+
{ command: "clear", description: "Clear context (fresh slate; memory in Hindsight)" },
|
|
43233
43237
|
{ command: "approve", description: "Approve pending tool permission" },
|
|
43234
43238
|
{ command: "deny", description: "Deny pending tool permission" },
|
|
43235
43239
|
{ command: "pending", description: "List pending permission prompts" },
|
|
@@ -43237,7 +43241,6 @@ var TELEGRAM_MENU_COMMANDS = [
|
|
|
43237
43241
|
{ command: "restart", description: "Restart the agent (drain by default)" },
|
|
43238
43242
|
{ command: "version", description: "Show versions + running agent health" },
|
|
43239
43243
|
{ command: "logs", description: "Show recent agent logs" },
|
|
43240
|
-
{ command: "inject", description: "Inject a Claude Code slash command (e.g. /cost)" },
|
|
43241
43244
|
{ command: "model", description: "Show or switch the Claude model" },
|
|
43242
43245
|
{ command: "effort", description: "Show or switch the reasoning effort" },
|
|
43243
43246
|
{ command: "doctor", description: "Health check (deps, services, MCP)" },
|
|
@@ -43254,7 +43257,8 @@ function switchroomHelpText(agentName3) {
|
|
|
43254
43257
|
``,
|
|
43255
43258
|
`<b>Session & approvals</b>`,
|
|
43256
43259
|
`<code>/new</code> \u2014 fresh session (flush handoff, restart)`,
|
|
43257
|
-
`<code>/
|
|
43260
|
+
`<code>/compact</code> \u2014 compact context (summarize, keep the thread)`,
|
|
43261
|
+
`<code>/clear</code> \u2014 clear context (fresh slate; memory in Hindsight)`,
|
|
43258
43262
|
`<code>/approve [id]</code> \u2014 approve pending tool permission`,
|
|
43259
43263
|
`<code>/deny [id]</code> \u2014 deny pending tool permission`,
|
|
43260
43264
|
`<code>/pending</code> \u2014 list pending permission prompts`,
|
|
@@ -43308,10 +43312,6 @@ function newSessionAckText(agentName3, flushedHandoff) {
|
|
|
43308
43312
|
const tail = flushedHandoff ? " \u00b7 flushed handoff" : "";
|
|
43309
43313
|
return `\uD83C\uDD95 Started fresh session for <b>${escapeHtml6(agentName3)}</b>${tail} \u00b7 restarting\u2026`;
|
|
43310
43314
|
}
|
|
43311
|
-
function resetSessionAckText(agentName3, flushedHandoff) {
|
|
43312
|
-
const tail = flushedHandoff ? " \u00b7 flushed handoff" : "";
|
|
43313
|
-
return `\uD83D\uDD04 Reset session for <b>${escapeHtml6(agentName3)}</b>${tail} \u00b7 restarting\u2026`;
|
|
43314
|
-
}
|
|
43315
43315
|
|
|
43316
43316
|
// gateway/auth-status-adapter.ts
|
|
43317
43317
|
function formatExpiresInRelative(expiresAt, now = Date.now()) {
|
|
@@ -46381,6 +46381,38 @@ function numField(obj, key) {
|
|
|
46381
46381
|
return 0;
|
|
46382
46382
|
}
|
|
46383
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
|
+
|
|
46384
46416
|
// gateway/proactive-compact.ts
|
|
46385
46417
|
var COMPACT_REARM_FRACTION = 0.6;
|
|
46386
46418
|
var COMPACT_COOLDOWN_TURNS = 3;
|
|
@@ -46407,6 +46439,36 @@ function decideProactiveCompact(state3, occupancy, cap) {
|
|
|
46407
46439
|
};
|
|
46408
46440
|
}
|
|
46409
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
|
+
|
|
46410
46472
|
// gateway/compact-notify.ts
|
|
46411
46473
|
function idleCompactNotifyState() {
|
|
46412
46474
|
return { phase: "idle", fileAtStart: null };
|
|
@@ -46980,13 +47042,13 @@ function startWebhookIngestServer(opts) {
|
|
|
46980
47042
|
}
|
|
46981
47043
|
|
|
46982
47044
|
// ../src/web/webhook-gateway-record.ts
|
|
46983
|
-
import { appendFileSync as appendFileSync4, mkdirSync as
|
|
46984
|
-
import { join as
|
|
47045
|
+
import { appendFileSync as appendFileSync4, mkdirSync as mkdirSync15 } from "fs";
|
|
47046
|
+
import { join as join21 } from "path";
|
|
46985
47047
|
import { homedir as homedir10 } from "os";
|
|
46986
47048
|
|
|
46987
47049
|
// ../src/web/webhook-handler.ts
|
|
46988
|
-
import { appendFileSync as appendFileSync3, existsSync as existsSync22, mkdirSync as
|
|
46989
|
-
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";
|
|
46990
47052
|
var DEDUP_MAX = 1000;
|
|
46991
47053
|
var DEDUP_TTL_MS = 24 * 60 * 60 * 1000;
|
|
46992
47054
|
function loadDedupFile(path) {
|
|
@@ -47007,7 +47069,7 @@ function saveDedupFile(path, deliveries, now) {
|
|
|
47007
47069
|
}
|
|
47008
47070
|
const sorted = Object.entries(pruned).sort((a, b) => b[1] - a[1]).slice(0, DEDUP_MAX);
|
|
47009
47071
|
const final = Object.fromEntries(sorted);
|
|
47010
|
-
|
|
47072
|
+
writeFileSync10(path, JSON.stringify({ deliveries: final }), {
|
|
47011
47073
|
mode: 384
|
|
47012
47074
|
});
|
|
47013
47075
|
}
|
|
@@ -47015,8 +47077,8 @@ var agentDedupCache = new Map;
|
|
|
47015
47077
|
function createFileDedupStore(resolveAgentDir) {
|
|
47016
47078
|
return {
|
|
47017
47079
|
check(agent, deliveryId, now) {
|
|
47018
|
-
const telegramDir =
|
|
47019
|
-
const filePath =
|
|
47080
|
+
const telegramDir = join19(resolveAgentDir(agent), "telegram");
|
|
47081
|
+
const filePath = join19(telegramDir, "webhook-dedup.json");
|
|
47020
47082
|
if (!agentDedupCache.has(agent)) {
|
|
47021
47083
|
agentDedupCache.set(agent, loadDedupFile(filePath));
|
|
47022
47084
|
}
|
|
@@ -47026,7 +47088,7 @@ function createFileDedupStore(resolveAgentDir) {
|
|
|
47026
47088
|
}
|
|
47027
47089
|
deliveries[deliveryId] = now;
|
|
47028
47090
|
try {
|
|
47029
|
-
|
|
47091
|
+
mkdirSync13(telegramDir, { recursive: true });
|
|
47030
47092
|
saveDedupFile(filePath, deliveries, now);
|
|
47031
47093
|
} catch {}
|
|
47032
47094
|
return;
|
|
@@ -47037,8 +47099,8 @@ var tokenBuckets = new Map;
|
|
|
47037
47099
|
var throttleIssueWindow = new Map;
|
|
47038
47100
|
|
|
47039
47101
|
// ../src/web/webhook-dispatch.ts
|
|
47040
|
-
import { existsSync as existsSync23, mkdirSync as
|
|
47041
|
-
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";
|
|
47042
47104
|
import { homedir as homedir9 } from "os";
|
|
47043
47105
|
|
|
47044
47106
|
// ../src/agent-scheduler/ipc-client.ts
|
|
@@ -47347,7 +47409,7 @@ function loadCooldownFile(path) {
|
|
|
47347
47409
|
}
|
|
47348
47410
|
function saveCooldownFile(path, dispatches) {
|
|
47349
47411
|
try {
|
|
47350
|
-
|
|
47412
|
+
writeFileSync11(path, JSON.stringify({ dispatches }), {
|
|
47351
47413
|
mode: 384
|
|
47352
47414
|
});
|
|
47353
47415
|
} catch {}
|
|
@@ -47358,8 +47420,8 @@ function createFileCooldownStore(resolveAgentDir) {
|
|
|
47358
47420
|
isCoolingDown(agent, key, cooldownMs, now) {
|
|
47359
47421
|
if (cooldownMs <= 0)
|
|
47360
47422
|
return false;
|
|
47361
|
-
const telegramDir =
|
|
47362
|
-
const filePath =
|
|
47423
|
+
const telegramDir = join20(resolveAgentDir(agent), "telegram");
|
|
47424
|
+
const filePath = join20(telegramDir, "webhook-cooldown.json");
|
|
47363
47425
|
if (!cache.has(agent)) {
|
|
47364
47426
|
cache.set(agent, loadCooldownFile(filePath));
|
|
47365
47427
|
}
|
|
@@ -47370,7 +47432,7 @@ function createFileCooldownStore(resolveAgentDir) {
|
|
|
47370
47432
|
}
|
|
47371
47433
|
dispatches[key] = now;
|
|
47372
47434
|
try {
|
|
47373
|
-
|
|
47435
|
+
mkdirSync14(telegramDir, { recursive: true });
|
|
47374
47436
|
saveCooldownFile(filePath, dispatches);
|
|
47375
47437
|
} catch {}
|
|
47376
47438
|
return false;
|
|
@@ -47416,9 +47478,9 @@ async function defaultInject(socketPath, agentName3, inbound) {
|
|
|
47416
47478
|
}
|
|
47417
47479
|
function injectWebhookInbound(agent, prompt, ctx, deps = {}) {
|
|
47418
47480
|
const log = deps.log ?? ((s) => process.stderr.write(s));
|
|
47419
|
-
const resolveAgentDir = deps.resolveAgentDir ?? ((a) =>
|
|
47481
|
+
const resolveAgentDir = deps.resolveAgentDir ?? ((a) => join20(homedir9(), ".switchroom", "agents", a));
|
|
47420
47482
|
const now = (deps.now ?? Date.now)();
|
|
47421
|
-
const socketPath =
|
|
47483
|
+
const socketPath = join20(resolveAgentDir(agent), "telegram", "gateway.sock");
|
|
47422
47484
|
const inbound = {
|
|
47423
47485
|
type: "inbound",
|
|
47424
47486
|
chatId: ctx.chatId,
|
|
@@ -47490,7 +47552,7 @@ function evaluateDispatch(args, deps = {}) {
|
|
|
47490
47552
|
const log = deps.log ?? ((s) => process.stderr.write(s));
|
|
47491
47553
|
const now = (deps.now ?? Date.now)();
|
|
47492
47554
|
const nowDate = deps.nowDate ?? (() => new Date(now));
|
|
47493
|
-
const resolveAgentDir = deps.resolveAgentDir ?? ((a) =>
|
|
47555
|
+
const resolveAgentDir = deps.resolveAgentDir ?? ((a) => join20(homedir9(), ".switchroom", "agents", a));
|
|
47494
47556
|
const cooldownStore = deps.cooldownStore ?? createFileCooldownStore(resolveAgentDir);
|
|
47495
47557
|
if (!DISPATCH_SOURCES.includes(args.source))
|
|
47496
47558
|
return 0;
|
|
@@ -47566,10 +47628,10 @@ function resolveChannelTarget(config, agentName3) {
|
|
|
47566
47628
|
function recordWebhookEvent(rec, deps = {}) {
|
|
47567
47629
|
const log = deps.log ?? ((s) => process.stderr.write(s));
|
|
47568
47630
|
const now = rec.ts || (deps.now ?? Date.now)();
|
|
47569
|
-
const resolveAgentDir = deps.resolveAgentDir ?? ((a) =>
|
|
47631
|
+
const resolveAgentDir = deps.resolveAgentDir ?? ((a) => join21(homedir10(), ".switchroom", "agents", a));
|
|
47570
47632
|
const dedupStore = deps.dedupStore ?? createFileDedupStore(resolveAgentDir);
|
|
47571
47633
|
const agent = rec.agent;
|
|
47572
|
-
const telegramDir =
|
|
47634
|
+
const telegramDir = join21(resolveAgentDir(agent), "telegram");
|
|
47573
47635
|
if (rec.source === "github" && rec.delivery_id) {
|
|
47574
47636
|
const originalTs = dedupStore.check(agent, rec.delivery_id, now);
|
|
47575
47637
|
if (originalTs !== undefined) {
|
|
@@ -47578,9 +47640,9 @@ function recordWebhookEvent(rec, deps = {}) {
|
|
|
47578
47640
|
return { status: "deduped", ts: originalTs };
|
|
47579
47641
|
}
|
|
47580
47642
|
}
|
|
47581
|
-
const logPath =
|
|
47643
|
+
const logPath = join21(telegramDir, "webhook-events.jsonl");
|
|
47582
47644
|
try {
|
|
47583
|
-
|
|
47645
|
+
mkdirSync15(telegramDir, { recursive: true });
|
|
47584
47646
|
const record = {
|
|
47585
47647
|
ts: now,
|
|
47586
47648
|
source: rec.source,
|
|
@@ -50255,6 +50317,20 @@ function createPollHealthCheck(options) {
|
|
|
50255
50317
|
};
|
|
50256
50318
|
}
|
|
50257
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
|
+
|
|
50258
50334
|
// gateway/reaction-trigger.ts
|
|
50259
50335
|
var REACTIONS_DEFAULTS = Object.freeze({
|
|
50260
50336
|
enabled: true,
|
|
@@ -50479,10 +50555,10 @@ function escapeBody2(s) {
|
|
|
50479
50555
|
}
|
|
50480
50556
|
|
|
50481
50557
|
// gateway/pid-file.ts
|
|
50482
|
-
import { writeFileSync as
|
|
50558
|
+
import { writeFileSync as writeFileSync12, readFileSync as readFileSync18, unlinkSync as unlinkSync7, renameSync as renameSync6 } from "node:fs";
|
|
50483
50559
|
function writePidFile(path, record) {
|
|
50484
50560
|
const tmp = `${path}.tmp-${process.pid}-${Date.now()}`;
|
|
50485
|
-
|
|
50561
|
+
writeFileSync12(tmp, JSON.stringify(record), "utf-8");
|
|
50486
50562
|
renameSync6(tmp, path);
|
|
50487
50563
|
}
|
|
50488
50564
|
function clearPidFile(path) {
|
|
@@ -50704,10 +50780,10 @@ function safeCount(fn) {
|
|
|
50704
50780
|
}
|
|
50705
50781
|
|
|
50706
50782
|
// gateway/session-marker.ts
|
|
50707
|
-
import { writeFileSync as
|
|
50783
|
+
import { writeFileSync as writeFileSync13, readFileSync as readFileSync20, renameSync as renameSync7, unlinkSync as unlinkSync8 } from "node:fs";
|
|
50708
50784
|
function writeSessionMarker(path, marker) {
|
|
50709
50785
|
const tmp = `${path}.tmp-${process.pid}-${Date.now()}`;
|
|
50710
|
-
|
|
50786
|
+
writeFileSync13(tmp, JSON.stringify(marker), "utf-8");
|
|
50711
50787
|
renameSync7(tmp, path);
|
|
50712
50788
|
}
|
|
50713
50789
|
function readSessionMarker(path) {
|
|
@@ -50734,11 +50810,11 @@ function shouldFireRestartBanner(input) {
|
|
|
50734
50810
|
}
|
|
50735
50811
|
|
|
50736
50812
|
// gateway/clean-shutdown-marker.ts
|
|
50737
|
-
import { writeFileSync as
|
|
50813
|
+
import { writeFileSync as writeFileSync14, readFileSync as readFileSync21, renameSync as renameSync8, unlinkSync as unlinkSync9 } from "node:fs";
|
|
50738
50814
|
var DEFAULT_MAX_AGE_MS = 60000;
|
|
50739
50815
|
function writeCleanShutdownMarker(path, marker) {
|
|
50740
50816
|
const tmp = `${path}.tmp-${process.pid}-${Date.now()}`;
|
|
50741
|
-
|
|
50817
|
+
writeFileSync14(tmp, JSON.stringify(marker), "utf-8");
|
|
50742
50818
|
renameSync8(tmp, path);
|
|
50743
50819
|
}
|
|
50744
50820
|
function readCleanShutdownMarker(path) {
|
|
@@ -51331,7 +51407,7 @@ import {
|
|
|
51331
51407
|
readdirSync as readdirSync3,
|
|
51332
51408
|
readFileSync as readFileSync23
|
|
51333
51409
|
} from "fs";
|
|
51334
|
-
import { join as
|
|
51410
|
+
import { join as join23 } from "path";
|
|
51335
51411
|
|
|
51336
51412
|
// operator-events.ts
|
|
51337
51413
|
function classifyClaudeError(raw) {
|
|
@@ -51817,18 +51893,18 @@ function bumpSubagentActivity(db2, args) {
|
|
|
51817
51893
|
import {
|
|
51818
51894
|
closeSync as closeSync3,
|
|
51819
51895
|
existsSync as existsSync24,
|
|
51820
|
-
mkdirSync as
|
|
51896
|
+
mkdirSync as mkdirSync16,
|
|
51821
51897
|
openSync as openSync3,
|
|
51822
51898
|
readFileSync as readFileSync22,
|
|
51823
51899
|
statSync as statSync6,
|
|
51824
51900
|
unlinkSync as unlinkSync10,
|
|
51825
51901
|
utimesSync,
|
|
51826
|
-
writeFileSync as
|
|
51902
|
+
writeFileSync as writeFileSync15
|
|
51827
51903
|
} from "node:fs";
|
|
51828
|
-
import { join as
|
|
51904
|
+
import { join as join22 } from "node:path";
|
|
51829
51905
|
var TURN_ACTIVE_MARKER_FILE = "turn-active.json";
|
|
51830
51906
|
function touchTurnActiveMarker(stateDir) {
|
|
51831
|
-
const path =
|
|
51907
|
+
const path = join22(stateDir, TURN_ACTIVE_MARKER_FILE);
|
|
51832
51908
|
if (!existsSync24(path))
|
|
51833
51909
|
return;
|
|
51834
51910
|
const now = new Date;
|
|
@@ -52355,8 +52431,8 @@ function startSubagentWatcher(config) {
|
|
|
52355
52431
|
function rescanSubagentDirs() {
|
|
52356
52432
|
if (stopped)
|
|
52357
52433
|
return;
|
|
52358
|
-
const claudeHome =
|
|
52359
|
-
const projectsRoot =
|
|
52434
|
+
const claudeHome = join23(agentDir, ".claude");
|
|
52435
|
+
const projectsRoot = join23(claudeHome, "projects");
|
|
52360
52436
|
if (!fs2.existsSync(projectsRoot))
|
|
52361
52437
|
return;
|
|
52362
52438
|
let projectDirs;
|
|
@@ -52373,7 +52449,7 @@ function startSubagentWatcher(config) {
|
|
|
52373
52449
|
}
|
|
52374
52450
|
continue;
|
|
52375
52451
|
}
|
|
52376
|
-
const projectPath =
|
|
52452
|
+
const projectPath = join23(projectsRoot, pDir);
|
|
52377
52453
|
let sessionDirs;
|
|
52378
52454
|
try {
|
|
52379
52455
|
sessionDirs = fs2.readdirSync(projectPath);
|
|
@@ -52383,7 +52459,7 @@ function startSubagentWatcher(config) {
|
|
|
52383
52459
|
for (const sDir of sessionDirs) {
|
|
52384
52460
|
if (sDir.endsWith(".jsonl"))
|
|
52385
52461
|
continue;
|
|
52386
|
-
const subagentsPath =
|
|
52462
|
+
const subagentsPath = join23(projectPath, sDir, "subagents");
|
|
52387
52463
|
if (!fs2.existsSync(subagentsPath))
|
|
52388
52464
|
continue;
|
|
52389
52465
|
if (!dirWatchers.has(subagentsPath)) {
|
|
@@ -52391,7 +52467,7 @@ function startSubagentWatcher(config) {
|
|
|
52391
52467
|
const w = fs2.watch(subagentsPath, (_event, filename) => {
|
|
52392
52468
|
if (!filename || !filename.toString().startsWith("agent-") || !filename.toString().endsWith(".jsonl"))
|
|
52393
52469
|
return;
|
|
52394
|
-
const filePath =
|
|
52470
|
+
const filePath = join23(subagentsPath, filename.toString());
|
|
52395
52471
|
if (!knownFiles.has(filePath)) {
|
|
52396
52472
|
scanSubagentsDir(subagentsPath);
|
|
52397
52473
|
}
|
|
@@ -52416,7 +52492,7 @@ function startSubagentWatcher(config) {
|
|
|
52416
52492
|
for (const e of entries) {
|
|
52417
52493
|
if (!e.startsWith("agent-") || !e.endsWith(".jsonl"))
|
|
52418
52494
|
continue;
|
|
52419
|
-
const filePath =
|
|
52495
|
+
const filePath = join23(subagentsPath, e);
|
|
52420
52496
|
if (knownFiles.has(filePath))
|
|
52421
52497
|
continue;
|
|
52422
52498
|
const agentId = e.slice("agent-".length, -".jsonl".length);
|
|
@@ -52542,15 +52618,15 @@ function determineRestartReason(opts) {
|
|
|
52542
52618
|
init_boot_card();
|
|
52543
52619
|
|
|
52544
52620
|
// gateway/update-announce.ts
|
|
52545
|
-
import { existsSync as existsSync30, mkdirSync as
|
|
52546
|
-
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";
|
|
52547
52623
|
import { homedir as homedir12 } from "node:os";
|
|
52548
52624
|
|
|
52549
52625
|
// ../src/host-control/audit-reader.ts
|
|
52550
52626
|
import { homedir as homedir11 } from "node:os";
|
|
52551
|
-
import { join as
|
|
52627
|
+
import { join as join27 } from "node:path";
|
|
52552
52628
|
function defaultAuditLogPath(home2 = homedir11()) {
|
|
52553
|
-
return
|
|
52629
|
+
return join27(home2, ".switchroom", "host-control-audit.log");
|
|
52554
52630
|
}
|
|
52555
52631
|
function parseAuditLine(line) {
|
|
52556
52632
|
const trimmed = line.trim();
|
|
@@ -52718,15 +52794,15 @@ function renderUpdateOutcomeLine(entry) {
|
|
|
52718
52794
|
`);
|
|
52719
52795
|
}
|
|
52720
52796
|
function claimUpdateAnnouncement(requestId, opts = {}) {
|
|
52721
|
-
const stateDir = opts.stateDir ?? process.env.TELEGRAM_STATE_DIR ??
|
|
52722
|
-
const dir =
|
|
52797
|
+
const stateDir = opts.stateDir ?? process.env.TELEGRAM_STATE_DIR ?? join28(homedir12(), ".switchroom");
|
|
52798
|
+
const dir = join28(stateDir, "update-announced");
|
|
52723
52799
|
try {
|
|
52724
|
-
|
|
52800
|
+
mkdirSync20(dir, { recursive: true });
|
|
52725
52801
|
} catch {
|
|
52726
52802
|
return false;
|
|
52727
52803
|
}
|
|
52728
52804
|
const safeId = requestId.replace(/[^A-Za-z0-9_.-]/g, "_").slice(0, 200);
|
|
52729
|
-
const path =
|
|
52805
|
+
const path = join28(dir, safeId);
|
|
52730
52806
|
try {
|
|
52731
52807
|
const fd = openSync5(path, "wx");
|
|
52732
52808
|
closeSync5(fd);
|
|
@@ -52745,7 +52821,7 @@ function maybeRenderUpdateAnnouncement(opts = {}) {
|
|
|
52745
52821
|
}
|
|
52746
52822
|
|
|
52747
52823
|
// issues-card.ts
|
|
52748
|
-
import { readFileSync as readFileSync30, writeFileSync as
|
|
52824
|
+
import { readFileSync as readFileSync30, writeFileSync as writeFileSync20 } from "node:fs";
|
|
52749
52825
|
var SEVERITY_EMOJI = {
|
|
52750
52826
|
info: "\u2139\ufe0f",
|
|
52751
52827
|
warn: "\u26a0\ufe0f",
|
|
@@ -52853,7 +52929,7 @@ function readPersistedMessageId(path, log) {
|
|
|
52853
52929
|
}
|
|
52854
52930
|
function writePersistedMessageId(path, messageId, log) {
|
|
52855
52931
|
try {
|
|
52856
|
-
|
|
52932
|
+
writeFileSync20(path, JSON.stringify({ messageId }) + `
|
|
52857
52933
|
`, { mode: 384 });
|
|
52858
52934
|
} catch (err) {
|
|
52859
52935
|
log(`issues-card: persist write failed (${err.message})`);
|
|
@@ -52947,23 +53023,23 @@ function createIssuesCardHandle(opts) {
|
|
|
52947
53023
|
|
|
52948
53024
|
// issues-watcher.ts
|
|
52949
53025
|
import { existsSync as existsSync32, statSync as statSync9 } from "node:fs";
|
|
52950
|
-
import { join as
|
|
53026
|
+
import { join as join30 } from "node:path";
|
|
52951
53027
|
|
|
52952
53028
|
// ../src/issues/store.ts
|
|
52953
53029
|
import {
|
|
52954
53030
|
closeSync as closeSync6,
|
|
52955
53031
|
existsSync as existsSync31,
|
|
52956
|
-
mkdirSync as
|
|
53032
|
+
mkdirSync as mkdirSync21,
|
|
52957
53033
|
openSync as openSync6,
|
|
52958
53034
|
readdirSync as readdirSync5,
|
|
52959
53035
|
readFileSync as readFileSync31,
|
|
52960
53036
|
renameSync as renameSync11,
|
|
52961
53037
|
statSync as statSync8,
|
|
52962
53038
|
unlinkSync as unlinkSync11,
|
|
52963
|
-
writeFileSync as
|
|
53039
|
+
writeFileSync as writeFileSync21,
|
|
52964
53040
|
writeSync as writeSync2
|
|
52965
53041
|
} from "node:fs";
|
|
52966
|
-
import { join as
|
|
53042
|
+
import { join as join29 } from "node:path";
|
|
52967
53043
|
import { randomBytes as randomBytes5 } from "node:crypto";
|
|
52968
53044
|
import { execSync } from "node:child_process";
|
|
52969
53045
|
|
|
@@ -52982,7 +53058,7 @@ init_redact();
|
|
|
52982
53058
|
var ISSUES_FILE = "issues.jsonl";
|
|
52983
53059
|
var ISSUES_LOCK = "issues.lock";
|
|
52984
53060
|
function readAll(stateDir) {
|
|
52985
|
-
const path =
|
|
53061
|
+
const path = join29(stateDir, ISSUES_FILE);
|
|
52986
53062
|
if (!existsSync31(path))
|
|
52987
53063
|
return [];
|
|
52988
53064
|
let raw;
|
|
@@ -53019,7 +53095,7 @@ function list(stateDir, opts = {}) {
|
|
|
53019
53095
|
});
|
|
53020
53096
|
}
|
|
53021
53097
|
function resolve6(stateDir, fingerprint, nowFn = Date.now) {
|
|
53022
|
-
if (!existsSync31(
|
|
53098
|
+
if (!existsSync31(join29(stateDir, ISSUES_FILE)))
|
|
53023
53099
|
return 0;
|
|
53024
53100
|
return withLock(stateDir, () => {
|
|
53025
53101
|
const all = readAll(stateDir);
|
|
@@ -53037,13 +53113,13 @@ function resolve6(stateDir, fingerprint, nowFn = Date.now) {
|
|
|
53037
53113
|
});
|
|
53038
53114
|
}
|
|
53039
53115
|
function writeAll(stateDir, events) {
|
|
53040
|
-
const path =
|
|
53116
|
+
const path = join29(stateDir, ISSUES_FILE);
|
|
53041
53117
|
sweepOrphanTmpFiles(stateDir);
|
|
53042
53118
|
const tmp = `${path}.tmp-${process.pid}-${randomBytes5(4).toString("hex")}`;
|
|
53043
53119
|
const body = events.length === 0 ? "" : events.map((e) => JSON.stringify(e)).join(`
|
|
53044
53120
|
`) + `
|
|
53045
53121
|
`;
|
|
53046
|
-
|
|
53122
|
+
writeFileSync21(tmp, body, "utf-8");
|
|
53047
53123
|
renameSync11(tmp, path);
|
|
53048
53124
|
}
|
|
53049
53125
|
var ORPHAN_TMP_TTL_MS = 60000;
|
|
@@ -53059,7 +53135,7 @@ function sweepOrphanTmpFiles(stateDir) {
|
|
|
53059
53135
|
for (const entry of entries) {
|
|
53060
53136
|
if (!entry.startsWith(TMP_PREFIX))
|
|
53061
53137
|
continue;
|
|
53062
|
-
const tmpPath2 =
|
|
53138
|
+
const tmpPath2 = join29(stateDir, entry);
|
|
53063
53139
|
try {
|
|
53064
53140
|
const stat = statSync8(tmpPath2);
|
|
53065
53141
|
if (stat.mtimeMs < cutoff) {
|
|
@@ -53071,7 +53147,7 @@ function sweepOrphanTmpFiles(stateDir) {
|
|
|
53071
53147
|
var LOCK_RETRY_MS = 25;
|
|
53072
53148
|
var LOCK_TIMEOUT_MS = 1e4;
|
|
53073
53149
|
function withLock(stateDir, fn) {
|
|
53074
|
-
const lockPath =
|
|
53150
|
+
const lockPath = join29(stateDir, ISSUES_LOCK);
|
|
53075
53151
|
const startedAt = Date.now();
|
|
53076
53152
|
let fd = null;
|
|
53077
53153
|
while (fd === null) {
|
|
@@ -53156,7 +53232,7 @@ function isIssueEvent(v) {
|
|
|
53156
53232
|
// issues-watcher.ts
|
|
53157
53233
|
var DEFAULT_POLL_INTERVAL_MS2 = 2000;
|
|
53158
53234
|
function startIssuesWatcher(opts) {
|
|
53159
|
-
const path =
|
|
53235
|
+
const path = join30(opts.stateDir, ISSUES_FILE);
|
|
53160
53236
|
const log = opts.log ?? (() => {});
|
|
53161
53237
|
const intervalMs = opts.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS2;
|
|
53162
53238
|
const setIntervalFn = opts.setInterval ?? setInterval;
|
|
@@ -54090,8 +54166,8 @@ function extractFlowItems(line) {
|
|
|
54090
54166
|
}
|
|
54091
54167
|
|
|
54092
54168
|
// credits-watch.ts
|
|
54093
|
-
import { readFileSync as readFileSync32, writeFileSync as
|
|
54094
|
-
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";
|
|
54095
54171
|
var STATE_FILE = "credits-watch.json";
|
|
54096
54172
|
var DEFAULT_CREDIT_FATAL_REASONS = new Set;
|
|
54097
54173
|
var KNOWN_CREDIT_REASONS = [
|
|
@@ -54113,7 +54189,7 @@ function emptyCreditState() {
|
|
|
54113
54189
|
return { lastNotifiedReason: null, lastNotifiedAt: 0 };
|
|
54114
54190
|
}
|
|
54115
54191
|
function readClaudeJsonOverage(claudeConfigDir) {
|
|
54116
|
-
const path =
|
|
54192
|
+
const path = join31(claudeConfigDir, ".claude.json");
|
|
54117
54193
|
if (!existsSync33(path))
|
|
54118
54194
|
return null;
|
|
54119
54195
|
let raw;
|
|
@@ -54199,7 +54275,7 @@ function escapeHtml9(s) {
|
|
|
54199
54275
|
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
54200
54276
|
}
|
|
54201
54277
|
function loadCreditState(stateDir) {
|
|
54202
|
-
const path =
|
|
54278
|
+
const path = join31(stateDir, STATE_FILE);
|
|
54203
54279
|
if (!existsSync33(path))
|
|
54204
54280
|
return emptyCreditState();
|
|
54205
54281
|
try {
|
|
@@ -54215,15 +54291,15 @@ function loadCreditState(stateDir) {
|
|
|
54215
54291
|
return emptyCreditState();
|
|
54216
54292
|
}
|
|
54217
54293
|
function saveCreditState(stateDir, state4) {
|
|
54218
|
-
|
|
54219
|
-
const path =
|
|
54220
|
-
|
|
54294
|
+
mkdirSync22(stateDir, { recursive: true });
|
|
54295
|
+
const path = join31(stateDir, STATE_FILE);
|
|
54296
|
+
writeFileSync22(path, JSON.stringify(state4, null, 2) + `
|
|
54221
54297
|
`, { mode: 384 });
|
|
54222
54298
|
}
|
|
54223
54299
|
|
|
54224
54300
|
// quota-watch.ts
|
|
54225
|
-
import { readFileSync as readFileSync33, writeFileSync as
|
|
54226
|
-
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";
|
|
54227
54303
|
var STATE_FILE2 = "quota-watch.json";
|
|
54228
54304
|
function emptyQuotaWatchState() {
|
|
54229
54305
|
return {};
|
|
@@ -54404,7 +54480,7 @@ function escapeHtml10(s) {
|
|
|
54404
54480
|
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
54405
54481
|
}
|
|
54406
54482
|
function loadQuotaWatchState(stateDir) {
|
|
54407
|
-
const path =
|
|
54483
|
+
const path = join32(stateDir, STATE_FILE2);
|
|
54408
54484
|
if (!existsSync34(path))
|
|
54409
54485
|
return emptyQuotaWatchState();
|
|
54410
54486
|
try {
|
|
@@ -54425,9 +54501,9 @@ function loadQuotaWatchState(stateDir) {
|
|
|
54425
54501
|
}
|
|
54426
54502
|
}
|
|
54427
54503
|
function saveQuotaWatchState(stateDir, state4) {
|
|
54428
|
-
|
|
54429
|
-
const path =
|
|
54430
|
-
|
|
54504
|
+
mkdirSync23(stateDir, { recursive: true });
|
|
54505
|
+
const path = join32(stateDir, STATE_FILE2);
|
|
54506
|
+
writeFileSync23(path, JSON.stringify(state4, null, 2) + `
|
|
54431
54507
|
`, { mode: 384 });
|
|
54432
54508
|
}
|
|
54433
54509
|
function patchQuotaWatchState(current, accountLabel, accountState) {
|
|
@@ -54441,25 +54517,25 @@ init_auth_snapshot_format();
|
|
|
54441
54517
|
import {
|
|
54442
54518
|
closeSync as closeSync7,
|
|
54443
54519
|
existsSync as existsSync35,
|
|
54444
|
-
mkdirSync as
|
|
54520
|
+
mkdirSync as mkdirSync24,
|
|
54445
54521
|
openSync as openSync7,
|
|
54446
54522
|
readFileSync as readFileSync34,
|
|
54447
54523
|
statSync as statSync10,
|
|
54448
54524
|
unlinkSync as unlinkSync12,
|
|
54449
54525
|
utimesSync as utimesSync2,
|
|
54450
|
-
writeFileSync as
|
|
54526
|
+
writeFileSync as writeFileSync24
|
|
54451
54527
|
} from "node:fs";
|
|
54452
|
-
import { join as
|
|
54528
|
+
import { join as join33 } from "node:path";
|
|
54453
54529
|
var TURN_ACTIVE_MARKER_FILE2 = "turn-active.json";
|
|
54454
54530
|
function writeTurnActiveMarker(stateDir, marker) {
|
|
54455
54531
|
try {
|
|
54456
|
-
|
|
54457
|
-
|
|
54532
|
+
mkdirSync24(stateDir, { recursive: true });
|
|
54533
|
+
writeFileSync24(join33(stateDir, TURN_ACTIVE_MARKER_FILE2), JSON.stringify(marker, null, 2) + `
|
|
54458
54534
|
`, { mode: 384 });
|
|
54459
54535
|
} catch {}
|
|
54460
54536
|
}
|
|
54461
54537
|
function touchTurnActiveMarker2(stateDir) {
|
|
54462
|
-
const path =
|
|
54538
|
+
const path = join33(stateDir, TURN_ACTIVE_MARKER_FILE2);
|
|
54463
54539
|
if (!existsSync35(path))
|
|
54464
54540
|
return;
|
|
54465
54541
|
const now = new Date;
|
|
@@ -54474,11 +54550,11 @@ function touchTurnActiveMarker2(stateDir) {
|
|
|
54474
54550
|
}
|
|
54475
54551
|
function removeTurnActiveMarker(stateDir) {
|
|
54476
54552
|
try {
|
|
54477
|
-
unlinkSync12(
|
|
54553
|
+
unlinkSync12(join33(stateDir, TURN_ACTIVE_MARKER_FILE2));
|
|
54478
54554
|
} catch {}
|
|
54479
54555
|
}
|
|
54480
54556
|
function sweepStaleTurnActiveMarker(stateDir, opts) {
|
|
54481
|
-
const path =
|
|
54557
|
+
const path = join33(stateDir, TURN_ACTIVE_MARKER_FILE2);
|
|
54482
54558
|
if (!existsSync35(path))
|
|
54483
54559
|
return false;
|
|
54484
54560
|
const now = opts.now ?? Date.now();
|
|
@@ -54509,7 +54585,7 @@ function sweepStaleTurnActiveMarker(stateDir, opts) {
|
|
|
54509
54585
|
}
|
|
54510
54586
|
}
|
|
54511
54587
|
function readTurnActiveMarkerAgeMs(stateDir, now) {
|
|
54512
|
-
const path =
|
|
54588
|
+
const path = join33(stateDir, TURN_ACTIVE_MARKER_FILE2);
|
|
54513
54589
|
try {
|
|
54514
54590
|
const st = statSync10(path);
|
|
54515
54591
|
return (now ?? Date.now()) - st.mtimeMs;
|
|
@@ -54519,11 +54595,11 @@ function readTurnActiveMarkerAgeMs(stateDir, now) {
|
|
|
54519
54595
|
}
|
|
54520
54596
|
|
|
54521
54597
|
// ../src/build-info.ts
|
|
54522
|
-
var VERSION = "0.15.
|
|
54523
|
-
var COMMIT_SHA = "
|
|
54524
|
-
var COMMIT_DATE = "2026-06-
|
|
54525
|
-
var LATEST_PR =
|
|
54526
|
-
var COMMITS_AHEAD_OF_TAG =
|
|
54598
|
+
var VERSION = "0.15.42";
|
|
54599
|
+
var COMMIT_SHA = "2c7e12da";
|
|
54600
|
+
var COMMIT_DATE = "2026-06-19T09:09:26+10:00";
|
|
54601
|
+
var LATEST_PR = 2434;
|
|
54602
|
+
var COMMITS_AHEAD_OF_TAG = 5;
|
|
54527
54603
|
|
|
54528
54604
|
// gateway/boot-version.ts
|
|
54529
54605
|
function formatRelativeAgo(iso) {
|
|
@@ -54596,11 +54672,11 @@ init_peercred();
|
|
|
54596
54672
|
import * as net5 from "node:net";
|
|
54597
54673
|
import * as fs2 from "node:fs";
|
|
54598
54674
|
import { homedir as homedir13 } from "node:os";
|
|
54599
|
-
import { join as
|
|
54675
|
+
import { join as join34 } from "node:path";
|
|
54600
54676
|
var DEFAULT_TIMEOUT_MS4 = 2000;
|
|
54601
54677
|
var UNLOCK_TIMEOUT_MS = 30000;
|
|
54602
|
-
var LEGACY_SOCKET_PATH2 =
|
|
54603
|
-
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");
|
|
54604
54680
|
function defaultBrokerSocketPath2() {
|
|
54605
54681
|
if (fs2.existsSync(OPERATOR_SOCKET_PATH2))
|
|
54606
54682
|
return OPERATOR_SOCKET_PATH2;
|
|
@@ -55533,8 +55609,8 @@ function matchesAdminOnlyKey(key, patterns) {
|
|
|
55533
55609
|
}
|
|
55534
55610
|
|
|
55535
55611
|
// registry/turns-schema.ts
|
|
55536
|
-
import { chmodSync as chmodSync5, mkdirSync as
|
|
55537
|
-
import { join as
|
|
55612
|
+
import { chmodSync as chmodSync5, mkdirSync as mkdirSync25 } from "fs";
|
|
55613
|
+
import { join as join35 } from "path";
|
|
55538
55614
|
var DatabaseClass2 = null;
|
|
55539
55615
|
function loadDatabaseClass2() {
|
|
55540
55616
|
if (DatabaseClass2 != null)
|
|
@@ -55597,9 +55673,9 @@ function applySchema(db2) {
|
|
|
55597
55673
|
}
|
|
55598
55674
|
function openTurnsDb(agentDir) {
|
|
55599
55675
|
const Database = loadDatabaseClass2();
|
|
55600
|
-
const dir =
|
|
55601
|
-
|
|
55602
|
-
const path =
|
|
55676
|
+
const dir = join35(agentDir, "telegram");
|
|
55677
|
+
mkdirSync25(dir, { recursive: true, mode: 448 });
|
|
55678
|
+
const path = join35(dir, "registry.db");
|
|
55603
55679
|
const db2 = new Database(path, { create: true });
|
|
55604
55680
|
applySchema(db2);
|
|
55605
55681
|
try {
|
|
@@ -55915,11 +55991,11 @@ installGlobalErrorHandlers();
|
|
|
55915
55991
|
process.on("beforeExit", () => {
|
|
55916
55992
|
shutdownAnalytics();
|
|
55917
55993
|
});
|
|
55918
|
-
var STATE_DIR = process.env.TELEGRAM_STATE_DIR ??
|
|
55919
|
-
var ACCESS_FILE =
|
|
55920
|
-
var APPROVED_DIR =
|
|
55921
|
-
var ENV_FILE =
|
|
55922
|
-
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");
|
|
55923
55999
|
function triggerSelfRestart(targetAgent, reason, delayMs = 300) {
|
|
55924
56000
|
const isDocker = process.env.SWITCHROOM_RUNTIME === "docker";
|
|
55925
56001
|
const selfAgent = process.env.SWITCHROOM_AGENT_NAME;
|
|
@@ -56111,7 +56187,7 @@ function assertSendable(f) {
|
|
|
56111
56187
|
} catch {
|
|
56112
56188
|
throw new Error(`refusing to send file \u2014 cannot resolve real path: ${f}`);
|
|
56113
56189
|
}
|
|
56114
|
-
const inbox =
|
|
56190
|
+
const inbox = join36(stateReal, "inbox");
|
|
56115
56191
|
if (real.startsWith(stateReal + sep3) && !real.startsWith(inbox + sep3)) {
|
|
56116
56192
|
throw new Error(`refusing to send channel state: ${f}`);
|
|
56117
56193
|
}
|
|
@@ -56198,9 +56274,9 @@ function assertAllowedChat(chat_id) {
|
|
|
56198
56274
|
function saveAccess(a) {
|
|
56199
56275
|
if (STATIC)
|
|
56200
56276
|
return;
|
|
56201
|
-
|
|
56277
|
+
mkdirSync27(STATE_DIR, { recursive: true, mode: 448 });
|
|
56202
56278
|
const tmp = ACCESS_FILE + ".tmp";
|
|
56203
|
-
|
|
56279
|
+
writeFileSync26(tmp, JSON.stringify(a, null, 2) + `
|
|
56204
56280
|
`, { mode: 384 });
|
|
56205
56281
|
renameSync12(tmp, ACCESS_FILE);
|
|
56206
56282
|
}
|
|
@@ -56220,7 +56296,7 @@ var HISTORY_ENABLED = HISTORY_ACCESS.historyEnabled !== false;
|
|
|
56220
56296
|
if (HISTORY_ENABLED) {
|
|
56221
56297
|
try {
|
|
56222
56298
|
initHistory(STATE_DIR, HISTORY_ACCESS.historyRetentionDays ?? 30);
|
|
56223
|
-
process.stderr.write(`telegram gateway: history capture enabled at ${
|
|
56299
|
+
process.stderr.write(`telegram gateway: history capture enabled at ${join36(STATE_DIR, "history.db")}
|
|
56224
56300
|
`);
|
|
56225
56301
|
} catch (err) {
|
|
56226
56302
|
process.stderr.write(`telegram gateway: history init failed (${err.message}) \u2014 capture disabled
|
|
@@ -56236,7 +56312,7 @@ try {
|
|
|
56236
56312
|
let markerTurnKey = null;
|
|
56237
56313
|
let markerAgeMs = null;
|
|
56238
56314
|
try {
|
|
56239
|
-
const markerPath =
|
|
56315
|
+
const markerPath = join36(STATE_DIR, TURN_ACTIVE_MARKER_FILE2);
|
|
56240
56316
|
if (existsSync38(markerPath)) {
|
|
56241
56317
|
const st = statSync13(markerPath);
|
|
56242
56318
|
markerAgeMs = Date.now() - st.mtimeMs;
|
|
@@ -56261,7 +56337,7 @@ try {
|
|
|
56261
56337
|
process.stderr.write(`telegram gateway: turn-registry boot-reaper stamped ${reaped} orphaned turn(s)${timeoutTurnKey ? ` (turnKey=${timeoutTurnKey} as 'timeout', markerAgeMs=${markerAgeMs})` : " as 'restart'"}
|
|
56262
56338
|
`);
|
|
56263
56339
|
} else {
|
|
56264
|
-
process.stderr.write(`telegram gateway: turn-registry initialized at ${
|
|
56340
|
+
process.stderr.write(`telegram gateway: turn-registry initialized at ${join36(agentDir, "telegram", "registry.db")}
|
|
56265
56341
|
`);
|
|
56266
56342
|
}
|
|
56267
56343
|
const pending2 = findLatestTurnIfInterrupted(turnsDb);
|
|
@@ -56298,7 +56374,7 @@ try {
|
|
|
56298
56374
|
`);
|
|
56299
56375
|
}
|
|
56300
56376
|
}
|
|
56301
|
-
const pendingEnvPath =
|
|
56377
|
+
const pendingEnvPath = join36(agentDir, ".pending-turn.env");
|
|
56302
56378
|
try {
|
|
56303
56379
|
if (pending2 != null) {
|
|
56304
56380
|
const lines = [
|
|
@@ -56312,7 +56388,7 @@ try {
|
|
|
56312
56388
|
pending2.interrupt_reason != null ? `SWITCHROOM_PENDING_INTERRUPT_REASON=${pending2.interrupt_reason}` : `SWITCHROOM_PENDING_INTERRUPT_REASON=`
|
|
56313
56389
|
];
|
|
56314
56390
|
const pendingEnvTmp = `${pendingEnvPath}.tmp-${process.pid}`;
|
|
56315
|
-
|
|
56391
|
+
writeFileSync26(pendingEnvTmp, lines.join(`
|
|
56316
56392
|
`) + `
|
|
56317
56393
|
`, { mode: 384 });
|
|
56318
56394
|
renameSync12(pendingEnvTmp, pendingEnvPath);
|
|
@@ -56391,7 +56467,7 @@ function checkApprovals() {
|
|
|
56391
56467
|
return;
|
|
56392
56468
|
}
|
|
56393
56469
|
for (const senderId of files) {
|
|
56394
|
-
const file =
|
|
56470
|
+
const file = join36(APPROVED_DIR, senderId);
|
|
56395
56471
|
bot.api.sendMessage(senderId, "Paired! Say hi to Claude.").then(() => rmSync4(file, { force: true }), (err) => {
|
|
56396
56472
|
process.stderr.write(`telegram gateway: failed to send approval confirm: ${err}
|
|
56397
56473
|
`);
|
|
@@ -56401,6 +56477,9 @@ function checkApprovals() {
|
|
|
56401
56477
|
}
|
|
56402
56478
|
if (!STATIC)
|
|
56403
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();
|
|
56404
56483
|
var chatThreadMap = new Map;
|
|
56405
56484
|
var activeStatusReactions = new Map;
|
|
56406
56485
|
var activeReactionMsgIds = new Map;
|
|
@@ -56471,10 +56550,10 @@ function noteAgentOutputAt(key, ts) {
|
|
|
56471
56550
|
lastAgentOutputAt.delete(oldest);
|
|
56472
56551
|
}
|
|
56473
56552
|
}
|
|
56474
|
-
var OBLIGATION_STORE_PATH =
|
|
56553
|
+
var OBLIGATION_STORE_PATH = join36(STATE_DIR, "obligations.json");
|
|
56475
56554
|
var obligationStoreFs = {
|
|
56476
56555
|
readFileSync: (p) => readFileSync36(p, "utf8"),
|
|
56477
|
-
writeFileSync: (p, d) =>
|
|
56556
|
+
writeFileSync: (p, d) => writeFileSync26(p, d),
|
|
56478
56557
|
renameSync: (a, b) => renameSync12(a, b),
|
|
56479
56558
|
existsSync: (p) => existsSync38(p)
|
|
56480
56559
|
};
|
|
@@ -56862,8 +56941,33 @@ function purgeReactionTracking(key, endingTurn) {
|
|
|
56862
56941
|
} else {
|
|
56863
56942
|
maybeProactiveCompact();
|
|
56864
56943
|
}
|
|
56944
|
+
snapshotContextOccupancy();
|
|
56865
56945
|
}
|
|
56866
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
|
+
}
|
|
56867
56971
|
function releaseTurnBufferGate(key, endingTurn) {
|
|
56868
56972
|
if (!activeTurnStartedAt.has(key))
|
|
56869
56973
|
return;
|
|
@@ -56955,6 +57059,81 @@ function maybeProactiveCompact() {
|
|
|
56955
57059
|
compactDispatching = false;
|
|
56956
57060
|
});
|
|
56957
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
|
+
}
|
|
56958
57137
|
async function postCompactCard(occ, cap) {
|
|
56959
57138
|
try {
|
|
56960
57139
|
const chatId = loadAccess().allowFrom[0];
|
|
@@ -57758,11 +57937,11 @@ var unpinProgressCardForChat = null;
|
|
|
57758
57937
|
var getPinnedProgressCardMessageId = null;
|
|
57759
57938
|
var completeProgressCardTurn = null;
|
|
57760
57939
|
var subagentWatcher = null;
|
|
57761
|
-
var SOCKET_PATH = process.env.SWITCHROOM_GATEWAY_SOCKET ??
|
|
57762
|
-
|
|
57763
|
-
var GATEWAY_PID_PATH = process.env.SWITCHROOM_GATEWAY_PID_FILE ??
|
|
57764
|
-
var GATEWAY_SESSION_MARKER_PATH = process.env.SWITCHROOM_GATEWAY_SESSION_MARKER ??
|
|
57765
|
-
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");
|
|
57766
57945
|
var GATEWAY_STARTED_AT_MS = Date.now();
|
|
57767
57946
|
var BOOT_CARD_ENABLED = process.env.SWITCHROOM_BOOT_CARD !== "false";
|
|
57768
57947
|
var activeBootCard = null;
|
|
@@ -57791,7 +57970,7 @@ function ensureIssuesCard(chatId, threadId) {
|
|
|
57791
57970
|
bot: botApi,
|
|
57792
57971
|
log: (msg) => process.stderr.write(`telegram gateway: ${msg}
|
|
57793
57972
|
`),
|
|
57794
|
-
persistPath:
|
|
57973
|
+
persistPath: join36(stateDir, "issues-card.json")
|
|
57795
57974
|
});
|
|
57796
57975
|
activeIssuesWatcher = startIssuesWatcher({
|
|
57797
57976
|
stateDir,
|
|
@@ -58020,11 +58199,11 @@ startTimer2({
|
|
|
58020
58199
|
}
|
|
58021
58200
|
});
|
|
58022
58201
|
var inboundSpool = STATIC ? undefined : createInboundSpool({
|
|
58023
|
-
path:
|
|
58202
|
+
path: join36(STATE_DIR, "inbound-spool.jsonl"),
|
|
58024
58203
|
fs: {
|
|
58025
58204
|
appendFileSync: (p, d) => appendFileSync5(p, d),
|
|
58026
58205
|
readFileSync: (p) => readFileSync36(p, "utf8"),
|
|
58027
|
-
writeFileSync: (p, d) =>
|
|
58206
|
+
writeFileSync: (p, d) => writeFileSync26(p, d),
|
|
58028
58207
|
renameSync: (a, b) => renameSync12(a, b),
|
|
58029
58208
|
existsSync: (p) => existsSync38(p),
|
|
58030
58209
|
statSizeSync: (p) => statSync13(p).size
|
|
@@ -58240,8 +58419,8 @@ var ipcServer = createIpcServer({
|
|
|
58240
58419
|
probeQuotaViaBroker: (t) => probeQuotaForBootCard(agentSlug, t),
|
|
58241
58420
|
tmuxSupervisor: process.env.SWITCHROOM_TMUX_SUPERVISOR === "1",
|
|
58242
58421
|
dockerMode: process.env.SWITCHROOM_RUNTIME === "docker",
|
|
58243
|
-
configSnapshotPath:
|
|
58244
|
-
bootCardStatePath:
|
|
58422
|
+
configSnapshotPath: join36(resolvedAgentDirForCard, ".config-snapshot.json"),
|
|
58423
|
+
bootCardStatePath: join36(resolvedAgentDirForCard, ".boot-card-msgid.json"),
|
|
58245
58424
|
...updateOutcomeLine ? { updateOutcomeLine } : {}
|
|
58246
58425
|
}, ackMsgId).then((handle) => {
|
|
58247
58426
|
activeBootCard = handle;
|
|
@@ -58665,6 +58844,7 @@ var ipcServer = createIpcServer({
|
|
|
58665
58844
|
});
|
|
58666
58845
|
},
|
|
58667
58846
|
onInjectInbound(_client, msg) {
|
|
58847
|
+
markIdleActivity();
|
|
58668
58848
|
const promptKey = typeof msg.inbound.meta?.prompt_key === "string" ? msg.inbound.meta.prompt_key : "unknown";
|
|
58669
58849
|
const source = typeof msg.inbound.meta?.source === "string" ? msg.inbound.meta.source : "unknown";
|
|
58670
58850
|
const { target, delivered, fellBackToMain } = deliverInjectWithFallback(msg.agentName, msg.inbound.meta, (t) => ipcServer.sendToAgent(t, msg.inbound));
|
|
@@ -58737,7 +58917,7 @@ var ipcServer = createIpcServer({
|
|
|
58737
58917
|
const receiverUid = receiverUidRaw ? Number(receiverUidRaw) : NaN;
|
|
58738
58918
|
if (Number.isInteger(receiverUid))
|
|
58739
58919
|
allowedUids.push(receiverUid);
|
|
58740
|
-
const socketPath =
|
|
58920
|
+
const socketPath = join36(STATE_DIR, "webhook.sock");
|
|
58741
58921
|
const webhookInject = (agentName3, inbound) => {
|
|
58742
58922
|
const msg = inbound;
|
|
58743
58923
|
const delivered = ipcServer.sendToAgent(agentName3, msg);
|
|
@@ -59848,7 +60028,7 @@ async function executeSendGif(rawArgs) {
|
|
|
59848
60028
|
};
|
|
59849
60029
|
}
|
|
59850
60030
|
async function publishToTelegraph(text2, shortName, authorName) {
|
|
59851
|
-
const accountPath =
|
|
60031
|
+
const accountPath = join36(STATE_DIR, "telegraph-account.json");
|
|
59852
60032
|
let account = null;
|
|
59853
60033
|
try {
|
|
59854
60034
|
if (existsSync38(accountPath)) {
|
|
@@ -59871,8 +60051,8 @@ async function publishToTelegraph(text2, shortName, authorName) {
|
|
|
59871
60051
|
}
|
|
59872
60052
|
account = created.value;
|
|
59873
60053
|
try {
|
|
59874
|
-
|
|
59875
|
-
|
|
60054
|
+
mkdirSync27(STATE_DIR, { recursive: true, mode: 448 });
|
|
60055
|
+
writeFileSync26(accountPath, JSON.stringify(account, null, 2), { mode: 384 });
|
|
59876
60056
|
} catch (err) {
|
|
59877
60057
|
process.stderr.write(`telegram gateway: telegraph cache write failed: ${err.message}
|
|
59878
60058
|
`);
|
|
@@ -60332,9 +60512,9 @@ async function executeDownloadAttachment(args) {
|
|
|
60332
60512
|
fileUniqueId: file.file_unique_id,
|
|
60333
60513
|
now: Date.now()
|
|
60334
60514
|
});
|
|
60335
|
-
|
|
60515
|
+
mkdirSync27(INBOX_DIR, { recursive: true, mode: 448 });
|
|
60336
60516
|
assertInsideInbox(INBOX_DIR, dlPath);
|
|
60337
|
-
|
|
60517
|
+
writeFileSync26(dlPath, buf, { mode: 384 });
|
|
60338
60518
|
return { content: [{ type: "text", text: dlPath }] };
|
|
60339
60519
|
}
|
|
60340
60520
|
async function executeEditMessage(args) {
|
|
@@ -60709,6 +60889,7 @@ function handleSessionEvent(ev) {
|
|
|
60709
60889
|
isDm: isDmChatId(ev.chatId)
|
|
60710
60890
|
};
|
|
60711
60891
|
currentTurn = next;
|
|
60892
|
+
markIdleActivity();
|
|
60712
60893
|
process.stderr.write(`telegram gateway: ${formatTurnLifecycle("set", "enqueue", next, startedAt)}
|
|
60713
60894
|
`);
|
|
60714
60895
|
rememberRecentTurn(next);
|
|
@@ -61495,6 +61676,7 @@ function maybeEarlyAckReaction(ctx, from) {
|
|
|
61495
61676
|
bot.api.sendChatAction(chatId, "typing").catch(() => {});
|
|
61496
61677
|
}
|
|
61497
61678
|
async function handleInbound(ctx, text2, downloadImage, attachment, extraAttachments) {
|
|
61679
|
+
markIdleActivity();
|
|
61498
61680
|
const isTopicMessage = ctx.message?.is_topic_message ?? false;
|
|
61499
61681
|
const messageThreadId = ctx.message?.message_thread_id;
|
|
61500
61682
|
if (TOPIC_ID != null) {
|
|
@@ -62319,14 +62501,14 @@ function restartMarkerPath() {
|
|
|
62319
62501
|
const agentDir = resolveAgentDirFromEnv();
|
|
62320
62502
|
if (!agentDir)
|
|
62321
62503
|
return null;
|
|
62322
|
-
return
|
|
62504
|
+
return join36(agentDir, "restart-pending.json");
|
|
62323
62505
|
}
|
|
62324
62506
|
function writeRestartMarker(marker) {
|
|
62325
62507
|
const p = restartMarkerPath();
|
|
62326
62508
|
if (!p)
|
|
62327
62509
|
return;
|
|
62328
62510
|
try {
|
|
62329
|
-
|
|
62511
|
+
writeFileSync26(p, JSON.stringify(marker));
|
|
62330
62512
|
lastPlannedRestartAt = Date.now();
|
|
62331
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}
|
|
62332
62514
|
`);
|
|
@@ -62511,12 +62693,12 @@ function _resetDockerReachableCache() {
|
|
|
62511
62693
|
}
|
|
62512
62694
|
function spawnSwitchroomDetached(args, onFailure) {
|
|
62513
62695
|
const fullArgs = SWITCHROOM_CONFIG ? ["--config", SWITCHROOM_CONFIG, ...args] : args;
|
|
62514
|
-
const logPath =
|
|
62696
|
+
const logPath = join36(STATE_DIR, "detached-spawn.log");
|
|
62515
62697
|
let outFd = null;
|
|
62516
62698
|
try {
|
|
62517
|
-
|
|
62699
|
+
mkdirSync27(STATE_DIR, { recursive: true });
|
|
62518
62700
|
outFd = openSync8(logPath, "a");
|
|
62519
|
-
|
|
62701
|
+
writeFileSync26(logPath, `
|
|
62520
62702
|
[${new Date().toISOString()}] spawn ${SWITCHROOM_CLI} ${fullArgs.join(" ")}
|
|
62521
62703
|
`, { flag: "a" });
|
|
62522
62704
|
} catch {}
|
|
@@ -62904,7 +63086,7 @@ bot.use(async (ctx, next) => {
|
|
|
62904
63086
|
});
|
|
62905
63087
|
function readRecentDenialsForAgent(agentName3, windowMs, limit) {
|
|
62906
63088
|
try {
|
|
62907
|
-
const auditPath =
|
|
63089
|
+
const auditPath = join36(homedir14(), ".switchroom", "vault-audit.log");
|
|
62908
63090
|
if (!existsSync38(auditPath))
|
|
62909
63091
|
return [];
|
|
62910
63092
|
const raw = readFileSync36(auditPath, "utf8");
|
|
@@ -62959,7 +63141,7 @@ async function buildAgentMetadata(agentName3) {
|
|
|
62959
63141
|
try {
|
|
62960
63142
|
const agentDir = resolveAgentDirFromEnv();
|
|
62961
63143
|
if (agentDir) {
|
|
62962
|
-
const raw = readFileSync36(
|
|
63144
|
+
const raw = readFileSync36(join36(agentDir, ".claude", ".claude.json"), "utf8");
|
|
62963
63145
|
claudeJson = JSON.parse(raw);
|
|
62964
63146
|
}
|
|
62965
63147
|
} catch {}
|
|
@@ -63074,17 +63256,26 @@ bot.command("agents", async (ctx) => {
|
|
|
63074
63256
|
`);
|
|
63075
63257
|
});
|
|
63076
63258
|
});
|
|
63077
|
-
|
|
63078
|
-
|
|
63079
|
-
isAuthorized: isAuthorizedSender,
|
|
63259
|
+
function buildInjectDeps(opts) {
|
|
63260
|
+
return {
|
|
63261
|
+
isAuthorized: opts?.open ? () => true : isAuthorizedSender,
|
|
63080
63262
|
inject: injectSlashCommand,
|
|
63081
|
-
reply: async (
|
|
63263
|
+
reply: async (ctx, text2, replyOpts) => switchroomReply(ctx, text2, { html: replyOpts?.html }),
|
|
63082
63264
|
getAgentName: getMyAgentName,
|
|
63083
|
-
getArgs: getCommandArgs,
|
|
63265
|
+
getArgs: opts?.fixedVerb != null ? () => opts.fixedVerb : getCommandArgs,
|
|
63084
63266
|
escapeHtml: escapeHtmlForTg,
|
|
63085
63267
|
preBlock,
|
|
63086
63268
|
formatOutput: formatSwitchroomOutput
|
|
63087
|
-
}
|
|
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" }));
|
|
63088
63279
|
});
|
|
63089
63280
|
function buildModelDeps() {
|
|
63090
63281
|
return {
|
|
@@ -63101,7 +63292,7 @@ function buildModelDeps() {
|
|
|
63101
63292
|
try {
|
|
63102
63293
|
const agentDir = resolveAgentDirFromEnv();
|
|
63103
63294
|
if (agentDir) {
|
|
63104
|
-
const local = await fetchQuota2({ claudeConfigDir:
|
|
63295
|
+
const local = await fetchQuota2({ claudeConfigDir: join36(agentDir, ".claude") });
|
|
63105
63296
|
if (local.ok)
|
|
63106
63297
|
return formatQuotaLine2(local.data);
|
|
63107
63298
|
}
|
|
@@ -63267,7 +63458,7 @@ bot.command("restart", async (ctx) => {
|
|
|
63267
63458
|
function flushAgentHandoff(agentDir) {
|
|
63268
63459
|
let removed = 0;
|
|
63269
63460
|
for (const fname of [".handoff.md", ".handoff-topic"]) {
|
|
63270
|
-
const p =
|
|
63461
|
+
const p = join36(agentDir, fname);
|
|
63271
63462
|
try {
|
|
63272
63463
|
if (existsSync38(p)) {
|
|
63273
63464
|
unlinkSync14(p);
|
|
@@ -63280,7 +63471,8 @@ function flushAgentHandoff(agentDir) {
|
|
|
63280
63471
|
}
|
|
63281
63472
|
return removed;
|
|
63282
63473
|
}
|
|
63283
|
-
async function
|
|
63474
|
+
async function handleNewCommand(ctx) {
|
|
63475
|
+
const kind = "new";
|
|
63284
63476
|
if (!isAuthorizedSender(ctx))
|
|
63285
63477
|
return;
|
|
63286
63478
|
const name = (typeof ctx.match === "string" ? ctx.match : "").trim() || getMyAgentName();
|
|
@@ -63307,7 +63499,7 @@ async function handleNewOrResetCommand(ctx, kind) {
|
|
|
63307
63499
|
const flushed = agentDir != null ? flushAgentHandoff(agentDir) : 0;
|
|
63308
63500
|
const chatId = String(ctx.chat.id);
|
|
63309
63501
|
const threadId = resolveThreadId(chatId, ctx.message?.message_thread_id);
|
|
63310
|
-
const ackText =
|
|
63502
|
+
const ackText = newSessionAckText(name, flushed > 0);
|
|
63311
63503
|
let ackId = null;
|
|
63312
63504
|
try {
|
|
63313
63505
|
const sent = await retryWithThreadFallback(robustApiCall, (tid) => lockedBot.api.sendMessage(chatId, ackText, {
|
|
@@ -63325,7 +63517,7 @@ async function handleNewOrResetCommand(ctx, kind) {
|
|
|
63325
63517
|
writeRestartMarker({ chat_id: chatId, thread_id: threadId ?? null, ack_message_id: ackId, ts: Date.now() });
|
|
63326
63518
|
if (agentDir != null) {
|
|
63327
63519
|
try {
|
|
63328
|
-
|
|
63520
|
+
writeFileSync26(join36(agentDir, ".force-fresh-session"), `${kind} at ${new Date().toISOString()}
|
|
63329
63521
|
`, "utf8");
|
|
63330
63522
|
} catch (err) {
|
|
63331
63523
|
process.stderr.write(`telegram gateway: failed to write force-fresh marker: ${err}
|
|
@@ -63352,8 +63544,7 @@ async function handleNewOrResetCommand(ctx, kind) {
|
|
|
63352
63544
|
await switchroomReply(ctx, `\u274C <b>${escapeHtmlForTg(kind)} ${escapeHtmlForTg(name)} failed via hostd</b> (result=${escapeHtmlForTg(hostdResp.result)}):
|
|
63353
63545
|
` + preBlock(hostdResp.error ?? "(no error message)"), { html: true });
|
|
63354
63546
|
}
|
|
63355
|
-
bot.command("new", async (ctx) =>
|
|
63356
|
-
bot.command("reset", async (ctx) => handleNewOrResetCommand(ctx, "reset"));
|
|
63547
|
+
bot.command("new", async (ctx) => handleNewCommand(ctx));
|
|
63357
63548
|
bot.command("update", async (ctx) => {
|
|
63358
63549
|
if (!isAuthorizedSender(ctx))
|
|
63359
63550
|
return;
|
|
@@ -63691,10 +63882,10 @@ bot.command("interrupt", async (ctx) => {
|
|
|
63691
63882
|
});
|
|
63692
63883
|
var lockoutOps = {
|
|
63693
63884
|
readFileSync: (p, enc) => readFileSync36(p, enc),
|
|
63694
|
-
writeFileSync: (p, data, opts) =>
|
|
63885
|
+
writeFileSync: (p, data, opts) => writeFileSync26(p, data, opts),
|
|
63695
63886
|
existsSync: (p) => existsSync38(p),
|
|
63696
|
-
mkdirSync: (p, opts) =>
|
|
63697
|
-
joinPath: (...parts) =>
|
|
63887
|
+
mkdirSync: (p, opts) => mkdirSync27(p, opts),
|
|
63888
|
+
joinPath: (...parts) => join36(...parts)
|
|
63698
63889
|
};
|
|
63699
63890
|
var FLEET_FALLBACK_DEDUP_MS = 30000;
|
|
63700
63891
|
function isAuthBrokerSocketReachable() {
|
|
@@ -63784,7 +63975,7 @@ async function runCreditWatch() {
|
|
|
63784
63975
|
if (!agentDir)
|
|
63785
63976
|
return;
|
|
63786
63977
|
const agentName3 = getMyAgentName();
|
|
63787
|
-
const claudeConfigDir =
|
|
63978
|
+
const claudeConfigDir = join36(agentDir, ".claude");
|
|
63788
63979
|
const stateDir = STATE_DIR;
|
|
63789
63980
|
const reason = readClaudeJsonOverage(claudeConfigDir);
|
|
63790
63981
|
const prev = loadCreditState(stateDir);
|
|
@@ -64310,10 +64501,10 @@ async function handleVaultRecentDenialCallback(ctx, data) {
|
|
|
64310
64501
|
return;
|
|
64311
64502
|
}
|
|
64312
64503
|
const { token, id } = result;
|
|
64313
|
-
const tokenPath =
|
|
64504
|
+
const tokenPath = join36(homedir14(), ".switchroom", "agents", agentName3, ".vault-token");
|
|
64314
64505
|
try {
|
|
64315
|
-
|
|
64316
|
-
|
|
64506
|
+
mkdirSync27(join36(homedir14(), ".switchroom", "agents", agentName3), { recursive: true });
|
|
64507
|
+
writeFileSync26(tokenPath, token, { mode: 384 });
|
|
64317
64508
|
} catch (err) {
|
|
64318
64509
|
await switchroomReply(ctx, `<b>Grant created (${escapeHtmlForTg(id)}) but token write failed:</b> ${escapeHtmlForTg(String(err))}
|
|
64319
64510
|
<i>Recover with: <code>switchroom vault grant ${escapeHtmlForTg(agentName3)} --keys ${escapeHtmlForTg(keyName)} --duration 30d</code> on the host.</i>`, { html: true });
|
|
@@ -64389,10 +64580,10 @@ async function performVaultAccessApproval(ctx, pending2, stageId, senderId, atte
|
|
|
64389
64580
|
return;
|
|
64390
64581
|
}
|
|
64391
64582
|
const { token, id } = result;
|
|
64392
|
-
const tokenPath =
|
|
64583
|
+
const tokenPath = join36(homedir14(), ".switchroom", "agents", pending2.agent, ".vault-token");
|
|
64393
64584
|
try {
|
|
64394
|
-
|
|
64395
|
-
|
|
64585
|
+
mkdirSync27(join36(homedir14(), ".switchroom", "agents", pending2.agent), { recursive: true });
|
|
64586
|
+
writeFileSync26(tokenPath, token, { mode: 384 });
|
|
64396
64587
|
} catch (err) {
|
|
64397
64588
|
await switchroomReply(ctx, `<b>Grant created (${escapeHtmlForTg(id)}) but token write failed:</b> ${escapeHtmlForTg(String(err))}
|
|
64398
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 });
|
|
@@ -64871,10 +65062,10 @@ async function executeGrantWizard(ctx, chatId, state4) {
|
|
|
64871
65062
|
return;
|
|
64872
65063
|
}
|
|
64873
65064
|
const { token, id } = result;
|
|
64874
|
-
const tokenPath =
|
|
65065
|
+
const tokenPath = join36(homedir14(), ".switchroom", "agents", state4.agent, ".vault-token");
|
|
64875
65066
|
try {
|
|
64876
|
-
|
|
64877
|
-
|
|
65067
|
+
mkdirSync27(join36(homedir14(), ".switchroom", "agents", state4.agent), { recursive: true });
|
|
65068
|
+
writeFileSync26(tokenPath, token, { mode: 384 });
|
|
64878
65069
|
} catch (err) {
|
|
64879
65070
|
await switchroomReply(ctx, `<b>Grant created but token write failed:</b> ${escapeHtmlForTg(String(err))}`, { html: true });
|
|
64880
65071
|
return;
|
|
@@ -65722,7 +65913,7 @@ bot.command("usage", async (ctx) => {
|
|
|
65722
65913
|
await switchroomReply(ctx, "<b>/usage:</b> cannot resolve agent dir.", { html: true });
|
|
65723
65914
|
return;
|
|
65724
65915
|
}
|
|
65725
|
-
const result = await fetchQuota2({ claudeConfigDir:
|
|
65916
|
+
const result = await fetchQuota2({ claudeConfigDir: join36(agentDir, ".claude") });
|
|
65726
65917
|
if (!result.ok) {
|
|
65727
65918
|
await switchroomReply(ctx, `<b>/usage:</b> ${escapeHtmlForTg(result.reason)}`, { html: true });
|
|
65728
65919
|
return;
|
|
@@ -66462,9 +66653,9 @@ bot.on("message:photo", async (ctx) => {
|
|
|
66462
66653
|
fileUniqueId: best.file_unique_id,
|
|
66463
66654
|
now: Date.now()
|
|
66464
66655
|
});
|
|
66465
|
-
|
|
66656
|
+
mkdirSync27(INBOX_DIR, { recursive: true, mode: 448 });
|
|
66466
66657
|
assertInsideInbox(INBOX_DIR, dlPath);
|
|
66467
|
-
|
|
66658
|
+
writeFileSync26(dlPath, buf, { mode: 384 });
|
|
66468
66659
|
return dlPath;
|
|
66469
66660
|
} catch (err) {
|
|
66470
66661
|
const msg = err instanceof Error ? err.message : "unknown error";
|
|
@@ -67243,25 +67434,14 @@ process.on("uncaughtException", (err) => {
|
|
|
67243
67434
|
shutdown("uncaughtException");
|
|
67244
67435
|
});
|
|
67245
67436
|
var runnerHandle = null;
|
|
67246
|
-
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);
|
|
67247
67438
|
var POLL_HEALTH_THRESHOLD = Number(process.env.SWITCHROOM_POLL_HEALTH_THRESHOLD ?? 3);
|
|
67248
67439
|
var pollHealthCheck = null;
|
|
67249
67440
|
if (POLL_HEALTH_INTERVAL_MS > 0) {
|
|
67250
67441
|
pollHealthCheck = createPollHealthCheck({
|
|
67251
67442
|
ping: () => bot.api.getMe(),
|
|
67252
67443
|
onStall: async () => {
|
|
67253
|
-
|
|
67254
|
-
process.stderr.write(`telegram gateway: poll.health_check.stall_recovery stopping runner agent=${agentName3}
|
|
67255
|
-
`);
|
|
67256
|
-
if (runnerHandle != null && runnerHandle.isRunning()) {
|
|
67257
|
-
try {
|
|
67258
|
-
await runnerHandle.stop();
|
|
67259
|
-
} catch (err) {
|
|
67260
|
-
process.stderr.write(`telegram gateway: poll.health_check.stall_recovery runner.stop error: ${err.message}
|
|
67261
|
-
`);
|
|
67262
|
-
}
|
|
67263
|
-
}
|
|
67264
|
-
runnerHandle = null;
|
|
67444
|
+
recoverFromPollStall({ agentName: process.env.SWITCHROOM_AGENT_NAME ?? "-" });
|
|
67265
67445
|
},
|
|
67266
67446
|
intervalMs: POLL_HEALTH_INTERVAL_MS,
|
|
67267
67447
|
failureThreshold: POLL_HEALTH_THRESHOLD,
|
|
@@ -67449,7 +67629,7 @@ var didOneTimeSetup = false;
|
|
|
67449
67629
|
return;
|
|
67450
67630
|
}
|
|
67451
67631
|
})();
|
|
67452
|
-
const resolvedAgentDirForBootCard = agentDir ??
|
|
67632
|
+
const resolvedAgentDirForBootCard = agentDir ?? join36(homedir14(), ".switchroom", "agents", agentSlug);
|
|
67453
67633
|
const handle = await startBootCard(chatId, threadId, botApiForCard, {
|
|
67454
67634
|
agentName: agentDisplayName,
|
|
67455
67635
|
agentSlug,
|
|
@@ -67463,8 +67643,8 @@ var didOneTimeSetup = false;
|
|
|
67463
67643
|
probeQuotaViaBroker: (t) => probeQuotaForBootCard(agentSlug, t),
|
|
67464
67644
|
tmuxSupervisor: process.env.SWITCHROOM_TMUX_SUPERVISOR === "1",
|
|
67465
67645
|
dockerMode: process.env.SWITCHROOM_RUNTIME === "docker",
|
|
67466
|
-
configSnapshotPath:
|
|
67467
|
-
bootCardStatePath:
|
|
67646
|
+
configSnapshotPath: join36(resolvedAgentDirForBootCard, ".config-snapshot.json"),
|
|
67647
|
+
bootCardStatePath: join36(resolvedAgentDirForBootCard, ".boot-card-msgid.json"),
|
|
67468
67648
|
...updateOutcomeLine ? { updateOutcomeLine } : {}
|
|
67469
67649
|
}, ackMsgId);
|
|
67470
67650
|
activeBootCard = handle;
|
|
@@ -67851,13 +68031,6 @@ var didOneTimeSetup = false;
|
|
|
67851
68031
|
pollHealthCheck?.stop();
|
|
67852
68032
|
pollHealthCheck?.start();
|
|
67853
68033
|
await runnerHandle.task();
|
|
67854
|
-
if (runnerHandle === null) {
|
|
67855
|
-
const agentName3 = process.env.SWITCHROOM_AGENT_NAME ?? "-";
|
|
67856
|
-
process.stderr.write(`telegram gateway: poll.health_check.stall_recovery restarting runner agent=${agentName3}
|
|
67857
|
-
`);
|
|
67858
|
-
await new Promise((r) => setTimeout(r, 2000));
|
|
67859
|
-
continue;
|
|
67860
|
-
}
|
|
67861
68034
|
return;
|
|
67862
68035
|
} catch (err) {
|
|
67863
68036
|
if (err instanceof import_grammy9.GrammyError && err.error_code === 409) {
|