switchroom 0.19.43 → 0.19.45

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.
Files changed (29) hide show
  1. package/dist/agent-scheduler/index.js +4 -1
  2. package/dist/auth-broker/index.js +254 -119
  3. package/dist/cli/notion-write-pretool.mjs +4 -1
  4. package/dist/cli/switchroom.js +3995 -3196
  5. package/dist/host-control/main.js +270 -130
  6. package/dist/vault/approvals/kernel-server.js +201 -66
  7. package/dist/vault/broker/server.js +263 -128
  8. package/package.json +1 -1
  9. package/profiles/_base/start.sh.hbs +11 -4
  10. package/telegram-plugin/dist/gateway/gateway.js +945 -638
  11. package/telegram-plugin/flushed-turn-supersede.ts +190 -16
  12. package/telegram-plugin/gateway/cron-session.ts +31 -0
  13. package/telegram-plugin/gateway/gateway.ts +66 -65
  14. package/telegram-plugin/gateway/outbound-send-path.ts +60 -8
  15. package/telegram-plugin/gateway/reply-owner-wiring.ts +128 -0
  16. package/telegram-plugin/gateway/stream-render.ts +91 -1
  17. package/telegram-plugin/gateway/subagent-handback-marker.ts +49 -9
  18. package/telegram-plugin/gateway/subagent-reply-authority.ts +147 -0
  19. package/telegram-plugin/gateway/turn-end.ts +9 -1
  20. package/telegram-plugin/reply-owner-resolve.ts +102 -26
  21. package/telegram-plugin/tests/flushed-turn-supersede.test.ts +110 -8
  22. package/telegram-plugin/tests/narrative-lane-golden.test.ts +2 -0
  23. package/telegram-plugin/tests/reply-owner-resolve.test.ts +297 -27
  24. package/telegram-plugin/tests/reply-quote-wire.test.ts +2 -0
  25. package/telegram-plugin/tests/send-reply-golden.test.ts +567 -5
  26. package/telegram-plugin/tests/stream-render-golden.test.ts +204 -1
  27. package/vendor/hindsight-memory/scripts/lib/config.py +15 -0
  28. package/vendor/hindsight-memory/scripts/tests/test_retain_provenance_tag.py +135 -0
  29. package/vendor/hindsight-memory/settings.json +1 -1
@@ -10994,7 +10994,7 @@ var init_observation_scopes = __esm(() => {
10994
10994
  });
10995
10995
 
10996
10996
  // src/config/schema.ts
