switchroom 0.19.28 → 0.19.30

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.
@@ -2120,7 +2120,7 @@ var init_esm = __esm(() => {
2120
2120
  });
2121
2121
 
2122
2122
  // src/build-info.ts
2123
- var VERSION = "0.19.28", COMMIT_SHA = "d9a5f4ae";
2123
+ var VERSION = "0.19.30", COMMIT_SHA = "efe8e2a7";
2124
2124
 
2125
2125
  // src/cli/resolve-version.ts
2126
2126
  import { existsSync, readFileSync } from "node:fs";
@@ -13571,13 +13571,46 @@ var init_zod = __esm(() => {
13571
13571
  init_external();
13572
13572
  });
13573
13573
 
13574
+ // src/memory/observation-scopes.ts
13575
+ function isObservationScope(value) {
13576
+ return typeof value === "string" && OBSERVATION_SCOPES.includes(value);
13577
+ }
13578
+ function observationScopesHint() {
13579
+ return OBSERVATION_SCOPES.join(", ");
13580
+ }
13581
+ function classifyObservationScope(raw) {
13582
+ if (raw === undefined)
13583
+ return { kind: "unset" };
13584
+ const value = raw.trim();
13585
+ if (!value)
13586
+ return { kind: "unset" };
13587
+ if (!isObservationScope(value))
13588
+ return { kind: "invalid", raw };
13589
+ return { kind: "set", scope: value };
13590
+ }
13591
+ function resolveObservationScope(configured, env2 = process.env) {
13592
+ if (configured !== undefined)
13593
+ return classifyObservationScope(configured);
13594
+ return classifyObservationScope(env2[OBSERVATION_SCOPES_ENV]);
13595
+ }
13596
+ var OBSERVATION_SCOPES, OBSERVATION_SCOPES_ENV = "HINDSIGHT_OBSERVATION_SCOPES";
13597
+ var init_observation_scopes = __esm(() => {
13598
+ OBSERVATION_SCOPES = [
13599
+ "per_tag",
13600
+ "combined",
13601
+ "all_combinations",
13602
+ "shared"
13603
+ ];
13604
+ });
13605
+
13574
13606
  // src/config/schema.ts
13575
13607
  function isValidTimezone(value) {
13576
13608
  return TIMEZONE_REGEX.test(value);
13577
13609
  }
