switchroom 0.19.42 → 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.
- package/dist/agent-scheduler/index.js +4 -1
- package/dist/auth-broker/index.js +284 -124
- package/dist/cli/notion-write-pretool.mjs +4 -1
- package/dist/cli/switchroom.js +4094 -2711
- package/dist/host-control/main.js +472 -155
- package/dist/vault/approvals/kernel-server.js +231 -71
- package/dist/vault/broker/server.js +293 -133
- package/package.json +3 -3
- package/profiles/_base/start.sh.hbs +93 -8
- package/telegram-plugin/bridge/bridge.ts +1 -1
- package/telegram-plugin/card-layout.ts +63 -1
- package/telegram-plugin/dist/bridge/bridge.js +4 -1
- package/telegram-plugin/dist/gateway/gateway.js +1682 -936
- package/telegram-plugin/dist/server.js +5 -2
- package/telegram-plugin/flood-429-ledger.ts +5 -3
- package/telegram-plugin/flood-circuit-breaker.ts +11 -3
- package/telegram-plugin/format.ts +223 -13
- package/telegram-plugin/gateway/boot-card.ts +7 -2
- package/telegram-plugin/gateway/flood-reply-queue.ts +5 -0
- package/telegram-plugin/gateway/gateway.ts +49 -51
- package/telegram-plugin/gateway/handback-orphan-recovery.ts +69 -0
- package/telegram-plugin/gateway/handback-preturn-signal.ts +170 -32
- package/telegram-plugin/gateway/ipc-protocol.ts +31 -3
- package/telegram-plugin/gateway/ipc-server.ts +13 -4
- package/telegram-plugin/gateway/obligation-store.ts +55 -0
- package/telegram-plugin/gateway/outbound-send-path.ts +127 -11
- package/telegram-plugin/gateway/outbox-sweep.ts +371 -61
- package/telegram-plugin/gateway/rollout-status-edit.ts +175 -0
- package/telegram-plugin/gateway/stream-render.ts +5 -0
- package/telegram-plugin/gateway/update-announce.ts +11 -4
- package/telegram-plugin/hooks/audience-classify.d.mts +50 -0
- package/telegram-plugin/hooks/audience-classify.mjs +364 -0
- package/telegram-plugin/hooks/silent-end-interrupt-stop.mjs +86 -3
- package/telegram-plugin/hooks/silent-end-scan.mjs +80 -4
- package/telegram-plugin/outbox.ts +103 -2
- package/telegram-plugin/render/rich-render.ts +10 -2
- package/telegram-plugin/reply-quote.ts +150 -0
- package/telegram-plugin/retry-api-call.ts +254 -11
- package/telegram-plugin/shared/bot-runtime.ts +69 -34
- package/telegram-plugin/shared/gw-trace-gate.ts +14 -2
- package/telegram-plugin/silent-end.ts +34 -2
- package/telegram-plugin/status-no-truncate.ts +31 -16
- package/telegram-plugin/stream-controller.ts +44 -9
- package/telegram-plugin/stream-reply-handler.ts +5 -3
- package/telegram-plugin/tests/boot-card-render.test.ts +2 -1
- package/telegram-plugin/tests/card-budget-never-overflows.test.ts +165 -0
- package/telegram-plugin/tests/card-hard-truncate-entity-safe.test.ts +153 -0
- package/telegram-plugin/tests/card-variants.golden.txt +9 -9
- package/telegram-plugin/tests/flood-reply-queue.test.ts +89 -4
- package/telegram-plugin/tests/format-consistency.test.ts +160 -14
- package/telegram-plugin/tests/handback-orphan-recovery.test.ts +131 -0
- package/telegram-plugin/tests/handback-preturn-signal.test.ts +212 -17
- package/telegram-plugin/tests/nested-indent-idiom.test.ts +162 -0
- package/telegram-plugin/tests/nested-prefix-indent.test.ts +115 -0
- package/telegram-plugin/tests/no-robust-api-call-factory.test.ts +62 -0
- package/telegram-plugin/tests/obligation-store.test.ts +51 -0
- package/telegram-plugin/tests/outbox-audience-3865.test.ts +579 -0
- package/telegram-plugin/tests/outbox-provenance-4141.test.ts +1126 -0
- package/telegram-plugin/tests/outbox-sweep-single-flight.test.ts +138 -0
- package/telegram-plugin/tests/pinned-card-collapse.test.ts +12 -10
- package/telegram-plugin/tests/plain-fallback-4096-cap.test.ts +509 -0
- package/telegram-plugin/tests/reply-quote-wire.test.ts +433 -0
- package/telegram-plugin/tests/retry-grammy-http-error.test.ts +404 -0
- package/telegram-plugin/tests/rollout-narration-edit-socket.test.ts +170 -0
- package/telegram-plugin/tests/rollout-status-edit-retry-policy.test.ts +366 -0
- package/telegram-plugin/tests/rollout-status-edit.test.ts +140 -0
- package/telegram-plugin/tests/rollout-status-wiring.test.ts +40 -7
- package/telegram-plugin/tests/tg-post-logger-error-shape.test.ts +4 -0
- package/telegram-plugin/tests/tg-post-retry-attribution.test.ts +196 -0
- package/telegram-plugin/tests/tool-activity-summary.test.ts +23 -17
- package/telegram-plugin/tool-activity-summary.ts +58 -30
- package/telegram-plugin/uat/scenarios/jtbd-foreground-subagent-activity-dm.test.ts +3 -2
- package/vendor/hindsight-memory/scripts/lib/config.py +15 -0
- package/vendor/hindsight-memory/scripts/tests/test_retain_provenance_tag.py +135 -0
- package/vendor/hindsight-memory/settings.json +1 -1
|
@@ -10994,7 +10994,7 @@ var init_observation_scopes = __esm(() => {
|
|
|
10994
10994
|
});
|
|
10995
10995
|
|
|
10996
10996
|
// src/config/schema.ts
|
|
10997
|
-
var CodeRepoEntrySchema, AgentBindMountSchema, HttpDiffPollSchema, PollSpecSchema, TelegramMessageActionSchema, WebhookActionSchema, ActionSpecSchema, ScheduleEntrySchema, AgentSoulSchema, AgentToolsSchema, ObservationScopesSchema, ObservationScopeStrategySchema, AgentMemorySchema, HookEntrySchema, AgentHooksSchema, SubagentSchema, SessionSchema, SessionContinuitySchema, webhookDispatchRule, TelegramChannelSchema, ChannelsSchema, TIMEZONE_REGEX, ApproverIdSchema, GoogleWorkspaceTierSchema, GoogleWorkspaceConfigSchema, LiteLLMConfigSchema, HindsightPerOpLlmSchema, HindsightConfigSchema, MicrosoftWorkspaceConfigSchema, NotionWorkspaceConfigSchema, AgentGoogleWorkspaceConfigSchema, MicrosoftAccountEmailSchema, MicrosoftToolTokenSchema, MicrosoftAccountBindingSchema, AgentMicrosoftWorkspaceConfigSchema, AgentNotionWorkspaceConfigSchema, ReactionsSchema, ReactionDispatchSchema, releaseBlockFields, ReleaseBlock, RootReleaseBlock, NetworkIsolationSchema, servesField, knowsField, profileFields, ProfileSchema, _omitExtends, defaultsFields, AgentDefaultsSchema, AgentSchema, TelegramConfigSchema, MemoryBackendConfigSchema, VaultConfigSchema, QuotaConfigSchema, AutoReleaseCheckSchema, HostControlConfigSchema, WebServiceConfigSchema, FleetHealthConfigSchema, HostdConfigSchema, CronEgressSchema, CronConfigSchema, UserSchema, SwitchroomConfigSchema;
|
|
10997
|
+
var CodeRepoEntrySchema, AgentBindMountSchema, HttpDiffPollSchema, PollSpecSchema, TelegramMessageActionSchema, WebhookActionSchema, ActionSpecSchema, ScheduleEntrySchema, AgentSoulSchema, AgentToolsSchema, ObservationScopesSchema, ObservationScopeStrategySchema, AntiConfabulationDirectiveSchema, AgentMemorySchema, HookEntrySchema, AgentHooksSchema, SubagentSchema, SessionSchema, SessionContinuitySchema, webhookDispatchRule, TelegramChannelSchema, ChannelsSchema, TIMEZONE_REGEX, ApproverIdSchema, GoogleWorkspaceTierSchema, GoogleWorkspaceConfigSchema, LiteLLMConfigSchema, HindsightPerOpLlmSchema, HindsightConfigSchema, MicrosoftWorkspaceConfigSchema, NotionWorkspaceConfigSchema, AgentGoogleWorkspaceConfigSchema, MicrosoftAccountEmailSchema, MicrosoftToolTokenSchema, MicrosoftAccountBindingSchema, AgentMicrosoftWorkspaceConfigSchema, AgentNotionWorkspaceConfigSchema, ReactionsSchema, ReactionDispatchSchema, releaseBlockFields, ReleaseBlock, RootReleaseBlock, NetworkIsolationSchema, servesField, knowsField, profileFields, ProfileSchema, _omitExtends, defaultsFields, AgentDefaultsSchema, AgentSchema, TelegramConfigSchema, MemoryBackendConfigSchema, VaultConfigSchema, QuotaConfigSchema, AutoReleaseCheckSchema, HostControlConfigSchema, WebServiceConfigSchema, FleetHealthConfigSchema, HostdConfigSchema, CronEgressSchema, CronConfigSchema, UserSchema, SwitchroomConfigSchema;
|
|
10998
10998
|
var init_schema = __esm(() => {
|
|
10999
10999
|
init_zod();
|
|
11000
11000
|
init_observation_scopes();
|
|
@@ -11111,6 +11111,7 @@ var init_schema = __esm(() => {
|
|
|
11111
11111
|
}).optional();
|
|
11112
11112
|
ObservationScopesSchema = exports_external.enum(OBSERVATION_SCOPES).optional().describe("Operator PIN for the per-row observation scope, overriding the " + 'default `curated` strategy (see docs/configuration.md). "shared" ' + "forces Hindsight's consolidation to write EVERY observation into ONE " + "global untagged scope — what several agents pooling one bank want so " + "their observations merge rather than sitting in parallel silos. " + '"combined" is the pin form of opting out (restores the pre-#4035 ' + "engine default). UNSET does NOT mean 'nothing on the wire': the " + "`curated` strategy still computes a scope per retain. Applies to " + "every retain path (Stop hook, " + "sidechain, boot reconcile, queue drain, backfill, session-handoff " + "mirror) and is carried on the queued payload, so a retain that fails " + "now and drains later still lands in this scope. Accepted values: " + `${OBSERVATION_SCOPES.join(", ")}. ` + "Cascade: override (per-agent wins over default).");
|
|
11113
11113
|
ObservationScopeStrategySchema = exports_external.enum(OBSERVATION_SCOPE_STRATEGIES).optional().describe("Per-retain strategy that decides how the observation scope is " + 'computed when no `observation_scopes` pin is set. "curated" ' + "(the default since #4035) strips volatile per-session provenance " + "tags from each retain's consolidation scope — keeping the stable " + "semantic ones on the source fact — so an agent's observations dedup " + "and merge across sessions instead of pocketing one-per-session. " + '"shared" sends every retain to the one global untagged scope. ' + '"combined" / "off" opt OUT: no per-row scope goes on the wire, ' + "restoring the pre-feature engine default (byte-identical to an " + "unconfigured client). OMITTED BY DEFAULT: unset leaves the plugin's " + "own default (`curated`) in force. A manual `observation_scopes` pin " + "wins over this strategy. Accepted values: " + `${OBSERVATION_SCOPE_STRATEGIES.join(", ")}. ` + "Cascade: override (per-agent wins over default).");
|
|
11114
|
+
AntiConfabulationDirectiveSchema = exports_external.union([exports_external.boolean(), exports_external.string().min(1)]).optional().describe("The seeded anti-confabulation directive: reflect has no relevance " + "floor (`min_scores` is a /recall-only parameter), so a bank holding " + "nothing relevant still yields a confident answer, and a question that " + "presupposes a decision reliably produces one. Directives ARE applied " + "during reflect, so this is the lever. Unset or true (the default) " + "seeds and maintains switchroom's own text; false disables seeding and " + "leaves any existing directive untouched; a string is operator-authored " + "text that wins outright. Switchroom only ever upgrades directive text " + "byte-equal to a default it shipped, so editing the directive in the " + "bank is also a permanent override. " + "Cascade: override (per-agent wins over default).");
|
|
11114
11115
|
AgentMemorySchema = exports_external.object({
|
|
11115
11116
|
collection: exports_external.string().describe("Hindsight collection name for this agent"),
|
|
11116
11117
|
auto_recall: exports_external.boolean().default(true).describe("Auto-search memories before each response"),
|
|
@@ -11146,6 +11147,7 @@ var init_schema = __esm(() => {
|
|
|
11146
11147
|
empathy: exports_external.number().int().min(1).max(5).optional().describe("How much the bank weights emotional/relational context (1-5; engine default 3).")
|
|
11147
11148
|
}).optional().describe("Personality traits (1-5 each) steering how this bank frames recall, " + "reflect, and observation synthesis — a coach leans empathy-high, a " + "lawyer/analyst leans skepticism/literalism-high. Maps to the engine's " + "flat `disposition_skepticism`/`_literalism`/`_empathy` fields. " + "Cascade: per-key merge (an agent overrides individual traits and " + "inherits the rest, matching `recall`)."),
|
|
11148
11149
|
directive_capture_nudge: exports_external.boolean().optional().describe("Deterministic directive-capture nudge (issue #2848 Stage B). When " + "on (switchroom default true — Stage A measured a ~55% miss rate on " + "durable corrections), the auto-recall hook regex-detects correction " + '/ standing-rule-shaped inbound ("always/never …", "from now on …", ' + `"stop doing …", a stated preference, "that's wrong, it's …") and ` + "appends a terse advisory to the turn's context telling the model to " + "persist the rule with mcp__hindsight__create_directive if it IS " + "durable. Detection is pure regex — the model does the judgment " + "in-session and calls create_directive itself (no model callsite, no " + "silent hook-side write). Set false to disable per-agent. " + "Cascade: override (per-agent wins over default)."),
|
|
11150
|
+
anti_confabulation_directive: AntiConfabulationDirectiveSchema,
|
|
11149
11151
|
observation_scopes: ObservationScopesSchema,
|
|
11150
11152
|
observation_scope_strategy: ObservationScopeStrategySchema,
|
|
11151
11153
|
recall: exports_external.object({
|
|
@@ -11416,7 +11418,7 @@ var init_schema = __esm(() => {
|
|
|
11416
11418
|
reflect: HindsightPerOpLlmSchema.optional().describe("Per-op override for the `reflect` LLM op (synthesis / mental-model " + "refresh). Emits `HINDSIGHT_API_REFLECT_LLM_*`. Absent → uses global."),
|
|
11417
11419
|
consolidation: HindsightPerOpLlmSchema.optional().describe("Per-op override for the `consolidation` LLM op (background memory " + "merge). Emits `HINDSIGHT_API_CONSOLIDATION_LLM_*`. Absent → global.")
|
|
11418
11420
|
}).optional().describe("LLM knob for the hindsight container. The flat `provider`/`model` set " + "the global default (backward-compatible); optional `retain`/`reflect`/" + "`consolidation` blocks override individual ops. All fields optional; " + "unset fields fall back to the hard-coded defaults."),
|
|
11419
|
-
env: exports_external.record(exports_external.union([exports_external.string(), exports_external.number(), exports_external.boolean()])).optional().describe("Operator overrides for switchroom's capability-gated Hindsight " + "performance defaults. Only the keys switchroom actually manages are " + "honoured (`HINDSIGHT_PERF_ENV_KEYS` in " + "src/setup/hindsight-perf-defaults.ts: RERANKER_LOCAL_FP16, " + "RERANKER_LOCAL_BATCH_SIZE, LLM_MAX_CONCURRENT, " + "RETAIN/CONSOLIDATION_LLM_MAX_CONCURRENT, LLM_STRICT_SCHEMA, " + "LLM_MAX_RETRIES, CONSOLIDATION_LLM_PARALLELISM, " + "MAX_OBSERVATIONS_PER_SCOPE, " + "RECALL_MAX_CANDIDATES_PER_SOURCE, LINK_EXPANSION_PER_ENTITY_LIMIT, " + "LINK_EXPANSION_TIMEOUT, LLM_REASONING_EFFORT, " + "RERANKER_LOCAL_BUCKET_BATCHING, RERANKER_MAX_CANDIDATES, " + "RERANKER_LOCAL_MAX_CONCURRENT, RECALL_MAX_CONCURRENT, " + "REFLECT_WALL_TIMEOUT,
|
|
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
|
|
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
|
|
18774
|
+
existsSync as existsSync9,
|
|
18772
18775
|
lstatSync as lstatSync2,
|
|
18773
18776
|
mkdirSync as mkdirSync5,
|
|
18774
|
-
readFileSync as
|
|
18775
|
-
realpathSync as
|
|
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
|
|
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
|
|
18853
|
-
import { resolve as
|
|
18854
|
-
function
|
|
18855
|
-
|
|
18856
|
-
|
|
18857
|
-
|
|
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
|
|
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 =
|
|
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 (
|
|
18882
|
-
import_handlebars.default.registerPartial(name,
|
|
18933
|
+
if (existsSync5(fragPath)) {
|
|
18934
|
+
import_handlebars.default.registerPartial(name, readFileSync4(fragPath, "utf-8"));
|
|
18883
18935
|
}
|
|
18884
18936
|
}
|
|
18885
18937
|
|
|
@@ -18980,7 +19032,9 @@ var HINDSIGHT_DEFAULT_RECALL_MAX_CONCURRENT = 8;
|
|
|
18980
19032
|
var HINDSIGHT_DEFAULT_REFLECT_WALL_TIMEOUT_S = 600;
|
|
18981
19033
|
var HINDSIGHT_DEFAULT_CONSOLIDATION_MAX_MEMORIES_PER_ROUND = 250;
|
|
18982
19034
|
var HINDSIGHT_DEFAULT_CONSOLIDATION_SLOT_LIMIT = 6;
|
|
18983
|
-
var
|
|
19035
|
+
var HINDSIGHT_DEFAULT_CONSOLIDATION_RESERVED_SLOTS = 1;
|
|
19036
|
+
var HINDSIGHT_DEFAULT_GRAPH_SEED_MIN_SIMILARITY = 0.3;
|
|
19037
|
+
var HINDSIGHT_DEFAULT_LLM_SUPPORTS_MAX_ITEMS = "false";
|
|
18984
19038
|
var HINDSIGHT_DEFAULT_RECENCY_DECAY_FUNCTION = "exponential";
|
|
18985
19039
|
var HINDSIGHT_DEFAULT_RECENCY_DECAY_HALFLIFE_DAYS = 30;
|
|
18986
19040
|
var HINDSIGHT_PERF_DEFAULTS_UNGATED = [
|
|
@@ -19026,8 +19080,8 @@ var HINDSIGHT_PERF_DEFAULTS_UNGATED = [
|
|
|
19026
19080
|
String(HINDSIGHT_DEFAULT_REFLECT_WALL_TIMEOUT_S)
|
|
19027
19081
|
],
|
|
19028
19082
|
[
|
|
19029
|
-
"
|
|
19030
|
-
String(
|
|
19083
|
+
"HINDSIGHT_API_WORKER_CONSOLIDATION_RESERVED_SLOTS",
|
|
19084
|
+
String(HINDSIGHT_DEFAULT_CONSOLIDATION_RESERVED_SLOTS)
|
|
19031
19085
|
],
|
|
19032
19086
|
[
|
|
19033
19087
|
"HINDSIGHT_API_WORKER_CONSOLIDATION_SLOT_LIMIT",
|
|
@@ -19044,6 +19098,10 @@ var HINDSIGHT_PERF_DEFAULTS_UNGATED = [
|
|
|
19044
19098
|
[
|
|
19045
19099
|
"HINDSIGHT_API_RECENCY_DECAY_HALFLIFE_DAYS",
|
|
19046
19100
|
String(HINDSIGHT_DEFAULT_RECENCY_DECAY_HALFLIFE_DAYS)
|
|
19101
|
+
],
|
|
19102
|
+
[
|
|
19103
|
+
"HINDSIGHT_API_GRAPH_SEED_MIN_SIMILARITY",
|
|
19104
|
+
String(HINDSIGHT_DEFAULT_GRAPH_SEED_MIN_SIMILARITY)
|
|
19047
19105
|
]
|
|
19048
19106
|
];
|
|
19049
19107
|
var HINDSIGHT_PERF_DEFAULTS_GPU = [
|
|
@@ -19064,22 +19122,42 @@ var HINDSIGHT_PERF_DEFAULTS_LOCAL_LLM = [
|
|
|
19064
19122
|
String(HINDSIGHT_DEFAULT_CONSOLIDATION_LLM_MAX_CONCURRENT)
|
|
19065
19123
|
],
|
|
19066
19124
|
["HINDSIGHT_API_LLM_STRICT_SCHEMA", HINDSIGHT_DEFAULT_LLM_STRICT_SCHEMA],
|
|
19067
|
-
["HINDSIGHT_API_LLM_MAX_RETRIES", String(HINDSIGHT_DEFAULT_LLM_MAX_RETRIES)]
|
|
19125
|
+
["HINDSIGHT_API_LLM_MAX_RETRIES", String(HINDSIGHT_DEFAULT_LLM_MAX_RETRIES)],
|
|
19126
|
+
["HINDSIGHT_API_LLM_SUPPORTS_MAX_ITEMS", HINDSIGHT_DEFAULT_LLM_SUPPORTS_MAX_ITEMS]
|
|
19068
19127
|
];
|
|
19069
19128
|
var HINDSIGHT_PERF_OVERRIDE_ONLY_KEYS = new Set([
|
|
19070
19129
|
"HINDSIGHT_API_WORKER_CONSOLIDATION_BANK_PRIORITY",
|
|
19071
19130
|
"HINDSIGHT_CE_DECISIVE_RELATIVE_GAP",
|
|
19072
19131
|
"HINDSIGHT_API_RECENCY_DECAY_LINEAR_WINDOW_DAYS",
|
|
19073
19132
|
"HINDSIGHT_API_WORKER_MAX_SLOTS",
|
|
19074
|
-
"
|
|
19133
|
+
"HINDSIGHT_API_WORKER_RETAIN_RESERVED_SLOTS",
|
|
19134
|
+
"HINDSIGHT_API_SEMANTIC_MIN_SIMILARITY",
|
|
19135
|
+
"HINDSIGHT_MCP_RECALL_BUDGET_MODE",
|
|
19136
|
+
"HINDSIGHT_API_LLM_TEMPERATURE_REFLECT",
|
|
19137
|
+
"HINDSIGHT_API_RETAIN_WALL_TIMEOUT",
|
|
19138
|
+
"HINDSIGHT_API_CONSOLIDATION_RECALL_MAX_CONCURRENT"
|
|
19075
19139
|
]);
|
|
19140
|
+
var HINDSIGHT_WORKER_SLOT_TYPES = [
|
|
19141
|
+
"consolidation",
|
|
19142
|
+
"file_convert_retain",
|
|
19143
|
+
"graph_maintenance",
|
|
19144
|
+
"import_documents",
|
|
19145
|
+
"refresh_mental_model",
|
|
19146
|
+
"retain"
|
|
19147
|
+
];
|
|
19148
|
+
var HINDSIGHT_WORKER_RESERVED_SLOT_ALIASES = new Map(HINDSIGHT_WORKER_SLOT_TYPES.map((type) => [
|
|
19149
|
+
`HINDSIGHT_API_WORKER_${type.toUpperCase()}_MAX_SLOTS`,
|
|
19150
|
+
`HINDSIGHT_API_WORKER_${type.toUpperCase()}_RESERVED_SLOTS`
|
|
19151
|
+
]));
|
|
19076
19152
|
var HINDSIGHT_PERF_ENV_KEYS = new Set([
|
|
19077
19153
|
...[
|
|
19078
19154
|
...HINDSIGHT_PERF_DEFAULTS_UNGATED,
|
|
19079
19155
|
...HINDSIGHT_PERF_DEFAULTS_GPU,
|
|
19080
19156
|
...HINDSIGHT_PERF_DEFAULTS_LOCAL_LLM
|
|
19081
19157
|
].map(([k]) => k),
|
|
19082
|
-
...HINDSIGHT_PERF_OVERRIDE_ONLY_KEYS
|
|
19158
|
+
...HINDSIGHT_PERF_OVERRIDE_ONLY_KEYS,
|
|
19159
|
+
...HINDSIGHT_WORKER_RESERVED_SLOT_ALIASES.keys(),
|
|
19160
|
+
...HINDSIGHT_WORKER_RESERVED_SLOT_ALIASES.values()
|
|
19083
19161
|
]);
|
|
19084
19162
|
|
|
19085
19163
|
// src/setup/hindsight-context-budget.ts
|
|
@@ -19152,6 +19230,15 @@ var DEFAULT_RETAIN_MISSION = `Extract durable facts that will still be true and
|
|
|
19152
19230
|
` + `- Hindsight's own errors, retries, backlogs, or internal state — the memory
|
|
19153
19231
|
` + ` system's self-reports are not memories.
|
|
19154
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.
|
|
19155
19242
|
` + `- Volatile state written as a timeless assertion. A version, count, size,
|
|
19156
19243
|
` + ` backlog, status, or any "X is running Y" / "X is at Y" / "X is currently Y"
|
|
19157
19244
|
` + ` claim is true only at the instant it was said. Concretely, never produce a
|
|
@@ -19307,6 +19394,64 @@ var SUPERSEDED_RETAIN_MISSIONS = [
|
|
|
19307
19394
|
` + `
|
|
19308
19395
|
` + `If a candidate fact matches an exclusion, drop it rather than rewording it. If
|
|
19309
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.
|
|
19310
19455
|
`
|
|
19311
19456
|
];
|
|
19312
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.
|
|
@@ -19407,6 +19552,22 @@ function normalizeMicrosoftBindings(mw) {
|
|
|
19407
19552
|
var warnedMissingPool = new Set;
|
|
19408
19553
|
var warnedMissingDefault = new Set;
|
|
19409
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
|
+
|
|
19410
19571
|
// src/telegram/state.ts
|
|
19411
19572
|
init_paths();
|
|
19412
19573
|
|
|
@@ -19444,7 +19605,6 @@ var RECALL_PASSTHROUGH_DEFAULTS = Object.freeze({
|
|
|
19444
19605
|
init_paths();
|
|
19445
19606
|
|
|
19446
19607
|
// src/agents/scaffold.ts
|
|
19447
|
-
var REPO_ROOT = resolve5(import.meta.dirname, "../..");
|
|
19448
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.";
|
|
19449
19609
|
var pendingBankOps = new Set;
|
|
19450
19610
|
var SWITCHROOM_OWNED_SETTINGS_KEYS = new Set([
|
|
@@ -20116,11 +20276,11 @@ async function fetchAndSummarizeExternalSpend(opts) {
|
|
|
20116
20276
|
|
|
20117
20277
|
// src/auth/broker/mirror-write.ts
|
|
20118
20278
|
init_atomic();
|
|
20119
|
-
import { lstatSync, realpathSync as
|
|
20279
|
+
import { lstatSync, realpathSync as realpathSync3 } from "node:fs";
|
|
20120
20280
|
function safeMirrorWrite(input) {
|
|
20121
20281
|
let realParent;
|
|
20122
20282
|
try {
|
|
20123
|
-
realParent =
|
|
20283
|
+
realParent = realpathSync3(input.parentDir);
|
|
20124
20284
|
} catch (err) {
|
|
20125
20285
|
input.logErr(`mirror parent realpath failed: ${err.message}`);
|
|
20126
20286
|
return false;
|
|
@@ -20152,16 +20312,16 @@ function safeMirrorWrite(input) {
|
|
|
20152
20312
|
init_atomic();
|
|
20153
20313
|
import {
|
|
20154
20314
|
chownSync,
|
|
20155
|
-
existsSync as
|
|
20315
|
+
existsSync as existsSync6,
|
|
20156
20316
|
mkdirSync as mkdirSync2,
|
|
20157
|
-
readFileSync as
|
|
20317
|
+
readFileSync as readFileSync5,
|
|
20158
20318
|
readdirSync as readdirSync3,
|
|
20159
20319
|
renameSync as renameSync2,
|
|
20160
20320
|
rmSync as rmSync2,
|
|
20161
20321
|
statSync as statSync3
|
|
20162
20322
|
} from "node:fs";
|
|
20163
20323
|
import { homedir as homedir4 } from "node:os";
|
|
20164
|
-
import { join as
|
|
20324
|
+
import { join as join4, resolve as resolve6 } from "node:path";
|
|
20165
20325
|
var LABEL_MAX = 64;
|
|
20166
20326
|
var LABEL_RE = /^[A-Za-z0-9._@+-]+$/;
|
|
20167
20327
|
function accountsRootOverride() {
|
|
@@ -20174,13 +20334,13 @@ function accountsRoot(home2 = homedir4()) {
|
|
|
20174
20334
|
return accountsRootOverride() ?? resolve6(home2, ".switchroom", "accounts");
|
|
20175
20335
|
}
|
|
20176
20336
|
function accountDir(label, home2 = homedir4()) {
|
|
20177
|
-
return
|
|
20337
|
+
return join4(accountsRoot(home2), label);
|
|
20178
20338
|
}
|
|
20179
20339
|
function accountCredentialsPath(label, home2 = homedir4()) {
|
|
20180
|
-
return
|
|
20340
|
+
return join4(accountDir(label, home2), "credentials.json");
|
|
20181
20341
|
}
|
|
20182
20342
|
function accountMetaPath(label, home2 = homedir4()) {
|
|
20183
|
-
return
|
|
20343
|
+
return join4(accountDir(label, home2), "meta.json");
|
|
20184
20344
|
}
|
|
20185
20345
|
function validateAccountLabel(label) {
|
|
20186
20346
|
if (typeof label !== "string" || label.length === 0) {
|
|
@@ -20201,12 +20361,12 @@ function validateAccountLabel(label) {
|
|
|
20201
20361
|
}
|
|
20202
20362
|
function listAccounts(home2 = homedir4()) {
|
|
20203
20363
|
const root = accountsRoot(home2);
|
|
20204
|
-
if (!
|
|
20364
|
+
if (!existsSync6(root))
|
|
20205
20365
|
return [];
|
|
20206
20366
|
try {
|
|
20207
20367
|
return readdirSync3(root).filter((name) => {
|
|
20208
20368
|
try {
|
|
20209
|
-
return statSync3(
|
|
20369
|
+
return statSync3(join4(root, name)).isDirectory();
|
|
20210
20370
|
} catch {
|
|
20211
20371
|
return false;
|
|
20212
20372
|
}
|
|
@@ -20216,14 +20376,14 @@ function listAccounts(home2 = homedir4()) {
|
|
|
20216
20376
|
}
|
|
20217
20377
|
}
|
|
20218
20378
|
function accountExists(label, home2 = homedir4()) {
|
|
20219
|
-
return
|
|
20379
|
+
return existsSync6(accountCredentialsPath(label, home2));
|
|
20220
20380
|
}
|
|
20221
20381
|
function readAccountCredentials(label, home2 = homedir4()) {
|
|
20222
20382
|
const p = accountCredentialsPath(label, home2);
|
|
20223
|
-
if (!
|
|
20383
|
+
if (!existsSync6(p))
|
|
20224
20384
|
return null;
|
|
20225
20385
|
try {
|
|
20226
|
-
return JSON.parse(
|
|
20386
|
+
return JSON.parse(readFileSync5(p, "utf-8"));
|
|
20227
20387
|
} catch {
|
|
20228
20388
|
return null;
|
|
20229
20389
|
}
|
|
@@ -20249,21 +20409,21 @@ function writeAccountCredentials(label, value, home2 = homedir4()) {
|
|
|
20249
20409
|
function chownAccountFiles(label, uid, home2 = homedir4()) {
|
|
20250
20410
|
validateAccountLabel(label);
|
|
20251
20411
|
const dir = accountDir(label, home2);
|
|
20252
|
-
if (
|
|
20412
|
+
if (existsSync6(dir))
|
|
20253
20413
|
chownSync(dir, uid, uid);
|
|
20254
20414
|
const creds = accountCredentialsPath(label, home2);
|
|
20255
|
-
if (
|
|
20415
|
+
if (existsSync6(creds))
|
|
20256
20416
|
chownSync(creds, uid, uid);
|
|
20257
20417
|
const meta = accountMetaPath(label, home2);
|
|
20258
|
-
if (
|
|
20418
|
+
if (existsSync6(meta))
|
|
20259
20419
|
chownSync(meta, uid, uid);
|
|
20260
20420
|
}
|
|
20261
20421
|
function readAccountMeta(label, home2 = homedir4()) {
|
|
20262
20422
|
const p = accountMetaPath(label, home2);
|
|
20263
|
-
if (!
|
|
20423
|
+
if (!existsSync6(p))
|
|
20264
20424
|
return null;
|
|
20265
20425
|
try {
|
|
20266
|
-
return JSON.parse(
|
|
20426
|
+
return JSON.parse(readFileSync5(p, "utf-8"));
|
|
20267
20427
|
} catch {
|
|
20268
20428
|
return null;
|
|
20269
20429
|
}
|
|
@@ -20787,8 +20947,8 @@ function resolveMicrosoftClientId(configClientId) {
|
|
|
20787
20947
|
|
|
20788
20948
|
// src/auth/broker/google-storage.ts
|
|
20789
20949
|
init_atomic();
|
|
20790
|
-
import { existsSync as
|
|
20791
|
-
import { dirname as dirname2, join as
|
|
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";
|
|
20792
20952
|
function normalizeGoogleAccountForStorage(account) {
|
|
20793
20953
|
return account.trim().toLowerCase();
|
|
20794
20954
|
}
|
|
@@ -20810,17 +20970,17 @@ function googleAccountDir(stateDir, account) {
|
|
|
20810
20970
|
return resolve7(stateDir, "google", normalizeGoogleAccountForStorage(account));
|
|
20811
20971
|
}
|
|
20812
20972
|
function googleAccountCredentialsPath(stateDir, account) {
|
|
20813
|
-
return
|
|
20973
|
+
return join5(googleAccountDir(stateDir, account), "credentials.json");
|
|
20814
20974
|
}
|
|
20815
20975
|
function googleAccountExists(stateDir, account) {
|
|
20816
|
-
return
|
|
20976
|
+
return existsSync7(googleAccountCredentialsPath(stateDir, account));
|
|
20817
20977
|
}
|
|
20818
20978
|
function readGoogleAccountCredentials(stateDir, account) {
|
|
20819
20979
|
const path = googleAccountCredentialsPath(stateDir, account);
|
|
20820
|
-
if (!
|
|
20980
|
+
if (!existsSync7(path))
|
|
20821
20981
|
return null;
|
|
20822
20982
|
try {
|
|
20823
|
-
const raw =
|
|
20983
|
+
const raw = readFileSync6(path, "utf-8");
|
|
20824
20984
|
const parsed = JSON.parse(raw);
|
|
20825
20985
|
if (!parsed?.googleOauth?.accessToken)
|
|
20826
20986
|
return null;
|
|
@@ -20837,27 +20997,27 @@ function writeGoogleAccountCredentials(stateDir, account, credentials) {
|
|
|
20837
20997
|
}
|
|
20838
20998
|
function removeGoogleAccount(stateDir, account) {
|
|
20839
20999
|
const dir = googleAccountDir(stateDir, account);
|
|
20840
|
-
if (
|
|
21000
|
+
if (existsSync7(dir)) {
|
|
20841
21001
|
rmSync3(dir, { recursive: true, force: true });
|
|
20842
21002
|
}
|
|
20843
21003
|
}
|
|
20844
21004
|
function listGoogleAccounts(stateDir) {
|
|
20845
|
-
const root =
|
|
20846
|
-
if (!
|
|
21005
|
+
const root = join5(stateDir, "google");
|
|
21006
|
+
if (!existsSync7(root))
|
|
20847
21007
|
return [];
|
|
20848
21008
|
return readdirSync4(root).filter((name) => {
|
|
20849
21009
|
try {
|
|
20850
|
-
return statSync4(
|
|
21010
|
+
return statSync4(join5(root, name)).isDirectory();
|
|
20851
21011
|
} catch {
|
|
20852
21012
|
return false;
|
|
20853
21013
|
}
|
|
20854
|
-
}).filter((name) =>
|
|
21014
|
+
}).filter((name) => existsSync7(join5(root, name, "credentials.json")));
|
|
20855
21015
|
}
|
|
20856
21016
|
|
|
20857
21017
|
// src/auth/broker/microsoft-storage.ts
|
|
20858
21018
|
init_atomic();
|
|
20859
|
-
import { existsSync as
|
|
20860
|
-
import { dirname as dirname3, join as
|
|
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";
|
|
20861
21021
|
function normalizeMicrosoftAccountForStorage(account) {
|
|
20862
21022
|
return account.trim().toLowerCase();
|
|
20863
21023
|
}
|
|
@@ -20879,17 +21039,17 @@ function microsoftAccountDir(stateDir, account) {
|
|
|
20879
21039
|
return resolve8(stateDir, "microsoft", normalizeMicrosoftAccountForStorage(account));
|
|
20880
21040
|
}
|
|
20881
21041
|
function microsoftAccountCredentialsPath(stateDir, account) {
|
|
20882
|
-
return
|
|
21042
|
+
return join6(microsoftAccountDir(stateDir, account), "credentials.json");
|
|
20883
21043
|
}
|
|
20884
21044
|
function microsoftAccountExists(stateDir, account) {
|
|
20885
|
-
return
|
|
21045
|
+
return existsSync8(microsoftAccountCredentialsPath(stateDir, account));
|
|
20886
21046
|
}
|
|
20887
21047
|
function readMicrosoftAccountCredentials(stateDir, account) {
|
|
20888
21048
|
const path = microsoftAccountCredentialsPath(stateDir, account);
|
|
20889
|
-
if (!
|
|
21049
|
+
if (!existsSync8(path))
|
|
20890
21050
|
return null;
|
|
20891
21051
|
try {
|
|
20892
|
-
const raw =
|
|
21052
|
+
const raw = readFileSync7(path, "utf-8");
|
|
20893
21053
|
const parsed = JSON.parse(raw);
|
|
20894
21054
|
if (!parsed?.microsoftOauth?.accessToken)
|
|
20895
21055
|
return null;
|
|
@@ -20906,21 +21066,21 @@ function writeMicrosoftAccountCredentials(stateDir, account, credentials) {
|
|
|
20906
21066
|
}
|
|
20907
21067
|
function removeMicrosoftAccount(stateDir, account) {
|
|
20908
21068
|
const dir = microsoftAccountDir(stateDir, account);
|
|
20909
|
-
if (
|
|
21069
|
+
if (existsSync8(dir)) {
|
|
20910
21070
|
rmSync4(dir, { recursive: true, force: true });
|
|
20911
21071
|
}
|
|
20912
21072
|
}
|
|
20913
21073
|
function listMicrosoftAccounts(stateDir) {
|
|
20914
|
-
const root =
|
|
20915
|
-
if (!
|
|
21074
|
+
const root = join6(stateDir, "microsoft");
|
|
21075
|
+
if (!existsSync8(root))
|
|
20916
21076
|
return [];
|
|
20917
21077
|
return readdirSync5(root).filter((name) => {
|
|
20918
21078
|
try {
|
|
20919
|
-
return statSync5(
|
|
21079
|
+
return statSync5(join6(root, name)).isDirectory();
|
|
20920
21080
|
} catch {
|
|
20921
21081
|
return false;
|
|
20922
21082
|
}
|
|
20923
|
-
}).filter((name) =>
|
|
21083
|
+
}).filter((name) => existsSync8(join6(root, name, "credentials.json")));
|
|
20924
21084
|
}
|
|
20925
21085
|
|
|
20926
21086
|
// src/auth/broker/provider.ts
|
|
@@ -21456,7 +21616,7 @@ class AuthBroker {
|
|
|
21456
21616
|
async start() {
|
|
21457
21617
|
process.umask(63);
|
|
21458
21618
|
mkdirSync5(this.stateDir, { recursive: true, mode: 448 });
|
|
21459
|
-
mkdirSync5(
|
|
21619
|
+
mkdirSync5(join7(this.stateDir, "refresh-lease"), { recursive: true, mode: 448 });
|
|
21460
21620
|
mkdirSync5(this.socketRoot, { recursive: true, mode: 493 });
|
|
21461
21621
|
this.loadStateFromDisk();
|
|
21462
21622
|
this.assertDriftFree();
|
|
@@ -21505,7 +21665,7 @@ class AuthBroker {
|
|
|
21505
21665
|
}
|
|
21506
21666
|
if (!this.opts.skipHealthyMarker) {
|
|
21507
21667
|
try {
|
|
21508
|
-
const healthyPath =
|
|
21668
|
+
const healthyPath = join7(this.stateDir, "healthy");
|
|
21509
21669
|
writeFileSync2(healthyPath, String(this.now()) + `
|
|
21510
21670
|
`, { mode: 384 });
|
|
21511
21671
|
} catch (err) {
|
|
@@ -21544,14 +21704,14 @@ class AuthBroker {
|
|
|
21544
21704
|
} catch {}
|
|
21545
21705
|
}
|
|
21546
21706
|
try {
|
|
21547
|
-
if (
|
|
21707
|
+
if (existsSync9(sock))
|
|
21548
21708
|
unlinkSync(sock);
|
|
21549
21709
|
} catch {}
|
|
21550
21710
|
}
|
|
21551
21711
|
this.listeners.clear();
|
|
21552
21712
|
try {
|
|
21553
|
-
const healthyPath =
|
|
21554
|
-
if (
|
|
21713
|
+
const healthyPath = join7(this.stateDir, "healthy");
|
|
21714
|
+
if (existsSync9(healthyPath))
|
|
21555
21715
|
unlinkSync(healthyPath);
|
|
21556
21716
|
} catch {}
|
|
21557
21717
|
}
|
|
@@ -21582,7 +21742,7 @@ class AuthBroker {
|
|
|
21582
21742
|
lis.server.close();
|
|
21583
21743
|
} catch {}
|
|
21584
21744
|
try {
|
|
21585
|
-
if (
|
|
21745
|
+
if (existsSync9(sock))
|
|
21586
21746
|
unlinkSync(sock);
|
|
21587
21747
|
} catch {}
|
|
21588
21748
|
for (const s of lis.sockets) {
|
|
@@ -21616,13 +21776,13 @@ class AuthBroker {
|
|
|
21616
21776
|
}
|
|
21617
21777
|
}
|
|
21618
21778
|
agentSocketPath(name) {
|
|
21619
|
-
return
|
|
21779
|
+
return join7(this.socketRoot, name, "sock");
|
|
21620
21780
|
}
|
|
21621
21781
|
consumerSocketPath(name) {
|
|
21622
|
-
return
|
|
21782
|
+
return join7(this.socketRoot, name, "sock");
|
|
21623
21783
|
}
|
|
21624
21784
|
operatorSocketPath() {
|
|
21625
|
-
return
|
|
21785
|
+
return join7(this.socketRoot, "operator", "sock");
|
|
21626
21786
|
}
|
|
21627
21787
|
async bindAgentListener(agentName) {
|
|
21628
21788
|
if (RESERVED_NAMES.has(agentName)) {
|
|
@@ -21654,7 +21814,7 @@ class AuthBroker {
|
|
|
21654
21814
|
}
|
|
21655
21815
|
async bindListener(sockPath, targetUid, sockMode, identity) {
|
|
21656
21816
|
const dir = dirname4(sockPath);
|
|
21657
|
-
if (
|
|
21817
|
+
if (existsSync9(dir)) {
|
|
21658
21818
|
try {
|
|
21659
21819
|
chownSync2(dir, 0, 0);
|
|
21660
21820
|
} catch {}
|
|
@@ -21664,7 +21824,7 @@ class AuthBroker {
|
|
|
21664
21824
|
} else {
|
|
21665
21825
|
mkdirSync5(dir, { recursive: true, mode: 448 });
|
|
21666
21826
|
}
|
|
21667
|
-
if (
|
|
21827
|
+
if (existsSync9(sockPath)) {
|
|
21668
21828
|
try {
|
|
21669
21829
|
unlinkSync(sockPath);
|
|
21670
21830
|
} catch {}
|
|
@@ -22491,7 +22651,7 @@ class AuthBroker {
|
|
|
22491
22651
|
...earliestReset != null ? { earliest_reset: earliestReset } : {},
|
|
22492
22652
|
at: this.now()
|
|
22493
22653
|
};
|
|
22494
|
-
atomicWriteJsonSync(
|
|
22654
|
+
atomicWriteJsonSync(join7(this.stateDir, "premium-tier-all-walled.json"), this.premiumTierAllWalled, 384);
|
|
22495
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
|
|
22496
22656
|
`);
|
|
22497
22657
|
}
|
|
@@ -22500,7 +22660,7 @@ class AuthBroker {
|
|
|
22500
22660
|
return;
|
|
22501
22661
|
this.premiumTierAllWalled = null;
|
|
22502
22662
|
try {
|
|
22503
|
-
unlinkSync(
|
|
22663
|
+
unlinkSync(join7(this.stateDir, "premium-tier-all-walled.json"));
|
|
22504
22664
|
} catch {}
|
|
22505
22665
|
}
|
|
22506
22666
|
premiumCanarySet() {
|
|
@@ -22836,10 +22996,10 @@ class AuthBroker {
|
|
|
22836
22996
|
if (envKey)
|
|
22837
22997
|
return envKey;
|
|
22838
22998
|
try {
|
|
22839
|
-
const path =
|
|
22840
|
-
if (!
|
|
22999
|
+
const path = join7(this.stateDir, LITELLM_MASTER_KEY_STATE_BASENAME);
|
|
23000
|
+
if (!existsSync9(path))
|
|
22841
23001
|
return null;
|
|
22842
|
-
const raw =
|
|
23002
|
+
const raw = readFileSync8(path, "utf-8").trim();
|
|
22843
23003
|
return raw.length > 0 ? raw : null;
|
|
22844
23004
|
} catch {
|
|
22845
23005
|
return null;
|
|
@@ -22960,7 +23120,7 @@ class AuthBroker {
|
|
|
22960
23120
|
delete this.softAvoidState[label];
|
|
22961
23121
|
delete this.softAvoidRolledTo[label];
|
|
22962
23122
|
this.chownAccountFilesIfRoot(label);
|
|
22963
|
-
const contents =
|
|
23123
|
+
const contents = readFileSync8(accountCredentialsPath(label, this.home), "utf-8");
|
|
22964
23124
|
this.shaIndex[label] = sha256Hex(contents);
|
|
22965
23125
|
this.lastWrittenExpiresAt.set(label, credentials.claudeAiOauth?.expiresAt);
|
|
22966
23126
|
this.persistShaIndex();
|
|
@@ -23358,7 +23518,7 @@ class AuthBroker {
|
|
|
23358
23518
|
return { kind: "noop" };
|
|
23359
23519
|
}
|
|
23360
23520
|
}
|
|
23361
|
-
const leasePath =
|
|
23521
|
+
const leasePath = join7(this.stateDir, "refresh-lease", label);
|
|
23362
23522
|
let leaseFd = null;
|
|
23363
23523
|
try {
|
|
23364
23524
|
leaseFd = openSync2(leasePath, constants2.O_RDWR | constants2.O_CREAT, 384);
|
|
@@ -23375,7 +23535,7 @@ class AuthBroker {
|
|
|
23375
23535
|
const creds = readAccountCredentials(label, this.home);
|
|
23376
23536
|
const newExpiresAt = creds?.claudeAiOauth?.expiresAt ?? outcome.newExpiresAt;
|
|
23377
23537
|
this.lastWrittenExpiresAt.set(label, newExpiresAt);
|
|
23378
|
-
const contents =
|
|
23538
|
+
const contents = readFileSync8(accountCredentialsPath(label, this.home), "utf-8");
|
|
23379
23539
|
this.shaIndex[label] = sha256Hex(contents);
|
|
23380
23540
|
this.persistShaIndex();
|
|
23381
23541
|
this.fanoutToAffectedAgents(label);
|
|
@@ -23462,9 +23622,9 @@ class AuthBroker {
|
|
|
23462
23622
|
if (!mirrorDir)
|
|
23463
23623
|
return false;
|
|
23464
23624
|
const credsPath = accountCredentialsPath(label, this.home);
|
|
23465
|
-
if (!
|
|
23625
|
+
if (!existsSync9(credsPath))
|
|
23466
23626
|
return false;
|
|
23467
|
-
const mirrorContent = enrichMirrorContent(
|
|
23627
|
+
const mirrorContent = enrichMirrorContent(readFileSync8(credsPath, "utf-8"));
|
|
23468
23628
|
const safe = this.resolveConsumerMirrorPathsSafe(consumer.name, mirrorDir);
|
|
23469
23629
|
if (!safe)
|
|
23470
23630
|
return false;
|
|
@@ -23507,7 +23667,7 @@ class AuthBroker {
|
|
|
23507
23667
|
return null;
|
|
23508
23668
|
}
|
|
23509
23669
|
}
|
|
23510
|
-
const targetPath =
|
|
23670
|
+
const targetPath = join7(mirrorDir, ".credentials.json");
|
|
23511
23671
|
const targetStat = this.lstatOrNull(targetPath);
|
|
23512
23672
|
if (targetStat) {
|
|
23513
23673
|
if (targetStat.isSymbolicLink())
|
|
@@ -23517,7 +23677,7 @@ class AuthBroker {
|
|
|
23517
23677
|
}
|
|
23518
23678
|
let expectedRealParent;
|
|
23519
23679
|
try {
|
|
23520
|
-
expectedRealParent =
|
|
23680
|
+
expectedRealParent = join7(realpathSync4(parent), basename2(mirrorDir));
|
|
23521
23681
|
} catch (err) {
|
|
23522
23682
|
this.logErr(`consumer-mirror ${consumerName}: realpath ${parent}: ${err.message}`);
|
|
23523
23683
|
return null;
|
|
@@ -23574,8 +23734,8 @@ class AuthBroker {
|
|
|
23574
23734
|
resolveMirrorPathsSafe(agentName) {
|
|
23575
23735
|
const agentsDir = resolveAgentsDir(this.config);
|
|
23576
23736
|
const agentDir = resolve9(agentsDir, agentName);
|
|
23577
|
-
const claudeDir =
|
|
23578
|
-
const targetPath =
|
|
23737
|
+
const claudeDir = join7(agentDir, ".claude");
|
|
23738
|
+
const targetPath = join7(claudeDir, ".credentials.json");
|
|
23579
23739
|
const refuse = (component, reason) => {
|
|
23580
23740
|
this.auditMirrorRefused({ kind: "agent", name: agentName, admin: false }, component, reason);
|
|
23581
23741
|
return null;
|
|
@@ -23608,7 +23768,7 @@ class AuthBroker {
|
|
|
23608
23768
|
if (!targetStat.isFile())
|
|
23609
23769
|
return refuse(".credentials.json", "is-not-a-regular-file");
|
|
23610
23770
|
}
|
|
23611
|
-
const expectedRealParent =
|
|
23771
|
+
const expectedRealParent = join7(realpathSync4(agentsDir), agentName, ".claude");
|
|
23612
23772
|
return { agentDir, claudeDir, targetPath, expectedRealParent };
|
|
23613
23773
|
}
|
|
23614
23774
|
lstatOrNull(path) {
|
|
@@ -23630,9 +23790,9 @@ class AuthBroker {
|
|
|
23630
23790
|
}
|
|
23631
23791
|
mirrorAccountToAgent(label, agentName) {
|
|
23632
23792
|
const credsPath = accountCredentialsPath(label, this.home);
|
|
23633
|
-
if (!
|
|
23793
|
+
if (!existsSync9(credsPath))
|
|
23634
23794
|
return false;
|
|
23635
|
-
const mirrorContent = enrichMirrorContent(
|
|
23795
|
+
const mirrorContent = enrichMirrorContent(readFileSync8(credsPath, "utf-8"));
|
|
23636
23796
|
const safe = this.resolveMirrorPathsSafe(agentName);
|
|
23637
23797
|
if (!safe)
|
|
23638
23798
|
return false;
|
|
@@ -23686,7 +23846,7 @@ class AuthBroker {
|
|
|
23686
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
|
|
23687
23847
|
`);
|
|
23688
23848
|
try {
|
|
23689
|
-
unlinkSync(
|
|
23849
|
+
unlinkSync(join7(this.stateDir, "active-override.json"));
|
|
23690
23850
|
} catch {}
|
|
23691
23851
|
return;
|
|
23692
23852
|
}
|
|
@@ -23710,34 +23870,34 @@ class AuthBroker {
|
|
|
23710
23870
|
yaml_active_at_write: this.yamlActive ?? null,
|
|
23711
23871
|
updated_at: this.now()
|
|
23712
23872
|
};
|
|
23713
|
-
atomicWriteJsonSync(
|
|
23873
|
+
atomicWriteJsonSync(join7(this.stateDir, "active-override.json"), entry, 384);
|
|
23714
23874
|
}
|
|
23715
23875
|
readJson(name) {
|
|
23716
|
-
const p =
|
|
23717
|
-
if (!
|
|
23876
|
+
const p = join7(this.stateDir, name);
|
|
23877
|
+
if (!existsSync9(p))
|
|
23718
23878
|
return null;
|
|
23719
23879
|
try {
|
|
23720
|
-
return JSON.parse(
|
|
23880
|
+
return JSON.parse(readFileSync8(p, "utf-8"));
|
|
23721
23881
|
} catch {
|
|
23722
23882
|
return null;
|
|
23723
23883
|
}
|
|
23724
23884
|
}
|
|
23725
23885
|
persistQuota() {
|
|
23726
|
-
atomicWriteJsonSync(
|
|
23886
|
+
atomicWriteJsonSync(join7(this.stateDir, "quota.json"), this.quota, 384);
|
|
23727
23887
|
}
|
|
23728
23888
|
persistLastQuotaCache() {
|
|
23729
|
-
atomicWriteJsonSync(
|
|
23889
|
+
atomicWriteJsonSync(join7(this.stateDir, "last-quota.json"), this.lastQuotaCache, 384);
|
|
23730
23890
|
}
|
|
23731
23891
|
persistExternalSpendCache() {
|
|
23732
23892
|
if (!this.externalSpendCache)
|
|
23733
23893
|
return;
|
|
23734
|
-
atomicWriteJsonSync(
|
|
23894
|
+
atomicWriteJsonSync(join7(this.stateDir, "external-spend.json"), this.externalSpendCache, 384);
|
|
23735
23895
|
}
|
|
23736
23896
|
persistLastTierQuotaCache() {
|
|
23737
|
-
atomicWriteJsonSync(
|
|
23897
|
+
atomicWriteJsonSync(join7(this.stateDir, "last-tier-quota.json"), this.lastTierQuotaCache, 384);
|
|
23738
23898
|
}
|
|
23739
23899
|
persistUsageLedger() {
|
|
23740
|
-
atomicWriteJsonSync(
|
|
23900
|
+
atomicWriteJsonSync(join7(this.stateDir, "usage-ledger.json"), this.usageLedger, 384);
|
|
23741
23901
|
}
|
|
23742
23902
|
recordUsage(label, tier, sample) {
|
|
23743
23903
|
if (sample == null)
|
|
@@ -23750,26 +23910,26 @@ class AuthBroker {
|
|
|
23750
23910
|
}
|
|
23751
23911
|
}
|
|
23752
23912
|
persistNotificationClaims() {
|
|
23753
|
-
atomicWriteJsonSync(
|
|
23913
|
+
atomicWriteJsonSync(join7(this.stateDir, "notification-claims.json"), this.notificationClaims, 384);
|
|
23754
23914
|
}
|
|
23755
23915
|
persistLastFleetRoll() {
|
|
23756
23916
|
if (this.lastFleetRoll)
|
|
23757
|
-
atomicWriteJsonSync(
|
|
23917
|
+
atomicWriteJsonSync(join7(this.stateDir, "last-fleet-roll.json"), this.lastFleetRoll, 384);
|
|
23758
23918
|
}
|
|
23759
23919
|
persistShaIndex() {
|
|
23760
|
-
atomicWriteJsonSync(
|
|
23920
|
+
atomicWriteJsonSync(join7(this.stateDir, "sha-index.json"), this.shaIndex, 384);
|
|
23761
23921
|
}
|
|
23762
23922
|
persistThresholdViolations() {
|
|
23763
|
-
atomicWriteJsonSync(
|
|
23923
|
+
atomicWriteJsonSync(join7(this.stateDir, "threshold-violations.json"), this.thresholdViolations, 384);
|
|
23764
23924
|
}
|
|
23765
23925
|
assertDriftFree() {
|
|
23766
23926
|
for (const label of Object.keys(this.shaIndex)) {
|
|
23767
23927
|
const p = accountCredentialsPath(label, this.home);
|
|
23768
|
-
if (!
|
|
23928
|
+
if (!existsSync9(p)) {
|
|
23769
23929
|
this.logErr(`DRIFT_DETECTED ${label}: index entry but no on-disk credentials`);
|
|
23770
23930
|
process.exit(1);
|
|
23771
23931
|
}
|
|
23772
|
-
const got = sha256Hex(
|
|
23932
|
+
const got = sha256Hex(readFileSync8(p, "utf-8"));
|
|
23773
23933
|
if (got !== this.shaIndex[label]) {
|
|
23774
23934
|
this.logErr(`DRIFT_DETECTED ${label}: sha256 mismatch (recover with 'switchroom auth add ${label} --replace')`);
|
|
23775
23935
|
process.exit(1);
|
|
@@ -23795,7 +23955,7 @@ class AuthBroker {
|
|
|
23795
23955
|
replace: entry.replace,
|
|
23796
23956
|
reason: entry.reason
|
|
23797
23957
|
});
|
|
23798
|
-
const auditPath =
|
|
23958
|
+
const auditPath = join7(this.stateDir, "audit.jsonl");
|
|
23799
23959
|
try {
|
|
23800
23960
|
this.rotateAuditIfLarge(auditPath);
|
|
23801
23961
|
const line = row + `
|
|
@@ -23836,7 +23996,7 @@ class AuthBroker {
|
|
|
23836
23996
|
for (let i = AUDIT_KEEP - 1;i >= 1; i--) {
|
|
23837
23997
|
const src = `${path}.${i}`;
|
|
23838
23998
|
const dst = `${path}.${i + 1}`;
|
|
23839
|
-
if (
|
|
23999
|
+
if (existsSync9(src)) {
|
|
23840
24000
|
try {
|
|
23841
24001
|
renameSync3(src, dst);
|
|
23842
24002
|
} catch {}
|
|
@@ -23935,14 +24095,14 @@ async function main() {
|
|
|
23935
24095
|
const flags = parseFlags(argv);
|
|
23936
24096
|
const operatorUid = flags.operatorUid ?? operatorUidFromEnv();
|
|
23937
24097
|
const configPath = process.env.SWITCHROOM_CONFIG;
|
|
23938
|
-
if (configPath !== undefined && !
|
|
24098
|
+
if (configPath !== undefined && !existsSync10(configPath)) {
|
|
23939
24099
|
process.stderr.write(`auth-broker fatal: SWITCHROOM_CONFIG='${configPath}' does not exist
|
|
23940
24100
|
`);
|
|
23941
24101
|
process.exit(1);
|
|
23942
24102
|
}
|
|
23943
24103
|
if (configPath !== undefined) {
|
|
23944
24104
|
try {
|
|
23945
|
-
|
|
24105
|
+
readFileSync9(configPath, "utf-8");
|
|
23946
24106
|
} catch (err) {
|
|
23947
24107
|
process.stderr.write(`auth-broker fatal: failed to read ${configPath}: ${err.message}
|
|
23948
24108
|
`);
|