switchroom 0.19.17 → 0.19.18
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/bin/run-hook.sh +148 -0
- package/bin/workspace-dynamic-hook.sh +147 -38
- package/dist/agent-scheduler/index.js +11 -3
- package/dist/auth-broker/index.js +29 -4
- package/dist/cli/notion-write-pretool.mjs +11 -3
- package/dist/cli/switchroom.js +8306 -7619
- package/dist/host-control/main.js +626 -36
- package/dist/vault/approvals/kernel-server.js +30 -5
- package/dist/vault/broker/server.js +71 -18
- package/package.json +3 -2
- package/profiles/_base/start.sh.hbs +8 -4
- package/profiles/coding/CLAUDE.md.hbs +1 -1
- package/profiles/default/CLAUDE.md.hbs +3 -3
- package/profiles/executive-assistant/CLAUDE.md.hbs +1 -1
- package/profiles/health-coach/CLAUDE.md.hbs +1 -1
- package/skills/mental-model-curator/SKILL.md +8 -6
- package/telegram-plugin/bridge/bridge.ts +11 -19
- package/telegram-plugin/bridge/mcp-instructions.ts +87 -0
- package/telegram-plugin/dist/bridge/bridge.js +15 -20
- package/telegram-plugin/dist/gateway/gateway.js +763 -373
- package/telegram-plugin/dist/server.js +19 -20
- package/telegram-plugin/gateway/boot-card.ts +5 -1
- package/telegram-plugin/gateway/boot-probes.ts +113 -0
- package/telegram-plugin/gateway/config-approval-handler.test.ts +54 -0
- package/telegram-plugin/gateway/config-approval-handler.ts +16 -1
- package/telegram-plugin/gateway/disconnect-flush.ts +17 -0
- package/telegram-plugin/gateway/gateway.ts +43 -1
- package/telegram-plugin/gateway/handback-preturn-signal.ts +61 -7
- package/telegram-plugin/gateway/ipc-protocol.ts +5 -0
- package/telegram-plugin/gateway/ipc-server.ts +13 -0
- package/telegram-plugin/gateway/liveness-wiring.ts +125 -5
- package/telegram-plugin/gateway/obligation-ledger.ts +84 -4
- package/telegram-plugin/gateway/resume-inbound-builder.ts +13 -4
- package/telegram-plugin/gateway/stream-render.ts +24 -5
- package/telegram-plugin/hooks/secret-guard-pretool.mjs +249 -76
- package/telegram-plugin/registry/turns-schema.test.ts +8 -3
- package/telegram-plugin/registry/turns-schema.ts +40 -12
- package/telegram-plugin/runtime-metrics.ts +14 -0
- package/telegram-plugin/silence-poke.ts +138 -0
- package/telegram-plugin/tests/boot-probe-drift.test.ts +152 -0
- package/telegram-plugin/tests/gateway-disconnect-flush.test.ts +32 -0
- package/telegram-plugin/tests/handback-preturn-signal.test.ts +62 -0
- package/telegram-plugin/tests/helpers/liveness-wiring-fixture.ts +178 -0
- package/telegram-plugin/tests/ipc-server-validate-config-approval.test.ts +95 -0
- package/telegram-plugin/tests/mcp-instructions-budget.test.ts +184 -0
- package/telegram-plugin/tests/multitopic-routing-wiring.test.ts +22 -2
- package/telegram-plugin/tests/obligation-determinism.test.ts +114 -3
- package/telegram-plugin/tests/obligation-ledger.test.ts +310 -0
- package/telegram-plugin/tests/registry-turns.test.ts +13 -0
- package/telegram-plugin/tests/resume-inbound-builder.test.ts +15 -0
- package/telegram-plugin/tests/secret-guard-pretool.test.ts +347 -16
- package/telegram-plugin/tests/silence-poke-orphan-reap.test.ts +392 -0
- package/telegram-plugin/tests/silence-poke-teardown-notice.test.ts +301 -0
- package/telegram-plugin/tests/stream-render-golden.test.ts +103 -1
- package/telegram-plugin/tests/tts-normalize.test.ts +43 -0
- package/telegram-plugin/tests/voice-normalize-text.test.ts +212 -3
- package/telegram-plugin/tts-normalize.ts +6 -4
- package/telegram-plugin/voice-normalize-text.ts +168 -11
- package/vendor/hindsight-memory/CHANGELOG.md +73 -0
- package/vendor/hindsight-memory/scripts/lib/config.py +8 -3
- package/vendor/hindsight-memory/scripts/lib/directives.py +62 -4
- package/vendor/hindsight-memory/scripts/recall.py +257 -12
- package/vendor/hindsight-memory/scripts/retain.py +12 -6
- package/vendor/hindsight-memory/scripts/tests/test_directives.py +80 -9
- package/vendor/hindsight-memory/scripts/tests/test_recall_integration.py +362 -18
- package/vendor/hindsight-memory/settings.json +1 -1
|
@@ -20663,7 +20663,7 @@ var require_dist = __commonJS((exports) => {
|
|
|
20663
20663
|
});
|
|
20664
20664
|
|
|
20665
20665
|
// ../src/config/schema.ts
|
|
20666
|
-
var CodeRepoEntrySchema, AgentBindMountSchema, HttpDiffPollSchema, PollSpecSchema, TelegramMessageActionSchema, WebhookActionSchema, ActionSpecSchema, ScheduleEntrySchema, AgentSoulSchema, AgentToolsSchema, 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, ReleaseBlock, NetworkIsolationSchema, servesField, knowsField, profileFields, ProfileSchema, _omitExtends, defaultsFields, AgentDefaultsSchema, AgentSchema, TelegramConfigSchema, MemoryBackendConfigSchema, VaultConfigSchema, QuotaConfigSchema, AutoReleaseCheckSchema, HostControlConfigSchema, WebServiceConfigSchema, FleetHealthConfigSchema, HostdConfigSchema, CronEgressSchema, CronConfigSchema, UserSchema, SwitchroomConfigSchema;
|
|
20666
|
+
var CodeRepoEntrySchema, AgentBindMountSchema, HttpDiffPollSchema, PollSpecSchema, TelegramMessageActionSchema, WebhookActionSchema, ActionSpecSchema, ScheduleEntrySchema, AgentSoulSchema, AgentToolsSchema, 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;
|
|
20667
20667
|
var init_schema = __esm(() => {
|
|
20668
20668
|
init_zod();
|
|
20669
20669
|
CodeRepoEntrySchema = exports_external.object({
|
|
@@ -20814,7 +20814,7 @@ var init_schema = __esm(() => {
|
|
|
20814
20814
|
recall: exports_external.object({
|
|
20815
20815
|
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)."),
|
|
20816
20816
|
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."),
|
|
20817
|
-
min_overlap: exports_external.number().min(0).max(1).optional().describe("Minimum
|
|
20817
|
+
min_overlap: exports_external.number().min(0).max(1).optional().describe("Minimum containment token overlap [0.0\u20131.0] between the user " + "prompt and a memory's text for the memory to be injected. " + "A cheap FLOOR that removes candidates with (near-)zero lexical " + "relationship to the prompt \u2014 NOT a precision control: at the " + "recommended 0.10 the gate is near-passthrough, and the " + "effective precision control is the engine rerank plus the " + "max_memories head-slice. 0.0 disables (default \u2014 current " + "behaviour). Use 0.10; values at or above 0.20 measurably " + "starve recall \u2014 on production replay 0.20 leaves ~41.9% of " + "turns with NO memories at all, re-creating the bug #3541 " + "fixed. Observe the `overlap_dropped` field via " + "`switchroom memory recall-log`."),
|
|
20818
20818
|
types: exports_external.array(exports_external.string()).optional().describe("Hindsight fact types to recall. Switchroom default is " + '["world", "experience", "observation"] \u2014 the synthesized ' + "`observation` tier is on by default. Set to " + '["world", "experience"] to opt out of observation-backed ' + "recall for this agent (or fleet-wide under defaults)."),
|
|
20819
20819
|
additional_banks: exports_external.array(exports_external.string()).optional().describe("Extra Hindsight banks to recall from on every turn, merged into " + "the agent's own bank results \u2014 e.g. a shared operator/household " + "profile bank authored via `switchroom memory profile`. Each is " + "recalled with an 8s timeout and is non-fatal on failure. Stays " + "within the single tenant: all banks are the operator's data, in " + "the operator's Hindsight instance (see the `single-tenant` " + "invariant). Defaults to [] (no extra banks)."),
|
|
20820
20820
|
sender_banks: exports_external.record(exports_external.string(), exports_external.string()).optional().describe("Per-speaker recall routing: a map of Telegram sender \u2192 extra " + "recall bank. When a message arrives, the agent also recalls the " + "speaker's bank (matched by Telegram username \u2014 a leading @ is " + "optional \u2014 or numeric user_id), merged " + "into its own results \u2014 so each trusted user gets their own " + "profile context. Additive recall scoping within the single " + "tenant: never an access boundary (who may drive an agent stays " + "the per-agent user assignment in `access.allowFrom`). Author the " + "banks via `switchroom memory profile`."),
|
|
@@ -21126,9 +21126,16 @@ var init_schema = __esm(() => {
|
|
|
21126
21126
|
enabled: exports_external.boolean().optional().describe("Master switch for the reaction-dispatch path. Default false \u2014 " + "with no reaction_dispatch block, reactions are persisted (and may " + "feed the `reactions` feedback path) but are NEVER dispatched as " + "event-driven inbound turns."),
|
|
21127
21127
|
emojis: exports_external.array(exports_external.string()).optional().describe('Emoji allowlist that triggers a `<channel event="reaction">` ' + "inbound turn when reacted to any message. Default [] (nothing " + "fires). Cascade mode: REPLACE (not union) \u2014 a layer's list " + "replaces lower layers entirely so an operator can narrow per-agent.")
|
|
21128
21128
|
}).optional();
|
|
21129
|
-
|
|
21129
|
+
releaseBlockFields = {
|
|
21130
21130
|
channel: exports_external.enum(["dev", "rc", "latest"]).optional(),
|
|
21131
21131
|
pin: exports_external.string().regex(/^(sha-[0-9a-f]{7,40}|v\d+\.\d+\.\d+)$/).optional()
|
|
21132
|
+
};
|
|
21133
|
+
ReleaseBlock = exports_external.object(releaseBlockFields).strict().refine((r) => !(r.channel && r.pin), {
|
|
21134
|
+
message: "release.channel and release.pin are mutually exclusive"
|
|
21135
|
+
});
|
|
21136
|
+
RootReleaseBlock = exports_external.object({
|
|
21137
|
+
...releaseBlockFields,
|
|
21138
|
+
auto_update: exports_external.boolean().optional().describe("Opt-in unattended fleet auto-update (KEN-131). When true, hostd's " + "release watcher polls the published release version and, on a new " + "release, drives the EXISTING staggered canary rollout " + "(`switchroom rollout --pin vX.Y.Z`) unattended: canary-first, " + "per-agent version assert, durable pin persisted only after the " + "canary is green, abort + operator alert card on canary failure, " + "and compose rollback on a failed apply. Default false \u2014 with it " + "unset/false, behaviour is unchanged.")
|
|
21132
21139
|
}).strict().refine((r) => !(r.channel && r.pin), {
|
|
21133
21140
|
message: "release.channel and release.pin are mutually exclusive"
|
|
21134
21141
|
});
|
|
@@ -21372,6 +21379,7 @@ var init_schema = __esm(() => {
|
|
|
21372
21379
|
enabled: exports_external.boolean().default(false).describe("When true, hostd polls the remote release tag every " + "`interval_minutes` and applies + restarts the fleet when a new " + "release is detected. Default false \u2014 opt-in."),
|
|
21373
21380
|
interval_minutes: exports_external.number().int().min(5).max(1440).default(5).describe("Poll interval in minutes. Floor of 5m matches the agent-config " + "cron rate limit; ceiling of 1440m (24h) is a sanity cap."),
|
|
21374
21381
|
apply_on_detect: exports_external.boolean().default(true).describe("When false, hostd logs `release_detected` but does NOT call " + "update_apply / restart all. Useful for dogfooding the detector " + "without rolling the fleet."),
|
|
21382
|
+
notify_on_detect: exports_external.boolean().default(false).describe("KEN-129 \u2014 operator-in-the-loop update prompt. Only consulted " + "when apply_on_detect is false (auto-apply supersedes notify): " + "a newly detected release posts ONE operator approval card " + "('fleet is behind \u2014 tap to apply') via an admin agent's " + "gateway; Approve runs hostd's update_apply path (fleet-" + "mutation-locked, durable status rows, get_status-pollable). " + "Dedup on release id: the last-notified id persists in " + "~/.switchroom/release-notify-state.json, so a card that " + "reached the operator is never re-posted for the same release."),
|
|
21375
21383
|
image_ref: exports_external.string().default("ghcr.io/switchroom/switchroom-agent:latest").describe("Image reference whose remote digest is compared to the local " + "image digest. Defaults to the agent image's :latest tag, which " + "is the canonical signal that a release has been promoted.")
|
|
21376
21384
|
});
|
|
21377
21385
|
HostControlConfigSchema = exports_external.object({
|
|
@@ -21414,7 +21422,7 @@ var init_schema = __esm(() => {
|
|
|
21414
21422
|
timezone: exports_external.string().regex(TIMEZONE_REGEX, "timezone must be an IANA zone name like 'Australia/Melbourne' or 'UTC'").optional().describe("Global default IANA timezone applied to every agent unless the " + "agent (or its profile) declares its own. See the per-agent " + "timezone field for the full cascade and auto-detection fallback.")
|
|
21415
21423
|
}),
|
|
21416
21424
|
telegram: TelegramConfigSchema,
|
|
21417
|
-
release:
|
|
21425
|
+
release: RootReleaseBlock.optional().describe("Fleet-wide default release-channel pin / pointer for the update " + "flow. Either `channel` (dev|rc|latest) or `pin` (sha-<hex>|v<semver>) " + "\u2014 mutually exclusive. Per-agent `release` REPLACES this entirely " + "(except `auto_update`, which is root-only \u2014 a fleet property read by " + "hostd's release watcher, never per-agent)."),
|
|
21418
21426
|
memory: MemoryBackendConfigSchema.optional(),
|
|
21419
21427
|
hindsight: HindsightConfigSchema.optional().describe("Fleet-singleton Hindsight (memory backend) configuration. Currently " + "just the LLM knob (provider + model) used for retain/reflect/" + "consolidation. Both fields optional; when unset the container falls " + "back to provider=claude-code + the hard-coded HINDSIGHT_DEFAULT_MODEL " + "so nothing changes for operators who don't set it. Read at container " + "launch by startHindsight() (src/setup/hindsight.ts) \u2014 takes effect on " + "the next `switchroom apply` / `memory setup --recreate`."),
|
|
21420
21428
|
vault: VaultConfigSchema.optional(),
|
|
@@ -29495,6 +29503,79 @@ var init_atomic = __esm(() => {
|
|
|
29495
29503
|
TMP_OPEN_FLAGS = constants2.O_WRONLY | constants2.O_CREAT | constants2.O_EXCL | (constants2.O_NOFOLLOW ?? 0);
|
|
29496
29504
|
});
|
|
29497
29505
|
|
|
29506
|
+
// ../src/agents/generation-stamp.ts
|
|
29507
|
+
import { createHash as createHash5 } from "node:crypto";
|
|
29508
|
+
import { existsSync as existsSync33, readFileSync as readFileSync32, writeFileSync as writeFileSync29 } from "node:fs";
|
|
29509
|
+
import { join as join38 } from "node:path";
|
|
29510
|
+
function sha256Text(s) {
|
|
29511
|
+
return createHash5("sha256").update(s, "utf-8").digest("hex");
|
|
29512
|
+
}
|
|
29513
|
+
function hashManagedClaudeMd(content3) {
|
|
29514
|
+
const idx = content3.indexOf(CLAUDE_MD_YOURS_MARKER);
|
|
29515
|
+
const managed = idx === -1 ? content3 : content3.slice(0, idx + CLAUDE_MD_YOURS_MARKER.length);
|
|
29516
|
+
return sha256Text(managed);
|
|
29517
|
+
}
|
|
29518
|
+
function hashStampedFileOnDisk(agentDir, rel) {
|
|
29519
|
+
const p = join38(agentDir, rel);
|
|
29520
|
+
if (!existsSync33(p))
|
|
29521
|
+
return null;
|
|
29522
|
+
let content3;
|
|
29523
|
+
try {
|
|
29524
|
+
content3 = readFileSync32(p, "utf-8");
|
|
29525
|
+
} catch {
|
|
29526
|
+
return null;
|
|
29527
|
+
}
|
|
29528
|
+
return rel === "CLAUDE.md" ? hashManagedClaudeMd(content3) : sha256Text(content3);
|
|
29529
|
+
}
|
|
29530
|
+
function readGenerationStamp(agentDir) {
|
|
29531
|
+
const p = join38(agentDir, GENERATION_STAMP_FILE);
|
|
29532
|
+
if (!existsSync33(p))
|
|
29533
|
+
return null;
|
|
29534
|
+
try {
|
|
29535
|
+
const parsed = JSON.parse(readFileSync32(p, "utf-8"));
|
|
29536
|
+
if (parsed?.version !== 1 || typeof parsed.files !== "object" || parsed.files === null || Array.isArray(parsed.files)) {
|
|
29537
|
+
return null;
|
|
29538
|
+
}
|
|
29539
|
+
return parsed;
|
|
29540
|
+
} catch {
|
|
29541
|
+
return null;
|
|
29542
|
+
}
|
|
29543
|
+
}
|
|
29544
|
+
function detectStampDrift(agentDir, opts = {}) {
|
|
29545
|
+
const stamp = readGenerationStamp(agentDir);
|
|
29546
|
+
if (!stamp)
|
|
29547
|
+
return { hasStamp: false, findings: [] };
|
|
29548
|
+
const findings = [];
|
|
29549
|
+
for (const rel of STAMPED_FILES) {
|
|
29550
|
+
const stamped = stamp.files[rel];
|
|
29551
|
+
if (!stamped)
|
|
29552
|
+
continue;
|
|
29553
|
+
const current = hashStampedFileOnDisk(agentDir, rel);
|
|
29554
|
+
if (current === null) {
|
|
29555
|
+
findings.push({ surface: rel, detail: "missing (was present at last apply)" });
|
|
29556
|
+
} else if (current !== stamped) {
|
|
29557
|
+
findings.push({ surface: rel, detail: "modified since last apply" });
|
|
29558
|
+
}
|
|
29559
|
+
}
|
|
29560
|
+
if (opts.currentConfigHash && opts.currentConfigHash !== stamp.configHash) {
|
|
29561
|
+
findings.push({
|
|
29562
|
+
surface: "config",
|
|
29563
|
+
detail: "switchroom.yaml changed since last apply (generated files are stale)"
|
|
29564
|
+
});
|
|
29565
|
+
}
|
|
29566
|
+
if (opts.currentVersion && opts.currentVersion !== stamp.switchroomVersion) {
|
|
29567
|
+
findings.push({
|
|
29568
|
+
surface: "version",
|
|
29569
|
+
detail: `generated by switchroom ${stamp.switchroomVersion}, installed ${opts.currentVersion} \u2014 re-apply to refresh`
|
|
29570
|
+
});
|
|
29571
|
+
}
|
|
29572
|
+
return { hasStamp: true, findings };
|
|
29573
|
+
}
|
|
29574
|
+
var GENERATION_STAMP_FILE = ".switchroom-generated.json", CLAUDE_MD_YOURS_MARKER = "# --- Yours (preserved across apply) ---", STAMPED_FILES;
|
|
29575
|
+
var init_generation_stamp = __esm(() => {
|
|
29576
|
+
STAMPED_FILES = ["start.sh", "CLAUDE.md", ".mcp.json"];
|
|
29577
|
+
});
|
|
29578
|
+
|
|
29498
29579
|
// ../node_modules/.bun/handlebars@4.7.9/node_modules/handlebars/dist/cjs/handlebars/utils.js
|
|
29499
29580
|
var require_utils = __commonJS((exports2) => {
|
|
29500
29581
|
exports2.__esModule = true;
|
|
@@ -32451,7 +32532,7 @@ var require_util = __commonJS((exports2) => {
|
|
|
32451
32532
|
return path2;
|
|
32452
32533
|
}
|
|
32453
32534
|
exports2.normalize = normalize;
|
|
32454
|
-
function
|
|
32535
|
+
function join40(aRoot, aPath) {
|
|
32455
32536
|
if (aRoot === "") {
|
|
32456
32537
|
aRoot = ".";
|
|
32457
32538
|
}
|
|
@@ -32483,7 +32564,7 @@ var require_util = __commonJS((exports2) => {
|
|
|
32483
32564
|
}
|
|
32484
32565
|
return joined;
|
|
32485
32566
|
}
|
|
32486
|
-
exports2.join =
|
|
32567
|
+
exports2.join = join40;
|
|
32487
32568
|
exports2.isAbsolute = function(aPath) {
|
|
32488
32569
|
return aPath.charAt(0) === "/" || urlRegexp.test(aPath);
|
|
32489
32570
|
};
|
|
@@ -32656,7 +32737,7 @@ var require_util = __commonJS((exports2) => {
|
|
|
32656
32737
|
parsed.path = parsed.path.substring(0, index2 + 1);
|
|
32657
32738
|
}
|
|
32658
32739
|
}
|
|
32659
|
-
sourceURL =
|
|
32740
|
+
sourceURL = join40(urlGenerate(parsed), sourceURL);
|
|
32660
32741
|
}
|
|
32661
32742
|
return normalize(sourceURL);
|
|
32662
32743
|
}
|
|
@@ -35261,19 +35342,19 @@ function renderAuthLine(state7, agentName3, now = Date.now()) {
|
|
|
35261
35342
|
}
|
|
35262
35343
|
|
|
35263
35344
|
// gateway/quota-cache.ts
|
|
35264
|
-
import { existsSync as
|
|
35265
|
-
import { join as
|
|
35345
|
+
import { existsSync as existsSync44, readFileSync as readFileSync45, writeFileSync as writeFileSync38, mkdirSync as mkdirSync38 } from "fs";
|
|
35346
|
+
import { join as join49, dirname as dirname15 } from "path";
|
|
35266
35347
|
function defaultCachePath() {
|
|
35267
|
-
return process.env.SWITCHROOM_QUOTA_CACHE_PATH ??
|
|
35348
|
+
return process.env.SWITCHROOM_QUOTA_CACHE_PATH ?? join49(process.env.HOME ?? "/tmp", ".switchroom", "quota-cache.json");
|
|
35268
35349
|
}
|
|
35269
35350
|
function readQuotaCache(opts = {}) {
|
|
35270
35351
|
const path2 = opts.path ?? defaultCachePath();
|
|
35271
35352
|
const now = opts.now ?? Date.now();
|
|
35272
|
-
if (!
|
|
35353
|
+
if (!existsSync44(path2))
|
|
35273
35354
|
return null;
|
|
35274
35355
|
let entry;
|
|
35275
35356
|
try {
|
|
35276
|
-
entry = JSON.parse(
|
|
35357
|
+
entry = JSON.parse(readFileSync45(path2, "utf8"));
|
|
35277
35358
|
} catch {
|
|
35278
35359
|
return null;
|
|
35279
35360
|
}
|
|
@@ -35300,7 +35381,7 @@ function writeQuotaCache(result, opts = {}) {
|
|
|
35300
35381
|
};
|
|
35301
35382
|
try {
|
|
35302
35383
|
mkdirSync38(dirname15(path2), { recursive: true });
|
|
35303
|
-
|
|
35384
|
+
writeFileSync38(path2, JSON.stringify(entry, null, 2), { mode: 384 });
|
|
35304
35385
|
} catch {}
|
|
35305
35386
|
}
|
|
35306
35387
|
var DEFAULT_TTL_MS4, RATE_LIMIT_TTL_MS;
|
|
@@ -35310,8 +35391,8 @@ var init_quota_cache = __esm(() => {
|
|
|
35310
35391
|
});
|
|
35311
35392
|
|
|
35312
35393
|
// gateway/boot-probes.ts
|
|
35313
|
-
import { readFileSync as
|
|
35314
|
-
import { join as
|
|
35394
|
+
import { readFileSync as readFileSync46, readdirSync as readdirSync10, existsSync as existsSync45 } from "fs";
|
|
35395
|
+
import { join as join50 } from "path";
|
|
35315
35396
|
import { execFile as execFileCb } from "child_process";
|
|
35316
35397
|
import { promisify } from "util";
|
|
35317
35398
|
async function withTimeout(label, p, timeoutMs = PROBE_TIMEOUT_MS) {
|
|
@@ -35353,11 +35434,11 @@ function mapPlan(billingType, hasExtra) {
|
|
|
35353
35434
|
}
|
|
35354
35435
|
async function probeAccount(agentDir) {
|
|
35355
35436
|
return withTimeout("Account", (async () => {
|
|
35356
|
-
const claudeDir =
|
|
35357
|
-
const claudeJsonPath =
|
|
35437
|
+
const claudeDir = join50(agentDir, ".claude");
|
|
35438
|
+
const claudeJsonPath = join50(claudeDir, ".claude.json");
|
|
35358
35439
|
let cfg = {};
|
|
35359
35440
|
try {
|
|
35360
|
-
const raw =
|
|
35441
|
+
const raw = readFileSync46(claudeJsonPath, "utf8");
|
|
35361
35442
|
cfg = JSON.parse(raw);
|
|
35362
35443
|
} catch {
|
|
35363
35444
|
return { status: "fail", label: "Account", detail: "no .claude.json" };
|
|
@@ -35375,12 +35456,12 @@ async function probeAccount(agentDir) {
|
|
|
35375
35456
|
let tokenStr = "";
|
|
35376
35457
|
let status = "ok";
|
|
35377
35458
|
for (const candidate of [
|
|
35378
|
-
|
|
35379
|
-
|
|
35459
|
+
join50(claudeDir, ".oauth-token.meta.json"),
|
|
35460
|
+
join50(claudeDir, "accounts", "default", ".oauth-token.meta.json")
|
|
35380
35461
|
]) {
|
|
35381
|
-
if (
|
|
35462
|
+
if (existsSync45(candidate)) {
|
|
35382
35463
|
try {
|
|
35383
|
-
const meta = JSON.parse(
|
|
35464
|
+
const meta = JSON.parse(readFileSync46(candidate, "utf8"));
|
|
35384
35465
|
if (meta.expiresAt) {
|
|
35385
35466
|
tokenStr = " \u00b7 " + formatDaysFromNow(meta.expiresAt);
|
|
35386
35467
|
const daysLeft = Math.round((meta.expiresAt - Date.now()) / 86400000);
|
|
@@ -35555,9 +35636,9 @@ async function resolveTmuxSupervisorPid(agentName3, execFileImpl) {
|
|
|
35555
35636
|
if (!cgroup)
|
|
35556
35637
|
return null;
|
|
35557
35638
|
const procsPath = `/sys/fs/cgroup${cgroup}/cgroup.procs`;
|
|
35558
|
-
if (!
|
|
35639
|
+
if (!existsSync45(procsPath))
|
|
35559
35640
|
return null;
|
|
35560
|
-
const pidsRaw =
|
|
35641
|
+
const pidsRaw = readFileSync46(procsPath, "utf-8");
|
|
35561
35642
|
const pids = pidsRaw.split(`
|
|
35562
35643
|
`).map((s) => s.trim()).filter(Boolean);
|
|
35563
35644
|
if (pids.length === 0)
|
|
@@ -35570,7 +35651,7 @@ async function resolveTmuxSupervisorPid(agentName3, execFileImpl) {
|
|
|
35570
35651
|
let rss = 0;
|
|
35571
35652
|
let comm = "";
|
|
35572
35653
|
try {
|
|
35573
|
-
const status =
|
|
35654
|
+
const status = readFileSync46(`/proc/${pid}/status`, "utf-8");
|
|
35574
35655
|
const rssLine = status.split(`
|
|
35575
35656
|
`).find((l) => l.startsWith("VmRSS:"));
|
|
35576
35657
|
if (rssLine) {
|
|
@@ -35582,7 +35663,7 @@ async function resolveTmuxSupervisorPid(agentName3, execFileImpl) {
|
|
|
35582
35663
|
continue;
|
|
35583
35664
|
}
|
|
35584
35665
|
try {
|
|
35585
|
-
comm =
|
|
35666
|
+
comm = readFileSync46(`/proc/${pid}/comm`, "utf-8").trim();
|
|
35586
35667
|
} catch {}
|
|
35587
35668
|
candidates.push({ pid, rss, comm });
|
|
35588
35669
|
}
|
|
@@ -35762,9 +35843,9 @@ async function probeQuota(claudeConfigDir, _agentDir, fetchImpl = fetch, opts =
|
|
|
35762
35843
|
let claudeDirForProbe = null;
|
|
35763
35844
|
for (const candidate of [
|
|
35764
35845
|
claudeConfigDir,
|
|
35765
|
-
|
|
35846
|
+
join50(claudeConfigDir, "accounts", "default")
|
|
35766
35847
|
]) {
|
|
35767
|
-
if (
|
|
35848
|
+
if (existsSync45(join50(candidate, ".oauth-token"))) {
|
|
35768
35849
|
claudeDirForProbe = candidate;
|
|
35769
35850
|
break;
|
|
35770
35851
|
}
|
|
@@ -35829,7 +35910,7 @@ async function probeHindsight(bankName, fetchImpl = fetch) {
|
|
|
35829
35910
|
}
|
|
35830
35911
|
function readContainerBootTimeMsForProbe() {
|
|
35831
35912
|
try {
|
|
35832
|
-
const stat1 =
|
|
35913
|
+
const stat1 = readFileSync46("/proc/1/stat", "utf8");
|
|
35833
35914
|
const lastParen = stat1.lastIndexOf(")");
|
|
35834
35915
|
if (lastParen < 0)
|
|
35835
35916
|
return null;
|
|
@@ -35837,7 +35918,7 @@ function readContainerBootTimeMsForProbe() {
|
|
|
35837
35918
|
const starttimeTicks = Number(after[19]);
|
|
35838
35919
|
if (!Number.isFinite(starttimeTicks))
|
|
35839
35920
|
return null;
|
|
35840
|
-
const procStat =
|
|
35921
|
+
const procStat = readFileSync46("/proc/stat", "utf8");
|
|
35841
35922
|
const btimeLine = procStat.split(`
|
|
35842
35923
|
`).find((l) => l.startsWith("btime "));
|
|
35843
35924
|
if (!btimeLine)
|
|
@@ -35935,7 +36016,7 @@ async function probeUds(label, socketPath, opts = {}) {
|
|
|
35935
36016
|
}
|
|
35936
36017
|
return withTimeout(label, (async () => {
|
|
35937
36018
|
if (!opts.connectImpl) {
|
|
35938
|
-
if (!
|
|
36019
|
+
if (!existsSync45(socketPath)) {
|
|
35939
36020
|
return {
|
|
35940
36021
|
status: "fail",
|
|
35941
36022
|
label,
|
|
@@ -35999,7 +36080,7 @@ async function probeSkills(agentDir, opts = {}) {
|
|
|
35999
36080
|
return withTimeout("Skills", (async () => {
|
|
36000
36081
|
const fs2 = opts.fs ?? realSkillsFs;
|
|
36001
36082
|
const max = opts.maxNamesShown ?? 3;
|
|
36002
|
-
const skillsDir =
|
|
36083
|
+
const skillsDir = join50(agentDir, ".claude", "skills");
|
|
36003
36084
|
if (!fs2.exists(skillsDir)) {
|
|
36004
36085
|
return { status: "ok", label: "Skills", detail: "no skills dir" };
|
|
36005
36086
|
}
|
|
@@ -36014,17 +36095,17 @@ async function probeSkills(agentDir, opts = {}) {
|
|
|
36014
36095
|
}
|
|
36015
36096
|
const dangling = [];
|
|
36016
36097
|
for (const name of entries) {
|
|
36017
|
-
const skillPath =
|
|
36098
|
+
const skillPath = join50(skillsDir, name);
|
|
36018
36099
|
if (!fs2.exists(skillPath)) {
|
|
36019
36100
|
dangling.push(name);
|
|
36020
36101
|
continue;
|
|
36021
36102
|
}
|
|
36022
|
-
const skillMd =
|
|
36103
|
+
const skillMd = join50(skillPath, "SKILL.md");
|
|
36023
36104
|
if (!fs2.exists(skillMd) && !fs2.exists(skillPath + ".md")) {
|
|
36024
36105
|
continue;
|
|
36025
36106
|
}
|
|
36026
36107
|
}
|
|
36027
|
-
const overlayDir = opts.overlaySkillsDir ??
|
|
36108
|
+
const overlayDir = opts.overlaySkillsDir ?? join50(agentDir, "skills.d");
|
|
36028
36109
|
const overlaySlugs = new Set;
|
|
36029
36110
|
if (fs2.exists(overlayDir)) {
|
|
36030
36111
|
let overlayEntries = [];
|
|
@@ -36066,8 +36147,8 @@ function renderBucketedSkills(switchroom, agent) {
|
|
|
36066
36147
|
}
|
|
36067
36148
|
async function probeConnections(agentDir, opts = {}) {
|
|
36068
36149
|
return withTimeout("Connections", (async () => {
|
|
36069
|
-
const path2 =
|
|
36070
|
-
const read = opts.readFileImpl ?? ((p) =>
|
|
36150
|
+
const path2 = join50(agentDir, ".claude", "connection-health.json");
|
|
36151
|
+
const read = opts.readFileImpl ?? ((p) => readFileSync46(p, "utf8"));
|
|
36071
36152
|
let issues = [];
|
|
36072
36153
|
try {
|
|
36073
36154
|
const parsed = JSON.parse(read(path2));
|
|
@@ -36091,31 +36172,87 @@ async function probeConnections(agentDir, opts = {}) {
|
|
|
36091
36172
|
};
|
|
36092
36173
|
})());
|
|
36093
36174
|
}
|
|
36175
|
+
async function probeDrift(agentDir, opts = {}) {
|
|
36176
|
+
return withTimeout("Drift", (async () => {
|
|
36177
|
+
const surfaces = [];
|
|
36178
|
+
let stampGeneratedAtMs = NaN;
|
|
36179
|
+
try {
|
|
36180
|
+
const stampResult = detectStampDrift(agentDir);
|
|
36181
|
+
for (const f of stampResult.findings) {
|
|
36182
|
+
surfaces.push(`${f.surface} (${f.detail})`);
|
|
36183
|
+
}
|
|
36184
|
+
if (stampResult.hasStamp) {
|
|
36185
|
+
const raw = readGenerationStamp(agentDir);
|
|
36186
|
+
if (raw?.generatedAt)
|
|
36187
|
+
stampGeneratedAtMs = Date.parse(raw.generatedAt);
|
|
36188
|
+
}
|
|
36189
|
+
} catch {}
|
|
36190
|
+
try {
|
|
36191
|
+
const raw = readFileSync46(join50(agentDir, ".switchroom-drift.json"), "utf8");
|
|
36192
|
+
const report = JSON.parse(raw);
|
|
36193
|
+
if (report?.version === 1 && Array.isArray(report.findings)) {
|
|
36194
|
+
const gen = report.generatedAt ? Date.parse(report.generatedAt) : NaN;
|
|
36195
|
+
const supersededByApply = Number.isFinite(stampGeneratedAtMs) && Number.isFinite(gen) && gen < stampGeneratedAtMs;
|
|
36196
|
+
if (!supersededByApply) {
|
|
36197
|
+
const seen = new Set(surfaces.map((s) => s.split(" ")[0]));
|
|
36198
|
+
let ageNote = "";
|
|
36199
|
+
if (Number.isFinite(gen)) {
|
|
36200
|
+
const ageMs = (opts.nowMs?.() ?? Date.now()) - gen;
|
|
36201
|
+
if (ageMs > 3600000) {
|
|
36202
|
+
const hours = Math.round(ageMs / 3600000);
|
|
36203
|
+
const human = hours >= 48 ? `${Math.round(hours / 24)}d` : `${hours}h`;
|
|
36204
|
+
ageNote = ` \u2014 as of ${human} ago`;
|
|
36205
|
+
}
|
|
36206
|
+
}
|
|
36207
|
+
for (const f of report.findings) {
|
|
36208
|
+
if (!f?.surface || seen.has(f.surface))
|
|
36209
|
+
continue;
|
|
36210
|
+
seen.add(f.surface);
|
|
36211
|
+
surfaces.push(`${f.surface}${ageNote}`);
|
|
36212
|
+
}
|
|
36213
|
+
}
|
|
36214
|
+
}
|
|
36215
|
+
} catch {}
|
|
36216
|
+
if (surfaces.length === 0) {
|
|
36217
|
+
return { status: "ok", label: "Drift", detail: "generated surfaces in sync" };
|
|
36218
|
+
}
|
|
36219
|
+
const shown = surfaces.slice(0, 4).join(", ");
|
|
36220
|
+
const more = surfaces.length > 4 ? ` +${surfaces.length - 4} more` : "";
|
|
36221
|
+
const target = opts.agentName ? ` ${opts.agentName}` : "";
|
|
36222
|
+
return {
|
|
36223
|
+
status: "degraded",
|
|
36224
|
+
label: "Drift",
|
|
36225
|
+
detail: `${surfaces.length} drifted surface(s): ${shown}${more}`,
|
|
36226
|
+
nextStep: `Run \`switchroom apply\` (or \`switchroom agent reconcile${target} --restart\`); see \`switchroom doctor\` for detail`
|
|
36227
|
+
};
|
|
36228
|
+
})());
|
|
36229
|
+
}
|
|
36094
36230
|
var execFile, PROBE_TIMEOUT_MS = 2000, QUOTA_BROKER_TIMEOUT_MS = 7000, QUOTA_DIRECT_FALLBACK_TIMEOUT_MS = 5000, QUOTA_PROBE_OUTER_TIMEOUT_MS = 9000, TOKEN_EXPIRING_SOON_DAYS = 7, AGENT_RETRY_INTERVAL_MS = 1500, AGENT_RETRY_MAX_MS = 12000, AGENT_LIVE_WINDOW_MS = 45000, AGENT_LIVE_POLL_INTERVAL_MS = 2000, AGENT_LIVE_FOLLOWUP_REPOLL_MS = 30000, realProcFs, SCHEDULER_LOCK_PATH_DEFAULT = "/state/agent/scheduler.lock", SCHEDULER_JSONL_PATH_DEFAULT = "/state/agent/scheduler.jsonl", SCHEDULER_FRESH_BOOT_MS = 30000, realSchedulerFs, realSkillsFs;
|
|
36095
36231
|
var init_boot_probes = __esm(() => {
|
|
36096
36232
|
init_quota_cache();
|
|
36233
|
+
init_generation_stamp();
|
|
36097
36234
|
init_quota_check();
|
|
36098
36235
|
execFile = promisify(execFileCb);
|
|
36099
36236
|
realProcFs = {
|
|
36100
36237
|
readdir: (p) => readdirSync10(p),
|
|
36101
|
-
readFile: (p) =>
|
|
36238
|
+
readFile: (p) => readFileSync46(p, "utf-8")
|
|
36102
36239
|
};
|
|
36103
36240
|
realSchedulerFs = {
|
|
36104
|
-
readFile: (p) =>
|
|
36241
|
+
readFile: (p) => readFileSync46(p, "utf-8"),
|
|
36105
36242
|
mtimeMs: (p) => {
|
|
36106
36243
|
const { statSync: statSync14 } = __require("fs");
|
|
36107
36244
|
return statSync14(p).mtimeMs;
|
|
36108
36245
|
},
|
|
36109
|
-
exists: (p) =>
|
|
36246
|
+
exists: (p) => existsSync45(p)
|
|
36110
36247
|
};
|
|
36111
36248
|
realSkillsFs = {
|
|
36112
36249
|
readdir: (p) => readdirSync10(p),
|
|
36113
|
-
exists: (p) =>
|
|
36250
|
+
exists: (p) => existsSync45(p)
|
|
36114
36251
|
};
|
|
36115
36252
|
});
|
|
36116
36253
|
|
|
36117
36254
|
// gateway/boot-issue-cache.ts
|
|
36118
|
-
import { existsSync as
|
|
36255
|
+
import { existsSync as existsSync46, readFileSync as readFileSync47, writeFileSync as writeFileSync39, mkdirSync as mkdirSync39, renameSync as renameSync19 } from "fs";
|
|
36119
36256
|
import { dirname as dirname16 } from "path";
|
|
36120
36257
|
function fingerprintProbe(key, r) {
|
|
36121
36258
|
if (r.status === "ok")
|
|
@@ -36195,11 +36332,11 @@ function diffProbes(probes, cache, opts = {}) {
|
|
|
36195
36332
|
return out;
|
|
36196
36333
|
}
|
|
36197
36334
|
function loadCache(path2, now = Date.now) {
|
|
36198
|
-
if (!
|
|
36335
|
+
if (!existsSync46(path2))
|
|
36199
36336
|
return { ...EMPTY_CACHE, probes: {} };
|
|
36200
36337
|
let raw;
|
|
36201
36338
|
try {
|
|
36202
|
-
raw =
|
|
36339
|
+
raw = readFileSync47(path2, "utf-8");
|
|
36203
36340
|
} catch {
|
|
36204
36341
|
return { ...EMPTY_CACHE, probes: {} };
|
|
36205
36342
|
}
|
|
@@ -36244,7 +36381,7 @@ function applyAndSave(path2, cache, diff) {
|
|
|
36244
36381
|
try {
|
|
36245
36382
|
mkdirSync39(dirname16(path2), { recursive: true });
|
|
36246
36383
|
const tmp = `${path2}.tmp`;
|
|
36247
|
-
|
|
36384
|
+
writeFileSync39(tmp, JSON.stringify(next), { mode: 384 });
|
|
36248
36385
|
renameSync19(tmp, path2);
|
|
36249
36386
|
} catch {}
|
|
36250
36387
|
return next;
|
|
@@ -36257,14 +36394,14 @@ var init_boot_issue_cache = __esm(() => {
|
|
|
36257
36394
|
});
|
|
36258
36395
|
|
|
36259
36396
|
// gateway/config-snapshot.ts
|
|
36260
|
-
import { createHash as
|
|
36261
|
-
import { existsSync as
|
|
36397
|
+
import { createHash as createHash6 } from "crypto";
|
|
36398
|
+
import { existsSync as existsSync47, readFileSync as readFileSync48, writeFileSync as writeFileSync40, mkdirSync as mkdirSync40, renameSync as renameSync20 } from "fs";
|
|
36262
36399
|
import { dirname as dirname17 } from "path";
|
|
36263
36400
|
function hashStringArray(items) {
|
|
36264
36401
|
if (!items || items.length === 0)
|
|
36265
36402
|
return null;
|
|
36266
36403
|
const sorted = [...items].sort();
|
|
36267
|
-
const raw =
|
|
36404
|
+
const raw = createHash6("sha256").update(sorted.join("\x00")).digest("hex");
|
|
36268
36405
|
return raw.slice(0, 12);
|
|
36269
36406
|
}
|
|
36270
36407
|
function normalizeModel(model) {
|
|
@@ -36323,11 +36460,11 @@ function renderConfigChangeDim(dim) {
|
|
|
36323
36460
|
}
|
|
36324
36461
|
}
|
|
36325
36462
|
function loadSnapshot(path2, now = Date.now) {
|
|
36326
|
-
if (!
|
|
36463
|
+
if (!existsSync47(path2))
|
|
36327
36464
|
return null;
|
|
36328
36465
|
let raw;
|
|
36329
36466
|
try {
|
|
36330
|
-
raw =
|
|
36467
|
+
raw = readFileSync48(path2, "utf-8");
|
|
36331
36468
|
} catch {
|
|
36332
36469
|
return null;
|
|
36333
36470
|
}
|
|
@@ -36359,7 +36496,7 @@ function persistSnapshot(path2, snapshot) {
|
|
|
36359
36496
|
try {
|
|
36360
36497
|
mkdirSync40(dirname17(path2), { recursive: true });
|
|
36361
36498
|
const tmp = `${path2}.tmp`;
|
|
36362
|
-
|
|
36499
|
+
writeFileSync40(tmp, JSON.stringify(snapshot), { mode: 384 });
|
|
36363
36500
|
renameSync20(tmp, path2);
|
|
36364
36501
|
} catch {}
|
|
36365
36502
|
}
|
|
@@ -36368,13 +36505,13 @@ var init_config_snapshot = __esm(() => {
|
|
|
36368
36505
|
});
|
|
36369
36506
|
|
|
36370
36507
|
// gateway/boot-card-msgid.ts
|
|
36371
|
-
import { readFileSync as
|
|
36508
|
+
import { readFileSync as readFileSync49, writeFileSync as writeFileSync41 } from "node:fs";
|
|
36372
36509
|
function bootCardChatKey(chatId, threadId) {
|
|
36373
36510
|
return `${chatId}:${threadId ?? ""}`;
|
|
36374
36511
|
}
|
|
36375
36512
|
function readStore(path2) {
|
|
36376
36513
|
try {
|
|
36377
|
-
const parsed = JSON.parse(
|
|
36514
|
+
const parsed = JSON.parse(readFileSync49(path2, "utf8"));
|
|
36378
36515
|
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
36379
36516
|
return parsed;
|
|
36380
36517
|
}
|
|
@@ -36393,7 +36530,7 @@ function saveBootCardMsgId(path2, chatKey3, messageId) {
|
|
|
36393
36530
|
if (store2[chatKey3] === messageId)
|
|
36394
36531
|
return;
|
|
36395
36532
|
store2[chatKey3] = messageId;
|
|
36396
|
-
|
|
36533
|
+
writeFileSync41(path2, JSON.stringify(store2), "utf8");
|
|
36397
36534
|
} catch {}
|
|
36398
36535
|
}
|
|
36399
36536
|
var init_boot_card_msgid = () => {};
|
|
@@ -36411,8 +36548,8 @@ __export(exports_boot_card, {
|
|
|
36411
36548
|
readRoutingMode: () => readRoutingMode,
|
|
36412
36549
|
containerBootStartMs: () => containerBootStartMs
|
|
36413
36550
|
});
|
|
36414
|
-
import { join as
|
|
36415
|
-
import { readFileSync as
|
|
36551
|
+
import { join as join51 } from "path";
|
|
36552
|
+
import { readFileSync as readFileSync50, statSync as statSync14 } from "fs";
|
|
36416
36553
|
function resolvePersonaName(slug, loadConfig3) {
|
|
36417
36554
|
try {
|
|
36418
36555
|
const config = loadConfig3 ? loadConfig3() : loadConfig();
|
|
@@ -36434,7 +36571,7 @@ function shouldSkipDuplicateBootCard(gate, site) {
|
|
|
36434
36571
|
}
|
|
36435
36572
|
return { skip: false };
|
|
36436
36573
|
}
|
|
36437
|
-
function containerBootStartMs(fsImpl = { readFileSync:
|
|
36574
|
+
function containerBootStartMs(fsImpl = { readFileSync: readFileSync50 }) {
|
|
36438
36575
|
try {
|
|
36439
36576
|
const btimeLine = fsImpl.readFileSync("/proc/stat", "utf-8").split(`
|
|
36440
36577
|
`).find((l) => l.startsWith("btime "));
|
|
@@ -36455,8 +36592,8 @@ function containerBootStartMs(fsImpl = { readFileSync: readFileSync49 }) {
|
|
|
36455
36592
|
return null;
|
|
36456
36593
|
}
|
|
36457
36594
|
}
|
|
36458
|
-
function readRoutingMode(agentDir, bootStartMs, fsImpl = { readFileSync:
|
|
36459
|
-
const path2 =
|
|
36595
|
+
function readRoutingMode(agentDir, bootStartMs, fsImpl = { readFileSync: readFileSync50, statSync: statSync14 }) {
|
|
36596
|
+
const path2 = join51(agentDir, ".routing-mode");
|
|
36460
36597
|
try {
|
|
36461
36598
|
const raw = fsImpl.readFileSync(path2, "utf-8");
|
|
36462
36599
|
const line = raw.split(`
|
|
@@ -36563,7 +36700,7 @@ function renderBootCard(opts) {
|
|
|
36563
36700
|
return stackCardLines(flatLines);
|
|
36564
36701
|
}
|
|
36565
36702
|
async function runAllProbes(opts) {
|
|
36566
|
-
const claudeDir =
|
|
36703
|
+
const claudeDir = join51(opts.agentDir, ".claude");
|
|
36567
36704
|
const probes = {};
|
|
36568
36705
|
const slug = opts.agentSlug ?? opts.agentName;
|
|
36569
36706
|
await Promise.allSettled([
|
|
@@ -36596,6 +36733,9 @@ async function runAllProbes(opts) {
|
|
|
36596
36733
|
}),
|
|
36597
36734
|
probeConnections(opts.agentDir).then((r) => {
|
|
36598
36735
|
probes.connections = r;
|
|
36736
|
+
}),
|
|
36737
|
+
probeDrift(opts.agentDir, { agentName: opts.agentSlug ?? opts.agentName }).then((r) => {
|
|
36738
|
+
probes.drift = r;
|
|
36599
36739
|
})
|
|
36600
36740
|
]);
|
|
36601
36741
|
return probes;
|
|
@@ -36854,7 +36994,8 @@ var init_boot_card = __esm(() => {
|
|
|
36854
36994
|
broker: "Broker",
|
|
36855
36995
|
kernel: "Kernel",
|
|
36856
36996
|
skills: "Skills",
|
|
36857
|
-
connections: "Connections"
|
|
36997
|
+
connections: "Connections",
|
|
36998
|
+
drift: "Drift"
|
|
36858
36999
|
};
|
|
36859
37000
|
PROBE_KEYS = [
|
|
36860
37001
|
"account",
|
|
@@ -36866,7 +37007,8 @@ var init_boot_card = __esm(() => {
|
|
|
36866
37007
|
"broker",
|
|
36867
37008
|
"kernel",
|
|
36868
37009
|
"skills",
|
|
36869
|
-
"connections"
|
|
37010
|
+
"connections",
|
|
37011
|
+
"drift"
|
|
36870
37012
|
];
|
|
36871
37013
|
REASON_EMOJI = {
|
|
36872
37014
|
planned: "\u2705",
|
|
@@ -37417,7 +37559,7 @@ __export(exports_tmux2, {
|
|
|
37417
37559
|
captureAgentPane: () => captureAgentPane2
|
|
37418
37560
|
});
|
|
37419
37561
|
import { execFileSync as execFileSync8 } from "node:child_process";
|
|
37420
|
-
import { chmodSync as chmodSync13, mkdirSync as mkdirSync48, readdirSync as readdirSync13, statSync as statSync19, unlinkSync as unlinkSync26, writeFileSync as
|
|
37562
|
+
import { chmodSync as chmodSync13, mkdirSync as mkdirSync48, readdirSync as readdirSync13, statSync as statSync19, unlinkSync as unlinkSync26, writeFileSync as writeFileSync50 } from "node:fs";
|
|
37421
37563
|
import { resolve as resolve12 } from "node:path";
|
|
37422
37564
|
function captureAgentPane2(opts) {
|
|
37423
37565
|
const { agentName: agentName3, agentDir, reason } = opts;
|
|
@@ -37468,7 +37610,7 @@ function captureAgentPane2(opts) {
|
|
|
37468
37610
|
` + `
|
|
37469
37611
|
`;
|
|
37470
37612
|
try {
|
|
37471
|
-
|
|
37613
|
+
writeFileSync50(outPath, Buffer.concat([Buffer.from(header, "utf8"), body]), {
|
|
37472
37614
|
mode: 384
|
|
37473
37615
|
});
|
|
37474
37616
|
} catch (err) {
|
|
@@ -37606,7 +37748,8 @@ function clipReason(reason) {
|
|
|
37606
37748
|
}
|
|
37607
37749
|
function buildConfigApprovalCardBody(args) {
|
|
37608
37750
|
const safeReason = clipReason(args.reason);
|
|
37609
|
-
const
|
|
37751
|
+
const safeTitle = args.title ? args.title.replace(/[\u0000-\u001f\u007f]+/g, " ").slice(0, 200) : undefined;
|
|
37752
|
+
const render = (diff) => `${safeTitle ?? "\uD83D\uDEE0 **Config edit proposed**"}
|
|
37610
37753
|
` + `Agent: \`${args.agentName}\`
|
|
37611
37754
|
` + `Reason: ${escapeMarkdown(safeReason)}
|
|
37612
37755
|
|
|
@@ -37651,7 +37794,8 @@ async function handleRequestConfigApproval(client3, msg, deps) {
|
|
|
37651
37794
|
const built = buildConfigApprovalCardBody({
|
|
37652
37795
|
agentName: msg.agentName,
|
|
37653
37796
|
reason: msg.reason,
|
|
37654
|
-
unifiedDiff: prelim
|
|
37797
|
+
unifiedDiff: prelim,
|
|
37798
|
+
...msg.title !== undefined ? { title: msg.title } : {}
|
|
37655
37799
|
});
|
|
37656
37800
|
const body = built.body;
|
|
37657
37801
|
const oversize = prelim !== msg.unifiedDiff || built.truncated;
|
|
@@ -38348,7 +38492,7 @@ __export(exports_materialize_bot_token, {
|
|
|
38348
38492
|
materializeBotToken: () => materializeBotToken,
|
|
38349
38493
|
BotTokenMaterializeError: () => BotTokenMaterializeError
|
|
38350
38494
|
});
|
|
38351
|
-
import { existsSync as
|
|
38495
|
+
import { existsSync as existsSync56 } from "node:fs";
|
|
38352
38496
|
function pickConfiguredToken(config, agentName3) {
|
|
38353
38497
|
if (agentName3) {
|
|
38354
38498
|
const agent = config.agents?.[agentName3];
|
|
@@ -38362,7 +38506,7 @@ function tryDirectVaultRead4(ref, config, passphrase) {
|
|
|
38362
38506
|
if (!passphrase)
|
|
38363
38507
|
return null;
|
|
38364
38508
|
const vaultPath = resolvePath(config.vault?.path ?? "~/.switchroom/vault.enc");
|
|
38365
|
-
if (!
|
|
38509
|
+
if (!existsSync56(vaultPath))
|
|
38366
38510
|
return null;
|
|
38367
38511
|
try {
|
|
38368
38512
|
const secrets = openVault(passphrase, vaultPath);
|
|
@@ -38514,11 +38658,11 @@ var init_approvals_commands = __esm(() => {
|
|
|
38514
38658
|
// gateway/gateway.ts
|
|
38515
38659
|
var import_grammy16 = __toESM(require_mod(), 1);
|
|
38516
38660
|
var import_runner3 = __toESM(require_mod3(), 1);
|
|
38517
|
-
import { randomBytes as randomBytes10, createHash as
|
|
38661
|
+
import { randomBytes as randomBytes10, createHash as createHash7 } from "crypto";
|
|
38518
38662
|
import { execFileSync as execFileSync9, execSync as execSync2, spawn as spawn2 } from "child_process";
|
|
38519
38663
|
import {
|
|
38520
|
-
readFileSync as
|
|
38521
|
-
writeFileSync as
|
|
38664
|
+
readFileSync as readFileSync61,
|
|
38665
|
+
writeFileSync as writeFileSync51,
|
|
38522
38666
|
mkdirSync as mkdirSync49,
|
|
38523
38667
|
readdirSync as readdirSync14,
|
|
38524
38668
|
rmSync as rmSync6,
|
|
@@ -38528,12 +38672,12 @@ import {
|
|
|
38528
38672
|
chmodSync as chmodSync14,
|
|
38529
38673
|
openSync as openSync12,
|
|
38530
38674
|
closeSync as closeSync12,
|
|
38531
|
-
existsSync as
|
|
38675
|
+
existsSync as existsSync57,
|
|
38532
38676
|
unlinkSync as unlinkSync27,
|
|
38533
38677
|
appendFileSync as appendFileSync9
|
|
38534
38678
|
} from "fs";
|
|
38535
38679
|
import { homedir as homedir20 } from "os";
|
|
38536
|
-
import { join as
|
|
38680
|
+
import { join as join63, sep as sep4, basename as basename15 } from "path";
|
|
38537
38681
|
|
|
38538
38682
|
// plugin-logger.ts
|
|
38539
38683
|
import { appendFileSync, mkdirSync, renameSync, statSync, existsSync } from "fs";
|
|
@@ -39942,8 +40086,89 @@ var ACRONYMS = new Set([
|
|
|
39942
40086
|
"HTTP",
|
|
39943
40087
|
"JSON",
|
|
39944
40088
|
"SQL",
|
|
39945
|
-
"UI"
|
|
40089
|
+
"UI",
|
|
40090
|
+
"HTTPS",
|
|
40091
|
+
"SSH",
|
|
40092
|
+
"DNS",
|
|
40093
|
+
"CLI",
|
|
40094
|
+
"AWS",
|
|
40095
|
+
"UTC",
|
|
40096
|
+
"MCP",
|
|
40097
|
+
"PDF",
|
|
40098
|
+
"ID",
|
|
40099
|
+
"OK",
|
|
40100
|
+
"VM",
|
|
40101
|
+
"LLM",
|
|
40102
|
+
"YAML",
|
|
40103
|
+
"RAM",
|
|
40104
|
+
"USB"
|
|
39946
40105
|
]);
|
|
40106
|
+
var ACRONYM_MAX_LEN = Math.max(...[...ACRONYMS].map((a) => a.length));
|
|
40107
|
+
var PATH_TOKEN_RE = /(?<![\w~./-])(?:~|\.{1,2}|[A-Za-z0-9_.@-]+)?(?:\/[A-Za-z0-9_.@+-]+){2,}\/?(?![\w/-])/g;
|
|
40108
|
+
function looksLikePath(m, segs) {
|
|
40109
|
+
if (/^(?:\/|\.{1,2}\/|~\/)/.test(m))
|
|
40110
|
+
return true;
|
|
40111
|
+
const last = segs[segs.length - 1];
|
|
40112
|
+
if (/\.[A-Za-z][A-Za-z0-9]{0,7}$/.test(last))
|
|
40113
|
+
return true;
|
|
40114
|
+
return !segs.every((sg) => /^[a-z][a-z0-9]*$/.test(sg));
|
|
40115
|
+
}
|
|
40116
|
+
function isSpeakableSegment(seg) {
|
|
40117
|
+
if (!/[A-Za-z]/.test(seg))
|
|
40118
|
+
return false;
|
|
40119
|
+
if (seg.length > 32)
|
|
40120
|
+
return false;
|
|
40121
|
+
if (/^[0-9a-f]{8,}$/i.test(seg))
|
|
40122
|
+
return false;
|
|
40123
|
+
return true;
|
|
40124
|
+
}
|
|
40125
|
+
function speakPaths(input) {
|
|
40126
|
+
return input.replace(PATH_TOKEN_RE, (m) => {
|
|
40127
|
+
const segs = m.split("/").filter((sg) => sg.length > 0);
|
|
40128
|
+
if (segs.length < 2)
|
|
40129
|
+
return m;
|
|
40130
|
+
if (segs.every((sg) => /^\d+$/.test(sg)))
|
|
40131
|
+
return m;
|
|
40132
|
+
if (!looksLikePath(m, segs))
|
|
40133
|
+
return m;
|
|
40134
|
+
const last = segs[segs.length - 1];
|
|
40135
|
+
return isSpeakableSegment(last) ? last : "a path";
|
|
40136
|
+
});
|
|
40137
|
+
}
|
|
40138
|
+
var BLOCK_MARKER_RE = /^[ \t]{0,3}(?:#{1,6}[ \t]+|[-*+][ \t]+|\d+[.)][ \t]+)/;
|
|
40139
|
+
var HEADING_MARKER_RE = /^[ \t]{0,3}#{1,6}[ \t]+/;
|
|
40140
|
+
function ensureTerminated(line) {
|
|
40141
|
+
const t = line.replace(/[ \t]+$/, "");
|
|
40142
|
+
if (!t.trim())
|
|
40143
|
+
return t;
|
|
40144
|
+
if (/[.!?:;]$/.test(t))
|
|
40145
|
+
return t;
|
|
40146
|
+
if (/,$/.test(t))
|
|
40147
|
+
return `${t.slice(0, -1)}.`;
|
|
40148
|
+
return `${t}.`;
|
|
40149
|
+
}
|
|
40150
|
+
function applyBlockPauses(input) {
|
|
40151
|
+
const lines = input.split(`
|
|
40152
|
+
`);
|
|
40153
|
+
const isBlock = lines.map((l) => BLOCK_MARKER_RE.test(l));
|
|
40154
|
+
const isHeading = lines.map((l) => HEADING_MARKER_RE.test(l));
|
|
40155
|
+
const stripped = lines.map((l, i) => isBlock[i] ? l.slice(l.match(BLOCK_MARKER_RE)[0].length) : l);
|
|
40156
|
+
const inUnit = [];
|
|
40157
|
+
for (let i = 0;i < stripped.length; i++) {
|
|
40158
|
+
inUnit[i] = isBlock[i] === true || i > 0 && inUnit[i - 1] === true && isHeading[i - 1] !== true && stripped[i].trim() !== "";
|
|
40159
|
+
}
|
|
40160
|
+
return stripped.map((line, i) => {
|
|
40161
|
+
const next = stripped[i + 1];
|
|
40162
|
+
const nextIsBlock = isBlock[i + 1] === true;
|
|
40163
|
+
const unitEndsHere = isHeading[i] === true || next === undefined || next.trim() === "" || nextIsBlock;
|
|
40164
|
+
if (inUnit[i] && unitEndsHere)
|
|
40165
|
+
return ensureTerminated(line);
|
|
40166
|
+
if (nextIsBlock && line.trim() !== "")
|
|
40167
|
+
return ensureTerminated(line);
|
|
40168
|
+
return line;
|
|
40169
|
+
}).join(`
|
|
40170
|
+
`);
|
|
40171
|
+
}
|
|
39947
40172
|
function normalizeForSpeech(input) {
|
|
39948
40173
|
if (!input)
|
|
39949
40174
|
return "";
|
|
@@ -39952,7 +40177,7 @@ function normalizeForSpeech(input) {
|
|
|
39952
40177
|
s = decodeHtmlEntities(s);
|
|
39953
40178
|
s = stripBackslashEscapes(s);
|
|
39954
40179
|
s = s.replace(/[\u{1F000}-\u{1FAFF}\u{1F1E6}-\u{1F1FF}\u{2600}-\u{27BF}\u{2B00}-\u{2BFF}\u{FE00}-\u{FE0F}\u{200D}\u{2B50}\u{3030}\u{303D}\u{3297}\u{3299}\u{24C2}]/gu, "");
|
|
39955
|
-
s = s.replace(
|
|
40180
|
+
s = s.replace(/(?<![\w:]):([a-z0-9][a-z0-9_+-]*):(?!\d)/gi, " ");
|
|
39956
40181
|
s = s.replace(/(^|\n)[ \t]*(`{3,}|~{3,})[^\n]*\n[\s\S]*?\n[ \t]*\2[ \t]*(?=\n|$)/g, `$1${CODE_BLOCK_PLACEHOLDER}.`);
|
|
39957
40182
|
s = s.replace(/(^|\n)[ \t]*(`{3,}|~{3,})[^\n]*\n[\s\S]*$/g, `$1${CODE_BLOCK_PLACEHOLDER}.`);
|
|
39958
40183
|
s = s.replace(/!\[([^\]]*)\]\([^)]*\)/g, "$1");
|
|
@@ -39961,6 +40186,7 @@ function normalizeForSpeech(input) {
|
|
|
39961
40186
|
s = s.replace(/\bhttps?:\/\/[^\s)]+/gi, "a link");
|
|
39962
40187
|
s = s.replace(/`([^`\n]+)`/g, "$1");
|
|
39963
40188
|
s = s.replace(/`/g, "");
|
|
40189
|
+
s = speakPaths(s);
|
|
39964
40190
|
s = s.replace(/\*\*\*(.+?)\*\*\*/g, "$1");
|
|
39965
40191
|
s = s.replace(/___(.+?)___/g, "$1");
|
|
39966
40192
|
s = s.replace(/\*\*(.+?)\*\*/g, "$1");
|
|
@@ -39968,10 +40194,8 @@ function normalizeForSpeech(input) {
|
|
|
39968
40194
|
s = s.replace(/\*(.+?)\*/g, "$1");
|
|
39969
40195
|
s = s.replace(/(?<![A-Za-z0-9])_(.+?)_(?![A-Za-z0-9])/g, "$1");
|
|
39970
40196
|
s = s.replace(/~~(.+?)~~/g, "$1");
|
|
39971
|
-
s = s.replace(/^[ \t]{0,3}#{1,6}[ \t]+/gm, "");
|
|
39972
40197
|
s = s.replace(/^[ \t]{0,3}>[ \t]?/gm, "");
|
|
39973
|
-
s = s
|
|
39974
|
-
s = s.replace(/^[ \t]{0,3}\d+[.)][ \t]+/gm, "");
|
|
40198
|
+
s = applyBlockPauses(s);
|
|
39975
40199
|
s = s.replace(/^[ \t]{0,3}([-_*])\1{2,}[ \t]*$/gm, "");
|
|
39976
40200
|
s = s.replace(/^[ \t]*\|?[ \t]*:?-{2,}:?[ \t]*(\|[ \t]*:?-{2,}:?[ \t]*)+\|?[ \t]*$/gm, "");
|
|
39977
40201
|
s = s.replace(/\|/g, " ");
|
|
@@ -39996,7 +40220,7 @@ function normalizeForSpeech(input) {
|
|
|
39996
40220
|
return m;
|
|
39997
40221
|
return `${MONTHS[month]} ${ord} ${yw}`;
|
|
39998
40222
|
});
|
|
39999
|
-
s = s.replace(
|
|
40223
|
+
s = s.replace(/(?<![\d:])\b([01]?\d|2[0-3]):([0-5]\d)(?!:?\d)/g, (m, hh, mm) => {
|
|
40000
40224
|
const h = Number(hh);
|
|
40001
40225
|
const min = Number(mm);
|
|
40002
40226
|
const hw = belowThousand(h);
|
|
@@ -40004,11 +40228,12 @@ function normalizeForSpeech(input) {
|
|
|
40004
40228
|
return `${hw} o'clock`;
|
|
40005
40229
|
return `${hw} ${minutesToWords(min)}`;
|
|
40006
40230
|
});
|
|
40007
|
-
s = s.replace(/\$(\d{1,9})(?:\.(\d{2}))
|
|
40008
|
-
const
|
|
40231
|
+
s = s.replace(/\$(\d{1,3}(?:,\d{3})+|\d{1,9})(?:\.(\d{2}))?(?!\d|[.,]\d)/g, (m, dollarsRaw, cents) => {
|
|
40232
|
+
const dollars = Number(String(dollarsRaw).replace(/,/g, ""));
|
|
40233
|
+
const dw = numberToWords(dollars);
|
|
40009
40234
|
if (!dw)
|
|
40010
40235
|
return m;
|
|
40011
|
-
const noun =
|
|
40236
|
+
const noun = dollars === 1 && !cents ? "dollar" : "dollars";
|
|
40012
40237
|
if (cents && cents !== "00") {
|
|
40013
40238
|
const cw = numberToWords(Number(cents));
|
|
40014
40239
|
return `${dw} ${noun} ${cw}`;
|
|
@@ -40040,7 +40265,7 @@ function normalizeForSpeech(input) {
|
|
|
40040
40265
|
s = s.replace(/\s=\s/g, " equals ");
|
|
40041
40266
|
s = s.replace(/(\s)&(\s)/g, "$1and$2");
|
|
40042
40267
|
s = s.replace(/(\w)&(\w)/g, "$1 and $2");
|
|
40043
|
-
s = s.replace(
|
|
40268
|
+
s = s.replace(new RegExp(`\\b[A-Z]{2,${ACRONYM_MAX_LEN}}\\b`, "g"), (tok) => ACRONYMS.has(tok) ? tok.split("").join(" ") : tok);
|
|
40044
40269
|
s = s.replace(/([!?.]){2,}/g, "$1");
|
|
40045
40270
|
s = s.replace(/[ \t]*\n[ \t]*\n[ \t]*/g, ". ");
|
|
40046
40271
|
s = s.replace(/\s*\n\s*/g, " ");
|
|
@@ -40106,8 +40331,24 @@ var ACRONYMS2 = new Set([
|
|
|
40106
40331
|
"HTTP",
|
|
40107
40332
|
"JSON",
|
|
40108
40333
|
"SQL",
|
|
40109
|
-
"UI"
|
|
40334
|
+
"UI",
|
|
40335
|
+
"HTTPS",
|
|
40336
|
+
"SSH",
|
|
40337
|
+
"DNS",
|
|
40338
|
+
"CLI",
|
|
40339
|
+
"AWS",
|
|
40340
|
+
"UTC",
|
|
40341
|
+
"MCP",
|
|
40342
|
+
"PDF",
|
|
40343
|
+
"ID",
|
|
40344
|
+
"OK",
|
|
40345
|
+
"VM",
|
|
40346
|
+
"LLM",
|
|
40347
|
+
"YAML",
|
|
40348
|
+
"RAM",
|
|
40349
|
+
"USB"
|
|
40110
40350
|
]);
|
|
40351
|
+
var ACRONYM_MAX_LEN2 = Math.max(...[...ACRONYMS2].map((a) => a.length));
|
|
40111
40352
|
|
|
40112
40353
|
// tts-normalize.ts
|
|
40113
40354
|
var NULL = "\x00";
|
|
@@ -40344,7 +40585,7 @@ table omitted.`);
|
|
|
40344
40585
|
words.push(ONES2[Number(ch)]);
|
|
40345
40586
|
return words.join(" ");
|
|
40346
40587
|
});
|
|
40347
|
-
s = s.replace(
|
|
40588
|
+
s = s.replace(/(?<![\d:])\b([01]?\d|2[0-3]):([0-5]\d)(?!:?\d)/g, (_m, hh, mm) => {
|
|
40348
40589
|
const h = Number(hh);
|
|
40349
40590
|
const min = Number(mm);
|
|
40350
40591
|
const hw = belowThousand2(h);
|
|
@@ -45438,7 +45679,7 @@ table omitted.`);
|
|
|
45438
45679
|
words.push(ONES3[Number(ch)]);
|
|
45439
45680
|
return words.join(" ");
|
|
45440
45681
|
});
|
|
45441
|
-
s = s.replace(
|
|
45682
|
+
s = s.replace(/(?<![\d:])\b([01]?\d|2[0-3]):([0-5]\d)(?!:?\d)/g, (_m, hh, mm) => {
|
|
45442
45683
|
const h = Number(hh);
|
|
45443
45684
|
const min = Number(mm);
|
|
45444
45685
|
const hw = belowThousand3(h);
|
|
@@ -51919,12 +52160,20 @@ function createHandbackPreturnSignal(deps) {
|
|
|
51919
52160
|
`);
|
|
51920
52161
|
});
|
|
51921
52162
|
}
|
|
52163
|
+
function stopTypingUnlessTurnLive(entry, reason) {
|
|
52164
|
+
if (deps.hasLiveTurn?.(entry.statusKey) === true) {
|
|
52165
|
+
log(`handback-preturn-signal: ${reason} key=${entry.statusKey} ` + `typing=kept (live turn owns the stop)
|
|
52166
|
+
`);
|
|
52167
|
+
return;
|
|
52168
|
+
}
|
|
52169
|
+
deps.stopTypingLoop(entry.chatId, entry.threadId);
|
|
52170
|
+
}
|
|
51922
52171
|
function reap(entry) {
|
|
51923
52172
|
entry.reapTimer = null;
|
|
51924
52173
|
if (entry.consumed)
|
|
51925
52174
|
return;
|
|
51926
52175
|
entry.consumed = true;
|
|
51927
|
-
|
|
52176
|
+
stopTypingUnlessTurnLive(entry, "orphan reap");
|
|
51928
52177
|
if (entry.activityMessageId != null) {
|
|
51929
52178
|
const record = {
|
|
51930
52179
|
turnKey: entry.syntheticTurnKey,
|
|
@@ -51951,11 +52200,20 @@ function createHandbackPreturnSignal(deps) {
|
|
|
51951
52200
|
return;
|
|
51952
52201
|
const threadId = inbound.threadId ?? null;
|
|
51953
52202
|
const adoptTurnId = deps.deriveTurnId(chatId, threadId, inbound.messageId);
|
|
51954
|
-
|
|
52203
|
+
const releaseKey = deps.chatKey(chatId, threadId);
|
|
52204
|
+
if (adoptTurnId == null) {
|
|
52205
|
+
log(`handback-preturn-signal: release key=${releaseKey} armed=no (no derivable turnId)
|
|
52206
|
+
`);
|
|
51955
52207
|
return;
|
|
51956
|
-
|
|
51957
|
-
|
|
52208
|
+
}
|
|
52209
|
+
const statusKey = releaseKey;
|
|
52210
|
+
if (byKey.has(statusKey)) {
|
|
52211
|
+
log(`handback-preturn-signal: release key=${statusKey} turnId=${adoptTurnId} ` + `armed=no (deduped: topic already has a live pre-turn entry)
|
|
52212
|
+
`);
|
|
51958
52213
|
return;
|
|
52214
|
+
}
|
|
52215
|
+
log(`handback-preturn-signal: release key=${statusKey} turnId=${adoptTurnId} armed=yes
|
|
52216
|
+
`);
|
|
51959
52217
|
const startedAt = now();
|
|
51960
52218
|
const syntheticTurnKey = `${PRETURN_TURNKEY_PREFIX}${statusKey}:${startedAt}`;
|
|
51961
52219
|
const entry = {
|
|
@@ -52021,7 +52279,7 @@ function createHandbackPreturnSignal(deps) {
|
|
|
52021
52279
|
if (entry == null)
|
|
52022
52280
|
return;
|
|
52023
52281
|
entry.consumed = true;
|
|
52024
|
-
|
|
52282
|
+
stopTypingUnlessTurnLive(entry, "reaped record");
|
|
52025
52283
|
dropEntry(entry);
|
|
52026
52284
|
},
|
|
52027
52285
|
pendingCount() {
|
|
@@ -68058,6 +68316,7 @@ __export(exports_silence_poke, {
|
|
|
68058
68316
|
longestInFlightTool: () => longestInFlightTool,
|
|
68059
68317
|
formatFrameworkFallbackText: () => formatFrameworkFallbackText,
|
|
68060
68318
|
endTurn: () => endTurn,
|
|
68319
|
+
decideFallbackTeardownNotice: () => decideFallbackTeardownNotice,
|
|
68061
68320
|
__tickForTests: () => __tickForTests,
|
|
68062
68321
|
__setDepsForTests: () => __setDepsForTests,
|
|
68063
68322
|
__resetAllForTests: () => __resetAllForTests2,
|
|
@@ -68224,6 +68483,22 @@ function formatFrameworkFallbackText(fallbackKind, silenceMs, inFlightTools = []
|
|
|
68224
68483
|
}
|
|
68225
68484
|
return null;
|
|
68226
68485
|
}
|
|
68486
|
+
function decideFallbackTeardownNotice(input) {
|
|
68487
|
+
if (input.userAddressedTextDelivered)
|
|
68488
|
+
return { send: false, reason: "user-addressed-text-delivered" };
|
|
68489
|
+
if (!input.tearsDownLiveTurn)
|
|
68490
|
+
return { send: false, reason: "no-live-turn-torn-down" };
|
|
68491
|
+
if (input.role !== "user")
|
|
68492
|
+
return { send: false, reason: "non-user-turn" };
|
|
68493
|
+
if (input.finalAnswerDelivered)
|
|
68494
|
+
return { send: false, reason: "answer-already-delivered" };
|
|
68495
|
+
const minutes = Math.max(1, Math.round(input.silenceMs / 60000));
|
|
68496
|
+
const tail = input.representWillFollow ? "Your message is still tracked and is being re-asked now." : "Your message was not answered \u2014 please re-send it.";
|
|
68497
|
+
return {
|
|
68498
|
+
send: true,
|
|
68499
|
+
text: `\u26a0\ufe0f no output for ${minutes} min \u2014 the framework ended that stalled turn. ${tail}`
|
|
68500
|
+
};
|
|
68501
|
+
}
|
|
68227
68502
|
function longestInFlightTool(key, now) {
|
|
68228
68503
|
const s = state2.get(key);
|
|
68229
68504
|
if (s == null)
|
|
@@ -68297,6 +68572,11 @@ function tick(now) {
|
|
|
68297
68572
|
const silence = now - zeroAt;
|
|
68298
68573
|
if (silence < 0)
|
|
68299
68574
|
continue;
|
|
68575
|
+
if (activeDeps.isTurnLive != null && !activeDeps.isTurnLive(key)) {
|
|
68576
|
+
state2.delete(key);
|
|
68577
|
+
activeDeps.emitMetric({ kind: "silence_poke_orphan_reaped", key, silence_ms: silence });
|
|
68578
|
+
continue;
|
|
68579
|
+
}
|
|
68300
68580
|
tryMidTurnFloor(key, s, now, false);
|
|
68301
68581
|
if (!s.fallbackFired && silence >= thresholds.fallback) {
|
|
68302
68582
|
const ceiling = thresholds.fallbackHardCeiling ?? Number.POSITIVE_INFINITY;
|
|
@@ -76716,6 +76996,7 @@ function recordTurnEnd(db3, args) {
|
|
|
76716
76996
|
}
|
|
76717
76997
|
var INTERRUPTED_VIA = new Set([
|
|
76718
76998
|
"restart",
|
|
76999
|
+
"reaped_stale",
|
|
76719
77000
|
"sigterm",
|
|
76720
77001
|
"timeout",
|
|
76721
77002
|
"unknown"
|
|
@@ -77425,6 +77706,22 @@ function formatFrameworkFallbackText2(fallbackKind, silenceMs, inFlightTools = [
|
|
|
77425
77706
|
}
|
|
77426
77707
|
return null;
|
|
77427
77708
|
}
|
|
77709
|
+
function decideFallbackTeardownNotice2(input) {
|
|
77710
|
+
if (input.userAddressedTextDelivered)
|
|
77711
|
+
return { send: false, reason: "user-addressed-text-delivered" };
|
|
77712
|
+
if (!input.tearsDownLiveTurn)
|
|
77713
|
+
return { send: false, reason: "no-live-turn-torn-down" };
|
|
77714
|
+
if (input.role !== "user")
|
|
77715
|
+
return { send: false, reason: "non-user-turn" };
|
|
77716
|
+
if (input.finalAnswerDelivered)
|
|
77717
|
+
return { send: false, reason: "answer-already-delivered" };
|
|
77718
|
+
const minutes = Math.max(1, Math.round(input.silenceMs / 60000));
|
|
77719
|
+
const tail = input.representWillFollow ? "Your message is still tracked and is being re-asked now." : "Your message was not answered \u2014 please re-send it.";
|
|
77720
|
+
return {
|
|
77721
|
+
send: true,
|
|
77722
|
+
text: `\u26a0\ufe0f no output for ${minutes} min \u2014 the framework ended that stalled turn. ${tail}`
|
|
77723
|
+
};
|
|
77724
|
+
}
|
|
77428
77725
|
|
|
77429
77726
|
// gateway/stream-render.ts
|
|
77430
77727
|
function handleSessionEvent(deps, ev) {
|
|
@@ -77602,9 +77899,11 @@ function handleSessionEvent(deps, ev) {
|
|
|
77602
77899
|
next.activityMessageId = handbackAdoption.activityMessageId;
|
|
77603
77900
|
next.activityEverOpened = true;
|
|
77604
77901
|
}
|
|
77605
|
-
|
|
77902
|
+
process.stderr.write(`telegram gateway: handback pre-turn adopted turnId=${turnId} ` + `key=${handbackAdoption.statusKey} card=${handbackAdoption.activityMessageId ?? "none"}
|
|
77903
|
+
`);
|
|
77606
77904
|
}
|
|
77607
77905
|
}
|
|
77906
|
+
startTurnTypingLoop(ev.chatId, enqThreadIdNum ?? null);
|
|
77608
77907
|
setCurrentTurn(next, statusKey(ev.chatId, enqThreadIdNum));
|
|
77609
77908
|
scheduleEarlyLivenessOpen(next);
|
|
77610
77909
|
process.stderr.write(`telegram gateway: ${formatTurnLifecycle("set", "enqueue", next, startedAt)}
|
|
@@ -79338,6 +79637,7 @@ function flushOnAgentDisconnect(deps) {
|
|
|
79338
79637
|
disposeProgressDriver,
|
|
79339
79638
|
stopTurnTypingLoops,
|
|
79340
79639
|
onDanglingTurnsSwept,
|
|
79640
|
+
onTurnKeyEnded,
|
|
79341
79641
|
log
|
|
79342
79642
|
} = deps;
|
|
79343
79643
|
if (agentName3 == null) {
|
|
@@ -79349,6 +79649,7 @@ function flushOnAgentDisconnect(deps) {
|
|
|
79349
79649
|
activeStatusReactions.delete(key);
|
|
79350
79650
|
activeReactionMsgIds.delete(key);
|
|
79351
79651
|
activeTurnStartedAt.delete(key);
|
|
79652
|
+
onTurnKeyEnded?.(key);
|
|
79352
79653
|
}
|
|
79353
79654
|
clearActiveReactions3();
|
|
79354
79655
|
const danglingKeys = [...activeTurnStartedAt.keys()];
|
|
@@ -79356,6 +79657,7 @@ function flushOnAgentDisconnect(deps) {
|
|
|
79356
79657
|
for (const k of danglingKeys) {
|
|
79357
79658
|
activeTurnStartedAt.delete(k);
|
|
79358
79659
|
activeReactionMsgIds.delete(k);
|
|
79660
|
+
onTurnKeyEnded?.(k);
|
|
79359
79661
|
}
|
|
79360
79662
|
log(`telegram gateway: disconnect-flush swept ${danglingKeys.length} dangling turn key(s) ` + `post-bridge-death (controller loop missed \u2014 finalize raced disconnect)`);
|
|
79361
79663
|
onDanglingTurnsSwept?.(danglingKeys);
|
|
@@ -81894,13 +82196,14 @@ async function discoverModels(agentName3, opts = {}) {
|
|
|
81894
82196
|
}
|
|
81895
82197
|
|
|
81896
82198
|
// ../src/agents/scaffold.ts
|
|
81897
|
-
import { dirname as dirname14, isAbsolute, join as
|
|
82199
|
+
import { dirname as dirname14, isAbsolute, join as join41, relative, resolve as resolve8 } from "node:path";
|
|
81898
82200
|
init_atomic();
|
|
81899
82201
|
|
|
81900
82202
|
// ../src/agents/agent-uid.ts
|
|
81901
82203
|
init_peercred();
|
|
81902
82204
|
|
|
81903
82205
|
// ../src/agents/scaffold.ts
|
|
82206
|
+
init_generation_stamp();
|
|
81904
82207
|
init_schema();
|
|
81905
82208
|
|
|
81906
82209
|
// ../src/config/users.ts
|
|
@@ -81911,7 +82214,7 @@ init_merge();
|
|
|
81911
82214
|
init_timezone();
|
|
81912
82215
|
|
|
81913
82216
|
// ../src/cli/agent-config.ts
|
|
81914
|
-
import { join as
|
|
82217
|
+
import { join as join39 } from "node:path";
|
|
81915
82218
|
import { homedir as homedir12 } from "node:os";
|
|
81916
82219
|
|
|
81917
82220
|
// ../src/cli/helpers.ts
|
|
@@ -81932,12 +82235,12 @@ var WEBKITE_VAULT_KEYS = new Set([
|
|
|
81932
82235
|
init_overlay_loader();
|
|
81933
82236
|
|
|
81934
82237
|
// ../src/cli/agent-config.ts
|
|
81935
|
-
var AUDIT_ROOT =
|
|
82238
|
+
var AUDIT_ROOT = join39(homedir12(), ".switchroom", "audit");
|
|
81936
82239
|
|
|
81937
82240
|
// ../src/agents/profiles.ts
|
|
81938
82241
|
var import_handlebars = __toESM(require_lib(), 1);
|
|
81939
|
-
import { readFileSync as
|
|
81940
|
-
import { resolve as resolve7, join as
|
|
82242
|
+
import { readFileSync as readFileSync33, writeFileSync as writeFileSync30, existsSync as existsSync34, readdirSync as readdirSync7, statSync as statSync12, copyFileSync, mkdirSync as mkdirSync31, realpathSync as realpathSync2 } from "node:fs";
|
|
82243
|
+
import { resolve as resolve7, join as join40, sep as pathSep } from "node:path";
|
|
81941
82244
|
function resolveProfilesRoot() {
|
|
81942
82245
|
const envOverride = process.env.SWITCHROOM_PROFILES_ROOT?.trim();
|
|
81943
82246
|
if (envOverride) {
|
|
@@ -81948,7 +82251,7 @@ function resolveProfilesRoot() {
|
|
|
81948
82251
|
resolve7(import.meta.dirname, "profiles")
|
|
81949
82252
|
];
|
|
81950
82253
|
for (const candidate of candidates) {
|
|
81951
|
-
if (
|
|
82254
|
+
if (existsSync34(candidate)) {
|
|
81952
82255
|
return candidate;
|
|
81953
82256
|
}
|
|
81954
82257
|
}
|
|
@@ -81964,9 +82267,9 @@ import_handlebars.default.registerHelper("isNumber", (value) => {
|
|
|
81964
82267
|
var SHARED_FRAGMENTS_DIR = resolve7(PROFILES_ROOT, "_shared");
|
|
81965
82268
|
var SHARED_FRAGMENTS = ["vault-protocol", "agent-self-service", "execution-discipline", "reply-discipline", "dev-protocol"];
|
|
81966
82269
|
for (const name of SHARED_FRAGMENTS) {
|
|
81967
|
-
const fragPath =
|
|
81968
|
-
if (
|
|
81969
|
-
import_handlebars.default.registerPartial(name,
|
|
82270
|
+
const fragPath = join40(SHARED_FRAGMENTS_DIR, `${name}.md.hbs`);
|
|
82271
|
+
if (existsSync34(fragPath)) {
|
|
82272
|
+
import_handlebars.default.registerPartial(name, readFileSync33(fragPath, "utf-8"));
|
|
81970
82273
|
}
|
|
81971
82274
|
}
|
|
81972
82275
|
|
|
@@ -81983,7 +82286,23 @@ var HINDSIGHT_HEALTHCHECK_PY = 'import urllib.request,sys; sys.exit(0 if urllib.
|
|
|
81983
82286
|
var HINDSIGHT_HEALTHCHECK_CMD = `python3 -c '${HINDSIGHT_HEALTHCHECK_PY}'`;
|
|
81984
82287
|
|
|
81985
82288
|
// ../src/memory/hindsight.ts
|
|
81986
|
-
var DEFAULT_RETAIN_MISSION = "Extract
|
|
82289
|
+
var DEFAULT_RETAIN_MISSION = "Extract durable facts that will still be true and useful weeks from now: " + "user preferences and standing rules, ongoing projects and recurring " + "commitments, technical and architectural decisions with their rationale, " + "and people/tool relationships. A preference revealed by a request is " + "durable \u2014 record the preference (what the user likes, wants, or always " + `does), not the request itself.
|
|
82290
|
+
|
|
82291
|
+
` + `NEVER extract:
|
|
82292
|
+
` + "- Agent tool-use traces or narration of what the assistant did (e.g. " + `"the assistant used X to query Y", "ran a search", "sent the message").
|
|
82293
|
+
` + "- In-flight workflow/process narration (a sub-task started, paused, or is " + "still running) \u2014 retain the outcome only once the task completes or a " + `decision is made.
|
|
82294
|
+
` + `- Operation, request, batch or session IDs, UUIDs, hashes, or error codes.
|
|
82295
|
+
` + "- Hindsight's own errors, retries, backlogs, or internal state \u2014 the " + `memory system's self-reports are not memories.
|
|
82296
|
+
` + `- Restatements of the user's current request or the task in progress.
|
|
82297
|
+
` + "- Transient state (unread counts, build status, what is running right now) " + "unless the fact is explicitly dated, in which case record it as a dated " + `observation.
|
|
82298
|
+
` + `- Greetings, acknowledgements, and routine operational chatter.
|
|
82299
|
+
|
|
82300
|
+
` + "If a candidate fact matches an exclusion, drop it rather than rewording " + "it. If nothing durable remains, return an empty facts list.";
|
|
82301
|
+
var SUPERSEDED_RETAIN_MISSIONS = [
|
|
82302
|
+
"Extract technical decisions, architectural choices, user preferences, project context, and people/tool relationships. Ignore routine greetings and transient operational details.",
|
|
82303
|
+
"Extract user preferences, ongoing projects, recurring commitments, " + "important context, and durable facts that should help across future " + "conversations. Skip one-off chatter and temporary task noise.",
|
|
82304
|
+
"Extract user preferences, ongoing projects, recurring commitments, " + "important context, and durable facts that should help across future " + "conversations. Skip one-off chatter and temporary task noise, " + "including in-flight workflow/process narration (a sub-task started, " + "paused, or is still running) \u2014 only retain the outcome once a task " + "actually completes or a decision is made."
|
|
82305
|
+
];
|
|
81987
82306
|
var PROFILE_MEMORY_DEFAULTS = {
|
|
81988
82307
|
"health-coach": {
|
|
81989
82308
|
disposition: { skepticism: 2, literalism: 2, empathy: 5 },
|
|
@@ -82028,6 +82347,7 @@ function normalizeModelAlias(model) {
|
|
|
82028
82347
|
return model === "claude-fable-5" ? "fable" : model;
|
|
82029
82348
|
}
|
|
82030
82349
|
var CLAUDE_MD_YOURS_PLACEHOLDER = "This space is yours. Add per-agent rules, exceptions, or context the " + "Switchroom template doesn't capture. Everything above the marker line is " + "regenerated on every apply; this section is preserved.";
|
|
82350
|
+
var pendingBankOps = new Set;
|
|
82031
82351
|
var SWITCHROOM_OWNED_SETTINGS_KEYS = new Set([
|
|
82032
82352
|
"permissions",
|
|
82033
82353
|
"mcpServers",
|
|
@@ -82573,7 +82893,7 @@ init_overlay_loader();
|
|
|
82573
82893
|
init_merge();
|
|
82574
82894
|
init_timezone();
|
|
82575
82895
|
var import_yaml4 = __toESM(require_dist(), 1);
|
|
82576
|
-
import { readFileSync as
|
|
82896
|
+
import { readFileSync as readFileSync34, existsSync as existsSync35 } from "node:fs";
|
|
82577
82897
|
import { homedir as homedir13 } from "node:os";
|
|
82578
82898
|
import { resolve as resolve9 } from "node:path";
|
|
82579
82899
|
|
|
@@ -82649,7 +82969,7 @@ function findConfigFile2(startDir) {
|
|
|
82649
82969
|
resolve9(userDir, "clerk.yml")
|
|
82650
82970
|
].filter(Boolean);
|
|
82651
82971
|
for (const path2 of searchPaths) {
|
|
82652
|
-
if (
|
|
82972
|
+
if (existsSync35(path2)) {
|
|
82653
82973
|
return path2;
|
|
82654
82974
|
}
|
|
82655
82975
|
}
|
|
@@ -82657,12 +82977,12 @@ function findConfigFile2(startDir) {
|
|
|
82657
82977
|
}
|
|
82658
82978
|
function loadConfig2(configPath) {
|
|
82659
82979
|
const filePath = configPath ?? findConfigFile2();
|
|
82660
|
-
if (!
|
|
82980
|
+
if (!existsSync35(filePath)) {
|
|
82661
82981
|
throw new ConfigError2(`Config file not found: ${filePath}`);
|
|
82662
82982
|
}
|
|
82663
82983
|
let raw;
|
|
82664
82984
|
try {
|
|
82665
|
-
raw =
|
|
82985
|
+
raw = readFileSync34(filePath, "utf-8");
|
|
82666
82986
|
} catch (err) {
|
|
82667
82987
|
throw new ConfigError2(`Failed to read config file: ${filePath}`, [
|
|
82668
82988
|
` ${err.message}`
|
|
@@ -83192,15 +83512,15 @@ function topicForRecipient(args) {
|
|
|
83192
83512
|
}
|
|
83193
83513
|
|
|
83194
83514
|
// ../src/agents/perf.ts
|
|
83195
|
-
import { existsSync as
|
|
83515
|
+
import { existsSync as existsSync36, readFileSync as readFileSync35 } from "node:fs";
|
|
83196
83516
|
function readTurnUsages(jsonlPath, lastN) {
|
|
83197
|
-
if (!
|
|
83517
|
+
if (!existsSync36(jsonlPath))
|
|
83198
83518
|
return [];
|
|
83199
83519
|
if (lastN <= 0)
|
|
83200
83520
|
return [];
|
|
83201
83521
|
let raw;
|
|
83202
83522
|
try {
|
|
83203
|
-
raw =
|
|
83523
|
+
raw = readFileSync35(jsonlPath, "utf-8");
|
|
83204
83524
|
} catch {
|
|
83205
83525
|
return [];
|
|
83206
83526
|
}
|
|
@@ -83291,8 +83611,8 @@ function numField(obj, key) {
|
|
|
83291
83611
|
}
|
|
83292
83612
|
|
|
83293
83613
|
// gateway/context-occupancy.ts
|
|
83294
|
-
import { mkdirSync as mkdirSync32, writeFileSync as
|
|
83295
|
-
import { join as
|
|
83614
|
+
import { mkdirSync as mkdirSync32, writeFileSync as writeFileSync31 } from "node:fs";
|
|
83615
|
+
import { join as join42 } from "node:path";
|
|
83296
83616
|
var CONTEXT_OCCUPANCY_FILENAME = "context-occupancy.json";
|
|
83297
83617
|
var TIGHT_FRACTION = 0.8;
|
|
83298
83618
|
function buildContextOccupancy(occupancy, cap, now) {
|
|
@@ -83315,9 +83635,9 @@ function buildContextOccupancy(occupancy, cap, now) {
|
|
|
83315
83635
|
}
|
|
83316
83636
|
function writeContextOccupancySnapshot(stateDir, snapshot, deps) {
|
|
83317
83637
|
try {
|
|
83318
|
-
const path2 =
|
|
83638
|
+
const path2 = join42(stateDir, CONTEXT_OCCUPANCY_FILENAME);
|
|
83319
83639
|
(deps?.mkdir ?? ((p, o) => mkdirSync32(p, o)))(stateDir, { recursive: true });
|
|
83320
|
-
(deps?.writeFile ?? ((p, d) =>
|
|
83640
|
+
(deps?.writeFile ?? ((p, d) => writeFileSync31(p, d)))(path2, JSON.stringify(snapshot, null, 2) + `
|
|
83321
83641
|
`);
|
|
83322
83642
|
} catch {}
|
|
83323
83643
|
}
|
|
@@ -83471,7 +83791,7 @@ function nextCompactNotify(state6, ev) {
|
|
|
83471
83791
|
}
|
|
83472
83792
|
|
|
83473
83793
|
// gateway/hostd-dispatch.ts
|
|
83474
|
-
import { existsSync as
|
|
83794
|
+
import { existsSync as existsSync37 } from "node:fs";
|
|
83475
83795
|
import { randomBytes as randomBytes7 } from "node:crypto";
|
|
83476
83796
|
init_loader();
|
|
83477
83797
|
var _hostdEnabled2;
|
|
@@ -83495,13 +83815,13 @@ function hostdSocketPath2(agentName3) {
|
|
|
83495
83815
|
function hostdWillBeUsed2(agentName3) {
|
|
83496
83816
|
if (!isHostdEnabled2())
|
|
83497
83817
|
return false;
|
|
83498
|
-
return
|
|
83818
|
+
return existsSync37(hostdSocketPath2(agentName3));
|
|
83499
83819
|
}
|
|
83500
83820
|
async function tryHostdDispatch2(agentName3, req, timeoutMs = 5000) {
|
|
83501
83821
|
if (!isHostdEnabled2())
|
|
83502
83822
|
return "not-configured";
|
|
83503
83823
|
const sockPath = hostdSocketPath2(agentName3);
|
|
83504
|
-
if (!
|
|
83824
|
+
if (!existsSync37(sockPath))
|
|
83505
83825
|
return "not-configured";
|
|
83506
83826
|
try {
|
|
83507
83827
|
return await hostdRequest({ socketPath: sockPath, timeoutMs }, req);
|
|
@@ -83530,7 +83850,7 @@ async function pollHostdStatus(agentName3, targetRequestId, opts) {
|
|
|
83530
83850
|
if (!isHostdEnabled2())
|
|
83531
83851
|
return "not-configured";
|
|
83532
83852
|
const sockPath = hostdSocketPath2(agentName3);
|
|
83533
|
-
if (!
|
|
83853
|
+
if (!existsSync37(sockPath))
|
|
83534
83854
|
return "not-configured";
|
|
83535
83855
|
const now = opts.now ?? Date.now;
|
|
83536
83856
|
const sleep2 = opts.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
|
|
@@ -83649,7 +83969,7 @@ function createDmPinSweeper(deps) {
|
|
|
83649
83969
|
|
|
83650
83970
|
// gateway/webhook-ingest-server.ts
|
|
83651
83971
|
import net4 from "node:net";
|
|
83652
|
-
import { chmodSync as chmodSync10, existsSync as
|
|
83972
|
+
import { chmodSync as chmodSync10, existsSync as existsSync38, unlinkSync as unlinkSync17 } from "node:fs";
|
|
83653
83973
|
var MAX_REQUEST_BYTES = 1024 * 1024;
|
|
83654
83974
|
function fdOf(conn) {
|
|
83655
83975
|
const handle = conn._handle;
|
|
@@ -83661,7 +83981,7 @@ function startWebhookIngestServer(opts) {
|
|
|
83661
83981
|
const log = opts.log ?? ((s) => process.stderr.write(s));
|
|
83662
83982
|
const allowed = new Set(opts.allowedUids);
|
|
83663
83983
|
try {
|
|
83664
|
-
if (
|
|
83984
|
+
if (existsSync38(opts.socketPath))
|
|
83665
83985
|
unlinkSync17(opts.socketPath);
|
|
83666
83986
|
} catch (err) {
|
|
83667
83987
|
log(`webhook-ingest-server: could not unlink stale socket: ${err.message}
|
|
@@ -83754,7 +84074,7 @@ function startWebhookIngestServer(opts) {
|
|
|
83754
84074
|
server.close();
|
|
83755
84075
|
} catch {}
|
|
83756
84076
|
try {
|
|
83757
|
-
if (
|
|
84077
|
+
if (existsSync38(opts.socketPath))
|
|
83758
84078
|
unlinkSync17(opts.socketPath);
|
|
83759
84079
|
} catch {}
|
|
83760
84080
|
}
|
|
@@ -83763,19 +84083,19 @@ function startWebhookIngestServer(opts) {
|
|
|
83763
84083
|
|
|
83764
84084
|
// ../src/web/webhook-gateway-record.ts
|
|
83765
84085
|
import { appendFileSync as appendFileSync8, mkdirSync as mkdirSync35 } from "fs";
|
|
83766
|
-
import { join as
|
|
84086
|
+
import { join as join45 } from "path";
|
|
83767
84087
|
import { homedir as homedir15 } from "os";
|
|
83768
84088
|
|
|
83769
84089
|
// ../src/web/webhook-handler.ts
|
|
83770
|
-
import { appendFileSync as appendFileSync7, existsSync as
|
|
83771
|
-
import { join as
|
|
84090
|
+
import { appendFileSync as appendFileSync7, existsSync as existsSync39, mkdirSync as mkdirSync33, readFileSync as readFileSync36, writeFileSync as writeFileSync32 } from "fs";
|
|
84091
|
+
import { join as join43 } from "path";
|
|
83772
84092
|
var DEDUP_MAX = 1000;
|
|
83773
84093
|
var DEDUP_TTL_MS = 24 * 60 * 60 * 1000;
|
|
83774
84094
|
function loadDedupFile(path2) {
|
|
83775
84095
|
try {
|
|
83776
|
-
if (!
|
|
84096
|
+
if (!existsSync39(path2))
|
|
83777
84097
|
return {};
|
|
83778
|
-
const raw = JSON.parse(
|
|
84098
|
+
const raw = JSON.parse(readFileSync36(path2, "utf-8"));
|
|
83779
84099
|
return typeof raw.deliveries === "object" && raw.deliveries !== null ? raw.deliveries : {};
|
|
83780
84100
|
} catch {
|
|
83781
84101
|
return {};
|
|
@@ -83789,7 +84109,7 @@ function saveDedupFile(path2, deliveries, now) {
|
|
|
83789
84109
|
}
|
|
83790
84110
|
const sorted = Object.entries(pruned).sort((a, b) => b[1] - a[1]).slice(0, DEDUP_MAX);
|
|
83791
84111
|
const final = Object.fromEntries(sorted);
|
|
83792
|
-
|
|
84112
|
+
writeFileSync32(path2, JSON.stringify({ deliveries: final }), {
|
|
83793
84113
|
mode: 384
|
|
83794
84114
|
});
|
|
83795
84115
|
}
|
|
@@ -83797,8 +84117,8 @@ var agentDedupCache = new Map;
|
|
|
83797
84117
|
function createFileDedupStore(resolveAgentDir) {
|
|
83798
84118
|
return {
|
|
83799
84119
|
check(agent, deliveryId, now) {
|
|
83800
|
-
const telegramDir =
|
|
83801
|
-
const filePath =
|
|
84120
|
+
const telegramDir = join43(resolveAgentDir(agent), "telegram");
|
|
84121
|
+
const filePath = join43(telegramDir, "webhook-dedup.json");
|
|
83802
84122
|
if (!agentDedupCache.has(agent)) {
|
|
83803
84123
|
agentDedupCache.set(agent, loadDedupFile(filePath));
|
|
83804
84124
|
}
|
|
@@ -83819,8 +84139,8 @@ var tokenBuckets = new Map;
|
|
|
83819
84139
|
var throttleIssueWindow = new Map;
|
|
83820
84140
|
|
|
83821
84141
|
// ../src/web/webhook-dispatch.ts
|
|
83822
|
-
import { existsSync as
|
|
83823
|
-
import { join as
|
|
84142
|
+
import { existsSync as existsSync40, mkdirSync as mkdirSync34, readFileSync as readFileSync37, writeFileSync as writeFileSync33 } from "fs";
|
|
84143
|
+
import { join as join44 } from "path";
|
|
83824
84144
|
import { homedir as homedir14 } from "os";
|
|
83825
84145
|
|
|
83826
84146
|
// ../src/agent-scheduler/ipc-client.ts
|
|
@@ -84119,9 +84439,9 @@ function cooldownKey(source, eventType, repo, number, ruleIndex) {
|
|
|
84119
84439
|
}
|
|
84120
84440
|
function loadCooldownFile(path2) {
|
|
84121
84441
|
try {
|
|
84122
|
-
if (!
|
|
84442
|
+
if (!existsSync40(path2))
|
|
84123
84443
|
return {};
|
|
84124
|
-
const raw = JSON.parse(
|
|
84444
|
+
const raw = JSON.parse(readFileSync37(path2, "utf-8"));
|
|
84125
84445
|
return typeof raw.dispatches === "object" && raw.dispatches !== null ? raw.dispatches : {};
|
|
84126
84446
|
} catch {
|
|
84127
84447
|
return {};
|
|
@@ -84129,7 +84449,7 @@ function loadCooldownFile(path2) {
|
|
|
84129
84449
|
}
|
|
84130
84450
|
function saveCooldownFile(path2, dispatches) {
|
|
84131
84451
|
try {
|
|
84132
|
-
|
|
84452
|
+
writeFileSync33(path2, JSON.stringify({ dispatches }), {
|
|
84133
84453
|
mode: 384
|
|
84134
84454
|
});
|
|
84135
84455
|
} catch {}
|
|
@@ -84140,8 +84460,8 @@ function createFileCooldownStore(resolveAgentDir) {
|
|
|
84140
84460
|
isCoolingDown(agent, key, cooldownMs, now) {
|
|
84141
84461
|
if (cooldownMs <= 0)
|
|
84142
84462
|
return false;
|
|
84143
|
-
const telegramDir =
|
|
84144
|
-
const filePath =
|
|
84463
|
+
const telegramDir = join44(resolveAgentDir(agent), "telegram");
|
|
84464
|
+
const filePath = join44(telegramDir, "webhook-cooldown.json");
|
|
84145
84465
|
if (!cache.has(agent)) {
|
|
84146
84466
|
cache.set(agent, loadCooldownFile(filePath));
|
|
84147
84467
|
}
|
|
@@ -84198,9 +84518,9 @@ async function defaultInject(socketPath, agentName3, inbound) {
|
|
|
84198
84518
|
}
|
|
84199
84519
|
function injectWebhookInbound(agent, prompt, ctx, deps = {}) {
|
|
84200
84520
|
const log = deps.log ?? ((s) => process.stderr.write(s));
|
|
84201
|
-
const resolveAgentDir = deps.resolveAgentDir ?? ((a) =>
|
|
84521
|
+
const resolveAgentDir = deps.resolveAgentDir ?? ((a) => join44(homedir14(), ".switchroom", "agents", a));
|
|
84202
84522
|
const now = (deps.now ?? Date.now)();
|
|
84203
|
-
const socketPath =
|
|
84523
|
+
const socketPath = join44(resolveAgentDir(agent), "telegram", "gateway.sock");
|
|
84204
84524
|
const inbound = {
|
|
84205
84525
|
type: "inbound",
|
|
84206
84526
|
chatId: ctx.chatId,
|
|
@@ -84272,7 +84592,7 @@ function evaluateDispatch(args, deps = {}) {
|
|
|
84272
84592
|
const log = deps.log ?? ((s) => process.stderr.write(s));
|
|
84273
84593
|
const now = (deps.now ?? Date.now)();
|
|
84274
84594
|
const nowDate = deps.nowDate ?? (() => new Date(now));
|
|
84275
|
-
const resolveAgentDir = deps.resolveAgentDir ?? ((a) =>
|
|
84595
|
+
const resolveAgentDir = deps.resolveAgentDir ?? ((a) => join44(homedir14(), ".switchroom", "agents", a));
|
|
84276
84596
|
const cooldownStore = deps.cooldownStore ?? createFileCooldownStore(resolveAgentDir);
|
|
84277
84597
|
if (!DISPATCH_SOURCES.includes(args.source))
|
|
84278
84598
|
return 0;
|
|
@@ -84350,10 +84670,10 @@ var CONSOLIDATION_COMPLETED_EVENT = "consolidation.completed";
|
|
|
84350
84670
|
function recordWebhookEvent(rec, deps = {}) {
|
|
84351
84671
|
const log = deps.log ?? ((s) => process.stderr.write(s));
|
|
84352
84672
|
const now = rec.ts || (deps.now ?? Date.now)();
|
|
84353
|
-
const resolveAgentDir = deps.resolveAgentDir ?? ((a) =>
|
|
84673
|
+
const resolveAgentDir = deps.resolveAgentDir ?? ((a) => join45(homedir15(), ".switchroom", "agents", a));
|
|
84354
84674
|
const dedupStore = deps.dedupStore ?? createFileDedupStore(resolveAgentDir);
|
|
84355
84675
|
const agent = rec.agent;
|
|
84356
|
-
const telegramDir =
|
|
84676
|
+
const telegramDir = join45(resolveAgentDir(agent), "telegram");
|
|
84357
84677
|
if (rec.source === "github" && rec.delivery_id) {
|
|
84358
84678
|
const originalTs = dedupStore.check(agent, rec.delivery_id, now);
|
|
84359
84679
|
if (originalTs !== undefined) {
|
|
@@ -84362,7 +84682,7 @@ function recordWebhookEvent(rec, deps = {}) {
|
|
|
84362
84682
|
return { status: "deduped", ts: originalTs };
|
|
84363
84683
|
}
|
|
84364
84684
|
}
|
|
84365
|
-
const logPath =
|
|
84685
|
+
const logPath = join45(telegramDir, "webhook-events.jsonl");
|
|
84366
84686
|
try {
|
|
84367
84687
|
mkdirSync35(telegramDir, { recursive: true });
|
|
84368
84688
|
const record = {
|
|
@@ -84588,6 +84908,8 @@ function validateClientMessage(msg) {
|
|
|
84588
84908
|
return false;
|
|
84589
84909
|
if (typeof m.timeoutMs !== "number" || !Number.isFinite(m.timeoutMs) || m.timeoutMs <= 0)
|
|
84590
84910
|
return false;
|
|
84911
|
+
if (m.title !== undefined && (typeof m.title !== "string" || m.title.length === 0 || m.title.length > 200 || /[\u0000-\u001f\u007f]/.test(m.title)))
|
|
84912
|
+
return false;
|
|
84591
84913
|
return true;
|
|
84592
84914
|
}
|
|
84593
84915
|
case "request_config_finalize": {
|
|
@@ -85899,6 +86221,22 @@ class ObligationLedger {
|
|
|
85899
86221
|
return { action: "escalate", obligation: o };
|
|
85900
86222
|
return { action: "represent", obligation: o };
|
|
85901
86223
|
}
|
|
86224
|
+
static representGraceStillProtecting(o, now, representGraceMs, trailingGraceMs) {
|
|
86225
|
+
if (representGraceMs <= 0)
|
|
86226
|
+
return false;
|
|
86227
|
+
if (o.lastRepresentedAt == null)
|
|
86228
|
+
return false;
|
|
86229
|
+
const sinceRepresent = now - o.lastRepresentedAt;
|
|
86230
|
+
if (sinceRepresent >= representGraceMs)
|
|
86231
|
+
return false;
|
|
86232
|
+
const representTurnEnded = o.lastTurnEndedAt != null && o.lastTurnEndedAt > o.lastRepresentedAt;
|
|
86233
|
+
if (representTurnEnded && trailingGraceMs > 0) {
|
|
86234
|
+
const floorMs = Math.min(representGraceMs, Math.max(trailingGraceMs, ObligationLedger.MIN_REPRESENT_INTERVAL_MS));
|
|
86235
|
+
return sinceRepresent < floorMs;
|
|
86236
|
+
}
|
|
86237
|
+
return true;
|
|
86238
|
+
}
|
|
86239
|
+
static MIN_REPRESENT_INTERVAL_MS = 30000;
|
|
85902
86240
|
oldestEligible(now, graceMs, backgroundWorkActive, backgroundGraceMs, representGraceMs) {
|
|
85903
86241
|
let best;
|
|
85904
86242
|
for (const o of this.open.values()) {
|
|
@@ -85906,7 +86244,7 @@ class ObligationLedger {
|
|
|
85906
86244
|
continue;
|
|
85907
86245
|
if (backgroundWorkActive && backgroundGraceMs > 0 && now - o.openedAt < backgroundGraceMs)
|
|
85908
86246
|
continue;
|
|
85909
|
-
if (
|
|
86247
|
+
if (ObligationLedger.representGraceStillProtecting(o, now, representGraceMs, graceMs))
|
|
85910
86248
|
continue;
|
|
85911
86249
|
if (best === undefined || o.openedAt < best.openedAt)
|
|
85912
86250
|
best = o;
|
|
@@ -88158,6 +88496,22 @@ class ObligationLedger2 {
|
|
|
88158
88496
|
return { action: "escalate", obligation: o };
|
|
88159
88497
|
return { action: "represent", obligation: o };
|
|
88160
88498
|
}
|
|
88499
|
+
static representGraceStillProtecting(o, now, representGraceMs, trailingGraceMs) {
|
|
88500
|
+
if (representGraceMs <= 0)
|
|
88501
|
+
return false;
|
|
88502
|
+
if (o.lastRepresentedAt == null)
|
|
88503
|
+
return false;
|
|
88504
|
+
const sinceRepresent = now - o.lastRepresentedAt;
|
|
88505
|
+
if (sinceRepresent >= representGraceMs)
|
|
88506
|
+
return false;
|
|
88507
|
+
const representTurnEnded = o.lastTurnEndedAt != null && o.lastTurnEndedAt > o.lastRepresentedAt;
|
|
88508
|
+
if (representTurnEnded && trailingGraceMs > 0) {
|
|
88509
|
+
const floorMs = Math.min(representGraceMs, Math.max(trailingGraceMs, ObligationLedger2.MIN_REPRESENT_INTERVAL_MS));
|
|
88510
|
+
return sinceRepresent < floorMs;
|
|
88511
|
+
}
|
|
88512
|
+
return true;
|
|
88513
|
+
}
|
|
88514
|
+
static MIN_REPRESENT_INTERVAL_MS = 30000;
|
|
88161
88515
|
oldestEligible(now, graceMs, backgroundWorkActive, backgroundGraceMs, representGraceMs) {
|
|
88162
88516
|
let best;
|
|
88163
88517
|
for (const o of this.open.values()) {
|
|
@@ -88165,7 +88519,7 @@ class ObligationLedger2 {
|
|
|
88165
88519
|
continue;
|
|
88166
88520
|
if (backgroundWorkActive && backgroundGraceMs > 0 && now - o.openedAt < backgroundGraceMs)
|
|
88167
88521
|
continue;
|
|
88168
|
-
if (
|
|
88522
|
+
if (ObligationLedger2.representGraceStillProtecting(o, now, representGraceMs, graceMs))
|
|
88169
88523
|
continue;
|
|
88170
88524
|
if (best === undefined || o.openedAt < best.openedAt)
|
|
88171
88525
|
best = o;
|
|
@@ -88540,10 +88894,12 @@ function buildSilencePokeOptions(deps) {
|
|
|
88540
88894
|
SILENCE_FALLBACK_HARD_MS,
|
|
88541
88895
|
SILENCE_FLOOR_MS,
|
|
88542
88896
|
SILENCE_DEFER_INFLIGHT_TOOLS,
|
|
88897
|
+
isObligationOpenForTurn,
|
|
88543
88898
|
TURN_PREVIEW_MAX,
|
|
88544
88899
|
STATE_DIR,
|
|
88545
88900
|
isLegitimatelyWorking,
|
|
88546
88901
|
getCurrentTurn,
|
|
88902
|
+
getCurrentTurnForKey,
|
|
88547
88903
|
getInFlightUpdate,
|
|
88548
88904
|
getTurnsDb,
|
|
88549
88905
|
getInboundSpool,
|
|
@@ -88569,6 +88925,7 @@ function buildSilencePokeOptions(deps) {
|
|
|
88569
88925
|
thresholdsMs: { fallback: SILENCE_FALLBACK_MS, fallbackHardCeiling: SILENCE_FALLBACK_HARD_MS, floor: SILENCE_FLOOR_MS },
|
|
88570
88926
|
deferFallbackWhileToolInFlight: SILENCE_DEFER_INFLIGHT_TOOLS,
|
|
88571
88927
|
isLegitimatelyWorking: (key) => isLegitimatelyWorking(key),
|
|
88928
|
+
isTurnLive: (key) => !(activeTurnStartedAt.get(key) == null && getCurrentTurnForKey(key) == null),
|
|
88572
88929
|
emitMetric: (event) => {
|
|
88573
88930
|
emitRuntimeMetric2(event);
|
|
88574
88931
|
},
|
|
@@ -88581,7 +88938,7 @@ function buildSilencePokeOptions(deps) {
|
|
|
88581
88938
|
return { role: turn.role, finalAnswerDelivered: turn.finalAnswerDelivered };
|
|
88582
88939
|
},
|
|
88583
88940
|
onMidTurnFloor: async (ctx) => {
|
|
88584
|
-
if (activeTurnStartedAt.get(ctx.key) == null &&
|
|
88941
|
+
if (activeTurnStartedAt.get(ctx.key) == null && getCurrentTurnForKey(ctx.key) == null)
|
|
88585
88942
|
return;
|
|
88586
88943
|
const blockedOnApproval = activeStatusReactions.get(statusKey(ctx.chatId, ctx.threadId))?.isAwaiting() ?? false;
|
|
88587
88944
|
if (!blockedOnApproval)
|
|
@@ -88598,7 +88955,7 @@ function buildSilencePokeOptions(deps) {
|
|
|
88598
88955
|
}
|
|
88599
88956
|
},
|
|
88600
88957
|
onFrameworkFallback: async (ctx) => {
|
|
88601
|
-
if (activeTurnStartedAt.get(ctx.key) == null &&
|
|
88958
|
+
if (activeTurnStartedAt.get(ctx.key) == null && getCurrentTurnForKey(ctx.key) == null) {
|
|
88602
88959
|
process.stderr.write(`telegram gateway: silence-poke framework-fallback late-fire skipped \u2014 ` + `turn ended cleanly during silence window ` + `chat=${ctx.chatId} thread=${ctx.threadId ?? "-"} silence_ms=${ctx.silenceMs}
|
|
88603
88960
|
`);
|
|
88604
88961
|
logStreamingEvent2({
|
|
@@ -88647,9 +89004,11 @@ function buildSilencePokeOptions(deps) {
|
|
|
88647
89004
|
silenceMs: ctx.silenceMs,
|
|
88648
89005
|
fallbackKind: ctx.fallbackKind
|
|
88649
89006
|
});
|
|
89007
|
+
let userAddressedTextDelivered = false;
|
|
88650
89008
|
if (text4 != null) {
|
|
88651
89009
|
try {
|
|
88652
89010
|
await sendSilenceText(ctx.chatId, ctx.threadId ?? null, text4, blockedOnApproval ? false : true);
|
|
89011
|
+
userAddressedTextDelivered = blockedOnApproval;
|
|
88653
89012
|
} catch (err) {
|
|
88654
89013
|
process.stderr.write(`silence-poke fallback sendMessage failed chat=${ctx.chatId} thread=${ctx.threadId}: ${err}
|
|
88655
89014
|
`);
|
|
@@ -88660,6 +89019,7 @@ function buildSilencePokeOptions(deps) {
|
|
|
88660
89019
|
const fbThreadId = ctx.threadId ?? undefined;
|
|
88661
89020
|
const wedgedTurn = getCurrentTurn();
|
|
88662
89021
|
const turnMatchesFallback = wedgedTurn != null && wedgedTurn.sessionChatId === fbChatId && wedgedTurn.sessionThreadId === fbThreadId;
|
|
89022
|
+
const tearsDownLiveTurn = turnMatchesFallback && wedgedTurn != null && turnLiveForItsTopic(wedgedTurn);
|
|
88663
89023
|
const turnStartedAt = activeTurnStartedAt.get(fbKey);
|
|
88664
89024
|
if (turnStartedAt != null) {
|
|
88665
89025
|
const turnDurationMs = Date.now() - turnStartedAt;
|
|
@@ -88717,17 +89077,36 @@ function buildSilencePokeOptions(deps) {
|
|
|
88717
89077
|
const sib = silenceMsForKey2(siblingKey, fbNow);
|
|
88718
89078
|
return sib == null || sib >= DEFAULT_THRESHOLDS2.fallback;
|
|
88719
89079
|
});
|
|
88720
|
-
if (
|
|
89080
|
+
if (tearsDownLiveTurn && wedgedTurn != null) {
|
|
88721
89081
|
process.stderr.write(`telegram gateway: ${formatTurnLifecycle("clear", "silence_fallback", wedgedTurn, Date.now())}
|
|
88722
89082
|
`);
|
|
88723
89083
|
endCurrentTurnForKey(wedgedTurn, fbKey);
|
|
88724
89084
|
}
|
|
89085
|
+
const teardownNotice = decideFallbackTeardownNotice2({
|
|
89086
|
+
tearsDownLiveTurn,
|
|
89087
|
+
role: wedgedTurn?.role ?? null,
|
|
89088
|
+
finalAnswerDelivered: wedgedTurn?.finalAnswerDelivered ?? false,
|
|
89089
|
+
userAddressedTextDelivered,
|
|
89090
|
+
representWillFollow: isObligationOpenForTurn(wedgedTurn?.turnId ?? null),
|
|
89091
|
+
silenceMs: ctx.silenceMs
|
|
89092
|
+
});
|
|
89093
|
+
if (teardownNotice.send) {
|
|
89094
|
+
try {
|
|
89095
|
+
await sendSilenceText(fbChatId, ctx.threadId ?? null, teardownNotice.text, false);
|
|
89096
|
+
} catch (err) {
|
|
89097
|
+
process.stderr.write(`silence-poke teardown notice sendMessage failed chat=${fbChatId} thread=${ctx.threadId}: ${err}
|
|
89098
|
+
`);
|
|
89099
|
+
}
|
|
89100
|
+
} else {
|
|
89101
|
+
process.stderr.write(`telegram gateway: silence-poke teardown notice skipped reason=${teardownNotice.reason} ` + `chat=${fbChatId} thread=${ctx.threadId ?? "-"}
|
|
89102
|
+
`);
|
|
89103
|
+
}
|
|
88725
89104
|
try {
|
|
88726
89105
|
clearSilentEndState2(fbKey);
|
|
88727
89106
|
} catch {}
|
|
88728
89107
|
const fbSelfAgent = process.env.SWITCHROOM_AGENT_NAME ?? "";
|
|
88729
89108
|
const fbRedeliver = redeliverBufferedInbound2(getPendingInboundBuffer(), fbSelfAgent, (m) => sendToAgent(fbSelfAgent, m), getInboundSpool(), trackRedeliveredInbound);
|
|
88730
|
-
process.stderr.write(`telegram gateway: silence-poke framework-fallback ended wedged turn ` + `chat=${fbChatId} thread=${ctx.threadId ?? "-"} silence_ms=${ctx.silenceMs} ` + `currentTurn_nulled=${
|
|
89109
|
+
process.stderr.write(`telegram gateway: silence-poke framework-fallback ended wedged turn ` + `chat=${fbChatId} thread=${ctx.threadId ?? "-"} silence_ms=${ctx.silenceMs} ` + `currentTurn_nulled=${tearsDownLiveTurn} ` + `drained_buffered=${fbRedeliver.redelivered}/${fbRedeliver.drained}` + `${fbRedeliver.rebuffered > 0 ? ` rebuffered=${fbRedeliver.rebuffered}` : ""}` + `${fbExtraPurge.purged.length > 0 ? ` extra_keys_purged=${fbExtraPurge.purged.length}` : ""}
|
|
88731
89110
|
`);
|
|
88732
89111
|
}
|
|
88733
89112
|
};
|
|
@@ -89248,26 +89627,26 @@ function skillProposalKeyboard(id) {
|
|
|
89248
89627
|
// ../src/self-improve/skill-proposals.ts
|
|
89249
89628
|
import {
|
|
89250
89629
|
closeSync as closeSync7,
|
|
89251
|
-
existsSync as
|
|
89630
|
+
existsSync as existsSync41,
|
|
89252
89631
|
mkdirSync as mkdirSync36,
|
|
89253
89632
|
openSync as openSync7,
|
|
89254
|
-
readFileSync as
|
|
89633
|
+
readFileSync as readFileSync38,
|
|
89255
89634
|
writeSync as writeSync5
|
|
89256
89635
|
} from "node:fs";
|
|
89257
|
-
import { join as
|
|
89636
|
+
import { join as join46 } from "node:path";
|
|
89258
89637
|
import { randomUUID as randomUUID5 } from "node:crypto";
|
|
89259
89638
|
var PROPOSALS_FILE2 = "skill-proposals.jsonl";
|
|
89260
89639
|
var REJECTED_FILE2 = "skill-proposals-rejected.jsonl";
|
|
89261
89640
|
var REJECTION_TTL_MS2 = 90 * 24 * 60 * 60 * 1000;
|
|
89262
89641
|
var PROPOSAL_SIM_THRESHOLD = 0.5;
|
|
89263
89642
|
function proposalsPath2(stateDir) {
|
|
89264
|
-
return
|
|
89643
|
+
return join46(stateDir, PROPOSALS_FILE2);
|
|
89265
89644
|
}
|
|
89266
89645
|
function rejectedPath2(stateDir) {
|
|
89267
|
-
return
|
|
89646
|
+
return join46(stateDir, REJECTED_FILE2);
|
|
89268
89647
|
}
|
|
89269
89648
|
function ensureDir3(stateDir) {
|
|
89270
|
-
if (!
|
|
89649
|
+
if (!existsSync41(stateDir)) {
|
|
89271
89650
|
mkdirSync36(stateDir, { recursive: true, mode: 493 });
|
|
89272
89651
|
}
|
|
89273
89652
|
}
|
|
@@ -89281,11 +89660,11 @@ function appendLine2(path2, obj) {
|
|
|
89281
89660
|
}
|
|
89282
89661
|
}
|
|
89283
89662
|
function readLines2(path2, isValid2) {
|
|
89284
|
-
if (!
|
|
89663
|
+
if (!existsSync41(path2))
|
|
89285
89664
|
return [];
|
|
89286
89665
|
let raw;
|
|
89287
89666
|
try {
|
|
89288
|
-
raw =
|
|
89667
|
+
raw = readFileSync38(path2, "utf-8");
|
|
89289
89668
|
} catch {
|
|
89290
89669
|
return [];
|
|
89291
89670
|
}
|
|
@@ -89882,10 +90261,10 @@ function escapeBody2(s) {
|
|
|
89882
90261
|
}
|
|
89883
90262
|
|
|
89884
90263
|
// gateway/pid-file.ts
|
|
89885
|
-
import { writeFileSync as
|
|
90264
|
+
import { writeFileSync as writeFileSync34, readFileSync as readFileSync39, unlinkSync as unlinkSync19, renameSync as renameSync15 } from "node:fs";
|
|
89886
90265
|
function writePidFile(path2, record) {
|
|
89887
90266
|
const tmp = `${path2}.tmp-${process.pid}-${Date.now()}`;
|
|
89888
|
-
|
|
90267
|
+
writeFileSync34(tmp, JSON.stringify(record), "utf-8");
|
|
89889
90268
|
renameSync15(tmp, path2);
|
|
89890
90269
|
}
|
|
89891
90270
|
function clearPidFile(path2) {
|
|
@@ -89901,10 +90280,10 @@ import {
|
|
|
89901
90280
|
writeFile as writeFileAsync,
|
|
89902
90281
|
readFile as readFileAsync
|
|
89903
90282
|
} from "node:fs/promises";
|
|
89904
|
-
import { readFileSync as
|
|
90283
|
+
import { readFileSync as readFileSync40 } from "node:fs";
|
|
89905
90284
|
function readCurrentBootId() {
|
|
89906
90285
|
try {
|
|
89907
|
-
const stat =
|
|
90286
|
+
const stat = readFileSync40("/proc/1/stat", "utf-8");
|
|
89908
90287
|
const lastParen = stat.lastIndexOf(")");
|
|
89909
90288
|
if (lastParen < 0)
|
|
89910
90289
|
return null;
|
|
@@ -90107,15 +90486,15 @@ function safeCount(fn) {
|
|
|
90107
90486
|
}
|
|
90108
90487
|
|
|
90109
90488
|
// gateway/session-marker.ts
|
|
90110
|
-
import { writeFileSync as
|
|
90489
|
+
import { writeFileSync as writeFileSync35, readFileSync as readFileSync41, renameSync as renameSync16, unlinkSync as unlinkSync20 } from "node:fs";
|
|
90111
90490
|
function writeSessionMarker(path2, marker) {
|
|
90112
90491
|
const tmp = `${path2}.tmp-${process.pid}-${Date.now()}`;
|
|
90113
|
-
|
|
90492
|
+
writeFileSync35(tmp, JSON.stringify(marker), "utf-8");
|
|
90114
90493
|
renameSync16(tmp, path2);
|
|
90115
90494
|
}
|
|
90116
90495
|
function readSessionMarker(path2) {
|
|
90117
90496
|
try {
|
|
90118
|
-
const raw =
|
|
90497
|
+
const raw = readFileSync41(path2, "utf-8");
|
|
90119
90498
|
const parsed = JSON.parse(raw);
|
|
90120
90499
|
if (typeof parsed.pid === "number" && typeof parsed.startedAtMs === "number" && Number.isFinite(parsed.pid) && Number.isFinite(parsed.startedAtMs)) {
|
|
90121
90500
|
return { pid: parsed.pid, startedAtMs: parsed.startedAtMs };
|
|
@@ -90137,16 +90516,16 @@ function shouldFireRestartBanner(input) {
|
|
|
90137
90516
|
}
|
|
90138
90517
|
|
|
90139
90518
|
// gateway/clean-shutdown-marker.ts
|
|
90140
|
-
import { writeFileSync as
|
|
90519
|
+
import { writeFileSync as writeFileSync36, readFileSync as readFileSync42, renameSync as renameSync17, unlinkSync as unlinkSync21 } from "node:fs";
|
|
90141
90520
|
var DEFAULT_MAX_AGE_MS = 60000;
|
|
90142
90521
|
function writeCleanShutdownMarker(path2, marker) {
|
|
90143
90522
|
const tmp = `${path2}.tmp-${process.pid}-${Date.now()}`;
|
|
90144
|
-
|
|
90523
|
+
writeFileSync36(tmp, JSON.stringify(marker), "utf-8");
|
|
90145
90524
|
renameSync17(tmp, path2);
|
|
90146
90525
|
}
|
|
90147
90526
|
function readCleanShutdownMarker(path2) {
|
|
90148
90527
|
try {
|
|
90149
|
-
const raw =
|
|
90528
|
+
const raw = readFileSync42(path2, "utf-8");
|
|
90150
90529
|
const parsed = JSON.parse(raw);
|
|
90151
90530
|
if (typeof parsed.ts === "number" && Number.isFinite(parsed.ts) && typeof parsed.signal === "string" && parsed.signal.length > 0) {
|
|
90152
90531
|
const out = { ts: parsed.ts, signal: parsed.signal };
|
|
@@ -90518,16 +90897,16 @@ function classifyAdminGate(text4, myAgentName) {
|
|
|
90518
90897
|
|
|
90519
90898
|
// subagent-watcher.ts
|
|
90520
90899
|
import {
|
|
90521
|
-
existsSync as
|
|
90900
|
+
existsSync as existsSync42,
|
|
90522
90901
|
openSync as openSync8,
|
|
90523
90902
|
readSync as readSync2,
|
|
90524
90903
|
statSync as statSync13,
|
|
90525
90904
|
closeSync as closeSync8,
|
|
90526
90905
|
watch,
|
|
90527
90906
|
readdirSync as readdirSync8,
|
|
90528
|
-
readFileSync as
|
|
90907
|
+
readFileSync as readFileSync43
|
|
90529
90908
|
} from "fs";
|
|
90530
|
-
import { join as
|
|
90909
|
+
import { join as join47 } from "path";
|
|
90531
90910
|
|
|
90532
90911
|
// session-tail.ts
|
|
90533
90912
|
function sanitizeCwdToProjectName2(cwd) {
|
|
@@ -90877,7 +91256,7 @@ function backfillJsonlAgentId(db3, jsonlPath, agentId, log) {
|
|
|
90877
91256
|
const metaPath = jsonlPath.replace(/\.jsonl$/, ".meta.json");
|
|
90878
91257
|
let meta;
|
|
90879
91258
|
try {
|
|
90880
|
-
const raw =
|
|
91259
|
+
const raw = readFileSync43(metaPath, "utf8");
|
|
90881
91260
|
meta = JSON.parse(raw);
|
|
90882
91261
|
} catch {
|
|
90883
91262
|
log?.(`subagent-watcher: backfill skip ${agentId} \u2014 meta.json not readable at ${metaPath}`);
|
|
@@ -91332,7 +91711,7 @@ function startSubagentWatcher(config) {
|
|
|
91332
91711
|
clearTimeout(ref.ref);
|
|
91333
91712
|
});
|
|
91334
91713
|
const fs2 = config.fs ?? {
|
|
91335
|
-
existsSync:
|
|
91714
|
+
existsSync: existsSync42,
|
|
91336
91715
|
readdirSync: readdirSync8,
|
|
91337
91716
|
statSync: statSync13,
|
|
91338
91717
|
openSync: openSync8,
|
|
@@ -91850,8 +92229,8 @@ function startSubagentWatcher(config) {
|
|
|
91850
92229
|
if (stopped)
|
|
91851
92230
|
return;
|
|
91852
92231
|
pruneVanishedDirWatchers();
|
|
91853
|
-
const claudeHome =
|
|
91854
|
-
const projectsRoot =
|
|
92232
|
+
const claudeHome = join47(agentDir, ".claude");
|
|
92233
|
+
const projectsRoot = join47(claudeHome, "projects");
|
|
91855
92234
|
if (!fs2.existsSync(projectsRoot))
|
|
91856
92235
|
return;
|
|
91857
92236
|
let projectDirs;
|
|
@@ -91885,7 +92264,7 @@ function startSubagentWatcher(config) {
|
|
|
91885
92264
|
continue;
|
|
91886
92265
|
}
|
|
91887
92266
|
warnedForeignSlugs.delete(pDir);
|
|
91888
|
-
const projectPath =
|
|
92267
|
+
const projectPath = join47(projectsRoot, pDir);
|
|
91889
92268
|
let sessionDirs;
|
|
91890
92269
|
try {
|
|
91891
92270
|
sessionDirs = fs2.readdirSync(projectPath);
|
|
@@ -91895,7 +92274,7 @@ function startSubagentWatcher(config) {
|
|
|
91895
92274
|
for (const sDir of sessionDirs) {
|
|
91896
92275
|
if (sDir.endsWith(".jsonl"))
|
|
91897
92276
|
continue;
|
|
91898
|
-
const subagentsPath =
|
|
92277
|
+
const subagentsPath = join47(projectPath, sDir, "subagents");
|
|
91899
92278
|
if (!fs2.existsSync(subagentsPath))
|
|
91900
92279
|
continue;
|
|
91901
92280
|
const watchAndScan = (dirPath) => {
|
|
@@ -91904,7 +92283,7 @@ function startSubagentWatcher(config) {
|
|
|
91904
92283
|
const w = fs2.watch(dirPath, (_event, filename) => {
|
|
91905
92284
|
if (!filename || !filename.toString().startsWith("agent-") || !filename.toString().endsWith(".jsonl"))
|
|
91906
92285
|
return;
|
|
91907
|
-
const filePath =
|
|
92286
|
+
const filePath = join47(dirPath, filename.toString());
|
|
91908
92287
|
if (!knownFiles.has(filePath)) {
|
|
91909
92288
|
scanSubagentsDir(dirPath);
|
|
91910
92289
|
}
|
|
@@ -91918,7 +92297,7 @@ function startSubagentWatcher(config) {
|
|
|
91918
92297
|
scanSubagentsDir(dirPath);
|
|
91919
92298
|
};
|
|
91920
92299
|
watchAndScan(subagentsPath);
|
|
91921
|
-
const workflowsPath =
|
|
92300
|
+
const workflowsPath = join47(subagentsPath, "workflows");
|
|
91922
92301
|
if (fs2.existsSync(workflowsPath)) {
|
|
91923
92302
|
let wfDirs;
|
|
91924
92303
|
try {
|
|
@@ -91928,7 +92307,7 @@ function startSubagentWatcher(config) {
|
|
|
91928
92307
|
}
|
|
91929
92308
|
for (const wfDir of wfDirs) {
|
|
91930
92309
|
try {
|
|
91931
|
-
const wfPath =
|
|
92310
|
+
const wfPath = join47(workflowsPath, wfDir);
|
|
91932
92311
|
if (!fs2.statSync(wfPath).isDirectory())
|
|
91933
92312
|
continue;
|
|
91934
92313
|
watchAndScan(wfPath);
|
|
@@ -91948,7 +92327,7 @@ function startSubagentWatcher(config) {
|
|
|
91948
92327
|
for (const e of entries) {
|
|
91949
92328
|
if (!e.startsWith("agent-") || !e.endsWith(".jsonl"))
|
|
91950
92329
|
continue;
|
|
91951
|
-
const filePath =
|
|
92330
|
+
const filePath = join47(subagentsPath, e);
|
|
91952
92331
|
if (knownFiles.has(filePath))
|
|
91953
92332
|
continue;
|
|
91954
92333
|
const agentId = e.slice("agent-".length, -".jsonl".length);
|
|
@@ -92077,20 +92456,20 @@ function startSubagentWatcher(config) {
|
|
|
92077
92456
|
// ../src/worktree/registry.ts
|
|
92078
92457
|
import {
|
|
92079
92458
|
mkdirSync as mkdirSync37,
|
|
92080
|
-
writeFileSync as
|
|
92081
|
-
readFileSync as
|
|
92459
|
+
writeFileSync as writeFileSync37,
|
|
92460
|
+
readFileSync as readFileSync44,
|
|
92082
92461
|
readdirSync as readdirSync9,
|
|
92083
92462
|
unlinkSync as unlinkSync22,
|
|
92084
|
-
existsSync as
|
|
92463
|
+
existsSync as existsSync43,
|
|
92085
92464
|
renameSync as renameSync18
|
|
92086
92465
|
} from "node:fs";
|
|
92087
|
-
import { join as
|
|
92466
|
+
import { join as join48, resolve as resolve10 } from "node:path";
|
|
92088
92467
|
import { homedir as homedir16 } from "node:os";
|
|
92089
92468
|
function registryDir() {
|
|
92090
|
-
return resolve10(process.env.SWITCHROOM_WORKTREE_DIR ??
|
|
92469
|
+
return resolve10(process.env.SWITCHROOM_WORKTREE_DIR ?? join48(homedir16(), ".switchroom", "worktrees"));
|
|
92091
92470
|
}
|
|
92092
92471
|
function recordPath(id) {
|
|
92093
|
-
return
|
|
92472
|
+
return join48(registryDir(), `${id}.json`);
|
|
92094
92473
|
}
|
|
92095
92474
|
function ensureDir4() {
|
|
92096
92475
|
mkdirSync37(registryDir(), { recursive: true });
|
|
@@ -92099,14 +92478,14 @@ function writeRecord(record) {
|
|
|
92099
92478
|
ensureDir4();
|
|
92100
92479
|
const target = recordPath(record.id);
|
|
92101
92480
|
const tmp = `${target}.tmp${process.pid}`;
|
|
92102
|
-
|
|
92481
|
+
writeFileSync37(tmp, JSON.stringify(record, null, 2) + `
|
|
92103
92482
|
`, { mode: 384 });
|
|
92104
92483
|
renameSync18(tmp, target);
|
|
92105
92484
|
}
|
|
92106
92485
|
function readRecord(id) {
|
|
92107
92486
|
const path2 = recordPath(id);
|
|
92108
92487
|
try {
|
|
92109
|
-
const raw =
|
|
92488
|
+
const raw = readFileSync44(path2, "utf8");
|
|
92110
92489
|
return JSON.parse(raw);
|
|
92111
92490
|
} catch {
|
|
92112
92491
|
return null;
|
|
@@ -92136,7 +92515,7 @@ function touchHeartbeat(id, onAfterRead) {
|
|
|
92136
92515
|
writeRecord({ ...rec, heartbeatAt: new Date().toISOString() });
|
|
92137
92516
|
}
|
|
92138
92517
|
function recordExists(id) {
|
|
92139
|
-
return
|
|
92518
|
+
return existsSync43(recordPath(id));
|
|
92140
92519
|
}
|
|
92141
92520
|
|
|
92142
92521
|
// worktree-watch-cwds.ts
|
|
@@ -92268,15 +92647,15 @@ function determineRestartReason(opts) {
|
|
|
92268
92647
|
init_boot_card();
|
|
92269
92648
|
|
|
92270
92649
|
// gateway/update-announce.ts
|
|
92271
|
-
import { existsSync as
|
|
92272
|
-
import { join as
|
|
92650
|
+
import { existsSync as existsSync48, mkdirSync as mkdirSync41, openSync as openSync9, closeSync as closeSync9, readFileSync as readFileSync51 } from "node:fs";
|
|
92651
|
+
import { join as join53 } from "node:path";
|
|
92273
92652
|
import { homedir as homedir18 } from "node:os";
|
|
92274
92653
|
|
|
92275
92654
|
// ../src/host-control/audit-reader.ts
|
|
92276
92655
|
import { homedir as homedir17 } from "node:os";
|
|
92277
|
-
import { join as
|
|
92656
|
+
import { join as join52 } from "node:path";
|
|
92278
92657
|
function defaultAuditLogPath(home2 = homedir17()) {
|
|
92279
|
-
return
|
|
92658
|
+
return join52(home2, ".switchroom", "host-control-audit.log");
|
|
92280
92659
|
}
|
|
92281
92660
|
function parseAuditLine(line) {
|
|
92282
92661
|
const trimmed = line.trim();
|
|
@@ -92399,8 +92778,8 @@ function readAndFilter(raw, filters, limit) {
|
|
|
92399
92778
|
var DEFAULT_LOOKBACK_MS = 10 * 60 * 1000;
|
|
92400
92779
|
function readLastTerminalUpdateAudit(opts = {}) {
|
|
92401
92780
|
const path2 = opts.auditLogPath ?? defaultAuditLogPath();
|
|
92402
|
-
const exists = opts.exists ??
|
|
92403
|
-
const readFile = opts.readFile ?? ((p) =>
|
|
92781
|
+
const exists = opts.exists ?? existsSync48;
|
|
92782
|
+
const readFile = opts.readFile ?? ((p) => readFileSync51(p, "utf-8"));
|
|
92404
92783
|
if (!exists(path2))
|
|
92405
92784
|
return null;
|
|
92406
92785
|
let raw;
|
|
@@ -92461,15 +92840,15 @@ function renderUpdateOutcomeLine(entry) {
|
|
|
92461
92840
|
`);
|
|
92462
92841
|
}
|
|
92463
92842
|
function claimUpdateAnnouncement(requestId, opts = {}) {
|
|
92464
|
-
const stateDir = opts.stateDir ?? process.env.TELEGRAM_STATE_DIR ??
|
|
92465
|
-
const dir =
|
|
92843
|
+
const stateDir = opts.stateDir ?? process.env.TELEGRAM_STATE_DIR ?? join53(homedir18(), ".switchroom");
|
|
92844
|
+
const dir = join53(stateDir, "update-announced");
|
|
92466
92845
|
try {
|
|
92467
92846
|
mkdirSync41(dir, { recursive: true });
|
|
92468
92847
|
} catch {
|
|
92469
92848
|
return false;
|
|
92470
92849
|
}
|
|
92471
92850
|
const safeId = requestId.replace(/[^A-Za-z0-9_.-]/g, "_").slice(0, 200);
|
|
92472
|
-
const path2 =
|
|
92851
|
+
const path2 = join53(dir, safeId);
|
|
92473
92852
|
try {
|
|
92474
92853
|
const fd = openSync9(path2, "wx");
|
|
92475
92854
|
closeSync9(fd);
|
|
@@ -92489,7 +92868,7 @@ function maybeRenderUpdateAnnouncement(opts = {}) {
|
|
|
92489
92868
|
|
|
92490
92869
|
// issues-card.ts
|
|
92491
92870
|
init_card_format();
|
|
92492
|
-
import { readFileSync as
|
|
92871
|
+
import { readFileSync as readFileSync52, writeFileSync as writeFileSync42 } from "node:fs";
|
|
92493
92872
|
var SEVERITY_EMOJI = {
|
|
92494
92873
|
info: "\u2139\ufe0f",
|
|
92495
92874
|
warn: "\u26a0\ufe0f",
|
|
@@ -92581,7 +92960,7 @@ function extractRetryAfterSecs2(err) {
|
|
|
92581
92960
|
var COOLDOWN_JITTER_MS2 = 500;
|
|
92582
92961
|
function readPersistedMessageId(path2, log) {
|
|
92583
92962
|
try {
|
|
92584
|
-
const raw =
|
|
92963
|
+
const raw = readFileSync52(path2, "utf8");
|
|
92585
92964
|
const parsed = JSON.parse(raw);
|
|
92586
92965
|
const v = parsed.messageId;
|
|
92587
92966
|
if (typeof v === "number" && Number.isInteger(v) && v > 0)
|
|
@@ -92597,7 +92976,7 @@ function readPersistedMessageId(path2, log) {
|
|
|
92597
92976
|
}
|
|
92598
92977
|
function writePersistedMessageId(path2, messageId, log) {
|
|
92599
92978
|
try {
|
|
92600
|
-
|
|
92979
|
+
writeFileSync42(path2, JSON.stringify({ messageId }) + `
|
|
92601
92980
|
`, { mode: 384 });
|
|
92602
92981
|
} catch (err) {
|
|
92603
92982
|
log(`issues-card: persist write failed (${err.message})`);
|
|
@@ -92690,24 +93069,24 @@ function createIssuesCardHandle(opts) {
|
|
|
92690
93069
|
}
|
|
92691
93070
|
|
|
92692
93071
|
// issues-watcher.ts
|
|
92693
|
-
import { existsSync as
|
|
92694
|
-
import { join as
|
|
93072
|
+
import { existsSync as existsSync50, statSync as statSync16 } from "node:fs";
|
|
93073
|
+
import { join as join55 } from "node:path";
|
|
92695
93074
|
|
|
92696
93075
|
// ../src/issues/store.ts
|
|
92697
93076
|
import {
|
|
92698
93077
|
closeSync as closeSync10,
|
|
92699
|
-
existsSync as
|
|
93078
|
+
existsSync as existsSync49,
|
|
92700
93079
|
mkdirSync as mkdirSync42,
|
|
92701
93080
|
openSync as openSync10,
|
|
92702
93081
|
readdirSync as readdirSync11,
|
|
92703
|
-
readFileSync as
|
|
93082
|
+
readFileSync as readFileSync53,
|
|
92704
93083
|
renameSync as renameSync21,
|
|
92705
93084
|
statSync as statSync15,
|
|
92706
93085
|
unlinkSync as unlinkSync23,
|
|
92707
|
-
writeFileSync as
|
|
93086
|
+
writeFileSync as writeFileSync43,
|
|
92708
93087
|
writeSync as writeSync6
|
|
92709
93088
|
} from "node:fs";
|
|
92710
|
-
import { join as
|
|
93089
|
+
import { join as join54 } from "node:path";
|
|
92711
93090
|
import { randomBytes as randomBytes8 } from "node:crypto";
|
|
92712
93091
|
import { execSync } from "node:child_process";
|
|
92713
93092
|
|
|
@@ -92726,12 +93105,12 @@ init_redact();
|
|
|
92726
93105
|
var ISSUES_FILE = "issues.jsonl";
|
|
92727
93106
|
var ISSUES_LOCK = "issues.lock";
|
|
92728
93107
|
function readAll(stateDir) {
|
|
92729
|
-
const path2 =
|
|
92730
|
-
if (!
|
|
93108
|
+
const path2 = join54(stateDir, ISSUES_FILE);
|
|
93109
|
+
if (!existsSync49(path2))
|
|
92731
93110
|
return [];
|
|
92732
93111
|
let raw;
|
|
92733
93112
|
try {
|
|
92734
|
-
raw =
|
|
93113
|
+
raw = readFileSync53(path2, "utf-8");
|
|
92735
93114
|
} catch {
|
|
92736
93115
|
return [];
|
|
92737
93116
|
}
|
|
@@ -92763,7 +93142,7 @@ function list2(stateDir, opts = {}) {
|
|
|
92763
93142
|
});
|
|
92764
93143
|
}
|
|
92765
93144
|
function resolve11(stateDir, fingerprint, nowFn = Date.now) {
|
|
92766
|
-
if (!
|
|
93145
|
+
if (!existsSync49(join54(stateDir, ISSUES_FILE)))
|
|
92767
93146
|
return 0;
|
|
92768
93147
|
return withLock(stateDir, () => {
|
|
92769
93148
|
const all2 = readAll(stateDir);
|
|
@@ -92781,13 +93160,13 @@ function resolve11(stateDir, fingerprint, nowFn = Date.now) {
|
|
|
92781
93160
|
});
|
|
92782
93161
|
}
|
|
92783
93162
|
function writeAll(stateDir, events) {
|
|
92784
|
-
const path2 =
|
|
93163
|
+
const path2 = join54(stateDir, ISSUES_FILE);
|
|
92785
93164
|
sweepOrphanTmpFiles(stateDir);
|
|
92786
93165
|
const tmp = `${path2}.tmp-${process.pid}-${randomBytes8(4).toString("hex")}`;
|
|
92787
93166
|
const body = events.length === 0 ? "" : events.map((e) => JSON.stringify(e)).join(`
|
|
92788
93167
|
`) + `
|
|
92789
93168
|
`;
|
|
92790
|
-
|
|
93169
|
+
writeFileSync43(tmp, body, "utf-8");
|
|
92791
93170
|
renameSync21(tmp, path2);
|
|
92792
93171
|
}
|
|
92793
93172
|
var ORPHAN_TMP_TTL_MS = 60000;
|
|
@@ -92803,7 +93182,7 @@ function sweepOrphanTmpFiles(stateDir) {
|
|
|
92803
93182
|
for (const entry of entries) {
|
|
92804
93183
|
if (!entry.startsWith(TMP_PREFIX))
|
|
92805
93184
|
continue;
|
|
92806
|
-
const tmpPath2 =
|
|
93185
|
+
const tmpPath2 = join54(stateDir, entry);
|
|
92807
93186
|
try {
|
|
92808
93187
|
const stat = statSync15(tmpPath2);
|
|
92809
93188
|
if (stat.mtimeMs < cutoff) {
|
|
@@ -92815,7 +93194,7 @@ function sweepOrphanTmpFiles(stateDir) {
|
|
|
92815
93194
|
var LOCK_RETRY_MS = 25;
|
|
92816
93195
|
var LOCK_TIMEOUT_MS = 1e4;
|
|
92817
93196
|
function withLock(stateDir, fn) {
|
|
92818
|
-
const lockPath =
|
|
93197
|
+
const lockPath = join54(stateDir, ISSUES_LOCK);
|
|
92819
93198
|
const startedAt = Date.now();
|
|
92820
93199
|
let fd = null;
|
|
92821
93200
|
while (fd === null) {
|
|
@@ -92850,7 +93229,7 @@ function withLock(stateDir, fn) {
|
|
|
92850
93229
|
function tryStealStaleLock(lockPath) {
|
|
92851
93230
|
let pidStr;
|
|
92852
93231
|
try {
|
|
92853
|
-
pidStr =
|
|
93232
|
+
pidStr = readFileSync53(lockPath, "utf-8").trim();
|
|
92854
93233
|
} catch {
|
|
92855
93234
|
return true;
|
|
92856
93235
|
}
|
|
@@ -92900,7 +93279,7 @@ function isIssueEvent(v) {
|
|
|
92900
93279
|
// issues-watcher.ts
|
|
92901
93280
|
var DEFAULT_POLL_INTERVAL_MS2 = 2000;
|
|
92902
93281
|
function startIssuesWatcher(opts) {
|
|
92903
|
-
const path2 =
|
|
93282
|
+
const path2 = join55(opts.stateDir, ISSUES_FILE);
|
|
92904
93283
|
const log = opts.log ?? (() => {});
|
|
92905
93284
|
const intervalMs = opts.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS2;
|
|
92906
93285
|
const setIntervalFn = opts.setInterval ?? setInterval;
|
|
@@ -92948,7 +93327,7 @@ function startIssuesWatcher(opts) {
|
|
|
92948
93327
|
};
|
|
92949
93328
|
}
|
|
92950
93329
|
function defaultSignatureProvider(path2) {
|
|
92951
|
-
if (!
|
|
93330
|
+
if (!existsSync50(path2))
|
|
92952
93331
|
return null;
|
|
92953
93332
|
try {
|
|
92954
93333
|
const stat = statSync16(path2);
|
|
@@ -93659,8 +94038,8 @@ function readBashCommand(inputPreview) {
|
|
|
93659
94038
|
}
|
|
93660
94039
|
|
|
93661
94040
|
// gateway/scoped-grant-store.ts
|
|
93662
|
-
import { readFileSync as
|
|
93663
|
-
import { join as
|
|
94041
|
+
import { readFileSync as readFileSync54, writeFileSync as writeFileSync44 } from "node:fs";
|
|
94042
|
+
import { join as join56 } from "node:path";
|
|
93664
94043
|
|
|
93665
94044
|
// scoped-approval.ts
|
|
93666
94045
|
var SCOPED_APPROVAL_DEFAULT_TTL_MS2 = 30 * 60 * 1000;
|
|
@@ -93702,11 +94081,11 @@ function scopedGrantPersistEnabled(env = process.env) {
|
|
|
93702
94081
|
return env.SWITCHROOM_SCOPED_GRANT_PERSIST !== "0";
|
|
93703
94082
|
}
|
|
93704
94083
|
function createScopedGrantStore(stateDir, env = process.env) {
|
|
93705
|
-
const filePath =
|
|
94084
|
+
const filePath = join56(stateDir, "scoped-grants.json");
|
|
93706
94085
|
const enabled7 = scopedGrantPersistEnabled(env);
|
|
93707
94086
|
function read() {
|
|
93708
94087
|
try {
|
|
93709
|
-
const raw =
|
|
94088
|
+
const raw = readFileSync54(filePath, "utf-8");
|
|
93710
94089
|
const parsed = JSON.parse(raw);
|
|
93711
94090
|
return Array.isArray(parsed) ? parsed : [];
|
|
93712
94091
|
} catch {
|
|
@@ -93724,7 +94103,7 @@ function createScopedGrantStore(stateDir, env = process.env) {
|
|
|
93724
94103
|
if (!enabled7)
|
|
93725
94104
|
return;
|
|
93726
94105
|
try {
|
|
93727
|
-
|
|
94106
|
+
writeFileSync44(filePath, JSON.stringify(serializeScopedGrants(store2)), {
|
|
93728
94107
|
encoding: "utf-8",
|
|
93729
94108
|
mode: 384
|
|
93730
94109
|
});
|
|
@@ -94075,8 +94454,8 @@ function isDiffPreApproved(agentName3, unifiedDiff, deps) {
|
|
|
94075
94454
|
|
|
94076
94455
|
// credits-watch.ts
|
|
94077
94456
|
init_card_format();
|
|
94078
|
-
import { readFileSync as
|
|
94079
|
-
import { join as
|
|
94457
|
+
import { readFileSync as readFileSync55, writeFileSync as writeFileSync45, existsSync as existsSync51, mkdirSync as mkdirSync43 } from "fs";
|
|
94458
|
+
import { join as join57 } from "path";
|
|
94080
94459
|
var STATE_FILE = "credits-watch.json";
|
|
94081
94460
|
var DEFAULT_CREDIT_FATAL_REASONS = new Set;
|
|
94082
94461
|
var KNOWN_CREDIT_REASONS = [
|
|
@@ -94098,12 +94477,12 @@ function emptyCreditState() {
|
|
|
94098
94477
|
return { lastNotifiedReason: null, lastNotifiedAt: 0 };
|
|
94099
94478
|
}
|
|
94100
94479
|
function readClaudeJsonOverage(claudeConfigDir) {
|
|
94101
|
-
const path2 =
|
|
94102
|
-
if (!
|
|
94480
|
+
const path2 = join57(claudeConfigDir, ".claude.json");
|
|
94481
|
+
if (!existsSync51(path2))
|
|
94103
94482
|
return null;
|
|
94104
94483
|
let raw;
|
|
94105
94484
|
try {
|
|
94106
|
-
raw =
|
|
94485
|
+
raw = readFileSync55(path2, "utf-8");
|
|
94107
94486
|
} catch {
|
|
94108
94487
|
return null;
|
|
94109
94488
|
}
|
|
@@ -94181,11 +94560,11 @@ function humanizeReason(reason) {
|
|
|
94181
94560
|
}
|
|
94182
94561
|
}
|
|
94183
94562
|
function loadCreditState(stateDir) {
|
|
94184
|
-
const path2 =
|
|
94185
|
-
if (!
|
|
94563
|
+
const path2 = join57(stateDir, STATE_FILE);
|
|
94564
|
+
if (!existsSync51(path2))
|
|
94186
94565
|
return emptyCreditState();
|
|
94187
94566
|
try {
|
|
94188
|
-
const raw =
|
|
94567
|
+
const raw = readFileSync55(path2, "utf-8");
|
|
94189
94568
|
const parsed = JSON.parse(raw);
|
|
94190
94569
|
if (parsed && typeof parsed === "object" && (parsed.lastNotifiedReason === null || typeof parsed.lastNotifiedReason === "string") && typeof parsed.lastNotifiedAt === "number" && Number.isFinite(parsed.lastNotifiedAt)) {
|
|
94191
94570
|
return {
|
|
@@ -94198,16 +94577,16 @@ function loadCreditState(stateDir) {
|
|
|
94198
94577
|
}
|
|
94199
94578
|
function saveCreditState(stateDir, state7) {
|
|
94200
94579
|
mkdirSync43(stateDir, { recursive: true });
|
|
94201
|
-
const path2 =
|
|
94202
|
-
|
|
94580
|
+
const path2 = join57(stateDir, STATE_FILE);
|
|
94581
|
+
writeFileSync45(path2, JSON.stringify(state7, null, 2) + `
|
|
94203
94582
|
`, { mode: 384 });
|
|
94204
94583
|
}
|
|
94205
94584
|
|
|
94206
94585
|
// quota-watch.ts
|
|
94207
94586
|
init_auth_snapshot_format();
|
|
94208
94587
|
init_card_format();
|
|
94209
|
-
import { readFileSync as
|
|
94210
|
-
import { join as
|
|
94588
|
+
import { readFileSync as readFileSync56, writeFileSync as writeFileSync46, existsSync as existsSync52, mkdirSync as mkdirSync44 } from "fs";
|
|
94589
|
+
import { join as join58 } from "path";
|
|
94211
94590
|
var STATE_FILE2 = "quota-watch.json";
|
|
94212
94591
|
function emptyQuotaWatchState() {
|
|
94213
94592
|
return {};
|
|
@@ -94445,11 +94824,11 @@ function buildRecoveryMessage(agentName3, snap) {
|
|
|
94445
94824
|
`);
|
|
94446
94825
|
}
|
|
94447
94826
|
function loadQuotaWatchState(stateDir) {
|
|
94448
|
-
const path2 =
|
|
94449
|
-
if (!
|
|
94827
|
+
const path2 = join58(stateDir, STATE_FILE2);
|
|
94828
|
+
if (!existsSync52(path2))
|
|
94450
94829
|
return emptyQuotaWatchState();
|
|
94451
94830
|
try {
|
|
94452
|
-
const raw =
|
|
94831
|
+
const raw = readFileSync56(path2, "utf-8");
|
|
94453
94832
|
const parsed = JSON.parse(raw);
|
|
94454
94833
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
94455
94834
|
return emptyQuotaWatchState();
|
|
@@ -94467,8 +94846,8 @@ function loadQuotaWatchState(stateDir) {
|
|
|
94467
94846
|
}
|
|
94468
94847
|
function saveQuotaWatchState(stateDir, state7) {
|
|
94469
94848
|
mkdirSync44(stateDir, { recursive: true });
|
|
94470
|
-
const path2 =
|
|
94471
|
-
|
|
94849
|
+
const path2 = join58(stateDir, STATE_FILE2);
|
|
94850
|
+
writeFileSync46(path2, JSON.stringify(state7, null, 2) + `
|
|
94472
94851
|
`, { mode: 384 });
|
|
94473
94852
|
}
|
|
94474
94853
|
function patchQuotaWatchState(current, accountLabel, accountState) {
|
|
@@ -94516,27 +94895,27 @@ init_auth_snapshot_format2();
|
|
|
94516
94895
|
// gateway/turn-active-marker.ts
|
|
94517
94896
|
import {
|
|
94518
94897
|
closeSync as closeSync11,
|
|
94519
|
-
existsSync as
|
|
94898
|
+
existsSync as existsSync53,
|
|
94520
94899
|
mkdirSync as mkdirSync45,
|
|
94521
94900
|
openSync as openSync11,
|
|
94522
|
-
readFileSync as
|
|
94901
|
+
readFileSync as readFileSync57,
|
|
94523
94902
|
statSync as statSync17,
|
|
94524
94903
|
unlinkSync as unlinkSync24,
|
|
94525
94904
|
utimesSync as utimesSync2,
|
|
94526
|
-
writeFileSync as
|
|
94905
|
+
writeFileSync as writeFileSync47
|
|
94527
94906
|
} from "node:fs";
|
|
94528
|
-
import { join as
|
|
94907
|
+
import { join as join59 } from "node:path";
|
|
94529
94908
|
var TURN_ACTIVE_MARKER_FILE2 = "turn-active.json";
|
|
94530
94909
|
var TURN_ACTIVE_HARD_TTL_MS2 = 10 * 60000;
|
|
94531
94910
|
var TURN_ACTIVE_IDLE_SWEEP_MS = 60000;
|
|
94532
94911
|
function removeTurnActiveMarker2(stateDir) {
|
|
94533
94912
|
try {
|
|
94534
|
-
unlinkSync24(
|
|
94913
|
+
unlinkSync24(join59(stateDir, TURN_ACTIVE_MARKER_FILE2));
|
|
94535
94914
|
} catch {}
|
|
94536
94915
|
}
|
|
94537
94916
|
function sweepStaleTurnActiveMarker(stateDir, opts) {
|
|
94538
|
-
const path2 =
|
|
94539
|
-
if (!
|
|
94917
|
+
const path2 = join59(stateDir, TURN_ACTIVE_MARKER_FILE2);
|
|
94918
|
+
if (!existsSync53(path2))
|
|
94540
94919
|
return false;
|
|
94541
94920
|
const now = opts.now ?? Date.now();
|
|
94542
94921
|
try {
|
|
@@ -94548,7 +94927,7 @@ function sweepStaleTurnActiveMarker(stateDir, opts) {
|
|
|
94548
94927
|
return false;
|
|
94549
94928
|
let payload = null;
|
|
94550
94929
|
try {
|
|
94551
|
-
payload =
|
|
94930
|
+
payload = readFileSync57(path2, "utf8");
|
|
94552
94931
|
} catch {}
|
|
94553
94932
|
unlinkSync24(path2);
|
|
94554
94933
|
if (opts.onRemove) {
|
|
@@ -94566,7 +94945,7 @@ function sweepStaleTurnActiveMarker(stateDir, opts) {
|
|
|
94566
94945
|
}
|
|
94567
94946
|
}
|
|
94568
94947
|
function readTurnActiveMarkerAgeMs2(stateDir, now) {
|
|
94569
|
-
const path2 =
|
|
94948
|
+
const path2 = join59(stateDir, TURN_ACTIVE_MARKER_FILE2);
|
|
94570
94949
|
try {
|
|
94571
94950
|
const st = statSync17(path2);
|
|
94572
94951
|
return (now ?? Date.now()) - st.mtimeMs;
|
|
@@ -94579,19 +94958,19 @@ function effectiveTurnAgeMs(markerAgeMs, turnStartedAt, now) {
|
|
|
94579
94958
|
}
|
|
94580
94959
|
|
|
94581
94960
|
// gateway/gateway-heartbeat.ts
|
|
94582
|
-
import { mkdirSync as mkdirSync46, utimesSync as utimesSync3, writeFileSync as
|
|
94583
|
-
import { join as
|
|
94961
|
+
import { mkdirSync as mkdirSync46, utimesSync as utimesSync3, writeFileSync as writeFileSync48 } from "node:fs";
|
|
94962
|
+
import { join as join60 } from "node:path";
|
|
94584
94963
|
var GATEWAY_HEARTBEAT_FILE = "gateway-heartbeat";
|
|
94585
94964
|
var GATEWAY_HEARTBEAT_INTERVAL_MS = 15000;
|
|
94586
94965
|
function touchGatewayHeartbeat(stateDir) {
|
|
94587
|
-
const path2 =
|
|
94966
|
+
const path2 = join60(stateDir, GATEWAY_HEARTBEAT_FILE);
|
|
94588
94967
|
const now = new Date;
|
|
94589
94968
|
try {
|
|
94590
94969
|
utimesSync3(path2, now, now);
|
|
94591
94970
|
} catch {
|
|
94592
94971
|
try {
|
|
94593
94972
|
mkdirSync46(stateDir, { recursive: true });
|
|
94594
|
-
|
|
94973
|
+
writeFileSync48(path2, `${Date.now()}
|
|
94595
94974
|
`, { mode: 384 });
|
|
94596
94975
|
} catch {}
|
|
94597
94976
|
}
|
|
@@ -94758,10 +95137,10 @@ function startOutboxSweep(deps) {
|
|
|
94758
95137
|
}
|
|
94759
95138
|
|
|
94760
95139
|
// ../src/build-info.ts
|
|
94761
|
-
var
|
|
94762
|
-
var COMMIT_SHA = "
|
|
94763
|
-
var COMMIT_DATE = "2026-07-
|
|
94764
|
-
var LATEST_PR =
|
|
95140
|
+
var VERSION2 = "0.19.18";
|
|
95141
|
+
var COMMIT_SHA = "d3a10dee";
|
|
95142
|
+
var COMMIT_DATE = "2026-07-25T15:13:16+10:00";
|
|
95143
|
+
var LATEST_PR = 3595;
|
|
94765
95144
|
var COMMITS_AHEAD_OF_TAG = 0;
|
|
94766
95145
|
|
|
94767
95146
|
// gateway/boot-version.ts
|
|
@@ -94839,11 +95218,11 @@ init_peercred();
|
|
|
94839
95218
|
import * as net5 from "node:net";
|
|
94840
95219
|
import * as fs2 from "node:fs";
|
|
94841
95220
|
import { homedir as homedir19 } from "node:os";
|
|
94842
|
-
import { join as
|
|
95221
|
+
import { join as join61 } from "node:path";
|
|
94843
95222
|
var DEFAULT_TIMEOUT_MS4 = 2000;
|
|
94844
95223
|
var UNLOCK_TIMEOUT_MS = 30000;
|
|
94845
|
-
var LEGACY_SOCKET_PATH2 =
|
|
94846
|
-
var OPERATOR_SOCKET_PATH2 =
|
|
95224
|
+
var LEGACY_SOCKET_PATH2 = join61(homedir19(), ".switchroom", "vault-broker.sock");
|
|
95225
|
+
var OPERATOR_SOCKET_PATH2 = join61(homedir19(), ".switchroom", "broker-operator", "sock");
|
|
94847
95226
|
function defaultBrokerSocketPath2() {
|
|
94848
95227
|
if (fs2.existsSync(OPERATOR_SOCKET_PATH2))
|
|
94849
95228
|
return OPERATOR_SOCKET_PATH2;
|
|
@@ -95726,7 +96105,7 @@ function resolveVaultApprovalPosture(broker) {
|
|
|
95726
96105
|
|
|
95727
96106
|
// registry/turns-schema.ts
|
|
95728
96107
|
import { chmodSync as chmodSync12, mkdirSync as mkdirSync47 } from "fs";
|
|
95729
|
-
import { join as
|
|
96108
|
+
import { join as join62 } from "path";
|
|
95730
96109
|
var DatabaseClass3 = null;
|
|
95731
96110
|
function loadDatabaseClass3() {
|
|
95732
96111
|
if (DatabaseClass3 != null)
|
|
@@ -95798,9 +96177,9 @@ function applySchema(db3) {
|
|
|
95798
96177
|
}
|
|
95799
96178
|
function openTurnsDb(agentDir) {
|
|
95800
96179
|
const Database = loadDatabaseClass3();
|
|
95801
|
-
const dir =
|
|
96180
|
+
const dir = join62(agentDir, "telegram");
|
|
95802
96181
|
mkdirSync47(dir, { recursive: true, mode: 448 });
|
|
95803
|
-
const path2 =
|
|
96182
|
+
const path2 = join62(dir, "registry.db");
|
|
95804
96183
|
const db3 = new Database(path2, { create: true });
|
|
95805
96184
|
applySchema(db3);
|
|
95806
96185
|
try {
|
|
@@ -95889,7 +96268,7 @@ function reapStaleOpenTurns(db3, opts) {
|
|
|
95889
96268
|
const stamp = db3.prepare(`
|
|
95890
96269
|
UPDATE turns
|
|
95891
96270
|
SET ended_at = ?,
|
|
95892
|
-
ended_via = '
|
|
96271
|
+
ended_via = 'reaped_stale',
|
|
95893
96272
|
updated_at = ?
|
|
95894
96273
|
WHERE turn_key = ? AND ended_at IS NULL
|
|
95895
96274
|
`);
|
|
@@ -95905,6 +96284,7 @@ function reapStaleOpenTurns(db3, opts) {
|
|
|
95905
96284
|
}
|
|
95906
96285
|
var INTERRUPTED_VIA2 = new Set([
|
|
95907
96286
|
"restart",
|
|
96287
|
+
"reaped_stale",
|
|
95908
96288
|
"sigterm",
|
|
95909
96289
|
"timeout",
|
|
95910
96290
|
"unknown"
|
|
@@ -96131,7 +96511,7 @@ function selectResumeBuilder(endedVia, opts) {
|
|
|
96131
96511
|
let kind;
|
|
96132
96512
|
if (endedVia === "timeout")
|
|
96133
96513
|
kind = "report";
|
|
96134
|
-
else if (endedVia === "restart" || endedVia === "sigterm" || endedVia === "unknown")
|
|
96514
|
+
else if (endedVia === "restart" || endedVia === "reaped_stale" || endedVia === "sigterm" || endedVia === "unknown")
|
|
96135
96515
|
kind = "resume";
|
|
96136
96516
|
else if (endedVia == null)
|
|
96137
96517
|
kind = "resume";
|
|
@@ -96144,7 +96524,7 @@ function selectResumeBuilder(endedVia, opts) {
|
|
|
96144
96524
|
}
|
|
96145
96525
|
|
|
96146
96526
|
// gateway/bridge-dead-watchdog.ts
|
|
96147
|
-
import { readFileSync as
|
|
96527
|
+
import { readFileSync as readFileSync59, writeFileSync as writeFileSync49, renameSync as renameSync22, unlinkSync as unlinkSync25 } from "node:fs";
|
|
96148
96528
|
|
|
96149
96529
|
// gateway/cron-session.ts
|
|
96150
96530
|
var CRON_IDENTITY_SUFFIX2 = "-cron";
|
|
@@ -96177,7 +96557,7 @@ function readFreshCrashLogTail(path2, opts = {}) {
|
|
|
96177
96557
|
const nowMs3 = opts.nowMs ?? Date.now();
|
|
96178
96558
|
const freshWindowMs = opts.freshWindowMs ?? CRASH_LOG_FRESH_WINDOW_MS;
|
|
96179
96559
|
const maxLines = opts.maxLines ?? CRASH_LOG_TAIL_LINES;
|
|
96180
|
-
const readFile = opts.readFile ?? ((p) =>
|
|
96560
|
+
const readFile = opts.readFile ?? ((p) => readFileSync59(p, "utf8"));
|
|
96181
96561
|
let raw;
|
|
96182
96562
|
try {
|
|
96183
96563
|
raw = readFile(path2);
|
|
@@ -96198,13 +96578,13 @@ function readFreshCrashLogTail(path2, opts = {}) {
|
|
|
96198
96578
|
}
|
|
96199
96579
|
function writeBridgeDeadEscalationMarker(path2, marker) {
|
|
96200
96580
|
const tmp = `${path2}.tmp-${process.pid}-${Date.now()}`;
|
|
96201
|
-
|
|
96581
|
+
writeFileSync49(tmp, JSON.stringify(marker), "utf8");
|
|
96202
96582
|
renameSync22(tmp, path2);
|
|
96203
96583
|
}
|
|
96204
96584
|
function consumeBridgeDeadEscalationMarker(path2, nowMs3 = Date.now(), maxAgeMs = ESCALATION_MARKER_MAX_AGE_MS) {
|
|
96205
96585
|
let marker = null;
|
|
96206
96586
|
try {
|
|
96207
|
-
const parsed = JSON.parse(
|
|
96587
|
+
const parsed = JSON.parse(readFileSync59(path2, "utf8"));
|
|
96208
96588
|
if (typeof parsed.ts === "number" && Number.isFinite(parsed.ts) && typeof parsed.reason === "string") {
|
|
96209
96589
|
const age = nowMs3 - parsed.ts;
|
|
96210
96590
|
if (age >= 0 && age < maxAgeMs) {
|
|
@@ -96357,15 +96737,16 @@ function createBridgeDeadWatchdog(opts) {
|
|
|
96357
96737
|
}
|
|
96358
96738
|
|
|
96359
96739
|
// gateway/boot-probes.ts
|
|
96360
|
-
import { readFileSync as
|
|
96740
|
+
import { readFileSync as readFileSync60, readdirSync as readdirSync12, existsSync as existsSync55 } from "fs";
|
|
96361
96741
|
init_quota_cache();
|
|
96742
|
+
init_generation_stamp();
|
|
96362
96743
|
init_quota_check();
|
|
96363
96744
|
import { execFile as execFileCb2 } from "child_process";
|
|
96364
96745
|
import { promisify as promisify2 } from "util";
|
|
96365
96746
|
var execFile2 = promisify2(execFileCb2);
|
|
96366
96747
|
var realProcFs2 = {
|
|
96367
96748
|
readdir: (p) => readdirSync12(p),
|
|
96368
|
-
readFile: (p) =>
|
|
96749
|
+
readFile: (p) => readFileSync60(p, "utf-8")
|
|
96369
96750
|
};
|
|
96370
96751
|
function findAgentProcessInContainer2(fs3 = realProcFs2) {
|
|
96371
96752
|
let entries;
|
|
@@ -96615,7 +96996,7 @@ if (isGatewayMain) {
|
|
|
96615
96996
|
shutdownAnalytics();
|
|
96616
96997
|
});
|
|
96617
96998
|
}
|
|
96618
|
-
var STATE_DIR = process.env.TELEGRAM_STATE_DIR ??
|
|
96999
|
+
var STATE_DIR = process.env.TELEGRAM_STATE_DIR ?? join63(homedir20(), ".claude", "channels", "telegram");
|
|
96619
97000
|
var permCardStore = createPermissionCardStore(STATE_DIR);
|
|
96620
97001
|
var BLOCKED_APPROVALS_DIR = process.env.SWITCHROOM_BLOCKED_APPROVALS_DIR ?? "/state/blocked-approvals";
|
|
96621
97002
|
var AGENT_NAME = process.env.SWITCHROOM_AGENT_NAME ?? "agent";
|
|
@@ -96654,7 +97035,7 @@ function alwaysAllowDrainDeps() {
|
|
|
96654
97035
|
return {
|
|
96655
97036
|
readConfigText: () => {
|
|
96656
97037
|
const cfgPath = process.env.SWITCHROOM_CONFIG ?? SWITCHROOM_CONFIG ?? findConfigFile2();
|
|
96657
|
-
return
|
|
97038
|
+
return readFileSync61(cfgPath, "utf8");
|
|
96658
97039
|
},
|
|
96659
97040
|
resolveAllowList: (_configText, agentName3) => {
|
|
96660
97041
|
const cfg = loadConfig2();
|
|
@@ -96715,11 +97096,11 @@ function scheduleAlwaysAllowPersistDrain() {
|
|
|
96715
97096
|
}, ALWAYS_ALLOW_DRAIN_INTERVAL_MS);
|
|
96716
97097
|
timer3.unref?.();
|
|
96717
97098
|
}
|
|
96718
|
-
var ACCESS_FILE =
|
|
96719
|
-
var APPROVED_DIR =
|
|
96720
|
-
var ENV_FILE =
|
|
96721
|
-
var INBOX_DIR =
|
|
96722
|
-
var PEOPLE_FILE =
|
|
97099
|
+
var ACCESS_FILE = join63(STATE_DIR, "access.json");
|
|
97100
|
+
var APPROVED_DIR = join63(STATE_DIR, "approved");
|
|
97101
|
+
var ENV_FILE = join63(STATE_DIR, ".env");
|
|
97102
|
+
var INBOX_DIR = join63(STATE_DIR, "inbox");
|
|
97103
|
+
var PEOPLE_FILE = join63(STATE_DIR, "people.json");
|
|
96723
97104
|
function triggerSelfRestart(targetAgent, reason, delayMs = 300) {
|
|
96724
97105
|
const isDocker = process.env.SWITCHROOM_RUNTIME === "docker";
|
|
96725
97106
|
const selfAgent = process.env.SWITCHROOM_AGENT_NAME;
|
|
@@ -96774,7 +97155,7 @@ function getClaudeCliVersion() {
|
|
|
96774
97155
|
}
|
|
96775
97156
|
function formatBootVersion() {
|
|
96776
97157
|
return composeBootVersionString({
|
|
96777
|
-
version:
|
|
97158
|
+
version: VERSION2,
|
|
96778
97159
|
commitSha: COMMIT_SHA,
|
|
96779
97160
|
commitDate: COMMIT_DATE,
|
|
96780
97161
|
latestPr: LATEST_PR,
|
|
@@ -96784,7 +97165,7 @@ function formatBootVersion() {
|
|
|
96784
97165
|
}
|
|
96785
97166
|
try {
|
|
96786
97167
|
chmodSync14(ENV_FILE, 384);
|
|
96787
|
-
for (const line of
|
|
97168
|
+
for (const line of readFileSync61(ENV_FILE, "utf8").split(`
|
|
96788
97169
|
`)) {
|
|
96789
97170
|
const m = line.match(/^(\w+)=(.*)$/);
|
|
96790
97171
|
if (m && process.env[m[1]] === undefined)
|
|
@@ -96805,7 +97186,7 @@ var bot;
|
|
|
96805
97186
|
var lastGetUpdatesHeartbeatMs = Date.now();
|
|
96806
97187
|
var GRAMMY_VERSION = (() => {
|
|
96807
97188
|
try {
|
|
96808
|
-
const raw =
|
|
97189
|
+
const raw = readFileSync61(new URL("../../node_modules/grammy/package.json", import.meta.url), "utf8");
|
|
96809
97190
|
return JSON.parse(raw).version ?? "unknown";
|
|
96810
97191
|
} catch {
|
|
96811
97192
|
return "unknown";
|
|
@@ -96868,7 +97249,7 @@ function assertSendable(f) {
|
|
|
96868
97249
|
} catch {
|
|
96869
97250
|
throw new Error(`refusing to send file \u2014 cannot resolve real path: ${f}`);
|
|
96870
97251
|
}
|
|
96871
|
-
const inbox =
|
|
97252
|
+
const inbox = join63(stateReal, "inbox");
|
|
96872
97253
|
if (real.startsWith(stateReal + sep4) && !real.startsWith(inbox + sep4)) {
|
|
96873
97254
|
throw new Error(`refusing to send channel state: ${f}`);
|
|
96874
97255
|
}
|
|
@@ -96887,7 +97268,7 @@ function assertSendable(f) {
|
|
|
96887
97268
|
}
|
|
96888
97269
|
function readAccessFile() {
|
|
96889
97270
|
try {
|
|
96890
|
-
const raw =
|
|
97271
|
+
const raw = readFileSync61(ACCESS_FILE, "utf8");
|
|
96891
97272
|
const parsed = JSON.parse(raw);
|
|
96892
97273
|
const allowFrom = validateStringArray("allowFrom", parsed.allowFrom ?? []);
|
|
96893
97274
|
const groups = {};
|
|
@@ -96949,7 +97330,7 @@ function loadAccess() {
|
|
|
96949
97330
|
}
|
|
96950
97331
|
function readPeopleFile() {
|
|
96951
97332
|
try {
|
|
96952
|
-
const raw =
|
|
97333
|
+
const raw = readFileSync61(PEOPLE_FILE, "utf8");
|
|
96953
97334
|
const parsed = JSON.parse(raw);
|
|
96954
97335
|
if (!Array.isArray(parsed.entries))
|
|
96955
97336
|
return [];
|
|
@@ -96973,7 +97354,7 @@ function saveAccess(a) {
|
|
|
96973
97354
|
return;
|
|
96974
97355
|
mkdirSync49(STATE_DIR, { recursive: true, mode: 448 });
|
|
96975
97356
|
const tmp = ACCESS_FILE + ".tmp";
|
|
96976
|
-
|
|
97357
|
+
writeFileSync51(tmp, JSON.stringify(a, null, 2) + `
|
|
96977
97358
|
`, { mode: 384 });
|
|
96978
97359
|
renameSync23(tmp, ACCESS_FILE);
|
|
96979
97360
|
}
|
|
@@ -96993,7 +97374,7 @@ var HISTORY_ENABLED = HISTORY_ACCESS.historyEnabled !== false;
|
|
|
96993
97374
|
if (isGatewayMain && HISTORY_ENABLED) {
|
|
96994
97375
|
try {
|
|
96995
97376
|
initHistory(STATE_DIR, HISTORY_ACCESS.historyRetentionDays ?? 30);
|
|
96996
|
-
process.stderr.write(`telegram gateway: history capture enabled at ${
|
|
97377
|
+
process.stderr.write(`telegram gateway: history capture enabled at ${join63(STATE_DIR, "history.db")}
|
|
96997
97378
|
`);
|
|
96998
97379
|
} catch (err) {
|
|
96999
97380
|
process.stderr.write(`telegram gateway: history init failed (${err.message}) \u2014 capture disabled
|
|
@@ -97012,12 +97393,12 @@ if (isGatewayMain)
|
|
|
97012
97393
|
let markerTurnKey = null;
|
|
97013
97394
|
let markerAgeMs = null;
|
|
97014
97395
|
try {
|
|
97015
|
-
const markerPath =
|
|
97016
|
-
if (
|
|
97396
|
+
const markerPath = join63(STATE_DIR, TURN_ACTIVE_MARKER_FILE2);
|
|
97397
|
+
if (existsSync57(markerPath)) {
|
|
97017
97398
|
const st = statSync20(markerPath);
|
|
97018
97399
|
markerAgeMs = Date.now() - st.mtimeMs;
|
|
97019
97400
|
try {
|
|
97020
|
-
const payload = JSON.parse(
|
|
97401
|
+
const payload = JSON.parse(readFileSync61(markerPath, "utf8"));
|
|
97021
97402
|
if (typeof payload.turnKey === "string" && payload.turnKey.length > 0) {
|
|
97022
97403
|
markerTurnKey = payload.turnKey;
|
|
97023
97404
|
}
|
|
@@ -97037,10 +97418,10 @@ if (isGatewayMain)
|
|
|
97037
97418
|
process.stderr.write(`telegram gateway: turn-registry boot-reaper stamped ${reaped} orphaned turn(s)` + `${timeoutTurnKey ? ` (turnKey=${timeoutTurnKey} as 'timeout', markerAgeMs=${markerAgeMs})` : " as 'restart'"}
|
|
97038
97419
|
`);
|
|
97039
97420
|
} else {
|
|
97040
|
-
process.stderr.write(`telegram gateway: turn-registry initialized at ${
|
|
97421
|
+
process.stderr.write(`telegram gateway: turn-registry initialized at ${join63(agentDir, "telegram", "registry.db")}
|
|
97041
97422
|
`);
|
|
97042
97423
|
}
|
|
97043
|
-
const bridgeDeadMarker = consumeBridgeDeadEscalationMarker(
|
|
97424
|
+
const bridgeDeadMarker = consumeBridgeDeadEscalationMarker(join63(STATE_DIR, "bridge-dead-escalation.json"));
|
|
97044
97425
|
if (bridgeDeadMarker != null) {
|
|
97045
97426
|
bridgeDeadPriorStreak = bridgeDeadMarker.count ?? 1;
|
|
97046
97427
|
process.stderr.write(`telegram gateway: boot: prior restart was a bridge-dead escalation (reason=${bridgeDeadMarker.reason}` + `, consecutive=${bridgeDeadPriorStreak}` + `${bridgeDeadMarker.crashTail ? `, crashTail=${bridgeDeadMarker.crashTail}` : ""})
|
|
@@ -97053,7 +97434,7 @@ if (isGatewayMain)
|
|
|
97053
97434
|
const pending2 = findLatestTurnIfInterrupted(turnsDb);
|
|
97054
97435
|
const selfAgent = process.env.SWITCHROOM_AGENT_NAME ?? "";
|
|
97055
97436
|
if (pending2 != null && selfAgent) {
|
|
97056
|
-
const bootResumeMarkerPath = process.env.SWITCHROOM_GATEWAY_CLEAN_SHUTDOWN_MARKER ??
|
|
97437
|
+
const bootResumeMarkerPath = process.env.SWITCHROOM_GATEWAY_CLEAN_SHUTDOWN_MARKER ?? join63(STATE_DIR, "clean-shutdown.json");
|
|
97057
97438
|
const bootResumeCleanMarker = readCleanShutdownMarker(bootResumeMarkerPath);
|
|
97058
97439
|
const bootResumeForceAlways = process.env.SWITCHROOM_BOOT_RESUME_ALWAYS === "1";
|
|
97059
97440
|
const bootResumeMode = parseBootResumeMode(process.env.SWITCHROOM_BOOT_RESUME);
|
|
@@ -97166,7 +97547,7 @@ if (isGatewayMain)
|
|
|
97166
97547
|
`);
|
|
97167
97548
|
}
|
|
97168
97549
|
}
|
|
97169
|
-
const pendingEnvPath =
|
|
97550
|
+
const pendingEnvPath = join63(agentDir, ".pending-turn.env");
|
|
97170
97551
|
try {
|
|
97171
97552
|
if (pending2 != null) {
|
|
97172
97553
|
const lines = [
|
|
@@ -97180,13 +97561,13 @@ if (isGatewayMain)
|
|
|
97180
97561
|
pending2.interrupt_reason != null ? `SWITCHROOM_PENDING_INTERRUPT_REASON=${pending2.interrupt_reason}` : `SWITCHROOM_PENDING_INTERRUPT_REASON=`
|
|
97181
97562
|
];
|
|
97182
97563
|
const pendingEnvTmp = `${pendingEnvPath}.tmp-${process.pid}`;
|
|
97183
|
-
|
|
97564
|
+
writeFileSync51(pendingEnvTmp, lines.join(`
|
|
97184
97565
|
`) + `
|
|
97185
97566
|
`, { mode: 384 });
|
|
97186
97567
|
renameSync23(pendingEnvTmp, pendingEnvPath);
|
|
97187
97568
|
process.stderr.write(`telegram gateway: pending-turn env written to ${pendingEnvPath} turnKey=${pending2.turn_key} endedVia=${pending2.ended_via ?? "open"}
|
|
97188
97569
|
`);
|
|
97189
|
-
} else if (
|
|
97570
|
+
} else if (existsSync57(pendingEnvPath)) {
|
|
97190
97571
|
rmSync6(pendingEnvPath, { force: true });
|
|
97191
97572
|
process.stderr.write(`telegram gateway: pending-turn env cleared (clean previous shutdown)
|
|
97192
97573
|
`);
|
|
@@ -97294,7 +97675,7 @@ function checkApprovals() {
|
|
|
97294
97675
|
return;
|
|
97295
97676
|
}
|
|
97296
97677
|
for (const senderId of files) {
|
|
97297
|
-
const file =
|
|
97678
|
+
const file = join63(APPROVED_DIR, senderId);
|
|
97298
97679
|
bot.api.sendMessage(senderId, "Paired! Say hi to Claude.").then(() => rmSync6(file, { force: true }), (err) => {
|
|
97299
97680
|
process.stderr.write(`telegram gateway: failed to send approval confirm: ${err}
|
|
97300
97681
|
`);
|
|
@@ -97467,12 +97848,12 @@ function noteAgentOutputAt(key, ts) {
|
|
|
97467
97848
|
lastAgentOutputAt.delete(oldest);
|
|
97468
97849
|
}
|
|
97469
97850
|
}
|
|
97470
|
-
var OBLIGATION_STORE_PATH =
|
|
97851
|
+
var OBLIGATION_STORE_PATH = join63(STATE_DIR, "obligations.json");
|
|
97471
97852
|
var obligationStoreFs = {
|
|
97472
|
-
readFileSync: (p) =>
|
|
97473
|
-
writeFileSync: (p, d) =>
|
|
97853
|
+
readFileSync: (p) => readFileSync61(p, "utf8"),
|
|
97854
|
+
writeFileSync: (p, d) => writeFileSync51(p, d),
|
|
97474
97855
|
renameSync: (a, b) => renameSync23(a, b),
|
|
97475
|
-
existsSync: (p) =>
|
|
97856
|
+
existsSync: (p) => existsSync57(p)
|
|
97476
97857
|
};
|
|
97477
97858
|
var obligationLedger = new ObligationLedger(OBLIGATION_REPRESENT_MAX, {
|
|
97478
97859
|
onChange: STATIC || !OBLIGATION_LEDGER_ENABLED ? undefined : (snapshot) => persistObligations(OBLIGATION_STORE_PATH, obligationStoreFs, snapshot)
|
|
@@ -98879,7 +99260,7 @@ function sweepStaleAlwaysAllowCorrelations(now = Date.now()) {
|
|
|
98879
99260
|
var MENTAL_MODEL_CORRELATION_TTL_MS = 720000;
|
|
98880
99261
|
var pendingMentalModelCorrelations = createSweepableStore((entry, now) => now - entry.createdAt > MENTAL_MODEL_CORRELATION_TTL_MS);
|
|
98881
99262
|
function mentalModelCorrelationKey(agentName3, unifiedDiff) {
|
|
98882
|
-
return `${agentName3}::${
|
|
99263
|
+
return `${agentName3}::${createHash7("sha256").update(unifiedDiff).digest("hex")}`;
|
|
98883
99264
|
}
|
|
98884
99265
|
function sweepStaleMentalModelCorrelations(now = Date.now()) {
|
|
98885
99266
|
pendingMentalModelCorrelations.sweep(now);
|
|
@@ -99815,28 +100196,28 @@ var statusPinState = new Map;
|
|
|
99815
100196
|
var statusPinChatIds = new Map;
|
|
99816
100197
|
var statusPinPinnedAt = new Map;
|
|
99817
100198
|
var statusPinRightsCache = new PinRightsCache2;
|
|
99818
|
-
var STATUS_PIN_STORE_PATH =
|
|
100199
|
+
var STATUS_PIN_STORE_PATH = join63(STATE_DIR, "status-pins.json");
|
|
99819
100200
|
var statusPinStoreFs = {
|
|
99820
|
-
readFileSync: (p) =>
|
|
99821
|
-
writeFileSync: (p, d) =>
|
|
100201
|
+
readFileSync: (p) => readFileSync61(p, "utf8"),
|
|
100202
|
+
writeFileSync: (p, d) => writeFileSync51(p, d),
|
|
99822
100203
|
renameSync: (a, b) => renameSync23(a, b),
|
|
99823
|
-
existsSync: (p) =>
|
|
100204
|
+
existsSync: (p) => existsSync57(p)
|
|
99824
100205
|
};
|
|
99825
100206
|
var statusPinPersistEnabled = !STATIC && PIN_STATUS_WHILE_WORKING;
|
|
99826
|
-
var ACTIVITY_CARD_STORE_PATH =
|
|
100207
|
+
var ACTIVITY_CARD_STORE_PATH = join63(STATE_DIR, "activity-cards-pending.json");
|
|
99827
100208
|
var activityCardStoreFs = {
|
|
99828
|
-
readFileSync: (p) =>
|
|
99829
|
-
writeFileSync: (p, d) =>
|
|
100209
|
+
readFileSync: (p) => readFileSync61(p, "utf8"),
|
|
100210
|
+
writeFileSync: (p, d) => writeFileSync51(p, d),
|
|
99830
100211
|
renameSync: (a, b) => renameSync23(a, b),
|
|
99831
|
-
existsSync: (p) =>
|
|
100212
|
+
existsSync: (p) => existsSync57(p)
|
|
99832
100213
|
};
|
|
99833
100214
|
var activityCardPersistEnabled = !STATIC;
|
|
99834
|
-
var QUEUED_CARD_STORE_PATH =
|
|
100215
|
+
var QUEUED_CARD_STORE_PATH = join63(STATE_DIR, "queued-cards-pending.json");
|
|
99835
100216
|
var queuedCardStoreFs = {
|
|
99836
|
-
readFileSync: (p) =>
|
|
99837
|
-
writeFileSync: (p, d) =>
|
|
100217
|
+
readFileSync: (p) => readFileSync61(p, "utf8"),
|
|
100218
|
+
writeFileSync: (p, d) => writeFileSync51(p, d),
|
|
99838
100219
|
renameSync: (a, b) => renameSync23(a, b),
|
|
99839
|
-
existsSync: (p) =>
|
|
100220
|
+
existsSync: (p) => existsSync57(p)
|
|
99840
100221
|
};
|
|
99841
100222
|
var queuedCardPersistEnabled = !STATIC;
|
|
99842
100223
|
function persistQueuedCard(key, chatId, threadId, messageId) {
|
|
@@ -99961,7 +100342,7 @@ async function runMidSessionCardReaper() {
|
|
|
99961
100342
|
now
|
|
99962
100343
|
});
|
|
99963
100344
|
if (reaped > 0) {
|
|
99964
|
-
process.stderr.write(`telegram gateway: mid-session reaper stamped ${reaped} orphaned turn(s) as '
|
|
100345
|
+
process.stderr.write(`telegram gateway: mid-session reaper stamped ${reaped} orphaned turn(s) as 'reaped_stale' (${reapedTurnKeys.join(",")})
|
|
99965
100346
|
`);
|
|
99966
100347
|
}
|
|
99967
100348
|
} catch (err) {
|
|
@@ -100209,12 +100590,12 @@ var getPinnedProgressCardMessageId = null;
|
|
|
100209
100590
|
var completeProgressCardTurn = null;
|
|
100210
100591
|
var subagentWatcher = null;
|
|
100211
100592
|
var workerActivityFeed = null;
|
|
100212
|
-
var SOCKET_PATH = process.env.SWITCHROOM_GATEWAY_SOCKET ??
|
|
100593
|
+
var SOCKET_PATH = process.env.SWITCHROOM_GATEWAY_SOCKET ?? join63(STATE_DIR, "gateway.sock");
|
|
100213
100594
|
if (isGatewayMain)
|
|
100214
100595
|
mkdirSync49(STATE_DIR, { recursive: true, mode: 448 });
|
|
100215
|
-
var GATEWAY_PID_PATH = process.env.SWITCHROOM_GATEWAY_PID_FILE ??
|
|
100216
|
-
var GATEWAY_SESSION_MARKER_PATH = process.env.SWITCHROOM_GATEWAY_SESSION_MARKER ??
|
|
100217
|
-
var GATEWAY_CLEAN_SHUTDOWN_MARKER_PATH = process.env.SWITCHROOM_GATEWAY_CLEAN_SHUTDOWN_MARKER ??
|
|
100596
|
+
var GATEWAY_PID_PATH = process.env.SWITCHROOM_GATEWAY_PID_FILE ?? join63(STATE_DIR, "gateway.pid.json");
|
|
100597
|
+
var GATEWAY_SESSION_MARKER_PATH = process.env.SWITCHROOM_GATEWAY_SESSION_MARKER ?? join63(STATE_DIR, "gateway-session.json");
|
|
100598
|
+
var GATEWAY_CLEAN_SHUTDOWN_MARKER_PATH = process.env.SWITCHROOM_GATEWAY_CLEAN_SHUTDOWN_MARKER ?? join63(STATE_DIR, "clean-shutdown.json");
|
|
100218
100599
|
var GATEWAY_STARTED_AT_MS = Date.now();
|
|
100219
100600
|
var BOOT_CARD_ENABLED = process.env.SWITCHROOM_BOOT_CARD !== "false";
|
|
100220
100601
|
var activeBootCard = null;
|
|
@@ -100243,7 +100624,7 @@ function ensureIssuesCard(chatId, threadId) {
|
|
|
100243
100624
|
bot: botApi,
|
|
100244
100625
|
log: (msg) => process.stderr.write(`telegram gateway: ${msg}
|
|
100245
100626
|
`),
|
|
100246
|
-
persistPath:
|
|
100627
|
+
persistPath: join63(stateDir, "issues-card.json")
|
|
100247
100628
|
});
|
|
100248
100629
|
activeIssuesWatcher = startIssuesWatcher({
|
|
100249
100630
|
stateDir,
|
|
@@ -100335,10 +100716,12 @@ function gatewayLivenessWiringDeps() {
|
|
|
100335
100716
|
SILENCE_FALLBACK_HARD_MS,
|
|
100336
100717
|
SILENCE_FLOOR_MS,
|
|
100337
100718
|
SILENCE_DEFER_INFLIGHT_TOOLS,
|
|
100719
|
+
isObligationOpenForTurn: (originTurnId) => OBLIGATION_LEDGER_ENABLED && originTurnId != null && obligationLedger.isOpen(originTurnId),
|
|
100338
100720
|
TURN_PREVIEW_MAX,
|
|
100339
100721
|
STATE_DIR,
|
|
100340
100722
|
isLegitimatelyWorking,
|
|
100341
100723
|
getCurrentTurn: () => currentTurn,
|
|
100724
|
+
getCurrentTurnForKey: (key) => currentTurnMap.get(key),
|
|
100342
100725
|
getInFlightUpdate: () => inFlightUpdate,
|
|
100343
100726
|
getTurnsDb: () => turnsDb,
|
|
100344
100727
|
getInboundSpool: () => inboundSpool,
|
|
@@ -100441,13 +100824,13 @@ if (isGatewayMain)
|
|
|
100441
100824
|
var inboundSpool;
|
|
100442
100825
|
if (isGatewayMain)
|
|
100443
100826
|
inboundSpool = STATIC ? undefined : createInboundSpool({
|
|
100444
|
-
path:
|
|
100827
|
+
path: join63(STATE_DIR, "inbound-spool.jsonl"),
|
|
100445
100828
|
fs: {
|
|
100446
100829
|
appendFileSync: (p, d) => appendFileSync9(p, d),
|
|
100447
|
-
readFileSync: (p) =>
|
|
100448
|
-
writeFileSync: (p, d) =>
|
|
100830
|
+
readFileSync: (p) => readFileSync61(p, "utf8"),
|
|
100831
|
+
writeFileSync: (p, d) => writeFileSync51(p, d),
|
|
100449
100832
|
renameSync: (a, b) => renameSync23(a, b),
|
|
100450
|
-
existsSync: (p) =>
|
|
100833
|
+
existsSync: (p) => existsSync57(p),
|
|
100451
100834
|
statSizeSync: (p) => statSync20(p).size
|
|
100452
100835
|
},
|
|
100453
100836
|
onDegraded: (info) => {
|
|
@@ -100509,13 +100892,13 @@ async function maybeRedeliverUndeliveredAnswer() {
|
|
|
100509
100892
|
let transcriptText;
|
|
100510
100893
|
try {
|
|
100511
100894
|
const projectsDir = getProjectsDirForCwd();
|
|
100512
|
-
const path2 =
|
|
100513
|
-
if (!
|
|
100895
|
+
const path2 = join63(projectsDir, `${sessionId}.jsonl`);
|
|
100896
|
+
if (!existsSync57(path2)) {
|
|
100514
100897
|
process.stderr.write(`telegram gateway: crash-redelivery \u2014 transcript not found for turnKey=${turn.turn_key} session=${sessionId} (${path2}); skipping
|
|
100515
100898
|
`);
|
|
100516
100899
|
return;
|
|
100517
100900
|
}
|
|
100518
|
-
transcriptText =
|
|
100901
|
+
transcriptText = readFileSync61(path2, "utf8");
|
|
100519
100902
|
} catch (err) {
|
|
100520
100903
|
process.stderr.write(`telegram gateway: crash-redelivery \u2014 transcript read failed turnKey=${turn.turn_key}: ${err.message}
|
|
100521
100904
|
`);
|
|
@@ -100674,8 +101057,8 @@ var bridgeDeadWatchdog = createBridgeDeadWatchdog({
|
|
|
100674
101057
|
isSessionAlive: () => findAgentProcessInContainer2()?.comm === "claude",
|
|
100675
101058
|
isShuttingDown: () => shuttingDown,
|
|
100676
101059
|
escalate: (reason) => triggerSelfRestart(process.env.SWITCHROOM_AGENT_NAME ?? "", reason, 1500),
|
|
100677
|
-
crashLogPath:
|
|
100678
|
-
markerPath:
|
|
101060
|
+
crashLogPath: join63(STATE_DIR, "bridge-crash.log"),
|
|
101061
|
+
markerPath: join63(STATE_DIR, "bridge-dead-escalation.json"),
|
|
100679
101062
|
log: (line) => process.stderr.write(`${line}
|
|
100680
101063
|
`),
|
|
100681
101064
|
priorStreak: bridgeDeadPriorStreak,
|
|
@@ -100781,8 +101164,8 @@ if (isGatewayMain)
|
|
|
100781
101164
|
probeQuotaViaBroker: (t) => probeQuotaForBootCard(agentSlug, t),
|
|
100782
101165
|
tmuxSupervisor: process.env.SWITCHROOM_TMUX_SUPERVISOR === "1",
|
|
100783
101166
|
dockerMode: process.env.SWITCHROOM_RUNTIME === "docker",
|
|
100784
|
-
configSnapshotPath:
|
|
100785
|
-
bootCardStatePath:
|
|
101167
|
+
configSnapshotPath: join63(resolvedAgentDirForCard, ".config-snapshot.json"),
|
|
101168
|
+
bootCardStatePath: join63(resolvedAgentDirForCard, ".boot-card-msgid.json"),
|
|
100786
101169
|
floodStatePath: FLOOD_STATE_PATH,
|
|
100787
101170
|
...updateOutcomeLine ? { updateOutcomeLine } : {}
|
|
100788
101171
|
}, ackMsgId).then((handle) => {
|
|
@@ -100834,6 +101217,7 @@ if (isGatewayMain)
|
|
|
100834
101217
|
turnTypingLoop.stopAll();
|
|
100835
101218
|
typingEmitter.reset();
|
|
100836
101219
|
},
|
|
101220
|
+
onTurnKeyEnded: (key) => endTurn(key),
|
|
100837
101221
|
onDanglingTurnsSwept: () => {
|
|
100838
101222
|
if (currentTurn != null) {
|
|
100839
101223
|
process.stderr.write(`telegram gateway: disconnect-flush nulled currentTurn (bridge died with turn in flight)
|
|
@@ -101449,7 +101833,7 @@ if (isGatewayMain)
|
|
|
101449
101833
|
const receiverUid = receiverUidRaw ? Number(receiverUidRaw) : NaN;
|
|
101450
101834
|
if (Number.isInteger(receiverUid))
|
|
101451
101835
|
allowedUids.push(receiverUid);
|
|
101452
|
-
const socketPath =
|
|
101836
|
+
const socketPath = join63(STATE_DIR, "webhook.sock");
|
|
101453
101837
|
const webhookInject = (agentName3, inbound) => {
|
|
101454
101838
|
const msg = inbound;
|
|
101455
101839
|
const delivered = ipcServer.sendToAgent(agentName3, msg);
|
|
@@ -101677,9 +102061,9 @@ function redactOutboundText(text5, site) {
|
|
|
101677
102061
|
var VOICE_OUT_DEFAULT_CHUNK_CHARS = 600;
|
|
101678
102062
|
var VOICE_OUT_HARD_CHUNK_CAP = 4096;
|
|
101679
102063
|
var voiceOnDemandCache = new VoiceOnDemandCache({
|
|
101680
|
-
persistPath:
|
|
102064
|
+
persistPath: join63(STATE_DIR, "voice-ondemand.json")
|
|
101681
102065
|
});
|
|
101682
|
-
var VOICE_CACHE_DIR =
|
|
102066
|
+
var VOICE_CACHE_DIR = join63(STATE_DIR, "voice-cache");
|
|
101683
102067
|
var voicePreSynthQueue = new PreSynthQueue({
|
|
101684
102068
|
runJob: async (job) => {
|
|
101685
102069
|
const sidecarToken = await materializeSidecarToken2();
|
|
@@ -102081,11 +102465,11 @@ async function executeSendGif(rawArgs) {
|
|
|
102081
102465
|
};
|
|
102082
102466
|
}
|
|
102083
102467
|
async function publishToTelegraph(text5, shortName, authorName) {
|
|
102084
|
-
const accountPath =
|
|
102468
|
+
const accountPath = join63(STATE_DIR, "telegraph-account.json");
|
|
102085
102469
|
let account = null;
|
|
102086
102470
|
try {
|
|
102087
|
-
if (
|
|
102088
|
-
const raw =
|
|
102471
|
+
if (existsSync57(accountPath)) {
|
|
102472
|
+
const raw = readFileSync61(accountPath, "utf-8");
|
|
102089
102473
|
const parsed = JSON.parse(raw);
|
|
102090
102474
|
if (parsed.shortName && parsed.accessToken) {
|
|
102091
102475
|
account = parsed;
|
|
@@ -102105,7 +102489,7 @@ async function publishToTelegraph(text5, shortName, authorName) {
|
|
|
102105
102489
|
account = created.value;
|
|
102106
102490
|
try {
|
|
102107
102491
|
mkdirSync49(STATE_DIR, { recursive: true, mode: 448 });
|
|
102108
|
-
|
|
102492
|
+
writeFileSync51(accountPath, JSON.stringify(account, null, 2), { mode: 384 });
|
|
102109
102493
|
} catch (err) {
|
|
102110
102494
|
process.stderr.write(`telegram gateway: telegraph cache write failed: ${err.message}
|
|
102111
102495
|
`);
|
|
@@ -102221,7 +102605,7 @@ _The secret was NOT saved. The agent can re-request with \`request_secret\`._`,
|
|
|
102221
102605
|
}
|
|
102222
102606
|
function readLiveSwitchroomConfigText() {
|
|
102223
102607
|
const cfgPath = process.env.SWITCHROOM_CONFIG ?? findConfigFile2();
|
|
102224
|
-
return
|
|
102608
|
+
return readFileSync61(cfgPath, "utf8");
|
|
102225
102609
|
}
|
|
102226
102610
|
async function executeReact(args) {
|
|
102227
102611
|
if (!args.chat_id)
|
|
@@ -102262,7 +102646,7 @@ async function executeDownloadAttachment(args) {
|
|
|
102262
102646
|
});
|
|
102263
102647
|
mkdirSync49(INBOX_DIR, { recursive: true, mode: 448 });
|
|
102264
102648
|
assertInsideInbox2(INBOX_DIR, dlPath);
|
|
102265
|
-
|
|
102649
|
+
writeFileSync51(dlPath, buf, { mode: 384 });
|
|
102266
102650
|
return { content: [{ type: "text", text: dlPath }] };
|
|
102267
102651
|
}
|
|
102268
102652
|
async function executeEditMessage(args) {
|
|
@@ -102645,6 +103029,12 @@ var handbackPreturnSignal = createHandbackPreturnSignal({
|
|
|
102645
103029
|
isTurnSettled: (key) => {
|
|
102646
103030
|
const live = currentTurnMap.get(key);
|
|
102647
103031
|
return live != null && (live.finalAnswerDelivered || live.endedAt != null);
|
|
103032
|
+
},
|
|
103033
|
+
hasLiveTurn: (key) => {
|
|
103034
|
+
const live = currentTurnMap.get(key);
|
|
103035
|
+
if (live == null || live.endedAt != null)
|
|
103036
|
+
return false;
|
|
103037
|
+
return statusKey(live.sessionChatId, live.sessionThreadId) === key;
|
|
102648
103038
|
}
|
|
102649
103039
|
});
|
|
102650
103040
|
var memoryLegibilityStager = new MemoryLegibilityStager;
|
|
@@ -103622,14 +104012,14 @@ function restartMarkerPath() {
|
|
|
103622
104012
|
const agentDir = resolveAgentDirFromEnv();
|
|
103623
104013
|
if (!agentDir)
|
|
103624
104014
|
return null;
|
|
103625
|
-
return
|
|
104015
|
+
return join63(agentDir, "restart-pending.json");
|
|
103626
104016
|
}
|
|
103627
104017
|
function writeRestartMarker(marker) {
|
|
103628
104018
|
const p = restartMarkerPath();
|
|
103629
104019
|
if (!p)
|
|
103630
104020
|
return;
|
|
103631
104021
|
try {
|
|
103632
|
-
|
|
104022
|
+
writeFileSync51(p, JSON.stringify(marker));
|
|
103633
104023
|
lastPlannedRestartAt = Date.now();
|
|
103634
104024
|
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}
|
|
103635
104025
|
`);
|
|
@@ -103648,7 +104038,7 @@ function readRestartMarker() {
|
|
|
103648
104038
|
if (!p)
|
|
103649
104039
|
return null;
|
|
103650
104040
|
try {
|
|
103651
|
-
return JSON.parse(
|
|
104041
|
+
return JSON.parse(readFileSync61(p, "utf8"));
|
|
103652
104042
|
} catch {
|
|
103653
104043
|
return null;
|
|
103654
104044
|
}
|
|
@@ -103797,7 +104187,7 @@ var _dockerReachable;
|
|
|
103797
104187
|
function isDockerReachable() {
|
|
103798
104188
|
if (_dockerReachable !== undefined)
|
|
103799
104189
|
return _dockerReachable;
|
|
103800
|
-
if (!
|
|
104190
|
+
if (!existsSync57("/var/run/docker.sock")) {
|
|
103801
104191
|
_dockerReachable = false;
|
|
103802
104192
|
return _dockerReachable;
|
|
103803
104193
|
}
|
|
@@ -103814,12 +104204,12 @@ function _resetDockerReachableCache() {
|
|
|
103814
104204
|
}
|
|
103815
104205
|
function spawnSwitchroomDetached(args, onFailure) {
|
|
103816
104206
|
const fullArgs = SWITCHROOM_CONFIG ? ["--config", SWITCHROOM_CONFIG, ...args] : args;
|
|
103817
|
-
const logPath =
|
|
104207
|
+
const logPath = join63(STATE_DIR, "detached-spawn.log");
|
|
103818
104208
|
let outFd = null;
|
|
103819
104209
|
try {
|
|
103820
104210
|
mkdirSync49(STATE_DIR, { recursive: true });
|
|
103821
104211
|
outFd = openSync12(logPath, "a");
|
|
103822
|
-
|
|
104212
|
+
writeFileSync51(logPath, `
|
|
103823
104213
|
[${new Date().toISOString()}] spawn ${SWITCHROOM_CLI} ${fullArgs.join(" ")}
|
|
103824
104214
|
`, { flag: "a" });
|
|
103825
104215
|
} catch {}
|
|
@@ -103845,7 +104235,7 @@ function spawnSwitchroomDetached(args, onFailure) {
|
|
|
103845
104235
|
return;
|
|
103846
104236
|
let tail = "";
|
|
103847
104237
|
try {
|
|
103848
|
-
const full =
|
|
104238
|
+
const full = readFileSync61(logPath, "utf8");
|
|
103849
104239
|
tail = full.split(`
|
|
103850
104240
|
`).slice(-30).join(`
|
|
103851
104241
|
`).trim();
|
|
@@ -104107,10 +104497,10 @@ ${preBlock(formatSwitchroomOutput(detail))}`, { html: true });
|
|
|
104107
104497
|
}
|
|
104108
104498
|
function readRecentDenialsForAgent(agentName3, windowMs, limit) {
|
|
104109
104499
|
try {
|
|
104110
|
-
const auditPath =
|
|
104111
|
-
if (!
|
|
104500
|
+
const auditPath = join63(homedir20(), ".switchroom", "vault-audit.log");
|
|
104501
|
+
if (!existsSync57(auditPath))
|
|
104112
104502
|
return [];
|
|
104113
|
-
const raw =
|
|
104503
|
+
const raw = readFileSync61(auditPath, "utf8");
|
|
104114
104504
|
return recentDenialsFromAuditLog(raw, { agentName: agentName3, windowMs, limit });
|
|
104115
104505
|
} catch {
|
|
104116
104506
|
return [];
|
|
@@ -104161,7 +104551,7 @@ async function buildAgentMetadata(agentName3) {
|
|
|
104161
104551
|
try {
|
|
104162
104552
|
const agentDir = resolveAgentDirFromEnv();
|
|
104163
104553
|
if (agentDir) {
|
|
104164
|
-
const raw =
|
|
104554
|
+
const raw = readFileSync61(join63(agentDir, ".claude", ".claude.json"), "utf8");
|
|
104165
104555
|
claudeJson = JSON.parse(raw);
|
|
104166
104556
|
}
|
|
104167
104557
|
} catch {}
|
|
@@ -104290,7 +104680,7 @@ function buildModelDeps(restartCtx) {
|
|
|
104290
104680
|
try {
|
|
104291
104681
|
const agentDir = resolveAgentDirFromEnv();
|
|
104292
104682
|
if (agentDir) {
|
|
104293
|
-
const local = await fetchQuota2({ claudeConfigDir:
|
|
104683
|
+
const local = await fetchQuota2({ claudeConfigDir: join63(agentDir, ".claude") });
|
|
104294
104684
|
if (local.ok)
|
|
104295
104685
|
return formatQuotaLine2(local.data);
|
|
104296
104686
|
}
|
|
@@ -104536,9 +104926,9 @@ function effortMenuReplyMarkup(reply) {
|
|
|
104536
104926
|
function flushAgentHandoff(agentDir) {
|
|
104537
104927
|
let removed = 0;
|
|
104538
104928
|
for (const fname of [".handoff.md", ".handoff-topic"]) {
|
|
104539
|
-
const p =
|
|
104929
|
+
const p = join63(agentDir, fname);
|
|
104540
104930
|
try {
|
|
104541
|
-
if (
|
|
104931
|
+
if (existsSync57(p)) {
|
|
104542
104932
|
unlinkSync27(p);
|
|
104543
104933
|
removed++;
|
|
104544
104934
|
}
|
|
@@ -104594,7 +104984,7 @@ async function handleNewCommand(ctx) {
|
|
|
104594
104984
|
writeRestartMarker({ chat_id: chatId, thread_id: threadId ?? null, ack_message_id: ackId, ts: Date.now() });
|
|
104595
104985
|
if (agentDir != null) {
|
|
104596
104986
|
try {
|
|
104597
|
-
|
|
104987
|
+
writeFileSync51(join63(agentDir, ".force-fresh-session"), `${kind} at ${new Date().toISOString()}
|
|
104598
104988
|
`, "utf8");
|
|
104599
104989
|
} catch (err) {
|
|
104600
104990
|
process.stderr.write(`telegram gateway: failed to write force-fresh marker: ${err}
|
|
@@ -104707,16 +105097,16 @@ function buildFolderPickerDeps() {
|
|
|
104707
105097
|
};
|
|
104708
105098
|
}
|
|
104709
105099
|
var lockoutOps = {
|
|
104710
|
-
readFileSync: (p, enc) =>
|
|
104711
|
-
writeFileSync: (p, data, opts) =>
|
|
104712
|
-
existsSync: (p) =>
|
|
105100
|
+
readFileSync: (p, enc) => readFileSync61(p, enc),
|
|
105101
|
+
writeFileSync: (p, data, opts) => writeFileSync51(p, data, opts),
|
|
105102
|
+
existsSync: (p) => existsSync57(p),
|
|
104713
105103
|
mkdirSync: (p, opts) => mkdirSync49(p, opts),
|
|
104714
|
-
joinPath: (...parts) =>
|
|
105104
|
+
joinPath: (...parts) => join63(...parts)
|
|
104715
105105
|
};
|
|
104716
105106
|
var FLEET_FALLBACK_DEDUP_MS = 30000;
|
|
104717
105107
|
function isAuthBrokerSocketReachable() {
|
|
104718
105108
|
try {
|
|
104719
|
-
return
|
|
105109
|
+
return existsSync57(resolveAuthBrokerSocketPath2());
|
|
104720
105110
|
} catch {
|
|
104721
105111
|
return false;
|
|
104722
105112
|
}
|
|
@@ -104971,7 +105361,7 @@ async function runCreditWatch() {
|
|
|
104971
105361
|
if (!agentDir)
|
|
104972
105362
|
return;
|
|
104973
105363
|
const agentName3 = getMyAgentName();
|
|
104974
|
-
const claudeConfigDir =
|
|
105364
|
+
const claudeConfigDir = join63(agentDir, ".claude");
|
|
104975
105365
|
const stateDir = STATE_DIR;
|
|
104976
105366
|
const reason = readClaudeJsonOverage(claudeConfigDir);
|
|
104977
105367
|
const prev = loadCreditState(stateDir);
|
|
@@ -106727,7 +107117,7 @@ Send \`/auth ${parsed.provider} cancel\` to abort.`, { html: true });
|
|
|
106727
107117
|
await switchroomReply(ctx, "**/usage:** cannot resolve agent dir.", { html: true });
|
|
106728
107118
|
return;
|
|
106729
107119
|
}
|
|
106730
|
-
const result = await fetchQuota2({ claudeConfigDir:
|
|
107120
|
+
const result = await fetchQuota2({ claudeConfigDir: join63(agentDir, ".claude") });
|
|
106731
107121
|
if (!result.ok) {
|
|
106732
107122
|
await switchroomReply(ctx, `**/usage:** ${escapeHtmlForTg2(result.reason)}`, { html: true });
|
|
106733
107123
|
return;
|
|
@@ -107113,7 +107503,7 @@ ${interimLabel}` : interimLabel
|
|
|
107113
107503
|
const unifiedDiff = (() => {
|
|
107114
107504
|
try {
|
|
107115
107505
|
const cfgPath = process.env.SWITCHROOM_CONFIG ?? SWITCHROOM_CONFIG ?? findConfigFile2();
|
|
107116
|
-
const raw =
|
|
107506
|
+
const raw = readFileSync61(cfgPath, "utf8");
|
|
107117
107507
|
return synthesizeAllowRuleDiff({ agentName: agentName3, rule: chosen.rule, configText: raw });
|
|
107118
107508
|
} catch (err) {
|
|
107119
107509
|
process.stderr.write(`telegram gateway: always-allow diff synth failed: ${err.message}
|
|
@@ -107970,7 +108360,7 @@ async function startGateway() {
|
|
|
107970
108360
|
return;
|
|
107971
108361
|
}
|
|
107972
108362
|
})();
|
|
107973
|
-
const resolvedAgentDirForBootCard = agentDir ??
|
|
108363
|
+
const resolvedAgentDirForBootCard = agentDir ?? join63(homedir20(), ".switchroom", "agents", agentSlug);
|
|
107974
108364
|
const handle = await startBootCard(chatId, threadId, botApiForCard, {
|
|
107975
108365
|
agentName: agentDisplayName,
|
|
107976
108366
|
agentSlug,
|
|
@@ -107984,8 +108374,8 @@ async function startGateway() {
|
|
|
107984
108374
|
probeQuotaViaBroker: (t) => probeQuotaForBootCard(agentSlug, t),
|
|
107985
108375
|
tmuxSupervisor: process.env.SWITCHROOM_TMUX_SUPERVISOR === "1",
|
|
107986
108376
|
dockerMode: process.env.SWITCHROOM_RUNTIME === "docker",
|
|
107987
|
-
configSnapshotPath:
|
|
107988
|
-
bootCardStatePath:
|
|
108377
|
+
configSnapshotPath: join63(resolvedAgentDirForBootCard, ".config-snapshot.json"),
|
|
108378
|
+
bootCardStatePath: join63(resolvedAgentDirForBootCard, ".boot-card-msgid.json"),
|
|
107989
108379
|
floodStatePath: FLOOD_STATE_PATH,
|
|
107990
108380
|
...updateOutcomeLine ? { updateOutcomeLine } : {}
|
|
107991
108381
|
}, ackMsgId);
|
|
@@ -108016,10 +108406,10 @@ async function startGateway() {
|
|
|
108016
108406
|
try {
|
|
108017
108407
|
const smAgentDir = resolveAgentDirFromEnv();
|
|
108018
108408
|
if (smAgentDir) {
|
|
108019
|
-
const activePath =
|
|
108020
|
-
if (
|
|
108409
|
+
const activePath = join63(smAgentDir, ".active-session-model");
|
|
108410
|
+
if (existsSync57(activePath)) {
|
|
108021
108411
|
try {
|
|
108022
|
-
const launched =
|
|
108412
|
+
const launched = readFileSync61(activePath, "utf8").trim();
|
|
108023
108413
|
const configured = (() => {
|
|
108024
108414
|
const d = switchroomExecJson(["agent", "list"]);
|
|
108025
108415
|
const raw = d?.agents?.find((a) => a.name === getMyAgentName())?.model ?? null;
|
|
@@ -108052,24 +108442,24 @@ async function startGateway() {
|
|
|
108052
108442
|
deliverModelSwitchBootNotice({
|
|
108053
108443
|
...modelBootCardDeps,
|
|
108054
108444
|
confirmation,
|
|
108055
|
-
hasSessionModelAlert:
|
|
108445
|
+
hasSessionModelAlert: existsSync57(join63(smAgentDir, ".session-model-alert"))
|
|
108056
108446
|
});
|
|
108057
108447
|
}
|
|
108058
108448
|
} catch {}
|
|
108059
108449
|
}
|
|
108060
|
-
const activeEffortPath =
|
|
108061
|
-
if (
|
|
108450
|
+
const activeEffortPath = join63(smAgentDir, ".active-session-effort");
|
|
108451
|
+
if (existsSync57(activeEffortPath)) {
|
|
108062
108452
|
try {
|
|
108063
|
-
const launchedEffort =
|
|
108453
|
+
const launchedEffort = readFileSync61(activeEffortPath, "utf8").trim();
|
|
108064
108454
|
const configuredEffort = getConfiguredEffortForPersist();
|
|
108065
108455
|
sessionEffortOverride = launchedEffort.length > 0 && launchedEffort !== configuredEffort ? launchedEffort : null;
|
|
108066
108456
|
} catch {}
|
|
108067
108457
|
}
|
|
108068
|
-
const alertPath =
|
|
108069
|
-
if (
|
|
108458
|
+
const alertPath = join63(smAgentDir, ".session-model-alert");
|
|
108459
|
+
if (existsSync57(alertPath)) {
|
|
108070
108460
|
let alertText = null;
|
|
108071
108461
|
try {
|
|
108072
|
-
alertText =
|
|
108462
|
+
alertText = readFileSync61(alertPath, "utf8").trim();
|
|
108073
108463
|
} catch {
|
|
108074
108464
|
alertText = null;
|
|
108075
108465
|
}
|