switchroom 0.14.20 → 0.14.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent-scheduler/index.js +2 -3
- package/dist/auth-broker/index.js +2 -3
- package/dist/cli/notion-write-pretool.mjs +2 -3
- package/dist/cli/switchroom.js +16 -8
- package/dist/host-control/main.js +2 -3
- package/dist/vault/approvals/kernel-server.js +2 -3
- package/dist/vault/broker/server.js +2 -3
- package/package.json +3 -3
- package/profiles/_base/start.sh.hbs +11 -24
- package/profiles/_shared/telegram-style.md.hbs +2 -2
- package/profiles/default/CLAUDE.md.hbs +4 -1
- package/skills/switchroom-runtime/SKILL.md +6 -16
- package/telegram-plugin/agent-dir.ts +15 -0
- package/telegram-plugin/dist/gateway/gateway.js +655 -514
- package/telegram-plugin/gateway/coalesce-attachments.ts +9 -0
- package/telegram-plugin/gateway/gateway.ts +246 -83
- package/telegram-plugin/gateway/inbound-spool.ts +15 -0
- package/telegram-plugin/gateway/interrupt-defer.ts +6 -0
- package/telegram-plugin/gateway/resume-inbound-builder.ts +180 -0
- package/telegram-plugin/registry/turns-schema.ts +138 -33
- package/telegram-plugin/stream-reply-handler.ts +1 -11
- package/telegram-plugin/tests/agent-dir.test.ts +25 -0
- package/telegram-plugin/tests/coalesce-attachments.test.ts +24 -6
- package/telegram-plugin/tests/e2e.test.ts +2 -77
- package/telegram-plugin/tests/inbound-spool.test.ts +45 -0
- package/telegram-plugin/tests/interrupt-defer.test.ts +13 -0
- package/telegram-plugin/tests/multi-turn-continuity.test.ts +0 -1
- package/telegram-plugin/tests/outbound-ordering.test.ts +0 -1
- package/telegram-plugin/tests/parse-mode-rotation.test.ts +0 -1
- package/telegram-plugin/tests/permission-verdict-resume-guard.test.ts +86 -0
- package/telegram-plugin/tests/races.test.ts +0 -26
- package/telegram-plugin/tests/registry-turns.test.ts +106 -29
- package/telegram-plugin/tests/resume-inbound-builder.test.ts +182 -0
- package/telegram-plugin/tests/status-accent.test.ts +0 -1
- package/telegram-plugin/tests/stream-reply-error-paths.test.ts +0 -1
- package/telegram-plugin/tests/stream-reply-handler.test.ts +0 -24
- package/telegram-plugin/tests/streaming-e2e.test.ts +0 -1
- package/telegram-plugin/tests/streaming-orchestration.test.ts +0 -1
- package/telegram-plugin/tests/tool-activity-summary.test.ts +44 -0
- package/telegram-plugin/tests/turns-writer.test.ts +16 -6
- package/telegram-plugin/tests/worker-activity-feed.test.ts +14 -0
- package/telegram-plugin/tool-activity-summary.ts +55 -0
- package/telegram-plugin/uat/assertions.ts +53 -0
- package/telegram-plugin/uat/driver.ts +30 -0
- package/telegram-plugin/uat/feed-matcher.test.ts +80 -0
- package/telegram-plugin/uat/fixtures/album/blue.jpg +0 -0
- package/telegram-plugin/uat/fixtures/album/green.jpg +0 -0
- package/telegram-plugin/uat/fixtures/album/red.jpg +0 -0
- package/telegram-plugin/uat/scenarios/jtbd-album-coalescing-dm.test.ts +136 -0
- package/telegram-plugin/uat/scenarios/jtbd-memory-survives-restart-dm.test.ts +17 -2
- package/telegram-plugin/worker-activity-feed.ts +11 -5
- package/telegram-plugin/handoff-continuity.ts +0 -206
- package/telegram-plugin/tests/handoff-continuity.test.ts +0 -262
|
@@ -23707,7 +23707,6 @@ var init_schema = __esm(() => {
|
|
|
23707
23707
|
}).optional();
|
|
23708
23708
|
SessionContinuitySchema = exports_external.object({
|
|
23709
23709
|
enabled: exports_external.boolean().optional().describe("Master switch for the session-handoff briefing (default true)."),
|
|
23710
|
-
show_handoff_line: exports_external.boolean().optional().describe("Whether the telegram plugin prepends a visible '\u21a9\ufe0f Picked up\u2026' " + "line to the first assistant reply after a restart (default true)."),
|
|
23711
23710
|
max_turns_in_briefing: exports_external.number().int().positive().optional().describe("Cap on recent user/assistant turn pairs fed to the summarizer."),
|
|
23712
23711
|
resume_mode: exports_external.enum(["auto", "continue", "handoff", "none"]).optional().describe("How to resume the next session. 'handoff' (default as of #362) " + "never passes --continue; a fresh Claude starts each restart and " + "reads a briefing assembled from recent Telegram messages, Hindsight " + "recall, and today's daily memory file. 'auto' uses --continue when " + "the latest JSONL is smaller than resume_max_bytes, else falls back " + "to the handoff briefing. 'continue' always passes --continue. " + "'none' starts completely fresh every time."),
|
|
23713
23712
|
resume_max_bytes: exports_external.number().int().positive().optional().describe("Byte threshold above which 'auto' mode falls back to handoff " + "instead of --continue. Default 2_000_000 (~2MB). Large transcripts " + "can blow out the context window even with prefix caching, and " + "--continue replay is known-fragile at scale.")
|
|
@@ -23739,10 +23738,10 @@ var init_schema = __esm(() => {
|
|
|
23739
23738
|
}).optional().describe("Long-reply publishing via Telegraph (#579). When enabled, replies " + "above the threshold publish as a Telegraph article rendered in " + "Telegram via native Instant View. Off by default \u2014 content " + "residency is real for some personas (lawyer, health-coach with PHI). " + "Cascades from defaults.channels.telegram.telegraph. " + "(Migrated from per-agent root in #596.)"),
|
|
23740
23739
|
coalesce: exports_external.object({
|
|
23741
23740
|
window_ms: exports_external.number().int().nonnegative().optional().describe("Sliding-window (ms) for merging consecutive inbound messages from " + "the same sender+topic into ONE Claude turn. Each new message resets " + "the timer; the turn starts once the sender pauses for this long. " + "Catches forwarded bursts, pasted text the Telegram client split " + "into several messages, and mixed text+media forwards. Default 500. " + "Set 0 to disable (every message becomes its own turn). Raise for " + "users who think in multiple short messages; the trade-off is the " + "single-message turn start is delayed by this much (the \uD83D\uDC40 ack still " + "fires immediately, so perceived latency is unchanged)."),
|
|
23742
|
-
max_attachments: exports_external.number().int().positive().optional().describe("Maximum number of media attachments carried into ONE coalesced " + "Claude turn. Default
|
|
23741
|
+
max_attachments: exports_external.number().int().positive().optional().describe("Maximum number of media attachments carried into ONE coalesced " + "Claude turn. Default 10 \u2014 a full Telegram album (media_group caps " + "at 10) or a text+multi-image forwarded burst arrives as a single " + "turn; the agent sees numbered attachment fields (image_path, " + "image_path_2, \u2026). Set 1 to restore the historical " + "single-attachment-per-turn behaviour. Excess attachments beyond " + "the cap spill into the next turn. Each attachment is downloaded, " + "so a high cap on a slow link delays turn start.")
|
|
23743
23742
|
}).optional().describe("Inbound coalescing \u2014 how the gateway groups rapid consecutive messages " + "into a single turn so a forwarded album or split paste doesn't fan out " + "into N separate turns. Cascades from defaults.channels.telegram.coalesce."),
|
|
23744
23743
|
interrupt: exports_external.object({
|
|
23745
|
-
safe_boundary: exports_external.boolean().optional().describe("When true, a `!`-prefix interrupt that arrives while the agent is
|
|
23744
|
+
safe_boundary: exports_external.boolean().optional().describe("When true (the default), a `!`-prefix interrupt that arrives while " + "the agent is mid-tool-call is DEFERRED: the SIGINT and the " + "replacement turn wait until the in-flight tool call finishes (a " + "clean boundary) instead of C-c'ing the agent mid-write/mid-bash. If " + "no tool is in flight the interrupt still fires immediately. Bounded " + "by max_wait_ms so a long tool never strands the user. Set false to " + "fire synchronously the moment `!` is received (historical " + "behaviour). Rapid repeated `!` while one is pending coalesce into a " + "single deferred interrupt carrying the latest body."),
|
|
23746
23745
|
max_wait_ms: exports_external.number().int().positive().optional().describe("Upper bound (ms) the gateway waits for a safe boundary before firing " + "a deferred `!` interrupt anyway. Only consulted when safe_boundary is " + "true. Default 8000. Keep it short \u2014 the user explicitly asked to " + "interrupt, so a long in-flight tool shouldn't ghost them; the cap " + "trades a tiny risk of a mid-tool C-c for a guaranteed response.")
|
|
23747
23746
|
}).optional().describe("Interrupt timing \u2014 how a `!`-prefix interrupt behaves when it lands " + "mid-tool-call. Off by default (fire immediately). Cascades from " + "defaults.channels.telegram.interrupt."),
|
|
23748
23747
|
webhook_sources: exports_external.array(exports_external.enum(["github", "generic"])).optional().describe("External webhook sources allowed to ingest events into this agent's " + "log. POST /webhook/<agent>/<source> on the switchroom web server. " + "Each source has its own signature verification ('github' = " + "X-Hub-Signature-256 HMAC-SHA256, 'generic' = Bearer token). " + "Per-source secret read from ~/.switchroom/webhook-secrets.json " + "keyed by [agent][source]. Verified events append to " + "<agent>/telegram/webhook-events.jsonl for the agent to read on " + "demand. Off by default \u2014 webhook is the only untrusted-inbound " + "surface in the system, so opt-in is mandatory. " + "Cascades from defaults.channels.telegram.webhook_sources. " + "(Migrated from per-agent root in #596 \u2014 see #577.)"),
|
|
@@ -25600,7 +25599,7 @@ function isDockerRuntime() {
|
|
|
25600
25599
|
import * as net3 from "node:net";
|
|
25601
25600
|
import * as fs from "node:fs";
|
|
25602
25601
|
import { homedir as homedir7 } from "node:os";
|
|
25603
|
-
import { join as
|
|
25602
|
+
import { join as join15 } from "node:path";
|
|
25604
25603
|
function defaultBrokerSocketPath() {
|
|
25605
25604
|
if (fs.existsSync(OPERATOR_SOCKET_PATH))
|
|
25606
25605
|
return OPERATOR_SOCKET_PATH;
|
|
@@ -25609,7 +25608,7 @@ function defaultBrokerSocketPath() {
|
|
|
25609
25608
|
return LEGACY_SOCKET_PATH;
|
|
25610
25609
|
}
|
|
25611
25610
|
function vaultTokenFilePath(agentSlug) {
|
|
25612
|
-
return
|
|
25611
|
+
return join15(homedir7(), ".switchroom", "agents", agentSlug, ".vault-token");
|
|
25613
25612
|
}
|
|
25614
25613
|
function readVaultTokenFile(agentSlug) {
|
|
25615
25614
|
const filePath = vaultTokenFilePath(agentSlug);
|
|
@@ -25764,8 +25763,8 @@ var DEFAULT_TIMEOUT_MS3 = 2000, LEGACY_SOCKET_PATH, OPERATOR_SOCKET_PATH;
|
|
|
25764
25763
|
var init_client2 = __esm(() => {
|
|
25765
25764
|
init_protocol2();
|
|
25766
25765
|
init_peercred();
|
|
25767
|
-
LEGACY_SOCKET_PATH =
|
|
25768
|
-
OPERATOR_SOCKET_PATH =
|
|
25766
|
+
LEGACY_SOCKET_PATH = join15(homedir7(), ".switchroom", "vault-broker.sock");
|
|
25767
|
+
OPERATOR_SOCKET_PATH = join15(homedir7(), ".switchroom", "broker-operator", "sock");
|
|
25769
25768
|
});
|
|
25770
25769
|
|
|
25771
25770
|
// ../src/drive/deep-links.ts
|
|
@@ -27878,7 +27877,7 @@ var init_secretlint_source = __esm(() => {
|
|
|
27878
27877
|
});
|
|
27879
27878
|
|
|
27880
27879
|
// gateway/auth-line.ts
|
|
27881
|
-
function
|
|
27880
|
+
function escapeHtml8(s) {
|
|
27882
27881
|
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
27883
27882
|
}
|
|
27884
27883
|
function formatRelativeMs2(ms) {
|
|
@@ -27938,7 +27937,7 @@ function renderAuthLine(state4, agentName3, now = Date.now()) {
|
|
|
27938
27937
|
if (!acc)
|
|
27939
27938
|
continue;
|
|
27940
27939
|
const marker = label === activeLabel ? "\u25b6" : "\u21b3";
|
|
27941
|
-
const labelHtml = `<code>${
|
|
27940
|
+
const labelHtml = `<code>${escapeHtml8(acc.label)}</code>`;
|
|
27942
27941
|
const quotaLine = formatAuthQuotaLine(acc, now);
|
|
27943
27942
|
rows.push(quotaLine ? `${marker} ${labelHtml} ${quotaLine}` : `${marker} ${labelHtml}`);
|
|
27944
27943
|
}
|
|
@@ -27946,19 +27945,19 @@ function renderAuthLine(state4, agentName3, now = Date.now()) {
|
|
|
27946
27945
|
}
|
|
27947
27946
|
|
|
27948
27947
|
// gateway/quota-cache.ts
|
|
27949
|
-
import { existsSync as
|
|
27950
|
-
import { join as
|
|
27948
|
+
import { existsSync as existsSync25, readFileSync as readFileSync23, writeFileSync as writeFileSync14, mkdirSync as mkdirSync14 } from "fs";
|
|
27949
|
+
import { join as join22, dirname as dirname8 } from "path";
|
|
27951
27950
|
function defaultCachePath() {
|
|
27952
|
-
return process.env.SWITCHROOM_QUOTA_CACHE_PATH ??
|
|
27951
|
+
return process.env.SWITCHROOM_QUOTA_CACHE_PATH ?? join22(process.env.HOME ?? "/tmp", ".switchroom", "quota-cache.json");
|
|
27953
27952
|
}
|
|
27954
27953
|
function readQuotaCache(opts = {}) {
|
|
27955
27954
|
const path = opts.path ?? defaultCachePath();
|
|
27956
27955
|
const now = opts.now ?? Date.now();
|
|
27957
|
-
if (!
|
|
27956
|
+
if (!existsSync25(path))
|
|
27958
27957
|
return null;
|
|
27959
27958
|
let entry;
|
|
27960
27959
|
try {
|
|
27961
|
-
entry = JSON.parse(
|
|
27960
|
+
entry = JSON.parse(readFileSync23(path, "utf8"));
|
|
27962
27961
|
} catch {
|
|
27963
27962
|
return null;
|
|
27964
27963
|
}
|
|
@@ -27985,7 +27984,7 @@ function writeQuotaCache(result, opts = {}) {
|
|
|
27985
27984
|
};
|
|
27986
27985
|
try {
|
|
27987
27986
|
mkdirSync14(dirname8(path), { recursive: true });
|
|
27988
|
-
|
|
27987
|
+
writeFileSync14(path, JSON.stringify(entry, null, 2), { mode: 384 });
|
|
27989
27988
|
} catch {}
|
|
27990
27989
|
}
|
|
27991
27990
|
var DEFAULT_TTL_MS4, RATE_LIMIT_TTL_MS;
|
|
@@ -27995,8 +27994,8 @@ var init_quota_cache = __esm(() => {
|
|
|
27995
27994
|
});
|
|
27996
27995
|
|
|
27997
27996
|
// gateway/boot-probes.ts
|
|
27998
|
-
import { readFileSync as
|
|
27999
|
-
import { join as
|
|
27997
|
+
import { readFileSync as readFileSync24, readdirSync as readdirSync4, existsSync as existsSync26 } from "fs";
|
|
27998
|
+
import { join as join23 } from "path";
|
|
28000
27999
|
import { execFile as execFileCb } from "child_process";
|
|
28001
28000
|
import { promisify as promisify3 } from "util";
|
|
28002
28001
|
async function withTimeout(label, p, timeoutMs = PROBE_TIMEOUT_MS) {
|
|
@@ -28038,11 +28037,11 @@ function mapPlan(billingType, hasExtra) {
|
|
|
28038
28037
|
}
|
|
28039
28038
|
async function probeAccount(agentDir) {
|
|
28040
28039
|
return withTimeout("Account", (async () => {
|
|
28041
|
-
const claudeDir =
|
|
28042
|
-
const claudeJsonPath =
|
|
28040
|
+
const claudeDir = join23(agentDir, ".claude");
|
|
28041
|
+
const claudeJsonPath = join23(claudeDir, ".claude.json");
|
|
28043
28042
|
let cfg = {};
|
|
28044
28043
|
try {
|
|
28045
|
-
const raw =
|
|
28044
|
+
const raw = readFileSync24(claudeJsonPath, "utf8");
|
|
28046
28045
|
cfg = JSON.parse(raw);
|
|
28047
28046
|
} catch {
|
|
28048
28047
|
return { status: "fail", label: "Account", detail: "no .claude.json" };
|
|
@@ -28060,12 +28059,12 @@ async function probeAccount(agentDir) {
|
|
|
28060
28059
|
let tokenStr = "";
|
|
28061
28060
|
let status = "ok";
|
|
28062
28061
|
for (const candidate of [
|
|
28063
|
-
|
|
28064
|
-
|
|
28062
|
+
join23(claudeDir, ".oauth-token.meta.json"),
|
|
28063
|
+
join23(claudeDir, "accounts", "default", ".oauth-token.meta.json")
|
|
28065
28064
|
]) {
|
|
28066
|
-
if (
|
|
28065
|
+
if (existsSync26(candidate)) {
|
|
28067
28066
|
try {
|
|
28068
|
-
const meta = JSON.parse(
|
|
28067
|
+
const meta = JSON.parse(readFileSync24(candidate, "utf8"));
|
|
28069
28068
|
if (meta.expiresAt) {
|
|
28070
28069
|
tokenStr = " \u00b7 " + formatDaysFromNow(meta.expiresAt);
|
|
28071
28070
|
const daysLeft = Math.round((meta.expiresAt - Date.now()) / 86400000);
|
|
@@ -28240,9 +28239,9 @@ async function resolveTmuxSupervisorPid(agentName3, execFileImpl) {
|
|
|
28240
28239
|
if (!cgroup)
|
|
28241
28240
|
return null;
|
|
28242
28241
|
const procsPath = `/sys/fs/cgroup${cgroup}/cgroup.procs`;
|
|
28243
|
-
if (!
|
|
28242
|
+
if (!existsSync26(procsPath))
|
|
28244
28243
|
return null;
|
|
28245
|
-
const pidsRaw =
|
|
28244
|
+
const pidsRaw = readFileSync24(procsPath, "utf-8");
|
|
28246
28245
|
const pids = pidsRaw.split(`
|
|
28247
28246
|
`).map((s) => s.trim()).filter(Boolean);
|
|
28248
28247
|
if (pids.length === 0)
|
|
@@ -28255,7 +28254,7 @@ async function resolveTmuxSupervisorPid(agentName3, execFileImpl) {
|
|
|
28255
28254
|
let rss = 0;
|
|
28256
28255
|
let comm = "";
|
|
28257
28256
|
try {
|
|
28258
|
-
const status =
|
|
28257
|
+
const status = readFileSync24(`/proc/${pid}/status`, "utf-8");
|
|
28259
28258
|
const rssLine = status.split(`
|
|
28260
28259
|
`).find((l) => l.startsWith("VmRSS:"));
|
|
28261
28260
|
if (rssLine) {
|
|
@@ -28267,7 +28266,7 @@ async function resolveTmuxSupervisorPid(agentName3, execFileImpl) {
|
|
|
28267
28266
|
continue;
|
|
28268
28267
|
}
|
|
28269
28268
|
try {
|
|
28270
|
-
comm =
|
|
28269
|
+
comm = readFileSync24(`/proc/${pid}/comm`, "utf-8").trim();
|
|
28271
28270
|
} catch {}
|
|
28272
28271
|
candidates.push({ pid, rss, comm });
|
|
28273
28272
|
}
|
|
@@ -28447,9 +28446,9 @@ async function probeQuota(claudeConfigDir, _agentDir, fetchImpl = fetch, opts =
|
|
|
28447
28446
|
let claudeDirForProbe = null;
|
|
28448
28447
|
for (const candidate of [
|
|
28449
28448
|
claudeConfigDir,
|
|
28450
|
-
|
|
28449
|
+
join23(claudeConfigDir, "accounts", "default")
|
|
28451
28450
|
]) {
|
|
28452
|
-
if (
|
|
28451
|
+
if (existsSync26(join23(candidate, ".oauth-token"))) {
|
|
28453
28452
|
claudeDirForProbe = candidate;
|
|
28454
28453
|
break;
|
|
28455
28454
|
}
|
|
@@ -28514,7 +28513,7 @@ async function probeHindsight(bankName, fetchImpl = fetch) {
|
|
|
28514
28513
|
}
|
|
28515
28514
|
function readContainerBootTimeMsForProbe() {
|
|
28516
28515
|
try {
|
|
28517
|
-
const stat1 =
|
|
28516
|
+
const stat1 = readFileSync24("/proc/1/stat", "utf8");
|
|
28518
28517
|
const lastParen = stat1.lastIndexOf(")");
|
|
28519
28518
|
if (lastParen < 0)
|
|
28520
28519
|
return null;
|
|
@@ -28522,7 +28521,7 @@ function readContainerBootTimeMsForProbe() {
|
|
|
28522
28521
|
const starttimeTicks = Number(after[19]);
|
|
28523
28522
|
if (!Number.isFinite(starttimeTicks))
|
|
28524
28523
|
return null;
|
|
28525
|
-
const procStat =
|
|
28524
|
+
const procStat = readFileSync24("/proc/stat", "utf8");
|
|
28526
28525
|
const btimeLine = procStat.split(`
|
|
28527
28526
|
`).find((l) => l.startsWith("btime "));
|
|
28528
28527
|
if (!btimeLine)
|
|
@@ -28620,7 +28619,7 @@ async function probeUds(label, socketPath, opts = {}) {
|
|
|
28620
28619
|
}
|
|
28621
28620
|
return withTimeout(label, (async () => {
|
|
28622
28621
|
if (!opts.connectImpl) {
|
|
28623
|
-
if (!
|
|
28622
|
+
if (!existsSync26(socketPath)) {
|
|
28624
28623
|
return {
|
|
28625
28624
|
status: "fail",
|
|
28626
28625
|
label,
|
|
@@ -28684,7 +28683,7 @@ async function probeSkills(agentDir, opts = {}) {
|
|
|
28684
28683
|
return withTimeout("Skills", (async () => {
|
|
28685
28684
|
const fs2 = opts.fs ?? realSkillsFs;
|
|
28686
28685
|
const max = opts.maxNamesShown ?? 3;
|
|
28687
|
-
const skillsDir =
|
|
28686
|
+
const skillsDir = join23(agentDir, ".claude", "skills");
|
|
28688
28687
|
if (!fs2.exists(skillsDir)) {
|
|
28689
28688
|
return { status: "ok", label: "Skills", detail: "no skills dir" };
|
|
28690
28689
|
}
|
|
@@ -28699,17 +28698,17 @@ async function probeSkills(agentDir, opts = {}) {
|
|
|
28699
28698
|
}
|
|
28700
28699
|
const dangling = [];
|
|
28701
28700
|
for (const name of entries) {
|
|
28702
|
-
const skillPath =
|
|
28701
|
+
const skillPath = join23(skillsDir, name);
|
|
28703
28702
|
if (!fs2.exists(skillPath)) {
|
|
28704
28703
|
dangling.push(name);
|
|
28705
28704
|
continue;
|
|
28706
28705
|
}
|
|
28707
|
-
const skillMd =
|
|
28706
|
+
const skillMd = join23(skillPath, "SKILL.md");
|
|
28708
28707
|
if (!fs2.exists(skillMd) && !fs2.exists(skillPath + ".md")) {
|
|
28709
28708
|
continue;
|
|
28710
28709
|
}
|
|
28711
28710
|
}
|
|
28712
|
-
const overlayDir = opts.overlaySkillsDir ??
|
|
28711
|
+
const overlayDir = opts.overlaySkillsDir ?? join23(agentDir, "skills.d");
|
|
28713
28712
|
const overlaySlugs = new Set;
|
|
28714
28713
|
if (fs2.exists(overlayDir)) {
|
|
28715
28714
|
let overlayEntries = [];
|
|
@@ -28756,24 +28755,24 @@ var init_boot_probes = __esm(() => {
|
|
|
28756
28755
|
execFile3 = promisify3(execFileCb);
|
|
28757
28756
|
realProcFs = {
|
|
28758
28757
|
readdir: (p) => readdirSync4(p),
|
|
28759
|
-
readFile: (p) =>
|
|
28758
|
+
readFile: (p) => readFileSync24(p, "utf-8")
|
|
28760
28759
|
};
|
|
28761
28760
|
realSchedulerFs = {
|
|
28762
|
-
readFile: (p) =>
|
|
28761
|
+
readFile: (p) => readFileSync24(p, "utf-8"),
|
|
28763
28762
|
mtimeMs: (p) => {
|
|
28764
28763
|
const { statSync: statSync7 } = __require("fs");
|
|
28765
28764
|
return statSync7(p).mtimeMs;
|
|
28766
28765
|
},
|
|
28767
|
-
exists: (p) =>
|
|
28766
|
+
exists: (p) => existsSync26(p)
|
|
28768
28767
|
};
|
|
28769
28768
|
realSkillsFs = {
|
|
28770
28769
|
readdir: (p) => readdirSync4(p),
|
|
28771
|
-
exists: (p) =>
|
|
28770
|
+
exists: (p) => existsSync26(p)
|
|
28772
28771
|
};
|
|
28773
28772
|
});
|
|
28774
28773
|
|
|
28775
28774
|
// gateway/boot-issue-cache.ts
|
|
28776
|
-
import { existsSync as
|
|
28775
|
+
import { existsSync as existsSync27, readFileSync as readFileSync25, writeFileSync as writeFileSync15, mkdirSync as mkdirSync15, renameSync as renameSync8 } from "fs";
|
|
28777
28776
|
import { dirname as dirname9 } from "path";
|
|
28778
28777
|
function fingerprintProbe(key, r) {
|
|
28779
28778
|
if (r.status === "ok")
|
|
@@ -28853,11 +28852,11 @@ function diffProbes(probes, cache, opts = {}) {
|
|
|
28853
28852
|
return out;
|
|
28854
28853
|
}
|
|
28855
28854
|
function loadCache(path, now = Date.now) {
|
|
28856
|
-
if (!
|
|
28855
|
+
if (!existsSync27(path))
|
|
28857
28856
|
return { ...EMPTY_CACHE, probes: {} };
|
|
28858
28857
|
let raw;
|
|
28859
28858
|
try {
|
|
28860
|
-
raw =
|
|
28859
|
+
raw = readFileSync25(path, "utf-8");
|
|
28861
28860
|
} catch {
|
|
28862
28861
|
return { ...EMPTY_CACHE, probes: {} };
|
|
28863
28862
|
}
|
|
@@ -28866,7 +28865,7 @@ function loadCache(path, now = Date.now) {
|
|
|
28866
28865
|
parsed = JSON.parse(raw);
|
|
28867
28866
|
} catch {
|
|
28868
28867
|
try {
|
|
28869
|
-
|
|
28868
|
+
renameSync8(path, `${path}.corrupt-${now()}`);
|
|
28870
28869
|
} catch {}
|
|
28871
28870
|
return { ...EMPTY_CACHE, probes: {} };
|
|
28872
28871
|
}
|
|
@@ -28902,8 +28901,8 @@ function applyAndSave(path, cache, diff) {
|
|
|
28902
28901
|
try {
|
|
28903
28902
|
mkdirSync15(dirname9(path), { recursive: true });
|
|
28904
28903
|
const tmp = `${path}.tmp`;
|
|
28905
|
-
|
|
28906
|
-
|
|
28904
|
+
writeFileSync15(tmp, JSON.stringify(next), { mode: 384 });
|
|
28905
|
+
renameSync8(tmp, path);
|
|
28907
28906
|
} catch {}
|
|
28908
28907
|
return next;
|
|
28909
28908
|
}
|
|
@@ -28916,7 +28915,7 @@ var init_boot_issue_cache = __esm(() => {
|
|
|
28916
28915
|
|
|
28917
28916
|
// gateway/config-snapshot.ts
|
|
28918
28917
|
import { createHash as createHash2 } from "crypto";
|
|
28919
|
-
import { existsSync as
|
|
28918
|
+
import { existsSync as existsSync28, readFileSync as readFileSync26, writeFileSync as writeFileSync16, mkdirSync as mkdirSync16, renameSync as renameSync9 } from "fs";
|
|
28920
28919
|
import { dirname as dirname10 } from "path";
|
|
28921
28920
|
function hashStringArray(items) {
|
|
28922
28921
|
if (!items || items.length === 0)
|
|
@@ -28981,11 +28980,11 @@ function renderConfigChangeDim(dim) {
|
|
|
28981
28980
|
}
|
|
28982
28981
|
}
|
|
28983
28982
|
function loadSnapshot(path, now = Date.now) {
|
|
28984
|
-
if (!
|
|
28983
|
+
if (!existsSync28(path))
|
|
28985
28984
|
return null;
|
|
28986
28985
|
let raw;
|
|
28987
28986
|
try {
|
|
28988
|
-
raw =
|
|
28987
|
+
raw = readFileSync26(path, "utf-8");
|
|
28989
28988
|
} catch {
|
|
28990
28989
|
return null;
|
|
28991
28990
|
}
|
|
@@ -28994,7 +28993,7 @@ function loadSnapshot(path, now = Date.now) {
|
|
|
28994
28993
|
parsed = JSON.parse(raw);
|
|
28995
28994
|
} catch {
|
|
28996
28995
|
try {
|
|
28997
|
-
|
|
28996
|
+
renameSync9(path, `${path}.corrupt-${now()}`);
|
|
28998
28997
|
} catch {}
|
|
28999
28998
|
return null;
|
|
29000
28999
|
}
|
|
@@ -29017,8 +29016,8 @@ function persistSnapshot(path, snapshot) {
|
|
|
29017
29016
|
try {
|
|
29018
29017
|
mkdirSync16(dirname10(path), { recursive: true });
|
|
29019
29018
|
const tmp = `${path}.tmp`;
|
|
29020
|
-
|
|
29021
|
-
|
|
29019
|
+
writeFileSync16(tmp, JSON.stringify(snapshot), { mode: 384 });
|
|
29020
|
+
renameSync9(tmp, path);
|
|
29022
29021
|
} catch {}
|
|
29023
29022
|
}
|
|
29024
29023
|
var init_config_snapshot = () => {};
|
|
@@ -29033,7 +29032,7 @@ __export(exports_boot_card, {
|
|
|
29033
29032
|
renderBootCard: () => renderBootCard,
|
|
29034
29033
|
renderAccountRows: () => renderAuthLine
|
|
29035
29034
|
});
|
|
29036
|
-
import { join as
|
|
29035
|
+
import { join as join24 } from "path";
|
|
29037
29036
|
function resolvePersonaName(slug, loadConfig3) {
|
|
29038
29037
|
try {
|
|
29039
29038
|
const config = loadConfig3 ? loadConfig3() : loadConfig();
|
|
@@ -29123,7 +29122,7 @@ function renderBootCard(opts) {
|
|
|
29123
29122
|
`);
|
|
29124
29123
|
}
|
|
29125
29124
|
async function runAllProbes(opts) {
|
|
29126
|
-
const claudeDir =
|
|
29125
|
+
const claudeDir = join24(opts.agentDir, ".claude");
|
|
29127
29126
|
const probes = {};
|
|
29128
29127
|
const slug = opts.agentSlug ?? opts.agentName;
|
|
29129
29128
|
await Promise.allSettled([
|
|
@@ -29472,16 +29471,16 @@ function renderAccountRow2(snap, opts) {
|
|
|
29472
29471
|
const lines = [];
|
|
29473
29472
|
const marker = snap.isActive ? "\u25cf " : "";
|
|
29474
29473
|
if (!snap.quota) {
|
|
29475
|
-
lines.push(`${marker}<code>${
|
|
29474
|
+
lines.push(`${marker}<code>${escapeHtml11(snap.label)}</code> <i>quota probe failed</i>`);
|
|
29476
29475
|
if (snap.quotaError) {
|
|
29477
|
-
lines.push(` <i>${
|
|
29476
|
+
lines.push(` <i>${escapeHtml11(snap.quotaError)}</i>`);
|
|
29478
29477
|
}
|
|
29479
29478
|
return lines;
|
|
29480
29479
|
}
|
|
29481
29480
|
const q = snap.quota;
|
|
29482
29481
|
const fiveStr = fmtPct2(q.fiveHourUtilizationPct);
|
|
29483
29482
|
const sevenStr = fmtPct2(q.sevenDayUtilizationPct);
|
|
29484
|
-
lines.push(`${marker}<code>${
|
|
29483
|
+
lines.push(`${marker}<code>${escapeHtml11(snap.label)}</code> ${fiveStr} / ${sevenStr}`);
|
|
29485
29484
|
const health = classifyHealth2(snap);
|
|
29486
29485
|
if (health === "blocked") {
|
|
29487
29486
|
const win = bindingWindow3(q);
|
|
@@ -29587,13 +29586,13 @@ function renderFallbackAnnouncement2(input) {
|
|
|
29587
29586
|
const limitWord = input.oldQuota ? limitWordFor2(input.oldQuota) : "quota";
|
|
29588
29587
|
const headerLimit = limitWord === "quota" ? "quota cap" : `${limitWord} limit`;
|
|
29589
29588
|
if (!input.newLabel) {
|
|
29590
|
-
lines.push(`\uD83D\uDD34 <b>All accounts blocked \u00b7 ${headerLimit} on ${
|
|
29589
|
+
lines.push(`\uD83D\uDD34 <b>All accounts blocked \u00b7 ${headerLimit} on ${escapeHtml11(input.oldLabel)}</b>`);
|
|
29591
29590
|
lines.push("");
|
|
29592
|
-
lines.push(`Triggered by: agent <b>${
|
|
29591
|
+
lines.push(`Triggered by: agent <b>${escapeHtml11(input.triggerAgent)}</b>`);
|
|
29593
29592
|
if (input.oldQuota) {
|
|
29594
29593
|
const recovery = recoveryAtFor2(input.oldQuota);
|
|
29595
29594
|
if (recovery) {
|
|
29596
|
-
lines.push(`${
|
|
29595
|
+
lines.push(`${escapeHtml11(input.oldLabel)} recovers ${formatAbsolute2(recovery, tz)} ` + `(in ${formatRelative2(recovery, now)})`);
|
|
29597
29596
|
}
|
|
29598
29597
|
}
|
|
29599
29598
|
lines.push("");
|
|
@@ -29601,15 +29600,15 @@ function renderFallbackAnnouncement2(input) {
|
|
|
29601
29600
|
return lines.join(`
|
|
29602
29601
|
`);
|
|
29603
29602
|
}
|
|
29604
|
-
lines.push(`\u2713 <b>Switched fleet \u00b7 ${headerLimit} on ${
|
|
29603
|
+
lines.push(`\u2713 <b>Switched fleet \u00b7 ${headerLimit} on ${escapeHtml11(input.oldLabel)}</b>`);
|
|
29605
29604
|
lines.push("");
|
|
29606
|
-
lines.push(`<code>${
|
|
29607
|
-
lines.push(`Triggered by: agent <b>${
|
|
29605
|
+
lines.push(`<code>${escapeHtml11(input.oldLabel)}</code> \u2192 <code>${escapeHtml11(input.newLabel)}</code>`);
|
|
29606
|
+
lines.push(`Triggered by: agent <b>${escapeHtml11(input.triggerAgent)}</b>`);
|
|
29608
29607
|
lines.push("");
|
|
29609
29608
|
if (input.oldQuota) {
|
|
29610
29609
|
const recovery = recoveryAtFor2(input.oldQuota);
|
|
29611
29610
|
if (recovery) {
|
|
29612
|
-
lines.push(`<code>${
|
|
29611
|
+
lines.push(`<code>${escapeHtml11(input.oldLabel)}</code> recovers ` + `${formatAbsolute2(recovery, tz)} (in ${formatRelative2(recovery, now)})`);
|
|
29613
29612
|
}
|
|
29614
29613
|
}
|
|
29615
29614
|
if (input.newQuota) {
|
|
@@ -29617,7 +29616,7 @@ function renderFallbackAnnouncement2(input) {
|
|
|
29617
29616
|
const sevenStr = fmtPct2(input.newQuota.sevenDayUtilizationPct);
|
|
29618
29617
|
const hasHeadroom = input.newQuota.fiveHourUtilizationPct < THROTTLING_THRESHOLD_PCT2 && input.newQuota.sevenDayUtilizationPct < THROTTLING_THRESHOLD_PCT2;
|
|
29619
29618
|
const headroomStr = hasHeadroom ? "<i>(plenty of headroom)</i>" : "<i>(near limit \u2014 watch this)</i>";
|
|
29620
|
-
lines.push(`<code>${
|
|
29619
|
+
lines.push(`<code>${escapeHtml11(input.newLabel)}</code> now: ${fiveStr} of 5h \u00b7 ${sevenStr} of 7d ${headroomStr}`);
|
|
29621
29620
|
} else {
|
|
29622
29621
|
lines.push(`<i>(quota probe for new account is pending \u2014 will reflect on next /auth)</i>`);
|
|
29623
29622
|
}
|
|
@@ -29676,7 +29675,7 @@ function switchPriority2(s) {
|
|
|
29676
29675
|
return 2;
|
|
29677
29676
|
return 3;
|
|
29678
29677
|
}
|
|
29679
|
-
function
|
|
29678
|
+
function escapeHtml11(s) {
|
|
29680
29679
|
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
29681
29680
|
}
|
|
29682
29681
|
function buildSnapshotsFromState2(state4, quotas) {
|
|
@@ -29741,12 +29740,12 @@ var init_flock = () => {};
|
|
|
29741
29740
|
// ../src/vault/vault.ts
|
|
29742
29741
|
import { randomBytes as randomBytes5, scryptSync, createCipheriv, createDecipheriv } from "node:crypto";
|
|
29743
29742
|
import {
|
|
29744
|
-
readFileSync as
|
|
29745
|
-
writeFileSync as
|
|
29746
|
-
existsSync as
|
|
29747
|
-
renameSync as
|
|
29743
|
+
readFileSync as readFileSync34,
|
|
29744
|
+
writeFileSync as writeFileSync22,
|
|
29745
|
+
existsSync as existsSync36,
|
|
29746
|
+
renameSync as renameSync11,
|
|
29748
29747
|
mkdirSync as mkdirSync23,
|
|
29749
|
-
unlinkSync as
|
|
29748
|
+
unlinkSync as unlinkSync12,
|
|
29750
29749
|
lstatSync,
|
|
29751
29750
|
realpathSync
|
|
29752
29751
|
} from "node:fs";
|
|
@@ -29781,12 +29780,12 @@ function normalizeSecrets(raw) {
|
|
|
29781
29780
|
return out;
|
|
29782
29781
|
}
|
|
29783
29782
|
function openVault(passphrase, vaultPath) {
|
|
29784
|
-
if (!
|
|
29783
|
+
if (!existsSync36(vaultPath)) {
|
|
29785
29784
|
throw new VaultError(`Vault file not found: ${vaultPath}`);
|
|
29786
29785
|
}
|
|
29787
29786
|
let vaultFile;
|
|
29788
29787
|
try {
|
|
29789
|
-
vaultFile = JSON.parse(
|
|
29788
|
+
vaultFile = JSON.parse(readFileSync34(vaultPath, "utf8"));
|
|
29790
29789
|
} catch {
|
|
29791
29790
|
throw new VaultError(`Failed to read vault file: ${vaultPath}`);
|
|
29792
29791
|
}
|
|
@@ -29840,7 +29839,7 @@ import {
|
|
|
29840
29839
|
statSync as statSync11,
|
|
29841
29840
|
writeSync as writeSync2
|
|
29842
29841
|
} from "node:fs";
|
|
29843
|
-
import { join as
|
|
29842
|
+
import { join as join34 } from "node:path";
|
|
29844
29843
|
import { tmpdir } from "node:os";
|
|
29845
29844
|
import { constants as fsConstants } from "node:fs";
|
|
29846
29845
|
function isVaultReference(value) {
|
|
@@ -29892,11 +29891,11 @@ function materializationRoot() {
|
|
|
29892
29891
|
return cachedRoot;
|
|
29893
29892
|
const xdg = process.env.XDG_RUNTIME_DIR;
|
|
29894
29893
|
if (xdg) {
|
|
29895
|
-
const base =
|
|
29894
|
+
const base = join34(xdg, "switchroom", "vault");
|
|
29896
29895
|
mkdirSync24(base, { recursive: true, mode: 448 });
|
|
29897
|
-
cachedRoot = mkdtempSync2(
|
|
29896
|
+
cachedRoot = mkdtempSync2(join34(base, "run-"));
|
|
29898
29897
|
} else {
|
|
29899
|
-
cachedRoot = mkdtempSync2(
|
|
29898
|
+
cachedRoot = mkdtempSync2(join34(tmpdir(), "switchroom-vault-"));
|
|
29900
29899
|
}
|
|
29901
29900
|
chmodSync5(cachedRoot, 448);
|
|
29902
29901
|
return cachedRoot;
|
|
@@ -29911,7 +29910,7 @@ function writeFileExclusive(filePath, content) {
|
|
|
29911
29910
|
}
|
|
29912
29911
|
}
|
|
29913
29912
|
function materializeFilesEntry(key, files) {
|
|
29914
|
-
const dir =
|
|
29913
|
+
const dir = join34(materializationRoot(), key);
|
|
29915
29914
|
if (materializedDirs.has(dir)) {
|
|
29916
29915
|
try {
|
|
29917
29916
|
rmSync3(dir, { recursive: true, force: true });
|
|
@@ -29927,7 +29926,7 @@ function materializeFilesEntry(key, files) {
|
|
|
29927
29926
|
if (filename.includes("/") || filename.includes("\\") || filename === ".." || filename === "." || filename.includes("\x00")) {
|
|
29928
29927
|
throw new Error(`Refusing to materialize vault file with unsafe name: ${filename}`);
|
|
29929
29928
|
}
|
|
29930
|
-
const filePath =
|
|
29929
|
+
const filePath = join34(dir, filename);
|
|
29931
29930
|
const content = encoding === "base64" ? Buffer.from(value, "base64") : value;
|
|
29932
29931
|
writeFileExclusive(filePath, content);
|
|
29933
29932
|
}
|
|
@@ -30060,7 +30059,7 @@ __export(exports_materialize_bot_token, {
|
|
|
30060
30059
|
materializeBotToken: () => materializeBotToken,
|
|
30061
30060
|
BotTokenMaterializeError: () => BotTokenMaterializeError
|
|
30062
30061
|
});
|
|
30063
|
-
import { existsSync as
|
|
30062
|
+
import { existsSync as existsSync37 } from "node:fs";
|
|
30064
30063
|
function pickConfiguredToken(config, agentName3) {
|
|
30065
30064
|
if (agentName3) {
|
|
30066
30065
|
const agent = config.agents?.[agentName3];
|
|
@@ -30074,7 +30073,7 @@ function tryDirectVaultRead(ref, config, passphrase) {
|
|
|
30074
30073
|
if (!passphrase)
|
|
30075
30074
|
return null;
|
|
30076
30075
|
const vaultPath = resolvePath(config.vault?.path ?? "~/.switchroom/vault.enc");
|
|
30077
|
-
if (!
|
|
30076
|
+
if (!existsSync37(vaultPath))
|
|
30078
30077
|
return null;
|
|
30079
30078
|
try {
|
|
30080
30079
|
const secrets = openVault(passphrase, vaultPath);
|
|
@@ -30166,7 +30165,7 @@ __export(exports_tmux, {
|
|
|
30166
30165
|
captureAgentPane: () => captureAgentPane
|
|
30167
30166
|
});
|
|
30168
30167
|
import { execFileSync as execFileSync4 } from "node:child_process";
|
|
30169
|
-
import { mkdirSync as mkdirSync25, readdirSync as readdirSync6, statSync as statSync12, unlinkSync as
|
|
30168
|
+
import { mkdirSync as mkdirSync25, readdirSync as readdirSync6, statSync as statSync12, unlinkSync as unlinkSync13, writeFileSync as writeFileSync23 } from "node:fs";
|
|
30170
30169
|
import { resolve as resolve7 } from "node:path";
|
|
30171
30170
|
function captureAgentPane(opts) {
|
|
30172
30171
|
const { agentName: agentName3, agentDir, reason } = opts;
|
|
@@ -30212,7 +30211,7 @@ function captureAgentPane(opts) {
|
|
|
30212
30211
|
` + `
|
|
30213
30212
|
`;
|
|
30214
30213
|
try {
|
|
30215
|
-
|
|
30214
|
+
writeFileSync23(outPath, Buffer.concat([Buffer.from(header, "utf8"), body]), {
|
|
30216
30215
|
mode: 420
|
|
30217
30216
|
});
|
|
30218
30217
|
} catch (err) {
|
|
@@ -30280,7 +30279,7 @@ function pruneOldReports(dir, retain) {
|
|
|
30280
30279
|
}).sort((a, b) => b.mtimeMs - a.mtimeMs);
|
|
30281
30280
|
for (const stale of files.slice(retain)) {
|
|
30282
30281
|
try {
|
|
30283
|
-
|
|
30282
|
+
unlinkSync13(stale.full);
|
|
30284
30283
|
} catch {}
|
|
30285
30284
|
}
|
|
30286
30285
|
}
|
|
@@ -30322,7 +30321,7 @@ function truncateDiffForCard(unifiedDiff, maxLines = 50, maxChars = 3000) {
|
|
|
30322
30321
|
}
|
|
30323
30322
|
return out === unifiedDiff ? out : out + sentinel;
|
|
30324
30323
|
}
|
|
30325
|
-
function
|
|
30324
|
+
function escapeHtml12(s) {
|
|
30326
30325
|
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
30327
30326
|
}
|
|
30328
30327
|
function clipReason(reason) {
|
|
@@ -30333,10 +30332,10 @@ function clipReason(reason) {
|
|
|
30333
30332
|
function buildConfigApprovalCardBody(args) {
|
|
30334
30333
|
const safeReason = clipReason(args.reason);
|
|
30335
30334
|
const render = (diff) => `\uD83D\uDEE0 <b>Config edit proposed</b>
|
|
30336
|
-
` + `Agent: <code>${
|
|
30337
|
-
` + `Reason: ${
|
|
30335
|
+
` + `Agent: <code>${escapeHtml12(args.agentName)}</code>
|
|
30336
|
+
` + `Reason: ${escapeHtml12(safeReason)}
|
|
30338
30337
|
|
|
30339
|
-
` + `<pre>${
|
|
30338
|
+
` + `<pre>${escapeHtml12(diff)}</pre>`;
|
|
30340
30339
|
return truncateRawToFit({
|
|
30341
30340
|
raw: args.unifiedDiff,
|
|
30342
30341
|
render,
|
|
@@ -30448,8 +30447,8 @@ async function handleRequestConfigFinalize(_client, msg, deps) {
|
|
|
30448
30447
|
}
|
|
30449
30448
|
pending.delete(msg.requestId);
|
|
30450
30449
|
const body = msg.outcome === "applied" ? `\u2705 <b>Applied</b>${msg.detail ? `
|
|
30451
|
-
${
|
|
30452
|
-
${
|
|
30450
|
+
${escapeHtml12(msg.detail)}` : ""}` : `\u26a0\ufe0f <b>Reconcile failed; rolled back</b>${msg.detail ? `
|
|
30451
|
+
${escapeHtml12(msg.detail)}` : ""}`;
|
|
30453
30452
|
try {
|
|
30454
30453
|
await deps.editCard({
|
|
30455
30454
|
chatId: entry.chatId,
|
|
@@ -30589,17 +30588,17 @@ function registerApprovalsCommands(bot, opts) {
|
|
|
30589
30588
|
return;
|
|
30590
30589
|
}
|
|
30591
30590
|
if (decisions.length === 0) {
|
|
30592
|
-
await ctx.reply(agentFilter ? `No active approvals for <code>${
|
|
30591
|
+
await ctx.reply(agentFilter ? `No active approvals for <code>${escapeHtml13(agentFilter)}</code>.` : "No active approvals.", { parse_mode: "HTML" });
|
|
30593
30592
|
return;
|
|
30594
30593
|
}
|
|
30595
30594
|
const byAgent = new Map;
|
|
30596
30595
|
for (const d of decisions)
|
|
30597
30596
|
byAgent.set(d.agent_unit, (byAgent.get(d.agent_unit) ?? 0) + 1);
|
|
30598
|
-
const summary = Array.from(byAgent.entries()).map(([a, n]) => `\u2022 <b>${
|
|
30597
|
+
const summary = Array.from(byAgent.entries()).map(([a, n]) => `\u2022 <b>${escapeHtml13(a)}</b>: ${n}`).join(`
|
|
30599
30598
|
`);
|
|
30600
30599
|
const detail = decisions.slice(0, 20).map((d) => {
|
|
30601
30600
|
const ttl = d.ttl_expires_at === null ? "always" : `until ${new Date(d.ttl_expires_at).toISOString().slice(0, 16).replace("T", " ")}`;
|
|
30602
|
-
return `<code>${
|
|
30601
|
+
return `<code>${escapeHtml13(d.id.slice(0, 8))}</code> ` + `${escapeHtml13(d.agent_unit)} \u2192 ` + `<code>${escapeHtml13(d.scope)}</code> ` + `(${escapeHtml13(d.action)}, ${ttl}) ` + `\u00b7 /approvals revoke ${escapeHtml13(d.id)}`;
|
|
30603
30602
|
}).join(`
|
|
30604
30603
|
`);
|
|
30605
30604
|
await ctx.reply(`<b>Active approvals</b>
|
|
@@ -30625,13 +30624,13 @@ ${detail}`, {
|
|
|
30625
30624
|
await ctx.reply("Approval kernel unreachable.");
|
|
30626
30625
|
return;
|
|
30627
30626
|
}
|
|
30628
|
-
await ctx.reply(ok ? `Revoked <code>${
|
|
30627
|
+
await ctx.reply(ok ? `Revoked <code>${escapeHtml13(id)}</code>.` : `No such active decision <code>${escapeHtml13(id)}</code>.`, { parse_mode: "HTML" });
|
|
30629
30628
|
return;
|
|
30630
30629
|
}
|
|
30631
|
-
await ctx.reply(`Unknown subcommand <code>${
|
|
30630
|
+
await ctx.reply(`Unknown subcommand <code>${escapeHtml13(sub)}</code>. ` + `Use <code>/approvals list</code> or <code>/approvals revoke <id></code>. ` + `(<code>add</code> and <code>stats</code> are coming in a follow-up.)`, { parse_mode: "HTML" });
|
|
30632
30631
|
});
|
|
30633
30632
|
}
|
|
30634
|
-
function
|
|
30633
|
+
function escapeHtml13(s) {
|
|
30635
30634
|
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
30636
30635
|
}
|
|
30637
30636
|
var init_approvals_commands = __esm(() => {
|
|
@@ -30772,23 +30771,23 @@ var import_runner2 = __toESM(require_mod3(), 1);
|
|
|
30772
30771
|
import { randomBytes as randomBytes6 } from "crypto";
|
|
30773
30772
|
import { execFileSync as execFileSync5, execSync as execSync2, spawn as spawn2 } from "child_process";
|
|
30774
30773
|
import {
|
|
30775
|
-
readFileSync as
|
|
30776
|
-
writeFileSync as
|
|
30774
|
+
readFileSync as readFileSync35,
|
|
30775
|
+
writeFileSync as writeFileSync24,
|
|
30777
30776
|
mkdirSync as mkdirSync26,
|
|
30778
30777
|
readdirSync as readdirSync7,
|
|
30779
30778
|
rmSync as rmSync4,
|
|
30780
30779
|
statSync as statSync13,
|
|
30781
|
-
renameSync as
|
|
30780
|
+
renameSync as renameSync12,
|
|
30782
30781
|
realpathSync as realpathSync2,
|
|
30783
30782
|
chmodSync as chmodSync6,
|
|
30784
30783
|
openSync as openSync8,
|
|
30785
30784
|
closeSync as closeSync8,
|
|
30786
|
-
existsSync as
|
|
30787
|
-
unlinkSync as
|
|
30785
|
+
existsSync as existsSync38,
|
|
30786
|
+
unlinkSync as unlinkSync14,
|
|
30788
30787
|
appendFileSync as appendFileSync5
|
|
30789
30788
|
} from "fs";
|
|
30790
30789
|
import { homedir as homedir14 } from "os";
|
|
30791
|
-
import { join as
|
|
30790
|
+
import { join as join35, extname, sep as sep3, basename as basename7 } from "path";
|
|
30792
30791
|
|
|
30793
30792
|
// plugin-logger.ts
|
|
30794
30793
|
import { appendFileSync, mkdirSync, renameSync, statSync, existsSync } from "fs";
|
|
@@ -31074,6 +31073,9 @@ function resolveInterruptMaxWaitMs(configured) {
|
|
|
31074
31073
|
return configured;
|
|
31075
31074
|
return DEFAULT_INTERRUPT_MAX_WAIT_MS;
|
|
31076
31075
|
}
|
|
31076
|
+
function resolveSafeBoundaryEnabled(configured) {
|
|
31077
|
+
return configured !== false;
|
|
31078
|
+
}
|
|
31077
31079
|
|
|
31078
31080
|
// sticker-aliases.ts
|
|
31079
31081
|
function looksLikeFileId(s) {
|
|
@@ -31613,6 +31615,10 @@ function inboundCoalesceKey(chatId, threadId, userId) {
|
|
|
31613
31615
|
}
|
|
31614
31616
|
|
|
31615
31617
|
// gateway/coalesce-attachments.ts
|
|
31618
|
+
var DEFAULT_MAX_ATTACHMENTS = 10;
|
|
31619
|
+
function resolveCoalesceMaxAttachments(configured) {
|
|
31620
|
+
return Math.max(1, configured ?? DEFAULT_MAX_ATTACHMENTS);
|
|
31621
|
+
}
|
|
31616
31622
|
function splitCoalescedAttachments(entries, hasAttachment, maxAttachments) {
|
|
31617
31623
|
const withAttachment = entries.filter(hasAttachment);
|
|
31618
31624
|
const capped = withAttachment.slice(0, Math.max(1, maxAttachments));
|
|
@@ -31956,6 +31962,9 @@ class DeferredDoneReactions {
|
|
|
31956
31962
|
}
|
|
31957
31963
|
|
|
31958
31964
|
// worker-activity-feed.ts
|
|
31965
|
+
function isWorkerActivityFeedEnabled(envVal) {
|
|
31966
|
+
return envVal !== "0";
|
|
31967
|
+
}
|
|
31959
31968
|
var DESC_MAX = 80;
|
|
31960
31969
|
var TOOL_ARG_MAX = 64;
|
|
31961
31970
|
var SUMMARY_MAX = 100;
|
|
@@ -32172,6 +32181,33 @@ function renderActivityFeed(lines) {
|
|
|
32172
32181
|
return out.join(`
|
|
32173
32182
|
`);
|
|
32174
32183
|
}
|
|
32184
|
+
var NESTED_MAX_LINES = 4;
|
|
32185
|
+
var NESTED_LINE_MAX = 90;
|
|
32186
|
+
var NESTED_PREFIX = " \u21b3 ";
|
|
32187
|
+
function renderActivityFeedWithNested(lines, childLines) {
|
|
32188
|
+
const children = childLines.map((s) => s.trim()).filter((s) => s.length > 0);
|
|
32189
|
+
if (children.length === 0)
|
|
32190
|
+
return renderActivityFeed(lines);
|
|
32191
|
+
const out = [];
|
|
32192
|
+
const shownParent = lines.slice(-MIRROR_MAX_LINES);
|
|
32193
|
+
const hiddenParent = lines.length - shownParent.length;
|
|
32194
|
+
if (hiddenParent > 0)
|
|
32195
|
+
out.push(`<i>\u2713 +${hiddenParent} earlier\u2026</i>`);
|
|
32196
|
+
for (const l of shownParent)
|
|
32197
|
+
out.push(`<i>\u2713 ${escapeFeedHtml(l)}</i>`);
|
|
32198
|
+
const shownChild = children.slice(-NESTED_MAX_LINES);
|
|
32199
|
+
const hiddenChild = children.length - shownChild.length;
|
|
32200
|
+
if (hiddenChild > 0)
|
|
32201
|
+
out.push(`${NESTED_PREFIX}<i>+${hiddenChild} earlier\u2026</i>`);
|
|
32202
|
+
const lastChildIdx = shownChild.length - 1;
|
|
32203
|
+
shownChild.forEach((l, i) => {
|
|
32204
|
+
const t = l.length > NESTED_LINE_MAX ? l.slice(0, NESTED_LINE_MAX - 1) + "\u2026" : l;
|
|
32205
|
+
const esc = escapeFeedHtml(t);
|
|
32206
|
+
out.push(i === lastChildIdx ? `${NESTED_PREFIX}<b>\u2192 ${esc}</b>` : `${NESTED_PREFIX}<i>${esc}</i>`);
|
|
32207
|
+
});
|
|
32208
|
+
return out.length > 0 ? out.join(`
|
|
32209
|
+
`) : null;
|
|
32210
|
+
}
|
|
32175
32211
|
function appendActivityLabel(lines, label) {
|
|
32176
32212
|
const l = (label ?? "").trim();
|
|
32177
32213
|
if (l.length === 0)
|
|
@@ -33188,11 +33224,6 @@ async function handleStreamReply(args, state, deps) {
|
|
|
33188
33224
|
done,
|
|
33189
33225
|
streamExisted
|
|
33190
33226
|
});
|
|
33191
|
-
if (!stream) {
|
|
33192
|
-
const prefix = deps.takeHandoffPrefix(format === "html" ? "html" : format === "markdownv2" ? "markdownv2" : "text");
|
|
33193
|
-
if (prefix.length > 0)
|
|
33194
|
-
effectiveText = prefix + effectiveText;
|
|
33195
|
-
}
|
|
33196
33227
|
if (!stream) {
|
|
33197
33228
|
let replyToMessageId;
|
|
33198
33229
|
if (args.reply_to != null) {
|
|
@@ -42119,125 +42150,29 @@ function isTurnFlushSafetyEnabled(env = process.env) {
|
|
|
42119
42150
|
return true;
|
|
42120
42151
|
}
|
|
42121
42152
|
|
|
42122
|
-
//
|
|
42123
|
-
import {
|
|
42124
|
-
import { dirname as dirname7, join as join13 } from "node:path";
|
|
42125
|
-
var TOPIC_DISPLAY_MAX = 117;
|
|
42126
|
-
var HANDOFF_TOPIC_FILENAME = ".handoff-topic";
|
|
42127
|
-
var LAST_TURN_SUMMARY_FILENAME = ".last-turn-summary";
|
|
42153
|
+
// agent-dir.ts
|
|
42154
|
+
import { dirname as dirname7 } from "node:path";
|
|
42128
42155
|
function resolveAgentDirFromEnv() {
|
|
42129
42156
|
const state3 = process.env.TELEGRAM_STATE_DIR;
|
|
42130
42157
|
if (!state3 || state3.trim().length === 0)
|
|
42131
42158
|
return null;
|
|
42132
42159
|
return dirname7(state3);
|
|
42133
42160
|
}
|
|
42134
|
-
function readHandoffTopic(agentDir) {
|
|
42135
|
-
const p = join13(agentDir, HANDOFF_TOPIC_FILENAME);
|
|
42136
|
-
if (!existsSync13(p))
|
|
42137
|
-
return null;
|
|
42138
|
-
let raw;
|
|
42139
|
-
try {
|
|
42140
|
-
raw = readFileSync9(p, "utf-8");
|
|
42141
|
-
} catch {
|
|
42142
|
-
return null;
|
|
42143
|
-
}
|
|
42144
|
-
const lines = raw.split(/\r?\n/).map((l) => l.trim()).filter((l) => l.length > 0);
|
|
42145
|
-
if (lines.length === 0)
|
|
42146
|
-
return null;
|
|
42147
|
-
let topic = lines[0];
|
|
42148
|
-
if (topic.length > TOPIC_DISPLAY_MAX) {
|
|
42149
|
-
topic = topic.slice(0, TOPIC_DISPLAY_MAX) + "\u2026";
|
|
42150
|
-
}
|
|
42151
|
-
return topic;
|
|
42152
|
-
}
|
|
42153
|
-
function readLastTurnSummary(agentDir) {
|
|
42154
|
-
const p = join13(agentDir, LAST_TURN_SUMMARY_FILENAME);
|
|
42155
|
-
if (!existsSync13(p))
|
|
42156
|
-
return null;
|
|
42157
|
-
let raw;
|
|
42158
|
-
try {
|
|
42159
|
-
raw = readFileSync9(p, "utf-8");
|
|
42160
|
-
} catch {
|
|
42161
|
-
return null;
|
|
42162
|
-
}
|
|
42163
|
-
const lines = raw.split(/\r?\n/).map((l) => l.trim()).filter((l) => l.length > 0);
|
|
42164
|
-
if (lines.length === 0)
|
|
42165
|
-
return null;
|
|
42166
|
-
let topic = lines[0];
|
|
42167
|
-
if (topic.length > TOPIC_DISPLAY_MAX) {
|
|
42168
|
-
topic = topic.slice(0, TOPIC_DISPLAY_MAX) + "\u2026";
|
|
42169
|
-
}
|
|
42170
|
-
return topic;
|
|
42171
|
-
}
|
|
42172
|
-
function consumeHandoffTopic(agentDir) {
|
|
42173
|
-
const primary = readHandoffTopic(agentDir);
|
|
42174
|
-
const primaryPath = join13(agentDir, HANDOFF_TOPIC_FILENAME);
|
|
42175
|
-
const fallbackPath = join13(agentDir, LAST_TURN_SUMMARY_FILENAME);
|
|
42176
|
-
const removeFallback = () => {
|
|
42177
|
-
try {
|
|
42178
|
-
unlinkSync2(fallbackPath);
|
|
42179
|
-
} catch {}
|
|
42180
|
-
};
|
|
42181
|
-
if (primary !== null) {
|
|
42182
|
-
try {
|
|
42183
|
-
unlinkSync2(primaryPath);
|
|
42184
|
-
} catch {}
|
|
42185
|
-
removeFallback();
|
|
42186
|
-
return primary;
|
|
42187
|
-
}
|
|
42188
|
-
const fallback = readLastTurnSummary(agentDir);
|
|
42189
|
-
if (fallback !== null) {
|
|
42190
|
-
removeFallback();
|
|
42191
|
-
return fallback;
|
|
42192
|
-
}
|
|
42193
|
-
return null;
|
|
42194
|
-
}
|
|
42195
|
-
function shouldShowHandoffLine() {
|
|
42196
|
-
const v = process.env.SWITCHROOM_HANDOFF_SHOW_LINE;
|
|
42197
|
-
if (v === undefined)
|
|
42198
|
-
return true;
|
|
42199
|
-
return v.toLowerCase() !== "false";
|
|
42200
|
-
}
|
|
42201
|
-
function formatHandoffLine(topic, format) {
|
|
42202
|
-
const prefix = "\u21a9\ufe0f Picked up where we left off, ";
|
|
42203
|
-
if (format === "html") {
|
|
42204
|
-
return `<i>${prefix}${escapeHtml7(topic)}</i>
|
|
42205
|
-
|
|
42206
|
-
`;
|
|
42207
|
-
}
|
|
42208
|
-
if (format === "markdownv2") {
|
|
42209
|
-
const escaped = escapeMarkdownV2(topic);
|
|
42210
|
-
const prefixEsc = escapeMarkdownV2(prefix);
|
|
42211
|
-
return `_${prefixEsc}${escaped}_
|
|
42212
|
-
|
|
42213
|
-
`;
|
|
42214
|
-
}
|
|
42215
|
-
return `${prefix}${topic}
|
|
42216
|
-
|
|
42217
|
-
`;
|
|
42218
|
-
}
|
|
42219
|
-
function escapeHtml7(s) {
|
|
42220
|
-
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
42221
|
-
}
|
|
42222
|
-
var MDV2_SPECIALS = /[_*\[\]()~`>#+\-=|{}.!\\]/g;
|
|
42223
|
-
function escapeMarkdownV2(s) {
|
|
42224
|
-
return s.replace(MDV2_SPECIALS, (m) => "\\" + m);
|
|
42225
|
-
}
|
|
42226
42161
|
|
|
42227
42162
|
// active-reactions.ts
|
|
42228
|
-
import { readFileSync as
|
|
42229
|
-
import { join as
|
|
42163
|
+
import { readFileSync as readFileSync9, writeFileSync as writeFileSync6, renameSync as renameSync2, existsSync as existsSync13, unlinkSync as unlinkSync2 } from "node:fs";
|
|
42164
|
+
import { join as join13 } from "node:path";
|
|
42230
42165
|
var ACTIVE_REACTIONS_FILENAME = ".active-reactions.json";
|
|
42231
42166
|
function reactionsPath(agentDir) {
|
|
42232
|
-
return
|
|
42167
|
+
return join13(agentDir, ACTIVE_REACTIONS_FILENAME);
|
|
42233
42168
|
}
|
|
42234
42169
|
function readActiveReactions(agentDir) {
|
|
42235
42170
|
const p = reactionsPath(agentDir);
|
|
42236
|
-
if (!
|
|
42171
|
+
if (!existsSync13(p))
|
|
42237
42172
|
return [];
|
|
42238
42173
|
let raw;
|
|
42239
42174
|
try {
|
|
42240
|
-
raw =
|
|
42175
|
+
raw = readFileSync9(p, "utf-8");
|
|
42241
42176
|
} catch {
|
|
42242
42177
|
return [];
|
|
42243
42178
|
}
|
|
@@ -42263,15 +42198,15 @@ function writeActiveReactions(agentDir, reactions) {
|
|
|
42263
42198
|
const p = reactionsPath(agentDir);
|
|
42264
42199
|
if (reactions.length === 0) {
|
|
42265
42200
|
try {
|
|
42266
|
-
|
|
42201
|
+
unlinkSync2(p);
|
|
42267
42202
|
} catch {}
|
|
42268
42203
|
return;
|
|
42269
42204
|
}
|
|
42270
42205
|
const tmp = `${p}.tmp-${process.pid}-${Date.now()}`;
|
|
42271
42206
|
try {
|
|
42272
|
-
|
|
42207
|
+
writeFileSync6(tmp, JSON.stringify(reactions) + `
|
|
42273
42208
|
`, "utf-8");
|
|
42274
|
-
|
|
42209
|
+
renameSync2(tmp, p);
|
|
42275
42210
|
} catch {}
|
|
42276
42211
|
}
|
|
42277
42212
|
function addActiveReaction(agentDir, reaction) {
|
|
@@ -42288,24 +42223,24 @@ function removeActiveReaction(agentDir, chatId, messageId) {
|
|
|
42288
42223
|
}
|
|
42289
42224
|
function clearActiveReactions(agentDir) {
|
|
42290
42225
|
try {
|
|
42291
|
-
|
|
42226
|
+
unlinkSync2(reactionsPath(agentDir));
|
|
42292
42227
|
} catch {}
|
|
42293
42228
|
}
|
|
42294
42229
|
|
|
42295
42230
|
// active-reactions.ts
|
|
42296
|
-
import { readFileSync as
|
|
42297
|
-
import { join as
|
|
42231
|
+
import { readFileSync as readFileSync10, writeFileSync as writeFileSync7, renameSync as renameSync3, existsSync as existsSync14, unlinkSync as unlinkSync3 } from "node:fs";
|
|
42232
|
+
import { join as join14 } from "node:path";
|
|
42298
42233
|
var ACTIVE_REACTIONS_FILENAME2 = ".active-reactions.json";
|
|
42299
42234
|
function reactionsPath2(agentDir) {
|
|
42300
|
-
return
|
|
42235
|
+
return join14(agentDir, ACTIVE_REACTIONS_FILENAME2);
|
|
42301
42236
|
}
|
|
42302
42237
|
function readActiveReactions2(agentDir) {
|
|
42303
42238
|
const p = reactionsPath2(agentDir);
|
|
42304
|
-
if (!
|
|
42239
|
+
if (!existsSync14(p))
|
|
42305
42240
|
return [];
|
|
42306
42241
|
let raw;
|
|
42307
42242
|
try {
|
|
42308
|
-
raw =
|
|
42243
|
+
raw = readFileSync10(p, "utf-8");
|
|
42309
42244
|
} catch {
|
|
42310
42245
|
return [];
|
|
42311
42246
|
}
|
|
@@ -42329,7 +42264,7 @@ function readActiveReactions2(agentDir) {
|
|
|
42329
42264
|
}
|
|
42330
42265
|
function clearActiveReactions2(agentDir) {
|
|
42331
42266
|
try {
|
|
42332
|
-
|
|
42267
|
+
unlinkSync3(reactionsPath2(agentDir));
|
|
42333
42268
|
} catch {}
|
|
42334
42269
|
}
|
|
42335
42270
|
|
|
@@ -43182,17 +43117,17 @@ async function approvalRecord(args, opts) {
|
|
|
43182
43117
|
}
|
|
43183
43118
|
|
|
43184
43119
|
// quota-check.ts
|
|
43185
|
-
import { readFileSync as
|
|
43186
|
-
import { join as
|
|
43120
|
+
import { readFileSync as readFileSync12, existsSync as existsSync16 } from "fs";
|
|
43121
|
+
import { join as join16 } from "path";
|
|
43187
43122
|
var OAUTH_BETA2 = "oauth-2025-04-20";
|
|
43188
43123
|
var DEFAULT_USER_AGENT2 = "claude-cli/1.0.0 (external, cli)";
|
|
43189
43124
|
var DEFAULT_PROBE_MODEL2 = "claude-haiku-4-5-20251001";
|
|
43190
43125
|
function readOauthToken2(claudeConfigDir) {
|
|
43191
|
-
const tokenFile =
|
|
43192
|
-
if (!
|
|
43126
|
+
const tokenFile = join16(claudeConfigDir, ".oauth-token");
|
|
43127
|
+
if (!existsSync16(tokenFile))
|
|
43193
43128
|
return null;
|
|
43194
43129
|
try {
|
|
43195
|
-
const raw =
|
|
43130
|
+
const raw = readFileSync12(tokenFile, "utf-8").trim();
|
|
43196
43131
|
return raw.length > 0 ? raw : null;
|
|
43197
43132
|
} catch {
|
|
43198
43133
|
return null;
|
|
@@ -43788,7 +43723,7 @@ init_schema();
|
|
|
43788
43723
|
init_paths();
|
|
43789
43724
|
init_overlay_loader();
|
|
43790
43725
|
init_merge();
|
|
43791
|
-
import { readFileSync as
|
|
43726
|
+
import { readFileSync as readFileSync13, existsSync as existsSync17 } from "node:fs";
|
|
43792
43727
|
import { homedir as homedir8 } from "node:os";
|
|
43793
43728
|
import { resolve as resolve5 } from "node:path";
|
|
43794
43729
|
|
|
@@ -43864,7 +43799,7 @@ function findConfigFile2(startDir) {
|
|
|
43864
43799
|
resolve5(userDir, "clerk.yml")
|
|
43865
43800
|
].filter(Boolean);
|
|
43866
43801
|
for (const path of searchPaths) {
|
|
43867
|
-
if (
|
|
43802
|
+
if (existsSync17(path)) {
|
|
43868
43803
|
return path;
|
|
43869
43804
|
}
|
|
43870
43805
|
}
|
|
@@ -43872,12 +43807,12 @@ function findConfigFile2(startDir) {
|
|
|
43872
43807
|
}
|
|
43873
43808
|
function loadConfig2(configPath) {
|
|
43874
43809
|
const filePath = configPath ?? findConfigFile2();
|
|
43875
|
-
if (!
|
|
43810
|
+
if (!existsSync17(filePath)) {
|
|
43876
43811
|
throw new ConfigError2(`Config file not found: ${filePath}`);
|
|
43877
43812
|
}
|
|
43878
43813
|
let raw;
|
|
43879
43814
|
try {
|
|
43880
|
-
raw =
|
|
43815
|
+
raw = readFileSync13(filePath, "utf-8");
|
|
43881
43816
|
} catch (err) {
|
|
43882
43817
|
throw new ConfigError2(`Failed to read config file: ${filePath}`, [
|
|
43883
43818
|
` ${err.message}`
|
|
@@ -44317,15 +44252,15 @@ function resolveOutboundTopic(config, event) {
|
|
|
44317
44252
|
}
|
|
44318
44253
|
|
|
44319
44254
|
// ../src/agents/perf.ts
|
|
44320
|
-
import { existsSync as
|
|
44255
|
+
import { existsSync as existsSync18, readFileSync as readFileSync14 } from "node:fs";
|
|
44321
44256
|
function readTurnUsages(jsonlPath, lastN) {
|
|
44322
|
-
if (!
|
|
44257
|
+
if (!existsSync18(jsonlPath))
|
|
44323
44258
|
return [];
|
|
44324
44259
|
if (lastN <= 0)
|
|
44325
44260
|
return [];
|
|
44326
44261
|
let raw;
|
|
44327
44262
|
try {
|
|
44328
|
-
raw =
|
|
44263
|
+
raw = readFileSync14(jsonlPath, "utf-8");
|
|
44329
44264
|
} catch {
|
|
44330
44265
|
return [];
|
|
44331
44266
|
}
|
|
@@ -44463,7 +44398,7 @@ function nextCompactNotify(state3, ev) {
|
|
|
44463
44398
|
}
|
|
44464
44399
|
|
|
44465
44400
|
// gateway/hostd-dispatch.ts
|
|
44466
|
-
import { existsSync as
|
|
44401
|
+
import { existsSync as existsSync19 } from "node:fs";
|
|
44467
44402
|
import { randomBytes as randomBytes3 } from "node:crypto";
|
|
44468
44403
|
|
|
44469
44404
|
// ../src/host-control/client.ts
|
|
@@ -44754,13 +44689,13 @@ function hostdSocketPath(agentName3) {
|
|
|
44754
44689
|
function hostdWillBeUsed(agentName3) {
|
|
44755
44690
|
if (!isHostdEnabled())
|
|
44756
44691
|
return false;
|
|
44757
|
-
return
|
|
44692
|
+
return existsSync19(hostdSocketPath(agentName3));
|
|
44758
44693
|
}
|
|
44759
44694
|
async function tryHostdDispatch(agentName3, req, timeoutMs = 5000) {
|
|
44760
44695
|
if (!isHostdEnabled())
|
|
44761
44696
|
return "not-configured";
|
|
44762
44697
|
const sockPath = hostdSocketPath(agentName3);
|
|
44763
|
-
if (!
|
|
44698
|
+
if (!existsSync19(sockPath))
|
|
44764
44699
|
return "not-configured";
|
|
44765
44700
|
try {
|
|
44766
44701
|
return await hostdRequest({ socketPath: sockPath, timeoutMs }, req);
|
|
@@ -44784,7 +44719,7 @@ async function hostdGetStatusOnce(agentName3, targetRequestId) {
|
|
|
44784
44719
|
if (!isHostdEnabled())
|
|
44785
44720
|
return "not-configured";
|
|
44786
44721
|
const sockPath = hostdSocketPath(agentName3);
|
|
44787
|
-
if (!
|
|
44722
|
+
if (!existsSync19(sockPath))
|
|
44788
44723
|
return "not-configured";
|
|
44789
44724
|
try {
|
|
44790
44725
|
const resp = await hostdRequest({ socketPath: sockPath, timeoutMs: 3000 }, {
|
|
@@ -44805,7 +44740,7 @@ async function pollHostdStatus(agentName3, targetRequestId, opts) {
|
|
|
44805
44740
|
if (!isHostdEnabled())
|
|
44806
44741
|
return "not-configured";
|
|
44807
44742
|
const sockPath = hostdSocketPath(agentName3);
|
|
44808
|
-
if (!
|
|
44743
|
+
if (!existsSync19(sockPath))
|
|
44809
44744
|
return "not-configured";
|
|
44810
44745
|
const now = opts.now ?? Date.now;
|
|
44811
44746
|
const sleep2 = opts.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
|
|
@@ -44884,7 +44819,7 @@ function shouldSweepChatAtBoot(chatId) {
|
|
|
44884
44819
|
|
|
44885
44820
|
// gateway/webhook-ingest-server.ts
|
|
44886
44821
|
import net4 from "node:net";
|
|
44887
|
-
import { chmodSync as chmodSync3, existsSync as
|
|
44822
|
+
import { chmodSync as chmodSync3, existsSync as existsSync20, unlinkSync as unlinkSync4 } from "node:fs";
|
|
44888
44823
|
var MAX_REQUEST_BYTES = 1024 * 1024;
|
|
44889
44824
|
function fdOf(conn) {
|
|
44890
44825
|
const handle = conn._handle;
|
|
@@ -44896,8 +44831,8 @@ function startWebhookIngestServer(opts) {
|
|
|
44896
44831
|
const log = opts.log ?? ((s) => process.stderr.write(s));
|
|
44897
44832
|
const allowed = new Set(opts.allowedUids);
|
|
44898
44833
|
try {
|
|
44899
|
-
if (
|
|
44900
|
-
|
|
44834
|
+
if (existsSync20(opts.socketPath))
|
|
44835
|
+
unlinkSync4(opts.socketPath);
|
|
44901
44836
|
} catch (err) {
|
|
44902
44837
|
log(`webhook-ingest-server: could not unlink stale socket: ${err.message}
|
|
44903
44838
|
`);
|
|
@@ -44989,8 +44924,8 @@ function startWebhookIngestServer(opts) {
|
|
|
44989
44924
|
server.close();
|
|
44990
44925
|
} catch {}
|
|
44991
44926
|
try {
|
|
44992
|
-
if (
|
|
44993
|
-
|
|
44927
|
+
if (existsSync20(opts.socketPath))
|
|
44928
|
+
unlinkSync4(opts.socketPath);
|
|
44994
44929
|
} catch {}
|
|
44995
44930
|
}
|
|
44996
44931
|
};
|
|
@@ -44998,19 +44933,19 @@ function startWebhookIngestServer(opts) {
|
|
|
44998
44933
|
|
|
44999
44934
|
// ../src/web/webhook-gateway-record.ts
|
|
45000
44935
|
import { appendFileSync as appendFileSync4, mkdirSync as mkdirSync12 } from "fs";
|
|
45001
|
-
import { join as
|
|
44936
|
+
import { join as join19 } from "path";
|
|
45002
44937
|
import { homedir as homedir10 } from "os";
|
|
45003
44938
|
|
|
45004
44939
|
// ../src/web/webhook-handler.ts
|
|
45005
|
-
import { appendFileSync as appendFileSync3, existsSync as
|
|
45006
|
-
import { join as
|
|
44940
|
+
import { appendFileSync as appendFileSync3, existsSync as existsSync21, mkdirSync as mkdirSync10, readFileSync as readFileSync15, writeFileSync as writeFileSync8 } from "fs";
|
|
44941
|
+
import { join as join17 } from "path";
|
|
45007
44942
|
var DEDUP_MAX = 1000;
|
|
45008
44943
|
var DEDUP_TTL_MS = 24 * 60 * 60 * 1000;
|
|
45009
44944
|
function loadDedupFile(path) {
|
|
45010
44945
|
try {
|
|
45011
|
-
if (!
|
|
44946
|
+
if (!existsSync21(path))
|
|
45012
44947
|
return {};
|
|
45013
|
-
const raw = JSON.parse(
|
|
44948
|
+
const raw = JSON.parse(readFileSync15(path, "utf-8"));
|
|
45014
44949
|
return typeof raw.deliveries === "object" && raw.deliveries !== null ? raw.deliveries : {};
|
|
45015
44950
|
} catch {
|
|
45016
44951
|
return {};
|
|
@@ -45024,7 +44959,7 @@ function saveDedupFile(path, deliveries, now) {
|
|
|
45024
44959
|
}
|
|
45025
44960
|
const sorted = Object.entries(pruned).sort((a, b) => b[1] - a[1]).slice(0, DEDUP_MAX);
|
|
45026
44961
|
const final = Object.fromEntries(sorted);
|
|
45027
|
-
|
|
44962
|
+
writeFileSync8(path, JSON.stringify({ deliveries: final }), {
|
|
45028
44963
|
mode: 384
|
|
45029
44964
|
});
|
|
45030
44965
|
}
|
|
@@ -45032,8 +44967,8 @@ var agentDedupCache = new Map;
|
|
|
45032
44967
|
function createFileDedupStore(resolveAgentDir) {
|
|
45033
44968
|
return {
|
|
45034
44969
|
check(agent, deliveryId, now) {
|
|
45035
|
-
const telegramDir =
|
|
45036
|
-
const filePath =
|
|
44970
|
+
const telegramDir = join17(resolveAgentDir(agent), "telegram");
|
|
44971
|
+
const filePath = join17(telegramDir, "webhook-dedup.json");
|
|
45037
44972
|
if (!agentDedupCache.has(agent)) {
|
|
45038
44973
|
agentDedupCache.set(agent, loadDedupFile(filePath));
|
|
45039
44974
|
}
|
|
@@ -45054,8 +44989,8 @@ var tokenBuckets = new Map;
|
|
|
45054
44989
|
var throttleIssueWindow = new Map;
|
|
45055
44990
|
|
|
45056
44991
|
// ../src/web/webhook-dispatch.ts
|
|
45057
|
-
import { existsSync as
|
|
45058
|
-
import { join as
|
|
44992
|
+
import { existsSync as existsSync22, mkdirSync as mkdirSync11, readFileSync as readFileSync16, writeFileSync as writeFileSync9 } from "fs";
|
|
44993
|
+
import { join as join18 } from "path";
|
|
45059
44994
|
import { homedir as homedir9 } from "os";
|
|
45060
44995
|
|
|
45061
44996
|
// ../src/agent-scheduler/ipc-client.ts
|
|
@@ -45266,9 +45201,9 @@ function cooldownKey(eventType, repo, number, ruleIndex) {
|
|
|
45266
45201
|
}
|
|
45267
45202
|
function loadCooldownFile(path) {
|
|
45268
45203
|
try {
|
|
45269
|
-
if (!
|
|
45204
|
+
if (!existsSync22(path))
|
|
45270
45205
|
return {};
|
|
45271
|
-
const raw = JSON.parse(
|
|
45206
|
+
const raw = JSON.parse(readFileSync16(path, "utf-8"));
|
|
45272
45207
|
return typeof raw.dispatches === "object" && raw.dispatches !== null ? raw.dispatches : {};
|
|
45273
45208
|
} catch {
|
|
45274
45209
|
return {};
|
|
@@ -45276,7 +45211,7 @@ function loadCooldownFile(path) {
|
|
|
45276
45211
|
}
|
|
45277
45212
|
function saveCooldownFile(path, dispatches) {
|
|
45278
45213
|
try {
|
|
45279
|
-
|
|
45214
|
+
writeFileSync9(path, JSON.stringify({ dispatches }), {
|
|
45280
45215
|
mode: 384
|
|
45281
45216
|
});
|
|
45282
45217
|
} catch {}
|
|
@@ -45287,8 +45222,8 @@ function createFileCooldownStore(resolveAgentDir) {
|
|
|
45287
45222
|
isCoolingDown(agent, key, cooldownMs, now) {
|
|
45288
45223
|
if (cooldownMs <= 0)
|
|
45289
45224
|
return false;
|
|
45290
|
-
const telegramDir =
|
|
45291
|
-
const filePath =
|
|
45225
|
+
const telegramDir = join18(resolveAgentDir(agent), "telegram");
|
|
45226
|
+
const filePath = join18(telegramDir, "webhook-cooldown.json");
|
|
45292
45227
|
if (!cache.has(agent)) {
|
|
45293
45228
|
cache.set(agent, loadCooldownFile(filePath));
|
|
45294
45229
|
}
|
|
@@ -45345,9 +45280,9 @@ async function defaultInject(socketPath, agentName3, inbound) {
|
|
|
45345
45280
|
}
|
|
45346
45281
|
function injectWebhookInbound(agent, prompt, ctx, deps = {}) {
|
|
45347
45282
|
const log = deps.log ?? ((s) => process.stderr.write(s));
|
|
45348
|
-
const resolveAgentDir = deps.resolveAgentDir ?? ((a) =>
|
|
45283
|
+
const resolveAgentDir = deps.resolveAgentDir ?? ((a) => join18(homedir9(), ".switchroom", "agents", a));
|
|
45349
45284
|
const now = (deps.now ?? Date.now)();
|
|
45350
|
-
const socketPath =
|
|
45285
|
+
const socketPath = join18(resolveAgentDir(agent), "telegram", "gateway.sock");
|
|
45351
45286
|
const inbound = {
|
|
45352
45287
|
type: "inbound",
|
|
45353
45288
|
chatId: ctx.chatId,
|
|
@@ -45372,7 +45307,7 @@ function evaluateDispatch(args, deps = {}) {
|
|
|
45372
45307
|
const log = deps.log ?? ((s) => process.stderr.write(s));
|
|
45373
45308
|
const now = (deps.now ?? Date.now)();
|
|
45374
45309
|
const nowDate = deps.nowDate ?? (() => new Date(now));
|
|
45375
|
-
const resolveAgentDir = deps.resolveAgentDir ?? ((a) =>
|
|
45310
|
+
const resolveAgentDir = deps.resolveAgentDir ?? ((a) => join18(homedir9(), ".switchroom", "agents", a));
|
|
45376
45311
|
const cooldownStore = deps.cooldownStore ?? createFileCooldownStore(resolveAgentDir);
|
|
45377
45312
|
if (args.source !== "github")
|
|
45378
45313
|
return 0;
|
|
@@ -45448,10 +45383,10 @@ function resolveChannelTarget(config, agentName3) {
|
|
|
45448
45383
|
function recordWebhookEvent(rec, deps = {}) {
|
|
45449
45384
|
const log = deps.log ?? ((s) => process.stderr.write(s));
|
|
45450
45385
|
const now = rec.ts || (deps.now ?? Date.now)();
|
|
45451
|
-
const resolveAgentDir = deps.resolveAgentDir ?? ((a) =>
|
|
45386
|
+
const resolveAgentDir = deps.resolveAgentDir ?? ((a) => join19(homedir10(), ".switchroom", "agents", a));
|
|
45452
45387
|
const dedupStore = deps.dedupStore ?? createFileDedupStore(resolveAgentDir);
|
|
45453
45388
|
const agent = rec.agent;
|
|
45454
|
-
const telegramDir =
|
|
45389
|
+
const telegramDir = join19(resolveAgentDir(agent), "telegram");
|
|
45455
45390
|
if (rec.source === "github" && rec.delivery_id) {
|
|
45456
45391
|
const originalTs = dedupStore.check(agent, rec.delivery_id, now);
|
|
45457
45392
|
if (originalTs !== undefined) {
|
|
@@ -45460,7 +45395,7 @@ function recordWebhookEvent(rec, deps = {}) {
|
|
|
45460
45395
|
return { status: "deduped", ts: originalTs };
|
|
45461
45396
|
}
|
|
45462
45397
|
}
|
|
45463
|
-
const logPath =
|
|
45398
|
+
const logPath = join19(telegramDir, "webhook-events.jsonl");
|
|
45464
45399
|
try {
|
|
45465
45400
|
mkdirSync12(telegramDir, { recursive: true });
|
|
45466
45401
|
const record = {
|
|
@@ -45518,7 +45453,7 @@ function recordWebhookEvent(rec, deps = {}) {
|
|
|
45518
45453
|
}
|
|
45519
45454
|
|
|
45520
45455
|
// gateway/ipc-server.ts
|
|
45521
|
-
import { renameSync as
|
|
45456
|
+
import { renameSync as renameSync4, unlinkSync as unlinkSync5 } from "fs";
|
|
45522
45457
|
var MAX_BUFFER_SIZE = 1024 * 1024;
|
|
45523
45458
|
var VALID_OPERATOR_KINDS = new Set([
|
|
45524
45459
|
"credentials-expired",
|
|
@@ -45633,10 +45568,10 @@ function createIpcServer(options) {
|
|
|
45633
45568
|
heartbeatTimeoutMs = 30000
|
|
45634
45569
|
} = options;
|
|
45635
45570
|
try {
|
|
45636
|
-
|
|
45571
|
+
renameSync4(socketPath, socketPath + ".bak");
|
|
45637
45572
|
} catch {}
|
|
45638
45573
|
try {
|
|
45639
|
-
|
|
45574
|
+
unlinkSync5(socketPath + ".bak");
|
|
45640
45575
|
} catch {}
|
|
45641
45576
|
const clients = new Set;
|
|
45642
45577
|
const agentIndex = new Map;
|
|
@@ -45958,7 +45893,7 @@ function createIpcServer(options) {
|
|
|
45958
45893
|
clientBySocketId.clear();
|
|
45959
45894
|
server.stop(true);
|
|
45960
45895
|
try {
|
|
45961
|
-
|
|
45896
|
+
renameSync4(socketPath, socketPath + ".bak");
|
|
45962
45897
|
} catch {}
|
|
45963
45898
|
}
|
|
45964
45899
|
};
|
|
@@ -46384,9 +46319,9 @@ function buildDiffPreviewCard(input) {
|
|
|
46384
46319
|
}
|
|
46385
46320
|
const preview = input.preview;
|
|
46386
46321
|
const bodyLines = [];
|
|
46387
|
-
bodyLines.push(`<b>${
|
|
46322
|
+
bodyLines.push(`<b>${escapeHtml7(preview.title)}</b>`);
|
|
46388
46323
|
for (const line of preview.lines) {
|
|
46389
|
-
bodyLines.push(
|
|
46324
|
+
bodyLines.push(escapeHtml7(line.text));
|
|
46390
46325
|
}
|
|
46391
46326
|
const text = bodyLines.join(`
|
|
46392
46327
|
`);
|
|
@@ -46439,7 +46374,7 @@ function buildDiffPreviewCard(input) {
|
|
|
46439
46374
|
}
|
|
46440
46375
|
return { text, reply_markup: kb };
|
|
46441
46376
|
}
|
|
46442
|
-
function
|
|
46377
|
+
function escapeHtml7(s) {
|
|
46443
46378
|
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
46444
46379
|
}
|
|
46445
46380
|
|
|
@@ -46585,6 +46520,9 @@ function spoolId(msg) {
|
|
|
46585
46520
|
if (msg.meta?.source === "subagent_progress" && typeof msg.meta?.subagent_jsonl_id === "string" && msg.meta.subagent_jsonl_id.length > 0 && typeof msg.meta?.bucket_idx === "string" && msg.meta.bucket_idx.length > 0) {
|
|
46586
46521
|
return `s:progress:${msg.meta.subagent_jsonl_id}:${msg.meta.bucket_idx}`;
|
|
46587
46522
|
}
|
|
46523
|
+
if ((msg.meta?.source === "resume_interrupted" || msg.meta?.source === "resume_watchdog_timeout") && typeof msg.meta?.resume_turn_key === "string" && msg.meta.resume_turn_key.length > 0) {
|
|
46524
|
+
return `s:resume:${msg.meta.resume_turn_key}`;
|
|
46525
|
+
}
|
|
46588
46526
|
if (typeof msg.messageId === "number" && msg.messageId > 0) {
|
|
46589
46527
|
return `m:${msg.chatId}:${msg.messageId}`;
|
|
46590
46528
|
}
|
|
@@ -47818,15 +47756,15 @@ function escapeBody(s) {
|
|
|
47818
47756
|
}
|
|
47819
47757
|
|
|
47820
47758
|
// gateway/pid-file.ts
|
|
47821
|
-
import { writeFileSync as
|
|
47759
|
+
import { writeFileSync as writeFileSync10, readFileSync as readFileSync17, unlinkSync as unlinkSync6, renameSync as renameSync5 } from "node:fs";
|
|
47822
47760
|
function writePidFile(path, record) {
|
|
47823
47761
|
const tmp = `${path}.tmp-${process.pid}-${Date.now()}`;
|
|
47824
|
-
|
|
47825
|
-
|
|
47762
|
+
writeFileSync10(tmp, JSON.stringify(record), "utf-8");
|
|
47763
|
+
renameSync5(tmp, path);
|
|
47826
47764
|
}
|
|
47827
47765
|
function clearPidFile(path) {
|
|
47828
47766
|
try {
|
|
47829
|
-
|
|
47767
|
+
unlinkSync6(path);
|
|
47830
47768
|
} catch {}
|
|
47831
47769
|
}
|
|
47832
47770
|
|
|
@@ -47837,10 +47775,10 @@ import {
|
|
|
47837
47775
|
writeFile as writeFileAsync,
|
|
47838
47776
|
readFile as readFileAsync
|
|
47839
47777
|
} from "node:fs/promises";
|
|
47840
|
-
import { readFileSync as
|
|
47778
|
+
import { readFileSync as readFileSync18 } from "node:fs";
|
|
47841
47779
|
function readCurrentBootId() {
|
|
47842
47780
|
try {
|
|
47843
|
-
const stat =
|
|
47781
|
+
const stat = readFileSync18("/proc/1/stat", "utf-8");
|
|
47844
47782
|
const lastParen = stat.lastIndexOf(")");
|
|
47845
47783
|
if (lastParen < 0)
|
|
47846
47784
|
return null;
|
|
@@ -48043,15 +47981,15 @@ function safeCount(fn) {
|
|
|
48043
47981
|
}
|
|
48044
47982
|
|
|
48045
47983
|
// gateway/session-marker.ts
|
|
48046
|
-
import { writeFileSync as
|
|
47984
|
+
import { writeFileSync as writeFileSync11, readFileSync as readFileSync19, renameSync as renameSync6, unlinkSync as unlinkSync7 } from "node:fs";
|
|
48047
47985
|
function writeSessionMarker(path, marker) {
|
|
48048
47986
|
const tmp = `${path}.tmp-${process.pid}-${Date.now()}`;
|
|
48049
|
-
|
|
48050
|
-
|
|
47987
|
+
writeFileSync11(tmp, JSON.stringify(marker), "utf-8");
|
|
47988
|
+
renameSync6(tmp, path);
|
|
48051
47989
|
}
|
|
48052
47990
|
function readSessionMarker(path) {
|
|
48053
47991
|
try {
|
|
48054
|
-
const raw =
|
|
47992
|
+
const raw = readFileSync19(path, "utf-8");
|
|
48055
47993
|
const parsed = JSON.parse(raw);
|
|
48056
47994
|
if (typeof parsed.pid === "number" && typeof parsed.startedAtMs === "number" && Number.isFinite(parsed.pid) && Number.isFinite(parsed.startedAtMs)) {
|
|
48057
47995
|
return { pid: parsed.pid, startedAtMs: parsed.startedAtMs };
|
|
@@ -48073,16 +48011,16 @@ function shouldFireRestartBanner(input) {
|
|
|
48073
48011
|
}
|
|
48074
48012
|
|
|
48075
48013
|
// gateway/clean-shutdown-marker.ts
|
|
48076
|
-
import { writeFileSync as
|
|
48014
|
+
import { writeFileSync as writeFileSync12, readFileSync as readFileSync20, renameSync as renameSync7, unlinkSync as unlinkSync8 } from "node:fs";
|
|
48077
48015
|
var DEFAULT_MAX_AGE_MS = 60000;
|
|
48078
48016
|
function writeCleanShutdownMarker(path, marker) {
|
|
48079
48017
|
const tmp = `${path}.tmp-${process.pid}-${Date.now()}`;
|
|
48080
|
-
|
|
48081
|
-
|
|
48018
|
+
writeFileSync12(tmp, JSON.stringify(marker), "utf-8");
|
|
48019
|
+
renameSync7(tmp, path);
|
|
48082
48020
|
}
|
|
48083
48021
|
function readCleanShutdownMarker(path) {
|
|
48084
48022
|
try {
|
|
48085
|
-
const raw =
|
|
48023
|
+
const raw = readFileSync20(path, "utf-8");
|
|
48086
48024
|
const parsed = JSON.parse(raw);
|
|
48087
48025
|
if (typeof parsed.ts === "number" && Number.isFinite(parsed.ts) && typeof parsed.signal === "string" && parsed.signal.length > 0) {
|
|
48088
48026
|
const out = { ts: parsed.ts, signal: parsed.signal };
|
|
@@ -48097,7 +48035,7 @@ function readCleanShutdownMarker(path) {
|
|
|
48097
48035
|
}
|
|
48098
48036
|
function clearCleanShutdownMarker(path) {
|
|
48099
48037
|
try {
|
|
48100
|
-
|
|
48038
|
+
unlinkSync8(path);
|
|
48101
48039
|
} catch {}
|
|
48102
48040
|
}
|
|
48103
48041
|
function shouldSuppressRecoveryBanner(marker, now, maxAgeMs = DEFAULT_MAX_AGE_MS) {
|
|
@@ -48877,16 +48815,16 @@ function classifyAdminGate(text, myAgentName) {
|
|
|
48877
48815
|
|
|
48878
48816
|
// subagent-watcher.ts
|
|
48879
48817
|
import {
|
|
48880
|
-
existsSync as
|
|
48818
|
+
existsSync as existsSync24,
|
|
48881
48819
|
openSync as openSync2,
|
|
48882
48820
|
readSync,
|
|
48883
48821
|
statSync as statSync6,
|
|
48884
48822
|
closeSync as closeSync2,
|
|
48885
48823
|
watch,
|
|
48886
48824
|
readdirSync as readdirSync3,
|
|
48887
|
-
readFileSync as
|
|
48825
|
+
readFileSync as readFileSync22
|
|
48888
48826
|
} from "fs";
|
|
48889
|
-
import { join as
|
|
48827
|
+
import { join as join21 } from "path";
|
|
48890
48828
|
|
|
48891
48829
|
// operator-events.ts
|
|
48892
48830
|
var DEFAULT_OPERATOR_EVENT_COOLDOWN_MS2 = 5 * 60000;
|
|
@@ -49129,20 +49067,20 @@ function bumpSubagentActivity(db2, args) {
|
|
|
49129
49067
|
// gateway/turn-active-marker.ts
|
|
49130
49068
|
import {
|
|
49131
49069
|
closeSync,
|
|
49132
|
-
existsSync as
|
|
49070
|
+
existsSync as existsSync23,
|
|
49133
49071
|
mkdirSync as mkdirSync13,
|
|
49134
49072
|
openSync,
|
|
49135
|
-
readFileSync as
|
|
49073
|
+
readFileSync as readFileSync21,
|
|
49136
49074
|
statSync as statSync5,
|
|
49137
|
-
unlinkSync as
|
|
49075
|
+
unlinkSync as unlinkSync9,
|
|
49138
49076
|
utimesSync,
|
|
49139
|
-
writeFileSync as
|
|
49077
|
+
writeFileSync as writeFileSync13
|
|
49140
49078
|
} from "node:fs";
|
|
49141
|
-
import { join as
|
|
49079
|
+
import { join as join20 } from "node:path";
|
|
49142
49080
|
var TURN_ACTIVE_MARKER_FILE = "turn-active.json";
|
|
49143
49081
|
function touchTurnActiveMarker(stateDir) {
|
|
49144
|
-
const path =
|
|
49145
|
-
if (!
|
|
49082
|
+
const path = join20(stateDir, TURN_ACTIVE_MARKER_FILE);
|
|
49083
|
+
if (!existsSync23(path))
|
|
49146
49084
|
return;
|
|
49147
49085
|
const now = new Date;
|
|
49148
49086
|
try {
|
|
@@ -49177,7 +49115,7 @@ function backfillJsonlAgentId(db2, jsonlPath, agentId, log) {
|
|
|
49177
49115
|
const metaPath = jsonlPath.replace(/\.jsonl$/, ".meta.json");
|
|
49178
49116
|
let meta;
|
|
49179
49117
|
try {
|
|
49180
|
-
const raw =
|
|
49118
|
+
const raw = readFileSync22(metaPath, "utf8");
|
|
49181
49119
|
meta = JSON.parse(raw);
|
|
49182
49120
|
} catch {
|
|
49183
49121
|
log?.(`subagent-watcher: backfill skip ${agentId} \u2014 meta.json not readable at ${metaPath}`);
|
|
@@ -49366,7 +49304,7 @@ function startSubagentWatcher(config) {
|
|
|
49366
49304
|
clearTimeout(ref.ref);
|
|
49367
49305
|
});
|
|
49368
49306
|
const fs2 = config.fs ?? {
|
|
49369
|
-
existsSync:
|
|
49307
|
+
existsSync: existsSync24,
|
|
49370
49308
|
readdirSync: readdirSync3,
|
|
49371
49309
|
statSync: statSync6,
|
|
49372
49310
|
openSync: openSync2,
|
|
@@ -49600,8 +49538,8 @@ function startSubagentWatcher(config) {
|
|
|
49600
49538
|
function rescanSubagentDirs() {
|
|
49601
49539
|
if (stopped)
|
|
49602
49540
|
return;
|
|
49603
|
-
const claudeHome =
|
|
49604
|
-
const projectsRoot =
|
|
49541
|
+
const claudeHome = join21(agentDir, ".claude");
|
|
49542
|
+
const projectsRoot = join21(claudeHome, "projects");
|
|
49605
49543
|
if (!fs2.existsSync(projectsRoot))
|
|
49606
49544
|
return;
|
|
49607
49545
|
let projectDirs;
|
|
@@ -49618,7 +49556,7 @@ function startSubagentWatcher(config) {
|
|
|
49618
49556
|
}
|
|
49619
49557
|
continue;
|
|
49620
49558
|
}
|
|
49621
|
-
const projectPath =
|
|
49559
|
+
const projectPath = join21(projectsRoot, pDir);
|
|
49622
49560
|
let sessionDirs;
|
|
49623
49561
|
try {
|
|
49624
49562
|
sessionDirs = fs2.readdirSync(projectPath);
|
|
@@ -49628,7 +49566,7 @@ function startSubagentWatcher(config) {
|
|
|
49628
49566
|
for (const sDir of sessionDirs) {
|
|
49629
49567
|
if (sDir.endsWith(".jsonl"))
|
|
49630
49568
|
continue;
|
|
49631
|
-
const subagentsPath =
|
|
49569
|
+
const subagentsPath = join21(projectPath, sDir, "subagents");
|
|
49632
49570
|
if (!fs2.existsSync(subagentsPath))
|
|
49633
49571
|
continue;
|
|
49634
49572
|
if (!dirWatchers.has(subagentsPath)) {
|
|
@@ -49636,7 +49574,7 @@ function startSubagentWatcher(config) {
|
|
|
49636
49574
|
const w = fs2.watch(subagentsPath, (_event, filename) => {
|
|
49637
49575
|
if (!filename || !filename.toString().startsWith("agent-") || !filename.toString().endsWith(".jsonl"))
|
|
49638
49576
|
return;
|
|
49639
|
-
const filePath =
|
|
49577
|
+
const filePath = join21(subagentsPath, filename.toString());
|
|
49640
49578
|
if (!knownFiles.has(filePath)) {
|
|
49641
49579
|
scanSubagentsDir(subagentsPath);
|
|
49642
49580
|
}
|
|
@@ -49661,7 +49599,7 @@ function startSubagentWatcher(config) {
|
|
|
49661
49599
|
for (const e of entries) {
|
|
49662
49600
|
if (!e.startsWith("agent-") || !e.endsWith(".jsonl"))
|
|
49663
49601
|
continue;
|
|
49664
|
-
const filePath =
|
|
49602
|
+
const filePath = join21(subagentsPath, e);
|
|
49665
49603
|
if (knownFiles.has(filePath))
|
|
49666
49604
|
continue;
|
|
49667
49605
|
const agentId = e.slice("agent-".length, -".jsonl".length);
|
|
@@ -49778,15 +49716,15 @@ function determineRestartReason(opts) {
|
|
|
49778
49716
|
init_boot_card();
|
|
49779
49717
|
|
|
49780
49718
|
// gateway/update-announce.ts
|
|
49781
|
-
import { existsSync as
|
|
49782
|
-
import { join as
|
|
49719
|
+
import { existsSync as existsSync29, mkdirSync as mkdirSync17, openSync as openSync3, closeSync as closeSync3, readFileSync as readFileSync27 } from "node:fs";
|
|
49720
|
+
import { join as join26 } from "node:path";
|
|
49783
49721
|
import { homedir as homedir12 } from "node:os";
|
|
49784
49722
|
|
|
49785
49723
|
// ../src/host-control/audit-reader.ts
|
|
49786
49724
|
import { homedir as homedir11 } from "node:os";
|
|
49787
|
-
import { join as
|
|
49725
|
+
import { join as join25 } from "node:path";
|
|
49788
49726
|
function defaultAuditLogPath(home2 = homedir11()) {
|
|
49789
|
-
return
|
|
49727
|
+
return join25(home2, ".switchroom", "host-control-audit.log");
|
|
49790
49728
|
}
|
|
49791
49729
|
function parseAuditLine(line) {
|
|
49792
49730
|
const trimmed = line.trim();
|
|
@@ -49892,8 +49830,8 @@ function readAndFilter(raw, filters, limit) {
|
|
|
49892
49830
|
var DEFAULT_LOOKBACK_MS = 10 * 60 * 1000;
|
|
49893
49831
|
function readLastTerminalUpdateAudit(opts = {}) {
|
|
49894
49832
|
const path = opts.auditLogPath ?? defaultAuditLogPath();
|
|
49895
|
-
const exists = opts.exists ??
|
|
49896
|
-
const readFile = opts.readFile ?? ((p) =>
|
|
49833
|
+
const exists = opts.exists ?? existsSync29;
|
|
49834
|
+
const readFile = opts.readFile ?? ((p) => readFileSync27(p, "utf-8"));
|
|
49897
49835
|
if (!exists(path))
|
|
49898
49836
|
return null;
|
|
49899
49837
|
let raw;
|
|
@@ -49954,15 +49892,15 @@ function renderUpdateOutcomeLine(entry) {
|
|
|
49954
49892
|
`);
|
|
49955
49893
|
}
|
|
49956
49894
|
function claimUpdateAnnouncement(requestId, opts = {}) {
|
|
49957
|
-
const stateDir = opts.stateDir ?? process.env.TELEGRAM_STATE_DIR ??
|
|
49958
|
-
const dir =
|
|
49895
|
+
const stateDir = opts.stateDir ?? process.env.TELEGRAM_STATE_DIR ?? join26(homedir12(), ".switchroom");
|
|
49896
|
+
const dir = join26(stateDir, "update-announced");
|
|
49959
49897
|
try {
|
|
49960
49898
|
mkdirSync17(dir, { recursive: true });
|
|
49961
49899
|
} catch {
|
|
49962
49900
|
return false;
|
|
49963
49901
|
}
|
|
49964
49902
|
const safeId = requestId.replace(/[^A-Za-z0-9_.-]/g, "_").slice(0, 200);
|
|
49965
|
-
const path =
|
|
49903
|
+
const path = join26(dir, safeId);
|
|
49966
49904
|
try {
|
|
49967
49905
|
const fd = openSync3(path, "wx");
|
|
49968
49906
|
closeSync3(fd);
|
|
@@ -49981,7 +49919,7 @@ function maybeRenderUpdateAnnouncement(opts = {}) {
|
|
|
49981
49919
|
}
|
|
49982
49920
|
|
|
49983
49921
|
// issues-card.ts
|
|
49984
|
-
import { readFileSync as
|
|
49922
|
+
import { readFileSync as readFileSync28, writeFileSync as writeFileSync17 } from "node:fs";
|
|
49985
49923
|
var SEVERITY_EMOJI = {
|
|
49986
49924
|
info: "\u2139\ufe0f",
|
|
49987
49925
|
warn: "\u26a0\ufe0f",
|
|
@@ -50073,7 +50011,7 @@ function extractRetryAfterSecs2(err) {
|
|
|
50073
50011
|
var COOLDOWN_JITTER_MS2 = 500;
|
|
50074
50012
|
function readPersistedMessageId(path, log) {
|
|
50075
50013
|
try {
|
|
50076
|
-
const raw =
|
|
50014
|
+
const raw = readFileSync28(path, "utf8");
|
|
50077
50015
|
const parsed = JSON.parse(raw);
|
|
50078
50016
|
const v = parsed.messageId;
|
|
50079
50017
|
if (typeof v === "number" && Number.isInteger(v) && v > 0)
|
|
@@ -50089,7 +50027,7 @@ function readPersistedMessageId(path, log) {
|
|
|
50089
50027
|
}
|
|
50090
50028
|
function writePersistedMessageId(path, messageId, log) {
|
|
50091
50029
|
try {
|
|
50092
|
-
|
|
50030
|
+
writeFileSync17(path, JSON.stringify({ messageId }) + `
|
|
50093
50031
|
`, { mode: 384 });
|
|
50094
50032
|
} catch (err) {
|
|
50095
50033
|
log(`issues-card: persist write failed (${err.message})`);
|
|
@@ -50182,24 +50120,24 @@ function createIssuesCardHandle(opts) {
|
|
|
50182
50120
|
}
|
|
50183
50121
|
|
|
50184
50122
|
// issues-watcher.ts
|
|
50185
|
-
import { existsSync as
|
|
50186
|
-
import { join as
|
|
50123
|
+
import { existsSync as existsSync31, statSync as statSync8 } from "node:fs";
|
|
50124
|
+
import { join as join28 } from "node:path";
|
|
50187
50125
|
|
|
50188
50126
|
// ../src/issues/store.ts
|
|
50189
50127
|
import {
|
|
50190
50128
|
closeSync as closeSync4,
|
|
50191
|
-
existsSync as
|
|
50129
|
+
existsSync as existsSync30,
|
|
50192
50130
|
mkdirSync as mkdirSync18,
|
|
50193
50131
|
openSync as openSync4,
|
|
50194
50132
|
readdirSync as readdirSync5,
|
|
50195
|
-
readFileSync as
|
|
50196
|
-
renameSync as
|
|
50133
|
+
readFileSync as readFileSync29,
|
|
50134
|
+
renameSync as renameSync10,
|
|
50197
50135
|
statSync as statSync7,
|
|
50198
|
-
unlinkSync as
|
|
50199
|
-
writeFileSync as
|
|
50136
|
+
unlinkSync as unlinkSync10,
|
|
50137
|
+
writeFileSync as writeFileSync18,
|
|
50200
50138
|
writeSync
|
|
50201
50139
|
} from "node:fs";
|
|
50202
|
-
import { join as
|
|
50140
|
+
import { join as join27 } from "node:path";
|
|
50203
50141
|
import { randomBytes as randomBytes4 } from "node:crypto";
|
|
50204
50142
|
import { execSync } from "node:child_process";
|
|
50205
50143
|
|
|
@@ -50215,12 +50153,12 @@ var SEVERITY_RANK2 = {
|
|
|
50215
50153
|
var ISSUES_FILE = "issues.jsonl";
|
|
50216
50154
|
var ISSUES_LOCK = "issues.lock";
|
|
50217
50155
|
function readAll(stateDir) {
|
|
50218
|
-
const path =
|
|
50219
|
-
if (!
|
|
50156
|
+
const path = join27(stateDir, ISSUES_FILE);
|
|
50157
|
+
if (!existsSync30(path))
|
|
50220
50158
|
return [];
|
|
50221
50159
|
let raw;
|
|
50222
50160
|
try {
|
|
50223
|
-
raw =
|
|
50161
|
+
raw = readFileSync29(path, "utf-8");
|
|
50224
50162
|
} catch {
|
|
50225
50163
|
return [];
|
|
50226
50164
|
}
|
|
@@ -50252,7 +50190,7 @@ function list(stateDir, opts = {}) {
|
|
|
50252
50190
|
});
|
|
50253
50191
|
}
|
|
50254
50192
|
function resolve6(stateDir, fingerprint, nowFn = Date.now) {
|
|
50255
|
-
if (!
|
|
50193
|
+
if (!existsSync30(join27(stateDir, ISSUES_FILE)))
|
|
50256
50194
|
return 0;
|
|
50257
50195
|
return withLock(stateDir, () => {
|
|
50258
50196
|
const all = readAll(stateDir);
|
|
@@ -50270,14 +50208,14 @@ function resolve6(stateDir, fingerprint, nowFn = Date.now) {
|
|
|
50270
50208
|
});
|
|
50271
50209
|
}
|
|
50272
50210
|
function writeAll(stateDir, events) {
|
|
50273
|
-
const path =
|
|
50211
|
+
const path = join27(stateDir, ISSUES_FILE);
|
|
50274
50212
|
sweepOrphanTmpFiles(stateDir);
|
|
50275
50213
|
const tmp = `${path}.tmp-${process.pid}-${randomBytes4(4).toString("hex")}`;
|
|
50276
50214
|
const body = events.length === 0 ? "" : events.map((e) => JSON.stringify(e)).join(`
|
|
50277
50215
|
`) + `
|
|
50278
50216
|
`;
|
|
50279
|
-
|
|
50280
|
-
|
|
50217
|
+
writeFileSync18(tmp, body, "utf-8");
|
|
50218
|
+
renameSync10(tmp, path);
|
|
50281
50219
|
}
|
|
50282
50220
|
var ORPHAN_TMP_TTL_MS = 60000;
|
|
50283
50221
|
var TMP_PREFIX = `${ISSUES_FILE}.tmp-`;
|
|
@@ -50292,11 +50230,11 @@ function sweepOrphanTmpFiles(stateDir) {
|
|
|
50292
50230
|
for (const entry of entries) {
|
|
50293
50231
|
if (!entry.startsWith(TMP_PREFIX))
|
|
50294
50232
|
continue;
|
|
50295
|
-
const tmpPath2 =
|
|
50233
|
+
const tmpPath2 = join27(stateDir, entry);
|
|
50296
50234
|
try {
|
|
50297
50235
|
const stat = statSync7(tmpPath2);
|
|
50298
50236
|
if (stat.mtimeMs < cutoff) {
|
|
50299
|
-
|
|
50237
|
+
unlinkSync10(tmpPath2);
|
|
50300
50238
|
}
|
|
50301
50239
|
} catch {}
|
|
50302
50240
|
}
|
|
@@ -50304,7 +50242,7 @@ function sweepOrphanTmpFiles(stateDir) {
|
|
|
50304
50242
|
var LOCK_RETRY_MS = 25;
|
|
50305
50243
|
var LOCK_TIMEOUT_MS = 1e4;
|
|
50306
50244
|
function withLock(stateDir, fn) {
|
|
50307
|
-
const lockPath =
|
|
50245
|
+
const lockPath = join27(stateDir, ISSUES_LOCK);
|
|
50308
50246
|
const startedAt = Date.now();
|
|
50309
50247
|
let fd = null;
|
|
50310
50248
|
while (fd === null) {
|
|
@@ -50332,27 +50270,27 @@ function withLock(stateDir, fn) {
|
|
|
50332
50270
|
closeSync4(fd);
|
|
50333
50271
|
} catch {}
|
|
50334
50272
|
try {
|
|
50335
|
-
|
|
50273
|
+
unlinkSync10(lockPath);
|
|
50336
50274
|
} catch {}
|
|
50337
50275
|
}
|
|
50338
50276
|
}
|
|
50339
50277
|
function tryStealStaleLock(lockPath) {
|
|
50340
50278
|
let pidStr;
|
|
50341
50279
|
try {
|
|
50342
|
-
pidStr =
|
|
50280
|
+
pidStr = readFileSync29(lockPath, "utf-8").trim();
|
|
50343
50281
|
} catch {
|
|
50344
50282
|
return true;
|
|
50345
50283
|
}
|
|
50346
50284
|
const pid = Number(pidStr);
|
|
50347
50285
|
if (!Number.isFinite(pid) || pid <= 0) {
|
|
50348
50286
|
try {
|
|
50349
|
-
|
|
50287
|
+
unlinkSync10(lockPath);
|
|
50350
50288
|
} catch {}
|
|
50351
50289
|
return true;
|
|
50352
50290
|
}
|
|
50353
50291
|
if (pid === process.pid) {
|
|
50354
50292
|
try {
|
|
50355
|
-
|
|
50293
|
+
unlinkSync10(lockPath);
|
|
50356
50294
|
} catch {}
|
|
50357
50295
|
return true;
|
|
50358
50296
|
}
|
|
@@ -50367,7 +50305,7 @@ function tryStealStaleLock(lockPath) {
|
|
|
50367
50305
|
return false;
|
|
50368
50306
|
}
|
|
50369
50307
|
try {
|
|
50370
|
-
|
|
50308
|
+
unlinkSync10(lockPath);
|
|
50371
50309
|
} catch {}
|
|
50372
50310
|
return true;
|
|
50373
50311
|
}
|
|
@@ -50389,7 +50327,7 @@ function isIssueEvent(v) {
|
|
|
50389
50327
|
// issues-watcher.ts
|
|
50390
50328
|
var DEFAULT_POLL_INTERVAL_MS2 = 2000;
|
|
50391
50329
|
function startIssuesWatcher(opts) {
|
|
50392
|
-
const path =
|
|
50330
|
+
const path = join28(opts.stateDir, ISSUES_FILE);
|
|
50393
50331
|
const log = opts.log ?? (() => {});
|
|
50394
50332
|
const intervalMs = opts.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS2;
|
|
50395
50333
|
const setIntervalFn = opts.setInterval ?? setInterval;
|
|
@@ -50437,7 +50375,7 @@ function startIssuesWatcher(opts) {
|
|
|
50437
50375
|
};
|
|
50438
50376
|
}
|
|
50439
50377
|
function defaultSignatureProvider(path) {
|
|
50440
|
-
if (!
|
|
50378
|
+
if (!existsSync31(path))
|
|
50441
50379
|
return null;
|
|
50442
50380
|
try {
|
|
50443
50381
|
const stat = statSync8(path);
|
|
@@ -51042,8 +50980,8 @@ function extractFlowItems(line) {
|
|
|
51042
50980
|
}
|
|
51043
50981
|
|
|
51044
50982
|
// credits-watch.ts
|
|
51045
|
-
import { readFileSync as
|
|
51046
|
-
import { join as
|
|
50983
|
+
import { readFileSync as readFileSync30, writeFileSync as writeFileSync19, existsSync as existsSync32, mkdirSync as mkdirSync19 } from "fs";
|
|
50984
|
+
import { join as join29 } from "path";
|
|
51047
50985
|
var STATE_FILE = "credits-watch.json";
|
|
51048
50986
|
var FATAL_REASONS = new Set([
|
|
51049
50987
|
"out_of_credits",
|
|
@@ -51055,12 +50993,12 @@ function emptyCreditState() {
|
|
|
51055
50993
|
return { lastNotifiedReason: null, lastNotifiedAt: 0 };
|
|
51056
50994
|
}
|
|
51057
50995
|
function readClaudeJsonOverage(claudeConfigDir) {
|
|
51058
|
-
const path =
|
|
51059
|
-
if (!
|
|
50996
|
+
const path = join29(claudeConfigDir, ".claude.json");
|
|
50997
|
+
if (!existsSync32(path))
|
|
51060
50998
|
return null;
|
|
51061
50999
|
let raw;
|
|
51062
51000
|
try {
|
|
51063
|
-
raw =
|
|
51001
|
+
raw = readFileSync30(path, "utf-8");
|
|
51064
51002
|
} catch {
|
|
51065
51003
|
return null;
|
|
51066
51004
|
}
|
|
@@ -51084,7 +51022,7 @@ function evaluateCreditState(args) {
|
|
|
51084
51022
|
if (!currentIsFatal && prevIsFatal) {
|
|
51085
51023
|
return {
|
|
51086
51024
|
kind: "notify",
|
|
51087
|
-
message: `\u2705 <b>${
|
|
51025
|
+
message: `\u2705 <b>${escapeHtml9(agentName3)}</b>: credits restored \u2014 agent should resume normal operation.`,
|
|
51088
51026
|
newState: { lastNotifiedReason: null, lastNotifiedAt: now },
|
|
51089
51027
|
transition: "exited"
|
|
51090
51028
|
};
|
|
@@ -51113,12 +51051,12 @@ function evaluateCreditState(args) {
|
|
|
51113
51051
|
function buildEntryMessage(agentName3, reason) {
|
|
51114
51052
|
const desc = humanizeReason(reason);
|
|
51115
51053
|
return [
|
|
51116
|
-
`\u26a0\ufe0f <b>${
|
|
51054
|
+
`\u26a0\ufe0f <b>${escapeHtml9(agentName3)}</b>: ${desc}`,
|
|
51117
51055
|
``,
|
|
51118
51056
|
`Cron tasks and inbound replies will fail until this is resolved. Check`,
|
|
51119
51057
|
`your subscription or pre-paid usage at <a href="https://console.anthropic.com">console.anthropic.com</a>.`,
|
|
51120
51058
|
``,
|
|
51121
|
-
`<i>Source: Claude CLI cache (cachedExtraUsageDisabledReason=${
|
|
51059
|
+
`<i>Source: Claude CLI cache (cachedExtraUsageDisabledReason=${escapeHtml9(reason)})</i>`
|
|
51122
51060
|
].join(`
|
|
51123
51061
|
`);
|
|
51124
51062
|
}
|
|
@@ -51136,15 +51074,15 @@ function humanizeReason(reason) {
|
|
|
51136
51074
|
return `usage disabled (${reason})`;
|
|
51137
51075
|
}
|
|
51138
51076
|
}
|
|
51139
|
-
function
|
|
51077
|
+
function escapeHtml9(s) {
|
|
51140
51078
|
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
51141
51079
|
}
|
|
51142
51080
|
function loadCreditState(stateDir) {
|
|
51143
|
-
const path =
|
|
51144
|
-
if (!
|
|
51081
|
+
const path = join29(stateDir, STATE_FILE);
|
|
51082
|
+
if (!existsSync32(path))
|
|
51145
51083
|
return emptyCreditState();
|
|
51146
51084
|
try {
|
|
51147
|
-
const raw =
|
|
51085
|
+
const raw = readFileSync30(path, "utf-8");
|
|
51148
51086
|
const parsed = JSON.parse(raw);
|
|
51149
51087
|
if (parsed && typeof parsed === "object" && (parsed.lastNotifiedReason === null || typeof parsed.lastNotifiedReason === "string") && typeof parsed.lastNotifiedAt === "number" && Number.isFinite(parsed.lastNotifiedAt)) {
|
|
51150
51088
|
return {
|
|
@@ -51157,14 +51095,14 @@ function loadCreditState(stateDir) {
|
|
|
51157
51095
|
}
|
|
51158
51096
|
function saveCreditState(stateDir, state4) {
|
|
51159
51097
|
mkdirSync19(stateDir, { recursive: true });
|
|
51160
|
-
const path =
|
|
51161
|
-
|
|
51098
|
+
const path = join29(stateDir, STATE_FILE);
|
|
51099
|
+
writeFileSync19(path, JSON.stringify(state4, null, 2) + `
|
|
51162
51100
|
`, { mode: 384 });
|
|
51163
51101
|
}
|
|
51164
51102
|
|
|
51165
51103
|
// quota-watch.ts
|
|
51166
|
-
import { readFileSync as
|
|
51167
|
-
import { join as
|
|
51104
|
+
import { readFileSync as readFileSync31, writeFileSync as writeFileSync20, existsSync as existsSync33, mkdirSync as mkdirSync20 } from "fs";
|
|
51105
|
+
import { join as join30 } from "path";
|
|
51168
51106
|
var STATE_FILE2 = "quota-watch.json";
|
|
51169
51107
|
function emptyQuotaWatchState() {
|
|
51170
51108
|
return {};
|
|
@@ -51221,11 +51159,11 @@ function buildThrottlingMessage(agentName3, snap) {
|
|
|
51221
51159
|
const resetAt = win === "5h" ? q.fiveHourResetAt : q.sevenDayResetAt;
|
|
51222
51160
|
const resetStr = resetAt ? ` \u00b7 refills in ${formatRelative(resetAt, new Date)}` : "";
|
|
51223
51161
|
const activeNote = snap.isActive ? "" : `
|
|
51224
|
-
This is a non-active account. Consider <code>/auth use ${
|
|
51162
|
+
This is a non-active account. Consider <code>/auth use ${escapeHtml10(snap.label)}</code> to switch, or keep it as a fallback reserve.`;
|
|
51225
51163
|
const altNote = snap.isActive ? `
|
|
51226
51164
|
Consider <code>/auth use <other-account></code> if you have a healthier account, or wait for the ${winLabel} window to refill${resetStr}.` : "";
|
|
51227
51165
|
return [
|
|
51228
|
-
`\uD83D\uDFE1 <b>Quota approaching limit</b> \u2014 <code>${
|
|
51166
|
+
`\uD83D\uDFE1 <b>Quota approaching limit</b> \u2014 <code>${escapeHtml10(snap.label)}</code>`,
|
|
51229
51167
|
``,
|
|
51230
51168
|
`${fiveStr} of 5h \u00b7 ${sevenStr} of 7d`,
|
|
51231
51169
|
`Binding window: ${winLabel}${resetStr}`,
|
|
@@ -51242,7 +51180,7 @@ function buildRecoveryMessage(agentName3, snap) {
|
|
|
51242
51180
|
const q = snap.quota;
|
|
51243
51181
|
const utilLine = q ? `Current: ${fmtPct(q.fiveHourUtilizationPct)} of 5h \u00b7 ${fmtPct(q.sevenDayUtilizationPct)} of 7d` : "Current quota data unavailable.";
|
|
51244
51182
|
return [
|
|
51245
|
-
`\uD83D\uDFE2 <b>Quota back in healthy range</b> \u2014 <code>${
|
|
51183
|
+
`\uD83D\uDFE2 <b>Quota back in healthy range</b> \u2014 <code>${escapeHtml10(snap.label)}</code>`,
|
|
51246
51184
|
``,
|
|
51247
51185
|
utilLine,
|
|
51248
51186
|
``,
|
|
@@ -51250,15 +51188,15 @@ function buildRecoveryMessage(agentName3, snap) {
|
|
|
51250
51188
|
].join(`
|
|
51251
51189
|
`);
|
|
51252
51190
|
}
|
|
51253
|
-
function
|
|
51191
|
+
function escapeHtml10(s) {
|
|
51254
51192
|
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
51255
51193
|
}
|
|
51256
51194
|
function loadQuotaWatchState(stateDir) {
|
|
51257
|
-
const path =
|
|
51258
|
-
if (!
|
|
51195
|
+
const path = join30(stateDir, STATE_FILE2);
|
|
51196
|
+
if (!existsSync33(path))
|
|
51259
51197
|
return emptyQuotaWatchState();
|
|
51260
51198
|
try {
|
|
51261
|
-
const raw =
|
|
51199
|
+
const raw = readFileSync31(path, "utf-8");
|
|
51262
51200
|
const parsed = JSON.parse(raw);
|
|
51263
51201
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
51264
51202
|
return emptyQuotaWatchState();
|
|
@@ -51276,8 +51214,8 @@ function loadQuotaWatchState(stateDir) {
|
|
|
51276
51214
|
}
|
|
51277
51215
|
function saveQuotaWatchState(stateDir, state4) {
|
|
51278
51216
|
mkdirSync20(stateDir, { recursive: true });
|
|
51279
|
-
const path =
|
|
51280
|
-
|
|
51217
|
+
const path = join30(stateDir, STATE_FILE2);
|
|
51218
|
+
writeFileSync20(path, JSON.stringify(state4, null, 2) + `
|
|
51281
51219
|
`, { mode: 384 });
|
|
51282
51220
|
}
|
|
51283
51221
|
function patchQuotaWatchState(current, accountLabel, accountState) {
|
|
@@ -51290,27 +51228,27 @@ init_auth_snapshot_format();
|
|
|
51290
51228
|
// gateway/turn-active-marker.ts
|
|
51291
51229
|
import {
|
|
51292
51230
|
closeSync as closeSync5,
|
|
51293
|
-
existsSync as
|
|
51231
|
+
existsSync as existsSync34,
|
|
51294
51232
|
mkdirSync as mkdirSync21,
|
|
51295
51233
|
openSync as openSync5,
|
|
51296
|
-
readFileSync as
|
|
51234
|
+
readFileSync as readFileSync32,
|
|
51297
51235
|
statSync as statSync9,
|
|
51298
|
-
unlinkSync as
|
|
51236
|
+
unlinkSync as unlinkSync11,
|
|
51299
51237
|
utimesSync as utimesSync2,
|
|
51300
|
-
writeFileSync as
|
|
51238
|
+
writeFileSync as writeFileSync21
|
|
51301
51239
|
} from "node:fs";
|
|
51302
|
-
import { join as
|
|
51240
|
+
import { join as join31 } from "node:path";
|
|
51303
51241
|
var TURN_ACTIVE_MARKER_FILE2 = "turn-active.json";
|
|
51304
51242
|
function writeTurnActiveMarker(stateDir, marker) {
|
|
51305
51243
|
try {
|
|
51306
51244
|
mkdirSync21(stateDir, { recursive: true });
|
|
51307
|
-
|
|
51245
|
+
writeFileSync21(join31(stateDir, TURN_ACTIVE_MARKER_FILE2), JSON.stringify(marker, null, 2) + `
|
|
51308
51246
|
`, { mode: 384 });
|
|
51309
51247
|
} catch {}
|
|
51310
51248
|
}
|
|
51311
51249
|
function touchTurnActiveMarker2(stateDir) {
|
|
51312
|
-
const path =
|
|
51313
|
-
if (!
|
|
51250
|
+
const path = join31(stateDir, TURN_ACTIVE_MARKER_FILE2);
|
|
51251
|
+
if (!existsSync34(path))
|
|
51314
51252
|
return;
|
|
51315
51253
|
const now = new Date;
|
|
51316
51254
|
try {
|
|
@@ -51324,12 +51262,12 @@ function touchTurnActiveMarker2(stateDir) {
|
|
|
51324
51262
|
}
|
|
51325
51263
|
function removeTurnActiveMarker(stateDir) {
|
|
51326
51264
|
try {
|
|
51327
|
-
|
|
51265
|
+
unlinkSync11(join31(stateDir, TURN_ACTIVE_MARKER_FILE2));
|
|
51328
51266
|
} catch {}
|
|
51329
51267
|
}
|
|
51330
51268
|
function sweepStaleTurnActiveMarker(stateDir, opts) {
|
|
51331
|
-
const path =
|
|
51332
|
-
if (!
|
|
51269
|
+
const path = join31(stateDir, TURN_ACTIVE_MARKER_FILE2);
|
|
51270
|
+
if (!existsSync34(path))
|
|
51333
51271
|
return false;
|
|
51334
51272
|
const now = opts.now ?? Date.now();
|
|
51335
51273
|
try {
|
|
@@ -51341,9 +51279,9 @@ function sweepStaleTurnActiveMarker(stateDir, opts) {
|
|
|
51341
51279
|
return false;
|
|
51342
51280
|
let payload = null;
|
|
51343
51281
|
try {
|
|
51344
|
-
payload =
|
|
51282
|
+
payload = readFileSync32(path, "utf8");
|
|
51345
51283
|
} catch {}
|
|
51346
|
-
|
|
51284
|
+
unlinkSync11(path);
|
|
51347
51285
|
if (opts.onRemove) {
|
|
51348
51286
|
try {
|
|
51349
51287
|
opts.onRemove({
|
|
@@ -51360,10 +51298,10 @@ function sweepStaleTurnActiveMarker(stateDir, opts) {
|
|
|
51360
51298
|
}
|
|
51361
51299
|
|
|
51362
51300
|
// ../src/build-info.ts
|
|
51363
|
-
var VERSION = "0.14.
|
|
51364
|
-
var COMMIT_SHA = "
|
|
51365
|
-
var COMMIT_DATE = "2026-05-
|
|
51366
|
-
var LATEST_PR =
|
|
51301
|
+
var VERSION = "0.14.22";
|
|
51302
|
+
var COMMIT_SHA = "ab2692b9";
|
|
51303
|
+
var COMMIT_DATE = "2026-05-31T06:26:06Z";
|
|
51304
|
+
var LATEST_PR = 2028;
|
|
51367
51305
|
var COMMITS_AHEAD_OF_TAG = 0;
|
|
51368
51306
|
|
|
51369
51307
|
// gateway/boot-version.ts
|
|
@@ -51437,11 +51375,11 @@ init_peercred();
|
|
|
51437
51375
|
import * as net5 from "node:net";
|
|
51438
51376
|
import * as fs2 from "node:fs";
|
|
51439
51377
|
import { homedir as homedir13 } from "node:os";
|
|
51440
|
-
import { join as
|
|
51378
|
+
import { join as join32 } from "node:path";
|
|
51441
51379
|
var DEFAULT_TIMEOUT_MS4 = 2000;
|
|
51442
51380
|
var UNLOCK_TIMEOUT_MS = 30000;
|
|
51443
|
-
var LEGACY_SOCKET_PATH2 =
|
|
51444
|
-
var OPERATOR_SOCKET_PATH2 =
|
|
51381
|
+
var LEGACY_SOCKET_PATH2 = join32(homedir13(), ".switchroom", "vault-broker.sock");
|
|
51382
|
+
var OPERATOR_SOCKET_PATH2 = join32(homedir13(), ".switchroom", "broker-operator", "sock");
|
|
51445
51383
|
function defaultBrokerSocketPath2() {
|
|
51446
51384
|
if (fs2.existsSync(OPERATOR_SOCKET_PATH2))
|
|
51447
51385
|
return OPERATOR_SOCKET_PATH2;
|
|
@@ -51664,7 +51602,7 @@ function resolveVaultApprovalPosture(broker) {
|
|
|
51664
51602
|
|
|
51665
51603
|
// registry/turns-schema.ts
|
|
51666
51604
|
import { chmodSync as chmodSync4, mkdirSync as mkdirSync22 } from "fs";
|
|
51667
|
-
import { join as
|
|
51605
|
+
import { join as join33 } from "path";
|
|
51668
51606
|
var DatabaseClass2 = null;
|
|
51669
51607
|
function loadDatabaseClass2() {
|
|
51670
51608
|
if (DatabaseClass2 != null)
|
|
@@ -51697,6 +51635,7 @@ var SCHEMA_SQL = `
|
|
|
51697
51635
|
user_prompt_preview TEXT,
|
|
51698
51636
|
assistant_reply_preview TEXT,
|
|
51699
51637
|
tool_call_count INTEGER,
|
|
51638
|
+
interrupt_reason TEXT,
|
|
51700
51639
|
created_at INTEGER NOT NULL,
|
|
51701
51640
|
updated_at INTEGER NOT NULL
|
|
51702
51641
|
);
|
|
@@ -51707,11 +51646,14 @@ var PHASE1_MIGRATIONS = [
|
|
|
51707
51646
|
`ALTER TABLE turns ADD COLUMN assistant_reply_preview TEXT`,
|
|
51708
51647
|
`ALTER TABLE turns ADD COLUMN tool_call_count INTEGER`
|
|
51709
51648
|
];
|
|
51649
|
+
var PHASE2_MIGRATIONS = [
|
|
51650
|
+
`ALTER TABLE turns ADD COLUMN interrupt_reason TEXT`
|
|
51651
|
+
];
|
|
51710
51652
|
function applySchema(db2) {
|
|
51711
51653
|
db2.exec("PRAGMA journal_mode = WAL");
|
|
51712
51654
|
db2.exec("PRAGMA synchronous = NORMAL");
|
|
51713
51655
|
db2.exec(SCHEMA_SQL);
|
|
51714
|
-
for (const sql of PHASE1_MIGRATIONS) {
|
|
51656
|
+
for (const sql of [...PHASE1_MIGRATIONS, ...PHASE2_MIGRATIONS]) {
|
|
51715
51657
|
try {
|
|
51716
51658
|
db2.exec(sql);
|
|
51717
51659
|
} catch (err) {
|
|
@@ -51723,9 +51665,9 @@ function applySchema(db2) {
|
|
|
51723
51665
|
}
|
|
51724
51666
|
function openTurnsDb(agentDir) {
|
|
51725
51667
|
const Database = loadDatabaseClass2();
|
|
51726
|
-
const dir =
|
|
51668
|
+
const dir = join33(agentDir, "telegram");
|
|
51727
51669
|
mkdirSync22(dir, { recursive: true, mode: 448 });
|
|
51728
|
-
const path =
|
|
51670
|
+
const path = join33(dir, "registry.db");
|
|
51729
51671
|
const db2 = new Database(path, { create: true });
|
|
51730
51672
|
applySchema(db2);
|
|
51731
51673
|
try {
|
|
@@ -51747,6 +51689,7 @@ function mapRow(row) {
|
|
|
51747
51689
|
user_prompt_preview: row.user_prompt_preview,
|
|
51748
51690
|
assistant_reply_preview: row.assistant_reply_preview,
|
|
51749
51691
|
tool_call_count: row.tool_call_count,
|
|
51692
|
+
interrupt_reason: row.interrupt_reason,
|
|
51750
51693
|
created_at: row.created_at,
|
|
51751
51694
|
updated_at: row.updated_at
|
|
51752
51695
|
};
|
|
@@ -51775,26 +51718,143 @@ function recordTurnEnd(db2, args) {
|
|
|
51775
51718
|
WHERE turn_key = ?
|
|
51776
51719
|
`).run(now, args.endedVia, args.lastAssistantMsgId ?? null, args.lastAssistantDone !== undefined ? args.lastAssistantDone ? 1 : 0 : null, args.assistantReplyPreview ?? null, args.toolCallCount !== undefined ? args.toolCallCount : null, now, args.turnKey);
|
|
51777
51720
|
}
|
|
51778
|
-
function
|
|
51779
|
-
const now = Date.now();
|
|
51780
|
-
const
|
|
51721
|
+
function markOrphanedWithTimeoutClassification(db2, opts) {
|
|
51722
|
+
const now = opts.now ?? Date.now();
|
|
51723
|
+
const isHang = opts.markerAgeMs != null && opts.markerAgeMs >= opts.hangThresholdMs && opts.markerTurnKey != null && opts.markerTurnKey.length > 0;
|
|
51724
|
+
let timeoutTurnKey = null;
|
|
51725
|
+
if (isHang) {
|
|
51726
|
+
const r = db2.prepare(`
|
|
51727
|
+
UPDATE turns
|
|
51728
|
+
SET ended_at = ?,
|
|
51729
|
+
ended_via = 'timeout',
|
|
51730
|
+
interrupt_reason = ?,
|
|
51731
|
+
updated_at = ?
|
|
51732
|
+
WHERE turn_key = ? AND ended_at IS NULL
|
|
51733
|
+
`).run(now, opts.reasonSnapshot ?? null, now, opts.markerTurnKey);
|
|
51734
|
+
if (r.changes > 0)
|
|
51735
|
+
timeoutTurnKey = opts.markerTurnKey ?? null;
|
|
51736
|
+
}
|
|
51737
|
+
const rest = db2.prepare(`
|
|
51781
51738
|
UPDATE turns
|
|
51782
51739
|
SET ended_at = ?,
|
|
51783
51740
|
ended_via = 'restart',
|
|
51784
51741
|
updated_at = ?
|
|
51785
51742
|
WHERE ended_at IS NULL
|
|
51786
51743
|
`).run(now, now);
|
|
51787
|
-
return
|
|
51744
|
+
return { reaped: (timeoutTurnKey ? 1 : 0) + rest.changes, timeoutTurnKey };
|
|
51788
51745
|
}
|
|
51789
|
-
|
|
51746
|
+
var INTERRUPTED_VIA = new Set([
|
|
51747
|
+
"restart",
|
|
51748
|
+
"sigterm",
|
|
51749
|
+
"timeout",
|
|
51750
|
+
"unknown"
|
|
51751
|
+
]);
|
|
51752
|
+
function findLatestTurnIfInterrupted(db2) {
|
|
51790
51753
|
const row = db2.prepare(`
|
|
51791
51754
|
SELECT * FROM turns
|
|
51792
|
-
WHERE ended_at IS NULL
|
|
51793
|
-
OR ended_via IN ('restart', 'sigterm', 'timeout')
|
|
51794
51755
|
ORDER BY started_at DESC
|
|
51795
51756
|
LIMIT 1
|
|
51796
51757
|
`).get();
|
|
51797
|
-
|
|
51758
|
+
if (!row)
|
|
51759
|
+
return null;
|
|
51760
|
+
const turn = mapRow(row);
|
|
51761
|
+
if (turn.ended_at == null)
|
|
51762
|
+
return turn;
|
|
51763
|
+
if (turn.ended_via != null && INTERRUPTED_VIA.has(turn.ended_via))
|
|
51764
|
+
return turn;
|
|
51765
|
+
return null;
|
|
51766
|
+
}
|
|
51767
|
+
|
|
51768
|
+
// gateway/resume-inbound-builder.ts
|
|
51769
|
+
function humanizeElapsed(ms) {
|
|
51770
|
+
if (!Number.isFinite(ms) || ms < 0)
|
|
51771
|
+
return "an unknown amount of time";
|
|
51772
|
+
const sec = Math.round(ms / 1000);
|
|
51773
|
+
if (sec < 45)
|
|
51774
|
+
return "moments";
|
|
51775
|
+
const min = Math.round(sec / 60);
|
|
51776
|
+
if (min < 60)
|
|
51777
|
+
return `~${min} min`;
|
|
51778
|
+
const hr = Math.round(min / 60);
|
|
51779
|
+
if (hr < 24)
|
|
51780
|
+
return `~${hr}h`;
|
|
51781
|
+
const days = Math.round(hr / 24);
|
|
51782
|
+
return `~${days} day${days === 1 ? "" : "s"}`;
|
|
51783
|
+
}
|
|
51784
|
+
function threadIdNum(turn) {
|
|
51785
|
+
if (turn.thread_id == null)
|
|
51786
|
+
return;
|
|
51787
|
+
const n = Number(turn.thread_id);
|
|
51788
|
+
return Number.isFinite(n) ? n : undefined;
|
|
51789
|
+
}
|
|
51790
|
+
function promptClause(turn) {
|
|
51791
|
+
const p = turn.user_prompt_preview?.trim();
|
|
51792
|
+
if (!p)
|
|
51793
|
+
return "";
|
|
51794
|
+
const snippet = p.length > 160 ? p.slice(0, 160) + "\u2026" : p;
|
|
51795
|
+
return ` The request was: "${snippet}".`;
|
|
51796
|
+
}
|
|
51797
|
+
function buildResumeInterruptedInbound(ctx) {
|
|
51798
|
+
const ts = ctx.nowMs ?? Date.now();
|
|
51799
|
+
const elapsed = humanizeElapsed(ts - ctx.turn.started_at);
|
|
51800
|
+
const meta = {
|
|
51801
|
+
source: "resume_interrupted",
|
|
51802
|
+
resume_turn_key: ctx.turn.turn_key,
|
|
51803
|
+
interrupted_via: ctx.turn.ended_via ?? "restart",
|
|
51804
|
+
started_at: String(ctx.turn.started_at)
|
|
51805
|
+
};
|
|
51806
|
+
if (ctx.turn.user_prompt_preview)
|
|
51807
|
+
meta.original_prompt = ctx.turn.user_prompt_preview;
|
|
51808
|
+
const threadId = threadIdNum(ctx.turn);
|
|
51809
|
+
return {
|
|
51810
|
+
type: "inbound",
|
|
51811
|
+
chatId: ctx.turn.chat_id,
|
|
51812
|
+
...threadId != null ? { threadId } : {},
|
|
51813
|
+
messageId: ts,
|
|
51814
|
+
user: "switchroom",
|
|
51815
|
+
userId: 0,
|
|
51816
|
+
ts,
|
|
51817
|
+
text: `You just restarted. Your previous turn was interrupted ${elapsed} ago, ` + `before it finished \u2014 it was cut off by a restart, not completed.` + promptClause(ctx.turn) + ` Pick that work back up now and continue it through to completion. ` + `In your first message, briefly let the user know you're resuming what ` + `was interrupted (mention roughly how long ago in plain language) so ` + `they're not left wondering \u2014 then carry on with the actual task. Do ` + `not ask whether to resume; just resume. If you genuinely can't tell ` + `what the work was, say so and ask.`,
|
|
51818
|
+
meta
|
|
51819
|
+
};
|
|
51820
|
+
}
|
|
51821
|
+
function buildResumeWatchdogReportInbound(ctx) {
|
|
51822
|
+
const ts = ctx.nowMs ?? Date.now();
|
|
51823
|
+
const idle = humanizeElapsed(ctx.idleMs);
|
|
51824
|
+
const since = humanizeElapsed(ts - ctx.turn.started_at);
|
|
51825
|
+
const toolClause = ctx.turn.tool_call_count != null && ctx.turn.tool_call_count > 0 ? ` You'd run ${ctx.turn.tool_call_count} tool call${ctx.turn.tool_call_count === 1 ? "" : "s"} before it stalled.` : "";
|
|
51826
|
+
const meta = {
|
|
51827
|
+
source: "resume_watchdog_timeout",
|
|
51828
|
+
resume_turn_key: ctx.turn.turn_key,
|
|
51829
|
+
interrupted_via: "timeout",
|
|
51830
|
+
idle_ms: String(ctx.idleMs),
|
|
51831
|
+
started_at: String(ctx.turn.started_at)
|
|
51832
|
+
};
|
|
51833
|
+
if (ctx.turn.tool_call_count != null)
|
|
51834
|
+
meta.tool_call_count = String(ctx.turn.tool_call_count);
|
|
51835
|
+
if (ctx.turn.user_prompt_preview)
|
|
51836
|
+
meta.original_prompt = ctx.turn.user_prompt_preview;
|
|
51837
|
+
const threadId = threadIdNum(ctx.turn);
|
|
51838
|
+
return {
|
|
51839
|
+
type: "inbound",
|
|
51840
|
+
chatId: ctx.turn.chat_id,
|
|
51841
|
+
...threadId != null ? { threadId } : {},
|
|
51842
|
+
messageId: ts,
|
|
51843
|
+
user: "switchroom",
|
|
51844
|
+
userId: 0,
|
|
51845
|
+
ts,
|
|
51846
|
+
text: `You just restarted. Your previous turn (started ${since} ago) was ` + `killed by the hang-watchdog: it made no observable progress for ${idle} ` + `and the watchdog restarts a turn that goes that long without activity.` + toolClause + promptClause(ctx.turn) + ` Do NOT silently resume it \u2014 it may hang again the same way. Instead, ` + `tell the user plainly what happened: that your last turn was killed ` + `after ${idle} of no progress, and roughly what it was doing. Then ask ` + `whether they want you to retry it or take a different angle. Report ` + `only the honest cause \u2014 no observable progress for that long \u2014 don't ` + `speculate about a deeper root cause you can't see.`,
|
|
51847
|
+
meta
|
|
51848
|
+
};
|
|
51849
|
+
}
|
|
51850
|
+
function selectResumeBuilder(endedVia) {
|
|
51851
|
+
if (endedVia === "timeout")
|
|
51852
|
+
return "report";
|
|
51853
|
+
if (endedVia === "restart" || endedVia === "sigterm" || endedVia === "unknown")
|
|
51854
|
+
return "resume";
|
|
51855
|
+
if (endedVia == null)
|
|
51856
|
+
return "resume";
|
|
51857
|
+
return null;
|
|
51798
51858
|
}
|
|
51799
51859
|
|
|
51800
51860
|
// registry/subagents-schema.ts
|
|
@@ -51894,11 +51954,11 @@ installGlobalErrorHandlers();
|
|
|
51894
51954
|
process.on("beforeExit", () => {
|
|
51895
51955
|
shutdownAnalytics();
|
|
51896
51956
|
});
|
|
51897
|
-
var STATE_DIR = process.env.TELEGRAM_STATE_DIR ??
|
|
51898
|
-
var ACCESS_FILE =
|
|
51899
|
-
var APPROVED_DIR =
|
|
51900
|
-
var ENV_FILE =
|
|
51901
|
-
var INBOX_DIR =
|
|
51957
|
+
var STATE_DIR = process.env.TELEGRAM_STATE_DIR ?? join35(homedir14(), ".claude", "channels", "telegram");
|
|
51958
|
+
var ACCESS_FILE = join35(STATE_DIR, "access.json");
|
|
51959
|
+
var APPROVED_DIR = join35(STATE_DIR, "approved");
|
|
51960
|
+
var ENV_FILE = join35(STATE_DIR, ".env");
|
|
51961
|
+
var INBOX_DIR = join35(STATE_DIR, "inbox");
|
|
51902
51962
|
function triggerSelfRestart(targetAgent, reason, delayMs = 300) {
|
|
51903
51963
|
const isDocker = process.env.SWITCHROOM_RUNTIME === "docker";
|
|
51904
51964
|
const selfAgent = process.env.SWITCHROOM_AGENT_NAME;
|
|
@@ -51963,7 +52023,7 @@ function formatBootVersion() {
|
|
|
51963
52023
|
}
|
|
51964
52024
|
try {
|
|
51965
52025
|
chmodSync6(ENV_FILE, 384);
|
|
51966
|
-
for (const line of
|
|
52026
|
+
for (const line of readFileSync35(ENV_FILE, "utf8").split(`
|
|
51967
52027
|
`)) {
|
|
51968
52028
|
const m = line.match(/^(\w+)=(.*)$/);
|
|
51969
52029
|
if (m && process.env[m[1]] === undefined)
|
|
@@ -52016,7 +52076,7 @@ installTgPostLogger(bot);
|
|
|
52016
52076
|
var _rawSendMessageDraft = bot.api.raw.sendMessageDraft;
|
|
52017
52077
|
var GRAMMY_VERSION = (() => {
|
|
52018
52078
|
try {
|
|
52019
|
-
const raw =
|
|
52079
|
+
const raw = readFileSync35(new URL("../../node_modules/grammy/package.json", import.meta.url), "utf8");
|
|
52020
52080
|
return JSON.parse(raw).version ?? "unknown";
|
|
52021
52081
|
} catch {
|
|
52022
52082
|
return "unknown";
|
|
@@ -52089,7 +52149,7 @@ function assertSendable(f) {
|
|
|
52089
52149
|
} catch {
|
|
52090
52150
|
throw new Error(`refusing to send file \u2014 cannot resolve real path: ${f}`);
|
|
52091
52151
|
}
|
|
52092
|
-
const inbox =
|
|
52152
|
+
const inbox = join35(stateReal, "inbox");
|
|
52093
52153
|
if (real.startsWith(stateReal + sep3) && !real.startsWith(inbox + sep3)) {
|
|
52094
52154
|
throw new Error(`refusing to send channel state: ${f}`);
|
|
52095
52155
|
}
|
|
@@ -52108,7 +52168,7 @@ function assertSendable(f) {
|
|
|
52108
52168
|
}
|
|
52109
52169
|
function readAccessFile() {
|
|
52110
52170
|
try {
|
|
52111
|
-
const raw =
|
|
52171
|
+
const raw = readFileSync35(ACCESS_FILE, "utf8");
|
|
52112
52172
|
const parsed = JSON.parse(raw);
|
|
52113
52173
|
const allowFrom = validateStringArray("allowFrom", parsed.allowFrom ?? []);
|
|
52114
52174
|
const groups = {};
|
|
@@ -52145,7 +52205,7 @@ function readAccessFile() {
|
|
|
52145
52205
|
if (err.code === "ENOENT")
|
|
52146
52206
|
return defaultAccess();
|
|
52147
52207
|
try {
|
|
52148
|
-
|
|
52208
|
+
renameSync12(ACCESS_FILE, `${ACCESS_FILE}.corrupt-${Date.now()}`);
|
|
52149
52209
|
} catch {}
|
|
52150
52210
|
process.stderr.write(`telegram gateway: access.json is corrupt, moved aside. Starting fresh.
|
|
52151
52211
|
`);
|
|
@@ -52178,9 +52238,9 @@ function saveAccess(a) {
|
|
|
52178
52238
|
return;
|
|
52179
52239
|
mkdirSync26(STATE_DIR, { recursive: true, mode: 448 });
|
|
52180
52240
|
const tmp = ACCESS_FILE + ".tmp";
|
|
52181
|
-
|
|
52241
|
+
writeFileSync24(tmp, JSON.stringify(a, null, 2) + `
|
|
52182
52242
|
`, { mode: 384 });
|
|
52183
|
-
|
|
52243
|
+
renameSync12(tmp, ACCESS_FILE);
|
|
52184
52244
|
}
|
|
52185
52245
|
function pruneExpired(a) {
|
|
52186
52246
|
const now = Date.now();
|
|
@@ -52198,7 +52258,7 @@ var HISTORY_ENABLED = HISTORY_ACCESS.historyEnabled !== false;
|
|
|
52198
52258
|
if (HISTORY_ENABLED) {
|
|
52199
52259
|
try {
|
|
52200
52260
|
initHistory(STATE_DIR, HISTORY_ACCESS.historyRetentionDays ?? 30);
|
|
52201
|
-
process.stderr.write(`telegram gateway: history capture enabled at ${
|
|
52261
|
+
process.stderr.write(`telegram gateway: history capture enabled at ${join35(STATE_DIR, "history.db")}
|
|
52202
52262
|
`);
|
|
52203
52263
|
} catch (err) {
|
|
52204
52264
|
process.stderr.write(`telegram gateway: history init failed (${err.message}) \u2014 capture disabled
|
|
@@ -52206,21 +52266,71 @@ if (HISTORY_ENABLED) {
|
|
|
52206
52266
|
}
|
|
52207
52267
|
}
|
|
52208
52268
|
var turnsDb = null;
|
|
52269
|
+
var bootResumeInbound = null;
|
|
52209
52270
|
try {
|
|
52210
52271
|
const agentDir = STATE_DIR.endsWith("/telegram") ? STATE_DIR.slice(0, -"/telegram".length) : STATE_DIR;
|
|
52211
52272
|
turnsDb = openTurnsDb(agentDir);
|
|
52212
52273
|
applySubagentsSchema(turnsDb);
|
|
52213
|
-
|
|
52274
|
+
let markerTurnKey = null;
|
|
52275
|
+
let markerAgeMs = null;
|
|
52276
|
+
try {
|
|
52277
|
+
const markerPath = join35(STATE_DIR, TURN_ACTIVE_MARKER_FILE2);
|
|
52278
|
+
if (existsSync38(markerPath)) {
|
|
52279
|
+
const st = statSync13(markerPath);
|
|
52280
|
+
markerAgeMs = Date.now() - st.mtimeMs;
|
|
52281
|
+
try {
|
|
52282
|
+
const payload = JSON.parse(readFileSync35(markerPath, "utf8"));
|
|
52283
|
+
if (typeof payload.turnKey === "string" && payload.turnKey.length > 0) {
|
|
52284
|
+
markerTurnKey = payload.turnKey;
|
|
52285
|
+
}
|
|
52286
|
+
} catch {}
|
|
52287
|
+
}
|
|
52288
|
+
} catch {}
|
|
52289
|
+
const hangSecs = Number(process.env.TURN_HANG_SECS);
|
|
52290
|
+
const hangThresholdMs = (Number.isFinite(hangSecs) && hangSecs > 0 ? hangSecs : 300) * 1000;
|
|
52291
|
+
const reasonSnapshot = markerAgeMs != null ? JSON.stringify({ idleMs: Math.round(markerAgeMs) }) : null;
|
|
52292
|
+
const { reaped, timeoutTurnKey } = markOrphanedWithTimeoutClassification(turnsDb, {
|
|
52293
|
+
markerTurnKey,
|
|
52294
|
+
markerAgeMs,
|
|
52295
|
+
hangThresholdMs,
|
|
52296
|
+
reasonSnapshot
|
|
52297
|
+
});
|
|
52214
52298
|
if (reaped > 0) {
|
|
52215
|
-
process.stderr.write(`telegram gateway: turn-registry boot-reaper stamped ${reaped} orphaned turn(s) as
|
|
52299
|
+
process.stderr.write(`telegram gateway: turn-registry boot-reaper stamped ${reaped} orphaned turn(s)${timeoutTurnKey ? ` (turnKey=${timeoutTurnKey} as 'timeout', markerAgeMs=${markerAgeMs})` : " as 'restart'"}
|
|
52216
52300
|
`);
|
|
52217
52301
|
} else {
|
|
52218
|
-
process.stderr.write(`telegram gateway: turn-registry initialized at ${
|
|
52302
|
+
process.stderr.write(`telegram gateway: turn-registry initialized at ${join35(agentDir, "telegram", "registry.db")}
|
|
52219
52303
|
`);
|
|
52220
52304
|
}
|
|
52221
|
-
const
|
|
52305
|
+
const pending2 = findLatestTurnIfInterrupted(turnsDb);
|
|
52306
|
+
const selfAgent = process.env.SWITCHROOM_AGENT_NAME ?? "";
|
|
52307
|
+
if (pending2 != null && selfAgent) {
|
|
52308
|
+
const kind = selectResumeBuilder(pending2.ended_via);
|
|
52309
|
+
if (kind === "resume") {
|
|
52310
|
+
bootResumeInbound = { agent: selfAgent, msg: buildResumeInterruptedInbound({ turn: pending2 }) };
|
|
52311
|
+
} else if (kind === "report") {
|
|
52312
|
+
let idleMs = pending2.turn_key === timeoutTurnKey && markerAgeMs != null ? markerAgeMs : null;
|
|
52313
|
+
if (idleMs == null && pending2.interrupt_reason) {
|
|
52314
|
+
try {
|
|
52315
|
+
const parsed = JSON.parse(pending2.interrupt_reason);
|
|
52316
|
+
if (typeof parsed.idleMs === "number" && Number.isFinite(parsed.idleMs))
|
|
52317
|
+
idleMs = parsed.idleMs;
|
|
52318
|
+
} catch {}
|
|
52319
|
+
}
|
|
52320
|
+
if (idleMs == null)
|
|
52321
|
+
idleMs = Math.max(0, Date.now() - pending2.started_at);
|
|
52322
|
+
bootResumeInbound = {
|
|
52323
|
+
agent: selfAgent,
|
|
52324
|
+
msg: buildResumeWatchdogReportInbound({ turn: pending2, idleMs })
|
|
52325
|
+
};
|
|
52326
|
+
}
|
|
52327
|
+
if (bootResumeInbound != null) {
|
|
52328
|
+
process.stderr.write(`telegram gateway: boot-resume queued kind=${kind} turnKey=${pending2.turn_key} endedVia=${pending2.ended_via ?? "open"} chat=${pending2.chat_id}
|
|
52329
|
+
`);
|
|
52330
|
+
}
|
|
52331
|
+
}
|
|
52332
|
+
const pendingEnvPath = join35(agentDir, ".pending-turn.env");
|
|
52222
52333
|
try {
|
|
52223
|
-
const pending2 = findMostRecentInterruptedTurn(turnsDb);
|
|
52224
52334
|
if (pending2 != null) {
|
|
52225
52335
|
const lines = [
|
|
52226
52336
|
`SWITCHROOM_PENDING_TURN=true`,
|
|
@@ -52229,22 +52339,23 @@ try {
|
|
|
52229
52339
|
pending2.thread_id != null ? `SWITCHROOM_PENDING_THREAD_ID=${pending2.thread_id}` : `SWITCHROOM_PENDING_THREAD_ID=`,
|
|
52230
52340
|
pending2.last_user_msg_id != null ? `SWITCHROOM_PENDING_USER_MSG_ID=${pending2.last_user_msg_id}` : `SWITCHROOM_PENDING_USER_MSG_ID=`,
|
|
52231
52341
|
`SWITCHROOM_PENDING_ENDED_VIA=${pending2.ended_via ?? "unknown"}`,
|
|
52232
|
-
`SWITCHROOM_PENDING_STARTED_AT=${pending2.started_at}
|
|
52342
|
+
`SWITCHROOM_PENDING_STARTED_AT=${pending2.started_at}`,
|
|
52343
|
+
pending2.interrupt_reason != null ? `SWITCHROOM_PENDING_INTERRUPT_REASON=${pending2.interrupt_reason}` : `SWITCHROOM_PENDING_INTERRUPT_REASON=`
|
|
52233
52344
|
];
|
|
52234
52345
|
const pendingEnvTmp = `${pendingEnvPath}.tmp-${process.pid}`;
|
|
52235
|
-
|
|
52346
|
+
writeFileSync24(pendingEnvTmp, lines.join(`
|
|
52236
52347
|
`) + `
|
|
52237
52348
|
`, { mode: 384 });
|
|
52238
|
-
|
|
52349
|
+
renameSync12(pendingEnvTmp, pendingEnvPath);
|
|
52239
52350
|
process.stderr.write(`telegram gateway: pending-turn env written to ${pendingEnvPath} turnKey=${pending2.turn_key} endedVia=${pending2.ended_via ?? "open"}
|
|
52240
52351
|
`);
|
|
52241
|
-
} else if (
|
|
52352
|
+
} else if (existsSync38(pendingEnvPath)) {
|
|
52242
52353
|
rmSync4(pendingEnvPath, { force: true });
|
|
52243
52354
|
process.stderr.write(`telegram gateway: pending-turn env cleared (clean previous shutdown)
|
|
52244
52355
|
`);
|
|
52245
52356
|
}
|
|
52246
52357
|
} catch (err) {
|
|
52247
|
-
process.stderr.write(`telegram gateway: pending-turn env write failed (${err.message})
|
|
52358
|
+
process.stderr.write(`telegram gateway: pending-turn env write failed (${err.message})
|
|
52248
52359
|
`);
|
|
52249
52360
|
}
|
|
52250
52361
|
} catch (err) {
|
|
@@ -52292,7 +52403,7 @@ function checkApprovals() {
|
|
|
52292
52403
|
return;
|
|
52293
52404
|
}
|
|
52294
52405
|
for (const senderId of files) {
|
|
52295
|
-
const file =
|
|
52406
|
+
const file = join35(APPROVED_DIR, senderId);
|
|
52296
52407
|
bot.api.sendMessage(senderId, "Paired! Say hi to Claude.").then(() => rmSync4(file, { force: true }), (err) => {
|
|
52297
52408
|
process.stderr.write(`telegram gateway: failed to send approval confirm: ${err}
|
|
52298
52409
|
`);
|
|
@@ -52659,26 +52770,6 @@ function probeAvailableReactions(chatId) {
|
|
|
52659
52770
|
}
|
|
52660
52771
|
})();
|
|
52661
52772
|
}
|
|
52662
|
-
var pendingHandoffTopic = null;
|
|
52663
|
-
function initHandoffContinuity() {
|
|
52664
|
-
if (!shouldShowHandoffLine()) {
|
|
52665
|
-
pendingHandoffTopic = null;
|
|
52666
|
-
return;
|
|
52667
|
-
}
|
|
52668
|
-
const agentDir = resolveAgentDirFromEnv();
|
|
52669
|
-
if (agentDir == null) {
|
|
52670
|
-
pendingHandoffTopic = null;
|
|
52671
|
-
return;
|
|
52672
|
-
}
|
|
52673
|
-
pendingHandoffTopic = consumeHandoffTopic(agentDir);
|
|
52674
|
-
}
|
|
52675
|
-
function takeHandoffPrefix(format) {
|
|
52676
|
-
if (pendingHandoffTopic == null)
|
|
52677
|
-
return "";
|
|
52678
|
-
const line = formatHandoffLine(pendingHandoffTopic, format);
|
|
52679
|
-
pendingHandoffTopic = null;
|
|
52680
|
-
return line;
|
|
52681
|
-
}
|
|
52682
52773
|
var PHOTO_EXTS = new Set([".jpg", ".jpeg", ".png", ".gif", ".webp"]);
|
|
52683
52774
|
function chunk2(text, limit, mode) {
|
|
52684
52775
|
if (text.length <= limit)
|
|
@@ -52703,7 +52794,7 @@ function chunk2(text, limit, mode) {
|
|
|
52703
52794
|
out.push(rest);
|
|
52704
52795
|
return out;
|
|
52705
52796
|
}
|
|
52706
|
-
function
|
|
52797
|
+
function escapeMarkdownV2(text) {
|
|
52707
52798
|
const specialChars = /[_*\[\]()~`>#+\-=|{}.!\\]/g;
|
|
52708
52799
|
const parts = [];
|
|
52709
52800
|
let last = 0;
|
|
@@ -53118,7 +53209,7 @@ function looksLikeAuthCode(text) {
|
|
|
53118
53209
|
}
|
|
53119
53210
|
var bufferedAttachmentKeys = new Map;
|
|
53120
53211
|
function coalesceMaxAttachments() {
|
|
53121
|
-
return
|
|
53212
|
+
return resolveCoalesceMaxAttachments(loadAccess().coalesceMaxAttachments);
|
|
53122
53213
|
}
|
|
53123
53214
|
var inboundCoalescer = createInboundCoalescer({
|
|
53124
53215
|
gapMs: () => loadAccess().coalescingGapMs ?? 500,
|
|
@@ -53248,11 +53339,11 @@ var unpinProgressCardForChat = null;
|
|
|
53248
53339
|
var getPinnedProgressCardMessageId = null;
|
|
53249
53340
|
var completeProgressCardTurn = null;
|
|
53250
53341
|
var subagentWatcher = null;
|
|
53251
|
-
var SOCKET_PATH = process.env.SWITCHROOM_GATEWAY_SOCKET ??
|
|
53342
|
+
var SOCKET_PATH = process.env.SWITCHROOM_GATEWAY_SOCKET ?? join35(STATE_DIR, "gateway.sock");
|
|
53252
53343
|
mkdirSync26(STATE_DIR, { recursive: true, mode: 448 });
|
|
53253
|
-
var GATEWAY_PID_PATH = process.env.SWITCHROOM_GATEWAY_PID_FILE ??
|
|
53254
|
-
var GATEWAY_SESSION_MARKER_PATH = process.env.SWITCHROOM_GATEWAY_SESSION_MARKER ??
|
|
53255
|
-
var GATEWAY_CLEAN_SHUTDOWN_MARKER_PATH = process.env.SWITCHROOM_GATEWAY_CLEAN_SHUTDOWN_MARKER ??
|
|
53344
|
+
var GATEWAY_PID_PATH = process.env.SWITCHROOM_GATEWAY_PID_FILE ?? join35(STATE_DIR, "gateway.pid.json");
|
|
53345
|
+
var GATEWAY_SESSION_MARKER_PATH = process.env.SWITCHROOM_GATEWAY_SESSION_MARKER ?? join35(STATE_DIR, "gateway-session.json");
|
|
53346
|
+
var GATEWAY_CLEAN_SHUTDOWN_MARKER_PATH = process.env.SWITCHROOM_GATEWAY_CLEAN_SHUTDOWN_MARKER ?? join35(STATE_DIR, "clean-shutdown.json");
|
|
53256
53347
|
var GATEWAY_STARTED_AT_MS = Date.now();
|
|
53257
53348
|
var BOOT_CARD_ENABLED = process.env.SWITCHROOM_BOOT_CARD !== "false";
|
|
53258
53349
|
var activeBootCard = null;
|
|
@@ -53281,7 +53372,7 @@ function ensureIssuesCard(chatId, threadId) {
|
|
|
53281
53372
|
bot: botApi,
|
|
53282
53373
|
log: (msg) => process.stderr.write(`telegram gateway: ${msg}
|
|
53283
53374
|
`),
|
|
53284
|
-
persistPath:
|
|
53375
|
+
persistPath: join35(stateDir, "issues-card.json")
|
|
53285
53376
|
});
|
|
53286
53377
|
activeIssuesWatcher = startIssuesWatcher({
|
|
53287
53378
|
stateDir,
|
|
@@ -53440,17 +53531,24 @@ startTimer2({
|
|
|
53440
53531
|
}
|
|
53441
53532
|
});
|
|
53442
53533
|
var inboundSpool = STATIC ? undefined : createInboundSpool({
|
|
53443
|
-
path:
|
|
53534
|
+
path: join35(STATE_DIR, "inbound-spool.jsonl"),
|
|
53444
53535
|
fs: {
|
|
53445
53536
|
appendFileSync: (p, d) => appendFileSync5(p, d),
|
|
53446
|
-
readFileSync: (p) =>
|
|
53447
|
-
writeFileSync: (p, d) =>
|
|
53448
|
-
renameSync: (a, b) =>
|
|
53449
|
-
existsSync: (p) =>
|
|
53537
|
+
readFileSync: (p) => readFileSync35(p, "utf8"),
|
|
53538
|
+
writeFileSync: (p, d) => writeFileSync24(p, d),
|
|
53539
|
+
renameSync: (a, b) => renameSync12(a, b),
|
|
53540
|
+
existsSync: (p) => existsSync38(p),
|
|
53450
53541
|
statSizeSync: (p) => statSync13(p).size
|
|
53451
53542
|
}
|
|
53452
53543
|
});
|
|
53453
53544
|
var pendingInboundBuffer = createPendingInboundBuffer({ spool: inboundSpool });
|
|
53545
|
+
if (bootResumeInbound != null) {
|
|
53546
|
+
if (inboundSpool != null) {
|
|
53547
|
+
inboundSpool.put(bootResumeInbound.agent, bootResumeInbound.msg);
|
|
53548
|
+
} else {
|
|
53549
|
+
pendingInboundBuffer.push(bootResumeInbound.agent, bootResumeInbound.msg);
|
|
53550
|
+
}
|
|
53551
|
+
}
|
|
53454
53552
|
if (inboundSpool != null) {
|
|
53455
53553
|
const replay = inboundSpool.liveEntries();
|
|
53456
53554
|
for (const e of replay)
|
|
@@ -53579,7 +53677,7 @@ var ipcServer = createIpcServer({
|
|
|
53579
53677
|
probeQuotaViaBroker: (t) => probeQuotaForBootCard(agentSlug, t),
|
|
53580
53678
|
tmuxSupervisor: process.env.SWITCHROOM_TMUX_SUPERVISOR === "1",
|
|
53581
53679
|
dockerMode: process.env.SWITCHROOM_RUNTIME === "docker",
|
|
53582
|
-
configSnapshotPath:
|
|
53680
|
+
configSnapshotPath: join35(resolvedAgentDirForCard, ".config-snapshot.json"),
|
|
53583
53681
|
...updateOutcomeLine ? { updateOutcomeLine } : {}
|
|
53584
53682
|
}, ackMsgId).then((handle) => {
|
|
53585
53683
|
activeBootCard = handle;
|
|
@@ -54001,7 +54099,7 @@ var ipcServer = createIpcServer({
|
|
|
54001
54099
|
const receiverUid = receiverUidRaw ? Number(receiverUidRaw) : NaN;
|
|
54002
54100
|
if (Number.isInteger(receiverUid))
|
|
54003
54101
|
allowedUids.push(receiverUid);
|
|
54004
|
-
const socketPath =
|
|
54102
|
+
const socketPath = join35(STATE_DIR, "webhook.sock");
|
|
54005
54103
|
const webhookInject = (agentName3, inbound) => {
|
|
54006
54104
|
const msg = inbound;
|
|
54007
54105
|
const delivered = ipcServer.sendToAgent(agentName3, msg);
|
|
@@ -54251,16 +54349,11 @@ ${url}`;
|
|
|
54251
54349
|
effectiveText = markdownToHtml(text);
|
|
54252
54350
|
} else if (format === "markdownv2") {
|
|
54253
54351
|
parseMode = "MarkdownV2";
|
|
54254
|
-
effectiveText =
|
|
54352
|
+
effectiveText = escapeMarkdownV2(text);
|
|
54255
54353
|
} else {
|
|
54256
54354
|
parseMode = undefined;
|
|
54257
54355
|
effectiveText = text;
|
|
54258
54356
|
}
|
|
54259
|
-
{
|
|
54260
|
-
const prefix = takeHandoffPrefix(format === "html" ? "html" : format === "markdownv2" ? "markdownv2" : "text");
|
|
54261
|
-
if (prefix.length > 0)
|
|
54262
|
-
effectiveText = prefix + effectiveText;
|
|
54263
|
-
}
|
|
54264
54357
|
assertAllowedChat(chat_id);
|
|
54265
54358
|
let threadId = resolveThreadId(chat_id, args.message_thread_id);
|
|
54266
54359
|
if (reply_to == null && quoteOptIn && HISTORY_ENABLED) {
|
|
@@ -54673,9 +54766,8 @@ async function executeStreamReply(args) {
|
|
|
54673
54766
|
bot: lockedBot,
|
|
54674
54767
|
retry: robustApiCall,
|
|
54675
54768
|
markdownToHtml,
|
|
54676
|
-
escapeMarkdownV2
|
|
54769
|
+
escapeMarkdownV2,
|
|
54677
54770
|
repairEscapedWhitespace,
|
|
54678
|
-
takeHandoffPrefix,
|
|
54679
54771
|
assertAllowedChat,
|
|
54680
54772
|
resolveThreadId,
|
|
54681
54773
|
disableLinkPreview: access.disableLinkPreview !== false,
|
|
@@ -54705,8 +54797,8 @@ async function executeStreamReply(args) {
|
|
|
54705
54797
|
progressDriver?.recordOutboundDelivered(args.chat_id, args.message_thread_id);
|
|
54706
54798
|
} catch {}
|
|
54707
54799
|
try {
|
|
54708
|
-
const
|
|
54709
|
-
noteSignal(statusKey(args.chat_id,
|
|
54800
|
+
const threadIdNum2 = args.message_thread_id != null ? Number(args.message_thread_id) : undefined;
|
|
54801
|
+
noteSignal(statusKey(args.chat_id, threadIdNum2), Date.now());
|
|
54710
54802
|
} catch {}
|
|
54711
54803
|
}
|
|
54712
54804
|
if (args.done === true && result.messageId != null && streamButtonMeta != null && streamButtonMeta.size > 0) {
|
|
@@ -54998,11 +55090,11 @@ async function executeSendGif(rawArgs) {
|
|
|
54998
55090
|
};
|
|
54999
55091
|
}
|
|
55000
55092
|
async function publishToTelegraph(text, shortName, authorName) {
|
|
55001
|
-
const accountPath =
|
|
55093
|
+
const accountPath = join35(STATE_DIR, "telegraph-account.json");
|
|
55002
55094
|
let account = null;
|
|
55003
55095
|
try {
|
|
55004
|
-
if (
|
|
55005
|
-
const raw =
|
|
55096
|
+
if (existsSync38(accountPath)) {
|
|
55097
|
+
const raw = readFileSync35(accountPath, "utf-8");
|
|
55006
55098
|
const parsed = JSON.parse(raw);
|
|
55007
55099
|
if (parsed.shortName && parsed.accessToken) {
|
|
55008
55100
|
account = parsed;
|
|
@@ -55022,7 +55114,7 @@ async function publishToTelegraph(text, shortName, authorName) {
|
|
|
55022
55114
|
account = created.value;
|
|
55023
55115
|
try {
|
|
55024
55116
|
mkdirSync26(STATE_DIR, { recursive: true, mode: 448 });
|
|
55025
|
-
|
|
55117
|
+
writeFileSync24(accountPath, JSON.stringify(account, null, 2), { mode: 384 });
|
|
55026
55118
|
} catch (err) {
|
|
55027
55119
|
process.stderr.write(`telegram gateway: telegraph cache write failed: ${err.message}
|
|
55028
55120
|
`);
|
|
@@ -55266,7 +55358,7 @@ async function executeDownloadAttachment(args) {
|
|
|
55266
55358
|
});
|
|
55267
55359
|
mkdirSync26(INBOX_DIR, { recursive: true, mode: 448 });
|
|
55268
55360
|
assertInsideInbox(INBOX_DIR, dlPath);
|
|
55269
|
-
|
|
55361
|
+
writeFileSync24(dlPath, buf, { mode: 384 });
|
|
55270
55362
|
return { content: [{ type: "text", text: dlPath }] };
|
|
55271
55363
|
}
|
|
55272
55364
|
async function executeEditMessage(args) {
|
|
@@ -55300,7 +55392,7 @@ async function executeEditMessage(args) {
|
|
|
55300
55392
|
editText = markdownToHtml(editRawText);
|
|
55301
55393
|
} else if (editFormat === "markdownv2") {
|
|
55302
55394
|
editParseMode = "MarkdownV2";
|
|
55303
|
-
editText =
|
|
55395
|
+
editText = escapeMarkdownV2(editRawText);
|
|
55304
55396
|
} else {
|
|
55305
55397
|
editParseMode = undefined;
|
|
55306
55398
|
editText = editRawText;
|
|
@@ -55487,6 +55579,14 @@ function closeProgressLane(chatId, threadId) {
|
|
|
55487
55579
|
}
|
|
55488
55580
|
}
|
|
55489
55581
|
}
|
|
55582
|
+
var FOREGROUND_SUBAGENT_ACCUM_MAX = 12;
|
|
55583
|
+
function composeTurnActivity(turn) {
|
|
55584
|
+
const childLines = [];
|
|
55585
|
+
for (const narrative of turn.foregroundSubAgents.values()) {
|
|
55586
|
+
childLines.push(...narrative);
|
|
55587
|
+
}
|
|
55588
|
+
return renderActivityFeedWithNested(turn.mirrorLines, childLines);
|
|
55589
|
+
}
|
|
55490
55590
|
async function drainActivitySummary(turn) {
|
|
55491
55591
|
try {
|
|
55492
55592
|
while (turn.activityPendingRender !== turn.activityLastSentRender) {
|
|
@@ -55579,6 +55679,7 @@ function handleSessionEvent(ev) {
|
|
|
55579
55679
|
activityPendingRender: null,
|
|
55580
55680
|
activityLastSentRender: null,
|
|
55581
55681
|
mirrorLines: [],
|
|
55682
|
+
foregroundSubAgents: new Map,
|
|
55582
55683
|
answerStream: null,
|
|
55583
55684
|
isDm: isDmChatId(ev.chatId)
|
|
55584
55685
|
};
|
|
@@ -55673,7 +55774,7 @@ function handleSessionEvent(ev) {
|
|
|
55673
55774
|
return;
|
|
55674
55775
|
const rendered = appendActivityLabel(turn.mirrorLines, ev.label);
|
|
55675
55776
|
if (rendered != null) {
|
|
55676
|
-
turn.activityPendingRender = rendered;
|
|
55777
|
+
turn.activityPendingRender = composeTurnActivity(turn) ?? rendered;
|
|
55677
55778
|
if (turn.activityInFlight == null) {
|
|
55678
55779
|
turn.activityInFlight = drainActivitySummary(turn);
|
|
55679
55780
|
}
|
|
@@ -56407,7 +56508,7 @@ async function handleInbound(ctx, text, downloadImage, attachment, extraAttachme
|
|
|
56407
56508
|
const agentName3 = process.env.SWITCHROOM_AGENT_NAME;
|
|
56408
56509
|
const access2 = loadAccess();
|
|
56409
56510
|
deferInterrupt = !interrupt.emptyBody && decideInterruptTiming({
|
|
56410
|
-
safeBoundaryEnabled: access2.interruptSafeBoundary
|
|
56511
|
+
safeBoundaryEnabled: resolveSafeBoundaryEnabled(access2.interruptSafeBoundary),
|
|
56411
56512
|
midToolCall: toolFlightTracker.isMidToolCall()
|
|
56412
56513
|
}) === "defer";
|
|
56413
56514
|
process.stderr.write(`telegram gateway: interrupt-marker received chat_id=${chat_id} agent=${agentName3 ?? "-"} body_len=${interrupt.body.length} empty=${interrupt.emptyBody} defer=${deferInterrupt} in_flight=${toolFlightTracker.inFlightCount()}
|
|
@@ -57096,14 +57197,14 @@ function restartMarkerPath() {
|
|
|
57096
57197
|
const agentDir = resolveAgentDirFromEnv();
|
|
57097
57198
|
if (!agentDir)
|
|
57098
57199
|
return null;
|
|
57099
|
-
return
|
|
57200
|
+
return join35(agentDir, "restart-pending.json");
|
|
57100
57201
|
}
|
|
57101
57202
|
function writeRestartMarker(marker) {
|
|
57102
57203
|
const p = restartMarkerPath();
|
|
57103
57204
|
if (!p)
|
|
57104
57205
|
return;
|
|
57105
57206
|
try {
|
|
57106
|
-
|
|
57207
|
+
writeFileSync24(p, JSON.stringify(marker));
|
|
57107
57208
|
lastPlannedRestartAt = Date.now();
|
|
57108
57209
|
process.stderr.write(`telegram gateway: restart-marker: write chat_id=${marker.chat_id} thread_id=${marker.thread_id ?? "-"} ack=${marker.ack_message_id ?? "-"} path=${p}
|
|
57109
57210
|
`);
|
|
@@ -57122,7 +57223,7 @@ function readRestartMarker() {
|
|
|
57122
57223
|
if (!p)
|
|
57123
57224
|
return null;
|
|
57124
57225
|
try {
|
|
57125
|
-
return JSON.parse(
|
|
57226
|
+
return JSON.parse(readFileSync35(p, "utf8"));
|
|
57126
57227
|
} catch {
|
|
57127
57228
|
return null;
|
|
57128
57229
|
}
|
|
@@ -57220,7 +57321,7 @@ var _dockerReachable;
|
|
|
57220
57321
|
function isDockerReachable() {
|
|
57221
57322
|
if (_dockerReachable !== undefined)
|
|
57222
57323
|
return _dockerReachable;
|
|
57223
|
-
if (!
|
|
57324
|
+
if (!existsSync38("/var/run/docker.sock")) {
|
|
57224
57325
|
_dockerReachable = false;
|
|
57225
57326
|
return _dockerReachable;
|
|
57226
57327
|
}
|
|
@@ -57237,12 +57338,12 @@ function _resetDockerReachableCache() {
|
|
|
57237
57338
|
}
|
|
57238
57339
|
function spawnSwitchroomDetached(args, onFailure) {
|
|
57239
57340
|
const fullArgs = SWITCHROOM_CONFIG ? ["--config", SWITCHROOM_CONFIG, ...args] : args;
|
|
57240
|
-
const logPath =
|
|
57341
|
+
const logPath = join35(STATE_DIR, "detached-spawn.log");
|
|
57241
57342
|
let outFd = null;
|
|
57242
57343
|
try {
|
|
57243
57344
|
mkdirSync26(STATE_DIR, { recursive: true });
|
|
57244
57345
|
outFd = openSync8(logPath, "a");
|
|
57245
|
-
|
|
57346
|
+
writeFileSync24(logPath, `
|
|
57246
57347
|
[${new Date().toISOString()}] spawn ${SWITCHROOM_CLI} ${fullArgs.join(" ")}
|
|
57247
57348
|
`, { flag: "a" });
|
|
57248
57349
|
} catch {}
|
|
@@ -57268,7 +57369,7 @@ function spawnSwitchroomDetached(args, onFailure) {
|
|
|
57268
57369
|
return;
|
|
57269
57370
|
let tail = "";
|
|
57270
57371
|
try {
|
|
57271
|
-
const full =
|
|
57372
|
+
const full = readFileSync35(logPath, "utf8");
|
|
57272
57373
|
tail = full.split(`
|
|
57273
57374
|
`).slice(-30).join(`
|
|
57274
57375
|
`).trim();
|
|
@@ -57610,10 +57711,10 @@ bot.use(async (ctx, next) => {
|
|
|
57610
57711
|
});
|
|
57611
57712
|
function readRecentDenialsForAgent(agentName3, windowMs, limit) {
|
|
57612
57713
|
try {
|
|
57613
|
-
const auditPath =
|
|
57614
|
-
if (!
|
|
57714
|
+
const auditPath = join35(homedir14(), ".switchroom", "vault-audit.log");
|
|
57715
|
+
if (!existsSync38(auditPath))
|
|
57615
57716
|
return [];
|
|
57616
|
-
const raw =
|
|
57717
|
+
const raw = readFileSync35(auditPath, "utf8");
|
|
57617
57718
|
return recentDenialsFromAuditLog(raw, { agentName: agentName3, windowMs, limit });
|
|
57618
57719
|
} catch {
|
|
57619
57720
|
return [];
|
|
@@ -57664,7 +57765,7 @@ async function buildAgentMetadata(agentName3) {
|
|
|
57664
57765
|
try {
|
|
57665
57766
|
const agentDir = resolveAgentDirFromEnv();
|
|
57666
57767
|
if (agentDir) {
|
|
57667
|
-
const raw =
|
|
57768
|
+
const raw = readFileSync35(join35(agentDir, ".claude", ".claude.json"), "utf8");
|
|
57668
57769
|
claudeJson = JSON.parse(raw);
|
|
57669
57770
|
}
|
|
57670
57771
|
} catch {}
|
|
@@ -57878,10 +57979,10 @@ bot.command("restart", async (ctx) => {
|
|
|
57878
57979
|
function flushAgentHandoff(agentDir) {
|
|
57879
57980
|
let removed = 0;
|
|
57880
57981
|
for (const fname of [".handoff.md", ".handoff-topic"]) {
|
|
57881
|
-
const p =
|
|
57982
|
+
const p = join35(agentDir, fname);
|
|
57882
57983
|
try {
|
|
57883
|
-
if (
|
|
57884
|
-
|
|
57984
|
+
if (existsSync38(p)) {
|
|
57985
|
+
unlinkSync14(p);
|
|
57885
57986
|
removed++;
|
|
57886
57987
|
}
|
|
57887
57988
|
} catch (err) {
|
|
@@ -57936,7 +58037,7 @@ async function handleNewOrResetCommand(ctx, kind) {
|
|
|
57936
58037
|
writeRestartMarker({ chat_id: chatId, thread_id: threadId ?? null, ack_message_id: ackId, ts: Date.now() });
|
|
57937
58038
|
if (agentDir != null) {
|
|
57938
58039
|
try {
|
|
57939
|
-
|
|
58040
|
+
writeFileSync24(join35(agentDir, ".force-fresh-session"), `${kind} at ${new Date().toISOString()}
|
|
57940
58041
|
`, "utf8");
|
|
57941
58042
|
} catch (err) {
|
|
57942
58043
|
process.stderr.write(`telegram gateway: failed to write force-fresh marker: ${err}
|
|
@@ -58296,16 +58397,16 @@ bot.command("interrupt", async (ctx) => {
|
|
|
58296
58397
|
await runSwitchroomCommand(ctx, ["agent", "interrupt", name], `interrupt ${name}`);
|
|
58297
58398
|
});
|
|
58298
58399
|
var lockoutOps = {
|
|
58299
|
-
readFileSync: (p, enc) =>
|
|
58300
|
-
writeFileSync: (p, data, opts) =>
|
|
58301
|
-
existsSync: (p) =>
|
|
58400
|
+
readFileSync: (p, enc) => readFileSync35(p, enc),
|
|
58401
|
+
writeFileSync: (p, data, opts) => writeFileSync24(p, data, opts),
|
|
58402
|
+
existsSync: (p) => existsSync38(p),
|
|
58302
58403
|
mkdirSync: (p, opts) => mkdirSync26(p, opts),
|
|
58303
|
-
joinPath: (...parts) =>
|
|
58404
|
+
joinPath: (...parts) => join35(...parts)
|
|
58304
58405
|
};
|
|
58305
58406
|
var FLEET_FALLBACK_DEDUP_MS = 30000;
|
|
58306
58407
|
function isAuthBrokerSocketReachable() {
|
|
58307
58408
|
try {
|
|
58308
|
-
return
|
|
58409
|
+
return existsSync38(resolveAuthBrokerSocketPath2());
|
|
58309
58410
|
} catch {
|
|
58310
58411
|
return false;
|
|
58311
58412
|
}
|
|
@@ -58366,7 +58467,7 @@ async function runCreditWatch() {
|
|
|
58366
58467
|
if (!agentDir)
|
|
58367
58468
|
return;
|
|
58368
58469
|
const agentName3 = getMyAgentName();
|
|
58369
|
-
const claudeConfigDir =
|
|
58470
|
+
const claudeConfigDir = join35(agentDir, ".claude");
|
|
58370
58471
|
const stateDir = STATE_DIR;
|
|
58371
58472
|
const reason = readClaudeJsonOverage(claudeConfigDir);
|
|
58372
58473
|
const prev = loadCreditState(stateDir);
|
|
@@ -58669,10 +58770,10 @@ async function handleVaultRecentDenialCallback(ctx, data) {
|
|
|
58669
58770
|
return;
|
|
58670
58771
|
}
|
|
58671
58772
|
const { token, id } = result;
|
|
58672
|
-
const tokenPath =
|
|
58773
|
+
const tokenPath = join35(homedir14(), ".switchroom", "agents", agentName3, ".vault-token");
|
|
58673
58774
|
try {
|
|
58674
|
-
mkdirSync26(
|
|
58675
|
-
|
|
58775
|
+
mkdirSync26(join35(homedir14(), ".switchroom", "agents", agentName3), { recursive: true });
|
|
58776
|
+
writeFileSync24(tokenPath, token, { mode: 384 });
|
|
58676
58777
|
} catch (err) {
|
|
58677
58778
|
await switchroomReply(ctx, `<b>Grant created (${escapeHtmlForTg(id)}) but token write failed:</b> ${escapeHtmlForTg(String(err))}
|
|
58678
58779
|
<i>Recover with: <code>switchroom vault grant ${escapeHtmlForTg(agentName3)} --keys ${escapeHtmlForTg(keyName)} --duration 30d</code> on the host.</i>`, { html: true });
|
|
@@ -58748,10 +58849,10 @@ async function performVaultAccessApproval(ctx, pending2, stageId, senderId, atte
|
|
|
58748
58849
|
return;
|
|
58749
58850
|
}
|
|
58750
58851
|
const { token, id } = result;
|
|
58751
|
-
const tokenPath =
|
|
58852
|
+
const tokenPath = join35(homedir14(), ".switchroom", "agents", pending2.agent, ".vault-token");
|
|
58752
58853
|
try {
|
|
58753
|
-
mkdirSync26(
|
|
58754
|
-
|
|
58854
|
+
mkdirSync26(join35(homedir14(), ".switchroom", "agents", pending2.agent), { recursive: true });
|
|
58855
|
+
writeFileSync24(tokenPath, token, { mode: 384 });
|
|
58755
58856
|
} catch (err) {
|
|
58756
58857
|
await switchroomReply(ctx, `<b>Grant created (${escapeHtmlForTg(id)}) but token write failed:</b> ${escapeHtmlForTg(String(err))}
|
|
58757
58858
|
<i>Recover with: <code>switchroom vault grant ${escapeHtmlForTg(pending2.agent)} --keys ${escapeHtmlForTg(pending2.key)} --duration ${Math.round(pending2.ttl_seconds / 86400)}d</code> on the host.</i>`, { html: true });
|
|
@@ -59226,10 +59327,10 @@ async function executeGrantWizard(ctx, chatId, state4) {
|
|
|
59226
59327
|
return;
|
|
59227
59328
|
}
|
|
59228
59329
|
const { token, id } = result;
|
|
59229
|
-
const tokenPath =
|
|
59330
|
+
const tokenPath = join35(homedir14(), ".switchroom", "agents", state4.agent, ".vault-token");
|
|
59230
59331
|
try {
|
|
59231
|
-
mkdirSync26(
|
|
59232
|
-
|
|
59332
|
+
mkdirSync26(join35(homedir14(), ".switchroom", "agents", state4.agent), { recursive: true });
|
|
59333
|
+
writeFileSync24(tokenPath, token, { mode: 384 });
|
|
59233
59334
|
} catch (err) {
|
|
59234
59335
|
await switchroomReply(ctx, `<b>Grant created but token write failed:</b> ${escapeHtmlForTg(String(err))}`, { html: true });
|
|
59235
59336
|
return;
|
|
@@ -60077,7 +60178,7 @@ bot.command("usage", async (ctx) => {
|
|
|
60077
60178
|
await switchroomReply(ctx, "<b>/usage:</b> cannot resolve agent dir.", { html: true });
|
|
60078
60179
|
return;
|
|
60079
60180
|
}
|
|
60080
|
-
const result = await fetchQuota2({ claudeConfigDir:
|
|
60181
|
+
const result = await fetchQuota2({ claudeConfigDir: join35(agentDir, ".claude") });
|
|
60081
60182
|
if (!result.ok) {
|
|
60082
60183
|
await switchroomReply(ctx, `<b>/usage:</b> ${escapeHtmlForTg(result.reason)}`, { html: true });
|
|
60083
60184
|
return;
|
|
@@ -60517,7 +60618,7 @@ ${preBlock(formatSwitchroomOutput(err.message ?? "unknown error"))}`, { html: tr
|
|
|
60517
60618
|
const unifiedDiff = (() => {
|
|
60518
60619
|
try {
|
|
60519
60620
|
const cfgPath = process.env.SWITCHROOM_CONFIG ?? SWITCHROOM_CONFIG ?? findConfigFile2();
|
|
60520
|
-
const raw =
|
|
60621
|
+
const raw = readFileSync35(cfgPath, "utf8");
|
|
60521
60622
|
return synthesizeAllowRuleDiff({ agentName: agentName3, rule: chosen.rule, configText: raw });
|
|
60522
60623
|
} catch (err) {
|
|
60523
60624
|
process.stderr.write(`telegram gateway: always-allow diff synth failed: ${err.message}
|
|
@@ -60659,7 +60760,7 @@ bot.on("message:photo", async (ctx) => {
|
|
|
60659
60760
|
});
|
|
60660
60761
|
mkdirSync26(INBOX_DIR, { recursive: true, mode: 448 });
|
|
60661
60762
|
assertInsideInbox(INBOX_DIR, dlPath);
|
|
60662
|
-
|
|
60763
|
+
writeFileSync24(dlPath, buf, { mode: 384 });
|
|
60663
60764
|
return dlPath;
|
|
60664
60765
|
} catch (err) {
|
|
60665
60766
|
const msg = err instanceof Error ? err.message : "unknown error";
|
|
@@ -60699,8 +60800,8 @@ async function maybeTranscribeVoice(fileId, mimeType, language) {
|
|
|
60699
60800
|
let apiKey = null;
|
|
60700
60801
|
try {
|
|
60701
60802
|
const path = __require("path").join(__require("os").homedir(), ".switchroom", "openai-api-key");
|
|
60702
|
-
if (
|
|
60703
|
-
apiKey =
|
|
60803
|
+
if (existsSync38(path)) {
|
|
60804
|
+
apiKey = readFileSync35(path, "utf-8").trim();
|
|
60704
60805
|
}
|
|
60705
60806
|
} catch (err) {
|
|
60706
60807
|
process.stderr.write(`telegram gateway: voice-in: failed to read api key: ${err.message}
|
|
@@ -61335,7 +61436,6 @@ process.on("SIGINT", () => void shutdown("SIGINT"));
|
|
|
61335
61436
|
`) });
|
|
61336
61437
|
}
|
|
61337
61438
|
}
|
|
61338
|
-
initHandoffContinuity();
|
|
61339
61439
|
process.on("unhandledRejection", (err) => {
|
|
61340
61440
|
const action = classifyRejection(err);
|
|
61341
61441
|
process.stderr.write(`telegram gateway: unhandled rejection (${action}): ${err}
|
|
@@ -61556,7 +61656,7 @@ var didOneTimeSetup = false;
|
|
|
61556
61656
|
return;
|
|
61557
61657
|
}
|
|
61558
61658
|
})();
|
|
61559
|
-
const resolvedAgentDirForBootCard = agentDir ??
|
|
61659
|
+
const resolvedAgentDirForBootCard = agentDir ?? join35(homedir14(), ".switchroom", "agents", agentSlug);
|
|
61560
61660
|
const handle = await startBootCard(chatId, threadId, botApiForCard, {
|
|
61561
61661
|
agentName: agentDisplayName,
|
|
61562
61662
|
agentSlug,
|
|
@@ -61570,7 +61670,7 @@ var didOneTimeSetup = false;
|
|
|
61570
61670
|
probeQuotaViaBroker: (t) => probeQuotaForBootCard(agentSlug, t),
|
|
61571
61671
|
tmuxSupervisor: process.env.SWITCHROOM_TMUX_SUPERVISOR === "1",
|
|
61572
61672
|
dockerMode: process.env.SWITCHROOM_RUNTIME === "docker",
|
|
61573
|
-
configSnapshotPath:
|
|
61673
|
+
configSnapshotPath: join35(resolvedAgentDirForBootCard, ".config-snapshot.json"),
|
|
61574
61674
|
...updateOutcomeLine ? { updateOutcomeLine } : {}
|
|
61575
61675
|
}, ackMsgId);
|
|
61576
61676
|
activeBootCard = handle;
|
|
@@ -61654,7 +61754,8 @@ var didOneTimeSetup = false;
|
|
|
61654
61754
|
if (streamMode === "checklist") {
|
|
61655
61755
|
const watcherAgentDir = resolveAgentDirFromEnv();
|
|
61656
61756
|
if (watcherAgentDir != null) {
|
|
61657
|
-
const workerFeedEnabled = process.env.SWITCHROOM_WORKER_ACTIVITY_FEED
|
|
61757
|
+
const workerFeedEnabled = isWorkerActivityFeedEnabled(process.env.SWITCHROOM_WORKER_ACTIVITY_FEED);
|
|
61758
|
+
const foregroundNestingEnabled = process.env.SWITCHROOM_FOREGROUND_SUBAGENT_NESTING !== "0";
|
|
61658
61759
|
const workerActivityFeed = createWorkerActivityFeed({
|
|
61659
61760
|
bot: {
|
|
61660
61761
|
sendMessage: async (cid, text, sendOpts) => {
|
|
@@ -61714,6 +61815,19 @@ var didOneTimeSetup = false;
|
|
|
61714
61815
|
} catch {}
|
|
61715
61816
|
}
|
|
61716
61817
|
const isBackground = dispatch.isBackground;
|
|
61818
|
+
if (!isBackground) {
|
|
61819
|
+
const turn = currentTurn;
|
|
61820
|
+
if (turn != null && turn.foregroundSubAgents.delete(agentId) && !turn.replyCalled) {
|
|
61821
|
+
const rendered = composeTurnActivity(turn);
|
|
61822
|
+
if (rendered != null) {
|
|
61823
|
+
turn.activityPendingRender = rendered;
|
|
61824
|
+
if (turn.activityInFlight == null) {
|
|
61825
|
+
turn.activityInFlight = drainActivitySummary(turn);
|
|
61826
|
+
}
|
|
61827
|
+
}
|
|
61828
|
+
}
|
|
61829
|
+
return;
|
|
61830
|
+
}
|
|
61717
61831
|
if (workerFeedEnabled) {
|
|
61718
61832
|
workerActivityFeed.finish(agentId, {
|
|
61719
61833
|
description: dispatch.feedDescription,
|
|
@@ -61774,8 +61888,35 @@ var didOneTimeSetup = false;
|
|
|
61774
61888
|
} catch {}
|
|
61775
61889
|
}
|
|
61776
61890
|
const isBackground = dispatch.isBackground;
|
|
61777
|
-
if (!isBackground)
|
|
61891
|
+
if (!isBackground) {
|
|
61892
|
+
if (!foregroundNestingEnabled)
|
|
61893
|
+
return;
|
|
61894
|
+
const turn = currentTurn;
|
|
61895
|
+
if (turn == null || turn.replyCalled)
|
|
61896
|
+
return;
|
|
61897
|
+
const child = latestSummary.trim().slice(0, 120);
|
|
61898
|
+
if (child.length === 0)
|
|
61899
|
+
return;
|
|
61900
|
+
let narrative = turn.foregroundSubAgents.get(agentId);
|
|
61901
|
+
if (narrative == null) {
|
|
61902
|
+
narrative = [];
|
|
61903
|
+
turn.foregroundSubAgents.set(agentId, narrative);
|
|
61904
|
+
}
|
|
61905
|
+
if (narrative[narrative.length - 1] !== child) {
|
|
61906
|
+
narrative.push(child);
|
|
61907
|
+
if (narrative.length > FOREGROUND_SUBAGENT_ACCUM_MAX) {
|
|
61908
|
+
narrative.splice(0, narrative.length - FOREGROUND_SUBAGENT_ACCUM_MAX);
|
|
61909
|
+
}
|
|
61910
|
+
}
|
|
61911
|
+
const rendered = composeTurnActivity(turn);
|
|
61912
|
+
if (rendered != null) {
|
|
61913
|
+
turn.activityPendingRender = rendered;
|
|
61914
|
+
if (turn.activityInFlight == null) {
|
|
61915
|
+
turn.activityInFlight = drainActivitySummary(turn);
|
|
61916
|
+
}
|
|
61917
|
+
}
|
|
61778
61918
|
return;
|
|
61919
|
+
}
|
|
61779
61920
|
if (workerFeedEnabled) {
|
|
61780
61921
|
workerActivityFeed.update(agentId, fleetChatId || (loadAccess().allowFrom[0] ?? ""), {
|
|
61781
61922
|
description: dispatch.feedDescription,
|