switchroom 0.17.6 → 0.17.10
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 +38 -4
- package/dist/auth-broker/index.js +302 -203
- package/dist/cli/notion-write-pretool.mjs +35 -2
- package/dist/cli/switchroom.js +1178 -576
- package/dist/host-control/main.js +148 -14
- package/dist/vault/approvals/kernel-server.js +140 -55
- package/dist/vault/broker/server.js +142 -57
- package/package.json +1 -1
- package/profiles/_base/start.sh.hbs +50 -6
- package/profiles/default/CLAUDE.md +116 -0
- package/skills/mental-model-curator/SKILL.md +162 -0
- package/telegram-plugin/bridge/bridge.ts +80 -1
- package/telegram-plugin/bridge/ipc-client.ts +19 -0
- package/telegram-plugin/bridge/permission-ledger.ts +61 -0
- package/telegram-plugin/consolidation-legibility.ts +279 -0
- package/telegram-plugin/dist/bridge/bridge.js +85 -1
- package/telegram-plugin/dist/gateway/gateway.js +2565 -610
- package/telegram-plugin/dist/server.js +86 -2
- package/telegram-plugin/feed-heartbeat-climb.ts +206 -0
- package/telegram-plugin/gateway/activity-card-store.ts +293 -0
- package/telegram-plugin/gateway/gateway.ts +1376 -82
- package/telegram-plugin/gateway/inbound-spool.ts +22 -0
- package/telegram-plugin/gateway/mental-model-propose-card.ts +69 -0
- package/telegram-plugin/gateway/mental-model-propose-diff.ts +171 -0
- package/telegram-plugin/gateway/mental-model-propose-inbound-builders.ts +147 -0
- package/telegram-plugin/gateway/mental-model-propose-resolve.ts +201 -0
- package/telegram-plugin/gateway/missed-approvals-card.ts +161 -0
- package/telegram-plugin/gateway/missed-approvals-store.ts +167 -0
- package/telegram-plugin/gateway/permission-rearm.ts +115 -0
- package/telegram-plugin/gateway/scoped-grant-store.ts +89 -0
- package/telegram-plugin/memory-legibility.ts +217 -0
- package/telegram-plugin/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -0
- package/telegram-plugin/scoped-approval.ts +59 -0
- package/telegram-plugin/silent-end.ts +78 -0
- package/telegram-plugin/subagent-watcher.ts +60 -6
- package/telegram-plugin/tests/activity-card-store.test.ts +436 -0
- package/telegram-plugin/tests/activity-card-wiring.test.ts +88 -0
- package/telegram-plugin/tests/consolidation-legibility.test.ts +224 -0
- package/telegram-plugin/tests/emission-authority-facade.test.ts +25 -10
- package/telegram-plugin/tests/feed-heartbeat-liveness-open.test.ts +33 -9
- package/telegram-plugin/tests/gateway-boot-marker-clear.test.ts +3 -3
- package/telegram-plugin/tests/inbound-spool.test.ts +105 -0
- package/telegram-plugin/tests/memory-legibility.test.ts +216 -0
- package/telegram-plugin/tests/mental-model-propose-callback-gate.test.ts +67 -0
- package/telegram-plugin/tests/mental-model-propose-card.test.ts +56 -0
- package/telegram-plugin/tests/mental-model-propose-diff.test.ts +201 -0
- package/telegram-plugin/tests/mental-model-propose-inbound-builders.test.ts +68 -0
- package/telegram-plugin/tests/mental-model-propose-resolve.test.ts +157 -0
- package/telegram-plugin/tests/missed-approvals-card.test.ts +145 -0
- package/telegram-plugin/tests/missed-approvals-store.test.ts +147 -0
- package/telegram-plugin/tests/missed-approvals-wiring.test.ts +89 -0
- package/telegram-plugin/tests/permission-ledger.test.ts +166 -0
- package/telegram-plugin/tests/permission-no-repeat-wiring.test.ts +1 -1
- package/telegram-plugin/tests/permission-rearm-wiring.test.ts +175 -0
- package/telegram-plugin/tests/permission-rearm.test.ts +126 -0
- package/telegram-plugin/tests/scoped-grant-persist.test.ts +223 -0
- package/telegram-plugin/tests/silent-end-transport.test.ts +290 -0
- package/telegram-plugin/tests/silent-turn-climb-transport.test.ts +337 -0
- package/telegram-plugin/tests/subagent-watcher.test.ts +139 -0
- package/telegram-plugin/tests/worktree-watch-cwds.test.ts +103 -0
- package/telegram-plugin/uat/assertions.ts +88 -4
- package/telegram-plugin/uat/feed-matcher.test.ts +69 -0
- package/telegram-plugin/uat/scenarios/fuzz-liveness-climb-dm.test.ts +155 -0
- package/telegram-plugin/uat/scenarios/jtbd-directive-capture-nudge-dm.test.ts +185 -0
- package/telegram-plugin/uat/scenarios/jtbd-liveness-climb-channel.test.ts +192 -0
- package/telegram-plugin/uat/scenarios/jtbd-liveness-climb-dm.test.ts +220 -0
- package/telegram-plugin/uat/scenarios/jtbd-liveness-narration-channel.test.ts +137 -0
- package/telegram-plugin/uat/scenarios/jtbd-liveness-narration-dm.test.ts +148 -0
- package/telegram-plugin/uat/scenarios/jtbd-memory-legibility-channel.test.ts +66 -0
- package/telegram-plugin/uat/scenarios/jtbd-memory-legibility-dm.test.ts +61 -0
- package/telegram-plugin/uat/scenarios/silent-end-recovery-channel.test.ts +136 -0
- package/telegram-plugin/uat/scenarios/silent-end-recovery-dm.test.ts +24 -2
- package/telegram-plugin/worktree-watch-cwds.ts +60 -0
- package/vendor/hindsight-memory/hooks/hooks.json +9 -0
- package/vendor/hindsight-memory/scripts/__pycache__/directive_verify.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/__pycache__/drain_pending.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/__pycache__/recall.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/__pycache__/retain.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/__pycache__/session_end.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/directive_verify.py +445 -0
- package/vendor/hindsight-memory/scripts/lib/__pycache__/__init__.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/lib/__pycache__/bank.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/lib/__pycache__/client.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/lib/__pycache__/config.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/lib/__pycache__/content.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/lib/__pycache__/daemon.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/lib/__pycache__/directives.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/lib/__pycache__/gateway_ipc.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/lib/__pycache__/llm.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/lib/__pycache__/pending.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/lib/__pycache__/state.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/lib/__pycache__/switchroom_envelope.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/lib/config.py +37 -0
- package/vendor/hindsight-memory/scripts/lib/directives.py +88 -0
- package/vendor/hindsight-memory/scripts/lib/switchroom_envelope.py +77 -0
- package/vendor/hindsight-memory/scripts/recall.py +153 -4
- package/vendor/hindsight-memory/scripts/retain.py +17 -0
- package/vendor/hindsight-memory/scripts/setup_hooks.py +9 -0
- package/vendor/hindsight-memory/scripts/tests/__pycache__/__init__.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/tests/__pycache__/test_config_client_casts.cpython-313-pytest-9.1.1.pyc +0 -0
- package/vendor/hindsight-memory/scripts/tests/__pycache__/test_config_client_casts.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/tests/__pycache__/test_directive_capture_nudge.cpython-313-pytest-9.1.1.pyc +0 -0
- package/vendor/hindsight-memory/scripts/tests/__pycache__/test_directive_capture_nudge.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/tests/__pycache__/test_directive_verify.cpython-313-pytest-9.1.1.pyc +0 -0
- package/vendor/hindsight-memory/scripts/tests/__pycache__/test_directive_verify.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/tests/__pycache__/test_directives.cpython-313-pytest-9.1.1.pyc +0 -0
- package/vendor/hindsight-memory/scripts/tests/__pycache__/test_directives.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/tests/__pycache__/test_gateway_ipc.cpython-313-pytest-9.1.1.pyc +0 -0
- package/vendor/hindsight-memory/scripts/tests/__pycache__/test_gateway_ipc.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/tests/__pycache__/test_recall_context_slice.cpython-313-pytest-9.1.1.pyc +0 -0
- package/vendor/hindsight-memory/scripts/tests/__pycache__/test_recall_context_slice.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/tests/__pycache__/test_recall_integration.cpython-313-pytest-9.1.1.pyc +0 -0
- package/vendor/hindsight-memory/scripts/tests/__pycache__/test_recall_integration.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/tests/__pycache__/test_recall_tag_filters.cpython-313-pytest-9.1.1.pyc +0 -0
- package/vendor/hindsight-memory/scripts/tests/__pycache__/test_recall_tag_filters.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/tests/__pycache__/test_recall_topic_filter.cpython-313-pytest-9.1.1.pyc +0 -0
- package/vendor/hindsight-memory/scripts/tests/__pycache__/test_recall_topic_filter.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/tests/__pycache__/test_recall_trivial_skip.cpython-313-pytest-9.1.1.pyc +0 -0
- package/vendor/hindsight-memory/scripts/tests/__pycache__/test_recall_trivial_skip.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/tests/__pycache__/test_retain_window.cpython-313-pytest-9.1.1.pyc +0 -0
- package/vendor/hindsight-memory/scripts/tests/__pycache__/test_retain_window.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/tests/__pycache__/test_sender_routing.cpython-313-pytest-9.1.1.pyc +0 -0
- package/vendor/hindsight-memory/scripts/tests/__pycache__/test_sender_routing.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/tests/__pycache__/test_switchroom_envelope.cpython-313-pytest-9.1.1.pyc +0 -0
- package/vendor/hindsight-memory/scripts/tests/test_directive_capture_nudge.py +185 -0
- package/vendor/hindsight-memory/scripts/tests/test_directive_verify.py +516 -0
- package/vendor/hindsight-memory/scripts/tests/test_directives.py +49 -0
- package/vendor/hindsight-memory/scripts/tests/test_retain_window.py +66 -1
- package/vendor/hindsight-memory/scripts/tests/test_switchroom_envelope.py +69 -0
- package/vendor/hindsight-memory/tests/__pycache__/conftest.cpython-313-pytest-9.0.3.pyc +0 -0
- package/vendor/hindsight-memory/tests/__pycache__/conftest.cpython-313-pytest-9.1.1.pyc +0 -0
- package/vendor/hindsight-memory/tests/__pycache__/test_bank.cpython-313-pytest-9.1.1.pyc +0 -0
- package/vendor/hindsight-memory/tests/__pycache__/test_bank.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/tests/__pycache__/test_client.cpython-313-pytest-9.1.1.pyc +0 -0
- package/vendor/hindsight-memory/tests/__pycache__/test_client.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/tests/__pycache__/test_config.cpython-313-pytest-9.0.3.pyc +0 -0
- package/vendor/hindsight-memory/tests/__pycache__/test_config.cpython-313-pytest-9.1.1.pyc +0 -0
- package/vendor/hindsight-memory/tests/__pycache__/test_config.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/tests/__pycache__/test_content.cpython-313-pytest-9.1.1.pyc +0 -0
- package/vendor/hindsight-memory/tests/__pycache__/test_content.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/tests/__pycache__/test_drain_pending.cpython-313-pytest-9.1.1.pyc +0 -0
- package/vendor/hindsight-memory/tests/__pycache__/test_drain_pending.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/tests/__pycache__/test_hooks.cpython-313-pytest-9.1.1.pyc +0 -0
- package/vendor/hindsight-memory/tests/__pycache__/test_hooks.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/tests/__pycache__/test_manifest.cpython-313-pytest-9.1.1.pyc +0 -0
- package/vendor/hindsight-memory/tests/__pycache__/test_manifest.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/tests/__pycache__/test_pending.cpython-313-pytest-9.1.1.pyc +0 -0
- package/vendor/hindsight-memory/tests/__pycache__/test_pending.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/tests/__pycache__/test_recall_exit_codes.cpython-313-pytest-9.1.1.pyc +0 -0
- package/vendor/hindsight-memory/tests/__pycache__/test_recall_exit_codes.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/tests/__pycache__/test_session_end_pending.cpython-313-pytest-9.1.1.pyc +0 -0
- package/vendor/hindsight-memory/tests/__pycache__/test_session_end_pending.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/tests/__pycache__/test_state.cpython-313-pytest-9.1.1.pyc +0 -0
- package/vendor/hindsight-memory/tests/__pycache__/test_state.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/tests/test_recall_exit_codes.py +49 -2
package/dist/cli/switchroom.js
CHANGED
|
@@ -13656,8 +13656,35 @@ var init_schema = __esm(() => {
|
|
|
13656
13656
|
auto_recall: exports_external.boolean().default(true).describe("Auto-search memories before each response"),
|
|
13657
13657
|
file: exports_external.boolean().default(true).describe("Maintain a curated workspace MEMORY.md file (seeded once, " + "auto-loaded every turn). Set false for hindsight-only memory: " + "the file is not seeded or re-created, so once migrated into " + "Hindsight and deleted it stays gone. Recall + directives carry " + "the memory instead. Cascade: override (per-agent wins over default)."),
|
|
13658
13658
|
isolation: exports_external.enum(["default", "strict"]).default("default").describe("strict = never shared cross-agent, default = eligible for reflect"),
|
|
13659
|
-
bank_mission: exports_external.string().optional().describe("Bank-level mission statement used during recall to contextualize results"),
|
|
13660
|
-
|
|
13659
|
+
bank_mission: exports_external.string().optional().describe("Bank-level mission statement used during recall to contextualize " + "results. NOTE: this is an alias for the Hindsight engine's " + "`reflect_mission` field (verified live: switchroom's bank_mission " + "lands in `config.reflect_mission`). Prefer `reflect_mission` going " + "forward; `bank_mission` is retained for back-compat. If both are " + "set, `reflect_mission` wins. Cascade: override."),
|
|
13660
|
+
reflect_mission: exports_external.string().optional().describe("Mission/context steering Hindsight Reflect operations (the bank's " + "'who am I / what matters' framing applied during recall). The " + "engine-accurate name for what `bank_mission` sets. Cascade: override."),
|
|
13661
|
+
retain_mission: exports_external.string().optional().describe("Instructions for the fact extraction LLM during retain. Cascade: override."),
|
|
13662
|
+
mental_models: exports_external.array(exports_external.object({
|
|
13663
|
+
name: exports_external.string().min(1).describe("Stable model name (identity key for idempotent ensure). Two " + "declarations with the same name in one agent are rejected."),
|
|
13664
|
+
source_query: exports_external.string().min(1).max(2000).describe("The reflection query the model answers, semantically " + "refreshed from the bank's content. Capped at 2000 chars \u2014 a " + "standing reflection query, not a document; the ceiling also " + "bounds what an agent-proposed model can smuggle past the " + "operator approval card."),
|
|
13665
|
+
refresh_after_consolidation: exports_external.boolean().optional().describe("Refresh this model after each consolidation. Defaults OFF \u2014 " + "refresh adds bounded background model-spend + timeout risk " + "(RFC Phase 5), so it is opt-in per model."),
|
|
13666
|
+
max_tokens: exports_external.number().int().positive().max(8192).optional().describe("Cap on the synthesized model's token size. Upper-bounded at " + "8192 \u2014 a mental model is a standing summary, not a corpus; the " + "ceiling also caps what an agent-proposed model can request.")
|
|
13667
|
+
})).superRefine((models, ctx) => {
|
|
13668
|
+
const seen = new Set;
|
|
13669
|
+
for (let i = 0;i < models.length; i++) {
|
|
13670
|
+
const key = models[i].name;
|
|
13671
|
+
if (seen.has(key)) {
|
|
13672
|
+
ctx.addIssue({
|
|
13673
|
+
code: exports_external.ZodIssueCode.custom,
|
|
13674
|
+
path: [i, "name"],
|
|
13675
|
+
message: `duplicate mental_models name "${key}" \u2014 model names must be ` + `unique within an agent (they are the idempotent-ensure key)`
|
|
13676
|
+
});
|
|
13677
|
+
}
|
|
13678
|
+
seen.add(key);
|
|
13679
|
+
}
|
|
13680
|
+
}).optional().describe("Operator-declared, per-specialist Hindsight mental models (RFC " + "Phase 5). Named, opt-in curated reflections this agent's bank should " + "carry \u2014 e.g. a coach's 'training-plan-state' or a lawyer's " + "'open-matters'. Ensured idempotently at scaffold/reconcile: NOTHING " + "is created unless declared here (zero declarations = zero models, " + "matching post-#2447 behaviour), and no fixed identity model is " + "reintroduced \u2014 'who the user is' stays owned by dedicated profile " + "banks (users.*.profile_bank), never a per-agent model. Per-agent " + "ONLY: intentionally not accepted at the defaults/profile tier, so a " + "model can never be fleet-seeded \u2014 each specialist opts in on its own " + "(the invariant-clean inverse of the retired blind auto-seeding)."),
|
|
13681
|
+
observations_mission: exports_external.string().optional().describe("Steers what the observation-consolidation LLM synthesises from raw " + "facts (the higher-order 'what patterns matter' lens). Cascade: override."),
|
|
13682
|
+
disposition: exports_external.object({
|
|
13683
|
+
skepticism: exports_external.number().int().min(1).max(5).optional().describe("How much the bank doubts unverified claims (1-5; engine default 3)."),
|
|
13684
|
+
literalism: exports_external.number().int().min(1).max(5).optional().describe("How literally the bank reads statements vs inferring intent (1-5; engine default 3)."),
|
|
13685
|
+
empathy: exports_external.number().int().min(1).max(5).optional().describe("How much the bank weights emotional/relational context (1-5; engine default 3).")
|
|
13686
|
+
}).optional().describe("Personality traits (1-5 each) steering how this bank frames recall, " + "reflect, and observation synthesis \u2014 a coach leans empathy-high, a " + "lawyer/analyst leans skepticism/literalism-high. Maps to the engine's " + "flat `disposition_skepticism`/`_literalism`/`_empathy` fields. " + "Cascade: per-key merge (an agent overrides individual traits and " + "inherits the rest, matching `recall`)."),
|
|
13687
|
+
directive_capture_nudge: exports_external.boolean().optional().describe("Deterministic directive-capture nudge (issue #2848 Stage B). When " + "on (switchroom default true \u2014 Stage A measured a ~55% miss rate on " + "durable corrections), the auto-recall hook regex-detects correction " + '/ standing-rule-shaped inbound ("always/never \u2026", "from now on \u2026", ' + `"stop doing \u2026", a stated preference, "that's wrong, it's \u2026") and ` + "appends a terse advisory to the turn's context telling the model to " + "persist the rule with mcp__hindsight__create_directive if it IS " + "durable. Detection is pure regex \u2014 the model does the judgment " + "in-session and calls create_directive itself (no model callsite, no " + "silent hook-side write). Set false to disable per-agent. " + "Cascade: override (per-agent wins over default)."),
|
|
13661
13688
|
recall: exports_external.object({
|
|
13662
13689
|
max_memories: exports_external.number().int().min(0).optional().describe("Cap on the number of memories injected into the prompt by " + "auto-recall, regardless of token budget. Plugin default is 12. " + "0 disables the cap (all memories Hindsight returns are injected)."),
|
|
13663
13690
|
cache_ttl_secs: exports_external.number().int().min(0).optional().describe("Per-session recall cache TTL in seconds. When > 0, identical " + "(prompt, bank) within the same session reuse the cached recall " + "result instead of round-tripping to Hindsight. 0 disables. " + "Default is 600 (10 min) for switchroom-managed agents."),
|
|
@@ -13922,6 +13949,7 @@ var init_schema = __esm(() => {
|
|
|
13922
13949
|
auto_recall: exports_external.boolean().optional(),
|
|
13923
13950
|
file: exports_external.boolean().optional(),
|
|
13924
13951
|
isolation: exports_external.enum(["default", "strict"]).optional(),
|
|
13952
|
+
directive_capture_nudge: exports_external.boolean().optional(),
|
|
13925
13953
|
recall: exports_external.object({
|
|
13926
13954
|
max_memories: exports_external.number().int().min(0).optional(),
|
|
13927
13955
|
cache_ttl_secs: exports_external.number().int().min(0).optional(),
|
|
@@ -14617,6 +14645,11 @@ function mergeAgentConfig(defaultsIn, agentIn) {
|
|
|
14617
14645
|
continue;
|
|
14618
14646
|
if (k === "recall" && base.recall && typeof v === "object" && v !== null && !Array.isArray(v)) {
|
|
14619
14647
|
combined[k] = { ...base.recall, ...v };
|
|
14648
|
+
} else if (k === "disposition" && base.disposition && typeof v === "object" && v !== null && !Array.isArray(v)) {
|
|
14649
|
+
combined[k] = {
|
|
14650
|
+
...base.disposition,
|
|
14651
|
+
...v
|
|
14652
|
+
};
|
|
14620
14653
|
} else {
|
|
14621
14654
|
combined[k] = v;
|
|
14622
14655
|
}
|
|
@@ -15122,9 +15155,427 @@ var init_users = __esm(() => {
|
|
|
15122
15155
|
init_merge();
|
|
15123
15156
|
});
|
|
15124
15157
|
|
|
15158
|
+
// src/util/atomic.ts
|
|
15159
|
+
var exports_atomic = {};
|
|
15160
|
+
__export(exports_atomic, {
|
|
15161
|
+
writeConfigFileSync: () => writeConfigFileSync,
|
|
15162
|
+
atomicWriteJsonSync: () => atomicWriteJsonSync,
|
|
15163
|
+
atomicWriteFileSync: () => atomicWriteFileSync
|
|
15164
|
+
});
|
|
15165
|
+
import { randomBytes } from "node:crypto";
|
|
15166
|
+
import { closeSync as closeSync2, constants, fsyncSync, openSync as openSync2, renameSync, rmSync, writeSync } from "node:fs";
|
|
15167
|
+
function atomicWriteFileSync(destPath, contents, mode = 384) {
|
|
15168
|
+
const tmp = `${destPath}.tmp-${process.pid}-${randomBytes(4).toString("hex")}`;
|
|
15169
|
+
const buf = typeof contents === "string" ? Buffer.from(contents, "utf-8") : contents;
|
|
15170
|
+
let fd = null;
|
|
15171
|
+
try {
|
|
15172
|
+
fd = openSync2(tmp, TMP_OPEN_FLAGS, mode);
|
|
15173
|
+
writeSync(fd, buf, 0, buf.length, 0);
|
|
15174
|
+
fsyncSync(fd);
|
|
15175
|
+
closeSync2(fd);
|
|
15176
|
+
fd = null;
|
|
15177
|
+
renameSync(tmp, destPath);
|
|
15178
|
+
} catch (err) {
|
|
15179
|
+
if (fd !== null) {
|
|
15180
|
+
try {
|
|
15181
|
+
closeSync2(fd);
|
|
15182
|
+
} catch {}
|
|
15183
|
+
}
|
|
15184
|
+
try {
|
|
15185
|
+
rmSync(tmp, { force: true });
|
|
15186
|
+
} catch {}
|
|
15187
|
+
throw err;
|
|
15188
|
+
}
|
|
15189
|
+
}
|
|
15190
|
+
function atomicWriteJsonSync(destPath, value, mode = 384) {
|
|
15191
|
+
atomicWriteFileSync(destPath, JSON.stringify(value, null, 2) + `
|
|
15192
|
+
`, mode);
|
|
15193
|
+
}
|
|
15194
|
+
function writeConfigFileSync(destPath, contents, mode = 420) {
|
|
15195
|
+
try {
|
|
15196
|
+
atomicWriteFileSync(destPath, contents, mode);
|
|
15197
|
+
} catch (err) {
|
|
15198
|
+
const code = err.code;
|
|
15199
|
+
if (code !== "EBUSY" && code !== "EXDEV" && code !== "EINVAL") {
|
|
15200
|
+
throw err;
|
|
15201
|
+
}
|
|
15202
|
+
const buf = typeof contents === "string" ? Buffer.from(contents, "utf-8") : contents;
|
|
15203
|
+
let fd = null;
|
|
15204
|
+
try {
|
|
15205
|
+
fd = openSync2(destPath, constants.O_WRONLY | constants.O_TRUNC, mode);
|
|
15206
|
+
writeSync(fd, buf, 0, buf.length, 0);
|
|
15207
|
+
fsyncSync(fd);
|
|
15208
|
+
closeSync2(fd);
|
|
15209
|
+
fd = null;
|
|
15210
|
+
} catch (fallbackErr) {
|
|
15211
|
+
if (fd !== null) {
|
|
15212
|
+
try {
|
|
15213
|
+
closeSync2(fd);
|
|
15214
|
+
} catch {}
|
|
15215
|
+
}
|
|
15216
|
+
throw fallbackErr;
|
|
15217
|
+
}
|
|
15218
|
+
}
|
|
15219
|
+
}
|
|
15220
|
+
var TMP_OPEN_FLAGS;
|
|
15221
|
+
var init_atomic = __esm(() => {
|
|
15222
|
+
TMP_OPEN_FLAGS = constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL | (constants.O_NOFOLLOW ?? 0);
|
|
15223
|
+
});
|
|
15224
|
+
|
|
15225
|
+
// src/setup/hindsight.ts
|
|
15226
|
+
import { execFileSync } from "node:child_process";
|
|
15227
|
+
import { createServer } from "node:net";
|
|
15228
|
+
function hindsightImageRef(tag) {
|
|
15229
|
+
const t = tag?.trim();
|
|
15230
|
+
if (!t)
|
|
15231
|
+
return HINDSIGHT_IMAGE;
|
|
15232
|
+
const v = t.replace(/^v/, "");
|
|
15233
|
+
return `${HINDSIGHT_IMAGE_REPO}:v${v}`;
|
|
15234
|
+
}
|
|
15235
|
+
function isPortFree(port) {
|
|
15236
|
+
return new Promise((resolve4) => {
|
|
15237
|
+
const server = createServer();
|
|
15238
|
+
server.once("error", () => resolve4(false));
|
|
15239
|
+
server.once("listening", () => {
|
|
15240
|
+
server.close(() => resolve4(true));
|
|
15241
|
+
});
|
|
15242
|
+
server.listen(port, "127.0.0.1");
|
|
15243
|
+
});
|
|
15244
|
+
}
|
|
15245
|
+
function describePortHolder(port) {
|
|
15246
|
+
const probes = [
|
|
15247
|
+
["ss", ["-ltnpH", `sport = :${port}`]],
|
|
15248
|
+
["lsof", ["-nP", `-iTCP:${port}`, "-sTCP:LISTEN"]]
|
|
15249
|
+
];
|
|
15250
|
+
for (const [cmd, args] of probes) {
|
|
15251
|
+
try {
|
|
15252
|
+
const out = execFileSync(cmd, args, {
|
|
15253
|
+
stdio: "pipe",
|
|
15254
|
+
encoding: "utf-8"
|
|
15255
|
+
}).trim();
|
|
15256
|
+
if (!out)
|
|
15257
|
+
continue;
|
|
15258
|
+
const ssMatch = out.match(/users:\(\("([^"]+)",pid=(\d+)/);
|
|
15259
|
+
if (ssMatch)
|
|
15260
|
+
return `pid ${ssMatch[2]} (${ssMatch[1]})`;
|
|
15261
|
+
const firstDataLine = out.split(`
|
|
15262
|
+
`).map((l) => l.trim()).find((l) => l && !/^COMMAND\b/.test(l));
|
|
15263
|
+
if (firstDataLine) {
|
|
15264
|
+
const parts = firstDataLine.split(/\s+/);
|
|
15265
|
+
if (cmd === "lsof" && parts.length >= 2) {
|
|
15266
|
+
return `pid ${parts[1]} (${parts[0]})`;
|
|
15267
|
+
}
|
|
15268
|
+
return firstDataLine.slice(0, 80);
|
|
15269
|
+
}
|
|
15270
|
+
} catch {}
|
|
15271
|
+
}
|
|
15272
|
+
return null;
|
|
15273
|
+
}
|
|
15274
|
+
async function preflightHindsightPorts(ports) {
|
|
15275
|
+
for (const port of [ports.apiPort, ports.uiPort]) {
|
|
15276
|
+
if (!await isPortFree(port)) {
|
|
15277
|
+
return { port, holder: describePortHolder(port) };
|
|
15278
|
+
}
|
|
15279
|
+
}
|
|
15280
|
+
return null;
|
|
15281
|
+
}
|
|
15282
|
+
async function findFreePort(start, maxAttempts = 50) {
|
|
15283
|
+
for (let i = 0;i < maxAttempts; i++) {
|
|
15284
|
+
const port = start + i;
|
|
15285
|
+
if (port < 1024)
|
|
15286
|
+
continue;
|
|
15287
|
+
if (await isPortFree(port)) {
|
|
15288
|
+
return port;
|
|
15289
|
+
}
|
|
15290
|
+
}
|
|
15291
|
+
return null;
|
|
15292
|
+
}
|
|
15293
|
+
async function pickHindsightPorts() {
|
|
15294
|
+
if (await isPortFree(HINDSIGHT_DEFAULT_API_PORT) && await isPortFree(HINDSIGHT_DEFAULT_UI_PORT)) {
|
|
15295
|
+
return {
|
|
15296
|
+
apiPort: HINDSIGHT_DEFAULT_API_PORT,
|
|
15297
|
+
uiPort: HINDSIGHT_DEFAULT_UI_PORT
|
|
15298
|
+
};
|
|
15299
|
+
}
|
|
15300
|
+
const apiPort = await findFreePort(18888);
|
|
15301
|
+
const uiPort = await findFreePort(19999);
|
|
15302
|
+
if (apiPort === null || uiPort === null) {
|
|
15303
|
+
throw new Error("Could not find a free port for Hindsight. " + "Stop whatever is using 18888 / 19999 and retry.");
|
|
15304
|
+
}
|
|
15305
|
+
return { apiPort, uiPort };
|
|
15306
|
+
}
|
|
15307
|
+
function isDockerAvailable() {
|
|
15308
|
+
try {
|
|
15309
|
+
execFileSync("docker", ["--version"], { stdio: "pipe" });
|
|
15310
|
+
return true;
|
|
15311
|
+
} catch {
|
|
15312
|
+
return false;
|
|
15313
|
+
}
|
|
15314
|
+
}
|
|
15315
|
+
function isHindsightRunning() {
|
|
15316
|
+
try {
|
|
15317
|
+
const output = execFileSync("docker", ["ps", "--filter", "name=switchroom-hindsight", "--format", "{{.Status}}"], { stdio: "pipe", encoding: "utf-8" });
|
|
15318
|
+
return output.trim().length > 0;
|
|
15319
|
+
} catch {
|
|
15320
|
+
return false;
|
|
15321
|
+
}
|
|
15322
|
+
}
|
|
15323
|
+
function isHindsightContainerExists() {
|
|
15324
|
+
try {
|
|
15325
|
+
const output = execFileSync("docker", ["ps", "-a", "--filter", "name=switchroom-hindsight", "--format", "{{.Names}}"], { stdio: "pipe", encoding: "utf-8" });
|
|
15326
|
+
return output.trim().length > 0;
|
|
15327
|
+
} catch {
|
|
15328
|
+
return false;
|
|
15329
|
+
}
|
|
15330
|
+
}
|
|
15331
|
+
function startHindsight(ports, litellm, imageTag) {
|
|
15332
|
+
const apiPort = ports?.apiPort ?? HINDSIGHT_DEFAULT_API_PORT;
|
|
15333
|
+
const uiPort = ports?.uiPort ?? HINDSIGHT_DEFAULT_UI_PORT;
|
|
15334
|
+
const envArgs = [
|
|
15335
|
+
"-e",
|
|
15336
|
+
`HINDSIGHT_API_MAX_OBSERVATIONS_PER_SCOPE=${HINDSIGHT_DEFAULT_MAX_OBSERVATIONS_PER_SCOPE}`,
|
|
15337
|
+
"-e",
|
|
15338
|
+
"HINDSIGHT_API_LLM_PROVIDER=claude-code",
|
|
15339
|
+
"-e",
|
|
15340
|
+
`HINDSIGHT_API_LLM_MODEL=${HINDSIGHT_DEFAULT_MODEL}`,
|
|
15341
|
+
"-e",
|
|
15342
|
+
`ANTHROPIC_MODEL=${HINDSIGHT_DEFAULT_MODEL}`,
|
|
15343
|
+
"-e",
|
|
15344
|
+
`HINDSIGHT_API_MCP_STATELESS=${HINDSIGHT_DEFAULT_MCP_STATELESS}`,
|
|
15345
|
+
"-e",
|
|
15346
|
+
`HINDSIGHT_API_RERANKER_LOCAL_BUCKET_BATCHING=${HINDSIGHT_DEFAULT_RERANKER_BUCKET_BATCHING}`,
|
|
15347
|
+
"-e",
|
|
15348
|
+
`HINDSIGHT_API_RERANKER_MAX_CANDIDATES=${HINDSIGHT_DEFAULT_RERANKER_MAX_CANDIDATES}`,
|
|
15349
|
+
"-e",
|
|
15350
|
+
`HINDSIGHT_API_RERANKER_LOCAL_MAX_CONCURRENT=${HINDSIGHT_DEFAULT_RERANKER_LOCAL_MAX_CONCURRENT}`,
|
|
15351
|
+
"-e",
|
|
15352
|
+
`HINDSIGHT_API_RECALL_MAX_CONCURRENT=${HINDSIGHT_DEFAULT_RECALL_MAX_CONCURRENT}`,
|
|
15353
|
+
"-e",
|
|
15354
|
+
`HINDSIGHT_API_REFLECT_WALL_TIMEOUT=${HINDSIGHT_DEFAULT_REFLECT_WALL_TIMEOUT_S}`,
|
|
15355
|
+
"-e",
|
|
15356
|
+
`HINDSIGHT_API_CONSOLIDATION_LLM_BATCH_SIZE=${HINDSIGHT_DEFAULT_CONSOLIDATION_LLM_BATCH_SIZE}`,
|
|
15357
|
+
"-e",
|
|
15358
|
+
`HINDSIGHT_API_WORKER_CONSOLIDATION_MAX_SLOTS=${HINDSIGHT_DEFAULT_CONSOLIDATION_MAX_SLOTS}`,
|
|
15359
|
+
"-e",
|
|
15360
|
+
`HINDSIGHT_API_CONSOLIDATION_LLM_PARALLELISM=${HINDSIGHT_DEFAULT_CONSOLIDATION_LLM_PARALLELISM}`,
|
|
15361
|
+
"-e",
|
|
15362
|
+
`HINDSIGHT_API_CONSOLIDATION_MAX_MEMORIES_PER_ROUND=${HINDSIGHT_DEFAULT_CONSOLIDATION_MAX_MEMORIES_PER_ROUND}`
|
|
15363
|
+
];
|
|
15364
|
+
if (litellm) {
|
|
15365
|
+
envArgs.push("-e", `HINDSIGHT_API_PORT=${apiPort}`, "-e", `ANTHROPIC_BASE_URL=${litellm.baseUrl.replace(/\/+$/, "")}/anthropic`, "-e", `ANTHROPIC_CUSTOM_HEADERS=x-litellm-api-key: Bearer ${litellm.apiKey}
|
|
15366
|
+
x-litellm-customer-id: hindsight
|
|
15367
|
+
x-litellm-tags: service:hindsight`);
|
|
15368
|
+
}
|
|
15369
|
+
const args = [
|
|
15370
|
+
"run",
|
|
15371
|
+
"-d",
|
|
15372
|
+
"--name",
|
|
15373
|
+
"switchroom-hindsight",
|
|
15374
|
+
"--restart",
|
|
15375
|
+
"always",
|
|
15376
|
+
`--memory=${HINDSIGHT_DEFAULT_MEM_LIMIT}`,
|
|
15377
|
+
`--memory-reservation=${HINDSIGHT_DEFAULT_MEM_RESERVATION}`,
|
|
15378
|
+
`--pids-limit=${HINDSIGHT_DEFAULT_PIDS_LIMIT}`,
|
|
15379
|
+
`--shm-size=${HINDSIGHT_DEFAULT_SHM_SIZE}`,
|
|
15380
|
+
"--health-cmd",
|
|
15381
|
+
litellm ? `python3 -c 'import urllib.request,sys; sys.exit(0 if urllib.request.urlopen("http://localhost:${apiPort}/health",timeout=4).getcode()==200 else 1)'` : HINDSIGHT_HEALTHCHECK_CMD,
|
|
15382
|
+
"--health-interval",
|
|
15383
|
+
"30s",
|
|
15384
|
+
"--health-timeout",
|
|
15385
|
+
"5s",
|
|
15386
|
+
"--health-retries",
|
|
15387
|
+
"3",
|
|
15388
|
+
"--health-start-period",
|
|
15389
|
+
"60s"
|
|
15390
|
+
];
|
|
15391
|
+
if (litellm) {
|
|
15392
|
+
args.push("--network", "host");
|
|
15393
|
+
} else {
|
|
15394
|
+
args.push("-p", `127.0.0.1:${apiPort}:8888`, "-p", `127.0.0.1:${uiPort}:9999`);
|
|
15395
|
+
}
|
|
15396
|
+
args.push("-v", "switchroom-hindsight-data:/home/hindsight/.pg0", "-v", "switchroom-hindsight-backups:/backups", "-v", `${HINDSIGHT_BROKER_SOCK_VOLUME}:/run/switchroom/auth-broker`, "--tmpfs", `/run/claude-creds:rw,mode=0700,uid=${HINDSIGHT_DEFAULT_UID},gid=${HINDSIGHT_DEFAULT_UID}`, ...envArgs, hindsightImageRef(imageTag));
|
|
15397
|
+
execFileSync("docker", args, { stdio: "pipe" });
|
|
15398
|
+
}
|
|
15399
|
+
function stopHindsight() {
|
|
15400
|
+
try {
|
|
15401
|
+
execFileSync("docker", ["stop", "switchroom-hindsight"], { stdio: "pipe" });
|
|
15402
|
+
} catch {}
|
|
15403
|
+
try {
|
|
15404
|
+
execFileSync("docker", ["rm", "switchroom-hindsight"], { stdio: "pipe" });
|
|
15405
|
+
} catch {}
|
|
15406
|
+
}
|
|
15407
|
+
function pullHindsightImage(imageTag) {
|
|
15408
|
+
execFileSync("docker", ["pull", hindsightImageRef(imageTag)], { stdio: "inherit" });
|
|
15409
|
+
}
|
|
15410
|
+
function getRunningHindsightPorts() {
|
|
15411
|
+
const CONTAINER_API_PORT = 8888;
|
|
15412
|
+
const CONTAINER_UI_PORT = 9999;
|
|
15413
|
+
const readPort = (containerPort) => {
|
|
15414
|
+
try {
|
|
15415
|
+
const out = execFileSync("docker", ["port", "switchroom-hindsight", `${containerPort}/tcp`], { stdio: "pipe", encoding: "utf-8" });
|
|
15416
|
+
const m = out.split(`
|
|
15417
|
+
`).map((l) => l.trim()).find((l) => l.length > 0);
|
|
15418
|
+
if (!m)
|
|
15419
|
+
return null;
|
|
15420
|
+
const port = Number(m.slice(m.lastIndexOf(":") + 1));
|
|
15421
|
+
return Number.isInteger(port) && port > 0 ? port : null;
|
|
15422
|
+
} catch {
|
|
15423
|
+
return null;
|
|
15424
|
+
}
|
|
15425
|
+
};
|
|
15426
|
+
const apiPort = readPort(CONTAINER_API_PORT);
|
|
15427
|
+
if (apiPort !== null) {
|
|
15428
|
+
const uiPort = readPort(CONTAINER_UI_PORT) ?? (apiPort === HINDSIGHT_DEFAULT_API_PORT ? HINDSIGHT_DEFAULT_UI_PORT : 19999);
|
|
15429
|
+
return { apiPort, uiPort };
|
|
15430
|
+
}
|
|
15431
|
+
return getHindsightPortsFromEnv();
|
|
15432
|
+
}
|
|
15433
|
+
function getHindsightPortsFromEnv() {
|
|
15434
|
+
let env2;
|
|
15435
|
+
try {
|
|
15436
|
+
env2 = execFileSync("docker", ["inspect", "--format", "{{.Config.Env}}", "switchroom-hindsight"], { stdio: "pipe", encoding: "utf-8" });
|
|
15437
|
+
} catch {
|
|
15438
|
+
return null;
|
|
15439
|
+
}
|
|
15440
|
+
const readVar = (name) => {
|
|
15441
|
+
const re = new RegExp(`(?:^|[\\s\\[])${name}=([^\\s\\]]+)`);
|
|
15442
|
+
const m = env2.match(re);
|
|
15443
|
+
if (!m)
|
|
15444
|
+
return null;
|
|
15445
|
+
const n = Number(m[1]);
|
|
15446
|
+
return Number.isInteger(n) && n > 0 ? n : null;
|
|
15447
|
+
};
|
|
15448
|
+
const apiPort = readVar("HINDSIGHT_API_PORT");
|
|
15449
|
+
if (apiPort === null)
|
|
15450
|
+
return null;
|
|
15451
|
+
const uiPort = readVar("HINDSIGHT_UI_PORT") ?? (apiPort === HINDSIGHT_DEFAULT_API_PORT ? HINDSIGHT_DEFAULT_UI_PORT : 19999);
|
|
15452
|
+
return { apiPort, uiPort };
|
|
15453
|
+
}
|
|
15454
|
+
function getHindsightStatus() {
|
|
15455
|
+
try {
|
|
15456
|
+
const output = execFileSync("docker", ["ps", "-a", "--filter", "name=switchroom-hindsight", "--format", "{{.Status}}"], { stdio: "pipe", encoding: "utf-8" });
|
|
15457
|
+
const status = output.trim();
|
|
15458
|
+
return status.length > 0 ? status : null;
|
|
15459
|
+
} catch {
|
|
15460
|
+
return null;
|
|
15461
|
+
}
|
|
15462
|
+
}
|
|
15463
|
+
function generateHindsightComposeSnippet() {
|
|
15464
|
+
return [
|
|
15465
|
+
"services:",
|
|
15466
|
+
" switchroom-hindsight:",
|
|
15467
|
+
` image: ${HINDSIGHT_IMAGE}`,
|
|
15468
|
+
" container_name: switchroom-hindsight",
|
|
15469
|
+
" ports:",
|
|
15470
|
+
` - "127.0.0.1:${HINDSIGHT_DEFAULT_API_PORT}:8888"`,
|
|
15471
|
+
' - "127.0.0.1:19999:9999"',
|
|
15472
|
+
" environment:",
|
|
15473
|
+
` - HINDSIGHT_API_MAX_OBSERVATIONS_PER_SCOPE=${HINDSIGHT_DEFAULT_MAX_OBSERVATIONS_PER_SCOPE}`,
|
|
15474
|
+
" - HINDSIGHT_API_LLM_PROVIDER=claude-code",
|
|
15475
|
+
` - HINDSIGHT_API_LLM_MODEL=${HINDSIGHT_DEFAULT_MODEL}`,
|
|
15476
|
+
` - ANTHROPIC_MODEL=${HINDSIGHT_DEFAULT_MODEL}`,
|
|
15477
|
+
` - HINDSIGHT_API_MCP_STATELESS=${HINDSIGHT_DEFAULT_MCP_STATELESS}`,
|
|
15478
|
+
` - HINDSIGHT_API_RERANKER_LOCAL_BUCKET_BATCHING=${HINDSIGHT_DEFAULT_RERANKER_BUCKET_BATCHING}`,
|
|
15479
|
+
` - HINDSIGHT_API_RERANKER_MAX_CANDIDATES=${HINDSIGHT_DEFAULT_RERANKER_MAX_CANDIDATES}`,
|
|
15480
|
+
` - HINDSIGHT_API_RERANKER_LOCAL_MAX_CONCURRENT=${HINDSIGHT_DEFAULT_RERANKER_LOCAL_MAX_CONCURRENT}`,
|
|
15481
|
+
` - HINDSIGHT_API_RECALL_MAX_CONCURRENT=${HINDSIGHT_DEFAULT_RECALL_MAX_CONCURRENT}`,
|
|
15482
|
+
` - HINDSIGHT_API_REFLECT_WALL_TIMEOUT=${HINDSIGHT_DEFAULT_REFLECT_WALL_TIMEOUT_S}`,
|
|
15483
|
+
` - HINDSIGHT_API_CONSOLIDATION_LLM_BATCH_SIZE=${HINDSIGHT_DEFAULT_CONSOLIDATION_LLM_BATCH_SIZE}`,
|
|
15484
|
+
` - HINDSIGHT_API_WORKER_CONSOLIDATION_MAX_SLOTS=${HINDSIGHT_DEFAULT_CONSOLIDATION_MAX_SLOTS}`,
|
|
15485
|
+
` - HINDSIGHT_API_CONSOLIDATION_LLM_PARALLELISM=${HINDSIGHT_DEFAULT_CONSOLIDATION_LLM_PARALLELISM}`,
|
|
15486
|
+
` - HINDSIGHT_API_CONSOLIDATION_MAX_MEMORIES_PER_ROUND=${HINDSIGHT_DEFAULT_CONSOLIDATION_MAX_MEMORIES_PER_ROUND}`,
|
|
15487
|
+
` mem_limit: ${HINDSIGHT_DEFAULT_MEM_LIMIT}`,
|
|
15488
|
+
` mem_reservation: ${HINDSIGHT_DEFAULT_MEM_RESERVATION}`,
|
|
15489
|
+
` pids_limit: ${HINDSIGHT_DEFAULT_PIDS_LIMIT}`,
|
|
15490
|
+
` shm_size: ${HINDSIGHT_DEFAULT_SHM_SIZE}`,
|
|
15491
|
+
" healthcheck:",
|
|
15492
|
+
` test: ${JSON.stringify(["CMD", "python3", "-c", HINDSIGHT_HEALTHCHECK_PY])}`,
|
|
15493
|
+
" interval: 30s",
|
|
15494
|
+
" timeout: 5s",
|
|
15495
|
+
" retries: 3",
|
|
15496
|
+
" start_period: 60s",
|
|
15497
|
+
" volumes:",
|
|
15498
|
+
" - switchroom-hindsight-data:/home/hindsight/.pg0",
|
|
15499
|
+
" - switchroom-hindsight-backups:/backups",
|
|
15500
|
+
` - ${HINDSIGHT_BROKER_SOCK_VOLUME}:/run/switchroom/auth-broker`,
|
|
15501
|
+
" tmpfs:",
|
|
15502
|
+
` - /run/claude-creds:rw,mode=0700,uid=${HINDSIGHT_DEFAULT_UID},gid=${HINDSIGHT_DEFAULT_UID}`,
|
|
15503
|
+
" restart: always",
|
|
15504
|
+
"",
|
|
15505
|
+
"volumes:",
|
|
15506
|
+
" switchroom-hindsight-data:",
|
|
15507
|
+
" switchroom-hindsight-backups:",
|
|
15508
|
+
` ${HINDSIGHT_BROKER_SOCK_VOLUME}:`,
|
|
15509
|
+
" external: true",
|
|
15510
|
+
" # Bound by the switchroom-auth-broker singleton in the main",
|
|
15511
|
+
" # switchroom compose project. Declare an `auth.consumers[]`",
|
|
15512
|
+
` # entry named "${HINDSIGHT_CONSUMER_NAME}" in switchroom.yaml.`
|
|
15513
|
+
].join(`
|
|
15514
|
+
`);
|
|
15515
|
+
}
|
|
15516
|
+
async function ensureHindsightConsumer(configPath, account, uid = HINDSIGHT_DEFAULT_UID) {
|
|
15517
|
+
const fs = await import("node:fs");
|
|
15518
|
+
const { parseDocument, isMap, isSeq, YAMLMap, YAMLSeq } = await Promise.resolve().then(() => __toESM(require_dist(), 1));
|
|
15519
|
+
const { atomicWriteFileSync: atomicWriteFileSync2 } = await Promise.resolve().then(() => (init_atomic(), exports_atomic));
|
|
15520
|
+
const raw = fs.readFileSync(configPath, "utf-8");
|
|
15521
|
+
const doc = parseDocument(raw);
|
|
15522
|
+
const root = doc.contents;
|
|
15523
|
+
if (!isMap(root)) {
|
|
15524
|
+
return { added: false, reason: "switchroom.yaml root is not a map" };
|
|
15525
|
+
}
|
|
15526
|
+
const authRaw = root.get("auth", true);
|
|
15527
|
+
let authNode;
|
|
15528
|
+
if (isMap(authRaw)) {
|
|
15529
|
+
authNode = authRaw;
|
|
15530
|
+
} else {
|
|
15531
|
+
authNode = new YAMLMap;
|
|
15532
|
+
doc.setIn(["auth"], authNode);
|
|
15533
|
+
}
|
|
15534
|
+
const consumersRaw = authNode.get("consumers", true);
|
|
15535
|
+
let consumersNode;
|
|
15536
|
+
if (isSeq(consumersRaw)) {
|
|
15537
|
+
consumersNode = consumersRaw;
|
|
15538
|
+
} else {
|
|
15539
|
+
consumersNode = new YAMLSeq;
|
|
15540
|
+
doc.setIn(["auth", "consumers"], consumersNode);
|
|
15541
|
+
}
|
|
15542
|
+
for (const item of consumersNode.items) {
|
|
15543
|
+
if (isMap(item)) {
|
|
15544
|
+
const name = item.get("name");
|
|
15545
|
+
if (name === HINDSIGHT_CONSUMER_NAME) {
|
|
15546
|
+
return { added: false, reason: "already present" };
|
|
15547
|
+
}
|
|
15548
|
+
}
|
|
15549
|
+
}
|
|
15550
|
+
const entry = new YAMLMap;
|
|
15551
|
+
entry.set("name", HINDSIGHT_CONSUMER_NAME);
|
|
15552
|
+
if (account !== undefined)
|
|
15553
|
+
entry.set("account", account);
|
|
15554
|
+
entry.set("uid", uid);
|
|
15555
|
+
consumersNode.add(entry);
|
|
15556
|
+
const out = String(doc);
|
|
15557
|
+
const tail = out.endsWith(`
|
|
15558
|
+
`) ? out : out + `
|
|
15559
|
+
`;
|
|
15560
|
+
let mode = 420;
|
|
15561
|
+
try {
|
|
15562
|
+
mode = fs.statSync(configPath).mode & 511;
|
|
15563
|
+
} catch {}
|
|
15564
|
+
atomicWriteFileSync2(configPath, tail, mode);
|
|
15565
|
+
return { added: true, reason: "added" };
|
|
15566
|
+
}
|
|
15567
|
+
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_UID = 11000, HINDSIGHT_IMAGE_REPO = "ghcr.io/switchroom/switchroom-hindsight", HINDSIGHT_IMAGE, HINDSIGHT_DEFAULT_MODEL = "claude-sonnet-5", HINDSIGHT_DEFAULT_MCP_STATELESS = true, HINDSIGHT_BROKER_SOCK_VOLUME, 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_DEFAULT_CONSOLIDATION_LLM_BATCH_SIZE = 12, HINDSIGHT_DEFAULT_CONSOLIDATION_MAX_SLOTS = 1, HINDSIGHT_DEFAULT_CONSOLIDATION_LLM_PARALLELISM = 2, HINDSIGHT_DEFAULT_CONSOLIDATION_MAX_MEMORIES_PER_ROUND = 100, HINDSIGHT_DEFAULT_MEM_LIMIT = "8g", HINDSIGHT_DEFAULT_MEM_RESERVATION = "4g", HINDSIGHT_DEFAULT_PIDS_LIMIT = 1000, HINDSIGHT_DEFAULT_SHM_SIZE = "2g", HINDSIGHT_HEALTHCHECK_PY = 'import urllib.request,sys; sys.exit(0 if urllib.request.urlopen("http://localhost:8888/health",timeout=4).getcode()==200 else 1)', HINDSIGHT_HEALTHCHECK_CMD;
|
|
15568
|
+
var init_hindsight = __esm(() => {
|
|
15569
|
+
HINDSIGHT_DEFAULT_MCP_URL = `http://127.0.0.1:${HINDSIGHT_DEFAULT_API_PORT}/mcp/`;
|
|
15570
|
+
HINDSIGHT_DEFAULT_API_BASE_URL = HINDSIGHT_DEFAULT_MCP_URL.replace(/\/mcp\/?$/, "");
|
|
15571
|
+
HINDSIGHT_IMAGE = `${HINDSIGHT_IMAGE_REPO}:latest`;
|
|
15572
|
+
HINDSIGHT_BROKER_SOCK_VOLUME = `auth-broker-${HINDSIGHT_CONSUMER_NAME}-sock`;
|
|
15573
|
+
HINDSIGHT_HEALTHCHECK_CMD = `python3 -c '${HINDSIGHT_HEALTHCHECK_PY}'`;
|
|
15574
|
+
});
|
|
15575
|
+
|
|
15125
15576
|
// src/memory/hindsight.ts
|
|
15126
15577
|
function generateHindsightMcpConfig(collection, memoryConfig) {
|
|
15127
|
-
const url = memoryConfig.config?.url ??
|
|
15578
|
+
const url = memoryConfig.config?.url ?? HINDSIGHT_DEFAULT_MCP_URL;
|
|
15128
15579
|
return {
|
|
15129
15580
|
type: "http",
|
|
15130
15581
|
url,
|
|
@@ -15167,6 +15618,26 @@ function isHindsightEnabled(config) {
|
|
|
15167
15618
|
return false;
|
|
15168
15619
|
return config?.memory?.backend === "hindsight";
|
|
15169
15620
|
}
|
|
15621
|
+
function mergeDisposition(base, over) {
|
|
15622
|
+
if (!base && !over)
|
|
15623
|
+
return;
|
|
15624
|
+
const merged = { ...base ?? {}, ...over ?? {} };
|
|
15625
|
+
return Object.keys(merged).length > 0 ? merged : undefined;
|
|
15626
|
+
}
|
|
15627
|
+
function resolveBankMissionExtras(memory, profileName) {
|
|
15628
|
+
const pd = PROFILE_MEMORY_DEFAULTS[profileName] ?? {};
|
|
15629
|
+
const out = {};
|
|
15630
|
+
const reflect = memory?.reflect_mission ?? pd.reflect_mission;
|
|
15631
|
+
if (reflect != null)
|
|
15632
|
+
out.reflect_mission = reflect;
|
|
15633
|
+
const observations = memory?.observations_mission ?? pd.observations_mission;
|
|
15634
|
+
if (observations != null)
|
|
15635
|
+
out.observations_mission = observations;
|
|
15636
|
+
const disposition = mergeDisposition(pd.disposition, memory?.disposition);
|
|
15637
|
+
if (disposition)
|
|
15638
|
+
out.disposition = disposition;
|
|
15639
|
+
return out;
|
|
15640
|
+
}
|
|
15170
15641
|
async function parseSseOrJson(resp) {
|
|
15171
15642
|
const text = await resp.text();
|
|
15172
15643
|
const dataLine = text.split(`
|
|
@@ -15253,9 +15724,19 @@ async function probeHindsight(apiUrl, opts) {
|
|
|
15253
15724
|
clearTimeout(timeout);
|
|
15254
15725
|
}
|
|
15255
15726
|
}
|
|
15256
|
-
async function
|
|
15727
|
+
async function ensureMentalModel(apiUrl, bankId, spec, opts) {
|
|
15257
15728
|
const fetchImpl = opts?.fetchImpl ?? fetch;
|
|
15258
15729
|
const timeoutMs = opts?.timeoutMs ?? 5000;
|
|
15730
|
+
const createArguments = {
|
|
15731
|
+
name: spec.name,
|
|
15732
|
+
source_query: spec.sourceQuery
|
|
15733
|
+
};
|
|
15734
|
+
if (spec.refreshAfterConsolidation !== undefined) {
|
|
15735
|
+
createArguments.trigger_refresh_after_consolidation = spec.refreshAfterConsolidation;
|
|
15736
|
+
}
|
|
15737
|
+
if (spec.maxTokens !== undefined) {
|
|
15738
|
+
createArguments.max_tokens = spec.maxTokens;
|
|
15739
|
+
}
|
|
15259
15740
|
try {
|
|
15260
15741
|
const controller = new AbortController;
|
|
15261
15742
|
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
|
@@ -15312,7 +15793,7 @@ async function ensureUserProfileMentalModel(apiUrl, bankId, opts) {
|
|
|
15312
15793
|
if (models && typeof models === "string") {
|
|
15313
15794
|
const parsed = JSON.parse(models);
|
|
15314
15795
|
const items = Array.isArray(parsed?.items) ? parsed.items : [];
|
|
15315
|
-
if (items.some((m) => m?.name ===
|
|
15796
|
+
if (items.some((m) => m?.name === spec.name)) {
|
|
15316
15797
|
return { ok: true };
|
|
15317
15798
|
}
|
|
15318
15799
|
}
|
|
@@ -15333,10 +15814,7 @@ async function ensureUserProfileMentalModel(apiUrl, bankId, opts) {
|
|
|
15333
15814
|
method: "tools/call",
|
|
15334
15815
|
params: {
|
|
15335
15816
|
name: "create_mental_model",
|
|
15336
|
-
arguments:
|
|
15337
|
-
name: "user-profile",
|
|
15338
|
-
source_query: "What are the key facts, preferences, context, and communication style about the user I talk to? Summarize what matters for making the agent feel like it knows them."
|
|
15339
|
-
}
|
|
15817
|
+
arguments: createArguments
|
|
15340
15818
|
}
|
|
15341
15819
|
}),
|
|
15342
15820
|
signal: controller.signal
|
|
@@ -15360,6 +15838,33 @@ async function ensureUserProfileMentalModel(apiUrl, bankId, opts) {
|
|
|
15360
15838
|
return { ok: false, reason: String(err) };
|
|
15361
15839
|
}
|
|
15362
15840
|
}
|
|
15841
|
+
async function ensureUserProfileMentalModel(apiUrl, bankId, opts) {
|
|
15842
|
+
return ensureMentalModel(apiUrl, bankId, { name: "user-profile", sourceQuery: USER_PROFILE_SOURCE_QUERY }, opts);
|
|
15843
|
+
}
|
|
15844
|
+
async function ensureDeclaredMentalModels(apiUrl, bankId, models, opts) {
|
|
15845
|
+
if (!models || models.length === 0)
|
|
15846
|
+
return [];
|
|
15847
|
+
const outcomes = [];
|
|
15848
|
+
const seen = new Set;
|
|
15849
|
+
for (const m of models) {
|
|
15850
|
+
if (seen.has(m.name))
|
|
15851
|
+
continue;
|
|
15852
|
+
seen.add(m.name);
|
|
15853
|
+
const spec = {
|
|
15854
|
+
name: m.name,
|
|
15855
|
+
sourceQuery: m.source_query,
|
|
15856
|
+
refreshAfterConsolidation: m.refresh_after_consolidation,
|
|
15857
|
+
maxTokens: m.max_tokens
|
|
15858
|
+
};
|
|
15859
|
+
try {
|
|
15860
|
+
const result = await ensureMentalModel(apiUrl, bankId, spec, opts);
|
|
15861
|
+
outcomes.push(result.ok ? { name: m.name, ok: true } : { name: m.name, ok: false, reason: result.reason });
|
|
15862
|
+
} catch (err) {
|
|
15863
|
+
outcomes.push({ name: m.name, ok: false, reason: String(err) });
|
|
15864
|
+
}
|
|
15865
|
+
}
|
|
15866
|
+
return outcomes;
|
|
15867
|
+
}
|
|
15363
15868
|
async function createBank(apiUrl, bankId, opts) {
|
|
15364
15869
|
const fetchImpl = opts?.fetchImpl ?? fetch;
|
|
15365
15870
|
const timeoutMs = opts?.timeoutMs ?? 5000;
|
|
@@ -15487,8 +15992,24 @@ async function updateBankMissions(apiUrl, bankId, missions, opts) {
|
|
|
15487
15992
|
name: "update_bank",
|
|
15488
15993
|
arguments: {
|
|
15489
15994
|
bank_id: bankId,
|
|
15490
|
-
...missions.bank_mission != null ? { mission: missions.bank_mission } : {},
|
|
15491
|
-
...
|
|
15995
|
+
...missions.reflect_mission == null && missions.bank_mission != null ? { mission: missions.bank_mission } : {},
|
|
15996
|
+
...(() => {
|
|
15997
|
+
const configUpdates = {};
|
|
15998
|
+
if (missions.retain_mission != null)
|
|
15999
|
+
configUpdates.retain_mission = missions.retain_mission;
|
|
16000
|
+
if (missions.reflect_mission != null)
|
|
16001
|
+
configUpdates.reflect_mission = missions.reflect_mission;
|
|
16002
|
+
if (missions.observations_mission != null)
|
|
16003
|
+
configUpdates.observations_mission = missions.observations_mission;
|
|
16004
|
+
const d = missions.disposition;
|
|
16005
|
+
if (d?.skepticism != null)
|
|
16006
|
+
configUpdates.disposition_skepticism = d.skepticism;
|
|
16007
|
+
if (d?.literalism != null)
|
|
16008
|
+
configUpdates.disposition_literalism = d.literalism;
|
|
16009
|
+
if (d?.empathy != null)
|
|
16010
|
+
configUpdates.disposition_empathy = d.empathy;
|
|
16011
|
+
return Object.keys(configUpdates).length > 0 ? { config_updates: configUpdates } : {};
|
|
16012
|
+
})()
|
|
15492
16013
|
}
|
|
15493
16014
|
}
|
|
15494
16015
|
}),
|
|
@@ -15586,10 +16107,23 @@ async function addMemoryTag(apiUrl, bankId, memoryId, tag, opts) {
|
|
|
15586
16107
|
return { ok: false, reason: String(err) };
|
|
15587
16108
|
}
|
|
15588
16109
|
}
|
|
15589
|
-
var DEFAULT_RETAIN_MISSION, DEMOTE_FROM_RECALL_TAG = "[demote-from-recall]";
|
|
15590
|
-
var
|
|
16110
|
+
var DEFAULT_RETAIN_MISSION, PROFILE_MEMORY_DEFAULTS, USER_PROFILE_SOURCE_QUERY = "What are the key facts, preferences, context, and communication style about the user I talk to? Summarize what matters for making the agent feel like it knows them.", DEMOTE_FROM_RECALL_TAG = "[demote-from-recall]";
|
|
16111
|
+
var init_hindsight2 = __esm(() => {
|
|
15591
16112
|
init_users();
|
|
16113
|
+
init_hindsight();
|
|
15592
16114
|
DEFAULT_RETAIN_MISSION = "Extract user preferences, ongoing projects, recurring commitments, " + "important context, and durable facts that should help across future " + "conversations. Skip one-off chatter and temporary task noise.";
|
|
16115
|
+
PROFILE_MEMORY_DEFAULTS = {
|
|
16116
|
+
"health-coach": {
|
|
16117
|
+
disposition: { skepticism: 2, literalism: 2, empathy: 5 },
|
|
16118
|
+
observations_mission: "Synthesise the person's wellbeing patterns, motivations, and emotional " + "context \u2014 how habits, setbacks, and encouragement connect over time."
|
|
16119
|
+
},
|
|
16120
|
+
"executive-assistant": {
|
|
16121
|
+
disposition: { skepticism: 4, literalism: 4, empathy: 3 }
|
|
16122
|
+
},
|
|
16123
|
+
coding: {
|
|
16124
|
+
disposition: { skepticism: 4, literalism: 5, empathy: 2 }
|
|
16125
|
+
}
|
|
16126
|
+
};
|
|
15593
16127
|
});
|
|
15594
16128
|
|
|
15595
16129
|
// src/cli/helpers.ts
|
|
@@ -22398,7 +22932,8 @@ function getBuiltinDefaultSkillEntries() {
|
|
|
22398
22932
|
"switchroom-cli",
|
|
22399
22933
|
"switchroom-status",
|
|
22400
22934
|
"switchroom-health",
|
|
22401
|
-
"switchroom-runtime"
|
|
22935
|
+
"switchroom-runtime",
|
|
22936
|
+
"mental-model-curator"
|
|
22402
22937
|
];
|
|
22403
22938
|
return [
|
|
22404
22939
|
...anthropic.map((key) => ({ key, optOutKey: key, source: "anthropic" })),
|
|
@@ -22407,7 +22942,7 @@ function getBuiltinDefaultSkillEntries() {
|
|
|
22407
22942
|
}
|
|
22408
22943
|
var GOOGLE_WORKSPACE_MCP_PINNED_SHA = "9d69115b63e6bc2ef0d4b5d7a3b962396382b44c", MICROSOFT_WORKSPACE_MCP_PINNED_VERSION = "0.113.0", MICROSOFT_WORKSPACE_MCP_PACKAGE = "@softeria/ms-365-mcp-server", NOTION_MCP_PINNED_VERSION = "1.8.1", NOTION_MCP_PACKAGE = "@notionhq/notion-mcp-server", NOTION_TOKEN_ENV = "NOTION_TOKEN";
|
|
22409
22944
|
var init_scaffold_integration = __esm(() => {
|
|
22410
|
-
|
|
22945
|
+
init_hindsight2();
|
|
22411
22946
|
});
|
|
22412
22947
|
|
|
22413
22948
|
// src/config/microsoft-workspace-acl.ts
|
|
@@ -22425,7 +22960,7 @@ function shouldEmitMs365Mcp(agentName, agentMicrosoftAccount, microsoftAccounts)
|
|
|
22425
22960
|
}
|
|
22426
22961
|
|
|
22427
22962
|
// src/agents/reconcile-default-skills.ts
|
|
22428
|
-
import { existsSync as existsSync7, lstatSync, mkdirSync as mkdirSync3, readdirSync as readdirSync3, readlinkSync as readlinkSync2, rmSync, symlinkSync } from "node:fs";
|
|
22963
|
+
import { existsSync as existsSync7, lstatSync, mkdirSync as mkdirSync3, readdirSync as readdirSync3, readlinkSync as readlinkSync2, rmSync as rmSync2, symlinkSync } from "node:fs";
|
|
22429
22964
|
import { homedir as homedir3 } from "node:os";
|
|
22430
22965
|
import { join as join5, resolve as resolve5 } from "node:path";
|
|
22431
22966
|
function warnMissingPoolDir(poolDir) {
|
|
@@ -22495,7 +23030,7 @@ function reconcileAgentDefaultSkills(agentDir, optOuts = {}, defaults = getBuilt
|
|
|
22495
23030
|
}
|
|
22496
23031
|
if (currentTarget && isOwnedStaleLink(currentTarget, poolDir)) {
|
|
22497
23032
|
try {
|
|
22498
|
-
|
|
23033
|
+
rmSync2(dest, { force: true });
|
|
22499
23034
|
} catch {}
|
|
22500
23035
|
} else {
|
|
22501
23036
|
result.conflicts.push(entry.key);
|
|
@@ -22560,7 +23095,7 @@ import {
|
|
|
22560
23095
|
writeFileSync as writeFileSync2,
|
|
22561
23096
|
mkdirSync as mkdirSync4,
|
|
22562
23097
|
existsSync as existsSync8,
|
|
22563
|
-
renameSync,
|
|
23098
|
+
renameSync as renameSync2,
|
|
22564
23099
|
copyFileSync as copyFileSync2,
|
|
22565
23100
|
unlinkSync
|
|
22566
23101
|
} from "node:fs";
|
|
@@ -22600,7 +23135,7 @@ function saveTopicState(state, statePath) {
|
|
|
22600
23135
|
try {
|
|
22601
23136
|
writeFileSync2(tmp, JSON.stringify(state, null, 2) + `
|
|
22602
23137
|
`, "utf-8");
|
|
22603
|
-
|
|
23138
|
+
renameSync2(tmp, path);
|
|
22604
23139
|
} catch (err) {
|
|
22605
23140
|
try {
|
|
22606
23141
|
if (existsSync8(tmp))
|
|
@@ -22616,10 +23151,10 @@ var init_state = __esm(() => {
|
|
|
22616
23151
|
// src/vault/flock.ts
|
|
22617
23152
|
import {
|
|
22618
23153
|
existsSync as existsSync9,
|
|
22619
|
-
openSync as
|
|
22620
|
-
closeSync as
|
|
22621
|
-
writeSync,
|
|
22622
|
-
fsyncSync,
|
|
23154
|
+
openSync as openSync3,
|
|
23155
|
+
closeSync as closeSync3,
|
|
23156
|
+
writeSync as writeSync2,
|
|
23157
|
+
fsyncSync as fsyncSync2,
|
|
22623
23158
|
unlinkSync as unlinkSync2,
|
|
22624
23159
|
readFileSync as readFileSync8,
|
|
22625
23160
|
readdirSync as readdirSync4,
|
|
@@ -22741,7 +23276,7 @@ function acquireLock(vaultPath, options = {}) {
|
|
|
22741
23276
|
while (true) {
|
|
22742
23277
|
let fd = null;
|
|
22743
23278
|
try {
|
|
22744
|
-
fd =
|
|
23279
|
+
fd = openSync3(lockPath, fsConstants.O_WRONLY | fsConstants.O_CREAT | fsConstants.O_EXCL, 384);
|
|
22745
23280
|
} catch (err) {
|
|
22746
23281
|
const code = err?.code ?? "";
|
|
22747
23282
|
if (code === "EEXIST") {
|
|
@@ -22777,11 +23312,11 @@ function acquireLock(vaultPath, options = {}) {
|
|
|
22777
23312
|
${Date.now()}
|
|
22778
23313
|
${process.argv[1] ?? ""}
|
|
22779
23314
|
`;
|
|
22780
|
-
|
|
22781
|
-
|
|
23315
|
+
writeSync2(fd, meta);
|
|
23316
|
+
fsyncSync2(fd);
|
|
22782
23317
|
} catch {
|
|
22783
23318
|
try {
|
|
22784
|
-
|
|
23319
|
+
closeSync3(fd);
|
|
22785
23320
|
} catch {}
|
|
22786
23321
|
try {
|
|
22787
23322
|
unlinkSync2(lockPath);
|
|
@@ -22792,7 +23327,7 @@ ${process.argv[1] ?? ""}
|
|
|
22792
23327
|
return {
|
|
22793
23328
|
release: () => {
|
|
22794
23329
|
try {
|
|
22795
|
-
|
|
23330
|
+
closeSync3(ownedFd);
|
|
22796
23331
|
} catch {}
|
|
22797
23332
|
try {
|
|
22798
23333
|
unlinkSync2(lockPath);
|
|
@@ -22849,22 +23384,22 @@ __export(exports_vault, {
|
|
|
22849
23384
|
KNOWN_VAULT_ARTIFACT_PATTERNS: () => KNOWN_VAULT_ARTIFACT_PATTERNS,
|
|
22850
23385
|
KNOWN_VAULT_ARTIFACT_NAMES: () => KNOWN_VAULT_ARTIFACT_NAMES
|
|
22851
23386
|
});
|
|
22852
|
-
import { randomBytes, scryptSync, createCipheriv, createDecipheriv } from "node:crypto";
|
|
23387
|
+
import { randomBytes as randomBytes2, scryptSync, createCipheriv, createDecipheriv } from "node:crypto";
|
|
22853
23388
|
import {
|
|
22854
23389
|
readFileSync as readFileSync9,
|
|
22855
|
-
writeSync as
|
|
23390
|
+
writeSync as writeSync3,
|
|
22856
23391
|
existsSync as existsSync10,
|
|
22857
|
-
renameSync as
|
|
23392
|
+
renameSync as renameSync3,
|
|
22858
23393
|
mkdirSync as mkdirSync5,
|
|
22859
23394
|
unlinkSync as unlinkSync3,
|
|
22860
|
-
fsyncSync as
|
|
22861
|
-
openSync as
|
|
22862
|
-
closeSync as
|
|
23395
|
+
fsyncSync as fsyncSync3,
|
|
23396
|
+
openSync as openSync4,
|
|
23397
|
+
closeSync as closeSync4,
|
|
22863
23398
|
lstatSync as lstatSync2,
|
|
22864
23399
|
realpathSync as realpathSync2
|
|
22865
23400
|
} from "node:fs";
|
|
22866
23401
|
import { dirname as dirname3, basename as basename3, resolve as resolve7 } from "node:path";
|
|
22867
|
-
function
|
|
23402
|
+
function atomicWriteFileSync2(path, data, mode) {
|
|
22868
23403
|
let effectivePath = path;
|
|
22869
23404
|
try {
|
|
22870
23405
|
if (existsSync10(path) && lstatSync2(path).isSymbolicLink()) {
|
|
@@ -22874,20 +23409,20 @@ function atomicWriteFileSync(path, data, mode) {
|
|
|
22874
23409
|
const dir = dirname3(resolve7(effectivePath));
|
|
22875
23410
|
const tmp = resolve7(dir, `.${basename3(effectivePath)}.${process.pid}.${Date.now()}.tmp`);
|
|
22876
23411
|
try {
|
|
22877
|
-
const fd =
|
|
23412
|
+
const fd = openSync4(tmp, "wx", mode);
|
|
22878
23413
|
try {
|
|
22879
|
-
|
|
22880
|
-
|
|
23414
|
+
writeSync3(fd, data);
|
|
23415
|
+
fsyncSync3(fd);
|
|
22881
23416
|
} finally {
|
|
22882
|
-
|
|
23417
|
+
closeSync4(fd);
|
|
22883
23418
|
}
|
|
22884
|
-
|
|
23419
|
+
renameSync3(tmp, effectivePath);
|
|
22885
23420
|
try {
|
|
22886
|
-
const dirFd =
|
|
23421
|
+
const dirFd = openSync4(dir, "r");
|
|
22887
23422
|
try {
|
|
22888
|
-
|
|
23423
|
+
fsyncSync3(dirFd);
|
|
22889
23424
|
} finally {
|
|
22890
|
-
|
|
23425
|
+
closeSync4(dirFd);
|
|
22891
23426
|
}
|
|
22892
23427
|
} catch {}
|
|
22893
23428
|
} catch (err) {
|
|
@@ -22907,7 +23442,7 @@ function deriveKey(passphrase, salt, params = { N: SCRYPT_N, r: SCRYPT_R, p: SCR
|
|
|
22907
23442
|
});
|
|
22908
23443
|
}
|
|
22909
23444
|
function encrypt(key, plaintext) {
|
|
22910
|
-
const iv =
|
|
23445
|
+
const iv = randomBytes2(12);
|
|
22911
23446
|
const cipher = createCipheriv("aes-256-gcm", key, iv);
|
|
22912
23447
|
const encrypted = Buffer.concat([cipher.update(plaintext, "utf8"), cipher.final()]);
|
|
22913
23448
|
const tag = cipher.getAuthTag();
|
|
@@ -23012,7 +23547,7 @@ function createVault(passphrase, vaultPath) {
|
|
|
23012
23547
|
if (!existsSync10(dir)) {
|
|
23013
23548
|
mkdirSync5(dir, { recursive: true, mode: 448 });
|
|
23014
23549
|
}
|
|
23015
|
-
const salt =
|
|
23550
|
+
const salt = randomBytes2(16);
|
|
23016
23551
|
const key = deriveKey(passphrase, salt);
|
|
23017
23552
|
const vaultData = { secrets: {} };
|
|
23018
23553
|
const { iv, data, tag } = encrypt(key, JSON.stringify(vaultData));
|
|
@@ -23023,7 +23558,7 @@ function createVault(passphrase, vaultPath) {
|
|
|
23023
23558
|
tag,
|
|
23024
23559
|
kdf: { N: SCRYPT_N, r: SCRYPT_R, p: SCRYPT_P }
|
|
23025
23560
|
};
|
|
23026
|
-
|
|
23561
|
+
atomicWriteFileSync2(vaultPath, JSON.stringify(vaultFile, null, 2), 384);
|
|
23027
23562
|
}
|
|
23028
23563
|
function openVault(passphrase, vaultPath) {
|
|
23029
23564
|
if (!existsSync10(vaultPath)) {
|
|
@@ -23080,7 +23615,7 @@ function saveVault(passphrase, vaultPath, secrets) {
|
|
|
23080
23615
|
vaultFile.iv = iv;
|
|
23081
23616
|
vaultFile.data = data;
|
|
23082
23617
|
vaultFile.tag = tag;
|
|
23083
|
-
|
|
23618
|
+
atomicWriteFileSync2(vaultPath, JSON.stringify(vaultFile, null, 2), 384);
|
|
23084
23619
|
} finally {
|
|
23085
23620
|
if (releaseLock !== null) {
|
|
23086
23621
|
try {
|
|
@@ -23262,7 +23797,7 @@ function parseSocketIdentity(socketPath, spec) {
|
|
|
23262
23797
|
}
|
|
23263
23798
|
|
|
23264
23799
|
// src/vault/broker/peercred.ts
|
|
23265
|
-
import { execFileSync } from "node:child_process";
|
|
23800
|
+
import { execFileSync as execFileSync2 } from "node:child_process";
|
|
23266
23801
|
import { readFileSync as readFileSync10, readlinkSync as readlinkSync3, fstatSync } from "node:fs";
|
|
23267
23802
|
function socketPathToIdentity(socketPath) {
|
|
23268
23803
|
return parseSocketIdentity(socketPath, {
|
|
@@ -23435,7 +23970,7 @@ function identify(socketPath, socket, execFileSyncOverride) {
|
|
|
23435
23970
|
if (process.platform !== "linux") {
|
|
23436
23971
|
return null;
|
|
23437
23972
|
}
|
|
23438
|
-
const runner = execFileSyncOverride ??
|
|
23973
|
+
const runner = execFileSyncOverride ?? execFileSync2;
|
|
23439
23974
|
let pid = null;
|
|
23440
23975
|
if (socket !== undefined) {
|
|
23441
23976
|
const fd = fdFromSocket(socket);
|
|
@@ -24295,13 +24830,13 @@ __export(exports_resolver, {
|
|
|
24295
24830
|
});
|
|
24296
24831
|
import {
|
|
24297
24832
|
chmodSync,
|
|
24298
|
-
closeSync as
|
|
24833
|
+
closeSync as closeSync5,
|
|
24299
24834
|
mkdirSync as mkdirSync6,
|
|
24300
24835
|
mkdtempSync,
|
|
24301
|
-
openSync as
|
|
24302
|
-
rmSync as
|
|
24836
|
+
openSync as openSync5,
|
|
24837
|
+
rmSync as rmSync3,
|
|
24303
24838
|
statSync as statSync6,
|
|
24304
|
-
writeSync as
|
|
24839
|
+
writeSync as writeSync4
|
|
24305
24840
|
} from "node:fs";
|
|
24306
24841
|
import { join as join7 } from "node:path";
|
|
24307
24842
|
import { tmpdir } from "node:os";
|
|
@@ -24329,13 +24864,13 @@ function registerCleanupHook() {
|
|
|
24329
24864
|
const cleanup = () => {
|
|
24330
24865
|
for (const dir of materializedDirs) {
|
|
24331
24866
|
try {
|
|
24332
|
-
|
|
24867
|
+
rmSync3(dir, { recursive: true, force: true });
|
|
24333
24868
|
} catch {}
|
|
24334
24869
|
}
|
|
24335
24870
|
materializedDirs.clear();
|
|
24336
24871
|
if (cachedRoot) {
|
|
24337
24872
|
try {
|
|
24338
|
-
|
|
24873
|
+
rmSync3(cachedRoot, { recursive: true, force: true });
|
|
24339
24874
|
} catch {}
|
|
24340
24875
|
cachedRoot = null;
|
|
24341
24876
|
}
|
|
@@ -24366,18 +24901,18 @@ function materializationRoot() {
|
|
|
24366
24901
|
}
|
|
24367
24902
|
function writeFileExclusive(filePath, content) {
|
|
24368
24903
|
const buf = typeof content === "string" ? Buffer.from(content, "utf8") : content;
|
|
24369
|
-
const fd =
|
|
24904
|
+
const fd = openSync5(filePath, fsConstants2.O_WRONLY | fsConstants2.O_CREAT | fsConstants2.O_EXCL, 384);
|
|
24370
24905
|
try {
|
|
24371
|
-
|
|
24906
|
+
writeSync4(fd, buf);
|
|
24372
24907
|
} finally {
|
|
24373
|
-
|
|
24908
|
+
closeSync5(fd);
|
|
24374
24909
|
}
|
|
24375
24910
|
}
|
|
24376
24911
|
function materializeFilesEntry(key, files) {
|
|
24377
24912
|
const dir = join7(materializationRoot(), key);
|
|
24378
24913
|
if (materializedDirs.has(dir)) {
|
|
24379
24914
|
try {
|
|
24380
|
-
|
|
24915
|
+
rmSync3(dir, { recursive: true, force: true });
|
|
24381
24916
|
} catch {}
|
|
24382
24917
|
}
|
|
24383
24918
|
mkdirSync6(dir, { recursive: true, mode: 448 });
|
|
@@ -24401,13 +24936,13 @@ function materializeFilesEntry(key, files) {
|
|
|
24401
24936
|
function cleanupMaterializedSecrets() {
|
|
24402
24937
|
for (const dir of materializedDirs) {
|
|
24403
24938
|
try {
|
|
24404
|
-
|
|
24939
|
+
rmSync3(dir, { recursive: true, force: true });
|
|
24405
24940
|
} catch {}
|
|
24406
24941
|
}
|
|
24407
24942
|
materializedDirs.clear();
|
|
24408
24943
|
if (cachedRoot) {
|
|
24409
24944
|
try {
|
|
24410
|
-
|
|
24945
|
+
rmSync3(cachedRoot, { recursive: true, force: true });
|
|
24411
24946
|
} catch {}
|
|
24412
24947
|
cachedRoot = null;
|
|
24413
24948
|
}
|
|
@@ -24728,7 +25263,7 @@ var init_onboarding = __esm(() => {
|
|
|
24728
25263
|
});
|
|
24729
25264
|
|
|
24730
25265
|
// src/repos/bare-clone.ts
|
|
24731
|
-
import { execFileSync as
|
|
25266
|
+
import { execFileSync as execFileSync3 } from "node:child_process";
|
|
24732
25267
|
import { existsSync as existsSync13, mkdirSync as mkdirSync8 } from "node:fs";
|
|
24733
25268
|
import { resolve as resolve9 } from "node:path";
|
|
24734
25269
|
function bareClonePath(slug) {
|
|
@@ -24741,7 +25276,7 @@ function ensureBareClone(slug, url) {
|
|
|
24741
25276
|
if (!existsSync13(clonePath)) {
|
|
24742
25277
|
process.stderr.write(`[switchroom] repo "${slug}": cloning ${url} \u2026
|
|
24743
25278
|
`);
|
|
24744
|
-
|
|
25279
|
+
execFileSync3("git", ["clone", "--bare", url, clonePath], {
|
|
24745
25280
|
stdio: ["ignore", "pipe", "pipe"]
|
|
24746
25281
|
});
|
|
24747
25282
|
process.stderr.write(`[switchroom] repo "${slug}": bare clone ready at ${clonePath}
|
|
@@ -24750,7 +25285,7 @@ function ensureBareClone(slug, url) {
|
|
|
24750
25285
|
process.stderr.write(`[switchroom] repo "${slug}": fetching ${clonePath} \u2026
|
|
24751
25286
|
`);
|
|
24752
25287
|
try {
|
|
24753
|
-
|
|
25288
|
+
execFileSync3("git", ["fetch", "--all"], {
|
|
24754
25289
|
cwd: clonePath,
|
|
24755
25290
|
stdio: ["ignore", "pipe", "pipe"]
|
|
24756
25291
|
});
|
|
@@ -24767,7 +25302,7 @@ var init_bare_clone = __esm(() => {
|
|
|
24767
25302
|
});
|
|
24768
25303
|
|
|
24769
25304
|
// src/repos/agent-worktree.ts
|
|
24770
|
-
import { execFileSync as
|
|
25305
|
+
import { execFileSync as execFileSync4 } from "node:child_process";
|
|
24771
25306
|
import { existsSync as existsSync14, mkdirSync as mkdirSync9, readdirSync as readdirSync5 } from "node:fs";
|
|
24772
25307
|
import { join as join9, resolve as resolve10 } from "node:path";
|
|
24773
25308
|
function agentBranchName(agentName) {
|
|
@@ -24778,7 +25313,7 @@ function agentWorktreePath(agentDir, slug) {
|
|
|
24778
25313
|
}
|
|
24779
25314
|
function isWorktreeDirty(worktreePath) {
|
|
24780
25315
|
try {
|
|
24781
|
-
const out =
|
|
25316
|
+
const out = execFileSync4("git", ["status", "--porcelain"], {
|
|
24782
25317
|
cwd: worktreePath,
|
|
24783
25318
|
stdio: ["ignore", "pipe", "pipe"],
|
|
24784
25319
|
encoding: "utf-8"
|
|
@@ -24790,7 +25325,7 @@ function isWorktreeDirty(worktreePath) {
|
|
|
24790
25325
|
}
|
|
24791
25326
|
function headShortSha(worktreePath) {
|
|
24792
25327
|
try {
|
|
24793
|
-
return
|
|
25328
|
+
return execFileSync4("git", ["rev-parse", "--short", "HEAD"], {
|
|
24794
25329
|
cwd: worktreePath,
|
|
24795
25330
|
stdio: ["ignore", "pipe", "pipe"],
|
|
24796
25331
|
encoding: "utf-8"
|
|
@@ -24801,7 +25336,7 @@ function headShortSha(worktreePath) {
|
|
|
24801
25336
|
}
|
|
24802
25337
|
function branchExistsInBare(bareClonePath2, branchName) {
|
|
24803
25338
|
try {
|
|
24804
|
-
|
|
25339
|
+
execFileSync4("git", ["rev-parse", "--verify", `refs/heads/${branchName}`], {
|
|
24805
25340
|
cwd: bareClonePath2,
|
|
24806
25341
|
stdio: ["ignore", "pipe", "pipe"]
|
|
24807
25342
|
});
|
|
@@ -24812,7 +25347,7 @@ function branchExistsInBare(bareClonePath2, branchName) {
|
|
|
24812
25347
|
}
|
|
24813
25348
|
function resolveDefaultBranch(bareClonePath2) {
|
|
24814
25349
|
try {
|
|
24815
|
-
const out =
|
|
25350
|
+
const out = execFileSync4("git", ["symbolic-ref", "refs/remotes/origin/HEAD"], {
|
|
24816
25351
|
cwd: bareClonePath2,
|
|
24817
25352
|
stdio: ["ignore", "pipe", "pipe"],
|
|
24818
25353
|
encoding: "utf-8"
|
|
@@ -24830,12 +25365,12 @@ function ensureAgentWorktree(agentName, slug, bareClonePath2, agentDir) {
|
|
|
24830
25365
|
if (!existsSync14(worktreePath)) {
|
|
24831
25366
|
mkdirSync9(join9(agentDir, "work"), { recursive: true });
|
|
24832
25367
|
if (branchExistsInBare(bareClonePath2, branch)) {
|
|
24833
|
-
|
|
25368
|
+
execFileSync4("git", ["worktree", "add", worktreePath, branch], {
|
|
24834
25369
|
cwd: bareClonePath2,
|
|
24835
25370
|
stdio: ["ignore", "pipe", "pipe"]
|
|
24836
25371
|
});
|
|
24837
25372
|
} else {
|
|
24838
|
-
|
|
25373
|
+
execFileSync4("git", [
|
|
24839
25374
|
"worktree",
|
|
24840
25375
|
"add",
|
|
24841
25376
|
worktreePath,
|
|
@@ -24863,11 +25398,11 @@ function ensureAgentWorktree(agentName, slug, bareClonePath2, agentDir) {
|
|
|
24863
25398
|
};
|
|
24864
25399
|
}
|
|
24865
25400
|
try {
|
|
24866
|
-
|
|
25401
|
+
execFileSync4("git", ["fetch", "origin"], {
|
|
24867
25402
|
cwd: worktreePath,
|
|
24868
25403
|
stdio: ["ignore", "pipe", "pipe"]
|
|
24869
25404
|
});
|
|
24870
|
-
|
|
25405
|
+
execFileSync4("git", ["merge", "--ff-only", `origin/${defaultBranch}`], {
|
|
24871
25406
|
cwd: worktreePath,
|
|
24872
25407
|
stdio: ["ignore", "pipe", "pipe"]
|
|
24873
25408
|
});
|
|
@@ -24885,7 +25420,7 @@ function removeAgentWorktree(agentName, slug, bareClonePath2, agentDir) {
|
|
|
24885
25420
|
const branch = agentBranchName(agentName);
|
|
24886
25421
|
if (existsSync14(worktreePath)) {
|
|
24887
25422
|
try {
|
|
24888
|
-
|
|
25423
|
+
execFileSync4("git", ["worktree", "remove", "--force", worktreePath], {
|
|
24889
25424
|
cwd: bareClonePath2,
|
|
24890
25425
|
stdio: ["ignore", "pipe", "pipe"]
|
|
24891
25426
|
});
|
|
@@ -24897,7 +25432,7 @@ function removeAgentWorktree(agentName, slug, bareClonePath2, agentDir) {
|
|
|
24897
25432
|
}
|
|
24898
25433
|
if (branchExistsInBare(bareClonePath2, branch)) {
|
|
24899
25434
|
try {
|
|
24900
|
-
|
|
25435
|
+
execFileSync4("git", ["branch", "-D", branch], {
|
|
24901
25436
|
cwd: bareClonePath2,
|
|
24902
25437
|
stdio: ["ignore", "pipe", "pipe"]
|
|
24903
25438
|
});
|
|
@@ -24908,7 +25443,7 @@ function removeAgentWorktree(agentName, slug, bareClonePath2, agentDir) {
|
|
|
24908
25443
|
}
|
|
24909
25444
|
}
|
|
24910
25445
|
try {
|
|
24911
|
-
|
|
25446
|
+
execFileSync4("git", ["worktree", "prune"], {
|
|
24912
25447
|
cwd: bareClonePath2,
|
|
24913
25448
|
stdio: ["ignore", "pipe", "pipe"]
|
|
24914
25449
|
});
|
|
@@ -24925,19 +25460,19 @@ import {
|
|
|
24925
25460
|
writeFileSync as writeFileSync4,
|
|
24926
25461
|
appendFileSync as appendFileSync2,
|
|
24927
25462
|
readFileSync as readFileSync13,
|
|
24928
|
-
renameSync as
|
|
25463
|
+
renameSync as renameSync4,
|
|
24929
25464
|
chmodSync as chmodSync2,
|
|
24930
25465
|
symlinkSync as symlinkSync2,
|
|
24931
25466
|
copyFileSync as copyFileSync4,
|
|
24932
25467
|
readdirSync as readdirSync6,
|
|
24933
|
-
rmSync as
|
|
25468
|
+
rmSync as rmSync4,
|
|
24934
25469
|
statSync as statSync7,
|
|
24935
25470
|
lstatSync as lstatSync3,
|
|
24936
25471
|
readlinkSync as readlinkSync4,
|
|
24937
25472
|
unlinkSync as unlinkSync4
|
|
24938
25473
|
} from "node:fs";
|
|
24939
25474
|
import { homedir as homedir5 } from "node:os";
|
|
24940
|
-
import { execSync, execFileSync as
|
|
25475
|
+
import { execSync, execFileSync as execFileSync5 } from "node:child_process";
|
|
24941
25476
|
import { join as join10, resolve as resolve11 } from "node:path";
|
|
24942
25477
|
import { createHash as createHash2 } from "node:crypto";
|
|
24943
25478
|
function prependReplyDiscipline(rendered, context) {
|
|
@@ -25071,13 +25606,13 @@ function alignAgentUid(name, agentDir, uid, opts = {}) {
|
|
|
25071
25606
|
`));
|
|
25072
25607
|
}
|
|
25073
25608
|
try {
|
|
25074
|
-
|
|
25609
|
+
execFileSync5("chown", ["-R", `${uid}:${uid}`, ...paths], {
|
|
25075
25610
|
stdio: ["ignore", "ignore", "pipe"]
|
|
25076
25611
|
});
|
|
25077
25612
|
return { chowned: true, paths };
|
|
25078
25613
|
} catch {}
|
|
25079
25614
|
try {
|
|
25080
|
-
|
|
25615
|
+
execFileSync5("sudo", ["chown", "-R", `${uid}:${uid}`, ...paths], {
|
|
25081
25616
|
stdio: "inherit"
|
|
25082
25617
|
});
|
|
25083
25618
|
return { chowned: true, paths };
|
|
@@ -25271,7 +25806,7 @@ function migrateLegacySkillsDir(agentDir, skillsPool) {
|
|
|
25271
25806
|
}
|
|
25272
25807
|
if (target && target.startsWith(skillsPool)) {
|
|
25273
25808
|
try {
|
|
25274
|
-
|
|
25809
|
+
rmSync4(entryPath, { force: true });
|
|
25275
25810
|
} catch {}
|
|
25276
25811
|
}
|
|
25277
25812
|
}
|
|
@@ -25306,7 +25841,7 @@ function syncGlobalSkills(agentDir, declared, skillsDirOverride) {
|
|
|
25306
25841
|
} catch {}
|
|
25307
25842
|
if (target && target.startsWith(skillsPool)) {
|
|
25308
25843
|
try {
|
|
25309
|
-
|
|
25844
|
+
rmSync4(dest, { force: true });
|
|
25310
25845
|
} catch {}
|
|
25311
25846
|
} else {
|
|
25312
25847
|
continue;
|
|
@@ -25336,7 +25871,7 @@ function syncGlobalSkills(agentDir, declared, skillsDirOverride) {
|
|
|
25336
25871
|
continue;
|
|
25337
25872
|
}
|
|
25338
25873
|
if (linkTarget && linkTarget.startsWith(skillsPool)) {
|
|
25339
|
-
|
|
25874
|
+
rmSync4(entryPath, { force: true });
|
|
25340
25875
|
}
|
|
25341
25876
|
}
|
|
25342
25877
|
}
|
|
@@ -25391,7 +25926,7 @@ function installSwitchroomSkills(agentDir, opts = {}) {
|
|
|
25391
25926
|
if (currentTarget !== join10(builtinSkillsDir, name))
|
|
25392
25927
|
continue;
|
|
25393
25928
|
try {
|
|
25394
|
-
|
|
25929
|
+
rmSync4(dest, { force: true });
|
|
25395
25930
|
} catch {}
|
|
25396
25931
|
}
|
|
25397
25932
|
return;
|
|
@@ -25414,7 +25949,7 @@ function installSwitchroomSkills(agentDir, opts = {}) {
|
|
|
25414
25949
|
if (currentTarget === src)
|
|
25415
25950
|
continue;
|
|
25416
25951
|
try {
|
|
25417
|
-
|
|
25952
|
+
rmSync4(dest, { force: true });
|
|
25418
25953
|
} catch {}
|
|
25419
25954
|
} else {
|
|
25420
25955
|
continue;
|
|
@@ -25666,7 +26201,7 @@ function migrateLegacyAgentsMdIfPresent(agentWorkspaceDir, created) {
|
|
|
25666
26201
|
}
|
|
25667
26202
|
const content = readFileSync13(agentsMd, "utf-8");
|
|
25668
26203
|
writeFileSync4(claudeMd, content, "utf-8");
|
|
25669
|
-
|
|
26204
|
+
rmSync4(agentsMd);
|
|
25670
26205
|
created.push(claudeMd);
|
|
25671
26206
|
console.log(source_default.dim(` migrated legacy workspace/AGENTS.md \u2192 workspace/CLAUDE.md (content preserved)`));
|
|
25672
26207
|
}
|
|
@@ -25682,9 +26217,9 @@ function ensureClaudeMdSymlinks(agentWorkspaceDir, changes) {
|
|
|
25682
26217
|
const target = readlinkSync4(linkPath);
|
|
25683
26218
|
if (target === "CLAUDE.md")
|
|
25684
26219
|
continue;
|
|
25685
|
-
|
|
26220
|
+
rmSync4(linkPath);
|
|
25686
26221
|
} else {
|
|
25687
|
-
|
|
26222
|
+
rmSync4(linkPath);
|
|
25688
26223
|
}
|
|
25689
26224
|
}
|
|
25690
26225
|
symlinkSync2("CLAUDE.md", linkPath);
|
|
@@ -25725,7 +26260,7 @@ Thumbs.db
|
|
|
25725
26260
|
execSync("git add -A", { cwd: workspaceDir, stdio: "pipe" });
|
|
25726
26261
|
const userEmail = process.env.GIT_AUTHOR_EMAIL || "switchroom@localhost";
|
|
25727
26262
|
const userName = process.env.GIT_AUTHOR_NAME || "Switchroom Agent";
|
|
25728
|
-
|
|
26263
|
+
execFileSync5("git", [
|
|
25729
26264
|
"-c",
|
|
25730
26265
|
`user.email=${userEmail}`,
|
|
25731
26266
|
"-c",
|
|
@@ -25773,13 +26308,13 @@ function installHindsightPlugin(agentName, agentDir, switchroomConfig) {
|
|
|
25773
26308
|
const upToDate = dirContentEquals(sourcePath, destPath, expectedSettings != null ? { "settings.json": expectedSettings } : undefined);
|
|
25774
26309
|
if (!upToDate) {
|
|
25775
26310
|
if (existsSync15(destPath)) {
|
|
25776
|
-
|
|
26311
|
+
rmSync4(destPath, { recursive: true, force: true });
|
|
25777
26312
|
}
|
|
25778
26313
|
copyDirRecursive2(sourcePath, destPath);
|
|
25779
26314
|
}
|
|
25780
26315
|
applyHindsightSettingsOverrides(destPath, additionalBanks);
|
|
25781
26316
|
const bankId = agentMemory?.collection ?? agentName;
|
|
25782
|
-
const mcpUrl = memory.config?.url ??
|
|
26317
|
+
const mcpUrl = memory.config?.url ?? HINDSIGHT_DEFAULT_MCP_URL;
|
|
25783
26318
|
const apiBaseUrl = mcpUrl.replace(/\/mcp\/?$/, "").replace(/\/$/, "");
|
|
25784
26319
|
return { pluginDir: destPath, apiBaseUrl, bankId };
|
|
25785
26320
|
}
|
|
@@ -25811,6 +26346,7 @@ function renderHindsightSettingsOverrides(raw, additionalBanks) {
|
|
|
25811
26346
|
settings.recallMinOverlap = 0.1;
|
|
25812
26347
|
settings.recallTypes = ["world", "experience", "observation"];
|
|
25813
26348
|
settings.recallSkipTrivial = true;
|
|
26349
|
+
settings.directiveCaptureNudge = true;
|
|
25814
26350
|
if (additionalBanks.length > 0) {
|
|
25815
26351
|
settings.recallAdditionalBanks = [...additionalBanks];
|
|
25816
26352
|
}
|
|
@@ -25838,6 +26374,7 @@ function buildWorkspaceContext(args) {
|
|
|
25838
26374
|
hindsightRecallMinOverlap,
|
|
25839
26375
|
hindsightRecallTypes,
|
|
25840
26376
|
hindsightRecallSkipTrivial,
|
|
26377
|
+
hindsightDirectiveCaptureNudge,
|
|
25841
26378
|
hindsightTopicAliasesJson,
|
|
25842
26379
|
hindsightSenderBanksJson,
|
|
25843
26380
|
hindsightTopicFilterMode
|
|
@@ -25882,6 +26419,7 @@ function buildWorkspaceContext(args) {
|
|
|
25882
26419
|
hindsightRecallMinOverlap,
|
|
25883
26420
|
hindsightRecallTypes,
|
|
25884
26421
|
hindsightRecallSkipTrivial,
|
|
26422
|
+
hindsightDirectiveCaptureNudge,
|
|
25885
26423
|
hindsightTopicAliasesJsonQ: hindsightTopicAliasesJson ? shellSingleQuote(hindsightTopicAliasesJson) : undefined,
|
|
25886
26424
|
hindsightSenderBanksJsonQ: hindsightSenderBanksJson ? shellSingleQuote(hindsightSenderBanksJson) : undefined,
|
|
25887
26425
|
hindsightTopicFilterMode,
|
|
@@ -26052,12 +26590,13 @@ function scaffoldAgent(name, agentConfigRaw, agentsDir, telegramConfig, switchro
|
|
|
26052
26590
|
]);
|
|
26053
26591
|
const hindsightAutoRecallEnabled = hindsightEnabled && agentConfig.memory?.auto_recall !== false;
|
|
26054
26592
|
const hindsightBankId = agentConfig.memory?.collection ?? name;
|
|
26055
|
-
const hindsightApiBaseUrl = switchroomConfig?.memory?.config?.url ? switchroomConfig.memory.config.url.replace(/\/mcp\/?$/, "").replace(/\/$/, "") :
|
|
26593
|
+
const hindsightApiBaseUrl = switchroomConfig?.memory?.config?.url ? switchroomConfig.memory.config.url.replace(/\/mcp\/?$/, "").replace(/\/$/, "") : HINDSIGHT_DEFAULT_API_BASE_URL;
|
|
26056
26594
|
const hindsightRecallMaxMemories = agentConfig.memory?.recall?.max_memories;
|
|
26057
26595
|
const hindsightRecallCacheTtlSecs = agentConfig.memory?.recall?.cache_ttl_secs;
|
|
26058
26596
|
const hindsightRecallMinOverlap = agentConfig.memory?.recall?.min_overlap;
|
|
26059
26597
|
const hindsightRecallTypes = agentConfig.memory?.recall?.types?.length ? agentConfig.memory.recall.types.join(",") : undefined;
|
|
26060
26598
|
const hindsightRecallSkipTrivial = agentConfig.memory?.recall?.skip_trivial === undefined ? undefined : String(agentConfig.memory.recall.skip_trivial);
|
|
26599
|
+
const hindsightDirectiveCaptureNudge = agentConfig.memory?.directive_capture_nudge === undefined ? undefined : String(agentConfig.memory.directive_capture_nudge);
|
|
26061
26600
|
const topicAliases = agentConfig.channels?.telegram?.topic_aliases;
|
|
26062
26601
|
const hindsightTopicAliasesJson = topicAliases && Object.keys(topicAliases).length > 0 ? JSON.stringify(topicAliases) : undefined;
|
|
26063
26602
|
const senderBanks = switchroomConfig ? resolveUsers(switchroomConfig, name).senderBanks : agentConfig.memory?.recall?.sender_banks ?? {};
|
|
@@ -26084,6 +26623,7 @@ function scaffoldAgent(name, agentConfigRaw, agentsDir, telegramConfig, switchro
|
|
|
26084
26623
|
hindsightRecallMinOverlap,
|
|
26085
26624
|
hindsightRecallTypes,
|
|
26086
26625
|
hindsightRecallSkipTrivial,
|
|
26626
|
+
hindsightDirectiveCaptureNudge,
|
|
26087
26627
|
hindsightTopicAliasesJson,
|
|
26088
26628
|
hindsightSenderBanksJson,
|
|
26089
26629
|
hindsightTopicFilterMode
|
|
@@ -26452,12 +26992,12 @@ ${body}
|
|
|
26452
26992
|
if (target === "workspace/SOUL.md") {
|
|
26453
26993
|
skipped.push(agentSoulPath);
|
|
26454
26994
|
} else {
|
|
26455
|
-
|
|
26995
|
+
rmSync4(agentSoulPath);
|
|
26456
26996
|
symlinkSync2("workspace/SOUL.md", agentSoulPath);
|
|
26457
26997
|
created.push(agentSoulPath);
|
|
26458
26998
|
}
|
|
26459
26999
|
} else {
|
|
26460
|
-
|
|
27000
|
+
rmSync4(agentSoulPath);
|
|
26461
27001
|
symlinkSync2("workspace/SOUL.md", agentSoulPath);
|
|
26462
27002
|
created.push(agentSoulPath);
|
|
26463
27003
|
}
|
|
@@ -26490,8 +27030,10 @@ ${body}
|
|
|
26490
27030
|
const userBankMission = agentConfig.memory?.bank_mission;
|
|
26491
27031
|
const userRetainMission = agentConfig.memory?.retain_mission;
|
|
26492
27032
|
const seededRetainMission = userRetainMission ?? DEFAULT_RETAIN_MISSION;
|
|
27033
|
+
const extras = resolveBankMissionExtras(agentConfig.memory, agentConfig.extends ?? DEFAULT_PROFILE);
|
|
26493
27034
|
const missions = {
|
|
26494
|
-
retain_mission: seededRetainMission
|
|
27035
|
+
retain_mission: seededRetainMission,
|
|
27036
|
+
...extras
|
|
26495
27037
|
};
|
|
26496
27038
|
if (userBankMission) {
|
|
26497
27039
|
missions.bank_mission = userBankMission;
|
|
@@ -26506,6 +27048,17 @@ ${body}
|
|
|
26506
27048
|
}).catch((err) => {
|
|
26507
27049
|
console.warn(` ${source_default.yellow("\u26a0")} Bank mission update error for ${formatAgentBankLabel(name, hindsightBankId)}: ${err}`);
|
|
26508
27050
|
});
|
|
27051
|
+
ensureDeclaredMentalModels(apiUrl, hindsightBankId, agentConfig.memory?.mental_models, { timeoutMs: 5000 }).then((outcomes) => {
|
|
27052
|
+
for (const o of outcomes) {
|
|
27053
|
+
if (o.ok) {
|
|
27054
|
+
console.log(` ${source_default.green("\u2713")} Mental model "${o.name}" ready for ${formatAgentBankLabel(name, hindsightBankId)}`);
|
|
27055
|
+
} else {
|
|
27056
|
+
console.warn(` ${source_default.yellow("\u26a0")} Failed to ensure mental model "${o.name}" for ${formatAgentBankLabel(name, hindsightBankId)}: ${o.reason}`);
|
|
27057
|
+
}
|
|
27058
|
+
}
|
|
27059
|
+
}).catch((err) => {
|
|
27060
|
+
console.warn(` ${source_default.yellow("\u26a0")} Mental model ensure error for ${formatAgentBankLabel(name, hindsightBankId)}: ${err}`);
|
|
27061
|
+
});
|
|
26509
27062
|
});
|
|
26510
27063
|
}
|
|
26511
27064
|
for (const f of rewrittenWithBackup) {
|
|
@@ -26884,7 +27437,7 @@ function reconcileAgent(name, agentConfigRaw, agentsDir, telegramConfig, switchr
|
|
|
26884
27437
|
mkdirSync10(workspaceDir, { recursive: true });
|
|
26885
27438
|
const legacyContent = readFileSync13(legacyCustomPath, "utf-8");
|
|
26886
27439
|
writeFileSync4(newCustomPath, legacyContent, "utf-8");
|
|
26887
|
-
|
|
27440
|
+
rmSync4(legacyCustomPath);
|
|
26888
27441
|
console.log(source_default.green(` moved CLAUDE.custom.md \u2192 workspace/CLAUDE.custom.md`));
|
|
26889
27442
|
}
|
|
26890
27443
|
const tools = agentConfig.tools ?? { allow: [], deny: [] };
|
|
@@ -26923,12 +27476,13 @@ function reconcileAgent(name, agentConfigRaw, agentsDir, telegramConfig, switchr
|
|
|
26923
27476
|
const resolvedBotToken = resolveBotToken(rawBotToken);
|
|
26924
27477
|
const hindsightAutoRecallEnabled = hindsightEnabled && agentConfig.memory?.auto_recall !== false;
|
|
26925
27478
|
const hindsightBankId = agentConfig.memory?.collection ?? name;
|
|
26926
|
-
const hindsightApiBaseUrl = switchroomConfig.memory?.config?.url ? switchroomConfig.memory.config.url.replace(/\/mcp\/?$/, "").replace(/\/$/, "") :
|
|
27479
|
+
const hindsightApiBaseUrl = switchroomConfig.memory?.config?.url ? switchroomConfig.memory.config.url.replace(/\/mcp\/?$/, "").replace(/\/$/, "") : HINDSIGHT_DEFAULT_API_BASE_URL;
|
|
26927
27480
|
const hindsightRecallMaxMemories = agentConfig.memory?.recall?.max_memories;
|
|
26928
27481
|
const hindsightRecallCacheTtlSecs = agentConfig.memory?.recall?.cache_ttl_secs;
|
|
26929
27482
|
const hindsightRecallMinOverlap = agentConfig.memory?.recall?.min_overlap;
|
|
26930
27483
|
const hindsightRecallTypes = agentConfig.memory?.recall?.types?.length ? agentConfig.memory.recall.types.join(",") : undefined;
|
|
26931
27484
|
const hindsightRecallSkipTrivial = agentConfig.memory?.recall?.skip_trivial === undefined ? undefined : String(agentConfig.memory.recall.skip_trivial);
|
|
27485
|
+
const hindsightDirectiveCaptureNudge = agentConfig.memory?.directive_capture_nudge === undefined ? undefined : String(agentConfig.memory.directive_capture_nudge);
|
|
26932
27486
|
const topicAliases = agentConfig.channels?.telegram?.topic_aliases;
|
|
26933
27487
|
const hindsightTopicAliasesJson = topicAliases && Object.keys(topicAliases).length > 0 ? JSON.stringify(topicAliases) : undefined;
|
|
26934
27488
|
const senderBanks = switchroomConfig ? resolveUsers(switchroomConfig, name).senderBanks : agentConfig.memory?.recall?.sender_banks ?? {};
|
|
@@ -26968,6 +27522,7 @@ function reconcileAgent(name, agentConfigRaw, agentsDir, telegramConfig, switchr
|
|
|
26968
27522
|
hindsightRecallMinOverlap,
|
|
26969
27523
|
hindsightRecallTypes,
|
|
26970
27524
|
hindsightRecallSkipTrivial,
|
|
27525
|
+
hindsightDirectiveCaptureNudge,
|
|
26971
27526
|
hindsightTopicAliasesJsonQ: hindsightTopicAliasesJson ? shellSingleQuote(hindsightTopicAliasesJson) : undefined,
|
|
26972
27527
|
hindsightSenderBanksJsonQ: hindsightSenderBanksJson ? shellSingleQuote(hindsightSenderBanksJson) : undefined,
|
|
26973
27528
|
hindsightTopicFilterMode,
|
|
@@ -27369,6 +27924,7 @@ ${body}
|
|
|
27369
27924
|
hindsightRecallMinOverlap,
|
|
27370
27925
|
hindsightRecallTypes,
|
|
27371
27926
|
hindsightRecallSkipTrivial,
|
|
27927
|
+
hindsightDirectiveCaptureNudge,
|
|
27372
27928
|
hindsightTopicAliasesJson,
|
|
27373
27929
|
hindsightSenderBanksJson,
|
|
27374
27930
|
hindsightTopicFilterMode
|
|
@@ -27397,12 +27953,12 @@ ${body}
|
|
|
27397
27953
|
if (stat.isSymbolicLink()) {
|
|
27398
27954
|
const target = readlinkSync4(agentSoulPath);
|
|
27399
27955
|
if (target !== "workspace/SOUL.md") {
|
|
27400
|
-
|
|
27956
|
+
rmSync4(agentSoulPath);
|
|
27401
27957
|
symlinkSync2("workspace/SOUL.md", agentSoulPath);
|
|
27402
27958
|
changes.push(agentSoulPath);
|
|
27403
27959
|
}
|
|
27404
27960
|
} else {
|
|
27405
|
-
|
|
27961
|
+
rmSync4(agentSoulPath);
|
|
27406
27962
|
symlinkSync2("workspace/SOUL.md", agentSoulPath);
|
|
27407
27963
|
changes.push(agentSoulPath);
|
|
27408
27964
|
}
|
|
@@ -27446,14 +28002,15 @@ ${body}
|
|
|
27446
28002
|
bankOpsChain.then((bankReady) => {
|
|
27447
28003
|
if (!bankReady)
|
|
27448
28004
|
return;
|
|
27449
|
-
|
|
27450
|
-
|
|
27451
|
-
|
|
27452
|
-
|
|
27453
|
-
|
|
27454
|
-
|
|
27455
|
-
|
|
27456
|
-
|
|
28005
|
+
const extras = resolveBankMissionExtras(agentConfig.memory, agentConfig.extends ?? DEFAULT_PROFILE);
|
|
28006
|
+
const missions = { ...extras };
|
|
28007
|
+
if (agentConfig.memory?.bank_mission) {
|
|
28008
|
+
missions.bank_mission = agentConfig.memory.bank_mission;
|
|
28009
|
+
}
|
|
28010
|
+
if (agentConfig.memory?.retain_mission) {
|
|
28011
|
+
missions.retain_mission = agentConfig.memory.retain_mission;
|
|
28012
|
+
}
|
|
28013
|
+
if (Object.keys(missions).length > 0) {
|
|
27457
28014
|
updateBankMissions(apiUrl, hindsightBankId, missions, { timeoutMs: 5000 }).then((result) => {
|
|
27458
28015
|
if (result.ok) {
|
|
27459
28016
|
console.log(` ${source_default.green("\u2713")} Bank missions updated for ${formatAgentBankLabel(name, hindsightBankId)}`);
|
|
@@ -27464,6 +28021,17 @@ ${body}
|
|
|
27464
28021
|
console.warn(` ${source_default.yellow("\u26a0")} Bank mission update error for ${formatAgentBankLabel(name, hindsightBankId)}: ${err}`);
|
|
27465
28022
|
});
|
|
27466
28023
|
}
|
|
28024
|
+
ensureDeclaredMentalModels(apiUrl, hindsightBankId, agentConfig.memory?.mental_models, { timeoutMs: 5000 }).then((outcomes) => {
|
|
28025
|
+
for (const o of outcomes) {
|
|
28026
|
+
if (o.ok) {
|
|
28027
|
+
console.log(` ${source_default.green("\u2713")} Mental model "${o.name}" ready for ${formatAgentBankLabel(name, hindsightBankId)}`);
|
|
28028
|
+
} else {
|
|
28029
|
+
console.warn(` ${source_default.yellow("\u26a0")} Failed to ensure mental model "${o.name}" for ${formatAgentBankLabel(name, hindsightBankId)}: ${o.reason}`);
|
|
28030
|
+
}
|
|
28031
|
+
}
|
|
28032
|
+
}).catch((err) => {
|
|
28033
|
+
console.warn(` ${source_default.yellow("\u26a0")} Mental model ensure error for ${formatAgentBankLabel(name, hindsightBankId)}: ${err}`);
|
|
28034
|
+
});
|
|
27467
28035
|
});
|
|
27468
28036
|
}
|
|
27469
28037
|
return {
|
|
@@ -27558,7 +28126,7 @@ function reconcileConfiguredGroup(accessPath, agentConfig, telegramConfig) {
|
|
|
27558
28126
|
const tmp = accessPath + ".tmp";
|
|
27559
28127
|
writeFileSync4(tmp, JSON.stringify(access, null, 2) + `
|
|
27560
28128
|
`, { mode: 384 });
|
|
27561
|
-
|
|
28129
|
+
renameSync4(tmp, accessPath);
|
|
27562
28130
|
console.log(source_default.green(` registered supergroup ${forumChatId} in access.json ` + `(responds to all topics; requireMention=false)`));
|
|
27563
28131
|
}
|
|
27564
28132
|
function buildAccessJson2(agentConfig, telegramConfig, resolvedTopicId, userId) {
|
|
@@ -27916,13 +28484,14 @@ var init_scaffold = __esm(() => {
|
|
|
27916
28484
|
init_profiles();
|
|
27917
28485
|
init_scaffold_integration();
|
|
27918
28486
|
init_reconcile_default_skills();
|
|
27919
|
-
|
|
28487
|
+
init_hindsight2();
|
|
27920
28488
|
init_state();
|
|
27921
28489
|
init_paths();
|
|
27922
28490
|
init_loader();
|
|
27923
28491
|
init_resolver();
|
|
27924
28492
|
init_vault();
|
|
27925
28493
|
init_onboarding();
|
|
28494
|
+
init_hindsight();
|
|
27926
28495
|
init_bare_clone();
|
|
27927
28496
|
init_agent_worktree();
|
|
27928
28497
|
REPO_ROOT = resolve11(import.meta.dirname, "../..");
|
|
@@ -27940,8 +28509,31 @@ var init_scaffold = __esm(() => {
|
|
|
27940
28509
|
"mcp__switchroom-telegram__progress_update"
|
|
27941
28510
|
];
|
|
27942
28511
|
HINDSIGHT_MCP_TOOLS = [
|
|
27943
|
-
"
|
|
27944
|
-
"
|
|
28512
|
+
"mcp__hindsight__recall",
|
|
28513
|
+
"mcp__hindsight__retain",
|
|
28514
|
+
"mcp__hindsight__sync_retain",
|
|
28515
|
+
"mcp__hindsight__reflect",
|
|
28516
|
+
"mcp__hindsight__create_directive",
|
|
28517
|
+
"mcp__hindsight__update_bank",
|
|
28518
|
+
"mcp__hindsight__delete_directive",
|
|
28519
|
+
"mcp__hindsight__list_directives",
|
|
28520
|
+
"mcp__hindsight__create_bank",
|
|
28521
|
+
"mcp__hindsight__get_bank",
|
|
28522
|
+
"mcp__hindsight__get_bank_stats",
|
|
28523
|
+
"mcp__hindsight__list_banks",
|
|
28524
|
+
"mcp__hindsight__delete_bank",
|
|
28525
|
+
"mcp__hindsight__get_memory",
|
|
28526
|
+
"mcp__hindsight__list_memories",
|
|
28527
|
+
"mcp__hindsight__clear_memories",
|
|
28528
|
+
"mcp__hindsight__get_document",
|
|
28529
|
+
"mcp__hindsight__list_documents",
|
|
28530
|
+
"mcp__hindsight__delete_document",
|
|
28531
|
+
"mcp__hindsight__get_mental_model",
|
|
28532
|
+
"mcp__hindsight__list_mental_models",
|
|
28533
|
+
"mcp__hindsight__list_tags",
|
|
28534
|
+
"mcp__hindsight__get_operation",
|
|
28535
|
+
"mcp__hindsight__list_operations",
|
|
28536
|
+
"mcp__hindsight__cancel_operation"
|
|
27945
28537
|
];
|
|
27946
28538
|
AGENT_CONFIG_MCP_TOOLS = [
|
|
27947
28539
|
"mcp__agent-config",
|
|
@@ -29033,7 +29625,7 @@ function resolveHostHomeForCompose() {
|
|
|
29033
29625
|
}
|
|
29034
29626
|
return homedir6();
|
|
29035
29627
|
}
|
|
29036
|
-
async function
|
|
29628
|
+
async function computeComposeContent(opts) {
|
|
29037
29629
|
const release = resolveRelease({ override: opts.releaseOverride, root: opts.config.release });
|
|
29038
29630
|
const imageTag = resolveImageTag(release);
|
|
29039
29631
|
const operatorUid = resolveOperatorUid();
|
|
@@ -29057,6 +29649,11 @@ async function writeComposeFile(opts) {
|
|
|
29057
29649
|
previous = null;
|
|
29058
29650
|
}
|
|
29059
29651
|
const previousImageTag = previous ? AGENT_IMAGE_TAG_RE.exec(previous)?.[1] ?? null : null;
|
|
29652
|
+
return { content, imageTag, previous, previousImageTag };
|
|
29653
|
+
}
|
|
29654
|
+
async function writeComposeFile(opts) {
|
|
29655
|
+
const { content, imageTag, previous, previousImageTag } = await computeComposeContent(opts);
|
|
29656
|
+
const operatorUid = resolveOperatorUid();
|
|
29060
29657
|
await mkdir(dirname6(opts.composePath), { recursive: true });
|
|
29061
29658
|
if (previous !== null) {
|
|
29062
29659
|
try {
|
|
@@ -29089,7 +29686,7 @@ var init_write_compose = __esm(() => {
|
|
|
29089
29686
|
});
|
|
29090
29687
|
|
|
29091
29688
|
// src/agents/tmux.ts
|
|
29092
|
-
import { execFileSync as
|
|
29689
|
+
import { execFileSync as execFileSync6 } from "node:child_process";
|
|
29093
29690
|
function sendAgentInterrupt(opts) {
|
|
29094
29691
|
const { agentName } = opts;
|
|
29095
29692
|
const attempts = typeof opts.attempts === "number" && opts.attempts > 0 ? opts.attempts : 1;
|
|
@@ -29099,7 +29696,7 @@ function sendAgentInterrupt(opts) {
|
|
|
29099
29696
|
let lastError = null;
|
|
29100
29697
|
for (let i = 0;i < attempts; i++) {
|
|
29101
29698
|
try {
|
|
29102
|
-
|
|
29699
|
+
execFileSync6("tmux", args, {
|
|
29103
29700
|
timeout: 3000,
|
|
29104
29701
|
stdio: ["ignore", "pipe", "pipe"]
|
|
29105
29702
|
});
|
|
@@ -29140,7 +29737,7 @@ var init_compose_env = () => {};
|
|
|
29140
29737
|
import { resolve as resolve13 } from "node:path";
|
|
29141
29738
|
import { mkdirSync as mkdirSync13, writeFileSync as writeFileSync6 } from "node:fs";
|
|
29142
29739
|
import { homedir as homedir7 } from "node:os";
|
|
29143
|
-
import { execFileSync as
|
|
29740
|
+
import { execFileSync as execFileSync7 } from "node:child_process";
|
|
29144
29741
|
function resolveSwitchroomHome(explicit) {
|
|
29145
29742
|
if (explicit && explicit.length > 0)
|
|
29146
29743
|
return explicit;
|
|
@@ -29171,7 +29768,7 @@ function bringUpAgentService(opts) {
|
|
|
29171
29768
|
const dockerBin = opts.dockerBin ?? "docker";
|
|
29172
29769
|
const stdio = opts.stdio ?? "inherit";
|
|
29173
29770
|
for (const svc of ["vault-broker", "approval-kernel", "switchroom-auth-broker"]) {
|
|
29174
|
-
|
|
29771
|
+
execFileSync7(dockerBin, [
|
|
29175
29772
|
"compose",
|
|
29176
29773
|
"-f",
|
|
29177
29774
|
composePath,
|
|
@@ -29183,7 +29780,7 @@ function bringUpAgentService(opts) {
|
|
|
29183
29780
|
svc
|
|
29184
29781
|
], { stdio });
|
|
29185
29782
|
}
|
|
29186
|
-
|
|
29783
|
+
execFileSync7(dockerBin, [
|
|
29187
29784
|
"compose",
|
|
29188
29785
|
"-f",
|
|
29189
29786
|
composePath,
|
|
@@ -29202,7 +29799,7 @@ var init_docker_fleet = __esm(() => {
|
|
|
29202
29799
|
});
|
|
29203
29800
|
|
|
29204
29801
|
// src/agents/singleton-reconcile.ts
|
|
29205
|
-
import { execFileSync as
|
|
29802
|
+
import { execFileSync as execFileSync8 } from "node:child_process";
|
|
29206
29803
|
import { readFileSync as readFileSync16 } from "node:fs";
|
|
29207
29804
|
function resolveSingletonServices(voiceEngine) {
|
|
29208
29805
|
const engine = voiceEngine ?? loadHostCapabilities()?.voice.engine ?? "cloud";
|
|
@@ -29228,7 +29825,7 @@ function readPinnedSingletonImages(composeText, services = SINGLETON_SERVICES) {
|
|
|
29228
29825
|
}
|
|
29229
29826
|
function defaultInspectImage(dockerBin, container) {
|
|
29230
29827
|
try {
|
|
29231
|
-
const out =
|
|
29828
|
+
const out = execFileSync8(dockerBin, ["inspect", "-f", "{{.Config.Image}}", container], { encoding: "utf-8", stdio: ["ignore", "pipe", "pipe"], timeout: 5000 }).trim();
|
|
29232
29829
|
return out.length > 0 ? out : null;
|
|
29233
29830
|
} catch {
|
|
29234
29831
|
return null;
|
|
@@ -29254,7 +29851,7 @@ function detectSingletonDrift(deps) {
|
|
|
29254
29851
|
});
|
|
29255
29852
|
}
|
|
29256
29853
|
function defaultRecreate(dockerBin, project, composeFile, service) {
|
|
29257
|
-
|
|
29854
|
+
execFileSync8(dockerBin, ["compose", "-p", project, "-f", composeFile, ...composeEnvFileArgs(composeFile), "up", "-d", "--no-deps", service], { stdio: ["ignore", "pipe", "pipe"], timeout: 120000 });
|
|
29258
29855
|
}
|
|
29259
29856
|
function reconcileSingletons(deps) {
|
|
29260
29857
|
const dockerBin = deps.dockerBin ?? "docker";
|
|
@@ -29297,8 +29894,8 @@ var init_singleton_reconcile = __esm(() => {
|
|
|
29297
29894
|
});
|
|
29298
29895
|
|
|
29299
29896
|
// src/agents/lifecycle.ts
|
|
29300
|
-
import { execFileSync as
|
|
29301
|
-
import { existsSync as existsSync20, mkdirSync as mkdirSync14, writeFileSync as writeFileSync7, renameSync as
|
|
29897
|
+
import { execFileSync as execFileSync9, spawn, spawnSync } from "node:child_process";
|
|
29898
|
+
import { existsSync as existsSync20, mkdirSync as mkdirSync14, writeFileSync as writeFileSync7, renameSync as renameSync5, readFileSync as readFileSync17 } from "node:fs";
|
|
29302
29899
|
import { resolve as resolve14, join as join14 } from "node:path";
|
|
29303
29900
|
import { connect } from "node:net";
|
|
29304
29901
|
function cleanShutdownMarkerPathForAgent(name) {
|
|
@@ -29320,7 +29917,7 @@ function writeRestartReasonMarker(name, reason, opts = {}) {
|
|
|
29320
29917
|
const marker = { ts: Date.now(), signal: "SIGTERM", reason };
|
|
29321
29918
|
const tmp = `${path}.tmp-${process.pid}-${Date.now()}`;
|
|
29322
29919
|
writeFileSync7(tmp, JSON.stringify(marker), "utf-8");
|
|
29323
|
-
|
|
29920
|
+
renameSync5(tmp, path);
|
|
29324
29921
|
} catch {}
|
|
29325
29922
|
}
|
|
29326
29923
|
function buildCliRestartReason(opts) {
|
|
@@ -29328,7 +29925,7 @@ function buildCliRestartReason(opts) {
|
|
|
29328
29925
|
if (!buildCommit)
|
|
29329
29926
|
return "cli: restart";
|
|
29330
29927
|
try {
|
|
29331
|
-
const head =
|
|
29928
|
+
const head = execFileSync9("git", ["rev-parse", "HEAD"], {
|
|
29332
29929
|
cwd: cwd ?? process.cwd(),
|
|
29333
29930
|
encoding: "utf-8",
|
|
29334
29931
|
stdio: ["ignore", "pipe", "ignore"]
|
|
@@ -29339,7 +29936,7 @@ function buildCliRestartReason(opts) {
|
|
|
29339
29936
|
return "cli: restart";
|
|
29340
29937
|
let subject = "";
|
|
29341
29938
|
try {
|
|
29342
|
-
subject =
|
|
29939
|
+
subject = execFileSync9("git", ["log", "-1", "--pretty=%s", head], {
|
|
29343
29940
|
cwd: cwd ?? process.cwd(),
|
|
29344
29941
|
encoding: "utf-8",
|
|
29345
29942
|
stdio: ["ignore", "pipe", "ignore"]
|
|
@@ -29369,7 +29966,7 @@ function reconcileSingletonImages(log) {
|
|
|
29369
29966
|
}
|
|
29370
29967
|
function dockerSync(args) {
|
|
29371
29968
|
try {
|
|
29372
|
-
return
|
|
29969
|
+
return execFileSync9("docker", args, {
|
|
29373
29970
|
encoding: "utf-8",
|
|
29374
29971
|
stdio: ["ignore", "pipe", "pipe"]
|
|
29375
29972
|
}).trim();
|
|
@@ -29758,12 +30355,12 @@ var init_lifecycle = __esm(() => {
|
|
|
29758
30355
|
});
|
|
29759
30356
|
|
|
29760
30357
|
// src/auth/pane-ready-probe.ts
|
|
29761
|
-
import { execFileSync as
|
|
30358
|
+
import { execFileSync as execFileSync11 } from "node:child_process";
|
|
29762
30359
|
function defaultPaneReadyDeps() {
|
|
29763
30360
|
return {
|
|
29764
30361
|
capturePane(sessionName) {
|
|
29765
30362
|
try {
|
|
29766
|
-
return
|
|
30363
|
+
return execFileSync11("tmux", ["capture-pane", "-p", "-t", sessionName, "-S", "-200"], {
|
|
29767
30364
|
encoding: "utf-8",
|
|
29768
30365
|
stdio: ["pipe", "pipe", "pipe"]
|
|
29769
30366
|
}).trim();
|
|
@@ -29805,12 +30402,12 @@ import {
|
|
|
29805
30402
|
mkdirSync as mkdirSync16,
|
|
29806
30403
|
readFileSync as readFileSync21,
|
|
29807
30404
|
readdirSync as readdirSync10,
|
|
29808
|
-
renameSync as
|
|
29809
|
-
rmSync as
|
|
30405
|
+
renameSync as renameSync7,
|
|
30406
|
+
rmSync as rmSync5,
|
|
29810
30407
|
statSync as statSync12,
|
|
29811
30408
|
writeFileSync as writeFileSync9
|
|
29812
30409
|
} from "node:fs";
|
|
29813
|
-
import { randomBytes as
|
|
30410
|
+
import { randomBytes as randomBytes3 } from "node:crypto";
|
|
29814
30411
|
import { join as join17 } from "node:path";
|
|
29815
30412
|
function claudeDir(agentDir) {
|
|
29816
30413
|
return join17(agentDir, ".claude");
|
|
@@ -29944,13 +30541,13 @@ function syncLegacyFromActive(agentDir) {
|
|
|
29944
30541
|
}
|
|
29945
30542
|
function atomicCopy(src, dest, mode) {
|
|
29946
30543
|
const contents = readFileSync21(src);
|
|
29947
|
-
const tmp = `${dest}.tmp-${process.pid}-${
|
|
30544
|
+
const tmp = `${dest}.tmp-${process.pid}-${randomBytes3(4).toString("hex")}`;
|
|
29948
30545
|
try {
|
|
29949
30546
|
writeFileSync9(tmp, contents, { mode });
|
|
29950
|
-
|
|
30547
|
+
renameSync7(tmp, dest);
|
|
29951
30548
|
} catch (err) {
|
|
29952
30549
|
try {
|
|
29953
|
-
|
|
30550
|
+
rmSync5(tmp);
|
|
29954
30551
|
} catch {}
|
|
29955
30552
|
throw err;
|
|
29956
30553
|
}
|
|
@@ -30044,7 +30641,7 @@ var init_accounts = __esm(() => {
|
|
|
30044
30641
|
});
|
|
30045
30642
|
|
|
30046
30643
|
// src/auth/manager.ts
|
|
30047
|
-
import { execFileSync as
|
|
30644
|
+
import { execFileSync as execFileSync12 } from "node:child_process";
|
|
30048
30645
|
import {
|
|
30049
30646
|
readFileSync as readFileSync22,
|
|
30050
30647
|
readdirSync as readdirSync11,
|
|
@@ -30052,7 +30649,7 @@ import {
|
|
|
30052
30649
|
writeFileSync as writeFileSync10,
|
|
30053
30650
|
mkdirSync as mkdirSync17,
|
|
30054
30651
|
mkdtempSync as mkdtempSync2,
|
|
30055
|
-
rmSync as
|
|
30652
|
+
rmSync as rmSync6,
|
|
30056
30653
|
chmodSync as chmodSync3,
|
|
30057
30654
|
statSync as statSync13
|
|
30058
30655
|
} from "node:fs";
|
|
@@ -30095,14 +30692,14 @@ function authSessionName(name, slot) {
|
|
|
30095
30692
|
return `${base}-${slot.replace(/[^a-zA-Z0-9_.-]/g, "-")}`;
|
|
30096
30693
|
}
|
|
30097
30694
|
function tmux(args) {
|
|
30098
|
-
return
|
|
30695
|
+
return execFileSync12("tmux", args, {
|
|
30099
30696
|
encoding: "utf-8",
|
|
30100
30697
|
stdio: ["pipe", "pipe", "pipe"]
|
|
30101
30698
|
}).trim();
|
|
30102
30699
|
}
|
|
30103
30700
|
function tmuxSessionExists(sessionName) {
|
|
30104
30701
|
try {
|
|
30105
|
-
|
|
30702
|
+
execFileSync12("tmux", ["has-session", "-t", sessionName], {
|
|
30106
30703
|
stdio: ["pipe", "pipe", "pipe"]
|
|
30107
30704
|
});
|
|
30108
30705
|
return true;
|
|
@@ -30180,7 +30777,7 @@ function writeAuthSessionMeta(agentDir, meta) {
|
|
|
30180
30777
|
});
|
|
30181
30778
|
}
|
|
30182
30779
|
function clearAuthSessionMeta(agentDir) {
|
|
30183
|
-
|
|
30780
|
+
rmSync6(authSessionMetaPath(agentDir), { force: true });
|
|
30184
30781
|
}
|
|
30185
30782
|
function isSessionStale(agentDir, sessionName) {
|
|
30186
30783
|
const meta = readJsonFile(authSessionMetaPath(agentDir));
|
|
@@ -30206,7 +30803,7 @@ function cleanupAuthTempDirs(agentDir) {
|
|
|
30206
30803
|
try {
|
|
30207
30804
|
for (const entry of readdirSync11(dir)) {
|
|
30208
30805
|
if (entry.startsWith(".setup-token-tmp-")) {
|
|
30209
|
-
|
|
30806
|
+
rmSync6(join18(dir, entry), { recursive: true, force: true });
|
|
30210
30807
|
}
|
|
30211
30808
|
}
|
|
30212
30809
|
} catch {}
|
|
@@ -30364,7 +30961,7 @@ function startAuthSession(name, agentDir, opts = {}) {
|
|
|
30364
30961
|
mkdirSync17(claudeDir2(agentDir), { recursive: true });
|
|
30365
30962
|
cleanupAuthTempDirs(agentDir);
|
|
30366
30963
|
const logPath = authLogPath(agentDir);
|
|
30367
|
-
|
|
30964
|
+
rmSync6(logPath, { force: true });
|
|
30368
30965
|
let configDir;
|
|
30369
30966
|
if (opts.force) {
|
|
30370
30967
|
configDir = mkdtempSync2(join18(claudeDir2(agentDir), ".setup-token-tmp-"));
|
|
@@ -30520,8 +31117,8 @@ function submitAuthCode(name, agentDir, code, slot, _opts = {}) {
|
|
|
30520
31117
|
} catch {}
|
|
30521
31118
|
clearAuthSessionMeta(agentDir);
|
|
30522
31119
|
cleanupAuthTempDirs(agentDir);
|
|
30523
|
-
|
|
30524
|
-
|
|
31120
|
+
rmSync6(authLogPath(agentDir), { force: true });
|
|
31121
|
+
rmSync6(credentialsPath(agentDir), { force: true });
|
|
30525
31122
|
const outcome = { kind: "success" };
|
|
30526
31123
|
return {
|
|
30527
31124
|
completed: true,
|
|
@@ -30652,7 +31249,7 @@ __export(exports_inject, {
|
|
|
30652
31249
|
INJECT_BLOCKED: () => INJECT_BLOCKED,
|
|
30653
31250
|
INJECT_ALLOWLIST: () => INJECT_ALLOWLIST
|
|
30654
31251
|
});
|
|
30655
|
-
import { execFileSync as
|
|
31252
|
+
import { execFileSync as execFileSync14 } from "node:child_process";
|
|
30656
31253
|
function validateInjectCommand(command) {
|
|
30657
31254
|
if (typeof command !== "string" || command.trim().length === 0) {
|
|
30658
31255
|
throw new InjectError("invalid", "command is empty");
|
|
@@ -30698,7 +31295,7 @@ function makeTmuxRunner(tmuxBin) {
|
|
|
30698
31295
|
return {
|
|
30699
31296
|
capture(socket, session) {
|
|
30700
31297
|
try {
|
|
30701
|
-
return
|
|
31298
|
+
return execFileSync14(tmuxBin, ["-L", socket, "capture-pane", "-p", "-t", session, "-S", "-200"], { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] });
|
|
30702
31299
|
} catch {
|
|
30703
31300
|
return null;
|
|
30704
31301
|
}
|
|
@@ -30708,11 +31305,11 @@ function makeTmuxRunner(tmuxBin) {
|
|
|
30708
31305
|
const flagEnd = rest.findIndex((a) => !a.startsWith("-"));
|
|
30709
31306
|
const flagsBeforeKeys = flagEnd === -1 ? rest : rest.slice(0, flagEnd);
|
|
30710
31307
|
const keys = flagEnd === -1 ? [] : rest.slice(flagEnd);
|
|
30711
|
-
|
|
31308
|
+
execFileSync14(tmuxBin, ["-L", socket, subcmd, ...flagsBeforeKeys, "-t", session, ...keys], { stdio: ["pipe", "pipe", "pipe"] });
|
|
30712
31309
|
},
|
|
30713
31310
|
hasSession(socket, session) {
|
|
30714
31311
|
try {
|
|
30715
|
-
|
|
31312
|
+
execFileSync14(tmuxBin, ["-L", socket, "has-session", "-t", session], {
|
|
30716
31313
|
stdio: ["pipe", "pipe", "pipe"]
|
|
30717
31314
|
});
|
|
30718
31315
|
return true;
|
|
@@ -31607,8 +32204,8 @@ import {
|
|
|
31607
32204
|
mkdirSync as mkdirSync19,
|
|
31608
32205
|
readFileSync as readFileSync26,
|
|
31609
32206
|
readdirSync as readdirSync15,
|
|
31610
|
-
renameSync as
|
|
31611
|
-
rmSync as
|
|
32207
|
+
renameSync as renameSync8,
|
|
32208
|
+
rmSync as rmSync11,
|
|
31612
32209
|
statSync as statSync16,
|
|
31613
32210
|
writeFileSync as writeFileSync14
|
|
31614
32211
|
} from "node:fs";
|
|
@@ -31737,73 +32334,6 @@ var init_google_workspace_yaml = __esm(() => {
|
|
|
31737
32334
|
import_yaml7 = __toESM(require_dist(), 1);
|
|
31738
32335
|
});
|
|
31739
32336
|
|
|
31740
|
-
// src/util/atomic.ts
|
|
31741
|
-
var exports_atomic = {};
|
|
31742
|
-
__export(exports_atomic, {
|
|
31743
|
-
writeConfigFileSync: () => writeConfigFileSync,
|
|
31744
|
-
atomicWriteJsonSync: () => atomicWriteJsonSync,
|
|
31745
|
-
atomicWriteFileSync: () => atomicWriteFileSync2
|
|
31746
|
-
});
|
|
31747
|
-
import { randomBytes as randomBytes3 } from "node:crypto";
|
|
31748
|
-
import { closeSync as closeSync5, constants, fsyncSync as fsyncSync3, openSync as openSync5, renameSync as renameSync8, rmSync as rmSync11, writeSync as writeSync4 } from "node:fs";
|
|
31749
|
-
function atomicWriteFileSync2(destPath, contents, mode = 384) {
|
|
31750
|
-
const tmp = `${destPath}.tmp-${process.pid}-${randomBytes3(4).toString("hex")}`;
|
|
31751
|
-
const buf = typeof contents === "string" ? Buffer.from(contents, "utf-8") : contents;
|
|
31752
|
-
let fd = null;
|
|
31753
|
-
try {
|
|
31754
|
-
fd = openSync5(tmp, TMP_OPEN_FLAGS, mode);
|
|
31755
|
-
writeSync4(fd, buf, 0, buf.length, 0);
|
|
31756
|
-
fsyncSync3(fd);
|
|
31757
|
-
closeSync5(fd);
|
|
31758
|
-
fd = null;
|
|
31759
|
-
renameSync8(tmp, destPath);
|
|
31760
|
-
} catch (err) {
|
|
31761
|
-
if (fd !== null) {
|
|
31762
|
-
try {
|
|
31763
|
-
closeSync5(fd);
|
|
31764
|
-
} catch {}
|
|
31765
|
-
}
|
|
31766
|
-
try {
|
|
31767
|
-
rmSync11(tmp, { force: true });
|
|
31768
|
-
} catch {}
|
|
31769
|
-
throw err;
|
|
31770
|
-
}
|
|
31771
|
-
}
|
|
31772
|
-
function atomicWriteJsonSync(destPath, value, mode = 384) {
|
|
31773
|
-
atomicWriteFileSync2(destPath, JSON.stringify(value, null, 2) + `
|
|
31774
|
-
`, mode);
|
|
31775
|
-
}
|
|
31776
|
-
function writeConfigFileSync(destPath, contents, mode = 420) {
|
|
31777
|
-
try {
|
|
31778
|
-
atomicWriteFileSync2(destPath, contents, mode);
|
|
31779
|
-
} catch (err) {
|
|
31780
|
-
const code = err.code;
|
|
31781
|
-
if (code !== "EBUSY" && code !== "EXDEV" && code !== "EINVAL") {
|
|
31782
|
-
throw err;
|
|
31783
|
-
}
|
|
31784
|
-
const buf = typeof contents === "string" ? Buffer.from(contents, "utf-8") : contents;
|
|
31785
|
-
let fd = null;
|
|
31786
|
-
try {
|
|
31787
|
-
fd = openSync5(destPath, constants.O_WRONLY | constants.O_TRUNC, mode);
|
|
31788
|
-
writeSync4(fd, buf, 0, buf.length, 0);
|
|
31789
|
-
fsyncSync3(fd);
|
|
31790
|
-
closeSync5(fd);
|
|
31791
|
-
fd = null;
|
|
31792
|
-
} catch (fallbackErr) {
|
|
31793
|
-
if (fd !== null) {
|
|
31794
|
-
try {
|
|
31795
|
-
closeSync5(fd);
|
|
31796
|
-
} catch {}
|
|
31797
|
-
}
|
|
31798
|
-
throw fallbackErr;
|
|
31799
|
-
}
|
|
31800
|
-
}
|
|
31801
|
-
}
|
|
31802
|
-
var TMP_OPEN_FLAGS;
|
|
31803
|
-
var init_atomic = __esm(() => {
|
|
31804
|
-
TMP_OPEN_FLAGS = constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL | (constants.O_NOFOLLOW ?? 0);
|
|
31805
|
-
});
|
|
31806
|
-
|
|
31807
32337
|
// src/drive/oauth.ts
|
|
31808
32338
|
var exports_oauth = {};
|
|
31809
32339
|
__export(exports_oauth, {
|
|
@@ -33536,7 +34066,7 @@ __export(exports_via_claude, {
|
|
|
33536
34066
|
PRE_PASTE_RULES: () => PRE_PASTE_RULES,
|
|
33537
34067
|
POST_PASTE_RULES: () => POST_PASTE_RULES
|
|
33538
34068
|
});
|
|
33539
|
-
import { execFileSync as
|
|
34069
|
+
import { execFileSync as execFileSync15, spawnSync as spawnSync3 } from "node:child_process";
|
|
33540
34070
|
import { existsSync as existsSync34, mkdirSync as mkdirSync20, readFileSync as readFileSync29 } from "node:fs";
|
|
33541
34071
|
import { join as join26, resolve as resolve24 } from "node:path";
|
|
33542
34072
|
function tmuxHasSession(session) {
|
|
@@ -33547,7 +34077,7 @@ function tmuxHasSession(session) {
|
|
|
33547
34077
|
}
|
|
33548
34078
|
function tmuxKillSession(session) {
|
|
33549
34079
|
try {
|
|
33550
|
-
|
|
34080
|
+
execFileSync15("tmux", ["kill-session", "-t", session], {
|
|
33551
34081
|
stdio: "ignore",
|
|
33552
34082
|
timeout: 3000
|
|
33553
34083
|
});
|
|
@@ -33555,7 +34085,7 @@ function tmuxKillSession(session) {
|
|
|
33555
34085
|
}
|
|
33556
34086
|
function tmuxCapturePane(session) {
|
|
33557
34087
|
try {
|
|
33558
|
-
const out =
|
|
34088
|
+
const out = execFileSync15("tmux", ["capture-pane", "-p", "-t", session, "-S", "-200"], { timeout: 3000, stdio: ["ignore", "pipe", "pipe"], maxBuffer: 4 * 1024 * 1024 });
|
|
33559
34089
|
return out.toString("utf8");
|
|
33560
34090
|
} catch {
|
|
33561
34091
|
return "";
|
|
@@ -33563,7 +34093,7 @@ function tmuxCapturePane(session) {
|
|
|
33563
34093
|
}
|
|
33564
34094
|
function tmuxSendKeys(session, keys, literal = false) {
|
|
33565
34095
|
try {
|
|
33566
|
-
|
|
34096
|
+
execFileSync15("tmux", ["send-keys", "-t", session, ...literal ? ["-l"] : [], ...keys], { timeout: 3000, stdio: ["ignore", "pipe", "pipe"] });
|
|
33567
34097
|
} catch {}
|
|
33568
34098
|
}
|
|
33569
34099
|
function extractAuthorizeUrl(pane) {
|
|
@@ -33592,7 +34122,7 @@ async function runViaClaude(opts) {
|
|
|
33592
34122
|
tmuxKillSession(SESSION);
|
|
33593
34123
|
}
|
|
33594
34124
|
const spawn4 = opts.spawnClaude ?? (() => {
|
|
33595
|
-
|
|
34125
|
+
execFileSync15("tmux", [
|
|
33596
34126
|
"new-session",
|
|
33597
34127
|
"-d",
|
|
33598
34128
|
"-s",
|
|
@@ -39970,6 +40500,30 @@ function classifyShmSize(bytes) {
|
|
|
39970
40500
|
const gib = (bytes / 1024 / 1024 / 1024).toFixed(bytes % 1024 ** 3 === 0 ? 0 : 1);
|
|
39971
40501
|
return { name: "hindsight shm-size", status: "ok", detail: `${gib}g` };
|
|
39972
40502
|
}
|
|
40503
|
+
function classifyConsolidationBacklog(queueDepth, oldestAgeSecs = null) {
|
|
40504
|
+
const ageStr = oldestAgeSecs !== null && oldestAgeSecs > 0 ? `, oldest ${Math.round(oldestAgeSecs / 60)}m old` : "";
|
|
40505
|
+
const base = `${queueDepth} pending consolidation op(s)${ageStr}`;
|
|
40506
|
+
if (queueDepth >= CONSOLIDATION_BACKLOG_FAIL) {
|
|
40507
|
+
return {
|
|
40508
|
+
name: "hindsight consolidation backlog",
|
|
40509
|
+
status: "fail",
|
|
40510
|
+
detail: `${base} \u2014 queue is deep enough to be wedged; retains are landing but ` + `not consolidating into recall (drains ~7 ops/hr under the #2894 throttle)`,
|
|
40511
|
+
fix: "Check `docker logs switchroom-hindsight` for the queue-lag WARNING and " + "consolidation errors (quota/429/shm); restart with `switchroom memory " + "--restart` if the worker is stuck."
|
|
40512
|
+
};
|
|
40513
|
+
}
|
|
40514
|
+
if (queueDepth >= CONSOLIDATION_BACKLOG_WARN) {
|
|
40515
|
+
return {
|
|
40516
|
+
name: "hindsight consolidation backlog",
|
|
40517
|
+
status: "warn",
|
|
40518
|
+
detail: `${base} \u2014 building faster than it drains (~7 ops/hr under the #2894 ` + `throttle); recall may lag recent turns until it catches up`
|
|
40519
|
+
};
|
|
40520
|
+
}
|
|
40521
|
+
return {
|
|
40522
|
+
name: "hindsight consolidation backlog",
|
|
40523
|
+
status: "ok",
|
|
40524
|
+
detail: queueDepth > 0 ? base : "queue drained (0 pending)"
|
|
40525
|
+
};
|
|
40526
|
+
}
|
|
39973
40527
|
function classifyExtractionLogs(logs) {
|
|
39974
40528
|
const noSpace = /No space left on device|could not resize shared memory/i.test(logs);
|
|
39975
40529
|
const llmError = /Claude Code returned an error result|claude_code_llm[\s\S]{0,80}error|Fact extraction failed|Content extraction failed/i.test(logs);
|
|
@@ -40026,6 +40580,49 @@ function checkHindsightContainerHealth(opts) {
|
|
|
40026
40580
|
} catch {}
|
|
40027
40581
|
return results;
|
|
40028
40582
|
}
|
|
40583
|
+
function classifyHindsightHealthProbe(status, port) {
|
|
40584
|
+
if (status === 200) {
|
|
40585
|
+
return {
|
|
40586
|
+
name: "hindsight /health",
|
|
40587
|
+
status: "ok",
|
|
40588
|
+
detail: `200 at :${port} \u2014 memory backend live`
|
|
40589
|
+
};
|
|
40590
|
+
}
|
|
40591
|
+
if (status === null) {
|
|
40592
|
+
return {
|
|
40593
|
+
name: "hindsight /health",
|
|
40594
|
+
status: "fail",
|
|
40595
|
+
detail: `no response on :${port} \u2014 the container is down or crash-looping ` + `(fleet memory is silently OFF: auto-recall/retain fail with no user error)`,
|
|
40596
|
+
fix: "Check `docker logs switchroom-hindsight` for `[Errno 98] address " + "already in use` (a foreign process on the port) and run " + "`switchroom memory --start` \u2014 the launcher now preflights the port " + "and reassigns instead of crash-looping."
|
|
40597
|
+
};
|
|
40598
|
+
}
|
|
40599
|
+
return {
|
|
40600
|
+
name: "hindsight /health",
|
|
40601
|
+
status: "fail",
|
|
40602
|
+
detail: `HTTP ${status} at :${port} \u2014 memory backend unhealthy`,
|
|
40603
|
+
fix: "Inspect `docker logs switchroom-hindsight`; restart with `switchroom memory --restart`."
|
|
40604
|
+
};
|
|
40605
|
+
}
|
|
40606
|
+
async function checkHindsightHealthEndpoint(mcpUrl, opts = {}) {
|
|
40607
|
+
const doFetch = opts.fetchImpl ?? fetch;
|
|
40608
|
+
const timeoutMs = opts.timeoutMs ?? 4000;
|
|
40609
|
+
const origin = mcpUrl.replace(/\/mcp\/?$/, "").replace(/\/$/, "");
|
|
40610
|
+
const healthUrl = `${origin}/health`;
|
|
40611
|
+
const port = (() => {
|
|
40612
|
+
const m = origin.match(/:(\d+)(?:$|\/)/);
|
|
40613
|
+
return m ? parseInt(m[1], 10) : 80;
|
|
40614
|
+
})();
|
|
40615
|
+
const controller = new AbortController;
|
|
40616
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
40617
|
+
try {
|
|
40618
|
+
const res = await doFetch(healthUrl, { signal: controller.signal });
|
|
40619
|
+
return classifyHindsightHealthProbe(res.status, port);
|
|
40620
|
+
} catch {
|
|
40621
|
+
return classifyHindsightHealthProbe(null, port);
|
|
40622
|
+
} finally {
|
|
40623
|
+
clearTimeout(timer);
|
|
40624
|
+
}
|
|
40625
|
+
}
|
|
40029
40626
|
function classifyToolContract(advertised) {
|
|
40030
40627
|
const byName = new Map(advertised.map((t) => [t.name, t]));
|
|
40031
40628
|
const results = [];
|
|
@@ -40068,7 +40665,7 @@ function classifyToolContract(advertised) {
|
|
|
40068
40665
|
}
|
|
40069
40666
|
return results;
|
|
40070
40667
|
}
|
|
40071
|
-
var MIN_HINDSIGHT_SHM_BYTES;
|
|
40668
|
+
var MIN_HINDSIGHT_SHM_BYTES, CONSOLIDATION_BACKLOG_WARN = 25, CONSOLIDATION_BACKLOG_FAIL = 200;
|
|
40072
40669
|
var init_doctor_memory = __esm(() => {
|
|
40073
40670
|
init_hindsight_tools();
|
|
40074
40671
|
MIN_HINDSIGHT_SHM_BYTES = 1024 * 1024 * 1024;
|
|
@@ -43474,6 +44071,19 @@ async function checkBankIngestHealth(config, url, opts) {
|
|
|
43474
44071
|
banks.set(bank, []);
|
|
43475
44072
|
}
|
|
43476
44073
|
const inspected = await Promise.all([...banks].map(async ([bankId, agents]) => [bankId, agents, await inspectBankHealth(url, bankId, { fetchImpl: opts?.fetchImpl })]));
|
|
44074
|
+
let backlogRow;
|
|
44075
|
+
if (opts?.includeConsolidationBacklog) {
|
|
44076
|
+
let totalPending = 0;
|
|
44077
|
+
let anyBankReachable = false;
|
|
44078
|
+
for (const [, , h] of inspected) {
|
|
44079
|
+
if (h.ok) {
|
|
44080
|
+
anyBankReachable = true;
|
|
44081
|
+
totalPending += h.pendingOperations;
|
|
44082
|
+
}
|
|
44083
|
+
}
|
|
44084
|
+
if (anyBankReachable)
|
|
44085
|
+
backlogRow = classifyConsolidationBacklog(totalPending);
|
|
44086
|
+
}
|
|
43477
44087
|
for (const [bankId, agents, h] of inspected) {
|
|
43478
44088
|
const label = `bank ${bankId}` + (profileBanks.has(bankId) ? " (profile)" : agents[0] !== bankId ? ` (${agents.join(", ")})` : "");
|
|
43479
44089
|
if (!h.ok) {
|
|
@@ -43535,6 +44145,8 @@ async function checkBankIngestHealth(config, url, opts) {
|
|
|
43535
44145
|
}
|
|
43536
44146
|
results.push({ name: label, status: "ok", detail: summary });
|
|
43537
44147
|
}
|
|
44148
|
+
if (backlogRow)
|
|
44149
|
+
results.push(backlogRow);
|
|
43538
44150
|
return results;
|
|
43539
44151
|
}
|
|
43540
44152
|
function hindsightDocReprocessUrl(mcpUrl, bankId, docId) {
|
|
@@ -43545,7 +44157,7 @@ async function checkHindsight(config) {
|
|
|
43545
44157
|
if (!isHindsightEnabled(config)) {
|
|
43546
44158
|
return [];
|
|
43547
44159
|
}
|
|
43548
|
-
const url = config.memory?.config?.url ??
|
|
44160
|
+
const url = config.memory?.config?.url ?? HINDSIGHT_DEFAULT_MCP_URL;
|
|
43549
44161
|
const results = [];
|
|
43550
44162
|
const match = url.match(/^https?:\/\/([^:/]+):?(\d+)?/);
|
|
43551
44163
|
if (!match) {
|
|
@@ -43591,7 +44203,8 @@ async function checkHindsight(config) {
|
|
|
43591
44203
|
}
|
|
43592
44204
|
results.push(checkHindsightConsumer(config));
|
|
43593
44205
|
results.push(...checkHindsightContainerHealth());
|
|
43594
|
-
results.push(
|
|
44206
|
+
results.push(await checkHindsightHealthEndpoint(url));
|
|
44207
|
+
results.push(...await checkBankIngestHealth(config, url, { includeConsolidationBacklog: true }));
|
|
43595
44208
|
for (const [agentName, agentConfig] of Object.entries(config.agents)) {
|
|
43596
44209
|
const bankId = agentConfig.memory?.collection ?? agentName;
|
|
43597
44210
|
const hasBankMission = !!agentConfig.memory?.bank_mission;
|
|
@@ -44745,6 +45358,7 @@ var init_doctor = __esm(() => {
|
|
|
44745
45358
|
init_manager();
|
|
44746
45359
|
init_accounts();
|
|
44747
45360
|
init_manifest();
|
|
45361
|
+
init_hindsight2();
|
|
44748
45362
|
init_hindsight();
|
|
44749
45363
|
init_doctor_memory();
|
|
44750
45364
|
init_doctor_docker();
|
|
@@ -48352,12 +48966,15 @@ var exports_apply = {};
|
|
|
48352
48966
|
__export(exports_apply, {
|
|
48353
48967
|
writeInstallTypeCache: () => writeInstallTypeCache,
|
|
48354
48968
|
runApplyPreflight: () => runApplyPreflight,
|
|
48969
|
+
runApplyDryRun: () => runApplyDryRun,
|
|
48355
48970
|
runApply: () => runApply,
|
|
48356
48971
|
resolveVaultBindMountDir: () => resolveVaultBindMountDir,
|
|
48357
48972
|
resolveOperatorVaultPassphrase: () => resolveOperatorVaultPassphrase,
|
|
48358
48973
|
registerApplyCommand: () => registerApplyCommand,
|
|
48359
48974
|
reexecUnderSudo: () => reexecUnderSudo,
|
|
48360
48975
|
provisionLiteLLMKeys: () => provisionLiteLLMKeys,
|
|
48976
|
+
probeVaultProvisioning: () => probeVaultProvisioning,
|
|
48977
|
+
parseComposeServiceNames: () => parseComposeServiceNames,
|
|
48361
48978
|
isInAgentContainer: () => isInAgentContainer,
|
|
48362
48979
|
inspectVaultBindMountDir: () => inspectVaultBindMountDir,
|
|
48363
48980
|
formatScaffoldFailureResolution: () => formatScaffoldFailureResolution,
|
|
@@ -49040,6 +49657,186 @@ Done. Scaffolded ${scaffolded}/${allAgentNames.length} agents.
|
|
|
49040
49657
|
failures
|
|
49041
49658
|
};
|
|
49042
49659
|
}
|
|
49660
|
+
async function probeVaultProvisioning(config, agentNames, home2) {
|
|
49661
|
+
const optedIn = [];
|
|
49662
|
+
for (const name of agentNames) {
|
|
49663
|
+
const agentConfig = config.agents[name];
|
|
49664
|
+
if (!agentConfig)
|
|
49665
|
+
continue;
|
|
49666
|
+
const resolved = resolveAgentConfig(config.defaults, config.profiles, agentConfig);
|
|
49667
|
+
if (effectiveLiteLLMEnabled(config, resolved.litellm))
|
|
49668
|
+
optedIn.push(name);
|
|
49669
|
+
}
|
|
49670
|
+
const needsHindsight = config.litellm?.enabled === true && config.memory?.backend === "hindsight";
|
|
49671
|
+
const passphraseAvailable = await resolveOperatorVaultPassphrase(home2) !== null;
|
|
49672
|
+
const alreadyProvisioned = [];
|
|
49673
|
+
const unprovisionedNew = [];
|
|
49674
|
+
const brokerUnreachable = [];
|
|
49675
|
+
if (optedIn.length > 0) {
|
|
49676
|
+
const { getViaBrokerStructured: getViaBrokerStructured2 } = await Promise.resolve().then(() => (init_client(), exports_client));
|
|
49677
|
+
for (const name of optedIn) {
|
|
49678
|
+
const existing = await getViaBrokerStructured2(`litellm/${name}/api-key`);
|
|
49679
|
+
if (existing.kind === "ok")
|
|
49680
|
+
alreadyProvisioned.push(name);
|
|
49681
|
+
else if (existing.kind === "unreachable")
|
|
49682
|
+
brokerUnreachable.push(name);
|
|
49683
|
+
else
|
|
49684
|
+
unprovisionedNew.push(name);
|
|
49685
|
+
}
|
|
49686
|
+
}
|
|
49687
|
+
return {
|
|
49688
|
+
optedInCount: optedIn.length,
|
|
49689
|
+
passphraseAvailable,
|
|
49690
|
+
alreadyProvisioned,
|
|
49691
|
+
unprovisionedNew,
|
|
49692
|
+
brokerUnreachable,
|
|
49693
|
+
needsHindsight
|
|
49694
|
+
};
|
|
49695
|
+
}
|
|
49696
|
+
function parseComposeServiceNames(compose) {
|
|
49697
|
+
const lines = compose.split(`
|
|
49698
|
+
`);
|
|
49699
|
+
const names = [];
|
|
49700
|
+
let inServices = false;
|
|
49701
|
+
for (const line of lines) {
|
|
49702
|
+
if (/^services:\s*$/.test(line)) {
|
|
49703
|
+
inServices = true;
|
|
49704
|
+
continue;
|
|
49705
|
+
}
|
|
49706
|
+
if (inServices && /^\S/.test(line))
|
|
49707
|
+
break;
|
|
49708
|
+
if (!inServices)
|
|
49709
|
+
continue;
|
|
49710
|
+
const m = /^ {2}([A-Za-z0-9_.-]+):\s*$/.exec(line);
|
|
49711
|
+
if (m)
|
|
49712
|
+
names.push(m[1]);
|
|
49713
|
+
}
|
|
49714
|
+
return names;
|
|
49715
|
+
}
|
|
49716
|
+
async function runApplyDryRun(config, options, deps = {}, switchroomConfigPath) {
|
|
49717
|
+
const writeOut = deps.writeOut ?? ((s) => process.stdout.write(s));
|
|
49718
|
+
const writeErr = deps.writeErr ?? ((s) => process.stderr.write(s));
|
|
49719
|
+
const blocking = [];
|
|
49720
|
+
const warnings = [];
|
|
49721
|
+
const info = [];
|
|
49722
|
+
writeOut(source_default.bold(`
|
|
49723
|
+
Dry-run: validating switchroom config (no changes will be written)...
|
|
49724
|
+
`));
|
|
49725
|
+
try {
|
|
49726
|
+
runApplyPreflight(config, { detectComposeV2: deps.detectComposeV2 });
|
|
49727
|
+
} catch (err) {
|
|
49728
|
+
blocking.push(`preflight: ${err.message}`);
|
|
49729
|
+
}
|
|
49730
|
+
const allAgentNames = Object.keys(config.agents ?? {});
|
|
49731
|
+
const agentNames = options.only !== undefined ? [options.only] : allAgentNames;
|
|
49732
|
+
if (options.only !== undefined && !allAgentNames.includes(options.only)) {
|
|
49733
|
+
blocking.push(`apply --only=${options.only}: no such agent in switchroom.yaml (defined: ${allAgentNames.join(", ") || "none"}).`);
|
|
49734
|
+
}
|
|
49735
|
+
for (const name of agentNames) {
|
|
49736
|
+
const agentConfig = config.agents[name];
|
|
49737
|
+
if (!agentConfig)
|
|
49738
|
+
continue;
|
|
49739
|
+
const profileName = agentConfig.extends ?? "default";
|
|
49740
|
+
try {
|
|
49741
|
+
getProfilePath(profileName);
|
|
49742
|
+
resolveAgentConfig(config.defaults, config.profiles, agentConfig);
|
|
49743
|
+
} catch (err) {
|
|
49744
|
+
blocking.push(`profile '${profileName}' for agent '${name}': ${err.message}`);
|
|
49745
|
+
}
|
|
49746
|
+
}
|
|
49747
|
+
try {
|
|
49748
|
+
const probe2 = await probeVaultProvisioning(config, agentNames, homedir47());
|
|
49749
|
+
if (probe2.alreadyProvisioned.length > 0) {
|
|
49750
|
+
info.push(`litellm: ${probe2.alreadyProvisioned.length} agent(s) already provisioned (${probe2.alreadyProvisioned.join(", ")}).`);
|
|
49751
|
+
}
|
|
49752
|
+
if (probe2.brokerUnreachable.length > 0) {
|
|
49753
|
+
warnings.push(`litellm: vault-broker unreachable \u2014 could not verify/provision ` + `${probe2.brokerUnreachable.length} agent(s) (${probe2.brokerUnreachable.join(", ")}); they keep their previous routing env.`);
|
|
49754
|
+
}
|
|
49755
|
+
const pending = [
|
|
49756
|
+
...probe2.unprovisionedNew,
|
|
49757
|
+
...probe2.needsHindsight ? ["hindsight (service)"] : []
|
|
49758
|
+
];
|
|
49759
|
+
if (pending.length > 0) {
|
|
49760
|
+
if (probe2.passphraseAvailable) {
|
|
49761
|
+
info.push(`litellm: would provision ${pending.length} new virtual key(s) (${pending.join(", ")}) \u2014 vault passphrase is available.`);
|
|
49762
|
+
} else {
|
|
49763
|
+
blocking.push(`litellm: ${pending.length} agent(s)/service(s) need a NEW vault key (${pending.join(", ")}) but the vault passphrase is unavailable (no SWITCHROOM_VAULT_PASSPHRASE env and machine-id auto-unlock blob ` + `missing/undecryptable) \u2014 a real apply could not provision them.`);
|
|
49764
|
+
}
|
|
49765
|
+
}
|
|
49766
|
+
} catch (err) {
|
|
49767
|
+
warnings.push(`litellm: provisioning probe failed \u2014 ${err.message}`);
|
|
49768
|
+
}
|
|
49769
|
+
let imageTag = null;
|
|
49770
|
+
let previousImageTag = null;
|
|
49771
|
+
let composeChanged = false;
|
|
49772
|
+
let addedServices = [];
|
|
49773
|
+
let removedServices = [];
|
|
49774
|
+
try {
|
|
49775
|
+
const computed = await computeComposeContent({
|
|
49776
|
+
config,
|
|
49777
|
+
composePath: options.outPath ?? DEFAULT_COMPOSE_PATH2,
|
|
49778
|
+
switchroomConfigPath,
|
|
49779
|
+
releaseOverride: options.releaseOverride,
|
|
49780
|
+
buildMode: options.buildLocal ? "local" : "pull",
|
|
49781
|
+
buildContext: options.buildContext
|
|
49782
|
+
});
|
|
49783
|
+
imageTag = computed.imageTag;
|
|
49784
|
+
previousImageTag = computed.previousImageTag;
|
|
49785
|
+
composeChanged = computed.previous !== computed.content;
|
|
49786
|
+
const newServices = parseComposeServiceNames(computed.content);
|
|
49787
|
+
const oldServices = computed.previous ? parseComposeServiceNames(computed.previous) : [];
|
|
49788
|
+
addedServices = newServices.filter((s) => !oldServices.includes(s));
|
|
49789
|
+
removedServices = oldServices.filter((s) => !newServices.includes(s));
|
|
49790
|
+
if (previousImageTag === null) {
|
|
49791
|
+
info.push(`compose: no existing compose on disk \u2014 a real apply would create it.`);
|
|
49792
|
+
} else if (previousImageTag !== imageTag) {
|
|
49793
|
+
info.push(`compose: agent image would change ${previousImageTag} \u2192 ${imageTag}.`);
|
|
49794
|
+
} else {
|
|
49795
|
+
info.push(`compose: agent image unchanged (${imageTag}).`);
|
|
49796
|
+
}
|
|
49797
|
+
if (addedServices.length > 0) {
|
|
49798
|
+
info.push(`compose: services added: ${addedServices.join(", ")}.`);
|
|
49799
|
+
}
|
|
49800
|
+
if (removedServices.length > 0) {
|
|
49801
|
+
info.push(`compose: services removed: ${removedServices.join(", ")}.`);
|
|
49802
|
+
}
|
|
49803
|
+
if (previousImageTag !== null && !composeChanged) {
|
|
49804
|
+
info.push(`compose: no change (would be byte-identical to the current file).`);
|
|
49805
|
+
}
|
|
49806
|
+
} catch (err) {
|
|
49807
|
+
blocking.push(`compose generation: ${err.message}`);
|
|
49808
|
+
}
|
|
49809
|
+
for (const line of info)
|
|
49810
|
+
writeOut(source_default.gray(` ~ ${line}
|
|
49811
|
+
`));
|
|
49812
|
+
for (const line of warnings)
|
|
49813
|
+
writeErr(source_default.yellow(` ! ${line}
|
|
49814
|
+
`));
|
|
49815
|
+
for (const line of blocking)
|
|
49816
|
+
writeErr(source_default.red(` x ${line}
|
|
49817
|
+
`));
|
|
49818
|
+
const wouldSucceed = blocking.length === 0;
|
|
49819
|
+
writeOut(`
|
|
49820
|
+
`);
|
|
49821
|
+
if (wouldSucceed) {
|
|
49822
|
+
writeOut(source_default.green.bold(`Dry-run: a real apply would SUCCEED. ${allAgentNames.length} agent(s) validated; nothing was written.
|
|
49823
|
+
`));
|
|
49824
|
+
} else {
|
|
49825
|
+
writeErr(source_default.red.bold(`Dry-run: a real apply would FAIL \u2014 ${blocking.length} blocking condition(s) above. Nothing was written.
|
|
49826
|
+
`));
|
|
49827
|
+
}
|
|
49828
|
+
return {
|
|
49829
|
+
blocking,
|
|
49830
|
+
warnings,
|
|
49831
|
+
info,
|
|
49832
|
+
imageTag,
|
|
49833
|
+
previousImageTag,
|
|
49834
|
+
composeChanged,
|
|
49835
|
+
addedServices,
|
|
49836
|
+
removedServices,
|
|
49837
|
+
wouldSucceed
|
|
49838
|
+
};
|
|
49839
|
+
}
|
|
49043
49840
|
function formatScaffoldFailureResolution(failures, scaffolded, agentsTotal) {
|
|
49044
49841
|
const fail3 = failures.length;
|
|
49045
49842
|
const lines = [];
|
|
@@ -49157,7 +49954,7 @@ ERROR: failed to spawn sudo: ${result.error.message}
|
|
|
49157
49954
|
process.exit(result.status ?? 1);
|
|
49158
49955
|
}
|
|
49159
49956
|
function registerApplyCommand(program3) {
|
|
49160
|
-
program3.command("apply").description("Apply switchroom.yaml: scaffold every agent and (re)generate the compose file. Run `docker compose -f <path> up -d` afterwards to bring the fleet up.").option("--build-local [context]", "Dev-only: emit `build:` blocks instead of GHCR `image:` refs so `docker compose up --build` rebuilds from in-tree Dockerfiles. Optional context path (defaults to cwd).").option("-o, --out <path>", `Override compose output path (default: ${DEFAULT_COMPOSE_PATH2}).`).option("--example <name>", "Copy an example config into cwd before applying (e.g., 'switchroom' or 'minimal').").option("--non-interactive", "Skip prompts (e.g. sudo-chown explainer for UID alignment). Use in CI / scripts.").option("--allow-unaligned", "Treat UID-alignment chown failures as warnings instead of hard errors. Unsafe: an unaligned state dir will break the agent on first write. Use only if you know you'll fix ownership out-of-band.").option("--only <agent>", "Restrict scaffold + UID-alignment to a single agent (compose still covers the full fleet). Use during a v0.6 \u2192 v0.7 cutover to migrate agents one at a time without breaking the systemd-managed siblings.").option("--compose-only", "Skip the per-agent scaffold loop entirely; only (re)generate the compose file. Use in CI / scripts that can't chown into per-agent state dirs (mode 0700, owned by per-agent UIDs in v0.7+ docker mode). The full apply still runs preflight + emits compose; only the start.sh / .mcp.json / settings.json refresh is skipped.").option("--no-doctor", "Skip the post-apply doctor sweep that surfaces stale start.sh / unhealthy agents (#929). Default: doctor runs after a successful scaffold so the operator sees whether the v0.7+ post-Phase-4 supervisor block is now in place. `switchroom update` passes this internally to avoid running doctor twice (it has its own doctor step).").addOption(new Option("--channel <c>", "Override the resolved `release` block for this apply run: follow the named channel pointer (dev|rc|latest). Mutually exclusive with --pin.").choices(["dev", "rc", "latest"]).conflicts("pin")).addOption(new Option("--pin <p>", "Override the resolved `release` block for this apply run: pin to a specific build (sha-<7-40 hex> or v<semver>). Mutually exclusive with --channel.").conflicts("channel")).option("--print-sudo-cmd", "Print the sudo invocation that `apply` would re-exec itself with when escalation is needed, then exit. Operators who want to script the escalation themselves (CI, custom orchestration) can capture this. Note: tokens are space-separated and not shell-quoted; re-quote arguments if pasting into a shell.").addOption(new Option("--skip-self-elevate").default(false).hideHelp()).action(async (opts) => {
|
|
49957
|
+
program3.command("apply").description("Apply switchroom.yaml: scaffold every agent and (re)generate the compose file. Run `docker compose -f <path> up -d` afterwards to bring the fleet up.").option("--build-local [context]", "Dev-only: emit `build:` blocks instead of GHCR `image:` refs so `docker compose up --build` rebuilds from in-tree Dockerfiles. Optional context path (defaults to cwd).").option("-o, --out <path>", `Override compose output path (default: ${DEFAULT_COMPOSE_PATH2}).`).option("--example <name>", "Copy an example config into cwd before applying (e.g., 'switchroom' or 'minimal').").option("--non-interactive", "Skip prompts (e.g. sudo-chown explainer for UID alignment). Use in CI / scripts.").option("--allow-unaligned", "Treat UID-alignment chown failures as warnings instead of hard errors. Unsafe: an unaligned state dir will break the agent on first write. Use only if you know you'll fix ownership out-of-band.").option("--only <agent>", "Restrict scaffold + UID-alignment to a single agent (compose still covers the full fleet). Use during a v0.6 \u2192 v0.7 cutover to migrate agents one at a time without breaking the systemd-managed siblings.").option("--compose-only", "Skip the per-agent scaffold loop entirely; only (re)generate the compose file. Use in CI / scripts that can't chown into per-agent state dirs (mode 0700, owned by per-agent UIDs in v0.7+ docker mode). The full apply still runs preflight + emits compose; only the start.sh / .mcp.json / settings.json refresh is skipped.").option("--dry-run", "Validate only: run the full compose-regeneration + validation pipeline IN MEMORY (resolve every agent's profile, probe the vault-broker for key-provisioning gaps, compute the would-be compose) and print a report \u2014 without writing the compose file, persisting anything, or touching a container. Exits non-zero if a real apply would fail (e.g. a profile can't resolve). Use as a pre-flight before a `rollout`/`update` (which only print the step plan, not the config-regeneration that actually fails).").option("--no-doctor", "Skip the post-apply doctor sweep that surfaces stale start.sh / unhealthy agents (#929). Default: doctor runs after a successful scaffold so the operator sees whether the v0.7+ post-Phase-4 supervisor block is now in place. `switchroom update` passes this internally to avoid running doctor twice (it has its own doctor step).").addOption(new Option("--channel <c>", "Override the resolved `release` block for this apply run: follow the named channel pointer (dev|rc|latest). Mutually exclusive with --pin.").choices(["dev", "rc", "latest"]).conflicts("pin")).addOption(new Option("--pin <p>", "Override the resolved `release` block for this apply run: pin to a specific build (sha-<7-40 hex> or v<semver>). Mutually exclusive with --channel.").conflicts("channel")).option("--print-sudo-cmd", "Print the sudo invocation that `apply` would re-exec itself with when escalation is needed, then exit. Operators who want to script the escalation themselves (CI, custom orchestration) can capture this. Note: tokens are space-separated and not shell-quoted; re-quote arguments if pasting into a shell.").addOption(new Option("--skip-self-elevate").default(false).hideHelp()).action(async (opts) => {
|
|
49161
49958
|
try {
|
|
49162
49959
|
if (opts.example) {
|
|
49163
49960
|
copyExampleConfig2(opts.example);
|
|
@@ -49171,6 +49968,23 @@ function registerApplyCommand(program3) {
|
|
|
49171
49968
|
`);
|
|
49172
49969
|
process.exit(0);
|
|
49173
49970
|
}
|
|
49971
|
+
if (opts.dryRun) {
|
|
49972
|
+
const dry = await runApplyDryRun(config, {
|
|
49973
|
+
buildLocal: !!opts.buildLocal,
|
|
49974
|
+
buildContext: typeof opts.buildLocal === "string" ? opts.buildLocal : process.cwd(),
|
|
49975
|
+
outPath: opts.out,
|
|
49976
|
+
only: opts.only,
|
|
49977
|
+
releaseOverride: opts.channel ? { channel: opts.channel } : opts.pin ? { pin: opts.pin } : undefined,
|
|
49978
|
+
dryRun: true
|
|
49979
|
+
}, {}, switchroomConfigPath);
|
|
49980
|
+
await captureEvent("apply_dry_run", {
|
|
49981
|
+
agents_total: Object.keys(config.agents ?? {}).length,
|
|
49982
|
+
would_succeed: dry.wouldSucceed,
|
|
49983
|
+
blocking: dry.blocking.length,
|
|
49984
|
+
warnings: dry.warnings.length
|
|
49985
|
+
});
|
|
49986
|
+
process.exit(dry.wouldSucceed ? 0 : 1);
|
|
49987
|
+
}
|
|
49174
49988
|
if (!opts.skipSelfElevate && !opts.composeOnly && process.geteuid?.() !== 0) {
|
|
49175
49989
|
const unwritable = findUnwritableAgentDirs(config, {
|
|
49176
49990
|
only: opts.only
|
|
@@ -49278,6 +50092,7 @@ var init_apply = __esm(() => {
|
|
|
49278
50092
|
init_update_prompt_hook();
|
|
49279
50093
|
init_compose();
|
|
49280
50094
|
init_write_compose();
|
|
50095
|
+
init_profiles();
|
|
49281
50096
|
init_install_detect();
|
|
49282
50097
|
init_operator_uid();
|
|
49283
50098
|
init_posthog();
|
|
@@ -63737,8 +64552,8 @@ import { existsSync, readFileSync } from "node:fs";
|
|
|
63737
64552
|
import { dirname, join } from "node:path";
|
|
63738
64553
|
|
|
63739
64554
|
// src/build-info.ts
|
|
63740
|
-
var VERSION = "0.17.
|
|
63741
|
-
var COMMIT_SHA = "
|
|
64555
|
+
var VERSION = "0.17.10";
|
|
64556
|
+
var COMMIT_SHA = "d38372bf";
|
|
63742
64557
|
|
|
63743
64558
|
// src/cli/resolve-version.ts
|
|
63744
64559
|
function readPackageVersion() {
|
|
@@ -63769,7 +64584,7 @@ var SWITCHROOM_VERSION = (() => {
|
|
|
63769
64584
|
init_esm();
|
|
63770
64585
|
init_source();
|
|
63771
64586
|
import { join as join21, resolve as resolve22 } from "node:path";
|
|
63772
|
-
import { rmSync as
|
|
64587
|
+
import { rmSync as rmSync10, existsSync as existsSync31, readFileSync as readFileSync25, writeFileSync as writeFileSync13 } from "node:fs";
|
|
63773
64588
|
import { homedir as homedir8 } from "node:os";
|
|
63774
64589
|
|
|
63775
64590
|
// src/agents/scheduler-state.ts
|
|
@@ -63833,6 +64648,7 @@ function formatSchedulerState(state) {
|
|
|
63833
64648
|
|
|
63834
64649
|
// src/cli/agent.ts
|
|
63835
64650
|
init_loader();
|
|
64651
|
+
init_hindsight2();
|
|
63836
64652
|
init_hindsight();
|
|
63837
64653
|
init_helpers();
|
|
63838
64654
|
init_scaffold();
|
|
@@ -64067,10 +64883,10 @@ function spinner(message) {
|
|
|
64067
64883
|
// src/agents/status.ts
|
|
64068
64884
|
import { existsSync as existsSync23, readFileSync as readFileSync20, statSync as statSync11 } from "node:fs";
|
|
64069
64885
|
import { join as join16 } from "node:path";
|
|
64070
|
-
import { execFileSync as
|
|
64886
|
+
import { execFileSync as execFileSync10 } from "node:child_process";
|
|
64071
64887
|
|
|
64072
64888
|
// src/agents/handoff-summarizer.ts
|
|
64073
|
-
import { readFileSync as readFileSync18, writeFileSync as writeFileSync8, renameSync as
|
|
64889
|
+
import { readFileSync as readFileSync18, writeFileSync as writeFileSync8, renameSync as renameSync6, mkdirSync as mkdirSync15, existsSync as existsSync21, statSync as statSync10, readdirSync as readdirSync9 } from "node:fs";
|
|
64074
64890
|
import { join as join15 } from "node:path";
|
|
64075
64891
|
var DEFAULT_MAX_TURNS = 50;
|
|
64076
64892
|
var TOPIC_MAX_CHARS = 117;
|
|
@@ -64212,8 +65028,8 @@ function writeSidecarsAtomic(agentDir, briefing, topic) {
|
|
|
64212
65028
|
const topicTmp = topicPath + ".tmp";
|
|
64213
65029
|
writeFileSync8(handoffTmp, briefing, "utf-8");
|
|
64214
65030
|
writeFileSync8(topicTmp, topic, "utf-8");
|
|
64215
|
-
|
|
64216
|
-
|
|
65031
|
+
renameSync6(handoffTmp, handoffPath);
|
|
65032
|
+
renameSync6(topicTmp, topicPath);
|
|
64217
65033
|
}
|
|
64218
65034
|
async function buildHandoff(opts) {
|
|
64219
65035
|
const maxTurns = opts.maxTurns ?? DEFAULT_MAX_TURNS;
|
|
@@ -64744,14 +65560,14 @@ function readLastMessages(historyDbPath) {
|
|
|
64744
65560
|
};
|
|
64745
65561
|
}
|
|
64746
65562
|
try {
|
|
64747
|
-
const out =
|
|
65563
|
+
const out = execFileSync10("bun", [
|
|
64748
65564
|
"-e",
|
|
64749
65565
|
`const { Database } = require("bun:sqlite"); ` + `const db = new Database(${JSON.stringify(historyDbPath)}, { readonly: true }); ` + `const rows = db.prepare("SELECT role, MAX(ts) as ts FROM messages GROUP BY role").all(); ` + `for (const r of rows) console.log(r.role + "|" + r.ts); ` + `db.close();`
|
|
64750
65566
|
], { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] });
|
|
64751
65567
|
return parseSqliteRoleTsOutput(out);
|
|
64752
65568
|
} catch {}
|
|
64753
65569
|
try {
|
|
64754
|
-
const out =
|
|
65570
|
+
const out = execFileSync10("sqlite3", [
|
|
64755
65571
|
historyDbPath,
|
|
64756
65572
|
"SELECT role, MAX(ts) FROM messages GROUP BY role;"
|
|
64757
65573
|
], { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] });
|
|
@@ -64863,7 +65679,7 @@ function escapeRegex(s) {
|
|
|
64863
65679
|
}
|
|
64864
65680
|
function readDockerContainer(containerName2) {
|
|
64865
65681
|
try {
|
|
64866
|
-
const out =
|
|
65682
|
+
const out = execFileSync10("docker", ["inspect", "--format", "{{json .State}}", containerName2], { encoding: "utf-8", stdio: ["ignore", "pipe", "pipe"] });
|
|
64867
65683
|
const state = JSON.parse(out.trim());
|
|
64868
65684
|
const active = state.Status === "running" ? "active" : state.Status === "restarting" ? "restarting" : "inactive";
|
|
64869
65685
|
const pid = typeof state.Pid === "number" && state.Pid > 0 ? state.Pid : null;
|
|
@@ -64925,7 +65741,7 @@ init_profiles();
|
|
|
64925
65741
|
init_lifecycle();
|
|
64926
65742
|
init_manager();
|
|
64927
65743
|
import { resolve as resolve17 } from "node:path";
|
|
64928
|
-
import { existsSync as existsSync26, rmSync as
|
|
65744
|
+
import { existsSync as existsSync26, rmSync as rmSync7 } from "node:fs";
|
|
64929
65745
|
|
|
64930
65746
|
// src/telegram/redact-token.ts
|
|
64931
65747
|
var REDACTED_PLACEHOLDER = "<redacted-bot-token>";
|
|
@@ -65074,7 +65890,7 @@ async function createAgent(opts) {
|
|
|
65074
65890
|
const agentDir = resolve17(agentsDir, name);
|
|
65075
65891
|
await withRollback(() => {
|
|
65076
65892
|
scaffoldAgent(name, agentConfig, agentsDir, config.telegram, config, undefined, configPath);
|
|
65077
|
-
rollbackStack.push(() =>
|
|
65893
|
+
rollbackStack.push(() => rmSync7(agentDir, { recursive: true, force: true }));
|
|
65078
65894
|
});
|
|
65079
65895
|
await withRollback(() => {
|
|
65080
65896
|
writeAgentEnv(agentDir, telegramBotToken);
|
|
@@ -65141,8 +65957,8 @@ async function completeCreation(name, code, opts = {}) {
|
|
|
65141
65957
|
|
|
65142
65958
|
// src/agents/add-orchestrator.ts
|
|
65143
65959
|
import { resolve as resolve19, join as join19 } from "node:path";
|
|
65144
|
-
import { existsSync as existsSync28, rmSync as
|
|
65145
|
-
import { execFileSync as
|
|
65960
|
+
import { existsSync as existsSync28, rmSync as rmSync8, statSync as statSync15 } from "node:fs";
|
|
65961
|
+
import { execFileSync as execFileSync13 } from "node:child_process";
|
|
65146
65962
|
init_onboarding();
|
|
65147
65963
|
|
|
65148
65964
|
// src/setup/botfather-walkthrough.ts
|
|
@@ -65708,7 +66524,7 @@ function pruneBundledSkills(agentDir, keep, scope) {
|
|
|
65708
66524
|
if (!isDir)
|
|
65709
66525
|
continue;
|
|
65710
66526
|
try {
|
|
65711
|
-
|
|
66527
|
+
rmSync8(entryPath, { recursive: true, force: true });
|
|
65712
66528
|
removed.push(entry);
|
|
65713
66529
|
} catch {}
|
|
65714
66530
|
}
|
|
@@ -65716,7 +66532,7 @@ function pruneBundledSkills(agentDir, keep, scope) {
|
|
|
65716
66532
|
}
|
|
65717
66533
|
function defaultIsUnitActive(unitName) {
|
|
65718
66534
|
try {
|
|
65719
|
-
const out =
|
|
66535
|
+
const out = execFileSync13("systemctl", ["--user", "is-active", unitName], {
|
|
65720
66536
|
encoding: "utf-8",
|
|
65721
66537
|
stdio: ["ignore", "pipe", "pipe"]
|
|
65722
66538
|
}).trim();
|
|
@@ -65743,7 +66559,7 @@ import { resolve as resolve20 } from "node:path";
|
|
|
65743
66559
|
import {
|
|
65744
66560
|
existsSync as existsSync29,
|
|
65745
66561
|
mkdtempSync as mkdtempSync3,
|
|
65746
|
-
rmSync as
|
|
66562
|
+
rmSync as rmSync9,
|
|
65747
66563
|
cpSync
|
|
65748
66564
|
} from "node:fs";
|
|
65749
66565
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
@@ -65768,7 +66584,7 @@ function defaultDeps() {
|
|
|
65768
66584
|
return tmp;
|
|
65769
66585
|
},
|
|
65770
66586
|
copyDir: (src, dst) => cpSync(src, dst, { recursive: true }),
|
|
65771
|
-
removeDir: (p) =>
|
|
66587
|
+
removeDir: (p) => rmSync9(p, { recursive: true, force: true }),
|
|
65772
66588
|
existsSync: existsSync29,
|
|
65773
66589
|
readFileSync: (p, enc) => readFileSync23(p, enc)
|
|
65774
66590
|
};
|
|
@@ -66137,16 +66953,16 @@ function parsePorcelainDirty(porcelain) {
|
|
|
66137
66953
|
}
|
|
66138
66954
|
function checkSwitchroomBranch() {
|
|
66139
66955
|
try {
|
|
66140
|
-
const { execFileSync:
|
|
66956
|
+
const { execFileSync: execFileSync15 } = __require("node:child_process");
|
|
66141
66957
|
const cwd = process.cwd();
|
|
66142
|
-
const branch =
|
|
66958
|
+
const branch = execFileSync15("git", ["-C", cwd, "rev-parse", "--abbrev-ref", "HEAD"], {
|
|
66143
66959
|
encoding: "utf-8",
|
|
66144
66960
|
stdio: ["ignore", "pipe", "ignore"]
|
|
66145
66961
|
}).trim();
|
|
66146
66962
|
if (branch === "" || branch === "HEAD")
|
|
66147
66963
|
return null;
|
|
66148
66964
|
if (branch === "main" || branch === "master") {
|
|
66149
|
-
const porcelain =
|
|
66965
|
+
const porcelain = execFileSync15("git", ["-C", cwd, "status", "--porcelain"], {
|
|
66150
66966
|
encoding: "utf-8",
|
|
66151
66967
|
stdio: ["ignore", "pipe", "ignore"]
|
|
66152
66968
|
});
|
|
@@ -66209,7 +67025,7 @@ function buildStatusInputs(name, config, agentsDir) {
|
|
|
66209
67025
|
let hindsightApiUrl = null;
|
|
66210
67026
|
let hindsightBankId = name;
|
|
66211
67027
|
if (isHindsightEnabled(config)) {
|
|
66212
|
-
const baseUrl = config.memory?.config?.url ??
|
|
67028
|
+
const baseUrl = config.memory?.config?.url ?? HINDSIGHT_DEFAULT_MCP_URL;
|
|
66213
67029
|
hindsightApiUrl = baseUrl.endsWith("/mcp/") ? baseUrl : baseUrl.replace(/\/$/, "") + "/mcp/";
|
|
66214
67030
|
hindsightBankId = agentConfig?.memory?.collection ?? name;
|
|
66215
67031
|
}
|
|
@@ -66477,7 +67293,7 @@ function registerAgentCommand(program3) {
|
|
|
66477
67293
|
let hindsightApiUrl = null;
|
|
66478
67294
|
let hindsightBankId = name;
|
|
66479
67295
|
if (isHindsightEnabled(config)) {
|
|
66480
|
-
const baseUrl = config.memory?.config?.url ??
|
|
67296
|
+
const baseUrl = config.memory?.config?.url ?? HINDSIGHT_DEFAULT_MCP_URL;
|
|
66481
67297
|
hindsightApiUrl = baseUrl.endsWith("/mcp/") ? baseUrl : baseUrl.replace(/\/$/, "") + "/mcp/";
|
|
66482
67298
|
hindsightBankId = agentConfig.memory?.collection ?? name;
|
|
66483
67299
|
}
|
|
@@ -67161,7 +67977,7 @@ Permissions for ${name}
|
|
|
67161
67977
|
}
|
|
67162
67978
|
}
|
|
67163
67979
|
if (existsSync31(agentDir)) {
|
|
67164
|
-
|
|
67980
|
+
rmSync10(agentDir, { recursive: true, force: true });
|
|
67165
67981
|
console.log(source_default.green(` Removed directory: ${agentDir}`));
|
|
67166
67982
|
} else {
|
|
67167
67983
|
console.log(source_default.gray(` Directory not found: ${agentDir}`));
|
|
@@ -67365,7 +68181,7 @@ switchroom agent add: ${name}
|
|
|
67365
68181
|
const agentsDirRollback = resolveAgentsDir(cfg);
|
|
67366
68182
|
const agentDirRollback = resolve22(agentsDirRollback, name);
|
|
67367
68183
|
if (existsSync31(agentDirRollback)) {
|
|
67368
|
-
|
|
68184
|
+
rmSync10(agentDirRollback, { recursive: true, force: true });
|
|
67369
68185
|
console.log(source_default.gray(` Rolled back: removed ${agentDirRollback}`));
|
|
67370
68186
|
}
|
|
67371
68187
|
process.exit(1);
|
|
@@ -69996,7 +70812,7 @@ function registerAuthCommand(program3) {
|
|
|
69996
70812
|
try {
|
|
69997
70813
|
mode = statSync17(yamlPath).mode & 511;
|
|
69998
70814
|
} catch {}
|
|
69999
|
-
|
|
70815
|
+
atomicWriteFileSync(yamlPath, after, mode);
|
|
70000
70816
|
if (!quiet) {
|
|
70001
70817
|
console.log(source_default.gray(` Pinned auth.active: ${label} in ${yamlPath}`));
|
|
70002
70818
|
}
|
|
@@ -70143,9 +70959,9 @@ import { readFileSync as readFileSync39 } from "node:fs";
|
|
|
70143
70959
|
init_source();
|
|
70144
70960
|
init_loader();
|
|
70145
70961
|
init_vault();
|
|
70146
|
-
|
|
70962
|
+
init_hindsight2();
|
|
70147
70963
|
import { readFileSync as readFileSync31, writeFileSync as writeFileSync17, copyFileSync as copyFileSync6, existsSync as existsSync36, readdirSync as readdirSync16, statSync as statSync18 } from "node:fs";
|
|
70148
|
-
import { execFileSync as
|
|
70964
|
+
import { execFileSync as execFileSync16 } from "node:child_process";
|
|
70149
70965
|
import { join as join28 } from "node:path";
|
|
70150
70966
|
import { homedir as homedir13 } from "node:os";
|
|
70151
70967
|
function getVaultPath2(configPath) {
|
|
@@ -70492,7 +71308,7 @@ function registerVaultSweep(vault, program3) {
|
|
|
70492
71308
|
function scrubSqlite(path, values, dryRun) {
|
|
70493
71309
|
let hasSqlite = true;
|
|
70494
71310
|
try {
|
|
70495
|
-
|
|
71311
|
+
execFileSync16("sqlite3", ["-version"], { timeout: 2000, stdio: "ignore" });
|
|
70496
71312
|
} catch {
|
|
70497
71313
|
hasSqlite = false;
|
|
70498
71314
|
}
|
|
@@ -70504,7 +71320,7 @@ function scrubSqlite(path, values, dryRun) {
|
|
|
70504
71320
|
for (const { value } of values) {
|
|
70505
71321
|
const esc = value.replace(/'/g, "''");
|
|
70506
71322
|
try {
|
|
70507
|
-
const out =
|
|
71323
|
+
const out = execFileSync16("sqlite3", [path, `SELECT COUNT(*) FROM messages WHERE text LIKE '%${esc}%'`], { encoding: "utf8", timeout: 1e4 });
|
|
70508
71324
|
const n = parseInt(out.trim(), 10);
|
|
70509
71325
|
if (!Number.isNaN(n))
|
|
70510
71326
|
matches += n;
|
|
@@ -70518,7 +71334,7 @@ function scrubSqlite(path, values, dryRun) {
|
|
|
70518
71334
|
for (const { key, value } of values) {
|
|
70519
71335
|
const esc = value.replace(/'/g, "''");
|
|
70520
71336
|
const replacement = `vault:${key}`.replace(/'/g, "''");
|
|
70521
|
-
|
|
71337
|
+
execFileSync16("sqlite3", [path, `UPDATE messages SET text = replace(text, '${esc}', '${replacement}')`], { timeout: 15000 });
|
|
70522
71338
|
}
|
|
70523
71339
|
return { target: path, matches, modified: true };
|
|
70524
71340
|
} catch (err) {
|
|
@@ -77429,10 +78245,10 @@ function fail2(msg) {
|
|
|
77429
78245
|
|
|
77430
78246
|
// src/cli/memory.ts
|
|
77431
78247
|
init_source();
|
|
77432
|
-
|
|
78248
|
+
init_hindsight2();
|
|
77433
78249
|
|
|
77434
78250
|
// src/memory/search.ts
|
|
77435
|
-
|
|
78251
|
+
init_hindsight2();
|
|
77436
78252
|
function shellQuote2(s) {
|
|
77437
78253
|
return "'" + s.replace(/'/g, `'"'"'`) + "'";
|
|
77438
78254
|
}
|
|
@@ -77459,312 +78275,7 @@ function reflectAcrossAgents(config) {
|
|
|
77459
78275
|
|
|
77460
78276
|
// src/cli/memory.ts
|
|
77461
78277
|
init_helpers();
|
|
77462
|
-
|
|
77463
|
-
// src/setup/hindsight.ts
|
|
77464
|
-
import { execFileSync as execFileSync16 } from "node:child_process";
|
|
77465
|
-
import { createServer as createServer4 } from "node:net";
|
|
77466
|
-
var HINDSIGHT_DEFAULT_API_PORT = 8888;
|
|
77467
|
-
var HINDSIGHT_DEFAULT_UI_PORT = 9999;
|
|
77468
|
-
var HINDSIGHT_DEFAULT_MAX_OBSERVATIONS_PER_SCOPE = 1000;
|
|
77469
|
-
var HINDSIGHT_CONSUMER_NAME = "hindsight";
|
|
77470
|
-
var HINDSIGHT_DEFAULT_UID = 11000;
|
|
77471
|
-
var HINDSIGHT_IMAGE_REPO = "ghcr.io/switchroom/switchroom-hindsight";
|
|
77472
|
-
var HINDSIGHT_IMAGE = `${HINDSIGHT_IMAGE_REPO}:latest`;
|
|
77473
|
-
function hindsightImageRef(tag) {
|
|
77474
|
-
const t = tag?.trim();
|
|
77475
|
-
if (!t)
|
|
77476
|
-
return HINDSIGHT_IMAGE;
|
|
77477
|
-
const v = t.replace(/^v/, "");
|
|
77478
|
-
return `${HINDSIGHT_IMAGE_REPO}:v${v}`;
|
|
77479
|
-
}
|
|
77480
|
-
var HINDSIGHT_DEFAULT_MODEL = "claude-sonnet-5";
|
|
77481
|
-
var HINDSIGHT_DEFAULT_MCP_STATELESS = true;
|
|
77482
|
-
var HINDSIGHT_BROKER_SOCK_VOLUME = `auth-broker-${HINDSIGHT_CONSUMER_NAME}-sock`;
|
|
77483
|
-
var HINDSIGHT_DEFAULT_RERANKER_BUCKET_BATCHING = "true";
|
|
77484
|
-
var HINDSIGHT_DEFAULT_RERANKER_MAX_CANDIDATES = 150;
|
|
77485
|
-
var HINDSIGHT_DEFAULT_RERANKER_LOCAL_MAX_CONCURRENT = 4;
|
|
77486
|
-
var HINDSIGHT_DEFAULT_RECALL_MAX_CONCURRENT = 8;
|
|
77487
|
-
var HINDSIGHT_DEFAULT_REFLECT_WALL_TIMEOUT_S = 600;
|
|
77488
|
-
var HINDSIGHT_DEFAULT_CONSOLIDATION_LLM_BATCH_SIZE = 12;
|
|
77489
|
-
var HINDSIGHT_DEFAULT_CONSOLIDATION_MAX_SLOTS = 3;
|
|
77490
|
-
var HINDSIGHT_DEFAULT_CONSOLIDATION_LLM_PARALLELISM = 6;
|
|
77491
|
-
var HINDSIGHT_DEFAULT_CONSOLIDATION_MAX_MEMORIES_PER_ROUND = 300;
|
|
77492
|
-
var HINDSIGHT_DEFAULT_MEM_LIMIT = "8g";
|
|
77493
|
-
var HINDSIGHT_DEFAULT_MEM_RESERVATION = "4g";
|
|
77494
|
-
var HINDSIGHT_DEFAULT_PIDS_LIMIT = 1000;
|
|
77495
|
-
var HINDSIGHT_DEFAULT_SHM_SIZE = "2g";
|
|
77496
|
-
var HINDSIGHT_HEALTHCHECK_PY = 'import urllib.request,sys; sys.exit(0 if urllib.request.urlopen("http://localhost:8888/health",timeout=4).getcode()==200 else 1)';
|
|
77497
|
-
var HINDSIGHT_HEALTHCHECK_CMD = `python3 -c '${HINDSIGHT_HEALTHCHECK_PY}'`;
|
|
77498
|
-
function isPortFree(port) {
|
|
77499
|
-
return new Promise((resolve29) => {
|
|
77500
|
-
const server = createServer4();
|
|
77501
|
-
server.once("error", () => resolve29(false));
|
|
77502
|
-
server.once("listening", () => {
|
|
77503
|
-
server.close(() => resolve29(true));
|
|
77504
|
-
});
|
|
77505
|
-
server.listen(port, "127.0.0.1");
|
|
77506
|
-
});
|
|
77507
|
-
}
|
|
77508
|
-
async function findFreePort(start, maxAttempts = 50) {
|
|
77509
|
-
for (let i = 0;i < maxAttempts; i++) {
|
|
77510
|
-
const port = start + i;
|
|
77511
|
-
if (port < 1024)
|
|
77512
|
-
continue;
|
|
77513
|
-
if (await isPortFree(port)) {
|
|
77514
|
-
return port;
|
|
77515
|
-
}
|
|
77516
|
-
}
|
|
77517
|
-
return null;
|
|
77518
|
-
}
|
|
77519
|
-
async function pickHindsightPorts() {
|
|
77520
|
-
if (await isPortFree(HINDSIGHT_DEFAULT_API_PORT) && await isPortFree(HINDSIGHT_DEFAULT_UI_PORT)) {
|
|
77521
|
-
return {
|
|
77522
|
-
apiPort: HINDSIGHT_DEFAULT_API_PORT,
|
|
77523
|
-
uiPort: HINDSIGHT_DEFAULT_UI_PORT
|
|
77524
|
-
};
|
|
77525
|
-
}
|
|
77526
|
-
const apiPort = await findFreePort(18888);
|
|
77527
|
-
const uiPort = await findFreePort(19999);
|
|
77528
|
-
if (apiPort === null || uiPort === null) {
|
|
77529
|
-
throw new Error("Could not find a free port for Hindsight. " + "Stop whatever is using 8888 / 9999 / 18888 / 19999 and retry.");
|
|
77530
|
-
}
|
|
77531
|
-
return { apiPort, uiPort };
|
|
77532
|
-
}
|
|
77533
|
-
function isDockerAvailable() {
|
|
77534
|
-
try {
|
|
77535
|
-
execFileSync16("docker", ["--version"], { stdio: "pipe" });
|
|
77536
|
-
return true;
|
|
77537
|
-
} catch {
|
|
77538
|
-
return false;
|
|
77539
|
-
}
|
|
77540
|
-
}
|
|
77541
|
-
function isHindsightRunning() {
|
|
77542
|
-
try {
|
|
77543
|
-
const output = execFileSync16("docker", ["ps", "--filter", "name=switchroom-hindsight", "--format", "{{.Status}}"], { stdio: "pipe", encoding: "utf-8" });
|
|
77544
|
-
return output.trim().length > 0;
|
|
77545
|
-
} catch {
|
|
77546
|
-
return false;
|
|
77547
|
-
}
|
|
77548
|
-
}
|
|
77549
|
-
function isHindsightContainerExists() {
|
|
77550
|
-
try {
|
|
77551
|
-
const output = execFileSync16("docker", ["ps", "-a", "--filter", "name=switchroom-hindsight", "--format", "{{.Names}}"], { stdio: "pipe", encoding: "utf-8" });
|
|
77552
|
-
return output.trim().length > 0;
|
|
77553
|
-
} catch {
|
|
77554
|
-
return false;
|
|
77555
|
-
}
|
|
77556
|
-
}
|
|
77557
|
-
function startHindsight(ports, litellm, imageTag) {
|
|
77558
|
-
const apiPort = ports?.apiPort ?? HINDSIGHT_DEFAULT_API_PORT;
|
|
77559
|
-
const uiPort = ports?.uiPort ?? HINDSIGHT_DEFAULT_UI_PORT;
|
|
77560
|
-
const envArgs = [
|
|
77561
|
-
"-e",
|
|
77562
|
-
`HINDSIGHT_API_MAX_OBSERVATIONS_PER_SCOPE=${HINDSIGHT_DEFAULT_MAX_OBSERVATIONS_PER_SCOPE}`,
|
|
77563
|
-
"-e",
|
|
77564
|
-
"HINDSIGHT_API_LLM_PROVIDER=claude-code",
|
|
77565
|
-
"-e",
|
|
77566
|
-
`HINDSIGHT_API_LLM_MODEL=${HINDSIGHT_DEFAULT_MODEL}`,
|
|
77567
|
-
"-e",
|
|
77568
|
-
`HINDSIGHT_API_MCP_STATELESS=${HINDSIGHT_DEFAULT_MCP_STATELESS}`,
|
|
77569
|
-
"-e",
|
|
77570
|
-
`HINDSIGHT_API_RERANKER_LOCAL_BUCKET_BATCHING=${HINDSIGHT_DEFAULT_RERANKER_BUCKET_BATCHING}`,
|
|
77571
|
-
"-e",
|
|
77572
|
-
`HINDSIGHT_API_RERANKER_MAX_CANDIDATES=${HINDSIGHT_DEFAULT_RERANKER_MAX_CANDIDATES}`,
|
|
77573
|
-
"-e",
|
|
77574
|
-
`HINDSIGHT_API_RERANKER_LOCAL_MAX_CONCURRENT=${HINDSIGHT_DEFAULT_RERANKER_LOCAL_MAX_CONCURRENT}`,
|
|
77575
|
-
"-e",
|
|
77576
|
-
`HINDSIGHT_API_RECALL_MAX_CONCURRENT=${HINDSIGHT_DEFAULT_RECALL_MAX_CONCURRENT}`,
|
|
77577
|
-
"-e",
|
|
77578
|
-
`HINDSIGHT_API_REFLECT_WALL_TIMEOUT=${HINDSIGHT_DEFAULT_REFLECT_WALL_TIMEOUT_S}`,
|
|
77579
|
-
"-e",
|
|
77580
|
-
`HINDSIGHT_API_CONSOLIDATION_LLM_BATCH_SIZE=${HINDSIGHT_DEFAULT_CONSOLIDATION_LLM_BATCH_SIZE}`,
|
|
77581
|
-
"-e",
|
|
77582
|
-
`HINDSIGHT_API_WORKER_CONSOLIDATION_MAX_SLOTS=${HINDSIGHT_DEFAULT_CONSOLIDATION_MAX_SLOTS}`,
|
|
77583
|
-
"-e",
|
|
77584
|
-
`HINDSIGHT_API_CONSOLIDATION_LLM_PARALLELISM=${HINDSIGHT_DEFAULT_CONSOLIDATION_LLM_PARALLELISM}`,
|
|
77585
|
-
"-e",
|
|
77586
|
-
`HINDSIGHT_API_CONSOLIDATION_MAX_MEMORIES_PER_ROUND=${HINDSIGHT_DEFAULT_CONSOLIDATION_MAX_MEMORIES_PER_ROUND}`
|
|
77587
|
-
];
|
|
77588
|
-
if (litellm) {
|
|
77589
|
-
envArgs.push("-e", `HINDSIGHT_API_PORT=${apiPort}`, "-e", `ANTHROPIC_BASE_URL=${litellm.baseUrl.replace(/\/+$/, "")}/anthropic`, "-e", `ANTHROPIC_CUSTOM_HEADERS=x-litellm-api-key: Bearer ${litellm.apiKey}
|
|
77590
|
-
x-litellm-customer-id: hindsight
|
|
77591
|
-
x-litellm-tags: service:hindsight`);
|
|
77592
|
-
}
|
|
77593
|
-
const args = [
|
|
77594
|
-
"run",
|
|
77595
|
-
"-d",
|
|
77596
|
-
"--name",
|
|
77597
|
-
"switchroom-hindsight",
|
|
77598
|
-
"--restart",
|
|
77599
|
-
"always",
|
|
77600
|
-
`--memory=${HINDSIGHT_DEFAULT_MEM_LIMIT}`,
|
|
77601
|
-
`--memory-reservation=${HINDSIGHT_DEFAULT_MEM_RESERVATION}`,
|
|
77602
|
-
`--pids-limit=${HINDSIGHT_DEFAULT_PIDS_LIMIT}`,
|
|
77603
|
-
`--shm-size=${HINDSIGHT_DEFAULT_SHM_SIZE}`,
|
|
77604
|
-
"--health-cmd",
|
|
77605
|
-
litellm ? `python3 -c 'import urllib.request,sys; sys.exit(0 if urllib.request.urlopen("http://localhost:${apiPort}/health",timeout=4).getcode()==200 else 1)'` : HINDSIGHT_HEALTHCHECK_CMD,
|
|
77606
|
-
"--health-interval",
|
|
77607
|
-
"30s",
|
|
77608
|
-
"--health-timeout",
|
|
77609
|
-
"5s",
|
|
77610
|
-
"--health-retries",
|
|
77611
|
-
"3",
|
|
77612
|
-
"--health-start-period",
|
|
77613
|
-
"60s"
|
|
77614
|
-
];
|
|
77615
|
-
if (litellm) {
|
|
77616
|
-
args.push("--network", "host");
|
|
77617
|
-
} else {
|
|
77618
|
-
args.push("-p", `127.0.0.1:${apiPort}:8888`, "-p", `127.0.0.1:${uiPort}:9999`);
|
|
77619
|
-
}
|
|
77620
|
-
args.push("-v", "switchroom-hindsight-data:/home/hindsight/.pg0", "-v", "switchroom-hindsight-backups:/backups", "-v", `${HINDSIGHT_BROKER_SOCK_VOLUME}:/run/switchroom/auth-broker`, "--tmpfs", `/run/claude-creds:rw,mode=0700,uid=${HINDSIGHT_DEFAULT_UID},gid=${HINDSIGHT_DEFAULT_UID}`, ...envArgs, hindsightImageRef(imageTag));
|
|
77621
|
-
execFileSync16("docker", args, { stdio: "pipe" });
|
|
77622
|
-
}
|
|
77623
|
-
function stopHindsight() {
|
|
77624
|
-
try {
|
|
77625
|
-
execFileSync16("docker", ["stop", "switchroom-hindsight"], { stdio: "pipe" });
|
|
77626
|
-
} catch {}
|
|
77627
|
-
try {
|
|
77628
|
-
execFileSync16("docker", ["rm", "switchroom-hindsight"], { stdio: "pipe" });
|
|
77629
|
-
} catch {}
|
|
77630
|
-
}
|
|
77631
|
-
function pullHindsightImage(imageTag) {
|
|
77632
|
-
execFileSync16("docker", ["pull", hindsightImageRef(imageTag)], { stdio: "inherit" });
|
|
77633
|
-
}
|
|
77634
|
-
function getRunningHindsightPorts() {
|
|
77635
|
-
const readPort = (containerPort) => {
|
|
77636
|
-
try {
|
|
77637
|
-
const out = execFileSync16("docker", ["port", "switchroom-hindsight", `${containerPort}/tcp`], { stdio: "pipe", encoding: "utf-8" });
|
|
77638
|
-
const m = out.split(`
|
|
77639
|
-
`).map((l) => l.trim()).find((l) => l.length > 0);
|
|
77640
|
-
if (!m)
|
|
77641
|
-
return null;
|
|
77642
|
-
const port = Number(m.slice(m.lastIndexOf(":") + 1));
|
|
77643
|
-
return Number.isInteger(port) && port > 0 ? port : null;
|
|
77644
|
-
} catch {
|
|
77645
|
-
return null;
|
|
77646
|
-
}
|
|
77647
|
-
};
|
|
77648
|
-
const apiPort = readPort(HINDSIGHT_DEFAULT_API_PORT);
|
|
77649
|
-
if (apiPort === null)
|
|
77650
|
-
return null;
|
|
77651
|
-
const uiPort = readPort(HINDSIGHT_DEFAULT_UI_PORT) ?? (apiPort === HINDSIGHT_DEFAULT_API_PORT ? HINDSIGHT_DEFAULT_UI_PORT : 19999);
|
|
77652
|
-
return { apiPort, uiPort };
|
|
77653
|
-
}
|
|
77654
|
-
function getHindsightStatus() {
|
|
77655
|
-
try {
|
|
77656
|
-
const output = execFileSync16("docker", ["ps", "-a", "--filter", "name=switchroom-hindsight", "--format", "{{.Status}}"], { stdio: "pipe", encoding: "utf-8" });
|
|
77657
|
-
const status = output.trim();
|
|
77658
|
-
return status.length > 0 ? status : null;
|
|
77659
|
-
} catch {
|
|
77660
|
-
return null;
|
|
77661
|
-
}
|
|
77662
|
-
}
|
|
77663
|
-
function generateHindsightComposeSnippet() {
|
|
77664
|
-
return [
|
|
77665
|
-
"services:",
|
|
77666
|
-
" switchroom-hindsight:",
|
|
77667
|
-
` image: ${HINDSIGHT_IMAGE}`,
|
|
77668
|
-
" container_name: switchroom-hindsight",
|
|
77669
|
-
" ports:",
|
|
77670
|
-
' - "8888:8888"',
|
|
77671
|
-
' - "9999:9999"',
|
|
77672
|
-
" environment:",
|
|
77673
|
-
` - HINDSIGHT_API_MAX_OBSERVATIONS_PER_SCOPE=${HINDSIGHT_DEFAULT_MAX_OBSERVATIONS_PER_SCOPE}`,
|
|
77674
|
-
" - HINDSIGHT_API_LLM_PROVIDER=claude-code",
|
|
77675
|
-
` - HINDSIGHT_API_LLM_MODEL=${HINDSIGHT_DEFAULT_MODEL}`,
|
|
77676
|
-
` - HINDSIGHT_API_MCP_STATELESS=${HINDSIGHT_DEFAULT_MCP_STATELESS}`,
|
|
77677
|
-
` - HINDSIGHT_API_RERANKER_LOCAL_BUCKET_BATCHING=${HINDSIGHT_DEFAULT_RERANKER_BUCKET_BATCHING}`,
|
|
77678
|
-
` - HINDSIGHT_API_RERANKER_MAX_CANDIDATES=${HINDSIGHT_DEFAULT_RERANKER_MAX_CANDIDATES}`,
|
|
77679
|
-
` - HINDSIGHT_API_RERANKER_LOCAL_MAX_CONCURRENT=${HINDSIGHT_DEFAULT_RERANKER_LOCAL_MAX_CONCURRENT}`,
|
|
77680
|
-
` - HINDSIGHT_API_RECALL_MAX_CONCURRENT=${HINDSIGHT_DEFAULT_RECALL_MAX_CONCURRENT}`,
|
|
77681
|
-
` - HINDSIGHT_API_REFLECT_WALL_TIMEOUT=${HINDSIGHT_DEFAULT_REFLECT_WALL_TIMEOUT_S}`,
|
|
77682
|
-
` - HINDSIGHT_API_CONSOLIDATION_LLM_BATCH_SIZE=${HINDSIGHT_DEFAULT_CONSOLIDATION_LLM_BATCH_SIZE}`,
|
|
77683
|
-
` - HINDSIGHT_API_WORKER_CONSOLIDATION_MAX_SLOTS=${HINDSIGHT_DEFAULT_CONSOLIDATION_MAX_SLOTS}`,
|
|
77684
|
-
` - HINDSIGHT_API_CONSOLIDATION_LLM_PARALLELISM=${HINDSIGHT_DEFAULT_CONSOLIDATION_LLM_PARALLELISM}`,
|
|
77685
|
-
` - HINDSIGHT_API_CONSOLIDATION_MAX_MEMORIES_PER_ROUND=${HINDSIGHT_DEFAULT_CONSOLIDATION_MAX_MEMORIES_PER_ROUND}`,
|
|
77686
|
-
` mem_limit: ${HINDSIGHT_DEFAULT_MEM_LIMIT}`,
|
|
77687
|
-
` mem_reservation: ${HINDSIGHT_DEFAULT_MEM_RESERVATION}`,
|
|
77688
|
-
` pids_limit: ${HINDSIGHT_DEFAULT_PIDS_LIMIT}`,
|
|
77689
|
-
` shm_size: ${HINDSIGHT_DEFAULT_SHM_SIZE}`,
|
|
77690
|
-
" healthcheck:",
|
|
77691
|
-
` test: ${JSON.stringify(["CMD", "python3", "-c", HINDSIGHT_HEALTHCHECK_PY])}`,
|
|
77692
|
-
" interval: 30s",
|
|
77693
|
-
" timeout: 5s",
|
|
77694
|
-
" retries: 3",
|
|
77695
|
-
" start_period: 60s",
|
|
77696
|
-
" volumes:",
|
|
77697
|
-
" - switchroom-hindsight-data:/home/hindsight/.pg0",
|
|
77698
|
-
" - switchroom-hindsight-backups:/backups",
|
|
77699
|
-
` - ${HINDSIGHT_BROKER_SOCK_VOLUME}:/run/switchroom/auth-broker`,
|
|
77700
|
-
" tmpfs:",
|
|
77701
|
-
` - /run/claude-creds:rw,mode=0700,uid=${HINDSIGHT_DEFAULT_UID},gid=${HINDSIGHT_DEFAULT_UID}`,
|
|
77702
|
-
" restart: always",
|
|
77703
|
-
"",
|
|
77704
|
-
"volumes:",
|
|
77705
|
-
" switchroom-hindsight-data:",
|
|
77706
|
-
" switchroom-hindsight-backups:",
|
|
77707
|
-
` ${HINDSIGHT_BROKER_SOCK_VOLUME}:`,
|
|
77708
|
-
" external: true",
|
|
77709
|
-
" # Bound by the switchroom-auth-broker singleton in the main",
|
|
77710
|
-
" # switchroom compose project. Declare an `auth.consumers[]`",
|
|
77711
|
-
` # entry named "${HINDSIGHT_CONSUMER_NAME}" in switchroom.yaml.`
|
|
77712
|
-
].join(`
|
|
77713
|
-
`);
|
|
77714
|
-
}
|
|
77715
|
-
async function ensureHindsightConsumer(configPath, account, uid = HINDSIGHT_DEFAULT_UID) {
|
|
77716
|
-
const fs4 = await import("node:fs");
|
|
77717
|
-
const { parseDocument: parseDocument7, isMap: isMap7, isSeq: isSeq4, YAMLMap, YAMLSeq } = await Promise.resolve().then(() => __toESM(require_dist(), 1));
|
|
77718
|
-
const { atomicWriteFileSync: atomicWriteFileSync3 } = await Promise.resolve().then(() => (init_atomic(), exports_atomic));
|
|
77719
|
-
const raw = fs4.readFileSync(configPath, "utf-8");
|
|
77720
|
-
const doc = parseDocument7(raw);
|
|
77721
|
-
const root = doc.contents;
|
|
77722
|
-
if (!isMap7(root)) {
|
|
77723
|
-
return { added: false, reason: "switchroom.yaml root is not a map" };
|
|
77724
|
-
}
|
|
77725
|
-
const authRaw = root.get("auth", true);
|
|
77726
|
-
let authNode;
|
|
77727
|
-
if (isMap7(authRaw)) {
|
|
77728
|
-
authNode = authRaw;
|
|
77729
|
-
} else {
|
|
77730
|
-
authNode = new YAMLMap;
|
|
77731
|
-
doc.setIn(["auth"], authNode);
|
|
77732
|
-
}
|
|
77733
|
-
const consumersRaw = authNode.get("consumers", true);
|
|
77734
|
-
let consumersNode;
|
|
77735
|
-
if (isSeq4(consumersRaw)) {
|
|
77736
|
-
consumersNode = consumersRaw;
|
|
77737
|
-
} else {
|
|
77738
|
-
consumersNode = new YAMLSeq;
|
|
77739
|
-
doc.setIn(["auth", "consumers"], consumersNode);
|
|
77740
|
-
}
|
|
77741
|
-
for (const item of consumersNode.items) {
|
|
77742
|
-
if (isMap7(item)) {
|
|
77743
|
-
const name = item.get("name");
|
|
77744
|
-
if (name === HINDSIGHT_CONSUMER_NAME) {
|
|
77745
|
-
return { added: false, reason: "already present" };
|
|
77746
|
-
}
|
|
77747
|
-
}
|
|
77748
|
-
}
|
|
77749
|
-
const entry = new YAMLMap;
|
|
77750
|
-
entry.set("name", HINDSIGHT_CONSUMER_NAME);
|
|
77751
|
-
if (account !== undefined)
|
|
77752
|
-
entry.set("account", account);
|
|
77753
|
-
entry.set("uid", uid);
|
|
77754
|
-
consumersNode.add(entry);
|
|
77755
|
-
const out = String(doc);
|
|
77756
|
-
const tail = out.endsWith(`
|
|
77757
|
-
`) ? out : out + `
|
|
77758
|
-
`;
|
|
77759
|
-
let mode = 420;
|
|
77760
|
-
try {
|
|
77761
|
-
mode = fs4.statSync(configPath).mode & 511;
|
|
77762
|
-
} catch {}
|
|
77763
|
-
atomicWriteFileSync3(configPath, tail, mode);
|
|
77764
|
-
return { added: true, reason: "added" };
|
|
77765
|
-
}
|
|
77766
|
-
|
|
77767
|
-
// src/cli/memory.ts
|
|
78278
|
+
init_hindsight();
|
|
77768
78279
|
init_client();
|
|
77769
78280
|
init_loader();
|
|
77770
78281
|
var import_yaml13 = __toESM(require_dist(), 1);
|
|
@@ -77984,6 +78495,21 @@ Cross-agent reflection plan
|
|
|
77984
78495
|
let ports;
|
|
77985
78496
|
if (reusePorts) {
|
|
77986
78497
|
ports = reusePorts;
|
|
78498
|
+
if (reusePorts.apiPort !== HINDSIGHT_DEFAULT_API_PORT) {
|
|
78499
|
+
let explicitUrl;
|
|
78500
|
+
try {
|
|
78501
|
+
explicitUrl = getConfig(program3).memory?.config?.url;
|
|
78502
|
+
} catch {
|
|
78503
|
+
explicitUrl = undefined;
|
|
78504
|
+
}
|
|
78505
|
+
if (!explicitUrl) {
|
|
78506
|
+
console.log(source_default.yellow(`
|
|
78507
|
+
\u26a0 MIGRATION HAZARD: reusing stale host port ${reusePorts.apiPort} ` + `for switchroom-hindsight, but scaffolding now defaults to ` + `${HINDSIGHT_DEFAULT_API_PORT} and no explicit memory.config.url is set.`));
|
|
78508
|
+
console.log(source_default.yellow(` Agents may be repointed to a dead URL (http://127.0.0.1:${HINDSIGHT_DEFAULT_API_PORT}/mcp/) ` + `while the container listens on ${reusePorts.apiPort}.`));
|
|
78509
|
+
console.log(source_default.yellow(` Fix: set memory.config.url: http://127.0.0.1:${reusePorts.apiPort}/mcp/ explicitly, ` + `or migrate the container to port ${HINDSIGHT_DEFAULT_API_PORT}.
|
|
78510
|
+
`));
|
|
78511
|
+
}
|
|
78512
|
+
}
|
|
77987
78513
|
} else {
|
|
77988
78514
|
try {
|
|
77989
78515
|
ports = await pickHindsightPorts();
|
|
@@ -77997,6 +78523,29 @@ Cross-agent reflection plan
|
|
|
77997
78523
|
if (ports.apiPort !== HINDSIGHT_DEFAULT_API_PORT) {
|
|
77998
78524
|
console.log(source_default.yellow(` Port ${HINDSIGHT_DEFAULT_API_PORT} is already in use; ` + `using ${ports.apiPort}/${ports.uiPort} instead.`));
|
|
77999
78525
|
}
|
|
78526
|
+
let conflict = await preflightHindsightPorts(ports);
|
|
78527
|
+
if (conflict) {
|
|
78528
|
+
const heldBy = conflict.holder ? ` (held by ${conflict.holder})` : "";
|
|
78529
|
+
console.log(source_default.yellow(`
|
|
78530
|
+
Chosen Hindsight port ${conflict.port} is occupied${heldBy}; ` + `selecting a free port instead of crash-looping.`));
|
|
78531
|
+
try {
|
|
78532
|
+
ports = await pickHindsightPorts();
|
|
78533
|
+
} catch (err) {
|
|
78534
|
+
console.error(source_default.red(`
|
|
78535
|
+
${err.message}
|
|
78536
|
+
`));
|
|
78537
|
+
process.exit(1);
|
|
78538
|
+
}
|
|
78539
|
+
conflict = await preflightHindsightPorts(ports);
|
|
78540
|
+
if (conflict) {
|
|
78541
|
+
const stillHeldBy = conflict.holder ? ` (held by ${conflict.holder})` : "";
|
|
78542
|
+
console.error(source_default.red(`
|
|
78543
|
+
Refusing to start Hindsight: port ${conflict.port} is still ` + `occupied${stillHeldBy} after reassignment. Free it and retry ` + `\`switchroom memory --start\`.
|
|
78544
|
+
`));
|
|
78545
|
+
process.exit(1);
|
|
78546
|
+
}
|
|
78547
|
+
console.log(source_default.green(` Reassigned Hindsight to port ${ports.apiPort}/${ports.uiPort}.`));
|
|
78548
|
+
}
|
|
78000
78549
|
if (opts.provider && opts.provider !== "claude-code") {
|
|
78001
78550
|
console.log(source_default.gray(` Note: --provider=${opts.provider} ignored. switchroom-hindsight is pinned to the ` + "`claude-code` provider (subscription-honest). The flag is kept for back-compat."));
|
|
78002
78551
|
}
|
|
@@ -78103,7 +78652,7 @@ ${name}:`));
|
|
|
78103
78652
|
process.exit(1);
|
|
78104
78653
|
}
|
|
78105
78654
|
const collection = getCollectionForAgent(agent, config);
|
|
78106
|
-
const apiUrl = config.memory?.config?.url ??
|
|
78655
|
+
const apiUrl = config.memory?.config?.url ?? HINDSIGHT_DEFAULT_MCP_URL;
|
|
78107
78656
|
const timeoutMs = Math.max(500, parseInt(opts.timeout, 10) || 5000);
|
|
78108
78657
|
console.log(source_default.bold(`
|
|
78109
78658
|
Demoting memory ${source_default.cyan(memoryId)}`));
|
|
@@ -78119,7 +78668,7 @@ Demoting memory ${source_default.cyan(memoryId)}`));
|
|
|
78119
78668
|
console.error(source_default.gray(" The Hindsight MCP `update_memory` tool may not be exposed by your deployment, or the memory ID may be wrong. Try `switchroom memory recall-log " + agent + " --json` to confirm the ID surfaced recently."));
|
|
78120
78669
|
process.exit(1);
|
|
78121
78670
|
}));
|
|
78122
|
-
const restBase = (url) => (url ??
|
|
78671
|
+
const restBase = (url) => (url ?? HINDSIGHT_DEFAULT_MCP_URL).replace(/\/mcp\/?$/, "").replace(/\/$/, "");
|
|
78123
78672
|
const VALID_BANK = /^[a-zA-Z0-9_.-]+$/;
|
|
78124
78673
|
const profile = memory.command("profile").description("Author + inspect operator profile banks (shared/per-user memory; wire via memory.recall.additional_banks)");
|
|
78125
78674
|
profile.command("add <bank> <fact...>").description("Add an operator-authored fact to a profile bank (creates the bank on first write)").option("--timeout <ms>", "HTTP timeout in milliseconds (retain runs synchronously; default: 60000)", "60000").action(withConfigError(async (bank, factWords, opts) => {
|
|
@@ -78227,13 +78776,13 @@ import { timingSafeEqual as timingSafeEqual3, randomBytes as randomBytes11 } fro
|
|
|
78227
78776
|
// src/web/api.ts
|
|
78228
78777
|
init_lifecycle();
|
|
78229
78778
|
init_manager();
|
|
78230
|
-
|
|
78779
|
+
init_hindsight2();
|
|
78231
78780
|
import { spawnSync as spawnSync6 } from "node:child_process";
|
|
78232
78781
|
import { existsSync as existsSync51, readFileSync as readFileSync45, statSync as statSync23 } from "node:fs";
|
|
78233
78782
|
import { resolve as resolve29, join as join45 } from "node:path";
|
|
78234
78783
|
|
|
78235
78784
|
// src/web/memory-remediation.ts
|
|
78236
|
-
|
|
78785
|
+
init_hindsight2();
|
|
78237
78786
|
function restBaseFromMcpUrl(mcpUrl) {
|
|
78238
78787
|
return hindsightRestBase(mcpUrl);
|
|
78239
78788
|
}
|
|
@@ -78295,6 +78844,7 @@ async function buildUserProfile(mcpUrl, bank, opts) {
|
|
|
78295
78844
|
}
|
|
78296
78845
|
|
|
78297
78846
|
// src/web/api.ts
|
|
78847
|
+
init_hindsight();
|
|
78298
78848
|
init_audit_reader();
|
|
78299
78849
|
|
|
78300
78850
|
// src/scheduler/dispatch.ts
|
|
@@ -82307,6 +82857,7 @@ var NVIDIA_TOOLKIT_INSTALL_HINT = "https://docs.nvidia.com/datacenter/cloud-nati
|
|
|
82307
82857
|
|
|
82308
82858
|
// src/cli/setup.ts
|
|
82309
82859
|
init_host_capabilities();
|
|
82860
|
+
init_hindsight();
|
|
82310
82861
|
init_client();
|
|
82311
82862
|
init_posthog();
|
|
82312
82863
|
|
|
@@ -82913,17 +83464,45 @@ async function stepMemoryBackend(config, nonInteractive, switchroomConfigPath) {
|
|
|
82913
83464
|
console.log(source_default.gray(" Found stopped switchroom-hindsight container, removing..."));
|
|
82914
83465
|
stopHindsight();
|
|
82915
83466
|
}
|
|
83467
|
+
let ports;
|
|
83468
|
+
try {
|
|
83469
|
+
ports = await pickHindsightPorts();
|
|
83470
|
+
} catch (err) {
|
|
83471
|
+
console.log(source_default.red(` ${err.message}`));
|
|
83472
|
+
return;
|
|
83473
|
+
}
|
|
83474
|
+
if (ports.apiPort !== HINDSIGHT_DEFAULT_API_PORT) {
|
|
83475
|
+
console.log(source_default.yellow(` Port ${HINDSIGHT_DEFAULT_API_PORT} is already in use; using ${ports.apiPort}/${ports.uiPort} instead.`));
|
|
83476
|
+
}
|
|
83477
|
+
let conflict = await preflightHindsightPorts(ports);
|
|
83478
|
+
if (conflict) {
|
|
83479
|
+
const heldBy = conflict.holder ? ` (held by ${conflict.holder})` : "";
|
|
83480
|
+
console.log(source_default.yellow(` Chosen Hindsight port ${conflict.port} is occupied${heldBy}; selecting a free port instead of crash-looping.`));
|
|
83481
|
+
try {
|
|
83482
|
+
ports = await pickHindsightPorts();
|
|
83483
|
+
} catch (err) {
|
|
83484
|
+
console.log(source_default.red(` ${err.message}`));
|
|
83485
|
+
return;
|
|
83486
|
+
}
|
|
83487
|
+
conflict = await preflightHindsightPorts(ports);
|
|
83488
|
+
if (conflict) {
|
|
83489
|
+
const stillHeldBy = conflict.holder ? ` (held by ${conflict.holder})` : "";
|
|
83490
|
+
console.log(source_default.red(` Refusing to start Hindsight: port ${conflict.port} is still occupied${stillHeldBy} after reassignment. Free it and re-run \`switchroom setup\`.`));
|
|
83491
|
+
return;
|
|
83492
|
+
}
|
|
83493
|
+
console.log(source_default.green(` Reassigned Hindsight to port ${ports.apiPort}/${ports.uiPort}.`));
|
|
83494
|
+
}
|
|
82916
83495
|
const spin = spinner("Starting Hindsight Docker container...");
|
|
82917
83496
|
try {
|
|
82918
83497
|
const litellmCfg = await resolveLiteLLMForHindsight2(config);
|
|
82919
|
-
startHindsight(
|
|
83498
|
+
startHindsight(ports, litellmCfg);
|
|
82920
83499
|
if (litellmCfg) {
|
|
82921
83500
|
console.log(source_default.gray(" LiteLLM routing enabled (--network host, ANTHROPIC_BASE_URL set)."));
|
|
82922
83501
|
}
|
|
82923
83502
|
if (isHindsightRunning()) {
|
|
82924
83503
|
spin.stop(source_default.green(`${STEP_DONE} Hindsight container started (switchroom-hindsight)`));
|
|
82925
|
-
console.log(source_default.gray(
|
|
82926
|
-
console.log(source_default.gray(
|
|
83504
|
+
console.log(source_default.gray(` API: http://localhost:${ports.apiPort}/mcp`));
|
|
83505
|
+
console.log(source_default.gray(` UI: http://localhost:${ports.uiPort}`));
|
|
82927
83506
|
} else {
|
|
82928
83507
|
spin.stop(source_default.yellow("Container started but may still be initializing"));
|
|
82929
83508
|
}
|
|
@@ -83450,6 +84029,22 @@ var UPDATE_RESULT_SENTINEL = "SWITCHROOM_UPDATE_RESULT:";
|
|
|
83450
84029
|
function encodeUpdateResultLine(payload) {
|
|
83451
84030
|
return `${UPDATE_RESULT_SENTINEL}${JSON.stringify(payload)}`;
|
|
83452
84031
|
}
|
|
84032
|
+
function resolveHindsightPinTag(opts) {
|
|
84033
|
+
if (typeof opts.hindsightPinTag === "string") {
|
|
84034
|
+
return opts.hindsightPinTag || undefined;
|
|
84035
|
+
}
|
|
84036
|
+
if (opts.channel)
|
|
84037
|
+
return;
|
|
84038
|
+
let candidate = opts.pin;
|
|
84039
|
+
if (!candidate) {
|
|
84040
|
+
try {
|
|
84041
|
+
candidate = loadConfig().release?.pin ?? undefined;
|
|
84042
|
+
} catch {
|
|
84043
|
+
candidate = undefined;
|
|
84044
|
+
}
|
|
84045
|
+
}
|
|
84046
|
+
return candidate && /^v\d+\.\d+\.\d+$/.test(candidate) ? candidate : undefined;
|
|
84047
|
+
}
|
|
83453
84048
|
function planUpdate(opts) {
|
|
83454
84049
|
const composePath = opts.composePath ?? DEFAULT_COMPOSE_PATH;
|
|
83455
84050
|
const runner = opts.runner ?? defaultRunner2;
|
|
@@ -83596,10 +84191,14 @@ function planUpdate(opts) {
|
|
|
83596
84191
|
}
|
|
83597
84192
|
steps.push({
|
|
83598
84193
|
name: "refresh-hindsight",
|
|
83599
|
-
description: "switchroom memory setup --recreate \u2014 pull
|
|
84194
|
+
description: "switchroom memory setup --recreate \u2014 pull the hindsight image (pinned to the release target when set, else :latest) + recreate the memory singleton (reusing its current port)",
|
|
83600
84195
|
skipReason: !memoryBackendHindsight ? "memory.backend is not hindsight \u2014 no hindsight singleton to refresh" : opts.skipImages ? "--skip-images flag set" : undefined,
|
|
83601
84196
|
run: () => {
|
|
83602
|
-
const
|
|
84197
|
+
const pinTag = resolveHindsightPinTag(opts);
|
|
84198
|
+
const setupArgs = ["memory", "setup", "--recreate"];
|
|
84199
|
+
if (pinTag)
|
|
84200
|
+
setupArgs.push("--tag", pinTag);
|
|
84201
|
+
const r = runner(process.execPath, [scriptPath, ...setupArgs]);
|
|
83603
84202
|
if (r.status !== 0)
|
|
83604
84203
|
throw new Error("switchroom memory setup --recreate failed");
|
|
83605
84204
|
}
|
|
@@ -83963,6 +84562,7 @@ import { homedir as homedir39 } from "node:os";
|
|
|
83963
84562
|
init_operator_uid();
|
|
83964
84563
|
init_atomic();
|
|
83965
84564
|
init_audit_reader();
|
|
84565
|
+
init_hindsight();
|
|
83966
84566
|
function normalizeVersion(v) {
|
|
83967
84567
|
return v.trim().replace(/^v/, "");
|
|
83968
84568
|
}
|
|
@@ -86902,7 +87502,7 @@ import { existsSync as existsSync72, readFileSync as readFileSync63, readdirSync
|
|
|
86902
87502
|
import { resolve as resolve45, join as join71 } from "node:path";
|
|
86903
87503
|
import { createHash as createHash14 } from "node:crypto";
|
|
86904
87504
|
init_merge();
|
|
86905
|
-
|
|
87505
|
+
init_hindsight2();
|
|
86906
87506
|
function formatBytes(bytes) {
|
|
86907
87507
|
return `${bytes.toLocaleString()} bytes`;
|
|
86908
87508
|
}
|
|
@@ -87323,6 +87923,8 @@ async function claimWorktree(input, codeRepos) {
|
|
|
87323
87923
|
const baseDir = worktreesBaseDir();
|
|
87324
87924
|
mkdirSync38(baseDir, { recursive: true });
|
|
87325
87925
|
worktreePath = join73(baseDir, `${id}-${taskSuffix}`);
|
|
87926
|
+
const ambientOwner = process.env.SWITCHROOM_AGENT_NAME;
|
|
87927
|
+
const ownerAgent = input.ownerAgent ?? (ambientOwner != null && ambientOwner !== "" ? ambientOwner : undefined);
|
|
87326
87928
|
const now = new Date().toISOString();
|
|
87327
87929
|
const record2 = {
|
|
87328
87930
|
id,
|
|
@@ -87332,7 +87934,7 @@ async function claimWorktree(input, codeRepos) {
|
|
|
87332
87934
|
path: worktreePath,
|
|
87333
87935
|
createdAt: now,
|
|
87334
87936
|
heartbeatAt: now,
|
|
87335
|
-
ownerAgent
|
|
87937
|
+
ownerAgent
|
|
87336
87938
|
};
|
|
87337
87939
|
writeRecord(record2);
|
|
87338
87940
|
} finally {
|