switchroom 0.19.22 → 0.19.24

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.
Files changed (51) hide show
  1. package/dist/agent-scheduler/index.js +5 -2
  2. package/dist/auth-broker/index.js +95 -2
  3. package/dist/cli/notion-write-pretool.mjs +5 -2
  4. package/dist/cli/switchroom.js +749 -357
  5. package/dist/host-control/main.js +96 -3
  6. package/dist/vault/approvals/kernel-server.js +98 -5
  7. package/dist/vault/broker/server.js +98 -5
  8. package/package.json +5 -4
  9. package/profiles/_base/start.sh.hbs +101 -0
  10. package/profiles/_shared/agent-self-service.md.hbs +64 -109
  11. package/profiles/_shared/delegation-golden-rule.md.hbs +5 -5
  12. package/profiles/_shared/dev-protocol.md.hbs +12 -42
  13. package/profiles/_shared/execution-discipline.md.hbs +7 -14
  14. package/profiles/coding/CLAUDE.md.hbs +0 -6
  15. package/profiles/default/CLAUDE.md.hbs +21 -50
  16. package/skills/dev-protocol/SKILL.md +97 -107
  17. package/skills/switchroom-release/SKILL.md +2 -1
  18. package/telegram-plugin/bunfig.toml +10 -0
  19. package/telegram-plugin/dist/gateway/gateway.js +267 -52
  20. package/telegram-plugin/gateway/backstop-delivery.ts +97 -16
  21. package/telegram-plugin/gateway/captured-answer-resume.ts +46 -17
  22. package/telegram-plugin/gateway/gateway.ts +43 -42
  23. package/telegram-plugin/gateway/latest-turn-lookup.ts +60 -0
  24. package/telegram-plugin/gateway/outbound-send-path.ts +61 -22
  25. package/telegram-plugin/gateway/stream-render.ts +6 -0
  26. package/telegram-plugin/gateway/subagent-handback-marker.ts +1 -1
  27. package/telegram-plugin/gateway/turn-end.ts +1 -1
  28. package/telegram-plugin/gateway/turn-record-status.ts +19 -0
  29. package/telegram-plugin/gateway/turns-jsonl-rotate.ts +65 -0
  30. package/telegram-plugin/reply-owner-resolve.ts +110 -9
  31. package/telegram-plugin/send-gate-degraded.test.ts +45 -16
  32. package/telegram-plugin/send-gate.ts +185 -24
  33. package/telegram-plugin/tests/activity-card-send-gate.test.ts +9 -9
  34. package/telegram-plugin/tests/agent-state-dir-preload.test.ts +33 -0
  35. package/telegram-plugin/tests/backstop-delivery.test.ts +204 -7
  36. package/telegram-plugin/tests/backstop-readback-probe.test.ts +12 -0
  37. package/telegram-plugin/tests/captured-answer-resume.test.ts +104 -0
  38. package/telegram-plugin/tests/latest-turn-lookup.test.ts +77 -0
  39. package/telegram-plugin/tests/narrative-lane-golden.test.ts +23 -1
  40. package/telegram-plugin/tests/reply-owner-resolve.test.ts +531 -0
  41. package/telegram-plugin/tests/send-reply-golden.test.ts +296 -28
  42. package/telegram-plugin/tests/stream-controller-send-gate.test.ts +134 -28
  43. package/telegram-plugin/tests/stream-render-golden.test.ts +25 -3
  44. package/telegram-plugin/tests/turns-jsonl-rotate.test.ts +92 -1
  45. package/vendor/hindsight-memory/scripts/drain_pending.py +113 -11
  46. package/vendor/hindsight-memory/scripts/lib/pending.py +802 -65
  47. package/vendor/hindsight-memory/scripts/lib/retain_split.py +54 -7
  48. package/vendor/hindsight-memory/scripts/tests/test_pending_drops.py +1445 -11
  49. package/vendor/hindsight-memory/scripts/tests/test_retain_split.py +78 -6
  50. package/vendor/hindsight-memory/tests/test_drain_pending.py +17 -2
  51. package/vendor/hindsight-memory/tests/test_pending.py +12 -4
@@ -2120,7 +2120,7 @@ var init_esm = __esm(() => {
2120
2120
  });
2121
2121
 
2122
2122
  // src/build-info.ts
2123
- var VERSION = "0.19.22", COMMIT_SHA = "50ef9fb5";
2123
+ var VERSION = "0.19.24", COMMIT_SHA = "0fa8ffb8";
2124
2124
 
2125
2125
  // src/cli/resolve-version.ts
2126
2126
  import { existsSync, readFileSync } from "node:fs";
@@ -13939,16 +13939,19 @@ var init_schema = __esm(() => {
13939
13939
  model: exports_external.string().min(1).optional().describe("Per-op model (upstream `HINDSIGHT_API_<OP>_LLM_MODEL`). Absent \u2192 " + "inherit the global `hindsight.llm.model`."),
13940
13940
  provider: exports_external.string().min(1).optional().describe("Per-op provider (upstream `HINDSIGHT_API_<OP>_LLM_PROVIDER`). " + "Absent \u2192 inherit the global `hindsight.llm.provider`."),
13941
13941
  base_url: exports_external.string().min(1).optional().describe("Per-op base URL (upstream `HINDSIGHT_API_<OP>_LLM_BASE_URL`). " + "Optional passthrough; absent \u2192 inherit the global."),
13942
- api_key: exports_external.string().min(1).optional().describe("Per-op API key (upstream `HINDSIGHT_API_<OP>_LLM_API_KEY`). Literal " + "or `vault:` reference. Optional passthrough; absent \u2192 inherit global.")
13942
+ api_key: exports_external.string().min(1).optional().describe("Per-op API key (upstream `HINDSIGHT_API_<OP>_LLM_API_KEY`). Literal " + "or `vault:` reference. Optional passthrough; absent \u2192 inherit global."),
13943
+ context_window: exports_external.number().int().positive().optional().describe("Context window (tokens) of the backend serving THIS op. NOT an " + "upstream env var \u2014 switchroom derives the op's token budget " + "(consolidation batch size / max-completion caps / reflect " + "max-context cap) from it so a single call can never overflow the " + "window. Absent \u2192 inherit " + "`hindsight.llm.context_window`, else a per-provider default " + "(conservative for non-`claude-code` providers, which usually mean " + "a local llama.cpp/Ollama slot; a self-hosted `base_url` \u2014 loopback, " + "RFC1918, `.local`/`.internal` \u2014 forces the conservative default too, " + "regardless of the provider NAME, since the endpoint is where the " + "traffic actually terminates). All three lanes (`retain`, " + "`reflect`, `consolidation`) are budgeted independently.")
13943
13944
  }).describe("Per-operation LLM override. Every field optional; an unset field (or " + "an omitted op block) inherits the global `hindsight.llm.*`, which is " + "already the engine's fallback \u2014 switchroom emits only the vars set.");
13944
13945
  HindsightConfigSchema = exports_external.object({
13945
13946
  llm: exports_external.object({
13946
13947
  provider: exports_external.string().min(1).optional().describe("Hindsight LLM provider (upstream `HINDSIGHT_API_LLM_PROVIDER`). " + "Defaults to `claude-code` (subscription-honest, broker-fed OAuth). " + "Any litellm-routable provider the upstream image supports is valid. " + "Serves as the GLOBAL default for every op absent a per-op override."),
13947
13948
  model: exports_external.string().min(1).optional().describe("Hindsight LLM model (upstream `HINDSIGHT_API_LLM_MODEL`). Defaults " + "to HINDSIGHT_DEFAULT_MODEL. Any model your LiteLLM proxy can route " + "is valid, e.g. `openrouter/z-ai/glm-5.2` when routing through the " + "fleet proxy. With provider=claude-code this value is ALSO exported " + "as `ANTHROPIC_MODEL` to the claude subprocess. Serves as the GLOBAL " + "default for every op absent a per-op override."),
13949
+ context_window: exports_external.number().int().positive().optional().describe("GLOBAL context window (tokens) of the backend serving hindsight's " + "LLM ops \u2014 the declared size switchroom derives every token budget " + "from. Set this to the real window of whatever you point " + "`hindsight.llm` at (e.g. 32768 for a llama.cpp slot launched with " + "`-c 65536 -np 2`, 131072 for a large-window OpenRouter model). " + "Absent \u2192 a per-provider default: 200000 for `claude-code`, a " + "conservative 32768 for everything else. Overflowing a local " + "backend's window does NOT error \u2014 llama.cpp context-shift silently " + "drops the system prompt and the model answers conversationally " + "with HTTP 200 \u2014 so this value is what makes the failure " + "detectable at setup time instead of never."),
13948
13950
  retain: HindsightPerOpLlmSchema.optional().describe("Per-op override for the `retain` LLM op (memory ingestion). Emits " + "`HINDSIGHT_API_RETAIN_LLM_*`. Absent \u2192 uses the global model/provider."),
13949
13951
  reflect: HindsightPerOpLlmSchema.optional().describe("Per-op override for the `reflect` LLM op (synthesis / mental-model " + "refresh). Emits `HINDSIGHT_API_REFLECT_LLM_*`. Absent \u2192 uses global."),
13950
13952
  consolidation: HindsightPerOpLlmSchema.optional().describe("Per-op override for the `consolidation` LLM op (background memory " + "merge). Emits `HINDSIGHT_API_CONSOLIDATION_LLM_*`. Absent \u2192 global.")
13951
- }).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.")
13953
+ }).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."),
13954
+ 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, " + "RECALL_MAX_CANDIDATES_PER_SOURCE, LINK_EXPANSION_PER_ENTITY_LIMIT, " + "LINK_EXPANSION_TIMEOUT, LLM_REASONING_EFFORT), the override-only keys " + "switchroom manages but ships NO default for " + "(`HINDSIGHT_PERF_OVERRIDE_ONLY_KEYS`: " + "HINDSIGHT_API_WORKER_CONSOLIDATION_BANK_PRIORITY \u2014 a per-deployment " + "`bank-pattern:priority,...` map; unset means upstream's flat " + "created_at FIFO across banks), plus the " + "embedded-PostgreSQL (pg0) sizing keys switchroom manages in " + "src/setup/hindsight-pg-defaults.ts (`HINDSIGHT_PG_ENV_KEYS`: " + "SWITCHROOM_HINDSIGHT_PG_EFFECTIVE_CACHE_SIZE, " + "SWITCHROOM_HINDSIGHT_PG_SHARED_BUFFERS \u2014 a postgres size string such " + "as `4GB`, or the sentinel `off` to leave pg0's own default for that " + "one knob). A value set here " + "REPLACES switchroom's default and is emitted even when the gating " + "capability is absent, so an operator can always force a knob. Other " + "`HINDSIGHT_API_*` keys are deliberately IGNORED \u2014 a blanket " + "passthrough would collide with the vars startHindsight() derives " + "itself (HINDSIGHT_API_PORT, the retain token/deadline budget).")
13952
13955
  });
