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.
- package/dist/agent-scheduler/index.js +4 -1
- package/dist/auth-broker/index.js +254 -119
- package/dist/cli/notion-write-pretool.mjs +4 -1
- package/dist/cli/switchroom.js +3281 -2416
- package/dist/host-control/main.js +270 -130
- package/dist/vault/approvals/kernel-server.js +201 -66
- package/dist/vault/broker/server.js +263 -128
- package/package.json +1 -1
- package/profiles/_base/start.sh.hbs +11 -4
- package/telegram-plugin/dist/gateway/gateway.js +463 -328
- 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
|
@@ -4030,7 +4030,7 @@ var init_observation_scopes = __esm(() => {
|
|
|
4030
4030
|
});
|
|
4031
4031
|
|
|
4032
4032
|
// src/config/schema.ts
|
|
4033
|
-
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;
|
|
4033
|
+
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;
|
|
4034
4034
|
var init_schema = __esm(() => {
|
|
4035
4035
|
init_zod();
|
|
4036
4036
|
init_observation_scopes();
|
|
@@ -4147,6 +4147,7 @@ var init_schema = __esm(() => {
|
|
|
4147
4147
|
}).optional();
|
|
4148
4148
|
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).");
|
|
4149
4149
|
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).");
|
|
4150
|
+
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).");
|
|
4150
4151
|
AgentMemorySchema = exports_external.object({
|
|
4151
4152
|
collection: exports_external.string().describe("Hindsight collection name for this agent"),
|
|
4152
4153
|
auto_recall: exports_external.boolean().default(true).describe("Auto-search memories before each response"),
|
|
@@ -4182,6 +4183,7 @@ var init_schema = __esm(() => {
|
|
|
4182
4183
|
empathy: exports_external.number().int().min(1).max(5).optional().describe("How much the bank weights emotional/relational context (1-5; engine default 3).")
|
|
4183
4184
|
}).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`)."),
|
|
4184
4185
|
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)."),
|
|
4186
|
+
anti_confabulation_directive: AntiConfabulationDirectiveSchema,
|
|
4185
4187
|
observation_scopes: ObservationScopesSchema,
|
|
4186
4188
|
observation_scope_strategy: ObservationScopeStrategySchema,
|
|
4187
4189
|
recall: exports_external.object({
|
|
@@ -4452,7 +4454,7 @@ var init_schema = __esm(() => {
|
|
|
4452
4454
|
reflect: HindsightPerOpLlmSchema.optional().describe("Per-op override for the `reflect` LLM op (synthesis / mental-model " + "refresh). Emits `HINDSIGHT_API_REFLECT_LLM_*`. Absent → uses global."),
|
|
4453
4455
|
consolidation: HindsightPerOpLlmSchema.optional().describe("Per-op override for the `consolidation` LLM op (background memory " + "merge). Emits `HINDSIGHT_API_CONSOLIDATION_LLM_*`. Absent → global.")
|
|
4454
4456
|
}).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."),
|
|
4455
|
-
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).")
|
|
4457
|
+
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).")
|
|
4456
4458
|
});
|
|
4457
4459
|
MicrosoftWorkspaceConfigSchema = exports_external.object({
|
|
4458
4460
|
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)."),
|
|
@@ -4586,6 +4588,7 @@ var init_schema = __esm(() => {
|
|
|
4586
4588
|
isolation: exports_external.enum(["default", "strict"]).optional(),
|
|
4587
4589
|
profile: exports_external.string().optional(),
|
|
4588
4590
|
directive_capture_nudge: exports_external.boolean().optional(),
|
|
4591
|
+
anti_confabulation_directive: AntiConfabulationDirectiveSchema,
|
|
4589
4592
|
observation_scopes: ObservationScopesSchema,
|
|
4590
4593
|
observation_scope_strategy: ObservationScopeStrategySchema,
|
|
4591
4594
|
recall: exports_external.object({
|
|
@@ -5075,7 +5078,7 @@ function parseSocketIdentity(socketPath, spec) {
|
|
|
5075
5078
|
|
|
5076
5079
|
// src/vault/broker/peercred.ts
|
|
5077
5080
|
import { execFileSync } from "node:child_process";
|
|
5078
|
-
import { readFileSync, readlinkSync, fstatSync } from "node:fs";
|
|
5081
|
+
import { readFileSync as readFileSync2, readlinkSync, fstatSync } from "node:fs";
|
|
5079
5082
|
function socketPathToIdentity(socketPath) {
|
|
5080
5083
|
return parseSocketIdentity(socketPath, {
|
|
5081
5084
|
patterns: [SOCKET_PATH_AGENT_RE, SOCKET_PATH_AGENT_SUBDIR_RE],
|
|
@@ -5156,7 +5159,7 @@ function findClientPidByServerInode(rows, socketPath, serverInode) {
|
|
|
5156
5159
|
}
|
|
5157
5160
|
function readUid(pid) {
|
|
5158
5161
|
try {
|
|
5159
|
-
const status =
|
|
5162
|
+
const status = readFileSync2(`/proc/${pid}/status`, "utf8");
|
|
5160
5163
|
const m = status.match(/^Uid:\s+(\d+)/m);
|
|
5161
5164
|
if (!m)
|
|
5162
5165
|
return null;
|
|
@@ -5174,7 +5177,7 @@ function readExe(pid) {
|
|
|
5174
5177
|
}
|
|
5175
5178
|
function readSystemdUnit(pid) {
|
|
5176
5179
|
try {
|
|
5177
|
-
const content =
|
|
5180
|
+
const content = readFileSync2(`/proc/${pid}/cgroup`, "utf8");
|
|
5178
5181
|
const lines = content.split(`
|
|
5179
5182
|
`);
|
|
5180
5183
|
for (const line of lines) {
|
|
@@ -12691,8 +12694,8 @@ var require_dist = __commonJS((exports) => {
|
|
|
12691
12694
|
});
|
|
12692
12695
|
|
|
12693
12696
|
// src/config/paths.ts
|
|
12694
|
-
import { existsSync } from "node:fs";
|
|
12695
|
-
import { resolve } from "node:path";
|
|
12697
|
+
import { existsSync as existsSync2 } from "node:fs";
|
|
12698
|
+
import { resolve as resolve2 } from "node:path";
|
|
12696
12699
|
function home() {
|
|
12697
12700
|
return process.env.HOME ?? "/root";
|
|
12698
12701
|
}
|
|
@@ -12707,12 +12710,12 @@ function resolveDualPath(pathStr) {
|
|
|
12707
12710
|
const h = home();
|
|
12708
12711
|
if (pathStr.startsWith("~/")) {
|
|
12709
12712
|
const rest = pathStr.slice(2);
|
|
12710
|
-
const absolute =
|
|
12713
|
+
const absolute = resolve2(h, rest);
|
|
12711
12714
|
if (rest.startsWith(`${DEFAULT_STATE_DIR}/`)) {
|
|
12712
12715
|
const frag = rest.slice(DEFAULT_STATE_DIR.length + 1);
|
|
12713
|
-
if (!
|
|
12714
|
-
const legacy =
|
|
12715
|
-
if (
|
|
12716
|
+
if (!existsSync2(absolute)) {
|
|
12717
|
+
const legacy = resolve2(h, LEGACY_STATE_DIR, frag);
|
|
12718
|
+
if (existsSync2(legacy)) {
|
|
12716
12719
|
warnLegacyStateOnce(legacy);
|
|
12717
12720
|
return legacy;
|
|
12718
12721
|
}
|
|
@@ -12720,7 +12723,7 @@ function resolveDualPath(pathStr) {
|
|
|
12720
12723
|
}
|
|
12721
12724
|
return absolute;
|
|
12722
12725
|
}
|
|
12723
|
-
return
|
|
12726
|
+
return resolve2(pathStr);
|
|
12724
12727
|
}
|
|
12725
12728
|
var DEFAULT_STATE_DIR = ".switchroom", LEGACY_STATE_DIR = ".clerk", _legacyStateWarned = false;
|
|
12726
12729
|
var init_paths = () => {};
|
|
@@ -12737,8 +12740,8 @@ var init_overlay_schema = __esm(() => {
|
|
|
12737
12740
|
});
|
|
12738
12741
|
|
|
12739
12742
|
// src/config/overlay-loader.ts
|
|
12740
|
-
import { existsSync as
|
|
12741
|
-
import { basename, resolve as
|
|
12743
|
+
import { existsSync as existsSync3, readFileSync as readFileSync3, readdirSync, statSync } from "node:fs";
|
|
12744
|
+
import { basename, resolve as resolve3 } from "node:path";
|
|
12742
12745
|
function deriveOverlayTitle(raw, fileName) {
|
|
12743
12746
|
const titleFromComment = raw.match(/^#[^\S\n]*name:[^\S\n]*(\S.*?)[^\S\n]*$/m)?.[1];
|
|
12744
12747
|
if (titleFromComment)
|
|
@@ -12750,10 +12753,10 @@ function deriveOverlayTitle(raw, fileName) {
|
|
|
12750
12753
|
}
|
|
12751
12754
|
function overlayDirFor(agentName, subdir) {
|
|
12752
12755
|
const base = resolveDualPath(`~/.switchroom/agents/${agentName}/${subdir}`);
|
|
12753
|
-
return
|
|
12756
|
+
return resolve3(base);
|
|
12754
12757
|
}
|
|
12755
12758
|
function listYamlFiles(dir) {
|
|
12756
|
-
if (!
|
|
12759
|
+
if (!existsSync3(dir))
|
|
12757
12760
|
return [];
|
|
12758
12761
|
let entries;
|
|
12759
12762
|
try {
|
|
@@ -12765,7 +12768,7 @@ function listYamlFiles(dir) {
|
|
|
12765
12768
|
for (const name of entries) {
|
|
12766
12769
|
if (!/\.ya?ml$/i.test(name))
|
|
12767
12770
|
continue;
|
|
12768
|
-
const full =
|
|
12771
|
+
const full = resolve3(dir, name);
|
|
12769
12772
|
try {
|
|
12770
12773
|
if (statSync(full).isFile())
|
|
12771
12774
|
out.push(full);
|
|
@@ -12801,7 +12804,7 @@ function applyAgentOverlays(config) {
|
|
|
12801
12804
|
const merged = [...agentCfg.schedule ?? []];
|
|
12802
12805
|
for (const file of files) {
|
|
12803
12806
|
try {
|
|
12804
|
-
const raw =
|
|
12807
|
+
const raw = readFileSync3(file, "utf-8");
|
|
12805
12808
|
const parsed = import_yaml.parse(raw);
|
|
12806
12809
|
const doc = OverlayDocSchema.parse(parsed);
|
|
12807
12810
|
const title = deriveOverlayTitle(raw, basename(file));
|
|
@@ -12842,7 +12845,7 @@ function applyAgentOverlays(config) {
|
|
|
12842
12845
|
const seen = new Set(merged);
|
|
12843
12846
|
for (const file of skillFiles) {
|
|
12844
12847
|
try {
|
|
12845
|
-
const raw =
|
|
12848
|
+
const raw = readFileSync3(file, "utf-8");
|
|
12846
12849
|
const parsed = import_yaml.parse(raw);
|
|
12847
12850
|
const doc = OverlayDocSchema.parse(parsed);
|
|
12848
12851
|
for (const skillName of doc.skills ?? []) {
|
|
@@ -12917,9 +12920,9 @@ __export(exports_loader, {
|
|
|
12917
12920
|
findConfigFile: () => findConfigFile,
|
|
12918
12921
|
ConfigError: () => ConfigError
|
|
12919
12922
|
});
|
|
12920
|
-
import { readFileSync as
|
|
12923
|
+
import { readFileSync as readFileSync4, existsSync as existsSync4 } from "node:fs";
|
|
12921
12924
|
import { homedir } from "node:os";
|
|
12922
|
-
import { resolve as
|
|
12925
|
+
import { resolve as resolve4 } from "node:path";
|
|
12923
12926
|
function formatZodErrors(error) {
|
|
12924
12927
|
return error.errors.map((e) => {
|
|
12925
12928
|
const path = e.path.join(".");
|
|
@@ -12967,24 +12970,24 @@ function coerceLegacyGoogleWorkspaceKeys(parsed, filePath) {
|
|
|
12967
12970
|
function findConfigFile(startDir) {
|
|
12968
12971
|
const envPath = process.env.SWITCHROOM_CONFIG;
|
|
12969
12972
|
const home2 = homedir();
|
|
12970
|
-
const userDir =
|
|
12973
|
+
const userDir = resolve4(home2, ".switchroom");
|
|
12971
12974
|
const searchPaths = [
|
|
12972
|
-
envPath ?
|
|
12973
|
-
startDir ?
|
|
12974
|
-
startDir ?
|
|
12975
|
-
startDir ?
|
|
12976
|
-
startDir ?
|
|
12977
|
-
|
|
12978
|
-
|
|
12979
|
-
|
|
12980
|
-
|
|
12981
|
-
|
|
12982
|
-
|
|
12983
|
-
|
|
12984
|
-
|
|
12975
|
+
envPath ? resolve4(envPath) : null,
|
|
12976
|
+
startDir ? resolve4(startDir, "switchroom.yaml") : null,
|
|
12977
|
+
startDir ? resolve4(startDir, "switchroom.yml") : null,
|
|
12978
|
+
startDir ? resolve4(startDir, "clerk.yaml") : null,
|
|
12979
|
+
startDir ? resolve4(startDir, "clerk.yml") : null,
|
|
12980
|
+
resolve4(process.cwd(), "switchroom.yaml"),
|
|
12981
|
+
resolve4(process.cwd(), "switchroom.yml"),
|
|
12982
|
+
resolve4(process.cwd(), "clerk.yaml"),
|
|
12983
|
+
resolve4(process.cwd(), "clerk.yml"),
|
|
12984
|
+
resolve4(userDir, "switchroom.yaml"),
|
|
12985
|
+
resolve4(userDir, "switchroom.yml"),
|
|
12986
|
+
resolve4(userDir, "clerk.yaml"),
|
|
12987
|
+
resolve4(userDir, "clerk.yml")
|
|
12985
12988
|
].filter(Boolean);
|
|
12986
12989
|
for (const path of searchPaths) {
|
|
12987
|
-
if (
|
|
12990
|
+
if (existsSync4(path)) {
|
|
12988
12991
|
return path;
|
|
12989
12992
|
}
|
|
12990
12993
|
}
|
|
@@ -12992,12 +12995,12 @@ function findConfigFile(startDir) {
|
|
|
12992
12995
|
}
|
|
12993
12996
|
function loadConfig(configPath) {
|
|
12994
12997
|
const filePath = configPath ?? findConfigFile();
|
|
12995
|
-
if (!
|
|
12998
|
+
if (!existsSync4(filePath)) {
|
|
12996
12999
|
throw new ConfigError(`Config file not found: ${filePath}`);
|
|
12997
13000
|
}
|
|
12998
13001
|
let raw;
|
|
12999
13002
|
try {
|
|
13000
|
-
raw =
|
|
13003
|
+
raw = readFileSync4(filePath, "utf-8");
|
|
13001
13004
|
} catch (err) {
|
|
13002
13005
|
throw new ConfigError(`Failed to read config file: ${filePath}`, [
|
|
13003
13006
|
` ${err.message}`
|
|
@@ -18674,13 +18677,13 @@ var require_lib = __commonJS((exports, module) => {
|
|
|
18674
18677
|
|
|
18675
18678
|
// src/vault/flock.ts
|
|
18676
18679
|
import {
|
|
18677
|
-
existsSync as
|
|
18680
|
+
existsSync as existsSync6,
|
|
18678
18681
|
openSync as openSync2,
|
|
18679
18682
|
closeSync as closeSync2,
|
|
18680
18683
|
writeSync as writeSync2,
|
|
18681
18684
|
fsyncSync as fsyncSync2,
|
|
18682
18685
|
unlinkSync,
|
|
18683
|
-
readFileSync as
|
|
18686
|
+
readFileSync as readFileSync6,
|
|
18684
18687
|
readdirSync as readdirSync3,
|
|
18685
18688
|
rmdirSync,
|
|
18686
18689
|
statSync as statSync3,
|
|
@@ -18691,7 +18694,7 @@ function lockPathFor(vaultPath) {
|
|
|
18691
18694
|
}
|
|
18692
18695
|
function readLockHolder(lockPath) {
|
|
18693
18696
|
try {
|
|
18694
|
-
const raw =
|
|
18697
|
+
const raw = readFileSync6(lockPath, "utf8");
|
|
18695
18698
|
const lines = raw.split(`
|
|
18696
18699
|
`);
|
|
18697
18700
|
const pid = Number.parseInt(lines[0] ?? "", 10);
|
|
@@ -18707,7 +18710,7 @@ function readLockHolder(lockPath) {
|
|
|
18707
18710
|
}
|
|
18708
18711
|
function pidIsLive(pid) {
|
|
18709
18712
|
if (process.platform === "linux") {
|
|
18710
|
-
return
|
|
18713
|
+
return existsSync6(`/proc/${pid}`);
|
|
18711
18714
|
}
|
|
18712
18715
|
try {
|
|
18713
18716
|
process.kill(pid, 0);
|
|
@@ -18743,8 +18746,8 @@ function pidStartTimeMs(pid) {
|
|
|
18743
18746
|
if (process.platform !== "linux")
|
|
18744
18747
|
return null;
|
|
18745
18748
|
try {
|
|
18746
|
-
const statLine =
|
|
18747
|
-
const procStat =
|
|
18749
|
+
const statLine = readFileSync6(`/proc/${pid}/stat`, "utf8");
|
|
18750
|
+
const procStat = readFileSync6("/proc/stat", "utf8");
|
|
18748
18751
|
return parseProcStartTimeMs(statLine, procStat, Date.now());
|
|
18749
18752
|
} catch {
|
|
18750
18753
|
return null;
|
|
@@ -18767,7 +18770,7 @@ function lockFileMtimeIsOlderThan(lockPath, budgetMs) {
|
|
|
18767
18770
|
}
|
|
18768
18771
|
function clearStaleSentinelDir(lockPath) {
|
|
18769
18772
|
try {
|
|
18770
|
-
if (!
|
|
18773
|
+
if (!existsSync6(lockPath))
|
|
18771
18774
|
return true;
|
|
18772
18775
|
const s = statSync3(lockPath);
|
|
18773
18776
|
if (!s.isDirectory())
|
|
@@ -18889,9 +18892,9 @@ var init_flock = __esm(() => {
|
|
|
18889
18892
|
// src/vault/vault.ts
|
|
18890
18893
|
import { randomBytes, scryptSync, createCipheriv, createDecipheriv } from "node:crypto";
|
|
18891
18894
|
import {
|
|
18892
|
-
readFileSync as
|
|
18895
|
+
readFileSync as readFileSync7,
|
|
18893
18896
|
writeSync as writeSync3,
|
|
18894
|
-
existsSync as
|
|
18897
|
+
existsSync as existsSync7,
|
|
18895
18898
|
renameSync as renameSync2,
|
|
18896
18899
|
mkdirSync as mkdirSync2,
|
|
18897
18900
|
unlinkSync as unlinkSync2,
|
|
@@ -18899,18 +18902,18 @@ import {
|
|
|
18899
18902
|
openSync as openSync3,
|
|
18900
18903
|
closeSync as closeSync3,
|
|
18901
18904
|
lstatSync,
|
|
18902
|
-
realpathSync as
|
|
18905
|
+
realpathSync as realpathSync3
|
|
18903
18906
|
} from "node:fs";
|
|
18904
|
-
import { dirname, basename as basename2, resolve as
|
|
18907
|
+
import { dirname as dirname2, basename as basename2, resolve as resolve6 } from "node:path";
|
|
18905
18908
|
function atomicWriteFileSync(path, data, mode) {
|
|
18906
18909
|
let effectivePath = path;
|
|
18907
18910
|
try {
|
|
18908
|
-
if (
|
|
18909
|
-
effectivePath =
|
|
18911
|
+
if (existsSync7(path) && lstatSync(path).isSymbolicLink()) {
|
|
18912
|
+
effectivePath = realpathSync3(path);
|
|
18910
18913
|
}
|
|
18911
18914
|
} catch {}
|
|
18912
|
-
const dir =
|
|
18913
|
-
const tmp =
|
|
18915
|
+
const dir = dirname2(resolve6(effectivePath));
|
|
18916
|
+
const tmp = resolve6(dir, `.${basename2(effectivePath)}.${process.pid}.${Date.now()}.tmp`);
|
|
18914
18917
|
try {
|
|
18915
18918
|
const fd = openSync3(tmp, "wx", mode);
|
|
18916
18919
|
try {
|
|
@@ -18930,7 +18933,7 @@ function atomicWriteFileSync(path, data, mode) {
|
|
|
18930
18933
|
} catch {}
|
|
18931
18934
|
} catch (err) {
|
|
18932
18935
|
try {
|
|
18933
|
-
if (
|
|
18936
|
+
if (existsSync7(tmp))
|
|
18934
18937
|
unlinkSync2(tmp);
|
|
18935
18938
|
} catch {}
|
|
18936
18939
|
throw err;
|
|
@@ -18978,12 +18981,12 @@ function normalizeSecrets(raw) {
|
|
|
18978
18981
|
return out;
|
|
18979
18982
|
}
|
|
18980
18983
|
function openVault(passphrase, vaultPath) {
|
|
18981
|
-
if (!
|
|
18984
|
+
if (!existsSync7(vaultPath)) {
|
|
18982
18985
|
throw new VaultError(`Vault file not found: ${vaultPath}`);
|
|
18983
18986
|
}
|
|
18984
18987
|
let vaultFile;
|
|
18985
18988
|
try {
|
|
18986
|
-
vaultFile = JSON.parse(
|
|
18989
|
+
vaultFile = JSON.parse(readFileSync7(vaultPath, "utf8"));
|
|
18987
18990
|
} catch {
|
|
18988
18991
|
throw new VaultError(`Failed to read vault file: ${vaultPath}`);
|
|
18989
18992
|
}
|
|
@@ -19005,7 +19008,7 @@ function openVault(passphrase, vaultPath) {
|
|
|
19005
19008
|
return normalizeSecrets(vaultData.secrets ?? {});
|
|
19006
19009
|
}
|
|
19007
19010
|
function saveVault(passphrase, vaultPath, secrets) {
|
|
19008
|
-
if (!
|
|
19011
|
+
if (!existsSync7(vaultPath)) {
|
|
19009
19012
|
throw new VaultError(`Vault file not found: ${vaultPath}`);
|
|
19010
19013
|
}
|
|
19011
19014
|
let releaseLock = null;
|
|
@@ -19020,7 +19023,7 @@ function saveVault(passphrase, vaultPath, secrets) {
|
|
|
19020
19023
|
try {
|
|
19021
19024
|
let vaultFile;
|
|
19022
19025
|
try {
|
|
19023
|
-
vaultFile = JSON.parse(
|
|
19026
|
+
vaultFile = JSON.parse(readFileSync7(vaultPath, "utf8"));
|
|
19024
19027
|
} catch {
|
|
19025
19028
|
throw new VaultError(`Failed to read vault file: ${vaultPath}`);
|
|
19026
19029
|
}
|
|
@@ -19456,15 +19459,72 @@ var init_resolver = __esm(() => {
|
|
|
19456
19459
|
|
|
19457
19460
|
// src/vault/broker/server.ts
|
|
19458
19461
|
import * as net from "node:net";
|
|
19459
|
-
import { mkdirSync as mkdirSync8, chmodSync as chmodSync5, chownSync as chownSync2, existsSync as
|
|
19462
|
+
import { mkdirSync as mkdirSync8, chmodSync as chmodSync5, chownSync as chownSync2, existsSync as existsSync13, readFileSync as readFileSync13, readdirSync as readdirSync5, statSync as statSync7, unlinkSync as unlinkSync6, writeFileSync as writeFileSync5 } from "node:fs";
|
|
19460
19463
|
|
|
19461
19464
|
// src/agents/compose.ts
|
|
19462
19465
|
init_schema();
|
|
19463
19466
|
|
|
19464
19467
|
// src/agents/scaffold.ts
|
|
19465
|
-
import { dirname as dirname2, isAbsolute, join as join4, relative, resolve as resolve6 } from "node:path";
|
|
19466
19468
|
init_atomic();
|
|
19467
19469
|
|
|
19470
|
+
// src/util/shipped-assets.ts
|
|
19471
|
+
import { existsSync, readFileSync, realpathSync } from "node:fs";
|
|
19472
|
+
import { dirname, resolve } from "node:path";
|
|
19473
|
+
var FHS_SHARE_ROOTS = [
|
|
19474
|
+
"/usr/local/share/switchroom",
|
|
19475
|
+
"/usr/share/switchroom"
|
|
19476
|
+
];
|
|
19477
|
+
var PROFILES_ASSET = {
|
|
19478
|
+
asset: "profiles",
|
|
19479
|
+
envVar: "SWITCHROOM_PROFILES_ROOT"
|
|
19480
|
+
};
|
|
19481
|
+
function orderedCandidates(spec, probe) {
|
|
19482
|
+
const out = [];
|
|
19483
|
+
const push = (path, source) => {
|
|
19484
|
+
if (!out.some((c) => c.path === path))
|
|
19485
|
+
out.push({ path, source });
|
|
19486
|
+
};
|
|
19487
|
+
if (probe.bundleDir) {
|
|
19488
|
+
push(resolve(probe.bundleDir, "../..", spec.asset), "npm");
|
|
19489
|
+
push(resolve(probe.bundleDir, spec.asset), "image");
|
|
19490
|
+
}
|
|
19491
|
+
if (probe.execPath) {
|
|
19492
|
+
push(resolve(dirname(probe.execPath), "../share/switchroom", spec.asset), "sea-sibling");
|
|
19493
|
+
}
|
|
19494
|
+
for (const root of FHS_SHARE_ROOTS) {
|
|
19495
|
+
push(resolve(root, spec.asset), "fhs");
|
|
19496
|
+
}
|
|
19497
|
+
return out;
|
|
19498
|
+
}
|
|
19499
|
+
function resolveShippedAsset(spec, probe) {
|
|
19500
|
+
const env = probe.env ?? process.env;
|
|
19501
|
+
const override = env[spec.envVar]?.trim();
|
|
19502
|
+
if (override) {
|
|
19503
|
+
const path = canonicalise(resolve(override), probe);
|
|
19504
|
+
return { path, candidates: [path], source: "env" };
|
|
19505
|
+
}
|
|
19506
|
+
const exists = probe.exists ?? existsSync;
|
|
19507
|
+
const candidates = orderedCandidates(spec, probe);
|
|
19508
|
+
for (const c of candidates) {
|
|
19509
|
+
if (exists(c.path)) {
|
|
19510
|
+
return {
|
|
19511
|
+
path: canonicalise(c.path, probe),
|
|
19512
|
+
candidates: candidates.map((x) => x.path),
|
|
19513
|
+
source: c.source
|
|
19514
|
+
};
|
|
19515
|
+
}
|
|
19516
|
+
}
|
|
19517
|
+
return { path: null, candidates: candidates.map((x) => x.path), source: "none" };
|
|
19518
|
+
}
|
|
19519
|
+
function canonicalise(path, probe) {
|
|
19520
|
+
const realpath = probe.realpath ?? realpathSync;
|
|
19521
|
+
try {
|
|
19522
|
+
return realpath(path);
|
|
19523
|
+
} catch {
|
|
19524
|
+
return path;
|
|
19525
|
+
}
|
|
19526
|
+
}
|
|
19527
|
+
|
|
19468
19528
|
// src/agents/agent-uid.ts
|
|
19469
19529
|
init_peercred();
|
|
19470
19530
|
import { createHash } from "node:crypto";
|
|
@@ -19695,37 +19755,29 @@ var AUDIT_ROOT = join(homedir2(), ".switchroom", "audit");
|
|
|
19695
19755
|
|
|
19696
19756
|
// src/agents/profiles.ts
|
|
19697
19757
|
var import_handlebars = __toESM(require_lib(), 1);
|
|
19698
|
-
import { readFileSync as
|
|
19699
|
-
import { resolve as
|
|
19700
|
-
function
|
|
19701
|
-
|
|
19702
|
-
|
|
19703
|
-
|
|
19704
|
-
}
|
|
19705
|
-
const candidates = [
|
|
19706
|
-
resolve4(import.meta.dirname, "../../profiles"),
|
|
19707
|
-
resolve4(import.meta.dirname, "profiles")
|
|
19708
|
-
];
|
|
19709
|
-
for (const candidate of candidates) {
|
|
19710
|
-
if (existsSync4(candidate)) {
|
|
19711
|
-
return candidate;
|
|
19712
|
-
}
|
|
19713
|
-
}
|
|
19714
|
-
return candidates[0];
|
|
19758
|
+
import { readFileSync as readFileSync5, writeFileSync, existsSync as existsSync5, readdirSync as readdirSync2, statSync as statSync2, copyFileSync, mkdirSync, realpathSync as realpathSync2 } from "node:fs";
|
|
19759
|
+
import { resolve as resolve5, join as join2, sep as pathSep } from "node:path";
|
|
19760
|
+
function resolveProfilesRootDetailed() {
|
|
19761
|
+
return resolveShippedAsset(PROFILES_ASSET, {
|
|
19762
|
+
bundleDir: import.meta.dirname,
|
|
19763
|
+
execPath: process.execPath
|
|
19764
|
+
});
|
|
19715
19765
|
}
|
|
19716
|
-
var
|
|
19766
|
+
var PROFILES_RESOLUTION = resolveProfilesRootDetailed();
|
|
19767
|
+
var PROFILES_ROOT = PROFILES_RESOLUTION.path ?? PROFILES_RESOLUTION.candidates[0];
|
|
19768
|
+
var PROFILES_ROOT_SEARCH = PROFILES_RESOLUTION.candidates;
|
|
19717
19769
|
import_handlebars.default.registerHelper("json", (value) => {
|
|
19718
19770
|
return new import_handlebars.default.SafeString(JSON.stringify(value, null, 2));
|
|
19719
19771
|
});
|
|
19720
19772
|
import_handlebars.default.registerHelper("isNumber", (value) => {
|
|
19721
19773
|
return typeof value === "number" && Number.isFinite(value);
|
|
19722
19774
|
});
|
|
19723
|
-
var SHARED_FRAGMENTS_DIR =
|
|
19775
|
+
var SHARED_FRAGMENTS_DIR = resolve5(PROFILES_ROOT, "_shared");
|
|
19724
19776
|
var SHARED_FRAGMENTS = ["vault-protocol", "agent-self-service", "execution-discipline", "reply-discipline", "dev-protocol"];
|
|
19725
19777
|
for (const name of SHARED_FRAGMENTS) {
|
|
19726
19778
|
const fragPath = join2(SHARED_FRAGMENTS_DIR, `${name}.md.hbs`);
|
|
19727
|
-
if (
|
|
19728
|
-
import_handlebars.default.registerPartial(name,
|
|
19779
|
+
if (existsSync5(fragPath)) {
|
|
19780
|
+
import_handlebars.default.registerPartial(name, readFileSync5(fragPath, "utf-8"));
|
|
19729
19781
|
}
|
|
19730
19782
|
}
|
|
19731
19783
|
|
|
@@ -19928,7 +19980,8 @@ var HINDSIGHT_PERF_OVERRIDE_ONLY_KEYS = new Set([
|
|
|
19928
19980
|
"HINDSIGHT_API_SEMANTIC_MIN_SIMILARITY",
|
|
19929
19981
|
"HINDSIGHT_MCP_RECALL_BUDGET_MODE",
|
|
19930
19982
|
"HINDSIGHT_API_LLM_TEMPERATURE_REFLECT",
|
|
19931
|
-
"HINDSIGHT_API_RETAIN_WALL_TIMEOUT"
|
|
19983
|
+
"HINDSIGHT_API_RETAIN_WALL_TIMEOUT",
|
|
19984
|
+
"HINDSIGHT_API_CONSOLIDATION_RECALL_MAX_CONCURRENT"
|
|
19932
19985
|
]);
|
|
19933
19986
|
var HINDSIGHT_WORKER_SLOT_TYPES = [
|
|
19934
19987
|
"consolidation",
|
|
@@ -20023,6 +20076,15 @@ var DEFAULT_RETAIN_MISSION = `Extract durable facts that will still be true and
|
|
|
20023
20076
|
` + `- Hindsight's own errors, retries, backlogs, or internal state — the memory
|
|
20024
20077
|
` + ` system's self-reports are not memories.
|
|
20025
20078
|
` + `- Restatements of the user's current request or the task in progress.
|
|
20079
|
+
` + `- The assistant's own answers, summaries, recaps, or reflect output. Before
|
|
20080
|
+
` + ` extracting, ask: does anything in this transcript support this claim OTHER
|
|
20081
|
+
` + ` than the assistant having asserted it? If not, drop it — a model's own
|
|
20082
|
+
` + ` synthesis re-extracted as a fact is how a guess becomes permanent.
|
|
20083
|
+
` + ` Concretely, never produce a fact whose only support is the assistant
|
|
20084
|
+
` + ` stating a date, a version, an attribution, a total, or a decision that the
|
|
20085
|
+
` + ` user never confirmed and no tool output shows. An unverified claim recalls
|
|
20086
|
+
` + ` later as though it had been established, which is worse than not
|
|
20087
|
+
` + ` remembering it at all.
|
|
20026
20088
|
` + `- Volatile state written as a timeless assertion. A version, count, size,
|
|
20027
20089
|
` + ` backlog, status, or any "X is running Y" / "X is at Y" / "X is currently Y"
|
|
20028
20090
|
` + ` claim is true only at the instant it was said. Concretely, never produce a
|
|
@@ -20178,6 +20240,64 @@ var SUPERSEDED_RETAIN_MISSIONS = [
|
|
|
20178
20240
|
` + `
|
|
20179
20241
|
` + `If a candidate fact matches an exclusion, drop it rather than rewording it. If
|
|
20180
20242
|
` + `nothing durable remains, return an empty facts list.
|
|
20243
|
+
`,
|
|
20244
|
+
`Extract durable facts that will still be true and useful weeks from now, once this session is forgotten.
|
|
20245
|
+
` + `
|
|
20246
|
+
` + `DURABILITY GATE. Emit a candidate ONLY if it is one of these five classes:
|
|
20247
|
+
` + `- PREFERENCE — what the user likes, wants, or always does; a standing rule or correction.
|
|
20248
|
+
` + `- 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.
|
|
20249
|
+
` + `- FINDING — a root cause, a measurement, or verified behaviour of a system. Include the number.
|
|
20250
|
+
` + `- OUTCOME — a completed result that changed the world: what shipped, what a thing turned out to be. Not the act of shipping it.
|
|
20251
|
+
` + `- RELATIONSHIP — who a person is, what a project or tool is, and how they connect.
|
|
20252
|
+
` + `If a candidate fits none of the five, it is not a memory. Drop it; do not reword it into one.
|
|
20253
|
+
` + `
|
|
20254
|
+
` + `A preference revealed by a request is durable — record the preference (what the user likes, wants, or always does), not the request itself.
|
|
20255
|
+
` + `
|
|
20256
|
+
` + `A TOOL RESULT IS NOT A FACT. Before extracting, ask: is the subject of this
|
|
20257
|
+
` + `candidate a file path, a command/process/agent/session id, a temp directory, or
|
|
20258
|
+
` + `the location where some output was written? If yes, drop it — it is transcript
|
|
20259
|
+
` + `exhaust, not memory.
|
|
20260
|
+
` + `
|
|
20261
|
+
` + `NEVER extract:
|
|
20262
|
+
` + `- Tool results verbatim or paraphrased. Concretely, never produce a fact whose
|
|
20263
|
+
` + ` text resembles any of these: "File created successfully at /path/to/file",
|
|
20264
|
+
` + ` "A background command with ID bctz4yskm is running, and its output will be
|
|
20265
|
+
` + ` written to /tmp/...", "Async agent a745598ba84e71df1 was launched successfully
|
|
20266
|
+
` + ` and is running in the background", "User executed a Bash command to sleep for
|
|
20267
|
+
` + ` 200 seconds", "The assistant used grep to locate 'truncateSync' in src/foo.ts".
|
|
20268
|
+
` + `- Anything mentioning a path under /tmp, a scratchpad directory, or a .tmp file.
|
|
20269
|
+
` + `- Agent tool-use traces or narration of what the assistant did (e.g. "the
|
|
20270
|
+
` + ` assistant used X to query Y", "ran a search", "sent the message").
|
|
20271
|
+
` + `- The act of delegating, dispatching, spawning, launching, steering or merging
|
|
20272
|
+
` + ` work — including when it succeeded. "X was dispatched and completed" is the
|
|
20273
|
+
` + ` session describing itself. Record only what the work LEARNED or CHANGED.
|
|
20274
|
+
` + `- In-flight workflow/process narration (a sub-task started, paused, or is still
|
|
20275
|
+
` + ` running) — retain the outcome only once the task completes or a decision is made.
|
|
20276
|
+
` + `- Operation, request, batch, agent, command or session IDs, UUIDs, hashes, or error codes.
|
|
20277
|
+
` + `- Slash commands the user typed and their effects (e.g. "User issued /clear to
|
|
20278
|
+
` + ` reset assistant state").
|
|
20279
|
+
` + `- Hindsight's own errors, retries, backlogs, or internal state — the memory
|
|
20280
|
+
` + ` system's self-reports are not memories.
|
|
20281
|
+
` + `- Restatements of the user's current request or the task in progress.
|
|
20282
|
+
` + `- Volatile state written as a timeless assertion. A version, count, size,
|
|
20283
|
+
` + ` backlog, status, or any "X is running Y" / "X is at Y" / "X is currently Y"
|
|
20284
|
+
` + ` claim is true only at the instant it was said. Concretely, never produce a
|
|
20285
|
+
` + ` fact whose text resembles any of these: "Switchroom fleet is running image
|
|
20286
|
+
` + ` version v0.18.19", "The switchroom repo is at /path/to/fleet, version
|
|
20287
|
+
` + ` v0.19.5", "Bank overlord has 43155 pending consolidations", "The build is
|
|
20288
|
+
` + ` currently green". If the claim is worth keeping, put the date INSIDE the
|
|
20289
|
+
` + ` fact text ("As of 2026-07-19 the fleet was running v0.18.19"); if you
|
|
20290
|
+
` + ` cannot date it, drop it. An undated one is recalled forever as though it
|
|
20291
|
+
` + ` were still true, which is worse than not remembering it at all.
|
|
20292
|
+
` + `- Transient state (unread counts, build status, what is running right now) unless
|
|
20293
|
+
` + ` the fact is explicitly dated, in which case record it as a dated observation.
|
|
20294
|
+
` + `- Greetings, acknowledgements, and routine operational chatter.
|
|
20295
|
+
` + `
|
|
20296
|
+
` + `Write each fact so it stands alone: name the thing, the number, and the date. A
|
|
20297
|
+
` + `sentence that only makes sense while reading this transcript is not durable.
|
|
20298
|
+
` + `
|
|
20299
|
+
` + `If a candidate fact matches an exclusion, drop it rather than rewording it. If
|
|
20300
|
+
` + `nothing durable remains, return an empty facts list.
|
|
20181
20301
|
`
|
|
20182
20302
|
];
|
|
20183
20303
|
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.
|
|
@@ -20255,6 +20375,22 @@ var PROFILE_MEMORY_DEFAULTS = {
|
|
|
20255
20375
|
var warnedMissingPool = new Set;
|
|
20256
20376
|
var warnedMissingDefault = new Set;
|
|
20257
20377
|
|
|
20378
|
+
// src/memory/hindsight-retain-provenance.ts
|
|
20379
|
+
var RETAIN_PROVENANCE_TAG = "source:transcript";
|
|
20380
|
+
var RETAIN_TAGS_DEFAULT = Object.freeze([
|
|
20381
|
+
"{session_id}",
|
|
20382
|
+
RETAIN_PROVENANCE_TAG
|
|
20383
|
+
]);
|
|
20384
|
+
|
|
20385
|
+
// src/memory/hindsight-seed-directives.ts
|
|
20386
|
+
var ANTI_CONFABULATION_DIRECTIVE = `When the retrieved memories do not support an answer, say so instead of composing one.
|
|
20387
|
+
` + `
|
|
20388
|
+
` + `- 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.
|
|
20389
|
+
` + `- 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.
|
|
20390
|
+
` + `- Keep what the bank RECORDED separate from what you INFER. State inferences as inferences, and say which memory a recorded fact came from.
|
|
20391
|
+
` + `- 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.
|
|
20392
|
+
` + "- Partial knowledge with its gaps named is more useful than a fluent answer that fills them in.";
|
|
20393
|
+
|
|
20258
20394
|
// src/telegram/state.ts
|
|
20259
20395
|
init_paths();
|
|
20260
20396
|
|
|
@@ -20292,7 +20428,6 @@ var RECALL_PASSTHROUGH_DEFAULTS = Object.freeze({
|
|
|
20292
20428
|
init_paths();
|
|
20293
20429
|
|
|
20294
20430
|
// src/agents/scaffold.ts
|
|
20295
|
-
var REPO_ROOT = resolve6(import.meta.dirname, "../..");
|
|
20296
20431
|
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.";
|
|
20297
20432
|
var pendingBankOps = new Set;
|
|
20298
20433
|
var SWITCHROOM_OWNED_SETTINGS_KEYS = new Set([
|
|
@@ -20378,8 +20513,8 @@ function migrateLegacyGrantsDbLocation(newDbPath = getGrantsDbPath(), deps) {
|
|
|
20378
20513
|
var BIND_MOUNT_EXACT_SOURCE_DENY = new Set(["/var/run/docker.sock"]);
|
|
20379
20514
|
|
|
20380
20515
|
// src/vault/broker/write-token-file.ts
|
|
20381
|
-
import { chownSync, mkdirSync as mkdirSync4, readFileSync as
|
|
20382
|
-
import { join as
|
|
20516
|
+
import { chownSync, mkdirSync as mkdirSync4, readFileSync as readFileSync8, writeFileSync as writeFileSync2 } from "node:fs";
|
|
20517
|
+
import { join as join5 } from "node:path";
|
|
20383
20518
|
class TokenFileWriteError extends Error {
|
|
20384
20519
|
tokenPath;
|
|
20385
20520
|
constructor(tokenPath, message, options) {
|
|
@@ -20396,13 +20531,13 @@ function writeAgentTokenFile(args, deps = {}) {
|
|
|
20396
20531
|
const writeFile = deps.writeFile ?? ((p, d) => {
|
|
20397
20532
|
writeFileSync2(p, d, { mode: 384 });
|
|
20398
20533
|
});
|
|
20399
|
-
const readFile = deps.readFile ?? ((p) =>
|
|
20534
|
+
const readFile = deps.readFile ?? ((p) => readFileSync8(p, "utf8"));
|
|
20400
20535
|
const chown = deps.chown ?? ((p, uid, gid) => {
|
|
20401
20536
|
chownSync(p, uid, gid);
|
|
20402
20537
|
});
|
|
20403
20538
|
const agentUid = deps.agentUid ?? allocateAgentUid;
|
|
20404
|
-
const tokenDir =
|
|
20405
|
-
const tokenPath =
|
|
20539
|
+
const tokenDir = join5(agentsDir, agent);
|
|
20540
|
+
const tokenPath = join5(tokenDir, ".vault-token");
|
|
20406
20541
|
try {
|
|
20407
20542
|
mkdir(tokenDir);
|
|
20408
20543
|
} catch (err) {
|
|
@@ -20455,26 +20590,26 @@ init_vault();
|
|
|
20455
20590
|
import {
|
|
20456
20591
|
copyFileSync as copyFileSync2,
|
|
20457
20592
|
chmodSync as chmodSync2,
|
|
20458
|
-
existsSync as
|
|
20593
|
+
existsSync as existsSync9,
|
|
20459
20594
|
fsyncSync as fsyncSync4,
|
|
20460
20595
|
lstatSync as lstatSync2,
|
|
20461
20596
|
mkdirSync as mkdirSync5,
|
|
20462
20597
|
openSync as openSync4,
|
|
20463
20598
|
closeSync as closeSync4,
|
|
20464
|
-
readFileSync as
|
|
20599
|
+
readFileSync as readFileSync9,
|
|
20465
20600
|
renameSync as renameSync4,
|
|
20466
20601
|
statSync as statSync4,
|
|
20467
20602
|
symlinkSync,
|
|
20468
20603
|
unlinkSync as unlinkSync3
|
|
20469
20604
|
} from "node:fs";
|
|
20470
20605
|
import { createHash as createHash2 } from "node:crypto";
|
|
20471
|
-
import { basename as basename3, dirname as dirname4, join as
|
|
20606
|
+
import { basename as basename3, dirname as dirname4, join as join6 } from "node:path";
|
|
20472
20607
|
function vaultLayoutPaths(home2) {
|
|
20473
|
-
const switchroomRoot =
|
|
20608
|
+
const switchroomRoot = join6(home2, ".switchroom");
|
|
20474
20609
|
return {
|
|
20475
|
-
oldPath:
|
|
20476
|
-
newPath:
|
|
20477
|
-
parent:
|
|
20610
|
+
oldPath: join6(switchroomRoot, "vault.enc"),
|
|
20611
|
+
newPath: join6(switchroomRoot, "vault", "vault.enc"),
|
|
20612
|
+
parent: join6(switchroomRoot, "vault"),
|
|
20478
20613
|
switchroomRoot
|
|
20479
20614
|
};
|
|
20480
20615
|
}
|
|
@@ -20483,11 +20618,11 @@ function inspectVaultLayout(home2) {
|
|
|
20483
20618
|
}
|
|
20484
20619
|
function runMigration(home2, opts) {
|
|
20485
20620
|
const { oldPath, newPath, parent, switchroomRoot } = vaultLayoutPaths(home2);
|
|
20486
|
-
const lockTarget =
|
|
20621
|
+
const lockTarget = existsSync9(newPath) ? newPath : existsSync9(oldPath) ? oldPath : null;
|
|
20487
20622
|
const release = !opts.dryRun && lockTarget !== null ? acquireVaultLock(lockTarget) : null;
|
|
20488
20623
|
try {
|
|
20489
20624
|
const oldStat = lstatSyncOrNull(oldPath);
|
|
20490
|
-
const newExists =
|
|
20625
|
+
const newExists = existsSync9(newPath);
|
|
20491
20626
|
if (oldStat === null && !newExists) {
|
|
20492
20627
|
return { kind: "no-vault" };
|
|
20493
20628
|
}
|
|
@@ -20551,12 +20686,12 @@ function lstatSyncOrNull(path2) {
|
|
|
20551
20686
|
}
|
|
20552
20687
|
}
|
|
20553
20688
|
function sha256File(path2) {
|
|
20554
|
-
const data =
|
|
20689
|
+
const data = readFileSync9(path2);
|
|
20555
20690
|
return createHash2("sha256").update(data).digest("hex");
|
|
20556
20691
|
}
|
|
20557
20692
|
function atomicReplaceWithSymlink(target, linkTarget) {
|
|
20558
|
-
const tmp =
|
|
20559
|
-
if (
|
|
20693
|
+
const tmp = join6(dirname4(target), `.${basename3(target)}.symlink-tmp`);
|
|
20694
|
+
if (existsSync9(tmp)) {
|
|
20560
20695
|
try {
|
|
20561
20696
|
unlinkSync3(tmp);
|
|
20562
20697
|
} catch {}
|
|
@@ -20589,11 +20724,11 @@ import { createHmac, randomBytes as randomBytes2, createCipheriv as createCipher
|
|
|
20589
20724
|
import {
|
|
20590
20725
|
chmodSync as chmodSync3,
|
|
20591
20726
|
closeSync as closeSync5,
|
|
20592
|
-
existsSync as
|
|
20727
|
+
existsSync as existsSync10,
|
|
20593
20728
|
fsyncSync as fsyncSync5,
|
|
20594
20729
|
mkdirSync as mkdirSync6,
|
|
20595
20730
|
openSync as openSync5,
|
|
20596
|
-
readFileSync as
|
|
20731
|
+
readFileSync as readFileSync10,
|
|
20597
20732
|
renameSync as renameSync5,
|
|
20598
20733
|
unlinkSync as unlinkSync4,
|
|
20599
20734
|
writeSync as writeSync4
|
|
@@ -20632,7 +20767,7 @@ function readMachineId() {
|
|
|
20632
20767
|
}
|
|
20633
20768
|
for (const path2 of [MACHINE_ID_PRIMARY, MACHINE_ID_FALLBACK]) {
|
|
20634
20769
|
try {
|
|
20635
|
-
const id =
|
|
20770
|
+
const id = readFileSync10(path2, "utf8").trim();
|
|
20636
20771
|
if (id.length > 0)
|
|
20637
20772
|
return id;
|
|
20638
20773
|
} catch {}
|
|
@@ -20669,12 +20804,12 @@ function decryptAutoUnlock(blob, machineId) {
|
|
|
20669
20804
|
}
|
|
20670
20805
|
}
|
|
20671
20806
|
function readAutoUnlockFile(filePath) {
|
|
20672
|
-
if (!
|
|
20807
|
+
if (!existsSync10(filePath)) {
|
|
20673
20808
|
throw new AutoUnlockDecryptError("io");
|
|
20674
20809
|
}
|
|
20675
20810
|
let blob;
|
|
20676
20811
|
try {
|
|
20677
|
-
blob =
|
|
20812
|
+
blob = readFileSync10(filePath);
|
|
20678
20813
|
} catch {
|
|
20679
20814
|
throw new AutoUnlockDecryptError("io");
|
|
20680
20815
|
}
|
|
@@ -20759,7 +20894,7 @@ function seedChain(path2) {
|
|
|
20759
20894
|
// src/vault/broker/test-isolation-guard.ts
|
|
20760
20895
|
import { mkdtempSync } from "node:fs";
|
|
20761
20896
|
import { homedir as homedir5, tmpdir } from "node:os";
|
|
20762
|
-
import { join as
|
|
20897
|
+
import { join as join7, resolve as resolve8, sep } from "node:path";
|
|
20763
20898
|
function isTestRuntime() {
|
|
20764
20899
|
return process.env.VITEST !== undefined;
|
|
20765
20900
|
}
|
|
@@ -20780,7 +20915,7 @@ function safeVaultPath(requestedPath) {
|
|
|
20780
20915
|
return requestedPath;
|
|
20781
20916
|
if (!isUnderRealSwitchroomHome(requestedPath))
|
|
20782
20917
|
return requestedPath;
|
|
20783
|
-
const redirected =
|
|
20918
|
+
const redirected = join7(mkdtempSync(join7(tmpdir(), "sw-test-vault-")), "vault.enc");
|
|
20784
20919
|
if (!warnedVault) {
|
|
20785
20920
|
warnedVault = true;
|
|
20786
20921
|
process.stderr.write(`[test-isolation guard] vault path redirected away from the ` + `production tree to an isolated tmpdir — a vault/broker test ` + `resolved its vault path inside ~/.switchroom. See CLAUDE.md ` + `"Vault & shared-state test discipline".
|
|
@@ -20796,7 +20931,7 @@ function safeAuditLogPath(requestedPath) {
|
|
|
20796
20931
|
if (!isUnderRealSwitchroomHome(requestedPath))
|
|
20797
20932
|
return requestedPath;
|
|
20798
20933
|
if (!cachedTmpAuditLog) {
|
|
20799
|
-
cachedTmpAuditLog =
|
|
20934
|
+
cachedTmpAuditLog = join7(mkdtempSync(join7(tmpdir(), "sw-test-audit-")), "vault-audit.log");
|
|
20800
20935
|
}
|
|
20801
20936
|
if (!warnedAudit) {
|
|
20802
20937
|
warnedAudit = true;
|
|
@@ -22943,8 +23078,8 @@ function listGrants(db, agent_slug) {
|
|
|
22943
23078
|
}
|
|
22944
23079
|
|
|
22945
23080
|
// src/vault/broker/reap-token-file.ts
|
|
22946
|
-
import { readFileSync as
|
|
22947
|
-
import { join as
|
|
23081
|
+
import { readFileSync as readFileSync12, writeFileSync as writeFileSync4 } from "node:fs";
|
|
23082
|
+
import { join as join10 } from "node:path";
|
|
22948
23083
|
var REAPABLE_REASONS = new Set(["grant-invalid", "grant-expired"]);
|
|
22949
23084
|
function reapUnusableGrantToken(args, deps = {}) {
|
|
22950
23085
|
const { agentsDir, agent, presentedToken, reason } = args;
|
|
@@ -22952,9 +23087,9 @@ function reapUnusableGrantToken(args, deps = {}) {
|
|
|
22952
23087
|
return "kept:reason-not-reapable";
|
|
22953
23088
|
if (!agent)
|
|
22954
23089
|
return "kept:no-agent-identity";
|
|
22955
|
-
const readFile = deps.readFile ?? ((p) =>
|
|
23090
|
+
const readFile = deps.readFile ?? ((p) => readFileSync12(p, "utf8"));
|
|
22956
23091
|
const writeFile = deps.writeFile ?? ((p, d) => writeFileSync4(p, d, { mode: 384 }));
|
|
22957
|
-
const tokenPath =
|
|
23092
|
+
const tokenPath = join10(agentsDir, agent, ".vault-token");
|
|
22958
23093
|
let onDisk;
|
|
22959
23094
|
try {
|
|
22960
23095
|
onDisk = readFile(tokenPath);
|
|
@@ -23539,7 +23674,7 @@ class VaultBroker {
|
|
|
23539
23674
|
chmodSync5(parentDir, 448);
|
|
23540
23675
|
} catch {}
|
|
23541
23676
|
for (const p of [this.socketPath, this.unlockSocketPath]) {
|
|
23542
|
-
if (
|
|
23677
|
+
if (existsSync13(p)) {
|
|
23543
23678
|
try {
|
|
23544
23679
|
unlinkSync6(p);
|
|
23545
23680
|
} catch {}
|
|
@@ -23575,7 +23710,7 @@ class VaultBroker {
|
|
|
23575
23710
|
try {
|
|
23576
23711
|
if (ready) {
|
|
23577
23712
|
writeFileSync5(p, "", { mode: 384 });
|
|
23578
|
-
} else if (
|
|
23713
|
+
} else if (existsSync13(p)) {
|
|
23579
23714
|
unlinkSync6(p);
|
|
23580
23715
|
}
|
|
23581
23716
|
} catch {}
|
|
@@ -23608,7 +23743,7 @@ class VaultBroker {
|
|
|
23608
23743
|
try {
|
|
23609
23744
|
entry.server.close();
|
|
23610
23745
|
} catch {}
|
|
23611
|
-
if (
|
|
23746
|
+
if (existsSync13(sockPath)) {
|
|
23612
23747
|
try {
|
|
23613
23748
|
unlinkSync6(sockPath);
|
|
23614
23749
|
} catch {}
|
|
@@ -23616,7 +23751,7 @@ class VaultBroker {
|
|
|
23616
23751
|
}
|
|
23617
23752
|
this.agentServers.clear();
|
|
23618
23753
|
for (const p of [this.socketPath, this.unlockSocketPath]) {
|
|
23619
|
-
if (p &&
|
|
23754
|
+
if (p && existsSync13(p)) {
|
|
23620
23755
|
try {
|
|
23621
23756
|
unlinkSync6(p);
|
|
23622
23757
|
} catch {}
|
|
@@ -23624,7 +23759,7 @@ class VaultBroker {
|
|
|
23624
23759
|
}
|
|
23625
23760
|
try {
|
|
23626
23761
|
const pidPath = resolvePath(PID_FILE_DEFAULT);
|
|
23627
|
-
if (
|
|
23762
|
+
if (existsSync13(pidPath))
|
|
23628
23763
|
unlinkSync6(pidPath);
|
|
23629
23764
|
} catch {}
|
|
23630
23765
|
}
|
|
@@ -23650,7 +23785,7 @@ class VaultBroker {
|
|
|
23650
23785
|
return new Promise((resolveP, rejectP) => {
|
|
23651
23786
|
if (abs.endsWith("/sock")) {
|
|
23652
23787
|
const dir = abs.slice(0, -"/sock".length);
|
|
23653
|
-
if (
|
|
23788
|
+
if (existsSync13(dir)) {
|
|
23654
23789
|
try {
|
|
23655
23790
|
chownSync2(dir, 0, 0);
|
|
23656
23791
|
} catch {}
|
|
@@ -23659,7 +23794,7 @@ class VaultBroker {
|
|
|
23659
23794
|
} catch {}
|
|
23660
23795
|
}
|
|
23661
23796
|
}
|
|
23662
|
-
if (
|
|
23797
|
+
if (existsSync13(abs)) {
|
|
23663
23798
|
try {
|
|
23664
23799
|
unlinkSync6(abs);
|
|
23665
23800
|
} catch (err) {
|
|
@@ -23691,7 +23826,7 @@ class VaultBroker {
|
|
|
23691
23826
|
} catch {}
|
|
23692
23827
|
this.agentServers.set(abs, { server, agentName });
|
|
23693
23828
|
const unlockAbs = unlockSocketFor(abs);
|
|
23694
|
-
if (
|
|
23829
|
+
if (existsSync13(unlockAbs)) {
|
|
23695
23830
|
try {
|
|
23696
23831
|
unlinkSync6(unlockAbs);
|
|
23697
23832
|
} catch {}
|
|
@@ -23746,7 +23881,7 @@ class VaultBroker {
|
|
|
23746
23881
|
if (!Number.isFinite(uid) || uid <= 0)
|
|
23747
23882
|
return;
|
|
23748
23883
|
try {
|
|
23749
|
-
if (
|
|
23884
|
+
if (existsSync13(this.vaultPath))
|
|
23750
23885
|
chownSync2(this.vaultPath, uid, uid);
|
|
23751
23886
|
} catch {}
|
|
23752
23887
|
}
|
|
@@ -23759,7 +23894,7 @@ class VaultBroker {
|
|
|
23759
23894
|
const unlockAbs = unlockSocketFor(abs);
|
|
23760
23895
|
if (abs.endsWith("/sock")) {
|
|
23761
23896
|
const dir = abs.slice(0, -"/sock".length);
|
|
23762
|
-
if (
|
|
23897
|
+
if (existsSync13(dir)) {
|
|
23763
23898
|
try {
|
|
23764
23899
|
chownSync2(dir, 0, 0);
|
|
23765
23900
|
} catch {}
|
|
@@ -23769,7 +23904,7 @@ class VaultBroker {
|
|
|
23769
23904
|
}
|
|
23770
23905
|
}
|
|
23771
23906
|
for (const p of [abs, unlockAbs]) {
|
|
23772
|
-
if (
|
|
23907
|
+
if (existsSync13(p)) {
|
|
23773
23908
|
try {
|
|
23774
23909
|
unlinkSync6(p);
|
|
23775
23910
|
} catch {}
|
|
@@ -24784,7 +24919,7 @@ class VaultBroker {
|
|
|
24784
24919
|
if (row && AgentNameSchema.safeParse(row.agent_slug).success) {
|
|
24785
24920
|
const agentsDir = this.config ? resolveAgentsDir(this.config) : path5.join(os3.homedir(), ".switchroom", "agents");
|
|
24786
24921
|
const tokenPath = path5.join(agentsDir, row.agent_slug, ".vault-token");
|
|
24787
|
-
if (
|
|
24922
|
+
if (existsSync13(tokenPath)) {
|
|
24788
24923
|
try {
|
|
24789
24924
|
unlinkSync6(tokenPath);
|
|
24790
24925
|
} catch {}
|
|
@@ -25022,7 +25157,7 @@ class VaultBroker {
|
|
|
25022
25157
|
const envPath = process.env.SWITCHROOM_VAULT_BROKER_AUTO_UNLOCK_PATH;
|
|
25023
25158
|
const configuredPath = (envPath && envPath.length > 0 ? envPath : undefined) ?? this.config?.vault?.broker?.autoUnlockCredentialPath ?? DEFAULT_AUTO_UNLOCK_PATH;
|
|
25024
25159
|
const filePath = resolvePath(configuredPath);
|
|
25025
|
-
if (!
|
|
25160
|
+
if (!existsSync13(filePath))
|
|
25026
25161
|
return false;
|
|
25027
25162
|
try {
|
|
25028
25163
|
if (statSync7(filePath).size === 0)
|
|
@@ -25065,7 +25200,7 @@ class VaultBroker {
|
|
|
25065
25200
|
const credPath = `${dir}/vault-passphrase`;
|
|
25066
25201
|
let passphrase;
|
|
25067
25202
|
try {
|
|
25068
|
-
passphrase =
|
|
25203
|
+
passphrase = readFileSync13(credPath, "utf8").replace(/\n+$/, "");
|
|
25069
25204
|
} catch (err) {
|
|
25070
25205
|
const code = err.code;
|
|
25071
25206
|
if (code === "ENOENT") {
|
|
@@ -25137,7 +25272,7 @@ async function main() {
|
|
|
25137
25272
|
const vaultPath = process.env.SWITCHROOM_VAULT_PATH;
|
|
25138
25273
|
let perAgentTargets = [];
|
|
25139
25274
|
try {
|
|
25140
|
-
if (
|
|
25275
|
+
if (existsSync13(perAgentDir)) {
|
|
25141
25276
|
const entries = readdirSync5(perAgentDir, { withFileTypes: true });
|
|
25142
25277
|
const flat = [];
|
|
25143
25278
|
const subdirs = [];
|
|
@@ -25192,7 +25327,7 @@ async function main() {
|
|
|
25192
25327
|
}
|
|
25193
25328
|
const operatorUidStr = process.env.SWITCHROOM_BROKER_OPERATOR_UID;
|
|
25194
25329
|
const operatorDir = "/run/switchroom/broker/operator";
|
|
25195
|
-
if (operatorUidStr !== undefined &&
|
|
25330
|
+
if (operatorUidStr !== undefined && existsSync13(operatorDir)) {
|
|
25196
25331
|
const operatorUid = parseInt(operatorUidStr, 10);
|
|
25197
25332
|
if (!Number.isFinite(operatorUid) || operatorUid <= 0) {
|
|
25198
25333
|
process.stderr.write(`[vault-broker] SWITCHROOM_BROKER_OPERATOR_UID='${operatorUidStr}' is not a positive integer; skipping operator listener
|