10997
- var CodeRepoEntrySchema, AgentBindMountSchema, HttpDiffPollSchema, PollSpecSchema, TelegramMessageActionSchema, WebhookActionSchema, ActionSpecSchema, ScheduleEntrySchema, AgentSoulSchema, AgentToolsSchema, ObservationScopesSchema, ObservationScopeStrategySchema, AgentMemorySchema, HookEntrySchema, AgentHooksSchema, SubagentSchema, SessionSchema, SessionContinuitySchema, webhookDispatchRule, TelegramChannelSchema, ChannelsSchema, TIMEZONE_REGEX, ApproverIdSchema, GoogleWorkspaceTierSchema, GoogleWorkspaceConfigSchema, LiteLLMConfigSchema, HindsightPerOpLlmSchema, HindsightConfigSchema, MicrosoftWorkspaceConfigSchema, NotionWorkspaceConfigSchema, AgentGoogleWorkspaceConfigSchema, MicrosoftAccountEmailSchema, MicrosoftToolTokenSchema, MicrosoftAccountBindingSchema, AgentMicrosoftWorkspaceConfigSchema, AgentNotionWorkspaceConfigSchema, ReactionsSchema, ReactionDispatchSchema, releaseBlockFields, ReleaseBlock, RootReleaseBlock, NetworkIsolationSchema, servesField, knowsField, profileFields, ProfileSchema, _omitExtends, defaultsFields, AgentDefaultsSchema, AgentSchema, TelegramConfigSchema, MemoryBackendConfigSchema, VaultConfigSchema, QuotaConfigSchema, AutoReleaseCheckSchema, HostControlConfigSchema, WebServiceConfigSchema, FleetHealthConfigSchema, HostdConfigSchema, CronEgressSchema, CronConfigSchema, UserSchema, SwitchroomConfigSchema;
10997
+ var CodeRepoEntrySchema, AgentBindMountSchema, HttpDiffPollSchema, PollSpecSchema, TelegramMessageActionSchema, WebhookActionSchema, ActionSpecSchema, ScheduleEntrySchema, AgentSoulSchema, AgentToolsSchema, ObservationScopesSchema, ObservationScopeStrategySchema, AntiConfabulationDirectiveSchema, 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;
10998
10998
  var init_schema = __esm(() => {
10999
10999
  init_zod();
11000
11000
  init_observation_scopes();
@@ -11111,6 +11111,7 @@ var init_schema = __esm(() => {
11111
11111
  }).optional();
11112
11112
  ObservationScopesSchema = exports_external.enum(OBSERVATION_SCOPES).optional().describe("Operator PIN for the per-row observation scope, overriding the " + 'default `curated` strategy (see docs/configuration.md). "shared" ' + "forces Hindsight's consolidation to write EVERY observation into ONE " + "global untagged scope — what several agents pooling one bank want so " + "their observations merge rather than sitting in parallel silos. " + '"combined" is the pin form of opting out (restores the pre-#4035 ' + "engine default). UNSET does NOT mean 'nothing on the wire': the " + "`curated` strategy still computes a scope per retain. Applies to " + "every retain path (Stop hook, " + "sidechain, boot reconcile, queue drain, backfill, session-handoff " + "mirror) and is carried on the queued payload, so a retain that fails " + "now and drains later still lands in this scope. Accepted values: " + `${OBSERVATION_SCOPES.join(", ")}. ` + "Cascade: override (per-agent wins over default).");
11113
11113
  ObservationScopeStrategySchema = exports_external.enum(OBSERVATION_SCOPE_STRATEGIES).optional().describe("Per-retain strategy that decides how the observation scope is " + 'computed when no `observation_scopes` pin is set. "curated" ' + "(the default since #4035) strips volatile per-session provenance " + "tags from each retain's consolidation scope — keeping the stable " + "semantic ones on the source fact — so an agent's observations dedup " + "and merge across sessions instead of pocketing one-per-session. " + '"shared" sends every retain to the one global untagged scope. ' + '"combined" / "off" opt OUT: no per-row scope goes on the wire, ' + "restoring the pre-feature engine default (byte-identical to an " + "unconfigured client). OMITTED BY DEFAULT: unset leaves the plugin's " + "own default (`curated`) in force. A manual `observation_scopes` pin " + "wins over this strategy. Accepted values: " + `${OBSERVATION_SCOPE_STRATEGIES.join(", ")}. ` + "Cascade: override (per-agent wins over default).");
11114
+ AntiConfabulationDirectiveSchema = exports_external.union([exports_external.boolean(), exports_external.string().min(1)]).optional().describe("The seeded anti-confabulation directive: reflect has no relevance " + "floor (`min_scores` is a /recall-only parameter), so a bank holding " + "nothing relevant still yields a confident answer, and a question that " + "presupposes a decision reliably produces one. Directives ARE applied " + "during reflect, so this is the lever. Unset or true (the default) " + "seeds and maintains switchroom's own text; false disables seeding and " + "leaves any existing directive untouched; a string is operator-authored " + "text that wins outright. Switchroom only ever upgrades directive text " + "byte-equal to a default it shipped, so editing the directive in the " + "bank is also a permanent override. " + "Cascade: override (per-agent wins over default).");
11114
11115
  AgentMemorySchema = exports_external.object({
11115
11116
  collection: exports_external.string().describe("Hindsight collection name for this agent"),
11116
11117
  auto_recall: exports_external.boolean().default(true).describe("Auto-search memories before each response"),
@@ -11146,6 +11147,7 @@ var init_schema = __esm(() => {
11146
11147
  empathy: exports_external.number().int().min(1).max(5).optional().describe("How much the bank weights emotional/relational context (1-5; engine default 3).")
11147
11148
  }).optional().describe("Personality traits (1-5 each) steering how this bank frames recall, " + "reflect, and observation synthesis — a coach leans empathy-high, a " + "lawyer/analyst leans skepticism/literalism-high. Maps to the engine's " + "flat `disposition_skepticism`/`_literalism`/`_empathy` fields. " + "Cascade: per-key merge (an agent overrides individual traits and " + "inherits the rest, matching `recall`)."),
11148
11149
  directive_capture_nudge: exports_external.boolean().optional().describe("Deterministic directive-capture nudge (issue #2848 Stage B). When " + "on (switchroom default true — Stage A measured a ~55% miss rate on " + "durable corrections), the auto-recall hook regex-detects correction " + '/ standing-rule-shaped inbound ("always/never …", "from now on …", ' + `"stop doing …", a stated preference, "that's wrong, it's …") and ` + "appends a terse advisory to the turn's context telling the model to " + "persist the rule with mcp__hindsight__create_directive if it IS " + "durable. Detection is pure regex — the model does the judgment " + "in-session and calls create_directive itself (no model callsite, no " + "silent hook-side write). Set false to disable per-agent. " + "Cascade: override (per-agent wins over default)."),
11150
+ anti_confabulation_directive: AntiConfabulationDirectiveSchema,
11149
11151
  observation_scopes: ObservationScopesSchema,
11150
11152
  observation_scope_strategy: ObservationScopeStrategySchema,
11151
11153
  recall: exports_external.object({
@@ -11416,7 +11418,7 @@ var init_schema = __esm(() => {
11416
11418
  reflect: HindsightPerOpLlmSchema.optional().describe("Per-op override for the `reflect` LLM op (synthesis / mental-model " + "refresh). Emits `HINDSIGHT_API_REFLECT_LLM_*`. Absent → uses global."),
11417
11419
  consolidation: HindsightPerOpLlmSchema.optional().describe("Per-op override for the `consolidation` LLM op (background memory " + "merge). Emits `HINDSIGHT_API_CONSOLIDATION_LLM_*`. Absent → global.")
11418
11420
  }).optional().describe("LLM knob for the hindsight container. The flat `provider`/`model` set " + "the global default (backward-compatible); optional `retain`/`reflect`/" + "`consolidation` blocks override individual ops. All fields optional; " + "unset fields fall back to the hard-coded defaults."),
11419
- env: exports_external.record(exports_external.union([exports_external.string(), exports_external.number(), exports_external.boolean()])).optional().describe("Operator overrides for switchroom's capability-gated Hindsight " + "performance defaults. Only the keys switchroom actually manages are " + "honoured (`HINDSIGHT_PERF_ENV_KEYS` in " + "src/setup/hindsight-perf-defaults.ts: RERANKER_LOCAL_FP16, " + "RERANKER_LOCAL_BATCH_SIZE, LLM_MAX_CONCURRENT, " + "RETAIN/CONSOLIDATION_LLM_MAX_CONCURRENT, LLM_STRICT_SCHEMA, " + "LLM_MAX_RETRIES, CONSOLIDATION_LLM_PARALLELISM, " + "MAX_OBSERVATIONS_PER_SCOPE, " + "RECALL_MAX_CANDIDATES_PER_SOURCE, LINK_EXPANSION_PER_ENTITY_LIMIT, " + "LINK_EXPANSION_TIMEOUT, LLM_REASONING_EFFORT, " + "RERANKER_LOCAL_BUCKET_BATCHING, RERANKER_MAX_CANDIDATES, " + "RERANKER_LOCAL_MAX_CONCURRENT, RECALL_MAX_CONCURRENT, " + "REFLECT_WALL_TIMEOUT, WORKER_CONSOLIDATION_RESERVED_SLOTS, " + "WORKER_CONSOLIDATION_SLOT_LIMIT, " + "CONSOLIDATION_MAX_MEMORIES_PER_ROUND, GRAPH_SEED_MIN_SIMILARITY, " + "LLM_SUPPORTS_MAX_ITEMS, RECENCY_DECAY_FUNCTION, " + "RECENCY_DECAY_HALFLIFE_DAYS — switchroom defaults recall's recency " + "curve to `exponential` with a 30-day half-life so a fact retained " + "today outranks a stale one, instead of upstream's near-flat " + "linear/365-day window), the override-only keys " + "switchroom manages but ships NO default for " + "(`HINDSIGHT_PERF_OVERRIDE_ONLY_KEYS`: " + "HINDSIGHT_API_WORKER_CONSOLIDATION_BANK_PRIORITY — a per-deployment " + "`bank-pattern:priority,...` map; unset means upstream's flat " + "created_at FIFO across banks; and " + "HINDSIGHT_CE_DECISIVE_RELATIVE_GAP — the rollback knob for " + "switchroom's CE-saturation damping patch, a float; >= ~0.65 backs the " + "damping out entirely, unset means the patch's own derived gap; and " + "HINDSIGHT_API_RECENCY_DECAY_LINEAR_WINDOW_DAYS — only read when the " + "decay function is `linear`, so switchroom ships no default for it but " + "still honours an operator who flips the function back; and " + "HINDSIGHT_API_WORKER_MAX_SLOTS — the worker poller's TOTAL in-flight " + "task budget, the pool WORKER_CONSOLIDATION_RESERVED_SLOTS reserves out of; " + "unset means upstream's own default; and " + "HINDSIGHT_API_WORKER_RETAIN_RESERVED_SLOTS — the reserved slot FLOOR for the " + "retain (memory write) lane, carved from that same total; unset means " + "upstream's own 0, i.e. no floor and retain competes for the shared " + "pool; and " + "the pre-0.8.6 name HINDSIGHT_API_WORKER_<TYPE>_MAX_SLOTS is still " + "accepted for every operation type and normalised to " + "..._RESERVED_SLOTS on the way in, because setting both names for " + "one type is a hard boot failure in the engine and switchroom " + "therefore emits only the canonical name; and " + "HINDSIGHT_API_SEMANTIC_MIN_SIMILARITY — the cosine-similarity floor a " + "candidate must reach to be returned by the semantic retrieval arm at " + "all (0..1); unset means upstream's own 0.3, and where it should sit " + "depends on the bank's embedding model and phrasing diversity, so " + "switchroom ships no opinion; and " + "HINDSIGHT_MCP_RECALL_BUDGET_MODE — the rollback knob for switchroom's " + "mcp-recall-token-budget image patch; `legacy` restores upstream's " + "exact pre-patch recall returns, anything else or unset is the " + "honest-envelope mode; and " + "HINDSIGHT_API_LLM_TEMPERATURE_REFLECT — upstream's own per-op reflect " + "temperature knob, made live by switchroom's reflect-temperature image " + "patch; a float, or `none` to omit the kwarg (provider default, " + "upstream's accidental pre-patch behaviour); unset means the image's " + "baked default 0.1), plus the " + "embedded-PostgreSQL (pg0) sizing keys switchroom manages in " + "src/setup/hindsight-pg-defaults.ts (`HINDSIGHT_PG_ENV_KEYS`: " + "SWITCHROOM_HINDSIGHT_PG_EFFECTIVE_CACHE_SIZE, " + "SWITCHROOM_HINDSIGHT_PG_SHARED_BUFFERS — a postgres size string such " + "as `4GB`, or the sentinel `off` to leave pg0's own default for that " + "one knob). A value set here " + "REPLACES switchroom's default and is emitted even when the gating " + "capability is absent, so an operator can always force a knob. Other " + "`HINDSIGHT_API_*` keys are deliberately IGNORED — a blanket " + "passthrough would collide with the vars startHindsight() derives " + "itself (HINDSIGHT_API_PORT, the retain token/deadline budget).")
11421
+ env: exports_external.record(exports_external.union([exports_external.string(), exports_external.number(), exports_external.boolean()])).optional().describe("Operator overrides for switchroom's capability-gated Hindsight " + "performance defaults. Only the keys switchroom actually manages are " + "honoured (`HINDSIGHT_PERF_ENV_KEYS` in " + "src/setup/hindsight-perf-defaults.ts: RERANKER_LOCAL_FP16, " + "RERANKER_LOCAL_BATCH_SIZE, LLM_MAX_CONCURRENT, " + "RETAIN/CONSOLIDATION_LLM_MAX_CONCURRENT, LLM_STRICT_SCHEMA, " + "LLM_MAX_RETRIES, CONSOLIDATION_LLM_PARALLELISM, " + "MAX_OBSERVATIONS_PER_SCOPE, " + "RECALL_MAX_CANDIDATES_PER_SOURCE, LINK_EXPANSION_PER_ENTITY_LIMIT, " + "LINK_EXPANSION_TIMEOUT, LLM_REASONING_EFFORT, " + "RERANKER_LOCAL_BUCKET_BATCHING, RERANKER_MAX_CANDIDATES, " + "RERANKER_LOCAL_MAX_CONCURRENT, RECALL_MAX_CONCURRENT, " + "REFLECT_WALL_TIMEOUT, WORKER_CONSOLIDATION_RESERVED_SLOTS, " + "WORKER_CONSOLIDATION_SLOT_LIMIT, " + "CONSOLIDATION_MAX_MEMORIES_PER_ROUND, GRAPH_SEED_MIN_SIMILARITY, " + "LLM_SUPPORTS_MAX_ITEMS, RECENCY_DECAY_FUNCTION, " + "RECENCY_DECAY_HALFLIFE_DAYS — switchroom defaults recall's recency " + "curve to `exponential` with a 30-day half-life so a fact retained " + "today outranks a stale one, instead of upstream's near-flat " + "linear/365-day window), the override-only keys " + "switchroom manages but ships NO default for " + "(`HINDSIGHT_PERF_OVERRIDE_ONLY_KEYS`: " + "HINDSIGHT_API_WORKER_CONSOLIDATION_BANK_PRIORITY — a per-deployment " + "`bank-pattern:priority,...` map; unset means upstream's flat " + "created_at FIFO across banks; and " + "HINDSIGHT_CE_DECISIVE_RELATIVE_GAP — the rollback knob for " + "switchroom's CE-saturation damping patch, a float; >= ~0.65 backs the " + "damping out entirely, unset means the patch's own derived gap; and " + "HINDSIGHT_API_RECENCY_DECAY_LINEAR_WINDOW_DAYS — only read when the " + "decay function is `linear`, so switchroom ships no default for it but " + "still honours an operator who flips the function back; and " + "HINDSIGHT_API_WORKER_MAX_SLOTS — the worker poller's TOTAL in-flight " + "task budget, the pool WORKER_CONSOLIDATION_RESERVED_SLOTS reserves out of; " + "unset means upstream's own default; and " + "HINDSIGHT_API_WORKER_RETAIN_RESERVED_SLOTS — the reserved slot FLOOR for the " + "retain (memory write) lane, carved from that same total; unset means " + "upstream's own 0, i.e. no floor and retain competes for the shared " + "pool; and " + "the pre-0.8.6 name HINDSIGHT_API_WORKER_<TYPE>_MAX_SLOTS is still " + "accepted for every operation type and normalised to " + "..._RESERVED_SLOTS on the way in, because setting both names for " + "one type is a hard boot failure in the engine and switchroom " + "therefore emits only the canonical name; and " + "HINDSIGHT_API_SEMANTIC_MIN_SIMILARITY — the cosine-similarity floor a " + "candidate must reach to be returned by the semantic retrieval arm at " + "all (0..1); unset means upstream's own 0.3, and where it should sit " + "depends on the bank's embedding model and phrasing diversity, so " + "switchroom ships no opinion; and " + "HINDSIGHT_MCP_RECALL_BUDGET_MODE — the rollback knob for switchroom's " + "mcp-recall-token-budget image patch; `legacy` restores upstream's " + "exact pre-patch recall returns, anything else or unset is the " + "honest-envelope mode; and " + "HINDSIGHT_API_LLM_TEMPERATURE_REFLECT — upstream's own per-op reflect " + "temperature knob, made live by switchroom's reflect-temperature image " + "patch; a float, or `none` to omit the kwarg (provider default, " + "upstream's accidental pre-patch behaviour); unset means the image's " + "baked default 0.1; and " + "HINDSIGHT_API_CONSOLIDATION_RECALL_MAX_CONCURRENT — the background " + "half of switchroom's recall-admission split (#3660): of the " + "RECALL_MAX_CONCURRENT admission slots, at most this many may be held " + "by background consolidation recalls at once, so foreground per-turn " + "recall always keeps the remainder; must be >= 1 and strictly less " + "than RECALL_MAX_CONCURRENT or the engine refuses to boot; unset " + "means the image's derived default min(2, RECALL_MAX_CONCURRENT - 1); " + "1 biases hard toward the interactive lane while a consolidation " + "backlog drains), plus the " + "embedded-PostgreSQL (pg0) sizing keys switchroom manages in " + "src/setup/hindsight-pg-defaults.ts (`HINDSIGHT_PG_ENV_KEYS`: " + "SWITCHROOM_HINDSIGHT_PG_EFFECTIVE_CACHE_SIZE, " + "SWITCHROOM_HINDSIGHT_PG_SHARED_BUFFERS — a postgres size string such " + "as `4GB`, or the sentinel `off` to leave pg0's own default for that " + "one knob). A value set here " + "REPLACES switchroom's default and is emitted even when the gating " + "capability is absent, so an operator can always force a knob. Other " + "`HINDSIGHT_API_*` keys are deliberately IGNORED — a blanket " + "passthrough would collide with the vars startHindsight() derives " + "itself (HINDSIGHT_API_PORT, the retain token/deadline budget).")
11420
11422
  });
11421
11423
  MicrosoftWorkspaceConfigSchema = exports_external.object({
11422
11424
  microsoft_client_id: exports_external.string().min(1).optional().describe("Microsoft OAuth application (client) ID from Entra portal " + "(literal string or vault reference e.g. " + "'vault:microsoft-oauth-client-id'). OPTIONAL — omit it to use " + "switchroom's shipped default Microsoft app (zero-config). " + "Set it only to bring your own Entra app (BYO)."),
@@ -11550,6 +11552,7 @@ var init_schema = __esm(() => {
11550
11552
  isolation: exports_external.enum(["default", "strict"]).optional(),
11551
11553
  profile: exports_external.string().optional(),
11552
11554
  directive_capture_nudge: exports_external.boolean().optional(),
11555
+ anti_confabulation_directive: AntiConfabulationDirectiveSchema,
11553
11556
  observation_scopes: ObservationScopesSchema,
11554
11557
  observation_scope_strategy: ObservationScopeStrategySchema,
11555
11558
  recall: exports_external.object({
@@ -18379,13 +18382,13 @@ var require_lib = __commonJS((exports, module) => {
18379
18382
 
18380
18383
  // src/vault/flock.ts
18381
18384
  import {
18382
- existsSync as existsSync6,
18385
+ existsSync as existsSync7,
18383
18386
  openSync as openSync2,
18384
18387
  closeSync as closeSync2,
18385
18388
  writeSync as writeSync2,
18386
18389
  fsyncSync as fsyncSync2,
18387
18390
  unlinkSync as unlinkSync2,
18388
- readFileSync as readFileSync5,
18391
+ readFileSync as readFileSync6,
18389
18392
  readdirSync as readdirSync3,
18390
18393
  rmdirSync,
18391
18394
  statSync as statSync3,
@@ -18418,8 +18421,8 @@ function pidStartTimeMs(pid) {
18418
18421
  if (process.platform !== "linux")
18419
18422
  return null;
18420
18423
  try {
18421
- const statLine = readFileSync5(`/proc/${pid}/stat`, "utf8");
18422
- const procStat = readFileSync5("/proc/stat", "utf8");
18424
+ const statLine = readFileSync6(`/proc/${pid}/stat`, "utf8");
18425
+ const procStat = readFileSync6("/proc/stat", "utf8");
18423
18426
  return parseProcStartTimeMs(statLine, procStat, Date.now());
18424
18427
  } catch {
18425
18428
  return null;
@@ -18815,13 +18818,13 @@ async function rpcRaw(req, opts) {
18815
18818
  async function rpc(req, opts) {
18816
18819
  const socketPath = resolveBrokerSocketPath(opts);
18817
18820
  const timeoutMs = opts?.timeoutMs ?? DEFAULT_TIMEOUT_MS;
18818
- return new Promise((resolve5) => {
18821
+ return new Promise((resolve6) => {
18819
18822
  let settled = false;
18820
18823
  const settle = (val) => {
18821
18824
  if (settled)
18822
18825
  return;
18823
18826
  settled = true;
18824
- resolve5(val);
18827
+ resolve6(val);
18825
18828
  };
18826
18829
  const client = new net.Socket;
18827
18830
  const timer = setTimeout(() => {
@@ -20931,8 +20934,8 @@ var require_commander = __commonJS((exports2) => {
20931
20934
  // src/host-control/main.ts
20932
20935
  init_loader();
20933
20936
  import { homedir as homedir8 } from "node:os";
20934
- import { existsSync as existsSync16, readdirSync as readdirSync7 } from "node:fs";
20935
- import { join as join14, resolve as resolve10 } from "node:path";
20937
+ import { existsSync as existsSync17, readdirSync as readdirSync7 } from "node:fs";
20938
+ import { join as join13, resolve as resolve10 } from "node:path";
20936
20939
 
20937
20940
  // src/host-control/config-degraded.ts
20938
20941
  init_loader();
@@ -21323,9 +21326,66 @@ async function waitForConfigRecovery(opts) {
21323
21326
  init_schema();
21324
21327
 
21325
21328
  // src/agents/scaffold.ts
21326
- import { dirname, isAbsolute, join as join5, relative, resolve as resolve5 } from "node:path";
21327
21329
  init_atomic();
21328
21330
 
21331
+ // src/util/shipped-assets.ts
21332
+ import { existsSync as existsSync5, readFileSync as readFileSync4, realpathSync } from "node:fs";
21333
+ import { dirname, resolve as resolve4 } from "node:path";
21334
+ var FHS_SHARE_ROOTS = [
21335
+ "/usr/local/share/switchroom",
21336
+ "/usr/share/switchroom"
21337
+ ];
21338
+ var PROFILES_ASSET = {
21339
+ asset: "profiles",
21340
+ envVar: "SWITCHROOM_PROFILES_ROOT"
21341
+ };
21342
+ function orderedCandidates(spec, probe) {
21343
+ const out = [];
21344
+ const push = (path, source) => {
21345
+ if (!out.some((c) => c.path === path))
21346
+ out.push({ path, source });
21347
+ };
21348
+ if (probe.bundleDir) {
21349
+ push(resolve4(probe.bundleDir, "../..", spec.asset), "npm");
21350
+ push(resolve4(probe.bundleDir, spec.asset), "image");
21351
+ }
21352
+ if (probe.execPath) {
21353
+ push(resolve4(dirname(probe.execPath), "../share/switchroom", spec.asset), "sea-sibling");
21354
+ }
21355
+ for (const root of FHS_SHARE_ROOTS) {
21356
+ push(resolve4(root, spec.asset), "fhs");
21357
+ }
21358
+ return out;
21359
+ }
21360
+ function resolveShippedAsset(spec, probe) {
21361
+ const env = probe.env ?? process.env;
21362
+ const override = env[spec.envVar]?.trim();
21363
+ if (override) {
21364
+ const path = canonicalise(resolve4(override), probe);
21365
+ return { path, candidates: [path], source: "env" };
21366
+ }
21367
+ const exists = probe.exists ?? existsSync5;
21368
+ const candidates = orderedCandidates(spec, probe);
21369
+ for (const c of candidates) {
21370
+ if (exists(c.path)) {
21371
+ return {
21372
+ path: canonicalise(c.path, probe),
21373
+ candidates: candidates.map((x) => x.path),
21374
+ source: c.source
21375
+ };
21376
+ }
21377
+ }
21378
+ return { path: null, candidates: candidates.map((x) => x.path), source: "none" };
21379
+ }
21380
+ function canonicalise(path, probe) {
21381
+ const realpath = probe.realpath ?? realpathSync;
21382
+ try {
21383
+ return realpath(path);
21384
+ } catch {
21385
+ return path;
21386
+ }
21387
+ }
21388
+
21329
21389
  // src/agents/agent-uid.ts
21330
21390
  init_peercred();
21331
21391
  import { createHash } from "node:crypto";
@@ -21342,7 +21402,7 @@ function allocateAgentUid(name) {
21342
21402
  }
21343
21403
 
21344
21404
  // src/build-info.ts
21345
- var VERSION = "0.19.43";
21405
+ var VERSION = "0.19.45";
21346
21406
 
21347
21407
  // src/setup/hindsight-recall-tunables.ts
21348
21408
  var RECALL_DEADLINE_HEADROOM_SECONDS = 2;
@@ -21387,37 +21447,29 @@ var AUDIT_ROOT = join2(homedir2(), ".switchroom", "audit");
21387
21447
 
21388
21448
  // src/agents/profiles.ts
21389
21449
  var import_handlebars = __toESM(require_lib(), 1);
21390
- import { readFileSync as readFileSync4, writeFileSync as writeFileSync2, existsSync as existsSync5, readdirSync as readdirSync2, statSync as statSync2, copyFileSync, mkdirSync as mkdirSync2, realpathSync } from "node:fs";
21391
- import { resolve as resolve4, join as join3, sep as pathSep } from "node:path";
21392
- function resolveProfilesRoot() {
21393
- const envOverride = process.env.SWITCHROOM_PROFILES_ROOT?.trim();
21394
- if (envOverride) {
21395
- return resolve4(envOverride);
21396
- }
21397
- const candidates = [
21398
- resolve4(import.meta.dirname, "../../profiles"),
21399
- resolve4(import.meta.dirname, "profiles")
21400
- ];
21401
- for (const candidate of candidates) {
21402
- if (existsSync5(candidate)) {
21403
- return candidate;
21404
- }
21405
- }
21406
- return candidates[0];
21450
+ import { readFileSync as readFileSync5, writeFileSync as writeFileSync2, existsSync as existsSync6, readdirSync as readdirSync2, statSync as statSync2, copyFileSync, mkdirSync as mkdirSync2, realpathSync as realpathSync2 } from "node:fs";
21451
+ import { resolve as resolve5, join as join3, sep as pathSep } from "node:path";
21452
+ function resolveProfilesRootDetailed() {
21453
+ return resolveShippedAsset(PROFILES_ASSET, {
21454
+ bundleDir: import.meta.dirname,
21455
+ execPath: process.execPath
21456
+ });
21407
21457
  }
21408
- var PROFILES_ROOT = resolveProfilesRoot();
21458
+ var PROFILES_RESOLUTION = resolveProfilesRootDetailed();
21459
+ var PROFILES_ROOT = PROFILES_RESOLUTION.path ?? PROFILES_RESOLUTION.candidates[0];
21460
+ var PROFILES_ROOT_SEARCH = PROFILES_RESOLUTION.candidates;
21409
21461
  import_handlebars.default.registerHelper("json", (value) => {
21410
21462
  return new import_handlebars.default.SafeString(JSON.stringify(value, null, 2));
21411
21463
  });
21412
21464
  import_handlebars.default.registerHelper("isNumber", (value) => {
21413
21465
  return typeof value === "number" && Number.isFinite(value);
21414
21466
  });
21415
- var SHARED_FRAGMENTS_DIR = resolve4(PROFILES_ROOT, "_shared");
21467
+ var SHARED_FRAGMENTS_DIR = resolve5(PROFILES_ROOT, "_shared");
21416
21468
  var SHARED_FRAGMENTS = ["vault-protocol", "agent-self-service", "execution-discipline", "reply-discipline", "dev-protocol"];
21417
21469
  for (const name of SHARED_FRAGMENTS) {
21418
21470
  const fragPath = join3(SHARED_FRAGMENTS_DIR, `${name}.md.hbs`);
21419
- if (existsSync5(fragPath)) {
21420
- import_handlebars.default.registerPartial(name, readFileSync4(fragPath, "utf-8"));
21471
+ if (existsSync6(fragPath)) {
21472
+ import_handlebars.default.registerPartial(name, readFileSync5(fragPath, "utf-8"));
21421
21473
  }
21422
21474
  }
21423
21475
 
@@ -21620,7 +21672,8 @@ var HINDSIGHT_PERF_OVERRIDE_ONLY_KEYS = new Set([
21620
21672
  "HINDSIGHT_API_SEMANTIC_MIN_SIMILARITY",
21621
21673
  "HINDSIGHT_MCP_RECALL_BUDGET_MODE",
21622
21674
  "HINDSIGHT_API_LLM_TEMPERATURE_REFLECT",
21623
- "HINDSIGHT_API_RETAIN_WALL_TIMEOUT"
21675
+ "HINDSIGHT_API_RETAIN_WALL_TIMEOUT",
21676
+ "HINDSIGHT_API_CONSOLIDATION_RECALL_MAX_CONCURRENT"
21624
21677
  ]);
21625
21678
  var HINDSIGHT_WORKER_SLOT_TYPES = [
21626
21679
  "consolidation",
@@ -21715,6 +21768,15 @@ var DEFAULT_RETAIN_MISSION = `Extract durable facts that will still be true and
21715
21768
  ` + `- Hindsight's own errors, retries, backlogs, or internal state — the memory
21716
21769
  ` + ` system's self-reports are not memories.
21717
21770
  ` + `- Restatements of the user's current request or the task in progress.
21771
+ ` + `- The assistant's own answers, summaries, recaps, or reflect output. Before
21772
+ ` + ` extracting, ask: does anything in this transcript support this claim OTHER
21773
+ ` + ` than the assistant having asserted it? If not, drop it — a model's own
21774
+ ` + ` synthesis re-extracted as a fact is how a guess becomes permanent.
21775
+ ` + ` Concretely, never produce a fact whose only support is the assistant
21776
+ ` + ` stating a date, a version, an attribution, a total, or a decision that the
21777
+ ` + ` user never confirmed and no tool output shows. An unverified claim recalls
21778
+ ` + ` later as though it had been established, which is worse than not
21779
+ ` + ` remembering it at all.
21718
21780
  ` + `- Volatile state written as a timeless assertion. A version, count, size,
21719
21781
  ` + ` backlog, status, or any "X is running Y" / "X is at Y" / "X is currently Y"
21720
21782
  ` + ` claim is true only at the instant it was said. Concretely, never produce a
@@ -21870,6 +21932,64 @@ var SUPERSEDED_RETAIN_MISSIONS = [
21870
21932
  ` + `
21871
21933
  ` + `If a candidate fact matches an exclusion, drop it rather than rewording it. If
21872
21934
  ` + `nothing durable remains, return an empty facts list.
21935
+ `,
21936
+ `Extract durable facts that will still be true and useful weeks from now, once this session is forgotten.
21937
+ ` + `
21938
+ ` + `DURABILITY GATE. Emit a candidate ONLY if it is one of these five classes:
21939
+ ` + `- PREFERENCE — what the user likes, wants, or always does; a standing rule or correction.
21940
+ ` + `- DECISION — a settled choice that changes how future work is done, including a choice NOT to do something. A decision about the mechanics of the CURRENT task (which worker to dispatch, which branch to rebase, which PR to merge now, what to do next) is process narration, not a durable decision — drop it unless it establishes a standing rule or permanently changes a system.
21941
+ ` + `- FINDING — a root cause, a measurement, or verified behaviour of a system. Include the number.
21942
+ ` + `- OUTCOME — a completed result that changed the world: what shipped, what a thing turned out to be. Not the act of shipping it.
21943
+ ` + `- RELATIONSHIP — who a person is, what a project or tool is, and how they connect.
21944
+ ` + `If a candidate fits none of the five, it is not a memory. Drop it; do not reword it into one.
21945
+ ` + `
21946
+ ` + `A preference revealed by a request is durable — record the preference (what the user likes, wants, or always does), not the request itself.
21947
+ ` + `
21948
+ ` + `A TOOL RESULT IS NOT A FACT. Before extracting, ask: is the subject of this
21949
+ ` + `candidate a file path, a command/process/agent/session id, a temp directory, or
21950
+ ` + `the location where some output was written? If yes, drop it — it is transcript
21951
+ ` + `exhaust, not memory.
21952
+ ` + `
21953
+ ` + `NEVER extract:
21954
+ ` + `- Tool results verbatim or paraphrased. Concretely, never produce a fact whose
21955
+ ` + ` text resembles any of these: "File created successfully at /path/to/file",
21956
+ ` + ` "A background command with ID bctz4yskm is running, and its output will be
21957
+ ` + ` written to /tmp/...", "Async agent a745598ba84e71df1 was launched successfully
21958
+ ` + ` and is running in the background", "User executed a Bash command to sleep for
21959
+ ` + ` 200 seconds", "The assistant used grep to locate 'truncateSync' in src/foo.ts".
21960
+ ` + `- Anything mentioning a path under /tmp, a scratchpad directory, or a .tmp file.
21961
+ ` + `- Agent tool-use traces or narration of what the assistant did (e.g. "the
21962
+ ` + ` assistant used X to query Y", "ran a search", "sent the message").
21963
+ ` + `- The act of delegating, dispatching, spawning, launching, steering or merging
21964
+ ` + ` work — including when it succeeded. "X was dispatched and completed" is the
21965
+ ` + ` session describing itself. Record only what the work LEARNED or CHANGED.
21966
+ ` + `- In-flight workflow/process narration (a sub-task started, paused, or is still
21967
+ ` + ` running) — retain the outcome only once the task completes or a decision is made.
21968
+ ` + `- Operation, request, batch, agent, command or session IDs, UUIDs, hashes, or error codes.
21969
+ ` + `- Slash commands the user typed and their effects (e.g. "User issued /clear to
21970
+ ` + ` reset assistant state").
21971
+ ` + `- Hindsight's own errors, retries, backlogs, or internal state — the memory
21972
+ ` + ` system's self-reports are not memories.
21973
+ ` + `- Restatements of the user's current request or the task in progress.
21974
+ ` + `- Volatile state written as a timeless assertion. A version, count, size,
21975
+ ` + ` backlog, status, or any "X is running Y" / "X is at Y" / "X is currently Y"
21976
+ ` + ` claim is true only at the instant it was said. Concretely, never produce a
21977
+ ` + ` fact whose text resembles any of these: "Switchroom fleet is running image
21978
+ ` + ` version v0.18.19", "The switchroom repo is at /path/to/fleet, version
21979
+ ` + ` v0.19.5", "Bank overlord has 43155 pending consolidations", "The build is
21980
+ ` + ` currently green". If the claim is worth keeping, put the date INSIDE the
21981
+ ` + ` fact text ("As of 2026-07-19 the fleet was running v0.18.19"); if you
21982
+ ` + ` cannot date it, drop it. An undated one is recalled forever as though it
21983
+ ` + ` were still true, which is worse than not remembering it at all.
21984
+ ` + `- Transient state (unread counts, build status, what is running right now) unless
21985
+ ` + ` the fact is explicitly dated, in which case record it as a dated observation.
21986
+ ` + `- Greetings, acknowledgements, and routine operational chatter.
21987
+ ` + `
21988
+ ` + `Write each fact so it stands alone: name the thing, the number, and the date. A
21989
+ ` + `sentence that only makes sense while reading this transcript is not durable.
21990
+ ` + `
21991
+ ` + `If a candidate fact matches an exclusion, drop it rather than rewording it. If
21992
+ ` + `nothing durable remains, return an empty facts list.
21873
21993
  `
21874
21994
  ];
21875
21995
  var DEFAULT_OBSERVATIONS_MISSION = `Synthesise durable, standing knowledge about the people, projects, and systems this agent works with: preferences and standing rules, roles and relationships, skills and recurring patterns, technical and operational decisions with their rationale, and the state of long-running work once it lands.
@@ -21947,6 +22067,22 @@ var PROFILE_MEMORY_DEFAULTS = {
21947
22067
  var warnedMissingPool = new Set;
21948
22068
  var warnedMissingDefault = new Set;
21949
22069
 
22070
+ // src/memory/hindsight-retain-provenance.ts
22071
+ var RETAIN_PROVENANCE_TAG = "source:transcript";
22072
+ var RETAIN_TAGS_DEFAULT = Object.freeze([
22073
+ "{session_id}",
22074
+ RETAIN_PROVENANCE_TAG
22075
+ ]);
22076
+
22077
+ // src/memory/hindsight-seed-directives.ts
22078
+ var ANTI_CONFABULATION_DIRECTIVE = `When the retrieved memories do not support an answer, say so instead of composing one.
22079
+ ` + `
22080
+ ` + `- If nothing retrieved scores meaningfully above zero, or what came back is off-topic, the honest answer is that the bank does not know. Say that plainly, and say what would settle it.
22081
+ ` + `- Never assert a date, a version, a number, an attribution, a decision, or an outcome that no retrieved memory states. A question that presupposes something is not evidence for it: asked when a decision was made, when the bank records no such decision, answer that none is recorded — do not supply one.
22082
+ ` + `- Keep what the bank RECORDED separate from what you INFER. State inferences as inferences, and say which memory a recorded fact came from.
22083
+ ` + `- Treat a memory that was extracted from a conversation transcript as an unverified claim, not an established fact — it may be an assistant's own earlier guess. If it is the only support for an answer, say that it is.
22084
+ ` + "- Partial knowledge with its gaps named is more useful than a fluent answer that fills them in.";
22085
+
21950
22086
  // src/telegram/state.ts
21951
22087
  init_paths();
21952
22088
 
@@ -21984,7 +22120,6 @@ var RECALL_PASSTHROUGH_DEFAULTS = Object.freeze({
21984
22120
  init_paths();
21985
22121
 
21986
22122
  // src/agents/scaffold.ts
21987
- var REPO_ROOT = resolve5(import.meta.dirname, "../..");
21988
22123
  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.";
21989
22124
  var pendingBankOps = new Set;
21990
22125
  var SWITCHROOM_OWNED_SETTINGS_KEYS = new Set([
@@ -22016,8 +22151,8 @@ import { spawn as spawn2, spawnSync as spawnSync4 } from "node:child_process";
22016
22151
  import { mkdir as mkdir2, chmod, chown, unlink, appendFile } from "node:fs/promises";
22017
22152
  import {
22018
22153
  readdirSync as readdirSync6,
22019
- existsSync as existsSync15,
22020
- readFileSync as readFileSync11,
22154
+ existsSync as existsSync16,
22155
+ readFileSync as readFileSync12,
22021
22156
  writeFileSync as writeFileSync6,
22022
22157
  renameSync as renameSync5,
22023
22158
  mkdirSync as mkdirSync5,
@@ -22029,7 +22164,7 @@ import {
22029
22164
  copyFileSync as copyFileSync3,
22030
22165
  unlinkSync as unlinkSync5
22031
22166
  } from "node:fs";
22032
- import { join as join13, dirname as dirname9, resolve as resolve9 } from "node:path";
22167
+ import { join as join12, dirname as dirname9, resolve as resolve9 } from "node:path";
22033
22168
  import { createHash as createHash6, randomUUID as randomUUID2, randomBytes } from "node:crypto";
22034
22169
 
22035
22170
  // src/host-control/protocol.ts
@@ -26721,9 +26856,9 @@ class PostHog extends PostHogBackendClient {
26721
26856
  // src/analytics/posthog.ts
26722
26857
  init_paths();
26723
26858
  import {
26724
- existsSync as existsSync8,
26859
+ existsSync as existsSync9,
26725
26860
  mkdirSync as mkdirSync3,
26726
- readFileSync as readFileSync7,
26861
+ readFileSync as readFileSync8,
26727
26862
  writeFileSync as writeFileSync3
26728
26863
  } from "node:fs";
26729
26864
  import { dirname as dirname3 } from "node:path";
@@ -26742,8 +26877,8 @@ function getDistinctId() {
26742
26877
  return cachedDistinctId;
26743
26878
  const path = resolveStatePath("analytics-id");
26744
26879
  try {
26745
- if (existsSync8(path)) {
26746
- const existing = readFileSync7(path, "utf-8").trim();
26880
+ if (existsSync9(path)) {
26881
+ const existing = readFileSync8(path, "utf-8").trim();
26747
26882
  if (existing) {
26748
26883
  cachedDistinctId = existing;
26749
26884
  return existing;
@@ -27952,8 +28087,8 @@ function releaseBlockIsCommented(doc) {
27952
28087
 
27953
28088
  // src/cli/rollout-pin-journal.ts
27954
28089
  import {
27955
- existsSync as existsSync11,
27956
- readFileSync as readFileSync8,
28090
+ existsSync as existsSync12,
28091
+ readFileSync as readFileSync9,
27957
28092
  writeFileSync as writeFileSync4,
27958
28093
  renameSync as renameSync4,
27959
28094
  unlinkSync as unlinkSync4,
@@ -27962,7 +28097,7 @@ import {
27962
28097
  } from "node:fs";
27963
28098
  import { homedir as homedir5 } from "node:os";
27964
28099
  import { createHash as createHash4 } from "node:crypto";
27965
- import { join as join8, basename as basename3, resolve as resolve7, dirname as dirname6 } from "node:path";
28100
+ import { join as join7, basename as basename3, resolve as resolve7, dirname as dirname6 } from "node:path";
27966
28101
  init_flock();
27967
28102
  var PIN_JOURNAL_MAX_AGE_MS = 15 * 60 * 1000;
27968
28103
  var STATE_DIR_NAME = ".switchroom";
@@ -27975,15 +28110,15 @@ function pinJournalDir(configPath) {
27975
28110
  if (basename3(dir) === STATE_DIR_NAME)
27976
28111
  return dir;
27977
28112
  }
27978
- return join8(homedir5(), STATE_DIR_NAME);
28113
+ return join7(homedir5(), STATE_DIR_NAME);
27979
28114
  }
27980
28115
  function pinJournalPath(configPath) {
27981
28116
  const abs = resolve7(configPath);
27982
28117
  const key = createHash4("sha256").update(abs).digest("hex").slice(0, 12);
27983
- return join8(pinJournalDir(abs), `.rollout-pin-journal.${basename3(abs)}.${key}.json`);
28118
+ return join7(pinJournalDir(abs), `.rollout-pin-journal.${basename3(abs)}.${key}.json`);
27984
28119
  }
27985
28120
  function hasPinJournal(configPath) {
27986
- return existsSync11(pinJournalPath(configPath));
28121
+ return existsSync12(pinJournalPath(configPath));
27987
28122
  }
27988
28123
  function isPidAlive(pid) {
27989
28124
  if (!Number.isInteger(pid) || pid <= 0)
@@ -28017,9 +28152,9 @@ function readPinJournal(configPath, warn = (m) => process.stderr.write(m)) {
28017
28152
  const p = pinJournalPath(configPath);
28018
28153
  let raw;
28019
28154
  try {
28020
- raw = readFileSync8(p, "utf8");
28155
+ raw = readFileSync9(p, "utf8");
28021
28156
  } catch (e) {
28022
- if (existsSync11(p)) {
28157
+ if (existsSync12(p)) {
28023
28158
  warn(`⚠️ rollout pin journal: ${p} exists but could not be read ` + `(${e.message}). A provisional \`release.pin\` may be ` + `uncommitted — verify it host-side before the next reconcile.
28024
28159
  `);
28025
28160
  }
@@ -28064,7 +28199,7 @@ function commitPinPersist(configPath) {
28064
28199
  unlinkSync4(p);
28065
28200
  return null;
28066
28201
  } catch (e) {
28067
- if (!existsSync11(p))
28202
+ if (!existsSync12(p))
28068
28203
  return null;
28069
28204
  return `rollout pin journal: FAILED to clear ${p} after a SUCCESSFUL roll ` + `(${e.message}). Delete it host-side — while it exists, ` + `recovery may revert a proven \`release.pin\`.`;
28070
28205
  }
@@ -28086,7 +28221,7 @@ function rollbackPinPersist(configPath, opts = {}) {
28086
28221
  }
28087
28222
  }
28088
28223
  try {
28089
- const current = readFileSync8(configPath, "utf8");
28224
+ const current = readFileSync9(configPath, "utf8");
28090
28225
  const next = journal.priorPin ? setReleasePinInConfig(current, journal.priorPin) : deleteReleasePinInConfig(current);
28091
28226
  let mode = 384;
28092
28227
  try {
@@ -28137,14 +28272,14 @@ function recoverPinJournal(configPath, opts = {}) {
28137
28272
  // src/cli/operator-uid.ts
28138
28273
  import {
28139
28274
  chownSync,
28140
- existsSync as existsSync12,
28275
+ existsSync as existsSync13,
28141
28276
  lstatSync as lstatSync2,
28142
28277
  readdirSync as readdirSync5,
28143
- realpathSync as realpathSync2,
28278
+ realpathSync as realpathSync3,
28144
28279
  statSync as statSync7
28145
28280
  } from "node:fs";
28146
28281
  import { homedir as homedir6 } from "node:os";
28147
- import { join as join9 } from "node:path";
28282
+ import { join as join8 } from "node:path";
28148
28283
  var CONTAINER_OPERATOR_HOME = "/host-home";
28149
28284
  var SWITCHROOM_HOME_MARKER = "switchroom.yaml";
28150
28285
  function operatorHomeCandidates(env, home2) {
@@ -28153,7 +28288,7 @@ function operatorHomeCandidates(env, home2) {
28153
28288
  for (const r of roots) {
28154
28289
  if (!r || r.length === 0)
28155
28290
  continue;
28156
- const dir = join9(r, ".switchroom");
28291
+ const dir = join8(r, ".switchroom");
28157
28292
  if (!out.includes(dir))
28158
28293
  out.push(dir);
28159
28294
  }
@@ -28162,7 +28297,7 @@ function operatorHomeCandidates(env, home2) {
28162
28297
  function resolveOperatorUidFromOwnership(deps = {}) {
28163
28298
  const env = deps.env ?? process.env;
28164
28299
  const home2 = deps.home ?? homedir6();
28165
- const exists = deps.exists ?? ((p) => existsSync12(p));
28300
+ const exists = deps.exists ?? ((p) => existsSync13(p));
28166
28301
  const statUid = deps.statUid ?? ((p) => {
28167
28302
  try {
28168
28303
  return statSync7(p).uid;
@@ -28171,7 +28306,7 @@ function resolveOperatorUidFromOwnership(deps = {}) {
28171
28306
  }
28172
28307
  });
28173
28308
  for (const dir of operatorHomeCandidates(env, home2)) {
28174
- if (!exists(join9(dir, SWITCHROOM_HOME_MARKER)))
28309
+ if (!exists(join8(dir, SWITCHROOM_HOME_MARKER)))
28175
28310
  continue;
28176
28311
  const uid = statUid(dir);
28177
28312
  if (uid !== undefined && Number.isFinite(uid) && uid > 0)
@@ -28204,15 +28339,15 @@ function resolveOperatorUid(ownershipDeps = {}) {
28204
28339
  init_atomic();
28205
28340
 
28206
28341
  // src/cli/resolve-version.ts
28207
- import { existsSync as existsSync13, readFileSync as readFileSync9 } from "node:fs";
28208
- import { dirname as dirname7, join as join10 } from "node:path";
28342
+ import { existsSync as existsSync14, readFileSync as readFileSync10 } from "node:fs";
28343
+ import { dirname as dirname7, join as join9 } from "node:path";
28209
28344
  function readPackageVersion() {
28210
28345
  let dir = import.meta.dirname;
28211
28346
  for (let i = 0;i < 12 && dir && dir !== "/"; i++) {
28212
- const p = join10(dir, "package.json");
28213
- if (existsSync13(p)) {
28347
+ const p = join9(dir, "package.json");
28348
+ if (existsSync14(p)) {
28214
28349
  try {
28215
- const pkg = JSON.parse(readFileSync9(p, "utf-8"));
28350
+ const pkg = JSON.parse(readFileSync10(p, "utf-8"));
28216
28351
  if (pkg?.name === "switchroom" && typeof pkg.version === "string") {
28217
28352
  return pkg.version;
28218
28353
  }
@@ -28613,7 +28748,7 @@ var import__2 = __toESM(require_commander(), 1);
28613
28748
 
28614
28749
  // src/cli/update.ts
28615
28750
  init_loader();
28616
- import { join as join11, dirname as dirname8, resolve as resolve8 } from "node:path";
28751
+ import { join as join10, dirname as dirname8 } from "node:path";
28617
28752
  import { homedir as homedir7 } from "node:os";
28618
28753
 
28619
28754
  // src/agents/lifecycle.ts
@@ -28716,7 +28851,12 @@ function getAllAgentStatuses(config) {
28716
28851
 
28717
28852
  // src/cli/update.ts
28718
28853
  init_atomic();
28719
- var DEFAULT_COMPOSE_PATH = join11(homedir7(), ".switchroom", "compose", "docker-compose.yml");
28854
+ var DEFAULT_COMPOSE_PATH = join10(homedir7(), ".switchroom", "compose", "docker-compose.yml");
28855
+ var PACKAGED_INSTALL_KINDS = new Set([
28856
+ "static-binary",
28857
+ "npm-global",
28858
+ "container"
28859
+ ]);
28720
28860
  var UPDATE_RESULT_SENTINEL = "SWITCHROOM_UPDATE_RESULT:";
28721
28861
  function parseUpdateResultLine(stdout) {
28722
28862
  const idx = stdout.lastIndexOf(UPDATE_RESULT_SENTINEL);
@@ -28802,9 +28942,9 @@ function resolvePriorPinFromFleet(components, target, scope) {
28802
28942
  // src/host-control/config-edit-validator.ts
28803
28943
  init_schema();
28804
28944
  var import_yaml5 = __toESM(require_dist(), 1);
28805
- import { mkdtempSync, writeFileSync as writeFileSync5, rmSync as rmSync2, existsSync as existsSync14, readFileSync as readFileSync10 } from "node:fs";
28945
+ import { mkdtempSync, writeFileSync as writeFileSync5, rmSync as rmSync2, existsSync as existsSync15, readFileSync as readFileSync11 } from "node:fs";
28806
28946
  import { tmpdir } from "node:os";
28807
- import { join as join12, isAbsolute as isAbsolute3, normalize, basename as basename4 } from "node:path";
28947
+ import { join as join11, isAbsolute as isAbsolute2, normalize, basename as basename4 } from "node:path";
28808
28948
  import { spawnSync as spawnSync3 } from "node:child_process";
28809
28949
  import { isDeepStrictEqual } from "node:util";
28810
28950
  var MAX_PATCH_BYTES = 1024 * 1024;
@@ -28817,12 +28957,12 @@ function isTargetPathHeader(headerPath, targetBasename) {
28817
28957
  let p = headerPath;
28818
28958
  if (p.startsWith("a/") || p.startsWith("b/"))
28819
28959
  p = p.slice(2);
28820
- if (isAbsolute3(p))
28960
+ if (isAbsolute2(p))
28821
28961
  return false;
28822
28962
  if (p.includes(".."))
28823
28963
  return false;
28824
28964
  const norm = normalize(p);
28825
- if (norm.includes("..") || isAbsolute3(norm))
28965
+ if (norm.includes("..") || isAbsolute2(norm))
28826
28966
  return false;
28827
28967
  return norm === targetBasename || basename4(norm) === targetBasename;
28828
28968
  }
@@ -28944,21 +29084,21 @@ function validateShape(unifiedDiff, targetPath) {
28944
29084
  return null;
28945
29085
  }
28946
29086
  function applyPatch(unifiedDiff, configPath, gitBin) {
28947
- if (!existsSync14(configPath)) {
29087
+ if (!existsSync15(configPath)) {
28948
29088
  return {
28949
29089
  ok: false,
28950
29090
  code: "E_PATCH_APPLY_FAILED",
28951
29091
  detail: `target config not found at ${configPath}`
28952
29092
  };
28953
29093
  }
28954
- const liveContent = readFileSync10(configPath, "utf8");
28955
- const scratchDir = mkdtempSync(join12(tmpdir(), "config-propose-edit-"));
29094
+ const liveContent = readFileSync11(configPath, "utf8");
29095
+ const scratchDir = mkdtempSync(join11(tmpdir(), "config-propose-edit-"));
28956
29096
  try {
28957
29097
  const targetBasename = configPath.split("/").pop() ?? "switchroom.yaml";
28958
- const scratchFile = join12(scratchDir, targetBasename);
29098
+ const scratchFile = join11(scratchDir, targetBasename);
28959
29099
  writeFileSync5(scratchFile, liveContent);
28960
29100
  const normalizedDiff = normalizeDiffHeadersToBasename(unifiedDiff, targetBasename);
28961
- const patchFile = join12(scratchDir, "proposal.patch");
29101
+ const patchFile = join11(scratchDir, "proposal.patch");
28962
29102
  writeFileSync5(patchFile, normalizedDiff);
28963
29103
  const bin = gitBin ?? "git";
28964
29104
  const baseArgs = [
@@ -28988,7 +29128,7 @@ function applyPatch(unifiedDiff, configPath, gitBin) {
28988
29128
  detail: `git apply failed: ${(real.stderr || "").trim().slice(0, 500)}`
28989
29129
  };
28990
29130
  }
28991
- const after = readFileSync10(scratchFile, "utf8");
29131
+ const after = readFileSync11(scratchFile, "utf8");
28992
29132
  return { ok: true, after };
28993
29133
  } finally {
28994
29134
  rmSync2(scratchDir, { recursive: true, force: true });
@@ -29186,7 +29326,7 @@ function validateConfigEdit(opts) {
29186
29326
  return schemaErr;
29187
29327
  let beforeData = {};
29188
29328
  try {
29189
- const beforeRaw = existsSync14(opts.configPath) ? readFileSync10(opts.configPath, "utf8") : "";
29329
+ const beforeRaw = existsSync15(opts.configPath) ? readFileSync11(opts.configPath, "utf8") : "";
29190
29330
  const beforeDoc = import_yaml5.parseDocument(beforeRaw, { merge: false, strict: false });
29191
29331
  beforeData = beforeDoc.toJS();
29192
29332
  } catch {
@@ -29770,11 +29910,11 @@ function resolveDigests(imageRefs) {
29770
29910
  return out;
29771
29911
  }
29772
29912
  function readCachedInstallType(bindRoot) {
29773
- const cacheDir = join13(bindRoot, ".switchroom");
29774
- const cachePath = join13(cacheDir, "install-type.json");
29775
- if (existsSync15(cachePath)) {
29913
+ const cacheDir = join12(bindRoot, ".switchroom");
29914
+ const cachePath = join12(cacheDir, "install-type.json");
29915
+ if (existsSync16(cachePath)) {
29776
29916
  try {
29777
- const raw = readFileSync11(cachePath, "utf-8");
29917
+ const raw = readFileSync12(cachePath, "utf-8");
29778
29918
  const parsed = JSON.parse(raw);
29779
29919
  if (parsed && typeof parsed.install_type === "string" && typeof parsed.detected_at === "string") {
29780
29920
  return parsed;
@@ -29826,7 +29966,7 @@ function writeFileInPlacePreservingInode(targetPath, content) {
29826
29966
  } finally {
29827
29967
  closeSync5(fd);
29828
29968
  }
29829
- const readBack = readFileSync11(targetPath);
29969
+ const readBack = readFileSync12(targetPath);
29830
29970
  if (readBack.length !== buf.length) {
29831
29971
  throw new Error(`in-place write short: wrote ${buf.length} bytes but read back ${readBack.length}`);
29832
29972
  }
@@ -29861,11 +30001,11 @@ class HostdServer {
29861
30001
  fleetMutationInFlight = null;
29862
30002
  lastAutoRolloutFailedPin = null;
29863
30003
  autoRolloutLatchPath() {
29864
- return join13(this.hostdDirPath(), AUTO_ROLLOUT_LATCH_FILENAME);
30004
+ return join12(this.hostdDirPath(), AUTO_ROLLOUT_LATCH_FILENAME);
29865
30005
  }
29866
30006
  readAutoRolloutLatch() {
29867
30007
  try {
29868
- const raw = readFileSync11(this.autoRolloutLatchPath(), "utf-8");
30008
+ const raw = readFileSync12(this.autoRolloutLatchPath(), "utf-8");
29869
30009
  const parsed = JSON.parse(raw);
29870
30010
  if (parsed && parsed.v === 1 && typeof parsed.pin === "string" && parsed.pin.length > 0 && (parsed.outcome === "attempting" || parsed.outcome === "failed")) {
29871
30011
  return parsed;
@@ -29904,7 +30044,7 @@ class HostdServer {
29904
30044
  return resolveHostdConfigPath(this.opts.configPath);
29905
30045
  }
29906
30046
  fleetComposePath() {
29907
- return join13(this.opts.bindRoot ?? this.opts.homeDir, ".switchroom", "compose", "docker-compose.yml");
30047
+ return join12(this.opts.bindRoot ?? this.opts.homeDir, ".switchroom", "compose", "docker-compose.yml");
29908
30048
  }
29909
30049
  recoverRolloutPinJournal(context) {
29910
30050
  let note = null;
@@ -29943,7 +30083,7 @@ class HostdServer {
29943
30083
  return note;
29944
30084
  }
29945
30085
  async start() {
29946
- const hostdDir = join13(this.opts.homeDir, ".switchroom", "hostd");
30086
+ const hostdDir = join12(this.opts.homeDir, ".switchroom", "hostd");
29947
30087
  await mkdir2(hostdDir, { recursive: true });
29948
30088
  await chmod(hostdDir, 493).catch(() => {
29949
30089
  return;
@@ -29964,13 +30104,13 @@ class HostdServer {
29964
30104
  }
29965
30105
  try {
29966
30106
  for (const name of agentNames) {
29967
- const dir = join13(hostdDir, name);
29968
- const sockPath = join13(dir, "sock");
30107
+ const dir = join12(hostdDir, name);
30108
+ const sockPath = join12(dir, "sock");
29969
30109
  await mkdir2(dir, { recursive: true });
29970
30110
  await chmod(dir, 493).catch(() => {
29971
30111
  return;
29972
30112
  });
29973
- if (existsSync15(sockPath))
30113
+ if (existsSync16(sockPath))
29974
30114
  await unlink(sockPath).catch(() => {
29975
30115
  return;
29976
30116
  });
@@ -30001,13 +30141,13 @@ class HostdServer {
30001
30141
  process.stderr.write(`hostd: SWITCHROOM_HOSTD_OPERATOR_UID='${opUidStr}' is not a positive integer; skipping operator listener
30002
30142
  `);
30003
30143
  } else {
30004
- const dir = join13(hostdDir, "operator");
30005
- const sockPath = join13(dir, "sock");
30144
+ const dir = join12(hostdDir, "operator");
30145
+ const sockPath = join12(dir, "sock");
30006
30146
  await mkdir2(dir, { recursive: true });
30007
30147
  await chmod(dir, 493).catch(() => {
30008
30148
  return;
30009
30149
  });
30010
- if (existsSync15(sockPath))
30150
+ if (existsSync16(sockPath))
30011
30151
  await unlink(sockPath).catch(() => {
30012
30152
  return;
30013
30153
  });
@@ -30043,11 +30183,11 @@ class HostdServer {
30043
30183
  }
30044
30184
  async reconcileOrphanedFleetMutations() {
30045
30185
  const path3 = this.auditLogPath();
30046
- if (!existsSync15(path3))
30186
+ if (!existsSync16(path3))
30047
30187
  return;
30048
30188
  let raw;
30049
30189
  try {
30050
- raw = readFileSync11(path3, "utf-8");
30190
+ raw = readFileSync12(path3, "utf-8");
30051
30191
  } catch {
30052
30192
  return;
30053
30193
  }
@@ -30446,11 +30586,11 @@ class HostdServer {
30446
30586
  missingApplyAssets() {
30447
30587
  const root = this.opts.applyAssetsRoot ?? resolve9(import.meta.dirname, "../..");
30448
30588
  return [
30449
- join13(root, "profiles"),
30450
- join13(root, "profiles", "default"),
30451
- join13(root, "vendor", "hindsight-memory"),
30452
- join13(root, "skills")
30453
- ].filter((p) => !existsSync15(p));
30589
+ join12(root, "profiles"),
30590
+ join12(root, "profiles", "default"),
30591
+ join12(root, "vendor", "hindsight-memory"),
30592
+ join12(root, "skills")
30593
+ ].filter((p) => !existsSync16(p));
30454
30594
  }
30455
30595
  applyAssetPreflight(request_id, started) {
30456
30596
  const missing = this.missingApplyAssets();
@@ -30708,19 +30848,19 @@ class HostdServer {
30708
30848
  return entry;
30709
30849
  }
30710
30850
  hostdDirPath() {
30711
- return join13(this.opts.homeDir, ".switchroom", "hostd");
30851
+ return join12(this.opts.homeDir, ".switchroom", "hostd");
30712
30852
  }
30713
30853
  hostdDirHostPath() {
30714
30854
  const hostHome = this.opts.hostHomeDir ?? process.env.SWITCHROOM_HOST_HOME?.trim() ?? this.opts.homeDir;
30715
- return join13(hostHome, ".switchroom", "hostd");
30855
+ return join12(hostHome, ".switchroom", "hostd");
30716
30856
  }
30717
30857
  async beginSelfBump(req, caller, started) {
30718
30858
  const ownVersion = this.opts.selfVersion ?? SWITCHROOM_VERSION;
30719
- const composePath = join13(this.hostdDirPath(), "docker-compose.yml");
30720
- if (!existsSync15(composePath)) {
30859
+ const composePath = join12(this.hostdDirPath(), "docker-compose.yml");
30860
+ if (!existsSync16(composePath)) {
30721
30861
  return deniedResponse(req.request_id, `rollout: hostd's CLI (v${ownVersion}) is older than the target ` + `${req.args.pin} and needs a self-bump first, but its compose file ` + `is missing at ${composePath}. Run \`switchroom hostd install ` + `--tag ${req.args.pin}\` on the host, then re-run the roll. ` + `Nothing was changed.`, Date.now() - started);
30722
30862
  }
30723
- const before = readFileSync11(composePath, "utf8");
30863
+ const before = readFileSync12(composePath, "utf8");
30724
30864
  const bumped = bumpHostdComposeImageTag(before, req.args.pin);
30725
30865
  if (!bumped) {
30726
30866
  return deniedResponse(req.request_id, `rollout: hostd needs a self-bump to ${req.args.pin} but no ` + `switchroom-hostd image line was found in ${composePath} ` + `(hand-edited compose?). Run \`switchroom hostd install --tag ` + `${req.args.pin}\` on the host, then re-run the roll. Nothing ` + `was changed.`, Date.now() - started);
@@ -30741,10 +30881,10 @@ class HostdServer {
30741
30881
  try {
30742
30882
  const baks = readdirSync6(this.hostdDirPath()).filter((f) => f.startsWith("docker-compose.yml.bak-selfbump-")).sort();
30743
30883
  for (const f of baks.slice(0, Math.max(0, baks.length - 4))) {
30744
- unlinkSync5(join13(this.hostdDirPath(), f));
30884
+ unlinkSync5(join12(this.hostdDirPath(), f));
30745
30885
  }
30746
30886
  } catch {}
30747
- const markerPath = join13(this.hostdDirPath(), SELF_BUMP_MARKER_FILENAME);
30887
+ const markerPath = join12(this.hostdDirPath(), SELF_BUMP_MARKER_FILENAME);
30748
30888
  const marker = {
30749
30889
  v: 1,
30750
30890
  request_id: req.request_id,
@@ -30828,7 +30968,7 @@ class HostdServer {
30828
30968
  rollbackBump();
30829
30969
  entry.result = "error";
30830
30970
  entry.finished_at = Date.now();
30831
- entry.error = `self-bump helper failed (container exit ${Number.isFinite(code) ? code : "unknown"}). ` + `See ${join13(this.hostdDirHostPath(), SELF_BUMP_LOG_FILENAME)} on the host. ` + `Compose bump rolled back. Fallback: \`switchroom hostd install ` + `--tag ${req.args.pin}\` host-side, then re-run the roll.`;
30971
+ entry.error = `self-bump helper failed (container exit ${Number.isFinite(code) ? code : "unknown"}). ` + `See ${join12(this.hostdDirHostPath(), SELF_BUMP_LOG_FILENAME)} on the host. ` + `Compose bump rolled back. Fallback: \`switchroom hostd install ` + `--tag ${req.args.pin}\` host-side, then re-run the roll.`;
30832
30972
  if (this.fleetMutationInFlight && this.fleetMutationInFlight.request_id === entry.request_id) {
30833
30973
  this.fleetMutationInFlight = null;
30834
30974
  }
@@ -30855,10 +30995,10 @@ class HostdServer {
30855
30995
  try {
30856
30996
  if (!Number.isInteger(messageId) || messageId <= 0)
30857
30997
  return;
30858
- const markerPath = join13(this.hostdDirPath(), SELF_BUMP_MARKER_FILENAME);
30859
- if (!existsSync15(markerPath))
30998
+ const markerPath = join12(this.hostdDirPath(), SELF_BUMP_MARKER_FILENAME);
30999
+ if (!existsSync16(markerPath))
30860
31000
  return;
30861
- const marker = parsePendingRolloutMarker(readFileSync11(markerPath, "utf8"));
31001
+ const marker = parsePendingRolloutMarker(readFileSync12(markerPath, "utf8"));
30862
31002
  if (!marker)
30863
31003
  return;
30864
31004
  if (marker.request_id !== requestId)
@@ -30873,12 +31013,12 @@ class HostdServer {
30873
31013
  } catch {}
30874
31014
  }
30875
31015
  async resumePendingSelfBumpRollout() {
30876
- const markerPath = join13(this.hostdDirPath(), SELF_BUMP_MARKER_FILENAME);
30877
- if (!existsSync15(markerPath))
31016
+ const markerPath = join12(this.hostdDirPath(), SELF_BUMP_MARKER_FILENAME);
31017
+ if (!existsSync16(markerPath))
30878
31018
  return;
30879
31019
  let raw;
30880
31020
  try {
30881
- raw = readFileSync11(markerPath, "utf8");
31021
+ raw = readFileSync12(markerPath, "utf8");
30882
31022
  } finally {
30883
31023
  try {
30884
31024
  unlinkSync5(markerPath);
@@ -30929,7 +31069,7 @@ class HostdServer {
30929
31069
  return;
30930
31070
  }
30931
31071
  if (needsSelfBump(ownVersion, marker.pin)) {
30932
- await failResume(`hostd is still on v${ownVersion} after the self-bump to ` + `${marker.pin} (helper failed? see ` + `${join13(this.hostdDirHostPath(), SELF_BUMP_LOG_FILENAME)}). ` + `Fallback: \`switchroom hostd install --tag ${marker.pin}\` ` + `host-side, then re-issue the rollout.`);
31072
+ await failResume(`hostd is still on v${ownVersion} after the self-bump to ` + `${marker.pin} (helper failed? see ` + `${join12(this.hostdDirHostPath(), SELF_BUMP_LOG_FILENAME)}). ` + `Fallback: \`switchroom hostd install --tag ${marker.pin}\` ` + `host-side, then re-issue the rollout.`);
30933
31073
  return;
30934
31074
  }
30935
31075
  process.stderr.write(`hostd: resuming rollout ${marker.request_id} → ${marker.pin} after ` + `self-bump (v${marker.prior_hostd_version} → v${ownVersion})
@@ -31037,7 +31177,7 @@ class HostdServer {
31037
31177
  }
31038
31178
  let beforeContent;
31039
31179
  try {
31040
- beforeContent = readFileSync11(configPath, "utf-8");
31180
+ beforeContent = readFileSync12(configPath, "utf-8");
31041
31181
  } catch {
31042
31182
  beforeContent = "";
31043
31183
  }
@@ -31157,7 +31297,7 @@ class HostdServer {
31157
31297
  try {
31158
31298
  let snapshot;
31159
31299
  try {
31160
- snapshot = readFileSync11(configPath, "utf-8");
31300
+ snapshot = readFileSync12(configPath, "utf-8");
31161
31301
  } catch (e) {
31162
31302
  await approval.finalize({
31163
31303
  outcome: "reconcile_failed_rolled_back",
@@ -31455,8 +31595,8 @@ ${output.recovery.stderr}` : "";
31455
31595
  if (this.opts.imageRefsForDigests)
31456
31596
  return this.opts.imageRefsForDigests();
31457
31597
  try {
31458
- const composePath = join13(this.opts.bindRoot ?? this.opts.homeDir, ".switchroom", "compose", "docker-compose.yml");
31459
- if (!existsSync15(composePath))
31598
+ const composePath = join12(this.opts.bindRoot ?? this.opts.homeDir, ".switchroom", "compose", "docker-compose.yml");
31599
+ if (!existsSync16(composePath))
31460
31600
  return [];
31461
31601
  const r = spawnSync4("docker", [
31462
31602
  "compose",
@@ -31714,10 +31854,10 @@ ${pinNote}`.trim();
31714
31854
  }
31715
31855
  rolloutRowExistsInLog(targetRequestId) {
31716
31856
  const path3 = this.auditLogPath();
31717
- if (!existsSync15(path3))
31857
+ if (!existsSync16(path3))
31718
31858
  return false;
31719
31859
  try {
31720
- const raw = readFileSync11(path3, "utf-8");
31860
+ const raw = readFileSync12(path3, "utf-8");
31721
31861
  return latestRolloutRowForRequest(raw, targetRequestId) !== null;
31722
31862
  } catch {
31723
31863
  return false;
@@ -31725,11 +31865,11 @@ ${pinNote}`.trim();
31725
31865
  }
31726
31866
  rolloutStatusFromLog(responseRequestId, targetRequestId, started) {
31727
31867
  const path3 = this.auditLogPath();
31728
- if (!existsSync15(path3))
31868
+ if (!existsSync16(path3))
31729
31869
  return null;
31730
31870
  let raw;
31731
31871
  try {
31732
- raw = readFileSync11(path3, "utf-8");
31872
+ raw = readFileSync12(path3, "utf-8");
31733
31873
  } catch {
31734
31874
  return null;
31735
31875
  }
@@ -31811,7 +31951,7 @@ ${pinNote}`.trim();
31811
31951
  }
31812
31952
  }
31813
31953
  auditLogPath() {
31814
- return this.opts.auditLogPath ?? join13(this.opts.homeDir, ".switchroom", "host-control-audit.log");
31954
+ return this.opts.auditLogPath ?? join12(this.opts.homeDir, ".switchroom", "host-control-audit.log");
31815
31955
  }
31816
31956
  maybeRotateAuditLog(path3) {
31817
31957
  const { maxBytes, maxFiles } = resolveRotationConfig({
@@ -33295,7 +33435,7 @@ function makeRestart(switchroomBin) {
33295
33435
  }
33296
33436
 
33297
33437
  // src/host-control/update-notifier.ts
33298
- import { mkdirSync as mkdirSync6, readFileSync as readFileSync12, renameSync as renameSync6, writeFileSync as writeFileSync7 } from "node:fs";
33438
+ import { mkdirSync as mkdirSync6, readFileSync as readFileSync13, renameSync as renameSync6, writeFileSync as writeFileSync7 } from "node:fs";
33299
33439
  import { randomBytes as randomBytes3 } from "node:crypto";
33300
33440
  import { dirname as dirname10 } from "node:path";
33301
33441
  var FINALIZE_DETAIL_MAX = 450;
@@ -33412,7 +33552,7 @@ class UpdateNotifier {
33412
33552
  }
33413
33553
  readState() {
33414
33554
  try {
33415
- const raw = readFileSync12(this.opts.statePath, "utf8");
33555
+ const raw = readFileSync13(this.opts.statePath, "utf8");
33416
33556
  const parsed = JSON.parse(raw);
33417
33557
  if (parsed === null || typeof parsed !== "object")
33418
33558
  return null;
@@ -33553,13 +33693,13 @@ async function loadConfigResilient() {
33553
33693
  } catch {
33554
33694
  configPath = undefined;
33555
33695
  }
33556
- const agentsDir = process.env.SWITCHROOM_AGENTS_DIR ?? join14(homedir8(), ".switchroom", "agents");
33696
+ const agentsDir = process.env.SWITCHROOM_AGENTS_DIR ?? join13(homedir8(), ".switchroom", "agents");
33557
33697
  const listGatewayCandidates = () => {
33558
33698
  const out = [];
33559
33699
  try {
33560
33700
  for (const name of readdirSync7(agentsDir).sort()) {
33561
33701
  const sock = resolve10(agentsDir, name, "telegram", "gateway.sock");
33562
- if (existsSync16(sock))
33702
+ if (existsSync17(sock))
33563
33703
  out.push({ agent: name, sock });
33564
33704
  }
33565
33705
  } catch {}
@@ -33606,10 +33746,10 @@ async function main() {
33606
33746
  `);
33607
33747
  process.exit(2);
33608
33748
  }
33609
- const agentsDir = process.env.SWITCHROOM_AGENTS_DIR ?? join14(homedir8(), ".switchroom", "agents");
33749
+ const agentsDir = process.env.SWITCHROOM_AGENTS_DIR ?? join13(homedir8(), ".switchroom", "agents");
33610
33750
  const resolveGatewaySocket = (agentName) => {
33611
33751
  const sock = resolve10(agentsDir, agentName, "telegram", "gateway.sock");
33612
- return existsSync16(sock) ? sock : null;
33752
+ return existsSync17(sock) ? sock : null;
33613
33753
  };
33614
33754
  const approvalGateway = new SocketApprovalGateway({
33615
33755
  resolveGatewaySocket,
@@ -33663,7 +33803,7 @@ async function main() {
33663
33803
  const autoRel = config.host_control?.auto_release_check;
33664
33804
  const watcherMode = resolveReleaseWatcherMode(config);
33665
33805
  if (watcherMode.mode !== "off") {
33666
- const eventsLog = join14(homedir8(), ".switchroom", "release-watcher-events.jsonl");
33806
+ const eventsLog = join13(homedir8(), ".switchroom", "release-watcher-events.jsonl");
33667
33807
  const log = (m) => {
33668
33808
  process.stderr.write(`hostd: ${m}
33669
33809
  `);
@@ -33722,7 +33862,7 @@ async function main() {
33722
33862
  `);
33723
33863
  } else
33724
33864
  notifier = new UpdateNotifier({
33725
- statePath: join14(homedir8(), ".switchroom", "release-notify-state.json"),
33865
+ statePath: join13(homedir8(), ".switchroom", "release-notify-state.json"),
33726
33866
  repostSuppressMs: cardTimeoutMs,
33727
33867
  isFleetMutationInFlight: () => server.isFleetMutationLocked(),
33728
33868
  startApply: (requestId) => {