13953
13956
  MicrosoftWorkspaceConfigSchema = exports_external.object({
13954
13957
  microsoft_client_id: exports_external.string().min(1).optional().describe("Microsoft OAuth application (client) ID from Entra portal " + "(literal string or vault reference e.g. " + "'vault:microsoft-oauth-client-id'). OPTIONAL \u2014 omit it to use " + "switchroom's shipped default Microsoft app (zero-config). " + "Set it only to bring your own Entra app (BYO)."),
@@ -15555,6 +15558,376 @@ var init_timeout_budget = __esm(() => {
15555
15558
  };
15556
15559
  });
15557
15560
 
15561
+ // src/setup/host-capabilities.ts
15562
+ import { existsSync as existsSync5, mkdirSync, readFileSync as readFileSync5, writeFileSync } from "node:fs";
15563
+ import { dirname as dirname2 } from "node:path";
15564
+ function hostCapabilitiesPath() {
15565
+ return resolveStatePath("host-capabilities.json");
15566
+ }
15567
+ function saveVoiceCapability(caps, now = () => new Date) {
15568
+ const doc = {
15569
+ version: HOST_CAPABILITIES_VERSION,
15570
+ voice: {
15571
+ gpuPresent: caps.gpuPresent,
15572
+ containerToolkit: caps.containerToolkit,
15573
+ engine: caps.engine,
15574
+ detectedAt: now().toISOString()
15575
+ }
15576
+ };
15577
+ const path = hostCapabilitiesPath();
15578
+ mkdirSync(dirname2(path), { recursive: true });
15579
+ writeFileSync(path, JSON.stringify(doc, null, 2) + `
15580
+ `, {
15581
+ encoding: "utf-8",
15582
+ mode: 384
15583
+ });
15584
+ return doc;
15585
+ }
15586
+ function loadHostCapabilities() {
15587
+ const path = hostCapabilitiesPath();
15588
+ if (!existsSync5(path))
15589
+ return null;
15590
+ try {
15591
+ const parsed = JSON.parse(readFileSync5(path, "utf-8"));
15592
+ if (parsed && typeof parsed === "object" && "voice" in parsed && typeof parsed.voice === "object") {
15593
+ return parsed;
15594
+ }
15595
+ return null;
15596
+ } catch {
15597
+ return null;
15598
+ }
15599
+ }
15600
+ var HOST_CAPABILITIES_VERSION = 1;
15601
+ var init_host_capabilities = __esm(() => {
15602
+ init_paths();
15603
+ });
15604
+
15605
+ // src/setup/hindsight-perf-defaults.ts
15606
+ function resolveHindsightPerfOverrides(configEnv, processEnv = process.env) {
15607
+ const out = new Map;
15608
+ for (const key of HINDSIGHT_PERF_ENV_KEYS) {
15609
+ const fromProcess = processEnv[key];
15610
+ if (typeof fromProcess === "string" && fromProcess.trim() !== "") {
15611
+ out.set(key, fromProcess.trim());
15612
+ }
15613
+ }
15614
+ for (const [key, raw] of Object.entries(configEnv ?? {})) {
15615
+ if (!HINDSIGHT_PERF_ENV_KEYS.has(key))
15616
+ continue;
15617
+ const value = String(raw).trim();
15618
+ if (value === "")
15619
+ continue;
15620
+ out.set(key, value);
15621
+ }
15622
+ return out;
15623
+ }
15624
+ function hindsightPerfEnv(caps, overrides = new Map) {
15625
+ const groups = [
15626
+ HINDSIGHT_PERF_DEFAULTS_UNGATED
15627
+ ];
15628
+ if (caps.gpu === true)
15629
+ groups.push(HINDSIGHT_PERF_DEFAULTS_GPU);
15630
+ if (caps.localLlm === true)
15631
+ groups.push(HINDSIGHT_PERF_DEFAULTS_LOCAL_LLM);
15632
+ const out = [];
15633
+ const emitted = new Set;
15634
+ for (const group of groups) {
15635
+ for (const [key, value] of group) {
15636
+ if (emitted.has(key))
15637
+ continue;
15638
+ emitted.add(key);
15639
+ out.push([key, overrides.get(key) ?? value]);
15640
+ }
15641
+ }
15642
+ for (const key of [...overrides.keys()].sort()) {
15643
+ if (emitted.has(key))
15644
+ continue;
15645
+ emitted.add(key);
15646
+ out.push([key, overrides.get(key)]);
15647
+ }
15648
+ return out;
15649
+ }
15650
+ var HINDSIGHT_RERANKER_MAX_CANDIDATES_FOR_DERIVATION = 150, HINDSIGHT_DEFAULT_RECALL_MAX_CANDIDATES_PER_SOURCE, HINDSIGHT_DEFAULT_LINK_EXPANSION_PER_ENTITY_LIMIT = 50, HINDSIGHT_DEFAULT_LINK_EXPANSION_TIMEOUT_S = 2, HINDSIGHT_DEFAULT_LLM_REASONING_EFFORT = "low", HINDSIGHT_DEFAULT_LLM_MAX_CONCURRENT = 4, HINDSIGHT_DEFAULT_RETAIN_LLM_MAX_CONCURRENT = 1, HINDSIGHT_DEFAULT_CONSOLIDATION_LLM_MAX_CONCURRENT = 1, HINDSIGHT_DEFAULT_RERANKER_LOCAL_FP16 = "true", HINDSIGHT_DEFAULT_RERANKER_LOCAL_BATCH_SIZE = 128, HINDSIGHT_DEFAULT_LLM_STRICT_SCHEMA = "true", HINDSIGHT_DEFAULT_LLM_MAX_RETRIES = 2, HINDSIGHT_DEFAULT_CONSOLIDATION_LLM_PARALLELISM = 2, HINDSIGHT_PERF_DEFAULTS_UNGATED, HINDSIGHT_PERF_DEFAULTS_GPU, HINDSIGHT_PERF_DEFAULTS_LOCAL_LLM, HINDSIGHT_PERF_OVERRIDE_ONLY_KEYS, HINDSIGHT_PERF_ENV_KEYS;
15651
+ var init_hindsight_perf_defaults = __esm(() => {
15652
+ HINDSIGHT_DEFAULT_RECALL_MAX_CANDIDATES_PER_SOURCE = Math.ceil(HINDSIGHT_RERANKER_MAX_CANDIDATES_FOR_DERIVATION * 0.4);
15653
+ HINDSIGHT_PERF_DEFAULTS_UNGATED = [
15654
+ [
15655
+ "HINDSIGHT_API_RECALL_MAX_CANDIDATES_PER_SOURCE",
15656
+ String(HINDSIGHT_DEFAULT_RECALL_MAX_CANDIDATES_PER_SOURCE)
15657
+ ],
15658
+ [
15659
+ "HINDSIGHT_API_LINK_EXPANSION_PER_ENTITY_LIMIT",
15660
+ String(HINDSIGHT_DEFAULT_LINK_EXPANSION_PER_ENTITY_LIMIT)
15661
+ ],
15662
+ [
15663
+ "HINDSIGHT_API_LINK_EXPANSION_TIMEOUT",
15664
+ String(HINDSIGHT_DEFAULT_LINK_EXPANSION_TIMEOUT_S)
15665
+ ],
15666
+ ["HINDSIGHT_API_LLM_REASONING_EFFORT", HINDSIGHT_DEFAULT_LLM_REASONING_EFFORT],
15667
+ [
15668
+ "HINDSIGHT_API_CONSOLIDATION_LLM_PARALLELISM",
15669
+ String(HINDSIGHT_DEFAULT_CONSOLIDATION_LLM_PARALLELISM)
15670
+ ]
15671
+ ];
15672
+ HINDSIGHT_PERF_DEFAULTS_GPU = [
15673
+ ["HINDSIGHT_API_RERANKER_LOCAL_FP16", HINDSIGHT_DEFAULT_RERANKER_LOCAL_FP16],
15674
+ [
15675
+ "HINDSIGHT_API_RERANKER_LOCAL_BATCH_SIZE",
15676
+ String(HINDSIGHT_DEFAULT_RERANKER_LOCAL_BATCH_SIZE)
15677
+ ]
15678
+ ];
15679
+ HINDSIGHT_PERF_DEFAULTS_LOCAL_LLM = [
15680
+ ["HINDSIGHT_API_LLM_MAX_CONCURRENT", String(HINDSIGHT_DEFAULT_LLM_MAX_CONCURRENT)],
15681
+ [
15682
+ "HINDSIGHT_API_RETAIN_LLM_MAX_CONCURRENT",
15683
+ String(HINDSIGHT_DEFAULT_RETAIN_LLM_MAX_CONCURRENT)
15684
+ ],
15685
+ [
15686
+ "HINDSIGHT_API_CONSOLIDATION_LLM_MAX_CONCURRENT",
15687
+ String(HINDSIGHT_DEFAULT_CONSOLIDATION_LLM_MAX_CONCURRENT)
15688
+ ],
15689
+ ["HINDSIGHT_API_LLM_STRICT_SCHEMA", HINDSIGHT_DEFAULT_LLM_STRICT_SCHEMA],
15690
+ ["HINDSIGHT_API_LLM_MAX_RETRIES", String(HINDSIGHT_DEFAULT_LLM_MAX_RETRIES)]
15691
+ ];
15692
+ HINDSIGHT_PERF_OVERRIDE_ONLY_KEYS = new Set([
15693
+ "HINDSIGHT_API_WORKER_CONSOLIDATION_BANK_PRIORITY"
15694
+ ]);
15695
+ HINDSIGHT_PERF_ENV_KEYS = new Set([
15696
+ ...[
15697
+ ...HINDSIGHT_PERF_DEFAULTS_UNGATED,
15698
+ ...HINDSIGHT_PERF_DEFAULTS_GPU,
15699
+ ...HINDSIGHT_PERF_DEFAULTS_LOCAL_LLM
15700
+ ].map(([k]) => k),
15701
+ ...HINDSIGHT_PERF_OVERRIDE_ONLY_KEYS
15702
+ ]);
15703
+ });
15704
+
15705
+ // src/setup/hindsight-pg-defaults.ts
15706
+ function pgMib(mib) {
15707
+ return `${mib}MB`;
15708
+ }
15709
+ function resolveHindsightPgOverrides(configEnv, processEnv = process.env) {
15710
+ const out = new Map;
15711
+ for (const key of HINDSIGHT_PG_ENV_KEYS) {
15712
+ const fromProcess = processEnv[key];
15713
+ if (typeof fromProcess === "string" && fromProcess.trim() !== "") {
15714
+ out.set(key, fromProcess.trim());
15715
+ }
15716
+ }
15717
+ for (const [key, raw] of Object.entries(configEnv ?? {})) {
15718
+ if (!HINDSIGHT_PG_ENV_KEYS.has(key))
15719
+ continue;
15720
+ const value = String(raw).trim();
15721
+ if (value === "")
15722
+ continue;
15723
+ out.set(key, value);
15724
+ }
15725
+ return out;
15726
+ }
15727
+ function hindsightPgEnv(overrides = new Map) {
15728
+ const out = [];
15729
+ const emitted = new Set;
15730
+ for (const [key, value] of HINDSIGHT_PG_DEFAULTS) {
15731
+ if (emitted.has(key))
15732
+ continue;
15733
+ emitted.add(key);
15734
+ out.push([key, overrides.get(key) ?? value]);
15735
+ }
15736
+ return out;
15737
+ }
15738
+ var HINDSIGHT_PG_MEM_LIMIT_MIB_FOR_DERIVATION, HINDSIGHT_PG_APP_ANON_MIB = 2560, HINDSIGHT_PG_PAGE_CACHE_FLOOR_MIB = 2048, HINDSIGHT_PG_SHARED_BUFFERS_BUDGET_MIB, HINDSIGHT_PG_DEFAULT_SHARED_BUFFERS_MIB = 1536, HINDSIGHT_PG_DEFAULT_EFFECTIVE_CACHE_SIZE_MIB = 4096, HINDSIGHT_PG_EFFECTIVE_CACHE_SIZE_ENV = "SWITCHROOM_HINDSIGHT_PG_EFFECTIVE_CACHE_SIZE", HINDSIGHT_PG_SHARED_BUFFERS_ENV = "SWITCHROOM_HINDSIGHT_PG_SHARED_BUFFERS", HINDSIGHT_PG_DEFAULTS, HINDSIGHT_PG_ENV_KEYS;
15739
+ var init_hindsight_pg_defaults = __esm(() => {
15740
+ HINDSIGHT_PG_MEM_LIMIT_MIB_FOR_DERIVATION = 8 * 1024;
15741
+ HINDSIGHT_PG_SHARED_BUFFERS_BUDGET_MIB = HINDSIGHT_PG_MEM_LIMIT_MIB_FOR_DERIVATION - HINDSIGHT_PG_APP_ANON_MIB - HINDSIGHT_PG_PAGE_CACHE_FLOOR_MIB;
15742
+ HINDSIGHT_PG_DEFAULTS = [
15743
+ [
15744
+ HINDSIGHT_PG_EFFECTIVE_CACHE_SIZE_ENV,
15745
+ pgMib(HINDSIGHT_PG_DEFAULT_EFFECTIVE_CACHE_SIZE_MIB)
15746
+ ],
15747
+ [HINDSIGHT_PG_SHARED_BUFFERS_ENV, pgMib(HINDSIGHT_PG_DEFAULT_SHARED_BUFFERS_MIB)]
15748
+ ];
15749
+ HINDSIGHT_PG_ENV_KEYS = new Set(HINDSIGHT_PG_DEFAULTS.map(([k]) => k));
15750
+ });
15751
+
15752
+ // src/setup/self-hosted-url.ts
15753
+ function isLoopbackHttpUrl(url) {
15754
+ try {
15755
+ const u = new URL(url.includes("://") ? url : `http://${url}`);
15756
+ const h = (u.hostname || "").toLowerCase().replace(/^\[|\]$/g, "");
15757
+ return h === "localhost" || h === "127.0.0.1" || h === "::1";
15758
+ } catch {
15759
+ return false;
15760
+ }
15761
+ }
15762
+ function isSelfHostedHttpUrl(url) {
15763
+ let host;
15764
+ try {
15765
+ const u = new URL(url.includes("://") ? url : `http://${url}`);
15766
+ host = (u.hostname || "").toLowerCase().replace(/^\[|\]$/g, "");
15767
+ } catch {
15768
+ return false;
15769
+ }
15770
+ if (!host)
15771
+ return false;
15772
+ if (isLoopbackHttpUrl(url))
15773
+ return true;
15774
+ if (host === "host.docker.internal" || host === "gateway.docker.internal")
15775
+ return true;
15776
+ if (host.endsWith(".local") || host.endsWith(".internal"))
15777
+ return true;
15778
+ if (/^f[cd][0-9a-f]{2}:/.test(host) || /^fe[89ab][0-9a-f]:/.test(host))
15779
+ return true;
15780
+ const v4 = host.match(/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/);
15781
+ if (!v4)
15782
+ return false;
15783
+ const [a, b] = [Number(v4[1]), Number(v4[2])];
15784
+ if (a === 10)
15785
+ return true;
15786
+ if (a === 127)
15787
+ return true;
15788
+ if (a === 192 && b === 168)
15789
+ return true;
15790
+ if (a === 172 && b >= 16 && b <= 31)
15791
+ return true;
15792
+ if (a === 169 && b === 254)
15793
+ return true;
15794
+ return false;
15795
+ }
15796
+
15797
+ // src/setup/hindsight-context-budget.ts
15798
+ function clamp(value, floor, ceiling) {
15799
+ return Math.min(ceiling, Math.max(floor, value));
15800
+ }
15801
+ function usableContextTokens(windowTokens) {
15802
+ return windowTokens - Math.floor(windowTokens * HINDSIGHT_CONTEXT_SAFETY_FRACTION);
15803
+ }
15804
+ function defaultContextWindowForProvider(provider, baseUrl) {
15805
+ const url = baseUrl?.trim();
15806
+ if (url && isSelfHostedHttpUrl(url))
15807
+ return HINDSIGHT_CONSERVATIVE_CONTEXT_WINDOW;
15808
+ const p = provider.trim().toLowerCase();
15809
+ if (p === "claude-code" || p === "anthropic")
15810
+ return HINDSIGHT_CLAUDE_CONTEXT_WINDOW;
15811
+ return HINDSIGHT_CONSERVATIVE_CONTEXT_WINDOW;
15812
+ }
15813
+ function resolveLaneContextWindow(lane, llm) {
15814
+ const perOp = llm?.[lane];
15815
+ const provider = perOp?.provider?.trim() || llm?.provider?.trim() || "claude-code";
15816
+ const baseUrl = perOp?.base_url?.trim() || undefined;
15817
+ const perOpWindow = perOp?.context_window;
15818
+ if (typeof perOpWindow === "number" && perOpWindow > 0) {
15819
+ return { windowTokens: Math.floor(perOpWindow), windowSource: "per-op", provider, baseUrl };
15820
+ }
15821
+ const globalWindow = llm?.context_window;
15822
+ if (typeof globalWindow === "number" && globalWindow > 0) {
15823
+ return { windowTokens: Math.floor(globalWindow), windowSource: "global", provider, baseUrl };
15824
+ }
15825
+ return {
15826
+ windowTokens: defaultContextWindowForProvider(provider, baseUrl),
15827
+ windowSource: "provider-default",
15828
+ provider,
15829
+ baseUrl
15830
+ };
15831
+ }
15832
+ function resolveHindsightContextBudget(llm) {
15833
+ const cons = resolveLaneContextWindow("consolidation", llm);
15834
+ const ret = resolveLaneContextWindow("retain", llm);
15835
+ const consMaxCompletion = clamp(Math.floor(cons.windowTokens / 4), HINDSIGHT_CONSOLIDATION_MAX_COMPLETION_FLOOR, HINDSIGHT_CONSOLIDATION_MAX_COMPLETION_CEILING);
15836
+ const consUsable = usableContextTokens(cons.windowTokens);
15837
+ const consPromptBudget = consUsable - consMaxCompletion;
15838
+ const batchSize = clamp(Math.floor((consPromptBudget - HINDSIGHT_CONSOLIDATION_PROMPT_OVERHEAD_TOKENS) / HINDSIGHT_CONSOLIDATION_TOKENS_PER_FACT), HINDSIGHT_CONSOLIDATION_BATCH_SIZE_FLOOR, HINDSIGHT_CONSOLIDATION_BATCH_SIZE_CEILING);
15839
+ const consPrompt = HINDSIGHT_CONSOLIDATION_PROMPT_OVERHEAD_TOKENS + batchSize * HINDSIGHT_CONSOLIDATION_TOKENS_PER_FACT;
15840
+ const retMaxCompletion = clamp(Math.floor(ret.windowTokens * 3 / 16), HINDSIGHT_RETAIN_MAX_COMPLETION_FLOOR, HINDSIGHT_RETAIN_MAX_COMPLETION_CEILING);
15841
+ const refl = resolveLaneContextWindow("reflect", llm);
15842
+ const reflCompletionReserve = clamp(Math.floor(refl.windowTokens * 3 / 16), HINDSIGHT_RETAIN_MAX_COMPLETION_FLOOR, HINDSIGHT_RETAIN_MAX_COMPLETION_CEILING);
15843
+ const reflUsable = usableContextTokens(refl.windowTokens);
15844
+ const reflMaxContext = Math.max(HINDSIGHT_REFLECT_MAX_CONTEXT_FLOOR, Math.floor((reflUsable - reflCompletionReserve) / 1000) * 1000);
15845
+ return {
15846
+ consolidation: {
15847
+ lane: "consolidation",
15848
+ windowTokens: cons.windowTokens,
15849
+ usableTokens: consUsable,
15850
+ windowSource: cons.windowSource,
15851
+ provider: cons.provider,
15852
+ batchSize,
15853
+ maxCompletionTokens: consMaxCompletion,
15854
+ estimatedPromptTokens: consPrompt,
15855
+ worstCaseTotalTokens: consPrompt + consMaxCompletion
15856
+ },
15857
+ reflect: {
15858
+ lane: "reflect",
15859
+ windowTokens: refl.windowTokens,
15860
+ usableTokens: reflUsable,
15861
+ windowSource: refl.windowSource,
15862
+ provider: refl.provider,
15863
+ maxContextTokens: reflMaxContext,
15864
+ completionReserveTokens: reflCompletionReserve,
15865
+ estimatedPromptTokens: reflMaxContext,
15866
+ worstCaseTotalTokens: reflMaxContext + reflCompletionReserve
15867
+ },
15868
+ retain: {
15869
+ lane: "retain",
15870
+ windowTokens: ret.windowTokens,
15871
+ usableTokens: usableContextTokens(ret.windowTokens),
15872
+ windowSource: ret.windowSource,
15873
+ provider: ret.provider,
15874
+ maxCompletionTokens: retMaxCompletion,
15875
+ estimatedPromptTokens: HINDSIGHT_RETAIN_PROMPT_ESTIMATE_TOKENS,
15876
+ worstCaseTotalTokens: HINDSIGHT_RETAIN_PROMPT_ESTIMATE_TOKENS + retMaxCompletion
15877
+ }
15878
+ };
15879
+ }
15880
+ function laneCheck(b) {
15881
+ return { ...b, completionTokens: b.maxCompletionTokens, completionLabel: "completion" };
15882
+ }
15883
+ function reflectLaneCheck(b) {
15884
+ return {
15885
+ ...b,
15886
+ completionTokens: b.completionReserveTokens,
15887
+ completionLabel: "completion reserve"
15888
+ };
15889
+ }
15890
+ function laneFailure(b) {
15891
+ if (b.worstCaseTotalTokens > b.usableTokens) {
15892
+ return `hindsight ${b.lane}: worst-case ${b.worstCaseTotalTokens} tokens ` + `(${b.estimatedPromptTokens} prompt + ${b.completionTokens} ${b.completionLabel}) ` + `exceeds the ${b.usableTokens} usable tokens of ` + `the declared ${b.windowTokens}-token context window ` + `(${Math.round(HINDSIGHT_CONTEXT_SAFETY_FRACTION * 100)}% safety band held back; ` + `source: ${b.windowSource}, provider: ${b.provider}).`;
15893
+ }
15894
+ if (b.lane === "retain" && b.maxCompletionTokens !== undefined && b.maxCompletionTokens <= HINDSIGHT_UPSTREAM_RETAIN_CHUNK_SIZE) {
15895
+ return `hindsight retain: derived max_completion_tokens ${b.maxCompletionTokens} is not ` + `greater than the upstream retain_chunk_size ${HINDSIGHT_UPSTREAM_RETAIN_CHUNK_SIZE}; ` + `the hindsight container refuses to boot with this combination.`;
15896
+ }
15897
+ return;
15898
+ }
15899
+ function assertHindsightContextBudgetFits(budget) {
15900
+ for (const lane of [
15901
+ laneCheck(budget.consolidation),
15902
+ laneCheck(budget.retain),
15903
+ reflectLaneCheck(budget.reflect)
15904
+ ]) {
15905
+ const failure = laneFailure(lane);
15906
+ if (failure) {
15907
+ throw new HindsightContextBudgetError(lane.lane, `${failure}
15908
+ ` + `A local backend does NOT error on context overflow \u2014 llama.cpp context-shift ` + `silently drops the system prompt and returns HTTP 200 with conversational text ` + `instead of the required JSON, so this must be caught here.
15909
+ ` + `Fix: raise \`hindsight.llm.context_window\` (or \`hindsight.llm.${lane.lane}.context_window\`) ` + `to the backend's real window, or point the lane at a larger-window model.`);
15910
+ }
15911
+ }
15912
+ }
15913
+ function resolveCheckedHindsightContextBudget(llm) {
15914
+ const budget = resolveHindsightContextBudget(llm);
15915
+ assertHindsightContextBudgetFits(budget);
15916
+ return budget;
15917
+ }
15918
+ var HINDSIGHT_CLAUDE_CONTEXT_WINDOW = 200000, HINDSIGHT_CONSERVATIVE_CONTEXT_WINDOW = 32768, HINDSIGHT_CONTEXT_SAFETY_FRACTION = 0.2, HINDSIGHT_CONSOLIDATION_PROMPT_OVERHEAD_TOKENS = 2270, HINDSIGHT_CONSOLIDATION_TOKENS_PER_FACT = 2500, HINDSIGHT_RETAIN_PROMPT_ESTIMATE_TOKENS = 8000, HINDSIGHT_CONSOLIDATION_BATCH_SIZE_CEILING = 12, HINDSIGHT_CONSOLIDATION_BATCH_SIZE_FLOOR = 1, HINDSIGHT_CONSOLIDATION_MAX_COMPLETION_CEILING = 16384, HINDSIGHT_RETAIN_MAX_COMPLETION_CEILING = 16384, HINDSIGHT_UPSTREAM_RETAIN_CHUNK_SIZE = 3000, HINDSIGHT_CONSOLIDATION_MAX_COMPLETION_FLOOR = 1024, HINDSIGHT_RETAIN_MAX_COMPLETION_FLOOR, HINDSIGHT_REFLECT_MAX_CONTEXT_FLOOR = 2048, HindsightContextBudgetError;
15919
+ var init_hindsight_context_budget = __esm(() => {
15920
+ HINDSIGHT_RETAIN_MAX_COMPLETION_FLOOR = HINDSIGHT_UPSTREAM_RETAIN_CHUNK_SIZE + 72;
15921
+ HindsightContextBudgetError = class HindsightContextBudgetError extends Error {
15922
+ lane;
15923
+ constructor(lane, message) {
15924
+ super(message);
15925
+ this.name = "HindsightContextBudgetError";
15926
+ this.lane = lane;
15927
+ }
15928
+ };
15929
+ });
15930
+
15558
15931
  // src/util/atomic.ts
15559
15932
  var exports_atomic = {};
15560
15933
  __export(exports_atomic, {
@@ -15678,8 +16051,9 @@ function hindsightInteractiveLlmTimeoutSeconds() {
15678
16051
  function hindsightConsolidationLlmTimeoutSeconds() {
15679
16052
  return clientBudgetSeconds(LITELLM_TIMEOUT_TIERS.consolidation);
15680
16053
  }
15681
- function hindsightLlmBudgetEnv() {
15682
- const maxCompletionTokens = HINDSIGHT_DEFAULT_RETAIN_MAX_COMPLETION_TOKENS;
16054
+ function hindsightLlmBudgetEnv(llm) {
16055
+ const contextBudget = resolveCheckedHindsightContextBudget(llm);
16056
+ const maxCompletionTokens = Math.min(HINDSIGHT_DEFAULT_RETAIN_MAX_COMPLETION_TOKENS, contextBudget.retain.maxCompletionTokens);
15683
16057
  const interactiveS = hindsightInteractiveLlmTimeoutSeconds();
15684
16058
  const retainS = hindsightRetainClientTimeoutSeconds();
15685
16059
  const consolidationS = hindsightConsolidationLlmTimeoutSeconds();
@@ -15709,7 +16083,16 @@ function hindsightLlmBudgetEnv() {
15709
16083
  ["HINDSIGHT_API_LLM_TIMEOUT", String(interactiveS)],
15710
16084
  ["HINDSIGHT_API_REFLECT_LLM_TIMEOUT", String(interactiveS)],
15711
16085
  ["HINDSIGHT_API_RETAIN_LLM_TIMEOUT", String(retainS)],
15712
- ["HINDSIGHT_API_CONSOLIDATION_LLM_TIMEOUT", String(consolidationS)]
16086
+ ["HINDSIGHT_API_CONSOLIDATION_LLM_TIMEOUT", String(consolidationS)],
16087
+ ["HINDSIGHT_API_CONSOLIDATION_LLM_BATCH_SIZE", String(contextBudget.consolidation.batchSize)],
16088
+ [
16089
+ "HINDSIGHT_API_CONSOLIDATION_MAX_COMPLETION_TOKENS",
16090
+ String(contextBudget.consolidation.maxCompletionTokens)
16091
+ ],
16092
+ [
16093
+ "HINDSIGHT_API_REFLECT_MAX_CONTEXT_TOKENS",
16094
+ String(contextBudget.reflect.maxContextTokens)
16095
+ ]
15713
16096
  ];
15714
16097
  }
15715
16098
  function isPortFree(port) {
@@ -15838,15 +16221,6 @@ function resolveHindsightLlm(llm, litellm) {
15838
16221
  const model = llm?.model?.trim() || (litellm ? litellm.model ?? HINDSIGHT_DEFAULT_LITELLM_MODEL : HINDSIGHT_DEFAULT_MODEL);
15839
16222
  return { provider, model };
15840
16223
  }
15841
- function isLoopbackHttpUrl(url) {
15842
- try {
15843
- const u = new URL(url.includes("://") ? url : `http://${url}`);
15844
- const h = (u.hostname || "").toLowerCase();
15845
- return h === "localhost" || h === "127.0.0.1" || h === "::1";
15846
- } catch {
15847
- return false;
15848
- }
15849
- }
15850
16224
  function collectHindsightLlmBaseUrls(llm, litellm) {
15851
16225
  const out = [];
15852
16226
  const seen = new Set;
@@ -15869,6 +16243,11 @@ function hindsightNeedsHostNetwork(llm, litellm) {
15869
16243
  return true;
15870
16244
  return collectHindsightLlmBaseUrls(llm).some(isLoopbackHttpUrl);
15871
16245
  }
16246
+ function hindsightLocalLlmEnabled(llm, litellm, override) {
16247
+ if (override !== undefined)
16248
+ return override;
16249
+ return collectHindsightLlmBaseUrls(llm, litellm).some(isSelfHostedHttpUrl);
16250
+ }
15872
16251
  function pickHindsightLiteLlmProbeUrl(llm, litellm) {
15873
16252
  return collectHindsightLlmBaseUrls(llm, litellm)[0];
15874
16253
  }
@@ -15885,7 +16264,22 @@ function buildLiteLlmAwareHealthPy(apiPort, litellmBaseUrl) {
15885
16264
  function buildLiteLlmAwareHealthCmd(apiPort, litellmBaseUrl) {
15886
16265
  return `python3 -c "${buildLiteLlmAwareHealthPy(apiPort, litellmBaseUrl)}"`;
15887
16266
  }
15888
- function startHindsight(ports, litellm, imageTag, llm, mirrorDir) {
16267
+ function hindsightGpuEnabled(override) {
16268
+ if (override !== undefined)
16269
+ return override;
16270
+ const caps = loadHostCapabilities();
16271
+ return caps?.voice?.gpuPresent === true && caps?.voice?.containerToolkit === true;
16272
+ }
16273
+ function hindsightPerfEnvPairs(llm, litellm, gpu, perf) {
16274
+ return hindsightPerfEnv({
16275
+ gpu: hindsightGpuEnabled(gpu),
16276
+ localLlm: hindsightLocalLlmEnabled(llm, litellm, perf?.localLlm)
16277
+ }, resolveHindsightPerfOverrides(perf?.env, perf?.processEnv));
16278
+ }
16279
+ function hindsightPgEnvPairs(perf) {
16280
+ return hindsightPgEnv(resolveHindsightPgOverrides(perf?.env, perf?.processEnv));
16281
+ }
16282
+ function startHindsight(ports, litellm, imageTag, llm, mirrorDir, gpu, perf) {
15889
16283
  const apiPort = ports?.apiPort ?? HINDSIGHT_DEFAULT_API_PORT;
15890
16284
  const uiPort = ports?.uiPort ?? HINDSIGHT_DEFAULT_UI_PORT;
15891
16285
  const { provider: llmProvider, model: llmModel } = resolveHindsightLlm(llm, litellm);
@@ -15910,17 +16304,17 @@ function startHindsight(ports, litellm, imageTag, llm, mirrorDir) {
15910
16304
  `HINDSIGHT_API_RECALL_MAX_CONCURRENT=${HINDSIGHT_DEFAULT_RECALL_MAX_CONCURRENT}`,
15911
16305
  "-e",
15912
16306
  `HINDSIGHT_API_REFLECT_WALL_TIMEOUT=${HINDSIGHT_DEFAULT_REFLECT_WALL_TIMEOUT_S}`,
15913
- ...hindsightLlmBudgetEnv().flatMap(([k, v]) => ["-e", `${k}=${v}`]),
15914
- "-e",
15915
- `HINDSIGHT_API_CONSOLIDATION_LLM_BATCH_SIZE=${HINDSIGHT_DEFAULT_CONSOLIDATION_LLM_BATCH_SIZE}`,
16307
+ ...hindsightLlmBudgetEnv(llm).flatMap(([k, v]) => ["-e", `${k}=${v}`]),
15916
16308
  "-e",
15917
16309
  `HINDSIGHT_API_WORKER_CONSOLIDATION_MAX_SLOTS=${HINDSIGHT_DEFAULT_CONSOLIDATION_MAX_SLOTS}`,
15918
16310
  "-e",
15919
- `HINDSIGHT_API_CONSOLIDATION_LLM_PARALLELISM=${HINDSIGHT_DEFAULT_CONSOLIDATION_LLM_PARALLELISM}`,
16311
+ `HINDSIGHT_API_WORKER_CONSOLIDATION_SLOT_LIMIT=${HINDSIGHT_DEFAULT_CONSOLIDATION_SLOT_LIMIT}`,
15920
16312
  "-e",
15921
16313
  `HINDSIGHT_API_CONSOLIDATION_MAX_MEMORIES_PER_ROUND=${HINDSIGHT_DEFAULT_CONSOLIDATION_MAX_MEMORIES_PER_ROUND}`,
15922
16314
  "-e",
15923
- `HINDSIGHT_API_WORKER_ID=${HINDSIGHT_DEFAULT_WORKER_ID}`
16315
+ `HINDSIGHT_API_WORKER_ID=${HINDSIGHT_DEFAULT_WORKER_ID}`,
16316
+ ...hindsightPerfEnvPairs(llm, litellm, gpu, perf).flatMap(([k, v]) => ["-e", `${k}=${v}`]),
16317
+ ...hindsightPgEnvPairs(perf).flatMap(([k, v]) => ["-e", `${k}=${v}`])
15924
16318
  ];
15925
16319
  if (llmProvider === "claude-code") {
15926
16320
  envArgs.push("-e", `ANTHROPIC_MODEL=${llmModel}`);
@@ -15943,6 +16337,7 @@ x-litellm-tags: service:hindsight`);
15943
16337
  `--memory-reservation=${HINDSIGHT_DEFAULT_MEM_RESERVATION}`,
15944
16338
  `--pids-limit=${HINDSIGHT_DEFAULT_PIDS_LIMIT}`,
15945
16339
  `--shm-size=${HINDSIGHT_DEFAULT_SHM_SIZE}`,
16340
+ ...hindsightGpuEnabled(gpu) ? ["--gpus", "all"] : [],
15946
16341
  ...(() => {
15947
16342
  const hostNet = hindsightNeedsHostNetwork(llm, litellm);
15948
16343
  const healthApiPort = hostNet ? apiPort : 8888;
@@ -16078,10 +16473,11 @@ function getHindsightStatus() {
16078
16473
  return null;
16079
16474
  }
16080
16475
  }
16081
- function generateHindsightComposeSnippet(llm, mirrorDir, litellm) {
16476
+ function generateHindsightComposeSnippet(llm, mirrorDir, litellm, gpu, perf) {
16082
16477
  const { provider: llmProvider, model: llmModel } = resolveHindsightLlm(llm, litellm);
16083
16478
  const perOpLlm = resolveHindsightPerOpLlm(llm);
16084
16479
  const hostNetwork = hindsightNeedsHostNetwork(llm, litellm);
16480
+ const gpuEnabled = hindsightGpuEnabled(gpu);
16085
16481
  const apiPort = HINDSIGHT_DEFAULT_API_PORT;
16086
16482
  const internalApiPort = hostNetwork ? apiPort : 8888;
16087
16483
  const probeUrl = pickHindsightLiteLlmProbeUrl(llm, litellm);
@@ -16141,16 +16537,26 @@ function generateHindsightComposeSnippet(llm, mirrorDir, litellm) {
16141
16537
  ` - HINDSIGHT_API_RERANKER_LOCAL_MAX_CONCURRENT=${HINDSIGHT_DEFAULT_RERANKER_LOCAL_MAX_CONCURRENT}`,
16142
16538
  ` - HINDSIGHT_API_RECALL_MAX_CONCURRENT=${HINDSIGHT_DEFAULT_RECALL_MAX_CONCURRENT}`,
16143
16539
  ` - HINDSIGHT_API_REFLECT_WALL_TIMEOUT=${HINDSIGHT_DEFAULT_REFLECT_WALL_TIMEOUT_S}`,
16144
- ...hindsightLlmBudgetEnv().map(([k, v]) => ` - ${k}=${v}`),
16145
- ` - HINDSIGHT_API_CONSOLIDATION_LLM_BATCH_SIZE=${HINDSIGHT_DEFAULT_CONSOLIDATION_LLM_BATCH_SIZE}`,
16540
+ ...hindsightLlmBudgetEnv(llm).map(([k, v]) => ` - ${k}=${v}`),
16146
16541
  ` - HINDSIGHT_API_WORKER_CONSOLIDATION_MAX_SLOTS=${HINDSIGHT_DEFAULT_CONSOLIDATION_MAX_SLOTS}`,
16147
- ` - HINDSIGHT_API_CONSOLIDATION_LLM_PARALLELISM=${HINDSIGHT_DEFAULT_CONSOLIDATION_LLM_PARALLELISM}`,
16542
+ ` - HINDSIGHT_API_WORKER_CONSOLIDATION_SLOT_LIMIT=${HINDSIGHT_DEFAULT_CONSOLIDATION_SLOT_LIMIT}`,
16148
16543
  ` - HINDSIGHT_API_CONSOLIDATION_MAX_MEMORIES_PER_ROUND=${HINDSIGHT_DEFAULT_CONSOLIDATION_MAX_MEMORIES_PER_ROUND}`,
16149
16544
  ` - HINDSIGHT_API_WORKER_ID=${HINDSIGHT_DEFAULT_WORKER_ID}`,
16545
+ ...hindsightPerfEnvPairs(llm, litellm, gpu, perf).map(([k, v]) => ` - ${k}=${v}`),
16546
+ ...hindsightPgEnvPairs(perf).map(([k, v]) => ` - ${k}=${v}`),
16150
16547
  ` mem_limit: ${HINDSIGHT_DEFAULT_MEM_LIMIT}`,
16151
16548
  ` mem_reservation: ${HINDSIGHT_DEFAULT_MEM_RESERVATION}`,
16152
16549
  ` pids_limit: ${HINDSIGHT_DEFAULT_PIDS_LIMIT}`,
16153
16550
  ` shm_size: ${HINDSIGHT_DEFAULT_SHM_SIZE}`,
16551
+ ...gpuEnabled ? [
16552
+ " deploy:",
16553
+ " resources:",
16554
+ " reservations:",
16555
+ " devices:",
16556
+ " - driver: nvidia",
16557
+ " count: 1",
16558
+ ' capabilities: ["gpu"]'
16559
+ ] : [],
16154
16560
  " healthcheck:",
16155
16561
  ` test: ${JSON.stringify(["CMD", "python3", "-c", healthPy])}`,
16156
16562
  " interval: 30s",
@@ -16236,7 +16642,7 @@ async function ensureHindsightConsumer(configPath, account, uid = HINDSIGHT_DEFA
16236
16642
  atomicWriteFileSync2(configPath, tail, mode);
16237
16643
  return { added: true, reason: "added" };
16238
16644
  }
16239
- var HINDSIGHT_DEFAULT_API_PORT = 18888, HINDSIGHT_DEFAULT_UI_PORT = 9999, HINDSIGHT_DEFAULT_MCP_URL, HINDSIGHT_DEFAULT_API_BASE_URL, HINDSIGHT_DEFAULT_MAX_OBSERVATIONS_PER_SCOPE = 1000, HINDSIGHT_CONSUMER_NAME = "hindsight", HINDSIGHT_DEFAULT_WORKER_ID = "switchroom-hindsight", HINDSIGHT_DATA_VOLUME = "switchroom-hindsight-data", HINDSIGHT_DEFAULT_UID = 11000, HINDSIGHT_IMAGE_REPO = "ghcr.io/switchroom/switchroom-hindsight", HINDSIGHT_IMAGE, HINDSIGHT_DEFAULT_MODEL = "claude-sonnet-5", HINDSIGHT_DEFAULT_LITELLM_MODEL = "openrouter/google/gemini-3.1-flash-lite", HINDSIGHT_DEFAULT_MCP_STATELESS = true, HINDSIGHT_BROKER_SOCK_VOLUME, HINDSIGHT_CREDS_MIRROR_VOLUME, HINDSIGHT_CRED_DIR = "/run/claude-creds", HINDSIGHT_DEFAULT_RERANKER_BUCKET_BATCHING = "true", HINDSIGHT_DEFAULT_RERANKER_MAX_CANDIDATES = 150, HINDSIGHT_DEFAULT_RERANKER_LOCAL_MAX_CONCURRENT = 4, HINDSIGHT_DEFAULT_RECALL_MAX_CONCURRENT = 8, HINDSIGHT_DEFAULT_REFLECT_WALL_TIMEOUT_S = 600, HINDSIGHT_RETAIN_CHUNK_SIZE = 3000, HINDSIGHT_DEFAULT_RETAIN_MAX_COMPLETION_TOKENS = 16384, HINDSIGHT_RETAIN_MIN_TOKENS_PER_SECOND = 80.6, HINDSIGHT_RETAIN_CLIENT_DEADLINE_S, HINDSIGHT_DEFAULT_CONSOLIDATION_LLM_BATCH_SIZE = 12, HINDSIGHT_DEFAULT_CONSOLIDATION_MAX_SLOTS = 1, HINDSIGHT_DEFAULT_CONSOLIDATION_LLM_PARALLELISM = 2, HINDSIGHT_DEFAULT_CONSOLIDATION_MAX_MEMORIES_PER_ROUND = 100, HINDSIGHT_DEFAULT_MEM_LIMIT = "8g", HINDSIGHT_DEFAULT_MEM_RESERVATION = "4g", HINDSIGHT_DEFAULT_PIDS_LIMIT = 1000, HINDSIGHT_DEFAULT_SHM_SIZE = "2g", HINDSIGHT_HEALTHCHECK_PY = 'import urllib.request,sys; sys.exit(0 if urllib.request.urlopen("http://localhost:8888/health",timeout=4).getcode()==200 else 1)', HINDSIGHT_HEALTHCHECK_CMD, DOCKER_PROBE_TIMEOUT_MS, defaultDockerProbe = (args) => {
16645
+ var HINDSIGHT_DEFAULT_API_PORT = 18888, HINDSIGHT_DEFAULT_UI_PORT = 9999, HINDSIGHT_DEFAULT_MCP_URL, HINDSIGHT_DEFAULT_API_BASE_URL, HINDSIGHT_DEFAULT_MAX_OBSERVATIONS_PER_SCOPE = 1000, HINDSIGHT_CONSUMER_NAME = "hindsight", HINDSIGHT_DEFAULT_WORKER_ID = "switchroom-hindsight", HINDSIGHT_DATA_VOLUME = "switchroom-hindsight-data", HINDSIGHT_DEFAULT_UID = 11000, HINDSIGHT_IMAGE_REPO = "ghcr.io/switchroom/switchroom-hindsight", HINDSIGHT_IMAGE, HINDSIGHT_DEFAULT_MODEL = "claude-sonnet-5", HINDSIGHT_DEFAULT_LITELLM_MODEL = "openrouter/google/gemini-3.1-flash-lite", HINDSIGHT_DEFAULT_MCP_STATELESS = true, HINDSIGHT_BROKER_SOCK_VOLUME, HINDSIGHT_CREDS_MIRROR_VOLUME, HINDSIGHT_CRED_DIR = "/run/claude-creds", HINDSIGHT_DEFAULT_RERANKER_BUCKET_BATCHING = "true", HINDSIGHT_DEFAULT_RERANKER_MAX_CANDIDATES = 150, HINDSIGHT_DEFAULT_RERANKER_LOCAL_MAX_CONCURRENT = 4, HINDSIGHT_DEFAULT_RECALL_MAX_CONCURRENT = 8, HINDSIGHT_DEFAULT_REFLECT_WALL_TIMEOUT_S = 600, HINDSIGHT_RETAIN_CHUNK_SIZE = 3000, HINDSIGHT_DEFAULT_RETAIN_MAX_COMPLETION_TOKENS = 16384, HINDSIGHT_RETAIN_MIN_TOKENS_PER_SECOND = 80.6, HINDSIGHT_RETAIN_CLIENT_DEADLINE_S, HINDSIGHT_DEFAULT_CONSOLIDATION_MAX_SLOTS = 1, HINDSIGHT_DEFAULT_CONSOLIDATION_MAX_MEMORIES_PER_ROUND = 500, HINDSIGHT_DEFAULT_CONSOLIDATION_SLOT_LIMIT = 6, HINDSIGHT_DEFAULT_MEM_LIMIT = "8g", HINDSIGHT_DEFAULT_MEM_RESERVATION = "4g", HINDSIGHT_DEFAULT_PIDS_LIMIT = 1000, HINDSIGHT_DEFAULT_SHM_SIZE = "2g", HINDSIGHT_HEALTHCHECK_PY = 'import urllib.request,sys; sys.exit(0 if urllib.request.urlopen("http://localhost:8888/health",timeout=4).getcode()==200 else 1)', HINDSIGHT_HEALTHCHECK_CMD, DOCKER_PROBE_TIMEOUT_MS, defaultDockerProbe = (args) => {
16240
16646
  try {
16241
16647
  return execFileSync("docker", args, {
16242
16648
  stdio: "pipe",
@@ -16251,6 +16657,11 @@ var HINDSIGHT_DEFAULT_API_PORT = 18888, HINDSIGHT_DEFAULT_UI_PORT = 9999, HINDSI
16251
16657
  var init_hindsight = __esm(() => {
16252
16658
  init_model_command();
16253
16659
  init_timeout_budget();
16660
+ init_host_capabilities();
16661
+ init_hindsight_perf_defaults();
16662
+ init_hindsight_pg_defaults();
16663
+ init_hindsight_context_budget();
16664
+ init_hindsight_perf_defaults();
16254
16665
  HINDSIGHT_DEFAULT_MCP_URL = `http://127.0.0.1:${HINDSIGHT_DEFAULT_API_PORT}/mcp/`;
16255
16666
  HINDSIGHT_DEFAULT_API_BASE_URL = HINDSIGHT_DEFAULT_MCP_URL.replace(/\/mcp\/?$/, "");
16256
16667
  HINDSIGHT_IMAGE = `${HINDSIGHT_IMAGE_REPO}:latest`;
@@ -17021,7 +17432,7 @@ function parseSocketIdentity(socketPath, spec) {
17021
17432
 
17022
17433
  // src/vault/broker/peercred.ts
17023
17434
  import { execFileSync as execFileSync2 } from "node:child_process";
17024
- import { readFileSync as readFileSync5, readlinkSync as readlinkSync2, fstatSync } from "node:fs";
17435
+ import { readFileSync as readFileSync6, readlinkSync as readlinkSync2, fstatSync } from "node:fs";
17025
17436
  function socketPathToIdentity(socketPath) {
17026
17437
  return parseSocketIdentity(socketPath, {
17027
17438
  patterns: [SOCKET_PATH_AGENT_RE, SOCKET_PATH_AGENT_SUBDIR_RE],
@@ -17102,7 +17513,7 @@ function findClientPidByServerInode(rows, socketPath, serverInode) {
17102
17513
  }
17103
17514
  function readUid(pid) {
17104
17515
  try {
17105
- const status = readFileSync5(`/proc/${pid}/status`, "utf8");
17516
+ const status = readFileSync6(`/proc/${pid}/status`, "utf8");
17106
17517
  const m = status.match(/^Uid:\s+(\d+)/m);
17107
17518
  if (!m)
17108
17519
  return null;
@@ -17120,7 +17531,7 @@ function readExe(pid) {
17120
17531
  }
17121
17532
  function readSystemdUnit(pid) {
17122
17533
  try {
17123
- const content = readFileSync5(`/proc/${pid}/cgroup`, "utf8");
17534
+ const content = readFileSync6(`/proc/${pid}/cgroup`, "utf8");
17124
17535
  const lines = content.split(`
17125
17536
  `);
17126
17537
  for (const line of lines) {
@@ -17287,7 +17698,7 @@ var init_agent_uid = __esm(() => {
17287
17698
 
17288
17699
  // src/agents/agent-owned-tree.ts
17289
17700
  import { execFileSync as execFileSync3 } from "node:child_process";
17290
- import { existsSync as existsSync5, lstatSync, readdirSync as readdirSync2, statSync as statSync3 } from "node:fs";
17701
+ import { existsSync as existsSync6, lstatSync, readdirSync as readdirSync2, statSync as statSync3 } from "node:fs";
17291
17702
  import { join as join3 } from "node:path";
17292
17703
  function scopedAssertCandidates(agentDir) {
17293
17704
  return [
@@ -17306,7 +17717,7 @@ function chownScopedTree(uid, gid, agentDir) {
17306
17717
  ownershipRuntime.chownShallow(uid, gid, [agentDir, ...children]);
17307
17718
  for (const sub of SCOPED_RECURSIVE_SUBDIRS) {
17308
17719
  const subPath = join3(agentDir, sub);
17309
- if (existsSync5(subPath)) {
17720
+ if (existsSync6(subPath)) {
17310
17721
  ownershipRuntime.chownTree(uid, gid, subPath);
17311
17722
  }
17312
17723
  }
@@ -17314,7 +17725,7 @@ function chownScopedTree(uid, gid, agentDir) {
17314
17725
  function alignAgentTreeOwnershipIfRoot(name, agentDir) {
17315
17726
  if (ownershipRuntime.geteuid() !== 0)
17316
17727
  return null;
17317
- if (!existsSync5(agentDir))
17728
+ if (!existsSync6(agentDir))
17318
17729
  return null;
17319
17730
  const uid = allocateAgentUid(name);
17320
17731
  const scoped = ownershipRuntime.scopedSweepEnabled();
@@ -17401,7 +17812,7 @@ var init_agent_owned_tree = __esm(() => {
17401
17812
 
17402
17813
  // src/agents/generation-stamp.ts
17403
17814
  import { createHash as createHash3 } from "node:crypto";
17404
- import { existsSync as existsSync6, readFileSync as readFileSync6, writeFileSync } from "node:fs";
17815
+ import { existsSync as existsSync7, readFileSync as readFileSync7, writeFileSync as writeFileSync2 } from "node:fs";
17405
17816
  import { join as join4 } from "node:path";
17406
17817
  function sha256Text(s) {
17407
17818
  return createHash3("sha256").update(s, "utf-8").digest("hex");
@@ -17424,11 +17835,11 @@ function hashManagedClaudeMd(content) {
17424
17835
  }
17425
17836
  function hashStampedFileOnDisk(agentDir, rel) {
17426
17837
  const p = join4(agentDir, rel);
17427
- if (!existsSync6(p))
17838
+ if (!existsSync7(p))
17428
17839
  return null;
17429
17840
  let content;
17430
17841
  try {
17431
- content = readFileSync6(p, "utf-8");
17842
+ content = readFileSync7(p, "utf-8");
17432
17843
  } catch {
17433
17844
  return null;
17434
17845
  }
@@ -17451,15 +17862,15 @@ function writeGenerationStamp(agentDir, stamp) {
17451
17862
  configHash: stamp.configHash,
17452
17863
  files: stamp.files
17453
17864
  };
17454
- writeFileSync(join4(agentDir, GENERATION_STAMP_FILE), JSON.stringify(full, null, 2) + `
17865
+ writeFileSync2(join4(agentDir, GENERATION_STAMP_FILE), JSON.stringify(full, null, 2) + `
17455
17866
  `, "utf-8");
17456
17867
  }
17457
17868
  function readGenerationStamp(agentDir) {
17458
17869
  const p = join4(agentDir, GENERATION_STAMP_FILE);
17459
- if (!existsSync6(p))
17870
+ if (!existsSync7(p))
17460
17871
  return null;
17461
17872
  try {
17462
- const parsed = JSON.parse(readFileSync6(p, "utf-8"));
17873
+ const parsed = JSON.parse(readFileSync7(p, "utf-8"));
17463
17874
  if (parsed?.version !== 1 || typeof parsed.files !== "object" || parsed.files === null || Array.isArray(parsed.files)) {
17464
17875
  return null;
17465
17876
  }
@@ -17976,10 +18387,10 @@ var init_cron_introspect = __esm(() => {
17976
18387
  import { join as join5 } from "node:path";
17977
18388
  import { homedir as homedir2 } from "node:os";
17978
18389
  import {
17979
- existsSync as existsSync7,
17980
- mkdirSync,
18390
+ existsSync as existsSync8,
18391
+ mkdirSync as mkdirSync2,
17981
18392
  appendFileSync,
17982
- readFileSync as readFileSync7
18393
+ readFileSync as readFileSync8
17983
18394
  } from "node:fs";
17984
18395
  function auditPathFor(agent) {
17985
18396
  return join5(AUDIT_ROOT, agent, "agent-config.jsonl");
@@ -17996,8 +18407,8 @@ function appendAudit(agent, cmd, args, exit, opts = {}) {
17996
18407
  const path = opts.auditPath ?? auditPathFor(agent);
17997
18408
  const dir = path.slice(0, path.lastIndexOf("/"));
17998
18409
  try {
17999
- if (!existsSync7(dir)) {
18000
- mkdirSync(dir, { recursive: true });
18410
+ if (!existsSync8(dir)) {
18411
+ mkdirSync2(dir, { recursive: true });
18001
18412
  }
18002
18413
  appendFileSync(path, JSON.stringify(row) + `
18003
18414
  `, { flag: "a" });
@@ -18008,7 +18419,7 @@ function isContainerContext(env2 = process.env, opts = {}) {
18008
18419
  return true;
18009
18420
  const probe = opts.dockerEnvPath ?? "/.dockerenv";
18010
18421
  try {
18011
- if (existsSync7(probe))
18422
+ if (existsSync8(probe))
18012
18423
  return true;
18013
18424
  } catch {}
18014
18425
  return false;
@@ -18136,11 +18547,11 @@ function getAgentSlice(config, agent) {
18136
18547
  }
18137
18548
  function readAuditTail(agent, limit, opts = {}) {
18138
18549
  const path = opts.auditPath ?? auditPathFor(agent);
18139
- if (!existsSync7(path))
18550
+ if (!existsSync8(path))
18140
18551
  return [];
18141
18552
  let raw;
18142
18553
  try {
18143
- raw = readFileSync7(path, "utf-8");
18554
+ raw = readFileSync8(path, "utf-8");
18144
18555
  } catch {
18145
18556
  return [];
18146
18557
  }
@@ -23946,7 +24357,7 @@ var require_lib = __commonJS((exports, module) => {
23946
24357
  });
23947
24358
 
23948
24359
  // src/agents/profiles.ts
23949
- import { readFileSync as readFileSync8, writeFileSync as writeFileSync2, existsSync as existsSync8, readdirSync as readdirSync3, statSync as statSync4, copyFileSync, mkdirSync as mkdirSync2, realpathSync } from "node:fs";
24360
+ import { readFileSync as readFileSync9, writeFileSync as writeFileSync3, existsSync as existsSync9, readdirSync as readdirSync3, statSync as statSync4, copyFileSync, mkdirSync as mkdirSync3, realpathSync } from "node:fs";
23950
24361
  import { resolve as resolve4, join as join6, sep as pathSep } from "node:path";
23951
24362
  function resolveProfilesRoot() {
23952
24363
  const envOverride = process.env.SWITCHROOM_PROFILES_ROOT?.trim();
@@ -23958,7 +24369,7 @@ function resolveProfilesRoot() {
23958
24369
  resolve4(import.meta.dirname, "profiles")
23959
24370
  ];
23960
24371
  for (const candidate of candidates) {
23961
- if (existsSync8(candidate)) {
24372
+ if (existsSync9(candidate)) {
23962
24373
  return candidate;
23963
24374
  }
23964
24375
  }
@@ -23978,11 +24389,11 @@ function getProfilePath(profileName) {
23978
24389
  if (real !== PROFILES_ROOT && !real.startsWith(PROFILES_ROOT + pathSep)) {
23979
24390
  throw new Error(`Invalid profile name: ${profileName}`);
23980
24391
  }
23981
- if (existsSync8(requested) && hasProfileFiles(requested)) {
24392
+ if (existsSync9(requested) && hasProfileFiles(requested)) {
23982
24393
  return requested;
23983
24394
  }
23984
24395
  const fallback = resolve4(PROFILES_ROOT, "default");
23985
- if (existsSync8(fallback)) {
24396
+ if (existsSync9(fallback)) {
23986
24397
  return fallback;
23987
24398
  }
23988
24399
  throw new Error(`Profile not found: ${profileName} (searched ${PROFILES_ROOT})`);
@@ -24012,19 +24423,19 @@ function getBaseProfilePath() {
24012
24423
  return resolve4(PROFILES_ROOT, "_base");
24013
24424
  }
24014
24425
  function renderTemplate(templatePath, context) {
24015
- const source = readFileSync8(templatePath, "utf-8");
24426
+ const source = readFileSync9(templatePath, "utf-8");
24016
24427
  const template = import_handlebars.default.compile(source, { noEscape: true });
24017
24428
  return template(context);
24018
24429
  }
24019
24430
  function copyProfileSkills(profilePath, destPath) {
24020
24431
  const skillsSrc = join6(profilePath, "skills");
24021
- if (!existsSync8(skillsSrc)) {
24432
+ if (!existsSync9(skillsSrc)) {
24022
24433
  return;
24023
24434
  }
24024
24435
  copyDirRecursive(skillsSrc, destPath);
24025
24436
  }
24026
24437
  function copyDirRecursive(src, dest) {
24027
- mkdirSync2(dest, { recursive: true });
24438
+ mkdirSync3(dest, { recursive: true });
24028
24439
  const entries = readdirSync3(src);
24029
24440
  for (const entry of entries) {
24030
24441
  const srcPath = join6(src, entry);
@@ -24033,7 +24444,7 @@ function copyDirRecursive(src, dest) {
24033
24444
  if (stat.isDirectory()) {
24034
24445
  copyDirRecursive(srcPath, destPath);
24035
24446
  } else {
24036
- if (!existsSync8(destPath)) {
24447
+ if (!existsSync9(destPath)) {
24037
24448
  copyFileSync(srcPath, destPath);
24038
24449
  }
24039
24450
  }
@@ -24041,49 +24452,49 @@ function copyDirRecursive(src, dest) {
24041
24452
  }
24042
24453
  function renderVaultProtocolFragment(context = {}, profilesRoot = PROFILES_ROOT) {
24043
24454
  const fragPath = join6(resolve4(profilesRoot, "_shared"), "vault-protocol.md.hbs");
24044
- if (!existsSync8(fragPath))
24455
+ if (!existsSync9(fragPath))
24045
24456
  return "";
24046
- const source = readFileSync8(fragPath, "utf-8");
24457
+ const source = readFileSync9(fragPath, "utf-8");
24047
24458
  const template = import_handlebars.default.compile(source, { noEscape: true });
24048
24459
  return template(context).trimEnd();
24049
24460
  }
24050
24461
  function renderAgentSelfServiceFragment(context = {}, profilesRoot = PROFILES_ROOT) {
24051
24462
  const fragPath = join6(resolve4(profilesRoot, "_shared"), "agent-self-service.md.hbs");
24052
- if (!existsSync8(fragPath))
24463
+ if (!existsSync9(fragPath))
24053
24464
  return "";
24054
- const source = readFileSync8(fragPath, "utf-8");
24465
+ const source = readFileSync9(fragPath, "utf-8");
24055
24466
  const template = import_handlebars.default.compile(source, { noEscape: true });
24056
24467
  return template(context).trimEnd();
24057
24468
  }
24058
24469
  function renderExecutionDisciplineFragment(context = {}, profilesRoot = PROFILES_ROOT) {
24059
24470
  const fragPath = join6(resolve4(profilesRoot, "_shared"), "execution-discipline.md.hbs");
24060
- if (!existsSync8(fragPath))
24471
+ if (!existsSync9(fragPath))
24061
24472
  return "";
24062
- const source = readFileSync8(fragPath, "utf-8");
24473
+ const source = readFileSync9(fragPath, "utf-8");
24063
24474
  const template = import_handlebars.default.compile(source, { noEscape: true });
24064
24475
  return template(context).trimEnd();
24065
24476
  }
24066
24477
  function renderDevProtocolFragment(context = {}, profilesRoot = PROFILES_ROOT) {
24067
24478
  const fragPath = join6(resolve4(profilesRoot, "_shared"), "dev-protocol.md.hbs");
24068
- if (!existsSync8(fragPath))
24479
+ if (!existsSync9(fragPath))
24069
24480
  return "";
24070
- const source = readFileSync8(fragPath, "utf-8");
24481
+ const source = readFileSync9(fragPath, "utf-8");
24071
24482
  const template = import_handlebars.default.compile(source, { noEscape: true });
24072
24483
  return template(context).trimEnd();
24073
24484
  }
24074
24485
  function renderDelegationGoldenRuleFragment(context = {}, profilesRoot = PROFILES_ROOT) {
24075
24486
  const fragPath = join6(resolve4(profilesRoot, "_shared"), "delegation-golden-rule.md.hbs");
24076
- if (!existsSync8(fragPath))
24487
+ if (!existsSync9(fragPath))
24077
24488
  return "";
24078
- const source = readFileSync8(fragPath, "utf-8");
24489
+ const source = readFileSync9(fragPath, "utf-8");
24079
24490
  const template = import_handlebars.default.compile(source, { noEscape: true });
24080
24491
  return template(context).trimEnd();
24081
24492
  }
24082
24493
  function renderReplyDisciplineFragment(context = {}, profilesRoot = PROFILES_ROOT) {
24083
24494
  const fragPath = join6(resolve4(profilesRoot, "_shared"), "reply-discipline.md.hbs");
24084
- if (!existsSync8(fragPath))
24495
+ if (!existsSync9(fragPath))
24085
24496
  return "";
24086
- const source = readFileSync8(fragPath, "utf-8");
24497
+ const source = readFileSync9(fragPath, "utf-8");
24087
24498
  const template = import_handlebars.default.compile(source, { noEscape: true });
24088
24499
  return template(context).trimEnd();
24089
24500
  }
@@ -24091,14 +24502,14 @@ function renderProfileClaudeTemplate(profileName, profilesRoot = PROFILES_ROOT)
24091
24502
  const profileDir = resolve4(profilesRoot, profileName);
24092
24503
  const hbsPath = join6(profileDir, "CLAUDE.md.hbs");
24093
24504
  const outPath = join6(profileDir, "CLAUDE.md");
24094
- if (!existsSync8(hbsPath)) {
24505
+ if (!existsSync9(hbsPath)) {
24095
24506
  return { wrote: false, path: outPath };
24096
24507
  }
24097
- const source = readFileSync8(hbsPath, "utf-8");
24508
+ const source = readFileSync9(hbsPath, "utf-8");
24098
24509
  const template = import_handlebars.default.compile(source, { noEscape: true });
24099
24510
  const rendered = template({ profile: profileName });
24100
24511
  try {
24101
- writeFileSync2(outPath, rendered, "utf-8");
24512
+ writeFileSync3(outPath, rendered, "utf-8");
24102
24513
  return { wrote: true, path: outPath };
24103
24514
  } catch (err) {
24104
24515
  const code = err.code;
@@ -24123,8 +24534,8 @@ var init_profiles = __esm(() => {
24123
24534
  SHARED_FRAGMENTS = ["vault-protocol", "agent-self-service", "execution-discipline", "reply-discipline", "dev-protocol"];
24124
24535
  for (const name of SHARED_FRAGMENTS) {
24125
24536
  const fragPath = join6(SHARED_FRAGMENTS_DIR, `${name}.md.hbs`);
24126
- if (existsSync8(fragPath)) {
24127
- import_handlebars.default.registerPartial(name, readFileSync8(fragPath, "utf-8"));
24537
+ if (existsSync9(fragPath)) {
24538
+ import_handlebars.default.registerPartial(name, readFileSync9(fragPath, "utf-8"));
24128
24539
  }
24129
24540
  }
24130
24541
  });
@@ -24289,9 +24700,9 @@ function bindingsForAgent(agentName, mw, microsoftAccounts) {
24289
24700
  }
24290
24701
 
24291
24702
  // src/agents/reconcile-default-skills.ts
24292
- import { existsSync as existsSync9, lstatSync as lstatSync2, mkdirSync as mkdirSync3, readdirSync as readdirSync4, readlinkSync as readlinkSync3, rmSync as rmSync2, symlinkSync } from "node:fs";
24703
+ import { existsSync as existsSync10, lstatSync as lstatSync2, mkdirSync as mkdirSync4, readdirSync as readdirSync4, readlinkSync as readlinkSync3, rmSync as rmSync2, symlinkSync } from "node:fs";
24293
24704
  import { homedir as homedir3 } from "node:os";
24294
- import { dirname as dirname2, isAbsolute, join as join7, relative, resolve as resolve5 } from "node:path";
24705
+ import { dirname as dirname3, isAbsolute, join as join7, relative, resolve as resolve5 } from "node:path";
24295
24706
  function warnMissingPoolDir(poolDir) {
24296
24707
  if (warnedMissingPool.has(poolDir))
24297
24708
  return;
@@ -24322,10 +24733,10 @@ function isOwnedStaleLink(target, poolDir) {
24322
24733
  return false;
24323
24734
  }
24324
24735
  function absoluteLinkTarget(linkPath, storedTarget) {
24325
- return isAbsolute(storedTarget) ? storedTarget : resolve5(dirname2(linkPath), storedTarget);
24736
+ return isAbsolute(storedTarget) ? storedTarget : resolve5(dirname3(linkPath), storedTarget);
24326
24737
  }
24327
24738
  function linkTargetFor(dest, src) {
24328
- return relative(dirname2(dest), src);
24739
+ return relative(dirname3(dest), src);
24329
24740
  }
24330
24741
  function isOwnedBundledLink(dest, poolDir) {
24331
24742
  let stored = null;
@@ -24338,7 +24749,7 @@ function isOwnedBundledLink(dest, poolDir) {
24338
24749
  }
24339
24750
  if (!stored)
24340
24751
  return false;
24341
- const resolved = isAbsolute(stored) ? stored : resolve5(dirname2(dest), stored);
24752
+ const resolved = isAbsolute(stored) ? stored : resolve5(dirname3(dest), stored);
24342
24753
  const poolPrefix = poolDir.endsWith("/") ? poolDir : poolDir + "/";
24343
24754
  return resolved === poolDir || resolved.startsWith(poolPrefix);
24344
24755
  }
@@ -24355,12 +24766,12 @@ function reconcileAgentDefaultSkills(agentDir, optOuts = {}, defaults = getBuilt
24355
24766
  changed: false
24356
24767
  };
24357
24768
  const claudeDir = join7(agentDir, ".claude");
24358
- if (!existsSync9(claudeDir)) {
24769
+ if (!existsSync10(claudeDir)) {
24359
24770
  return result;
24360
24771
  }
24361
24772
  const targetDir = join7(claudeDir, "skills");
24362
- mkdirSync3(targetDir, { recursive: true });
24363
- if (!existsSync9(poolDir)) {
24773
+ mkdirSync4(targetDir, { recursive: true });
24774
+ if (!existsSync10(poolDir)) {
24364
24775
  warnMissingPoolDir(poolDir);
24365
24776
  return result;
24366
24777
  }
@@ -24378,7 +24789,7 @@ function reconcileAgentDefaultSkills(agentDir, optOuts = {}, defaults = getBuilt
24378
24789
  continue;
24379
24790
  }
24380
24791
  const src = join7(poolDir, entry.key);
24381
- if (!existsSync9(src)) {
24792
+ if (!existsSync10(src)) {
24382
24793
  result.missingFromPool.push(entry.key);
24383
24794
  warnMissingBuiltinDefault(poolDir, entry.key);
24384
24795
  if (isOwnedBundledLink(dest, poolDir)) {
@@ -24439,7 +24850,7 @@ function reconcileAgentDefaultSkills(agentDir, optOuts = {}, defaults = getBuilt
24439
24850
  continue;
24440
24851
  try {
24441
24852
  const st = lstatSync2(join7(poolDir, poolName));
24442
- if (st.isDirectory() && existsSync9(join7(poolDir, poolName, "SKILL.md"))) {
24853
+ if (st.isDirectory() && existsSync10(join7(poolDir, poolName, "SKILL.md"))) {
24443
24854
  legitimate.add(poolName);
24444
24855
  }
24445
24856
  } catch {}
@@ -24518,25 +24929,25 @@ function applyTelegramProgressGuidance(body, args) {
24518
24929
 
24519
24930
  // src/telegram/state.ts
24520
24931
  import {
24521
- readFileSync as readFileSync9,
24522
- writeFileSync as writeFileSync3,
24523
- mkdirSync as mkdirSync4,
24524
- existsSync as existsSync10,
24932
+ readFileSync as readFileSync10,
24933
+ writeFileSync as writeFileSync4,
24934
+ mkdirSync as mkdirSync5,
24935
+ existsSync as existsSync11,
24525
24936
  renameSync as renameSync2,
24526
24937
  copyFileSync as copyFileSync2,
24527
24938
  unlinkSync
24528
24939
  } from "node:fs";
24529
- import { basename as basename2, dirname as dirname3, resolve as resolve6 } from "node:path";
24940
+ import { basename as basename2, dirname as dirname4, resolve as resolve6 } from "node:path";
24530
24941
  function defaultStatePath() {
24531
24942
  return resolveStatePath("topics.json");
24532
24943
  }
24533
24944
  function loadTopicState(statePath) {
24534
24945
  const path = statePath ?? defaultStatePath();
24535
- if (!existsSync10(path)) {
24946
+ if (!existsSync11(path)) {
24536
24947
  return { topics: {} };
24537
24948
  }
24538
24949
  try {
24539
- const raw = readFileSync9(path, "utf-8");
24950
+ const raw = readFileSync10(path, "utf-8");
24540
24951
  const parsed = JSON.parse(raw);
24541
24952
  if (parsed && typeof parsed === "object" && parsed.topics && typeof parsed.topics === "object") {
24542
24953
  return parsed;
@@ -24553,19 +24964,19 @@ function loadTopicState(statePath) {
24553
24964
  }
24554
24965
  function saveTopicState(state, statePath) {
24555
24966
  const path = statePath ?? defaultStatePath();
24556
- const dir = dirname3(path);
24557
- if (!existsSync10(dir)) {
24558
- mkdirSync4(dir, { recursive: true });
24967
+ const dir = dirname4(path);
24968
+ if (!existsSync11(dir)) {
24969
+ mkdirSync5(dir, { recursive: true });
24559
24970
  }
24560
24971
  const absDir = resolve6(dir);
24561
24972
  const tmp = resolve6(absDir, `.${basename2(path)}.${process.pid}.${Date.now()}.tmp`);
24562
24973
  try {
24563
- writeFileSync3(tmp, JSON.stringify(state, null, 2) + `
24974
+ writeFileSync4(tmp, JSON.stringify(state, null, 2) + `
24564
24975
  `, "utf-8");
24565
24976
  renameSync2(tmp, path);
24566
24977
  } catch (err) {
24567
24978
  try {
24568
- if (existsSync10(tmp))
24979
+ if (existsSync11(tmp))
24569
24980
  unlinkSync(tmp);
24570
24981
  } catch {}
24571
24982
  throw err;
@@ -24577,13 +24988,13 @@ var init_state = __esm(() => {
24577
24988
 
24578
24989
  // src/vault/flock.ts
24579
24990
  import {
24580
- existsSync as existsSync11,
24991
+ existsSync as existsSync12,
24581
24992
  openSync as openSync3,
24582
24993
  closeSync as closeSync3,
24583
24994
  writeSync as writeSync2,
24584
24995
  fsyncSync as fsyncSync2,
24585
24996
  unlinkSync as unlinkSync2,
24586
- readFileSync as readFileSync10,
24997
+ readFileSync as readFileSync11,
24587
24998
  readdirSync as readdirSync5,
24588
24999
  rmdirSync,
24589
25000
  statSync as statSync5,
@@ -24594,7 +25005,7 @@ function lockPathFor(vaultPath) {
24594
25005
  }
24595
25006
  function readLockHolder(lockPath) {
24596
25007
  try {
24597
- const raw = readFileSync10(lockPath, "utf8");
25008
+ const raw = readFileSync11(lockPath, "utf8");
24598
25009
  const lines = raw.split(`
24599
25010
  `);
24600
25011
  const pid = Number.parseInt(lines[0] ?? "", 10);
@@ -24610,7 +25021,7 @@ function readLockHolder(lockPath) {
24610
25021
  }
24611
25022
  function pidIsLive(pid) {
24612
25023
  if (process.platform === "linux") {
24613
- return existsSync11(`/proc/${pid}`);
25024
+ return existsSync12(`/proc/${pid}`);
24614
25025
  }
24615
25026
  try {
24616
25027
  process.kill(pid, 0);
@@ -24646,8 +25057,8 @@ function pidStartTimeMs(pid) {
24646
25057
  if (process.platform !== "linux")
24647
25058
  return null;
24648
25059
  try {
24649
- const statLine = readFileSync10(`/proc/${pid}/stat`, "utf8");
24650
- const procStat = readFileSync10("/proc/stat", "utf8");
25060
+ const statLine = readFileSync11(`/proc/${pid}/stat`, "utf8");
25061
+ const procStat = readFileSync11("/proc/stat", "utf8");
24651
25062
  return parseProcStartTimeMs(statLine, procStat, Date.now());
24652
25063
  } catch {
24653
25064
  return null;
@@ -24670,7 +25081,7 @@ function lockFileMtimeIsOlderThan(lockPath, budgetMs) {
24670
25081
  }
24671
25082
  function clearStaleSentinelDir(lockPath) {
24672
25083
  try {
24673
- if (!existsSync11(lockPath))
25084
+ if (!existsSync12(lockPath))
24674
25085
  return true;
24675
25086
  const s = statSync5(lockPath);
24676
25087
  if (!s.isDirectory())
@@ -24813,11 +25224,11 @@ __export(exports_vault, {
24813
25224
  });
24814
25225
  import { randomBytes as randomBytes2, scryptSync, createCipheriv, createDecipheriv } from "node:crypto";
24815
25226
  import {
24816
- readFileSync as readFileSync11,
25227
+ readFileSync as readFileSync12,
24817
25228
  writeSync as writeSync3,
24818
- existsSync as existsSync12,
25229
+ existsSync as existsSync13,
24819
25230
  renameSync as renameSync3,
24820
- mkdirSync as mkdirSync5,
25231
+ mkdirSync as mkdirSync6,
24821
25232
  unlinkSync as unlinkSync3,
24822
25233
  fsyncSync as fsyncSync3,
24823
25234
  openSync as openSync4,
@@ -24825,15 +25236,15 @@ import {
24825
25236
  lstatSync as lstatSync3,
24826
25237
  realpathSync as realpathSync2
24827
25238
  } from "node:fs";
24828
- import { dirname as dirname4, basename as basename3, resolve as resolve7 } from "node:path";
25239
+ import { dirname as dirname5, basename as basename3, resolve as resolve7 } from "node:path";
24829
25240
  function atomicWriteFileSync2(path, data, mode) {
24830
25241
  let effectivePath = path;
24831
25242
  try {
24832
- if (existsSync12(path) && lstatSync3(path).isSymbolicLink()) {
25243
+ if (existsSync13(path) && lstatSync3(path).isSymbolicLink()) {
24833
25244
  effectivePath = realpathSync2(path);
24834
25245
  }
24835
25246
  } catch {}
24836
- const dir = dirname4(resolve7(effectivePath));
25247
+ const dir = dirname5(resolve7(effectivePath));
24837
25248
  const tmp = resolve7(dir, `.${basename3(effectivePath)}.${process.pid}.${Date.now()}.tmp`);
24838
25249
  try {
24839
25250
  const fd = openSync4(tmp, "wx", mode);
@@ -24854,7 +25265,7 @@ function atomicWriteFileSync2(path, data, mode) {
24854
25265
  } catch {}
24855
25266
  } catch (err) {
24856
25267
  try {
24857
- if (existsSync12(tmp))
25268
+ if (existsSync13(tmp))
24858
25269
  unlinkSync3(tmp);
24859
25270
  } catch {}
24860
25271
  throw err;
@@ -24967,12 +25378,12 @@ function detectFormat(value) {
24967
25378
  return null;
24968
25379
  }
24969
25380
  function createVault(passphrase, vaultPath) {
24970
- if (existsSync12(vaultPath)) {
25381
+ if (existsSync13(vaultPath)) {
24971
25382
  throw new VaultError(`Vault file already exists: ${vaultPath}`);
24972
25383
  }
24973
- const dir = dirname4(vaultPath);
24974
- if (!existsSync12(dir)) {
24975
- mkdirSync5(dir, { recursive: true, mode: 448 });
25384
+ const dir = dirname5(vaultPath);
25385
+ if (!existsSync13(dir)) {
25386
+ mkdirSync6(dir, { recursive: true, mode: 448 });
24976
25387
  }
24977
25388
  const salt = randomBytes2(16);
24978
25389
  const key = deriveKey(passphrase, salt);
@@ -24988,12 +25399,12 @@ function createVault(passphrase, vaultPath) {
24988
25399
  atomicWriteFileSync2(vaultPath, JSON.stringify(vaultFile, null, 2), 384);
24989
25400
  }
24990
25401
  function openVault(passphrase, vaultPath) {
24991
- if (!existsSync12(vaultPath)) {
25402
+ if (!existsSync13(vaultPath)) {
24992
25403
  throw new VaultError(`Vault file not found: ${vaultPath}`);
24993
25404
  }
24994
25405
  let vaultFile;
24995
25406
  try {
24996
- vaultFile = JSON.parse(readFileSync11(vaultPath, "utf8"));
25407
+ vaultFile = JSON.parse(readFileSync12(vaultPath, "utf8"));
24997
25408
  } catch {
24998
25409
  throw new VaultError(`Failed to read vault file: ${vaultPath}`);
24999
25410
  }
@@ -25015,7 +25426,7 @@ function openVault(passphrase, vaultPath) {
25015
25426
  return normalizeSecrets(vaultData.secrets ?? {});
25016
25427
  }
25017
25428
  function saveVault(passphrase, vaultPath, secrets) {
25018
- if (!existsSync12(vaultPath)) {
25429
+ if (!existsSync13(vaultPath)) {
25019
25430
  throw new VaultError(`Vault file not found: ${vaultPath}`);
25020
25431
  }
25021
25432
  let releaseLock = null;
@@ -25030,7 +25441,7 @@ function saveVault(passphrase, vaultPath, secrets) {
25030
25441
  try {
25031
25442
  let vaultFile;
25032
25443
  try {
25033
- vaultFile = JSON.parse(readFileSync11(vaultPath, "utf8"));
25444
+ vaultFile = JSON.parse(readFileSync12(vaultPath, "utf8"));
25034
25445
  } catch {
25035
25446
  throw new VaultError(`Failed to read vault file: ${vaultPath}`);
25036
25447
  }
@@ -25937,7 +26348,7 @@ __export(exports_resolver, {
25937
26348
  import {
25938
26349
  chmodSync,
25939
26350
  closeSync as closeSync5,
25940
- mkdirSync as mkdirSync6,
26351
+ mkdirSync as mkdirSync7,
25941
26352
  mkdtempSync,
25942
26353
  openSync as openSync5,
25943
26354
  rmSync as rmSync3,
@@ -25997,7 +26408,7 @@ function materializationRoot() {
25997
26408
  const xdg = process.env.XDG_RUNTIME_DIR;
25998
26409
  if (xdg) {
25999
26410
  const base = join9(xdg, "switchroom", "vault");
26000
- mkdirSync6(base, { recursive: true, mode: 448 });
26411
+ mkdirSync7(base, { recursive: true, mode: 448 });
26001
26412
  cachedRoot = mkdtempSync(join9(base, "run-"));
26002
26413
  } else {
26003
26414
  cachedRoot = mkdtempSync(join9(tmpdir(), "switchroom-vault-"));
@@ -26021,7 +26432,7 @@ function materializeFilesEntry(key, files) {
26021
26432
  rmSync3(dir, { recursive: true, force: true });
26022
26433
  } catch {}
26023
26434
  }
26024
- mkdirSync6(dir, { recursive: true, mode: 448 });
26435
+ mkdirSync7(dir, { recursive: true, mode: 448 });
26025
26436
  chmodSync(dir, 448);
26026
26437
  const st = statSync7(dir);
26027
26438
  if (typeof process.getuid === "function" && st.uid !== process.getuid()) {
@@ -26178,7 +26589,7 @@ var init_resolver = __esm(() => {
26178
26589
  });
26179
26590
 
26180
26591
  // src/setup/onboarding.ts
26181
- import { existsSync as existsSync14, copyFileSync as copyFileSync3, mkdirSync as mkdirSync7, writeFileSync as writeFileSync4, readFileSync as readFileSync13 } from "node:fs";
26592
+ import { existsSync as existsSync15, copyFileSync as copyFileSync3, mkdirSync as mkdirSync8, writeFileSync as writeFileSync5, readFileSync as readFileSync14 } from "node:fs";
26182
26593
  import { resolve as resolve8, join as join10 } from "node:path";
26183
26594
  function findExistingClaudeJson() {
26184
26595
  const home2 = process.env.HOME ?? "/root";
@@ -26190,7 +26601,7 @@ function findExistingClaudeJson() {
26190
26601
  resolve8(home2, ".claude", "config.json")
26191
26602
  ];
26192
26603
  for (const candidate of candidates) {
26193
- if (existsSync14(candidate)) {
26604
+ if (existsSync15(candidate)) {
26194
26605
  return candidate;
26195
26606
  }
26196
26607
  }
@@ -26202,9 +26613,9 @@ function findExistingClaudeJson() {
26202
26613
  }
26203
26614
  function copyOnboardingState(sourcePath, agentDir, configDirName = ".claude") {
26204
26615
  const claudeDir = join10(agentDir, configDirName);
26205
- mkdirSync7(claudeDir, { recursive: true });
26616
+ mkdirSync8(claudeDir, { recursive: true });
26206
26617
  const destPath = join10(claudeDir, ".claude.json");
26207
- if (!existsSync14(destPath)) {
26618
+ if (!existsSync15(destPath)) {
26208
26619
  copyFileSync3(sourcePath, destPath);
26209
26620
  }
26210
26621
  }
@@ -26227,18 +26638,18 @@ function buildAccessJson(userId, forumChatId, topicId, opts = {}) {
26227
26638
  }
26228
26639
  function writeAccessJson(agentDir, userId, forumChatId, topicId, opts = {}) {
26229
26640
  const telegramDir = join10(agentDir, "telegram");
26230
- mkdirSync7(telegramDir, { recursive: true });
26641
+ mkdirSync8(telegramDir, { recursive: true });
26231
26642
  const accessPath = join10(telegramDir, "access.json");
26232
- writeFileSync4(accessPath, buildAccessJson(userId, forumChatId, topicId, opts), {
26643
+ writeFileSync5(accessPath, buildAccessJson(userId, forumChatId, topicId, opts), {
26233
26644
  encoding: "utf-8",
26234
26645
  mode: 384
26235
26646
  });
26236
26647
  }
26237
26648
  function writeAgentEnv(agentDir, botToken) {
26238
26649
  const telegramDir = join10(agentDir, "telegram");
26239
- mkdirSync7(telegramDir, { recursive: true });
26650
+ mkdirSync8(telegramDir, { recursive: true });
26240
26651
  const envPath = join10(telegramDir, ".env");
26241
- writeFileSync4(envPath, `TELEGRAM_BOT_TOKEN=${botToken}
26652
+ writeFileSync5(envPath, `TELEGRAM_BOT_TOKEN=${botToken}
26242
26653
  `, {
26243
26654
  encoding: "utf-8",
26244
26655
  mode: 384
@@ -26250,12 +26661,12 @@ function userConfigPath() {
26250
26661
  function saveUserConfig(userId, username) {
26251
26662
  const configPath = userConfigPath();
26252
26663
  const dir = join10(configPath, "..");
26253
- mkdirSync7(dir, { recursive: true });
26664
+ mkdirSync8(dir, { recursive: true });
26254
26665
  const config = { userId };
26255
26666
  if (username) {
26256
26667
  config.username = username;
26257
26668
  }
26258
- writeFileSync4(configPath, JSON.stringify(config, null, 2) + `
26669
+ writeFileSync5(configPath, JSON.stringify(config, null, 2) + `
26259
26670
  `, {
26260
26671
  encoding: "utf-8",
26261
26672
  mode: 384
@@ -26263,11 +26674,11 @@ function saveUserConfig(userId, username) {
26263
26674
  }
26264
26675
  function loadUserConfig() {
26265
26676
  const configPath = userConfigPath();
26266
- if (!existsSync14(configPath)) {
26677
+ if (!existsSync15(configPath)) {
26267
26678
  return null;
26268
26679
  }
26269
26680
  try {
26270
- const raw = readFileSync13(configPath, "utf-8");
26681
+ const raw = readFileSync14(configPath, "utf-8");
26271
26682
  const parsed = JSON.parse(raw);
26272
26683
  if (parsed && typeof parsed === "object") {
26273
26684
  return parsed;
@@ -26279,11 +26690,11 @@ function loadUserConfig() {
26279
26690
  }
26280
26691
  function preTrustWorkspace(agentDir, configDirName = ".claude") {
26281
26692
  const configPath = join10(agentDir, configDirName, ".claude.json");
26282
- if (!existsSync14(configPath)) {
26693
+ if (!existsSync15(configPath)) {
26283
26694
  return;
26284
26695
  }
26285
26696
  try {
26286
- const raw = readFileSync13(configPath, "utf-8");
26697
+ const raw = readFileSync14(configPath, "utf-8");
26287
26698
  const config = JSON.parse(raw);
26288
26699
  if (!config.projects) {
26289
26700
  config.projects = {};
@@ -26295,7 +26706,7 @@ function preTrustWorkspace(agentDir, configDirName = ".claude") {
26295
26706
  allowedTools: []
26296
26707
  };
26297
26708
  }
26298
- writeFileSync4(configPath, JSON.stringify(config, null, 2) + `
26709
+ writeFileSync5(configPath, JSON.stringify(config, null, 2) + `
26299
26710
  `, {
26300
26711
  encoding: "utf-8",
26301
26712
  mode: 384
@@ -26304,11 +26715,11 @@ function preTrustWorkspace(agentDir, configDirName = ".claude") {
26304
26715
  }
26305
26716
  function ensureMcpServersTrusted(agentDir, serverKeys, configDirName = ".claude") {
26306
26717
  const configPath = join10(agentDir, configDirName, ".claude.json");
26307
- if (!existsSync14(configPath)) {
26718
+ if (!existsSync15(configPath)) {
26308
26719
  return;
26309
26720
  }
26310
26721
  try {
26311
- const raw = readFileSync13(configPath, "utf-8");
26722
+ const raw = readFileSync14(configPath, "utf-8");
26312
26723
  const config = JSON.parse(raw);
26313
26724
  if (!config.projects) {
26314
26725
  config.projects = {};
@@ -26322,7 +26733,7 @@ function ensureMcpServersTrusted(agentDir, serverKeys, configDirName = ".claude"
26322
26733
  const existing = Array.isArray(project.enabledMcpjsonServers) ? project.enabledMcpjsonServers : [];
26323
26734
  project.enabledMcpjsonServers = Array.from(new Set([...existing, ...serverKeys]));
26324
26735
  config.projects[absDir] = project;
26325
- writeFileSync4(configPath, JSON.stringify(config, null, 2) + `
26736
+ writeFileSync5(configPath, JSON.stringify(config, null, 2) + `
26326
26737
  `, {
26327
26738
  encoding: "utf-8",
26328
26739
  mode: 384
@@ -26333,14 +26744,14 @@ function ensureMcpServersTrusted(agentDir, serverKeys, configDirName = ".claude"
26333
26744
  }
26334
26745
  function createMinimalClaudeConfig(agentDir, configDirName = ".claude") {
26335
26746
  const claudeDir = join10(agentDir, configDirName);
26336
- mkdirSync7(claudeDir, { recursive: true });
26747
+ mkdirSync8(claudeDir, { recursive: true });
26337
26748
  const configPath = join10(claudeDir, ".claude.json");
26338
- if (!existsSync14(configPath)) {
26749
+ if (!existsSync15(configPath)) {
26339
26750
  const minimal = {
26340
26751
  hasCompletedOnboarding: true,
26341
26752
  numStartups: 1
26342
26753
  };
26343
- writeFileSync4(configPath, JSON.stringify(minimal, null, 2) + `
26754
+ writeFileSync5(configPath, JSON.stringify(minimal, null, 2) + `
26344
26755
  `, {
26345
26756
  encoding: "utf-8",
26346
26757
  mode: 384
@@ -26351,8 +26762,8 @@ function seedCronConfigDir(agentDir, serverKeys) {
26351
26762
  const mainConfig = join10(agentDir, ".claude", ".claude.json");
26352
26763
  const cronDir = join10(agentDir, ".claude-cron");
26353
26764
  const cronConfig = join10(cronDir, ".claude.json");
26354
- mkdirSync7(cronDir, { recursive: true });
26355
- if (existsSync14(mainConfig)) {
26765
+ mkdirSync8(cronDir, { recursive: true });
26766
+ if (existsSync15(mainConfig)) {
26356
26767
  try {
26357
26768
  copyFileSync3(mainConfig, cronConfig);
26358
26769
  } catch {
@@ -26370,16 +26781,16 @@ var init_onboarding = __esm(() => {
26370
26781
 
26371
26782
  // src/repos/bare-clone.ts
26372
26783
  import { execFileSync as execFileSync4 } from "node:child_process";
26373
- import { existsSync as existsSync15, mkdirSync as mkdirSync8 } from "node:fs";
26784
+ import { existsSync as existsSync16, mkdirSync as mkdirSync9 } from "node:fs";
26374
26785
  import { resolve as resolve9 } from "node:path";
26375
26786
  function bareClonePath(slug) {
26376
26787
  return resolveStatePath(`repos/${slug}.git`);
26377
26788
  }
26378
26789
  function ensureBareClone(slug, url) {
26379
26790
  const reposDir = resolveStatePath("repos");
26380
- mkdirSync8(reposDir, { recursive: true });
26791
+ mkdirSync9(reposDir, { recursive: true });
26381
26792
  const clonePath = bareClonePath(slug);
26382
- if (!existsSync15(clonePath)) {
26793
+ if (!existsSync16(clonePath)) {
26383
26794
  process.stderr.write(`[switchroom] repo "${slug}": cloning ${url} \u2026
26384
26795
  `);
26385
26796
  execFileSync4("git", ["clone", "--bare", url, clonePath], {
@@ -26409,7 +26820,7 @@ var init_bare_clone = __esm(() => {
26409
26820
 
26410
26821
  // src/repos/agent-worktree.ts
26411
26822
  import { execFileSync as execFileSync5 } from "node:child_process";
26412
- import { existsSync as existsSync16, mkdirSync as mkdirSync9, readdirSync as readdirSync6 } from "node:fs";
26823
+ import { existsSync as existsSync17, mkdirSync as mkdirSync10, readdirSync as readdirSync6 } from "node:fs";
26413
26824
  import { join as join11, resolve as resolve10 } from "node:path";
26414
26825
  function agentBranchName(agentName) {
26415
26826
  return `agent/${agentName}/main`;
@@ -26468,8 +26879,8 @@ function ensureAgentWorktree(agentName, slug, bareClonePath2, agentDir) {
26468
26879
  const worktreePath = agentWorktreePath(agentDir, slug);
26469
26880
  const branch = agentBranchName(agentName);
26470
26881
  const defaultBranch = resolveDefaultBranch(bareClonePath2);
26471
- if (!existsSync16(worktreePath)) {
26472
- mkdirSync9(join11(agentDir, "work"), { recursive: true });
26882
+ if (!existsSync17(worktreePath)) {
26883
+ mkdirSync10(join11(agentDir, "work"), { recursive: true });
26473
26884
  if (branchExistsInBare(bareClonePath2, branch)) {
26474
26885
  execFileSync5("git", ["worktree", "add", worktreePath, branch], {
26475
26886
  cwd: bareClonePath2,
@@ -26524,7 +26935,7 @@ function ensureAgentWorktree(agentName, slug, bareClonePath2, agentDir) {
26524
26935
  function removeAgentWorktree(agentName, slug, bareClonePath2, agentDir) {
26525
26936
  const worktreePath = agentWorktreePath(agentDir, slug);
26526
26937
  const branch = agentBranchName(agentName);
26527
- if (existsSync16(worktreePath)) {
26938
+ if (existsSync17(worktreePath)) {
26528
26939
  try {
26529
26940
  execFileSync5("git", ["worktree", "remove", "--force", worktreePath], {
26530
26941
  cwd: bareClonePath2,
@@ -26561,11 +26972,11 @@ var init_agent_worktree = () => {};
26561
26972
 
26562
26973
  // src/agents/scaffold.ts
26563
26974
  import {
26564
- existsSync as existsSync17,
26565
- mkdirSync as mkdirSync10,
26566
- writeFileSync as writeFileSync5,
26975
+ existsSync as existsSync18,
26976
+ mkdirSync as mkdirSync11,
26977
+ writeFileSync as writeFileSync6,
26567
26978
  appendFileSync as appendFileSync2,
26568
- readFileSync as readFileSync14,
26979
+ readFileSync as readFileSync15,
26569
26980
  renameSync as renameSync4,
26570
26981
  chmodSync as chmodSync2,
26571
26982
  symlinkSync as symlinkSync2,
@@ -26579,7 +26990,7 @@ import {
26579
26990
  } from "node:fs";
26580
26991
  import { homedir as homedir5 } from "node:os";
26581
26992
  import { execSync, execFileSync as execFileSync6 } from "node:child_process";
26582
- import { dirname as dirname5, isAbsolute as isAbsolute2, join as join12, relative as relative2, resolve as resolve11 } from "node:path";
26993
+ import { dirname as dirname6, isAbsolute as isAbsolute2, join as join12, relative as relative2, resolve as resolve11 } from "node:path";
26583
26994
  import { createHash as createHash4 } from "node:crypto";
26584
26995
  function prependReplyDiscipline(rendered, context) {
26585
26996
  const replyDiscipline = renderReplyDisciplineFragment(context);
@@ -26661,12 +27072,12 @@ function maybeWriteCronMcp(agentDir, mcpServers, cronSessionEnabled) {
26661
27072
  "switchroom-telegram": cronTelegram
26662
27073
  };
26663
27074
  const cronDir = join12(agentDir, ".claude-cron");
26664
- mkdirSync10(cronDir, { recursive: true });
27075
+ mkdirSync11(cronDir, { recursive: true });
26665
27076
  const path = join12(cronDir, ".mcp.json");
26666
27077
  const content = JSON.stringify({ mcpServers: cronServers }, null, 2) + `
26667
27078
  `;
26668
- if (!existsSync17(path) || readFileSync14(path, "utf-8") !== content) {
26669
- writeFileSync5(path, content, { encoding: "utf-8", mode: 384 });
27079
+ if (!existsSync18(path) || readFileSync15(path, "utf-8") !== content) {
27080
+ writeFileSync6(path, content, { encoding: "utf-8", mode: 384 });
26670
27081
  }
26671
27082
  seedCronConfigDir(agentDir, Object.keys(cronServers));
26672
27083
  return path;
@@ -26677,13 +27088,13 @@ function alignAgentUid(name, agentDir, uid, opts = {}) {
26677
27088
  const auditDir = join12(homedir5(), ".switchroom", "audit", name);
26678
27089
  const configMirrorDir = join12(homedir5(), ".switchroom-config", "agents", name, "personal-skills");
26679
27090
  const paths = [];
26680
- if (existsSync17(agentDir))
27091
+ if (existsSync18(agentDir))
26681
27092
  paths.push(agentDir);
26682
- if (existsSync17(logsDir))
27093
+ if (existsSync18(logsDir))
26683
27094
  paths.push(logsDir);
26684
- if (existsSync17(auditDir))
27095
+ if (existsSync18(auditDir))
26685
27096
  paths.push(auditDir);
26686
- if (existsSync17(configMirrorDir))
27097
+ if (existsSync18(configMirrorDir))
26687
27098
  paths.push(configMirrorDir);
26688
27099
  if (paths.length === 0)
26689
27100
  return { chowned: false, paths: [] };
@@ -26700,7 +27111,7 @@ function alignAgentUid(name, agentDir, uid, opts = {}) {
26700
27111
  return { chowned: false, paths };
26701
27112
  try {
26702
27113
  const logPath = join12(homedir5(), ".switchroom", ".uid-alignment.log");
26703
- mkdirSync10(join12(homedir5(), ".switchroom"), { recursive: true });
27114
+ mkdirSync11(join12(homedir5(), ".switchroom"), { recursive: true });
26704
27115
  const ts = new Date().toISOString();
26705
27116
  for (const prior of priors) {
26706
27117
  if (prior.uid !== undefined && prior.gid !== undefined && (prior.uid !== uid || prior.gid !== uid)) {
@@ -26756,16 +27167,16 @@ function refreshTelegramBotTokenEnv(agentDir, resolvedBotToken, changes) {
26756
27167
  if (!resolvedBotToken)
26757
27168
  return;
26758
27169
  const envPath = join12(agentDir, "telegram", ".env");
26759
- if (!existsSync17(envPath))
27170
+ if (!existsSync18(envPath))
26760
27171
  return;
26761
- const current = readFileSync14(envPath, "utf-8");
27172
+ const current = readFileSync15(envPath, "utf-8");
26762
27173
  if (!isTelegramEnvPlaceholder(current))
26763
27174
  return;
26764
27175
  const next = `TELEGRAM_BOT_TOKEN=${resolvedBotToken}
26765
27176
  `;
26766
27177
  if (next === current)
26767
27178
  return;
26768
- writeFileSync5(envPath, next, { encoding: "utf-8", mode: 384 });
27179
+ writeFileSync6(envPath, next, { encoding: "utf-8", mode: 384 });
26769
27180
  changes.push(envPath);
26770
27181
  }
26771
27182
  function stripOfficialTelegramPlugin(payload) {
@@ -26793,10 +27204,10 @@ function setupPlugins(agentDir, useSwitchroomPlugin = false) {
26793
27204
  const globalPluginsDir = join12(home2, ".claude", "plugins");
26794
27205
  const agentPluginsDir = join12(agentDir, ".claude", "plugins");
26795
27206
  const agentMarketplacesDir = join12(agentPluginsDir, "marketplaces");
26796
- mkdirSync10(agentMarketplacesDir, { recursive: true });
27207
+ mkdirSync11(agentMarketplacesDir, { recursive: true });
26797
27208
  const globalMarketplace = join12(globalPluginsDir, "marketplaces", "claude-plugins-official");
26798
27209
  const agentMarketplace = join12(agentMarketplacesDir, "claude-plugins-official");
26799
- if (existsSync17(globalMarketplace) && !existsSync17(agentMarketplace)) {
27210
+ if (existsSync18(globalMarketplace) && !existsSync18(agentMarketplace)) {
26800
27211
  try {
26801
27212
  symlinkSync2(globalMarketplace, agentMarketplace);
26802
27213
  } catch {}
@@ -26805,11 +27216,11 @@ function setupPlugins(agentDir, useSwitchroomPlugin = false) {
26805
27216
  for (const file of configFiles) {
26806
27217
  const globalFile = join12(globalPluginsDir, file);
26807
27218
  const agentFile = join12(agentPluginsDir, file);
26808
- if (existsSync17(globalFile) && !existsSync17(agentFile)) {
27219
+ if (existsSync18(globalFile) && !existsSync18(agentFile)) {
26809
27220
  try {
26810
27221
  if (useSwitchroomPlugin && file === "installed_plugins.json") {
26811
- const scrubbed = stripOfficialTelegramPlugin(readFileSync14(globalFile, "utf8"));
26812
- writeFileSync5(agentFile, scrubbed);
27222
+ const scrubbed = stripOfficialTelegramPlugin(readFileSync15(globalFile, "utf8"));
27223
+ writeFileSync6(agentFile, scrubbed);
26813
27224
  } else {
26814
27225
  copyFileSync4(globalFile, agentFile);
26815
27226
  }
@@ -26820,9 +27231,9 @@ function setupPlugins(agentDir, useSwitchroomPlugin = false) {
26820
27231
  function webkiteBinaryAvailable() {
26821
27232
  const override = process.env.SWITCHROOM_WEBKITE_BINARY;
26822
27233
  if (override !== undefined && override !== "") {
26823
- return existsSync17(override);
27234
+ return existsSync18(override);
26824
27235
  }
26825
- return existsSync17(join12(homedir5(), ".switchroom", "bin", "webkite")) || existsSync17(WEBKITE_BINARY_CONTAINER_PATH);
27236
+ return existsSync18(join12(homedir5(), ".switchroom", "bin", "webkite")) || existsSync18(WEBKITE_BINARY_CONTAINER_PATH);
26826
27237
  }
26827
27238
  function webkiteDenyForAgent(agentConfig) {
26828
27239
  if (agentConfig.mcp_servers?.["webkite"] === false)
@@ -26875,9 +27286,9 @@ function toHostHomePath(p) {
26875
27286
  return p;
26876
27287
  }
26877
27288
  function composeWithSidecar(renderedBase, sidecarPath) {
26878
- if (!existsSync17(sidecarPath))
27289
+ if (!existsSync18(sidecarPath))
26879
27290
  return renderedBase;
26880
- const sidecar = readFileSync14(sidecarPath, "utf-8").trimEnd();
27291
+ const sidecar = readFileSync15(sidecarPath, "utf-8").trimEnd();
26881
27292
  if (sidecar.length === 0)
26882
27293
  return renderedBase;
26883
27294
  return `${renderedBase.trimEnd()}
@@ -26895,8 +27306,8 @@ function resolveClaudeMdYoursSection(existingFileContent, sidecarPath) {
26895
27306
  return afterMarker.trimEnd();
26896
27307
  }
26897
27308
  }
26898
- if (existsSync17(sidecarPath)) {
26899
- const sidecar = readFileSync14(sidecarPath, "utf-8").trimEnd();
27309
+ if (existsSync18(sidecarPath)) {
27310
+ const sidecar = readFileSync15(sidecarPath, "utf-8").trimEnd();
26900
27311
  if (sidecar.length > 0) {
26901
27312
  const deprecatedPath = sidecarPath + ".deprecated";
26902
27313
  try {
@@ -26919,7 +27330,7 @@ ${yours.trimEnd()}
26919
27330
  }
26920
27331
  function renderSoulMd(profilePath, agentWorkspaceDir, soul) {
26921
27332
  const soulMdSrc = join12(profilePath, "workspace", "SOUL.md.hbs");
26922
- if (!existsSync17(soulMdSrc))
27333
+ if (!existsSync18(soulMdSrc))
26923
27334
  return null;
26924
27335
  const rendered = renderTemplate(soulMdSrc, { soul });
26925
27336
  const customSoulPath = join12(agentWorkspaceDir, "SOUL.custom.md");
@@ -27007,7 +27418,7 @@ function migrateLegacySkillsDir(agentDir, skillsPool) {
27007
27418
  } catch {
27008
27419
  continue;
27009
27420
  }
27010
- const resolved = target ? isAbsolute2(target) ? target : resolve11(dirname5(entryPath), target) : null;
27421
+ const resolved = target ? isAbsolute2(target) ? target : resolve11(dirname6(entryPath), target) : null;
27011
27422
  if (resolved && resolved.startsWith(skillsPool)) {
27012
27423
  try {
27013
27424
  rmSync4(entryPath, { force: true });
@@ -27018,7 +27429,7 @@ function migrateLegacySkillsDir(agentDir, skillsPool) {
27018
27429
  function syncGlobalSkills(agentDir, declared, skillsDirOverride) {
27019
27430
  const skillsPool = resolveSkillsPoolDir(skillsDirOverride);
27020
27431
  const agentSkillsDir = join12(agentDir, ".claude", "skills");
27021
- mkdirSync10(agentSkillsDir, { recursive: true });
27432
+ mkdirSync11(agentSkillsDir, { recursive: true });
27022
27433
  migrateLegacySkillsDir(agentDir, skillsPool);
27023
27434
  for (const name of declared) {
27024
27435
  if (!name || name.includes("/") || name === "." || name === "..") {
@@ -27027,11 +27438,11 @@ function syncGlobalSkills(agentDir, declared, skillsDirOverride) {
27027
27438
  }
27028
27439
  const src = join12(skillsPool, name);
27029
27440
  const dest = join12(agentSkillsDir, name);
27030
- if (!existsSync17(src)) {
27441
+ if (!existsSync18(src)) {
27031
27442
  console.warn(` WARNING: skill "${name}" not found in pool (${skillsPool}) \u2014 skipping`);
27032
27443
  continue;
27033
27444
  }
27034
- const relTarget = relative2(dirname5(dest), src);
27445
+ const relTarget = relative2(dirname6(dest), src);
27035
27446
  let linkStat;
27036
27447
  try {
27037
27448
  linkStat = lstatSync4(dest);
@@ -27047,7 +27458,7 @@ function syncGlobalSkills(agentDir, declared, skillsDirOverride) {
27047
27458
  if (target === relTarget) {
27048
27459
  continue;
27049
27460
  }
27050
- const resolved = target ? isAbsolute2(target) ? target : resolve11(dirname5(dest), target) : null;
27461
+ const resolved = target ? isAbsolute2(target) ? target : resolve11(dirname6(dest), target) : null;
27051
27462
  if (resolved && resolved.startsWith(skillsPool)) {
27052
27463
  try {
27053
27464
  rmSync4(dest, { force: true });
@@ -27076,7 +27487,7 @@ function syncGlobalSkills(agentDir, declared, skillsDirOverride) {
27076
27487
  } catch {
27077
27488
  continue;
27078
27489
  }
27079
- const resolved = linkTarget ? isAbsolute2(linkTarget) ? linkTarget : resolve11(dirname5(entryPath), linkTarget) : null;
27490
+ const resolved = linkTarget ? isAbsolute2(linkTarget) ? linkTarget : resolve11(dirname6(entryPath), linkTarget) : null;
27080
27491
  if (resolved && resolved.includes("/.switchroom/skills/_bundled/")) {
27081
27492
  continue;
27082
27493
  }
@@ -27087,13 +27498,13 @@ function syncGlobalSkills(agentDir, declared, skillsDirOverride) {
27087
27498
  }
27088
27499
  function installSwitchroomSkills(agentDir, opts = {}) {
27089
27500
  const builtinSkillsDir = resolve11(homedir5(), ".switchroom/skills/_bundled");
27090
- if (!existsSync17(builtinSkillsDir)) {
27501
+ if (!existsSync18(builtinSkillsDir)) {
27091
27502
  process.stderr.write(`switchroom: bundled skills pool dir not found at ${builtinSkillsDir} \u2014 run \`switchroom update\` to install it.
27092
27503
  `);
27093
27504
  return;
27094
27505
  }
27095
27506
  const targetDir = join12(agentDir, ".claude", "skills");
27096
- mkdirSync10(targetDir, { recursive: true });
27507
+ mkdirSync11(targetDir, { recursive: true });
27097
27508
  let entries;
27098
27509
  try {
27099
27510
  entries = readdirSync7(builtinSkillsDir);
@@ -27113,7 +27524,7 @@ function installSwitchroomSkills(agentDir, opts = {}) {
27113
27524
  const src = join12(builtinSkillsDir, name);
27114
27525
  try {
27115
27526
  const st = lstatSync4(src);
27116
- return st.isDirectory() && existsSync17(join12(src, "SKILL.md"));
27527
+ return st.isDirectory() && existsSync18(join12(src, "SKILL.md"));
27117
27528
  } catch {
27118
27529
  return false;
27119
27530
  }
@@ -27135,7 +27546,7 @@ function installSwitchroomSkills(agentDir, opts = {}) {
27135
27546
  } catch {}
27136
27547
  if (!currentTarget)
27137
27548
  continue;
27138
- const resolvedTarget = isAbsolute2(currentTarget) ? currentTarget : resolve11(dirname5(dest), currentTarget);
27549
+ const resolvedTarget = isAbsolute2(currentTarget) ? currentTarget : resolve11(dirname6(dest), currentTarget);
27139
27550
  if (resolvedTarget !== join12(builtinSkillsDir, name))
27140
27551
  continue;
27141
27552
  try {
@@ -27147,7 +27558,7 @@ function installSwitchroomSkills(agentDir, opts = {}) {
27147
27558
  for (const name of switchroomSkillNames) {
27148
27559
  const src = join12(builtinSkillsDir, name);
27149
27560
  const dest = join12(targetDir, name);
27150
- const relTarget = relative2(dirname5(dest), src);
27561
+ const relTarget = relative2(dirname6(dest), src);
27151
27562
  let existing;
27152
27563
  try {
27153
27564
  existing = lstatSync4(dest);
@@ -27289,9 +27700,9 @@ function filterMcpServers(servers) {
27289
27700
  return Object.keys(out).length > 0 ? out : undefined;
27290
27701
  }
27291
27702
  function copyDirRecursive2(src, dest) {
27292
- if (!existsSync17(src))
27703
+ if (!existsSync18(src))
27293
27704
  return;
27294
- mkdirSync10(dest, { recursive: true });
27705
+ mkdirSync11(dest, { recursive: true });
27295
27706
  for (const entry of readdirSync7(src)) {
27296
27707
  const srcPath = join12(src, entry);
27297
27708
  const destPath = join12(dest, entry);
@@ -27307,17 +27718,17 @@ function copyDirRecursive2(src, dest) {
27307
27718
  }
27308
27719
  }
27309
27720
  function writeFileSyncIfChanged(filePath, content, mode) {
27310
- if (existsSync17(filePath)) {
27721
+ if (existsSync18(filePath)) {
27311
27722
  try {
27312
- if (readFileSync14(filePath, "utf-8") === content)
27723
+ if (readFileSync15(filePath, "utf-8") === content)
27313
27724
  return false;
27314
27725
  } catch {}
27315
27726
  }
27316
- writeFileSync5(filePath, content, mode !== undefined ? { encoding: "utf-8", mode } : "utf-8");
27727
+ writeFileSync6(filePath, content, mode !== undefined ? { encoding: "utf-8", mode } : "utf-8");
27317
27728
  return true;
27318
27729
  }
27319
27730
  function dirContentEquals(src, dest, topLevelOverrides) {
27320
- if (!existsSync17(src) || !existsSync17(dest))
27731
+ if (!existsSync18(src) || !existsSync18(dest))
27321
27732
  return false;
27322
27733
  let srcEntries;
27323
27734
  let destEntries;
@@ -27355,9 +27766,9 @@ function dirContentEquals(src, dest, topLevelOverrides) {
27355
27766
  try {
27356
27767
  const expected = topLevelOverrides?.[entry];
27357
27768
  if (expected !== undefined) {
27358
- if (readFileSync14(d, "utf-8") !== expected)
27769
+ if (readFileSync15(d, "utf-8") !== expected)
27359
27770
  return false;
27360
- } else if (!readFileSync14(s).equals(readFileSync14(d))) {
27771
+ } else if (!readFileSync15(s).equals(readFileSync15(d))) {
27361
27772
  return false;
27362
27773
  }
27363
27774
  } catch {
@@ -27369,14 +27780,14 @@ function dirContentEquals(src, dest, topLevelOverrides) {
27369
27780
  }
27370
27781
  function seedWorkspaceBootstrapFiles(params) {
27371
27782
  const profileWorkspaceDir = join12(params.profilePath, "workspace");
27372
- if (!existsSync17(profileWorkspaceDir)) {
27783
+ if (!existsSync18(profileWorkspaceDir)) {
27373
27784
  return;
27374
27785
  }
27375
27786
  const agentWorkspaceDir = join12(params.agentDir, "workspace");
27376
- mkdirSync10(agentWorkspaceDir, { recursive: true });
27787
+ mkdirSync11(agentWorkspaceDir, { recursive: true });
27377
27788
  const walk = (relDir) => {
27378
27789
  const srcDir = join12(profileWorkspaceDir, relDir);
27379
- if (!existsSync17(srcDir))
27790
+ if (!existsSync18(srcDir))
27380
27791
  return;
27381
27792
  for (const entry of readdirSync7(srcDir)) {
27382
27793
  if (entry.startsWith(".") && entry !== ".gitkeep")
@@ -27385,7 +27796,7 @@ function seedWorkspaceBootstrapFiles(params) {
27385
27796
  const srcPath = join12(profileWorkspaceDir, relPath);
27386
27797
  const srcStat = statSync8(srcPath);
27387
27798
  if (srcStat.isDirectory()) {
27388
- mkdirSync10(join12(agentWorkspaceDir, relPath), { recursive: true });
27799
+ mkdirSync11(join12(agentWorkspaceDir, relPath), { recursive: true });
27389
27800
  walk(relPath);
27390
27801
  continue;
27391
27802
  }
@@ -27400,10 +27811,10 @@ function seedWorkspaceBootstrapFiles(params) {
27400
27811
  const destPath = join12(agentWorkspaceDir, destRel);
27401
27812
  if (destRel === "SOUL.default.md") {
27402
27813
  const rendered = renderTemplate(srcPath, params.context);
27403
- const existed = existsSync17(destPath);
27404
- const current = existed ? readFileSync14(destPath, "utf8") : null;
27814
+ const existed = existsSync18(destPath);
27815
+ const current = existed ? readFileSync15(destPath, "utf8") : null;
27405
27816
  if (current !== rendered) {
27406
- writeFileSync5(destPath, rendered);
27817
+ writeFileSync6(destPath, rendered);
27407
27818
  params.created.push(destPath);
27408
27819
  } else {
27409
27820
  params.skipped.push(destPath);
@@ -27419,7 +27830,7 @@ function seedWorkspaceBootstrapFiles(params) {
27419
27830
  writeIfMissing(destPath, renderFn, params.created, params.skipped);
27420
27831
  } else {
27421
27832
  const destPath = join12(agentWorkspaceDir, relPath);
27422
- if (!existsSync17(destPath)) {
27833
+ if (!existsSync18(destPath)) {
27423
27834
  copyFileSync4(srcPath, destPath);
27424
27835
  params.created.push(destPath);
27425
27836
  } else {
@@ -27433,27 +27844,27 @@ function seedWorkspaceBootstrapFiles(params) {
27433
27844
  function migrateLegacyAgentsMdIfPresent(agentWorkspaceDir, created) {
27434
27845
  const agentsMd = join12(agentWorkspaceDir, "AGENTS.md");
27435
27846
  const claudeMd = join12(agentWorkspaceDir, "CLAUDE.md");
27436
- if (!existsSync17(agentsMd))
27847
+ if (!existsSync18(agentsMd))
27437
27848
  return;
27438
27849
  const stat = lstatSync4(agentsMd);
27439
27850
  if (stat.isSymbolicLink())
27440
27851
  return;
27441
- if (existsSync17(claudeMd)) {
27852
+ if (existsSync18(claudeMd)) {
27442
27853
  return;
27443
27854
  }
27444
- const content = readFileSync14(agentsMd, "utf-8");
27445
- writeFileSync5(claudeMd, content, "utf-8");
27855
+ const content = readFileSync15(agentsMd, "utf-8");
27856
+ writeFileSync6(claudeMd, content, "utf-8");
27446
27857
  rmSync4(agentsMd);
27447
27858
  created.push(claudeMd);
27448
27859
  console.log(source_default.dim(` migrated legacy workspace/AGENTS.md \u2192 workspace/CLAUDE.md (content preserved)`));
27449
27860
  }
27450
27861
  function ensureClaudeMdSymlinks(agentWorkspaceDir, changes) {
27451
27862
  const claudeMd = join12(agentWorkspaceDir, "CLAUDE.md");
27452
- if (!existsSync17(claudeMd))
27863
+ if (!existsSync18(claudeMd))
27453
27864
  return;
27454
27865
  for (const name of ["AGENTS.md", "AGENT.md"]) {
27455
27866
  const linkPath = join12(agentWorkspaceDir, name);
27456
- if (existsSync17(linkPath) || lstatExists(linkPath)) {
27867
+ if (existsSync18(linkPath) || lstatExists(linkPath)) {
27457
27868
  const stat = lstatSync4(linkPath);
27458
27869
  if (stat.isSymbolicLink()) {
27459
27870
  const target = readlinkSync4(linkPath);
@@ -27484,7 +27895,7 @@ function initWorkspaceGitRepo(workspaceDir, agentName) {
27484
27895
  return false;
27485
27896
  }
27486
27897
  const gitDir = join12(workspaceDir, ".git");
27487
- if (existsSync17(gitDir)) {
27898
+ if (existsSync18(gitDir)) {
27488
27899
  return true;
27489
27900
  }
27490
27901
  const gitignore = `# Ephemeral runtime state
@@ -27496,7 +27907,7 @@ Thumbs.db
27496
27907
  *.swp
27497
27908
  *~
27498
27909
  `;
27499
- writeFileSync5(join12(workspaceDir, ".gitignore"), gitignore, "utf-8");
27910
+ writeFileSync6(join12(workspaceDir, ".gitignore"), gitignore, "utf-8");
27500
27911
  try {
27501
27912
  execSync("git init --quiet", { cwd: workspaceDir, stdio: "pipe" });
27502
27913
  execSync("git add -A", { cwd: workspaceDir, stdio: "pipe" });
@@ -27533,7 +27944,7 @@ function installHindsightPlugin(agentName, agentDir, switchroomConfig) {
27533
27944
  if (agentMemory?.auto_recall === false)
27534
27945
  return null;
27535
27946
  const sourcePath = resolveHindsightVendorPath();
27536
- if (!existsSync17(sourcePath)) {
27947
+ if (!existsSync18(sourcePath)) {
27537
27948
  process.stderr.write(`installHindsightPlugin: vendor source missing at ${sourcePath} ` + `\u2014 hindsight plugin NOT installed for ${agentName}. ` + `Likely a packaging regression: check the npm tarball's files array.
27538
27949
  `);
27539
27950
  return null;
@@ -27544,13 +27955,13 @@ function installHindsightPlugin(agentName, agentDir, switchroomConfig) {
27544
27955
  let expectedSettings = null;
27545
27956
  try {
27546
27957
  const vendorSettingsPath = join12(sourcePath, "settings.json");
27547
- if (existsSync17(vendorSettingsPath)) {
27548
- expectedSettings = renderHindsightSettingsOverrides(readFileSync14(vendorSettingsPath, "utf-8"), additionalBanks, retainConfig);
27958
+ if (existsSync18(vendorSettingsPath)) {
27959
+ expectedSettings = renderHindsightSettingsOverrides(readFileSync15(vendorSettingsPath, "utf-8"), additionalBanks, retainConfig);
27549
27960
  }
27550
27961
  } catch {}
27551
27962
  const upToDate = dirContentEquals(sourcePath, destPath, expectedSettings != null ? { "settings.json": expectedSettings } : undefined);
27552
27963
  if (!upToDate) {
27553
- if (existsSync17(destPath)) {
27964
+ if (existsSync18(destPath)) {
27554
27965
  rmSync4(destPath, { recursive: true, force: true });
27555
27966
  }
27556
27967
  copyDirRecursive2(sourcePath, destPath);
@@ -27571,11 +27982,11 @@ function resolveHindsightRetainConfig(switchroomConfig, agentName) {
27571
27982
  }
27572
27983
  function applyHindsightSettingsOverrides(pluginDestPath, additionalBanks, retainConfig) {
27573
27984
  const settingsPath = join12(pluginDestPath, "settings.json");
27574
- if (!existsSync17(settingsPath))
27985
+ if (!existsSync18(settingsPath))
27575
27986
  return;
27576
27987
  let raw;
27577
27988
  try {
27578
- raw = readFileSync14(settingsPath, "utf-8");
27989
+ raw = readFileSync15(settingsPath, "utf-8");
27579
27990
  } catch {
27580
27991
  return;
27581
27992
  }
@@ -27974,23 +28385,23 @@ function scaffoldAgent(name, agentConfigRaw, agentsDir, telegramConfig, switchro
27974
28385
  join12(agentDir, "telegram")
27975
28386
  ];
27976
28387
  for (const dir of dirs) {
27977
- mkdirSync10(dir, { recursive: true });
28388
+ mkdirSync11(dir, { recursive: true });
27978
28389
  }
27979
28390
  writeIfChanged(join12(agentDir, "start.sh"), () => renderTemplate(join12(basePath, "start.sh.hbs"), context), created, skipped);
27980
- if (existsSync17(join12(agentDir, "start.sh"))) {
28391
+ if (existsSync18(join12(agentDir, "start.sh"))) {
27981
28392
  chmodSync2(join12(agentDir, "start.sh"), 448);
27982
28393
  }
27983
28394
  if (context.cronSessionEnabled) {
27984
28395
  writeIfChanged(join12(agentDir, "cron-session.sh"), () => renderTemplate(join12(basePath, "cron-session.sh.hbs"), context), created, skipped);
27985
- if (existsSync17(join12(agentDir, "cron-session.sh"))) {
28396
+ if (existsSync18(join12(agentDir, "cron-session.sh"))) {
27986
28397
  chmodSync2(join12(agentDir, "cron-session.sh"), 448);
27987
28398
  }
27988
28399
  }
27989
28400
  writeIfMissing(join12(agentDir, ".claude", "settings.json"), () => renderTemplate(join12(basePath, "settings.json.hbs"), context), created, skipped, 384);
27990
28401
  if (switchroomConfig) {
27991
28402
  const settingsPath = join12(agentDir, ".claude", "settings.json");
27992
- if (existsSync17(settingsPath)) {
27993
- const settings = JSON.parse(readFileSync14(settingsPath, "utf-8"));
28403
+ if (existsSync18(settingsPath)) {
28404
+ const settings = JSON.parse(readFileSync15(settingsPath, "utf-8"));
27994
28405
  if (!settings.mcpServers) {
27995
28406
  settings.mcpServers = {};
27996
28407
  }
@@ -28133,7 +28544,7 @@ function scaffoldAgent(name, agentConfigRaw, agentsDir, telegramConfig, switchro
28133
28544
  ];
28134
28545
  for (const { src, dest } of templateFiles) {
28135
28546
  const srcPath = join12(profilePath, src);
28136
- if (existsSync17(srcPath)) {
28547
+ if (existsSync18(srcPath)) {
28137
28548
  const renderManaged = () => {
28138
28549
  let rendered = renderTemplate(srcPath, context);
28139
28550
  if (dest === "CLAUDE.md") {
@@ -28190,7 +28601,7 @@ function scaffoldAgent(name, agentConfigRaw, agentsDir, telegramConfig, switchro
28190
28601
  }
28191
28602
  }
28192
28603
  const phase5WorkspaceDir = join12(agentDir, "workspace");
28193
- mkdirSync10(phase5WorkspaceDir, { recursive: true });
28604
+ mkdirSync11(phase5WorkspaceDir, { recursive: true });
28194
28605
  migrateLegacyAgentsMdIfPresent(phase5WorkspaceDir, created);
28195
28606
  seedWorkspaceBootstrapFiles({
28196
28607
  profilePath,
@@ -28203,9 +28614,9 @@ function scaffoldAgent(name, agentConfigRaw, agentsDir, telegramConfig, switchro
28203
28614
  });
28204
28615
  ensureClaudeMdSymlinks(phase5WorkspaceDir, created);
28205
28616
  const persistentHomeDir = join12(agentDir, "home");
28206
- mkdirSync10(persistentHomeDir, { recursive: true });
28617
+ mkdirSync11(persistentHomeDir, { recursive: true });
28207
28618
  for (const sub of [".local/bin", ".npm-global", "bin"]) {
28208
- mkdirSync10(join12(persistentHomeDir, sub), { recursive: true });
28619
+ mkdirSync11(join12(persistentHomeDir, sub), { recursive: true });
28209
28620
  }
28210
28621
  if (!isContainerContext() && process.env.HOME) {
28211
28622
  const dotSwitchroomLink = join12(persistentHomeDir, ".switchroom");
@@ -28247,7 +28658,7 @@ function scaffoldAgent(name, agentConfigRaw, agentsDir, telegramConfig, switchro
28247
28658
  const existingClaudeJson = findExistingClaudeJson();
28248
28659
  if (existingClaudeJson) {
28249
28660
  copyOnboardingState(existingClaudeJson, agentDir);
28250
- if (!existsSync17(join12(agentDir, ".claude", "config.json"))) {
28661
+ if (!existsSync18(join12(agentDir, ".claude", "config.json"))) {
28251
28662
  writeIfMissing(join12(agentDir, ".claude", "config.json"), () => JSON.stringify({ hasCompletedOnboarding: true, numStartups: 1 }, null, 2) + `
28252
28663
  `, created, skipped, 384);
28253
28664
  }
@@ -28274,7 +28685,7 @@ This file is auto-maintained. Do not edit manually.
28274
28685
  writeFileSyncIfChanged(join12(agentDir, "telegram", "people.json"), buildPeopleJson(switchroomConfig), 384);
28275
28686
  if (agentConfig.subagents) {
28276
28687
  const agentsDir2 = join12(agentDir, ".claude", "agents");
28277
- mkdirSync10(agentsDir2, { recursive: true });
28688
+ mkdirSync11(agentsDir2, { recursive: true });
28278
28689
  for (const [saName, saDef] of Object.entries(agentConfig.subagents)) {
28279
28690
  const mdPath = join12(agentsDir2, `${saName}.md`);
28280
28691
  if (!saDef.description) {
@@ -28331,13 +28742,13 @@ ${body}
28331
28742
  }
28332
28743
  {
28333
28744
  const telegramDir = join12(agentDir, "telegram");
28334
- if (existsSync17(telegramDir)) {
28745
+ if (existsSync18(telegramDir)) {
28335
28746
  for (const file of readdirSync7(telegramDir)) {
28336
28747
  const isCronScript = CRON_SCRIPT_BASENAME_RE.test(file) || LEGACY_CRON_SCRIPT_BASENAME_RE.test(file);
28337
28748
  if (isCronScript) {
28338
28749
  unlinkSync4(join12(telegramDir, file));
28339
28750
  const sidecar = join12(telegramDir, file.replace(/\.sh$/, ".source"));
28340
- if (existsSync17(sidecar))
28751
+ if (existsSync18(sidecar))
28341
28752
  unlinkSync4(sidecar);
28342
28753
  }
28343
28754
  }
@@ -28353,8 +28764,8 @@ ${body}
28353
28764
  setupPlugins(agentDir, usesSwitchroomTelegramPlugin(agentConfig));
28354
28765
  const agentSoulPath = join12(agentDir, "SOUL.md");
28355
28766
  const workspaceSoulPath = join12(agentDir, "workspace", "SOUL.md");
28356
- if (existsSync17(workspaceSoulPath)) {
28357
- if (existsSync17(agentSoulPath)) {
28767
+ if (existsSync18(workspaceSoulPath)) {
28768
+ if (existsSync18(agentSoulPath)) {
28358
28769
  const stat = lstatSync4(agentSoulPath);
28359
28770
  if (stat.isSymbolicLink()) {
28360
28771
  const target = readlinkSync4(agentSoulPath);
@@ -28684,7 +29095,7 @@ function buildSettingsHooksBlock(p) {
28684
29095
  ` + ` - Question with a short answer \u2192 just reply with the answer.
28685
29096
  ` + " - Complex tool-driven work \u2192 go straight to the tools (the " + "compose-area preview is the ambient liveness signal), then reply " + 'once with the answer or a genuine mid-work pivot ("halfway ' + 'through \u2014 found an unexpected issue, want me to continue?"). Not ' + `"still working".
28686
29097
 
28687
- ` + "NARRATE YOUR INTENT (your reasoning is NOT shown to the user). The " + "user cannot see your thinking \u2014 only your tool activity and your " + "replies. So before you fire a burst of tools or work silently for a " + "while, first write ONE short plain-language line of intent as " + "ordinary working text (NOT a reply tool call): name what you are " + 'about to do and why \u2014 "Now checking the gateway logs to find why ' + 'the turn stalled". The framework mirrors that line into the live ' + "compose-area preview the user watches; because it is plain text and " + "not a reply, it is never a chat message and never pings. One intent " + "line before a silent stretch, in plain words, never raw tool names " + '("calling Bash", "Read(x)") and never a debug dump. And when you run ' + "a Bash command, always give it a plain-English `description` naming " + "the goal \u2014 that description, not the raw command, is what the user " + "sees. This intent narration is the ONE thing to keep saying; it is " + "NOT the placeholder ack banned above (that is a chat reply, which " + `you still skip).
29098
+ ` + "LABEL THE WORK AS YOU DO IT. The user watches a live preview built " + "from your tool activity, so what you put in a tool call is what they " + "read. Two output requirements. FIRST: every Bash and tool call gets a " + 'plain-English `description` naming the goal \u2014 "Checking the gateway ' + 'logs to find why the turn stalled", never raw tool names ("calling ' + 'Bash", "Read(x)") and never a debug dump. That description, not the ' + "raw command, is what the user sees. SECOND: before a long stretch of " + "work, post ONE short status line saying what you are starting \u2014 one " + "line per stretch, in plain words. Emit it as the `description` on the " + "first tool call of the stretch, or as a status line through the " + "progress tool where you have one; do NOT leave it sitting as unsent " + "transcript text (see the reply rule below). This labelling is the ONE " + "thing to keep doing; it is NOT the placeholder ack banned above (that " + `is a chat reply, which you still skip).
28688
29099
 
28689
29100
  ` + 'Do NOT send a trailing confirmation after your answer \u2014 no "Done.", ' + '"Sent.", "Hope that helps." as a separate message once you have ' + "already replied. Your answer is the last thing the user should " + `see; a follow-up "Done." is dead-air clutter (and the user's ` + `device already pinged on the answer). Stop after the answer.
28690
29101
 
@@ -28694,7 +29105,7 @@ function buildSettingsHooksBlock(p) {
28694
29105
 
28695
29106
  ` + "TURN-END SHAPE: the human reads your turn-end on a phone. Build it " + "for a thumb, not a terminal. LEAD WITH THE ONE DECISION OR ANSWER " + "in the first line \u2014 the single thing the user has to know. A routine " + "turn-end is a FEW SHORT LINES, not a multi-section report; if you " + "find yourself writing more than ~6 lines or stacking labelled " + "sections, you are dumping, not answering. ONE IDEA PER MESSAGE: " + "split genuinely separate topics into separate replies rather than " + "piling sections into one bubble. Push detail BELOW the answer or " + 'leave it out \u2014 offer "want the detail?" instead of pre-emptively ' + "dumping logs, full file contents, or a section per thread you " + "touched. RESERVE the long multi-section structured message for when " + 'the operator EXPLICITLY asked to go deep ("full breakdown", "walk me ' + 'through it", "show everything"). Default to short; expand on ' + "request. A wall of text the user has to re-read to find the one fact " + `that matters is the failure this exists to prevent.
28696
29107
 
28697
- ` + 'CRITICAL: "answer" means a call to the reply tool ' + "(mcp__switchroom-telegram__reply). " + "Your terminal/transcript text is NEVER delivered to Telegram \u2014 the " + "user sees only what you send through the reply tool. After a long " + "tool sequence (scheduling, multi-step research, sub-agent handback), " + "do not let your closing narration stand as the answer: end the turn " + "by passing that narration to the reply tool. No reply tool call = the " + "user got nothing, however much text you wrote. Call the reply tool as " + "your FIRST action when you have the answer \u2014 do not write it out as " + "transcript text first and call reply afterward: a framework backstop " + "flushes unsent text after a delay and then your real reply lands late " + "and out of order.</turn-pacing>";
29108
+ ` + 'CRITICAL: "answer" means a call to the reply tool ' + "(mcp__switchroom-telegram__reply). " + "Your terminal/transcript text is NEVER delivered to Telegram \u2014 the " + "user sees only what you send through the reply tool. After a long " + "tool sequence (scheduling, multi-step research, sub-agent handback), " + "do not let your closing narration stand as the answer: end the turn " + "by passing that narration to the reply tool. No reply tool call = the " + "user got nothing, however much text you wrote. Call the reply tool as " + "your FIRST action when you have the answer \u2014 do not write it out as " + "transcript text first and call reply afterward: a framework backstop " + "flushes unsent text after a delay and then your real reply lands late " + "and out of order. This is why the work-labelling above rides on tool " + "`description`s rather than loose prose: a tool description reaches the " + "preview immediately and is never subject to that flush, so there is " + "exactly one rule \u2014 user-facing text goes through a tool call, always." + "</turn-pacing>";
28698
29109
  const switchroomUserPromptSubmit = [
28699
29110
  ...useHotReloadStable ? [
28700
29111
  {
@@ -28829,13 +29240,13 @@ function reconcileAgentInner(name, agentConfigRaw, agentsDir, telegramConfig, sw
28829
29240
  }
28830
29241
  });
28831
29242
  }
28832
- if (!existsSync17(agentDir)) {
29243
+ if (!existsSync18(agentDir)) {
28833
29244
  throw new Error(`Agent directory does not exist: ${agentDir}. Run \`switchroom agent create ${name}\` first.`);
28834
29245
  }
28835
29246
  {
28836
29247
  const explicitResumeMode = agentConfig.session_continuity?.resume_mode;
28837
29248
  const markerPath = join12(agentDir, ".resume-mode-migration-warned");
28838
- if (!explicitResumeMode && !existsSync17(markerPath)) {
29249
+ if (!explicitResumeMode && !existsSync18(markerPath)) {
28839
29250
  console.warn(` ${source_default.yellow("\u26a0")} [${name}] resume_mode default changed from 'auto' to 'handoff' (switchroom #362).`);
28840
29251
  console.warn(` Your agent will now start a fresh Claude session on every restart, using a`);
28841
29252
  console.warn(` context briefing instead of --continue. This prevents stale MCP servers`);
@@ -28845,7 +29256,7 @@ function reconcileAgentInner(name, agentConfigRaw, agentsDir, telegramConfig, sw
28845
29256
  console.warn(` resume_mode: auto`);
28846
29257
  console.warn(` (This warning fires once per agent directory and is then suppressed.)`);
28847
29258
  try {
28848
- writeFileSync5(markerPath, `resume_mode default changed to handoff at reconcile
29259
+ writeFileSync6(markerPath, `resume_mode default changed to handoff at reconcile
28849
29260
  `, "utf-8");
28850
29261
  } catch {}
28851
29262
  }
@@ -28853,10 +29264,10 @@ function reconcileAgentInner(name, agentConfigRaw, agentsDir, telegramConfig, sw
28853
29264
  const legacyCustomPath = join12(agentDir, "CLAUDE.custom.md");
28854
29265
  const workspaceDir = join12(agentDir, "workspace");
28855
29266
  const newCustomPath = join12(workspaceDir, "CLAUDE.custom.md");
28856
- if (existsSync17(legacyCustomPath) && !existsSync17(newCustomPath)) {
28857
- mkdirSync10(workspaceDir, { recursive: true });
28858
- const legacyContent = readFileSync14(legacyCustomPath, "utf-8");
28859
- writeFileSync5(newCustomPath, legacyContent, "utf-8");
29267
+ if (existsSync18(legacyCustomPath) && !existsSync18(newCustomPath)) {
29268
+ mkdirSync11(workspaceDir, { recursive: true });
29269
+ const legacyContent = readFileSync15(legacyCustomPath, "utf-8");
29270
+ writeFileSync6(newCustomPath, legacyContent, "utf-8");
28860
29271
  rmSync4(legacyCustomPath);
28861
29272
  console.log(source_default.green(` moved CLAUDE.custom.md \u2192 workspace/CLAUDE.custom.md`));
28862
29273
  }
@@ -28989,19 +29400,19 @@ ${baseAppend}` : TELEGRAM_FORMATTING_FLOOR_CARD;
28989
29400
  resumeMaxBytes: agentConfig.session_continuity?.resume_max_bytes ?? 2000000,
28990
29401
  bootResumeMode: agentConfig.session_continuity?.boot_resume ?? "in-flight"
28991
29402
  };
28992
- const beforeStartSh = existsSync17(startShPath) ? readFileSync14(startShPath, "utf-8") : "";
29403
+ const beforeStartSh = existsSync18(startShPath) ? readFileSync15(startShPath, "utf-8") : "";
28993
29404
  const afterStartSh = renderTemplate(join12(basePath, "start.sh.hbs"), startShContext);
28994
29405
  if (afterStartSh !== beforeStartSh) {
28995
- writeFileSync5(startShPath, afterStartSh, "utf-8");
29406
+ writeFileSync6(startShPath, afterStartSh, "utf-8");
28996
29407
  chmodSync2(startShPath, 493);
28997
29408
  changes.push(startShPath);
28998
29409
  }
28999
29410
  if (startShContext.cronSessionEnabled) {
29000
29411
  const cronShPath = join12(agentDir, "cron-session.sh");
29001
- const beforeCronSh = existsSync17(cronShPath) ? readFileSync14(cronShPath, "utf-8") : "";
29412
+ const beforeCronSh = existsSync18(cronShPath) ? readFileSync15(cronShPath, "utf-8") : "";
29002
29413
  const afterCronSh = renderTemplate(join12(basePath, "cron-session.sh.hbs"), startShContext);
29003
29414
  if (afterCronSh !== beforeCronSh) {
29004
- writeFileSync5(cronShPath, afterCronSh, "utf-8");
29415
+ writeFileSync6(cronShPath, afterCronSh, "utf-8");
29005
29416
  chmodSync2(cronShPath, 493);
29006
29417
  changes.push(cronShPath);
29007
29418
  }
@@ -29012,7 +29423,7 @@ ${baseAppend}` : TELEGRAM_FORMATTING_FLOOR_CARD;
29012
29423
  const claudeMdSrc = join12(profilePath, "CLAUDE.md.hbs");
29013
29424
  const claudeMdDest = join12(agentDir, "CLAUDE.md");
29014
29425
  const claudeCustomPath = join12(agentDir, "workspace", "CLAUDE.custom.md");
29015
- if (existsSync17(claudeMdSrc)) {
29426
+ if (existsSync18(claudeMdSrc)) {
29016
29427
  const claudeContext = {
29017
29428
  name,
29018
29429
  agentDir,
@@ -29071,17 +29482,17 @@ ${baseAppend}` : TELEGRAM_FORMATTING_FLOOR_CARD;
29071
29482
  ` + agentConfig.claude_md_raw + `
29072
29483
  `;
29073
29484
  }
29074
- const before = existsSync17(claudeMdDest) ? readFileSync14(claudeMdDest, "utf-8") : "";
29485
+ const before = existsSync18(claudeMdDest) ? readFileSync15(claudeMdDest, "utf-8") : "";
29075
29486
  const composed = composeTwoSectionClaudeMd(rendered, before.length > 0 ? before : null, claudeCustomPath);
29076
29487
  if (composed !== before) {
29077
- writeFileSync5(claudeMdDest, composed, "utf-8");
29488
+ writeFileSync6(claudeMdDest, composed, "utf-8");
29078
29489
  changes.push(claudeMdDest);
29079
29490
  }
29080
29491
  }
29081
29492
  }
29082
29493
  const settingsPath = join12(agentDir, ".claude", "settings.json");
29083
- if (existsSync17(settingsPath)) {
29084
- const before = readFileSync14(settingsPath, "utf-8");
29494
+ if (existsSync18(settingsPath)) {
29495
+ const before = readFileSync15(settingsPath, "utf-8");
29085
29496
  const settings = JSON.parse(before);
29086
29497
  settings.permissions = settings.permissions ?? {};
29087
29498
  settings.permissions.allow = desiredAllow;
@@ -29143,15 +29554,15 @@ ${baseAppend}` : TELEGRAM_FORMATTING_FLOOR_CARD;
29143
29554
  });
29144
29555
  let greetingUserId;
29145
29556
  const accessPath = join12(agentDir, "telegram", "access.json");
29146
- if (existsSync17(accessPath)) {
29557
+ if (existsSync18(accessPath)) {
29147
29558
  try {
29148
- const access = JSON.parse(readFileSync14(accessPath, "utf-8"));
29559
+ const access = JSON.parse(readFileSync15(accessPath, "utf-8"));
29149
29560
  greetingUserId = access.allowFrom?.[0];
29150
29561
  } catch {}
29151
29562
  }
29152
29563
  if (agentConfig.subagents) {
29153
29564
  const saDir = join12(agentDir, ".claude", "agents");
29154
- mkdirSync10(saDir, { recursive: true });
29565
+ mkdirSync11(saDir, { recursive: true });
29155
29566
  for (const [saName, saDef] of Object.entries(agentConfig.subagents)) {
29156
29567
  const mdPath = join12(saDir, `${saName}.md`);
29157
29568
  if (!saDef.description) {
@@ -29203,9 +29614,9 @@ ${fmLines}
29203
29614
 
29204
29615
  ${body}
29205
29616
  `;
29206
- const before2 = existsSync17(mdPath) ? readFileSync14(mdPath, "utf-8") : "";
29617
+ const before2 = existsSync18(mdPath) ? readFileSync15(mdPath, "utf-8") : "";
29207
29618
  if (content !== before2) {
29208
- writeFileSync5(mdPath, content, "utf-8");
29619
+ writeFileSync6(mdPath, content, "utf-8");
29209
29620
  changes.push(mdPath);
29210
29621
  }
29211
29622
  }
@@ -29223,7 +29634,7 @@ ${body}
29223
29634
  }
29224
29635
  }
29225
29636
  const telegramDir = join12(agentDir, "telegram");
29226
- if (existsSync17(telegramDir)) {
29637
+ if (existsSync18(telegramDir)) {
29227
29638
  const files = readdirSync7(telegramDir);
29228
29639
  for (const file of files) {
29229
29640
  const isCronScript = CRON_SCRIPT_BASENAME_RE.test(file) || LEGACY_CRON_SCRIPT_BASENAME_RE.test(file);
@@ -29232,7 +29643,7 @@ ${body}
29232
29643
  unlinkSync4(staleScript);
29233
29644
  changes.push(staleScript);
29234
29645
  const sourceSidecar = staleScript.replace(/\.sh$/, ".source");
29235
- if (existsSync17(sourceSidecar)) {
29646
+ if (existsSync18(sourceSidecar)) {
29236
29647
  unlinkSync4(sourceSidecar);
29237
29648
  changes.push(sourceSidecar);
29238
29649
  }
@@ -29310,7 +29721,7 @@ ${body}
29310
29721
  const mcpJson = { mcpServers };
29311
29722
  const after = JSON.stringify(mcpJson, null, 2) + `
29312
29723
  `;
29313
- const before = existsSync17(mcpJsonPath) ? readFileSync14(mcpJsonPath, "utf-8") : "";
29724
+ const before = existsSync18(mcpJsonPath) ? readFileSync15(mcpJsonPath, "utf-8") : "";
29314
29725
  if (after !== before) {
29315
29726
  atomicWriteFileSync(mcpJsonPath, after, 384);
29316
29727
  changes.push(mcpJsonPath);
@@ -29349,7 +29760,7 @@ ${body}
29349
29760
  hindsightSenderBanksJson,
29350
29761
  hindsightTopicFilterMode
29351
29762
  });
29352
- mkdirSync10(reconcileWorkspaceDir, { recursive: true });
29763
+ mkdirSync11(reconcileWorkspaceDir, { recursive: true });
29353
29764
  migrateLegacyAgentsMdIfPresent(reconcileWorkspaceDir, changes);
29354
29765
  seedWorkspaceBootstrapFiles({
29355
29766
  profilePath: reconcileProfilePath,
@@ -29362,13 +29773,13 @@ ${body}
29362
29773
  });
29363
29774
  ensureClaudeMdSymlinks(reconcileWorkspaceDir, changes);
29364
29775
  }
29365
- if (existsSync17(reconcileWorkspaceDir)) {
29776
+ if (existsSync18(reconcileWorkspaceDir)) {
29366
29777
  initWorkspaceGitRepo(reconcileWorkspaceDir, name);
29367
29778
  }
29368
29779
  const agentSoulPath = join12(agentDir, "SOUL.md");
29369
29780
  const workspaceSoulPath = join12(agentDir, "workspace", "SOUL.md");
29370
- if (existsSync17(workspaceSoulPath)) {
29371
- if (existsSync17(agentSoulPath)) {
29781
+ if (existsSync18(workspaceSoulPath)) {
29782
+ if (existsSync18(agentSoulPath)) {
29372
29783
  const stat = lstatSync4(agentSoulPath);
29373
29784
  if (stat.isSymbolicLink()) {
29374
29785
  const target = readlinkSync4(agentSoulPath);
@@ -29479,7 +29890,7 @@ ${body}
29479
29890
  };
29480
29891
  }
29481
29892
  function writeIfMissing(filePath, contentFn, created, skipped, mode) {
29482
- if (existsSync17(filePath)) {
29893
+ if (existsSync18(filePath)) {
29483
29894
  skipped.push(filePath);
29484
29895
  return;
29485
29896
  }
@@ -29488,12 +29899,12 @@ function writeIfMissing(filePath, contentFn, created, skipped, mode) {
29488
29899
  }
29489
29900
  function writeIfChanged(filePath, contentFn, created, skipped, mode) {
29490
29901
  const next = contentFn();
29491
- const prev = existsSync17(filePath) ? readFileSync14(filePath, "utf-8") : null;
29902
+ const prev = existsSync18(filePath) ? readFileSync15(filePath, "utf-8") : null;
29492
29903
  if (prev === next) {
29493
29904
  skipped.push(filePath);
29494
29905
  return;
29495
29906
  }
29496
- writeFileSync5(filePath, next, mode !== undefined ? { encoding: "utf-8", mode } : "utf-8");
29907
+ writeFileSync6(filePath, next, mode !== undefined ? { encoding: "utf-8", mode } : "utf-8");
29497
29908
  created.push(filePath);
29498
29909
  }
29499
29910
  function claudeMdManagedSection(content) {
@@ -29503,42 +29914,42 @@ function claudeMdManagedSection(content) {
29503
29914
  return content.slice(0, idx + CLAUDE_MD_YOURS_MARKER.length);
29504
29915
  }
29505
29916
  function writeTwoSectionClaudeMdWithFingerprint(filePath, managedFn, sidecarPath, created, skipped, rewrittenWithBackup) {
29506
- const prev = existsSync17(filePath) ? readFileSync14(filePath, "utf-8") : null;
29917
+ const prev = existsSync18(filePath) ? readFileSync15(filePath, "utf-8") : null;
29507
29918
  const next = composeTwoSectionClaudeMd(managedFn(), prev, sidecarPath);
29508
29919
  const nextManagedHash = createHash4("sha256").update(claudeMdManagedSection(next), "utf-8").digest("hex");
29509
29920
  const fingerprintPath = filePath + ".fingerprint";
29510
29921
  if (prev === null) {
29511
- writeFileSync5(filePath, next, "utf-8");
29512
- writeFileSync5(fingerprintPath, nextManagedHash, "utf-8");
29922
+ writeFileSync6(filePath, next, "utf-8");
29923
+ writeFileSync6(fingerprintPath, nextManagedHash, "utf-8");
29513
29924
  created.push(filePath);
29514
29925
  return;
29515
29926
  }
29516
29927
  if (prev === next) {
29517
29928
  try {
29518
- writeFileSync5(fingerprintPath, nextManagedHash, "utf-8");
29929
+ writeFileSync6(fingerprintPath, nextManagedHash, "utf-8");
29519
29930
  } catch {}
29520
29931
  skipped.push(filePath);
29521
29932
  return;
29522
29933
  }
29523
29934
  const prevManagedHash = createHash4("sha256").update(claudeMdManagedSection(prev), "utf-8").digest("hex");
29524
- const recordedFingerprint = existsSync17(fingerprintPath) ? readFileSync14(fingerprintPath, "utf-8").trim() : null;
29935
+ const recordedFingerprint = existsSync18(fingerprintPath) ? readFileSync15(fingerprintPath, "utf-8").trim() : null;
29525
29936
  if (recordedFingerprint === prevManagedHash) {
29526
- writeFileSync5(filePath, next, "utf-8");
29527
- writeFileSync5(fingerprintPath, nextManagedHash, "utf-8");
29937
+ writeFileSync6(filePath, next, "utf-8");
29938
+ writeFileSync6(fingerprintPath, nextManagedHash, "utf-8");
29528
29939
  created.push(filePath);
29529
29940
  return;
29530
29941
  }
29531
29942
  const backupPath = `${filePath}.before-rerender.${Date.now()}`;
29532
29943
  try {
29533
- writeFileSync5(backupPath, prev, "utf-8");
29944
+ writeFileSync6(backupPath, prev, "utf-8");
29534
29945
  } catch (err) {
29535
29946
  process.stderr.write(`scaffold: refusing to overwrite ${filePath} \u2014 backup write failed ` + `(${err.message}). Operator edits preserved.
29536
29947
  `);
29537
29948
  skipped.push(filePath);
29538
29949
  return;
29539
29950
  }
29540
- writeFileSync5(filePath, next, "utf-8");
29541
- writeFileSync5(fingerprintPath, nextManagedHash, "utf-8");
29951
+ writeFileSync6(filePath, next, "utf-8");
29952
+ writeFileSync6(fingerprintPath, nextManagedHash, "utf-8");
29542
29953
  rewrittenWithBackup.push(filePath);
29543
29954
  created.push(filePath);
29544
29955
  }
@@ -29547,39 +29958,39 @@ function rerenderWithFingerprint(filePath, contentFn, created, skipped, rewritte
29547
29958
  const nextHash = createHash4("sha256").update(next, "utf-8").digest("hex");
29548
29959
  const fingerprintPath = filePath + ".fingerprint";
29549
29960
  const writeMode = mode !== undefined ? { encoding: "utf-8", mode } : "utf-8";
29550
- if (!existsSync17(filePath)) {
29551
- writeFileSync5(filePath, next, writeMode);
29552
- writeFileSync5(fingerprintPath, nextHash, "utf-8");
29961
+ if (!existsSync18(filePath)) {
29962
+ writeFileSync6(filePath, next, writeMode);
29963
+ writeFileSync6(fingerprintPath, nextHash, "utf-8");
29553
29964
  created.push(filePath);
29554
29965
  return;
29555
29966
  }
29556
- const prev = readFileSync14(filePath, "utf-8");
29967
+ const prev = readFileSync15(filePath, "utf-8");
29557
29968
  if (prev === next) {
29558
29969
  try {
29559
- writeFileSync5(fingerprintPath, nextHash, "utf-8");
29970
+ writeFileSync6(fingerprintPath, nextHash, "utf-8");
29560
29971
  } catch {}
29561
29972
  skipped.push(filePath);
29562
29973
  return;
29563
29974
  }
29564
29975
  const prevHash = createHash4("sha256").update(prev, "utf-8").digest("hex");
29565
- const recordedFingerprint = existsSync17(fingerprintPath) ? readFileSync14(fingerprintPath, "utf-8").trim() : null;
29976
+ const recordedFingerprint = existsSync18(fingerprintPath) ? readFileSync15(fingerprintPath, "utf-8").trim() : null;
29566
29977
  if (recordedFingerprint === prevHash) {
29567
- writeFileSync5(filePath, next, writeMode);
29568
- writeFileSync5(fingerprintPath, nextHash, "utf-8");
29978
+ writeFileSync6(filePath, next, writeMode);
29979
+ writeFileSync6(fingerprintPath, nextHash, "utf-8");
29569
29980
  created.push(filePath);
29570
29981
  return;
29571
29982
  }
29572
29983
  const backupPath = `${filePath}.before-rerender.${Date.now()}`;
29573
29984
  try {
29574
- writeFileSync5(backupPath, prev, "utf-8");
29985
+ writeFileSync6(backupPath, prev, "utf-8");
29575
29986
  } catch (err) {
29576
29987
  process.stderr.write(`scaffold: refusing to overwrite ${filePath} \u2014 backup write failed ` + `(${err.message}). Operator edits preserved.
29577
29988
  `);
29578
29989
  skipped.push(filePath);
29579
29990
  return;
29580
29991
  }
29581
- writeFileSync5(filePath, next, writeMode);
29582
- writeFileSync5(fingerprintPath, nextHash, "utf-8");
29992
+ writeFileSync6(filePath, next, writeMode);
29993
+ writeFileSync6(fingerprintPath, nextHash, "utf-8");
29583
29994
  rewrittenWithBackup.push(filePath);
29584
29995
  created.push(filePath);
29585
29996
  }
@@ -29590,7 +30001,7 @@ function resolveAgentForumChatId(agentConfig, telegramConfig) {
29590
30001
  return telegramConfig.forum_chat_id ?? "";
29591
30002
  }
29592
30003
  function reconcileConfiguredGroup(accessPath, agentConfig, telegramConfig) {
29593
- if (!existsSync17(accessPath))
30004
+ if (!existsSync18(accessPath))
29594
30005
  return;
29595
30006
  const forumChatId = resolveAgentForumChatId(agentConfig, telegramConfig);
29596
30007
  const hasRealForumChat = forumChatId !== "" && forumChatId !== "0";
@@ -29598,7 +30009,7 @@ function reconcileConfiguredGroup(accessPath, agentConfig, telegramConfig) {
29598
30009
  return;
29599
30010
  let access;
29600
30011
  try {
29601
- access = JSON.parse(readFileSync14(accessPath, "utf-8"));
30012
+ access = JSON.parse(readFileSync15(accessPath, "utf-8"));
29602
30013
  } catch {
29603
30014
  return;
29604
30015
  }
@@ -29608,7 +30019,7 @@ function reconcileConfiguredGroup(accessPath, agentConfig, telegramConfig) {
29608
30019
  const allowFrom = Array.isArray(access.allowFrom) ? access.allowFrom : [];
29609
30020
  groups[forumChatId] = { requireMention: false, allowFrom };
29610
30021
  const tmp = accessPath + ".tmp";
29611
- writeFileSync5(tmp, JSON.stringify(access, null, 2) + `
30022
+ writeFileSync6(tmp, JSON.stringify(access, null, 2) + `
29612
30023
  `, { mode: 384 });
29613
30024
  renameSync4(tmp, accessPath);
29614
30025
  console.log(source_default.green(` registered supergroup ${forumChatId} in access.json ` + `(responds to all topics; requireMention=false)`));
@@ -29752,22 +30163,19 @@ messaging a capable colleague \u2014 not a tool emitting output. Five beats:
29752
30163
  compose the full answer. This holds even for a pure-thinking
29753
30164
  answer: if it will run to a paragraph, ack first. It is the line
29754
30165
  between a colleague and a black box.
29755
- 2. **Then go quiet and work \u2014 but leave a trail of intent.** Heads-down
30166
+ 2. **Then go quiet and work \u2014 but label what you are doing.** Heads-down
29756
30167
  is correct: do NOT narrate every tool call, and a typing indicator
29757
- runs automatically (you do not maintain it). One thing you SHOULD do,
29758
- because your private reasoning is never shown to the user: before a
29759
- burst of tool calls or a long silent stretch, drop ONE short
29760
- plain-language line of intent in your own working text \u2014 *what* you
29761
- are about to do and *why* ("Now checking the gateway logs to find why
29762
- the turn stalled"). Write it as ordinary text, NOT a \`reply\` call, so
29763
- it never becomes a chat message and never pings \u2014 the framework
29764
- mirrors that line into the live preview the user watches while you
29765
- work. One line per stretch, plain words, never raw tool names
29766
- ("calling Bash") or debug dumps. It is the difference between the user
29767
- seeing "checking the logs to find the stall" and seeing a black box.
29768
- When a step is a \`Bash\` command, always give it a plain-English
29769
- \`description\` naming the goal \u2014 that description, not the raw command,
29770
- is what the user sees.
30168
+ runs automatically (you do not maintain it). What you SHOULD do is
30169
+ make the work legible through the tool calls themselves: always give a
30170
+ \`Bash\` (or any tool) call a plain-English \`description\` naming the
30171
+ goal \u2014 "Checking the gateway logs to find why the turn stalled". That
30172
+ description, not the raw command, is what the user sees in the live
30173
+ preview. Before a long silent stretch, put one short status line there
30174
+ too, saying what you are starting. One line per stretch, plain words,
30175
+ never raw tool names ("calling Bash") or debug dumps. It is the
30176
+ difference between the user seeing "checking the logs to find the
30177
+ stall" and seeing a black box. Keep it in the tool call \u2014 loose
30178
+ transcript prose is not delivered and can be flushed late (see beat 5).
29771
30179
  3. **Surface meaningful progress** at genuine inflection points \u2014 a
29772
30180
  hard step finished, a blocker, a pivot, dispatching a sub-agent, a
29773
30181
  notably slow wait, a finding worth knowing now. One short \`reply\`,
@@ -30200,50 +30608,6 @@ var init_scaffold = __esm(() => {
30200
30608
  ];
30201
30609
  });
30202
30610
 
30203
- // src/setup/host-capabilities.ts
30204
- import { existsSync as existsSync18, mkdirSync as mkdirSync11, readFileSync as readFileSync15, writeFileSync as writeFileSync6 } from "node:fs";
30205
- import { dirname as dirname6 } from "node:path";
30206
- function hostCapabilitiesPath() {
30207
- return resolveStatePath("host-capabilities.json");
30208
- }
30209
- function saveVoiceCapability(caps, now = () => new Date) {
30210
- const doc = {
30211
- version: HOST_CAPABILITIES_VERSION,
30212
- voice: {
30213
- gpuPresent: caps.gpuPresent,
30214
- containerToolkit: caps.containerToolkit,
30215
- engine: caps.engine,
30216
- detectedAt: now().toISOString()
30217
- }
30218
- };
30219
- const path = hostCapabilitiesPath();
30220
- mkdirSync11(dirname6(path), { recursive: true });
30221
- writeFileSync6(path, JSON.stringify(doc, null, 2) + `
30222
- `, {
30223
- encoding: "utf-8",
30224
- mode: 384
30225
- });
30226
- return doc;
30227
- }
30228
- function loadHostCapabilities() {
30229
- const path = hostCapabilitiesPath();
30230
- if (!existsSync18(path))
30231
- return null;
30232
- try {
30233
- const parsed = JSON.parse(readFileSync15(path, "utf-8"));
30234
- if (parsed && typeof parsed === "object" && "voice" in parsed && typeof parsed.voice === "object") {
30235
- return parsed;
30236
- }
30237
- return null;
30238
- } catch {
30239
- return null;
30240
- }
30241
- }
30242
- var HOST_CAPABILITIES_VERSION = 1;
30243
- var init_host_capabilities = __esm(() => {
30244
- init_paths();
30245
- });
30246
-
30247
30611
  // src/vault/grants-db-path.ts
30248
30612
  var exports_grants_db_path = {};
30249
30613
  __export(exports_grants_db_path, {
@@ -51186,7 +51550,7 @@ function probePendingRetainsQueue(agentName) {
51186
51550
  function buildPendingRetainsProbeScript(base, now = Date.now()) {
51187
51551
  const dir = `${base}/pending-retains`;
51188
51552
  const cutoff = new Date(now - PENDING_RETAINS_EVICTION_WINDOW_DAYS * 86400000).toISOString().replace(/\.\d+Z$/, "Z");
51189
- return `P=0; D=0; X=0; E=0; C=\${HINDSIGHT_PENDING_MAX_ENTRIES:-${PENDING_RETAINS_DEFAULT_MAX_ENTRIES}}; ` + `if [ -d '${dir}' ]; then ` + `P=$(ls -1 '${dir}' 2>/dev/null | grep -c '\\.json$' || true); ` + `D=$(ls -1 '${dir}' 2>/dev/null | grep -c '\\.json\\.dead$' || true); ` + `fi; ` + `for L in '${base}/pending-drops.json' '${dir}/.drops.json'; do ` + `if [ "$X" = 0 ] && [ -f "$L" ]; then ` + `X=$(grep -o '"count"[^0-9]*[0-9][0-9]*' "$L" 2>/dev/null ` + `| grep -o '[0-9][0-9]*$' | head -n1); ` + `[ -n "$X" ] || X=0; ` + `fi; done; ` + `if [ -f '${base}/pending-evictions.log' ]; then ` + `E=$(awk -v c='${cutoff}' '$1 >= c && /evicted=/ {n++} END {print n+0}' ` + `'${base}/pending-evictions.log' 2>/dev/null || echo 0); ` + `[ -n "$E" ] || E=0; ` + `fi; ` + `echo "P=$P D=$D X=$X E=$E C=$C"`;
51553
+ return `P=0; D=0; X=0; E=0; C=\${HINDSIGHT_PENDING_MAX_ENTRIES:-${PENDING_RETAINS_DEFAULT_MAX_ENTRIES}}; ` + `if [ -d '${dir}' ]; then ` + `P=$(ls -1 '${dir}' 2>/dev/null | grep -c '\\.json$' || true); ` + `D=$(ls -1 '${dir}' 2>/dev/null | grep -c '\\.json\\.dead$' || true); ` + `fi; ` + `if [ -d '${base}/pending-dead' ]; then ` + `D=$((D + $(ls -1 '${base}/pending-dead' 2>/dev/null | grep -c '\\.dead$' || true))); ` + `fi; ` + `for L in '${base}/pending-drops.json' '${dir}/.drops.json'; do ` + `if [ "$X" = 0 ] && [ -f "$L" ]; then ` + `X=$(grep -o '"count"[^0-9]*[0-9][0-9]*' "$L" 2>/dev/null ` + `| grep -o '[0-9][0-9]*$' | head -n1); ` + `[ -n "$X" ] || X=0; ` + `fi; done; ` + `if [ -f '${base}/pending-evictions.log' ]; then ` + `E=$(awk -v c='${cutoff}' '$1 >= c && /evicted=/ {n++} END {print n+0}' ` + `'${base}/pending-evictions.log' 2>/dev/null || echo 0); ` + `[ -n "$E" ] || E=0; ` + `fi; ` + `echo "P=$P D=$D X=$X E=$E C=$C"`;
51190
51554
  }
51191
51555
  function parsePendingRetainsProbeOutput(r) {
51192
51556
  const unreachable = {
@@ -66572,11 +66936,24 @@ function parseTurns(text) {
66572
66936
  if (!line)
66573
66937
  continue;
66574
66938
  try {
66575
- out.push(JSON.parse(line));
66939
+ const parsed = JSON.parse(line);
66940
+ if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed))
66941
+ continue;
66942
+ out.push(parsed);
66576
66943
  } catch {}
66577
66944
  }
66578
66945
  return out;
66579
66946
  }
66947
+ function ownedTurns(agent, turns) {
66948
+ const slug = agent.trim().toLowerCase();
66949
+ return turns.filter((t) => {
66950
+ const raw = t?.agent;
66951
+ if (typeof raw !== "string")
66952
+ return true;
66953
+ const owner = raw.trim().toLowerCase();
66954
+ return owner === "" || owner === slug;
66955
+ });
66956
+ }
66580
66957
  function isoFromTs(ts) {
66581
66958
  if (typeof ts !== "number" || !Number.isFinite(ts))
66582
66959
  return null;
@@ -66585,8 +66962,8 @@ function isoFromTs(ts) {
66585
66962
  function detectTurnFindings(agent, turns, opts = {}) {
66586
66963
  const findings = [];
66587
66964
  const silentNoopFloorTs = opts.silentNoopFloorTs ?? SILENT_NOOP_FLOOR_TS;
66588
- for (const t of turns) {
66589
- const tid = t.turn_id ?? "?";
66965
+ for (const t of ownedTurns(agent, turns)) {
66966
+ const tid = typeof t.turn_id === "string" ? t.turn_id : "?";
66590
66967
  const st = t.status;
66591
66968
  const tl = typeof t.tools === "number" ? t.tools : 0;
66592
66969
  const dur = typeof t.duration_ms === "number" ? t.duration_ms : 0;
@@ -66667,7 +67044,7 @@ function extractTs(line) {
66667
67044
  return m ? m[0] : null;
66668
67045
  }
66669
67046
  function scanAgent(agent, turnsText, gatewayText, opts = {}) {
66670
- const turns = parseTurns(turnsText);
67047
+ const turns = ownedTurns(agent, parseTurns(turnsText));
66671
67048
  const status_mix = {};
66672
67049
  for (const t of turns) {
66673
67050
  const st = t.status ?? "unknown";
@@ -66677,7 +67054,16 @@ function scanAgent(agent, turnsText, gatewayText, opts = {}) {
66677
67054
  const { findings: gwFindings, gw_hits } = detectGatewayFindings(agent, gatewayText);
66678
67055
  const findings = [...turnFindings, ...gwFindings];
66679
67056
  const escalate = turnFindings.some((f) => f.signal === "killed-incomplete-turn" || f.signal === "hang-long-stalled" || f.signal === "silent-no-op-candidate" || f.signal === "send-failed-delivery") || gw_hits["duplicate-delivery-represent"] > 0 || gw_hits["reply-delivery-failure"] > 0;
66680
- return { agent, turns: turns.length, status_mix, findings, gw_hits, escalate };
67057
+ const landed_unconfirmed_turns = turns.filter((t) => (t.landed_unconfirmed ?? 0) > 0).length;
67058
+ return {
67059
+ agent,
67060
+ turns: turns.length,
67061
+ status_mix,
67062
+ findings,
67063
+ gw_hits,
67064
+ landed_unconfirmed_turns,
67065
+ escalate
67066
+ };
66681
67067
  }
66682
67068
  var HANG_MS = 360000, HANG_MAXTOOLS = 2, SILENT_NOOP_FLOOR_TS = 1783900800, GATEWAY_SIGNATURES;
66683
67069
  var init_detect = __esm(() => {
@@ -81563,7 +81949,7 @@ Cross-agent reflection plan
81563
81949
  try {
81564
81950
  const hindsightConfig = getConfig(program3);
81565
81951
  const litellmCfg = await resolveLiteLLMForHindsight(hindsightConfig);
81566
- startHindsight(ports, litellmCfg, effectiveTag, hindsightConfig.hindsight?.llm, hindsightConsumerMirrorDir(hindsightConfig));
81952
+ startHindsight(ports, litellmCfg, effectiveTag, hindsightConfig.hindsight?.llm, hindsightConsumerMirrorDir(hindsightConfig), undefined, { env: hindsightConfig.hindsight?.env });
81567
81953
  if (litellmCfg) {
81568
81954
  console.log(source_default.gray(" LiteLLM routing enabled for hindsight (--network host)."));
81569
81955
  }
@@ -81594,7 +81980,7 @@ Cross-agent reflection plan
81594
81980
  `));
81595
81981
  {
81596
81982
  const snippetConfig = getConfig(program3);
81597
- console.log(generateHindsightComposeSnippet(snippetConfig.hindsight?.llm, hindsightConsumerMirrorDir(snippetConfig)));
81983
+ console.log(generateHindsightComposeSnippet(snippetConfig.hindsight?.llm, hindsightConsumerMirrorDir(snippetConfig), undefined, undefined, { env: snippetConfig.hindsight?.env }));
81598
81984
  }
81599
81985
  console.log();
81600
81986
  });
@@ -99752,7 +100138,7 @@ var RING_MAX = 8;
99752
100138
  var MAX_SAMPLE_AGE_MS = 3 * 60 * 60000;
99753
100139
  var MIN_RETAIN_SAMPLES = 20;
99754
100140
  var RETAIN_FAILURE_RATE = 0.1;
99755
- var PARTS_PER_MEMORY = 4.4;
100141
+ var PARTS_PER_MEMORY = 6;
99756
100142
  function inParts(memories) {
99757
100143
  return Math.round(memories * PARTS_PER_MEMORY);
99758
100144
  }
@@ -99878,6 +100264,12 @@ function probeSpool(agentsDir = resolve61(process.env.SWITCHROOM_HOME ?? process
99878
100264
  evicted++;
99879
100265
  }
99880
100266
  } catch {}
100267
+ try {
100268
+ for (const f of readdirSync43(resolve61(hindsightDir, "pending-dead"))) {
100269
+ if (f.endsWith(".dead"))
100270
+ dead++;
100271
+ }
100272
+ } catch {}
99881
100273
  drops += readDropCount(hindsightDir);
99882
100274
  }
99883
100275
  return { pending, dead, evicted, drops, agents };