switchroom 0.19.39 → 0.19.40
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 +10 -1
- package/dist/auth-broker/index.js +12 -3
- package/dist/cli/notion-write-pretool.mjs +10 -1
- package/dist/cli/switchroom.js +524 -189
- package/dist/host-control/main.js +217 -5
- package/dist/vault/approvals/kernel-server.js +12 -3
- package/dist/vault/broker/server.js +12 -3
- package/package.json +1 -1
- package/profiles/_base/start.sh.hbs +10 -0
- package/telegram-plugin/dist/bridge/bridge.js +8 -0
- package/telegram-plugin/dist/gateway/gateway.js +314 -191
- package/telegram-plugin/dist/server.js +8 -0
- package/telegram-plugin/gateway/backstop-delivery.ts +48 -0
- package/telegram-plugin/gateway/compaction-marker.ts +84 -0
- package/telegram-plugin/gateway/gateway.ts +3 -3
- package/telegram-plugin/gateway/liveness-wiring.ts +15 -0
- package/telegram-plugin/gateway/outbound-send-path.ts +20 -0
- package/telegram-plugin/gateway/outbox-sweep.ts +116 -18
- package/telegram-plugin/gateway/silence-poke-session-event.ts +13 -0
- package/telegram-plugin/gateway/stream-render.ts +39 -1
- package/telegram-plugin/hooks/compaction-marker-precompact.mjs +70 -0
- package/telegram-plugin/hooks/hooks.json +11 -0
- package/telegram-plugin/session-tail.ts +20 -0
- package/telegram-plugin/silence-poke.ts +28 -0
- package/telegram-plugin/tests/outbox-delivery.test.ts +38 -1
- package/telegram-plugin/tests/outbox-flush-ack-claim-race.test.ts +213 -0
- package/telegram-plugin/tests/outbox-reply-then-recap-e2e.test.ts +1 -1
- package/telegram-plugin/tests/outbox-sweep-flood-breaker.test.ts +4 -4
- package/telegram-plugin/tests/outbox-sweep-listen-button.test.ts +71 -8
- package/telegram-plugin/tests/send-reply-golden.test.ts +47 -0
- package/telegram-plugin/tests/silence-poke-compaction.test.ts +222 -0
|
@@ -21726,7 +21726,7 @@ var require_dist = __commonJS((exports) => {
|
|
|
21726
21726
|
});
|
|
21727
21727
|
|
|
21728
21728
|
// ../src/memory/observation-scopes.ts
|
|
21729
|
-
var OBSERVATION_SCOPES;
|
|
21729
|
+
var OBSERVATION_SCOPES, OBSERVATION_SCOPE_STRATEGIES;
|
|
21730
21730
|
var init_observation_scopes = __esm(() => {
|
|
21731
21731
|
OBSERVATION_SCOPES = [
|
|
21732
21732
|
"per_tag",
|
|
@@ -21734,10 +21734,16 @@ var init_observation_scopes = __esm(() => {
|
|
|
21734
21734
|
"all_combinations",
|
|
21735
21735
|
"shared"
|
|
21736
21736
|
];
|
|
21737
|
+
OBSERVATION_SCOPE_STRATEGIES = [
|
|
21738
|
+
"curated",
|
|
21739
|
+
"shared",
|
|
21740
|
+
"combined",
|
|
21741
|
+
"off"
|
|
21742
|
+
];
|
|
21737
21743
|
});
|
|
21738
21744
|
|
|
21739
21745
|
// ../src/config/schema.ts
|
|
21740
|
-
var CodeRepoEntrySchema, AgentBindMountSchema, HttpDiffPollSchema, PollSpecSchema, TelegramMessageActionSchema, WebhookActionSchema, ActionSpecSchema, ScheduleEntrySchema, AgentSoulSchema, AgentToolsSchema, ObservationScopesSchema, AgentMemorySchema, HookEntrySchema, AgentHooksSchema, SubagentSchema, SessionSchema, SessionContinuitySchema, webhookDispatchRule, TelegramChannelSchema, ChannelsSchema, TIMEZONE_REGEX, ApproverIdSchema, GoogleWorkspaceTierSchema, GoogleWorkspaceConfigSchema, LiteLLMConfigSchema, HindsightPerOpLlmSchema, HindsightConfigSchema, MicrosoftWorkspaceConfigSchema, NotionWorkspaceConfigSchema, AgentGoogleWorkspaceConfigSchema, MicrosoftAccountEmailSchema, MicrosoftToolTokenSchema, MicrosoftAccountBindingSchema, AgentMicrosoftWorkspaceConfigSchema, AgentNotionWorkspaceConfigSchema, ReactionsSchema, ReactionDispatchSchema, releaseBlockFields, ReleaseBlock, RootReleaseBlock, NetworkIsolationSchema, servesField, knowsField, profileFields, ProfileSchema, _omitExtends, defaultsFields, AgentDefaultsSchema, AgentSchema, TelegramConfigSchema, MemoryBackendConfigSchema, VaultConfigSchema, QuotaConfigSchema, AutoReleaseCheckSchema, HostControlConfigSchema, WebServiceConfigSchema, FleetHealthConfigSchema, HostdConfigSchema, CronEgressSchema, CronConfigSchema, UserSchema, SwitchroomConfigSchema;
|
|
21746
|
+
var CodeRepoEntrySchema, AgentBindMountSchema, HttpDiffPollSchema, PollSpecSchema, TelegramMessageActionSchema, WebhookActionSchema, ActionSpecSchema, ScheduleEntrySchema, AgentSoulSchema, AgentToolsSchema, ObservationScopesSchema, ObservationScopeStrategySchema, AgentMemorySchema, HookEntrySchema, AgentHooksSchema, SubagentSchema, SessionSchema, SessionContinuitySchema, webhookDispatchRule, TelegramChannelSchema, ChannelsSchema, TIMEZONE_REGEX, ApproverIdSchema, GoogleWorkspaceTierSchema, GoogleWorkspaceConfigSchema, LiteLLMConfigSchema, HindsightPerOpLlmSchema, HindsightConfigSchema, MicrosoftWorkspaceConfigSchema, NotionWorkspaceConfigSchema, AgentGoogleWorkspaceConfigSchema, MicrosoftAccountEmailSchema, MicrosoftToolTokenSchema, MicrosoftAccountBindingSchema, AgentMicrosoftWorkspaceConfigSchema, AgentNotionWorkspaceConfigSchema, ReactionsSchema, ReactionDispatchSchema, releaseBlockFields, ReleaseBlock, RootReleaseBlock, NetworkIsolationSchema, servesField, knowsField, profileFields, ProfileSchema, _omitExtends, defaultsFields, AgentDefaultsSchema, AgentSchema, TelegramConfigSchema, MemoryBackendConfigSchema, VaultConfigSchema, QuotaConfigSchema, AutoReleaseCheckSchema, HostControlConfigSchema, WebServiceConfigSchema, FleetHealthConfigSchema, HostdConfigSchema, CronEgressSchema, CronConfigSchema, UserSchema, SwitchroomConfigSchema;
|
|
21741
21747
|
var init_schema = __esm(() => {
|
|
21742
21748
|
init_zod();
|
|
21743
21749
|
init_observation_scopes();
|
|
@@ -21852,7 +21858,8 @@ var init_schema = __esm(() => {
|
|
|
21852
21858
|
allow: exports_external.array(exports_external.string()).default([]).describe("Allowed tools (use ['all'] for unrestricted)"),
|
|
21853
21859
|
deny: exports_external.array(exports_external.string()).default([]).describe("Denied tools (overrides allow)")
|
|
21854
21860
|
}).optional();
|
|
21855
|
-
ObservationScopesSchema = exports_external.enum(OBSERVATION_SCOPES).optional().describe("
|
|
21861
|
+
ObservationScopesSchema = exports_external.enum(OBSERVATION_SCOPES).optional().describe("Operator PIN for the per-row observation scope, overriding the " + 'default `curated` strategy (see docs/configuration.md). "shared" ' + "forces Hindsight's consolidation to write EVERY observation into ONE " + "global untagged scope \u2014 what several agents pooling one bank want so " + "their observations merge rather than sitting in parallel silos. " + '"combined" is the pin form of opting out (restores the pre-#4035 ' + "engine default). UNSET does NOT mean 'nothing on the wire': the " + "`curated` strategy still computes a scope per retain. Applies to " + "every retain path (Stop hook, " + "sidechain, boot reconcile, queue drain, backfill, session-handoff " + "mirror) and is carried on the queued payload, so a retain that fails " + "now and drains later still lands in this scope. Accepted values: " + `${OBSERVATION_SCOPES.join(", ")}. ` + "Cascade: override (per-agent wins over default).");
|
|
21862
|
+
ObservationScopeStrategySchema = exports_external.enum(OBSERVATION_SCOPE_STRATEGIES).optional().describe("Per-retain strategy that decides how the observation scope is " + 'computed when no `observation_scopes` pin is set. "curated" ' + "(the default since #4035) strips volatile per-session provenance " + "tags from each retain's consolidation scope \u2014 keeping the stable " + "semantic ones on the source fact \u2014 so an agent's observations dedup " + "and merge across sessions instead of pocketing one-per-session. " + '"shared" sends every retain to the one global untagged scope. ' + '"combined" / "off" opt OUT: no per-row scope goes on the wire, ' + "restoring the pre-feature engine default (byte-identical to an " + "unconfigured client). OMITTED BY DEFAULT: unset leaves the plugin's " + "own default (`curated`) in force. A manual `observation_scopes` pin " + "wins over this strategy. Accepted values: " + `${OBSERVATION_SCOPE_STRATEGIES.join(", ")}. ` + "Cascade: override (per-agent wins over default).");
|
|
21856
21863
|
AgentMemorySchema = exports_external.object({
|
|
21857
21864
|
collection: exports_external.string().describe("Hindsight collection name for this agent"),
|
|
21858
21865
|
auto_recall: exports_external.boolean().default(true).describe("Auto-search memories before each response"),
|
|
@@ -21888,6 +21895,7 @@ var init_schema = __esm(() => {
|
|
|
21888
21895
|
}).optional().describe("Personality traits (1-5 each) steering how this bank frames recall, " + "reflect, and observation synthesis \u2014 a coach leans empathy-high, a " + "lawyer/analyst leans skepticism/literalism-high. Maps to the engine's " + "flat `disposition_skepticism`/`_literalism`/`_empathy` fields. " + "Cascade: per-key merge (an agent overrides individual traits and " + "inherits the rest, matching `recall`)."),
|
|
21889
21896
|
directive_capture_nudge: exports_external.boolean().optional().describe("Deterministic directive-capture nudge (issue #2848 Stage B). When " + "on (switchroom default true \u2014 Stage A measured a ~55% miss rate on " + "durable corrections), the auto-recall hook regex-detects correction " + '/ standing-rule-shaped inbound ("always/never \u2026", "from now on \u2026", ' + `"stop doing \u2026", a stated preference, "that's wrong, it's \u2026") and ` + "appends a terse advisory to the turn's context telling the model to " + "persist the rule with mcp__hindsight__create_directive if it IS " + "durable. Detection is pure regex \u2014 the model does the judgment " + "in-session and calls create_directive itself (no model callsite, no " + "silent hook-side write). Set false to disable per-agent. " + "Cascade: override (per-agent wins over default)."),
|
|
21890
21897
|
observation_scopes: ObservationScopesSchema,
|
|
21898
|
+
observation_scope_strategy: ObservationScopeStrategySchema,
|
|
21891
21899
|
recall: exports_external.object({
|
|
21892
21900
|
max_memories: exports_external.number().int().min(0).optional().describe("Cap on the number of memories injected into the prompt by " + "auto-recall, regardless of token budget. Plugin default is 12. " + "0 disables the cap (all memories Hindsight returns are injected)."),
|
|
21893
21901
|
cache_ttl_secs: exports_external.number().int().min(0).optional().describe("Per-session recall cache TTL in seconds. When > 0, identical " + "(prompt, bank) within the same session reuse the cached recall " + "result instead of round-tripping to Hindsight. 0 disables. " + "Default is 600 (10 min) for switchroom-managed agents."),
|
|
@@ -22288,6 +22296,7 @@ var init_schema = __esm(() => {
|
|
|
22288
22296
|
isolation: exports_external.enum(["default", "strict"]).optional(),
|
|
22289
22297
|
directive_capture_nudge: exports_external.boolean().optional(),
|
|
22290
22298
|
observation_scopes: ObservationScopesSchema,
|
|
22299
|
+
observation_scope_strategy: ObservationScopeStrategySchema,
|
|
22291
22300
|
recall: exports_external.object({
|
|
22292
22301
|
max_memories: exports_external.number().int().min(0).optional(),
|
|
22293
22302
|
cache_ttl_secs: exports_external.number().int().min(0).optional(),
|
|
@@ -35875,9 +35884,9 @@ function renderAuthLine(state7, agentName3, now = Date.now()) {
|
|
|
35875
35884
|
|
|
35876
35885
|
// gateway/quota-cache.ts
|
|
35877
35886
|
import { existsSync as existsSync44, readFileSync as readFileSync44, writeFileSync as writeFileSync37, mkdirSync as mkdirSync39 } from "fs";
|
|
35878
|
-
import { join as
|
|
35887
|
+
import { join as join54, dirname as dirname22 } from "path";
|
|
35879
35888
|
function defaultCachePath() {
|
|
35880
|
-
return process.env.SWITCHROOM_QUOTA_CACHE_PATH ??
|
|
35889
|
+
return process.env.SWITCHROOM_QUOTA_CACHE_PATH ?? join54(process.env.HOME ?? "/tmp", ".switchroom", "quota-cache.json");
|
|
35881
35890
|
}
|
|
35882
35891
|
function readQuotaCache(opts = {}) {
|
|
35883
35892
|
const path3 = opts.path ?? defaultCachePath();
|
|
@@ -35924,7 +35933,7 @@ var init_quota_cache = __esm(() => {
|
|
|
35924
35933
|
|
|
35925
35934
|
// gateway/boot-probes.ts
|
|
35926
35935
|
import { readFileSync as readFileSync45, readdirSync as readdirSync12, existsSync as existsSync45 } from "fs";
|
|
35927
|
-
import { join as
|
|
35936
|
+
import { join as join55 } from "path";
|
|
35928
35937
|
import { execFile as execFileCb } from "child_process";
|
|
35929
35938
|
import { promisify } from "util";
|
|
35930
35939
|
async function withTimeout(label, p, timeoutMs = PROBE_TIMEOUT_MS) {
|
|
@@ -35966,8 +35975,8 @@ function mapPlan(billingType, hasExtra) {
|
|
|
35966
35975
|
}
|
|
35967
35976
|
async function probeAccount(agentDir) {
|
|
35968
35977
|
return withTimeout("Account", (async () => {
|
|
35969
|
-
const claudeDir =
|
|
35970
|
-
const claudeJsonPath =
|
|
35978
|
+
const claudeDir = join55(agentDir, ".claude");
|
|
35979
|
+
const claudeJsonPath = join55(claudeDir, ".claude.json");
|
|
35971
35980
|
let cfg = {};
|
|
35972
35981
|
try {
|
|
35973
35982
|
const raw = readFileSync45(claudeJsonPath, "utf8");
|
|
@@ -35988,8 +35997,8 @@ async function probeAccount(agentDir) {
|
|
|
35988
35997
|
let tokenStr = "";
|
|
35989
35998
|
let status = "ok";
|
|
35990
35999
|
for (const candidate of [
|
|
35991
|
-
|
|
35992
|
-
|
|
36000
|
+
join55(claudeDir, ".oauth-token.meta.json"),
|
|
36001
|
+
join55(claudeDir, "accounts", "default", ".oauth-token.meta.json")
|
|
35993
36002
|
]) {
|
|
35994
36003
|
if (existsSync45(candidate)) {
|
|
35995
36004
|
try {
|
|
@@ -36375,9 +36384,9 @@ async function probeQuota(claudeConfigDir, _agentDir, fetchImpl = fetch, opts =
|
|
|
36375
36384
|
let claudeDirForProbe = null;
|
|
36376
36385
|
for (const candidate of [
|
|
36377
36386
|
claudeConfigDir,
|
|
36378
|
-
|
|
36387
|
+
join55(claudeConfigDir, "accounts", "default")
|
|
36379
36388
|
]) {
|
|
36380
|
-
if (existsSync45(
|
|
36389
|
+
if (existsSync45(join55(candidate, ".oauth-token"))) {
|
|
36381
36390
|
claudeDirForProbe = candidate;
|
|
36382
36391
|
break;
|
|
36383
36392
|
}
|
|
@@ -36612,7 +36621,7 @@ async function probeSkills(agentDir, opts = {}) {
|
|
|
36612
36621
|
return withTimeout("Skills", (async () => {
|
|
36613
36622
|
const fs3 = opts.fs ?? realSkillsFs;
|
|
36614
36623
|
const max = opts.maxNamesShown ?? 3;
|
|
36615
|
-
const skillsDir =
|
|
36624
|
+
const skillsDir = join55(agentDir, ".claude", "skills");
|
|
36616
36625
|
if (!fs3.exists(skillsDir)) {
|
|
36617
36626
|
return { status: "ok", label: "Skills", detail: "no skills dir" };
|
|
36618
36627
|
}
|
|
@@ -36627,17 +36636,17 @@ async function probeSkills(agentDir, opts = {}) {
|
|
|
36627
36636
|
}
|
|
36628
36637
|
const dangling = [];
|
|
36629
36638
|
for (const name of entries) {
|
|
36630
|
-
const skillPath =
|
|
36639
|
+
const skillPath = join55(skillsDir, name);
|
|
36631
36640
|
if (!fs3.exists(skillPath)) {
|
|
36632
36641
|
dangling.push(name);
|
|
36633
36642
|
continue;
|
|
36634
36643
|
}
|
|
36635
|
-
const skillMd =
|
|
36644
|
+
const skillMd = join55(skillPath, "SKILL.md");
|
|
36636
36645
|
if (!fs3.exists(skillMd) && !fs3.exists(skillPath + ".md")) {
|
|
36637
36646
|
continue;
|
|
36638
36647
|
}
|
|
36639
36648
|
}
|
|
36640
|
-
const overlayDir = opts.overlaySkillsDir ??
|
|
36649
|
+
const overlayDir = opts.overlaySkillsDir ?? join55(agentDir, "skills.d");
|
|
36641
36650
|
const overlaySlugs = new Set;
|
|
36642
36651
|
if (fs3.exists(overlayDir)) {
|
|
36643
36652
|
let overlayEntries = [];
|
|
@@ -36679,7 +36688,7 @@ function renderBucketedSkills(switchroom, agent) {
|
|
|
36679
36688
|
}
|
|
36680
36689
|
async function probeConnections(agentDir, opts = {}) {
|
|
36681
36690
|
return withTimeout("Connections", (async () => {
|
|
36682
|
-
const path3 =
|
|
36691
|
+
const path3 = join55(agentDir, ".claude", "connection-health.json");
|
|
36683
36692
|
const read = opts.readFileImpl ?? ((p) => readFileSync45(p, "utf8"));
|
|
36684
36693
|
let issues = [];
|
|
36685
36694
|
try {
|
|
@@ -36720,7 +36729,7 @@ async function probeDrift(agentDir, opts = {}) {
|
|
|
36720
36729
|
}
|
|
36721
36730
|
} catch {}
|
|
36722
36731
|
try {
|
|
36723
|
-
const raw = readFileSync45(
|
|
36732
|
+
const raw = readFileSync45(join55(agentDir, ".switchroom-drift.json"), "utf8");
|
|
36724
36733
|
const report = JSON.parse(raw);
|
|
36725
36734
|
if (report?.version === 1 && Array.isArray(report.findings)) {
|
|
36726
36735
|
const gen = report.generatedAt ? Date.parse(report.generatedAt) : NaN;
|
|
@@ -36772,8 +36781,8 @@ var init_boot_probes = __esm(() => {
|
|
|
36772
36781
|
realSchedulerFs = {
|
|
36773
36782
|
readFile: (p) => readFileSync45(p, "utf-8"),
|
|
36774
36783
|
mtimeMs: (p) => {
|
|
36775
|
-
const { statSync:
|
|
36776
|
-
return
|
|
36784
|
+
const { statSync: statSync17 } = __require("fs");
|
|
36785
|
+
return statSync17(p).mtimeMs;
|
|
36777
36786
|
},
|
|
36778
36787
|
exists: (p) => existsSync45(p)
|
|
36779
36788
|
};
|
|
@@ -37080,8 +37089,8 @@ __export(exports_boot_card, {
|
|
|
37080
37089
|
readRoutingMode: () => readRoutingMode,
|
|
37081
37090
|
containerBootStartMs: () => containerBootStartMs
|
|
37082
37091
|
});
|
|
37083
|
-
import { join as
|
|
37084
|
-
import { readFileSync as readFileSync49, statSync as
|
|
37092
|
+
import { join as join56 } from "path";
|
|
37093
|
+
import { readFileSync as readFileSync49, statSync as statSync17 } from "fs";
|
|
37085
37094
|
function resolvePersonaName(slug, loadConfig3) {
|
|
37086
37095
|
try {
|
|
37087
37096
|
const config = loadConfig3 ? loadConfig3() : loadConfig();
|
|
@@ -37124,8 +37133,8 @@ function containerBootStartMs(fsImpl = { readFileSync: readFileSync49 }) {
|
|
|
37124
37133
|
return null;
|
|
37125
37134
|
}
|
|
37126
37135
|
}
|
|
37127
|
-
function readRoutingMode(agentDir, bootStartMs, fsImpl = { readFileSync: readFileSync49, statSync:
|
|
37128
|
-
const path3 =
|
|
37136
|
+
function readRoutingMode(agentDir, bootStartMs, fsImpl = { readFileSync: readFileSync49, statSync: statSync17 }) {
|
|
37137
|
+
const path3 = join56(agentDir, ".routing-mode");
|
|
37129
37138
|
try {
|
|
37130
37139
|
const raw = fsImpl.readFileSync(path3, "utf-8");
|
|
37131
37140
|
const line = raw.split(`
|
|
@@ -37232,7 +37241,7 @@ function renderBootCard(opts) {
|
|
|
37232
37241
|
return stackCardLines(flatLines);
|
|
37233
37242
|
}
|
|
37234
37243
|
async function runAllProbes(opts) {
|
|
37235
|
-
const claudeDir =
|
|
37244
|
+
const claudeDir = join56(opts.agentDir, ".claude");
|
|
37236
37245
|
const probes = {};
|
|
37237
37246
|
const slug = opts.agentSlug ?? opts.agentName;
|
|
37238
37247
|
await Promise.allSettled([
|
|
@@ -38094,7 +38103,7 @@ __export(exports_tmux2, {
|
|
|
38094
38103
|
captureAgentPane: () => captureAgentPane2
|
|
38095
38104
|
});
|
|
38096
38105
|
import { execFileSync as execFileSync8 } from "node:child_process";
|
|
38097
|
-
import { chmodSync as chmodSync13, mkdirSync as mkdirSync50, readdirSync as readdirSync15, statSync as
|
|
38106
|
+
import { chmodSync as chmodSync13, mkdirSync as mkdirSync50, readdirSync as readdirSync15, statSync as statSync22, unlinkSync as unlinkSync30, writeFileSync as writeFileSync49 } from "node:fs";
|
|
38098
38107
|
import { resolve as resolve12 } from "node:path";
|
|
38099
38108
|
function captureAgentPane2(opts) {
|
|
38100
38109
|
const { agentName: agentName3, agentDir, reason } = opts;
|
|
@@ -38223,13 +38232,13 @@ function pruneOldReports2(dir, retain) {
|
|
|
38223
38232
|
const full = resolve12(dir, n);
|
|
38224
38233
|
let mtimeMs = 0;
|
|
38225
38234
|
try {
|
|
38226
|
-
mtimeMs =
|
|
38235
|
+
mtimeMs = statSync22(full).mtimeMs;
|
|
38227
38236
|
} catch {}
|
|
38228
38237
|
return { full, mtimeMs };
|
|
38229
38238
|
}).sort((a, b) => b.mtimeMs - a.mtimeMs);
|
|
38230
38239
|
for (const stale of files.slice(retain)) {
|
|
38231
38240
|
try {
|
|
38232
|
-
|
|
38241
|
+
unlinkSync30(stale.full);
|
|
38233
38242
|
} catch {}
|
|
38234
38243
|
}
|
|
38235
38244
|
}
|
|
@@ -39203,14 +39212,14 @@ import {
|
|
|
39203
39212
|
mkdirSync as mkdirSync51,
|
|
39204
39213
|
readdirSync as readdirSync16,
|
|
39205
39214
|
rmSync as rmSync8,
|
|
39206
|
-
statSync as
|
|
39215
|
+
statSync as statSync23,
|
|
39207
39216
|
renameSync as renameSync27,
|
|
39208
39217
|
realpathSync as realpathSync4,
|
|
39209
39218
|
chmodSync as chmodSync14,
|
|
39210
39219
|
openSync as openSync15,
|
|
39211
39220
|
closeSync as closeSync15,
|
|
39212
39221
|
existsSync as existsSync57,
|
|
39213
|
-
unlinkSync as
|
|
39222
|
+
unlinkSync as unlinkSync31,
|
|
39214
39223
|
appendFileSync as appendFileSync9
|
|
39215
39224
|
} from "fs";
|
|
39216
39225
|
|
|
@@ -39264,8 +39273,8 @@ function fsyncPathSync(path) {
|
|
|
39264
39273
|
}
|
|
39265
39274
|
|
|
39266
39275
|
// gateway/gateway.ts
|
|
39267
|
-
import { homedir as
|
|
39268
|
-
import { join as
|
|
39276
|
+
import { homedir as homedir20 } from "os";
|
|
39277
|
+
import { join as join69, sep as sep4, basename as basename17 } from "path";
|
|
39269
39278
|
|
|
39270
39279
|
// plugin-logger.ts
|
|
39271
39280
|
import { appendFileSync, mkdirSync, renameSync as renameSync2, statSync, existsSync } from "fs";
|
|
@@ -70372,6 +70381,8 @@ function tick(now) {
|
|
|
70372
70381
|
const ceiling = thresholds.fallbackHardCeiling ?? Number.POSITIVE_INFINITY;
|
|
70373
70382
|
const underCeiling = silence < ceiling;
|
|
70374
70383
|
if (underCeiling) {
|
|
70384
|
+
if (activeDeps.isCompactionInFlight?.(key) === true)
|
|
70385
|
+
continue;
|
|
70375
70386
|
const forceDisable = process.env.SWITCHROOM_SILENCE_DEFER_INFLIGHT_TOOLS === "0";
|
|
70376
70387
|
if (!forceDisable && activeDeps.isLegitimatelyWorking != null) {
|
|
70377
70388
|
if (activeDeps.isLegitimatelyWorking(key))
|
|
@@ -71596,6 +71607,14 @@ function projectTranscriptLine(line) {
|
|
|
71596
71607
|
{ kind: "turn_end", durationMs: obj.durationMs ?? 0 }
|
|
71597
71608
|
];
|
|
71598
71609
|
}
|
|
71610
|
+
if (type === "system" && obj.subtype === "compact_boundary") {
|
|
71611
|
+
const meta = obj.compactMetadata;
|
|
71612
|
+
return [{
|
|
71613
|
+
kind: "compact_boundary",
|
|
71614
|
+
trigger: typeof meta?.trigger === "string" ? meta.trigger : null,
|
|
71615
|
+
compactDurationMs: typeof meta?.durationMs === "number" ? meta.durationMs : null
|
|
71616
|
+
}];
|
|
71617
|
+
}
|
|
71599
71618
|
return [];
|
|
71600
71619
|
}
|
|
71601
71620
|
|
|
@@ -77127,6 +77146,10 @@ function compactIfLarge(path2) {
|
|
|
77127
77146
|
} catch {}
|
|
77128
77147
|
}
|
|
77129
77148
|
|
|
77149
|
+
// gateway/outbox-sweep.ts
|
|
77150
|
+
init_rich_send();
|
|
77151
|
+
init_format();
|
|
77152
|
+
|
|
77130
77153
|
// registry/subagents-schema.ts
|
|
77131
77154
|
function countRunningBackgroundSubagents(db2) {
|
|
77132
77155
|
const row = db2.prepare("SELECT count(*) AS n FROM subagents WHERE background = 1 AND status = 'running'").get();
|
|
@@ -78928,13 +78951,28 @@ async function deliverCapturedProse(deps, args) {
|
|
|
78928
78951
|
const plainChunks = splitMarkdownChunks(plain, RICH_MESSAGE_MAX_CHARS);
|
|
78929
78952
|
try {
|
|
78930
78953
|
let liveThreadId = threadId;
|
|
78954
|
+
const plainSentIds = [];
|
|
78955
|
+
const plainSentTexts = [];
|
|
78931
78956
|
for (const c of plainChunks) {
|
|
78932
78957
|
const sent = await retryWithThreadFallback(robustApiCall, (tid) => bot.api.sendMessage(chatId, c, tid != null ? { message_thread_id: tid } : {}), { threadId: liveThreadId, chat_id: chatId, verb: "captured-prose-plain-fallback.sendMessage" });
|
|
78958
|
+
const sentId = sent.message_id;
|
|
78959
|
+
if (sentId != null) {
|
|
78960
|
+
plainSentIds.push(sentId);
|
|
78961
|
+
plainSentTexts.push(c);
|
|
78962
|
+
}
|
|
78933
78963
|
if (liveThreadId != null && sent.message_thread_id == null) {
|
|
78934
78964
|
liveThreadId = undefined;
|
|
78935
78965
|
}
|
|
78936
78966
|
}
|
|
78937
78967
|
outboundDedup.record(chatId, threadId, text4, Date.now(), registryKey);
|
|
78968
|
+
if (HISTORY_ENABLED && plainSentIds.length > 0) {
|
|
78969
|
+
try {
|
|
78970
|
+
recordOutbound2({ chat_id: chatId, thread_id: threadId ?? null, message_ids: plainSentIds, texts: plainSentTexts });
|
|
78971
|
+
} catch (histErr) {
|
|
78972
|
+
process.stderr.write(`telegram gateway: history recordOutbound (captured-prose plain fallback) failed: ${histErr instanceof Error ? histErr.message : String(histErr)}
|
|
78973
|
+
`);
|
|
78974
|
+
}
|
|
78975
|
+
}
|
|
78938
78976
|
process.stderr.write(`telegram gateway: captured-prose recovered via plain-text fallback ` + `(chat=${chatId} origin=${originTurnId})
|
|
78939
78977
|
`);
|
|
78940
78978
|
} catch (plainErr) {
|
|
@@ -81016,6 +81054,7 @@ function handleSessionEvent(deps, ev) {
|
|
|
81016
81054
|
let sentIds = [];
|
|
81017
81055
|
let chunkCount = 0;
|
|
81018
81056
|
let delivered = false;
|
|
81057
|
+
let earlyJournaled = false;
|
|
81019
81058
|
try {
|
|
81020
81059
|
const delivery = await deliverAnswer({
|
|
81021
81060
|
chatId: backstopChatId,
|
|
@@ -81023,7 +81062,21 @@ function handleSessionEvent(deps, ev) {
|
|
|
81023
81062
|
text: capturedText,
|
|
81024
81063
|
turnId: turn.turnId,
|
|
81025
81064
|
cardMessageId: backstopCardMessageId,
|
|
81026
|
-
replyToMessageId: turn.sourceMessageId
|
|
81065
|
+
replyToMessageId: turn.sourceMessageId,
|
|
81066
|
+
onAckClaim: (ackIds) => {
|
|
81067
|
+
try {
|
|
81068
|
+
journalExternalDelivery({
|
|
81069
|
+
turnNonce: turn.turnId,
|
|
81070
|
+
text: capturedText,
|
|
81071
|
+
tgMessageId: ackIds.length > 0 ? ackIds[0] : undefined,
|
|
81072
|
+
deliverySource: "flush"
|
|
81073
|
+
}, STATE_DIR);
|
|
81074
|
+
earlyJournaled = true;
|
|
81075
|
+
} catch (err) {
|
|
81076
|
+
process.stderr.write(`telegram gateway: turn-flush send-ack journal write failed (non-fatal): ${err.message}
|
|
81077
|
+
`);
|
|
81078
|
+
}
|
|
81079
|
+
}
|
|
81027
81080
|
});
|
|
81028
81081
|
sentIds = delivery.sentIds;
|
|
81029
81082
|
chunkCount = delivery.chunkCount;
|
|
@@ -81069,7 +81122,7 @@ function handleSessionEvent(deps, ev) {
|
|
|
81069
81122
|
chunkCount,
|
|
81070
81123
|
cardMessageId: backstopCardMessageId
|
|
81071
81124
|
});
|
|
81072
|
-
if (delivered) {
|
|
81125
|
+
if (delivered && !earlyJournaled) {
|
|
81073
81126
|
try {
|
|
81074
81127
|
journalExternalDelivery({
|
|
81075
81128
|
turnNonce: turn.turnId,
|
|
@@ -91852,6 +91905,7 @@ async function runBackstopDelivery(ledger, turnId, chunks, cardMessageId, deps,
|
|
|
91852
91905
|
const stderr = deps.stderr ?? (() => {});
|
|
91853
91906
|
const chunkCount = chunks.length;
|
|
91854
91907
|
let attempts = 0;
|
|
91908
|
+
let ackClaimed = false;
|
|
91855
91909
|
for (let attempt = 1;attempt <= Math.max(1, maxAttempts); attempt++) {
|
|
91856
91910
|
attempts = attempt;
|
|
91857
91911
|
const resume = ledger.unsentIndices(turnId, chunkCount);
|
|
@@ -91875,6 +91929,18 @@ async function runBackstopDelivery(ledger, turnId, chunks, cardMessageId, deps,
|
|
|
91875
91929
|
`);
|
|
91876
91930
|
}
|
|
91877
91931
|
}
|
|
91932
|
+
if (!ackClaimed && deps.onAckClaim != null) {
|
|
91933
|
+
const allLandedNow = chunkCount > 0 && ledger.unsentIndices(turnId, chunkCount).length === 0;
|
|
91934
|
+
if (allLandedNow && backstopReceiptIds(ledger.sentIds(turnId), cardMessageId).length > 0) {
|
|
91935
|
+
ackClaimed = true;
|
|
91936
|
+
try {
|
|
91937
|
+
deps.onAckClaim(ledger.sentIds(turnId));
|
|
91938
|
+
} catch (err) {
|
|
91939
|
+
stderr(`telegram gateway: backstop send-ack claim callback threw for turn ${turnId} ` + `(non-fatal): ${err instanceof Error ? err.message : String(err)}
|
|
91940
|
+
`);
|
|
91941
|
+
}
|
|
91942
|
+
}
|
|
91943
|
+
}
|
|
91878
91944
|
let demotedAny = false;
|
|
91879
91945
|
for (const i of ledger.landedUnconfirmedIndices(turnId, chunkCount)) {
|
|
91880
91946
|
if (deps.readBack == null) {
|
|
@@ -93239,6 +93305,28 @@ function purgeStaleTurnsForChat(chatId, keys, purger, isStale = () => true) {
|
|
|
93239
93305
|
return { purged };
|
|
93240
93306
|
}
|
|
93241
93307
|
|
|
93308
|
+
// gateway/compaction-marker.ts
|
|
93309
|
+
import { statSync as statSync15, unlinkSync as unlinkSync21 } from "node:fs";
|
|
93310
|
+
import { homedir as homedir15 } from "node:os";
|
|
93311
|
+
import { join as join50 } from "node:path";
|
|
93312
|
+
var COMPACTION_MARKER_FILE = "compaction-in-flight.json";
|
|
93313
|
+
function resolveTelegramStateDir() {
|
|
93314
|
+
return process.env.TELEGRAM_STATE_DIR ?? join50(homedir15(), ".claude", "channels", "telegram");
|
|
93315
|
+
}
|
|
93316
|
+
function readCompactionMarkerAgeMs(stateDir, now) {
|
|
93317
|
+
try {
|
|
93318
|
+
const st = statSync15(join50(stateDir, COMPACTION_MARKER_FILE));
|
|
93319
|
+
return (now ?? Date.now()) - st.mtimeMs;
|
|
93320
|
+
} catch {
|
|
93321
|
+
return null;
|
|
93322
|
+
}
|
|
93323
|
+
}
|
|
93324
|
+
function removeCompactionMarker(stateDir) {
|
|
93325
|
+
try {
|
|
93326
|
+
unlinkSync21(join50(stateDir, COMPACTION_MARKER_FILE));
|
|
93327
|
+
} catch {}
|
|
93328
|
+
}
|
|
93329
|
+
|
|
93242
93330
|
// gateway/hang-restart-decision.ts
|
|
93243
93331
|
var HUMAN_WAIT_TOOLS2 = new Set(["ask_user"]);
|
|
93244
93332
|
var BACKGROUND_TOOLS2 = new Set(["Task", "Agent"]);
|
|
@@ -93346,6 +93434,10 @@ function buildSilencePokeOptions(deps) {
|
|
|
93346
93434
|
thresholdsMs: { fallback: SILENCE_FALLBACK_MS, fallbackHardCeiling: SILENCE_FALLBACK_HARD_MS, floor: SILENCE_FLOOR_MS },
|
|
93347
93435
|
deferFallbackWhileToolInFlight: SILENCE_DEFER_INFLIGHT_TOOLS,
|
|
93348
93436
|
isLegitimatelyWorking: (key) => isLegitimatelyWorking(key),
|
|
93437
|
+
isCompactionInFlight: () => {
|
|
93438
|
+
const ageMs = readCompactionMarkerAgeMs(STATE_DIR);
|
|
93439
|
+
return ageMs != null && ageMs >= 0 && ageMs < SILENCE_FALLBACK_HARD_MS;
|
|
93440
|
+
},
|
|
93349
93441
|
isTurnLive: (key) => !(activeTurnStartedAt.get(key) == null && getCurrentTurnForKey(key) == null),
|
|
93350
93442
|
emitMetric: (event) => {
|
|
93351
93443
|
emitRuntimeMetric2(event);
|
|
@@ -93769,6 +93861,9 @@ function applySilencePokeSessionEvent(silencePoke, pendingProgress, key, ev) {
|
|
|
93769
93861
|
if (ev.toolUseId != null && ev.toolUseId.length > 0) {
|
|
93770
93862
|
silencePoke.noteToolEnd(key, ev.toolUseId, Date.now());
|
|
93771
93863
|
}
|
|
93864
|
+
} else if (ev.kind === "compact_boundary") {
|
|
93865
|
+
removeCompactionMarker(resolveTelegramStateDir());
|
|
93866
|
+
silencePoke.noteProduction(key, Date.now());
|
|
93772
93867
|
}
|
|
93773
93868
|
applyBackgroundShellLiveness(silencePoke, key, ev);
|
|
93774
93869
|
}
|
|
@@ -94054,17 +94149,17 @@ import {
|
|
|
94054
94149
|
readFileSync as readFileSync37,
|
|
94055
94150
|
writeSync as writeSync6
|
|
94056
94151
|
} from "node:fs";
|
|
94057
|
-
import { join as
|
|
94152
|
+
import { join as join51 } from "node:path";
|
|
94058
94153
|
import { randomUUID as randomUUID5 } from "node:crypto";
|
|
94059
94154
|
var PROPOSALS_FILE2 = "skill-proposals.jsonl";
|
|
94060
94155
|
var REJECTED_FILE2 = "skill-proposals-rejected.jsonl";
|
|
94061
94156
|
var REJECTION_TTL_MS2 = 90 * 24 * 60 * 60 * 1000;
|
|
94062
94157
|
var PROPOSAL_SIM_THRESHOLD = 0.5;
|
|
94063
94158
|
function proposalsPath2(stateDir) {
|
|
94064
|
-
return
|
|
94159
|
+
return join51(stateDir, PROPOSALS_FILE2);
|
|
94065
94160
|
}
|
|
94066
94161
|
function rejectedPath2(stateDir) {
|
|
94067
|
-
return
|
|
94162
|
+
return join51(stateDir, REJECTED_FILE2);
|
|
94068
94163
|
}
|
|
94069
94164
|
function ensureDir3(stateDir) {
|
|
94070
94165
|
if (!existsSync41(stateDir)) {
|
|
@@ -94682,7 +94777,7 @@ function escapeBody2(s) {
|
|
|
94682
94777
|
}
|
|
94683
94778
|
|
|
94684
94779
|
// gateway/pid-file.ts
|
|
94685
|
-
import { writeFileSync as writeFileSync33, readFileSync as readFileSync38, unlinkSync as
|
|
94780
|
+
import { writeFileSync as writeFileSync33, readFileSync as readFileSync38, unlinkSync as unlinkSync22, renameSync as renameSync18 } from "node:fs";
|
|
94686
94781
|
function writePidFile(path3, record) {
|
|
94687
94782
|
const tmp = `${path3}.tmp-${process.pid}-${Date.now()}`;
|
|
94688
94783
|
writeFileSync33(tmp, JSON.stringify(record), "utf-8");
|
|
@@ -94690,7 +94785,7 @@ function writePidFile(path3, record) {
|
|
|
94690
94785
|
}
|
|
94691
94786
|
function clearPidFile(path3) {
|
|
94692
94787
|
try {
|
|
94693
|
-
|
|
94788
|
+
unlinkSync22(path3);
|
|
94694
94789
|
} catch {}
|
|
94695
94790
|
}
|
|
94696
94791
|
|
|
@@ -94907,7 +95002,7 @@ function safeCount(fn) {
|
|
|
94907
95002
|
}
|
|
94908
95003
|
|
|
94909
95004
|
// gateway/session-marker.ts
|
|
94910
|
-
import { writeFileSync as writeFileSync34, readFileSync as readFileSync40, renameSync as renameSync19, unlinkSync as
|
|
95005
|
+
import { writeFileSync as writeFileSync34, readFileSync as readFileSync40, renameSync as renameSync19, unlinkSync as unlinkSync23 } from "node:fs";
|
|
94911
95006
|
function writeSessionMarker(path3, marker) {
|
|
94912
95007
|
const tmp = `${path3}.tmp-${process.pid}-${Date.now()}`;
|
|
94913
95008
|
writeFileSync34(tmp, JSON.stringify(marker), "utf-8");
|
|
@@ -94937,7 +95032,7 @@ function shouldFireRestartBanner(input) {
|
|
|
94937
95032
|
}
|
|
94938
95033
|
|
|
94939
95034
|
// gateway/clean-shutdown-marker.ts
|
|
94940
|
-
import { writeFileSync as writeFileSync35, readFileSync as readFileSync41, renameSync as renameSync20, unlinkSync as
|
|
95035
|
+
import { writeFileSync as writeFileSync35, readFileSync as readFileSync41, renameSync as renameSync20, unlinkSync as unlinkSync24 } from "node:fs";
|
|
94941
95036
|
var DEFAULT_MAX_AGE_MS = 60000;
|
|
94942
95037
|
function writeCleanShutdownMarker(path3, marker) {
|
|
94943
95038
|
const tmp = `${path3}.tmp-${process.pid}-${Date.now()}`;
|
|
@@ -94961,7 +95056,7 @@ function readCleanShutdownMarker(path3) {
|
|
|
94961
95056
|
}
|
|
94962
95057
|
function clearCleanShutdownMarker(path3) {
|
|
94963
95058
|
try {
|
|
94964
|
-
|
|
95059
|
+
unlinkSync24(path3);
|
|
94965
95060
|
} catch {}
|
|
94966
95061
|
}
|
|
94967
95062
|
function shouldSuppressRecoveryBanner(marker, now, maxAgeMs = DEFAULT_MAX_AGE_MS) {
|
|
@@ -95321,13 +95416,13 @@ import {
|
|
|
95321
95416
|
existsSync as existsSync42,
|
|
95322
95417
|
openSync as openSync10,
|
|
95323
95418
|
readSync as readSync2,
|
|
95324
|
-
statSync as
|
|
95419
|
+
statSync as statSync16,
|
|
95325
95420
|
closeSync as closeSync10,
|
|
95326
95421
|
watch,
|
|
95327
95422
|
readdirSync as readdirSync10,
|
|
95328
95423
|
readFileSync as readFileSync42
|
|
95329
95424
|
} from "fs";
|
|
95330
|
-
import { join as
|
|
95425
|
+
import { join as join52 } from "path";
|
|
95331
95426
|
|
|
95332
95427
|
// session-tail.ts
|
|
95333
95428
|
function sanitizeCwdToProjectName2(cwd) {
|
|
@@ -96134,7 +96229,7 @@ function startSubagentWatcher(config) {
|
|
|
96134
96229
|
const fs3 = config.fs ?? {
|
|
96135
96230
|
existsSync: existsSync42,
|
|
96136
96231
|
readdirSync: readdirSync10,
|
|
96137
|
-
statSync:
|
|
96232
|
+
statSync: statSync16,
|
|
96138
96233
|
openSync: openSync10,
|
|
96139
96234
|
closeSync: closeSync10,
|
|
96140
96235
|
readSync: readSync2,
|
|
@@ -96650,8 +96745,8 @@ function startSubagentWatcher(config) {
|
|
|
96650
96745
|
if (stopped)
|
|
96651
96746
|
return;
|
|
96652
96747
|
pruneVanishedDirWatchers();
|
|
96653
|
-
const claudeHome =
|
|
96654
|
-
const projectsRoot =
|
|
96748
|
+
const claudeHome = join52(agentDir, ".claude");
|
|
96749
|
+
const projectsRoot = join52(claudeHome, "projects");
|
|
96655
96750
|
if (!fs3.existsSync(projectsRoot))
|
|
96656
96751
|
return;
|
|
96657
96752
|
let projectDirs;
|
|
@@ -96685,7 +96780,7 @@ function startSubagentWatcher(config) {
|
|
|
96685
96780
|
continue;
|
|
96686
96781
|
}
|
|
96687
96782
|
warnedForeignSlugs.delete(pDir);
|
|
96688
|
-
const projectPath =
|
|
96783
|
+
const projectPath = join52(projectsRoot, pDir);
|
|
96689
96784
|
let sessionDirs;
|
|
96690
96785
|
try {
|
|
96691
96786
|
sessionDirs = fs3.readdirSync(projectPath);
|
|
@@ -96695,7 +96790,7 @@ function startSubagentWatcher(config) {
|
|
|
96695
96790
|
for (const sDir of sessionDirs) {
|
|
96696
96791
|
if (sDir.endsWith(".jsonl"))
|
|
96697
96792
|
continue;
|
|
96698
|
-
const subagentsPath =
|
|
96793
|
+
const subagentsPath = join52(projectPath, sDir, "subagents");
|
|
96699
96794
|
if (!fs3.existsSync(subagentsPath))
|
|
96700
96795
|
continue;
|
|
96701
96796
|
const watchAndScan = (dirPath) => {
|
|
@@ -96704,7 +96799,7 @@ function startSubagentWatcher(config) {
|
|
|
96704
96799
|
const w = fs3.watch(dirPath, (_event, filename) => {
|
|
96705
96800
|
if (!filename || !filename.toString().startsWith("agent-") || !filename.toString().endsWith(".jsonl"))
|
|
96706
96801
|
return;
|
|
96707
|
-
const filePath =
|
|
96802
|
+
const filePath = join52(dirPath, filename.toString());
|
|
96708
96803
|
if (!knownFiles.has(filePath)) {
|
|
96709
96804
|
scanSubagentsDir(dirPath);
|
|
96710
96805
|
}
|
|
@@ -96718,7 +96813,7 @@ function startSubagentWatcher(config) {
|
|
|
96718
96813
|
scanSubagentsDir(dirPath);
|
|
96719
96814
|
};
|
|
96720
96815
|
watchAndScan(subagentsPath);
|
|
96721
|
-
const workflowsPath =
|
|
96816
|
+
const workflowsPath = join52(subagentsPath, "workflows");
|
|
96722
96817
|
if (fs3.existsSync(workflowsPath)) {
|
|
96723
96818
|
let wfDirs;
|
|
96724
96819
|
try {
|
|
@@ -96728,7 +96823,7 @@ function startSubagentWatcher(config) {
|
|
|
96728
96823
|
}
|
|
96729
96824
|
for (const wfDir of wfDirs) {
|
|
96730
96825
|
try {
|
|
96731
|
-
const wfPath =
|
|
96826
|
+
const wfPath = join52(workflowsPath, wfDir);
|
|
96732
96827
|
if (!fs3.statSync(wfPath).isDirectory())
|
|
96733
96828
|
continue;
|
|
96734
96829
|
watchAndScan(wfPath);
|
|
@@ -96748,7 +96843,7 @@ function startSubagentWatcher(config) {
|
|
|
96748
96843
|
for (const e of entries) {
|
|
96749
96844
|
if (!e.startsWith("agent-") || !e.endsWith(".jsonl"))
|
|
96750
96845
|
continue;
|
|
96751
|
-
const filePath =
|
|
96846
|
+
const filePath = join52(subagentsPath, e);
|
|
96752
96847
|
if (knownFiles.has(filePath))
|
|
96753
96848
|
continue;
|
|
96754
96849
|
const agentId = e.slice("agent-".length, -".jsonl".length);
|
|
@@ -96880,17 +96975,17 @@ import {
|
|
|
96880
96975
|
writeFileSync as writeFileSync36,
|
|
96881
96976
|
readFileSync as readFileSync43,
|
|
96882
96977
|
readdirSync as readdirSync11,
|
|
96883
|
-
unlinkSync as
|
|
96978
|
+
unlinkSync as unlinkSync25,
|
|
96884
96979
|
existsSync as existsSync43,
|
|
96885
96980
|
renameSync as renameSync21
|
|
96886
96981
|
} from "node:fs";
|
|
96887
|
-
import { join as
|
|
96888
|
-
import { homedir as
|
|
96982
|
+
import { join as join53, resolve as resolve10 } from "node:path";
|
|
96983
|
+
import { homedir as homedir16 } from "node:os";
|
|
96889
96984
|
function registryDir() {
|
|
96890
|
-
return resolve10(process.env.SWITCHROOM_WORKTREE_DIR ??
|
|
96985
|
+
return resolve10(process.env.SWITCHROOM_WORKTREE_DIR ?? join53(homedir16(), ".switchroom", "worktrees"));
|
|
96891
96986
|
}
|
|
96892
96987
|
function recordPath(id) {
|
|
96893
|
-
return
|
|
96988
|
+
return join53(registryDir(), `${id}.json`);
|
|
96894
96989
|
}
|
|
96895
96990
|
function ensureDir4() {
|
|
96896
96991
|
mkdirSync38(registryDir(), { recursive: true });
|
|
@@ -97066,19 +97161,19 @@ function determineRestartReason(opts) {
|
|
|
97066
97161
|
|
|
97067
97162
|
// gateway/update-announce.ts
|
|
97068
97163
|
import { existsSync as existsSync48, mkdirSync as mkdirSync42, openSync as openSync11, closeSync as closeSync11, readFileSync as readFileSync50 } from "node:fs";
|
|
97069
|
-
import { join as
|
|
97070
|
-
import { homedir as
|
|
97164
|
+
import { join as join58 } from "node:path";
|
|
97165
|
+
import { homedir as homedir18 } from "node:os";
|
|
97071
97166
|
|
|
97072
97167
|
// ../src/host-control/audit-reader.ts
|
|
97073
|
-
import { homedir as
|
|
97074
|
-
import { join as
|
|
97168
|
+
import { homedir as homedir17 } from "node:os";
|
|
97169
|
+
import { join as join57 } from "node:path";
|
|
97075
97170
|
|
|
97076
97171
|
// ../src/host-control/audit-rotation-config.ts
|
|
97077
97172
|
var DEFAULT_HOSTD_AUDIT_MAX_BYTES = 32 * 1024 * 1024;
|
|
97078
97173
|
|
|
97079
97174
|
// ../src/host-control/audit-reader.ts
|
|
97080
|
-
function defaultAuditLogPath(home2 =
|
|
97081
|
-
return
|
|
97175
|
+
function defaultAuditLogPath(home2 = homedir17()) {
|
|
97176
|
+
return join57(home2, ".switchroom", "host-control-audit.log");
|
|
97082
97177
|
}
|
|
97083
97178
|
var AUDIT_READ_WINDOW_BYTES = 4 * 1024 * 1024;
|
|
97084
97179
|
function parseAuditLine(line) {
|
|
@@ -97273,15 +97368,15 @@ function renderUpdateOutcomeLine(entry) {
|
|
|
97273
97368
|
`);
|
|
97274
97369
|
}
|
|
97275
97370
|
function claimUpdateAnnouncement(requestId, opts = {}) {
|
|
97276
|
-
const stateDir = opts.stateDir ?? process.env.TELEGRAM_STATE_DIR ??
|
|
97277
|
-
const dir =
|
|
97371
|
+
const stateDir = opts.stateDir ?? process.env.TELEGRAM_STATE_DIR ?? join58(homedir18(), ".switchroom");
|
|
97372
|
+
const dir = join58(stateDir, "update-announced");
|
|
97278
97373
|
try {
|
|
97279
97374
|
mkdirSync42(dir, { recursive: true });
|
|
97280
97375
|
} catch {
|
|
97281
97376
|
return false;
|
|
97282
97377
|
}
|
|
97283
97378
|
const safeId = requestId.replace(/[^A-Za-z0-9_.-]/g, "_").slice(0, 200);
|
|
97284
|
-
const path3 =
|
|
97379
|
+
const path3 = join58(dir, safeId);
|
|
97285
97380
|
try {
|
|
97286
97381
|
const fd = openSync11(path3, "wx");
|
|
97287
97382
|
closeSync11(fd);
|
|
@@ -97502,8 +97597,8 @@ function createIssuesCardHandle(opts) {
|
|
|
97502
97597
|
}
|
|
97503
97598
|
|
|
97504
97599
|
// issues-watcher.ts
|
|
97505
|
-
import { existsSync as existsSync50, statSync as
|
|
97506
|
-
import { join as
|
|
97600
|
+
import { existsSync as existsSync50, statSync as statSync19 } from "node:fs";
|
|
97601
|
+
import { join as join60 } from "node:path";
|
|
97507
97602
|
|
|
97508
97603
|
// ../src/issues/store.ts
|
|
97509
97604
|
import {
|
|
@@ -97514,12 +97609,12 @@ import {
|
|
|
97514
97609
|
readdirSync as readdirSync13,
|
|
97515
97610
|
readFileSync as readFileSync52,
|
|
97516
97611
|
renameSync as renameSync24,
|
|
97517
|
-
statSync as
|
|
97518
|
-
unlinkSync as
|
|
97612
|
+
statSync as statSync18,
|
|
97613
|
+
unlinkSync as unlinkSync26,
|
|
97519
97614
|
writeFileSync as writeFileSync42,
|
|
97520
97615
|
writeSync as writeSync7
|
|
97521
97616
|
} from "node:fs";
|
|
97522
|
-
import { join as
|
|
97617
|
+
import { join as join59 } from "node:path";
|
|
97523
97618
|
import { randomBytes as randomBytes11 } from "node:crypto";
|
|
97524
97619
|
import { execSync } from "node:child_process";
|
|
97525
97620
|
|
|
@@ -97538,7 +97633,7 @@ init_redact();
|
|
|
97538
97633
|
var ISSUES_FILE = "issues.jsonl";
|
|
97539
97634
|
var ISSUES_LOCK = "issues.lock";
|
|
97540
97635
|
function readAll(stateDir) {
|
|
97541
|
-
const path3 =
|
|
97636
|
+
const path3 = join59(stateDir, ISSUES_FILE);
|
|
97542
97637
|
if (!existsSync49(path3))
|
|
97543
97638
|
return [];
|
|
97544
97639
|
let raw;
|
|
@@ -97575,7 +97670,7 @@ function list2(stateDir, opts = {}) {
|
|
|
97575
97670
|
});
|
|
97576
97671
|
}
|
|
97577
97672
|
function resolve11(stateDir, fingerprint, nowFn = Date.now) {
|
|
97578
|
-
if (!existsSync49(
|
|
97673
|
+
if (!existsSync49(join59(stateDir, ISSUES_FILE)))
|
|
97579
97674
|
return 0;
|
|
97580
97675
|
return withLock(stateDir, () => {
|
|
97581
97676
|
const all2 = readAll(stateDir);
|
|
@@ -97593,7 +97688,7 @@ function resolve11(stateDir, fingerprint, nowFn = Date.now) {
|
|
|
97593
97688
|
});
|
|
97594
97689
|
}
|
|
97595
97690
|
function writeAll(stateDir, events) {
|
|
97596
|
-
const path3 =
|
|
97691
|
+
const path3 = join59(stateDir, ISSUES_FILE);
|
|
97597
97692
|
sweepOrphanTmpFiles(stateDir);
|
|
97598
97693
|
const tmp = `${path3}.tmp-${process.pid}-${randomBytes11(4).toString("hex")}`;
|
|
97599
97694
|
const body = events.length === 0 ? "" : events.map((e) => JSON.stringify(e)).join(`
|
|
@@ -97615,11 +97710,11 @@ function sweepOrphanTmpFiles(stateDir) {
|
|
|
97615
97710
|
for (const entry of entries) {
|
|
97616
97711
|
if (!entry.startsWith(TMP_PREFIX))
|
|
97617
97712
|
continue;
|
|
97618
|
-
const tmpPath2 =
|
|
97713
|
+
const tmpPath2 = join59(stateDir, entry);
|
|
97619
97714
|
try {
|
|
97620
|
-
const stat =
|
|
97715
|
+
const stat = statSync18(tmpPath2);
|
|
97621
97716
|
if (stat.mtimeMs < cutoff) {
|
|
97622
|
-
|
|
97717
|
+
unlinkSync26(tmpPath2);
|
|
97623
97718
|
}
|
|
97624
97719
|
} catch {}
|
|
97625
97720
|
}
|
|
@@ -97627,7 +97722,7 @@ function sweepOrphanTmpFiles(stateDir) {
|
|
|
97627
97722
|
var LOCK_RETRY_MS = 25;
|
|
97628
97723
|
var LOCK_TIMEOUT_MS = 1e4;
|
|
97629
97724
|
function withLock(stateDir, fn) {
|
|
97630
|
-
const lockPath =
|
|
97725
|
+
const lockPath = join59(stateDir, ISSUES_LOCK);
|
|
97631
97726
|
const startedAt = Date.now();
|
|
97632
97727
|
let fd = null;
|
|
97633
97728
|
while (fd === null) {
|
|
@@ -97655,7 +97750,7 @@ function withLock(stateDir, fn) {
|
|
|
97655
97750
|
closeSync12(fd);
|
|
97656
97751
|
} catch {}
|
|
97657
97752
|
try {
|
|
97658
|
-
|
|
97753
|
+
unlinkSync26(lockPath);
|
|
97659
97754
|
} catch {}
|
|
97660
97755
|
}
|
|
97661
97756
|
}
|
|
@@ -97669,13 +97764,13 @@ function tryStealStaleLock(lockPath) {
|
|
|
97669
97764
|
const pid = Number(pidStr);
|
|
97670
97765
|
if (!Number.isFinite(pid) || pid <= 0) {
|
|
97671
97766
|
try {
|
|
97672
|
-
|
|
97767
|
+
unlinkSync26(lockPath);
|
|
97673
97768
|
} catch {}
|
|
97674
97769
|
return true;
|
|
97675
97770
|
}
|
|
97676
97771
|
if (pid === process.pid) {
|
|
97677
97772
|
try {
|
|
97678
|
-
|
|
97773
|
+
unlinkSync26(lockPath);
|
|
97679
97774
|
} catch {}
|
|
97680
97775
|
return true;
|
|
97681
97776
|
}
|
|
@@ -97690,7 +97785,7 @@ function tryStealStaleLock(lockPath) {
|
|
|
97690
97785
|
return false;
|
|
97691
97786
|
}
|
|
97692
97787
|
try {
|
|
97693
|
-
|
|
97788
|
+
unlinkSync26(lockPath);
|
|
97694
97789
|
} catch {}
|
|
97695
97790
|
return true;
|
|
97696
97791
|
}
|
|
@@ -97712,7 +97807,7 @@ function isIssueEvent(v) {
|
|
|
97712
97807
|
// issues-watcher.ts
|
|
97713
97808
|
var DEFAULT_POLL_INTERVAL_MS2 = 2000;
|
|
97714
97809
|
function startIssuesWatcher(opts) {
|
|
97715
|
-
const path3 =
|
|
97810
|
+
const path3 = join60(opts.stateDir, ISSUES_FILE);
|
|
97716
97811
|
const log = opts.log ?? (() => {});
|
|
97717
97812
|
const intervalMs = opts.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS2;
|
|
97718
97813
|
const setIntervalFn = opts.setInterval ?? setInterval;
|
|
@@ -97763,7 +97858,7 @@ function defaultSignatureProvider(path3) {
|
|
|
97763
97858
|
if (!existsSync50(path3))
|
|
97764
97859
|
return null;
|
|
97765
97860
|
try {
|
|
97766
|
-
const stat =
|
|
97861
|
+
const stat = statSync19(path3);
|
|
97767
97862
|
return `${stat.mtimeMs}:${stat.size}`;
|
|
97768
97863
|
} catch {
|
|
97769
97864
|
return null;
|
|
@@ -98472,7 +98567,7 @@ function readBashCommand(inputPreview) {
|
|
|
98472
98567
|
|
|
98473
98568
|
// gateway/scoped-grant-store.ts
|
|
98474
98569
|
init_atomic();
|
|
98475
|
-
import { join as
|
|
98570
|
+
import { join as join61 } from "node:path";
|
|
98476
98571
|
|
|
98477
98572
|
// scoped-approval.ts
|
|
98478
98573
|
var SCOPED_APPROVAL_DEFAULT_TTL_MS2 = 30 * 60 * 1000;
|
|
@@ -98514,7 +98609,7 @@ function scopedGrantPersistEnabled(env = process.env) {
|
|
|
98514
98609
|
return env.SWITCHROOM_SCOPED_GRANT_PERSIST !== "0";
|
|
98515
98610
|
}
|
|
98516
98611
|
function createScopedGrantStore(stateDir, env = process.env, log = (l) => process.stderr.write(l)) {
|
|
98517
|
-
const filePath =
|
|
98612
|
+
const filePath = join61(stateDir, "scoped-grants.json");
|
|
98518
98613
|
const enabled7 = scopedGrantPersistEnabled(env);
|
|
98519
98614
|
let unreadable = false;
|
|
98520
98615
|
function read() {
|
|
@@ -98893,7 +98988,7 @@ function isDiffPreApproved(agentName3, unifiedDiff, deps) {
|
|
|
98893
98988
|
// credits-watch.ts
|
|
98894
98989
|
init_card_format();
|
|
98895
98990
|
import { readFileSync as readFileSync53, writeFileSync as writeFileSync43, existsSync as existsSync51, mkdirSync as mkdirSync44 } from "fs";
|
|
98896
|
-
import { join as
|
|
98991
|
+
import { join as join62 } from "path";
|
|
98897
98992
|
var STATE_FILE = "credits-watch.json";
|
|
98898
98993
|
var DEFAULT_CREDIT_FATAL_REASONS = new Set;
|
|
98899
98994
|
var KNOWN_CREDIT_REASONS = [
|
|
@@ -98915,7 +99010,7 @@ function emptyCreditState() {
|
|
|
98915
99010
|
return { lastNotifiedReason: null, lastNotifiedAt: 0 };
|
|
98916
99011
|
}
|
|
98917
99012
|
function readClaudeJsonOverage(claudeConfigDir) {
|
|
98918
|
-
const path3 =
|
|
99013
|
+
const path3 = join62(claudeConfigDir, ".claude.json");
|
|
98919
99014
|
if (!existsSync51(path3))
|
|
98920
99015
|
return null;
|
|
98921
99016
|
let raw;
|
|
@@ -98998,7 +99093,7 @@ function humanizeReason(reason) {
|
|
|
98998
99093
|
}
|
|
98999
99094
|
}
|
|
99000
99095
|
function loadCreditState(stateDir) {
|
|
99001
|
-
const path3 =
|
|
99096
|
+
const path3 = join62(stateDir, STATE_FILE);
|
|
99002
99097
|
if (!existsSync51(path3))
|
|
99003
99098
|
return emptyCreditState();
|
|
99004
99099
|
try {
|
|
@@ -99015,7 +99110,7 @@ function loadCreditState(stateDir) {
|
|
|
99015
99110
|
}
|
|
99016
99111
|
function saveCreditState(stateDir, state7) {
|
|
99017
99112
|
mkdirSync44(stateDir, { recursive: true });
|
|
99018
|
-
const path3 =
|
|
99113
|
+
const path3 = join62(stateDir, STATE_FILE);
|
|
99019
99114
|
writeFileSync43(path3, JSON.stringify(state7, null, 2) + `
|
|
99020
99115
|
`, { mode: 384 });
|
|
99021
99116
|
}
|
|
@@ -99024,7 +99119,7 @@ function saveCreditState(stateDir, state7) {
|
|
|
99024
99119
|
init_auth_snapshot_format();
|
|
99025
99120
|
init_card_format();
|
|
99026
99121
|
import { readFileSync as readFileSync54, writeFileSync as writeFileSync44, existsSync as existsSync52, mkdirSync as mkdirSync45 } from "fs";
|
|
99027
|
-
import { join as
|
|
99122
|
+
import { join as join63 } from "path";
|
|
99028
99123
|
var STATE_FILE2 = "quota-watch.json";
|
|
99029
99124
|
function emptyQuotaWatchState() {
|
|
99030
99125
|
return {};
|
|
@@ -99262,7 +99357,7 @@ function buildRecoveryMessage(agentName3, snap) {
|
|
|
99262
99357
|
`);
|
|
99263
99358
|
}
|
|
99264
99359
|
function loadQuotaWatchState(stateDir) {
|
|
99265
|
-
const path3 =
|
|
99360
|
+
const path3 = join63(stateDir, STATE_FILE2);
|
|
99266
99361
|
if (!existsSync52(path3))
|
|
99267
99362
|
return emptyQuotaWatchState();
|
|
99268
99363
|
try {
|
|
@@ -99284,7 +99379,7 @@ function loadQuotaWatchState(stateDir) {
|
|
|
99284
99379
|
}
|
|
99285
99380
|
function saveQuotaWatchState(stateDir, state7) {
|
|
99286
99381
|
mkdirSync45(stateDir, { recursive: true });
|
|
99287
|
-
const path3 =
|
|
99382
|
+
const path3 = join63(stateDir, STATE_FILE2);
|
|
99288
99383
|
writeFileSync44(path3, JSON.stringify(state7, null, 2) + `
|
|
99289
99384
|
`, { mode: 384 });
|
|
99290
99385
|
}
|
|
@@ -99337,27 +99432,27 @@ import {
|
|
|
99337
99432
|
mkdirSync as mkdirSync46,
|
|
99338
99433
|
openSync as openSync13,
|
|
99339
99434
|
readFileSync as readFileSync55,
|
|
99340
|
-
statSync as
|
|
99341
|
-
unlinkSync as
|
|
99435
|
+
statSync as statSync20,
|
|
99436
|
+
unlinkSync as unlinkSync27,
|
|
99342
99437
|
utimesSync as utimesSync2,
|
|
99343
99438
|
writeFileSync as writeFileSync45
|
|
99344
99439
|
} from "node:fs";
|
|
99345
|
-
import { join as
|
|
99440
|
+
import { join as join64 } from "node:path";
|
|
99346
99441
|
var TURN_ACTIVE_MARKER_FILE2 = "turn-active.json";
|
|
99347
99442
|
var TURN_ACTIVE_HARD_TTL_MS2 = 10 * 60000;
|
|
99348
99443
|
var TURN_ACTIVE_IDLE_SWEEP_MS = 60000;
|
|
99349
99444
|
function removeTurnActiveMarker2(stateDir) {
|
|
99350
99445
|
try {
|
|
99351
|
-
|
|
99446
|
+
unlinkSync27(join64(stateDir, TURN_ACTIVE_MARKER_FILE2));
|
|
99352
99447
|
} catch {}
|
|
99353
99448
|
}
|
|
99354
99449
|
function sweepStaleTurnActiveMarker(stateDir, opts) {
|
|
99355
|
-
const path3 =
|
|
99450
|
+
const path3 = join64(stateDir, TURN_ACTIVE_MARKER_FILE2);
|
|
99356
99451
|
if (!existsSync53(path3))
|
|
99357
99452
|
return false;
|
|
99358
99453
|
const now = opts.now ?? Date.now();
|
|
99359
99454
|
try {
|
|
99360
|
-
const st =
|
|
99455
|
+
const st = statSync20(path3);
|
|
99361
99456
|
const ageMs = now - st.mtimeMs;
|
|
99362
99457
|
const hardExpired = ageMs > opts.hardTtlMs;
|
|
99363
99458
|
const idleExpired = !opts.turnInFlight && ageMs > opts.idleSweepMs;
|
|
@@ -99367,7 +99462,7 @@ function sweepStaleTurnActiveMarker(stateDir, opts) {
|
|
|
99367
99462
|
try {
|
|
99368
99463
|
payload = readFileSync55(path3, "utf8");
|
|
99369
99464
|
} catch {}
|
|
99370
|
-
|
|
99465
|
+
unlinkSync27(path3);
|
|
99371
99466
|
if (opts.onRemove) {
|
|
99372
99467
|
try {
|
|
99373
99468
|
opts.onRemove({
|
|
@@ -99383,9 +99478,9 @@ function sweepStaleTurnActiveMarker(stateDir, opts) {
|
|
|
99383
99478
|
}
|
|
99384
99479
|
}
|
|
99385
99480
|
function readTurnActiveMarkerAgeMs2(stateDir, now) {
|
|
99386
|
-
const path3 =
|
|
99481
|
+
const path3 = join64(stateDir, TURN_ACTIVE_MARKER_FILE2);
|
|
99387
99482
|
try {
|
|
99388
|
-
const st =
|
|
99483
|
+
const st = statSync20(path3);
|
|
99389
99484
|
return (now ?? Date.now()) - st.mtimeMs;
|
|
99390
99485
|
} catch {
|
|
99391
99486
|
return null;
|
|
@@ -99397,11 +99492,11 @@ function effectiveTurnAgeMs(markerAgeMs, turnStartedAt, now) {
|
|
|
99397
99492
|
|
|
99398
99493
|
// gateway/gateway-heartbeat.ts
|
|
99399
99494
|
import { mkdirSync as mkdirSync47, utimesSync as utimesSync3, writeFileSync as writeFileSync46 } from "node:fs";
|
|
99400
|
-
import { join as
|
|
99495
|
+
import { join as join65 } from "node:path";
|
|
99401
99496
|
var GATEWAY_HEARTBEAT_FILE = "gateway-heartbeat";
|
|
99402
99497
|
var GATEWAY_HEARTBEAT_INTERVAL_MS = 15000;
|
|
99403
99498
|
function touchGatewayHeartbeat(stateDir) {
|
|
99404
|
-
const path3 =
|
|
99499
|
+
const path3 = join65(stateDir, GATEWAY_HEARTBEAT_FILE);
|
|
99405
99500
|
const now = new Date;
|
|
99406
99501
|
try {
|
|
99407
99502
|
utimesSync3(path3, now, now);
|
|
@@ -99429,10 +99524,10 @@ import {
|
|
|
99429
99524
|
openSync as openSync14,
|
|
99430
99525
|
readFileSync as readFileSync56,
|
|
99431
99526
|
renameSync as renameSync25,
|
|
99432
|
-
unlinkSync as
|
|
99527
|
+
unlinkSync as unlinkSync28,
|
|
99433
99528
|
writeFileSync as writeFileSync47
|
|
99434
99529
|
} from "node:fs";
|
|
99435
|
-
import { join as
|
|
99530
|
+
import { join as join66 } from "node:path";
|
|
99436
99531
|
var BOOT_BEACON_FILE = "gateway-beacon.json";
|
|
99437
99532
|
var HOST_BOOT_ID_PATH = "/proc/sys/kernel/random/boot_id";
|
|
99438
99533
|
var CGROUP_MEMORY_EVENTS_PATH = "/sys/fs/cgroup/memory.events";
|
|
@@ -99531,7 +99626,7 @@ function sampleBeaconHost(paths = {}) {
|
|
|
99531
99626
|
return sample3;
|
|
99532
99627
|
}
|
|
99533
99628
|
function writeBootBeaconFile(stateDir, beacon) {
|
|
99534
|
-
const path3 =
|
|
99629
|
+
const path3 = join66(stateDir, BOOT_BEACON_FILE);
|
|
99535
99630
|
const tmp = `${path3}.tmp-${process.pid}`;
|
|
99536
99631
|
let fd;
|
|
99537
99632
|
try {
|
|
@@ -99550,7 +99645,7 @@ function writeBootBeaconFile(stateDir, beacon) {
|
|
|
99550
99645
|
} catch {}
|
|
99551
99646
|
}
|
|
99552
99647
|
try {
|
|
99553
|
-
|
|
99648
|
+
unlinkSync28(tmp);
|
|
99554
99649
|
} catch {}
|
|
99555
99650
|
return false;
|
|
99556
99651
|
}
|
|
@@ -99578,6 +99673,8 @@ function attachBootBeacon(stateDir, next) {
|
|
|
99578
99673
|
}
|
|
99579
99674
|
|
|
99580
99675
|
// gateway/outbox-sweep.ts
|
|
99676
|
+
init_rich_send();
|
|
99677
|
+
init_format();
|
|
99581
99678
|
init_flood_circuit_breaker();
|
|
99582
99679
|
async function sweepOutbox(deps) {
|
|
99583
99680
|
const now = deps.now?.() ?? Date.now();
|
|
@@ -99650,15 +99747,20 @@ async function sweepOutbox(deps) {
|
|
|
99650
99747
|
}
|
|
99651
99748
|
const resolvedChat = resolved;
|
|
99652
99749
|
try {
|
|
99653
|
-
const
|
|
99750
|
+
const sendResult = await deps.send(resolvedChat.chatId, resolvedChat.threadId, decision.text ?? record2.text);
|
|
99654
99751
|
appendDelivered({
|
|
99655
99752
|
turnNonce: record2.turnNonce,
|
|
99656
99753
|
textSha256: record2.textSha256,
|
|
99657
|
-
tgMessageId: messageId,
|
|
99754
|
+
tgMessageId: sendResult.messageId,
|
|
99658
99755
|
ts: now,
|
|
99659
99756
|
deliverySource: "sweep",
|
|
99660
99757
|
replyAlreadyDeliveredThisTurn: record2.replyAlreadyDeliveredThisTurn === true
|
|
99661
99758
|
}, deps.stateDir);
|
|
99759
|
+
if (deps.recordOutbound != null && sendResult.chunks.length > 0) {
|
|
99760
|
+
try {
|
|
99761
|
+
deps.recordOutbound(resolvedChat.chatId, resolvedChat.threadId, sendResult.chunks.map((c) => c.messageId), sendResult.chunks.map((c) => c.text));
|
|
99762
|
+
} catch {}
|
|
99763
|
+
}
|
|
99662
99764
|
removeClaimed(record2.turnNonce, deps.stateDir);
|
|
99663
99765
|
summary.delivered++;
|
|
99664
99766
|
log(`outbox-sweep: delivered nonce=${record2.turnNonce} via=${resolvedChat.via} ` + `source=${record2.source} chars=${record2.text.length}${decision.action === "send-delayed" ? " (delayed)" : ""}
|
|
@@ -99713,27 +99815,42 @@ function createSweepBackoff(cfg = {}) {
|
|
|
99713
99815
|
failures: () => failures
|
|
99714
99816
|
};
|
|
99715
99817
|
}
|
|
99818
|
+
async function sendChunkRich(bot, chatId, chunk2, opts) {
|
|
99819
|
+
try {
|
|
99820
|
+
return await bot.api.sendRichMessage(chatId, richMessage(chunk2), opts);
|
|
99821
|
+
} catch (err) {
|
|
99822
|
+
if (isParseEntitiesError(err)) {
|
|
99823
|
+
return await bot.api.sendMessage(chatId, chunk2, opts);
|
|
99824
|
+
}
|
|
99825
|
+
throw err;
|
|
99826
|
+
}
|
|
99827
|
+
}
|
|
99716
99828
|
function createOutboxSend(deps) {
|
|
99717
99829
|
return async (chatId, threadId, text4) => {
|
|
99718
99830
|
const bot = deps.getBot();
|
|
99719
99831
|
if (bot == null)
|
|
99720
99832
|
throw new Error("outbox-sweep: bot unavailable");
|
|
99721
99833
|
if (text4.length === 0)
|
|
99722
|
-
return;
|
|
99834
|
+
return { messageId: undefined, chunks: [] };
|
|
99723
99835
|
const replyMarkup = deps.resolveReplyMarkup?.(chatId, threadId, text4);
|
|
99724
|
-
|
|
99725
|
-
const
|
|
99726
|
-
for (let
|
|
99727
|
-
const chunk2 =
|
|
99728
|
-
const isLast = idx ===
|
|
99836
|
+
const landed = [];
|
|
99837
|
+
const chunks = splitMarkdownChunks(text4);
|
|
99838
|
+
for (let idx = 0;idx < chunks.length; idx++) {
|
|
99839
|
+
const chunk2 = chunks[idx];
|
|
99840
|
+
const isLast = idx === chunks.length - 1;
|
|
99729
99841
|
const res = await retryWithThreadFallback(deps.retry, (tid) => {
|
|
99730
99842
|
const base = tid != null ? { message_thread_id: tid } : {};
|
|
99731
99843
|
const opts = isLast && replyMarkup != null ? { ...base, reply_markup: replyMarkup } : base;
|
|
99732
|
-
return bot
|
|
99844
|
+
return sendChunkRich(bot, chatId, chunk2, opts);
|
|
99733
99845
|
}, { threadId: threadId ?? undefined, chat_id: chatId, verb: "outbox-sweep.sendMessage" });
|
|
99734
|
-
|
|
99846
|
+
const id = res?.message_id;
|
|
99847
|
+
if (id != null)
|
|
99848
|
+
landed.push({ messageId: id, text: chunk2 });
|
|
99735
99849
|
}
|
|
99736
|
-
return
|
|
99850
|
+
return {
|
|
99851
|
+
messageId: landed.length > 0 ? landed[landed.length - 1].messageId : undefined,
|
|
99852
|
+
chunks: landed
|
|
99853
|
+
};
|
|
99737
99854
|
};
|
|
99738
99855
|
}
|
|
99739
99856
|
function startOutboxSweep(deps) {
|
|
@@ -99764,6 +99881,7 @@ function startOutboxSweep(deps) {
|
|
|
99764
99881
|
stateDir: deps.stateDir,
|
|
99765
99882
|
log: deps.log,
|
|
99766
99883
|
send,
|
|
99884
|
+
...deps.recordOutbound != null ? { recordOutbound: deps.recordOutbound } : {},
|
|
99767
99885
|
floodWaitRemainingMs: floodProbe,
|
|
99768
99886
|
textAlreadyDelivered: (chatId, threadId, text4) => deps.dedupCheck(chatId, threadId ?? undefined, text4),
|
|
99769
99887
|
registryChainLookup: (taskId) => {
|
|
@@ -99799,10 +99917,10 @@ function startOutboxSweep(deps) {
|
|
|
99799
99917
|
}
|
|
99800
99918
|
|
|
99801
99919
|
// ../src/build-info.ts
|
|
99802
|
-
var VERSION2 = "0.19.
|
|
99803
|
-
var COMMIT_SHA = "
|
|
99804
|
-
var COMMIT_DATE = "2026-07-
|
|
99805
|
-
var LATEST_PR =
|
|
99920
|
+
var VERSION2 = "0.19.40";
|
|
99921
|
+
var COMMIT_SHA = "89286b22";
|
|
99922
|
+
var COMMIT_DATE = "2026-07-31T04:28:56Z";
|
|
99923
|
+
var LATEST_PR = 4061;
|
|
99806
99924
|
var COMMITS_AHEAD_OF_TAG = 0;
|
|
99807
99925
|
|
|
99808
99926
|
// gateway/boot-version.ts
|
|
@@ -99879,12 +99997,12 @@ init_protocol2();
|
|
|
99879
99997
|
init_peercred();
|
|
99880
99998
|
import * as net5 from "node:net";
|
|
99881
99999
|
import * as fs3 from "node:fs";
|
|
99882
|
-
import { homedir as
|
|
99883
|
-
import { join as
|
|
100000
|
+
import { homedir as homedir19 } from "node:os";
|
|
100001
|
+
import { join as join67 } from "node:path";
|
|
99884
100002
|
var DEFAULT_TIMEOUT_MS4 = 2000;
|
|
99885
100003
|
var UNLOCK_TIMEOUT_MS = 30000;
|
|
99886
|
-
var LEGACY_SOCKET_PATH2 =
|
|
99887
|
-
var OPERATOR_SOCKET_PATH2 =
|
|
100004
|
+
var LEGACY_SOCKET_PATH2 = join67(homedir19(), ".switchroom", "vault-broker.sock");
|
|
100005
|
+
var OPERATOR_SOCKET_PATH2 = join67(homedir19(), ".switchroom", "broker-operator", "sock");
|
|
99888
100006
|
function defaultBrokerSocketPath2() {
|
|
99889
100007
|
if (fs3.existsSync(OPERATOR_SOCKET_PATH2))
|
|
99890
100008
|
return OPERATOR_SOCKET_PATH2;
|
|
@@ -100767,7 +100885,7 @@ function resolveVaultApprovalPosture(broker) {
|
|
|
100767
100885
|
|
|
100768
100886
|
// registry/turns-schema.ts
|
|
100769
100887
|
import { chmodSync as chmodSync12, mkdirSync as mkdirSync49 } from "fs";
|
|
100770
|
-
import { join as
|
|
100888
|
+
import { join as join68 } from "path";
|
|
100771
100889
|
var DatabaseClass3 = null;
|
|
100772
100890
|
function loadDatabaseClass3() {
|
|
100773
100891
|
if (DatabaseClass3 != null)
|
|
@@ -100839,9 +100957,9 @@ function applySchema(db3) {
|
|
|
100839
100957
|
}
|
|
100840
100958
|
function openTurnsDb(agentDir) {
|
|
100841
100959
|
const Database = loadDatabaseClass3();
|
|
100842
|
-
const dir =
|
|
100960
|
+
const dir = join68(agentDir, "telegram");
|
|
100843
100961
|
mkdirSync49(dir, { recursive: true, mode: 448 });
|
|
100844
|
-
const path3 =
|
|
100962
|
+
const path3 = join68(dir, "registry.db");
|
|
100845
100963
|
const db3 = new Database(path3, { create: true });
|
|
100846
100964
|
applySchema(db3);
|
|
100847
100965
|
try {
|
|
@@ -101186,7 +101304,7 @@ function selectResumeBuilder(endedVia, opts) {
|
|
|
101186
101304
|
}
|
|
101187
101305
|
|
|
101188
101306
|
// gateway/bridge-dead-watchdog.ts
|
|
101189
|
-
import { readFileSync as readFileSync58, writeFileSync as writeFileSync48, renameSync as renameSync26, unlinkSync as
|
|
101307
|
+
import { readFileSync as readFileSync58, writeFileSync as writeFileSync48, renameSync as renameSync26, unlinkSync as unlinkSync29 } from "node:fs";
|
|
101190
101308
|
|
|
101191
101309
|
// gateway/cron-session.ts
|
|
101192
101310
|
var CRON_IDENTITY_SUFFIX2 = "-cron";
|
|
@@ -101258,7 +101376,7 @@ function consumeBridgeDeadEscalationMarker(path3, nowMs3 = Date.now(), maxAgeMs
|
|
|
101258
101376
|
}
|
|
101259
101377
|
} catch {}
|
|
101260
101378
|
try {
|
|
101261
|
-
|
|
101379
|
+
unlinkSync29(path3);
|
|
101262
101380
|
} catch {}
|
|
101263
101381
|
return marker;
|
|
101264
101382
|
}
|
|
@@ -101658,7 +101776,7 @@ if (isGatewayMain) {
|
|
|
101658
101776
|
shutdownAnalytics();
|
|
101659
101777
|
});
|
|
101660
101778
|
}
|
|
101661
|
-
var STATE_DIR = process.env.TELEGRAM_STATE_DIR ??
|
|
101779
|
+
var STATE_DIR = process.env.TELEGRAM_STATE_DIR ?? join69(homedir20(), ".claude", "channels", "telegram");
|
|
101662
101780
|
var permCardStore = createPermissionCardStore(STATE_DIR);
|
|
101663
101781
|
var BLOCKED_APPROVALS_DIR = process.env.SWITCHROOM_BLOCKED_APPROVALS_DIR ?? "/state/blocked-approvals";
|
|
101664
101782
|
var AGENT_NAME = process.env.SWITCHROOM_AGENT_NAME ?? "agent";
|
|
@@ -101758,11 +101876,11 @@ function scheduleAlwaysAllowPersistDrain() {
|
|
|
101758
101876
|
}, ALWAYS_ALLOW_DRAIN_INTERVAL_MS);
|
|
101759
101877
|
timer3.unref?.();
|
|
101760
101878
|
}
|
|
101761
|
-
var ACCESS_FILE =
|
|
101762
|
-
var APPROVED_DIR =
|
|
101763
|
-
var ENV_FILE =
|
|
101764
|
-
var INBOX_DIR =
|
|
101765
|
-
var PEOPLE_FILE =
|
|
101879
|
+
var ACCESS_FILE = join69(STATE_DIR, "access.json");
|
|
101880
|
+
var APPROVED_DIR = join69(STATE_DIR, "approved");
|
|
101881
|
+
var ENV_FILE = join69(STATE_DIR, ".env");
|
|
101882
|
+
var INBOX_DIR = join69(STATE_DIR, "inbox");
|
|
101883
|
+
var PEOPLE_FILE = join69(STATE_DIR, "people.json");
|
|
101766
101884
|
function triggerSelfRestart(targetAgent, reason, delayMs = 300) {
|
|
101767
101885
|
const isDocker = process.env.SWITCHROOM_RUNTIME === "docker";
|
|
101768
101886
|
const selfAgent = process.env.SWITCHROOM_AGENT_NAME;
|
|
@@ -101911,7 +102029,7 @@ function assertSendable(f) {
|
|
|
101911
102029
|
} catch {
|
|
101912
102030
|
throw new Error(`refusing to send file \u2014 cannot resolve real path: ${f}`);
|
|
101913
102031
|
}
|
|
101914
|
-
const inbox =
|
|
102032
|
+
const inbox = join69(stateReal, "inbox");
|
|
101915
102033
|
if (real.startsWith(stateReal + sep4) && !real.startsWith(inbox + sep4)) {
|
|
101916
102034
|
throw new Error(`refusing to send channel state: ${f}`);
|
|
101917
102035
|
}
|
|
@@ -102036,7 +102154,7 @@ var HISTORY_ENABLED = HISTORY_ACCESS.historyEnabled !== false;
|
|
|
102036
102154
|
if (isGatewayMain && HISTORY_ENABLED) {
|
|
102037
102155
|
try {
|
|
102038
102156
|
initHistory(STATE_DIR, HISTORY_ACCESS.historyRetentionDays ?? 30);
|
|
102039
|
-
process.stderr.write(`telegram gateway: history capture enabled at ${
|
|
102157
|
+
process.stderr.write(`telegram gateway: history capture enabled at ${join69(STATE_DIR, "history.db")}
|
|
102040
102158
|
`);
|
|
102041
102159
|
} catch (err) {
|
|
102042
102160
|
process.stderr.write(`telegram gateway: history init failed (${err.message}) \u2014 capture disabled
|
|
@@ -102055,9 +102173,9 @@ if (isGatewayMain)
|
|
|
102055
102173
|
let markerTurnKey = null;
|
|
102056
102174
|
let markerAgeMs = null;
|
|
102057
102175
|
try {
|
|
102058
|
-
const markerPath =
|
|
102176
|
+
const markerPath = join69(STATE_DIR, TURN_ACTIVE_MARKER_FILE2);
|
|
102059
102177
|
if (existsSync57(markerPath)) {
|
|
102060
|
-
const st =
|
|
102178
|
+
const st = statSync23(markerPath);
|
|
102061
102179
|
markerAgeMs = Date.now() - st.mtimeMs;
|
|
102062
102180
|
try {
|
|
102063
102181
|
const payload = JSON.parse(readFileSync60(markerPath, "utf8"));
|
|
@@ -102080,10 +102198,10 @@ if (isGatewayMain)
|
|
|
102080
102198
|
process.stderr.write(`telegram gateway: turn-registry boot-reaper stamped ${reaped} orphaned turn(s)` + `${timeoutTurnKey ? ` (turnKey=${timeoutTurnKey} as 'timeout', markerAgeMs=${markerAgeMs})` : " as 'restart'"}
|
|
102081
102199
|
`);
|
|
102082
102200
|
} else {
|
|
102083
|
-
process.stderr.write(`telegram gateway: turn-registry initialized at ${
|
|
102201
|
+
process.stderr.write(`telegram gateway: turn-registry initialized at ${join69(agentDir, "telegram", "registry.db")}
|
|
102084
102202
|
`);
|
|
102085
102203
|
}
|
|
102086
|
-
const bridgeDeadMarker = consumeBridgeDeadEscalationMarker(
|
|
102204
|
+
const bridgeDeadMarker = consumeBridgeDeadEscalationMarker(join69(STATE_DIR, "bridge-dead-escalation.json"));
|
|
102087
102205
|
if (bridgeDeadMarker != null) {
|
|
102088
102206
|
bridgeDeadPriorStreak = bridgeDeadMarker.count ?? 1;
|
|
102089
102207
|
process.stderr.write(`telegram gateway: boot: prior restart was a bridge-dead escalation (reason=${bridgeDeadMarker.reason}` + `, consecutive=${bridgeDeadPriorStreak}` + `${bridgeDeadMarker.crashTail ? `, crashTail=${bridgeDeadMarker.crashTail}` : ""})
|
|
@@ -102096,7 +102214,7 @@ if (isGatewayMain)
|
|
|
102096
102214
|
const pending2 = findLatestTurnIfInterrupted(turnsDb);
|
|
102097
102215
|
const selfAgent = process.env.SWITCHROOM_AGENT_NAME ?? "";
|
|
102098
102216
|
if (pending2 != null && selfAgent) {
|
|
102099
|
-
const bootResumeMarkerPath = process.env.SWITCHROOM_GATEWAY_CLEAN_SHUTDOWN_MARKER ??
|
|
102217
|
+
const bootResumeMarkerPath = process.env.SWITCHROOM_GATEWAY_CLEAN_SHUTDOWN_MARKER ?? join69(STATE_DIR, "clean-shutdown.json");
|
|
102100
102218
|
const bootResumeCleanMarker = readCleanShutdownMarker(bootResumeMarkerPath);
|
|
102101
102219
|
const bootResumeForceAlways = process.env.SWITCHROOM_BOOT_RESUME_ALWAYS === "1";
|
|
102102
102220
|
const bootResumeMode = parseBootResumeMode(process.env.SWITCHROOM_BOOT_RESUME);
|
|
@@ -102209,7 +102327,7 @@ if (isGatewayMain)
|
|
|
102209
102327
|
`);
|
|
102210
102328
|
}
|
|
102211
102329
|
}
|
|
102212
|
-
const pendingEnvPath =
|
|
102330
|
+
const pendingEnvPath = join69(agentDir, ".pending-turn.env");
|
|
102213
102331
|
try {
|
|
102214
102332
|
if (pending2 != null) {
|
|
102215
102333
|
const lines = [
|
|
@@ -102337,7 +102455,7 @@ function checkApprovals() {
|
|
|
102337
102455
|
return;
|
|
102338
102456
|
}
|
|
102339
102457
|
for (const senderId of files) {
|
|
102340
|
-
const file =
|
|
102458
|
+
const file = join69(APPROVED_DIR, senderId);
|
|
102341
102459
|
bot.api.sendMessage(senderId, "Paired! Say hi to Claude.").then(() => rmSync8(file, { force: true }), (err) => {
|
|
102342
102460
|
process.stderr.write(`telegram gateway: failed to send approval confirm: ${err}
|
|
102343
102461
|
`);
|
|
@@ -102423,6 +102541,7 @@ async function deliverAnswer(args) {
|
|
|
102423
102541
|
const result = await runBackstopDelivery(backstopDeliveryLedger, turnId, chunks, args.resume ? null : args.cardMessageId, {
|
|
102424
102542
|
sendChunk,
|
|
102425
102543
|
readBack,
|
|
102544
|
+
onAckClaim: args.onAckClaim,
|
|
102426
102545
|
recordOutbound: HISTORY_ENABLED ? (messageIds, texts) => {
|
|
102427
102546
|
try {
|
|
102428
102547
|
recordOutbound({
|
|
@@ -102511,7 +102630,7 @@ function noteAgentOutputAt(key, ts) {
|
|
|
102511
102630
|
lastAgentOutputAt.delete(oldest);
|
|
102512
102631
|
}
|
|
102513
102632
|
}
|
|
102514
|
-
var OBLIGATION_STORE_PATH =
|
|
102633
|
+
var OBLIGATION_STORE_PATH = join69(STATE_DIR, "obligations.json");
|
|
102515
102634
|
var obligationStoreFs = {
|
|
102516
102635
|
readFileSync: (p) => readFileSync60(p, "utf8"),
|
|
102517
102636
|
writeFileSync: (p, d) => writeFileSync50(p, d),
|
|
@@ -103288,7 +103407,7 @@ function emitTurnRecord(turn, endedAt) {
|
|
|
103288
103407
|
maybeRotate(turnsPath, {
|
|
103289
103408
|
statSize: (p) => {
|
|
103290
103409
|
try {
|
|
103291
|
-
return
|
|
103410
|
+
return statSync23(p).size;
|
|
103292
103411
|
} catch {
|
|
103293
103412
|
return;
|
|
103294
103413
|
}
|
|
@@ -104856,7 +104975,7 @@ var PIN_STATUS_WHILE_WORKING = (() => {
|
|
|
104856
104975
|
})();
|
|
104857
104976
|
var statusPinClaims = new Map;
|
|
104858
104977
|
var statusPinRightsCache = new PinRightsCache2;
|
|
104859
|
-
var STATUS_PIN_STORE_PATH =
|
|
104978
|
+
var STATUS_PIN_STORE_PATH = join69(STATE_DIR, "status-pins.json");
|
|
104860
104979
|
var statusPinStoreFs = {
|
|
104861
104980
|
readFileSync: (p) => readFileSync60(p, "utf8"),
|
|
104862
104981
|
writeFileSync: (p, d) => writeFileSync50(p, d),
|
|
@@ -104864,7 +104983,7 @@ var statusPinStoreFs = {
|
|
|
104864
104983
|
existsSync: (p) => existsSync57(p)
|
|
104865
104984
|
};
|
|
104866
104985
|
var statusPinPersistEnabled = !STATIC && PIN_STATUS_WHILE_WORKING;
|
|
104867
|
-
var ACTIVITY_CARD_STORE_PATH =
|
|
104986
|
+
var ACTIVITY_CARD_STORE_PATH = join69(STATE_DIR, "activity-cards-pending.json");
|
|
104868
104987
|
var activityCardStoreFs = {
|
|
104869
104988
|
readFileSync: (p) => readFileSync60(p, "utf8"),
|
|
104870
104989
|
writeFileSync: (p, d) => writeFileSync50(p, d),
|
|
@@ -104872,7 +104991,7 @@ var activityCardStoreFs = {
|
|
|
104872
104991
|
existsSync: (p) => existsSync57(p)
|
|
104873
104992
|
};
|
|
104874
104993
|
var activityCardPersistEnabled = !STATIC;
|
|
104875
|
-
var QUEUED_CARD_STORE_PATH =
|
|
104994
|
+
var QUEUED_CARD_STORE_PATH = join69(STATE_DIR, "queued-cards-pending.json");
|
|
104876
104995
|
var queuedCardStoreFs = {
|
|
104877
104996
|
readFileSync: (p) => readFileSync60(p, "utf8"),
|
|
104878
104997
|
writeFileSync: (p, d) => writeFileSync50(p, d),
|
|
@@ -105164,7 +105283,7 @@ async function unpinAllStatusPins() {
|
|
|
105164
105283
|
}
|
|
105165
105284
|
}
|
|
105166
105285
|
var stalePinSweepEligible = false;
|
|
105167
|
-
var STALE_PIN_SWEEP_STORE_PATH =
|
|
105286
|
+
var STALE_PIN_SWEEP_STORE_PATH = join69(STATE_DIR, "stale-pin-sweep.json");
|
|
105168
105287
|
var stalePinSweeper = createGatewayStalePinSweeper({
|
|
105169
105288
|
telegram: { handle: () => lockedBot, call: robustApiCall },
|
|
105170
105289
|
claims: () => statusPinClaims.values(),
|
|
@@ -105205,12 +105324,12 @@ var getPinnedProgressCardMessageId = null;
|
|
|
105205
105324
|
var completeProgressCardTurn = null;
|
|
105206
105325
|
var subagentWatcher = null;
|
|
105207
105326
|
var workerActivityFeed = null;
|
|
105208
|
-
var SOCKET_PATH = process.env.SWITCHROOM_GATEWAY_SOCKET ??
|
|
105327
|
+
var SOCKET_PATH = process.env.SWITCHROOM_GATEWAY_SOCKET ?? join69(STATE_DIR, "gateway.sock");
|
|
105209
105328
|
if (isGatewayMain)
|
|
105210
105329
|
mkdirSync51(STATE_DIR, { recursive: true, mode: 448 });
|
|
105211
|
-
var GATEWAY_PID_PATH = process.env.SWITCHROOM_GATEWAY_PID_FILE ??
|
|
105212
|
-
var GATEWAY_SESSION_MARKER_PATH = process.env.SWITCHROOM_GATEWAY_SESSION_MARKER ??
|
|
105213
|
-
var GATEWAY_CLEAN_SHUTDOWN_MARKER_PATH = process.env.SWITCHROOM_GATEWAY_CLEAN_SHUTDOWN_MARKER ??
|
|
105330
|
+
var GATEWAY_PID_PATH = process.env.SWITCHROOM_GATEWAY_PID_FILE ?? join69(STATE_DIR, "gateway.pid.json");
|
|
105331
|
+
var GATEWAY_SESSION_MARKER_PATH = process.env.SWITCHROOM_GATEWAY_SESSION_MARKER ?? join69(STATE_DIR, "gateway-session.json");
|
|
105332
|
+
var GATEWAY_CLEAN_SHUTDOWN_MARKER_PATH = process.env.SWITCHROOM_GATEWAY_CLEAN_SHUTDOWN_MARKER ?? join69(STATE_DIR, "clean-shutdown.json");
|
|
105214
105333
|
var GATEWAY_STARTED_AT_MS = Date.now();
|
|
105215
105334
|
var BOOT_CARD_ENABLED = process.env.SWITCHROOM_BOOT_CARD !== "false";
|
|
105216
105335
|
var activeBootCard = null;
|
|
@@ -105239,7 +105358,7 @@ function ensureIssuesCard(chatId, threadId) {
|
|
|
105239
105358
|
bot: botApi,
|
|
105240
105359
|
log: (msg) => process.stderr.write(`telegram gateway: ${msg}
|
|
105241
105360
|
`),
|
|
105242
|
-
persistPath:
|
|
105361
|
+
persistPath: join69(stateDir, "issues-card.json")
|
|
105243
105362
|
});
|
|
105244
105363
|
activeIssuesWatcher = startIssuesWatcher({
|
|
105245
105364
|
stateDir,
|
|
@@ -105417,7 +105536,11 @@ function runDeliveryConfirmSweep() {
|
|
|
105417
105536
|
}
|
|
105418
105537
|
var _deliveryConfirmSweep = isGatewayMain ? setInterval(runDeliveryConfirmSweep, DELIVERY_CONFIRM_SWEEP_MS) : undefined;
|
|
105419
105538
|
_deliveryConfirmSweep?.unref?.();
|
|
105420
|
-
startOutboxSweep({ isGatewayMain, stateDir: STATE_DIR, getBot: () => bot, getTurnsDb: () => turnsDb, dedupCheck: (c, t, x) => outboundDedup.check(c, t, x, Date.now()) != null,
|
|
105539
|
+
startOutboxSweep({ isGatewayMain, stateDir: STATE_DIR, getBot: () => bot, getTurnsDb: () => turnsDb, dedupCheck: (c, t, x) => outboundDedup.check(c, t, x, Date.now()) != null, recordOutbound: HISTORY_ENABLED ? (chatId, threadId, message_ids, texts) => {
|
|
105540
|
+
try {
|
|
105541
|
+
recordOutbound({ chat_id: chatId, thread_id: threadId, message_ids, texts });
|
|
105542
|
+
} catch {}
|
|
105543
|
+
} : undefined, resolveReplyMarkup: makeOutboxListenMarkupResolver({ resolveVoiceOutPlan: (t) => resolveVoiceOutPlan(loadAccess().voice_out, t), cachePut: (token, payload) => voiceOnDemandCache.put(token, payload), eagerVoiceEnabled, enqueuePreSynth: (j) => voicePreSynthQueue.enqueue(j) }), log: (l) => process.stderr.write(l) });
|
|
105421
105544
|
if (isGatewayMain)
|
|
105422
105545
|
startTimer2({
|
|
105423
105546
|
editMessage: async (ctx) => {
|
|
@@ -105439,14 +105562,14 @@ if (isGatewayMain)
|
|
|
105439
105562
|
var inboundSpool;
|
|
105440
105563
|
if (isGatewayMain)
|
|
105441
105564
|
inboundSpool = STATIC ? undefined : createInboundSpool({
|
|
105442
|
-
path:
|
|
105565
|
+
path: join69(STATE_DIR, "inbound-spool.jsonl"),
|
|
105443
105566
|
fs: {
|
|
105444
105567
|
appendFileSync: (p, d) => appendFileSync9(p, d),
|
|
105445
105568
|
readFileSync: (p) => readFileSync60(p, "utf8"),
|
|
105446
105569
|
writeFileSync: (p, d) => writeFileSync50(p, d),
|
|
105447
105570
|
renameSync: (a, b) => renameSync27(a, b),
|
|
105448
105571
|
existsSync: (p) => existsSync57(p),
|
|
105449
|
-
statSizeSync: (p) =>
|
|
105572
|
+
statSizeSync: (p) => statSync23(p).size,
|
|
105450
105573
|
fsyncFileSync: fsyncPathSync,
|
|
105451
105574
|
fsyncDirSync: fsyncPathSync
|
|
105452
105575
|
},
|
|
@@ -105509,7 +105632,7 @@ async function maybeRedeliverUndeliveredAnswer() {
|
|
|
105509
105632
|
let transcriptText;
|
|
105510
105633
|
try {
|
|
105511
105634
|
const projectsDir = getProjectsDirForCwd();
|
|
105512
|
-
const path3 =
|
|
105635
|
+
const path3 = join69(projectsDir, `${sessionId}.jsonl`);
|
|
105513
105636
|
if (!existsSync57(path3)) {
|
|
105514
105637
|
process.stderr.write(`telegram gateway: crash-redelivery \u2014 transcript not found for turnKey=${turn.turn_key} session=${sessionId} (${path3}); skipping
|
|
105515
105638
|
`);
|
|
@@ -105674,8 +105797,8 @@ var bridgeDeadWatchdog = createBridgeDeadWatchdog({
|
|
|
105674
105797
|
isSessionAlive: () => findAgentProcessInContainer2()?.comm === "claude",
|
|
105675
105798
|
isShuttingDown: () => shuttingDown,
|
|
105676
105799
|
escalate: (reason) => triggerSelfRestart(process.env.SWITCHROOM_AGENT_NAME ?? "", reason, 1500),
|
|
105677
|
-
crashLogPath:
|
|
105678
|
-
markerPath:
|
|
105800
|
+
crashLogPath: join69(STATE_DIR, "bridge-crash.log"),
|
|
105801
|
+
markerPath: join69(STATE_DIR, "bridge-dead-escalation.json"),
|
|
105679
105802
|
log: (line) => process.stderr.write(`${line}
|
|
105680
105803
|
`),
|
|
105681
105804
|
priorStreak: bridgeDeadPriorStreak,
|
|
@@ -105781,8 +105904,8 @@ if (isGatewayMain)
|
|
|
105781
105904
|
probeQuotaViaBroker: (t) => probeQuotaForBootCard(agentSlug, t),
|
|
105782
105905
|
tmuxSupervisor: process.env.SWITCHROOM_TMUX_SUPERVISOR === "1",
|
|
105783
105906
|
dockerMode: process.env.SWITCHROOM_RUNTIME === "docker",
|
|
105784
|
-
configSnapshotPath:
|
|
105785
|
-
bootCardStatePath:
|
|
105907
|
+
configSnapshotPath: join69(resolvedAgentDirForCard, ".config-snapshot.json"),
|
|
105908
|
+
bootCardStatePath: join69(resolvedAgentDirForCard, ".boot-card-msgid.json"),
|
|
105786
105909
|
floodStatePath: FLOOD_STATE_PATH,
|
|
105787
105910
|
...updateOutcomeLine ? { updateOutcomeLine } : {}
|
|
105788
105911
|
}, ackMsgId).then((handle) => {
|
|
@@ -106450,7 +106573,7 @@ if (isGatewayMain)
|
|
|
106450
106573
|
const receiverUid = receiverUidRaw ? Number(receiverUidRaw) : NaN;
|
|
106451
106574
|
if (Number.isInteger(receiverUid))
|
|
106452
106575
|
allowedUids.push(receiverUid);
|
|
106453
|
-
const socketPath =
|
|
106576
|
+
const socketPath = join69(STATE_DIR, "webhook.sock");
|
|
106454
106577
|
const webhookInject = (agentName3, inbound) => {
|
|
106455
106578
|
const msg = inbound;
|
|
106456
106579
|
const delivered = ipcServer.sendToAgent(agentName3, msg);
|
|
@@ -106678,9 +106801,9 @@ function redactOutboundText(text5, site) {
|
|
|
106678
106801
|
var VOICE_OUT_DEFAULT_CHUNK_CHARS = 600;
|
|
106679
106802
|
var VOICE_OUT_HARD_CHUNK_CAP = 4096;
|
|
106680
106803
|
var voiceOnDemandCache = new VoiceOnDemandCache({
|
|
106681
|
-
persistPath:
|
|
106804
|
+
persistPath: join69(STATE_DIR, "voice-ondemand.json")
|
|
106682
106805
|
});
|
|
106683
|
-
var VOICE_CACHE_DIR =
|
|
106806
|
+
var VOICE_CACHE_DIR = join69(STATE_DIR, "voice-cache");
|
|
106684
106807
|
var voicePreSynthQueue = new PreSynthQueue({
|
|
106685
106808
|
runJob: async (job) => {
|
|
106686
106809
|
const sidecarToken = await materializeSidecarToken2();
|
|
@@ -107082,7 +107205,7 @@ async function executeSendGif(rawArgs) {
|
|
|
107082
107205
|
};
|
|
107083
107206
|
}
|
|
107084
107207
|
async function publishToTelegraph(text5, shortName, authorName) {
|
|
107085
|
-
const accountPath =
|
|
107208
|
+
const accountPath = join69(STATE_DIR, "telegraph-account.json");
|
|
107086
107209
|
let account = null;
|
|
107087
107210
|
try {
|
|
107088
107211
|
if (existsSync57(accountPath)) {
|
|
@@ -108637,7 +108760,7 @@ function restartMarkerPath() {
|
|
|
108637
108760
|
const agentDir = resolveAgentDirFromEnv();
|
|
108638
108761
|
if (!agentDir)
|
|
108639
108762
|
return null;
|
|
108640
|
-
return
|
|
108763
|
+
return join69(agentDir, "restart-pending.json");
|
|
108641
108764
|
}
|
|
108642
108765
|
function writeRestartMarker(marker) {
|
|
108643
108766
|
const p = restartMarkerPath();
|
|
@@ -108829,7 +108952,7 @@ function _resetDockerReachableCache() {
|
|
|
108829
108952
|
}
|
|
108830
108953
|
function spawnSwitchroomDetached(args, onFailure) {
|
|
108831
108954
|
const fullArgs = SWITCHROOM_CONFIG ? ["--config", SWITCHROOM_CONFIG, ...args] : args;
|
|
108832
|
-
const logPath =
|
|
108955
|
+
const logPath = join69(STATE_DIR, "detached-spawn.log");
|
|
108833
108956
|
let outFd = null;
|
|
108834
108957
|
try {
|
|
108835
108958
|
mkdirSync51(STATE_DIR, { recursive: true });
|
|
@@ -109122,7 +109245,7 @@ ${preBlock(formatSwitchroomOutput(detail))}`, { html: true });
|
|
|
109122
109245
|
}
|
|
109123
109246
|
function readRecentDenialsForAgent(agentName3, windowMs, limit) {
|
|
109124
109247
|
try {
|
|
109125
|
-
const auditPath =
|
|
109248
|
+
const auditPath = join69(homedir20(), ".switchroom", "vault-audit.log");
|
|
109126
109249
|
if (!existsSync57(auditPath))
|
|
109127
109250
|
return [];
|
|
109128
109251
|
const raw = readFileSync60(auditPath, "utf8");
|
|
@@ -109176,7 +109299,7 @@ async function buildAgentMetadata(agentName3) {
|
|
|
109176
109299
|
try {
|
|
109177
109300
|
const agentDir = resolveAgentDirFromEnv();
|
|
109178
109301
|
if (agentDir) {
|
|
109179
|
-
const raw = readFileSync60(
|
|
109302
|
+
const raw = readFileSync60(join69(agentDir, ".claude", ".claude.json"), "utf8");
|
|
109180
109303
|
claudeJson = JSON.parse(raw);
|
|
109181
109304
|
}
|
|
109182
109305
|
} catch {}
|
|
@@ -109305,7 +109428,7 @@ function buildModelDeps(restartCtx) {
|
|
|
109305
109428
|
try {
|
|
109306
109429
|
const agentDir = resolveAgentDirFromEnv();
|
|
109307
109430
|
if (agentDir) {
|
|
109308
|
-
const local = await fetchQuota2({ claudeConfigDir:
|
|
109431
|
+
const local = await fetchQuota2({ claudeConfigDir: join69(agentDir, ".claude") });
|
|
109309
109432
|
if (local.ok)
|
|
109310
109433
|
return formatQuotaLine2(local.data);
|
|
109311
109434
|
}
|
|
@@ -109552,10 +109675,10 @@ function effortMenuReplyMarkup(reply) {
|
|
|
109552
109675
|
function flushAgentHandoff(agentDir) {
|
|
109553
109676
|
let removed = 0;
|
|
109554
109677
|
for (const fname of [".handoff.md", ".handoff-topic"]) {
|
|
109555
|
-
const p =
|
|
109678
|
+
const p = join69(agentDir, fname);
|
|
109556
109679
|
try {
|
|
109557
109680
|
if (existsSync57(p)) {
|
|
109558
|
-
|
|
109681
|
+
unlinkSync31(p);
|
|
109559
109682
|
removed++;
|
|
109560
109683
|
}
|
|
109561
109684
|
} catch (err) {
|
|
@@ -109610,7 +109733,7 @@ async function handleNewCommand(ctx) {
|
|
|
109610
109733
|
writeRestartMarker({ chat_id: chatId, thread_id: threadId ?? null, ack_message_id: ackId, ts: Date.now() });
|
|
109611
109734
|
if (agentDir != null) {
|
|
109612
109735
|
try {
|
|
109613
|
-
writeFileSync50(
|
|
109736
|
+
writeFileSync50(join69(agentDir, ".force-fresh-session"), `${kind} at ${new Date().toISOString()}
|
|
109614
109737
|
`, "utf8");
|
|
109615
109738
|
} catch (err) {
|
|
109616
109739
|
process.stderr.write(`telegram gateway: failed to write force-fresh marker: ${err}
|
|
@@ -109727,7 +109850,7 @@ var lockoutOps = {
|
|
|
109727
109850
|
writeFileSync: (p, data, opts) => writeFileSync50(p, data, opts),
|
|
109728
109851
|
existsSync: (p) => existsSync57(p),
|
|
109729
109852
|
mkdirSync: (p, opts) => mkdirSync51(p, opts),
|
|
109730
|
-
joinPath: (...parts) =>
|
|
109853
|
+
joinPath: (...parts) => join69(...parts)
|
|
109731
109854
|
};
|
|
109732
109855
|
var FLEET_FALLBACK_DEDUP_MS = 30000;
|
|
109733
109856
|
function isAuthBrokerSocketReachable() {
|
|
@@ -109987,7 +110110,7 @@ async function runCreditWatch() {
|
|
|
109987
110110
|
if (!agentDir)
|
|
109988
110111
|
return;
|
|
109989
110112
|
const agentName3 = getMyAgentName();
|
|
109990
|
-
const claudeConfigDir =
|
|
110113
|
+
const claudeConfigDir = join69(agentDir, ".claude");
|
|
109991
110114
|
const stateDir = STATE_DIR;
|
|
109992
110115
|
const reason = readClaudeJsonOverage(claudeConfigDir);
|
|
109993
110116
|
const prev = loadCreditState(stateDir);
|
|
@@ -111743,7 +111866,7 @@ Send \`/auth ${parsed.provider} cancel\` to abort.`, { html: true });
|
|
|
111743
111866
|
await switchroomReply(ctx, "**/usage:** cannot resolve agent dir.", { html: true });
|
|
111744
111867
|
return;
|
|
111745
111868
|
}
|
|
111746
|
-
const result = await fetchQuota2({ claudeConfigDir:
|
|
111869
|
+
const result = await fetchQuota2({ claudeConfigDir: join69(agentDir, ".claude") });
|
|
111747
111870
|
if (!result.ok) {
|
|
111748
111871
|
await switchroomReply(ctx, `**/usage:** ${escapeHtmlForTg2(result.reason)}`, { html: true });
|
|
111749
111872
|
return;
|
|
@@ -112999,7 +113122,7 @@ async function startGateway() {
|
|
|
112999
113122
|
return;
|
|
113000
113123
|
}
|
|
113001
113124
|
})();
|
|
113002
|
-
const resolvedAgentDirForBootCard = agentDir ??
|
|
113125
|
+
const resolvedAgentDirForBootCard = agentDir ?? join69(homedir20(), ".switchroom", "agents", agentSlug);
|
|
113003
113126
|
const handle = await startBootCard(chatId, threadId, botApiForCard, {
|
|
113004
113127
|
agentName: agentDisplayName,
|
|
113005
113128
|
agentSlug,
|
|
@@ -113013,8 +113136,8 @@ async function startGateway() {
|
|
|
113013
113136
|
probeQuotaViaBroker: (t) => probeQuotaForBootCard(agentSlug, t),
|
|
113014
113137
|
tmuxSupervisor: process.env.SWITCHROOM_TMUX_SUPERVISOR === "1",
|
|
113015
113138
|
dockerMode: process.env.SWITCHROOM_RUNTIME === "docker",
|
|
113016
|
-
configSnapshotPath:
|
|
113017
|
-
bootCardStatePath:
|
|
113139
|
+
configSnapshotPath: join69(resolvedAgentDirForBootCard, ".config-snapshot.json"),
|
|
113140
|
+
bootCardStatePath: join69(resolvedAgentDirForBootCard, ".boot-card-msgid.json"),
|
|
113018
113141
|
floodStatePath: FLOOD_STATE_PATH,
|
|
113019
113142
|
...updateOutcomeLine ? { updateOutcomeLine } : {}
|
|
113020
113143
|
}, ackMsgId);
|
|
@@ -113047,7 +113170,7 @@ async function startGateway() {
|
|
|
113047
113170
|
if (smAgentDir) {
|
|
113048
113171
|
const resolutionTimeoutMs = resolveSessionModelResolutionTimeoutMs(process.env.SWITCHROOM_SESSION_MODEL_RESOLUTION_TIMEOUT_MS);
|
|
113049
113172
|
const resolved = await waitForSessionModelResolution({
|
|
113050
|
-
barrierExists: () => existsSync57(
|
|
113173
|
+
barrierExists: () => existsSync57(join69(smAgentDir, ".session-model-resolved")),
|
|
113051
113174
|
timeoutMs: resolutionTimeoutMs
|
|
113052
113175
|
});
|
|
113053
113176
|
if (!resolved) {
|
|
@@ -113055,7 +113178,7 @@ async function startGateway() {
|
|
|
113055
113178
|
process.stderr.write(`telegram gateway: gw /model relaunch UNRESOLVED agent=${getMyAgentName()} target=${target} (barrier timeout after ${resolutionTimeoutMs}ms)
|
|
113056
113179
|
`);
|
|
113057
113180
|
} else {
|
|
113058
|
-
const activePath =
|
|
113181
|
+
const activePath = join69(smAgentDir, ".active-session-model");
|
|
113059
113182
|
if (existsSync57(activePath)) {
|
|
113060
113183
|
try {
|
|
113061
113184
|
const launched = readFileSync60(activePath, "utf8").trim();
|
|
@@ -113091,12 +113214,12 @@ async function startGateway() {
|
|
|
113091
113214
|
deliverModelSwitchBootNotice({
|
|
113092
113215
|
...modelBootCardDeps,
|
|
113093
113216
|
confirmation,
|
|
113094
|
-
hasSessionModelAlert: existsSync57(
|
|
113217
|
+
hasSessionModelAlert: existsSync57(join69(smAgentDir, ".session-model-alert"))
|
|
113095
113218
|
});
|
|
113096
113219
|
}
|
|
113097
113220
|
} catch {}
|
|
113098
113221
|
}
|
|
113099
|
-
const activeEffortPath =
|
|
113222
|
+
const activeEffortPath = join69(smAgentDir, ".active-session-effort");
|
|
113100
113223
|
if (existsSync57(activeEffortPath)) {
|
|
113101
113224
|
try {
|
|
113102
113225
|
const launchedEffort = readFileSync60(activeEffortPath, "utf8").trim();
|
|
@@ -113104,7 +113227,7 @@ async function startGateway() {
|
|
|
113104
113227
|
sessionEffortOverride = launchedEffort.length > 0 && launchedEffort !== configuredEffort ? launchedEffort : null;
|
|
113105
113228
|
} catch {}
|
|
113106
113229
|
}
|
|
113107
|
-
const alertPath =
|
|
113230
|
+
const alertPath = join69(smAgentDir, ".session-model-alert");
|
|
113108
113231
|
if (existsSync57(alertPath)) {
|
|
113109
113232
|
let alertText = null;
|
|
113110
113233
|
try {
|
|
@@ -113113,7 +113236,7 @@ async function startGateway() {
|
|
|
113113
113236
|
alertText = null;
|
|
113114
113237
|
}
|
|
113115
113238
|
try {
|
|
113116
|
-
|
|
113239
|
+
unlinkSync31(alertPath);
|
|
113117
113240
|
} catch {}
|
|
113118
113241
|
if (alertText && alertText.length > 0) {
|
|
113119
113242
|
const operators = loadAccess().allowFrom;
|