switchroom 0.19.41 → 0.19.42
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 +22 -2
- package/dist/auth-broker/index.js +22 -2
- package/dist/cli/notion-write-pretool.mjs +22 -2
- package/dist/cli/switchroom.js +772 -234
- package/dist/host-control/main.js +23 -3
- package/dist/vault/approvals/kernel-server.js +22 -2
- package/dist/vault/broker/server.js +22 -2
- package/package.json +1 -1
- package/telegram-plugin/dist/gateway/gateway.js +97 -83
- package/telegram-plugin/gateway/approval-callback-consume-record.test.ts +132 -0
- package/telegram-plugin/gateway/gateway.ts +7 -7
- package/telegram-plugin/gateway/liveness-wiring.ts +12 -0
- package/telegram-plugin/gateway/model-command.ts +21 -109
- package/telegram-plugin/gateway/stream-render.ts +57 -0
- package/telegram-plugin/tests/framework-fallback-drains-parked.test.ts +134 -0
- package/telegram-plugin/tests/helpers/liveness-wiring-fixture.ts +3 -0
- package/vendor/hindsight-memory/CHANGELOG.md +28 -0
- package/vendor/hindsight-memory/docs/measurements/subagent-volume-gate-3994.md +84 -0
- package/vendor/hindsight-memory/scripts/backfill_transcripts.py +254 -40
- package/vendor/hindsight-memory/scripts/lib/content.py +11 -2
- package/vendor/hindsight-memory/scripts/recall.py +18 -3
- package/vendor/hindsight-memory/scripts/subagent_retain.py +59 -38
- package/vendor/hindsight-memory/scripts/tests/data/replay_volume_gate_3994.py +295 -0
- package/vendor/hindsight-memory/scripts/tests/test_backfill_from_logs.py +109 -0
- package/vendor/hindsight-memory/scripts/tests/test_overlap_tokens.py +135 -0
- package/vendor/hindsight-memory/scripts/tests/test_recall_no_lexical_gate.py +20 -23
- package/vendor/hindsight-memory/scripts/tests/test_recall_query_shaping.py +48 -0
- package/vendor/hindsight-memory/scripts/tests/test_subagent_retain.py +94 -1
- package/vendor/hindsight-memory/scripts/tests/test_subagent_retain_learnings.py +98 -42
package/dist/cli/switchroom.js
CHANGED
|
@@ -2120,7 +2120,7 @@ var init_esm = __esm(() => {
|
|
|
2120
2120
|
});
|
|
2121
2121
|
|
|
2122
2122
|
// src/build-info.ts
|
|
2123
|
-
var VERSION = "0.19.
|
|
2123
|
+
var VERSION = "0.19.42", COMMIT_SHA = "e288dac2";
|
|
2124
2124
|
|
|
2125
2125
|
// src/cli/resolve-version.ts
|
|
2126
2126
|
import { existsSync, readFileSync } from "node:fs";
|
|
@@ -13613,6 +13613,9 @@ var init_observation_scopes = __esm(() => {
|
|
|
13613
13613
|
function isValidTimezone(value) {
|
|
13614
13614
|
return TIMEZONE_REGEX.test(value);
|
|
13615
13615
|
}
|
|
13616
|
+
function resolveMemoryProfile(agentConfig) {
|
|
13617
|
+
return agentConfig?.memory?.profile ?? agentConfig?.extends ?? DEFAULT_PROFILE;
|
|
13618
|
+
}
|
|
13616
13619
|
var CodeRepoEntrySchema, AgentBindMountSchema, HttpDiffPollSchema, PollSpecSchema, TelegramMessageActionSchema, WebhookActionSchema, ActionSpecSchema, ScheduleEntrySchema, AgentSoulSchema, AgentToolsSchema, ObservationScopesSchema, ObservationScopeStrategySchema, AgentMemorySchema, HookEntrySchema, AgentHooksSchema, SubagentSchema, SessionSchema, SessionContinuitySchema, webhookDispatchRule, TelegramChannelSchema, ChannelsSchema, TIMEZONE_REGEX, ApproverIdSchema, GoogleWorkspaceTierSchema, GoogleWorkspaceConfigSchema, LiteLLMConfigSchema, HindsightPerOpLlmSchema, HindsightConfigSchema, MicrosoftWorkspaceConfigSchema, NotionWorkspaceConfigSchema, AgentGoogleWorkspaceConfigSchema, MicrosoftAccountEmailSchema, MicrosoftToolTokenSchema, MicrosoftAccountBindingSchema, AgentMicrosoftWorkspaceConfigSchema, AgentNotionWorkspaceConfigSchema, ReactionsSchema, ReactionDispatchSchema, releaseBlockFields, ReleaseBlock, RootReleaseBlock, NetworkIsolationSchema, servesField, knowsField, profileFields, ProfileSchema, _omitExtends, defaultsFields, AgentDefaultsSchema, DEFAULT_PROFILE = "default", AgentSchema, TelegramConfigSchema, MemoryBackendConfigSchema, VaultConfigSchema, QuotaConfigSchema, AutoReleaseCheckSchema, HostControlConfigSchema, WebServiceConfigSchema, FleetHealthConfigSchema, HostdConfigSchema, CronEgressSchema, CronConfigSchema, UserSchema, SwitchroomConfigSchema;
|
|
13617
13620
|
var init_schema = __esm(() => {
|
|
13618
13621
|
init_zod();
|
|
@@ -13735,6 +13738,7 @@ var init_schema = __esm(() => {
|
|
|
13735
13738
|
auto_recall: exports_external.boolean().default(true).describe("Auto-search memories before each response"),
|
|
13736
13739
|
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)."),
|
|
13737
13740
|
isolation: exports_external.enum(["default", "strict"]).default("default").describe("strict = never shared cross-agent, default = eligible for reflect"),
|
|
13741
|
+
profile: exports_external.string().optional().describe("Memory profile bank this agent's curated memory defaults key off \u2014 " + "the built-in disposition + observations_mission in PROFILE_MEMORY_DEFAULTS. " + "Decouples the memory profile from `extends` (the filesystem persona " + "profile), so an agent on `extends: default` can opt into the `coding` " + "memory bundle via `memory.profile: coding` without inheriting the coding " + "persona. Resolution: memory.profile \u2192 extends \u2192 DEFAULT_PROFILE (see " + "resolveMemoryProfile). Unset \u21d2 byte-identical to keying off `extends`."),
|
|
13738
13742
|
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."),
|
|
13739
13743
|
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."),
|
|
13740
13744
|
retain_mission: exports_external.string().optional().describe("Instructions for the fact extraction LLM during retain. Cascade: override."),
|
|
@@ -14028,6 +14032,8 @@ var init_schema = __esm(() => {
|
|
|
14028
14032
|
provider: exports_external.string().min(1).optional().describe("Hindsight LLM provider (upstream `HINDSIGHT_API_LLM_PROVIDER`). " + "Defaults to `claude-code` (subscription-honest, broker-fed OAuth). " + "Any litellm-routable provider the upstream image supports is valid. " + "Serves as the GLOBAL default for every op absent a per-op override."),
|
|
14029
14033
|
model: exports_external.string().min(1).optional().describe("Hindsight LLM model (upstream `HINDSIGHT_API_LLM_MODEL`). Defaults " + "to HINDSIGHT_DEFAULT_MODEL. Any model your LiteLLM proxy can route " + "is valid, e.g. `openrouter/z-ai/glm-5.2` when routing through the " + "fleet proxy. With provider=claude-code this value is ALSO exported " + "as `ANTHROPIC_MODEL` to the claude subprocess. Serves as the GLOBAL " + "default for every op absent a per-op override."),
|
|
14030
14034
|
context_window: exports_external.number().int().positive().optional().describe("GLOBAL context window (tokens) of the backend serving hindsight's " + "LLM ops \u2014 the declared size switchroom derives every token budget " + "from. Set this to the real window of whatever you point " + "`hindsight.llm` at (e.g. 32768 for a llama.cpp slot launched with " + "`-c 65536 -np 2`, 131072 for a large-window OpenRouter model). " + "Absent \u2192 a per-provider default: 200000 for `claude-code`, a " + "conservative 32768 for everything else. Overflowing a local " + "backend's window does NOT error \u2014 llama.cpp context-shift silently " + "drops the system prompt and the model answers conversationally " + "with HTTP 200 \u2014 so this value is what makes the failure " + "detectable at setup time instead of never."),
|
|
14035
|
+
base_url: exports_external.string().min(1).optional().describe("GLOBAL LLM base URL (upstream `HINDSIGHT_API_LLM_BASE_URL`). The " + "default endpoint every op inherits absent a per-op " + "`hindsight.llm.<op>.base_url`. Optional passthrough; unset \u2192 the " + "engine's provider default. When this points at host loopback the " + "hindsight container is forced onto host networking, same as a " + "per-op base URL, so the endpoint stays reachable (#3687)."),
|
|
14036
|
+
api_key: exports_external.string().min(1).optional().describe("GLOBAL LLM API key (upstream `HINDSIGHT_API_LLM_API_KEY`). Literal " + "or `vault:` reference. The default credential every op inherits " + "absent a per-op `hindsight.llm.<op>.api_key`; the engine reads it as " + "a plain env fallback. Optional passthrough (#3687)."),
|
|
14031
14037
|
retain: HindsightPerOpLlmSchema.optional().describe("Per-op override for the `retain` LLM op (memory ingestion). Emits " + "`HINDSIGHT_API_RETAIN_LLM_*`. Absent \u2192 uses the global model/provider."),
|
|
14032
14038
|
reflect: HindsightPerOpLlmSchema.optional().describe("Per-op override for the `reflect` LLM op (synthesis / mental-model " + "refresh). Emits `HINDSIGHT_API_REFLECT_LLM_*`. Absent \u2192 uses global."),
|
|
14033
14039
|
consolidation: HindsightPerOpLlmSchema.optional().describe("Per-op override for the `consolidation` LLM op (background memory " + "merge). Emits `HINDSIGHT_API_CONSOLIDATION_LLM_*`. Absent \u2192 global.")
|
|
@@ -14164,6 +14170,7 @@ var init_schema = __esm(() => {
|
|
|
14164
14170
|
auto_recall: exports_external.boolean().optional(),
|
|
14165
14171
|
file: exports_external.boolean().optional(),
|
|
14166
14172
|
isolation: exports_external.enum(["default", "strict"]).optional(),
|
|
14173
|
+
profile: exports_external.string().optional(),
|
|
14167
14174
|
directive_capture_nudge: exports_external.boolean().optional(),
|
|
14168
14175
|
observation_scopes: ObservationScopesSchema,
|
|
14169
14176
|
observation_scope_strategy: ObservationScopeStrategySchema,
|
|
@@ -14205,7 +14212,23 @@ var init_schema = __esm(() => {
|
|
|
14205
14212
|
max_query_chars: exports_external.number().int().min(1).optional(),
|
|
14206
14213
|
parallel: exports_external.boolean().optional(),
|
|
14207
14214
|
additional_banks: exports_external.array(exports_external.string()).optional(),
|
|
14208
|
-
sender_banks: exports_external.record(exports_external.string(), exports_external.string()).optional()
|
|
14215
|
+
sender_banks: exports_external.record(exports_external.string(), exports_external.string()).optional(),
|
|
14216
|
+
types: exports_external.array(exports_external.string()).optional(),
|
|
14217
|
+
skip_trivial: exports_external.boolean().optional(),
|
|
14218
|
+
topic_filter_mode: exports_external.enum(["soft-preamble", "hard-filter"]).optional()
|
|
14219
|
+
}).optional(),
|
|
14220
|
+
retain: exports_external.object({
|
|
14221
|
+
every_n_turns: exports_external.number().int().min(1).optional(),
|
|
14222
|
+
overlap_turns: exports_external.number().int().min(0).optional()
|
|
14223
|
+
}).optional(),
|
|
14224
|
+
bank_mission: exports_external.string().optional(),
|
|
14225
|
+
reflect_mission: exports_external.string().optional(),
|
|
14226
|
+
retain_mission: exports_external.string().optional(),
|
|
14227
|
+
observations_mission: exports_external.string().optional(),
|
|
14228
|
+
disposition: exports_external.object({
|
|
14229
|
+
skepticism: exports_external.number().int().min(1).max(5).optional(),
|
|
14230
|
+
literalism: exports_external.number().int().min(1).max(5).optional(),
|
|
14231
|
+
empathy: exports_external.number().int().min(1).max(5).optional()
|
|
14209
14232
|
}).optional()
|
|
14210
14233
|
}).optional(),
|
|
14211
14234
|
schedule: exports_external.array(ScheduleEntrySchema).optional(),
|
|
@@ -15594,6 +15617,43 @@ var init_thinking_effort_risk = __esm(() => {
|
|
|
15594
15617
|
RISKY_EFFORTS = new Set(["medium", "high", "xhigh", "max"]);
|
|
15595
15618
|
});
|
|
15596
15619
|
|
|
15620
|
+
// src/agents/model-aliases.ts
|
|
15621
|
+
function expandSrAlias(arg) {
|
|
15622
|
+
return SR_MODEL_ALIASES[arg.toLowerCase()] ?? arg;
|
|
15623
|
+
}
|
|
15624
|
+
function expandClaudeAlias(arg) {
|
|
15625
|
+
return CLAUDE_MODEL_ALIASES[arg.trim().toLowerCase()] ?? arg;
|
|
15626
|
+
}
|
|
15627
|
+
function canonicalModelToken(arg) {
|
|
15628
|
+
const trimmed = arg.trim();
|
|
15629
|
+
const lower = trimmed.toLowerCase();
|
|
15630
|
+
return lower.startsWith("claude-") ? lower : trimmed;
|
|
15631
|
+
}
|
|
15632
|
+
function expandModelAlias(arg) {
|
|
15633
|
+
return canonicalModelToken(expandSrAlias(expandClaudeAlias(arg)));
|
|
15634
|
+
}
|
|
15635
|
+
var CLAUDE_MODEL_ALIASES, SR_MODEL_ALIASES;
|
|
15636
|
+
var init_model_aliases = __esm(() => {
|
|
15637
|
+
CLAUDE_MODEL_ALIASES = {
|
|
15638
|
+
opus48: "claude-opus-4-8",
|
|
15639
|
+
"opus-4-8": "claude-opus-4-8"
|
|
15640
|
+
};
|
|
15641
|
+
SR_MODEL_ALIASES = {
|
|
15642
|
+
flash: "sr-gemini-2.5-flash",
|
|
15643
|
+
gemini: "sr-gemini-2.5-pro",
|
|
15644
|
+
deepseek: "sr-deepseek-v3",
|
|
15645
|
+
r1: "sr-deepseek-r1",
|
|
15646
|
+
glm: "sr-glm-5",
|
|
15647
|
+
codex: "sr-codex-5.5",
|
|
15648
|
+
grok: "sr-grok-4.5",
|
|
15649
|
+
kimi: "sr-kimi-k3",
|
|
15650
|
+
gpt: "sr-gpt-5.6-sol",
|
|
15651
|
+
sol: "sr-gpt-5.6-sol",
|
|
15652
|
+
terra: "sr-gpt-5.6-terra",
|
|
15653
|
+
luna: "sr-gpt-5.6-luna"
|
|
15654
|
+
};
|
|
15655
|
+
});
|
|
15656
|
+
|
|
15597
15657
|
// telegram-plugin/gateway/model-command.ts
|
|
15598
15658
|
function isClaudeModel(name) {
|
|
15599
15659
|
const lower = name.toLowerCase();
|
|
@@ -15603,14 +15663,11 @@ function isClaudeModel(name) {
|
|
|
15603
15663
|
return true;
|
|
15604
15664
|
return lower.startsWith("claude-");
|
|
15605
15665
|
}
|
|
15606
|
-
var MODEL_ALIASES
|
|
15666
|
+
var MODEL_ALIASES;
|
|
15607
15667
|
var init_model_command = __esm(() => {
|
|
15608
15668
|
init_thinking_effort_risk();
|
|
15669
|
+
init_model_aliases();
|
|
15609
15670
|
MODEL_ALIASES = ["opus", "sonnet", "haiku", "fable", "default"];
|
|
15610
|
-
CLAUDE_MODEL_ALIASES = {
|
|
15611
|
-
opus48: "claude-opus-4-8",
|
|
15612
|
-
"opus-4-8": "claude-opus-4-8"
|
|
15613
|
-
};
|
|
15614
15671
|
});
|
|
15615
15672
|
|
|
15616
15673
|
// src/litellm/timeout-budget.ts
|
|
@@ -17466,11 +17523,11 @@ var init_protocol = __esm(() => {
|
|
|
17466
17523
|
OkRevokeGrantResponseSchema,
|
|
17467
17524
|
OkApprovalRequestResponseSchema,
|
|
17468
17525
|
OkApprovalLookupResponseSchema,
|
|
17526
|
+
OkApprovalConsumeRecordResponseSchema,
|
|
17469
17527
|
OkApprovalConsumeResponseSchema,
|
|
17470
17528
|
OkApprovalRevokeResponseSchema,
|
|
17471
17529
|
OkApprovalListResponseSchema,
|
|
17472
17530
|
OkApprovalRecordResponseSchema,
|
|
17473
|
-
OkApprovalConsumeRecordResponseSchema,
|
|
17474
17531
|
ErrorResponseSchema
|
|
17475
17532
|
]);
|
|
17476
17533
|
});
|
|
@@ -18433,6 +18490,18 @@ function resolveHindsightPerOpLlm(llm) {
|
|
|
18433
18490
|
}
|
|
18434
18491
|
return out;
|
|
18435
18492
|
}
|
|
18493
|
+
function resolveHindsightGlobalLlmExtras(llm) {
|
|
18494
|
+
const out = [];
|
|
18495
|
+
if (!llm)
|
|
18496
|
+
return out;
|
|
18497
|
+
const baseUrl = llm.base_url?.trim();
|
|
18498
|
+
const apiKey = llm.api_key?.trim();
|
|
18499
|
+
if (baseUrl)
|
|
18500
|
+
out.push(["HINDSIGHT_API_LLM_BASE_URL", baseUrl]);
|
|
18501
|
+
if (apiKey)
|
|
18502
|
+
out.push(["HINDSIGHT_API_LLM_API_KEY", apiKey]);
|
|
18503
|
+
return out;
|
|
18504
|
+
}
|
|
18436
18505
|
function resolveHindsightLlm(llm, litellm) {
|
|
18437
18506
|
const provider = llm?.provider?.trim() || "claude-code";
|
|
18438
18507
|
const model = llm?.model?.trim() || (litellm ? litellm.model ?? HINDSIGHT_DEFAULT_LITELLM_MODEL : HINDSIGHT_DEFAULT_MODEL);
|
|
@@ -18449,6 +18518,7 @@ function collectHindsightLlmBaseUrls(llm, litellm) {
|
|
|
18449
18518
|
out.push(v);
|
|
18450
18519
|
};
|
|
18451
18520
|
push(litellm?.baseUrl);
|
|
18521
|
+
push(llm?.base_url);
|
|
18452
18522
|
for (const [k, v] of resolveHindsightPerOpLlm(llm)) {
|
|
18453
18523
|
if (k.endsWith("_BASE_URL"))
|
|
18454
18524
|
push(v);
|
|
@@ -18618,6 +18688,7 @@ function hindsightContainerEnvPairs(opts) {
|
|
|
18618
18688
|
const pairs = [
|
|
18619
18689
|
["HINDSIGHT_API_LLM_PROVIDER", llmProvider],
|
|
18620
18690
|
["HINDSIGHT_API_LLM_MODEL", llmModel],
|
|
18691
|
+
...resolveHindsightGlobalLlmExtras(llm),
|
|
18621
18692
|
...perOpLlm,
|
|
18622
18693
|
["HINDSIGHT_API_MCP_STATELESS", String(HINDSIGHT_DEFAULT_MCP_STATELESS)],
|
|
18623
18694
|
...hindsightLlmBudgetEnv(llm),
|
|
@@ -18845,6 +18916,7 @@ function generateHindsightComposeSnippet(llm, mirrorDir, litellm, gpu, perf, cpA
|
|
|
18845
18916
|
const environment = [
|
|
18846
18917
|
` - HINDSIGHT_API_LLM_PROVIDER=${llmProvider}`,
|
|
18847
18918
|
` - HINDSIGHT_API_LLM_MODEL=${llmModel}`,
|
|
18919
|
+
...resolveHindsightGlobalLlmExtras(llm).map(([k, v]) => ` - ${k}=${v}`),
|
|
18848
18920
|
...perOpLlm.map(([k, v]) => ` - ${k}=${v}`),
|
|
18849
18921
|
...llmProvider === "claude-code" ? [` - ANTHROPIC_MODEL=${llmModel}`] : [],
|
|
18850
18922
|
...hostNetwork ? hindsightHostNetworkBindEnvPairs(apiPort).map(([k, v]) => ` - ${k}=${v}`) : [` - HINDSIGHT_CP_DATAPLANE_API_URL=http://localhost:8888`],
|
|
@@ -28256,7 +28328,9 @@ function declaredRoutingMode(resolvedModel) {
|
|
|
28256
28328
|
return isClaudeModel(resolvedModel) ? "passthrough" : "router-root";
|
|
28257
28329
|
}
|
|
28258
28330
|
function normalizeModelAlias(model) {
|
|
28259
|
-
|
|
28331
|
+
if (model === "claude-fable-5")
|
|
28332
|
+
return "fable";
|
|
28333
|
+
return expandModelAlias(model);
|
|
28260
28334
|
}
|
|
28261
28335
|
function dedupe2(items) {
|
|
28262
28336
|
const seen = new Set;
|
|
@@ -28959,8 +29033,9 @@ function installHindsightPlugin(agentName, agentDir, switchroomConfig, resolvedA
|
|
|
28959
29033
|
if (!memory)
|
|
28960
29034
|
return null;
|
|
28961
29035
|
const agentMemory = switchroomConfig.agents[agentName]?.memory;
|
|
28962
|
-
if (
|
|
29036
|
+
if (resolveHindsightAutoRecall(switchroomConfig, agentName, resolvedAgentConfig) === false) {
|
|
28963
29037
|
return null;
|
|
29038
|
+
}
|
|
28964
29039
|
const sourcePath = resolveHindsightVendorPath();
|
|
28965
29040
|
if (!existsSync17(sourcePath)) {
|
|
28966
29041
|
process.stderr.write(`installHindsightPlugin: vendor source missing at ${sourcePath} ` + `\u2014 hindsight plugin NOT installed for ${agentName}. ` + `Likely a packaging regression: check the npm tarball's files array.
|
|
@@ -28969,13 +29044,14 @@ function installHindsightPlugin(agentName, agentDir, switchroomConfig, resolvedA
|
|
|
28969
29044
|
}
|
|
28970
29045
|
const destPath = join12(agentDir, ".claude", "plugins", "hindsight-memory");
|
|
28971
29046
|
const additionalBanks = resolveUsers(switchroomConfig, agentName).additionalBanks;
|
|
28972
|
-
const retainConfig = resolveHindsightRetainConfig(switchroomConfig, agentName);
|
|
29047
|
+
const retainConfig = resolveHindsightRetainConfig(switchroomConfig, agentName, resolvedAgentConfig);
|
|
29048
|
+
const observationScopeSettings = resolveHindsightObservationScopeSettings(switchroomConfig, agentName, resolvedAgentConfig);
|
|
28973
29049
|
const recallTunables = resolveHindsightRecallTunables(resolveHindsightRecallConfig(switchroomConfig, agentName, resolvedAgentConfig));
|
|
28974
29050
|
const contentOverrides = {};
|
|
28975
29051
|
try {
|
|
28976
29052
|
const vendorSettingsPath = join12(sourcePath, "settings.json");
|
|
28977
29053
|
if (existsSync17(vendorSettingsPath)) {
|
|
28978
|
-
const expectedSettings = renderHindsightSettingsOverrides(readFileSync15(vendorSettingsPath, "utf-8"), additionalBanks, retainConfig, recallTunables);
|
|
29054
|
+
const expectedSettings = renderHindsightSettingsOverrides(readFileSync15(vendorSettingsPath, "utf-8"), additionalBanks, retainConfig, recallTunables, observationScopeSettings);
|
|
28979
29055
|
if (expectedSettings != null)
|
|
28980
29056
|
contentOverrides["settings.json"] = expectedSettings;
|
|
28981
29057
|
}
|
|
@@ -28993,21 +29069,34 @@ function installHindsightPlugin(agentName, agentDir, switchroomConfig, resolvedA
|
|
|
28993
29069
|
}
|
|
28994
29070
|
copyDirRecursive2(sourcePath, destPath);
|
|
28995
29071
|
}
|
|
28996
|
-
applyHindsightSettingsOverrides(destPath, additionalBanks, retainConfig, recallTunables);
|
|
29072
|
+
applyHindsightSettingsOverrides(destPath, additionalBanks, retainConfig, recallTunables, observationScopeSettings);
|
|
28997
29073
|
applyHindsightHooksOverrides(destPath, recallTunables);
|
|
28998
29074
|
const bankId = agentMemory?.collection ?? agentName;
|
|
28999
29075
|
const mcpUrl = memory.config?.url ?? HINDSIGHT_DEFAULT_MCP_URL;
|
|
29000
29076
|
const apiBaseUrl = mcpUrl.replace(/\/mcp\/?$/, "").replace(/\/$/, "");
|
|
29001
29077
|
return { pluginDir: destPath, apiBaseUrl, bankId };
|
|
29002
29078
|
}
|
|
29003
|
-
function resolveHindsightRetainConfig(switchroomConfig, agentName) {
|
|
29004
|
-
const
|
|
29005
|
-
const retain = resolved?.memory?.retain;
|
|
29079
|
+
function resolveHindsightRetainConfig(switchroomConfig, agentName, resolvedAgentConfig) {
|
|
29080
|
+
const retain = resolvedAgentConfig ? resolvedAgentConfig.memory?.retain : switchroomConfig ? resolveAgentConfig(switchroomConfig.defaults, switchroomConfig.profiles, switchroomConfig.agents[agentName] ?? {})?.memory?.retain : undefined;
|
|
29006
29081
|
return {
|
|
29007
29082
|
everyNTurns: retain?.every_n_turns ?? HINDSIGHT_DEFAULT_RETAIN_EVERY_N_TURNS,
|
|
29008
29083
|
overlapTurns: retain?.overlap_turns ?? HINDSIGHT_DEFAULT_RETAIN_OVERLAP_TURNS
|
|
29009
29084
|
};
|
|
29010
29085
|
}
|
|
29086
|
+
function resolveHindsightAutoRecall(switchroomConfig, agentName, resolvedAgentConfig) {
|
|
29087
|
+
if (resolvedAgentConfig)
|
|
29088
|
+
return resolvedAgentConfig.memory?.auto_recall;
|
|
29089
|
+
if (!switchroomConfig)
|
|
29090
|
+
return;
|
|
29091
|
+
return resolveAgentConfig(switchroomConfig.defaults, switchroomConfig.profiles, switchroomConfig.agents[agentName] ?? {})?.memory?.auto_recall;
|
|
29092
|
+
}
|
|
29093
|
+
function resolveHindsightObservationScopeSettings(switchroomConfig, agentName, resolvedAgentConfig) {
|
|
29094
|
+
const memory = resolvedAgentConfig ? resolvedAgentConfig.memory : switchroomConfig ? resolveAgentConfig(switchroomConfig.defaults, switchroomConfig.profiles, switchroomConfig.agents[agentName] ?? {})?.memory : undefined;
|
|
29095
|
+
return {
|
|
29096
|
+
observationScopes: memory?.observation_scopes,
|
|
29097
|
+
observationScopeStrategy: memory?.observation_scope_strategy
|
|
29098
|
+
};
|
|
29099
|
+
}
|
|
29011
29100
|
function resolveHindsightRecallConfig(switchroomConfig, agentName, resolvedAgentConfig) {
|
|
29012
29101
|
if (resolvedAgentConfig) {
|
|
29013
29102
|
return resolvedAgentConfig.memory?.recall;
|
|
@@ -29032,7 +29121,7 @@ function applyHindsightHooksOverrides(pluginDestPath, tunables) {
|
|
|
29032
29121
|
return;
|
|
29033
29122
|
writeFileSyncIfChanged(hooksPath, next);
|
|
29034
29123
|
}
|
|
29035
|
-
function applyHindsightSettingsOverrides(pluginDestPath, additionalBanks, retainConfig, recallTunables) {
|
|
29124
|
+
function applyHindsightSettingsOverrides(pluginDestPath, additionalBanks, retainConfig, recallTunables, observationScopeSettings) {
|
|
29036
29125
|
const settingsPath = join12(pluginDestPath, "settings.json");
|
|
29037
29126
|
if (!existsSync17(settingsPath))
|
|
29038
29127
|
return;
|
|
@@ -29042,12 +29131,12 @@ function applyHindsightSettingsOverrides(pluginDestPath, additionalBanks, retain
|
|
|
29042
29131
|
} catch {
|
|
29043
29132
|
return;
|
|
29044
29133
|
}
|
|
29045
|
-
const next = renderHindsightSettingsOverrides(raw, additionalBanks, retainConfig, recallTunables);
|
|
29134
|
+
const next = renderHindsightSettingsOverrides(raw, additionalBanks, retainConfig, recallTunables, observationScopeSettings);
|
|
29046
29135
|
if (next == null)
|
|
29047
29136
|
return;
|
|
29048
29137
|
writeFileSyncIfChanged(settingsPath, next);
|
|
29049
29138
|
}
|
|
29050
|
-
function renderHindsightSettingsOverrides(raw, additionalBanks, retainConfig, recallTunables) {
|
|
29139
|
+
function renderHindsightSettingsOverrides(raw, additionalBanks, retainConfig, recallTunables, observationScopeSettings) {
|
|
29051
29140
|
let settings;
|
|
29052
29141
|
try {
|
|
29053
29142
|
settings = JSON.parse(raw);
|
|
@@ -29069,6 +29158,14 @@ function renderHindsightSettingsOverrides(raw, additionalBanks, retainConfig, re
|
|
|
29069
29158
|
if (additionalBanks.length > 0) {
|
|
29070
29159
|
settings.recallAdditionalBanks = [...additionalBanks];
|
|
29071
29160
|
}
|
|
29161
|
+
const observationScopes = observationScopeSettings.observationScopes;
|
|
29162
|
+
if (typeof observationScopes === "string" && observationScopes.length > 0) {
|
|
29163
|
+
settings.observationScopes = observationScopes;
|
|
29164
|
+
}
|
|
29165
|
+
const observationScopeStrategy = observationScopeSettings.observationScopeStrategy;
|
|
29166
|
+
if (typeof observationScopeStrategy === "string" && observationScopeStrategy.length > 0) {
|
|
29167
|
+
settings.observationScopeStrategy = observationScopeStrategy;
|
|
29168
|
+
}
|
|
29072
29169
|
return JSON.stringify(settings, null, 2) + `
|
|
29073
29170
|
`;
|
|
29074
29171
|
}
|
|
@@ -29549,8 +29646,7 @@ function scaffoldAgent(name, agentConfigRaw, agentsDir, telegramConfig, switchro
|
|
|
29549
29646
|
retractStaleIntegrationKeys(integration, new Set(entries.map((e) => e.key)), settings.mcpServers, site1Protected);
|
|
29550
29647
|
}
|
|
29551
29648
|
installHindsightPlugin(name, agentDir, switchroomConfig, agentConfig);
|
|
29552
|
-
|
|
29553
|
-
if (hindsightOn) {
|
|
29649
|
+
if (hindsightAutoRecallEnabled) {
|
|
29554
29650
|
settings.autoMemoryEnabled = false;
|
|
29555
29651
|
}
|
|
29556
29652
|
settings.hooks = buildSettingsHooksBlock({
|
|
@@ -29907,13 +30003,13 @@ ${body}
|
|
|
29907
30003
|
const userBankMission = agentConfig.memory?.bank_mission;
|
|
29908
30004
|
const userRetainMission = agentConfig.memory?.retain_mission;
|
|
29909
30005
|
const seededRetainMission = await resolveRetainMissionPush(apiUrl, hindsightBankId, userRetainMission, formatAgentBankLabel(name, hindsightBankId));
|
|
29910
|
-
const extras = resolveBankMissionExtras(agentConfig.memory, agentConfig
|
|
30006
|
+
const extras = resolveBankMissionExtras(agentConfig.memory, resolveMemoryProfile(agentConfig));
|
|
29911
30007
|
const missions = { ...extras };
|
|
29912
30008
|
if (seededRetainMission) {
|
|
29913
30009
|
missions.retain_mission = seededRetainMission;
|
|
29914
30010
|
}
|
|
29915
30011
|
delete missions.observations_mission;
|
|
29916
|
-
const seededObservationsMission = await resolveObservationsMissionPush(apiUrl, hindsightBankId, agentConfig.memory?.observations_mission, agentConfig
|
|
30012
|
+
const seededObservationsMission = await resolveObservationsMissionPush(apiUrl, hindsightBankId, agentConfig.memory?.observations_mission, resolveMemoryProfile(agentConfig), formatAgentBankLabel(name, hindsightBankId));
|
|
29917
30013
|
if (seededObservationsMission) {
|
|
29918
30014
|
missions.observations_mission = seededObservationsMission;
|
|
29919
30015
|
}
|
|
@@ -30985,7 +31081,7 @@ ${body}
|
|
|
30985
31081
|
trackBankOps(bankOpsChain.then(async (bankReady) => {
|
|
30986
31082
|
if (!bankReady)
|
|
30987
31083
|
return;
|
|
30988
|
-
const extras = resolveBankMissionExtras(agentConfig.memory, agentConfig
|
|
31084
|
+
const extras = resolveBankMissionExtras(agentConfig.memory, resolveMemoryProfile(agentConfig));
|
|
30989
31085
|
const missions = { ...extras };
|
|
30990
31086
|
if (agentConfig.memory?.bank_mission) {
|
|
30991
31087
|
missions.bank_mission = agentConfig.memory.bank_mission;
|
|
@@ -30994,7 +31090,7 @@ ${body}
|
|
|
30994
31090
|
if (retainMission)
|
|
30995
31091
|
missions.retain_mission = retainMission;
|
|
30996
31092
|
delete missions.observations_mission;
|
|
30997
|
-
const observationsMission = await resolveObservationsMissionPush(apiUrl, hindsightBankId, agentConfig.memory?.observations_mission, agentConfig
|
|
31093
|
+
const observationsMission = await resolveObservationsMissionPush(apiUrl, hindsightBankId, agentConfig.memory?.observations_mission, resolveMemoryProfile(agentConfig), formatAgentBankLabel(name, hindsightBankId));
|
|
30998
31094
|
if (observationsMission)
|
|
30999
31095
|
missions.observations_mission = observationsMission;
|
|
31000
31096
|
if (Object.keys(missions).length > 0) {
|
|
@@ -31583,6 +31679,7 @@ never reaches them.`, TELEGRAM_ENV_PLACEHOLDER = `# Set your bot token: TELEGRAM
|
|
|
31583
31679
|
var init_scaffold = __esm(() => {
|
|
31584
31680
|
init_source();
|
|
31585
31681
|
init_cron_unit_name();
|
|
31682
|
+
init_model_aliases();
|
|
31586
31683
|
init_atomic();
|
|
31587
31684
|
init_agent_owned_tree();
|
|
31588
31685
|
init_generation_stamp();
|
|
@@ -39142,15 +39239,15 @@ async function getJson(url, opts) {
|
|
|
39142
39239
|
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
|
39143
39240
|
try {
|
|
39144
39241
|
const resp = await fetchImpl(url, { signal: controller.signal });
|
|
39145
|
-
clearTimeout(timeout);
|
|
39146
39242
|
if (!resp.ok)
|
|
39147
39243
|
return { ok: false, reason: `HTTP ${resp.status}` };
|
|
39148
39244
|
return { ok: true, data: await resp.json() };
|
|
39149
39245
|
} catch (err) {
|
|
39150
|
-
clearTimeout(timeout);
|
|
39151
39246
|
if (err.name === "AbortError")
|
|
39152
39247
|
return { ok: false, reason: "Timeout" };
|
|
39153
39248
|
return { ok: false, reason: String(err.message ?? err) };
|
|
39249
|
+
} finally {
|
|
39250
|
+
clearTimeout(timeout);
|
|
39154
39251
|
}
|
|
39155
39252
|
}
|
|
39156
39253
|
function summarizeBasedOn(reflect) {
|
|
@@ -39182,6 +39279,7 @@ async function inspectBankHealth(mcpUrl, bankId, opts) {
|
|
|
39182
39279
|
totalDocuments: 0,
|
|
39183
39280
|
totalFacts: 0,
|
|
39184
39281
|
pendingOperations: 0,
|
|
39282
|
+
pendingConsolidation: 0,
|
|
39185
39283
|
newestDocumentAt: null,
|
|
39186
39284
|
unextractedDocuments: [],
|
|
39187
39285
|
mentalModels: [],
|
|
@@ -39222,6 +39320,7 @@ async function inspectBankHealth(mcpUrl, bankId, opts) {
|
|
|
39222
39320
|
totalDocuments: stats.data.total_documents ?? docItems.length,
|
|
39223
39321
|
totalFacts: stats.data.total_nodes ?? 0,
|
|
39224
39322
|
pendingOperations: stats.data.pending_operations ?? 0,
|
|
39323
|
+
pendingConsolidation: stats.data.pending_consolidation ?? 0,
|
|
39225
39324
|
newestDocumentAt,
|
|
39226
39325
|
unextractedDocuments: unextracted,
|
|
39227
39326
|
activeDirectiveCount,
|
|
@@ -45067,6 +45166,84 @@ var init_manifest = __esm(() => {
|
|
|
45067
45166
|
]);
|
|
45068
45167
|
});
|
|
45069
45168
|
|
|
45169
|
+
// src/config/memory-key-drift.ts
|
|
45170
|
+
function unwrapObjectNode(schema) {
|
|
45171
|
+
let s = schema;
|
|
45172
|
+
for (let i = 0;s && i < 10; i++) {
|
|
45173
|
+
const node = s;
|
|
45174
|
+
if (node.shape && typeof node.shape === "object") {
|
|
45175
|
+
return node;
|
|
45176
|
+
}
|
|
45177
|
+
if (node._def?.innerType) {
|
|
45178
|
+
s = node._def.innerType;
|
|
45179
|
+
continue;
|
|
45180
|
+
}
|
|
45181
|
+
if (node._def?.schema) {
|
|
45182
|
+
s = node._def.schema;
|
|
45183
|
+
continue;
|
|
45184
|
+
}
|
|
45185
|
+
break;
|
|
45186
|
+
}
|
|
45187
|
+
return;
|
|
45188
|
+
}
|
|
45189
|
+
function objectShapeKeys(schema) {
|
|
45190
|
+
const node = unwrapObjectNode(schema);
|
|
45191
|
+
return node?.shape ? new Set(Object.keys(node.shape)) : new Set;
|
|
45192
|
+
}
|
|
45193
|
+
function memoryChildSchema(memorySchema, child) {
|
|
45194
|
+
return unwrapObjectNode(memorySchema)?.shape?.[child];
|
|
45195
|
+
}
|
|
45196
|
+
function tierKnownKeys(memorySchema) {
|
|
45197
|
+
return {
|
|
45198
|
+
memory: objectShapeKeys(memorySchema),
|
|
45199
|
+
recall: objectShapeKeys(memoryChildSchema(memorySchema, "recall")),
|
|
45200
|
+
retain: objectShapeKeys(memoryChildSchema(memorySchema, "retain"))
|
|
45201
|
+
};
|
|
45202
|
+
}
|
|
45203
|
+
function isPlainObject2(v) {
|
|
45204
|
+
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
45205
|
+
}
|
|
45206
|
+
function diffScope(obj, scope, known, retired, out) {
|
|
45207
|
+
if (!isPlainObject2(obj))
|
|
45208
|
+
return;
|
|
45209
|
+
for (const key of Object.keys(obj)) {
|
|
45210
|
+
if (known.has(key))
|
|
45211
|
+
continue;
|
|
45212
|
+
out.push(key in retired ? { scope, key, replacement: retired[key] } : { scope, key });
|
|
45213
|
+
}
|
|
45214
|
+
}
|
|
45215
|
+
function findUnknownMemoryKeys(memory, tier = "agent") {
|
|
45216
|
+
if (!isPlainObject2(memory))
|
|
45217
|
+
return [];
|
|
45218
|
+
const known = KNOWN_BY_TIER[tier];
|
|
45219
|
+
const out = [];
|
|
45220
|
+
diffScope(memory, "memory", known.memory, RETIRED_MEMORY_KEYS, out);
|
|
45221
|
+
diffScope(memory.recall, "memory.recall", known.recall, RETIRED_MEMORY_RECALL_KEYS, out);
|
|
45222
|
+
diffScope(memory.retain, "memory.retain", known.retain, RETIRED_MEMORY_RETAIN_KEYS, out);
|
|
45223
|
+
return out.sort((a, b) => a.scope === b.scope ? a.key.localeCompare(b.key) : a.scope.localeCompare(b.scope));
|
|
45224
|
+
}
|
|
45225
|
+
var AGENT_KNOWN, MIRROR_KNOWN, KNOWN_BY_TIER, KNOWN_MEMORY_KEYS, KNOWN_MEMORY_RECALL_KEYS, KNOWN_MEMORY_RETAIN_KEYS, KNOWN_MIRROR_MEMORY_KEYS, KNOWN_MIRROR_MEMORY_RECALL_KEYS, KNOWN_MIRROR_MEMORY_RETAIN_KEYS, RETIRED_MEMORY_KEYS, RETIRED_MEMORY_RECALL_KEYS, RETIRED_MEMORY_RETAIN_KEYS;
|
|
45226
|
+
var init_memory_key_drift = __esm(() => {
|
|
45227
|
+
init_schema();
|
|
45228
|
+
AGENT_KNOWN = tierKnownKeys(AgentMemorySchema);
|
|
45229
|
+
MIRROR_KNOWN = tierKnownKeys(ProfileSchema.shape.memory);
|
|
45230
|
+
KNOWN_BY_TIER = {
|
|
45231
|
+
agent: AGENT_KNOWN,
|
|
45232
|
+
mirror: MIRROR_KNOWN
|
|
45233
|
+
};
|
|
45234
|
+
KNOWN_MEMORY_KEYS = AGENT_KNOWN.memory;
|
|
45235
|
+
KNOWN_MEMORY_RECALL_KEYS = AGENT_KNOWN.recall;
|
|
45236
|
+
KNOWN_MEMORY_RETAIN_KEYS = AGENT_KNOWN.retain;
|
|
45237
|
+
KNOWN_MIRROR_MEMORY_KEYS = MIRROR_KNOWN.memory;
|
|
45238
|
+
KNOWN_MIRROR_MEMORY_RECALL_KEYS = MIRROR_KNOWN.recall;
|
|
45239
|
+
KNOWN_MIRROR_MEMORY_RETAIN_KEYS = MIRROR_KNOWN.retain;
|
|
45240
|
+
RETIRED_MEMORY_KEYS = {};
|
|
45241
|
+
RETIRED_MEMORY_RECALL_KEYS = {
|
|
45242
|
+
min_overlap: "removed in #3761 \u2014 the lexical-overlap recall gate it tuned was deleted " + "outright, with no replacement floor; delete the line"
|
|
45243
|
+
};
|
|
45244
|
+
RETIRED_MEMORY_RETAIN_KEYS = {};
|
|
45245
|
+
});
|
|
45246
|
+
|
|
45070
45247
|
// src/cli/doctor-memory.ts
|
|
45071
45248
|
import { execFileSync as execFileSync19 } from "node:child_process";
|
|
45072
45249
|
function classifyDirectiveCount(count, bankLabel) {
|
|
@@ -45105,28 +45282,75 @@ function classifyShmSize(bytes) {
|
|
|
45105
45282
|
const gib = (bytes / 1024 / 1024 / 1024).toFixed(bytes % 1024 ** 3 === 0 ? 0 : 1);
|
|
45106
45283
|
return { name: "hindsight shm-size", status: "ok", detail: `${gib}g` };
|
|
45107
45284
|
}
|
|
45108
|
-
function
|
|
45285
|
+
function classifyAsyncOpQueue(queueDepth, oldestAgeSecs = null) {
|
|
45109
45286
|
const ageStr = oldestAgeSecs !== null && oldestAgeSecs > 0 ? `, oldest ${Math.round(oldestAgeSecs / 60)}m old` : "";
|
|
45110
|
-
const base = `${queueDepth} pending
|
|
45111
|
-
if (queueDepth >=
|
|
45287
|
+
const base = `${queueDepth} pending async op(s)${ageStr}`;
|
|
45288
|
+
if (queueDepth >= OP_QUEUE_FAIL) {
|
|
45289
|
+
return {
|
|
45290
|
+
name: "hindsight async op queue",
|
|
45291
|
+
status: "fail",
|
|
45292
|
+
detail: `${base} \u2014 the operation queue is deep enough to be wedged; ops are ` + `enqueued but not draining (a stuck reaper / claim stall)`,
|
|
45293
|
+
fix: "Check `docker logs switchroom-hindsight` for claim/reaper errors " + "(quota/429/shm, stuck 'processing' ops); restart with `switchroom " + "memory --restart` if the worker is stuck."
|
|
45294
|
+
};
|
|
45295
|
+
}
|
|
45296
|
+
if (queueDepth >= OP_QUEUE_WARN) {
|
|
45297
|
+
return {
|
|
45298
|
+
name: "hindsight async op queue",
|
|
45299
|
+
status: "warn",
|
|
45300
|
+
detail: `${base} \u2014 non-terminal ops are building faster than they drain; ` + `claims may be lagging (check the reaper before it wedges)`
|
|
45301
|
+
};
|
|
45302
|
+
}
|
|
45303
|
+
return {
|
|
45304
|
+
name: "hindsight async op queue",
|
|
45305
|
+
status: "ok",
|
|
45306
|
+
detail: queueDepth > 0 ? base : "queue drained (0 pending)"
|
|
45307
|
+
};
|
|
45308
|
+
}
|
|
45309
|
+
function classifyConsolidationBacklog(pendingConsolidation) {
|
|
45310
|
+
const base = `${pendingConsolidation} memory unit(s) pending consolidation`;
|
|
45311
|
+
if (pendingConsolidation >= CONSOLIDATION_BACKLOG_FAIL) {
|
|
45112
45312
|
return {
|
|
45113
45313
|
name: "hindsight consolidation backlog",
|
|
45114
45314
|
status: "fail",
|
|
45115
|
-
detail: `${base} \u2014
|
|
45116
|
-
fix: "Check `docker logs switchroom-hindsight` for
|
|
45315
|
+
detail: `${base} \u2014 consolidation has stalled far behind ingest; retained ` + `memory is landing but not reaching recall (drains ~7 ops/hr under ` + `the #2894 throttle)`,
|
|
45316
|
+
fix: "Check `docker logs switchroom-hindsight` for consolidation errors " + "(quota/429/shm, HNSW index faults) and the consolidation-failure-" + "streak signal; a bank legitimately mid-ingest can read deep, so " + "confirm the number is not draining before restarting."
|
|
45117
45317
|
};
|
|
45118
45318
|
}
|
|
45119
|
-
if (
|
|
45319
|
+
if (pendingConsolidation >= CONSOLIDATION_BACKLOG_WARN) {
|
|
45120
45320
|
return {
|
|
45121
45321
|
name: "hindsight consolidation backlog",
|
|
45122
45322
|
status: "warn",
|
|
45123
|
-
detail: `${base} \u2014 building faster than it drains (~7 ops/hr under the #2894 ` + `throttle); recall may lag recent turns until it catches up`
|
|
45323
|
+
detail: `${base} \u2014 building faster than it drains (~7 ops/hr under the #2894 ` + `throttle); recall may lag recent turns until it catches up (a bank ` + `mid-ingest can read deep \u2014 confirm it is draining)`
|
|
45124
45324
|
};
|
|
45125
45325
|
}
|
|
45126
45326
|
return {
|
|
45127
45327
|
name: "hindsight consolidation backlog",
|
|
45128
45328
|
status: "ok",
|
|
45129
|
-
detail:
|
|
45329
|
+
detail: pendingConsolidation > 0 ? base : "consolidation drained (0 pending)"
|
|
45330
|
+
};
|
|
45331
|
+
}
|
|
45332
|
+
function classifyBankConfigReachability(result, bankId) {
|
|
45333
|
+
const name = BANK_CONFIG_API_CHECK_NAME;
|
|
45334
|
+
if (result.ok) {
|
|
45335
|
+
return {
|
|
45336
|
+
name,
|
|
45337
|
+
status: "ok",
|
|
45338
|
+
detail: `retain_mission readable on bank ${bankId} \u2014 the fail-closed seed path (#3529) can read the field`
|
|
45339
|
+
};
|
|
45340
|
+
}
|
|
45341
|
+
if (result.reason === "Unexpected shape") {
|
|
45342
|
+
return {
|
|
45343
|
+
name,
|
|
45344
|
+
status: "fail",
|
|
45345
|
+
detail: `GET /v1/default/banks/${bankId}/config returned no retain_mission key \u2014 ` + `the bank-config API is disabled or the field was renamed/renested. ` + `Retain-mission seeding is fail-closed (#3529): with this read failing, ` + `apply seeds retain_mission for NO agent (including new ones) and still ` + `exits 0, so the fleet silently runs with unset retain missions.`,
|
|
45346
|
+
fix: "Confirm `HINDSIGHT_API_ENABLE_BANK_CONFIG_API` is true on the Hindsight " + "deployment (config.py default True) and that GET .../config still nests " + "`retain_mission` under `config`. If Hindsight renamed/renested the field, " + "update `fetchBankMissionField` in src/memory/hindsight.ts to match."
|
|
45347
|
+
};
|
|
45348
|
+
}
|
|
45349
|
+
return {
|
|
45350
|
+
name,
|
|
45351
|
+
status: "warn",
|
|
45352
|
+
detail: `could not probe the bank-config API on ${bankId}: ${result.reason} \u2014 ` + `retain-mission seeding is fail-closed (#3529) and can't be confirmed. If ` + `this persists it silently skips seeding for every agent.`,
|
|
45353
|
+
fix: "Check the hindsight container is up and `HINDSIGHT_API_ENABLE_BANK_CONFIG_API` " + "is enabled; re-run once it is reachable."
|
|
45130
45354
|
};
|
|
45131
45355
|
}
|
|
45132
45356
|
function classifyExtractionLogs(logs) {
|
|
@@ -45533,7 +45757,7 @@ function classifyToolContract(advertised) {
|
|
|
45533
45757
|
}
|
|
45534
45758
|
return results;
|
|
45535
45759
|
}
|
|
45536
|
-
var MIN_HINDSIGHT_SHM_BYTES, MAX_DIRECTIVES = 30, DIRECTIVE_WARN_THRESHOLD = 24,
|
|
45760
|
+
var MIN_HINDSIGHT_SHM_BYTES, MAX_DIRECTIVES = 30, DIRECTIVE_WARN_THRESHOLD = 24, OP_QUEUE_WARN = 25, OP_QUEUE_FAIL = 200, CONSOLIDATION_BACKLOG_WARN = 1000, CONSOLIDATION_BACKLOG_FAIL = 1e4, BANK_CONFIG_API_CHECK_NAME = "hindsight bank-config API";
|
|
45537
45761
|
var init_doctor_memory = __esm(() => {
|
|
45538
45762
|
init_hindsight_tools();
|
|
45539
45763
|
init_hindsight_repair();
|
|
@@ -45741,8 +45965,83 @@ var init_doctor_hnsw_index = __esm(() => {
|
|
|
45741
45965
|
});
|
|
45742
45966
|
|
|
45743
45967
|
// src/cli/doctor-observation-scopes.ts
|
|
45744
|
-
function
|
|
45745
|
-
|
|
45968
|
+
function fnmatchTranslate(pat) {
|
|
45969
|
+
const ANY = "[\\s\\S]";
|
|
45970
|
+
let res = "";
|
|
45971
|
+
let i = 0;
|
|
45972
|
+
const n = pat.length;
|
|
45973
|
+
while (i < n) {
|
|
45974
|
+
const c = pat[i];
|
|
45975
|
+
i += 1;
|
|
45976
|
+
if (c === "*") {
|
|
45977
|
+
res += ANY + "*";
|
|
45978
|
+
} else if (c === "?") {
|
|
45979
|
+
res += ANY;
|
|
45980
|
+
} else if (c === "[") {
|
|
45981
|
+
let j = i;
|
|
45982
|
+
if (j < n && pat[j] === "!")
|
|
45983
|
+
j += 1;
|
|
45984
|
+
if (j < n && pat[j] === "]")
|
|
45985
|
+
j += 1;
|
|
45986
|
+
while (j < n && pat[j] !== "]")
|
|
45987
|
+
j += 1;
|
|
45988
|
+
if (j >= n) {
|
|
45989
|
+
res += "\\[";
|
|
45990
|
+
} else {
|
|
45991
|
+
let stuff = pat.slice(i, j).replace(/\\/g, "\\\\");
|
|
45992
|
+
i = j + 1;
|
|
45993
|
+
if (stuff.startsWith("!"))
|
|
45994
|
+
stuff = "^" + stuff.slice(1);
|
|
45995
|
+
else if (stuff.startsWith("^") || stuff.startsWith("["))
|
|
45996
|
+
stuff = "\\" + stuff;
|
|
45997
|
+
res += "[" + stuff + "]";
|
|
45998
|
+
}
|
|
45999
|
+
} else {
|
|
46000
|
+
res += c.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
46001
|
+
}
|
|
46002
|
+
}
|
|
46003
|
+
return new RegExp("^(?:" + res + ")$");
|
|
46004
|
+
}
|
|
46005
|
+
function fnmatchcase(name, pat) {
|
|
46006
|
+
return fnmatchTranslate(pat).test(name);
|
|
46007
|
+
}
|
|
46008
|
+
function parseScopeLimitRules(raw) {
|
|
46009
|
+
if (!Array.isArray(raw))
|
|
46010
|
+
return [];
|
|
46011
|
+
const rules = [];
|
|
46012
|
+
for (const entry of raw) {
|
|
46013
|
+
if (entry == null || typeof entry !== "object")
|
|
46014
|
+
continue;
|
|
46015
|
+
const scope = entry.scope;
|
|
46016
|
+
const limit = entry.limit;
|
|
46017
|
+
if (!Array.isArray(scope) || scope.length === 0)
|
|
46018
|
+
continue;
|
|
46019
|
+
if (!scope.every((g) => typeof g === "string" && g.length > 0))
|
|
46020
|
+
continue;
|
|
46021
|
+
if (typeof limit !== "number" || !Number.isInteger(limit))
|
|
46022
|
+
continue;
|
|
46023
|
+
rules.push({ globs: scope, limit });
|
|
46024
|
+
}
|
|
46025
|
+
return rules;
|
|
46026
|
+
}
|
|
46027
|
+
function scopeMatchesGlobs(globs, tags) {
|
|
46028
|
+
if (tags.length === 0)
|
|
46029
|
+
return false;
|
|
46030
|
+
if (!tags.every((t) => globs.some((g) => fnmatchcase(t, g))))
|
|
46031
|
+
return false;
|
|
46032
|
+
if (!globs.every((g) => tags.some((t) => fnmatchcase(t, g))))
|
|
46033
|
+
return false;
|
|
46034
|
+
return true;
|
|
46035
|
+
}
|
|
46036
|
+
function effectiveScopeLimit(rules, tags, bankCap) {
|
|
46037
|
+
for (const rule of rules) {
|
|
46038
|
+
if (scopeMatchesGlobs(rule.globs, tags))
|
|
46039
|
+
return rule.limit;
|
|
46040
|
+
}
|
|
46041
|
+
return bankCap;
|
|
46042
|
+
}
|
|
46043
|
+
function computeScopeSaturation(scopes, cap, warnRatio = OBSERVATION_SCOPE_WARN_RATIO, scopeLimitRules = []) {
|
|
46044
|
+
if (cap < 0 && scopeLimitRules.length === 0)
|
|
45746
46045
|
return [];
|
|
45747
46046
|
const sets = scopes.map((s) => ({ tags: s.tags, set: new Set(s.tags), count: s.count }));
|
|
45748
46047
|
const postings = new Map;
|
|
@@ -45781,8 +46080,11 @@ function computeScopeSaturation(scopes, cap, warnRatio = OBSERVATION_SCOPE_WARN_
|
|
|
45781
46080
|
if (contains)
|
|
45782
46081
|
effective += other.count;
|
|
45783
46082
|
}
|
|
45784
|
-
const
|
|
45785
|
-
|
|
46083
|
+
const scopeCap = effectiveScopeLimit(scopeLimitRules, entry.tags, cap);
|
|
46084
|
+
if (scopeCap < 0)
|
|
46085
|
+
continue;
|
|
46086
|
+
const status = scopeCap > 0 && effective >= scopeCap ? "fail" : scopeCap > 0 && effective >= scopeCap * warnRatio ? "warn" : "ok";
|
|
46087
|
+
out.push({ tags: [...entry.tags], exact: entry.count, effective, cap: scopeCap, status });
|
|
45786
46088
|
}
|
|
45787
46089
|
out.sort((a, b) => b.effective - a.effective);
|
|
45788
46090
|
return out;
|
|
@@ -45806,17 +46108,17 @@ function classifyObservationScopeSaturation(reports) {
|
|
|
45806
46108
|
detail: `could not read observation scopes for ${unreadable.length} bank(s): ` + unreadable.map((r) => `${r.bankId} (${r.reason ?? "unknown"})`).join(", ")
|
|
45807
46109
|
};
|
|
45808
46110
|
}
|
|
45809
|
-
const unjudged = readable.filter((r) =>
|
|
45810
|
-
const judged = readable.filter((r) =>
|
|
46111
|
+
const unjudged = readable.filter((r) => !r.observationsEnabled);
|
|
46112
|
+
const judged = readable.filter((r) => r.observationsEnabled);
|
|
45811
46113
|
const notes = [];
|
|
45812
46114
|
if (unreadable.length > 0) {
|
|
45813
46115
|
notes.push(`${unreadable.length} bank(s) unreadable: ` + unreadable.map((r) => `${r.bankId} (${r.reason ?? "unknown"})`).join(", "));
|
|
45814
46116
|
}
|
|
45815
|
-
const withRules =
|
|
46117
|
+
const withRules = judged.filter((r) => r.hasScopeLimitRules);
|
|
45816
46118
|
if (withRules.length > 0) {
|
|
45817
|
-
notes.push(`${withRules.length} bank(s)
|
|
46119
|
+
notes.push(`${withRules.length} bank(s) judged with per-scope observation_scope_limits ` + `glob caps: ${withRules.map((r) => r.bankId).join(", ")}`);
|
|
45818
46120
|
}
|
|
45819
|
-
const disabled = unjudged.filter((r) => !r.
|
|
46121
|
+
const disabled = unjudged.filter((r) => !r.observationsEnabled);
|
|
45820
46122
|
if (disabled.length > 0) {
|
|
45821
46123
|
notes.push(`${disabled.length} bank(s) have observations disabled: ` + disabled.map((r) => r.bankId).join(", "));
|
|
45822
46124
|
}
|
|
@@ -45831,8 +46133,8 @@ function classifyObservationScopeSaturation(reports) {
|
|
|
45831
46133
|
const closed = judged.filter((r) => r.cap === 0);
|
|
45832
46134
|
const unlimited = judged.filter((r) => r.cap < 0);
|
|
45833
46135
|
const capped = judged.filter((r) => r.cap > 0);
|
|
45834
|
-
const failing =
|
|
45835
|
-
const warning =
|
|
46136
|
+
const failing = judged.flatMap((r) => r.saturated.filter((s) => s.status === "fail").map((s) => ({ report: r, scope: s })));
|
|
46137
|
+
const warning = judged.flatMap((r) => r.saturated.filter((s) => s.status === "warn").map((s) => ({ report: r, scope: s })));
|
|
45836
46138
|
failing.sort((a, b) => b.scope.effective - a.scope.effective);
|
|
45837
46139
|
warning.sort((a, b) => b.scope.effective - a.scope.effective);
|
|
45838
46140
|
const backlog = (rows) => {
|
|
@@ -45866,7 +46168,7 @@ function classifyObservationScopeSaturation(reports) {
|
|
|
45866
46168
|
const detail = warning.length > 0 ? scopeClause + growthSuffix + suffix : `${growthNote}${suffix}`;
|
|
45867
46169
|
return { name, status: "warn", detail, ...warning.length > 0 ? { fix } : {} };
|
|
45868
46170
|
}
|
|
45869
|
-
const totalScopes = capped.reduce((n, r) => n + r.
|
|
46171
|
+
const totalScopes = capped.reduce((n, r) => n + r.judgedScopeCount, 0);
|
|
45870
46172
|
const detailBase = capped.length === 0 ? `no bank has a positive observation cap` : `${totalScopes} scope(s) across ${capped.length} bank(s) below ` + `${Math.round(OBSERVATION_SCOPE_WARN_RATIO * 100)}% of the cap`;
|
|
45871
46173
|
const extra = [];
|
|
45872
46174
|
if (unlimited.length > 0) {
|
|
@@ -45881,24 +46183,6 @@ function classifyObservationScopeSaturation(reports) {
|
|
|
45881
46183
|
detail: detailBase + (extra.length > 0 ? ` \u00b7 ${extra.join(" \u00b7 ")}` : "") + suffix
|
|
45882
46184
|
};
|
|
45883
46185
|
}
|
|
45884
|
-
async function getJson3(url, opts) {
|
|
45885
|
-
const fetchImpl = opts?.fetchImpl ?? fetch;
|
|
45886
|
-
const timeoutMs = opts?.timeoutMs ?? 15000;
|
|
45887
|
-
const controller = new AbortController;
|
|
45888
|
-
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
|
45889
|
-
try {
|
|
45890
|
-
const resp = await fetchImpl(url, { signal: controller.signal });
|
|
45891
|
-
clearTimeout(timeout);
|
|
45892
|
-
if (!resp.ok)
|
|
45893
|
-
return { ok: false, reason: `HTTP ${resp.status}` };
|
|
45894
|
-
return { ok: true, data: await resp.json() };
|
|
45895
|
-
} catch (err) {
|
|
45896
|
-
clearTimeout(timeout);
|
|
45897
|
-
if (err.name === "AbortError")
|
|
45898
|
-
return { ok: false, reason: "Timeout" };
|
|
45899
|
-
return { ok: false, reason: String(err.message ?? err) };
|
|
45900
|
-
}
|
|
45901
|
-
}
|
|
45902
46186
|
async function inspectBankScopes(apiUrl, bankId, opts) {
|
|
45903
46187
|
const base = hindsightRestBase(apiUrl);
|
|
45904
46188
|
const bank = encodeURIComponent(bankId);
|
|
@@ -45910,11 +46194,12 @@ async function inspectBankScopes(apiUrl, bankId, opts) {
|
|
|
45910
46194
|
observationsEnabled: false,
|
|
45911
46195
|
hasScopeLimitRules: false,
|
|
45912
46196
|
scopeCount: 0,
|
|
46197
|
+
judgedScopeCount: 0,
|
|
45913
46198
|
untaggedCount: 0,
|
|
45914
46199
|
saturated: [],
|
|
45915
46200
|
pendingConsolidation: null
|
|
45916
46201
|
});
|
|
45917
|
-
const cfg = await
|
|
46202
|
+
const cfg = await getJson(`${base}/v1/default/banks/${bank}/config`, opts);
|
|
45918
46203
|
if (!cfg.ok)
|
|
45919
46204
|
return fail4(cfg.reason);
|
|
45920
46205
|
const config = cfg.data?.config;
|
|
@@ -45924,10 +46209,10 @@ async function inspectBankScopes(apiUrl, bankId, opts) {
|
|
|
45924
46209
|
if (typeof rawCap !== "number" || !Number.isFinite(rawCap)) {
|
|
45925
46210
|
return fail4("Unexpected shape");
|
|
45926
46211
|
}
|
|
45927
|
-
const
|
|
45928
|
-
const hasScopeLimitRules =
|
|
46212
|
+
const scopeLimitRules = parseScopeLimitRules(config["observation_scope_limits"]);
|
|
46213
|
+
const hasScopeLimitRules = scopeLimitRules.length > 0;
|
|
45929
46214
|
const observationsEnabled = config["enable_observations"] !== false;
|
|
45930
|
-
const scopesResp = await
|
|
46215
|
+
const scopesResp = await getJson(`${base}/v1/default/banks/${bank}/observations/scopes`, opts);
|
|
45931
46216
|
if (!scopesResp.ok)
|
|
45932
46217
|
return fail4(scopesResp.reason);
|
|
45933
46218
|
const rawScopes = scopesResp.data?.scopes;
|
|
@@ -45943,11 +46228,12 @@ async function inspectBankScopes(apiUrl, bankId, opts) {
|
|
|
45943
46228
|
continue;
|
|
45944
46229
|
scopes.push({ tags, count });
|
|
45945
46230
|
}
|
|
45946
|
-
const
|
|
46231
|
+
const allJudged = computeScopeSaturation(scopes, rawCap, OBSERVATION_SCOPE_WARN_RATIO, scopeLimitRules);
|
|
46232
|
+
const saturated = allJudged.filter((s) => s.status !== "ok");
|
|
45947
46233
|
const untaggedCount = scopes.filter((s) => s.tags.length === 0).reduce((n, s) => n + s.count, 0);
|
|
45948
46234
|
let pendingConsolidation = null;
|
|
45949
|
-
if (saturated.length > 0 &&
|
|
45950
|
-
const stats = await
|
|
46235
|
+
if (saturated.length > 0 && observationsEnabled) {
|
|
46236
|
+
const stats = await getJson(`${base}/v1/default/banks/${bank}/stats`, opts);
|
|
45951
46237
|
if (stats.ok && typeof stats.data?.pending_consolidation === "number") {
|
|
45952
46238
|
pendingConsolidation = stats.data.pending_consolidation;
|
|
45953
46239
|
}
|
|
@@ -45959,6 +46245,7 @@ async function inspectBankScopes(apiUrl, bankId, opts) {
|
|
|
45959
46245
|
observationsEnabled,
|
|
45960
46246
|
hasScopeLimitRules,
|
|
45961
46247
|
scopeCount: scopes.length,
|
|
46248
|
+
judgedScopeCount: allJudged.length,
|
|
45962
46249
|
untaggedCount,
|
|
45963
46250
|
saturated,
|
|
45964
46251
|
pendingConsolidation
|
|
@@ -45982,7 +46269,8 @@ var init_doctor_observation_scopes = __esm(() => {
|
|
|
45982
46269
|
});
|
|
45983
46270
|
|
|
45984
46271
|
// src/hindsight-watch/install-cron.ts
|
|
45985
|
-
import {
|
|
46272
|
+
import { execFileSync as execFileSync20 } from "node:child_process";
|
|
46273
|
+
import { chownSync as chownSync6, existsSync as existsSync63, mkdirSync as mkdirSync35, readFileSync as readFileSync60, renameSync as renameSync15, statSync as statSync38, writeFileSync as writeFileSync22 } from "node:fs";
|
|
45986
46274
|
import { dirname as dirname22 } from "node:path";
|
|
45987
46275
|
function renderCron(opts) {
|
|
45988
46276
|
return `# switchroom hindsight-watch \u2014 model-free memory watchdog.
|
|
@@ -46009,6 +46297,58 @@ function installCron(opts) {
|
|
|
46009
46297
|
renameSync15(tmp, path6);
|
|
46010
46298
|
return { status: "installed", path: path6, content };
|
|
46011
46299
|
}
|
|
46300
|
+
function ensureLogFile(opts) {
|
|
46301
|
+
const path6 = opts.path ?? CRON_LOG_PATH;
|
|
46302
|
+
if (existsSync63(path6))
|
|
46303
|
+
return { status: "unchanged", path: path6 };
|
|
46304
|
+
const { uid, gid } = (opts.resolveIds ?? defaultIdResolver)(opts.user);
|
|
46305
|
+
mkdirSync35(dirname22(path6), { recursive: true });
|
|
46306
|
+
writeFileSync22(path6, "", { mode: 420, flag: "a" });
|
|
46307
|
+
const isRoot = process.getuid?.() === 0;
|
|
46308
|
+
let alreadyOwned = false;
|
|
46309
|
+
try {
|
|
46310
|
+
const st = statSync38(path6);
|
|
46311
|
+
alreadyOwned = st.uid === uid && st.gid === gid;
|
|
46312
|
+
} catch {}
|
|
46313
|
+
if (isRoot && !alreadyOwned) {
|
|
46314
|
+
try {
|
|
46315
|
+
chownSync6(path6, uid, gid);
|
|
46316
|
+
} catch (e) {
|
|
46317
|
+
if (e.code !== "EPERM")
|
|
46318
|
+
throw e;
|
|
46319
|
+
}
|
|
46320
|
+
}
|
|
46321
|
+
return { status: "created", path: path6 };
|
|
46322
|
+
}
|
|
46323
|
+
function renderLogrotate(opts) {
|
|
46324
|
+
return `${opts.logPath} {
|
|
46325
|
+
` + ` weekly
|
|
46326
|
+
` + ` rotate 8
|
|
46327
|
+
` + ` compress
|
|
46328
|
+
` + ` delaycompress
|
|
46329
|
+
` + ` missingok
|
|
46330
|
+
` + ` notifempty
|
|
46331
|
+
` + ` copytruncate
|
|
46332
|
+
` + ` su ${opts.user} ${opts.user}
|
|
46333
|
+
` + `}
|
|
46334
|
+
`;
|
|
46335
|
+
}
|
|
46336
|
+
function installLogrotate(opts) {
|
|
46337
|
+
const path6 = opts.path ?? LOGROTATE_PATH;
|
|
46338
|
+
const content = renderLogrotate({ logPath: opts.logPath ?? CRON_LOG_PATH, user: opts.user });
|
|
46339
|
+
if (existsSync63(path6)) {
|
|
46340
|
+
try {
|
|
46341
|
+
if (readFileSync60(path6, "utf8") === content) {
|
|
46342
|
+
return { status: "unchanged", path: path6, content };
|
|
46343
|
+
}
|
|
46344
|
+
} catch {}
|
|
46345
|
+
}
|
|
46346
|
+
mkdirSync35(dirname22(path6), { recursive: true });
|
|
46347
|
+
const tmp = `${path6}.${process.pid}.tmp`;
|
|
46348
|
+
writeFileSync22(tmp, content, { mode: 420 });
|
|
46349
|
+
renameSync15(tmp, path6);
|
|
46350
|
+
return { status: "installed", path: path6, content };
|
|
46351
|
+
}
|
|
46012
46352
|
function parseCronUser(content) {
|
|
46013
46353
|
for (const line of content.split(`
|
|
46014
46354
|
`)) {
|
|
@@ -46041,7 +46381,14 @@ function reconcileCron(opts) {
|
|
|
46041
46381
|
content: r.content
|
|
46042
46382
|
};
|
|
46043
46383
|
}
|
|
46044
|
-
var CRON_PATH = "/etc/cron.d/hindsight-watch", CRON_SCHEDULE = "*/15 * * * *", CRON_LOG_PATH = "/var/log/hindsight-watch.log", CRON_LOCK_PATH = "/run/lock/hindsight-watch.lock"
|
|
46384
|
+
var CRON_PATH = "/etc/cron.d/hindsight-watch", CRON_SCHEDULE = "*/15 * * * *", CRON_LOG_PATH = "/var/log/hindsight-watch.log", LOGROTATE_PATH = "/etc/logrotate.d/hindsight-watch", CRON_LOCK_PATH = "/run/lock/hindsight-watch.lock", defaultIdResolver = (user) => {
|
|
46385
|
+
const uid = Number(execFileSync20("id", ["-u", user], { encoding: "utf8" }).trim());
|
|
46386
|
+
const gid = Number(execFileSync20("id", ["-g", user], { encoding: "utf8" }).trim());
|
|
46387
|
+
if (!Number.isInteger(uid) || !Number.isInteger(gid)) {
|
|
46388
|
+
throw new Error(`could not resolve uid/gid for user ${JSON.stringify(user)}`);
|
|
46389
|
+
}
|
|
46390
|
+
return { uid, gid };
|
|
46391
|
+
};
|
|
46045
46392
|
var init_install_cron = () => {};
|
|
46046
46393
|
|
|
46047
46394
|
// src/hindsight-watch/thresholds.ts
|
|
@@ -46190,7 +46537,7 @@ var init_state2 = __esm(() => {
|
|
|
46190
46537
|
});
|
|
46191
46538
|
|
|
46192
46539
|
// src/cli/doctor-hindsight-watch.ts
|
|
46193
|
-
import { existsSync as existsSync64, statSync as
|
|
46540
|
+
import { existsSync as existsSync64, statSync as statSync39 } from "node:fs";
|
|
46194
46541
|
function classifyWatchArmed(cronPresent, stateMtimeMs, now) {
|
|
46195
46542
|
const name = "hindsight-watch armed";
|
|
46196
46543
|
if (stateMtimeMs === null) {
|
|
@@ -46228,7 +46575,7 @@ function classifyWatchArmed(cronPresent, stateMtimeMs, now) {
|
|
|
46228
46575
|
function checkHindsightWatchArmed(now = Date.now(), cronPath = CRON_PATH, statePath = defaultStatePath2()) {
|
|
46229
46576
|
let mtime = null;
|
|
46230
46577
|
try {
|
|
46231
|
-
mtime =
|
|
46578
|
+
mtime = statSync39(statePath).mtimeMs;
|
|
46232
46579
|
} catch {}
|
|
46233
46580
|
return classifyWatchArmed(existsSync64(cronPath), mtime, now);
|
|
46234
46581
|
}
|
|
@@ -46789,10 +47136,11 @@ function requiredKeyModels(config, proxyModels) {
|
|
|
46789
47136
|
apiKeyRef: ref && ref.length > 0 ? ref : null
|
|
46790
47137
|
});
|
|
46791
47138
|
};
|
|
46792
|
-
|
|
46793
|
-
consider(llm.
|
|
46794
|
-
consider(llm.
|
|
46795
|
-
consider(llm.
|
|
47139
|
+
const globalKey = llm.api_key;
|
|
47140
|
+
consider(llm.model, llm.provider, globalKey, "hindsight.llm.model (global)");
|
|
47141
|
+
consider(llm.retain?.model, llm.retain?.provider, llm.retain?.api_key ?? globalKey, "hindsight.llm.retain");
|
|
47142
|
+
consider(llm.reflect?.model, llm.reflect?.provider, llm.reflect?.api_key ?? globalKey, "hindsight.llm.reflect");
|
|
47143
|
+
consider(llm.consolidation?.model, llm.consolidation?.provider, llm.consolidation?.api_key ?? globalKey, "hindsight.llm.consolidation");
|
|
46796
47144
|
return out;
|
|
46797
47145
|
}
|
|
46798
47146
|
function classifyAllowlist(models) {
|
|
@@ -47467,7 +47815,7 @@ import {
|
|
|
47467
47815
|
constants as fsConstants4,
|
|
47468
47816
|
existsSync as existsSync66,
|
|
47469
47817
|
realpathSync as realpathSync5,
|
|
47470
|
-
statSync as
|
|
47818
|
+
statSync as statSync40
|
|
47471
47819
|
} from "node:fs";
|
|
47472
47820
|
import { userInfo, homedir as homedir33 } from "node:os";
|
|
47473
47821
|
import { join as join61 } from "node:path";
|
|
@@ -47485,7 +47833,7 @@ function defaultStatVault(path6) {
|
|
|
47485
47833
|
let uid = -1;
|
|
47486
47834
|
let mode = 0;
|
|
47487
47835
|
try {
|
|
47488
|
-
const s =
|
|
47836
|
+
const s = statSync40(real);
|
|
47489
47837
|
uid = s.uid;
|
|
47490
47838
|
mode = s.mode & 511;
|
|
47491
47839
|
} catch {
|
|
@@ -49648,7 +49996,7 @@ __export(exports_voice_sidecar_token, {
|
|
|
49648
49996
|
VOICE_SIDECAR_TOKEN_ENV: () => VOICE_SIDECAR_TOKEN_ENV
|
|
49649
49997
|
});
|
|
49650
49998
|
import { randomBytes as randomBytes12 } from "node:crypto";
|
|
49651
|
-
import { chmodSync as chmodSync13, chownSync as
|
|
49999
|
+
import { chmodSync as chmodSync13, chownSync as chownSync7, existsSync as existsSync69, mkdirSync as mkdirSync40, readFileSync as readFileSync67, rmSync as rmSync12, writeFileSync as writeFileSync27 } from "node:fs";
|
|
49652
50000
|
import { dirname as dirname27 } from "node:path";
|
|
49653
50001
|
async function defaultResolveOrSeedToken(home2, writeErr) {
|
|
49654
50002
|
const [{ getViaBrokerStructured: getViaBrokerStructured2, putViaBroker: putViaBroker2 }, { resolveOperatorVaultPassphrase }] = await Promise.all([
|
|
@@ -49722,7 +50070,7 @@ async function provisionVoiceSidecarToken(composePath, home2, ctx) {
|
|
|
49722
50070
|
chmodSync13(envPath, 384);
|
|
49723
50071
|
if (ctx.operatorUid !== undefined && process.geteuid?.() === 0) {
|
|
49724
50072
|
try {
|
|
49725
|
-
|
|
50073
|
+
chownSync7(envPath, ctx.operatorUid, ctx.operatorUid);
|
|
49726
50074
|
} catch {}
|
|
49727
50075
|
}
|
|
49728
50076
|
ctx.writeOut(source_default.gray(` Wrote voice-sidecar token to ${envPath} (0600)
|
|
@@ -49769,13 +50117,13 @@ __export(exports_apply, {
|
|
|
49769
50117
|
DEFAULT_COMPOSE_PATH: () => DEFAULT_COMPOSE_PATH,
|
|
49770
50118
|
COMPOSE_PROJECT: () => COMPOSE_PROJECT2
|
|
49771
50119
|
});
|
|
49772
|
-
import { accessSync as accessSync2, chmodSync as chmodSync14, chownSync as
|
|
50120
|
+
import { accessSync as accessSync2, chmodSync as chmodSync14, chownSync as chownSync8, constants as fsConstants5, copyFileSync as copyFileSync10, existsSync as existsSync70, mkdirSync as mkdirSync41, readFileSync as readFileSync68, readdirSync as readdirSync24, renameSync as renameSync17, statSync as statSync41, writeFileSync as writeFileSync28 } from "node:fs";
|
|
49773
50121
|
import { mkdir as mkdir2 } from "node:fs/promises";
|
|
49774
50122
|
import { spawnSync as childSpawnSync } from "node:child_process";
|
|
49775
50123
|
import readline from "node:readline";
|
|
49776
50124
|
import { dirname as dirname28, join as join69, resolve as resolve40 } from "node:path";
|
|
49777
50125
|
import { homedir as homedir37 } from "node:os";
|
|
49778
|
-
import { execFileSync as
|
|
50126
|
+
import { execFileSync as execFileSync21 } from "node:child_process";
|
|
49779
50127
|
function effectiveLiteLLMEnabled(config, agentResolvedLitellm) {
|
|
49780
50128
|
return agentResolvedLitellm?.enabled ?? config.litellm?.enabled ?? false;
|
|
49781
50129
|
}
|
|
@@ -50074,7 +50422,7 @@ async function provisionLiteLLMKeys(config, agentNames, switchroomConfigPath, ct
|
|
|
50074
50422
|
try {
|
|
50075
50423
|
let cfgMode = 420;
|
|
50076
50424
|
try {
|
|
50077
|
-
cfgMode =
|
|
50425
|
+
cfgMode = statSync41(switchroomConfigPath).mode & 511;
|
|
50078
50426
|
} catch {}
|
|
50079
50427
|
writeConfigFileSync(switchroomConfigPath, configText, cfgMode);
|
|
50080
50428
|
writeOut(source_default.gray(` ~ litellm: granted read-ACL on per-agent keys (updated ${switchroomConfigPath})
|
|
@@ -50296,7 +50644,7 @@ async function ensureHostMountSources(config) {
|
|
|
50296
50644
|
}
|
|
50297
50645
|
try {
|
|
50298
50646
|
const uid = allocateAgentUid(name);
|
|
50299
|
-
|
|
50647
|
+
chownSync8(tokenPath, uid, uid);
|
|
50300
50648
|
} catch {}
|
|
50301
50649
|
}
|
|
50302
50650
|
const fleetDir = join69(home2, ".switchroom", "fleet");
|
|
@@ -50316,7 +50664,7 @@ async function ensureHostMountSources(config) {
|
|
|
50316
50664
|
}
|
|
50317
50665
|
function detectComposeV2() {
|
|
50318
50666
|
try {
|
|
50319
|
-
const out =
|
|
50667
|
+
const out = execFileSync21("docker", ["compose", "version"], {
|
|
50320
50668
|
stdio: ["ignore", "pipe", "pipe"],
|
|
50321
50669
|
encoding: "utf8"
|
|
50322
50670
|
});
|
|
@@ -51104,7 +51452,7 @@ var init_apply = __esm(() => {
|
|
|
51104
51452
|
});
|
|
51105
51453
|
|
|
51106
51454
|
// src/agents/drift.ts
|
|
51107
|
-
import { execFileSync as
|
|
51455
|
+
import { execFileSync as execFileSync22 } from "node:child_process";
|
|
51108
51456
|
import { createHash as createHash15 } from "node:crypto";
|
|
51109
51457
|
import {
|
|
51110
51458
|
existsSync as existsSync71,
|
|
@@ -51439,7 +51787,7 @@ function detectAgentDrift(name, agentConfigRaw, agentsDir, config, configPath, o
|
|
|
51439
51787
|
}
|
|
51440
51788
|
return findings;
|
|
51441
51789
|
}
|
|
51442
|
-
var BUNDLED_POOL_SEGMENT = "/.switchroom/skills/_bundled/", defaultExec = (cmd, args) =>
|
|
51790
|
+
var BUNDLED_POOL_SEGMENT = "/.switchroom/skills/_bundled/", defaultExec = (cmd, args) => execFileSync22(cmd, args, {
|
|
51443
51791
|
encoding: "utf-8",
|
|
51444
51792
|
stdio: ["ignore", "pipe", "ignore"],
|
|
51445
51793
|
timeout: 1e4
|
|
@@ -52421,7 +52769,7 @@ function runCredentialsMigrationChecks(config, deps = {}) {
|
|
|
52421
52769
|
var init_doctor_credentials_migration = () => {};
|
|
52422
52770
|
|
|
52423
52771
|
// src/cli/doctor-agent-dotfile-ownership.ts
|
|
52424
|
-
import { existsSync as existsSync73, lstatSync as lstatSync10, statSync as
|
|
52772
|
+
import { existsSync as existsSync73, lstatSync as lstatSync10, statSync as statSync42 } from "node:fs";
|
|
52425
52773
|
import { join as join75 } from "node:path";
|
|
52426
52774
|
function agentDotfileCandidates(agentDir) {
|
|
52427
52775
|
return [
|
|
@@ -52438,7 +52786,7 @@ function runAgentDotfileOwnershipChecks(config, deps = {}) {
|
|
|
52438
52786
|
if (agents.length === 0)
|
|
52439
52787
|
return results;
|
|
52440
52788
|
const exists = deps.existsSync ?? existsSync73;
|
|
52441
|
-
const scanFs = deps.scanFs ?? { lstatSync: lstatSync10, statSync:
|
|
52789
|
+
const scanFs = deps.scanFs ?? { lstatSync: lstatSync10, statSync: statSync42 };
|
|
52442
52790
|
let agentsDir = deps.agentsDir;
|
|
52443
52791
|
if (agentsDir === undefined) {
|
|
52444
52792
|
try {
|
|
@@ -52583,7 +52931,7 @@ var init_doctor_inlined_secrets = __esm(() => {
|
|
|
52583
52931
|
});
|
|
52584
52932
|
|
|
52585
52933
|
// src/cli/doctor-approval-attribution.ts
|
|
52586
|
-
import { execFileSync as
|
|
52934
|
+
import { execFileSync as execFileSync23 } from "node:child_process";
|
|
52587
52935
|
import { readFileSync as fsReadFileSync2 } from "node:fs";
|
|
52588
52936
|
import { join as join76 } from "node:path";
|
|
52589
52937
|
function parseApproverSet(canonical) {
|
|
@@ -52685,7 +53033,7 @@ async function defaultLoadDecisions() {
|
|
|
52685
53033
|
} catch {}
|
|
52686
53034
|
try {
|
|
52687
53035
|
const SEP2 = String.fromCharCode(31);
|
|
52688
|
-
const stdout =
|
|
53036
|
+
const stdout = execFileSync23("docker", [
|
|
52689
53037
|
"exec",
|
|
52690
53038
|
"switchroom-vault-broker",
|
|
52691
53039
|
"sqlite3",
|
|
@@ -52957,8 +53305,8 @@ var init_doctor_agent_smoke = __esm(() => {
|
|
|
52957
53305
|
});
|
|
52958
53306
|
|
|
52959
53307
|
// src/cli/doctor-vault-broker-durability.ts
|
|
52960
|
-
import { execFileSync as
|
|
52961
|
-
import { existsSync as existsSync75, statSync as
|
|
53308
|
+
import { execFileSync as execFileSync24 } from "node:child_process";
|
|
53309
|
+
import { existsSync as existsSync75, statSync as statSync43, readFileSync as readFileSync70 } from "node:fs";
|
|
52962
53310
|
import { homedir as homedir43 } from "node:os";
|
|
52963
53311
|
import { join as join79 } from "node:path";
|
|
52964
53312
|
import { createRequire as createRequire2 } from "node:module";
|
|
@@ -52986,7 +53334,7 @@ function defaultStatHost(p) {
|
|
|
52986
53334
|
if (!existsSync75(p))
|
|
52987
53335
|
return null;
|
|
52988
53336
|
try {
|
|
52989
|
-
const s =
|
|
53337
|
+
const s = statSync43(p, { bigint: true });
|
|
52990
53338
|
return { ino: s.ino, size: Number(s.size) };
|
|
52991
53339
|
} catch {
|
|
52992
53340
|
return null;
|
|
@@ -53020,7 +53368,7 @@ function spawnDockerStat(p) {
|
|
|
53020
53368
|
}
|
|
53021
53369
|
function spawnDockerStatForContainer(containerName2, p) {
|
|
53022
53370
|
try {
|
|
53023
|
-
const stdout =
|
|
53371
|
+
const stdout = execFileSync24("docker", ["exec", containerName2, "stat", "-c", "%i %s", p], { stdio: ["ignore", "pipe", "pipe"], timeout: 3000, encoding: "utf8" });
|
|
53024
53372
|
return { status: 0, stdout, stderr: "", error: null };
|
|
53025
53373
|
} catch (err) {
|
|
53026
53374
|
const e = err;
|
|
@@ -53094,7 +53442,7 @@ function probeBrokerUnlocked(opts) {
|
|
|
53094
53442
|
}
|
|
53095
53443
|
function defaultBrokerStatusProbe() {
|
|
53096
53444
|
try {
|
|
53097
|
-
const out =
|
|
53445
|
+
const out = execFileSync24("switchroom", ["vault", "broker", "status"], { stdio: ["ignore", "pipe", "pipe"], timeout: 3000, encoding: "utf8" });
|
|
53098
53446
|
const parsed = JSON.parse(out.trim());
|
|
53099
53447
|
if (!parsed.running)
|
|
53100
53448
|
return null;
|
|
@@ -53195,7 +53543,7 @@ function defaultKernelStatBroker(p) {
|
|
|
53195
53543
|
}
|
|
53196
53544
|
function defaultWalStatBroker(p) {
|
|
53197
53545
|
try {
|
|
53198
|
-
const stdout =
|
|
53546
|
+
const stdout = execFileSync24("docker", ["exec", "switchroom-vault-broker", "stat", "-c", "%Y %s", p], { stdio: ["ignore", "pipe", "pipe"], timeout: 3000, encoding: "utf8" });
|
|
53199
53547
|
const [mStr, sStr] = stdout.trim().split(/\s+/);
|
|
53200
53548
|
const mtime = Number(mStr);
|
|
53201
53549
|
const size = Number(sStr);
|
|
@@ -53324,7 +53672,7 @@ function probeOrphanVaultTokens(config, opts) {
|
|
|
53324
53672
|
}
|
|
53325
53673
|
function defaultTokenMtimeMs(agent, home2) {
|
|
53326
53674
|
try {
|
|
53327
|
-
return
|
|
53675
|
+
return statSync43(join79(home2, ".switchroom", "agents", agent, ".vault-token")).mtimeMs;
|
|
53328
53676
|
} catch {
|
|
53329
53677
|
return null;
|
|
53330
53678
|
}
|
|
@@ -53372,7 +53720,7 @@ function probeAutoUnlockBlob(home2) {
|
|
|
53372
53720
|
fix: "Run `switchroom vault broker enable-auto-unlock` to seal the blob with the current passphrase + machine-id"
|
|
53373
53721
|
};
|
|
53374
53722
|
}
|
|
53375
|
-
const sz =
|
|
53723
|
+
const sz = statSync43(blobPath).size;
|
|
53376
53724
|
if (sz === 0) {
|
|
53377
53725
|
return {
|
|
53378
53726
|
name: "vault-broker: auto-unlock blob",
|
|
@@ -53728,6 +54076,7 @@ __export(exports_doctor, {
|
|
|
53728
54076
|
checkMffAuthFlow: () => checkMffAuthFlow,
|
|
53729
54077
|
checkMffApiReachable: () => checkMffApiReachable,
|
|
53730
54078
|
checkMff: () => checkMff,
|
|
54079
|
+
checkMemoryKeysAreKnown: () => checkMemoryKeysAreKnown,
|
|
53731
54080
|
checkManifestDrift: () => checkManifestDrift,
|
|
53732
54081
|
checkLegacyState: () => checkLegacyState,
|
|
53733
54082
|
checkLeakedHomeSwitchroom: () => checkLeakedHomeSwitchroom,
|
|
@@ -53739,6 +54088,7 @@ __export(exports_doctor, {
|
|
|
53739
54088
|
checkConfig: () => checkConfig,
|
|
53740
54089
|
checkBankObservationsMissions: () => checkBankObservationsMissions,
|
|
53741
54090
|
checkBankIngestHealth: () => checkBankIngestHealth,
|
|
54091
|
+
checkBankConfigApiReachable: () => checkBankConfigApiReachable,
|
|
53742
54092
|
checkAgents: () => checkAgents,
|
|
53743
54093
|
buildPendingRetainsProbeScript: () => buildPendingRetainsProbeScript,
|
|
53744
54094
|
PENDING_RETAINS_EVICTION_WINDOW_DAYS: () => PENDING_RETAINS_EVICTION_WINDOW_DAYS,
|
|
@@ -53754,7 +54104,7 @@ import {
|
|
|
53754
54104
|
mkdirSync as mkdirSync42,
|
|
53755
54105
|
readFileSync as readFileSync72,
|
|
53756
54106
|
readdirSync as readdirSync26,
|
|
53757
|
-
statSync as
|
|
54107
|
+
statSync as statSync44
|
|
53758
54108
|
} from "node:fs";
|
|
53759
54109
|
import { dirname as dirname31, join as join82, resolve as resolve44 } from "node:path";
|
|
53760
54110
|
import { createPublicKey, createPrivateKey } from "node:crypto";
|
|
@@ -53781,7 +54131,7 @@ function findInNvm(bin) {
|
|
|
53781
54131
|
for (const v of versions) {
|
|
53782
54132
|
const candidate = join82(nvmRoot, v, "bin", bin);
|
|
53783
54133
|
try {
|
|
53784
|
-
const s =
|
|
54134
|
+
const s = statSync44(candidate);
|
|
53785
54135
|
if (s.isFile() || s.isSymbolicLink()) {
|
|
53786
54136
|
return candidate;
|
|
53787
54137
|
}
|
|
@@ -54074,8 +54424,68 @@ function checkConfig(config, configPath) {
|
|
|
54074
54424
|
detail: effortRisks.length > 0 ? `${effortRisks.length} agent(s) on pinned Opus 4.x with thinking_effort > low: ${effortRisks.join(", ")}` : "no risky model/effort combos",
|
|
54075
54425
|
fix: effortRisks.length > 0 ? "Pin `thinking_effort: low` for pinned Opus 4.x agents, or move them to a current Opus model. medium+ could 400 on 'thinking blocks cannot be modified' with concurrent sub-agents (issue #1978); the upstream fix shipped in claude-code " + `${CLAUDE_CLI_THINKING_MERGE_FIX_VERSION} but the Opus 4.x reproduction was never re-tested. Removing the field is NOT a fix (Opus defaults effort=high when unset).` : undefined
|
|
54076
54426
|
});
|
|
54427
|
+
results.push(checkMemoryKeysAreKnown(configPath));
|
|
54077
54428
|
return results;
|
|
54078
54429
|
}
|
|
54430
|
+
function checkMemoryKeysAreKnown(configPath) {
|
|
54431
|
+
const name = "memory config keys";
|
|
54432
|
+
let rawText;
|
|
54433
|
+
try {
|
|
54434
|
+
rawText = readFileSync72(configPath, "utf-8");
|
|
54435
|
+
} catch {
|
|
54436
|
+
return { name, status: "ok", detail: "config unreadable \u2014 skipped (see load row)" };
|
|
54437
|
+
}
|
|
54438
|
+
let parsed;
|
|
54439
|
+
try {
|
|
54440
|
+
parsed = import_yaml19.parse(rawText);
|
|
54441
|
+
} catch {
|
|
54442
|
+
return { name, status: "ok", detail: "config unparseable \u2014 skipped (see load row)" };
|
|
54443
|
+
}
|
|
54444
|
+
if (typeof parsed !== "object" || parsed === null) {
|
|
54445
|
+
return { name, status: "ok", detail: "no memory config to check" };
|
|
54446
|
+
}
|
|
54447
|
+
const root = parsed;
|
|
54448
|
+
const sources = [];
|
|
54449
|
+
const asObj = (v) => typeof v === "object" && v !== null && !Array.isArray(v) ? v : undefined;
|
|
54450
|
+
const defaults = asObj(root.defaults);
|
|
54451
|
+
if (defaults?.memory !== undefined)
|
|
54452
|
+
sources.push({ label: "defaults", memory: defaults.memory, tier: "mirror" });
|
|
54453
|
+
const profiles = asObj(root.profiles);
|
|
54454
|
+
for (const [pname, pval] of Object.entries(profiles ?? {})) {
|
|
54455
|
+
const p = asObj(pval);
|
|
54456
|
+
if (p?.memory !== undefined)
|
|
54457
|
+
sources.push({ label: `profile ${pname}`, memory: p.memory, tier: "mirror" });
|
|
54458
|
+
}
|
|
54459
|
+
const agents = asObj(root.agents);
|
|
54460
|
+
for (const [aname, aval] of Object.entries(agents ?? {})) {
|
|
54461
|
+
const a = asObj(aval);
|
|
54462
|
+
if (a?.memory !== undefined)
|
|
54463
|
+
sources.push({ label: aname, memory: a.memory, tier: "agent" });
|
|
54464
|
+
}
|
|
54465
|
+
const lines = [];
|
|
54466
|
+
let retiredCount = 0;
|
|
54467
|
+
for (const { label, memory, tier } of sources) {
|
|
54468
|
+
for (const finding of findUnknownMemoryKeys(memory, tier)) {
|
|
54469
|
+
if (finding.replacement) {
|
|
54470
|
+
retiredCount++;
|
|
54471
|
+
lines.push(`${label}: \`${finding.scope}.${finding.key}\` \u2014 ${finding.replacement}`);
|
|
54472
|
+
} else {
|
|
54473
|
+
lines.push(`${label}: \`${finding.scope}.${finding.key}\` is not a recognized key \u2014 silently ignored`);
|
|
54474
|
+
}
|
|
54475
|
+
}
|
|
54476
|
+
}
|
|
54477
|
+
if (lines.length === 0) {
|
|
54478
|
+
return { name, status: "ok", detail: "all `memory` keys are recognized" };
|
|
54479
|
+
}
|
|
54480
|
+
return {
|
|
54481
|
+
name,
|
|
54482
|
+
status: "fail",
|
|
54483
|
+
detail: `${lines.length} unrecognized \`memory\` key(s)` + (retiredCount > 0 ? ` (${retiredCount} removed knob(s))` : "") + ` \u2014 silently stripped at parse time, so the line reads as set but does nothing:
|
|
54484
|
+
` + lines.join(`
|
|
54485
|
+
`),
|
|
54486
|
+
fix: "Remove or fix the key(s). A removed knob is gone for good \u2014 delete the " + "line; a typo must match a declared key. The recognized keys live on " + "`AgentMemorySchema` (and its `recall`/`retain`) in src/config/schema.ts."
|
|
54487
|
+
};
|
|
54488
|
+
}
|
|
54079
54489
|
function checkUserDeclaredMcps(name, agentConfig, config, renderedMcpServers) {
|
|
54080
54490
|
const resolved = resolveAgentConfig(config.defaults, config.profiles, agentConfig);
|
|
54081
54491
|
const declaredMcp = resolved.mcp_servers ?? {};
|
|
@@ -54377,7 +54787,7 @@ function checkHostCapabilitiesReadable(config, read = readHostCapabilities()) {
|
|
|
54377
54787
|
const proven = voice?.gpuPresent === true && voice?.containerToolkit === true;
|
|
54378
54788
|
let mode;
|
|
54379
54789
|
try {
|
|
54380
|
-
mode =
|
|
54790
|
+
mode = statSync44(read.path).mode & 511;
|
|
54381
54791
|
} catch {
|
|
54382
54792
|
mode = undefined;
|
|
54383
54793
|
}
|
|
@@ -54467,7 +54877,8 @@ async function checkBankObservationsMissions(config, url, opts) {
|
|
|
54467
54877
|
detail: `could not read observations_mission: ${read.reason}`
|
|
54468
54878
|
};
|
|
54469
54879
|
}
|
|
54470
|
-
const
|
|
54880
|
+
const memoryProfile = resolveMemoryProfile(entry.config);
|
|
54881
|
+
const profileDefault = PROFILE_MEMORY_DEFAULTS[memoryProfile]?.observations_mission;
|
|
54471
54882
|
const decision = decideObservationsMissionUpgrade(entry.config.memory?.observations_mission, profileDefault, read.mission);
|
|
54472
54883
|
if (decision.action === "upgrade") {
|
|
54473
54884
|
return {
|
|
@@ -54489,10 +54900,22 @@ async function checkBankObservationsMissions(config, url, opts) {
|
|
|
54489
54900
|
return {
|
|
54490
54901
|
name: label,
|
|
54491
54902
|
status: "ok",
|
|
54492
|
-
detail: isSwitchroomDefault ? profileDefault != null ? `switchroom ${
|
|
54903
|
+
detail: isSwitchroomDefault ? profileDefault != null ? `switchroom ${memoryProfile}-profile default` : "switchroom fleet default" : "operator-authored (left untouched by the never-clobber rule)"
|
|
54493
54904
|
};
|
|
54494
54905
|
});
|
|
54495
54906
|
}
|
|
54907
|
+
async function checkBankConfigApiReachable(config, url, opts) {
|
|
54908
|
+
let bankId;
|
|
54909
|
+
for (const [agentName, agentConfig] of Object.entries(config.agents)) {
|
|
54910
|
+
const resolved = resolveAgentConfig(config.defaults, config.profiles, agentConfig);
|
|
54911
|
+
bankId = resolved.memory?.collection ?? agentName;
|
|
54912
|
+
break;
|
|
54913
|
+
}
|
|
54914
|
+
if (bankId === undefined)
|
|
54915
|
+
return [];
|
|
54916
|
+
const read = await fetchBankRetainMission(url, bankId, { fetchImpl: opts?.fetchImpl });
|
|
54917
|
+
return [classifyBankConfigReachability(read, bankId)];
|
|
54918
|
+
}
|
|
54496
54919
|
async function checkBankIngestHealth(config, url, opts) {
|
|
54497
54920
|
const results = [];
|
|
54498
54921
|
const now = opts?.now ?? new Date;
|
|
@@ -54507,18 +54930,22 @@ async function checkBankIngestHealth(config, url, opts) {
|
|
|
54507
54930
|
banks.set(bank, []);
|
|
54508
54931
|
}
|
|
54509
54932
|
const inspected = await Promise.all([...banks].map(async ([bankId, agents]) => [bankId, agents, await inspectBankHealth(url, bankId, { fetchImpl: opts?.fetchImpl })]));
|
|
54510
|
-
|
|
54933
|
+
const backlogRows = [];
|
|
54511
54934
|
if (opts?.includeConsolidationBacklog) {
|
|
54512
|
-
let
|
|
54935
|
+
let totalPendingOps = 0;
|
|
54936
|
+
let totalPendingConsolidation = 0;
|
|
54513
54937
|
let anyBankReachable = false;
|
|
54514
54938
|
for (const [, , h] of inspected) {
|
|
54515
54939
|
if (h.ok) {
|
|
54516
54940
|
anyBankReachable = true;
|
|
54517
|
-
|
|
54941
|
+
totalPendingOps += h.pendingOperations;
|
|
54942
|
+
totalPendingConsolidation += h.pendingConsolidation;
|
|
54518
54943
|
}
|
|
54519
54944
|
}
|
|
54520
|
-
if (anyBankReachable)
|
|
54521
|
-
|
|
54945
|
+
if (anyBankReachable) {
|
|
54946
|
+
backlogRows.push(classifyAsyncOpQueue(totalPendingOps));
|
|
54947
|
+
backlogRows.push(classifyConsolidationBacklog(totalPendingConsolidation));
|
|
54948
|
+
}
|
|
54522
54949
|
}
|
|
54523
54950
|
for (const [bankId, agents, h] of inspected) {
|
|
54524
54951
|
const label = `bank ${bankId}` + (profileBanks.has(bankId) ? " (profile)" : agents[0] !== bankId ? ` (${agents.join(", ")})` : "");
|
|
@@ -54584,8 +55011,7 @@ async function checkBankIngestHealth(config, url, opts) {
|
|
|
54584
55011
|
}
|
|
54585
55012
|
results.push({ name: label, status: "ok", detail: summary });
|
|
54586
55013
|
}
|
|
54587
|
-
|
|
54588
|
-
results.push(backlogRow);
|
|
55014
|
+
results.push(...backlogRows);
|
|
54589
55015
|
return results;
|
|
54590
55016
|
}
|
|
54591
55017
|
function hindsightDocReprocessUrl(mcpUrl, bankId, docId) {
|
|
@@ -54653,6 +55079,7 @@ async function checkHindsight(config) {
|
|
|
54653
55079
|
results.push(await checkHindsightHealthEndpoint(url));
|
|
54654
55080
|
results.push(...await checkBankIngestHealth(config, url, { includeConsolidationBacklog: true }));
|
|
54655
55081
|
results.push(...await checkBankObservationsMissions(config, url));
|
|
55082
|
+
results.push(...await checkBankConfigApiReachable(config, url));
|
|
54656
55083
|
results.push(await checkObservationScopeSaturation(config, url));
|
|
54657
55084
|
const memoryAgentsDir = resolveAgentsDir(config);
|
|
54658
55085
|
for (const agentName of Object.keys(config.agents)) {
|
|
@@ -54690,7 +55117,7 @@ function probePendingRetainsQueue(agentName) {
|
|
|
54690
55117
|
function buildPendingRetainsProbeScript(base, now = Date.now()) {
|
|
54691
55118
|
const dir = `${base}/pending-retains`;
|
|
54692
55119
|
const cutoff = new Date(now - PENDING_RETAINS_EVICTION_WINDOW_DAYS * 86400000).toISOString().replace(/\.\d+Z$/, "Z");
|
|
54693
|
-
return `P=0; D=0; X=0; E=0; C=\${HINDSIGHT_PENDING_MAX_ENTRIES:-${PENDING_RETAINS_DEFAULT_MAX_ENTRIES}}; ` + `if [ -d '${dir}' ]; then ` + `P=$(ls -1 '${dir}' 2>/dev/null | grep -c '\\.json$' || true); ` + `D=$(ls -1 '${dir}' 2>/dev/null | grep -c '\\.json\\.dead$' || true); ` + `fi; ` + `if [ -d '${base}/pending-dead' ]; then ` + `D=$((D + $(ls -1 '${base}/pending-dead' 2>/dev/null | grep -c '\\.dead$' || true))); ` + `fi; ` + `for L in '${base}/pending-drops.json' '${dir}/.drops.json'; do ` + `if [ "$X" = 0 ] && [ -f "$L" ]; then ` + `X=$(grep -o '"count"[^0-9]*[0-9][0-9]*' "$L" 2>/dev/null ` + `| grep -o '[0-9][0-9]*$' | head -n1); ` + `[ -n "$X" ] || X=0; ` + `fi; done; ` + `if [ -f '${base}/pending-evictions.log' ]; then ` + `E=$(awk -v c='${cutoff}' '$1 >= c && /evicted=/ {n++} END {print n+0}' ` + `'${base}/pending-evictions.log' 2>/dev/null || echo 0); ` + `[ -n "$E" ] || E=0; ` + `fi; ` + `echo "P=$P D=$D X=$X E=$E C=$C"`;
|
|
55120
|
+
return `P=0; D=0; X=0; E=0; Q=0; C=\${HINDSIGHT_PENDING_MAX_ENTRIES:-${PENDING_RETAINS_DEFAULT_MAX_ENTRIES}}; ` + `if [ -d '${dir}' ]; then ` + `P=$(ls -1 '${dir}' 2>/dev/null | grep -c '\\.json$' || true); ` + `D=$(ls -1 '${dir}' 2>/dev/null | grep -c '\\.json\\.dead$' || true); ` + `fi; ` + `if [ -d '${base}/pending-dead' ]; then ` + `D=$((D + $(ls -1 '${base}/pending-dead' 2>/dev/null | grep -c '\\.dead$' || true))); ` + `fi; ` + `for L in '${base}/pending-drops.json' '${dir}/.drops.json'; do ` + `if [ "$X" = 0 ] && [ -f "$L" ]; then ` + `X=$(grep -o '"count"[^0-9]*[0-9][0-9]*' "$L" 2>/dev/null ` + `| grep -o '[0-9][0-9]*$' | head -n1); ` + `[ -n "$X" ] || X=0; ` + `fi; done; ` + `if [ -f '${base}/pending-evictions.log' ]; then ` + `E=$(awk -v c='${cutoff}' '$1 >= c && /evicted=/ {n++} END {print n+0}' ` + `'${base}/pending-evictions.log' 2>/dev/null || echo 0); ` + `[ -n "$E" ] || E=0; ` + `fi; ` + `if [ -d '${base}/pending-duplicate' ]; then ` + `Q=$(ls -1 '${base}/pending-duplicate' 2>/dev/null | grep -c '\\.json$' || true); ` + `fi; ` + `echo "P=$P D=$D X=$X E=$E Q=$Q C=$C"`;
|
|
54694
55121
|
}
|
|
54695
55122
|
function parsePendingRetainsProbeOutput(r) {
|
|
54696
55123
|
const unreachable = {
|
|
@@ -54699,6 +55126,7 @@ function parsePendingRetainsProbeOutput(r) {
|
|
|
54699
55126
|
dead: 0,
|
|
54700
55127
|
dropped: 0,
|
|
54701
55128
|
evicted: 0,
|
|
55129
|
+
duplicates: 0,
|
|
54702
55130
|
cap: PENDING_RETAINS_DEFAULT_MAX_ENTRIES
|
|
54703
55131
|
};
|
|
54704
55132
|
if (r.error || r.status !== 0)
|
|
@@ -54712,11 +55140,13 @@ function parsePendingRetainsProbeOutput(r) {
|
|
|
54712
55140
|
const dead = parseInt(dm[1], 10);
|
|
54713
55141
|
const xm = out.match(/X=(\d+)/);
|
|
54714
55142
|
const em = out.match(/E=(\d+)/);
|
|
55143
|
+
const qm = out.match(/Q=(\d+)/);
|
|
54715
55144
|
const cm = out.match(/C=(\d+)/);
|
|
54716
55145
|
const dropped = xm ? parseInt(xm[1], 10) : 0;
|
|
54717
55146
|
const evicted = em ? parseInt(em[1], 10) : 0;
|
|
55147
|
+
const duplicates = qm ? parseInt(qm[1], 10) : 0;
|
|
54718
55148
|
const cap = cm && parseInt(cm[1], 10) > 0 ? parseInt(cm[1], 10) : PENDING_RETAINS_DEFAULT_MAX_ENTRIES;
|
|
54719
|
-
const common = { pending, dead, dropped, evicted, cap };
|
|
55149
|
+
const common = { pending, dead, dropped, evicted, duplicates, cap };
|
|
54720
55150
|
if (dead > 0)
|
|
54721
55151
|
return { state: "dead", ...common };
|
|
54722
55152
|
if (pending > 0)
|
|
@@ -54748,12 +55178,15 @@ function checkPendingRetainsQueues(config, opts) {
|
|
|
54748
55178
|
const unreachable = [];
|
|
54749
55179
|
const dropped = [];
|
|
54750
55180
|
const evicted = [];
|
|
55181
|
+
const duplicated = [];
|
|
54751
55182
|
let okCount = 0;
|
|
54752
55183
|
for (const [a, r] of results) {
|
|
54753
55184
|
if (r.dropped > 0)
|
|
54754
55185
|
dropped.push(`${a} (${r.dropped})`);
|
|
54755
55186
|
if (r.evicted > 0)
|
|
54756
55187
|
evicted.push(`${a} (${r.evicted})`);
|
|
55188
|
+
if (r.duplicates > 0)
|
|
55189
|
+
duplicated.push(`${a} (${r.duplicates})`);
|
|
54757
55190
|
if (r.state === "dead") {
|
|
54758
55191
|
dead.push(`${a} (${r.dead} dead${r.pending > 0 ? `, ${r.pending} queued` : ""})`);
|
|
54759
55192
|
} else if (r.state === "backlog") {
|
|
@@ -54768,6 +55201,7 @@ function checkPendingRetainsQueues(config, opts) {
|
|
|
54768
55201
|
}
|
|
54769
55202
|
}
|
|
54770
55203
|
const skippedNote = unreachable.length > 0 ? `; skipped (unreachable): ${unreachable.join(", ")}` : "";
|
|
55204
|
+
const dupNote = duplicated.length > 0 ? `; collapsed duplicates (not loss): ${duplicated.join(", ")}` : "";
|
|
54771
55205
|
const backlogFix = `Clear a backlog explicitly (the SessionStart drain cannot \u2014 its per-entry ` + `timeout is clamped to the hook budget, far below a real retain): ` + `\`docker exec switchroom-<agent> python3 ` + `/state/agent/.claude/plugins/hindsight-memory/scripts/drain_pending.py --backlog\`. ` + `Run \`--phase reconcile\` first \u2014 it costs nothing and typically clears most of ` + `the queue by confirming those documents already exist. PACE THE REST: the retain ` + `model pool is small and shared with live traffic, so drain ONE agent at a time at ` + `the default HINDSIGHT_DRAIN_CONCURRENCY=1, and keep HINDSIGHT_DRAIN_SLEEP_S set. ` + `Point HINDSIGHT_DRAIN_P95_CMD at the same figure the latency watchdog alarms on so ` + `the replay backs off instead of causing the alarm \u2014 on a LiteLLM deployment that is ` + `\`docker exec -i <postgres> psql -U "$POSTGRES_USER" -d "$POSTGRES_DB" -At -c ` + `"select coalesce((select round(percentile_cont(0.95) within group (order by ` + `request_duration_ms)) from \\"LiteLLM_SpendLogs\\" where \\"startTime\\" > now() ` + `- interval '10 minutes' and metadata->>'user_api_key_alias' like 'hindsight-%' and ` + `status='success' and request_duration_ms is not null), -1)"\` (must print a ` + `millisecond integer on stdout; unset = no backoff). ` + `SAFE ALONGSIDE THE \`hindsight-drain\` SIDECAR and a session booting underneath ` + `you: drain_pending.py takes an exclusive lock on ` + `\`$HOME/.hindsight/drain-pending.lock\` for the whole run, so whichever drain ` + `starts second prints "another drain holds ..." and exits without touching the ` + `queue. Do NOT wrap this in \`flock\` on that path yourself \u2014 you would hold the ` + `lock the script then asks for, and it would skip every time. Entries past the ` + `attempt ceiling (HINDSIGHT_DRAIN_ATTEMPT_CEILING, default 20) are PARKED and ` + `reported rather than retried forever; add \`--force\` once the upstream is fixed ` + `to replay them. ` + `Fix the upstream first (bank ` + `health rows above), or every retry just ages entries toward .dead. Entries the ` + `drain retires are MOVED to \`.hindsight/pending-reconciled/\` (bounded, so they ` + `expire), never deleted \u2014 every retire rests on an HTTP 200, which is an ack and ` + `not proof, so the payload stays recoverable. If that archive cannot be written ` + `(disk full, permissions) the entry STAYS QUEUED and the drain says so on stderr; ` + `a failure to archive is never a reason to delete. FIX THE DISK ANYWAY \u2014 "stays ` + `queued" is bounded: entries pile up, the queue hits ` + `HINDSIGHT_PENDING_MAX_ENTRIES/MAX_BYTES, and enqueue then sheds the OLDEST ` + `entries to keep accepting the newest. While the disk is full their archive move ` + `fails too, so those oldest turns are removed outright (the ledger line reads ` + `\`reason=...+archive-failed\`). Under sustained ENOSPC "keep it queued" degrades ` + `to "keep the newest, drop the oldest".`;
|
|
54772
55206
|
if (dead.length > 0 || dropped.length > 0 || evicted.length > 0) {
|
|
54773
55207
|
const parts = [];
|
|
@@ -54798,7 +55232,7 @@ function checkPendingRetainsQueues(config, opts) {
|
|
|
54798
55232
|
return {
|
|
54799
55233
|
name,
|
|
54800
55234
|
status: "fail",
|
|
54801
|
-
detail: `${parts.join("; ")}${skippedNote}`,
|
|
55235
|
+
detail: `${parts.join("; ")}${skippedNote}${dupNote}`,
|
|
54802
55236
|
fix: fixParts.join(" ")
|
|
54803
55237
|
};
|
|
54804
55238
|
}
|
|
@@ -54812,14 +55246,14 @@ function checkPendingRetainsQueues(config, opts) {
|
|
|
54812
55246
|
return {
|
|
54813
55247
|
name,
|
|
54814
55248
|
status: "warn",
|
|
54815
|
-
detail: `${parts.join("; ")}${skippedNote}`,
|
|
55249
|
+
detail: `${parts.join("; ")}${skippedNote}${dupNote}`,
|
|
54816
55250
|
fix: backlogFix
|
|
54817
55251
|
};
|
|
54818
55252
|
}
|
|
54819
55253
|
return {
|
|
54820
55254
|
name,
|
|
54821
55255
|
status: "ok",
|
|
54822
|
-
detail: `${okCount}/${agentNames.length} agents: empty (no failed retains)${skippedNote}`
|
|
55256
|
+
detail: `${okCount}/${agentNames.length} agents: empty (no failed retains)${skippedNote}${dupNote}`
|
|
54823
55257
|
};
|
|
54824
55258
|
}
|
|
54825
55259
|
function classifyReadError(err) {
|
|
@@ -55949,8 +56383,8 @@ function registerDoctorCommand(program3) {
|
|
|
55949
56383
|
results: await runContextChecks(config, {
|
|
55950
56384
|
readSnapshot: async (agent) => {
|
|
55951
56385
|
try {
|
|
55952
|
-
const { execFileSync:
|
|
55953
|
-
const out =
|
|
56386
|
+
const { execFileSync: execFileSync25 } = await import("node:child_process");
|
|
56387
|
+
const out = execFileSync25("docker", ["exec", `switchroom-${agent}`, "cat", "/state/agent/context-occupancy.json"], { encoding: "utf-8", stdio: ["ignore", "pipe", "ignore"], timeout: 5000 });
|
|
55954
56388
|
return { kind: "ok", snapshot: JSON.parse(out) };
|
|
55955
56389
|
} catch (err) {
|
|
55956
56390
|
const msg = err.message ?? String(err);
|
|
@@ -56051,7 +56485,7 @@ function registerDoctorCommand(program3) {
|
|
|
56051
56485
|
}
|
|
56052
56486
|
}));
|
|
56053
56487
|
}
|
|
56054
|
-
var DEFAULT_DEPLOY_MOUNTS_DEPS, PENDING_RETAINS_DEFAULT_MAX_ENTRIES = 2000, PENDING_RETAINS_EVICTION_WINDOW_DAYS = 7, MFF_VAULT_KEY = "mff/agent-private-key", MANIFEST_WARN_ONLY;
|
|
56488
|
+
var import_yaml19, DEFAULT_DEPLOY_MOUNTS_DEPS, PENDING_RETAINS_DEFAULT_MAX_ENTRIES = 2000, PENDING_RETAINS_EVICTION_WINDOW_DAYS = 7, MFF_VAULT_KEY = "mff/agent-private-key", MANIFEST_WARN_ONLY;
|
|
56055
56489
|
var init_doctor = __esm(() => {
|
|
56056
56490
|
init_source();
|
|
56057
56491
|
init_doctor_status();
|
|
@@ -56072,6 +56506,7 @@ var init_doctor = __esm(() => {
|
|
|
56072
56506
|
init_hindsight();
|
|
56073
56507
|
init_host_capabilities();
|
|
56074
56508
|
init_hindsight_perf_defaults();
|
|
56509
|
+
init_memory_key_drift();
|
|
56075
56510
|
init_doctor_memory();
|
|
56076
56511
|
init_doctor_recall_health();
|
|
56077
56512
|
init_doctor_hnsw_index();
|
|
@@ -56103,6 +56538,7 @@ var init_doctor = __esm(() => {
|
|
|
56103
56538
|
init_doctor_timezone();
|
|
56104
56539
|
init_doctor_fix_session_model();
|
|
56105
56540
|
init_doctor_claude_cli();
|
|
56541
|
+
import_yaml19 = __toESM(require_dist(), 1);
|
|
56106
56542
|
DEFAULT_DEPLOY_MOUNTS_DEPS = { pathKind: defaultPathKind };
|
|
56107
56543
|
MANIFEST_WARN_ONLY = new Set([
|
|
56108
56544
|
"@playwright/mcp",
|
|
@@ -56197,7 +56633,7 @@ __export(exports_util, {
|
|
|
56197
56633
|
jsonStringifyReplacer: () => jsonStringifyReplacer,
|
|
56198
56634
|
joinValues: () => joinValues,
|
|
56199
56635
|
issue: () => issue,
|
|
56200
|
-
isPlainObject: () =>
|
|
56636
|
+
isPlainObject: () => isPlainObject3,
|
|
56201
56637
|
isObject: () => isObject2,
|
|
56202
56638
|
getSizableOrigin: () => getSizableOrigin,
|
|
56203
56639
|
getParsedType: () => getParsedType2,
|
|
@@ -56338,7 +56774,7 @@ function esc(str) {
|
|
|
56338
56774
|
function isObject2(data) {
|
|
56339
56775
|
return typeof data === "object" && data !== null && !Array.isArray(data);
|
|
56340
56776
|
}
|
|
56341
|
-
function
|
|
56777
|
+
function isPlainObject3(o) {
|
|
56342
56778
|
if (isObject2(o) === false)
|
|
56343
56779
|
return false;
|
|
56344
56780
|
const ctor = o.constructor;
|
|
@@ -56466,7 +56902,7 @@ function omit(schema, mask) {
|
|
|
56466
56902
|
});
|
|
56467
56903
|
}
|
|
56468
56904
|
function extend(schema, shape) {
|
|
56469
|
-
if (!
|
|
56905
|
+
if (!isPlainObject3(shape)) {
|
|
56470
56906
|
throw new Error("Invalid input to extend: expected a plain object");
|
|
56471
56907
|
}
|
|
56472
56908
|
const def = {
|
|
@@ -57408,7 +57844,7 @@ function mergeValues2(a, b) {
|
|
|
57408
57844
|
if (a instanceof Date && b instanceof Date && +a === +b) {
|
|
57409
57845
|
return { valid: true, data: a };
|
|
57410
57846
|
}
|
|
57411
|
-
if (
|
|
57847
|
+
if (isPlainObject3(a) && isPlainObject3(b)) {
|
|
57412
57848
|
const bKeys = Object.keys(b);
|
|
57413
57849
|
const sharedKeys = Object.keys(a).filter((key) => bKeys.indexOf(key) !== -1);
|
|
57414
57850
|
const newObj = { ...a, ...b };
|
|
@@ -58253,7 +58689,7 @@ var init_schemas = __esm(() => {
|
|
|
58253
58689
|
$ZodType.init(inst, def);
|
|
58254
58690
|
inst._zod.parse = (payload, ctx) => {
|
|
58255
58691
|
const input = payload.value;
|
|
58256
|
-
if (!
|
|
58692
|
+
if (!isPlainObject3(input)) {
|
|
58257
58693
|
payload.issues.push({
|
|
58258
58694
|
expected: "record",
|
|
58259
58695
|
code: "invalid_type",
|
|
@@ -61936,7 +62372,7 @@ class Protocol {
|
|
|
61936
62372
|
};
|
|
61937
62373
|
}
|
|
61938
62374
|
}
|
|
61939
|
-
function
|
|
62375
|
+
function isPlainObject4(value) {
|
|
61940
62376
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
61941
62377
|
}
|
|
61942
62378
|
function mergeCapabilities(base, additional) {
|
|
@@ -61947,7 +62383,7 @@ function mergeCapabilities(base, additional) {
|
|
|
61947
62383
|
if (addValue === undefined)
|
|
61948
62384
|
continue;
|
|
61949
62385
|
const baseValue = result[k];
|
|
61950
|
-
if (
|
|
62386
|
+
if (isPlainObject4(baseValue) && isPlainObject4(addValue)) {
|
|
61951
62387
|
result[k] = { ...baseValue, ...addValue };
|
|
61952
62388
|
} else {
|
|
61953
62389
|
result[k] = addValue;
|
|
@@ -70661,14 +71097,14 @@ function detectHeaderMisconfig(parsed) {
|
|
|
70661
71097
|
}
|
|
70662
71098
|
function parseLitellmConfig(text) {
|
|
70663
71099
|
try {
|
|
70664
|
-
return
|
|
71100
|
+
return import_yaml27.parse(text);
|
|
70665
71101
|
} catch {
|
|
70666
71102
|
return null;
|
|
70667
71103
|
}
|
|
70668
71104
|
}
|
|
70669
|
-
var
|
|
71105
|
+
var import_yaml27, FORWARD_HEADERS_FLAG = "forward_client_headers_to_llm_api", COOLIFY_SERVICES_DIR = "/data/coolify/services", LIVE_LITELLM_CONFIG_BASENAME = "litellm-config.yaml", BARE_ANTHROPIC_FAMILY_ALIASES;
|
|
70670
71106
|
var init_header_passthrough_guard = __esm(() => {
|
|
70671
|
-
|
|
71107
|
+
import_yaml27 = __toESM(require_dist(), 1);
|
|
70672
71108
|
BARE_ANTHROPIC_FAMILY_ALIASES = new Set([
|
|
70673
71109
|
"opus",
|
|
70674
71110
|
"sonnet",
|
|
@@ -73897,6 +74333,9 @@ function summarizeReconcileBatch(totalCount, failures) {
|
|
|
73897
74333
|
lines: failures.map((f) => `${f.name}: ${f.error}`)
|
|
73898
74334
|
};
|
|
73899
74335
|
}
|
|
74336
|
+
function shouldAutoRestartAfterReconcile(opts, restartRequiredCount) {
|
|
74337
|
+
return opts.restart !== false && restartRequiredCount > 0;
|
|
74338
|
+
}
|
|
73900
74339
|
function resolveLogsTail(lines) {
|
|
73901
74340
|
const DEFAULT_TAIL = 50;
|
|
73902
74341
|
const MAX_TAIL = 1000;
|
|
@@ -74745,7 +75184,7 @@ Scaffolding agent: ${name}
|
|
|
74745
75184
|
}
|
|
74746
75185
|
}
|
|
74747
75186
|
const needsRestart = restartRequired.length > 0 || staleTillRestart.length > 0;
|
|
74748
|
-
const autoRestart = restartRequired.length
|
|
75187
|
+
const autoRestart = shouldAutoRestartAfterReconcile(opts, restartRequired.length);
|
|
74749
75188
|
if (!needsRestart && hot.length > 0) {
|
|
74750
75189
|
console.log(source_default.green(`
|
|
74751
75190
|
(no restart needed, changes active next turn)`));
|
|
@@ -74765,7 +75204,7 @@ Restart: switchroom agent restart ${n}`));
|
|
|
74765
75204
|
}
|
|
74766
75205
|
}
|
|
74767
75206
|
const changesBySemantics = result.changesBySemantics;
|
|
74768
|
-
const autoRestartNeeded =
|
|
75207
|
+
const autoRestartNeeded = changesBySemantics !== undefined && shouldAutoRestartAfterReconcile(opts, changesBySemantics.restartRequired.length);
|
|
74769
75208
|
const shouldRestart = (opts.restart || opts.gracefulRestart || autoRestartNeeded) && allChanges.length > 0;
|
|
74770
75209
|
if (shouldRestart) {
|
|
74771
75210
|
try {
|
|
@@ -85897,14 +86336,21 @@ ${name}:`)];
|
|
|
85897
86336
|
const max = memCounts.length > 0 ? Math.max(...memCounts) : null;
|
|
85898
86337
|
const medians = entries.map((e) => e.injected_score_median).filter((n) => typeof n === "number");
|
|
85899
86338
|
const avgScore = medians.length > 0 ? Math.round(medians.reduce((s, n) => s + n, 0) / medians.length * 100) / 100 : null;
|
|
85900
|
-
|
|
86339
|
+
const collapseTurns = entries.filter((e) => typeof e.injected_own_bank_count === "number" && e.injected_own_bank_count === 0 && typeof e.injected_additional_bank_count === "number" && e.injected_additional_bank_count > 0).length;
|
|
86340
|
+
lines.push(source_default.gray(` last ${total} turn${total === 1 ? "" : "s"}: ` + `avg=${avg ?? "\u2014"} max=${max ?? "\u2014"} ` + `avg_score=${avgScore ?? "\u2014"} ` + `cache_hits=${hits} capped=${cappedTurns}` + (omittedTurns > 0 ? ` directives_omitted_turns=${omittedTurns}` : "") + (collapseTurns > 0 ? source_default.red(` own_bank_collapse_turns=${collapseTurns}`) : "")));
|
|
85901
86341
|
for (const e of entries) {
|
|
85902
86342
|
const flag = e.cache_hit ? source_default.cyan("CACHE") : e.capped ? source_default.yellow("CAP") : source_default.green("OK");
|
|
85903
86343
|
const dem = e.demoted_count && e.demoted_count > 0 ? source_default.dim(` -${e.demoted_count}d`) : "";
|
|
85904
86344
|
const omitted = typeof e.directives_omitted === "number" && e.directives_omitted > 0 ? source_default.yellow(` +${e.directives_omitted}omitted`) : "";
|
|
85905
86345
|
const ids = e.memory_ids && e.memory_ids.length > 0 ? source_default.dim(` ids=${e.memory_ids.slice(0, 3).join(",")}${e.memory_ids.length > 3 ? `\u2026+${e.memory_ids.length - 3}` : ""}`) : "";
|
|
86346
|
+
const own = e.injected_own_bank_count;
|
|
86347
|
+
const additional = e.injected_additional_bank_count;
|
|
86348
|
+
const composition = typeof own === "number" || typeof additional === "number" ? (() => {
|
|
86349
|
+
const text = ` own/add=${own ?? "\u2014"}/${additional ?? "\u2014"}`;
|
|
86350
|
+
return own === 0 && typeof additional === "number" && additional > 0 ? source_default.red(text) : source_default.dim(text);
|
|
86351
|
+
})() : "";
|
|
85906
86352
|
const score = typeof e.injected_score_min === "number" && typeof e.injected_score_median === "number" && typeof e.injected_score_max === "number" ? source_default.dim(` score=${e.injected_score_min.toFixed(2)}/` + `${e.injected_score_median.toFixed(2)}/` + `${e.injected_score_max.toFixed(2)}`) : "";
|
|
85907
|
-
lines.push(` ${source_default.gray(e.ts)} ${flag} ` + `n=${e.result_count ?? "\u2014"}${e.pre_cap_count != null && e.pre_cap_count !== e.result_count ? `/${e.pre_cap_count}` : ""}` + `${dem}${omitted}${score}${ids}`);
|
|
86353
|
+
lines.push(` ${source_default.gray(e.ts)} ${flag} ` + `n=${e.result_count ?? "\u2014"}${e.pre_cap_count != null && e.pre_cap_count !== e.result_count ? `/${e.pre_cap_count}` : ""}` + `${dem}${omitted}${score}${composition}${ids}`);
|
|
85908
86354
|
}
|
|
85909
86355
|
return lines;
|
|
85910
86356
|
}
|
|
@@ -92414,24 +92860,24 @@ init_source();
|
|
|
92414
92860
|
init_loader();
|
|
92415
92861
|
init_lifecycle();
|
|
92416
92862
|
init_compose_env();
|
|
92417
|
-
import { existsSync as existsSync81, mkdirSync as mkdirSync46, readFileSync as readFileSync75, realpathSync as realpathSync6, statSync as
|
|
92863
|
+
import { existsSync as existsSync81, mkdirSync as mkdirSync46, readFileSync as readFileSync75, realpathSync as realpathSync6, statSync as statSync46, chownSync as chownSync9 } from "node:fs";
|
|
92418
92864
|
import { spawnSync as spawnSync17 } from "node:child_process";
|
|
92419
92865
|
import { join as join84, dirname as dirname34, resolve as resolve45 } from "node:path";
|
|
92420
92866
|
import { homedir as homedir44 } from "node:os";
|
|
92421
92867
|
|
|
92422
92868
|
// src/cli/release-yaml.ts
|
|
92423
|
-
var
|
|
92869
|
+
var import_yaml20 = __toESM(require_dist(), 1);
|
|
92424
92870
|
function getReleasePinFromConfig(yamlText) {
|
|
92425
92871
|
let v;
|
|
92426
92872
|
try {
|
|
92427
|
-
v =
|
|
92873
|
+
v = import_yaml20.parseDocument(yamlText).getIn(["release", "pin"]);
|
|
92428
92874
|
} catch {
|
|
92429
92875
|
return;
|
|
92430
92876
|
}
|
|
92431
92877
|
return typeof v === "string" ? v : undefined;
|
|
92432
92878
|
}
|
|
92433
92879
|
function setReleasePinInConfig(yamlText, pin, now = new Date().toISOString().slice(0, 10)) {
|
|
92434
|
-
const doc =
|
|
92880
|
+
const doc = import_yaml20.parseDocument(yamlText);
|
|
92435
92881
|
const currentPin = doc.getIn(["release", "pin"]);
|
|
92436
92882
|
const hasChannel = doc.hasIn(["release", "channel"]);
|
|
92437
92883
|
if (currentPin === pin && !hasChannel)
|
|
@@ -92440,31 +92886,31 @@ function setReleasePinInConfig(yamlText, pin, now = new Date().toISOString().sli
|
|
|
92440
92886
|
doc.deleteIn(["release", "channel"]);
|
|
92441
92887
|
doc.setIn(["release", "pin"], pin);
|
|
92442
92888
|
const node = doc.getIn(["release", "pin"], true);
|
|
92443
|
-
if (
|
|
92889
|
+
if (import_yaml20.isScalar(node)) {
|
|
92444
92890
|
node.comment = ` ${now}: rolled by switchroom rollout`;
|
|
92445
92891
|
}
|
|
92446
92892
|
return String(doc);
|
|
92447
92893
|
}
|
|
92448
92894
|
function deleteReleasePinInConfig(yamlText) {
|
|
92449
|
-
const doc =
|
|
92895
|
+
const doc = import_yaml20.parseDocument(yamlText);
|
|
92450
92896
|
if (!doc.hasIn(["release", "pin"]))
|
|
92451
92897
|
return yamlText;
|
|
92452
92898
|
doc.deleteIn(["release", "pin"]);
|
|
92453
92899
|
const rel = doc.getIn(["release"]);
|
|
92454
|
-
if (
|
|
92900
|
+
if (import_yaml20.isMap(rel) && rel.items.length === 0 && !releaseBlockIsCommented(doc)) {
|
|
92455
92901
|
doc.delete("release");
|
|
92456
92902
|
}
|
|
92457
92903
|
return String(doc);
|
|
92458
92904
|
}
|
|
92459
92905
|
function releaseBlockIsCommented(doc) {
|
|
92460
92906
|
const contents = doc.contents;
|
|
92461
|
-
if (!
|
|
92907
|
+
if (!import_yaml20.isMap(contents))
|
|
92462
92908
|
return false;
|
|
92463
92909
|
for (const item of contents.items) {
|
|
92464
|
-
if (!
|
|
92910
|
+
if (!import_yaml20.isPair(item))
|
|
92465
92911
|
continue;
|
|
92466
92912
|
const key = item.key;
|
|
92467
|
-
if (!
|
|
92913
|
+
if (!import_yaml20.isScalar(key) || key.value !== "release")
|
|
92468
92914
|
continue;
|
|
92469
92915
|
const nodes = [key, item.value];
|
|
92470
92916
|
return nodes.some((n) => !!n && typeof n === "object" && (("commentBefore" in n) && !!n.commentBefore || ("comment" in n) && !!n.comment));
|
|
@@ -92477,7 +92923,7 @@ init_operator_uid();
|
|
|
92477
92923
|
init_atomic();
|
|
92478
92924
|
|
|
92479
92925
|
// src/cli/preflight-mounts.ts
|
|
92480
|
-
import { statSync as
|
|
92926
|
+
import { statSync as statSync45 } from "node:fs";
|
|
92481
92927
|
function parseHostBindSources(composeText) {
|
|
92482
92928
|
const out = [];
|
|
92483
92929
|
const FILE_HINT = /\.(ya?ml|db|log|toml|json|token|id)$|\/\.vault-token$|machine-id$|localtime$|vault-auto-unlock$|\/webkite$/;
|
|
@@ -92504,7 +92950,7 @@ function parseHostBindSources(composeText) {
|
|
|
92504
92950
|
return out;
|
|
92505
92951
|
}
|
|
92506
92952
|
function validateBindSources(composeText, deps = {}) {
|
|
92507
|
-
const stat = deps.stat ?? ((p) =>
|
|
92953
|
+
const stat = deps.stat ?? ((p) => statSync45(p));
|
|
92508
92954
|
const sources = parseHostBindSources(composeText);
|
|
92509
92955
|
const issues = [];
|
|
92510
92956
|
const seen = new Set;
|
|
@@ -93093,12 +93539,12 @@ function defaultPersistPin(configPath) {
|
|
|
93093
93539
|
const after = setReleasePinInConfig(before, pin);
|
|
93094
93540
|
if (after === before)
|
|
93095
93541
|
return;
|
|
93096
|
-
writeConfigFileSync(path7, after,
|
|
93542
|
+
writeConfigFileSync(path7, after, statSync46(path7).mode & 511);
|
|
93097
93543
|
try {
|
|
93098
93544
|
if (typeof process.geteuid === "function" && process.geteuid() === 0) {
|
|
93099
93545
|
const uid = resolveOperatorUid();
|
|
93100
93546
|
if (uid !== undefined)
|
|
93101
|
-
|
|
93547
|
+
chownSync9(path7, uid, uid);
|
|
93102
93548
|
}
|
|
93103
93549
|
} catch {}
|
|
93104
93550
|
};
|
|
@@ -93577,7 +94023,7 @@ function defaultStatusProbe(composePath) {
|
|
|
93577
94023
|
} catch {}
|
|
93578
94024
|
if (scriptPath) {
|
|
93579
94025
|
try {
|
|
93580
|
-
cliBuiltAt = new Date(
|
|
94026
|
+
cliBuiltAt = new Date(statSync46(scriptPath).mtimeMs).toISOString();
|
|
93581
94027
|
} catch {}
|
|
93582
94028
|
let dir = dirname34(scriptPath);
|
|
93583
94029
|
for (let i = 0;i < 8; i++) {
|
|
@@ -93853,7 +94299,7 @@ function registerUpdateCommand(program3) {
|
|
|
93853
94299
|
// src/cli/rollout.ts
|
|
93854
94300
|
init_helpers();
|
|
93855
94301
|
import { spawnSync as spawnSync19 } from "node:child_process";
|
|
93856
|
-
import { readFileSync as readFileSync77, chownSync as
|
|
94302
|
+
import { readFileSync as readFileSync77, chownSync as chownSync10, statSync as statSync48 } from "node:fs";
|
|
93857
94303
|
import { homedir as homedir46 } from "node:os";
|
|
93858
94304
|
|
|
93859
94305
|
// src/cli/rollout-pin-journal.ts
|
|
@@ -93863,7 +94309,7 @@ import {
|
|
|
93863
94309
|
writeFileSync as writeFileSync32,
|
|
93864
94310
|
renameSync as renameSync21,
|
|
93865
94311
|
unlinkSync as unlinkSync15,
|
|
93866
|
-
statSync as
|
|
94312
|
+
statSync as statSync47,
|
|
93867
94313
|
mkdirSync as mkdirSync47
|
|
93868
94314
|
} from "node:fs";
|
|
93869
94315
|
import { homedir as homedir45 } from "node:os";
|
|
@@ -94020,7 +94466,7 @@ function rollbackPinPersist(configPath, opts = {}) {
|
|
|
94020
94466
|
const next = journal.priorPin ? setReleasePinInConfig(current, journal.priorPin) : deleteReleasePinInConfig(current);
|
|
94021
94467
|
let mode = 384;
|
|
94022
94468
|
try {
|
|
94023
|
-
mode =
|
|
94469
|
+
mode = statSync47(configPath).mode & 511;
|
|
94024
94470
|
} catch {}
|
|
94025
94471
|
if (opts.writeConfig) {
|
|
94026
94472
|
opts.writeConfig(configPath, next, mode);
|
|
@@ -94599,7 +95045,7 @@ function createRolloutDeps(params) {
|
|
|
94599
95045
|
if (typeof process.geteuid === "function" && process.geteuid() === 0) {
|
|
94600
95046
|
const uid = resolveOperatorUid();
|
|
94601
95047
|
if (uid !== undefined) {
|
|
94602
|
-
|
|
95048
|
+
chownSync10(path7, uid, uid);
|
|
94603
95049
|
} else if (hostdCtx) {
|
|
94604
95050
|
warn("\u26a0\ufe0f rollout (hostd path): could not resolve operator uid; " + "skipping config chown-back to avoid leaving switchroom.yaml " + "root-owned. Verify ownership host-side if other yaml verbs " + `EACCES.
|
|
94605
95051
|
`);
|
|
@@ -94659,7 +95105,7 @@ function createRolloutDeps(params) {
|
|
|
94659
95105
|
if (after === before)
|
|
94660
95106
|
return false;
|
|
94661
95107
|
beginPinPersist(configPath, pin);
|
|
94662
|
-
writeConfigPreservingOwnership(configPath, after,
|
|
95108
|
+
writeConfigPreservingOwnership(configPath, after, statSync48(configPath).mode & 511);
|
|
94663
95109
|
return true;
|
|
94664
95110
|
},
|
|
94665
95111
|
commitPin: () => {
|
|
@@ -95105,7 +95551,7 @@ init_merge();
|
|
|
95105
95551
|
import {
|
|
95106
95552
|
existsSync as existsSync84,
|
|
95107
95553
|
readdirSync as readdirSync28,
|
|
95108
|
-
statSync as
|
|
95554
|
+
statSync as statSync49,
|
|
95109
95555
|
unlinkSync as unlinkSync16
|
|
95110
95556
|
} from "node:fs";
|
|
95111
95557
|
import { join as join87 } from "node:path";
|
|
@@ -95128,7 +95574,7 @@ function collectSessionJsonl(claudeConfigDir) {
|
|
|
95128
95574
|
const full = join87(dir, name);
|
|
95129
95575
|
let st;
|
|
95130
95576
|
try {
|
|
95131
|
-
st =
|
|
95577
|
+
st = statSync49(full);
|
|
95132
95578
|
} catch {
|
|
95133
95579
|
continue;
|
|
95134
95580
|
}
|
|
@@ -95256,7 +95702,7 @@ import {
|
|
|
95256
95702
|
readdirSync as readdirSync29,
|
|
95257
95703
|
readFileSync as readFileSync79,
|
|
95258
95704
|
renameSync as renameSync22,
|
|
95259
|
-
statSync as
|
|
95705
|
+
statSync as statSync50,
|
|
95260
95706
|
unlinkSync as unlinkSync17,
|
|
95261
95707
|
writeFileSync as writeFileSync33,
|
|
95262
95708
|
writeSync as writeSync9
|
|
@@ -95459,7 +95905,7 @@ function sweepOrphanTmpFiles(stateDir) {
|
|
|
95459
95905
|
continue;
|
|
95460
95906
|
const tmpPath = join88(stateDir, entry);
|
|
95461
95907
|
try {
|
|
95462
|
-
const stat =
|
|
95908
|
+
const stat = statSync50(tmpPath);
|
|
95463
95909
|
if (stat.mtimeMs < cutoff) {
|
|
95464
95910
|
unlinkSync17(tmpPath);
|
|
95465
95911
|
}
|
|
@@ -95767,7 +96213,7 @@ import {
|
|
|
95767
96213
|
} from "node:fs";
|
|
95768
96214
|
import { dirname as dirname37, join as join89 } from "node:path";
|
|
95769
96215
|
import { homedir as homedir47 } from "node:os";
|
|
95770
|
-
import { execFileSync as
|
|
96216
|
+
import { execFileSync as execFileSync25 } from "node:child_process";
|
|
95771
96217
|
|
|
95772
96218
|
class PythonEnvError extends Error {
|
|
95773
96219
|
stderr;
|
|
@@ -95814,7 +96260,7 @@ function ensurePythonEnv(opts) {
|
|
|
95814
96260
|
}
|
|
95815
96261
|
mkdirSync49(dirname37(venvDir), { recursive: true });
|
|
95816
96262
|
try {
|
|
95817
|
-
|
|
96263
|
+
execFileSync25(hostPython, ["-m", "venv", venvDir], { stdio: "pipe" });
|
|
95818
96264
|
} catch (err) {
|
|
95819
96265
|
const e = err;
|
|
95820
96266
|
throw new PythonEnvError(`Failed to create venv for skill "${skillName}" with ${hostPython}: ${e.message}`, e.stderr?.toString());
|
|
@@ -95826,7 +96272,7 @@ function ensurePythonEnv(opts) {
|
|
|
95826
96272
|
delete childEnv.PIP_TARGET;
|
|
95827
96273
|
delete childEnv.PIP_PREFIX;
|
|
95828
96274
|
delete childEnv.PYTHONUSERBASE;
|
|
95829
|
-
|
|
96275
|
+
execFileSync25(pipBin, ["install", "--disable-pip-version-check", "-r", requirementsPath], { stdio: "pipe", env: childEnv });
|
|
95830
96276
|
} catch (err) {
|
|
95831
96277
|
const e = err;
|
|
95832
96278
|
throw new PythonEnvError(`Failed to install requirements for skill "${skillName}": ${e.message}`, e.stderr?.toString());
|
|
@@ -95855,7 +96301,7 @@ import {
|
|
|
95855
96301
|
} from "node:fs";
|
|
95856
96302
|
import { dirname as dirname38, join as join90 } from "node:path";
|
|
95857
96303
|
import { homedir as homedir48 } from "node:os";
|
|
95858
|
-
import { execFileSync as
|
|
96304
|
+
import { execFileSync as execFileSync26 } from "node:child_process";
|
|
95859
96305
|
|
|
95860
96306
|
class NodeEnvError extends Error {
|
|
95861
96307
|
stderr;
|
|
@@ -95939,10 +96385,10 @@ function ensureNodeEnv(opts) {
|
|
|
95939
96385
|
try {
|
|
95940
96386
|
if (installer === "bun") {
|
|
95941
96387
|
const args = copiedLockfile ? ["install", "--frozen-lockfile"] : ["install"];
|
|
95942
|
-
|
|
96388
|
+
execFileSync26("bun", args, { cwd: envDir, stdio: "pipe" });
|
|
95943
96389
|
} else {
|
|
95944
96390
|
const args = copiedLockfile ? ["ci"] : ["install"];
|
|
95945
|
-
|
|
96391
|
+
execFileSync26("npm", args, { cwd: envDir, stdio: "pipe" });
|
|
95946
96392
|
}
|
|
95947
96393
|
} catch (err) {
|
|
95948
96394
|
const e = err;
|
|
@@ -97024,7 +97470,7 @@ function registerSoulCommand(program3) {
|
|
|
97024
97470
|
// src/cli/debug.ts
|
|
97025
97471
|
init_helpers();
|
|
97026
97472
|
init_loader();
|
|
97027
|
-
import { existsSync as existsSync91, readFileSync as readFileSync83, readdirSync as readdirSync30, statSync as
|
|
97473
|
+
import { existsSync as existsSync91, readFileSync as readFileSync83, readdirSync as readdirSync30, statSync as statSync51 } from "node:fs";
|
|
97028
97474
|
import { resolve as resolve53, join as join93 } from "node:path";
|
|
97029
97475
|
import { createHash as createHash20 } from "node:crypto";
|
|
97030
97476
|
init_merge();
|
|
@@ -97063,7 +97509,7 @@ function findLatestTranscriptJsonl(claudeConfigDir) {
|
|
|
97063
97509
|
const transcriptPath = join93(projectPath, "transcript.jsonl");
|
|
97064
97510
|
if (!existsSync91(transcriptPath))
|
|
97065
97511
|
continue;
|
|
97066
|
-
const stat3 =
|
|
97512
|
+
const stat3 = statSync51(transcriptPath);
|
|
97067
97513
|
if (!latest || stat3.mtimeMs > latest.mtime) {
|
|
97068
97514
|
latest = { path: transcriptPath, mtime: stat3.mtimeMs };
|
|
97069
97515
|
}
|
|
@@ -97298,7 +97744,7 @@ function registerDebugCommand(program3) {
|
|
|
97298
97744
|
init_source();
|
|
97299
97745
|
|
|
97300
97746
|
// src/worktree/claim.ts
|
|
97301
|
-
import { execFileSync as
|
|
97747
|
+
import { execFileSync as execFileSync27 } from "node:child_process";
|
|
97302
97748
|
import { closeSync as closeSync17, mkdirSync as mkdirSync52, openSync as openSync17, existsSync as existsSync93, unlinkSync as unlinkSync19 } from "node:fs";
|
|
97303
97749
|
import { join as join95, resolve as resolve55 } from "node:path";
|
|
97304
97750
|
import { homedir as homedir51 } from "node:os";
|
|
@@ -97467,7 +97913,7 @@ async function claimWorktree(input, codeRepos) {
|
|
|
97467
97913
|
releaseLock();
|
|
97468
97914
|
}
|
|
97469
97915
|
try {
|
|
97470
|
-
|
|
97916
|
+
execFileSync27("git", ["worktree", "add", "-b", branch, worktreePath], {
|
|
97471
97917
|
cwd: repoPath,
|
|
97472
97918
|
stdio: "pipe"
|
|
97473
97919
|
});
|
|
@@ -97480,7 +97926,7 @@ async function claimWorktree(input, codeRepos) {
|
|
|
97480
97926
|
}
|
|
97481
97927
|
|
|
97482
97928
|
// src/worktree/release.ts
|
|
97483
|
-
import { execFileSync as
|
|
97929
|
+
import { execFileSync as execFileSync28 } from "node:child_process";
|
|
97484
97930
|
import { existsSync as existsSync94 } from "node:fs";
|
|
97485
97931
|
function releaseWorktree(input) {
|
|
97486
97932
|
const { id } = input;
|
|
@@ -97491,7 +97937,7 @@ function releaseWorktree(input) {
|
|
|
97491
97937
|
let gitSuccess = true;
|
|
97492
97938
|
if (existsSync94(record2.path)) {
|
|
97493
97939
|
try {
|
|
97494
|
-
|
|
97940
|
+
execFileSync28("git", ["worktree", "remove", "--force", record2.path], {
|
|
97495
97941
|
cwd: record2.repo,
|
|
97496
97942
|
stdio: "pipe"
|
|
97497
97943
|
});
|
|
@@ -97527,7 +97973,7 @@ function listWorktrees() {
|
|
|
97527
97973
|
}
|
|
97528
97974
|
|
|
97529
97975
|
// src/worktree/reaper.ts
|
|
97530
|
-
import { execFileSync as
|
|
97976
|
+
import { execFileSync as execFileSync29 } from "node:child_process";
|
|
97531
97977
|
import { existsSync as existsSync95 } from "node:fs";
|
|
97532
97978
|
var STALE_THRESHOLD_MS = 10 * 60 * 1000;
|
|
97533
97979
|
function reapSkipReasonText(action) {
|
|
@@ -97545,7 +97991,7 @@ function reapSkipReasonText(action) {
|
|
|
97545
97991
|
function probePathInUse(path10) {
|
|
97546
97992
|
let probeRan = false;
|
|
97547
97993
|
try {
|
|
97548
|
-
|
|
97994
|
+
execFileSync29("fuser", [path10], { stdio: "pipe" });
|
|
97549
97995
|
return "in-use";
|
|
97550
97996
|
} catch (err) {
|
|
97551
97997
|
if (err.code !== "ENOENT") {
|
|
@@ -97553,7 +97999,7 @@ function probePathInUse(path10) {
|
|
|
97553
97999
|
}
|
|
97554
98000
|
}
|
|
97555
98001
|
try {
|
|
97556
|
-
const out =
|
|
98002
|
+
const out = execFileSync29("lsof", ["-t", path10], {
|
|
97557
98003
|
stdio: ["ignore", "pipe", "ignore"]
|
|
97558
98004
|
}).toString().trim();
|
|
97559
98005
|
probeRan = true;
|
|
@@ -97568,7 +98014,7 @@ function probePathInUse(path10) {
|
|
|
97568
98014
|
}
|
|
97569
98015
|
function hasUncommittedChanges(_repoPath, worktreePath) {
|
|
97570
98016
|
try {
|
|
97571
|
-
const out =
|
|
98017
|
+
const out = execFileSync29("git", ["-C", worktreePath, "status", "--porcelain"], { stdio: "pipe" }).toString();
|
|
97572
98018
|
return out.trim().length > 0;
|
|
97573
98019
|
} catch {
|
|
97574
98020
|
return true;
|
|
@@ -97576,7 +98022,7 @@ function hasUncommittedChanges(_repoPath, worktreePath) {
|
|
|
97576
98022
|
}
|
|
97577
98023
|
function defaultRemoveWorktree(repoPath, worktreePath) {
|
|
97578
98024
|
try {
|
|
97579
|
-
|
|
98025
|
+
execFileSync29("git", ["worktree", "remove", "--force", worktreePath], {
|
|
97580
98026
|
cwd: repoPath,
|
|
97581
98027
|
stdio: "pipe"
|
|
97582
98028
|
});
|
|
@@ -97668,12 +98114,12 @@ function runReaper(nowMs, deps = {}) {
|
|
|
97668
98114
|
}
|
|
97669
98115
|
|
|
97670
98116
|
// src/worktree/gc.ts
|
|
97671
|
-
import { execFileSync as
|
|
98117
|
+
import { execFileSync as execFileSync30 } from "node:child_process";
|
|
97672
98118
|
import {
|
|
97673
98119
|
existsSync as existsSync96,
|
|
97674
98120
|
readFileSync as readFileSync85,
|
|
97675
98121
|
readdirSync as readdirSync32,
|
|
97676
|
-
statSync as
|
|
98122
|
+
statSync as statSync52,
|
|
97677
98123
|
renameSync as renameSync24,
|
|
97678
98124
|
mkdirSync as mkdirSync53,
|
|
97679
98125
|
rmSync as rmSync17
|
|
@@ -97761,7 +98207,7 @@ function isEphemeralPath(path10) {
|
|
|
97761
98207
|
}
|
|
97762
98208
|
return false;
|
|
97763
98209
|
}
|
|
97764
|
-
var defaultExec3 = (file, args, cwd) =>
|
|
98210
|
+
var defaultExec3 = (file, args, cwd) => execFileSync30(file, args, { cwd, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).toString();
|
|
97765
98211
|
function defaultPrSignal(repo, branch, exec) {
|
|
97766
98212
|
try {
|
|
97767
98213
|
const out = exec("gh", [
|
|
@@ -97800,7 +98246,7 @@ function planGc(roots, deps = {}) {
|
|
|
97800
98246
|
const exists = deps.existsSync ?? existsSync96;
|
|
97801
98247
|
const readDir = deps.readDir ?? ((p) => readdirSync32(p));
|
|
97802
98248
|
const readFile4 = deps.readFile ?? ((p) => readFileSync85(p, "utf8"));
|
|
97803
|
-
const stat3 = deps.stat ?? ((p) =>
|
|
98249
|
+
const stat3 = deps.stat ?? ((p) => statSync52(p));
|
|
97804
98250
|
const exec = deps.exec ?? defaultExec3;
|
|
97805
98251
|
const prSignal = deps.prSignal ?? ((repo, branch) => defaultPrSignal(repo, branch, exec));
|
|
97806
98252
|
const stamp = deps.dateStamp ?? "undated";
|
|
@@ -97994,7 +98440,7 @@ function listTrashEntries(nowMs, deps = {}) {
|
|
|
97994
98440
|
const p = join96(stampDir, name);
|
|
97995
98441
|
let mtimeMs = nowMs;
|
|
97996
98442
|
try {
|
|
97997
|
-
mtimeMs =
|
|
98443
|
+
mtimeMs = statSync52(p).mtimeMs;
|
|
97998
98444
|
} catch {}
|
|
97999
98445
|
out.push({ path: p, ageDays: (nowMs - mtimeMs) / 86400000 });
|
|
98000
98446
|
}
|
|
@@ -99269,6 +99715,54 @@ function redactToolCallParams(params) {
|
|
|
99269
99715
|
}
|
|
99270
99716
|
return { ...called, arguments: redactToolArguments(called.arguments) };
|
|
99271
99717
|
}
|
|
99718
|
+
var DEFAULT_RECALL_MAX_TOKENS = 1024;
|
|
99719
|
+
var DEFAULT_REFLECT_MAX_TOKENS = 1024;
|
|
99720
|
+
function resolveClampMaxTokens(envVal, fallback) {
|
|
99721
|
+
if (envVal === undefined || envVal === "")
|
|
99722
|
+
return fallback;
|
|
99723
|
+
const n = Number.parseInt(envVal, 10);
|
|
99724
|
+
return Number.isFinite(n) && n > 0 ? n : fallback;
|
|
99725
|
+
}
|
|
99726
|
+
function acceptedArgs(name) {
|
|
99727
|
+
const spec = FALLBACK_TOOL_TABLE[name];
|
|
99728
|
+
if (!spec)
|
|
99729
|
+
return;
|
|
99730
|
+
return new Set([...spec[0], ...spec[1]]);
|
|
99731
|
+
}
|
|
99732
|
+
function unknownArgMessage(name, unknownKeys, allowed) {
|
|
99733
|
+
const keys = unknownKeys.map((k) => `'${k}'`).join(", ");
|
|
99734
|
+
const plural = unknownKeys.length > 1 ? "parameters" : "parameter";
|
|
99735
|
+
const base = `${name} has no ${keys} ${plural}. ` + `Accepted arguments: ${[...allowed].sort().join(", ")}.`;
|
|
99736
|
+
if (name === "recall" || name === "reflect") {
|
|
99737
|
+
return `${base} There is no 'limit'/'top_k' \u2014 cap results with max_tokens ` + `(a token budget over each fact's text, in relevance order) or ` + `budget: low|mid|high.`;
|
|
99738
|
+
}
|
|
99739
|
+
return base;
|
|
99740
|
+
}
|
|
99741
|
+
function guardAndClampToolCall(params, env2) {
|
|
99742
|
+
const called = params;
|
|
99743
|
+
const name = called?.name;
|
|
99744
|
+
if (typeof name !== "string")
|
|
99745
|
+
return { ok: true, params };
|
|
99746
|
+
const allowed = acceptedArgs(name);
|
|
99747
|
+
if (!allowed)
|
|
99748
|
+
return { ok: true, params };
|
|
99749
|
+
const rawArgs = called?.arguments;
|
|
99750
|
+
const args = rawArgs && typeof rawArgs === "object" && !Array.isArray(rawArgs) ? rawArgs : {};
|
|
99751
|
+
const unknownKeys = Object.keys(args).filter((k) => !allowed.has(k));
|
|
99752
|
+
if (unknownKeys.length > 0) {
|
|
99753
|
+
return { ok: false, text: unknownArgMessage(name, unknownKeys, allowed) };
|
|
99754
|
+
}
|
|
99755
|
+
if (name === "recall" || name === "reflect") {
|
|
99756
|
+
const injected = { ...args };
|
|
99757
|
+
if (injected.max_tokens === undefined) {
|
|
99758
|
+
injected.max_tokens = name === "recall" ? resolveClampMaxTokens(env2.HINDSIGHT_SHIM_RECALL_MAX_TOKENS, DEFAULT_RECALL_MAX_TOKENS) : resolveClampMaxTokens(env2.HINDSIGHT_SHIM_REFLECT_MAX_TOKENS, DEFAULT_REFLECT_MAX_TOKENS);
|
|
99759
|
+
}
|
|
99760
|
+
if (injected.budget === undefined)
|
|
99761
|
+
injected.budget = "low";
|
|
99762
|
+
return { ok: true, params: { ...called, arguments: injected } };
|
|
99763
|
+
}
|
|
99764
|
+
return { ok: true, params };
|
|
99765
|
+
}
|
|
99272
99766
|
function buildFallbackToolsList() {
|
|
99273
99767
|
const tools = Object.entries(FALLBACK_TOOL_TABLE).map(([name, [required, props]]) => ({
|
|
99274
99768
|
name,
|
|
@@ -99595,7 +100089,11 @@ class HindsightShim {
|
|
|
99595
100089
|
if (called?.name && SYNTHESIZED_TOOL_NAMES.includes(called.name)) {
|
|
99596
100090
|
return this.synthesizedCall(called.name, called.arguments);
|
|
99597
100091
|
}
|
|
99598
|
-
const
|
|
100092
|
+
const guarded = guardAndClampToolCall(params, process.env);
|
|
100093
|
+
if (!guarded.ok) {
|
|
100094
|
+
return { content: [{ type: "text", text: guarded.text }], isError: true };
|
|
100095
|
+
}
|
|
100096
|
+
const forwarded = redactToolCallParams(guarded.params);
|
|
99599
100097
|
try {
|
|
99600
100098
|
const res = await this.upstream.request("tools/call", forwarded, this.opts.toolsCallTimeoutMs ?? TOOLS_CALL_TIMEOUT_MS);
|
|
99601
100099
|
this.notifyIfRecovered();
|
|
@@ -99698,7 +100196,7 @@ function registerHindsightMcpShimCommand(program3) {
|
|
|
99698
100196
|
|
|
99699
100197
|
// src/cli/deliver-file.ts
|
|
99700
100198
|
init_client2();
|
|
99701
|
-
import { readFileSync as readFileSync87, statSync as
|
|
100199
|
+
import { readFileSync as readFileSync87, statSync as statSync53 } from "node:fs";
|
|
99702
100200
|
import { basename as basename13 } from "node:path";
|
|
99703
100201
|
|
|
99704
100202
|
// src/delivery/onedrive.ts
|
|
@@ -100037,7 +100535,7 @@ async function defaultResolveProvider() {
|
|
|
100037
100535
|
}
|
|
100038
100536
|
async function runDeliverFile(localPath, deps = {}) {
|
|
100039
100537
|
const agentName = safeAgentName(deps.agentName ?? process.env.SWITCHROOM_AGENT_NAME);
|
|
100040
|
-
const sizeOf = deps.fileSize ?? ((p) =>
|
|
100538
|
+
const sizeOf = deps.fileSize ?? ((p) => statSync53(p).size);
|
|
100041
100539
|
const read = deps.readFile ?? ((p) => new Uint8Array(readFileSync87(p)));
|
|
100042
100540
|
const resolveProvider = deps.resolveProvider ?? defaultResolveProvider;
|
|
100043
100541
|
let size;
|
|
@@ -100733,7 +101231,7 @@ init_agent_config();
|
|
|
100733
101231
|
|
|
100734
101232
|
// src/cli/agent-config-write.ts
|
|
100735
101233
|
init_agent_config();
|
|
100736
|
-
var
|
|
101234
|
+
var import_yaml22 = __toESM(require_dist(), 1);
|
|
100737
101235
|
|
|
100738
101236
|
// src/config/overlay-writer.ts
|
|
100739
101237
|
init_paths();
|
|
@@ -100746,7 +101244,7 @@ import {
|
|
|
100746
101244
|
readdirSync as readdirSync35,
|
|
100747
101245
|
readFileSync as readFileSync89,
|
|
100748
101246
|
renameSync as renameSync26,
|
|
100749
|
-
statSync as
|
|
101247
|
+
statSync as statSync54,
|
|
100750
101248
|
unlinkSync as unlinkSync20,
|
|
100751
101249
|
writeSync as writeSync10
|
|
100752
101250
|
} from "node:fs";
|
|
@@ -100790,7 +101288,7 @@ function withAgentLock(paths, fn) {
|
|
|
100790
101288
|
if (e.code !== "EEXIST")
|
|
100791
101289
|
throw err;
|
|
100792
101290
|
try {
|
|
100793
|
-
const age = Date.now() -
|
|
101291
|
+
const age = Date.now() - statSync54(paths.lockPath).mtimeMs;
|
|
100794
101292
|
if (age > 30000) {
|
|
100795
101293
|
unlinkSync20(paths.lockPath);
|
|
100796
101294
|
continue;
|
|
@@ -100934,7 +101432,7 @@ init_overlay_schema();
|
|
|
100934
101432
|
init_schema();
|
|
100935
101433
|
init_cron_unit_name();
|
|
100936
101434
|
init_lifecycle();
|
|
100937
|
-
var
|
|
101435
|
+
var import_yaml21 = __toESM(require_dist(), 1);
|
|
100938
101436
|
|
|
100939
101437
|
// src/scheduler/cron-gap.ts
|
|
100940
101438
|
function expandMinuteTerm(term) {
|
|
@@ -101024,7 +101522,7 @@ function violatesMinInterval(cron) {
|
|
|
101024
101522
|
function dryRunReconcile(input) {
|
|
101025
101523
|
let parsed;
|
|
101026
101524
|
try {
|
|
101027
|
-
parsed =
|
|
101525
|
+
parsed = import_yaml21.parse(input.yamlText);
|
|
101028
101526
|
} catch (err) {
|
|
101029
101527
|
return {
|
|
101030
101528
|
ok: false,
|
|
@@ -101223,7 +101721,7 @@ function denyPendingScheduleEntry(opts) {
|
|
|
101223
101721
|
|
|
101224
101722
|
// src/cli/agent-config-write.ts
|
|
101225
101723
|
import { existsSync as existsSync101, readFileSync as readFileSync91 } from "node:fs";
|
|
101226
|
-
import { execFileSync as
|
|
101724
|
+
import { execFileSync as execFileSync31 } from "node:child_process";
|
|
101227
101725
|
|
|
101228
101726
|
// src/scheduler/schedule-report.ts
|
|
101229
101727
|
var TIER_WEIGHT = { poll: 0, action: 0, cheap: 1, main: 5 };
|
|
@@ -101372,7 +101870,7 @@ function parseOverlayCronAndName(e) {
|
|
|
101372
101870
|
if (headerMatch)
|
|
101373
101871
|
name = headerMatch[1];
|
|
101374
101872
|
try {
|
|
101375
|
-
const doc =
|
|
101873
|
+
const doc = import_yaml22.parse(e.raw);
|
|
101376
101874
|
const first = doc?.schedule?.[0];
|
|
101377
101875
|
if (first) {
|
|
101378
101876
|
if (typeof first.cron === "string")
|
|
@@ -101408,7 +101906,7 @@ function scheduleAdd(opts) {
|
|
|
101408
101906
|
]
|
|
101409
101907
|
};
|
|
101410
101908
|
const yamlText = (() => {
|
|
101411
|
-
const body =
|
|
101909
|
+
const body = import_yaml22.stringify(doc);
|
|
101412
101910
|
const header = opts.name ? `# name: ${opts.name}
|
|
101413
101911
|
` : "";
|
|
101414
101912
|
return header + body;
|
|
@@ -101544,7 +102042,7 @@ function scheduleAddOrStage(opts) {
|
|
|
101544
102042
|
]
|
|
101545
102043
|
};
|
|
101546
102044
|
const yamlText = (opts.name ? `# name: ${opts.name}
|
|
101547
|
-
` : "") +
|
|
102045
|
+
` : "") + import_yaml22.stringify(doc);
|
|
101548
102046
|
const summary = (() => {
|
|
101549
102047
|
const parts = [`cron=${opts.cronExpr}`];
|
|
101550
102048
|
if (opts.secrets?.length)
|
|
@@ -101594,7 +102092,7 @@ function scheduleRemove(opts) {
|
|
|
101594
102092
|
break;
|
|
101595
102093
|
}
|
|
101596
102094
|
try {
|
|
101597
|
-
const parsed =
|
|
102095
|
+
const parsed = import_yaml22.parse(e.raw);
|
|
101598
102096
|
if (parsed && parsed.name === opts.name) {
|
|
101599
102097
|
match = e;
|
|
101600
102098
|
break;
|
|
@@ -101819,7 +102317,7 @@ function registerAgentConfigWriteCommands(program3) {
|
|
|
101819
102317
|
blob = existsSync101(opts.jsonl) ? readFileSync91(opts.jsonl, "utf-8") : "";
|
|
101820
102318
|
} else {
|
|
101821
102319
|
try {
|
|
101822
|
-
blob =
|
|
102320
|
+
blob = execFileSync31("docker", ["exec", `switchroom-${agent}`, "cat", "/state/agent/scheduler.jsonl"], {
|
|
101823
102321
|
encoding: "utf-8",
|
|
101824
102322
|
stdio: ["ignore", "pipe", "ignore"]
|
|
101825
102323
|
});
|
|
@@ -101847,11 +102345,11 @@ function registerAgentConfigWriteCommands(program3) {
|
|
|
101847
102345
|
}
|
|
101848
102346
|
|
|
101849
102347
|
// src/cli/agent-config-skill-write.ts
|
|
101850
|
-
var
|
|
102348
|
+
var import_yaml23 = __toESM(require_dist(), 1);
|
|
101851
102349
|
import { existsSync as existsSync102 } from "node:fs";
|
|
101852
102350
|
init_reconcile_default_skills();
|
|
101853
102351
|
init_agent_config();
|
|
101854
|
-
var
|
|
102352
|
+
var import_yaml24 = __toESM(require_dist(), 1);
|
|
101855
102353
|
import { join as join103 } from "node:path";
|
|
101856
102354
|
var MAX_SKILLS_PER_AGENT = 20;
|
|
101857
102355
|
var V1_ALLOWED_SOURCE_PREFIX = "bundled:";
|
|
@@ -101901,7 +102399,7 @@ function countCurrentSkills(agent, opts) {
|
|
|
101901
102399
|
let total = 0;
|
|
101902
102400
|
for (const e of entries) {
|
|
101903
102401
|
try {
|
|
101904
|
-
const doc =
|
|
102402
|
+
const doc = import_yaml24.parse(e.raw);
|
|
101905
102403
|
total += (doc?.skills ?? []).length;
|
|
101906
102404
|
} catch {}
|
|
101907
102405
|
}
|
|
@@ -101931,7 +102429,7 @@ function skillInstall(opts) {
|
|
|
101931
102429
|
if (!existsSync102(skillPath)) {
|
|
101932
102430
|
return err("E_SKILL_NOT_FOUND", `bundled skill not found at ${skillPath}. The operator needs to ` + `place the skill at this path before the agent can opt in.`);
|
|
101933
102431
|
}
|
|
101934
|
-
const yamlText =
|
|
102432
|
+
const yamlText = import_yaml23.stringify({ skills: [skillName] });
|
|
101935
102433
|
let path10;
|
|
101936
102434
|
try {
|
|
101937
102435
|
path10 = writeSkillsOverlayEntry(agent, slug, yamlText, { root: opts.root });
|
|
@@ -102091,6 +102589,7 @@ function registerAgentConfigSkillWriteCommands(program3) {
|
|
|
102091
102589
|
|
|
102092
102590
|
// src/cli/skill.ts
|
|
102093
102591
|
import {
|
|
102592
|
+
chmodSync as chmodSync17,
|
|
102094
102593
|
closeSync as closeSync20,
|
|
102095
102594
|
existsSync as existsSync103,
|
|
102096
102595
|
lstatSync as lstatSync12,
|
|
@@ -102102,7 +102601,7 @@ import {
|
|
|
102102
102601
|
realpathSync as realpathSync7,
|
|
102103
102602
|
renameSync as renameSync28,
|
|
102104
102603
|
rmSync as rmSync19,
|
|
102105
|
-
statSync as
|
|
102604
|
+
statSync as statSync55,
|
|
102106
102605
|
writeFileSync as writeFileSync43
|
|
102107
102606
|
} from "node:fs";
|
|
102108
102607
|
import { tmpdir as tmpdir6, homedir as homedir54 } from "node:os";
|
|
@@ -102110,7 +102609,7 @@ import { dirname as dirname41, join as join104, relative as relative4, resolve a
|
|
|
102110
102609
|
import { spawnSync as spawnSync21 } from "node:child_process";
|
|
102111
102610
|
|
|
102112
102611
|
// src/cli/skill-common.ts
|
|
102113
|
-
var
|
|
102612
|
+
var import_yaml25 = __toESM(require_dist(), 1);
|
|
102114
102613
|
var MAX_SKILLS_PER_AGENT2 = 20;
|
|
102115
102614
|
var MAX_FILE_BYTES = 256 * 1024;
|
|
102116
102615
|
var MAX_SKILL_BYTES = 2 * 1024 * 1024;
|
|
@@ -102215,7 +102714,7 @@ function validateSkillMd(content, expectedName) {
|
|
|
102215
102714
|
}
|
|
102216
102715
|
let parsed;
|
|
102217
102716
|
try {
|
|
102218
|
-
parsed =
|
|
102717
|
+
parsed = import_yaml25.parse(fmText);
|
|
102219
102718
|
} catch (e) {
|
|
102220
102719
|
return authorErr("E_SKILL_INVALID_FRONTMATTER", `frontmatter is not valid YAML: ${e.message}`);
|
|
102221
102720
|
}
|
|
@@ -102373,7 +102872,7 @@ function isTarballPath(p) {
|
|
|
102373
102872
|
}
|
|
102374
102873
|
function loadFromDir(dir) {
|
|
102375
102874
|
const abs = realpathSync7(dir);
|
|
102376
|
-
if (!
|
|
102875
|
+
if (!statSync55(abs).isDirectory()) {
|
|
102377
102876
|
fail4(`--from path is not a directory: ${dir}`);
|
|
102378
102877
|
}
|
|
102379
102878
|
const files = {};
|
|
@@ -102573,6 +103072,7 @@ function writePayload(poolDir, name, files) {
|
|
|
102573
103072
|
fail4(`refusing to overwrite symlink at ${target}; investigate manually`);
|
|
102574
103073
|
}
|
|
102575
103074
|
const staging = mkdtempSync5(join104(poolDir, `.skill-apply-stage-${name}-`));
|
|
103075
|
+
chmodSync17(staging, 493);
|
|
102576
103076
|
let oldRename = null;
|
|
102577
103077
|
try {
|
|
102578
103078
|
for (const [path10, content] of Object.entries(files)) {
|
|
@@ -102633,7 +103133,7 @@ function registerSkillCommand(program3) {
|
|
|
102633
103133
|
if (!existsSync103(fromPath)) {
|
|
102634
103134
|
fail4(`--from path does not exist: ${opts.from}`);
|
|
102635
103135
|
}
|
|
102636
|
-
const st =
|
|
103136
|
+
const st = statSync55(fromPath);
|
|
102637
103137
|
if (st.isDirectory()) {
|
|
102638
103138
|
files = loadFromDir(fromPath);
|
|
102639
103139
|
} else if (isTarballPath(fromPath)) {
|
|
@@ -102685,6 +103185,7 @@ function sumBytes(files) {
|
|
|
102685
103185
|
// src/cli/skill-personal.ts
|
|
102686
103186
|
init_esm();
|
|
102687
103187
|
import {
|
|
103188
|
+
chmodSync as chmodSync18,
|
|
102688
103189
|
closeSync as closeSync21,
|
|
102689
103190
|
existsSync as existsSync104,
|
|
102690
103191
|
lstatSync as lstatSync13,
|
|
@@ -102695,7 +103196,7 @@ import {
|
|
|
102695
103196
|
readdirSync as readdirSync38,
|
|
102696
103197
|
renameSync as renameSync29,
|
|
102697
103198
|
rmSync as rmSync20,
|
|
102698
|
-
statSync as
|
|
103199
|
+
statSync as statSync56,
|
|
102699
103200
|
utimesSync,
|
|
102700
103201
|
writeFileSync as writeFileSync44
|
|
102701
103202
|
} from "node:fs";
|
|
@@ -102779,6 +103280,7 @@ function mirrorToConfigRepo(agent, name, liveSkillDir) {
|
|
|
102779
103280
|
}
|
|
102780
103281
|
};
|
|
102781
103282
|
walk2(liveSkillDir, staging);
|
|
103283
|
+
chmodSync18(staging, 493);
|
|
102782
103284
|
if (existsSync104(dest)) {
|
|
102783
103285
|
const prior = join105(configSkillsRoot, `.${name}-prior-${Date.now()}`);
|
|
102784
103286
|
renameSync29(dest, prior);
|
|
@@ -102851,7 +103353,7 @@ function readStdinSync2() {
|
|
|
102851
103353
|
}
|
|
102852
103354
|
function loadFromDir2(dir) {
|
|
102853
103355
|
const abs = resolve59(dir);
|
|
102854
|
-
if (!
|
|
103356
|
+
if (!statSync56(abs).isDirectory()) {
|
|
102855
103357
|
fail5(`--from path is not a directory: ${dir}`);
|
|
102856
103358
|
}
|
|
102857
103359
|
const files = {};
|
|
@@ -102937,7 +103439,7 @@ function sweepTrash(agentsRoot, agent) {
|
|
|
102937
103439
|
continue;
|
|
102938
103440
|
const entPath = join105(trash, ent.name);
|
|
102939
103441
|
try {
|
|
102940
|
-
const st =
|
|
103442
|
+
const st = statSync56(entPath);
|
|
102941
103443
|
if (now - st.mtimeMs > TRASH_TTL_MS) {
|
|
102942
103444
|
rmSync20(entPath, { recursive: true, force: true });
|
|
102943
103445
|
}
|
|
@@ -102957,6 +103459,7 @@ function writePersonalSkill(targetDir, files) {
|
|
|
102957
103459
|
}
|
|
102958
103460
|
mkdirSync61(dirname42(targetDir), { recursive: true, mode: 493 });
|
|
102959
103461
|
const staging = mkdtempSync6(join105(dirname42(targetDir), `.skill-personal-stage-`));
|
|
103462
|
+
chmodSync18(staging, 493);
|
|
102960
103463
|
let oldRename = null;
|
|
102961
103464
|
try {
|
|
102962
103465
|
for (const [path10, content] of Object.entries(files)) {
|
|
@@ -103062,7 +103565,7 @@ function loadFiles(opts) {
|
|
|
103062
103565
|
if (!existsSync104(p)) {
|
|
103063
103566
|
fail5(`--from path does not exist: ${opts.from}`);
|
|
103064
103567
|
}
|
|
103065
|
-
const st =
|
|
103568
|
+
const st = statSync56(p);
|
|
103066
103569
|
if (st.isDirectory()) {
|
|
103067
103570
|
return loadFromDir2(p);
|
|
103068
103571
|
}
|
|
@@ -103283,7 +103786,7 @@ function listPersonalAction(opts) {
|
|
|
103283
103786
|
if (e.isFile()) {
|
|
103284
103787
|
fileCount += 1;
|
|
103285
103788
|
try {
|
|
103286
|
-
totalBytes +=
|
|
103789
|
+
totalBytes += statSync56(join105(sub, e.name)).size;
|
|
103287
103790
|
} catch {}
|
|
103288
103791
|
} else if (e.isDirectory()) {
|
|
103289
103792
|
walk2(join105(sub, e.name));
|
|
@@ -103400,8 +103903,8 @@ function registerSelfImproveProposeSkillCommand(program3) {
|
|
|
103400
103903
|
// src/cli/skill-search.ts
|
|
103401
103904
|
init_esm();
|
|
103402
103905
|
init_helpers();
|
|
103403
|
-
var
|
|
103404
|
-
import { existsSync as existsSync105, readdirSync as readdirSync39, readFileSync as readFileSync95, statSync as
|
|
103906
|
+
var import_yaml26 = __toESM(require_dist(), 1);
|
|
103907
|
+
import { existsSync as existsSync105, readdirSync as readdirSync39, readFileSync as readFileSync95, statSync as statSync57 } from "node:fs";
|
|
103405
103908
|
import { homedir as homedir57 } from "node:os";
|
|
103406
103909
|
import { join as join107, resolve as resolve60 } from "node:path";
|
|
103407
103910
|
var PERSONAL_PREFIX2 = "personal-";
|
|
@@ -103440,7 +103943,7 @@ function readSkillFrontmatter(skillDir) {
|
|
|
103440
103943
|
const fmText = rest.slice(0, endIdx);
|
|
103441
103944
|
let parsed;
|
|
103442
103945
|
try {
|
|
103443
|
-
parsed =
|
|
103946
|
+
parsed = import_yaml26.parse(fmText);
|
|
103444
103947
|
} catch (e) {
|
|
103445
103948
|
return { error: `yaml parse: ${e.message}` };
|
|
103446
103949
|
}
|
|
@@ -103452,7 +103955,7 @@ function readSkillFrontmatter(skillDir) {
|
|
|
103452
103955
|
function statSkillMd(skillDir) {
|
|
103453
103956
|
const mdPath = join107(skillDir, "SKILL.md");
|
|
103454
103957
|
try {
|
|
103455
|
-
const st =
|
|
103958
|
+
const st = statSync57(mdPath);
|
|
103456
103959
|
return { size: st.size, mtime: st.mtime.toISOString() };
|
|
103457
103960
|
} catch {
|
|
103458
103961
|
return null;
|
|
@@ -103476,7 +103979,7 @@ function listPersonalSkills(agent, agentsRoot = defaultAgentsRoot()) {
|
|
|
103476
103979
|
continue;
|
|
103477
103980
|
const dirPath = join107(skillsDir, ent);
|
|
103478
103981
|
try {
|
|
103479
|
-
if (!
|
|
103982
|
+
if (!statSync57(dirPath).isDirectory())
|
|
103480
103983
|
continue;
|
|
103481
103984
|
} catch {
|
|
103482
103985
|
continue;
|
|
@@ -103516,7 +104019,7 @@ function listSharedSkills(sharedRoot = defaultSharedRoot2()) {
|
|
|
103516
104019
|
continue;
|
|
103517
104020
|
const dirPath = join107(sharedRoot, ent);
|
|
103518
104021
|
try {
|
|
103519
|
-
if (!
|
|
104022
|
+
if (!statSync57(dirPath).isDirectory())
|
|
103520
104023
|
continue;
|
|
103521
104024
|
} catch {
|
|
103522
104025
|
continue;
|
|
@@ -103552,7 +104055,7 @@ function listBundledSkills(bundledRoot = defaultBundledRoot2()) {
|
|
|
103552
104055
|
continue;
|
|
103553
104056
|
const dirPath = join107(bundledRoot, ent);
|
|
103554
104057
|
try {
|
|
103555
|
-
if (!
|
|
104058
|
+
if (!statSync57(dirPath).isDirectory())
|
|
103556
104059
|
continue;
|
|
103557
104060
|
} catch {
|
|
103558
104061
|
continue;
|
|
@@ -103700,7 +104203,7 @@ import {
|
|
|
103700
104203
|
mkdirSync as mkdirSync62,
|
|
103701
104204
|
readdirSync as readdirSync40,
|
|
103702
104205
|
writeFileSync as writeFileSync45,
|
|
103703
|
-
statSync as
|
|
104206
|
+
statSync as statSync58,
|
|
103704
104207
|
lstatSync as lstatSync14,
|
|
103705
104208
|
realpathSync as realpathSync8,
|
|
103706
104209
|
copyFileSync as copyFileSync14
|
|
@@ -103812,6 +104315,10 @@ services:
|
|
|
103812
104315
|
# HAProxy writes its runtime state/pidfile under /run; read_only rootfs
|
|
103813
104316
|
# otherwise makes it crash-loop on boot.
|
|
103814
104317
|
- /run
|
|
104318
|
+
# The tecnativa/docker-socket-proxy entrypoint renders /tmp/haproxy.cfg
|
|
104319
|
+
# on boot; with a read_only rootfs and no /tmp tmpfs it can't write the
|
|
104320
|
+
# generated config and crash-loops before HAProxy ever starts.
|
|
104321
|
+
- /tmp
|
|
103815
104322
|
cap_drop:
|
|
103816
104323
|
- ALL
|
|
103817
104324
|
cap_add:
|
|
@@ -104213,7 +104720,7 @@ function doStatus() {
|
|
|
104213
104720
|
continue;
|
|
104214
104721
|
const sockPath = join108(dir, name, "sock");
|
|
104215
104722
|
if (existsSync107(sockPath)) {
|
|
104216
|
-
const st =
|
|
104723
|
+
const st = statSync58(sockPath);
|
|
104217
104724
|
if ((st.mode & 61440) === 49152) {
|
|
104218
104725
|
entries.push(`${name} \u2192 ${sockPath}`);
|
|
104219
104726
|
}
|
|
@@ -104304,7 +104811,7 @@ init_source();
|
|
|
104304
104811
|
init_helpers();
|
|
104305
104812
|
init_operator_uid();
|
|
104306
104813
|
init_compose();
|
|
104307
|
-
import { chownSync as
|
|
104814
|
+
import { chownSync as chownSync11, existsSync as existsSync108, mkdirSync as mkdirSync63, writeFileSync as writeFileSync46, copyFileSync as copyFileSync15 } from "node:fs";
|
|
104308
104815
|
import { homedir as homedir59 } from "node:os";
|
|
104309
104816
|
import { join as join109 } from "node:path";
|
|
104310
104817
|
import { spawnSync as spawnSync26 } from "node:child_process";
|
|
@@ -104484,10 +104991,10 @@ async function doInstall2(opts, program3) {
|
|
|
104484
104991
|
writeFileSync46(composePath, yaml, "utf8");
|
|
104485
104992
|
try {
|
|
104486
104993
|
if (typeof process.geteuid === "function" && process.geteuid() === 0) {
|
|
104487
|
-
|
|
104488
|
-
|
|
104994
|
+
chownSync11(dir, operatorUid, operatorUid);
|
|
104995
|
+
chownSync11(composePath, operatorUid, operatorUid);
|
|
104489
104996
|
if (bak)
|
|
104490
|
-
|
|
104997
|
+
chownSync11(bak, operatorUid, operatorUid);
|
|
104491
104998
|
}
|
|
104492
104999
|
} catch {}
|
|
104493
105000
|
console.log(source_default.green(` \u2713 Wrote ${composePath}`));
|
|
@@ -105104,7 +105611,7 @@ init_install_cron();
|
|
|
105104
105611
|
|
|
105105
105612
|
// src/hindsight-watch/probe.ts
|
|
105106
105613
|
import { spawnSync as spawnSync28 } from "node:child_process";
|
|
105107
|
-
import { readFileSync as readFileSync100, readdirSync as readdirSync44, statSync as
|
|
105614
|
+
import { readFileSync as readFileSync100, readdirSync as readdirSync44, statSync as statSync60 } from "node:fs";
|
|
105108
105615
|
import { homedir as homedir62 } from "node:os";
|
|
105109
105616
|
import { resolve as resolve62 } from "node:path";
|
|
105110
105617
|
|
|
@@ -105232,7 +105739,7 @@ function readLlmSignals(series) {
|
|
|
105232
105739
|
}
|
|
105233
105740
|
|
|
105234
105741
|
// src/hindsight-watch/recall-log.ts
|
|
105235
|
-
import { closeSync as closeSync22, existsSync as existsSync112, openSync as openSync22, readdirSync as readdirSync43, readSync as readSync6, statSync as
|
|
105742
|
+
import { closeSync as closeSync22, existsSync as existsSync112, openSync as openSync22, readdirSync as readdirSync43, readSync as readSync6, statSync as statSync59 } from "node:fs";
|
|
105236
105743
|
import { join as join111 } from "node:path";
|
|
105237
105744
|
var RECALL_WINDOW_ROWS = 200;
|
|
105238
105745
|
var RECALL_MAX_TAIL_BYTES = 1024 * 1024;
|
|
@@ -105247,7 +105754,7 @@ function readRecallLogTail2(path10, windowRows = RECALL_WINDOW_ROWS) {
|
|
|
105247
105754
|
let truncatedHead = false;
|
|
105248
105755
|
let fd;
|
|
105249
105756
|
try {
|
|
105250
|
-
const size =
|
|
105757
|
+
const size = statSync59(path10).size;
|
|
105251
105758
|
const start = Math.max(0, size - RECALL_MAX_TAIL_BYTES);
|
|
105252
105759
|
truncatedHead = start > 0;
|
|
105253
105760
|
const length = size - start;
|
|
@@ -105479,7 +105986,7 @@ function readDropCount(hindsightDir) {
|
|
|
105479
105986
|
const path10 = resolve62(hindsightDir, "pending-drops.json");
|
|
105480
105987
|
let parsed;
|
|
105481
105988
|
try {
|
|
105482
|
-
if (
|
|
105989
|
+
if (statSync60(path10).size > DROPS_LEDGER_MAX_BYTES)
|
|
105483
105990
|
return 0;
|
|
105484
105991
|
parsed = JSON.parse(readFileSync100(path10, "utf8"));
|
|
105485
105992
|
} catch {
|
|
@@ -105504,6 +106011,7 @@ function probeSpool(agentsDir = resolveAgentsDir2()) {
|
|
|
105504
106011
|
let dead = 0;
|
|
105505
106012
|
let evicted = 0;
|
|
105506
106013
|
let drops = 0;
|
|
106014
|
+
let duplicates = 0;
|
|
105507
106015
|
let agents = 0;
|
|
105508
106016
|
for (const name of names) {
|
|
105509
106017
|
const hindsightDir = resolve62(agentsDir, name, "home", ".hindsight");
|
|
@@ -105532,9 +106040,15 @@ function probeSpool(agentsDir = resolveAgentsDir2()) {
|
|
|
105532
106040
|
dead++;
|
|
105533
106041
|
}
|
|
105534
106042
|
} catch {}
|
|
106043
|
+
try {
|
|
106044
|
+
for (const f of readdirSync44(resolve62(hindsightDir, "pending-duplicate"))) {
|
|
106045
|
+
if (f.endsWith(".json"))
|
|
106046
|
+
duplicates++;
|
|
106047
|
+
}
|
|
106048
|
+
} catch {}
|
|
105535
106049
|
drops += readDropCount(hindsightDir);
|
|
105536
106050
|
}
|
|
105537
|
-
return { pending, dead, evicted, drops, agents };
|
|
106051
|
+
return { pending, dead, evicted, drops, duplicates, agents };
|
|
105538
106052
|
}
|
|
105539
106053
|
var PSQL_BOOTSTRAP_SH = `
|
|
105540
106054
|
set -e
|
|
@@ -105711,6 +106225,7 @@ async function probeOnce(opts = {}) {
|
|
|
105711
106225
|
dead: spool.dead,
|
|
105712
106226
|
evicted: spool.evicted,
|
|
105713
106227
|
drops: spool.drops,
|
|
106228
|
+
duplicates: spool.duplicates,
|
|
105714
106229
|
restartCount: container.restartCount,
|
|
105715
106230
|
startedAt: container.startedAt,
|
|
105716
106231
|
health: container.health,
|
|
@@ -105817,7 +106332,9 @@ function evaluateRetainLoss(ring) {
|
|
|
105817
106332
|
["dropped retain", last.drops - prev.drops]
|
|
105818
106333
|
];
|
|
105819
106334
|
const risen = channels.filter(([, added]) => added > 0);
|
|
105820
|
-
const
|
|
106335
|
+
const dup = last.duplicates;
|
|
106336
|
+
const dupNote = typeof dup === "number" && dup > 0 ? `, ${dup} collapsed-duplicate (not loss)` : "";
|
|
106337
|
+
const totals = `totals: ${last.dead} dead, ${last.evicted} evicted, ${last.drops} dropped${dupNote}`;
|
|
105821
106338
|
if (risen.length > 0) {
|
|
105822
106339
|
return {
|
|
105823
106340
|
signal,
|
|
@@ -105829,7 +106346,8 @@ function evaluateRetainLoss(ring) {
|
|
|
105829
106346
|
dropsAdded: last.drops - prev.drops,
|
|
105830
106347
|
dead: last.dead,
|
|
105831
106348
|
evicted: last.evicted,
|
|
105832
|
-
drops: last.drops
|
|
106349
|
+
drops: last.drops,
|
|
106350
|
+
duplicates: dup ?? 0
|
|
105833
106351
|
}
|
|
105834
106352
|
};
|
|
105835
106353
|
}
|
|
@@ -105843,7 +106361,8 @@ function evaluateRetainLoss(ring) {
|
|
|
105843
106361
|
dropsAdded: 0,
|
|
105844
106362
|
dead: last.dead,
|
|
105845
106363
|
evicted: last.evicted,
|
|
105846
|
-
drops: last.drops
|
|
106364
|
+
drops: last.drops,
|
|
106365
|
+
duplicates: dup ?? 0
|
|
105847
106366
|
}
|
|
105848
106367
|
};
|
|
105849
106368
|
}
|
|
@@ -106033,7 +106552,7 @@ function evaluateConsolidationQueueAge(ring) {
|
|
|
106033
106552
|
return {
|
|
106034
106553
|
signal,
|
|
106035
106554
|
state: "ok",
|
|
106036
|
-
detail: "
|
|
106555
|
+
detail: "no pending/processing operations (failures are not counted here \u2014 " + "see consolidation-failure-streak)",
|
|
106037
106556
|
measured: { pending: 0, oldestAgeS: 0 }
|
|
106038
106557
|
};
|
|
106039
106558
|
}
|
|
@@ -106513,8 +107032,27 @@ function runInstallCron(cronUser) {
|
|
|
106513
107032
|
`) + " This path needs root; re-run under sudo with `--cron-user <operator>`.\n");
|
|
106514
107033
|
return 1;
|
|
106515
107034
|
}
|
|
107035
|
+
let logStatus;
|
|
107036
|
+
try {
|
|
107037
|
+
const logRes = ensureLogFile({ user });
|
|
107038
|
+
logStatus = logRes.status === "created" ? `created ${logRes.path} (owned by ${user})` : `${logRes.path} already present`;
|
|
107039
|
+
} catch (e) {
|
|
107040
|
+
process.stderr.write(source_default.red(`hindsight-watch: could not provision the log file: ${e.message}
|
|
107041
|
+
`) + ` The cron redirects into ${CRON_LOG_PATH} and would die at ` + `the redirection every tick.
|
|
107042
|
+
This path needs root; re-run under sudo with ` + "`--cron-user <operator>`.\n");
|
|
107043
|
+
return 1;
|
|
107044
|
+
}
|
|
107045
|
+
let rotateStatus;
|
|
107046
|
+
try {
|
|
107047
|
+
const rotateRes = installLogrotate({ user });
|
|
107048
|
+
rotateStatus = rotateRes.status === "installed" ? `installed ${rotateRes.path}` : `${rotateRes.path} already up to date`;
|
|
107049
|
+
} catch (e) {
|
|
107050
|
+
rotateStatus = source_default.yellow(`logrotate drop-in NOT written (${e.message}) \u2014 log growth is unbounded`);
|
|
107051
|
+
}
|
|
106516
107052
|
const verb = res.status === "installed" ? "installed" : "already up to date";
|
|
106517
107053
|
process.stdout.write(`${source_default.green("\u2713")} hindsight-watch cron ${verb} at ${res.path} ` + `(${CRON_SCHEDULE}, as ${user})
|
|
107054
|
+
` + ` log: ${logStatus}
|
|
107055
|
+
` + ` logrotate: ${rotateStatus}
|
|
106518
107056
|
` + ` Verify with: switchroom doctor | switchroom hindsight-watch --dry-run
|
|
106519
107057
|
`);
|
|
106520
107058
|
return 0;
|