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
|
@@ -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,
|
|
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
|
|
|
@@ -19826,7 +19878,9 @@ var HINDSIGHT_DEFAULT_RECALL_MAX_CONCURRENT = 8;
|
|
|
19826
19878
|
var HINDSIGHT_DEFAULT_REFLECT_WALL_TIMEOUT_S = 600;
|
|
19827
19879
|
var HINDSIGHT_DEFAULT_CONSOLIDATION_MAX_MEMORIES_PER_ROUND = 250;
|
|
19828
19880
|
var HINDSIGHT_DEFAULT_CONSOLIDATION_SLOT_LIMIT = 6;
|
|
19829
|
-
var
|
|
19881
|
+
var HINDSIGHT_DEFAULT_CONSOLIDATION_RESERVED_SLOTS = 1;
|
|
19882
|
+
var HINDSIGHT_DEFAULT_GRAPH_SEED_MIN_SIMILARITY = 0.3;
|
|
19883
|
+
var HINDSIGHT_DEFAULT_LLM_SUPPORTS_MAX_ITEMS = "false";
|
|
19830
19884
|
var HINDSIGHT_DEFAULT_RECENCY_DECAY_FUNCTION = "exponential";
|
|
19831
19885
|
var HINDSIGHT_DEFAULT_RECENCY_DECAY_HALFLIFE_DAYS = 30;
|
|
19832
19886
|
var HINDSIGHT_PERF_DEFAULTS_UNGATED = [
|
|
@@ -19872,8 +19926,8 @@ var HINDSIGHT_PERF_DEFAULTS_UNGATED = [
|
|
|
19872
19926
|
String(HINDSIGHT_DEFAULT_REFLECT_WALL_TIMEOUT_S)
|
|
19873
19927
|
],
|
|
19874
19928
|
[
|
|
19875
|
-
"
|
|
19876
|
-
String(
|
|
19929
|
+
"HINDSIGHT_API_WORKER_CONSOLIDATION_RESERVED_SLOTS",
|
|
19930
|
+
String(HINDSIGHT_DEFAULT_CONSOLIDATION_RESERVED_SLOTS)
|
|
19877
19931
|
],
|
|
19878
19932
|
[
|
|
19879
19933
|
"HINDSIGHT_API_WORKER_CONSOLIDATION_SLOT_LIMIT",
|
|
@@ -19890,6 +19944,10 @@ var HINDSIGHT_PERF_DEFAULTS_UNGATED = [
|
|
|
19890
19944
|
[
|
|
19891
19945
|
"HINDSIGHT_API_RECENCY_DECAY_HALFLIFE_DAYS",
|
|
19892
19946
|
String(HINDSIGHT_DEFAULT_RECENCY_DECAY_HALFLIFE_DAYS)
|
|
19947
|
+
],
|
|
19948
|
+
[
|
|
19949
|
+
"HINDSIGHT_API_GRAPH_SEED_MIN_SIMILARITY",
|
|
19950
|
+
String(HINDSIGHT_DEFAULT_GRAPH_SEED_MIN_SIMILARITY)
|
|
19893
19951
|
]
|
|
19894
19952
|
];
|
|
19895
19953
|
var HINDSIGHT_PERF_DEFAULTS_GPU = [
|
|
@@ -19910,22 +19968,42 @@ var HINDSIGHT_PERF_DEFAULTS_LOCAL_LLM = [
|
|
|
19910
19968
|
String(HINDSIGHT_DEFAULT_CONSOLIDATION_LLM_MAX_CONCURRENT)
|
|
19911
19969
|
],
|
|
19912
19970
|
["HINDSIGHT_API_LLM_STRICT_SCHEMA", HINDSIGHT_DEFAULT_LLM_STRICT_SCHEMA],
|
|
19913
|
-
["HINDSIGHT_API_LLM_MAX_RETRIES", String(HINDSIGHT_DEFAULT_LLM_MAX_RETRIES)]
|
|
19971
|
+
["HINDSIGHT_API_LLM_MAX_RETRIES", String(HINDSIGHT_DEFAULT_LLM_MAX_RETRIES)],
|
|
19972
|
+
["HINDSIGHT_API_LLM_SUPPORTS_MAX_ITEMS", HINDSIGHT_DEFAULT_LLM_SUPPORTS_MAX_ITEMS]
|
|
19914
19973
|
];
|
|
19915
19974
|
var HINDSIGHT_PERF_OVERRIDE_ONLY_KEYS = new Set([
|
|
19916
19975
|
"HINDSIGHT_API_WORKER_CONSOLIDATION_BANK_PRIORITY",
|
|
19917
19976
|
"HINDSIGHT_CE_DECISIVE_RELATIVE_GAP",
|
|
19918
19977
|
"HINDSIGHT_API_RECENCY_DECAY_LINEAR_WINDOW_DAYS",
|
|
19919
19978
|
"HINDSIGHT_API_WORKER_MAX_SLOTS",
|
|
19920
|
-
"
|
|
19979
|
+
"HINDSIGHT_API_WORKER_RETAIN_RESERVED_SLOTS",
|
|
19980
|
+
"HINDSIGHT_API_SEMANTIC_MIN_SIMILARITY",
|
|
19981
|
+
"HINDSIGHT_MCP_RECALL_BUDGET_MODE",
|
|
19982
|
+
"HINDSIGHT_API_LLM_TEMPERATURE_REFLECT",
|
|
19983
|
+
"HINDSIGHT_API_RETAIN_WALL_TIMEOUT",
|
|
19984
|
+
"HINDSIGHT_API_CONSOLIDATION_RECALL_MAX_CONCURRENT"
|
|
19921
19985
|
]);
|
|
19986
|
+
var HINDSIGHT_WORKER_SLOT_TYPES = [
|
|
19987
|
+
"consolidation",
|
|
19988
|
+
"file_convert_retain",
|
|
19989
|
+
"graph_maintenance",
|
|
19990
|
+
"import_documents",
|
|
19991
|
+
"refresh_mental_model",
|
|
19992
|
+
"retain"
|
|
19993
|
+
];
|
|
19994
|
+
var HINDSIGHT_WORKER_RESERVED_SLOT_ALIASES = new Map(HINDSIGHT_WORKER_SLOT_TYPES.map((type) => [
|
|
19995
|
+
`HINDSIGHT_API_WORKER_${type.toUpperCase()}_MAX_SLOTS`,
|
|
19996
|
+
`HINDSIGHT_API_WORKER_${type.toUpperCase()}_RESERVED_SLOTS`
|
|
19997
|
+
]));
|
|
19922
19998
|
var HINDSIGHT_PERF_ENV_KEYS = new Set([
|
|
19923
19999
|
...[
|
|
19924
20000
|
...HINDSIGHT_PERF_DEFAULTS_UNGATED,
|
|
19925
20001
|
...HINDSIGHT_PERF_DEFAULTS_GPU,
|
|
19926
20002
|
...HINDSIGHT_PERF_DEFAULTS_LOCAL_LLM
|
|
19927
20003
|
].map(([k]) => k),
|
|
19928
|
-
...HINDSIGHT_PERF_OVERRIDE_ONLY_KEYS
|
|
20004
|
+
...HINDSIGHT_PERF_OVERRIDE_ONLY_KEYS,
|
|
20005
|
+
...HINDSIGHT_WORKER_RESERVED_SLOT_ALIASES.keys(),
|
|
20006
|
+
...HINDSIGHT_WORKER_RESERVED_SLOT_ALIASES.values()
|
|
19929
20007
|
]);
|
|
19930
20008
|
|
|
19931
20009
|
// src/setup/hindsight-context-budget.ts
|
|
@@ -19998,6 +20076,15 @@ var DEFAULT_RETAIN_MISSION = `Extract durable facts that will still be true and
|
|
|
19998
20076
|
` + `- Hindsight's own errors, retries, backlogs, or internal state — the memory
|
|
19999
20077
|
` + ` system's self-reports are not memories.
|
|
20000
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.
|
|
20001
20088
|
` + `- Volatile state written as a timeless assertion. A version, count, size,
|
|
20002
20089
|
` + ` backlog, status, or any "X is running Y" / "X is at Y" / "X is currently Y"
|
|
20003
20090
|
` + ` claim is true only at the instant it was said. Concretely, never produce a
|
|
@@ -20153,6 +20240,64 @@ var SUPERSEDED_RETAIN_MISSIONS = [
|
|
|
20153
20240
|
` + `
|
|
20154
20241
|
` + `If a candidate fact matches an exclusion, drop it rather than rewording it. If
|
|
20155
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.
|
|
20156
20301
|
`
|
|
20157
20302
|
];
|
|
20158
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.
|
|
@@ -20230,6 +20375,22 @@ var PROFILE_MEMORY_DEFAULTS = {
|
|
|
20230
20375
|
var warnedMissingPool = new Set;
|
|
20231
20376
|
var warnedMissingDefault = new Set;
|
|
20232
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
|
+
|
|
20233
20394
|
// src/telegram/state.ts
|
|
20234
20395
|
init_paths();
|
|
20235
20396
|
|
|
@@ -20267,7 +20428,6 @@ var RECALL_PASSTHROUGH_DEFAULTS = Object.freeze({
|
|
|
20267
20428
|
init_paths();
|
|
20268
20429
|
|
|
20269
20430
|
// src/agents/scaffold.ts
|
|
20270
|
-
var REPO_ROOT = resolve6(import.meta.dirname, "../..");
|
|
20271
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.";
|
|
20272
20432
|
var pendingBankOps = new Set;
|
|
20273
20433
|
var SWITCHROOM_OWNED_SETTINGS_KEYS = new Set([
|
|
@@ -20353,8 +20513,8 @@ function migrateLegacyGrantsDbLocation(newDbPath = getGrantsDbPath(), deps) {
|
|
|
20353
20513
|
var BIND_MOUNT_EXACT_SOURCE_DENY = new Set(["/var/run/docker.sock"]);
|
|
20354
20514
|
|
|
20355
20515
|
// src/vault/broker/write-token-file.ts
|
|
20356
|
-
import { chownSync, mkdirSync as mkdirSync4, readFileSync as
|
|
20357
|
-
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";
|
|
20358
20518
|
class TokenFileWriteError extends Error {
|
|
20359
20519
|
tokenPath;
|
|
20360
20520
|
constructor(tokenPath, message, options) {
|
|
@@ -20371,13 +20531,13 @@ function writeAgentTokenFile(args, deps = {}) {
|
|
|
20371
20531
|
const writeFile = deps.writeFile ?? ((p, d) => {
|
|
20372
20532
|
writeFileSync2(p, d, { mode: 384 });
|
|
20373
20533
|
});
|
|
20374
|
-
const readFile = deps.readFile ?? ((p) =>
|
|
20534
|
+
const readFile = deps.readFile ?? ((p) => readFileSync8(p, "utf8"));
|
|
20375
20535
|
const chown = deps.chown ?? ((p, uid, gid) => {
|
|
20376
20536
|
chownSync(p, uid, gid);
|
|
20377
20537
|
});
|
|
20378
20538
|
const agentUid = deps.agentUid ?? allocateAgentUid;
|
|
20379
|
-
const tokenDir =
|
|
20380
|
-
const tokenPath =
|
|
20539
|
+
const tokenDir = join5(agentsDir, agent);
|
|
20540
|
+
const tokenPath = join5(tokenDir, ".vault-token");
|
|
20381
20541
|
try {
|
|
20382
20542
|
mkdir(tokenDir);
|
|
20383
20543
|
} catch (err) {
|
|
@@ -20430,26 +20590,26 @@ init_vault();
|
|
|
20430
20590
|
import {
|
|
20431
20591
|
copyFileSync as copyFileSync2,
|
|
20432
20592
|
chmodSync as chmodSync2,
|
|
20433
|
-
existsSync as
|
|
20593
|
+
existsSync as existsSync9,
|
|
20434
20594
|
fsyncSync as fsyncSync4,
|
|
20435
20595
|
lstatSync as lstatSync2,
|
|
20436
20596
|
mkdirSync as mkdirSync5,
|
|
20437
20597
|
openSync as openSync4,
|
|
20438
20598
|
closeSync as closeSync4,
|
|
20439
|
-
readFileSync as
|
|
20599
|
+
readFileSync as readFileSync9,
|
|
20440
20600
|
renameSync as renameSync4,
|
|
20441
20601
|
statSync as statSync4,
|
|
20442
20602
|
symlinkSync,
|
|
20443
20603
|
unlinkSync as unlinkSync3
|
|
20444
20604
|
} from "node:fs";
|
|
20445
20605
|
import { createHash as createHash2 } from "node:crypto";
|
|
20446
|
-
import { basename as basename3, dirname as dirname4, join as
|
|
20606
|
+
import { basename as basename3, dirname as dirname4, join as join6 } from "node:path";
|
|
20447
20607
|
function vaultLayoutPaths(home2) {
|
|
20448
|
-
const switchroomRoot =
|
|
20608
|
+
const switchroomRoot = join6(home2, ".switchroom");
|
|
20449
20609
|
return {
|
|
20450
|
-
oldPath:
|
|
20451
|
-
newPath:
|
|
20452
|
-
parent:
|
|
20610
|
+
oldPath: join6(switchroomRoot, "vault.enc"),
|
|
20611
|
+
newPath: join6(switchroomRoot, "vault", "vault.enc"),
|
|
20612
|
+
parent: join6(switchroomRoot, "vault"),
|
|
20453
20613
|
switchroomRoot
|
|
20454
20614
|
};
|
|
20455
20615
|
}
|
|
@@ -20458,11 +20618,11 @@ function inspectVaultLayout(home2) {
|
|
|
20458
20618
|
}
|
|
20459
20619
|
function runMigration(home2, opts) {
|
|
20460
20620
|
const { oldPath, newPath, parent, switchroomRoot } = vaultLayoutPaths(home2);
|
|
20461
|
-
const lockTarget =
|
|
20621
|
+
const lockTarget = existsSync9(newPath) ? newPath : existsSync9(oldPath) ? oldPath : null;
|
|
20462
20622
|
const release = !opts.dryRun && lockTarget !== null ? acquireVaultLock(lockTarget) : null;
|
|
20463
20623
|
try {
|
|
20464
20624
|
const oldStat = lstatSyncOrNull(oldPath);
|
|
20465
|
-
const newExists =
|
|
20625
|
+
const newExists = existsSync9(newPath);
|
|
20466
20626
|
if (oldStat === null && !newExists) {
|
|
20467
20627
|
return { kind: "no-vault" };
|
|
20468
20628
|
}
|
|
@@ -20526,12 +20686,12 @@ function lstatSyncOrNull(path2) {
|
|
|
20526
20686
|
}
|
|
20527
20687
|
}
|
|
20528
20688
|
function sha256File(path2) {
|
|
20529
|
-
const data =
|
|
20689
|
+
const data = readFileSync9(path2);
|
|
20530
20690
|
return createHash2("sha256").update(data).digest("hex");
|
|
20531
20691
|
}
|
|
20532
20692
|
function atomicReplaceWithSymlink(target, linkTarget) {
|
|
20533
|
-
const tmp =
|
|
20534
|
-
if (
|
|
20693
|
+
const tmp = join6(dirname4(target), `.${basename3(target)}.symlink-tmp`);
|
|
20694
|
+
if (existsSync9(tmp)) {
|
|
20535
20695
|
try {
|
|
20536
20696
|
unlinkSync3(tmp);
|
|
20537
20697
|
} catch {}
|
|
@@ -20564,11 +20724,11 @@ import { createHmac, randomBytes as randomBytes2, createCipheriv as createCipher
|
|
|
20564
20724
|
import {
|
|
20565
20725
|
chmodSync as chmodSync3,
|
|
20566
20726
|
closeSync as closeSync5,
|
|
20567
|
-
existsSync as
|
|
20727
|
+
existsSync as existsSync10,
|
|
20568
20728
|
fsyncSync as fsyncSync5,
|
|
20569
20729
|
mkdirSync as mkdirSync6,
|
|
20570
20730
|
openSync as openSync5,
|
|
20571
|
-
readFileSync as
|
|
20731
|
+
readFileSync as readFileSync10,
|
|
20572
20732
|
renameSync as renameSync5,
|
|
20573
20733
|
unlinkSync as unlinkSync4,
|
|
20574
20734
|
writeSync as writeSync4
|
|
@@ -20607,7 +20767,7 @@ function readMachineId() {
|
|
|
20607
20767
|
}
|
|
20608
20768
|
for (const path2 of [MACHINE_ID_PRIMARY, MACHINE_ID_FALLBACK]) {
|
|
20609
20769
|
try {
|
|
20610
|
-
const id =
|
|
20770
|
+
const id = readFileSync10(path2, "utf8").trim();
|
|
20611
20771
|
if (id.length > 0)
|
|
20612
20772
|
return id;
|
|
20613
20773
|
} catch {}
|
|
@@ -20644,12 +20804,12 @@ function decryptAutoUnlock(blob, machineId) {
|
|
|
20644
20804
|
}
|
|
20645
20805
|
}
|
|
20646
20806
|
function readAutoUnlockFile(filePath) {
|
|
20647
|
-
if (!
|
|
20807
|
+
if (!existsSync10(filePath)) {
|
|
20648
20808
|
throw new AutoUnlockDecryptError("io");
|
|
20649
20809
|
}
|
|
20650
20810
|
let blob;
|
|
20651
20811
|
try {
|
|
20652
|
-
blob =
|
|
20812
|
+
blob = readFileSync10(filePath);
|
|
20653
20813
|
} catch {
|
|
20654
20814
|
throw new AutoUnlockDecryptError("io");
|
|
20655
20815
|
}
|
|
@@ -20734,7 +20894,7 @@ function seedChain(path2) {
|
|
|
20734
20894
|
// src/vault/broker/test-isolation-guard.ts
|
|
20735
20895
|
import { mkdtempSync } from "node:fs";
|
|
20736
20896
|
import { homedir as homedir5, tmpdir } from "node:os";
|
|
20737
|
-
import { join as
|
|
20897
|
+
import { join as join7, resolve as resolve8, sep } from "node:path";
|
|
20738
20898
|
function isTestRuntime() {
|
|
20739
20899
|
return process.env.VITEST !== undefined;
|
|
20740
20900
|
}
|
|
@@ -20755,7 +20915,7 @@ function safeVaultPath(requestedPath) {
|
|
|
20755
20915
|
return requestedPath;
|
|
20756
20916
|
if (!isUnderRealSwitchroomHome(requestedPath))
|
|
20757
20917
|
return requestedPath;
|
|
20758
|
-
const redirected =
|
|
20918
|
+
const redirected = join7(mkdtempSync(join7(tmpdir(), "sw-test-vault-")), "vault.enc");
|
|
20759
20919
|
if (!warnedVault) {
|
|
20760
20920
|
warnedVault = true;
|
|
20761
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".
|
|
@@ -20771,7 +20931,7 @@ function safeAuditLogPath(requestedPath) {
|
|
|
20771
20931
|
if (!isUnderRealSwitchroomHome(requestedPath))
|
|
20772
20932
|
return requestedPath;
|
|
20773
20933
|
if (!cachedTmpAuditLog) {
|
|
20774
|
-
cachedTmpAuditLog =
|
|
20934
|
+
cachedTmpAuditLog = join7(mkdtempSync(join7(tmpdir(), "sw-test-audit-")), "vault-audit.log");
|
|
20775
20935
|
}
|
|
20776
20936
|
if (!warnedAudit) {
|
|
20777
20937
|
warnedAudit = true;
|
|
@@ -22918,8 +23078,8 @@ function listGrants(db, agent_slug) {
|
|
|
22918
23078
|
}
|
|
22919
23079
|
|
|
22920
23080
|
// src/vault/broker/reap-token-file.ts
|
|
22921
|
-
import { readFileSync as
|
|
22922
|
-
import { join as
|
|
23081
|
+
import { readFileSync as readFileSync12, writeFileSync as writeFileSync4 } from "node:fs";
|
|
23082
|
+
import { join as join10 } from "node:path";
|
|
22923
23083
|
var REAPABLE_REASONS = new Set(["grant-invalid", "grant-expired"]);
|
|
22924
23084
|
function reapUnusableGrantToken(args, deps = {}) {
|
|
22925
23085
|
const { agentsDir, agent, presentedToken, reason } = args;
|
|
@@ -22927,9 +23087,9 @@ function reapUnusableGrantToken(args, deps = {}) {
|
|
|
22927
23087
|
return "kept:reason-not-reapable";
|
|
22928
23088
|
if (!agent)
|
|
22929
23089
|
return "kept:no-agent-identity";
|
|
22930
|
-
const readFile = deps.readFile ?? ((p) =>
|
|
23090
|
+
const readFile = deps.readFile ?? ((p) => readFileSync12(p, "utf8"));
|
|
22931
23091
|
const writeFile = deps.writeFile ?? ((p, d) => writeFileSync4(p, d, { mode: 384 }));
|
|
22932
|
-
const tokenPath =
|
|
23092
|
+
const tokenPath = join10(agentsDir, agent, ".vault-token");
|
|
22933
23093
|
let onDisk;
|
|
22934
23094
|
try {
|
|
22935
23095
|
onDisk = readFile(tokenPath);
|
|
@@ -23514,7 +23674,7 @@ class VaultBroker {
|
|
|
23514
23674
|
chmodSync5(parentDir, 448);
|
|
23515
23675
|
} catch {}
|
|
23516
23676
|
for (const p of [this.socketPath, this.unlockSocketPath]) {
|
|
23517
|
-
if (
|
|
23677
|
+
if (existsSync13(p)) {
|
|
23518
23678
|
try {
|
|
23519
23679
|
unlinkSync6(p);
|
|
23520
23680
|
} catch {}
|
|
@@ -23550,7 +23710,7 @@ class VaultBroker {
|
|
|
23550
23710
|
try {
|
|
23551
23711
|
if (ready) {
|
|
23552
23712
|
writeFileSync5(p, "", { mode: 384 });
|
|
23553
|
-
} else if (
|
|
23713
|
+
} else if (existsSync13(p)) {
|
|
23554
23714
|
unlinkSync6(p);
|
|
23555
23715
|
}
|
|
23556
23716
|
} catch {}
|
|
@@ -23583,7 +23743,7 @@ class VaultBroker {
|
|
|
23583
23743
|
try {
|
|
23584
23744
|
entry.server.close();
|
|
23585
23745
|
} catch {}
|
|
23586
|
-
if (
|
|
23746
|
+
if (existsSync13(sockPath)) {
|
|
23587
23747
|
try {
|
|
23588
23748
|
unlinkSync6(sockPath);
|
|
23589
23749
|
} catch {}
|
|
@@ -23591,7 +23751,7 @@ class VaultBroker {
|
|
|
23591
23751
|
}
|
|
23592
23752
|
this.agentServers.clear();
|
|
23593
23753
|
for (const p of [this.socketPath, this.unlockSocketPath]) {
|
|
23594
|
-
if (p &&
|
|
23754
|
+
if (p && existsSync13(p)) {
|
|
23595
23755
|
try {
|
|
23596
23756
|
unlinkSync6(p);
|
|
23597
23757
|
} catch {}
|
|
@@ -23599,7 +23759,7 @@ class VaultBroker {
|
|
|
23599
23759
|
}
|
|
23600
23760
|
try {
|
|
23601
23761
|
const pidPath = resolvePath(PID_FILE_DEFAULT);
|
|
23602
|
-
if (
|
|
23762
|
+
if (existsSync13(pidPath))
|
|
23603
23763
|
unlinkSync6(pidPath);
|
|
23604
23764
|
} catch {}
|
|
23605
23765
|
}
|
|
@@ -23625,7 +23785,7 @@ class VaultBroker {
|
|
|
23625
23785
|
return new Promise((resolveP, rejectP) => {
|
|
23626
23786
|
if (abs.endsWith("/sock")) {
|
|
23627
23787
|
const dir = abs.slice(0, -"/sock".length);
|
|
23628
|
-
if (
|
|
23788
|
+
if (existsSync13(dir)) {
|
|
23629
23789
|
try {
|
|
23630
23790
|
chownSync2(dir, 0, 0);
|
|
23631
23791
|
} catch {}
|
|
@@ -23634,7 +23794,7 @@ class VaultBroker {
|
|
|
23634
23794
|
} catch {}
|
|
23635
23795
|
}
|
|
23636
23796
|
}
|
|
23637
|
-
if (
|
|
23797
|
+
if (existsSync13(abs)) {
|
|
23638
23798
|
try {
|
|
23639
23799
|
unlinkSync6(abs);
|
|
23640
23800
|
} catch (err) {
|
|
@@ -23666,7 +23826,7 @@ class VaultBroker {
|
|
|
23666
23826
|
} catch {}
|
|
23667
23827
|
this.agentServers.set(abs, { server, agentName });
|
|
23668
23828
|
const unlockAbs = unlockSocketFor(abs);
|
|
23669
|
-
if (
|
|
23829
|
+
if (existsSync13(unlockAbs)) {
|
|
23670
23830
|
try {
|
|
23671
23831
|
unlinkSync6(unlockAbs);
|
|
23672
23832
|
} catch {}
|
|
@@ -23721,7 +23881,7 @@ class VaultBroker {
|
|
|
23721
23881
|
if (!Number.isFinite(uid) || uid <= 0)
|
|
23722
23882
|
return;
|
|
23723
23883
|
try {
|
|
23724
|
-
if (
|
|
23884
|
+
if (existsSync13(this.vaultPath))
|
|
23725
23885
|
chownSync2(this.vaultPath, uid, uid);
|
|
23726
23886
|
} catch {}
|
|
23727
23887
|
}
|
|
@@ -23734,7 +23894,7 @@ class VaultBroker {
|
|
|
23734
23894
|
const unlockAbs = unlockSocketFor(abs);
|
|
23735
23895
|
if (abs.endsWith("/sock")) {
|
|
23736
23896
|
const dir = abs.slice(0, -"/sock".length);
|
|
23737
|
-
if (
|
|
23897
|
+
if (existsSync13(dir)) {
|
|
23738
23898
|
try {
|
|
23739
23899
|
chownSync2(dir, 0, 0);
|
|
23740
23900
|
} catch {}
|
|
@@ -23744,7 +23904,7 @@ class VaultBroker {
|
|
|
23744
23904
|
}
|
|
23745
23905
|
}
|
|
23746
23906
|
for (const p of [abs, unlockAbs]) {
|
|
23747
|
-
if (
|
|
23907
|
+
if (existsSync13(p)) {
|
|
23748
23908
|
try {
|
|
23749
23909
|
unlinkSync6(p);
|
|
23750
23910
|
} catch {}
|
|
@@ -24759,7 +24919,7 @@ class VaultBroker {
|
|
|
24759
24919
|
if (row && AgentNameSchema.safeParse(row.agent_slug).success) {
|
|
24760
24920
|
const agentsDir = this.config ? resolveAgentsDir(this.config) : path5.join(os3.homedir(), ".switchroom", "agents");
|
|
24761
24921
|
const tokenPath = path5.join(agentsDir, row.agent_slug, ".vault-token");
|
|
24762
|
-
if (
|
|
24922
|
+
if (existsSync13(tokenPath)) {
|
|
24763
24923
|
try {
|
|
24764
24924
|
unlinkSync6(tokenPath);
|
|
24765
24925
|
} catch {}
|
|
@@ -24997,7 +25157,7 @@ class VaultBroker {
|
|
|
24997
25157
|
const envPath = process.env.SWITCHROOM_VAULT_BROKER_AUTO_UNLOCK_PATH;
|
|
24998
25158
|
const configuredPath = (envPath && envPath.length > 0 ? envPath : undefined) ?? this.config?.vault?.broker?.autoUnlockCredentialPath ?? DEFAULT_AUTO_UNLOCK_PATH;
|
|
24999
25159
|
const filePath = resolvePath(configuredPath);
|
|
25000
|
-
if (!
|
|
25160
|
+
if (!existsSync13(filePath))
|
|
25001
25161
|
return false;
|
|
25002
25162
|
try {
|
|
25003
25163
|
if (statSync7(filePath).size === 0)
|
|
@@ -25040,7 +25200,7 @@ class VaultBroker {
|
|
|
25040
25200
|
const credPath = `${dir}/vault-passphrase`;
|
|
25041
25201
|
let passphrase;
|
|
25042
25202
|
try {
|
|
25043
|
-
passphrase =
|
|
25203
|
+
passphrase = readFileSync13(credPath, "utf8").replace(/\n+$/, "");
|
|
25044
25204
|
} catch (err) {
|
|
25045
25205
|
const code = err.code;
|
|
25046
25206
|
if (code === "ENOENT") {
|
|
@@ -25112,7 +25272,7 @@ async function main() {
|
|
|
25112
25272
|
const vaultPath = process.env.SWITCHROOM_VAULT_PATH;
|
|
25113
25273
|
let perAgentTargets = [];
|
|
25114
25274
|
try {
|
|
25115
|
-
if (
|
|
25275
|
+
if (existsSync13(perAgentDir)) {
|
|
25116
25276
|
const entries = readdirSync5(perAgentDir, { withFileTypes: true });
|
|
25117
25277
|
const flat = [];
|
|
25118
25278
|
const subdirs = [];
|
|
@@ -25167,7 +25327,7 @@ async function main() {
|
|
|
25167
25327
|
}
|
|
25168
25328
|
const operatorUidStr = process.env.SWITCHROOM_BROKER_OPERATOR_UID;
|
|
25169
25329
|
const operatorDir = "/run/switchroom/broker/operator";
|
|
25170
|
-
if (operatorUidStr !== undefined &&
|
|
25330
|
+
if (operatorUidStr !== undefined && existsSync13(operatorDir)) {
|
|
25171
25331
|
const operatorUid = parseInt(operatorUidStr, 10);
|
|
25172
25332
|
if (!Number.isFinite(operatorUid) || operatorUid <= 0) {
|
|
25173
25333
|
process.stderr.write(`[vault-broker] SWITCHROOM_BROKER_OPERATOR_UID='${operatorUidStr}' is not a positive integer; skipping operator listener
|