switchroom 0.19.43 → 0.19.44

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.
@@ -10994,7 +10994,7 @@ var init_observation_scopes = __esm(() => {
10994
10994
  });
10995
10995
 
10996
10996
  // src/config/schema.ts
10997
- var CodeRepoEntrySchema, AgentBindMountSchema, HttpDiffPollSchema, PollSpecSchema, TelegramMessageActionSchema, WebhookActionSchema, ActionSpecSchema, ScheduleEntrySchema, AgentSoulSchema, AgentToolsSchema, ObservationScopesSchema, ObservationScopeStrategySchema, AgentMemorySchema, HookEntrySchema, AgentHooksSchema, SubagentSchema, SessionSchema, SessionContinuitySchema, webhookDispatchRule, TelegramChannelSchema, ChannelsSchema, TIMEZONE_REGEX, ApproverIdSchema, GoogleWorkspaceTierSchema, GoogleWorkspaceConfigSchema, LiteLLMConfigSchema, HindsightPerOpLlmSchema, HindsightConfigSchema, MicrosoftWorkspaceConfigSchema, NotionWorkspaceConfigSchema, AgentGoogleWorkspaceConfigSchema, MicrosoftAccountEmailSchema, MicrosoftToolTokenSchema, MicrosoftAccountBindingSchema, AgentMicrosoftWorkspaceConfigSchema, AgentNotionWorkspaceConfigSchema, ReactionsSchema, ReactionDispatchSchema, releaseBlockFields, ReleaseBlock, RootReleaseBlock, NetworkIsolationSchema, servesField, knowsField, profileFields, ProfileSchema, _omitExtends, defaultsFields, AgentDefaultsSchema, AgentSchema, TelegramConfigSchema, MemoryBackendConfigSchema, VaultConfigSchema, QuotaConfigSchema, AutoReleaseCheckSchema, HostControlConfigSchema, WebServiceConfigSchema, FleetHealthConfigSchema, HostdConfigSchema, CronEgressSchema, CronConfigSchema, UserSchema, SwitchroomConfigSchema;
10997
+ var CodeRepoEntrySchema, AgentBindMountSchema, HttpDiffPollSchema, PollSpecSchema, TelegramMessageActionSchema, WebhookActionSchema, ActionSpecSchema, ScheduleEntrySchema, AgentSoulSchema, AgentToolsSchema, ObservationScopesSchema, ObservationScopeStrategySchema, AntiConfabulationDirectiveSchema, AgentMemorySchema, HookEntrySchema, AgentHooksSchema, SubagentSchema, SessionSchema, SessionContinuitySchema, webhookDispatchRule, TelegramChannelSchema, ChannelsSchema, TIMEZONE_REGEX, ApproverIdSchema, GoogleWorkspaceTierSchema, GoogleWorkspaceConfigSchema, LiteLLMConfigSchema, HindsightPerOpLlmSchema, HindsightConfigSchema, MicrosoftWorkspaceConfigSchema, NotionWorkspaceConfigSchema, AgentGoogleWorkspaceConfigSchema, MicrosoftAccountEmailSchema, MicrosoftToolTokenSchema, MicrosoftAccountBindingSchema, AgentMicrosoftWorkspaceConfigSchema, AgentNotionWorkspaceConfigSchema, ReactionsSchema, ReactionDispatchSchema, releaseBlockFields, ReleaseBlock, RootReleaseBlock, NetworkIsolationSchema, servesField, knowsField, profileFields, ProfileSchema, _omitExtends, defaultsFields, AgentDefaultsSchema, AgentSchema, TelegramConfigSchema, MemoryBackendConfigSchema, VaultConfigSchema, QuotaConfigSchema, AutoReleaseCheckSchema, HostControlConfigSchema, WebServiceConfigSchema, FleetHealthConfigSchema, HostdConfigSchema, CronEgressSchema, CronConfigSchema, UserSchema, SwitchroomConfigSchema;
10998
10998
  var init_schema = __esm(() => {
10999
10999
  init_zod();
11000
11000
  init_observation_scopes();
@@ -11111,6 +11111,7 @@ var init_schema = __esm(() => {
11111
11111
  }).optional();
11112
11112
  ObservationScopesSchema = exports_external.enum(OBSERVATION_SCOPES).optional().describe("Operator PIN for the per-row observation scope, overriding the " + 'default `curated` strategy (see docs/configuration.md). "shared" ' + "forces Hindsight's consolidation to write EVERY observation into ONE " + "global untagged scope — what several agents pooling one bank want so " + "their observations merge rather than sitting in parallel silos. " + '"combined" is the pin form of opting out (restores the pre-#4035 ' + "engine default). UNSET does NOT mean 'nothing on the wire': the " + "`curated` strategy still computes a scope per retain. Applies to " + "every retain path (Stop hook, " + "sidechain, boot reconcile, queue drain, backfill, session-handoff " + "mirror) and is carried on the queued payload, so a retain that fails " + "now and drains later still lands in this scope. Accepted values: " + `${OBSERVATION_SCOPES.join(", ")}. ` + "Cascade: override (per-agent wins over default).");
11113
11113
  ObservationScopeStrategySchema = exports_external.enum(OBSERVATION_SCOPE_STRATEGIES).optional().describe("Per-retain strategy that decides how the observation scope is " + 'computed when no `observation_scopes` pin is set. "curated" ' + "(the default since #4035) strips volatile per-session provenance " + "tags from each retain's consolidation scope — keeping the stable " + "semantic ones on the source fact — so an agent's observations dedup " + "and merge across sessions instead of pocketing one-per-session. " + '"shared" sends every retain to the one global untagged scope. ' + '"combined" / "off" opt OUT: no per-row scope goes on the wire, ' + "restoring the pre-feature engine default (byte-identical to an " + "unconfigured client). OMITTED BY DEFAULT: unset leaves the plugin's " + "own default (`curated`) in force. A manual `observation_scopes` pin " + "wins over this strategy. Accepted values: " + `${OBSERVATION_SCOPE_STRATEGIES.join(", ")}. ` + "Cascade: override (per-agent wins over default).");
11114
+ AntiConfabulationDirectiveSchema = exports_external.union([exports_external.boolean(), exports_external.string().min(1)]).optional().describe("The seeded anti-confabulation directive: reflect has no relevance " + "floor (`min_scores` is a /recall-only parameter), so a bank holding " + "nothing relevant still yields a confident answer, and a question that " + "presupposes a decision reliably produces one. Directives ARE applied " + "during reflect, so this is the lever. Unset or true (the default) " + "seeds and maintains switchroom's own text; false disables seeding and " + "leaves any existing directive untouched; a string is operator-authored " + "text that wins outright. Switchroom only ever upgrades directive text " + "byte-equal to a default it shipped, so editing the directive in the " + "bank is also a permanent override. " + "Cascade: override (per-agent wins over default).");
11114
11115
  AgentMemorySchema = exports_external.object({
11115
11116
  collection: exports_external.string().describe("Hindsight collection name for this agent"),
11116
11117
  auto_recall: exports_external.boolean().default(true).describe("Auto-search memories before each response"),
@@ -11146,6 +11147,7 @@ var init_schema = __esm(() => {
11146
11147
  empathy: exports_external.number().int().min(1).max(5).optional().describe("How much the bank weights emotional/relational context (1-5; engine default 3).")
11147
11148
  }).optional().describe("Personality traits (1-5 each) steering how this bank frames recall, " + "reflect, and observation synthesis — a coach leans empathy-high, a " + "lawyer/analyst leans skepticism/literalism-high. Maps to the engine's " + "flat `disposition_skepticism`/`_literalism`/`_empathy` fields. " + "Cascade: per-key merge (an agent overrides individual traits and " + "inherits the rest, matching `recall`)."),
11148
11149
  directive_capture_nudge: exports_external.boolean().optional().describe("Deterministic directive-capture nudge (issue #2848 Stage B). When " + "on (switchroom default true — Stage A measured a ~55% miss rate on " + "durable corrections), the auto-recall hook regex-detects correction " + '/ standing-rule-shaped inbound ("always/never …", "from now on …", ' + `"stop doing …", a stated preference, "that's wrong, it's …") and ` + "appends a terse advisory to the turn's context telling the model to " + "persist the rule with mcp__hindsight__create_directive if it IS " + "durable. Detection is pure regex — the model does the judgment " + "in-session and calls create_directive itself (no model callsite, no " + "silent hook-side write). Set false to disable per-agent. " + "Cascade: override (per-agent wins over default)."),
11150
+ anti_confabulation_directive: AntiConfabulationDirectiveSchema,
11149
11151
  observation_scopes: ObservationScopesSchema,
11150
11152
  observation_scope_strategy: ObservationScopeStrategySchema,
11151
11153
  recall: exports_external.object({
@@ -11416,7 +11418,7 @@ var init_schema = __esm(() => {
11416
11418
  reflect: HindsightPerOpLlmSchema.optional().describe("Per-op override for the `reflect` LLM op (synthesis / mental-model " + "refresh). Emits `HINDSIGHT_API_REFLECT_LLM_*`. Absent → uses global."),
11417
11419
  consolidation: HindsightPerOpLlmSchema.optional().describe("Per-op override for the `consolidation` LLM op (background memory " + "merge). Emits `HINDSIGHT_API_CONSOLIDATION_LLM_*`. Absent → global.")
11418
11420
  }).optional().describe("LLM knob for the hindsight container. The flat `provider`/`model` set " + "the global default (backward-compatible); optional `retain`/`reflect`/" + "`consolidation` blocks override individual ops. All fields optional; " + "unset fields fall back to the hard-coded defaults."),
11419
- env: exports_external.record(exports_external.union([exports_external.string(), exports_external.number(), exports_external.boolean()])).optional().describe("Operator overrides for switchroom's capability-gated Hindsight " + "performance defaults. Only the keys switchroom actually manages are " + "honoured (`HINDSIGHT_PERF_ENV_KEYS` in " + "src/setup/hindsight-perf-defaults.ts: RERANKER_LOCAL_FP16, " + "RERANKER_LOCAL_BATCH_SIZE, LLM_MAX_CONCURRENT, " + "RETAIN/CONSOLIDATION_LLM_MAX_CONCURRENT, LLM_STRICT_SCHEMA, " + "LLM_MAX_RETRIES, CONSOLIDATION_LLM_PARALLELISM, " + "MAX_OBSERVATIONS_PER_SCOPE, " + "RECALL_MAX_CANDIDATES_PER_SOURCE, LINK_EXPANSION_PER_ENTITY_LIMIT, " + "LINK_EXPANSION_TIMEOUT, LLM_REASONING_EFFORT, " + "RERANKER_LOCAL_BUCKET_BATCHING, RERANKER_MAX_CANDIDATES, " + "RERANKER_LOCAL_MAX_CONCURRENT, RECALL_MAX_CONCURRENT, " + "REFLECT_WALL_TIMEOUT, WORKER_CONSOLIDATION_RESERVED_SLOTS, " + "WORKER_CONSOLIDATION_SLOT_LIMIT, " + "CONSOLIDATION_MAX_MEMORIES_PER_ROUND, GRAPH_SEED_MIN_SIMILARITY, " + "LLM_SUPPORTS_MAX_ITEMS, RECENCY_DECAY_FUNCTION, " + "RECENCY_DECAY_HALFLIFE_DAYS — switchroom defaults recall's recency " + "curve to `exponential` with a 30-day half-life so a fact retained " + "today outranks a stale one, instead of upstream's near-flat " + "linear/365-day window), the override-only keys " + "switchroom manages but ships NO default for " + "(`HINDSIGHT_PERF_OVERRIDE_ONLY_KEYS`: " + "HINDSIGHT_API_WORKER_CONSOLIDATION_BANK_PRIORITY — a per-deployment " + "`bank-pattern:priority,...` map; unset means upstream's flat " + "created_at FIFO across banks; and " + "HINDSIGHT_CE_DECISIVE_RELATIVE_GAP — the rollback knob for " + "switchroom's CE-saturation damping patch, a float; >= ~0.65 backs the " + "damping out entirely, unset means the patch's own derived gap; and " + "HINDSIGHT_API_RECENCY_DECAY_LINEAR_WINDOW_DAYS — only read when the " + "decay function is `linear`, so switchroom ships no default for it but " + "still honours an operator who flips the function back; and " + "HINDSIGHT_API_WORKER_MAX_SLOTS — the worker poller's TOTAL in-flight " + "task budget, the pool WORKER_CONSOLIDATION_RESERVED_SLOTS reserves out of; " + "unset means upstream's own default; and " + "HINDSIGHT_API_WORKER_RETAIN_RESERVED_SLOTS — the reserved slot FLOOR for the " + "retain (memory write) lane, carved from that same total; unset means " + "upstream's own 0, i.e. no floor and retain competes for the shared " + "pool; and " + "the pre-0.8.6 name HINDSIGHT_API_WORKER_<TYPE>_MAX_SLOTS is still " + "accepted for every operation type and normalised to " + "..._RESERVED_SLOTS on the way in, because setting both names for " + "one type is a hard boot failure in the engine and switchroom " + "therefore emits only the canonical name; and " + "HINDSIGHT_API_SEMANTIC_MIN_SIMILARITY — the cosine-similarity floor a " + "candidate must reach to be returned by the semantic retrieval arm at " + "all (0..1); unset means upstream's own 0.3, and where it should sit " + "depends on the bank's embedding model and phrasing diversity, so " + "switchroom ships no opinion; and " + "HINDSIGHT_MCP_RECALL_BUDGET_MODE — the rollback knob for switchroom's " + "mcp-recall-token-budget image patch; `legacy` restores upstream's " + "exact pre-patch recall returns, anything else or unset is the " + "honest-envelope mode; and " + "HINDSIGHT_API_LLM_TEMPERATURE_REFLECT — upstream's own per-op reflect " + "temperature knob, made live by switchroom's reflect-temperature image " + "patch; a float, or `none` to omit the kwarg (provider default, " + "upstream's accidental pre-patch behaviour); unset means the image's " + "baked default 0.1), plus the " + "embedded-PostgreSQL (pg0) sizing keys switchroom manages in " + "src/setup/hindsight-pg-defaults.ts (`HINDSIGHT_PG_ENV_KEYS`: " + "SWITCHROOM_HINDSIGHT_PG_EFFECTIVE_CACHE_SIZE, " + "SWITCHROOM_HINDSIGHT_PG_SHARED_BUFFERS — a postgres size string such " + "as `4GB`, or the sentinel `off` to leave pg0's own default for that " + "one knob). A value set here " + "REPLACES switchroom's default and is emitted even when the gating " + "capability is absent, so an operator can always force a knob. Other " + "`HINDSIGHT_API_*` keys are deliberately IGNORED — a blanket " + "passthrough would collide with the vars startHindsight() derives " + "itself (HINDSIGHT_API_PORT, the retain token/deadline budget).")
11421
+ env: exports_external.record(exports_external.union([exports_external.string(), exports_external.number(), exports_external.boolean()])).optional().describe("Operator overrides for switchroom's capability-gated Hindsight " + "performance defaults. Only the keys switchroom actually manages are " + "honoured (`HINDSIGHT_PERF_ENV_KEYS` in " + "src/setup/hindsight-perf-defaults.ts: RERANKER_LOCAL_FP16, " + "RERANKER_LOCAL_BATCH_SIZE, LLM_MAX_CONCURRENT, " + "RETAIN/CONSOLIDATION_LLM_MAX_CONCURRENT, LLM_STRICT_SCHEMA, " + "LLM_MAX_RETRIES, CONSOLIDATION_LLM_PARALLELISM, " + "MAX_OBSERVATIONS_PER_SCOPE, " + "RECALL_MAX_CANDIDATES_PER_SOURCE, LINK_EXPANSION_PER_ENTITY_LIMIT, " + "LINK_EXPANSION_TIMEOUT, LLM_REASONING_EFFORT, " + "RERANKER_LOCAL_BUCKET_BATCHING, RERANKER_MAX_CANDIDATES, " + "RERANKER_LOCAL_MAX_CONCURRENT, RECALL_MAX_CONCURRENT, " + "REFLECT_WALL_TIMEOUT, WORKER_CONSOLIDATION_RESERVED_SLOTS, " + "WORKER_CONSOLIDATION_SLOT_LIMIT, " + "CONSOLIDATION_MAX_MEMORIES_PER_ROUND, GRAPH_SEED_MIN_SIMILARITY, " + "LLM_SUPPORTS_MAX_ITEMS, RECENCY_DECAY_FUNCTION, " + "RECENCY_DECAY_HALFLIFE_DAYS — switchroom defaults recall's recency " + "curve to `exponential` with a 30-day half-life so a fact retained " + "today outranks a stale one, instead of upstream's near-flat " + "linear/365-day window), the override-only keys " + "switchroom manages but ships NO default for " + "(`HINDSIGHT_PERF_OVERRIDE_ONLY_KEYS`: " + "HINDSIGHT_API_WORKER_CONSOLIDATION_BANK_PRIORITY — a per-deployment " + "`bank-pattern:priority,...` map; unset means upstream's flat " + "created_at FIFO across banks; and " + "HINDSIGHT_CE_DECISIVE_RELATIVE_GAP — the rollback knob for " + "switchroom's CE-saturation damping patch, a float; >= ~0.65 backs the " + "damping out entirely, unset means the patch's own derived gap; and " + "HINDSIGHT_API_RECENCY_DECAY_LINEAR_WINDOW_DAYS — only read when the " + "decay function is `linear`, so switchroom ships no default for it but " + "still honours an operator who flips the function back; and " + "HINDSIGHT_API_WORKER_MAX_SLOTS — the worker poller's TOTAL in-flight " + "task budget, the pool WORKER_CONSOLIDATION_RESERVED_SLOTS reserves out of; " + "unset means upstream's own default; and " + "HINDSIGHT_API_WORKER_RETAIN_RESERVED_SLOTS — the reserved slot FLOOR for the " + "retain (memory write) lane, carved from that same total; unset means " + "upstream's own 0, i.e. no floor and retain competes for the shared " + "pool; and " + "the pre-0.8.6 name HINDSIGHT_API_WORKER_<TYPE>_MAX_SLOTS is still " + "accepted for every operation type and normalised to " + "..._RESERVED_SLOTS on the way in, because setting both names for " + "one type is a hard boot failure in the engine and switchroom " + "therefore emits only the canonical name; and " + "HINDSIGHT_API_SEMANTIC_MIN_SIMILARITY — the cosine-similarity floor a " + "candidate must reach to be returned by the semantic retrieval arm at " + "all (0..1); unset means upstream's own 0.3, and where it should sit " + "depends on the bank's embedding model and phrasing diversity, so " + "switchroom ships no opinion; and " + "HINDSIGHT_MCP_RECALL_BUDGET_MODE — the rollback knob for switchroom's " + "mcp-recall-token-budget image patch; `legacy` restores upstream's " + "exact pre-patch recall returns, anything else or unset is the " + "honest-envelope mode; and " + "HINDSIGHT_API_LLM_TEMPERATURE_REFLECT — upstream's own per-op reflect " + "temperature knob, made live by switchroom's reflect-temperature image " + "patch; a float, or `none` to omit the kwarg (provider default, " + "upstream's accidental pre-patch behaviour); unset means the image's " + "baked default 0.1; and " + "HINDSIGHT_API_CONSOLIDATION_RECALL_MAX_CONCURRENT — the background " + "half of switchroom's recall-admission split (#3660): of the " + "RECALL_MAX_CONCURRENT admission slots, at most this many may be held " + "by background consolidation recalls at once, so foreground per-turn " + "recall always keeps the remainder; must be >= 1 and strictly less " + "than RECALL_MAX_CONCURRENT or the engine refuses to boot; unset " + "means the image's derived default min(2, RECALL_MAX_CONCURRENT - 1); " + "1 biases hard toward the interactive lane while a consolidation " + "backlog drains), plus the " + "embedded-PostgreSQL (pg0) sizing keys switchroom manages in " + "src/setup/hindsight-pg-defaults.ts (`HINDSIGHT_PG_ENV_KEYS`: " + "SWITCHROOM_HINDSIGHT_PG_EFFECTIVE_CACHE_SIZE, " + "SWITCHROOM_HINDSIGHT_PG_SHARED_BUFFERS — a postgres size string such " + "as `4GB`, or the sentinel `off` to leave pg0's own default for that " + "one knob). A value set here " + "REPLACES switchroom's default and is emitted even when the gating " + "capability is absent, so an operator can always force a knob. Other " + "`HINDSIGHT_API_*` keys are deliberately IGNORED — a blanket " + "passthrough would collide with the vars startHindsight() derives " + "itself (HINDSIGHT_API_PORT, the retain token/deadline budget).")
11420
11422
  });
11421
11423
  MicrosoftWorkspaceConfigSchema = exports_external.object({
11422
11424
  microsoft_client_id: exports_external.string().min(1).optional().describe("Microsoft OAuth application (client) ID from Entra portal " + "(literal string or vault reference e.g. " + "'vault:microsoft-oauth-client-id'). OPTIONAL — omit it to use " + "switchroom's shipped default Microsoft app (zero-config). " + "Set it only to bring your own Entra app (BYO)."),
@@ -11550,6 +11552,7 @@ var init_schema = __esm(() => {
11550
11552
  isolation: exports_external.enum(["default", "strict"]).optional(),
11551
11553
  profile: exports_external.string().optional(),
11552
11554
  directive_capture_nudge: exports_external.boolean().optional(),
11555
+ anti_confabulation_directive: AntiConfabulationDirectiveSchema,
11553
11556
  observation_scopes: ObservationScopesSchema,
11554
11557
  observation_scope_strategy: ObservationScopeStrategySchema,
11555
11558
  recall: exports_external.object({
@@ -18761,18 +18764,18 @@ var init_resolver = __esm(() => {
18761
18764
 
18762
18765
  // src/auth/broker/index.ts
18763
18766
  init_loader();
18764
- import { existsSync as existsSync9, readFileSync as readFileSync8 } from "node:fs";
18767
+ import { existsSync as existsSync10, readFileSync as readFileSync9 } from "node:fs";
18765
18768
 
18766
18769
  // src/auth/broker/server.ts
18767
18770
  import * as net from "node:net";
18768
18771
  import {
18769
18772
  chmodSync,
18770
18773
  chownSync as chownSync2,
18771
- existsSync as existsSync8,
18774
+ existsSync as existsSync9,
18772
18775
  lstatSync as lstatSync2,
18773
18776
  mkdirSync as mkdirSync5,
18774
- readFileSync as readFileSync7,
18775
- realpathSync as realpathSync3,
18777
+ readFileSync as readFileSync8,
18778
+ realpathSync as realpathSync4,
18776
18779
  renameSync as renameSync3,
18777
18780
  rmSync as rmSync5,
18778
18781
  statSync as statSync6,
@@ -18782,15 +18785,72 @@ import {
18782
18785
  import { closeSync as closeSync2, openSync as openSync2, writeSync as writeSync2 } from "node:fs";
18783
18786
  import * as constants2 from "node:constants";
18784
18787
  import { createHash as createHash2 } from "node:crypto";
18785
- import { basename as basename2, dirname as dirname4, join as join8, resolve as resolve9 } from "node:path";
18788
+ import { basename as basename2, dirname as dirname4, join as join7, resolve as resolve9 } from "node:path";
18786
18789
 
18787
18790
  // src/agents/compose.ts
18788
18791
  init_schema();
18789
18792
 
18790
18793
  // src/agents/scaffold.ts
18791
- import { dirname, isAbsolute, join as join4, relative, resolve as resolve5 } from "node:path";
18792
18794
  init_atomic();
18793
18795
 
18796
+ // src/util/shipped-assets.ts
18797
+ import { existsSync as existsSync4, readFileSync as readFileSync3, realpathSync } from "node:fs";
18798
+ import { dirname, resolve as resolve4 } from "node:path";
18799
+ var FHS_SHARE_ROOTS = [
18800
+ "/usr/local/share/switchroom",
18801
+ "/usr/share/switchroom"
18802
+ ];
18803
+ var PROFILES_ASSET = {
18804
+ asset: "profiles",
18805
+ envVar: "SWITCHROOM_PROFILES_ROOT"
18806
+ };
18807
+ function orderedCandidates(spec, probe) {
18808
+ const out = [];
18809
+ const push = (path, source) => {
18810
+ if (!out.some((c) => c.path === path))
18811
+ out.push({ path, source });
18812
+ };
18813
+ if (probe.bundleDir) {
18814
+ push(resolve4(probe.bundleDir, "../..", spec.asset), "npm");
18815
+ push(resolve4(probe.bundleDir, spec.asset), "image");
18816
+ }
18817
+ if (probe.execPath) {
18818
+ push(resolve4(dirname(probe.execPath), "../share/switchroom", spec.asset), "sea-sibling");
18819
+ }
18820
+ for (const root of FHS_SHARE_ROOTS) {
18821
+ push(resolve4(root, spec.asset), "fhs");
18822
+ }
18823
+ return out;
18824
+ }
18825
+ function resolveShippedAsset(spec, probe) {
18826
+ const env = probe.env ?? process.env;
18827
+ const override = env[spec.envVar]?.trim();
18828
+ if (override) {
18829
+ const path = canonicalise(resolve4(override), probe);
18830
+ return { path, candidates: [path], source: "env" };
18831
+ }
18832
+ const exists = probe.exists ?? existsSync4;
18833
+ const candidates = orderedCandidates(spec, probe);
18834
+ for (const c of candidates) {
18835
+ if (exists(c.path)) {
18836
+ return {
18837
+ path: canonicalise(c.path, probe),
18838
+ candidates: candidates.map((x) => x.path),
18839
+ source: c.source
18840
+ };
18841
+ }
18842
+ }
18843
+ return { path: null, candidates: candidates.map((x) => x.path), source: "none" };
18844
+ }
18845
+ function canonicalise(path, probe) {
18846
+ const realpath = probe.realpath ?? realpathSync;
18847
+ try {
18848
+ return realpath(path);
18849
+ } catch {
18850
+ return path;
18851
+ }
18852
+ }
18853
+
18794
18854
  // src/agents/agent-uid.ts
18795
18855
  init_peercred();
18796
18856
  import { createHash } from "node:crypto";
@@ -18849,37 +18909,29 @@ var AUDIT_ROOT = join(homedir2(), ".switchroom", "audit");
18849
18909
 
18850
18910
  // src/agents/profiles.ts
18851
18911
  var import_handlebars = __toESM(require_lib(), 1);
18852
- import { readFileSync as readFileSync3, writeFileSync, existsSync as existsSync4, readdirSync as readdirSync2, statSync as statSync2, copyFileSync, mkdirSync, realpathSync } from "node:fs";
18853
- import { resolve as resolve4, join as join2, sep as pathSep } from "node:path";
18854
- function resolveProfilesRoot() {
18855
- const envOverride = process.env.SWITCHROOM_PROFILES_ROOT?.trim();
18856
- if (envOverride) {
18857
- return resolve4(envOverride);
18858
- }
18859
- const candidates = [
18860
- resolve4(import.meta.dirname, "../../profiles"),
18861
- resolve4(import.meta.dirname, "profiles")
18862
- ];
18863
- for (const candidate of candidates) {
18864
- if (existsSync4(candidate)) {
18865
- return candidate;
18866
- }
18867
- }
18868
- return candidates[0];
18912
+ import { readFileSync as readFileSync4, writeFileSync, existsSync as existsSync5, readdirSync as readdirSync2, statSync as statSync2, copyFileSync, mkdirSync, realpathSync as realpathSync2 } from "node:fs";
18913
+ import { resolve as resolve5, join as join2, sep as pathSep } from "node:path";
18914
+ function resolveProfilesRootDetailed() {
18915
+ return resolveShippedAsset(PROFILES_ASSET, {
18916
+ bundleDir: import.meta.dirname,
18917
+ execPath: process.execPath
18918
+ });
18869
18919
  }
18870
- var PROFILES_ROOT = resolveProfilesRoot();
18920
+ var PROFILES_RESOLUTION = resolveProfilesRootDetailed();
18921
+ var PROFILES_ROOT = PROFILES_RESOLUTION.path ?? PROFILES_RESOLUTION.candidates[0];
18922
+ var PROFILES_ROOT_SEARCH = PROFILES_RESOLUTION.candidates;
18871
18923
  import_handlebars.default.registerHelper("json", (value) => {
18872
18924
  return new import_handlebars.default.SafeString(JSON.stringify(value, null, 2));
18873
18925
  });
18874
18926
  import_handlebars.default.registerHelper("isNumber", (value) => {
18875
18927
  return typeof value === "number" && Number.isFinite(value);
18876
18928
  });
18877
- var SHARED_FRAGMENTS_DIR = resolve4(PROFILES_ROOT, "_shared");
18929
+ var SHARED_FRAGMENTS_DIR = resolve5(PROFILES_ROOT, "_shared");
18878
18930
  var SHARED_FRAGMENTS = ["vault-protocol", "agent-self-service", "execution-discipline", "reply-discipline", "dev-protocol"];
18879
18931
  for (const name of SHARED_FRAGMENTS) {
18880
18932
  const fragPath = join2(SHARED_FRAGMENTS_DIR, `${name}.md.hbs`);
18881
- if (existsSync4(fragPath)) {
18882
- import_handlebars.default.registerPartial(name, readFileSync3(fragPath, "utf-8"));
18933
+ if (existsSync5(fragPath)) {
18934
+ import_handlebars.default.registerPartial(name, readFileSync4(fragPath, "utf-8"));
18883
18935
  }
18884
18936
  }
18885
18937
 
@@ -19082,7 +19134,8 @@ var HINDSIGHT_PERF_OVERRIDE_ONLY_KEYS = new Set([
19082
19134
  "HINDSIGHT_API_SEMANTIC_MIN_SIMILARITY",
19083
19135
  "HINDSIGHT_MCP_RECALL_BUDGET_MODE",
19084
19136
  "HINDSIGHT_API_LLM_TEMPERATURE_REFLECT",
19085
- "HINDSIGHT_API_RETAIN_WALL_TIMEOUT"
19137
+ "HINDSIGHT_API_RETAIN_WALL_TIMEOUT",
19138
+ "HINDSIGHT_API_CONSOLIDATION_RECALL_MAX_CONCURRENT"
19086
19139
  ]);
19087
19140
  var HINDSIGHT_WORKER_SLOT_TYPES = [
19088
19141
  "consolidation",
@@ -19177,6 +19230,15 @@ var DEFAULT_RETAIN_MISSION = `Extract durable facts that will still be true and
19177
19230
  ` + `- Hindsight's own errors, retries, backlogs, or internal state — the memory
19178
19231
  ` + ` system's self-reports are not memories.
19179
19232
  ` + `- Restatements of the user's current request or the task in progress.
19233
+ ` + `- The assistant's own answers, summaries, recaps, or reflect output. Before
19234
+ ` + ` extracting, ask: does anything in this transcript support this claim OTHER
19235
+ ` + ` than the assistant having asserted it? If not, drop it — a model's own
19236
+ ` + ` synthesis re-extracted as a fact is how a guess becomes permanent.
19237
+ ` + ` Concretely, never produce a fact whose only support is the assistant
19238
+ ` + ` stating a date, a version, an attribution, a total, or a decision that the
19239
+ ` + ` user never confirmed and no tool output shows. An unverified claim recalls
19240
+ ` + ` later as though it had been established, which is worse than not
19241
+ ` + ` remembering it at all.
19180
19242
  ` + `- Volatile state written as a timeless assertion. A version, count, size,
19181
19243
  ` + ` backlog, status, or any "X is running Y" / "X is at Y" / "X is currently Y"
19182
19244
  ` + ` claim is true only at the instant it was said. Concretely, never produce a
@@ -19332,6 +19394,64 @@ var SUPERSEDED_RETAIN_MISSIONS = [
19332
19394
  ` + `
19333
19395
  ` + `If a candidate fact matches an exclusion, drop it rather than rewording it. If
19334
19396
  ` + `nothing durable remains, return an empty facts list.
19397
+ `,
19398
+ `Extract durable facts that will still be true and useful weeks from now, once this session is forgotten.
19399
+ ` + `
19400
+ ` + `DURABILITY GATE. Emit a candidate ONLY if it is one of these five classes:
19401
+ ` + `- PREFERENCE — what the user likes, wants, or always does; a standing rule or correction.
19402
+ ` + `- DECISION — a settled choice that changes how future work is done, including a choice NOT to do something. A decision about the mechanics of the CURRENT task (which worker to dispatch, which branch to rebase, which PR to merge now, what to do next) is process narration, not a durable decision — drop it unless it establishes a standing rule or permanently changes a system.
19403
+ ` + `- FINDING — a root cause, a measurement, or verified behaviour of a system. Include the number.
19404
+ ` + `- OUTCOME — a completed result that changed the world: what shipped, what a thing turned out to be. Not the act of shipping it.
19405
+ ` + `- RELATIONSHIP — who a person is, what a project or tool is, and how they connect.
19406
+ ` + `If a candidate fits none of the five, it is not a memory. Drop it; do not reword it into one.
19407
+ ` + `
19408
+ ` + `A preference revealed by a request is durable — record the preference (what the user likes, wants, or always does), not the request itself.
19409
+ ` + `
19410
+ ` + `A TOOL RESULT IS NOT A FACT. Before extracting, ask: is the subject of this
19411
+ ` + `candidate a file path, a command/process/agent/session id, a temp directory, or
19412
+ ` + `the location where some output was written? If yes, drop it — it is transcript
19413
+ ` + `exhaust, not memory.
19414
+ ` + `
19415
+ ` + `NEVER extract:
19416
+ ` + `- Tool results verbatim or paraphrased. Concretely, never produce a fact whose
19417
+ ` + ` text resembles any of these: "File created successfully at /path/to/file",
19418
+ ` + ` "A background command with ID bctz4yskm is running, and its output will be
19419
+ ` + ` written to /tmp/...", "Async agent a745598ba84e71df1 was launched successfully
19420
+ ` + ` and is running in the background", "User executed a Bash command to sleep for
19421
+ ` + ` 200 seconds", "The assistant used grep to locate 'truncateSync' in src/foo.ts".
19422
+ ` + `- Anything mentioning a path under /tmp, a scratchpad directory, or a .tmp file.
19423
+ ` + `- Agent tool-use traces or narration of what the assistant did (e.g. "the
19424
+ ` + ` assistant used X to query Y", "ran a search", "sent the message").
19425
+ ` + `- The act of delegating, dispatching, spawning, launching, steering or merging
19426
+ ` + ` work — including when it succeeded. "X was dispatched and completed" is the
19427
+ ` + ` session describing itself. Record only what the work LEARNED or CHANGED.
19428
+ ` + `- In-flight workflow/process narration (a sub-task started, paused, or is still
19429
+ ` + ` running) — retain the outcome only once the task completes or a decision is made.
19430
+ ` + `- Operation, request, batch, agent, command or session IDs, UUIDs, hashes, or error codes.
19431
+ ` + `- Slash commands the user typed and their effects (e.g. "User issued /clear to
19432
+ ` + ` reset assistant state").
19433
+ ` + `- Hindsight's own errors, retries, backlogs, or internal state — the memory
19434
+ ` + ` system's self-reports are not memories.
19435
+ ` + `- Restatements of the user's current request or the task in progress.
19436
+ ` + `- Volatile state written as a timeless assertion. A version, count, size,
19437
+ ` + ` backlog, status, or any "X is running Y" / "X is at Y" / "X is currently Y"
19438
+ ` + ` claim is true only at the instant it was said. Concretely, never produce a
19439
+ ` + ` fact whose text resembles any of these: "Switchroom fleet is running image
19440
+ ` + ` version v0.18.19", "The switchroom repo is at /path/to/fleet, version
19441
+ ` + ` v0.19.5", "Bank overlord has 43155 pending consolidations", "The build is
19442
+ ` + ` currently green". If the claim is worth keeping, put the date INSIDE the
19443
+ ` + ` fact text ("As of 2026-07-19 the fleet was running v0.18.19"); if you
19444
+ ` + ` cannot date it, drop it. An undated one is recalled forever as though it
19445
+ ` + ` were still true, which is worse than not remembering it at all.
19446
+ ` + `- Transient state (unread counts, build status, what is running right now) unless
19447
+ ` + ` the fact is explicitly dated, in which case record it as a dated observation.
19448
+ ` + `- Greetings, acknowledgements, and routine operational chatter.
19449
+ ` + `
19450
+ ` + `Write each fact so it stands alone: name the thing, the number, and the date. A
19451
+ ` + `sentence that only makes sense while reading this transcript is not durable.
19452
+ ` + `
19453
+ ` + `If a candidate fact matches an exclusion, drop it rather than rewording it. If
19454
+ ` + `nothing durable remains, return an empty facts list.
19335
19455
  `
19336
19456
  ];
19337
19457
  var DEFAULT_OBSERVATIONS_MISSION = `Synthesise durable, standing knowledge about the people, projects, and systems this agent works with: preferences and standing rules, roles and relationships, skills and recurring patterns, technical and operational decisions with their rationale, and the state of long-running work once it lands.
@@ -19432,6 +19552,22 @@ function normalizeMicrosoftBindings(mw) {
19432
19552
  var warnedMissingPool = new Set;
19433
19553
  var warnedMissingDefault = new Set;
19434
19554
 
19555
+ // src/memory/hindsight-retain-provenance.ts
19556
+ var RETAIN_PROVENANCE_TAG = "source:transcript";
19557
+ var RETAIN_TAGS_DEFAULT = Object.freeze([
19558
+ "{session_id}",
19559
+ RETAIN_PROVENANCE_TAG
19560
+ ]);
19561
+
19562
+ // src/memory/hindsight-seed-directives.ts
19563
+ var ANTI_CONFABULATION_DIRECTIVE = `When the retrieved memories do not support an answer, say so instead of composing one.
19564
+ ` + `
19565
+ ` + `- If nothing retrieved scores meaningfully above zero, or what came back is off-topic, the honest answer is that the bank does not know. Say that plainly, and say what would settle it.
19566
+ ` + `- Never assert a date, a version, a number, an attribution, a decision, or an outcome that no retrieved memory states. A question that presupposes something is not evidence for it: asked when a decision was made, when the bank records no such decision, answer that none is recorded — do not supply one.
19567
+ ` + `- Keep what the bank RECORDED separate from what you INFER. State inferences as inferences, and say which memory a recorded fact came from.
19568
+ ` + `- Treat a memory that was extracted from a conversation transcript as an unverified claim, not an established fact — it may be an assistant's own earlier guess. If it is the only support for an answer, say that it is.
19569
+ ` + "- Partial knowledge with its gaps named is more useful than a fluent answer that fills them in.";
19570
+
19435
19571
  // src/telegram/state.ts
19436
19572
  init_paths();
19437
19573
 
@@ -19469,7 +19605,6 @@ var RECALL_PASSTHROUGH_DEFAULTS = Object.freeze({
19469
19605
  init_paths();
19470
19606
 
19471
19607
  // src/agents/scaffold.ts
19472
- var REPO_ROOT = resolve5(import.meta.dirname, "../..");
19473
19608
  var CLAUDE_MD_YOURS_PLACEHOLDER = "This space is yours. Add per-agent rules, exceptions, or context the " + "Switchroom template doesn't capture. Everything above the marker line is " + "regenerated on every apply; this section is preserved.";
19474
19609
  var pendingBankOps = new Set;
19475
19610
  var SWITCHROOM_OWNED_SETTINGS_KEYS = new Set([
@@ -20141,11 +20276,11 @@ async function fetchAndSummarizeExternalSpend(opts) {
20141
20276
 
20142
20277
  // src/auth/broker/mirror-write.ts
20143
20278
  init_atomic();
20144
- import { lstatSync, realpathSync as realpathSync2 } from "node:fs";
20279
+ import { lstatSync, realpathSync as realpathSync3 } from "node:fs";
20145
20280
  function safeMirrorWrite(input) {
20146
20281
  let realParent;
20147
20282
  try {
20148
- realParent = realpathSync2(input.parentDir);
20283
+ realParent = realpathSync3(input.parentDir);
20149
20284
  } catch (err) {
20150
20285
  input.logErr(`mirror parent realpath failed: ${err.message}`);
20151
20286
  return false;
@@ -20177,16 +20312,16 @@ function safeMirrorWrite(input) {
20177
20312
  init_atomic();
20178
20313
  import {
20179
20314
  chownSync,
20180
- existsSync as existsSync5,
20315
+ existsSync as existsSync6,
20181
20316
  mkdirSync as mkdirSync2,
20182
- readFileSync as readFileSync4,
20317
+ readFileSync as readFileSync5,
20183
20318
  readdirSync as readdirSync3,
20184
20319
  renameSync as renameSync2,
20185
20320
  rmSync as rmSync2,
20186
20321
  statSync as statSync3
20187
20322
  } from "node:fs";
20188
20323
  import { homedir as homedir4 } from "node:os";
20189
- import { join as join5, resolve as resolve6 } from "node:path";
20324
+ import { join as join4, resolve as resolve6 } from "node:path";
20190
20325
  var LABEL_MAX = 64;
20191
20326
  var LABEL_RE = /^[A-Za-z0-9._@+-]+$/;
20192
20327
  function accountsRootOverride() {
@@ -20199,13 +20334,13 @@ function accountsRoot(home2 = homedir4()) {
20199
20334
  return accountsRootOverride() ?? resolve6(home2, ".switchroom", "accounts");
20200
20335
  }
20201
20336
  function accountDir(label, home2 = homedir4()) {
20202
- return join5(accountsRoot(home2), label);
20337
+ return join4(accountsRoot(home2), label);
20203
20338
  }
20204
20339
  function accountCredentialsPath(label, home2 = homedir4()) {
20205
- return join5(accountDir(label, home2), "credentials.json");
20340
+ return join4(accountDir(label, home2), "credentials.json");
20206
20341
  }
20207
20342
  function accountMetaPath(label, home2 = homedir4()) {
20208
- return join5(accountDir(label, home2), "meta.json");
20343
+ return join4(accountDir(label, home2), "meta.json");
20209
20344
  }
20210
20345
  function validateAccountLabel(label) {
20211
20346
  if (typeof label !== "string" || label.length === 0) {
@@ -20226,12 +20361,12 @@ function validateAccountLabel(label) {
20226
20361
  }
20227
20362
  function listAccounts(home2 = homedir4()) {
20228
20363
  const root = accountsRoot(home2);
20229
- if (!existsSync5(root))
20364
+ if (!existsSync6(root))
20230
20365
  return [];
20231
20366
  try {
20232
20367
  return readdirSync3(root).filter((name) => {
20233
20368
  try {
20234
- return statSync3(join5(root, name)).isDirectory();
20369
+ return statSync3(join4(root, name)).isDirectory();
20235
20370
  } catch {
20236
20371
  return false;
20237
20372
  }
@@ -20241,14 +20376,14 @@ function listAccounts(home2 = homedir4()) {
20241
20376
  }
20242
20377
  }
20243
20378
  function accountExists(label, home2 = homedir4()) {
20244
- return existsSync5(accountCredentialsPath(label, home2));
20379
+ return existsSync6(accountCredentialsPath(label, home2));
20245
20380
  }
20246
20381
  function readAccountCredentials(label, home2 = homedir4()) {
20247
20382
  const p = accountCredentialsPath(label, home2);
20248
- if (!existsSync5(p))
20383
+ if (!existsSync6(p))
20249
20384
  return null;
20250
20385
  try {
20251
- return JSON.parse(readFileSync4(p, "utf-8"));
20386
+ return JSON.parse(readFileSync5(p, "utf-8"));
20252
20387
  } catch {
20253
20388
  return null;
20254
20389
  }
@@ -20274,21 +20409,21 @@ function writeAccountCredentials(label, value, home2 = homedir4()) {
20274
20409
  function chownAccountFiles(label, uid, home2 = homedir4()) {
20275
20410
  validateAccountLabel(label);
20276
20411
  const dir = accountDir(label, home2);
20277
- if (existsSync5(dir))
20412
+ if (existsSync6(dir))
20278
20413
  chownSync(dir, uid, uid);
20279
20414
  const creds = accountCredentialsPath(label, home2);
20280
- if (existsSync5(creds))
20415
+ if (existsSync6(creds))
20281
20416
  chownSync(creds, uid, uid);
20282
20417
  const meta = accountMetaPath(label, home2);
20283
- if (existsSync5(meta))
20418
+ if (existsSync6(meta))
20284
20419
  chownSync(meta, uid, uid);
20285
20420
  }
20286
20421
  function readAccountMeta(label, home2 = homedir4()) {
20287
20422
  const p = accountMetaPath(label, home2);
20288
- if (!existsSync5(p))
20423
+ if (!existsSync6(p))
20289
20424
  return null;
20290
20425
  try {
20291
- return JSON.parse(readFileSync4(p, "utf-8"));
20426
+ return JSON.parse(readFileSync5(p, "utf-8"));
20292
20427
  } catch {
20293
20428
  return null;
20294
20429
  }
@@ -20812,8 +20947,8 @@ function resolveMicrosoftClientId(configClientId) {
20812
20947
 
20813
20948
  // src/auth/broker/google-storage.ts
20814
20949
  init_atomic();
20815
- import { existsSync as existsSync6, mkdirSync as mkdirSync3, readdirSync as readdirSync4, readFileSync as readFileSync5, rmSync as rmSync3, statSync as statSync4 } from "node:fs";
20816
- import { dirname as dirname2, join as join6, resolve as resolve7 } from "node:path";
20950
+ import { existsSync as existsSync7, mkdirSync as mkdirSync3, readdirSync as readdirSync4, readFileSync as readFileSync6, rmSync as rmSync3, statSync as statSync4 } from "node:fs";
20951
+ import { dirname as dirname2, join as join5, resolve as resolve7 } from "node:path";
20817
20952
  function normalizeGoogleAccountForStorage(account) {
20818
20953
  return account.trim().toLowerCase();
20819
20954
  }
@@ -20835,17 +20970,17 @@ function googleAccountDir(stateDir, account) {
20835
20970
  return resolve7(stateDir, "google", normalizeGoogleAccountForStorage(account));
20836
20971
  }
20837
20972
  function googleAccountCredentialsPath(stateDir, account) {
20838
- return join6(googleAccountDir(stateDir, account), "credentials.json");
20973
+ return join5(googleAccountDir(stateDir, account), "credentials.json");
20839
20974
  }
20840
20975
  function googleAccountExists(stateDir, account) {
20841
- return existsSync6(googleAccountCredentialsPath(stateDir, account));
20976
+ return existsSync7(googleAccountCredentialsPath(stateDir, account));
20842
20977
  }
20843
20978
  function readGoogleAccountCredentials(stateDir, account) {
20844
20979
  const path = googleAccountCredentialsPath(stateDir, account);
20845
- if (!existsSync6(path))
20980
+ if (!existsSync7(path))
20846
20981
  return null;
20847
20982
  try {
20848
- const raw = readFileSync5(path, "utf-8");
20983
+ const raw = readFileSync6(path, "utf-8");
20849
20984
  const parsed = JSON.parse(raw);
20850
20985
  if (!parsed?.googleOauth?.accessToken)
20851
20986
  return null;
@@ -20862,27 +20997,27 @@ function writeGoogleAccountCredentials(stateDir, account, credentials) {
20862
20997
  }
20863
20998
  function removeGoogleAccount(stateDir, account) {
20864
20999
  const dir = googleAccountDir(stateDir, account);
20865
- if (existsSync6(dir)) {
21000
+ if (existsSync7(dir)) {
20866
21001
  rmSync3(dir, { recursive: true, force: true });
20867
21002
  }
20868
21003
  }
20869
21004
  function listGoogleAccounts(stateDir) {
20870
- const root = join6(stateDir, "google");
20871
- if (!existsSync6(root))
21005
+ const root = join5(stateDir, "google");
21006
+ if (!existsSync7(root))
20872
21007
  return [];
20873
21008
  return readdirSync4(root).filter((name) => {
20874
21009
  try {
20875
- return statSync4(join6(root, name)).isDirectory();
21010
+ return statSync4(join5(root, name)).isDirectory();
20876
21011
  } catch {
20877
21012
  return false;
20878
21013
  }
20879
- }).filter((name) => existsSync6(join6(root, name, "credentials.json")));
21014
+ }).filter((name) => existsSync7(join5(root, name, "credentials.json")));
20880
21015
  }
20881
21016
 
20882
21017
  // src/auth/broker/microsoft-storage.ts
20883
21018
  init_atomic();
20884
- import { existsSync as existsSync7, mkdirSync as mkdirSync4, readdirSync as readdirSync5, readFileSync as readFileSync6, rmSync as rmSync4, statSync as statSync5 } from "node:fs";
20885
- import { dirname as dirname3, join as join7, resolve as resolve8 } from "node:path";
21019
+ import { existsSync as existsSync8, mkdirSync as mkdirSync4, readdirSync as readdirSync5, readFileSync as readFileSync7, rmSync as rmSync4, statSync as statSync5 } from "node:fs";
21020
+ import { dirname as dirname3, join as join6, resolve as resolve8 } from "node:path";
20886
21021
  function normalizeMicrosoftAccountForStorage(account) {
20887
21022
  return account.trim().toLowerCase();
20888
21023
  }
@@ -20904,17 +21039,17 @@ function microsoftAccountDir(stateDir, account) {
20904
21039
  return resolve8(stateDir, "microsoft", normalizeMicrosoftAccountForStorage(account));
20905
21040
  }
20906
21041
  function microsoftAccountCredentialsPath(stateDir, account) {
20907
- return join7(microsoftAccountDir(stateDir, account), "credentials.json");
21042
+ return join6(microsoftAccountDir(stateDir, account), "credentials.json");
20908
21043
  }
20909
21044
  function microsoftAccountExists(stateDir, account) {
20910
- return existsSync7(microsoftAccountCredentialsPath(stateDir, account));
21045
+ return existsSync8(microsoftAccountCredentialsPath(stateDir, account));
20911
21046
  }
20912
21047
  function readMicrosoftAccountCredentials(stateDir, account) {
20913
21048
  const path = microsoftAccountCredentialsPath(stateDir, account);
20914
- if (!existsSync7(path))
21049
+ if (!existsSync8(path))
20915
21050
  return null;
20916
21051
  try {
20917
- const raw = readFileSync6(path, "utf-8");
21052
+ const raw = readFileSync7(path, "utf-8");
20918
21053
  const parsed = JSON.parse(raw);
20919
21054
  if (!parsed?.microsoftOauth?.accessToken)
20920
21055
  return null;
@@ -20931,21 +21066,21 @@ function writeMicrosoftAccountCredentials(stateDir, account, credentials) {
20931
21066
  }
20932
21067
  function removeMicrosoftAccount(stateDir, account) {
20933
21068
  const dir = microsoftAccountDir(stateDir, account);
20934
- if (existsSync7(dir)) {
21069
+ if (existsSync8(dir)) {
20935
21070
  rmSync4(dir, { recursive: true, force: true });
20936
21071
  }
20937
21072
  }
20938
21073
  function listMicrosoftAccounts(stateDir) {
20939
- const root = join7(stateDir, "microsoft");
20940
- if (!existsSync7(root))
21074
+ const root = join6(stateDir, "microsoft");
21075
+ if (!existsSync8(root))
20941
21076
  return [];
20942
21077
  return readdirSync5(root).filter((name) => {
20943
21078
  try {
20944
- return statSync5(join7(root, name)).isDirectory();
21079
+ return statSync5(join6(root, name)).isDirectory();
20945
21080
  } catch {
20946
21081
  return false;
20947
21082
  }
20948
- }).filter((name) => existsSync7(join7(root, name, "credentials.json")));
21083
+ }).filter((name) => existsSync8(join6(root, name, "credentials.json")));
20949
21084
  }
20950
21085
 
20951
21086
  // src/auth/broker/provider.ts
@@ -21481,7 +21616,7 @@ class AuthBroker {
21481
21616
  async start() {
21482
21617
  process.umask(63);
21483
21618
  mkdirSync5(this.stateDir, { recursive: true, mode: 448 });
21484
- mkdirSync5(join8(this.stateDir, "refresh-lease"), { recursive: true, mode: 448 });
21619
+ mkdirSync5(join7(this.stateDir, "refresh-lease"), { recursive: true, mode: 448 });
21485
21620
  mkdirSync5(this.socketRoot, { recursive: true, mode: 493 });
21486
21621
  this.loadStateFromDisk();
21487
21622
  this.assertDriftFree();
@@ -21530,7 +21665,7 @@ class AuthBroker {
21530
21665
  }
21531
21666
  if (!this.opts.skipHealthyMarker) {
21532
21667
  try {
21533
- const healthyPath = join8(this.stateDir, "healthy");
21668
+ const healthyPath = join7(this.stateDir, "healthy");
21534
21669
  writeFileSync2(healthyPath, String(this.now()) + `
21535
21670
  `, { mode: 384 });
21536
21671
  } catch (err) {
@@ -21569,14 +21704,14 @@ class AuthBroker {
21569
21704
  } catch {}
21570
21705
  }
21571
21706
  try {
21572
- if (existsSync8(sock))
21707
+ if (existsSync9(sock))
21573
21708
  unlinkSync(sock);
21574
21709
  } catch {}
21575
21710
  }
21576
21711
  this.listeners.clear();
21577
21712
  try {
21578
- const healthyPath = join8(this.stateDir, "healthy");
21579
- if (existsSync8(healthyPath))
21713
+ const healthyPath = join7(this.stateDir, "healthy");
21714
+ if (existsSync9(healthyPath))
21580
21715
  unlinkSync(healthyPath);
21581
21716
  } catch {}
21582
21717
  }
@@ -21607,7 +21742,7 @@ class AuthBroker {
21607
21742
  lis.server.close();
21608
21743
  } catch {}
21609
21744
  try {
21610
- if (existsSync8(sock))
21745
+ if (existsSync9(sock))
21611
21746
  unlinkSync(sock);
21612
21747
  } catch {}
21613
21748
  for (const s of lis.sockets) {
@@ -21641,13 +21776,13 @@ class AuthBroker {
21641
21776
  }
21642
21777
  }
21643
21778
  agentSocketPath(name) {
21644
- return join8(this.socketRoot, name, "sock");
21779
+ return join7(this.socketRoot, name, "sock");
21645
21780
  }
21646
21781
  consumerSocketPath(name) {
21647
- return join8(this.socketRoot, name, "sock");
21782
+ return join7(this.socketRoot, name, "sock");
21648
21783
  }
21649
21784
  operatorSocketPath() {
21650
- return join8(this.socketRoot, "operator", "sock");
21785
+ return join7(this.socketRoot, "operator", "sock");
21651
21786
  }
21652
21787
  async bindAgentListener(agentName) {
21653
21788
  if (RESERVED_NAMES.has(agentName)) {
@@ -21679,7 +21814,7 @@ class AuthBroker {
21679
21814
  }
21680
21815
  async bindListener(sockPath, targetUid, sockMode, identity) {
21681
21816
  const dir = dirname4(sockPath);
21682
- if (existsSync8(dir)) {
21817
+ if (existsSync9(dir)) {
21683
21818
  try {
21684
21819
  chownSync2(dir, 0, 0);
21685
21820
  } catch {}
@@ -21689,7 +21824,7 @@ class AuthBroker {
21689
21824
  } else {
21690
21825
  mkdirSync5(dir, { recursive: true, mode: 448 });
21691
21826
  }
21692
- if (existsSync8(sockPath)) {
21827
+ if (existsSync9(sockPath)) {
21693
21828
  try {
21694
21829
  unlinkSync(sockPath);
21695
21830
  } catch {}
@@ -22516,7 +22651,7 @@ class AuthBroker {
22516
22651
  ...earliestReset != null ? { earliest_reset: earliestReset } : {},
22517
22652
  at: this.now()
22518
22653
  };
22519
- atomicWriteJsonSync(join8(this.stateDir, "premium-tier-all-walled.json"), this.premiumTierAllWalled, 384);
22654
+ atomicWriteJsonSync(join7(this.stateDir, "premium-tier-all-walled.json"), this.premiumTierAllWalled, 384);
22520
22655
  process.stdout.write(`auth-broker: ALL accounts flagship-tier walled (bucket=${bucket ?? "?"}` + `${earliestReset != null ? `, earliest reset ${new Date(earliestReset).toISOString()}` : ""}) ` + `— no premium-eligible failover target
22521
22656
  `);
22522
22657
  }
@@ -22525,7 +22660,7 @@ class AuthBroker {
22525
22660
  return;
22526
22661
  this.premiumTierAllWalled = null;
22527
22662
  try {
22528
- unlinkSync(join8(this.stateDir, "premium-tier-all-walled.json"));
22663
+ unlinkSync(join7(this.stateDir, "premium-tier-all-walled.json"));
22529
22664
  } catch {}
22530
22665
  }
22531
22666
  premiumCanarySet() {
@@ -22861,10 +22996,10 @@ class AuthBroker {
22861
22996
  if (envKey)
22862
22997
  return envKey;
22863
22998
  try {
22864
- const path = join8(this.stateDir, LITELLM_MASTER_KEY_STATE_BASENAME);
22865
- if (!existsSync8(path))
22999
+ const path = join7(this.stateDir, LITELLM_MASTER_KEY_STATE_BASENAME);
23000
+ if (!existsSync9(path))
22866
23001
  return null;
22867
- const raw = readFileSync7(path, "utf-8").trim();
23002
+ const raw = readFileSync8(path, "utf-8").trim();
22868
23003
  return raw.length > 0 ? raw : null;
22869
23004
  } catch {
22870
23005
  return null;
@@ -22985,7 +23120,7 @@ class AuthBroker {
22985
23120
  delete this.softAvoidState[label];
22986
23121
  delete this.softAvoidRolledTo[label];
22987
23122
  this.chownAccountFilesIfRoot(label);
22988
- const contents = readFileSync7(accountCredentialsPath(label, this.home), "utf-8");
23123
+ const contents = readFileSync8(accountCredentialsPath(label, this.home), "utf-8");
22989
23124
  this.shaIndex[label] = sha256Hex(contents);
22990
23125
  this.lastWrittenExpiresAt.set(label, credentials.claudeAiOauth?.expiresAt);
22991
23126
  this.persistShaIndex();
@@ -23383,7 +23518,7 @@ class AuthBroker {
23383
23518
  return { kind: "noop" };
23384
23519
  }
23385
23520
  }
23386
- const leasePath = join8(this.stateDir, "refresh-lease", label);
23521
+ const leasePath = join7(this.stateDir, "refresh-lease", label);
23387
23522
  let leaseFd = null;
23388
23523
  try {
23389
23524
  leaseFd = openSync2(leasePath, constants2.O_RDWR | constants2.O_CREAT, 384);
@@ -23400,7 +23535,7 @@ class AuthBroker {
23400
23535
  const creds = readAccountCredentials(label, this.home);
23401
23536
  const newExpiresAt = creds?.claudeAiOauth?.expiresAt ?? outcome.newExpiresAt;
23402
23537
  this.lastWrittenExpiresAt.set(label, newExpiresAt);
23403
- const contents = readFileSync7(accountCredentialsPath(label, this.home), "utf-8");
23538
+ const contents = readFileSync8(accountCredentialsPath(label, this.home), "utf-8");
23404
23539
  this.shaIndex[label] = sha256Hex(contents);
23405
23540
  this.persistShaIndex();
23406
23541
  this.fanoutToAffectedAgents(label);
@@ -23487,9 +23622,9 @@ class AuthBroker {
23487
23622
  if (!mirrorDir)
23488
23623
  return false;
23489
23624
  const credsPath = accountCredentialsPath(label, this.home);
23490
- if (!existsSync8(credsPath))
23625
+ if (!existsSync9(credsPath))
23491
23626
  return false;
23492
- const mirrorContent = enrichMirrorContent(readFileSync7(credsPath, "utf-8"));
23627
+ const mirrorContent = enrichMirrorContent(readFileSync8(credsPath, "utf-8"));
23493
23628
  const safe = this.resolveConsumerMirrorPathsSafe(consumer.name, mirrorDir);
23494
23629
  if (!safe)
23495
23630
  return false;
@@ -23532,7 +23667,7 @@ class AuthBroker {
23532
23667
  return null;
23533
23668
  }
23534
23669
  }
23535
- const targetPath = join8(mirrorDir, ".credentials.json");
23670
+ const targetPath = join7(mirrorDir, ".credentials.json");
23536
23671
  const targetStat = this.lstatOrNull(targetPath);
23537
23672
  if (targetStat) {
23538
23673
  if (targetStat.isSymbolicLink())
@@ -23542,7 +23677,7 @@ class AuthBroker {
23542
23677
  }
23543
23678
  let expectedRealParent;
23544
23679
  try {
23545
- expectedRealParent = join8(realpathSync3(parent), basename2(mirrorDir));
23680
+ expectedRealParent = join7(realpathSync4(parent), basename2(mirrorDir));
23546
23681
  } catch (err) {
23547
23682
  this.logErr(`consumer-mirror ${consumerName}: realpath ${parent}: ${err.message}`);
23548
23683
  return null;
@@ -23599,8 +23734,8 @@ class AuthBroker {
23599
23734
  resolveMirrorPathsSafe(agentName) {
23600
23735
  const agentsDir = resolveAgentsDir(this.config);
23601
23736
  const agentDir = resolve9(agentsDir, agentName);
23602
- const claudeDir = join8(agentDir, ".claude");
23603
- const targetPath = join8(claudeDir, ".credentials.json");
23737
+ const claudeDir = join7(agentDir, ".claude");
23738
+ const targetPath = join7(claudeDir, ".credentials.json");
23604
23739
  const refuse = (component, reason) => {
23605
23740
  this.auditMirrorRefused({ kind: "agent", name: agentName, admin: false }, component, reason);
23606
23741
  return null;
@@ -23633,7 +23768,7 @@ class AuthBroker {
23633
23768
  if (!targetStat.isFile())
23634
23769
  return refuse(".credentials.json", "is-not-a-regular-file");
23635
23770
  }
23636
- const expectedRealParent = join8(realpathSync3(agentsDir), agentName, ".claude");
23771
+ const expectedRealParent = join7(realpathSync4(agentsDir), agentName, ".claude");
23637
23772
  return { agentDir, claudeDir, targetPath, expectedRealParent };
23638
23773
  }
23639
23774
  lstatOrNull(path) {
@@ -23655,9 +23790,9 @@ class AuthBroker {
23655
23790
  }
23656
23791
  mirrorAccountToAgent(label, agentName) {
23657
23792
  const credsPath = accountCredentialsPath(label, this.home);
23658
- if (!existsSync8(credsPath))
23793
+ if (!existsSync9(credsPath))
23659
23794
  return false;
23660
- const mirrorContent = enrichMirrorContent(readFileSync7(credsPath, "utf-8"));
23795
+ const mirrorContent = enrichMirrorContent(readFileSync8(credsPath, "utf-8"));
23661
23796
  const safe = this.resolveMirrorPathsSafe(agentName);
23662
23797
  if (!safe)
23663
23798
  return false;
@@ -23711,7 +23846,7 @@ class AuthBroker {
23711
23846
  process.stdout.write(`auth-broker: active-override dropped — yaml auth.active changed since the swap ` + `(${ov.yaml_active_at_write ?? "unset"} → ${yamlActive ?? "unset"}); yaml wins
23712
23847
  `);
23713
23848
  try {
23714
- unlinkSync(join8(this.stateDir, "active-override.json"));
23849
+ unlinkSync(join7(this.stateDir, "active-override.json"));
23715
23850
  } catch {}
23716
23851
  return;
23717
23852
  }
@@ -23735,34 +23870,34 @@ class AuthBroker {
23735
23870
  yaml_active_at_write: this.yamlActive ?? null,
23736
23871
  updated_at: this.now()
23737
23872
  };
23738
- atomicWriteJsonSync(join8(this.stateDir, "active-override.json"), entry, 384);
23873
+ atomicWriteJsonSync(join7(this.stateDir, "active-override.json"), entry, 384);
23739
23874
  }
23740
23875
  readJson(name) {
23741
- const p = join8(this.stateDir, name);
23742
- if (!existsSync8(p))
23876
+ const p = join7(this.stateDir, name);
23877
+ if (!existsSync9(p))
23743
23878
  return null;
23744
23879
  try {
23745
- return JSON.parse(readFileSync7(p, "utf-8"));
23880
+ return JSON.parse(readFileSync8(p, "utf-8"));
23746
23881
  } catch {
23747
23882
  return null;
23748
23883
  }
23749
23884
  }
23750
23885
  persistQuota() {
23751
- atomicWriteJsonSync(join8(this.stateDir, "quota.json"), this.quota, 384);
23886
+ atomicWriteJsonSync(join7(this.stateDir, "quota.json"), this.quota, 384);
23752
23887
  }
23753
23888
  persistLastQuotaCache() {
23754
- atomicWriteJsonSync(join8(this.stateDir, "last-quota.json"), this.lastQuotaCache, 384);
23889
+ atomicWriteJsonSync(join7(this.stateDir, "last-quota.json"), this.lastQuotaCache, 384);
23755
23890
  }
23756
23891
  persistExternalSpendCache() {
23757
23892
  if (!this.externalSpendCache)
23758
23893
  return;
23759
- atomicWriteJsonSync(join8(this.stateDir, "external-spend.json"), this.externalSpendCache, 384);
23894
+ atomicWriteJsonSync(join7(this.stateDir, "external-spend.json"), this.externalSpendCache, 384);
23760
23895
  }
23761
23896
  persistLastTierQuotaCache() {
23762
- atomicWriteJsonSync(join8(this.stateDir, "last-tier-quota.json"), this.lastTierQuotaCache, 384);
23897
+ atomicWriteJsonSync(join7(this.stateDir, "last-tier-quota.json"), this.lastTierQuotaCache, 384);
23763
23898
  }
23764
23899
  persistUsageLedger() {
23765
- atomicWriteJsonSync(join8(this.stateDir, "usage-ledger.json"), this.usageLedger, 384);
23900
+ atomicWriteJsonSync(join7(this.stateDir, "usage-ledger.json"), this.usageLedger, 384);
23766
23901
  }
23767
23902
  recordUsage(label, tier, sample) {
23768
23903
  if (sample == null)
@@ -23775,26 +23910,26 @@ class AuthBroker {
23775
23910
  }
23776
23911
  }
23777
23912
  persistNotificationClaims() {
23778
- atomicWriteJsonSync(join8(this.stateDir, "notification-claims.json"), this.notificationClaims, 384);
23913
+ atomicWriteJsonSync(join7(this.stateDir, "notification-claims.json"), this.notificationClaims, 384);
23779
23914
  }
23780
23915
  persistLastFleetRoll() {
23781
23916
  if (this.lastFleetRoll)
23782
- atomicWriteJsonSync(join8(this.stateDir, "last-fleet-roll.json"), this.lastFleetRoll, 384);
23917
+ atomicWriteJsonSync(join7(this.stateDir, "last-fleet-roll.json"), this.lastFleetRoll, 384);
23783
23918
  }
23784
23919
  persistShaIndex() {
23785
- atomicWriteJsonSync(join8(this.stateDir, "sha-index.json"), this.shaIndex, 384);
23920
+ atomicWriteJsonSync(join7(this.stateDir, "sha-index.json"), this.shaIndex, 384);
23786
23921
  }
23787
23922
  persistThresholdViolations() {
23788
- atomicWriteJsonSync(join8(this.stateDir, "threshold-violations.json"), this.thresholdViolations, 384);
23923
+ atomicWriteJsonSync(join7(this.stateDir, "threshold-violations.json"), this.thresholdViolations, 384);
23789
23924
  }
23790
23925
  assertDriftFree() {
23791
23926
  for (const label of Object.keys(this.shaIndex)) {
23792
23927
  const p = accountCredentialsPath(label, this.home);
23793
- if (!existsSync8(p)) {
23928
+ if (!existsSync9(p)) {
23794
23929
  this.logErr(`DRIFT_DETECTED ${label}: index entry but no on-disk credentials`);
23795
23930
  process.exit(1);
23796
23931
  }
23797
- const got = sha256Hex(readFileSync7(p, "utf-8"));
23932
+ const got = sha256Hex(readFileSync8(p, "utf-8"));
23798
23933
  if (got !== this.shaIndex[label]) {
23799
23934
  this.logErr(`DRIFT_DETECTED ${label}: sha256 mismatch (recover with 'switchroom auth add ${label} --replace')`);
23800
23935
  process.exit(1);
@@ -23820,7 +23955,7 @@ class AuthBroker {
23820
23955
  replace: entry.replace,
23821
23956
  reason: entry.reason
23822
23957
  });
23823
- const auditPath = join8(this.stateDir, "audit.jsonl");
23958
+ const auditPath = join7(this.stateDir, "audit.jsonl");
23824
23959
  try {
23825
23960
  this.rotateAuditIfLarge(auditPath);
23826
23961
  const line = row + `
@@ -23861,7 +23996,7 @@ class AuthBroker {
23861
23996
  for (let i = AUDIT_KEEP - 1;i >= 1; i--) {
23862
23997
  const src = `${path}.${i}`;
23863
23998
  const dst = `${path}.${i + 1}`;
23864
- if (existsSync8(src)) {
23999
+ if (existsSync9(src)) {
23865
24000
  try {
23866
24001
  renameSync3(src, dst);
23867
24002
  } catch {}
@@ -23960,14 +24095,14 @@ async function main() {
23960
24095
  const flags = parseFlags(argv);
23961
24096
  const operatorUid = flags.operatorUid ?? operatorUidFromEnv();
23962
24097
  const configPath = process.env.SWITCHROOM_CONFIG;
23963
- if (configPath !== undefined && !existsSync9(configPath)) {
24098
+ if (configPath !== undefined && !existsSync10(configPath)) {
23964
24099
  process.stderr.write(`auth-broker fatal: SWITCHROOM_CONFIG='${configPath}' does not exist
23965
24100
  `);
23966
24101
  process.exit(1);
23967
24102
  }
23968
24103
  if (configPath !== undefined) {
23969
24104
  try {
23970
- readFileSync8(configPath, "utf-8");
24105
+ readFileSync9(configPath, "utf-8");
23971
24106
  } catch (err) {
23972
24107
  process.stderr.write(`auth-broker fatal: failed to read ${configPath}: ${err.message}
23973
24108
  `);