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.
- package/dist/agent-scheduler/index.js +5 -2
- package/dist/auth-broker/index.js +95 -2
- package/dist/cli/notion-write-pretool.mjs +5 -2
- package/dist/cli/switchroom.js +749 -357
- package/dist/host-control/main.js +96 -3
- package/dist/vault/approvals/kernel-server.js +98 -5
- package/dist/vault/broker/server.js +98 -5
- package/package.json +5 -4
- package/profiles/_base/start.sh.hbs +101 -0
- package/profiles/_shared/agent-self-service.md.hbs +64 -109
- package/profiles/_shared/delegation-golden-rule.md.hbs +5 -5
- package/profiles/_shared/dev-protocol.md.hbs +12 -42
- package/profiles/_shared/execution-discipline.md.hbs +7 -14
- package/profiles/coding/CLAUDE.md.hbs +0 -6
- package/profiles/default/CLAUDE.md.hbs +21 -50
- package/skills/dev-protocol/SKILL.md +97 -107
- package/skills/switchroom-release/SKILL.md +2 -1
- package/telegram-plugin/bunfig.toml +10 -0
- package/telegram-plugin/dist/gateway/gateway.js +267 -52
- package/telegram-plugin/gateway/backstop-delivery.ts +97 -16
- package/telegram-plugin/gateway/captured-answer-resume.ts +46 -17
- package/telegram-plugin/gateway/gateway.ts +43 -42
- package/telegram-plugin/gateway/latest-turn-lookup.ts +60 -0
- package/telegram-plugin/gateway/outbound-send-path.ts +61 -22
- package/telegram-plugin/gateway/stream-render.ts +6 -0
- package/telegram-plugin/gateway/subagent-handback-marker.ts +1 -1
- package/telegram-plugin/gateway/turn-end.ts +1 -1
- package/telegram-plugin/gateway/turn-record-status.ts +19 -0
- package/telegram-plugin/gateway/turns-jsonl-rotate.ts +65 -0
- package/telegram-plugin/reply-owner-resolve.ts +110 -9
- package/telegram-plugin/send-gate-degraded.test.ts +45 -16
- package/telegram-plugin/send-gate.ts +185 -24
- package/telegram-plugin/tests/activity-card-send-gate.test.ts +9 -9
- package/telegram-plugin/tests/agent-state-dir-preload.test.ts +33 -0
- package/telegram-plugin/tests/backstop-delivery.test.ts +204 -7
- package/telegram-plugin/tests/backstop-readback-probe.test.ts +12 -0
- package/telegram-plugin/tests/captured-answer-resume.test.ts +104 -0
- package/telegram-plugin/tests/latest-turn-lookup.test.ts +77 -0
- package/telegram-plugin/tests/narrative-lane-golden.test.ts +23 -1
- package/telegram-plugin/tests/reply-owner-resolve.test.ts +531 -0
- package/telegram-plugin/tests/send-reply-golden.test.ts +296 -28
- package/telegram-plugin/tests/stream-controller-send-gate.test.ts +134 -28
- package/telegram-plugin/tests/stream-render-golden.test.ts +25 -3
- package/telegram-plugin/tests/turns-jsonl-rotate.test.ts +92 -1
- package/vendor/hindsight-memory/scripts/drain_pending.py +113 -11
- package/vendor/hindsight-memory/scripts/lib/pending.py +802 -65
- package/vendor/hindsight-memory/scripts/lib/retain_split.py +54 -7
- package/vendor/hindsight-memory/scripts/tests/test_pending_drops.py +1445 -11
- package/vendor/hindsight-memory/scripts/tests/test_retain_split.py +78 -6
- package/vendor/hindsight-memory/tests/test_drain_pending.py +17 -2
- package/vendor/hindsight-memory/tests/test_pending.py +12 -4
package/dist/cli/switchroom.js
CHANGED
|
@@ -2120,7 +2120,7 @@ var init_esm = __esm(() => {
|
|
|
2120
2120
|
});
|
|
2121
2121
|
|
|
2122
2122
|
// src/build-info.ts
|
|
2123
|
-
var VERSION = "0.19.
|
|
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
|
|
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
|
|
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
|
-
`
|
|
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
|
-
` -
|
|
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,
|
|
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
|
|
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 =
|
|
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 =
|
|
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
|
|
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 (
|
|
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 (!
|
|
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
|
|
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 (!
|
|
17838
|
+
if (!existsSync7(p))
|
|
17428
17839
|
return null;
|
|
17429
17840
|
let content;
|
|
17430
17841
|
try {
|
|
17431
|
-
content =
|
|
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
|
-
|
|
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 (!
|
|
17870
|
+
if (!existsSync7(p))
|
|
17460
17871
|
return null;
|
|
17461
17872
|
try {
|
|
17462
|
-
const parsed = JSON.parse(
|
|
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
|
|
17980
|
-
mkdirSync,
|
|
18390
|
+
existsSync as existsSync8,
|
|
18391
|
+
mkdirSync as mkdirSync2,
|
|
17981
18392
|
appendFileSync,
|
|
17982
|
-
readFileSync as
|
|
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 (!
|
|
18000
|
-
|
|
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 (
|
|
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 (!
|
|
18550
|
+
if (!existsSync8(path))
|
|
18140
18551
|
return [];
|
|
18141
18552
|
let raw;
|
|
18142
18553
|
try {
|
|
18143
|
-
raw =
|
|
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
|
|
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 (
|
|
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 (
|
|
24392
|
+
if (existsSync9(requested) && hasProfileFiles(requested)) {
|
|
23982
24393
|
return requested;
|
|
23983
24394
|
}
|
|
23984
24395
|
const fallback = resolve4(PROFILES_ROOT, "default");
|
|
23985
|
-
if (
|
|
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 =
|
|
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 (!
|
|
24432
|
+
if (!existsSync9(skillsSrc)) {
|
|
24022
24433
|
return;
|
|
24023
24434
|
}
|
|
24024
24435
|
copyDirRecursive(skillsSrc, destPath);
|
|
24025
24436
|
}
|
|
24026
24437
|
function copyDirRecursive(src, dest) {
|
|
24027
|
-
|
|
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 (!
|
|
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 (!
|
|
24455
|
+
if (!existsSync9(fragPath))
|
|
24045
24456
|
return "";
|
|
24046
|
-
const source =
|
|
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 (!
|
|
24463
|
+
if (!existsSync9(fragPath))
|
|
24053
24464
|
return "";
|
|
24054
|
-
const source =
|
|
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 (!
|
|
24471
|
+
if (!existsSync9(fragPath))
|
|
24061
24472
|
return "";
|
|
24062
|
-
const source =
|
|
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 (!
|
|
24479
|
+
if (!existsSync9(fragPath))
|
|
24069
24480
|
return "";
|
|
24070
|
-
const source =
|
|
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 (!
|
|
24487
|
+
if (!existsSync9(fragPath))
|
|
24077
24488
|
return "";
|
|
24078
|
-
const source =
|
|
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 (!
|
|
24495
|
+
if (!existsSync9(fragPath))
|
|
24085
24496
|
return "";
|
|
24086
|
-
const source =
|
|
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 (!
|
|
24505
|
+
if (!existsSync9(hbsPath)) {
|
|
24095
24506
|
return { wrote: false, path: outPath };
|
|
24096
24507
|
}
|
|
24097
|
-
const source =
|
|
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
|
-
|
|
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 (
|
|
24127
|
-
import_handlebars.default.registerPartial(name,
|
|
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
|
|
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
|
|
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(
|
|
24736
|
+
return isAbsolute(storedTarget) ? storedTarget : resolve5(dirname3(linkPath), storedTarget);
|
|
24326
24737
|
}
|
|
24327
24738
|
function linkTargetFor(dest, src) {
|
|
24328
|
-
return relative(
|
|
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(
|
|
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 (!
|
|
24769
|
+
if (!existsSync10(claudeDir)) {
|
|
24359
24770
|
return result;
|
|
24360
24771
|
}
|
|
24361
24772
|
const targetDir = join7(claudeDir, "skills");
|
|
24362
|
-
|
|
24363
|
-
if (!
|
|
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 (!
|
|
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() &&
|
|
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
|
|
24522
|
-
writeFileSync as
|
|
24523
|
-
mkdirSync as
|
|
24524
|
-
existsSync as
|
|
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
|
|
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 (!
|
|
24946
|
+
if (!existsSync11(path)) {
|
|
24536
24947
|
return { topics: {} };
|
|
24537
24948
|
}
|
|
24538
24949
|
try {
|
|
24539
|
-
const raw =
|
|
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 =
|
|
24557
|
-
if (!
|
|
24558
|
-
|
|
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
|
-
|
|
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 (
|
|
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
|
|
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
|
|
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 =
|
|
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
|
|
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 =
|
|
24650
|
-
const procStat =
|
|
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 (!
|
|
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
|
|
25227
|
+
readFileSync as readFileSync12,
|
|
24817
25228
|
writeSync as writeSync3,
|
|
24818
|
-
existsSync as
|
|
25229
|
+
existsSync as existsSync13,
|
|
24819
25230
|
renameSync as renameSync3,
|
|
24820
|
-
mkdirSync as
|
|
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
|
|
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 (
|
|
25243
|
+
if (existsSync13(path) && lstatSync3(path).isSymbolicLink()) {
|
|
24833
25244
|
effectivePath = realpathSync2(path);
|
|
24834
25245
|
}
|
|
24835
25246
|
} catch {}
|
|
24836
|
-
const dir =
|
|
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 (
|
|
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 (
|
|
25381
|
+
if (existsSync13(vaultPath)) {
|
|
24971
25382
|
throw new VaultError(`Vault file already exists: ${vaultPath}`);
|
|
24972
25383
|
}
|
|
24973
|
-
const dir =
|
|
24974
|
-
if (!
|
|
24975
|
-
|
|
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 (!
|
|
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(
|
|
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 (!
|
|
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(
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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 (
|
|
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
|
-
|
|
26616
|
+
mkdirSync8(claudeDir, { recursive: true });
|
|
26206
26617
|
const destPath = join10(claudeDir, ".claude.json");
|
|
26207
|
-
if (!
|
|
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
|
-
|
|
26641
|
+
mkdirSync8(telegramDir, { recursive: true });
|
|
26231
26642
|
const accessPath = join10(telegramDir, "access.json");
|
|
26232
|
-
|
|
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
|
-
|
|
26650
|
+
mkdirSync8(telegramDir, { recursive: true });
|
|
26240
26651
|
const envPath = join10(telegramDir, ".env");
|
|
26241
|
-
|
|
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
|
-
|
|
26664
|
+
mkdirSync8(dir, { recursive: true });
|
|
26254
26665
|
const config = { userId };
|
|
26255
26666
|
if (username) {
|
|
26256
26667
|
config.username = username;
|
|
26257
26668
|
}
|
|
26258
|
-
|
|
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 (!
|
|
26677
|
+
if (!existsSync15(configPath)) {
|
|
26267
26678
|
return null;
|
|
26268
26679
|
}
|
|
26269
26680
|
try {
|
|
26270
|
-
const raw =
|
|
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 (!
|
|
26693
|
+
if (!existsSync15(configPath)) {
|
|
26283
26694
|
return;
|
|
26284
26695
|
}
|
|
26285
26696
|
try {
|
|
26286
|
-
const raw =
|
|
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
|
-
|
|
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 (!
|
|
26718
|
+
if (!existsSync15(configPath)) {
|
|
26308
26719
|
return;
|
|
26309
26720
|
}
|
|
26310
26721
|
try {
|
|
26311
|
-
const raw =
|
|
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
|
-
|
|
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
|
-
|
|
26747
|
+
mkdirSync8(claudeDir, { recursive: true });
|
|
26337
26748
|
const configPath = join10(claudeDir, ".claude.json");
|
|
26338
|
-
if (!
|
|
26749
|
+
if (!existsSync15(configPath)) {
|
|
26339
26750
|
const minimal = {
|
|
26340
26751
|
hasCompletedOnboarding: true,
|
|
26341
26752
|
numStartups: 1
|
|
26342
26753
|
};
|
|
26343
|
-
|
|
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
|
-
|
|
26355
|
-
if (
|
|
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
|
|
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
|
-
|
|
26791
|
+
mkdirSync9(reposDir, { recursive: true });
|
|
26381
26792
|
const clonePath = bareClonePath(slug);
|
|
26382
|
-
if (!
|
|
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
|
|
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 (!
|
|
26472
|
-
|
|
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 (
|
|
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
|
|
26565
|
-
mkdirSync as
|
|
26566
|
-
writeFileSync as
|
|
26975
|
+
existsSync as existsSync18,
|
|
26976
|
+
mkdirSync as mkdirSync11,
|
|
26977
|
+
writeFileSync as writeFileSync6,
|
|
26567
26978
|
appendFileSync as appendFileSync2,
|
|
26568
|
-
readFileSync as
|
|
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
|
|
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
|
-
|
|
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 (!
|
|
26669
|
-
|
|
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 (
|
|
27091
|
+
if (existsSync18(agentDir))
|
|
26681
27092
|
paths.push(agentDir);
|
|
26682
|
-
if (
|
|
27093
|
+
if (existsSync18(logsDir))
|
|
26683
27094
|
paths.push(logsDir);
|
|
26684
|
-
if (
|
|
27095
|
+
if (existsSync18(auditDir))
|
|
26685
27096
|
paths.push(auditDir);
|
|
26686
|
-
if (
|
|
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
|
-
|
|
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 (!
|
|
27170
|
+
if (!existsSync18(envPath))
|
|
26760
27171
|
return;
|
|
26761
|
-
const current =
|
|
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
|
-
|
|
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
|
-
|
|
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 (
|
|
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 (
|
|
27219
|
+
if (existsSync18(globalFile) && !existsSync18(agentFile)) {
|
|
26809
27220
|
try {
|
|
26810
27221
|
if (useSwitchroomPlugin && file === "installed_plugins.json") {
|
|
26811
|
-
const scrubbed = stripOfficialTelegramPlugin(
|
|
26812
|
-
|
|
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
|
|
27234
|
+
return existsSync18(override);
|
|
26824
27235
|
}
|
|
26825
|
-
return
|
|
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 (!
|
|
27289
|
+
if (!existsSync18(sidecarPath))
|
|
26879
27290
|
return renderedBase;
|
|
26880
|
-
const sidecar =
|
|
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 (
|
|
26899
|
-
const sidecar =
|
|
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 (!
|
|
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(
|
|
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
|
-
|
|
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 (!
|
|
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(
|
|
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(
|
|
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(
|
|
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 (!
|
|
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
|
-
|
|
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() &&
|
|
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(
|
|
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(
|
|
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 (!
|
|
27703
|
+
if (!existsSync18(src))
|
|
27293
27704
|
return;
|
|
27294
|
-
|
|
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 (
|
|
27721
|
+
if (existsSync18(filePath)) {
|
|
27311
27722
|
try {
|
|
27312
|
-
if (
|
|
27723
|
+
if (readFileSync15(filePath, "utf-8") === content)
|
|
27313
27724
|
return false;
|
|
27314
27725
|
} catch {}
|
|
27315
27726
|
}
|
|
27316
|
-
|
|
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 (!
|
|
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 (
|
|
27769
|
+
if (readFileSync15(d, "utf-8") !== expected)
|
|
27359
27770
|
return false;
|
|
27360
|
-
} else if (!
|
|
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 (!
|
|
27783
|
+
if (!existsSync18(profileWorkspaceDir)) {
|
|
27373
27784
|
return;
|
|
27374
27785
|
}
|
|
27375
27786
|
const agentWorkspaceDir = join12(params.agentDir, "workspace");
|
|
27376
|
-
|
|
27787
|
+
mkdirSync11(agentWorkspaceDir, { recursive: true });
|
|
27377
27788
|
const walk = (relDir) => {
|
|
27378
27789
|
const srcDir = join12(profileWorkspaceDir, relDir);
|
|
27379
|
-
if (!
|
|
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
|
-
|
|
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 =
|
|
27404
|
-
const current = existed ?
|
|
27814
|
+
const existed = existsSync18(destPath);
|
|
27815
|
+
const current = existed ? readFileSync15(destPath, "utf8") : null;
|
|
27405
27816
|
if (current !== rendered) {
|
|
27406
|
-
|
|
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 (!
|
|
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 (!
|
|
27847
|
+
if (!existsSync18(agentsMd))
|
|
27437
27848
|
return;
|
|
27438
27849
|
const stat = lstatSync4(agentsMd);
|
|
27439
27850
|
if (stat.isSymbolicLink())
|
|
27440
27851
|
return;
|
|
27441
|
-
if (
|
|
27852
|
+
if (existsSync18(claudeMd)) {
|
|
27442
27853
|
return;
|
|
27443
27854
|
}
|
|
27444
|
-
const content =
|
|
27445
|
-
|
|
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 (!
|
|
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 (
|
|
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 (
|
|
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
|
-
|
|
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 (!
|
|
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 (
|
|
27548
|
-
expectedSettings = renderHindsightSettingsOverrides(
|
|
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 (
|
|
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 (!
|
|
27985
|
+
if (!existsSync18(settingsPath))
|
|
27575
27986
|
return;
|
|
27576
27987
|
let raw;
|
|
27577
27988
|
try {
|
|
27578
|
-
raw =
|
|
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
|
-
|
|
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 (
|
|
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 (
|
|
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 (
|
|
27993
|
-
const settings = JSON.parse(
|
|
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 (
|
|
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
|
-
|
|
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
|
-
|
|
28617
|
+
mkdirSync11(persistentHomeDir, { recursive: true });
|
|
28207
28618
|
for (const sub of [".local/bin", ".npm-global", "bin"]) {
|
|
28208
|
-
|
|
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 (!
|
|
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
|
-
|
|
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 (
|
|
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 (
|
|
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 (
|
|
28357
|
-
if (
|
|
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
|
-
` + "
|
|
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
|
|
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 (!
|
|
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 && !
|
|
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
|
-
|
|
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 (
|
|
28857
|
-
|
|
28858
|
-
const legacyContent =
|
|
28859
|
-
|
|
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 =
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
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 (
|
|
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 =
|
|
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
|
-
|
|
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 (
|
|
29084
|
-
const before =
|
|
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 (
|
|
29557
|
+
if (existsSync18(accessPath)) {
|
|
29147
29558
|
try {
|
|
29148
|
-
const access = JSON.parse(
|
|
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
|
-
|
|
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 =
|
|
29617
|
+
const before2 = existsSync18(mdPath) ? readFileSync15(mdPath, "utf-8") : "";
|
|
29207
29618
|
if (content !== before2) {
|
|
29208
|
-
|
|
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 (
|
|
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 (
|
|
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 =
|
|
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
|
-
|
|
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 (
|
|
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 (
|
|
29371
|
-
if (
|
|
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 (
|
|
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 =
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
29512
|
-
|
|
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
|
-
|
|
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 =
|
|
29935
|
+
const recordedFingerprint = existsSync18(fingerprintPath) ? readFileSync15(fingerprintPath, "utf-8").trim() : null;
|
|
29525
29936
|
if (recordedFingerprint === prevManagedHash) {
|
|
29526
|
-
|
|
29527
|
-
|
|
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
|
-
|
|
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
|
-
|
|
29541
|
-
|
|
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 (!
|
|
29551
|
-
|
|
29552
|
-
|
|
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 =
|
|
29967
|
+
const prev = readFileSync15(filePath, "utf-8");
|
|
29557
29968
|
if (prev === next) {
|
|
29558
29969
|
try {
|
|
29559
|
-
|
|
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 =
|
|
29976
|
+
const recordedFingerprint = existsSync18(fingerprintPath) ? readFileSync15(fingerprintPath, "utf-8").trim() : null;
|
|
29566
29977
|
if (recordedFingerprint === prevHash) {
|
|
29567
|
-
|
|
29568
|
-
|
|
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
|
-
|
|
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
|
-
|
|
29582
|
-
|
|
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 (!
|
|
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(
|
|
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
|
-
|
|
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
|
|
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).
|
|
29758
|
-
|
|
29759
|
-
|
|
29760
|
-
|
|
29761
|
-
|
|
29762
|
-
|
|
29763
|
-
|
|
29764
|
-
|
|
29765
|
-
|
|
29766
|
-
|
|
29767
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
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 };
|