13578
- 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, DEFAULT_PROFILE = "default", AgentSchema, TelegramConfigSchema, MemoryBackendConfigSchema, VaultConfigSchema, QuotaConfigSchema, AutoReleaseCheckSchema, HostControlConfigSchema, WebServiceConfigSchema, FleetHealthConfigSchema, HostdConfigSchema, CronEgressSchema, CronConfigSchema, UserSchema, SwitchroomConfigSchema;
13610
+ var CodeRepoEntrySchema, AgentBindMountSchema, HttpDiffPollSchema, PollSpecSchema, TelegramMessageActionSchema, WebhookActionSchema, ActionSpecSchema, ScheduleEntrySchema, AgentSoulSchema, AgentToolsSchema, ObservationScopesSchema, AgentMemorySchema, HookEntrySchema, AgentHooksSchema, SubagentSchema, SessionSchema, SessionContinuitySchema, webhookDispatchRule, TelegramChannelSchema, ChannelsSchema, TIMEZONE_REGEX, ApproverIdSchema, GoogleWorkspaceTierSchema, GoogleWorkspaceConfigSchema, LiteLLMConfigSchema, HindsightPerOpLlmSchema, HindsightConfigSchema, MicrosoftWorkspaceConfigSchema, NotionWorkspaceConfigSchema, AgentGoogleWorkspaceConfigSchema, MicrosoftAccountEmailSchema, MicrosoftToolTokenSchema, MicrosoftAccountBindingSchema, AgentMicrosoftWorkspaceConfigSchema, AgentNotionWorkspaceConfigSchema, ReactionsSchema, ReactionDispatchSchema, releaseBlockFields, ReleaseBlock, RootReleaseBlock, NetworkIsolationSchema, servesField, knowsField, profileFields, ProfileSchema, _omitExtends, defaultsFields, AgentDefaultsSchema, DEFAULT_PROFILE = "default", AgentSchema, TelegramConfigSchema, MemoryBackendConfigSchema, VaultConfigSchema, QuotaConfigSchema, AutoReleaseCheckSchema, HostControlConfigSchema, WebServiceConfigSchema, FleetHealthConfigSchema, HostdConfigSchema, CronEgressSchema, CronConfigSchema, UserSchema, SwitchroomConfigSchema;
13579
13611
  var init_schema = __esm(() => {
13580
13612
  init_zod();
13613
+ init_observation_scopes();
13581
13614
  CodeRepoEntrySchema = exports_external.object({
13582
13615
  name: exports_external.string().describe("Short alias used when claiming (e.g. 'switchroom')"),
13583
13616
  source: exports_external.string().describe("Absolute or home-relative path to the repo (e.g. ~/code/switchroom)"),
@@ -13689,6 +13722,7 @@ var init_schema = __esm(() => {
13689
13722
  allow: exports_external.array(exports_external.string()).default([]).describe("Allowed tools (use ['all'] for unrestricted)"),
13690
13723
  deny: exports_external.array(exports_external.string()).default([]).describe("Denied tools (overrides allow)")
13691
13724
  }).optional();
13725
+ ObservationScopesSchema = exports_external.enum(OBSERVATION_SCOPES).optional().describe("Per-row observation scope stamped on every memory this agent " + `retains. "shared" makes Hindsight's consolidation write the ` + "resulting observations into ONE global untagged scope instead of a " + "scope per tag \u2014 what several agents pooling one bank need so their " + "observations actually merge rather than sitting in parallel " + "per-tag silos. OMITTED BY DEFAULT: unset means the field never goes " + "on the wire and the engine's own default stands, which is the " + "shipped behaviour. 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).");
13692
13726
  AgentMemorySchema = exports_external.object({
13693
13727
  collection: exports_external.string().describe("Hindsight collection name for this agent"),
13694
13728
  auto_recall: exports_external.boolean().default(true).describe("Auto-search memories before each response"),
@@ -13723,6 +13757,7 @@ var init_schema = __esm(() => {
13723
13757
  empathy: exports_external.number().int().min(1).max(5).optional().describe("How much the bank weights emotional/relational context (1-5; engine default 3).")
13724
13758
  }).optional().describe("Personality traits (1-5 each) steering how this bank frames recall, " + "reflect, and observation synthesis \u2014 a coach leans empathy-high, a " + "lawyer/analyst leans skepticism/literalism-high. Maps to the engine's " + "flat `disposition_skepticism`/`_literalism`/`_empathy` fields. " + "Cascade: per-key merge (an agent overrides individual traits and " + "inherits the rest, matching `recall`)."),
13725
13759
  directive_capture_nudge: exports_external.boolean().optional().describe("Deterministic directive-capture nudge (issue #2848 Stage B). When " + "on (switchroom default true \u2014 Stage A measured a ~55% miss rate on " + "durable corrections), the auto-recall hook regex-detects correction " + '/ standing-rule-shaped inbound ("always/never \u2026", "from now on \u2026", ' + `"stop doing \u2026", a stated preference, "that's wrong, it's \u2026") and ` + "appends a terse advisory to the turn's context telling the model to " + "persist the rule with mcp__hindsight__create_directive if it IS " + "durable. Detection is pure regex \u2014 the model does the judgment " + "in-session and calls create_directive itself (no model callsite, no " + "silent hook-side write). Set false to disable per-agent. " + "Cascade: override (per-agent wins over default)."),
13760
+ observation_scopes: ObservationScopesSchema,
13726
13761
  recall: exports_external.object({
13727
13762
  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)."),
13728
13763
  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."),
@@ -13988,7 +14023,7 @@ var init_schema = __esm(() => {
13988
14023
  reflect: HindsightPerOpLlmSchema.optional().describe("Per-op override for the `reflect` LLM op (synthesis / mental-model " + "refresh). Emits `HINDSIGHT_API_REFLECT_LLM_*`. Absent \u2192 uses global."),
13989
14024
  consolidation: HindsightPerOpLlmSchema.optional().describe("Per-op override for the `consolidation` LLM op (background memory " + "merge). Emits `HINDSIGHT_API_CONSOLIDATION_LLM_*`. Absent \u2192 global.")
13990
14025
  }).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."),
13991
- 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_MAX_SLOTS, " + "WORKER_CONSOLIDATION_SLOT_LIMIT, " + "CONSOLIDATION_MAX_MEMORIES_PER_ROUND, RECENCY_DECAY_FUNCTION, " + "RECENCY_DECAY_HALFLIFE_DAYS \u2014 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 \u2014 a per-deployment " + "`bank-pattern:priority,...` map; unset means upstream's flat " + "created_at FIFO across banks; and " + "HINDSIGHT_CE_DECISIVE_RELATIVE_GAP \u2014 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 \u2014 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 \u2014 the worker poller's TOTAL in-flight " + "task budget, the pool WORKER_CONSOLIDATION_MAX_SLOTS reserves out of; " + "unset means upstream's own default), 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 \u2014 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 \u2014 a blanket " + "passthrough would collide with the vars startHindsight() derives " + "itself (HINDSIGHT_API_PORT, the retain token/deadline budget).")
14026
+ 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_MAX_SLOTS, " + "WORKER_CONSOLIDATION_SLOT_LIMIT, " + "CONSOLIDATION_MAX_MEMORIES_PER_ROUND, RECENCY_DECAY_FUNCTION, " + "RECENCY_DECAY_HALFLIFE_DAYS \u2014 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 \u2014 a per-deployment " + "`bank-pattern:priority,...` map; unset means upstream's flat " + "created_at FIFO across banks; and " + "HINDSIGHT_CE_DECISIVE_RELATIVE_GAP \u2014 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 \u2014 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 \u2014 the worker poller's TOTAL in-flight " + "task budget, the pool WORKER_CONSOLIDATION_MAX_SLOTS reserves out of; " + "unset means upstream's own default; and " + "HINDSIGHT_API_WORKER_RETAIN_MAX_SLOTS \u2014 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), 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 \u2014 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 \u2014 a blanket " + "passthrough would collide with the vars startHindsight() derives " + "itself (HINDSIGHT_API_PORT, the retain token/deadline budget).")
13992
14027
  });
13993
14028
  MicrosoftWorkspaceConfigSchema = exports_external.object({
13994
14029
  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 \u2014 omit it to use " + "switchroom's shipped default Microsoft app (zero-config). " + "Set it only to bring your own Entra app (BYO)."),
@@ -14121,6 +14156,7 @@ var init_schema = __esm(() => {
14121
14156
  file: exports_external.boolean().optional(),
14122
14157
  isolation: exports_external.enum(["default", "strict"]).optional(),
14123
14158
  directive_capture_nudge: exports_external.boolean().optional(),
14159
+ observation_scopes: ObservationScopesSchema,
14124
14160
  recall: exports_external.object({
14125
14161
  max_memories: exports_external.number().int().min(0).optional(),
14126
14162
  cache_ttl_secs: exports_external.number().int().min(0).optional(),
@@ -15922,7 +15958,8 @@ var init_hindsight_perf_defaults = __esm(() => {
15922
15958
  "HINDSIGHT_API_WORKER_CONSOLIDATION_BANK_PRIORITY",
15923
15959
  "HINDSIGHT_CE_DECISIVE_RELATIVE_GAP",
15924
15960
  "HINDSIGHT_API_RECENCY_DECAY_LINEAR_WINDOW_DAYS",
15925
- "HINDSIGHT_API_WORKER_MAX_SLOTS"
15961
+ "HINDSIGHT_API_WORKER_MAX_SLOTS",
15962
+ "HINDSIGHT_API_WORKER_RETAIN_MAX_SLOTS"
15926
15963
  ]);
15927
15964
  HINDSIGHT_PERF_ENV_KEYS = new Set([
15928
15965
  ...[
@@ -28867,6 +28904,7 @@ function buildWorkspaceContext(args) {
28867
28904
  hindsightRecallTypes,
28868
28905
  hindsightRecallSkipTrivial,
28869
28906
  hindsightDirectiveCaptureNudge,
28907
+ hindsightObservationScopes,
28870
28908
  hindsightTopicAliasesJson,
28871
28909
  hindsightSenderBanksJson,
28872
28910
  hindsightTopicFilterMode
@@ -28920,6 +28958,7 @@ function buildWorkspaceContext(args) {
28920
28958
  hindsightRecallTypes,
28921
28959
  hindsightRecallSkipTrivial,
28922
28960
  hindsightDirectiveCaptureNudge,
28961
+ hindsightObservationScopesQ: hindsightObservationScopes ? shellSingleQuote(hindsightObservationScopes) : undefined,
28923
28962
  hindsightTopicAliasesJsonQ: hindsightTopicAliasesJson ? shellSingleQuote(hindsightTopicAliasesJson) : undefined,
28924
28963
  hindsightSenderBanksJsonQ: hindsightSenderBanksJson ? shellSingleQuote(hindsightSenderBanksJson) : undefined,
28925
28964
  hindsightTopicFilterMode,
@@ -29184,6 +29223,7 @@ function scaffoldAgent(name, agentConfigRaw, agentsDir, telegramConfig, switchro
29184
29223
  const hindsightRecallTypes = agentConfig.memory?.recall?.types?.length ? agentConfig.memory.recall.types.join(",") : undefined;
29185
29224
  const hindsightRecallSkipTrivial = agentConfig.memory?.recall?.skip_trivial === undefined ? undefined : String(agentConfig.memory.recall.skip_trivial);
29186
29225
  const hindsightDirectiveCaptureNudge = agentConfig.memory?.directive_capture_nudge === undefined ? undefined : String(agentConfig.memory.directive_capture_nudge);
29226
+ const hindsightObservationScopes = agentConfig.memory?.observation_scopes;
29187
29227
  const topicAliases = agentConfig.channels?.telegram?.topic_aliases;
29188
29228
  const hindsightTopicAliasesJson = topicAliases && Object.keys(topicAliases).length > 0 ? JSON.stringify(topicAliases) : undefined;
29189
29229
  const senderBanks = switchroomConfig ? resolveUsers(switchroomConfig, name).senderBanks : agentConfig.memory?.recall?.sender_banks ?? {};
@@ -29219,6 +29259,7 @@ function scaffoldAgent(name, agentConfigRaw, agentsDir, telegramConfig, switchro
29219
29259
  hindsightRecallTypes,
29220
29260
  hindsightRecallSkipTrivial,
29221
29261
  hindsightDirectiveCaptureNudge,
29262
+ hindsightObservationScopes,
29222
29263
  hindsightTopicAliasesJson,
29223
29264
  hindsightSenderBanksJson,
29224
29265
  hindsightTopicFilterMode
@@ -30163,6 +30204,7 @@ function reconcileAgentInner(name, agentConfigRaw, agentsDir, telegramConfig, sw
30163
30204
  const hindsightRecallTypes = agentConfig.memory?.recall?.types?.length ? agentConfig.memory.recall.types.join(",") : undefined;
30164
30205
  const hindsightRecallSkipTrivial = agentConfig.memory?.recall?.skip_trivial === undefined ? undefined : String(agentConfig.memory.recall.skip_trivial);
30165
30206
  const hindsightDirectiveCaptureNudge = agentConfig.memory?.directive_capture_nudge === undefined ? undefined : String(agentConfig.memory.directive_capture_nudge);
30207
+ const hindsightObservationScopes = agentConfig.memory?.observation_scopes;
30166
30208
  const topicAliases = agentConfig.channels?.telegram?.topic_aliases;
30167
30209
  const hindsightTopicAliasesJson = topicAliases && Object.keys(topicAliases).length > 0 ? JSON.stringify(topicAliases) : undefined;
30168
30210
  const senderBanks = switchroomConfig ? resolveUsers(switchroomConfig, name).senderBanks : agentConfig.memory?.recall?.sender_banks ?? {};
@@ -30211,6 +30253,7 @@ function reconcileAgentInner(name, agentConfigRaw, agentsDir, telegramConfig, sw
30211
30253
  hindsightRecallTypes,
30212
30254
  hindsightRecallSkipTrivial,
30213
30255
  hindsightDirectiveCaptureNudge,
30256
+ hindsightObservationScopesQ: hindsightObservationScopes ? shellSingleQuote(hindsightObservationScopes) : undefined,
30214
30257
  hindsightTopicAliasesJsonQ: hindsightTopicAliasesJson ? shellSingleQuote(hindsightTopicAliasesJson) : undefined,
30215
30258
  hindsightSenderBanksJsonQ: hindsightSenderBanksJson ? shellSingleQuote(hindsightSenderBanksJson) : undefined,
30216
30259
  hindsightTopicFilterMode,
@@ -30633,6 +30676,7 @@ ${body}
30633
30676
  hindsightRecallTypes,
30634
30677
  hindsightRecallSkipTrivial,
30635
30678
  hindsightDirectiveCaptureNudge,
30679
+ hindsightObservationScopes,
30636
30680
  hindsightTopicAliasesJson,
30637
30681
  hindsightSenderBanksJson,
30638
30682
  hindsightTopicFilterMode
@@ -50816,6 +50860,209 @@ var init_doctor_drift = __esm(() => {
50816
50860
  init_write_compose();
50817
50861
  });
50818
50862
 
50863
+ // src/cli/component-versions.ts
50864
+ function versionFromImageRef(ref) {
50865
+ const at = ref.indexOf("@");
50866
+ const withoutDigest = at >= 0 ? ref.slice(0, at) : ref;
50867
+ const slash = withoutDigest.lastIndexOf("/");
50868
+ const colon = withoutDigest.indexOf(":", slash + 1);
50869
+ if (colon < 0) {
50870
+ return { version: null, detail: `image ref has no tag (${ref})` };
50871
+ }
50872
+ const tag = withoutDigest.slice(colon + 1);
50873
+ if (SEMVER_TAG_RE.test(tag))
50874
+ return { version: tag };
50875
+ return { version: null, detail: `non-semver tag "${tag}"` };
50876
+ }
50877
+ function isSwitchroomReleaseImage(_name, imageRef) {
50878
+ return /\/switchroom-[a-z0-9-]+(:|@|$)/.test(imageRef);
50879
+ }
50880
+ function collectContainerComponents(exec) {
50881
+ const r = exec("docker", [
50882
+ "ps",
50883
+ "--filter",
50884
+ "name=switchroom-",
50885
+ "--format",
50886
+ "{{.Names}}\t{{.Image}}"
50887
+ ]);
50888
+ if (r.status !== 0)
50889
+ return [];
50890
+ const out = [];
50891
+ for (const line of r.stdout.split(`
50892
+ `)) {
50893
+ const [name, imageRef] = line.trim().split("\t");
50894
+ if (!name || !imageRef)
50895
+ continue;
50896
+ if (!isSwitchroomReleaseImage(name, imageRef))
50897
+ continue;
50898
+ const { version: version2, detail } = versionFromImageRef(imageRef);
50899
+ out.push({
50900
+ name,
50901
+ kind: "container",
50902
+ version: version2,
50903
+ imageRef,
50904
+ ...detail ? { detail } : {}
50905
+ });
50906
+ }
50907
+ return out.sort((a, b) => a.name.localeCompare(b.name));
50908
+ }
50909
+ function normalizeSemverTag(v) {
50910
+ if (!v)
50911
+ return null;
50912
+ const tag = `v${v.trim().replace(/^v/, "")}`;
50913
+ return SEMVER_TAG_RE.test(tag) ? tag : null;
50914
+ }
50915
+ function resolveDriftTarget(components, releasePin) {
50916
+ const pin = normalizeSemverTag(releasePin);
50917
+ if (pin)
50918
+ return { target: pin, targetSource: "release.pin" };
50919
+ let highest = null;
50920
+ for (const c of components) {
50921
+ if (!c.version)
50922
+ continue;
50923
+ if (highest === null) {
50924
+ highest = c.version;
50925
+ continue;
50926
+ }
50927
+ const cmp = compareReleaseTags(c.version, highest);
50928
+ if (cmp !== null && cmp > 0)
50929
+ highest = c.version;
50930
+ }
50931
+ return highest ? { target: highest, targetSource: "highest-deployed" } : { target: null, targetSource: "none" };
50932
+ }
50933
+ function detectComponentDrift(components, releasePin) {
50934
+ const { target, targetSource } = resolveDriftTarget(components, releasePin);
50935
+ const report = {
50936
+ target,
50937
+ targetSource,
50938
+ behind: [],
50939
+ ahead: [],
50940
+ current: [],
50941
+ unknown: []
50942
+ };
50943
+ for (const c of components) {
50944
+ if (!c.version || !target) {
50945
+ report.unknown.push(c);
50946
+ continue;
50947
+ }
50948
+ const cmp = compareReleaseTags(c.version, target);
50949
+ if (cmp === null)
50950
+ report.unknown.push(c);
50951
+ else if (cmp < 0)
50952
+ report.behind.push(c);
50953
+ else if (cmp > 0)
50954
+ report.ahead.push(c);
50955
+ else
50956
+ report.current.push(c);
50957
+ }
50958
+ return report;
50959
+ }
50960
+ function collectComponents(cliVersion, exec) {
50961
+ const cli = {
50962
+ name: "cli (host)",
50963
+ kind: "cli",
50964
+ version: normalizeSemverTag(cliVersion),
50965
+ ...normalizeSemverTag(cliVersion) ? {} : { detail: `unparseable CLI version "${cliVersion}"` }
50966
+ };
50967
+ return [cli, ...collectContainerComponents(exec)];
50968
+ }
50969
+ function formatComponentDrift(report) {
50970
+ const lines = [];
50971
+ if (!report.target) {
50972
+ lines.push("Component versions: no comparable version found (no release.pin and no semver-tagged component).");
50973
+ } else {
50974
+ const src = report.targetSource === "release.pin" ? "release.pin" : "highest deployed version (no release.pin set)";
50975
+ lines.push(`Component versions \u2014 target ${report.target} (from ${src}):`);
50976
+ }
50977
+ for (const c of report.behind) {
50978
+ lines.push(` BEHIND ${c.name} \u2014 ${c.version} (target ${report.target})`);
50979
+ }
50980
+ for (const c of report.ahead) {
50981
+ lines.push(` ahead ${c.name} \u2014 ${c.version}`);
50982
+ }
50983
+ for (const c of report.unknown) {
50984
+ lines.push(` unknown ${c.name} \u2014 ${c.detail ?? "version not readable"}`);
50985
+ }
50986
+ for (const c of report.current) {
50987
+ lines.push(` ok ${c.name} \u2014 ${c.version}`);
50988
+ }
50989
+ return lines.join(`
50990
+ `);
50991
+ }
50992
+ var SEMVER_TAG_RE;
50993
+ var init_component_versions = __esm(() => {
50994
+ SEMVER_TAG_RE = /^v\d+\.\d+\.\d+$/;
50995
+ });
50996
+
50997
+ // src/cli/doctor-component-versions.ts
50998
+ import { spawnSync as spawnSync12 } from "node:child_process";
50999
+ function fixFor(c, target) {
51000
+ if (c.kind === "cli") {
51001
+ return `switchroom update (self-updates the host CLI to ${target} first, then everything else)`;
51002
+ }
51003
+ if (c.name === "switchroom-web") {
51004
+ return `switchroom webd install --tag ${target} (run host-side: webd install fails inside hostd when the hostd compose predates SWITCHROOM_HOSTD_OPERATOR_UID)`;
51005
+ }
51006
+ if (c.name === "switchroom-hindsight-autoheal" || c.name === "switchroom-hostd") {
51007
+ return `switchroom hostd install --tag ${target} (regenerates the hostd compose project \u2014 both hostd and the autoheal sidecar)`;
51008
+ }
51009
+ return `switchroom update`;
51010
+ }
51011
+ function runComponentVersionChecks(config, opts = {}) {
51012
+ const exec = opts.exec ?? defaultExec2;
51013
+ const components = collectComponents(opts.cliVersion ?? SWITCHROOM_VERSION, exec);
51014
+ const report = detectComponentDrift(components, config.release?.pin);
51015
+ if (!report.target) {
51016
+ return [
51017
+ {
51018
+ name: "component versions",
51019
+ status: "skip",
51020
+ detail: "no release.pin set and no semver-tagged switchroom container running \u2014 nothing to compare."
51021
+ }
51022
+ ];
51023
+ }
51024
+ const results = [];
51025
+ const src = report.targetSource === "release.pin" ? `release.pin ${report.target}` : `${report.target} (highest deployed \u2014 no release.pin set)`;
51026
+ if (report.behind.length === 0) {
51027
+ results.push({
51028
+ name: "component versions",
51029
+ status: "ok",
51030
+ detail: `all ${report.current.length} comparable component(s) on ${src}`
51031
+ });
51032
+ }
51033
+ for (const c of report.behind) {
51034
+ results.push({
51035
+ name: `component behind: ${c.name}`,
51036
+ status: "warn",
51037
+ detail: `on ${c.version}, expected ${report.target} (${src})`,
51038
+ fix: fixFor(c, report.target)
51039
+ });
51040
+ }
51041
+ for (const c of report.ahead) {
51042
+ results.push({
51043
+ name: `component ahead: ${c.name}`,
51044
+ status: "warn",
51045
+ detail: `on ${c.version}, ahead of ${src} \u2014 a roll may be mid-flight, or release.pin is stale`
51046
+ });
51047
+ }
51048
+ for (const c of report.unknown) {
51049
+ results.push({
51050
+ name: `component version unknown: ${c.name}`,
51051
+ status: "skip",
51052
+ detail: c.detail ?? "version not readable"
51053
+ });
51054
+ }
51055
+ return results;
51056
+ }
51057
+ var defaultExec2 = (cmd, args) => {
51058
+ const r = spawnSync12(cmd, args, { encoding: "utf-8", timeout: 1e4 });
51059
+ return { status: r.status ?? 1, stdout: r.stdout ?? "" };
51060
+ };
51061
+ var init_doctor_component_versions = __esm(() => {
51062
+ init_resolve_version();
51063
+ init_component_versions();
51064
+ });
51065
+
50819
51066
  // src/cli/doctor-scaffold-wiring.ts
50820
51067
  import { join as join70, resolve as resolve43 } from "node:path";
50821
51068
  function readJson2(d, path7) {
@@ -52570,7 +52817,7 @@ var init_doctor_fix_session_model = __esm(() => {
52570
52817
  });
52571
52818
 
52572
52819
  // src/cli/doctor-claude-cli.ts
52573
- import { spawnSync as spawnSync12 } from "node:child_process";
52820
+ import { spawnSync as spawnSync13 } from "node:child_process";
52574
52821
  import { existsSync as existsSync76, readFileSync as readFileSync70 } from "node:fs";
52575
52822
  import { dirname as dirname30, join as join80 } from "node:path";
52576
52823
  function parseClaudeCliVersion(raw) {
@@ -52635,7 +52882,7 @@ function parseClaudeCliProbeOutput(r) {
52635
52882
  return { state: "read", bin, raw };
52636
52883
  }
52637
52884
  function probeAgentClaudeCli(agentName) {
52638
- const r = spawnSync12("docker", ["exec", `switchroom-${agentName}`, "sh", "-c", buildClaudeCliProbeScript()], { stdio: "pipe", timeout: 3000 });
52885
+ const r = spawnSync13("docker", ["exec", `switchroom-${agentName}`, "sh", "-c", buildClaudeCliProbeScript()], { stdio: "pipe", timeout: 3000 });
52639
52886
  return parseClaudeCliProbeOutput(r);
52640
52887
  }
52641
52888
  function assessClaudeCliFloor(input) {
@@ -52758,7 +53005,7 @@ __export(exports_doctor, {
52758
53005
  PENDING_RETAINS_EVICTION_WINDOW_DAYS: () => PENDING_RETAINS_EVICTION_WINDOW_DAYS,
52759
53006
  MFF_VAULT_KEY: () => MFF_VAULT_KEY
52760
53007
  });
52761
- import { spawnSync as spawnSync13 } from "node:child_process";
53008
+ import { spawnSync as spawnSync14 } from "node:child_process";
52762
53009
  import { Socket as Socket3 } from "node:net";
52763
53010
  import {
52764
53011
  accessSync as accessSync3,
@@ -52904,7 +53151,7 @@ function readVersion(bin, parser) {
52904
53151
  const path7 = which(bin);
52905
53152
  if (!path7)
52906
53153
  return null;
52907
- const proc = spawnSync13(path7, ["--version"], {
53154
+ const proc = spawnSync14(path7, ["--version"], {
52908
53155
  stdio: ["ignore", "pipe", "pipe"]
52909
53156
  });
52910
53157
  if (proc.error || proc.status !== 0)
@@ -53196,7 +53443,7 @@ function probeVaultBrokerSocketPair(agentName) {
53196
53443
  const dataPath = `/run/switchroom/broker/${agentName}/sock`;
53197
53444
  const unlockPath = `/run/switchroom/broker/${agentName}/unlock`;
53198
53445
  const script = `D=0; U=0; ` + `test -S '${dataPath}' && D=1; ` + `test -S '${unlockPath}' && U=1; ` + `echo "D=$D U=$U"`;
53199
- const r = spawnSync13("docker", ["exec", "switchroom-vault-broker", "sh", "-c", script], { stdio: "pipe", timeout: 3000 });
53446
+ const r = spawnSync14("docker", ["exec", "switchroom-vault-broker", "sh", "-c", script], { stdio: "pipe", timeout: 3000 });
53200
53447
  if (r.error || r.status === null)
53201
53448
  return "unreachable";
53202
53449
  if (r.status !== 0) {
@@ -53446,7 +53693,7 @@ function checkHindsightConsumer(config, opts) {
53446
53693
  }
53447
53694
  function probeAuthBrokerSocket(consumerName) {
53448
53695
  const containerPath = `/run/switchroom/auth-broker/${consumerName}/sock`;
53449
- const r = spawnSync13("docker", ["exec", "switchroom-auth-broker", "test", "-S", containerPath], { stdio: "pipe", timeout: 3000 });
53696
+ const r = spawnSync14("docker", ["exec", "switchroom-auth-broker", "test", "-S", containerPath], { stdio: "pipe", timeout: 3000 });
53450
53697
  if (r.error || r.status === null)
53451
53698
  return "unreachable";
53452
53699
  if (r.status === 0)
@@ -53697,7 +53944,7 @@ async function checkHindsight(config) {
53697
53944
  function probePendingRetainsQueue(agentName) {
53698
53945
  const base = "/state/agent/home/.hindsight";
53699
53946
  const script = buildPendingRetainsProbeScript(base);
53700
- const r = spawnSync13("docker", ["exec", `switchroom-${agentName}`, "sh", "-c", script], { stdio: "pipe", timeout: 3000 });
53947
+ const r = spawnSync14("docker", ["exec", `switchroom-${agentName}`, "sh", "-c", script], { stdio: "pipe", timeout: 3000 });
53701
53948
  return parsePendingRetainsProbeOutput(r);
53702
53949
  }
53703
53950
  function buildPendingRetainsProbeScript(base, now = Date.now()) {
@@ -54564,7 +54811,7 @@ async function checkMffAuthFlow(envPath = mffEnvPath(), timeoutMs = 8000) {
54564
54811
  const python3 = which("python3") ?? "python3";
54565
54812
  let token;
54566
54813
  try {
54567
- const result = spawnSync13(python3, [authScript, "--quiet"], {
54814
+ const result = spawnSync14(python3, [authScript, "--quiet"], {
54568
54815
  timeout: timeoutMs,
54569
54816
  encoding: "utf-8",
54570
54817
  env: { ...process.env, ...env2 }
@@ -54993,6 +55240,22 @@ function registerDoctorCommand(program3) {
54993
55240
  })
54994
55241
  },
54995
55242
  { title: "Cron Session", results: runCronSessionChecks(config) },
55243
+ {
55244
+ title: "Component versions (#3919)",
55245
+ results: (() => {
55246
+ try {
55247
+ return runComponentVersionChecks(config);
55248
+ } catch (err) {
55249
+ return [
55250
+ {
55251
+ name: "component versions",
55252
+ status: "skip",
55253
+ detail: `not checkable: ${err?.message ?? String(err)}`
55254
+ }
55255
+ ];
55256
+ }
55257
+ })()
55258
+ },
54996
55259
  {
54997
55260
  title: "Generated-surface drift (KEN-130)",
54998
55261
  results: await runGeneratedSurfaceDriftChecks(config, configPath, {
@@ -55080,6 +55343,7 @@ var init_doctor = __esm(() => {
55080
55343
  init_doctor_cron_session();
55081
55344
  init_doctor_flood_pressure();
55082
55345
  init_doctor_drift();
55346
+ init_doctor_component_versions();
55083
55347
  init_doctor_microsoft();
55084
55348
  init_doctor_notion();
55085
55349
  init_doctor_credentials_migration();
@@ -68028,9 +68292,9 @@ __export(exports_server, {
68028
68292
  dispatchTool: () => dispatchTool,
68029
68293
  TOOLS: () => TOOLS
68030
68294
  });
68031
- import { spawnSync as spawnSync20 } from "node:child_process";
68295
+ import { spawnSync as spawnSync22 } from "node:child_process";
68032
68296
  function execCli(args, stdin) {
68033
- const r = spawnSync20(CLI_BIN, args, {
68297
+ const r = spawnSync22(CLI_BIN, args, {
68034
68298
  encoding: "utf-8",
68035
68299
  env: process.env,
68036
68300
  timeout: 15000,
@@ -68539,7 +68803,7 @@ __export(exports_server2, {
68539
68803
  TOOLS: () => TOOLS2
68540
68804
  });
68541
68805
  import { randomBytes as randomBytes16 } from "node:crypto";
68542
- import { existsSync as existsSync104 } from "node:fs";
68806
+ import { existsSync as existsSync105 } from "node:fs";
68543
68807
  function selfSocketPath() {
68544
68808
  return `/run/switchroom/hostd/${SELF_AGENT}/sock`;
68545
68809
  }
@@ -68566,7 +68830,7 @@ async function dispatchTool2(name, args) {
68566
68830
  return errorText2("hostd MCP: SWITCHROOM_AGENT_NAME env var is not set \u2014 cannot " + "determine which per-agent socket to talk to.");
68567
68831
  }
68568
68832
  const sockPath = selfSocketPath();
68569
- if (!existsSync104(sockPath)) {
68833
+ if (!existsSync105(sockPath)) {
68570
68834
  return errorText2(`hostd MCP: socket not bound at ${sockPath}. The host-control ` + `daemon is either not installed (run \`switchroom hostd install\`) ` + `or this agent isn't admin-flagged in switchroom.yaml. RFC C ` + `bind-mounts the per-agent socket only when host_control.enabled ` + `is true AND the agent has admin: true.`);
68571
68835
  }
68572
68836
  let req;
@@ -68817,13 +69081,13 @@ function resolveAuditLogPath() {
68817
69081
  if (process.env.HOSTD_AUDIT_LOG_PATH)
68818
69082
  return process.env.HOSTD_AUDIT_LOG_PATH;
68819
69083
  const bindMounted = "/host-home/.switchroom/host-control-audit.log";
68820
- if (existsSync104(bindMounted))
69084
+ if (existsSync105(bindMounted))
68821
69085
  return bindMounted;
68822
69086
  return defaultAuditLogPath2();
68823
69087
  }
68824
69088
  function getLastUpdateApplyStatus() {
68825
69089
  const path10 = resolveAuditLogPath();
68826
- if (!existsSync104(path10)) {
69090
+ if (!existsSync105(path10)) {
68827
69091
  return errorText2(`get_status: audit log not found at ${path10}. No update_apply has run yet?`);
68828
69092
  }
68829
69093
  let raw;
@@ -69542,11 +69806,11 @@ var init_test_agents = __esm(() => {
69542
69806
  });
69543
69807
 
69544
69808
  // src/litellm/header-passthrough-guard.ts
69545
- import { existsSync as existsSync107, readdirSync as readdirSync41 } from "node:fs";
69809
+ import { existsSync as existsSync108, readdirSync as readdirSync41 } from "node:fs";
69546
69810
  function discoverLiveLitellmConfigPath(opts) {
69547
69811
  const servicesDir = opts?.servicesDir ?? COOLIFY_SERVICES_DIR;
69548
69812
  const readdir2 = opts?.readdirFn ?? ((p) => readdirSync41(p));
69549
- const exists = opts?.existsFn ?? existsSync107;
69813
+ const exists = opts?.existsFn ?? existsSync108;
69550
69814
  let entries;
69551
69815
  try {
69552
69816
  entries = readdir2(servicesDir);
@@ -69636,7 +69900,7 @@ var init_header_passthrough_guard = __esm(() => {
69636
69900
  });
69637
69901
 
69638
69902
  // src/fleet-health/litellm-config-sensor.ts
69639
- import { readFileSync as readFileSync96, existsSync as existsSync108 } from "node:fs";
69903
+ import { readFileSync as readFileSync96, existsSync as existsSync109 } from "node:fs";
69640
69904
  function resolveLitellmConfigPath(explicit, discoverFn = () => discoverLiveLitellmConfigPath()) {
69641
69905
  if (explicit)
69642
69906
  return explicit;
@@ -69647,7 +69911,7 @@ function resolveLitellmConfigPath(explicit, discoverFn = () => discoverLiveLitel
69647
69911
  }
69648
69912
  function scanLitellmConfig(opts = {}) {
69649
69913
  const path10 = resolveLitellmConfigPath(opts.path, opts.discoverFn);
69650
- const exists = opts.existsFn ?? existsSync108;
69914
+ const exists = opts.existsFn ?? existsSync109;
69651
69915
  const read = opts.readFn ?? ((p) => readFileSync96(p, "utf-8"));
69652
69916
  const log = opts.log ?? (() => {});
69653
69917
  const nowIso = opts.nowIso ?? new Date().toISOString();
@@ -69725,18 +69989,18 @@ __export(exports_scan, {
69725
69989
  import {
69726
69990
  readFileSync as readFileSync97,
69727
69991
  readdirSync as readdirSync42,
69728
- existsSync as existsSync109,
69729
- mkdirSync as mkdirSync62,
69992
+ existsSync as existsSync110,
69993
+ mkdirSync as mkdirSync63,
69730
69994
  writeFileSync as writeFileSync46
69731
69995
  } from "node:fs";
69732
- import { resolve as resolve61, dirname as dirname41 } from "node:path";
69996
+ import { resolve as resolve61, dirname as dirname42 } from "node:path";
69733
69997
  import { homedir as homedir60 } from "node:os";
69734
69998
  function resolveSwitchroomBase(home2 = process.env.SWITCHROOM_HOME ?? process.env.HOME ?? homedir60()) {
69735
69999
  return resolve61(home2, ".switchroom");
69736
70000
  }
69737
70001
  function listAgents(base) {
69738
70002
  const dir = resolve61(base, "agents");
69739
- if (!existsSync109(dir))
70003
+ if (!existsSync110(dir))
69740
70004
  return [];
69741
70005
  try {
69742
70006
  return readdirSync42(dir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name).sort();
@@ -69765,7 +70029,7 @@ function runScan(opts = {}) {
69765
70029
  let gwText = "";
69766
70030
  let sawArtifact = false;
69767
70031
  try {
69768
- if (existsSync109(turnsPath)) {
70032
+ if (existsSync110(turnsPath)) {
69769
70033
  turnsText = readFileSync97(turnsPath, "utf-8");
69770
70034
  sawArtifact = true;
69771
70035
  }
@@ -69773,7 +70037,7 @@ function runScan(opts = {}) {
69773
70037
  log(`fleet-health: WARN skipping ${agent} turns.jsonl unreadable: ${String(e)}`);
69774
70038
  }
69775
70039
  try {
69776
- if (existsSync109(gwPath)) {
70040
+ if (existsSync110(gwPath)) {
69777
70041
  gwText = readFileSync97(gwPath, "utf-8");
69778
70042
  sawArtifact = true;
69779
70043
  }
@@ -69816,7 +70080,7 @@ function runScan(opts = {}) {
69816
70080
  function readLedgerIfPresent(base) {
69817
70081
  const path10 = ledgerPathForBase(base);
69818
70082
  try {
69819
- if (!existsSync109(path10))
70083
+ if (!existsSync110(path10))
69820
70084
  return null;
69821
70085
  return JSON.parse(readFileSync97(path10, "utf-8"));
69822
70086
  } catch {
@@ -69824,11 +70088,11 @@ function readLedgerIfPresent(base) {
69824
70088
  }
69825
70089
  }
69826
70090
  function ledgerPathForBase(base) {
69827
- return fleetHealthLedgerPath(dirname41(base));
70091
+ return fleetHealthLedgerPath(dirname42(base));
69828
70092
  }
69829
70093
  function writeLedger(base, ledger) {
69830
70094
  const path10 = ledgerPathForBase(base);
69831
- mkdirSync62(dirname41(path10), { recursive: true });
70095
+ mkdirSync63(dirname42(path10), { recursive: true });
69832
70096
  writeFileSync46(path10, JSON.stringify(ledger, null, 2) + `
69833
70097
  `, "utf-8");
69834
70098
  return path10;
@@ -70195,8 +70459,10 @@ import { join as join19 } from "node:path";
70195
70459
  import { execFileSync as execFileSync11 } from "node:child_process";
70196
70460
 
70197
70461
  // src/agents/handoff-summarizer.ts
70462
+ init_observation_scopes();
70198
70463
  import { readFileSync as readFileSync19, writeFileSync as writeFileSync9, renameSync as renameSync7, mkdirSync as mkdirSync16, existsSync as existsSync23, statSync as statSync11, readdirSync as readdirSync10 } from "node:fs";
70199
70464
  import { join as join18 } from "node:path";
70465
+ var HANDOFF_STATUS_MIRROR_SKIPPED = "mirror-skipped-invalid-scope";
70200
70466
  var DEFAULT_MAX_TURNS = 50;
70201
70467
  var TOPIC_MAX_CHARS = 117;
70202
70468
  var TURN_TEXT_MAX_CHARS = 1200;
@@ -70355,8 +70621,8 @@ async function buildHandoff(opts) {
70355
70621
  `);
70356
70622
  return "write-error";
70357
70623
  }
70358
- await mirrorToHindsight(briefing, opts).catch(() => {});
70359
- return "ok";
70624
+ const mirrored = await mirrorToHindsight(briefing, opts).catch(() => true);
70625
+ return mirrored ? "ok" : HANDOFF_STATUS_MIRROR_SKIPPED;
70360
70626
  }
70361
70627
  function errMsg(err) {
70362
70628
  if (err && typeof err === "object" && "message" in err) {
@@ -70368,15 +70634,22 @@ async function mirrorToHindsight(briefing, opts) {
70368
70634
  const url = opts.hindsightUrl ?? process.env.HINDSIGHT_API_URL;
70369
70635
  const bankId = opts.hindsightBankId ?? process.env.HINDSIGHT_BANK_ID ?? "default";
70370
70636
  if (!url)
70371
- return;
70637
+ return true;
70372
70638
  const fetchFn = opts.fetch ?? fetch;
70373
70639
  const endpoint = `${url.replace(/\/$/, "")}/v1/default/banks/${encodeURIComponent(bankId)}/memories`;
70640
+ const scope = resolveObservationScope(opts.observationScopes, opts.env ?? process.env);
70641
+ if (scope.kind === "invalid") {
70642
+ process.stderr.write(`handoff: hindsight mirror SKIPPED \u2014 observation scope ` + `${JSON.stringify(scope.raw)} is not valid ` + `(accepted: ${observationScopesHint()}). Fix ` + `memory.observation_scopes in switchroom.yaml (or the ` + `${OBSERVATION_SCOPES_ENV} export), then \`switchroom apply\` and ` + `restart the agent. The on-disk handoff sidecars were written ` + `normally; only the recallable Hindsight copy was skipped.
70643
+ `);
70644
+ return false;
70645
+ }
70374
70646
  const body = {
70375
70647
  items: [
70376
70648
  {
70377
70649
  content: briefing,
70378
70650
  document_id: "session_handoff",
70379
- tags: ["session_handoff", opts.agentName]
70651
+ tags: ["session_handoff", opts.agentName],
70652
+ ...scope.kind === "set" ? { observation_scopes: scope.scope } : {}
70380
70653
  }
70381
70654
  ],
70382
70655
  async: true
@@ -70391,6 +70664,7 @@ async function mirrorToHindsight(briefing, opts) {
70391
70664
  process.stderr.write(`handoff: hindsight mirror failed \u2014 ${errMsg(err)}
70392
70665
  `);
70393
70666
  }
70667
+ return true;
70394
70668
  }
70395
70669
  function findLatestSessionJsonl(claudeConfigDir) {
70396
70670
  const projects = join18(claudeConfigDir, "projects");
@@ -90529,9 +90803,9 @@ init_source();
90529
90803
  init_loader();
90530
90804
  init_lifecycle();
90531
90805
  init_compose_env();
90532
- import { existsSync as existsSync79, mkdirSync as mkdirSync44, readFileSync as readFileSync73, realpathSync as realpathSync6, statSync as statSync45, chownSync as chownSync8 } from "node:fs";
90533
- import { spawnSync as spawnSync14 } from "node:child_process";
90534
- import { join as join83, dirname as dirname32, resolve as resolve45 } from "node:path";
90806
+ import { existsSync as existsSync80, mkdirSync as mkdirSync45, readFileSync as readFileSync73, realpathSync as realpathSync6, statSync as statSync45, chownSync as chownSync8 } from "node:fs";
90807
+ import { spawnSync as spawnSync16 } from "node:child_process";
90808
+ import { join as join83, dirname as dirname33, resolve as resolve45 } from "node:path";
90535
90809
  import { homedir as homedir43 } from "node:os";
90536
90810
 
90537
90811
  // src/cli/release-yaml.ts
@@ -90779,6 +91053,286 @@ function syncBundledSkills(opts) {
90779
91053
 
90780
91054
  // src/cli/update.ts
90781
91055
  init_resolve_version();
91056
+
91057
+ // src/cli/self-update.ts
91058
+ var SELF_UPDATE_ENV_SENTINEL = "SWITCHROOM_SELF_UPDATED";
91059
+ var VERSION_STORE_DIRNAME = ".switchroom-versions";
91060
+ var GITHUB_LATEST_RELEASE_URL = "https://api.github.com/repos/switchroom/switchroom/releases/latest";
91061
+ function detectInstallKind(p) {
91062
+ if (p.inContainer) {
91063
+ return {
91064
+ kind: "container",
91065
+ reason: "running inside a switchroom container \u2014 the CLI here comes from the " + "container image and is updated by pulling a new image, not by " + "replacing a file. Nothing to self-update."
91066
+ };
91067
+ }
91068
+ if (p.bundleDir.startsWith("/$bunfs")) {
91069
+ if (!p.execPath) {
91070
+ return {
91071
+ kind: "unknown",
91072
+ reason: "compiled binary but process.execPath is empty \u2014 cannot locate the " + "file to replace. Re-run install.sh to upgrade."
91073
+ };
91074
+ }
91075
+ return {
91076
+ kind: "static-binary",
91077
+ binaryPath: p.execPath,
91078
+ reason: `published static binary at ${p.execPath}`
91079
+ };
91080
+ }
91081
+ if (/[/\\]node_modules[/\\]switchroom[/\\]/.test(p.scriptPath)) {
91082
+ return {
91083
+ kind: "npm-global",
91084
+ reason: "installed via npm \u2014 self-update does not manage npm installs. " + "Upgrade with `npm i -g switchroom@latest`."
91085
+ };
91086
+ }
91087
+ if (isSwitchroomSourceCheckoutPath(p.scriptPath)) {
91088
+ return {
91089
+ kind: "source-checkout",
91090
+ reason: "running from a switchroom source checkout \u2014 self-update would clobber " + "your working tree. Upgrade with `git pull && bun install && npm run build`."
91091
+ };
91092
+ }
91093
+ return {
91094
+ kind: "unknown",
91095
+ reason: `could not identify how switchroom was installed (running from ` + `"${p.scriptPath}"). Not touching it. Upgrade with the method you used ` + `to install, or re-run install.sh.`
91096
+ };
91097
+ }
91098
+ function isSwitchroomSourceCheckoutPath(scriptPath) {
91099
+ return /[/\\](src|dist)[/\\]cli[/\\]switchroom(\.js|\.ts)?$/.test(scriptPath);
91100
+ }
91101
+ function releaseAssetName(platform, arch) {
91102
+ const os6 = platform === "linux" ? "linux" : platform === "darwin" ? "macos" : null;
91103
+ const cpu = arch === "x64" ? "amd64" : arch === "arm64" ? "arm64" : null;
91104
+ if (!os6 || !cpu)
91105
+ return null;
91106
+ return `switchroom-${os6}-${cpu}`;
91107
+ }
91108
+ function parseLatestReleaseTag(body) {
91109
+ try {
91110
+ const parsed = JSON.parse(body);
91111
+ if (parsed?.draft === true)
91112
+ return null;
91113
+ const tag = parsed?.tag_name;
91114
+ if (typeof tag !== "string" || !/^v\d+\.\d+\.\d+$/.test(tag))
91115
+ return null;
91116
+ return tag;
91117
+ } catch {
91118
+ return null;
91119
+ }
91120
+ }
91121
+ function expectedChecksum(checksumsText, asset) {
91122
+ for (const line of checksumsText.split(`
91123
+ `)) {
91124
+ const idx = line.indexOf(` ${asset}`);
91125
+ if (idx <= 0)
91126
+ continue;
91127
+ if (line.slice(idx + 2).trim() !== asset)
91128
+ continue;
91129
+ const hash2 = line.slice(0, idx).trim();
91130
+ if (/^[0-9a-f]{64}$/i.test(hash2))
91131
+ return hash2.toLowerCase();
91132
+ }
91133
+ return null;
91134
+ }
91135
+ function planSelfUpdate(opts) {
91136
+ const { detection, currentVersion, latestTag } = opts;
91137
+ if (detection.kind !== "static-binary" || !detection.binaryPath) {
91138
+ return { action: "skip", reason: detection.reason };
91139
+ }
91140
+ if (!latestTag) {
91141
+ return {
91142
+ action: "skip",
91143
+ reason: "could not resolve the latest published release from GitHub " + "(offline, rate-limited, or the API changed shape) \u2014 leaving the CLI as-is."
91144
+ };
91145
+ }
91146
+ const current = `v${currentVersion.trim().replace(/^v/, "")}`;
91147
+ const cmp = compareReleaseTags(current, latestTag);
91148
+ if (cmp === null) {
91149
+ return {
91150
+ action: "skip",
91151
+ reason: `local CLI version "${currentVersion}" is not a comparable semver \u2014 ` + `refusing to guess whether ${latestTag} is an upgrade.`
91152
+ };
91153
+ }
91154
+ if (cmp >= 0)
91155
+ return { action: "current", version: current };
91156
+ return {
91157
+ action: "update",
91158
+ from: current,
91159
+ to: latestTag,
91160
+ binaryPath: detection.binaryPath
91161
+ };
91162
+ }
91163
+ function versionStorePath(installDir, version2, sep4 = "/") {
91164
+ return `${installDir}${sep4}${VERSION_STORE_DIRNAME}${sep4}switchroom-${version2.replace(/^v/, "")}`;
91165
+ }
91166
+ function rollbackHint(installDir, previousVersion) {
91167
+ return `Rollback: cp ${versionStorePath(installDir, previousVersion)} ` + `${installDir}/switchroom`;
91168
+ }
91169
+ function alreadySelfUpdated(env2) {
91170
+ return env2[SELF_UPDATE_ENV_SENTINEL] === "1";
91171
+ }
91172
+ async function fetchLatestReleaseTag(io) {
91173
+ try {
91174
+ return parseLatestReleaseTag(await io.httpGetText(GITHUB_LATEST_RELEASE_URL));
91175
+ } catch {
91176
+ return null;
91177
+ }
91178
+ }
91179
+ async function performSelfUpdate(opts) {
91180
+ const { plan, assetName, io } = opts;
91181
+ const log = opts.log ?? (() => {});
91182
+ const installDir = io.dirname(plan.binaryPath);
91183
+ const store = `${installDir}/${VERSION_STORE_DIRNAME}`;
91184
+ const base = `https://github.com/switchroom/switchroom/releases/download/${plan.to}`;
91185
+ const staged = versionStorePath(installDir, plan.to);
91186
+ const tmp = `${staged}.download`;
91187
+ io.mkdirp(store);
91188
+ io.remove(tmp);
91189
+ log(`downloading ${assetName} ${plan.to}`);
91190
+ try {
91191
+ await io.httpDownload(`${base}/${assetName}`, tmp);
91192
+ } catch (err) {
91193
+ io.remove(tmp);
91194
+ throw new Error(`self-update: download of ${base}/${assetName} failed (${err.message}). ` + `The installed CLI is unchanged.`);
91195
+ }
91196
+ let checksums;
91197
+ try {
91198
+ checksums = await io.httpGetText(`${base}/switchroom-checksums.txt`);
91199
+ } catch (err) {
91200
+ io.remove(tmp);
91201
+ throw new Error(`self-update: could not fetch switchroom-checksums.txt for ${plan.to} ` + `(${err.message}) \u2014 refusing to install an unverified binary. ` + `The installed CLI is unchanged.`);
91202
+ }
91203
+ const expected = expectedChecksum(checksums, assetName);
91204
+ if (!expected) {
91205
+ io.remove(tmp);
91206
+ throw new Error(`self-update: release ${plan.to} has no checksum entry for ${assetName} \u2014 ` + `refusing to install an unverified binary. The installed CLI is unchanged.`);
91207
+ }
91208
+ const actual = io.sha256File(tmp).toLowerCase();
91209
+ if (actual !== expected) {
91210
+ io.remove(tmp);
91211
+ throw new Error(`self-update: SHA256 mismatch for ${assetName} (expected ${expected}, got ${actual}) \u2014 ` + `refusing to install. The installed CLI is unchanged.`);
91212
+ }
91213
+ io.chmodExec(tmp);
91214
+ const proved = io.probeBinaryVersion(tmp);
91215
+ if (!proved) {
91216
+ io.remove(tmp);
91217
+ throw new Error(`self-update: the downloaded ${plan.to} binary did not run cleanly on this host ` + `(\`switchroom version\` failed) \u2014 refusing to install it. The installed CLI is unchanged.`);
91218
+ }
91219
+ const previousArchive = versionStorePath(installDir, plan.from);
91220
+ try {
91221
+ if (!io.exists(previousArchive))
91222
+ io.copyFile(plan.binaryPath, previousArchive);
91223
+ } catch (err) {
91224
+ io.remove(tmp);
91225
+ throw new Error(`self-update: could not archive the current binary to ${previousArchive} ` + `(${err.message}) \u2014 refusing to swap without a rollback copy. ` + `The installed CLI is unchanged.`);
91226
+ }
91227
+ io.rename(tmp, staged);
91228
+ io.copyFile(staged, `${plan.binaryPath}.new`);
91229
+ io.chmodExec(`${plan.binaryPath}.new`);
91230
+ io.rename(`${plan.binaryPath}.new`, plan.binaryPath);
91231
+ log(rollbackHint(installDir, plan.from));
91232
+ return {
91233
+ replaced: true,
91234
+ newVersion: plan.to,
91235
+ binaryPath: plan.binaryPath,
91236
+ message: `host CLI ${plan.from} \u2192 ${plan.to} (verified sha256, ran clean). ` + rollbackHint(installDir, plan.from)
91237
+ };
91238
+ }
91239
+
91240
+ // src/cli/self-update-io.ts
91241
+ import { createHash as createHash16 } from "node:crypto";
91242
+ import {
91243
+ chmodSync as chmodSync15,
91244
+ closeSync as closeSync15,
91245
+ copyFileSync as copyFileSync11,
91246
+ createWriteStream,
91247
+ existsSync as existsSync79,
91248
+ mkdirSync as mkdirSync44,
91249
+ openSync as openSync15,
91250
+ readSync as readSync5,
91251
+ renameSync as renameSync19,
91252
+ rmSync as rmSync14
91253
+ } from "node:fs";
91254
+ import { dirname as dirname32 } from "node:path";
91255
+ import { Readable } from "node:stream";
91256
+ import { pipeline } from "node:stream/promises";
91257
+ import { spawnSync as spawnSync15 } from "node:child_process";
91258
+ var HTTP_TIMEOUT_MS = 60000;
91259
+ function defaultSelfUpdateIO() {
91260
+ return {
91261
+ async httpGetText(url) {
91262
+ const res = await fetch(url, {
91263
+ headers: { "user-agent": "switchroom-cli-self-update" },
91264
+ signal: AbortSignal.timeout(HTTP_TIMEOUT_MS)
91265
+ });
91266
+ if (!res.ok)
91267
+ throw new Error(`HTTP ${res.status} for ${url}`);
91268
+ return await res.text();
91269
+ },
91270
+ async httpDownload(url, dest) {
91271
+ const res = await fetch(url, {
91272
+ headers: { "user-agent": "switchroom-cli-self-update" },
91273
+ redirect: "follow",
91274
+ signal: AbortSignal.timeout(HTTP_TIMEOUT_MS)
91275
+ });
91276
+ if (!res.ok)
91277
+ throw new Error(`HTTP ${res.status} for ${url}`);
91278
+ if (!res.body)
91279
+ throw new Error(`empty response body for ${url}`);
91280
+ await pipeline(Readable.fromWeb(res.body), createWriteStream(dest));
91281
+ },
91282
+ sha256File(path7) {
91283
+ const hash2 = createHash16("sha256");
91284
+ const fd = openSync15(path7, "r");
91285
+ try {
91286
+ const buf = Buffer.allocUnsafe(1 << 20);
91287
+ for (;; ) {
91288
+ const n = readSync5(fd, buf, 0, buf.length, null);
91289
+ if (n <= 0)
91290
+ break;
91291
+ hash2.update(buf.subarray(0, n));
91292
+ }
91293
+ } finally {
91294
+ closeSync15(fd);
91295
+ }
91296
+ return hash2.digest("hex");
91297
+ },
91298
+ probeBinaryVersion(path7) {
91299
+ const r = spawnSync15(path7, ["version"], {
91300
+ encoding: "utf-8",
91301
+ timeout: 30000,
91302
+ env: { ...process.env, SWITCHROOM_SELF_UPDATED: "" }
91303
+ });
91304
+ if (r.status !== 0)
91305
+ return null;
91306
+ const out = `${r.stdout ?? ""}`.trim();
91307
+ const m = out.match(/\d+\.\d+\.\d+/);
91308
+ return m ? m[0] : null;
91309
+ },
91310
+ mkdirp(dir) {
91311
+ mkdirSync44(dir, { recursive: true });
91312
+ },
91313
+ copyFile(src, dest) {
91314
+ copyFileSync11(src, dest);
91315
+ },
91316
+ chmodExec(path7) {
91317
+ chmodSync15(path7, 493);
91318
+ },
91319
+ rename(src, dest) {
91320
+ renameSync19(src, dest);
91321
+ },
91322
+ remove(path7) {
91323
+ rmSync14(path7, { force: true });
91324
+ },
91325
+ exists(path7) {
91326
+ return existsSync79(path7);
91327
+ },
91328
+ dirname(path7) {
91329
+ return dirname32(path7);
91330
+ }
91331
+ };
91332
+ }
91333
+
91334
+ // src/cli/update.ts
91335
+ init_component_versions();
90782
91336
  function defaultPersistPin(configPath) {
90783
91337
  return (pin) => {
90784
91338
  const path7 = configPath ?? findConfigFile();
@@ -90798,16 +91352,16 @@ function defaultPersistPin(configPath) {
90798
91352
  }
90799
91353
  var DEFAULT_COMPOSE_PATH2 = join83(homedir43(), ".switchroom", "compose", "docker-compose.yml");
90800
91354
  function runningFromSwitchroomCheckout(scriptPath) {
90801
- let dir = dirname32(scriptPath);
91355
+ let dir = dirname33(scriptPath);
90802
91356
  for (let i = 0;i < 12; i++) {
90803
- if (existsSync79(join83(dir, ".git"))) {
91357
+ if (existsSync80(join83(dir, ".git"))) {
90804
91358
  try {
90805
91359
  const pkg = JSON.parse(readFileSync73(join83(dir, "package.json"), "utf-8"));
90806
91360
  if (pkg.name === "switchroom")
90807
91361
  return true;
90808
91362
  } catch {}
90809
91363
  }
90810
- const parent = dirname32(dir);
91364
+ const parent = dirname33(dir);
90811
91365
  if (parent === dir)
90812
91366
  break;
90813
91367
  dir = parent;
@@ -90852,6 +91406,61 @@ function planUpdate(opts) {
90852
91406
  const scriptPath = opts.scriptPath ?? process.argv[1] ?? "";
90853
91407
  const steps = [];
90854
91408
  const hostdContext = typeof opts.hostdContext === "boolean" ? opts.hostdContext : isHostdContext();
91409
+ steps.push({
91410
+ name: "self-update-cli",
91411
+ description: "Replace the host operator CLI binary with the latest published release (checksum-verified, run-proved, atomic swap + versioned rollback copy), then re-exec the new binary for the remaining steps",
91412
+ skipReason: opts.skipSelfUpdate ? "--skip-self-update flag set" : alreadySelfUpdated(process.env) ? "already re-exec'd from a freshly installed binary in this run" : undefined,
91413
+ run: async () => {
91414
+ const say = opts.stdout ?? ((t) => process.stdout.write(t));
91415
+ const io = opts.selfUpdateIO ?? defaultSelfUpdateIO();
91416
+ const detection = detectInstallKind(opts.installProbe ?? {
91417
+ bundleDir: import.meta.dirname,
91418
+ execPath: process.execPath,
91419
+ scriptPath,
91420
+ inContainer: process.env.SWITCHROOM_HOSTD_CONTEXT === "1" || existsSync80("/.dockerenv")
91421
+ });
91422
+ if (detection.kind !== "static-binary") {
91423
+ say(source_default.gray(` host CLI not self-updated: ${detection.reason}
91424
+ `));
91425
+ return;
91426
+ }
91427
+ const latestTag = opts.latestReleaseTagFn ? await opts.latestReleaseTagFn() : await fetchLatestReleaseTag(io);
91428
+ const plan = planSelfUpdate({
91429
+ detection,
91430
+ currentVersion: SWITCHROOM_VERSION,
91431
+ latestTag
91432
+ });
91433
+ if (plan.action === "skip") {
91434
+ say(source_default.gray(` host CLI not self-updated: ${plan.reason}
91435
+ `));
91436
+ return;
91437
+ }
91438
+ if (plan.action === "current") {
91439
+ say(source_default.gray(` host CLI already on ${plan.version}
91440
+ `));
91441
+ return;
91442
+ }
91443
+ const asset = releaseAssetName(process.platform, process.arch);
91444
+ if (!asset) {
91445
+ say(source_default.gray(` host CLI not self-updated: no published binary for ${process.platform}/${process.arch}
91446
+ `));
91447
+ return;
91448
+ }
91449
+ const result = await performSelfUpdate({
91450
+ plan,
91451
+ assetName: asset,
91452
+ io,
91453
+ log: (s) => say(source_default.gray(` ${s}
91454
+ `))
91455
+ });
91456
+ if (result.replaced && opts.selfUpdateSink) {
91457
+ opts.selfUpdateSink.replacedWith = result.newVersion;
91458
+ opts.selfUpdateSink.binaryPath = result.binaryPath;
91459
+ }
91460
+ say(source_default.green(` ${result.message}
91461
+ `));
91462
+ }
91463
+ });
90855
91464
  if (opts.pin) {
90856
91465
  const pin = opts.pin;
90857
91466
  const persist = opts.persistPinFn ?? defaultPersistPin(opts.configPath);
@@ -90887,7 +91496,7 @@ function planUpdate(opts) {
90887
91496
  steps.push({
90888
91497
  name: "pull-images",
90889
91498
  description: "Pull broker / kernel / agent images from GHCR",
90890
- skipReason: opts.skipImages ? "--skip-images flag set" : !existsSync79(composePath) ? `compose file not found at ${composePath} (run \`switchroom apply --compose-only\` first)` : undefined,
91499
+ skipReason: opts.skipImages ? "--skip-images flag set" : !existsSync80(composePath) ? `compose file not found at ${composePath} (run \`switchroom apply --compose-only\` first)` : undefined,
90891
91500
  run: () => {
90892
91501
  const r = runner("docker", [
90893
91502
  "compose",
@@ -91014,13 +91623,13 @@ function planUpdate(opts) {
91014
91623
  }
91015
91624
  const source = resolve45(import.meta.dirname, "../../skills");
91016
91625
  const dest = join83(homedir43(), ".switchroom", "skills", "_bundled");
91017
- if (!existsSync79(source)) {
91626
+ if (!existsSync80(source)) {
91018
91627
  process.stderr.write(`switchroom update: sync-bundled-skills \u2014 CLI bundle has no adjacent skills/ at ${source}; skipping.
91019
91628
  `);
91020
91629
  return;
91021
91630
  }
91022
91631
  try {
91023
- mkdirSync44(dirname32(dest), { recursive: true });
91632
+ mkdirSync45(dirname33(dest), { recursive: true });
91024
91633
  const r = syncBundledSkills({
91025
91634
  source,
91026
91635
  dest,
@@ -91046,10 +91655,10 @@ function planUpdate(opts) {
91046
91655
  if (opts.syncBundledSkillsFn)
91047
91656
  return;
91048
91657
  const dest = join83(homedir43(), ".switchroom", "skills", "_bundled");
91049
- if (!existsSync79(dest)) {
91658
+ if (!existsSync80(dest)) {
91050
91659
  return;
91051
91660
  }
91052
- const missing = getBuiltinDefaultSkillEntries().map((e) => e.key).filter((key) => !existsSync79(join83(dest, key)));
91661
+ const missing = getBuiltinDefaultSkillEntries().map((e) => e.key).filter((key) => !existsSync80(join83(dest, key)));
91053
91662
  if (missing.length > 0) {
91054
91663
  throw new Error(`verify-bundled-skills: builtin default skill(s) missing from the pool after sync: ` + `${missing.join(", ")}. These ship in the CLI package and must exist in ${dest}. ` + `This is a broken sync or a packaging regression \u2014 the pool is not converged.`);
91055
91664
  }
@@ -91118,7 +91727,7 @@ function planUpdate(opts) {
91118
91727
  return steps;
91119
91728
  }
91120
91729
  function defaultRunner2(cmd, args) {
91121
- const r = spawnSync14(cmd, args, { stdio: "inherit" });
91730
+ const r = spawnSync16(cmd, args, { stdio: "inherit" });
91122
91731
  return { status: r.status ?? 1 };
91123
91732
  }
91124
91733
  function writeMarkerInPreferredLocation(agent, reason, runner) {
@@ -91161,10 +91770,10 @@ function defaultStatusProbe(composePath) {
91161
91770
  try {
91162
91771
  cliBuiltAt = new Date(statSync45(scriptPath).mtimeMs).toISOString();
91163
91772
  } catch {}
91164
- let dir = dirname32(scriptPath);
91773
+ let dir = dirname33(scriptPath);
91165
91774
  for (let i = 0;i < 8; i++) {
91166
91775
  const pkgPath = join83(dir, "package.json");
91167
- if (existsSync79(pkgPath)) {
91776
+ if (existsSync80(pkgPath)) {
91168
91777
  try {
91169
91778
  const pkg = JSON.parse(readFileSync73(pkgPath, "utf-8"));
91170
91779
  if (typeof pkg.version === "string")
@@ -91174,7 +91783,7 @@ function defaultStatusProbe(composePath) {
91174
91783
  }
91175
91784
  break;
91176
91785
  }
91177
- const parent = dirname32(dir);
91786
+ const parent = dirname33(dir);
91178
91787
  if (parent === dir)
91179
91788
  break;
91180
91789
  dir = parent;
@@ -91187,13 +91796,13 @@ function defaultStatusProbe(composePath) {
91187
91796
  warnings.push("could not resolve CLI version (no package.json found above the resolved script path)");
91188
91797
  }
91189
91798
  const services = [];
91190
- if (!existsSync79(composePath)) {
91799
+ if (!existsSync80(composePath)) {
91191
91800
  warnings.push(`compose file not found at ${composePath}; service status unknown`);
91192
91801
  return { cliVersion, cliBuiltAt, services, warnings };
91193
91802
  }
91194
91803
  let serviceList = [];
91195
91804
  try {
91196
- const r = spawnSync14("docker", ["compose", "-p", "switchroom", "-f", composePath, "config", "--services"], { encoding: "utf-8", timeout: 1e4 });
91805
+ const r = spawnSync16("docker", ["compose", "-p", "switchroom", "-f", composePath, "config", "--services"], { encoding: "utf-8", timeout: 1e4 });
91197
91806
  if (r.status !== 0) {
91198
91807
  warnings.push(`docker compose config --services failed: ${r.stderr?.trim() ?? r.error?.message ?? "unknown"}`);
91199
91808
  return { cliVersion, cliBuiltAt, services, warnings };
@@ -91210,7 +91819,7 @@ function defaultStatusProbe(composePath) {
91210
91819
  let containerCreatedAt = null;
91211
91820
  let status = "<unknown>";
91212
91821
  try {
91213
- const r = spawnSync14("docker", ["inspect", "-f", "{{.Config.Image}}|{{.Created}}|{{.State.Status}}", containerName2], { encoding: "utf-8", timeout: 5000 });
91822
+ const r = spawnSync16("docker", ["inspect", "-f", "{{.Config.Image}}|{{.Created}}|{{.State.Status}}", containerName2], { encoding: "utf-8", timeout: 5000 });
91214
91823
  if (r.status === 0) {
91215
91824
  const [img, created, st] = r.stdout.trim().split("|");
91216
91825
  image = img ?? null;
@@ -91226,7 +91835,7 @@ function defaultStatusProbe(composePath) {
91226
91835
  let imagePulledAt = null;
91227
91836
  if (image) {
91228
91837
  try {
91229
- const r = spawnSync14("docker", ["image", "inspect", "-f", "{{.Id}}|{{.Created}}|{{.Metadata.LastTagTime}}", image], { encoding: "utf-8", timeout: 5000 });
91838
+ const r = spawnSync16("docker", ["image", "inspect", "-f", "{{.Id}}|{{.Created}}|{{.Metadata.LastTagTime}}", image], { encoding: "utf-8", timeout: 5000 });
91230
91839
  if (r.status === 0) {
91231
91840
  const [id, created, lastTag] = r.stdout.trim().split("|");
91232
91841
  imageDigestShort = id?.replace(/^sha256:/, "").slice(0, 12) ?? null;
@@ -91290,6 +91899,47 @@ function formatStatusReport(rep) {
91290
91899
  return lines.join(`
91291
91900
  `);
91292
91901
  }
91902
+ function buildReexecArgs(opts) {
91903
+ const args = ["update", "--skip-self-update"];
91904
+ if (opts.skipImages)
91905
+ args.push("--skip-images");
91906
+ if (opts.channel)
91907
+ args.push("--channel", opts.channel);
91908
+ if (opts.pin)
91909
+ args.push("--pin", opts.pin);
91910
+ return args;
91911
+ }
91912
+ function defaultReexec(binaryPath, args) {
91913
+ const r = spawnSync16(binaryPath, args, {
91914
+ stdio: "inherit",
91915
+ env: { ...process.env, [SELF_UPDATE_ENV_SENTINEL]: "1" }
91916
+ });
91917
+ return { status: r.status ?? 1 };
91918
+ }
91919
+ function renderDriftPreamble(opts) {
91920
+ try {
91921
+ const exec = opts.componentExec ?? ((cmd, args) => {
91922
+ const r = spawnSync16(cmd, args, { encoding: "utf-8", timeout: 1e4 });
91923
+ return { status: r.status ?? 1, stdout: r.stdout ?? "" };
91924
+ });
91925
+ let pin;
91926
+ try {
91927
+ pin = loadConfig().release?.pin ?? undefined;
91928
+ } catch {
91929
+ pin = undefined;
91930
+ }
91931
+ const report = detectComponentDrift(collectComponents(SWITCHROOM_VERSION, exec), pin);
91932
+ const body = formatComponentDrift(report);
91933
+ return report.behind.length > 0 ? `${body}
91934
+
91935
+ ${report.behind.length} component(s) BEHIND ${report.target} \u2014 this update targets them.
91936
+ ` : `${body}
91937
+ `;
91938
+ } catch (err) {
91939
+ return `Component versions: not checkable (${err.message})
91940
+ `;
91941
+ }
91942
+ }
91293
91943
  async function runUpdate(opts) {
91294
91944
  const stdout = opts.stdout ?? ((s) => process.stdout.write(s));
91295
91945
  const stderr = opts.stderr ?? ((s) => process.stderr.write(s));
@@ -91319,11 +91969,14 @@ async function runUpdate(opts) {
91319
91969
  return 2;
91320
91970
  }
91321
91971
  }
91322
- const steps = planUpdate(opts);
91972
+ const selfUpdateSink = opts.selfUpdateSink ?? {};
91973
+ const steps = planUpdate({ ...opts, selfUpdateSink });
91323
91974
  if (opts.check) {
91324
91975
  stdout(source_default.bold(`switchroom update --check (dry-run)
91325
91976
 
91326
91977
  `));
91978
+ stdout(renderDriftPreamble(opts) + `
91979
+ `);
91327
91980
  for (const step of steps) {
91328
91981
  const status = step.skipReason ? source_default.gray(`[skip] ${step.skipReason}`) : source_default.green("[run]");
91329
91982
  stdout(` ${status} ${step.name} \u2014 ${step.description}
@@ -91343,7 +91996,15 @@ Dry-run only; nothing was changed. Re-run without --check to apply.
91343
91996
  stdout(source_default.bold(`\u25b8 ${step.name}
91344
91997
  `));
91345
91998
  try {
91346
- step.run();
91999
+ await step.run();
92000
+ if (step.name === "self-update-cli" && selfUpdateSink.binaryPath) {
92001
+ const reexec = opts.reexecFn ?? defaultReexec;
92002
+ stdout(source_default.bold(`
92003
+ \u25b8 re-exec ${selfUpdateSink.binaryPath} (${selfUpdateSink.replacedWith}) for the remaining steps
92004
+ `));
92005
+ const r = reexec(selfUpdateSink.binaryPath, buildReexecArgs(opts));
92006
+ return r.status;
92007
+ }
91347
92008
  } catch (err) {
91348
92009
  stderr(source_default.red(`\u2717 ${step.name} failed: ${err.message}
91349
92010
  `));
@@ -91366,7 +92027,7 @@ Dry-run only; nothing was changed. Re-run without --check to apply.
91366
92027
  return 0;
91367
92028
  }
91368
92029
  function registerUpdateCommand(program3) {
91369
- program3.command("update").description("Update switchroom on this host: pull images, refresh scaffolds, recreate containers. Wraps the full `pull && apply && up -d` flow.").option("--check", "Dry-run: print the steps that would execute, exit 0.").option("--skip-images", "Skip the docker image pull (offline mode).").option("--rebuild", "Source-checkout / maintainer only: git pull + bun install + npm run build before applying. REFUSED on a published install \u2014 use `npm i -g switchroom@latest && switchroom update` there.").option("--status", "Read-only snapshot: report local CLI version, image digest + pull time, container creation time per service. Does NOT invoke any update steps. Wired by Telegram /upgrade-status (#927).").option("--json", "Output as JSON (currently only honored under --status; other modes ignore).").addOption(new Option("--channel <c>", "Override the resolved release block for this update run: follow the named channel (dev|rc|latest). Mutually exclusive with --pin.").choices(["dev", "rc", "latest"]).conflicts("pin")).addOption(new Option("--pin <p>", "Override the resolved release block for this update run: pin to a specific build (sha-<7-40 hex> or v<semver>). Mutually exclusive with --channel.").conflicts("channel")).option("--force", "[legacy v0.6 no-op]").option("--no-restart", "[legacy v0.6 no-op]").option("--resume <file>", "[legacy v0.6 no-op]").option("--phase <phase>", "[legacy v0.6 no-op]").action(async (opts) => {
92030
+ program3.command("update").description("Update EVERY switchroom component on this host \u2014 the operator CLI itself (first), images, scaffolds, the hostd / web / hindsight singletons, and the agent fleet. Wraps the full `self-update && pull && apply && up -d` flow.").option("--check", "Dry-run: print the steps that would execute, exit 0.").option("--skip-images", "Skip the docker image pull (offline mode).").option("--skip-self-update", "Do NOT replace the host operator CLI binary. By default `update` updates every switchroom component INCLUDING itself (and does it first, because `apply` renders scaffolds from templates shipped inside the CLI).").option("--rebuild", "Source-checkout / maintainer only: git pull + bun install + npm run build before applying. REFUSED on a published install \u2014 use `npm i -g switchroom@latest && switchroom update` there.").option("--status", "Read-only snapshot: report local CLI version, image digest + pull time, container creation time per service. Does NOT invoke any update steps. Wired by Telegram /upgrade-status (#927).").option("--json", "Output as JSON (currently only honored under --status; other modes ignore).").addOption(new Option("--channel <c>", "Override the resolved release block for this update run: follow the named channel (dev|rc|latest). Mutually exclusive with --pin.").choices(["dev", "rc", "latest"]).conflicts("pin")).addOption(new Option("--pin <p>", "Override the resolved release block for this update run: pin to a specific build (sha-<7-40 hex> or v<semver>). Mutually exclusive with --channel.").conflicts("channel")).option("--force", "[legacy v0.6 no-op]").option("--no-restart", "[legacy v0.6 no-op]").option("--resume <file>", "[legacy v0.6 no-op]").option("--phase <phase>", "[legacy v0.6 no-op]").action(async (opts) => {
91370
92031
  if (opts.pin && !/^(sha-[0-9a-f]{7,40}|v\d+\.\d+\.\d+)$/.test(opts.pin)) {
91371
92032
  console.error(source_default.red(`--pin "${opts.pin}" is invalid. Expected sha-<7-40 hex> or v<semver>.`));
91372
92033
  process.exit(2);
@@ -91382,23 +92043,23 @@ function registerUpdateCommand(program3) {
91382
92043
 
91383
92044
  // src/cli/rollout.ts
91384
92045
  init_helpers();
91385
- import { spawnSync as spawnSync16 } from "node:child_process";
92046
+ import { spawnSync as spawnSync18 } from "node:child_process";
91386
92047
  import { readFileSync as readFileSync75, chownSync as chownSync9, statSync as statSync47 } from "node:fs";
91387
92048
  import { homedir as homedir45 } from "node:os";
91388
92049
 
91389
92050
  // src/cli/rollout-pin-journal.ts
91390
92051
  import {
91391
- existsSync as existsSync80,
92052
+ existsSync as existsSync81,
91392
92053
  readFileSync as readFileSync74,
91393
92054
  writeFileSync as writeFileSync31,
91394
- renameSync as renameSync19,
92055
+ renameSync as renameSync20,
91395
92056
  unlinkSync as unlinkSync15,
91396
92057
  statSync as statSync46,
91397
- mkdirSync as mkdirSync45
92058
+ mkdirSync as mkdirSync46
91398
92059
  } from "node:fs";
91399
92060
  import { homedir as homedir44 } from "node:os";
91400
- import { createHash as createHash16 } from "node:crypto";
91401
- import { join as join84, basename as basename9, resolve as resolve46, dirname as dirname33 } from "node:path";
92061
+ import { createHash as createHash17 } from "node:crypto";
92062
+ import { join as join84, basename as basename9, resolve as resolve46, dirname as dirname34 } from "node:path";
91402
92063
  init_flock();
91403
92064
  var PIN_JOURNAL_MAX_AGE_MS = 15 * 60 * 1000;
91404
92065
  var STATE_DIR_NAME = ".switchroom";
@@ -91407,7 +92068,7 @@ function pinJournalDir(configPath) {
91407
92068
  if (override && override.trim().length > 0)
91408
92069
  return override.trim();
91409
92070
  if (configPath) {
91410
- const dir = dirname33(resolve46(configPath));
92071
+ const dir = dirname34(resolve46(configPath));
91411
92072
  if (basename9(dir) === STATE_DIR_NAME)
91412
92073
  return dir;
91413
92074
  }
@@ -91415,7 +92076,7 @@ function pinJournalDir(configPath) {
91415
92076
  }
91416
92077
  function pinJournalPath(configPath) {
91417
92078
  const abs = resolve46(configPath);
91418
- const key = createHash16("sha256").update(abs).digest("hex").slice(0, 12);
92079
+ const key = createHash17("sha256").update(abs).digest("hex").slice(0, 12);
91419
92080
  return join84(pinJournalDir(abs), `.rollout-pin-journal.${basename9(abs)}.${key}.json`);
91420
92081
  }
91421
92082
  function isPidAlive(pid) {
@@ -91452,7 +92113,7 @@ function readPinJournal(configPath, warn = (m) => process.stderr.write(m)) {
91452
92113
  try {
91453
92114
  raw = readFileSync74(p, "utf8");
91454
92115
  } catch (e) {
91455
- if (existsSync80(p)) {
92116
+ if (existsSync81(p)) {
91456
92117
  warn(`\u26a0\ufe0f rollout pin journal: ${p} exists but could not be read ` + `(${e.message}). A provisional \`release.pin\` may be ` + `uncommitted \u2014 verify it host-side before the next reconcile.
91457
92118
  `);
91458
92119
  }
@@ -91512,10 +92173,10 @@ function beginPinPersist(configPath, pin, opts = {}) {
91512
92173
  pid: process.pid,
91513
92174
  at: new Date().toISOString()
91514
92175
  };
91515
- mkdirSync45(dirname33(p), { recursive: true });
92176
+ mkdirSync46(dirname34(p), { recursive: true });
91516
92177
  const tmp = `${p}.${process.pid}.tmp`;
91517
92178
  writeFileSync31(tmp, JSON.stringify(journal), { encoding: "utf8", mode: 384 });
91518
- renameSync19(tmp, p);
92179
+ renameSync20(tmp, p);
91519
92180
  return journal;
91520
92181
  }
91521
92182
  function commitPinPersist(configPath) {
@@ -91524,7 +92185,7 @@ function commitPinPersist(configPath) {
91524
92185
  unlinkSync15(p);
91525
92186
  return null;
91526
92187
  } catch (e) {
91527
- if (!existsSync80(p))
92188
+ if (!existsSync81(p))
91528
92189
  return null;
91529
92190
  return `rollout pin journal: FAILED to clear ${p} after a SUCCESSFUL roll ` + `(${e.message}). Delete it host-side \u2014 while it exists, ` + `recovery may revert a proven \`release.pin\`.`;
91530
92191
  }
@@ -91602,10 +92263,10 @@ init_audit_reader();
91602
92263
  init_hindsight();
91603
92264
 
91604
92265
  // src/cli/deploy-version-guard.ts
91605
- import { spawnSync as spawnSync15 } from "node:child_process";
92266
+ import { spawnSync as spawnSync17 } from "node:child_process";
91606
92267
  var DOCKER_INSPECT_TIMEOUT_MS = 60 * 1000;
91607
92268
  var defaultRunner3 = (args) => {
91608
- const r = spawnSync15("docker", args, {
92269
+ const r = spawnSync17("docker", args, {
91609
92270
  encoding: "utf8",
91610
92271
  timeout: DOCKER_INSPECT_TIMEOUT_MS,
91611
92272
  killSignal: "SIGKILL"
@@ -91979,7 +92640,7 @@ function isSpawnTimeout(r, killSignal) {
91979
92640
  var ROLLOUT_KILL_SIGNAL = "SIGKILL";
91980
92641
  function createRolloutDeps(params) {
91981
92642
  const { configPath, scriptPath, hostdCtx } = params;
91982
- const spawn6 = params.spawn ?? spawnSync16;
92643
+ const spawn6 = params.spawn ?? spawnSync18;
91983
92644
  const warn = params.warn ?? ((line) => process.stderr.write(line));
91984
92645
  const dockerRun = (args) => {
91985
92646
  let r;
@@ -92233,8 +92894,8 @@ init_helpers();
92233
92894
  init_lifecycle();
92234
92895
  init_resolve_version();
92235
92896
  import { execSync as execSync3 } from "node:child_process";
92236
- import { existsSync as existsSync81, readFileSync as readFileSync76 } from "node:fs";
92237
- import { dirname as dirname34, join as join85 } from "node:path";
92897
+ import { existsSync as existsSync82, readFileSync as readFileSync76 } from "node:fs";
92898
+ import { dirname as dirname35, join as join85 } from "node:path";
92238
92899
  function getClaudeCodeVersion() {
92239
92900
  try {
92240
92901
  const out = execSync3("claude --version 2>/dev/null", {
@@ -92285,15 +92946,15 @@ function locateSwitchroomInstallDir() {
92285
92946
  let dir = import.meta.dirname;
92286
92947
  for (let i = 0;i < 10 && dir && dir !== "/"; i++) {
92287
92948
  const pkgPath = join85(dir, "package.json");
92288
- if (existsSync81(pkgPath)) {
92949
+ if (existsSync82(pkgPath)) {
92289
92950
  try {
92290
92951
  const pkg = JSON.parse(readFileSync76(pkgPath, "utf-8"));
92291
- if (pkg.name === "switchroom" && existsSync81(join85(dir, ".git"))) {
92952
+ if (pkg.name === "switchroom" && existsSync82(join85(dir, ".git"))) {
92292
92953
  return dir;
92293
92954
  }
92294
92955
  } catch {}
92295
92956
  }
92296
- dir = dirname34(dir);
92957
+ dir = dirname35(dir);
92297
92958
  }
92298
92959
  return null;
92299
92960
  }
@@ -92463,10 +93124,11 @@ Dependency manifest`));
92463
93124
  init_helpers();
92464
93125
  init_loader();
92465
93126
  import { resolve as resolve48 } from "node:path";
93127
+ init_merge();
92466
93128
 
92467
93129
  // src/agents/session-retention.ts
92468
93130
  import {
92469
- existsSync as existsSync82,
93131
+ existsSync as existsSync83,
92470
93132
  readdirSync as readdirSync28,
92471
93133
  statSync as statSync48,
92472
93134
  unlinkSync as unlinkSync16
@@ -92477,7 +93139,7 @@ var DEFAULT_SESSION_RETENTION_MAX_AGE_DAYS = 30;
92477
93139
  var MIN_KEEP = 2;
92478
93140
  function collectSessionJsonl(claudeConfigDir) {
92479
93141
  const projects = join86(claudeConfigDir, "projects");
92480
- if (!existsSync82(projects))
93142
+ if (!existsSync83(projects))
92481
93143
  return [];
92482
93144
  const found = [];
92483
93145
  const walk2 = (dir) => {
@@ -92554,12 +93216,13 @@ function registerHandoffCommand(program3) {
92554
93216
  let agentDir;
92555
93217
  try {
92556
93218
  const config = getConfig(program3);
92557
- agentConfig = config.agents[agentName];
92558
- if (!agentConfig) {
93219
+ const rawAgent = config.agents[agentName];
93220
+ if (!rawAgent) {
92559
93221
  process.stderr.write(`handoff: agent "${agentName}" not defined in switchroom.yaml
92560
93222
  `);
92561
93223
  return;
92562
93224
  }
93225
+ agentConfig = resolveAgentConfig(config.defaults, config.profiles, rawAgent);
92563
93226
  const agentsDir = resolveAgentsDir(config);
92564
93227
  agentDir = resolve48(agentsDir, agentName);
92565
93228
  } catch (err) {
@@ -92589,10 +93252,14 @@ function registerHandoffCommand(program3) {
92589
93252
  jsonlPath: jsonl,
92590
93253
  agentDir,
92591
93254
  agentName,
92592
- maxTurns: cappedMaxTurns
93255
+ maxTurns: cappedMaxTurns,
93256
+ observationScopes: agentConfig?.memory?.observation_scopes
92593
93257
  });
92594
93258
  process.stderr.write(`handoff: ${status}
92595
93259
  `);
93260
+ if (status === HANDOFF_STATUS_MIRROR_SKIPPED) {
93261
+ process.exitCode = 1;
93262
+ }
92596
93263
  const retention = pruneSessionJsonl(claudeConfigDir, {
92597
93264
  maxCount: continuity?.session_retention_max_count ?? DEFAULT_SESSION_RETENTION_MAX_COUNT,
92598
93265
  maxAgeDays: continuity?.session_retention_max_age_days ?? DEFAULT_SESSION_RETENTION_MAX_AGE_DAYS,
@@ -92607,13 +93274,13 @@ function registerHandoffCommand(program3) {
92607
93274
 
92608
93275
  // src/issues/store.ts
92609
93276
  import {
92610
- closeSync as closeSync15,
92611
- existsSync as existsSync83,
92612
- mkdirSync as mkdirSync46,
92613
- openSync as openSync15,
93277
+ closeSync as closeSync16,
93278
+ existsSync as existsSync84,
93279
+ mkdirSync as mkdirSync47,
93280
+ openSync as openSync16,
92614
93281
  readdirSync as readdirSync29,
92615
93282
  readFileSync as readFileSync77,
92616
- renameSync as renameSync20,
93283
+ renameSync as renameSync21,
92617
93284
  statSync as statSync49,
92618
93285
  unlinkSync as unlinkSync17,
92619
93286
  writeFileSync as writeFileSync32,
@@ -93051,7 +93718,7 @@ var ISSUES_FILE = "issues.jsonl";
93051
93718
  var ISSUES_LOCK = "issues.lock";
93052
93719
  function readAll(stateDir) {
93053
93720
  const path7 = join87(stateDir, ISSUES_FILE);
93054
- if (!existsSync83(path7))
93721
+ if (!existsSync84(path7))
93055
93722
  return [];
93056
93723
  let raw;
93057
93724
  try {
@@ -93128,7 +93795,7 @@ function record(stateDir, input, nowFn = Date.now) {
93128
93795
  });
93129
93796
  }
93130
93797
  function resolve49(stateDir, fingerprint, nowFn = Date.now) {
93131
- if (!existsSync83(join87(stateDir, ISSUES_FILE)))
93798
+ if (!existsSync84(join87(stateDir, ISSUES_FILE)))
93132
93799
  return 0;
93133
93800
  return withLock(stateDir, () => {
93134
93801
  const all = readAll(stateDir);
@@ -93146,7 +93813,7 @@ function resolve49(stateDir, fingerprint, nowFn = Date.now) {
93146
93813
  });
93147
93814
  }
93148
93815
  function resolveAllBySource(stateDir, source, nowFn = Date.now) {
93149
- if (!existsSync83(join87(stateDir, ISSUES_FILE)))
93816
+ if (!existsSync84(join87(stateDir, ISSUES_FILE)))
93150
93817
  return 0;
93151
93818
  return withLock(stateDir, () => {
93152
93819
  const all = readAll(stateDir);
@@ -93164,7 +93831,7 @@ function resolveAllBySource(stateDir, source, nowFn = Date.now) {
93164
93831
  });
93165
93832
  }
93166
93833
  function prune(stateDir, opts = {}) {
93167
- if (!existsSync83(join87(stateDir, ISSUES_FILE)))
93834
+ if (!existsSync84(join87(stateDir, ISSUES_FILE)))
93168
93835
  return 0;
93169
93836
  return withLock(stateDir, () => {
93170
93837
  const all = readAll(stateDir);
@@ -93194,7 +93861,7 @@ function prune(stateDir, opts = {}) {
93194
93861
  });
93195
93862
  }
93196
93863
  function ensureDir(stateDir) {
93197
- mkdirSync46(stateDir, { recursive: true });
93864
+ mkdirSync47(stateDir, { recursive: true });
93198
93865
  }
93199
93866
  function writeAll(stateDir, events) {
93200
93867
  const path7 = join87(stateDir, ISSUES_FILE);
@@ -93204,7 +93871,7 @@ function writeAll(stateDir, events) {
93204
93871
  `) + `
93205
93872
  `;
93206
93873
  writeFileSync32(tmp, body, "utf-8");
93207
- renameSync20(tmp, path7);
93874
+ renameSync21(tmp, path7);
93208
93875
  }
93209
93876
  var ORPHAN_TMP_TTL_MS = 60000;
93210
93877
  var TMP_PREFIX = `${ISSUES_FILE}.tmp-`;
@@ -93236,7 +93903,7 @@ function withLock(stateDir, fn) {
93236
93903
  let fd = null;
93237
93904
  while (fd === null) {
93238
93905
  try {
93239
- fd = openSync15(lockPath, "wx");
93906
+ fd = openSync16(lockPath, "wx");
93240
93907
  try {
93241
93908
  writeSync9(fd, String(process.pid));
93242
93909
  } catch {}
@@ -93256,7 +93923,7 @@ function withLock(stateDir, fn) {
93256
93923
  return fn();
93257
93924
  } finally {
93258
93925
  try {
93259
- closeSync15(fd);
93926
+ closeSync16(fd);
93260
93927
  } catch {}
93261
93928
  try {
93262
93929
  unlinkSync17(lockPath);
@@ -93514,20 +94181,20 @@ function relTime(deltaMs) {
93514
94181
 
93515
94182
  // src/cli/deps.ts
93516
94183
  init_source();
93517
- import { existsSync as existsSync86 } from "node:fs";
94184
+ import { existsSync as existsSync87 } from "node:fs";
93518
94185
  import { homedir as homedir48 } from "node:os";
93519
94186
  import { join as join90, resolve as resolve50 } from "node:path";
93520
94187
 
93521
94188
  // src/deps/python.ts
93522
- import { createHash as createHash17 } from "node:crypto";
94189
+ import { createHash as createHash18 } from "node:crypto";
93523
94190
  import {
93524
- existsSync as existsSync84,
93525
- mkdirSync as mkdirSync47,
94191
+ existsSync as existsSync85,
94192
+ mkdirSync as mkdirSync48,
93526
94193
  readFileSync as readFileSync78,
93527
- rmSync as rmSync14,
94194
+ rmSync as rmSync15,
93528
94195
  writeFileSync as writeFileSync33
93529
94196
  } from "node:fs";
93530
- import { dirname as dirname35, join as join88 } from "node:path";
94197
+ import { dirname as dirname36, join as join88 } from "node:path";
93531
94198
  import { homedir as homedir46 } from "node:os";
93532
94199
  import { execFileSync as execFileSync24 } from "node:child_process";
93533
94200
 
@@ -93543,13 +94210,13 @@ function defaultPythonCacheRoot() {
93543
94210
  return join88(homedir46(), ".switchroom", "deps", "python");
93544
94211
  }
93545
94212
  function hashFile(path7) {
93546
- return createHash17("sha256").update(readFileSync78(path7)).digest("hex");
94213
+ return createHash18("sha256").update(readFileSync78(path7)).digest("hex");
93547
94214
  }
93548
94215
  function ensurePythonEnv(opts) {
93549
94216
  const { skillName, requirementsPath, force = false } = opts;
93550
94217
  const cacheRoot = opts.cacheRoot ?? defaultPythonCacheRoot();
93551
94218
  const hostPython = opts.pythonBin ?? "python3";
93552
- if (!existsSync84(requirementsPath)) {
94219
+ if (!existsSync85(requirementsPath)) {
93553
94220
  throw new PythonEnvError(`requirements file not found: ${requirementsPath}`);
93554
94221
  }
93555
94222
  const venvDir = join88(cacheRoot, skillName);
@@ -93558,7 +94225,7 @@ function ensurePythonEnv(opts) {
93558
94225
  const pythonBin = join88(binDir, "python");
93559
94226
  const pipBin = join88(binDir, "pip");
93560
94227
  const targetHash = hashFile(requirementsPath);
93561
- if (!force && existsSync84(stampPath) && existsSync84(pythonBin)) {
94228
+ if (!force && existsSync85(stampPath) && existsSync85(pythonBin)) {
93562
94229
  const existingHash = readFileSync78(stampPath, "utf8").trim();
93563
94230
  if (existingHash === targetHash) {
93564
94231
  return {
@@ -93571,10 +94238,10 @@ function ensurePythonEnv(opts) {
93571
94238
  };
93572
94239
  }
93573
94240
  }
93574
- if (existsSync84(venvDir)) {
93575
- rmSync14(venvDir, { recursive: true, force: true });
94241
+ if (existsSync85(venvDir)) {
94242
+ rmSync15(venvDir, { recursive: true, force: true });
93576
94243
  }
93577
- mkdirSync47(dirname35(venvDir), { recursive: true });
94244
+ mkdirSync48(dirname36(venvDir), { recursive: true });
93578
94245
  try {
93579
94246
  execFileSync24(hostPython, ["-m", "venv", venvDir], { stdio: "pipe" });
93580
94247
  } catch (err) {
@@ -93606,16 +94273,16 @@ function ensurePythonEnv(opts) {
93606
94273
  }
93607
94274
 
93608
94275
  // src/deps/node.ts
93609
- import { createHash as createHash18 } from "node:crypto";
94276
+ import { createHash as createHash19 } from "node:crypto";
93610
94277
  import {
93611
- copyFileSync as copyFileSync11,
93612
- existsSync as existsSync85,
93613
- mkdirSync as mkdirSync48,
94278
+ copyFileSync as copyFileSync12,
94279
+ existsSync as existsSync86,
94280
+ mkdirSync as mkdirSync49,
93614
94281
  readFileSync as readFileSync79,
93615
- rmSync as rmSync15,
94282
+ rmSync as rmSync16,
93616
94283
  writeFileSync as writeFileSync34
93617
94284
  } from "node:fs";
93618
- import { dirname as dirname36, join as join89 } from "node:path";
94285
+ import { dirname as dirname37, join as join89 } from "node:path";
93619
94286
  import { homedir as homedir47 } from "node:os";
93620
94287
  import { execFileSync as execFileSync25 } from "node:child_process";
93621
94288
 
@@ -93642,14 +94309,14 @@ function defaultNodeCacheRoot() {
93642
94309
  return join89(homedir47(), ".switchroom", "deps", "node");
93643
94310
  }
93644
94311
  function hashDepInputs(packageJsonPath) {
93645
- const sourceDir = dirname36(packageJsonPath);
93646
- const hasher = createHash18("sha256");
94312
+ const sourceDir = dirname37(packageJsonPath);
94313
+ const hasher = createHash19("sha256");
93647
94314
  hasher.update(`package.json
93648
94315
  `);
93649
94316
  hasher.update(readFileSync79(packageJsonPath));
93650
94317
  for (const lockName of ALL_LOCKFILES) {
93651
94318
  const lockPath = join89(sourceDir, lockName);
93652
- if (existsSync85(lockPath)) {
94319
+ if (existsSync86(lockPath)) {
93653
94320
  hasher.update(`
93654
94321
  `);
93655
94322
  hasher.update(lockName);
@@ -93664,16 +94331,16 @@ function ensureNodeEnv(opts) {
93664
94331
  const { skillName, packageJsonPath, force = false } = opts;
93665
94332
  const cacheRoot = opts.cacheRoot ?? defaultNodeCacheRoot();
93666
94333
  const installer = opts.installer ?? "bun";
93667
- if (!existsSync85(packageJsonPath)) {
94334
+ if (!existsSync86(packageJsonPath)) {
93668
94335
  throw new NodeEnvError(`package.json not found: ${packageJsonPath}`);
93669
94336
  }
93670
- const sourceDir = dirname36(packageJsonPath);
94337
+ const sourceDir = dirname37(packageJsonPath);
93671
94338
  const envDir = join89(cacheRoot, skillName);
93672
94339
  const stampPath = join89(envDir, ".package.sha256");
93673
94340
  const nodeModulesDir = join89(envDir, "node_modules");
93674
94341
  const binDir = join89(nodeModulesDir, ".bin");
93675
94342
  const targetHash = hashDepInputs(packageJsonPath);
93676
- if (!force && existsSync85(stampPath) && existsSync85(nodeModulesDir)) {
94343
+ if (!force && existsSync86(stampPath) && existsSync86(nodeModulesDir)) {
93677
94344
  const existingHash = readFileSync79(stampPath, "utf8").trim();
93678
94345
  if (existingHash === targetHash) {
93679
94346
  return {
@@ -93685,16 +94352,16 @@ function ensureNodeEnv(opts) {
93685
94352
  };
93686
94353
  }
93687
94354
  }
93688
- if (existsSync85(envDir)) {
93689
- rmSync15(envDir, { recursive: true, force: true });
94355
+ if (existsSync86(envDir)) {
94356
+ rmSync16(envDir, { recursive: true, force: true });
93690
94357
  }
93691
- mkdirSync48(envDir, { recursive: true });
93692
- copyFileSync11(packageJsonPath, join89(envDir, "package.json"));
94358
+ mkdirSync49(envDir, { recursive: true });
94359
+ copyFileSync12(packageJsonPath, join89(envDir, "package.json"));
93693
94360
  let copiedLockfile = false;
93694
94361
  for (const lockName of LOCKFILES_FOR[installer]) {
93695
94362
  const lockPath = join89(sourceDir, lockName);
93696
- if (existsSync85(lockPath)) {
93697
- copyFileSync11(lockPath, join89(envDir, lockName));
94363
+ if (existsSync86(lockPath)) {
94364
+ copyFileSync12(lockPath, join89(envDir, lockName));
93698
94365
  copiedLockfile = true;
93699
94366
  }
93700
94367
  }
@@ -93729,22 +94396,22 @@ function registerDepsCommand(program3) {
93729
94396
  const deps = program3.command("deps").description("Manage cached per-skill dependency environments");
93730
94397
  deps.command("rebuild <skill>").description("Rebuild the Python venv and/or Node node_modules cache for a skill").option("-p, --python", "Rebuild only the Python env").option("-n, --node", "Rebuild only the Node env").action(async (skill, opts) => {
93731
94398
  const skillsRoot = builtinSkillsRoot();
93732
- if (!existsSync86(skillsRoot)) {
94399
+ if (!existsSync87(skillsRoot)) {
93733
94400
  console.error(source_default.red(`Bundled skills pool dir not found at ${skillsRoot} \u2014 run \`switchroom update\` to install it.`));
93734
94401
  process.exit(1);
93735
94402
  }
93736
94403
  const skillDir = join90(skillsRoot, skill);
93737
- if (!existsSync86(skillDir)) {
94404
+ if (!existsSync87(skillDir)) {
93738
94405
  console.error(source_default.red(`Unknown skill: ${skill} (no dir at ${skillDir})`));
93739
94406
  process.exit(1);
93740
94407
  }
93741
94408
  const requirementsPath = join90(skillDir, "requirements.txt");
93742
94409
  const packageJsonPath = join90(skillDir, "package.json");
93743
- const wantPython = opts.python ?? (!opts.python && !opts.node && existsSync86(requirementsPath));
93744
- const wantNode = opts.node ?? (!opts.python && !opts.node && existsSync86(packageJsonPath));
94410
+ const wantPython = opts.python ?? (!opts.python && !opts.node && existsSync87(requirementsPath));
94411
+ const wantNode = opts.node ?? (!opts.python && !opts.node && existsSync87(packageJsonPath));
93745
94412
  let did = 0;
93746
94413
  if (wantPython) {
93747
- if (!existsSync86(requirementsPath)) {
94414
+ if (!existsSync87(requirementsPath)) {
93748
94415
  console.error(source_default.red(`Skill "${skill}" has no requirements.txt at ${requirementsPath}`));
93749
94416
  process.exit(1);
93750
94417
  }
@@ -93768,7 +94435,7 @@ function registerDepsCommand(program3) {
93768
94435
  }
93769
94436
  }
93770
94437
  if (wantNode) {
93771
- if (!existsSync86(packageJsonPath)) {
94438
+ if (!existsSync87(packageJsonPath)) {
93772
94439
  console.error(source_default.red(`Skill "${skill}" has no package.json at ${packageJsonPath}`));
93773
94440
  process.exit(1);
93774
94441
  }
@@ -93801,9 +94468,9 @@ function registerDepsCommand(program3) {
93801
94468
  // src/cli/workspace.ts
93802
94469
  init_helpers();
93803
94470
  init_loader();
93804
- import { existsSync as existsSync87 } from "node:fs";
94471
+ import { existsSync as existsSync88 } from "node:fs";
93805
94472
  import { resolve as resolve51, sep as sep4 } from "node:path";
93806
- import { spawnSync as spawnSync17 } from "node:child_process";
94473
+ import { spawnSync as spawnSync19 } from "node:child_process";
93807
94474
 
93808
94475
  // src/agents/workspace.ts
93809
94476
  import { readFile as readFile2, stat } from "node:fs/promises";
@@ -94516,7 +95183,7 @@ function registerWorkspaceCommand(program3) {
94516
95183
  process.exit(1);
94517
95184
  }
94518
95185
  const editor = process.env["EDITOR"] ?? process.env["VISUAL"] ?? "vi";
94519
- const child = spawnSync17(editor, [target], { stdio: "inherit" });
95186
+ const child = spawnSync19(editor, [target], { stdio: "inherit" });
94520
95187
  if (child.status !== 0 && child.status !== null) {
94521
95188
  process.exit(child.status);
94522
95189
  }
@@ -94578,12 +95245,12 @@ function registerWorkspaceCommand(program3) {
94578
95245
  if (!dir)
94579
95246
  return;
94580
95247
  const gitDir = resolve51(dir, ".git");
94581
- if (!existsSync87(gitDir)) {
95248
+ if (!existsSync88(gitDir)) {
94582
95249
  process.stdout.write(`Workspace is not a git repository. Re-run \`switchroom agent create ${agentName}\` ` + `or manually \`git init\` in ${dir} to enable versioning.
94583
95250
  `);
94584
95251
  return;
94585
95252
  }
94586
- const statusResult = spawnSync17("git", ["status", "--short"], {
95253
+ const statusResult = spawnSync19("git", ["status", "--short"], {
94587
95254
  cwd: dir,
94588
95255
  encoding: "utf-8"
94589
95256
  });
@@ -94598,7 +95265,7 @@ function registerWorkspaceCommand(program3) {
94598
95265
  return;
94599
95266
  }
94600
95267
  const message = opts.message || `checkpoint: ${new Date().toISOString()}`;
94601
- const addResult = spawnSync17("git", ["add", "-A"], {
95268
+ const addResult = spawnSync19("git", ["add", "-A"], {
94602
95269
  cwd: dir,
94603
95270
  encoding: "utf-8"
94604
95271
  });
@@ -94607,7 +95274,7 @@ function registerWorkspaceCommand(program3) {
94607
95274
  `);
94608
95275
  process.exit(1);
94609
95276
  }
94610
- const commitResult = spawnSync17("git", ["commit", "-m", message], {
95277
+ const commitResult = spawnSync19("git", ["commit", "-m", message], {
94611
95278
  cwd: dir,
94612
95279
  encoding: "utf-8"
94613
95280
  });
@@ -94616,7 +95283,7 @@ function registerWorkspaceCommand(program3) {
94616
95283
  `);
94617
95284
  process.exit(1);
94618
95285
  }
94619
- const shaResult = spawnSync17("git", ["rev-parse", "--short", "HEAD"], {
95286
+ const shaResult = spawnSync19("git", ["rev-parse", "--short", "HEAD"], {
94620
95287
  cwd: dir,
94621
95288
  encoding: "utf-8"
94622
95289
  });
@@ -94632,12 +95299,12 @@ function registerWorkspaceCommand(program3) {
94632
95299
  if (!dir)
94633
95300
  return;
94634
95301
  const gitDir = resolve51(dir, ".git");
94635
- if (!existsSync87(gitDir)) {
95302
+ if (!existsSync88(gitDir)) {
94636
95303
  process.stdout.write(`Workspace is not a git repository.
94637
95304
  `);
94638
95305
  return;
94639
95306
  }
94640
- const child = spawnSync17("git", ["status", "--short"], {
95307
+ const child = spawnSync19("git", ["status", "--short"], {
94641
95308
  cwd: dir,
94642
95309
  stdio: "inherit"
94643
95310
  });
@@ -94657,7 +95324,7 @@ function resolveAgentWorkspaceDirOrExit(program3, agentName) {
94657
95324
  const agentsDir = resolveAgentsDir(config);
94658
95325
  const agentDir = resolve51(agentsDir, agentName);
94659
95326
  const dir = resolveAgentWorkspaceDir(agentDir);
94660
- if (!existsSync87(dir)) {
95327
+ if (!existsSync88(dir)) {
94661
95328
  process.stderr.write(`workspace: ${dir} does not exist yet. Run \`switchroom setup\` or \`switchroom agent scaffold ${agentName}\` to seed it.
94662
95329
  `);
94663
95330
  return;
@@ -94693,7 +95360,7 @@ function safeParseInt(value, fallback) {
94693
95360
  init_helpers();
94694
95361
  init_loader();
94695
95362
  init_merge();
94696
- import { copyFileSync as copyFileSync12, existsSync as existsSync88, readFileSync as readFileSync80, writeFileSync as writeFileSync35 } from "node:fs";
95363
+ import { copyFileSync as copyFileSync13, existsSync as existsSync89, readFileSync as readFileSync80, writeFileSync as writeFileSync35 } from "node:fs";
94697
95364
  import { join as join91, resolve as resolve52 } from "node:path";
94698
95365
  init_scaffold();
94699
95366
  init_profiles();
@@ -94711,7 +95378,7 @@ function resolveSoulTargetOrExit(program3, agentName) {
94711
95378
  const agentsDir = resolveAgentsDir(config);
94712
95379
  const agentDir = resolve52(agentsDir, agentName);
94713
95380
  const workspaceDir = resolveAgentWorkspaceDir(agentDir);
94714
- if (!existsSync88(workspaceDir)) {
95381
+ if (!existsSync89(workspaceDir)) {
94715
95382
  console.error(`soul: ${workspaceDir} does not exist yet. Run \`switchroom setup\` ` + `or \`switchroom agent scaffold ${agentName}\` to seed it.`);
94716
95383
  process.exit(1);
94717
95384
  }
@@ -94737,7 +95404,7 @@ function registerSoulCommand(program3) {
94737
95404
  const t = resolveSoulTargetOrExit(program3, agentName);
94738
95405
  if (!t)
94739
95406
  return;
94740
- if (!existsSync88(t.soulPath)) {
95407
+ if (!existsSync89(t.soulPath)) {
94741
95408
  console.error(`soul: ${t.soulPath} does not exist yet \u2014 run ` + `\`switchroom soul reset ${agentName}\` to seed it.`);
94742
95409
  process.exit(1);
94743
95410
  }
@@ -94752,7 +95419,7 @@ function registerSoulCommand(program3) {
94752
95419
  console.error(`soul: profile "${t.profileName}" ships no SOUL.md.hbs \u2014 ` + `nothing to re-seed from.`);
94753
95420
  process.exit(1);
94754
95421
  }
94755
- const exists = existsSync88(t.soulPath);
95422
+ const exists = existsSync89(t.soulPath);
94756
95423
  if (exists && !opts.yes) {
94757
95424
  if (!isInteractive()) {
94758
95425
  console.error(`soul: ${t.soulPath} already exists. Re-run with --yes to ` + `replace it (the current file is backed up to SOUL.md.bak).`);
@@ -94767,10 +95434,10 @@ function registerSoulCommand(program3) {
94767
95434
  let backupPath;
94768
95435
  if (exists) {
94769
95436
  backupPath = `${t.soulPath}.bak`;
94770
- if (existsSync88(backupPath)) {
95437
+ if (existsSync89(backupPath)) {
94771
95438
  backupPath = `${t.soulPath}.bak.${Date.now()}`;
94772
95439
  }
94773
- copyFileSync12(t.soulPath, backupPath);
95440
+ copyFileSync13(t.soulPath, backupPath);
94774
95441
  }
94775
95442
  writeFileSync35(t.soulPath, content, "utf-8");
94776
95443
  if (backupPath) {
@@ -94786,9 +95453,9 @@ function registerSoulCommand(program3) {
94786
95453
  // src/cli/debug.ts
94787
95454
  init_helpers();
94788
95455
  init_loader();
94789
- import { existsSync as existsSync89, readFileSync as readFileSync81, readdirSync as readdirSync30, statSync as statSync50 } from "node:fs";
95456
+ import { existsSync as existsSync90, readFileSync as readFileSync81, readdirSync as readdirSync30, statSync as statSync50 } from "node:fs";
94790
95457
  import { resolve as resolve53, join as join92 } from "node:path";
94791
- import { createHash as createHash19 } from "node:crypto";
95458
+ import { createHash as createHash20 } from "node:crypto";
94792
95459
  init_merge();
94793
95460
  init_hindsight2();
94794
95461
  function formatBytes(bytes) {
@@ -94799,7 +95466,7 @@ function estimateTokens(bytes) {
94799
95466
  }
94800
95467
  function readMcpServerNames(agentDir) {
94801
95468
  const mcpPath = join92(agentDir, ".mcp.json");
94802
- if (!existsSync89(mcpPath))
95469
+ if (!existsSync90(mcpPath))
94803
95470
  return [];
94804
95471
  try {
94805
95472
  const parsed = JSON.parse(readFileSync81(mcpPath, "utf-8"));
@@ -94809,11 +95476,11 @@ function readMcpServerNames(agentDir) {
94809
95476
  }
94810
95477
  }
94811
95478
  function sha256(content) {
94812
- return createHash19("sha256").update(content).digest("hex").slice(0, 16);
95479
+ return createHash20("sha256").update(content).digest("hex").slice(0, 16);
94813
95480
  }
94814
95481
  function findLatestTranscriptJsonl(claudeConfigDir) {
94815
95482
  const projectsDir = join92(claudeConfigDir, "projects");
94816
- if (!existsSync89(projectsDir))
95483
+ if (!existsSync90(projectsDir))
94817
95484
  return;
94818
95485
  try {
94819
95486
  const entries = readdirSync30(projectsDir, { withFileTypes: true });
@@ -94823,7 +95490,7 @@ function findLatestTranscriptJsonl(claudeConfigDir) {
94823
95490
  continue;
94824
95491
  const projectPath = join92(projectsDir, entry.name);
94825
95492
  const transcriptPath = join92(projectPath, "transcript.jsonl");
94826
- if (!existsSync89(transcriptPath))
95493
+ if (!existsSync90(transcriptPath))
94827
95494
  continue;
94828
95495
  const stat3 = statSync50(transcriptPath);
94829
95496
  if (!latest || stat3.mtimeMs > latest.mtime) {
@@ -94886,7 +95553,7 @@ function registerDebugCommand(program3) {
94886
95553
  }
94887
95554
  const agentsDir = resolveAgentsDir(config);
94888
95555
  const agentDir = resolve53(agentsDir, agentName);
94889
- if (!existsSync89(agentDir)) {
95556
+ if (!existsSync90(agentDir)) {
94890
95557
  console.error(`Agent directory not found: ${agentDir}`);
94891
95558
  process.exit(1);
94892
95559
  }
@@ -94941,7 +95608,7 @@ function registerDebugCommand(program3) {
94941
95608
  }
94942
95609
  console.log(`=== Append System Prompt (per-session) ===
94943
95610
  `);
94944
- const handoffContent = existsSync89(handoffPath) ? readFileSync81(handoffPath, "utf-8") : "";
95611
+ const handoffContent = existsSync90(handoffPath) ? readFileSync81(handoffPath, "utf-8") : "";
94945
95612
  if (handoffContent.trim().length > 0) {
94946
95613
  console.log(`-- Handoff Briefing (${formatBytes(handoffContent.length)}) --`);
94947
95614
  console.log(handoffContent);
@@ -94952,7 +95619,7 @@ function registerDebugCommand(program3) {
94952
95619
  }
94953
95620
  console.log(`=== CLAUDE.md (auto-loaded by Claude Code) ===
94954
95621
  `);
94955
- const claudeMdContent = existsSync89(claudeMdPath) ? readFileSync81(claudeMdPath, "utf-8") : "";
95622
+ const claudeMdContent = existsSync90(claudeMdPath) ? readFileSync81(claudeMdPath, "utf-8") : "";
94956
95623
  if (claudeMdContent.trim().length > 0) {
94957
95624
  console.log(`(${formatBytes(claudeMdContent.length)})`);
94958
95625
  console.log(claudeMdContent);
@@ -94963,7 +95630,7 @@ function registerDebugCommand(program3) {
94963
95630
  }
94964
95631
  console.log(`=== Persona (SOUL.md) ===
94965
95632
  `);
94966
- const soulMdContent = existsSync89(soulMdPath) ? readFileSync81(soulMdPath, "utf-8") : existsSync89(workspaceSoulMdPath) ? readFileSync81(workspaceSoulMdPath, "utf-8") : "";
95633
+ const soulMdContent = existsSync90(soulMdPath) ? readFileSync81(soulMdPath, "utf-8") : existsSync90(workspaceSoulMdPath) ? readFileSync81(workspaceSoulMdPath, "utf-8") : "";
94967
95634
  if (soulMdContent.trim().length > 0) {
94968
95635
  console.log(`(${formatBytes(soulMdContent.length)})`);
94969
95636
  console.log(soulMdContent);
@@ -95027,8 +95694,8 @@ function registerDebugCommand(program3) {
95027
95694
  const fleetDir = join92(agentsDir, "..", "fleet");
95028
95695
  const fleetInvPath = join92(fleetDir, "switchroom-invariants.md");
95029
95696
  const fleetClaudePath = join92(fleetDir, "CLAUDE.md");
95030
- const fleetInvBytes = existsSync89(fleetInvPath) ? readFileSync81(fleetInvPath, "utf-8").length : 0;
95031
- const fleetClaudeBytes = existsSync89(fleetClaudePath) ? readFileSync81(fleetClaudePath, "utf-8").length : 0;
95697
+ const fleetInvBytes = existsSync90(fleetInvPath) ? readFileSync81(fleetInvPath, "utf-8").length : 0;
95698
+ const fleetClaudeBytes = existsSync90(fleetClaudePath) ? readFileSync81(fleetClaudePath, "utf-8").length : 0;
95032
95699
  const fleetBytes = fleetInvBytes + fleetClaudeBytes;
95033
95700
  const totalBytes = stableBytes + perSessionBytes + claudeMdBytes + fleetBytes + perTurnBytes + userBytes;
95034
95701
  console.log(`Stable prefix: ${formatBytes(stableBytes).padEnd(20)} (cache-hot; includes SOUL.md ${soulMdBytes.toLocaleString()}B)`);
@@ -95061,20 +95728,20 @@ init_source();
95061
95728
 
95062
95729
  // src/worktree/claim.ts
95063
95730
  import { execFileSync as execFileSync26 } from "node:child_process";
95064
- import { closeSync as closeSync16, mkdirSync as mkdirSync50, openSync as openSync16, existsSync as existsSync91, unlinkSync as unlinkSync19 } from "node:fs";
95731
+ import { closeSync as closeSync17, mkdirSync as mkdirSync51, openSync as openSync17, existsSync as existsSync92, unlinkSync as unlinkSync19 } from "node:fs";
95065
95732
  import { join as join94, resolve as resolve55 } from "node:path";
95066
95733
  import { homedir as homedir50 } from "node:os";
95067
95734
  import { randomBytes as randomBytes14 } from "node:crypto";
95068
95735
 
95069
95736
  // src/worktree/registry.ts
95070
95737
  import {
95071
- mkdirSync as mkdirSync49,
95738
+ mkdirSync as mkdirSync50,
95072
95739
  writeFileSync as writeFileSync36,
95073
95740
  readFileSync as readFileSync82,
95074
95741
  readdirSync as readdirSync31,
95075
95742
  unlinkSync as unlinkSync18,
95076
- existsSync as existsSync90,
95077
- renameSync as renameSync21
95743
+ existsSync as existsSync91,
95744
+ renameSync as renameSync22
95078
95745
  } from "node:fs";
95079
95746
  import { join as join93, resolve as resolve54 } from "node:path";
95080
95747
  import { homedir as homedir49 } from "node:os";
@@ -95085,7 +95752,7 @@ function recordPath(id) {
95085
95752
  return join93(registryDir(), `${id}.json`);
95086
95753
  }
95087
95754
  function ensureDir2() {
95088
- mkdirSync49(registryDir(), { recursive: true });
95755
+ mkdirSync50(registryDir(), { recursive: true });
95089
95756
  }
95090
95757
  function writeRecord(record2) {
95091
95758
  ensureDir2();
@@ -95093,7 +95760,7 @@ function writeRecord(record2) {
95093
95760
  const tmp = `${target}.tmp${process.pid}`;
95094
95761
  writeFileSync36(tmp, JSON.stringify(record2, null, 2) + `
95095
95762
  `, { mode: 384 });
95096
- renameSync21(tmp, target);
95763
+ renameSync22(tmp, target);
95097
95764
  }
95098
95765
  function readRecord(id) {
95099
95766
  const path10 = recordPath(id);
@@ -95131,14 +95798,14 @@ function countByRepo(repoPath) {
95131
95798
  // src/worktree/claim.ts
95132
95799
  function acquireRepoLock(repoPath) {
95133
95800
  const lockDir = registryDir();
95134
- mkdirSync50(lockDir, { recursive: true });
95801
+ mkdirSync51(lockDir, { recursive: true });
95135
95802
  const lockName = repoPath.replace(/[^A-Za-z0-9]/g, "_");
95136
95803
  const lockPath = join94(lockDir, `.lock-${lockName}`);
95137
95804
  const deadline = Date.now() + 5000;
95138
95805
  let fd = null;
95139
95806
  while (fd === null) {
95140
95807
  try {
95141
- fd = openSync16(lockPath, "wx");
95808
+ fd = openSync17(lockPath, "wx");
95142
95809
  } catch (err) {
95143
95810
  if (err.code !== "EEXIST")
95144
95811
  throw err;
@@ -95151,7 +95818,7 @@ function acquireRepoLock(repoPath) {
95151
95818
  }
95152
95819
  return () => {
95153
95820
  try {
95154
- closeSync16(fd);
95821
+ closeSync17(fd);
95155
95822
  } catch {}
95156
95823
  try {
95157
95824
  unlinkSync19(lockPath);
@@ -95187,7 +95854,7 @@ function expandHome(p) {
95187
95854
  }
95188
95855
  async function claimWorktree(input, codeRepos) {
95189
95856
  const repoPath = resolveRepoPath(input.repo, codeRepos);
95190
- if (!existsSync91(repoPath)) {
95857
+ if (!existsSync92(repoPath)) {
95191
95858
  throw new Error(`Repository path does not exist: ${repoPath}`);
95192
95859
  }
95193
95860
  let concurrencyCap = DEFAULT_CONCURRENCY;
@@ -95209,7 +95876,7 @@ async function claimWorktree(input, codeRepos) {
95209
95876
  const taskSuffix = input.taskName ? sanitizeTaskName(input.taskName) : "task";
95210
95877
  branch = `task/${taskSuffix}-${id}`;
95211
95878
  const baseDir = worktreesBaseDir();
95212
- mkdirSync50(baseDir, { recursive: true });
95879
+ mkdirSync51(baseDir, { recursive: true });
95213
95880
  worktreePath = join94(baseDir, `${id}-${taskSuffix}`);
95214
95881
  const ambientOwner = process.env.SWITCHROOM_AGENT_NAME;
95215
95882
  const ownerAgent = input.ownerAgent ?? (ambientOwner != null && ambientOwner !== "" ? ambientOwner : undefined);
@@ -95243,7 +95910,7 @@ async function claimWorktree(input, codeRepos) {
95243
95910
 
95244
95911
  // src/worktree/release.ts
95245
95912
  import { execFileSync as execFileSync27 } from "node:child_process";
95246
- import { existsSync as existsSync92 } from "node:fs";
95913
+ import { existsSync as existsSync93 } from "node:fs";
95247
95914
  function releaseWorktree(input) {
95248
95915
  const { id } = input;
95249
95916
  const record2 = readRecord(id);
@@ -95251,7 +95918,7 @@ function releaseWorktree(input) {
95251
95918
  return { released: true };
95252
95919
  }
95253
95920
  let gitSuccess = true;
95254
- if (existsSync92(record2.path)) {
95921
+ if (existsSync93(record2.path)) {
95255
95922
  try {
95256
95923
  execFileSync27("git", ["worktree", "remove", "--force", record2.path], {
95257
95924
  cwd: record2.repo,
@@ -95290,7 +95957,7 @@ function listWorktrees() {
95290
95957
 
95291
95958
  // src/worktree/reaper.ts
95292
95959
  import { execFileSync as execFileSync28 } from "node:child_process";
95293
- import { existsSync as existsSync93 } from "node:fs";
95960
+ import { existsSync as existsSync94 } from "node:fs";
95294
95961
  var STALE_THRESHOLD_MS = 10 * 60 * 1000;
95295
95962
  function reapSkipReasonText(action) {
95296
95963
  switch (action) {
@@ -95351,7 +96018,7 @@ function planReaper(nowMs, deps = {}) {
95351
96018
  const plan = [];
95352
96019
  for (const record2 of listRecords()) {
95353
96020
  const heartbeatAge = now - new Date(record2.heartbeatAt).getTime();
95354
- const worktreeExists = existsSync93(record2.path);
96021
+ const worktreeExists = existsSync94(record2.path);
95355
96022
  if (!worktreeExists) {
95356
96023
  plan.push({
95357
96024
  record: record2,
@@ -95432,13 +96099,13 @@ function runReaper(nowMs, deps = {}) {
95432
96099
  // src/worktree/gc.ts
95433
96100
  import { execFileSync as execFileSync29 } from "node:child_process";
95434
96101
  import {
95435
- existsSync as existsSync94,
96102
+ existsSync as existsSync95,
95436
96103
  readFileSync as readFileSync83,
95437
96104
  readdirSync as readdirSync32,
95438
96105
  statSync as statSync51,
95439
- renameSync as renameSync22,
95440
- mkdirSync as mkdirSync51,
95441
- rmSync as rmSync16
96106
+ renameSync as renameSync23,
96107
+ mkdirSync as mkdirSync52,
96108
+ rmSync as rmSync17
95442
96109
  } from "node:fs";
95443
96110
  import { homedir as homedir51 } from "node:os";
95444
96111
  import { join as join95, resolve as resolve56 } from "node:path";
@@ -95523,7 +96190,7 @@ function isEphemeralPath(path10) {
95523
96190
  }
95524
96191
  return false;
95525
96192
  }
95526
- var defaultExec2 = (file, args, cwd) => execFileSync29(file, args, { cwd, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).toString();
96193
+ var defaultExec3 = (file, args, cwd) => execFileSync29(file, args, { cwd, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).toString();
95527
96194
  function defaultPrSignal(repo, branch, exec) {
95528
96195
  try {
95529
96196
  const out = exec("gh", [
@@ -95559,11 +96226,11 @@ function trashRoot() {
95559
96226
  return resolve56(process.env.SWITCHROOM_WORKTREE_TRASH ?? join95(homedir51(), ".switchroom", "worktree-gc-trash"));
95560
96227
  }
95561
96228
  function planGc(roots, deps = {}) {
95562
- const exists = deps.existsSync ?? existsSync94;
96229
+ const exists = deps.existsSync ?? existsSync95;
95563
96230
  const readDir = deps.readDir ?? ((p) => readdirSync32(p));
95564
96231
  const readFile4 = deps.readFile ?? ((p) => readFileSync83(p, "utf8"));
95565
96232
  const stat3 = deps.stat ?? ((p) => statSync51(p));
95566
- const exec = deps.exec ?? defaultExec2;
96233
+ const exec = deps.exec ?? defaultExec3;
95567
96234
  const prSignal = deps.prSignal ?? ((repo, branch) => defaultPrSignal(repo, branch, exec));
95568
96235
  const stamp = deps.dateStamp ?? "undated";
95569
96236
  const trash = join95(trashRoot(), stamp);
@@ -95688,11 +96355,11 @@ function planGc(roots, deps = {}) {
95688
96355
  };
95689
96356
  }
95690
96357
  function applyGc(plan, deps = {}) {
95691
- const exec = deps.exec ?? defaultExec2;
95692
- const mkdirp = deps.mkdirp ?? ((p) => void mkdirSync51(p, { recursive: true }));
96358
+ const exec = deps.exec ?? defaultExec3;
96359
+ const mkdirp = deps.mkdirp ?? ((p) => void mkdirSync52(p, { recursive: true }));
95693
96360
  const move = deps.move ?? ((src, dest) => {
95694
96361
  try {
95695
- renameSync22(src, dest);
96362
+ renameSync23(src, dest);
95696
96363
  } catch {
95697
96364
  exec("mv", [src, dest]);
95698
96365
  }
@@ -95738,7 +96405,7 @@ function selectPurgeTargets(entries, olderThanDays) {
95738
96405
  return entries.filter((e) => e.ageDays >= olderThanDays).map((e) => e.path);
95739
96406
  }
95740
96407
  function listTrashEntries(nowMs, deps = {}) {
95741
- const exists = deps.existsSync ?? existsSync94;
96408
+ const exists = deps.existsSync ?? existsSync95;
95742
96409
  const readDir = deps.readDir ?? ((p) => readdirSync32(p));
95743
96410
  const root = trashRoot();
95744
96411
  if (!exists(root))
@@ -95768,7 +96435,7 @@ function purgeTrash(paths) {
95768
96435
  const errors2 = [];
95769
96436
  for (const p of paths) {
95770
96437
  try {
95771
- rmSync16(p, { recursive: true, force: true });
96438
+ rmSync17(p, { recursive: true, force: true });
95772
96439
  deleted.push(p);
95773
96440
  } catch (e) {
95774
96441
  errors2.push(`${p}: ${e.message}`);
@@ -95999,10 +96666,10 @@ init_drive();
95999
96666
  // src/cli/drive-mcp-launcher.ts
96000
96667
  init_scaffold_integration();
96001
96668
  import {
96002
- chmodSync as chmodSync15,
96003
- mkdirSync as mkdirSync52,
96669
+ chmodSync as chmodSync16,
96670
+ mkdirSync as mkdirSync53,
96004
96671
  readdirSync as readdirSync33,
96005
- rmSync as rmSync17,
96672
+ rmSync as rmSync18,
96006
96673
  writeFileSync as writeFileSync37
96007
96674
  } from "node:fs";
96008
96675
  import { join as join96 } from "node:path";
@@ -96198,15 +96865,15 @@ function resolveCredentialsDir(env2) {
96198
96865
  return join96(stateBase, "google-workspace-mcp", "credentials");
96199
96866
  }
96200
96867
  function writeSeedFile(dir, email, seed) {
96201
- mkdirSync52(dir, { recursive: true, mode: 448 });
96202
- chmodSync15(dir, 448);
96868
+ mkdirSync53(dir, { recursive: true, mode: 448 });
96869
+ chmodSync16(dir, 448);
96203
96870
  for (const name of readdirSync33(dir)) {
96204
- rmSync17(join96(dir, name), { force: true, recursive: true });
96871
+ rmSync18(join96(dir, name), { force: true, recursive: true });
96205
96872
  }
96206
96873
  const filename = encodeCredentialsFilename(email);
96207
96874
  const filePath = join96(dir, filename);
96208
96875
  writeFileSync37(filePath, JSON.stringify(seed), { mode: 384 });
96209
- chmodSync15(filePath, 384);
96876
+ chmodSync16(filePath, 384);
96210
96877
  return filePath;
96211
96878
  }
96212
96879
  async function fetchBrokerGoogleCreds() {
@@ -96363,8 +97030,8 @@ function registerDriveMcpLauncherCommand(program3) {
96363
97030
  // src/cli/m365-mcp-launcher.ts
96364
97031
  init_scaffold_integration();
96365
97032
  import { spawn as spawn6 } from "node:child_process";
96366
- import { writeFileSync as writeFileSync38, mkdirSync as mkdirSync53 } from "node:fs";
96367
- import { dirname as dirname37, join as join97 } from "node:path";
97033
+ import { writeFileSync as writeFileSync38, mkdirSync as mkdirSync54 } from "node:fs";
97034
+ import { dirname as dirname38, join as join97 } from "node:path";
96368
97035
  var SOFTERIA_TOKEN_ENV = "MS365_MCP_OAUTH_TOKEN";
96369
97036
  var DEFAULT_REFRESH_LEAD_MS = 5 * 60 * 1000;
96370
97037
  var MAX_REFRESH_INTERVAL_MS = 60 * 60 * 1000;
@@ -96400,7 +97067,7 @@ function computeRefreshDelayMs(expiresAt, now, leadMs = DEFAULT_REFRESH_LEAD_MS)
96400
97067
  function writeRefreshHeartbeat(agentName, data, account) {
96401
97068
  const path10 = heartbeatPath(agentName, account);
96402
97069
  try {
96403
- mkdirSync53(dirname37(path10), { recursive: true });
97070
+ mkdirSync54(dirname38(path10), { recursive: true });
96404
97071
  writeFileSync38(path10, JSON.stringify(data, null, 2), { mode: 420 });
96405
97072
  } catch {}
96406
97073
  }
@@ -96641,8 +97308,8 @@ function registerM365McpLauncherCommand(program3) {
96641
97308
  // src/cli/notion-mcp-launcher.ts
96642
97309
  init_scaffold_integration();
96643
97310
  import { spawn as spawn7 } from "node:child_process";
96644
- import { existsSync as existsSync95, mkdirSync as mkdirSync54, writeFileSync as writeFileSync39 } from "node:fs";
96645
- import { dirname as dirname38 } from "node:path";
97311
+ import { existsSync as existsSync96, mkdirSync as mkdirSync55, writeFileSync as writeFileSync39 } from "node:fs";
97312
+ import { dirname as dirname39 } from "node:path";
96646
97313
  var HEARTBEAT_WRITE_INTERVAL_MS = 30 * 1000;
96647
97314
  var DEFAULT_HEARTBEAT_PATH = "/state/agent/notion-launcher.heartbeat.json";
96648
97315
  var DEFAULT_VAULT_KEY = "notion/integration-token";
@@ -96652,9 +97319,9 @@ function buildNotionMcpArgs(opts) {
96652
97319
  }
96653
97320
  function defaultWriteHeartbeat(path10, contents) {
96654
97321
  try {
96655
- const dir = dirname38(path10);
96656
- if (!existsSync95(dir))
96657
- mkdirSync54(dir, { recursive: true });
97322
+ const dir = dirname39(path10);
97323
+ if (!existsSync96(dir))
97324
+ mkdirSync55(dir, { recursive: true });
96658
97325
  writeFileSync39(path10, contents);
96659
97326
  } catch {}
96660
97327
  }
@@ -96765,7 +97432,7 @@ function registerNotionMcpLauncherCommand(program3) {
96765
97432
 
96766
97433
  // src/cli/hindsight-mcp-shim.ts
96767
97434
  init_hindsight();
96768
- import { mkdirSync as mkdirSync55, readFileSync as readFileSync84, renameSync as renameSync23, writeFileSync as writeFileSync40 } from "node:fs";
97435
+ import { mkdirSync as mkdirSync56, readFileSync as readFileSync84, renameSync as renameSync24, writeFileSync as writeFileSync40 } from "node:fs";
96769
97436
  import { tmpdir as tmpdir5 } from "node:os";
96770
97437
  import { join as join98 } from "node:path";
96771
97438
  import { createInterface as createInterface6 } from "node:readline";
@@ -96997,11 +97664,11 @@ class HindsightShim {
96997
97664
  }
96998
97665
  writeCache(result) {
96999
97666
  try {
97000
- mkdirSync55(this.opts.cacheDir, { recursive: true });
97667
+ mkdirSync56(this.opts.cacheDir, { recursive: true });
97001
97668
  const tmp = join98(this.opts.cacheDir, `.${TOOLS_CACHE_FILENAME}.${process.pid}.tmp`);
97002
97669
  writeFileSync40(tmp, JSON.stringify(result, null, 2) + `
97003
97670
  `);
97004
- renameSync23(tmp, this.cachePath);
97671
+ renameSync24(tmp, this.cachePath);
97005
97672
  } catch (err) {
97006
97673
  this.log(`[hindsight-shim] cache write failed: ${String(err)}`);
97007
97674
  }
@@ -97845,7 +98512,7 @@ function runRedactStdin() {
97845
98512
  }
97846
98513
 
97847
98514
  // src/cli/status-ask.ts
97848
- import { readFileSync as readFileSync86, existsSync as existsSync96, readdirSync as readdirSync34 } from "node:fs";
98515
+ import { readFileSync as readFileSync86, existsSync as existsSync97, readdirSync as readdirSync34 } from "node:fs";
97849
98516
  import { join as join99 } from "node:path";
97850
98517
  import { homedir as homedir52 } from "node:os";
97851
98518
 
@@ -98168,7 +98835,7 @@ function runReport(opts) {
98168
98835
  function resolveSources(explicitPath) {
98169
98836
  if (explicitPath != null && explicitPath.trim() !== "") {
98170
98837
  const trimmed = explicitPath.trim();
98171
- if (!existsSync96(trimmed)) {
98838
+ if (!existsSync97(trimmed)) {
98172
98839
  process.stderr.write(`status-ask report: ${trimmed}: file not found
98173
98840
  `);
98174
98841
  process.exit(1);
@@ -98184,7 +98851,7 @@ function resolveSources(explicitPath) {
98184
98851
  } catch {
98185
98852
  agentsDir = join99(homedir52(), ".switchroom", "agents");
98186
98853
  }
98187
- if (!existsSync96(agentsDir))
98854
+ if (!existsSync97(agentsDir))
98188
98855
  return [];
98189
98856
  const sources = [];
98190
98857
  let entries;
@@ -98195,7 +98862,7 @@ function resolveSources(explicitPath) {
98195
98862
  }
98196
98863
  for (const name of entries) {
98197
98864
  const path10 = join99(agentsDir, name, "runtime-metrics.jsonl");
98198
- if (existsSync96(path10)) {
98865
+ if (existsSync97(path10)) {
98199
98866
  sources.push({ path: path10, agent: name });
98200
98867
  }
98201
98868
  }
@@ -98224,14 +98891,14 @@ var import_yaml21 = __toESM(require_dist(), 1);
98224
98891
  // src/config/overlay-writer.ts
98225
98892
  init_paths();
98226
98893
  import {
98227
- closeSync as closeSync17,
98228
- existsSync as existsSync97,
98894
+ closeSync as closeSync18,
98895
+ existsSync as existsSync98,
98229
98896
  fsyncSync as fsyncSync8,
98230
- mkdirSync as mkdirSync56,
98231
- openSync as openSync17,
98897
+ mkdirSync as mkdirSync57,
98898
+ openSync as openSync18,
98232
98899
  readdirSync as readdirSync35,
98233
98900
  readFileSync as readFileSync87,
98234
- renameSync as renameSync24,
98901
+ renameSync as renameSync25,
98235
98902
  statSync as statSync53,
98236
98903
  unlinkSync as unlinkSync20,
98237
98904
  writeSync as writeSync10
@@ -98255,21 +98922,21 @@ function overlayPathsFor(agent, opts = {}) {
98255
98922
  };
98256
98923
  }
98257
98924
  function ensureDirs(paths) {
98258
- mkdirSync56(paths.scheduleDir, { recursive: true });
98259
- mkdirSync56(paths.scheduleStagingDir, { recursive: true });
98925
+ mkdirSync57(paths.scheduleDir, { recursive: true });
98926
+ mkdirSync57(paths.scheduleStagingDir, { recursive: true });
98260
98927
  }
98261
98928
  function ensureSkillsDirs(paths) {
98262
- mkdirSync56(paths.skillsDir, { recursive: true });
98263
- mkdirSync56(paths.skillsStagingDir, { recursive: true });
98929
+ mkdirSync57(paths.skillsDir, { recursive: true });
98930
+ mkdirSync57(paths.skillsStagingDir, { recursive: true });
98264
98931
  }
98265
98932
  function withAgentLock(paths, fn) {
98266
- mkdirSync56(paths.agentRoot, { recursive: true });
98933
+ mkdirSync57(paths.agentRoot, { recursive: true });
98267
98934
  const start = Date.now();
98268
98935
  const TIMEOUT_MS = 5000;
98269
98936
  let fd = null;
98270
98937
  while (Date.now() - start < TIMEOUT_MS) {
98271
98938
  try {
98272
- fd = openSync17(paths.lockPath, "wx");
98939
+ fd = openSync18(paths.lockPath, "wx");
98273
98940
  break;
98274
98941
  } catch (err) {
98275
98942
  const e = err;
@@ -98293,7 +98960,7 @@ function withAgentLock(paths, fn) {
98293
98960
  return fn();
98294
98961
  } finally {
98295
98962
  try {
98296
- closeSync17(fd);
98963
+ closeSync18(fd);
98297
98964
  } catch {}
98298
98965
  try {
98299
98966
  unlinkSync20(paths.lockPath);
@@ -98306,14 +98973,14 @@ function writeOverlayEntry(agent, slug, yamlText, opts = {}) {
98306
98973
  ensureDirs(paths);
98307
98974
  const stagingPath = join100(paths.scheduleStagingDir, `${slug}.yaml`);
98308
98975
  const finalPath = join100(paths.scheduleDir, `${slug}.yaml`);
98309
- const fd = openSync17(stagingPath, "w", 384);
98976
+ const fd = openSync18(stagingPath, "w", 384);
98310
98977
  try {
98311
98978
  writeSync10(fd, yamlText);
98312
98979
  fsyncSync8(fd);
98313
98980
  } finally {
98314
- closeSync17(fd);
98981
+ closeSync18(fd);
98315
98982
  }
98316
- renameSync24(stagingPath, finalPath);
98983
+ renameSync25(stagingPath, finalPath);
98317
98984
  return finalPath;
98318
98985
  });
98319
98986
  }
@@ -98323,14 +98990,14 @@ function writeSkillsOverlayEntry(agent, slug, yamlText, opts = {}) {
98323
98990
  ensureSkillsDirs(paths);
98324
98991
  const stagingPath = join100(paths.skillsStagingDir, `${slug}.yaml`);
98325
98992
  const finalPath = join100(paths.skillsDir, `${slug}.yaml`);
98326
- const fd = openSync17(stagingPath, "w", 384);
98993
+ const fd = openSync18(stagingPath, "w", 384);
98327
98994
  try {
98328
98995
  writeSync10(fd, yamlText);
98329
98996
  fsyncSync8(fd);
98330
98997
  } finally {
98331
- closeSync17(fd);
98998
+ closeSync18(fd);
98332
98999
  }
98333
- renameSync24(stagingPath, finalPath);
99000
+ renameSync25(stagingPath, finalPath);
98334
99001
  return finalPath;
98335
99002
  });
98336
99003
  }
@@ -98338,7 +99005,7 @@ function deleteSkillsOverlayEntry(agent, slug, opts = {}) {
98338
99005
  const paths = overlayPathsFor(agent, opts);
98339
99006
  return withAgentLock(paths, () => {
98340
99007
  const finalPath = join100(paths.skillsDir, `${slug}.yaml`);
98341
- if (!existsSync97(finalPath))
99008
+ if (!existsSync98(finalPath))
98342
99009
  return false;
98343
99010
  unlinkSync20(finalPath);
98344
99011
  return true;
@@ -98346,7 +99013,7 @@ function deleteSkillsOverlayEntry(agent, slug, opts = {}) {
98346
99013
  }
98347
99014
  function listSkillsOverlayEntries(agent, opts = {}) {
98348
99015
  const paths = overlayPathsFor(agent, opts);
98349
- if (!existsSync97(paths.skillsDir))
99016
+ if (!existsSync98(paths.skillsDir))
98350
99017
  return [];
98351
99018
  const out = [];
98352
99019
  for (const name of readdirSync35(paths.skillsDir)) {
@@ -98365,7 +99032,7 @@ function deleteOverlayEntry(agent, slug, opts = {}) {
98365
99032
  const paths = overlayPathsFor(agent, opts);
98366
99033
  return withAgentLock(paths, () => {
98367
99034
  const finalPath = join100(paths.scheduleDir, `${slug}.yaml`);
98368
- if (!existsSync97(finalPath))
99035
+ if (!existsSync98(finalPath))
98369
99036
  return false;
98370
99037
  unlinkSync20(finalPath);
98371
99038
  return true;
@@ -98373,7 +99040,7 @@ function deleteOverlayEntry(agent, slug, opts = {}) {
98373
99040
  }
98374
99041
  function listOverlayEntries(agent, opts = {}) {
98375
99042
  const paths = overlayPathsFor(agent, opts);
98376
- if (!existsSync97(paths.scheduleDir))
99043
+ if (!existsSync98(paths.scheduleDir))
98377
99044
  return [];
98378
99045
  const out = [];
98379
99046
  for (const name of readdirSync35(paths.scheduleDir)) {
@@ -98600,14 +99267,14 @@ function reconcileAgentCronOnly(agent) {
98600
99267
 
98601
99268
  // src/cli/agent-config-pending.ts
98602
99269
  import {
98603
- closeSync as closeSync18,
98604
- existsSync as existsSync98,
99270
+ closeSync as closeSync19,
99271
+ existsSync as existsSync99,
98605
99272
  fsyncSync as fsyncSync9,
98606
- mkdirSync as mkdirSync57,
98607
- openSync as openSync18,
99273
+ mkdirSync as mkdirSync58,
99274
+ openSync as openSync19,
98608
99275
  readdirSync as readdirSync36,
98609
99276
  readFileSync as readFileSync88,
98610
- renameSync as renameSync25,
99277
+ renameSync as renameSync26,
98611
99278
  unlinkSync as unlinkSync21,
98612
99279
  writeFileSync as writeFileSync41,
98613
99280
  writeSync as writeSync11
@@ -98621,7 +99288,7 @@ function pendingDir(agent, opts = {}) {
98621
99288
  }
98622
99289
  function ensurePendingDir(agent, opts = {}) {
98623
99290
  const dir = pendingDir(agent, opts);
98624
- mkdirSync57(dir, { recursive: true });
99291
+ mkdirSync58(dir, { recursive: true });
98625
99292
  return dir;
98626
99293
  }
98627
99294
  function newStageId() {
@@ -98643,14 +99310,14 @@ function stagePendingScheduleEntry(opts) {
98643
99310
  };
98644
99311
  const yamlTmp = `${yamlPath}.tmp-${process.pid}`;
98645
99312
  {
98646
- const fd = openSync18(yamlTmp, "w", 384);
99313
+ const fd = openSync19(yamlTmp, "w", 384);
98647
99314
  try {
98648
99315
  writeSync11(fd, opts.yamlText);
98649
99316
  fsyncSync9(fd);
98650
99317
  } finally {
98651
- closeSync18(fd);
99318
+ closeSync19(fd);
98652
99319
  }
98653
- renameSync25(yamlTmp, yamlPath);
99320
+ renameSync26(yamlTmp, yamlPath);
98654
99321
  }
98655
99322
  writeFileSync41(metaPath, JSON.stringify(meta, null, 2) + `
98656
99323
  `, { mode: 384 });
@@ -98658,7 +99325,7 @@ function stagePendingScheduleEntry(opts) {
98658
99325
  }
98659
99326
  function listPendingScheduleEntries(agent, opts = {}) {
98660
99327
  const dir = pendingDir(agent, opts);
98661
- if (!existsSync98(dir))
99328
+ if (!existsSync99(dir))
98662
99329
  return [];
98663
99330
  const out = [];
98664
99331
  for (const name of readdirSync36(dir).sort()) {
@@ -98667,7 +99334,7 @@ function listPendingScheduleEntries(agent, opts = {}) {
98667
99334
  const stageId = name.slice(0, -".meta.json".length);
98668
99335
  const metaPath = join101(dir, name);
98669
99336
  const yamlPath = join101(dir, `${stageId}.yaml`);
98670
- if (!existsSync98(yamlPath))
99337
+ if (!existsSync99(yamlPath))
98671
99338
  continue;
98672
99339
  try {
98673
99340
  const meta = JSON.parse(readFileSync88(metaPath, "utf-8"));
@@ -98686,10 +99353,10 @@ function commitPendingScheduleEntry(opts) {
98686
99353
  const slug = match.meta.entry.name ?? match.stageId;
98687
99354
  const paths = overlayPathsFor(opts.agent, { root: opts.root });
98688
99355
  const finalPath = join101(paths.scheduleDir, `${slug}.yaml`);
98689
- if (existsSync98(finalPath)) {
99356
+ if (existsSync99(finalPath)) {
98690
99357
  return { committed: false, reason: "slug_collision" };
98691
99358
  }
98692
- renameSync25(match.yamlPath, finalPath);
99359
+ renameSync26(match.yamlPath, finalPath);
98693
99360
  unlinkSync21(match.metaPath);
98694
99361
  return { committed: true, path: finalPath, slug };
98695
99362
  }
@@ -98708,7 +99375,7 @@ function denyPendingScheduleEntry(opts) {
98708
99375
  }
98709
99376
 
98710
99377
  // src/cli/agent-config-write.ts
98711
- import { existsSync as existsSync99, readFileSync as readFileSync89 } from "node:fs";
99378
+ import { existsSync as existsSync100, readFileSync as readFileSync89 } from "node:fs";
98712
99379
  import { execFileSync as execFileSync30 } from "node:child_process";
98713
99380
 
98714
99381
  // src/scheduler/schedule-report.ts
@@ -99098,7 +99765,7 @@ function scheduleRemove(opts) {
99098
99765
  }
99099
99766
  let priorContent = null;
99100
99767
  try {
99101
- if (existsSync99(match.path))
99768
+ if (existsSync100(match.path))
99102
99769
  priorContent = readFileSync89(match.path, "utf-8");
99103
99770
  } catch {}
99104
99771
  deleteOverlayEntry(agent, match.slug, { root: opts.root });
@@ -99302,7 +99969,7 @@ function registerAgentConfigWriteCommands(program3) {
99302
99969
  }
99303
99970
  let blob;
99304
99971
  if (opts.jsonl) {
99305
- blob = existsSync99(opts.jsonl) ? readFileSync89(opts.jsonl, "utf-8") : "";
99972
+ blob = existsSync100(opts.jsonl) ? readFileSync89(opts.jsonl, "utf-8") : "";
99306
99973
  } else {
99307
99974
  try {
99308
99975
  blob = execFileSync30("docker", ["exec", `switchroom-${agent}`, "cat", "/state/agent/scheduler.jsonl"], {
@@ -99334,7 +100001,7 @@ function registerAgentConfigWriteCommands(program3) {
99334
100001
 
99335
100002
  // src/cli/agent-config-skill-write.ts
99336
100003
  var import_yaml22 = __toESM(require_dist(), 1);
99337
- import { existsSync as existsSync100 } from "node:fs";
100004
+ import { existsSync as existsSync101 } from "node:fs";
99338
100005
  init_reconcile_default_skills();
99339
100006
  init_agent_config();
99340
100007
  var import_yaml23 = __toESM(require_dist(), 1);
@@ -99414,7 +100081,7 @@ function skillInstall(opts) {
99414
100081
  }
99415
100082
  const poolDir = opts.bundledSkillsPoolDir ?? getBundledSkillsPoolDir();
99416
100083
  const skillPath = join102(poolDir, skillName);
99417
- if (!existsSync100(skillPath)) {
100084
+ if (!existsSync101(skillPath)) {
99418
100085
  return err("E_SKILL_NOT_FOUND", `bundled skill not found at ${skillPath}. The operator needs to ` + `place the skill at this path before the agent can opt in.`);
99419
100086
  }
99420
100087
  const yamlText = import_yaml22.stringify({ skills: [skillName] });
@@ -99577,23 +100244,23 @@ function registerAgentConfigSkillWriteCommands(program3) {
99577
100244
 
99578
100245
  // src/cli/skill.ts
99579
100246
  import {
99580
- closeSync as closeSync19,
99581
- existsSync as existsSync101,
100247
+ closeSync as closeSync20,
100248
+ existsSync as existsSync102,
99582
100249
  lstatSync as lstatSync12,
99583
- mkdirSync as mkdirSync58,
100250
+ mkdirSync as mkdirSync59,
99584
100251
  mkdtempSync as mkdtempSync5,
99585
- openSync as openSync19,
100252
+ openSync as openSync20,
99586
100253
  readFileSync as readFileSync90,
99587
100254
  readdirSync as readdirSync37,
99588
100255
  realpathSync as realpathSync7,
99589
- renameSync as renameSync26,
99590
- rmSync as rmSync18,
100256
+ renameSync as renameSync27,
100257
+ rmSync as rmSync19,
99591
100258
  statSync as statSync54,
99592
100259
  writeFileSync as writeFileSync42
99593
100260
  } from "node:fs";
99594
100261
  import { tmpdir as tmpdir6, homedir as homedir53 } from "node:os";
99595
- import { dirname as dirname39, join as join103, relative as relative4, resolve as resolve58 } from "node:path";
99596
- import { spawnSync as spawnSync18 } from "node:child_process";
100262
+ import { dirname as dirname40, join as join103, relative as relative4, resolve as resolve58 } from "node:path";
100263
+ import { spawnSync as spawnSync20 } from "node:child_process";
99597
100264
 
99598
100265
  // src/cli/skill-common.ts
99599
100266
  var import_yaml24 = __toESM(require_dist(), 1);
@@ -99886,7 +100553,7 @@ function loadFromDir(dir) {
99886
100553
  function loadFromTarball(tarPath) {
99887
100554
  const isGz = tarPath.endsWith(".gz") || tarPath.endsWith(".tgz");
99888
100555
  const listFlags = isGz ? ["-tzf"] : ["-tf"];
99889
- const list2 = spawnSync18("tar", [...listFlags, tarPath], {
100556
+ const list2 = spawnSync20("tar", [...listFlags, tarPath], {
99890
100557
  encoding: "utf-8",
99891
100558
  stdio: ["ignore", "pipe", "pipe"]
99892
100559
  });
@@ -99903,7 +100570,7 @@ function loadFromTarball(tarPath) {
99903
100570
  const staging = mkdtempSync5(join103(tmpdir6(), "skill-apply-extract-"));
99904
100571
  try {
99905
100572
  const flags = isGz ? ["-xzf"] : ["-xf"];
99906
- const r = spawnSync18("tar", [
100573
+ const r = spawnSync20("tar", [
99907
100574
  ...flags,
99908
100575
  tarPath,
99909
100576
  "-C",
@@ -99919,7 +100586,7 @@ function loadFromTarball(tarPath) {
99919
100586
  return loadFromDir(staging);
99920
100587
  } finally {
99921
100588
  try {
99922
- rmSync18(staging, { recursive: true, force: true });
100589
+ rmSync19(staging, { recursive: true, force: true });
99923
100590
  } catch {}
99924
100591
  }
99925
100592
  }
@@ -99978,7 +100645,7 @@ function validatePayload(name, files) {
99978
100645
  if (errors2.length === 0) {
99979
100646
  for (const [path10, content] of Object.entries(files)) {
99980
100647
  if (SH_SCRIPT_RE2.test(path10)) {
99981
- const r = spawnSync18("bash", ["-n"], {
100648
+ const r = spawnSync20("bash", ["-n"], {
99982
100649
  input: content,
99983
100650
  encoding: "utf-8"
99984
100651
  });
@@ -99990,14 +100657,14 @@ function validatePayload(name, files) {
99990
100657
  const tmpPy = join103(tmp, "check.py");
99991
100658
  try {
99992
100659
  writeFileSync42(tmpPy, content);
99993
- const r = spawnSync18("python3", ["-m", "py_compile", tmpPy], {
100660
+ const r = spawnSync20("python3", ["-m", "py_compile", tmpPy], {
99994
100661
  encoding: "utf-8"
99995
100662
  });
99996
100663
  if (r.status !== 0) {
99997
100664
  errors2.push(`${path10} fails \`python3 -m py_compile\` syntax check: ${(r.stderr ?? "").trim()}`);
99998
100665
  }
99999
100666
  } finally {
100000
- rmSync18(tmp, { recursive: true, force: true });
100667
+ rmSync19(tmp, { recursive: true, force: true });
100001
100668
  }
100002
100669
  }
100003
100670
  }
@@ -100007,7 +100674,7 @@ function validatePayload(name, files) {
100007
100674
  function diffSummary(currentDir, files) {
100008
100675
  const lines = [];
100009
100676
  const currentFiles = {};
100010
- if (existsSync101(currentDir)) {
100677
+ if (existsSync102(currentDir)) {
100011
100678
  const walk2 = (sub) => {
100012
100679
  for (const ent of readdirSync37(sub, { withFileTypes: true })) {
100013
100680
  const full = join103(sub, ent.name);
@@ -100043,8 +100710,8 @@ function diffSummary(currentDir, files) {
100043
100710
  `);
100044
100711
  }
100045
100712
  function writePayload(poolDir, name, files) {
100046
- if (!existsSync101(poolDir)) {
100047
- mkdirSync58(poolDir, { recursive: true, mode: 493 });
100713
+ if (!existsSync102(poolDir)) {
100714
+ mkdirSync59(poolDir, { recursive: true, mode: 493 });
100048
100715
  }
100049
100716
  const target = join103(poolDir, name);
100050
100717
  let targetIsSymlink = false;
@@ -100062,12 +100729,12 @@ function writePayload(poolDir, name, files) {
100062
100729
  try {
100063
100730
  for (const [path10, content] of Object.entries(files)) {
100064
100731
  const full = join103(staging, path10);
100065
- mkdirSync58(dirname39(full), { recursive: true, mode: 493 });
100066
- const fd = openSync19(full, "wx");
100732
+ mkdirSync59(dirname40(full), { recursive: true, mode: 493 });
100733
+ const fd = openSync20(full, "wx");
100067
100734
  try {
100068
100735
  writeFileSync42(fd, content);
100069
100736
  } finally {
100070
- closeSync19(fd);
100737
+ closeSync20(fd);
100071
100738
  }
100072
100739
  if (SH_SCRIPT_RE2.test(path10) || PY_SCRIPT_RE2.test(path10)) {
100073
100740
  const fs7 = __require("node:fs");
@@ -100081,23 +100748,23 @@ function writePayload(poolDir, name, files) {
100081
100748
  } catch {}
100082
100749
  if (targetExists) {
100083
100750
  oldRename = `${target}.skill-apply-old-${Date.now()}`;
100084
- renameSync26(target, oldRename);
100751
+ renameSync27(target, oldRename);
100085
100752
  }
100086
- renameSync26(staging, target);
100753
+ renameSync27(staging, target);
100087
100754
  if (oldRename) {
100088
- rmSync18(oldRename, { recursive: true, force: true });
100755
+ rmSync19(oldRename, { recursive: true, force: true });
100089
100756
  oldRename = null;
100090
100757
  }
100091
100758
  } catch (err2) {
100092
100759
  try {
100093
- rmSync18(staging, { recursive: true, force: true });
100760
+ rmSync19(staging, { recursive: true, force: true });
100094
100761
  } catch {}
100095
- if (oldRename && existsSync101(oldRename)) {
100762
+ if (oldRename && existsSync102(oldRename)) {
100096
100763
  try {
100097
- if (existsSync101(target)) {
100098
- rmSync18(target, { recursive: true, force: true });
100764
+ if (existsSync102(target)) {
100765
+ rmSync19(target, { recursive: true, force: true });
100099
100766
  }
100100
- renameSync26(oldRename, target);
100767
+ renameSync27(oldRename, target);
100101
100768
  } catch {}
100102
100769
  }
100103
100770
  throw err2;
@@ -100115,7 +100782,7 @@ function registerSkillCommand(program3) {
100115
100782
  files = loadFromStdin();
100116
100783
  } else {
100117
100784
  const fromPath = resolve58(opts.from);
100118
- if (!existsSync101(fromPath)) {
100785
+ if (!existsSync102(fromPath)) {
100119
100786
  fail4(`--from path does not exist: ${opts.from}`);
100120
100787
  }
100121
100788
  const st = statSync54(fromPath);
@@ -100153,7 +100820,7 @@ function registerSkillCommand(program3) {
100153
100820
  \u2713 Wrote ${name} to ${currentDir}`));
100154
100821
  const applyBin = process.argv[1] ?? "switchroom";
100155
100822
  console.log(source_default.gray(`Running \`switchroom apply --non-interactive\`...`));
100156
- const r = spawnSync18(process.argv0, [applyBin, "apply", "--non-interactive"], { stdio: "inherit" });
100823
+ const r = spawnSync20(process.argv0, [applyBin, "apply", "--non-interactive"], { stdio: "inherit" });
100157
100824
  if (r.status !== 0) {
100158
100825
  console.error(source_default.yellow(`(warning: \`switchroom apply\` exited ${r.status} \u2014 skill is ` + `in the pool but symlinks may not be refreshed. Re-run manually.)`));
100159
100826
  }
@@ -100170,23 +100837,23 @@ function sumBytes(files) {
100170
100837
  // src/cli/skill-personal.ts
100171
100838
  init_esm();
100172
100839
  import {
100173
- closeSync as closeSync20,
100174
- existsSync as existsSync102,
100840
+ closeSync as closeSync21,
100841
+ existsSync as existsSync103,
100175
100842
  lstatSync as lstatSync13,
100176
- mkdirSync as mkdirSync59,
100843
+ mkdirSync as mkdirSync60,
100177
100844
  mkdtempSync as mkdtempSync6,
100178
- openSync as openSync20,
100845
+ openSync as openSync21,
100179
100846
  readFileSync as readFileSync91,
100180
100847
  readdirSync as readdirSync38,
100181
- renameSync as renameSync27,
100182
- rmSync as rmSync19,
100848
+ renameSync as renameSync28,
100849
+ rmSync as rmSync20,
100183
100850
  statSync as statSync55,
100184
100851
  utimesSync,
100185
100852
  writeFileSync as writeFileSync43
100186
100853
  } from "node:fs";
100187
- import { dirname as dirname40, join as join104, relative as relative5, resolve as resolve59 } from "node:path";
100854
+ import { dirname as dirname41, join as join104, relative as relative5, resolve as resolve59 } from "node:path";
100188
100855
  import { homedir as homedir54, tmpdir as tmpdir7 } from "node:os";
100189
- import { spawnSync as spawnSync19 } from "node:child_process";
100856
+ import { spawnSync as spawnSync21 } from "node:child_process";
100190
100857
  init_helpers();
100191
100858
  init_agent_config();
100192
100859
  init_source();
@@ -100197,14 +100864,14 @@ var PERSONAL_SKILLS_SUBPATH = "personal-skills";
100197
100864
  function resolveConfigSkillsDir(agent) {
100198
100865
  const override = process.env.SWITCHROOM_CONFIG_DIR;
100199
100866
  const candidate = override ? resolve59(override) : join104(homedir54(), ".switchroom-config");
100200
- if (!existsSync102(candidate))
100867
+ if (!existsSync103(candidate))
100201
100868
  return null;
100202
100869
  return join104(candidate, "agents", agent, PERSONAL_SKILLS_SUBPATH);
100203
100870
  }
100204
100871
  var MIRROR_PRIOR_TTL_MS = 24 * 60 * 60 * 1000;
100205
100872
  function sweepMirrorPriors(configSkillsRoot) {
100206
100873
  try {
100207
- if (!existsSync102(configSkillsRoot))
100874
+ if (!existsSync103(configSkillsRoot))
100208
100875
  return;
100209
100876
  const now = Date.now();
100210
100877
  for (const ent of readdirSync38(configSkillsRoot)) {
@@ -100217,7 +100884,7 @@ function sweepMirrorPriors(configSkillsRoot) {
100217
100884
  if (now - ts < MIRROR_PRIOR_TTL_MS)
100218
100885
  continue;
100219
100886
  try {
100220
- rmSync19(join104(configSkillsRoot, ent), { recursive: true, force: true });
100887
+ rmSync20(join104(configSkillsRoot, ent), { recursive: true, force: true });
100221
100888
  } catch {}
100222
100889
  }
100223
100890
  } catch {}
@@ -100240,17 +100907,17 @@ function mirrorToConfigRepo(agent, name, liveSkillDir) {
100240
100907
  }
100241
100908
  if (liveSkillDir === null) {
100242
100909
  sweepMirrorPriors(configSkillsRoot);
100243
- if (existsSync102(dest)) {
100910
+ if (existsSync103(dest)) {
100244
100911
  const trash = join104(configSkillsRoot, `.${name}-trash-${Date.now()}`);
100245
- renameSync27(dest, trash);
100912
+ renameSync28(dest, trash);
100246
100913
  }
100247
100914
  return;
100248
100915
  }
100249
- mkdirSync59(configSkillsRoot, { recursive: true, mode: 493 });
100916
+ mkdirSync60(configSkillsRoot, { recursive: true, mode: 493 });
100250
100917
  sweepMirrorPriors(configSkillsRoot);
100251
100918
  const staging = mkdtempSync6(join104(configSkillsRoot, `.${name}-staging-`));
100252
100919
  const walk2 = (src, dst) => {
100253
- mkdirSync59(dst, { recursive: true, mode: 493 });
100920
+ mkdirSync60(dst, { recursive: true, mode: 493 });
100254
100921
  for (const ent of readdirSync38(src, { withFileTypes: true })) {
100255
100922
  const s = join104(src, ent.name);
100256
100923
  const d = join104(dst, ent.name);
@@ -100264,11 +100931,11 @@ function mirrorToConfigRepo(agent, name, liveSkillDir) {
100264
100931
  }
100265
100932
  };
100266
100933
  walk2(liveSkillDir, staging);
100267
- if (existsSync102(dest)) {
100934
+ if (existsSync103(dest)) {
100268
100935
  const prior = join104(configSkillsRoot, `.${name}-prior-${Date.now()}`);
100269
- renameSync27(dest, prior);
100936
+ renameSync28(dest, prior);
100270
100937
  }
100271
- renameSync27(staging, dest);
100938
+ renameSync28(staging, dest);
100272
100939
  } catch (err2) {
100273
100940
  process.stderr.write(source_default.yellow(`warning: mirror to ${dest} failed (${err2.message ?? err2}); ` + `live copy still works, but this skill is not version-controlled until next successful sync.
100274
100941
  `));
@@ -100305,7 +100972,7 @@ function trashDir(agentsRoot, agent) {
100305
100972
  }
100306
100973
  function countPersonalSkills(agentsRoot, agent) {
100307
100974
  const skillsDir = join104(agentsRoot, agent, ".claude", "skills");
100308
- if (!existsSync102(skillsDir))
100975
+ if (!existsSync103(skillsDir))
100309
100976
  return 0;
100310
100977
  let n = 0;
100311
100978
  for (const ent of readdirSync38(skillsDir, { withFileTypes: true })) {
@@ -100390,7 +101057,7 @@ function behavioralValidate(files) {
100390
101057
  const errors2 = [];
100391
101058
  for (const [path10, content] of Object.entries(files)) {
100392
101059
  if (SH_SCRIPT_RE.test(path10)) {
100393
- const r = spawnSync19("bash", ["-n"], { input: content, encoding: "utf-8" });
101060
+ const r = spawnSync21("bash", ["-n"], { input: content, encoding: "utf-8" });
100394
101061
  if (r.status !== 0) {
100395
101062
  errors2.push(`${path10} fails \`bash -n\`: ${(r.stderr ?? "").trim()}`);
100396
101063
  }
@@ -100399,14 +101066,14 @@ function behavioralValidate(files) {
100399
101066
  const tmpPy = join104(tmp, "check.py");
100400
101067
  try {
100401
101068
  writeFileSync43(tmpPy, content);
100402
- const r = spawnSync19("python3", ["-m", "py_compile", tmpPy], {
101069
+ const r = spawnSync21("python3", ["-m", "py_compile", tmpPy], {
100403
101070
  encoding: "utf-8"
100404
101071
  });
100405
101072
  if (r.status !== 0) {
100406
101073
  errors2.push(`${path10} fails \`python3 -m py_compile\`: ${(r.stderr ?? "").trim()}`);
100407
101074
  }
100408
101075
  } finally {
100409
- rmSync19(tmp, { recursive: true, force: true });
101076
+ rmSync20(tmp, { recursive: true, force: true });
100410
101077
  }
100411
101078
  }
100412
101079
  }
@@ -100414,7 +101081,7 @@ function behavioralValidate(files) {
100414
101081
  }
100415
101082
  function sweepTrash(agentsRoot, agent) {
100416
101083
  const trash = trashDir(agentsRoot, agent);
100417
- if (!existsSync102(trash))
101084
+ if (!existsSync103(trash))
100418
101085
  return;
100419
101086
  const now = Date.now();
100420
101087
  for (const ent of readdirSync38(trash, { withFileTypes: true })) {
@@ -100424,7 +101091,7 @@ function sweepTrash(agentsRoot, agent) {
100424
101091
  try {
100425
101092
  const st = statSync55(entPath);
100426
101093
  if (now - st.mtimeMs > TRASH_TTL_MS) {
100427
- rmSync19(entPath, { recursive: true, force: true });
101094
+ rmSync20(entPath, { recursive: true, force: true });
100428
101095
  }
100429
101096
  } catch {}
100430
101097
  }
@@ -100440,18 +101107,18 @@ function writePersonalSkill(targetDir, files) {
100440
101107
  if (targetIsSymlink) {
100441
101108
  fail5(`refusing to overwrite symlink at ${targetDir}; investigate manually`);
100442
101109
  }
100443
- mkdirSync59(dirname40(targetDir), { recursive: true, mode: 493 });
100444
- const staging = mkdtempSync6(join104(dirname40(targetDir), `.skill-personal-stage-`));
101110
+ mkdirSync60(dirname41(targetDir), { recursive: true, mode: 493 });
101111
+ const staging = mkdtempSync6(join104(dirname41(targetDir), `.skill-personal-stage-`));
100445
101112
  let oldRename = null;
100446
101113
  try {
100447
101114
  for (const [path10, content] of Object.entries(files)) {
100448
101115
  const full = join104(staging, path10);
100449
- mkdirSync59(dirname40(full), { recursive: true, mode: 493 });
100450
- const fd = openSync20(full, "wx");
101116
+ mkdirSync60(dirname41(full), { recursive: true, mode: 493 });
101117
+ const fd = openSync21(full, "wx");
100451
101118
  try {
100452
101119
  writeFileSync43(fd, content);
100453
101120
  } finally {
100454
- closeSync20(fd);
101121
+ closeSync21(fd);
100455
101122
  }
100456
101123
  if (SH_SCRIPT_RE.test(path10) || PY_SCRIPT_RE.test(path10)) {
100457
101124
  const fs7 = __require("node:fs");
@@ -100465,23 +101132,23 @@ function writePersonalSkill(targetDir, files) {
100465
101132
  } catch {}
100466
101133
  if (targetExists) {
100467
101134
  oldRename = `${targetDir}.personal-old-${Date.now()}`;
100468
- renameSync27(targetDir, oldRename);
101135
+ renameSync28(targetDir, oldRename);
100469
101136
  }
100470
- renameSync27(staging, targetDir);
101137
+ renameSync28(staging, targetDir);
100471
101138
  if (oldRename) {
100472
- rmSync19(oldRename, { recursive: true, force: true });
101139
+ rmSync20(oldRename, { recursive: true, force: true });
100473
101140
  oldRename = null;
100474
101141
  }
100475
101142
  } catch (err2) {
100476
101143
  try {
100477
- rmSync19(staging, { recursive: true, force: true });
101144
+ rmSync20(staging, { recursive: true, force: true });
100478
101145
  } catch {}
100479
- if (oldRename && existsSync102(oldRename)) {
101146
+ if (oldRename && existsSync103(oldRename)) {
100480
101147
  try {
100481
- if (existsSync102(targetDir)) {
100482
- rmSync19(targetDir, { recursive: true, force: true });
101148
+ if (existsSync103(targetDir)) {
101149
+ rmSync20(targetDir, { recursive: true, force: true });
100483
101150
  }
100484
- renameSync27(oldRename, targetDir);
101151
+ renameSync28(oldRename, targetDir);
100485
101152
  } catch {}
100486
101153
  }
100487
101154
  throw err2;
@@ -100544,7 +101211,7 @@ function loadFiles(opts) {
100544
101211
  return loadFromStdin2();
100545
101212
  }
100546
101213
  const p = resolve59(opts.from);
100547
- if (!existsSync102(p)) {
101214
+ if (!existsSync103(p)) {
100548
101215
  fail5(`--from path does not exist: ${opts.from}`);
100549
101216
  }
100550
101217
  const st = statSync55(p);
@@ -100606,7 +101273,7 @@ function resolveCloneSource(source, opts) {
100606
101273
  const slug = m[2];
100607
101274
  const root = tier === "bundled" ? opts.bundledRoot ?? defaultBundledRoot() : opts.sharedRoot ?? defaultSharedRoot();
100608
101275
  const dir = join104(root, slug);
100609
- if (!existsSync102(dir)) {
101276
+ if (!existsSync103(dir)) {
100610
101277
  fail5(`clone source ${JSON.stringify(source)} not found at ${dir}; ` + `check \`switchroom skill search --tier ${tier}\``, 1);
100611
101278
  }
100612
101279
  const st = lstatSync13(dir);
@@ -100730,10 +101397,10 @@ function removePersonalAction(name, opts) {
100730
101397
  throw err2;
100731
101398
  }
100732
101399
  const trashRoot2 = trashDir(agentsRoot, agent);
100733
- mkdirSync59(trashRoot2, { recursive: true, mode: 493 });
101400
+ mkdirSync60(trashRoot2, { recursive: true, mode: 493 });
100734
101401
  const ts = Date.now();
100735
101402
  const trashTarget = join104(trashRoot2, `${name}-${ts}`);
100736
- renameSync27(target, trashTarget);
101403
+ renameSync28(target, trashTarget);
100737
101404
  const now = new Date(ts);
100738
101405
  utimesSync(trashTarget, now, now);
100739
101406
  mirrorToConfigRepo(agent, name, null);
@@ -100753,7 +101420,7 @@ function listPersonalAction(opts) {
100753
101420
  sweepTrash(agentsRoot, agent);
100754
101421
  const skillsDir = join104(agentsRoot, agent, ".claude", "skills");
100755
101422
  const personal = [];
100756
- if (existsSync102(skillsDir)) {
101423
+ if (existsSync103(skillsDir)) {
100757
101424
  for (const ent of readdirSync38(skillsDir, { withFileTypes: true })) {
100758
101425
  if (!ent.isDirectory())
100759
101426
  continue;
@@ -100886,7 +101553,7 @@ function registerSelfImproveProposeSkillCommand(program3) {
100886
101553
  init_esm();
100887
101554
  init_helpers();
100888
101555
  var import_yaml25 = __toESM(require_dist(), 1);
100889
- import { existsSync as existsSync103, readdirSync as readdirSync39, readFileSync as readFileSync93, statSync as statSync56 } from "node:fs";
101556
+ import { existsSync as existsSync104, readdirSync as readdirSync39, readFileSync as readFileSync93, statSync as statSync56 } from "node:fs";
100890
101557
  import { homedir as homedir56 } from "node:os";
100891
101558
  import { join as join106, resolve as resolve60 } from "node:path";
100892
101559
  var PERSONAL_PREFIX2 = "personal-";
@@ -100903,7 +101570,7 @@ function defaultBundledRoot2() {
100903
101570
  }
100904
101571
  function readSkillFrontmatter(skillDir) {
100905
101572
  const mdPath = join106(skillDir, "SKILL.md");
100906
- if (!existsSync103(mdPath))
101573
+ if (!existsSync104(mdPath))
100907
101574
  return null;
100908
101575
  let content;
100909
101576
  try {
@@ -100947,7 +101614,7 @@ function listPersonalSkills(agent, agentsRoot = defaultAgentsRoot()) {
100947
101614
  if (!AGENT_NAME_RE3.test(agent))
100948
101615
  return [];
100949
101616
  const skillsDir = join106(agentsRoot, agent, ".claude/skills");
100950
- if (!existsSync103(skillsDir))
101617
+ if (!existsSync104(skillsDir))
100951
101618
  return [];
100952
101619
  const out = [];
100953
101620
  let entries;
@@ -100985,7 +101652,7 @@ function listPersonalSkills(agent, agentsRoot = defaultAgentsRoot()) {
100985
101652
  return out;
100986
101653
  }
100987
101654
  function listSharedSkills(sharedRoot = defaultSharedRoot2()) {
100988
- if (!existsSync103(sharedRoot))
101655
+ if (!existsSync104(sharedRoot))
100989
101656
  return [];
100990
101657
  const out = [];
100991
101658
  let entries;
@@ -101023,7 +101690,7 @@ function listSharedSkills(sharedRoot = defaultSharedRoot2()) {
101023
101690
  return out;
101024
101691
  }
101025
101692
  function listBundledSkills(bundledRoot = defaultBundledRoot2()) {
101026
- if (!existsSync103(bundledRoot))
101693
+ if (!existsSync104(bundledRoot))
101027
101694
  return [];
101028
101695
  const out = [];
101029
101696
  let entries;
@@ -101181,24 +101848,24 @@ init_source();
101181
101848
  init_helpers();
101182
101849
  init_operator_uid();
101183
101850
  import {
101184
- existsSync as existsSync105,
101185
- mkdirSync as mkdirSync60,
101851
+ existsSync as existsSync106,
101852
+ mkdirSync as mkdirSync61,
101186
101853
  readdirSync as readdirSync40,
101187
101854
  writeFileSync as writeFileSync44,
101188
101855
  statSync as statSync57,
101189
101856
  lstatSync as lstatSync14,
101190
101857
  realpathSync as realpathSync8,
101191
- copyFileSync as copyFileSync13
101858
+ copyFileSync as copyFileSync14
101192
101859
  } from "node:fs";
101193
101860
  import { homedir as homedir57 } from "node:os";
101194
101861
  import { join as join107 } from "node:path";
101195
- import { spawnSync as spawnSync22 } from "node:child_process";
101862
+ import { spawnSync as spawnSync24 } from "node:child_process";
101196
101863
 
101197
101864
  // src/cli/singleton-stale-cleanup.ts
101198
- import { spawnSync as spawnSync21 } from "node:child_process";
101865
+ import { spawnSync as spawnSync23 } from "node:child_process";
101199
101866
  function makeDockerRunner() {
101200
101867
  return (args) => {
101201
- const r = spawnSync21("docker", args, { encoding: "utf8" });
101868
+ const r = spawnSync23("docker", args, { encoding: "utf8" });
101202
101869
  return {
101203
101870
  ok: r.status === 0,
101204
101871
  stdout: r.stdout ?? "",
@@ -101557,7 +102224,7 @@ function resolveHostdSkillsTarget(hostHome) {
101557
102224
  console.warn(`switchroom hostd install: ~/.switchroom/skills is a symlink whose target ` + `does not resolve (dangling) \u2014 skipping the skills bind mount. Bundled ` + `skills will be unavailable to rollout/update until the symlink is fixed.`);
101558
102225
  return;
101559
102226
  }
101560
- if (!existsSync105(target)) {
102227
+ if (!existsSync106(target)) {
101561
102228
  console.warn(`switchroom hostd install: ~/.switchroom/skills resolves to "${target}", ` + `which does not exist \u2014 skipping the skills bind mount. Bundled skills ` + `will be unavailable to rollout/update until the symlink target exists.`);
101562
102229
  return;
101563
102230
  }
@@ -101571,15 +102238,15 @@ function hostdComposePath() {
101571
102238
  }
101572
102239
  function backupExistingCompose() {
101573
102240
  const p = hostdComposePath();
101574
- if (!existsSync105(p))
102241
+ if (!existsSync106(p))
101575
102242
  return null;
101576
102243
  const ts = new Date().toISOString().replace(/[:.]/g, "-");
101577
102244
  const bak = `${p}.bak-${ts}`;
101578
- copyFileSync13(p, bak);
102245
+ copyFileSync14(p, bak);
101579
102246
  return bak;
101580
102247
  }
101581
102248
  function runDocker(args) {
101582
- const r = spawnSync22("docker", args, { encoding: "utf8" });
102249
+ const r = spawnSync24("docker", args, { encoding: "utf8" });
101583
102250
  return {
101584
102251
  ok: r.status === 0,
101585
102252
  stdout: r.stdout ?? "",
@@ -101604,7 +102271,7 @@ async function doInstall(opts, program3) {
101604
102271
  }
101605
102272
  const dir = hostdDir();
101606
102273
  const composePath = hostdComposePath();
101607
- mkdirSync60(dir, { recursive: true });
102274
+ mkdirSync61(dir, { recursive: true });
101608
102275
  const imageTag = resolveHostdImageTag(opts.tag, cfg.release);
101609
102276
  const guard = checkDowngrade({
101610
102277
  container: "switchroom-hostd",
@@ -101669,7 +102336,7 @@ function doStatus() {
101669
102336
  const composeYml = hostdComposePath();
101670
102337
  console.log(source_default.bold("switchroom-hostd"));
101671
102338
  console.log("");
101672
- if (!existsSync105(composeYml)) {
102339
+ if (!existsSync106(composeYml)) {
101673
102340
  console.log(source_default.yellow(" compose: not installed"));
101674
102341
  console.log(source_default.dim(" run `switchroom hostd install` to set up."));
101675
102342
  return;
@@ -101690,14 +102357,14 @@ function doStatus() {
101690
102357
  } else {
101691
102358
  console.log(source_default.green(` container: ${ps.stdout.trim()}`));
101692
102359
  }
101693
- if (existsSync105(dir)) {
102360
+ if (existsSync106(dir)) {
101694
102361
  const entries = [];
101695
102362
  try {
101696
102363
  for (const name of readdirSync40(dir)) {
101697
102364
  if (name === "docker-compose.yml" || name.startsWith("docker-compose.yml."))
101698
102365
  continue;
101699
102366
  const sockPath = join107(dir, name, "sock");
101700
- if (existsSync105(sockPath)) {
102367
+ if (existsSync106(sockPath)) {
101701
102368
  const st = statSync57(sockPath);
101702
102369
  if ((st.mode & 61440) === 49152) {
101703
102370
  entries.push(`${name} \u2192 ${sockPath}`);
@@ -101716,7 +102383,7 @@ function doStatus() {
101716
102383
  }
101717
102384
  function doUninstall() {
101718
102385
  const composeYml = hostdComposePath();
101719
- if (!existsSync105(composeYml)) {
102386
+ if (!existsSync106(composeYml)) {
101720
102387
  console.log(source_default.yellow(" No hostd install detected (no compose file at this path)."));
101721
102388
  return;
101722
102389
  }
@@ -101740,7 +102407,7 @@ function registerHostdCommand(program3) {
101740
102407
  hostd.command("uninstall").description("Stop the hostd container. Leaves the compose file in place for re-install.").action(() => doUninstall());
101741
102408
  hostd.command("audit").description("Tail and filter the hostd audit log (privileged-verb call history)").option("--tail <n>", "Number of matching entries to show (default: 50)", "50").option("--agent <name>", "Filter to a specific caller agent").option("--op <verb>", "Filter to a specific hostd verb (e.g. update_apply, agent_restart)").option("--error", "Show only failed (error/denied) entries").option("--verbose", "Show the captured stderr / error tail under each failed row").option("--path <file>", "Override audit log path (for debugging)").action((opts) => {
101742
102409
  const logPath = opts.path ?? defaultAuditLogPath2();
101743
- if (!existsSync105(logPath)) {
102410
+ if (!existsSync106(logPath)) {
101744
102411
  console.error(source_default.yellow(`Audit log not found at ${logPath}.`) + source_default.gray(`
101745
102412
  The log is created when hostd handles its first privileged-verb request.`));
101746
102413
  return;
@@ -101788,10 +102455,10 @@ The log is created when hostd handles its first privileged-verb request.`));
101788
102455
  init_source();
101789
102456
  init_helpers();
101790
102457
  init_operator_uid();
101791
- import { chownSync as chownSync10, existsSync as existsSync106, mkdirSync as mkdirSync61, writeFileSync as writeFileSync45, copyFileSync as copyFileSync14 } from "node:fs";
102458
+ import { chownSync as chownSync10, existsSync as existsSync107, mkdirSync as mkdirSync62, writeFileSync as writeFileSync45, copyFileSync as copyFileSync15 } from "node:fs";
101792
102459
  import { homedir as homedir58 } from "node:os";
101793
102460
  import { join as join108 } from "node:path";
101794
- import { spawnSync as spawnSync23 } from "node:child_process";
102461
+ import { spawnSync as spawnSync25 } from "node:child_process";
101795
102462
  function resolveWebImageTag(explicitTag, release) {
101796
102463
  if (explicitTag)
101797
102464
  return explicitTag;
@@ -101897,15 +102564,15 @@ function webdComposePath() {
101897
102564
  }
101898
102565
  function backupExistingCompose2() {
101899
102566
  const p = webdComposePath();
101900
- if (!existsSync106(p))
102567
+ if (!existsSync107(p))
101901
102568
  return null;
101902
102569
  const ts = new Date().toISOString().replace(/[:.]/g, "-");
101903
102570
  const bak = `${p}.bak-${ts}`;
101904
- copyFileSync14(p, bak);
102571
+ copyFileSync15(p, bak);
101905
102572
  return bak;
101906
102573
  }
101907
102574
  function runDocker2(args) {
101908
- const r = spawnSync23("docker", args, { encoding: "utf8" });
102575
+ const r = spawnSync25("docker", args, { encoding: "utf8" });
101909
102576
  return {
101910
102577
  ok: r.status === 0,
101911
102578
  stdout: r.stdout ?? "",
@@ -101922,7 +102589,7 @@ async function doInstall2(opts, program3) {
101922
102589
  }
101923
102590
  const dir = webdDir();
101924
102591
  const composePath = webdComposePath();
101925
- mkdirSync61(dir, { recursive: true });
102592
+ mkdirSync62(dir, { recursive: true });
101926
102593
  const cfg = getConfig(program3);
101927
102594
  const imageTag = resolveWebImageTag(opts.tag, cfg.release);
101928
102595
  const port = cfg.web_service?.port ?? 8080;
@@ -101999,7 +102666,7 @@ function doStatus2() {
101999
102666
  const composeYml = webdComposePath();
102000
102667
  console.log(source_default.bold("switchroom-web"));
102001
102668
  console.log("");
102002
- if (!existsSync106(composeYml)) {
102669
+ if (!existsSync107(composeYml)) {
102003
102670
  console.log(source_default.yellow(" compose: not installed"));
102004
102671
  console.log(source_default.dim(" run `switchroom webd install` to set up."));
102005
102672
  return;
@@ -102023,7 +102690,7 @@ function doStatus2() {
102023
102690
  }
102024
102691
  function doUninstall2() {
102025
102692
  const composeYml = webdComposePath();
102026
- if (!existsSync106(composeYml)) {
102693
+ if (!existsSync107(composeYml)) {
102027
102694
  console.log(source_default.yellow(" No web-service install detected (no compose file at this path)."));
102028
102695
  return;
102029
102696
  }
@@ -102209,7 +102876,7 @@ function applyMountRepairs(items, deps) {
102209
102876
  function registerHostCommand(program3) {
102210
102877
  const host = program3.command("host").description("Host-level maintenance operations for switchroom");
102211
102878
  host.command("repair-mounts").description("Detect and remove known auto-dir artifacts left by a container-context deploy " + "(2026-06-23 outage class). Default: dry-run. Pass --yes to apply.").option("--yes", "Actually perform the removals (default is dry-run)").action(async (opts) => {
102212
- const { rmdirSync: rmdirSync2, rmSync: rmSync20, lstatSync: lstatSync15, readdirSync: readdirSync41 } = await import("node:fs");
102879
+ const { rmdirSync: rmdirSync2, rmSync: rmSync21, lstatSync: lstatSync15, readdirSync: readdirSync41 } = await import("node:fs");
102213
102880
  const probe2 = {
102214
102881
  lstat(path10) {
102215
102882
  try {
@@ -102268,7 +102935,7 @@ ${safeItems.length} item(s) would be removed. Run with --yes to apply.`));
102268
102935
  rmdirSync2(path10);
102269
102936
  },
102270
102937
  rmRf(path10) {
102271
- rmSync20(path10, { recursive: true, force: true });
102938
+ rmSync21(path10, { recursive: true, force: true });
102272
102939
  }
102273
102940
  };
102274
102941
  const results = applyMountRepairs(safeItems, applyDeps);
@@ -102298,13 +102965,13 @@ init_helpers();
102298
102965
  init_scan();
102299
102966
 
102300
102967
  // src/fleet-health/gh-sync.ts
102301
- import { spawnSync as spawnSync24 } from "node:child_process";
102968
+ import { spawnSync as spawnSync26 } from "node:child_process";
102302
102969
  var LABEL = "fleet-health";
102303
102970
  function defaultGhDeps(log) {
102304
102971
  return {
102305
102972
  log,
102306
102973
  run: (args) => {
102307
- const r = spawnSync24("gh", args, { encoding: "utf-8" });
102974
+ const r = spawnSync26("gh", args, { encoding: "utf-8" });
102308
102975
  return {
102309
102976
  ok: r.status === 0,
102310
102977
  stdout: (r.stdout ?? "").trim(),
@@ -102509,7 +103176,7 @@ function printDeepDiveBrief(targets) {
102509
103176
 
102510
103177
  // src/cli/hindsight-watch.ts
102511
103178
  init_source();
102512
- import { existsSync as existsSync111, readdirSync as readdirSync45 } from "node:fs";
103179
+ import { existsSync as existsSync112, readdirSync as readdirSync45 } from "node:fs";
102513
103180
  import { homedir as homedir62 } from "node:os";
102514
103181
  import { join as join111, resolve as resolve63 } from "node:path";
102515
103182
 
@@ -102579,7 +103246,7 @@ function postOperatorNoticeViaGateways(candidates, text, log, connectTimeoutMs =
102579
103246
  init_install_cron();
102580
103247
 
102581
103248
  // src/hindsight-watch/probe.ts
102582
- import { spawnSync as spawnSync25 } from "node:child_process";
103249
+ import { spawnSync as spawnSync27 } from "node:child_process";
102583
103250
  import { readFileSync as readFileSync98, readdirSync as readdirSync44, statSync as statSync59 } from "node:fs";
102584
103251
  import { homedir as homedir61 } from "node:os";
102585
103252
  import { resolve as resolve62 } from "node:path";
@@ -102708,7 +103375,7 @@ function readLlmSignals(series) {
102708
103375
  }
102709
103376
 
102710
103377
  // src/hindsight-watch/recall-log.ts
102711
- import { closeSync as closeSync21, existsSync as existsSync110, openSync as openSync21, readdirSync as readdirSync43, readSync as readSync5, statSync as statSync58 } from "node:fs";
103378
+ import { closeSync as closeSync22, existsSync as existsSync111, openSync as openSync22, readdirSync as readdirSync43, readSync as readSync6, statSync as statSync58 } from "node:fs";
102712
103379
  import { join as join110 } from "node:path";
102713
103380
  var RECALL_WINDOW_ROWS = 200;
102714
103381
  var RECALL_MAX_TAIL_BYTES = 1024 * 1024;
@@ -102717,7 +103384,7 @@ function recallLogPath2(agentDir) {
102717
103384
  return join110(agentDir, ".claude", "plugins", "data", "hindsight-memory-inline", "state", "recall_log.jsonl");
102718
103385
  }
102719
103386
  function readRecallLogTail2(path10, windowRows = RECALL_WINDOW_ROWS) {
102720
- if (!existsSync110(path10))
103387
+ if (!existsSync111(path10))
102721
103388
  return [];
102722
103389
  let text;
102723
103390
  let truncatedHead = false;
@@ -102728,10 +103395,10 @@ function readRecallLogTail2(path10, windowRows = RECALL_WINDOW_ROWS) {
102728
103395
  truncatedHead = start > 0;
102729
103396
  const length = size - start;
102730
103397
  const buf = Buffer.alloc(length);
102731
- fd = openSync21(path10, "r");
103398
+ fd = openSync22(path10, "r");
102732
103399
  let read = 0;
102733
103400
  while (read < length) {
102734
- const n = readSync5(fd, buf, read, length - read, start + read);
103401
+ const n = readSync6(fd, buf, read, length - read, start + read);
102735
103402
  if (n <= 0)
102736
103403
  break;
102737
103404
  read += n;
@@ -102742,7 +103409,7 @@ function readRecallLogTail2(path10, windowRows = RECALL_WINDOW_ROWS) {
102742
103409
  } finally {
102743
103410
  if (fd !== undefined) {
102744
103411
  try {
102745
- closeSync21(fd);
103412
+ closeSync22(fd);
102746
103413
  } catch {}
102747
103414
  }
102748
103415
  }
@@ -102929,7 +103596,7 @@ async function probeMetrics(url = DEFAULT_METRICS_URL, fetchImpl = fetch) {
102929
103596
  }
102930
103597
  }
102931
103598
  var defaultRunner4 = (cmd, args) => {
102932
- const r = spawnSync25(cmd, args, { stdio: "pipe", timeout: 1e4, encoding: "utf8" });
103599
+ const r = spawnSync27(cmd, args, { stdio: "pipe", timeout: 1e4, encoding: "utf8" });
102933
103600
  if (r.error)
102934
103601
  return { status: null, stdout: "", stderr: r.error.message };
102935
103602
  return { status: r.status, stdout: r.stdout ?? "", stderr: r.stderr ?? "" };
@@ -103749,7 +104416,7 @@ async function notifyOperator(agentsDir, text, log) {
103749
104416
  try {
103750
104417
  for (const name of readdirSync45(agentsDir).sort()) {
103751
104418
  const sock = resolve63(agentsDir, name, "telegram", "gateway.sock");
103752
- if (existsSync111(sock))
104419
+ if (existsSync112(sock))
103753
104420
  candidates.push({ agent: name, sock });
103754
104421
  }
103755
104422
  } catch (e) {
@@ -103765,13 +104432,13 @@ async function notifyOperator(agentsDir, text, log) {
103765
104432
 
103766
104433
  // src/cli/openrouter-watch.ts
103767
104434
  init_source();
103768
- import { existsSync as existsSync113, readdirSync as readdirSync46 } from "node:fs";
104435
+ import { existsSync as existsSync114, readdirSync as readdirSync46 } from "node:fs";
103769
104436
  import { homedir as homedir64 } from "node:os";
103770
104437
  import { join as join112, resolve as resolve65 } from "node:path";
103771
104438
 
103772
104439
  // src/openrouter/install-cron.ts
103773
- import { existsSync as existsSync112, mkdirSync as mkdirSync63, readFileSync as readFileSync99, renameSync as renameSync28, writeFileSync as writeFileSync47 } from "node:fs";
103774
- import { dirname as dirname42 } from "node:path";
104440
+ import { existsSync as existsSync113, mkdirSync as mkdirSync64, readFileSync as readFileSync99, renameSync as renameSync29, writeFileSync as writeFileSync47 } from "node:fs";
104441
+ import { dirname as dirname43 } from "node:path";
103775
104442
  var CRON_PATH2 = "/etc/cron.d/openrouter-watch";
103776
104443
  var CRON_SCHEDULE2 = "17 * * * *";
103777
104444
  var CRON_LOG_PATH2 = "/var/log/openrouter-watch.log";
@@ -103788,23 +104455,23 @@ function renderCron2(opts) {
103788
104455
  function installCron2(opts) {
103789
104456
  const path10 = opts.path ?? CRON_PATH2;
103790
104457
  const content = renderCron2(opts);
103791
- if (existsSync112(path10)) {
104458
+ if (existsSync113(path10)) {
103792
104459
  try {
103793
104460
  if (readFileSync99(path10, "utf8") === content) {
103794
104461
  return { status: "unchanged", path: path10, content };
103795
104462
  }
103796
104463
  } catch {}
103797
104464
  }
103798
- mkdirSync63(dirname42(path10), { recursive: true });
104465
+ mkdirSync64(dirname43(path10), { recursive: true });
103799
104466
  const tmp = `${path10}.${process.pid}.tmp`;
103800
104467
  writeFileSync47(tmp, content, { mode: 420 });
103801
- renameSync28(tmp, path10);
104468
+ renameSync29(tmp, path10);
103802
104469
  return { status: "installed", path: path10, content };
103803
104470
  }
103804
104471
  // src/openrouter/state.ts
103805
- import { mkdirSync as mkdirSync64, readFileSync as readFileSync100, renameSync as renameSync29, writeFileSync as writeFileSync48 } from "node:fs";
104472
+ import { mkdirSync as mkdirSync65, readFileSync as readFileSync100, renameSync as renameSync30, writeFileSync as writeFileSync48 } from "node:fs";
103806
104473
  import { homedir as homedir63 } from "node:os";
103807
- import { dirname as dirname43, resolve as resolve64 } from "node:path";
104474
+ import { dirname as dirname44, resolve as resolve64 } from "node:path";
103808
104475
  function defaultStatePath3(home2 = process.env.SWITCHROOM_HOME ?? process.env.HOME ?? homedir63()) {
103809
104476
  return resolve64(home2, ".switchroom", "openrouter-watch", "state.json");
103810
104477
  }
@@ -103831,12 +104498,12 @@ function loadState2(path10) {
103831
104498
  return out;
103832
104499
  }
103833
104500
  function saveState2(path10, state) {
103834
- mkdirSync64(dirname43(path10), { recursive: true, mode: 448 });
104501
+ mkdirSync65(dirname44(path10), { recursive: true, mode: 448 });
103835
104502
  const tmp = `${path10}.${process.pid}.tmp`;
103836
104503
  const payload = { v: 1, ...state };
103837
104504
  writeFileSync48(tmp, JSON.stringify(payload, null, 2) + `
103838
104505
  `, { mode: 384 });
103839
- renameSync29(tmp, path10);
104506
+ renameSync30(tmp, path10);
103840
104507
  }
103841
104508
 
103842
104509
  // src/openrouter/watch.ts
@@ -104021,7 +104688,7 @@ async function notifyOperator2(agentsDir, text, log) {
104021
104688
  try {
104022
104689
  for (const name of readdirSync46(agentsDir).sort()) {
104023
104690
  const sock = resolve65(agentsDir, name, "telegram", "gateway.sock");
104024
- if (existsSync113(sock))
104691
+ if (existsSync114(sock))
104025
104692
  candidates.push({ agent: name, sock });
104026
104693
  }
104027
104694
  } catch (e) {