switchroom 0.19.40 → 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 +810 -241
- package/dist/host-control/main.js +94 -19
- 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/bridge/bridge.ts +2 -2
- package/telegram-plugin/dist/bridge/bridge.js +2 -2
- package/telegram-plugin/dist/gateway/gateway.js +332 -124
- package/telegram-plugin/dist/server.js +2 -2
- package/telegram-plugin/gateway/approval-callback-consume-record.test.ts +132 -0
- package/telegram-plugin/gateway/checklist-fallback.ts +370 -0
- package/telegram-plugin/gateway/gateway.ts +76 -76
- 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/gateway/turn-record-status.ts +80 -0
- package/telegram-plugin/tests/checklist-fallback.test.ts +317 -0
- package/telegram-plugin/tests/framework-fallback-drains-parked.test.ts +134 -0
- package/telegram-plugin/tests/gateway-outbound-redact.test.ts +10 -6
- package/telegram-plugin/tests/helpers/liveness-wiring-fixture.ts +3 -0
- package/telegram-plugin/tests/turn-record-status.test.ts +62 -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;
|
|
@@ -70125,6 +70561,9 @@ var init_server4 = __esm(() => {
|
|
|
70125
70561
|
];
|
|
70126
70562
|
});
|
|
70127
70563
|
|
|
70564
|
+
// telegram-plugin/gateway/turn-record-status.ts
|
|
70565
|
+
var ROUTE_FIELD_SHIP_TS = 1785456000;
|
|
70566
|
+
|
|
70128
70567
|
// src/fleet-health/detect.ts
|
|
70129
70568
|
function parseTurns(text) {
|
|
70130
70569
|
const out = [];
|
|
@@ -70160,12 +70599,14 @@ function isoFromTs(ts) {
|
|
|
70160
70599
|
function detectTurnFindings(agent, turns, opts = {}) {
|
|
70161
70600
|
const findings = [];
|
|
70162
70601
|
const silentNoopFloorTs = opts.silentNoopFloorTs ?? SILENT_NOOP_FLOOR_TS;
|
|
70602
|
+
const routeFieldShipTs = opts.routeFieldShipTs ?? ROUTE_FIELD_SHIP_TS;
|
|
70163
70603
|
for (const t of ownedTurns(agent, turns)) {
|
|
70164
70604
|
const tid = typeof t.turn_id === "string" ? t.turn_id : "?";
|
|
70165
70605
|
const st = t.status;
|
|
70166
70606
|
const tl = typeof t.tools === "number" ? t.tools : 0;
|
|
70167
70607
|
const dur = typeof t.duration_ms === "number" ? t.duration_ms : 0;
|
|
70168
70608
|
const synthetic = tid.includes("synthetic-");
|
|
70609
|
+
const route = typeof t.route === "string" ? t.route : undefined;
|
|
70169
70610
|
const ts = isoFromTs(t.ts);
|
|
70170
70611
|
if (st === "send_failed") {
|
|
70171
70612
|
findings.push({
|
|
@@ -70194,13 +70635,33 @@ function detectTurnFindings(agent, turns, opts = {}) {
|
|
|
70194
70635
|
});
|
|
70195
70636
|
}
|
|
70196
70637
|
if (st === "complete" && tl === 0 && !synthetic && t.ts != null && t.ts >= silentNoopFloorTs) {
|
|
70197
|
-
|
|
70198
|
-
|
|
70199
|
-
|
|
70200
|
-
|
|
70201
|
-
|
|
70202
|
-
|
|
70203
|
-
|
|
70638
|
+
if (route === "flush") {
|
|
70639
|
+
findings.push({
|
|
70640
|
+
signal: "flush-recovered-turn",
|
|
70641
|
+
agent,
|
|
70642
|
+
turn_id: tid,
|
|
70643
|
+
log_pointer: `turns.jsonl:${tid} tools=0 route=flush`,
|
|
70644
|
+
ts
|
|
70645
|
+
});
|
|
70646
|
+
} else if (route === "reply" || route === "stream") {} else if (route === "none") {
|
|
70647
|
+
findings.push({
|
|
70648
|
+
signal: "silent-no-op-candidate",
|
|
70649
|
+
agent,
|
|
70650
|
+
turn_id: tid,
|
|
70651
|
+
log_pointer: `turns.jsonl:${tid} tools=0 route=none`,
|
|
70652
|
+
ts
|
|
70653
|
+
});
|
|
70654
|
+
} else {
|
|
70655
|
+
if (t.ts >= routeFieldShipTs) {
|
|
70656
|
+
findings.push({
|
|
70657
|
+
signal: "silent-no-op-candidate",
|
|
70658
|
+
agent,
|
|
70659
|
+
turn_id: tid,
|
|
70660
|
+
log_pointer: `turns.jsonl:${tid} tools=0`,
|
|
70661
|
+
ts
|
|
70662
|
+
});
|
|
70663
|
+
}
|
|
70664
|
+
}
|
|
70204
70665
|
}
|
|
70205
70666
|
}
|
|
70206
70667
|
return findings;
|
|
@@ -70314,6 +70775,12 @@ var init_mapping = __esm(() => {
|
|
|
70314
70775
|
job_spec: "know-what-my-agent-is-doing",
|
|
70315
70776
|
signature: "silent-no-op:completed-zero-tools"
|
|
70316
70777
|
},
|
|
70778
|
+
"flush-recovered-turn": {
|
|
70779
|
+
failure_mode: "drift",
|
|
70780
|
+
severity: 1,
|
|
70781
|
+
job_spec: "know-what-my-agent-is-doing",
|
|
70782
|
+
signature: "flush-recovered:completed-zero-tools-route-flush"
|
|
70783
|
+
},
|
|
70317
70784
|
"duplicate-delivery-represent": {
|
|
70318
70785
|
failure_mode: "duplicate",
|
|
70319
70786
|
severity: 2,
|
|
@@ -70630,14 +71097,14 @@ function detectHeaderMisconfig(parsed) {
|
|
|
70630
71097
|
}
|
|
70631
71098
|
function parseLitellmConfig(text) {
|
|
70632
71099
|
try {
|
|
70633
|
-
return
|
|
71100
|
+
return import_yaml27.parse(text);
|
|
70634
71101
|
} catch {
|
|
70635
71102
|
return null;
|
|
70636
71103
|
}
|
|
70637
71104
|
}
|
|
70638
|
-
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;
|
|
70639
71106
|
var init_header_passthrough_guard = __esm(() => {
|
|
70640
|
-
|
|
71107
|
+
import_yaml27 = __toESM(require_dist(), 1);
|
|
70641
71108
|
BARE_ANTHROPIC_FAMILY_ALIASES = new Set([
|
|
70642
71109
|
"opus",
|
|
70643
71110
|
"sonnet",
|
|
@@ -73866,6 +74333,9 @@ function summarizeReconcileBatch(totalCount, failures) {
|
|
|
73866
74333
|
lines: failures.map((f) => `${f.name}: ${f.error}`)
|
|
73867
74334
|
};
|
|
73868
74335
|
}
|
|
74336
|
+
function shouldAutoRestartAfterReconcile(opts, restartRequiredCount) {
|
|
74337
|
+
return opts.restart !== false && restartRequiredCount > 0;
|
|
74338
|
+
}
|
|
73869
74339
|
function resolveLogsTail(lines) {
|
|
73870
74340
|
const DEFAULT_TAIL = 50;
|
|
73871
74341
|
const MAX_TAIL = 1000;
|
|
@@ -74714,7 +75184,7 @@ Scaffolding agent: ${name}
|
|
|
74714
75184
|
}
|
|
74715
75185
|
}
|
|
74716
75186
|
const needsRestart = restartRequired.length > 0 || staleTillRestart.length > 0;
|
|
74717
|
-
const autoRestart = restartRequired.length
|
|
75187
|
+
const autoRestart = shouldAutoRestartAfterReconcile(opts, restartRequired.length);
|
|
74718
75188
|
if (!needsRestart && hot.length > 0) {
|
|
74719
75189
|
console.log(source_default.green(`
|
|
74720
75190
|
(no restart needed, changes active next turn)`));
|
|
@@ -74734,7 +75204,7 @@ Restart: switchroom agent restart ${n}`));
|
|
|
74734
75204
|
}
|
|
74735
75205
|
}
|
|
74736
75206
|
const changesBySemantics = result.changesBySemantics;
|
|
74737
|
-
const autoRestartNeeded =
|
|
75207
|
+
const autoRestartNeeded = changesBySemantics !== undefined && shouldAutoRestartAfterReconcile(opts, changesBySemantics.restartRequired.length);
|
|
74738
75208
|
const shouldRestart = (opts.restart || opts.gracefulRestart || autoRestartNeeded) && allChanges.length > 0;
|
|
74739
75209
|
if (shouldRestart) {
|
|
74740
75210
|
try {
|
|
@@ -85866,14 +86336,21 @@ ${name}:`)];
|
|
|
85866
86336
|
const max = memCounts.length > 0 ? Math.max(...memCounts) : null;
|
|
85867
86337
|
const medians = entries.map((e) => e.injected_score_median).filter((n) => typeof n === "number");
|
|
85868
86338
|
const avgScore = medians.length > 0 ? Math.round(medians.reduce((s, n) => s + n, 0) / medians.length * 100) / 100 : null;
|
|
85869
|
-
|
|
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}`) : "")));
|
|
85870
86341
|
for (const e of entries) {
|
|
85871
86342
|
const flag = e.cache_hit ? source_default.cyan("CACHE") : e.capped ? source_default.yellow("CAP") : source_default.green("OK");
|
|
85872
86343
|
const dem = e.demoted_count && e.demoted_count > 0 ? source_default.dim(` -${e.demoted_count}d`) : "";
|
|
85873
86344
|
const omitted = typeof e.directives_omitted === "number" && e.directives_omitted > 0 ? source_default.yellow(` +${e.directives_omitted}omitted`) : "";
|
|
85874
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
|
+
})() : "";
|
|
85875
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)}`) : "";
|
|
85876
|
-
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}`);
|
|
85877
86354
|
}
|
|
85878
86355
|
return lines;
|
|
85879
86356
|
}
|
|
@@ -92383,24 +92860,24 @@ init_source();
|
|
|
92383
92860
|
init_loader();
|
|
92384
92861
|
init_lifecycle();
|
|
92385
92862
|
init_compose_env();
|
|
92386
|
-
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";
|
|
92387
92864
|
import { spawnSync as spawnSync17 } from "node:child_process";
|
|
92388
92865
|
import { join as join84, dirname as dirname34, resolve as resolve45 } from "node:path";
|
|
92389
92866
|
import { homedir as homedir44 } from "node:os";
|
|
92390
92867
|
|
|
92391
92868
|
// src/cli/release-yaml.ts
|
|
92392
|
-
var
|
|
92869
|
+
var import_yaml20 = __toESM(require_dist(), 1);
|
|
92393
92870
|
function getReleasePinFromConfig(yamlText) {
|
|
92394
92871
|
let v;
|
|
92395
92872
|
try {
|
|
92396
|
-
v =
|
|
92873
|
+
v = import_yaml20.parseDocument(yamlText).getIn(["release", "pin"]);
|
|
92397
92874
|
} catch {
|
|
92398
92875
|
return;
|
|
92399
92876
|
}
|
|
92400
92877
|
return typeof v === "string" ? v : undefined;
|
|
92401
92878
|
}
|
|
92402
92879
|
function setReleasePinInConfig(yamlText, pin, now = new Date().toISOString().slice(0, 10)) {
|
|
92403
|
-
const doc =
|
|
92880
|
+
const doc = import_yaml20.parseDocument(yamlText);
|
|
92404
92881
|
const currentPin = doc.getIn(["release", "pin"]);
|
|
92405
92882
|
const hasChannel = doc.hasIn(["release", "channel"]);
|
|
92406
92883
|
if (currentPin === pin && !hasChannel)
|
|
@@ -92409,31 +92886,31 @@ function setReleasePinInConfig(yamlText, pin, now = new Date().toISOString().sli
|
|
|
92409
92886
|
doc.deleteIn(["release", "channel"]);
|
|
92410
92887
|
doc.setIn(["release", "pin"], pin);
|
|
92411
92888
|
const node = doc.getIn(["release", "pin"], true);
|
|
92412
|
-
if (
|
|
92889
|
+
if (import_yaml20.isScalar(node)) {
|
|
92413
92890
|
node.comment = ` ${now}: rolled by switchroom rollout`;
|
|
92414
92891
|
}
|
|
92415
92892
|
return String(doc);
|
|
92416
92893
|
}
|
|
92417
92894
|
function deleteReleasePinInConfig(yamlText) {
|
|
92418
|
-
const doc =
|
|
92895
|
+
const doc = import_yaml20.parseDocument(yamlText);
|
|
92419
92896
|
if (!doc.hasIn(["release", "pin"]))
|
|
92420
92897
|
return yamlText;
|
|
92421
92898
|
doc.deleteIn(["release", "pin"]);
|
|
92422
92899
|
const rel = doc.getIn(["release"]);
|
|
92423
|
-
if (
|
|
92900
|
+
if (import_yaml20.isMap(rel) && rel.items.length === 0 && !releaseBlockIsCommented(doc)) {
|
|
92424
92901
|
doc.delete("release");
|
|
92425
92902
|
}
|
|
92426
92903
|
return String(doc);
|
|
92427
92904
|
}
|
|
92428
92905
|
function releaseBlockIsCommented(doc) {
|
|
92429
92906
|
const contents = doc.contents;
|
|
92430
|
-
if (!
|
|
92907
|
+
if (!import_yaml20.isMap(contents))
|
|
92431
92908
|
return false;
|
|
92432
92909
|
for (const item of contents.items) {
|
|
92433
|
-
if (!
|
|
92910
|
+
if (!import_yaml20.isPair(item))
|
|
92434
92911
|
continue;
|
|
92435
92912
|
const key = item.key;
|
|
92436
|
-
if (!
|
|
92913
|
+
if (!import_yaml20.isScalar(key) || key.value !== "release")
|
|
92437
92914
|
continue;
|
|
92438
92915
|
const nodes = [key, item.value];
|
|
92439
92916
|
return nodes.some((n) => !!n && typeof n === "object" && (("commentBefore" in n) && !!n.commentBefore || ("comment" in n) && !!n.comment));
|
|
@@ -92446,7 +92923,7 @@ init_operator_uid();
|
|
|
92446
92923
|
init_atomic();
|
|
92447
92924
|
|
|
92448
92925
|
// src/cli/preflight-mounts.ts
|
|
92449
|
-
import { statSync as
|
|
92926
|
+
import { statSync as statSync45 } from "node:fs";
|
|
92450
92927
|
function parseHostBindSources(composeText) {
|
|
92451
92928
|
const out = [];
|
|
92452
92929
|
const FILE_HINT = /\.(ya?ml|db|log|toml|json|token|id)$|\/\.vault-token$|machine-id$|localtime$|vault-auto-unlock$|\/webkite$/;
|
|
@@ -92473,7 +92950,7 @@ function parseHostBindSources(composeText) {
|
|
|
92473
92950
|
return out;
|
|
92474
92951
|
}
|
|
92475
92952
|
function validateBindSources(composeText, deps = {}) {
|
|
92476
|
-
const stat = deps.stat ?? ((p) =>
|
|
92953
|
+
const stat = deps.stat ?? ((p) => statSync45(p));
|
|
92477
92954
|
const sources = parseHostBindSources(composeText);
|
|
92478
92955
|
const issues = [];
|
|
92479
92956
|
const seen = new Set;
|
|
@@ -93062,12 +93539,12 @@ function defaultPersistPin(configPath) {
|
|
|
93062
93539
|
const after = setReleasePinInConfig(before, pin);
|
|
93063
93540
|
if (after === before)
|
|
93064
93541
|
return;
|
|
93065
|
-
writeConfigFileSync(path7, after,
|
|
93542
|
+
writeConfigFileSync(path7, after, statSync46(path7).mode & 511);
|
|
93066
93543
|
try {
|
|
93067
93544
|
if (typeof process.geteuid === "function" && process.geteuid() === 0) {
|
|
93068
93545
|
const uid = resolveOperatorUid();
|
|
93069
93546
|
if (uid !== undefined)
|
|
93070
|
-
|
|
93547
|
+
chownSync9(path7, uid, uid);
|
|
93071
93548
|
}
|
|
93072
93549
|
} catch {}
|
|
93073
93550
|
};
|
|
@@ -93546,7 +94023,7 @@ function defaultStatusProbe(composePath) {
|
|
|
93546
94023
|
} catch {}
|
|
93547
94024
|
if (scriptPath) {
|
|
93548
94025
|
try {
|
|
93549
|
-
cliBuiltAt = new Date(
|
|
94026
|
+
cliBuiltAt = new Date(statSync46(scriptPath).mtimeMs).toISOString();
|
|
93550
94027
|
} catch {}
|
|
93551
94028
|
let dir = dirname34(scriptPath);
|
|
93552
94029
|
for (let i = 0;i < 8; i++) {
|
|
@@ -93822,7 +94299,7 @@ function registerUpdateCommand(program3) {
|
|
|
93822
94299
|
// src/cli/rollout.ts
|
|
93823
94300
|
init_helpers();
|
|
93824
94301
|
import { spawnSync as spawnSync19 } from "node:child_process";
|
|
93825
|
-
import { readFileSync as readFileSync77, chownSync as
|
|
94302
|
+
import { readFileSync as readFileSync77, chownSync as chownSync10, statSync as statSync48 } from "node:fs";
|
|
93826
94303
|
import { homedir as homedir46 } from "node:os";
|
|
93827
94304
|
|
|
93828
94305
|
// src/cli/rollout-pin-journal.ts
|
|
@@ -93832,7 +94309,7 @@ import {
|
|
|
93832
94309
|
writeFileSync as writeFileSync32,
|
|
93833
94310
|
renameSync as renameSync21,
|
|
93834
94311
|
unlinkSync as unlinkSync15,
|
|
93835
|
-
statSync as
|
|
94312
|
+
statSync as statSync47,
|
|
93836
94313
|
mkdirSync as mkdirSync47
|
|
93837
94314
|
} from "node:fs";
|
|
93838
94315
|
import { homedir as homedir45 } from "node:os";
|
|
@@ -93989,7 +94466,7 @@ function rollbackPinPersist(configPath, opts = {}) {
|
|
|
93989
94466
|
const next = journal.priorPin ? setReleasePinInConfig(current, journal.priorPin) : deleteReleasePinInConfig(current);
|
|
93990
94467
|
let mode = 384;
|
|
93991
94468
|
try {
|
|
93992
|
-
mode =
|
|
94469
|
+
mode = statSync47(configPath).mode & 511;
|
|
93993
94470
|
} catch {}
|
|
93994
94471
|
if (opts.writeConfig) {
|
|
93995
94472
|
opts.writeConfig(configPath, next, mode);
|
|
@@ -94568,7 +95045,7 @@ function createRolloutDeps(params) {
|
|
|
94568
95045
|
if (typeof process.geteuid === "function" && process.geteuid() === 0) {
|
|
94569
95046
|
const uid = resolveOperatorUid();
|
|
94570
95047
|
if (uid !== undefined) {
|
|
94571
|
-
|
|
95048
|
+
chownSync10(path7, uid, uid);
|
|
94572
95049
|
} else if (hostdCtx) {
|
|
94573
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.
|
|
94574
95051
|
`);
|
|
@@ -94628,7 +95105,7 @@ function createRolloutDeps(params) {
|
|
|
94628
95105
|
if (after === before)
|
|
94629
95106
|
return false;
|
|
94630
95107
|
beginPinPersist(configPath, pin);
|
|
94631
|
-
writeConfigPreservingOwnership(configPath, after,
|
|
95108
|
+
writeConfigPreservingOwnership(configPath, after, statSync48(configPath).mode & 511);
|
|
94632
95109
|
return true;
|
|
94633
95110
|
},
|
|
94634
95111
|
commitPin: () => {
|
|
@@ -95074,7 +95551,7 @@ init_merge();
|
|
|
95074
95551
|
import {
|
|
95075
95552
|
existsSync as existsSync84,
|
|
95076
95553
|
readdirSync as readdirSync28,
|
|
95077
|
-
statSync as
|
|
95554
|
+
statSync as statSync49,
|
|
95078
95555
|
unlinkSync as unlinkSync16
|
|
95079
95556
|
} from "node:fs";
|
|
95080
95557
|
import { join as join87 } from "node:path";
|
|
@@ -95097,7 +95574,7 @@ function collectSessionJsonl(claudeConfigDir) {
|
|
|
95097
95574
|
const full = join87(dir, name);
|
|
95098
95575
|
let st;
|
|
95099
95576
|
try {
|
|
95100
|
-
st =
|
|
95577
|
+
st = statSync49(full);
|
|
95101
95578
|
} catch {
|
|
95102
95579
|
continue;
|
|
95103
95580
|
}
|
|
@@ -95225,7 +95702,7 @@ import {
|
|
|
95225
95702
|
readdirSync as readdirSync29,
|
|
95226
95703
|
readFileSync as readFileSync79,
|
|
95227
95704
|
renameSync as renameSync22,
|
|
95228
|
-
statSync as
|
|
95705
|
+
statSync as statSync50,
|
|
95229
95706
|
unlinkSync as unlinkSync17,
|
|
95230
95707
|
writeFileSync as writeFileSync33,
|
|
95231
95708
|
writeSync as writeSync9
|
|
@@ -95428,7 +95905,7 @@ function sweepOrphanTmpFiles(stateDir) {
|
|
|
95428
95905
|
continue;
|
|
95429
95906
|
const tmpPath = join88(stateDir, entry);
|
|
95430
95907
|
try {
|
|
95431
|
-
const stat =
|
|
95908
|
+
const stat = statSync50(tmpPath);
|
|
95432
95909
|
if (stat.mtimeMs < cutoff) {
|
|
95433
95910
|
unlinkSync17(tmpPath);
|
|
95434
95911
|
}
|
|
@@ -95736,7 +96213,7 @@ import {
|
|
|
95736
96213
|
} from "node:fs";
|
|
95737
96214
|
import { dirname as dirname37, join as join89 } from "node:path";
|
|
95738
96215
|
import { homedir as homedir47 } from "node:os";
|
|
95739
|
-
import { execFileSync as
|
|
96216
|
+
import { execFileSync as execFileSync25 } from "node:child_process";
|
|
95740
96217
|
|
|
95741
96218
|
class PythonEnvError extends Error {
|
|
95742
96219
|
stderr;
|
|
@@ -95783,7 +96260,7 @@ function ensurePythonEnv(opts) {
|
|
|
95783
96260
|
}
|
|
95784
96261
|
mkdirSync49(dirname37(venvDir), { recursive: true });
|
|
95785
96262
|
try {
|
|
95786
|
-
|
|
96263
|
+
execFileSync25(hostPython, ["-m", "venv", venvDir], { stdio: "pipe" });
|
|
95787
96264
|
} catch (err) {
|
|
95788
96265
|
const e = err;
|
|
95789
96266
|
throw new PythonEnvError(`Failed to create venv for skill "${skillName}" with ${hostPython}: ${e.message}`, e.stderr?.toString());
|
|
@@ -95795,7 +96272,7 @@ function ensurePythonEnv(opts) {
|
|
|
95795
96272
|
delete childEnv.PIP_TARGET;
|
|
95796
96273
|
delete childEnv.PIP_PREFIX;
|
|
95797
96274
|
delete childEnv.PYTHONUSERBASE;
|
|
95798
|
-
|
|
96275
|
+
execFileSync25(pipBin, ["install", "--disable-pip-version-check", "-r", requirementsPath], { stdio: "pipe", env: childEnv });
|
|
95799
96276
|
} catch (err) {
|
|
95800
96277
|
const e = err;
|
|
95801
96278
|
throw new PythonEnvError(`Failed to install requirements for skill "${skillName}": ${e.message}`, e.stderr?.toString());
|
|
@@ -95824,7 +96301,7 @@ import {
|
|
|
95824
96301
|
} from "node:fs";
|
|
95825
96302
|
import { dirname as dirname38, join as join90 } from "node:path";
|
|
95826
96303
|
import { homedir as homedir48 } from "node:os";
|
|
95827
|
-
import { execFileSync as
|
|
96304
|
+
import { execFileSync as execFileSync26 } from "node:child_process";
|
|
95828
96305
|
|
|
95829
96306
|
class NodeEnvError extends Error {
|
|
95830
96307
|
stderr;
|
|
@@ -95908,10 +96385,10 @@ function ensureNodeEnv(opts) {
|
|
|
95908
96385
|
try {
|
|
95909
96386
|
if (installer === "bun") {
|
|
95910
96387
|
const args = copiedLockfile ? ["install", "--frozen-lockfile"] : ["install"];
|
|
95911
|
-
|
|
96388
|
+
execFileSync26("bun", args, { cwd: envDir, stdio: "pipe" });
|
|
95912
96389
|
} else {
|
|
95913
96390
|
const args = copiedLockfile ? ["ci"] : ["install"];
|
|
95914
|
-
|
|
96391
|
+
execFileSync26("npm", args, { cwd: envDir, stdio: "pipe" });
|
|
95915
96392
|
}
|
|
95916
96393
|
} catch (err) {
|
|
95917
96394
|
const e = err;
|
|
@@ -96993,7 +97470,7 @@ function registerSoulCommand(program3) {
|
|
|
96993
97470
|
// src/cli/debug.ts
|
|
96994
97471
|
init_helpers();
|
|
96995
97472
|
init_loader();
|
|
96996
|
-
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";
|
|
96997
97474
|
import { resolve as resolve53, join as join93 } from "node:path";
|
|
96998
97475
|
import { createHash as createHash20 } from "node:crypto";
|
|
96999
97476
|
init_merge();
|
|
@@ -97032,7 +97509,7 @@ function findLatestTranscriptJsonl(claudeConfigDir) {
|
|
|
97032
97509
|
const transcriptPath = join93(projectPath, "transcript.jsonl");
|
|
97033
97510
|
if (!existsSync91(transcriptPath))
|
|
97034
97511
|
continue;
|
|
97035
|
-
const stat3 =
|
|
97512
|
+
const stat3 = statSync51(transcriptPath);
|
|
97036
97513
|
if (!latest || stat3.mtimeMs > latest.mtime) {
|
|
97037
97514
|
latest = { path: transcriptPath, mtime: stat3.mtimeMs };
|
|
97038
97515
|
}
|
|
@@ -97267,7 +97744,7 @@ function registerDebugCommand(program3) {
|
|
|
97267
97744
|
init_source();
|
|
97268
97745
|
|
|
97269
97746
|
// src/worktree/claim.ts
|
|
97270
|
-
import { execFileSync as
|
|
97747
|
+
import { execFileSync as execFileSync27 } from "node:child_process";
|
|
97271
97748
|
import { closeSync as closeSync17, mkdirSync as mkdirSync52, openSync as openSync17, existsSync as existsSync93, unlinkSync as unlinkSync19 } from "node:fs";
|
|
97272
97749
|
import { join as join95, resolve as resolve55 } from "node:path";
|
|
97273
97750
|
import { homedir as homedir51 } from "node:os";
|
|
@@ -97436,7 +97913,7 @@ async function claimWorktree(input, codeRepos) {
|
|
|
97436
97913
|
releaseLock();
|
|
97437
97914
|
}
|
|
97438
97915
|
try {
|
|
97439
|
-
|
|
97916
|
+
execFileSync27("git", ["worktree", "add", "-b", branch, worktreePath], {
|
|
97440
97917
|
cwd: repoPath,
|
|
97441
97918
|
stdio: "pipe"
|
|
97442
97919
|
});
|
|
@@ -97449,7 +97926,7 @@ async function claimWorktree(input, codeRepos) {
|
|
|
97449
97926
|
}
|
|
97450
97927
|
|
|
97451
97928
|
// src/worktree/release.ts
|
|
97452
|
-
import { execFileSync as
|
|
97929
|
+
import { execFileSync as execFileSync28 } from "node:child_process";
|
|
97453
97930
|
import { existsSync as existsSync94 } from "node:fs";
|
|
97454
97931
|
function releaseWorktree(input) {
|
|
97455
97932
|
const { id } = input;
|
|
@@ -97460,7 +97937,7 @@ function releaseWorktree(input) {
|
|
|
97460
97937
|
let gitSuccess = true;
|
|
97461
97938
|
if (existsSync94(record2.path)) {
|
|
97462
97939
|
try {
|
|
97463
|
-
|
|
97940
|
+
execFileSync28("git", ["worktree", "remove", "--force", record2.path], {
|
|
97464
97941
|
cwd: record2.repo,
|
|
97465
97942
|
stdio: "pipe"
|
|
97466
97943
|
});
|
|
@@ -97496,7 +97973,7 @@ function listWorktrees() {
|
|
|
97496
97973
|
}
|
|
97497
97974
|
|
|
97498
97975
|
// src/worktree/reaper.ts
|
|
97499
|
-
import { execFileSync as
|
|
97976
|
+
import { execFileSync as execFileSync29 } from "node:child_process";
|
|
97500
97977
|
import { existsSync as existsSync95 } from "node:fs";
|
|
97501
97978
|
var STALE_THRESHOLD_MS = 10 * 60 * 1000;
|
|
97502
97979
|
function reapSkipReasonText(action) {
|
|
@@ -97514,7 +97991,7 @@ function reapSkipReasonText(action) {
|
|
|
97514
97991
|
function probePathInUse(path10) {
|
|
97515
97992
|
let probeRan = false;
|
|
97516
97993
|
try {
|
|
97517
|
-
|
|
97994
|
+
execFileSync29("fuser", [path10], { stdio: "pipe" });
|
|
97518
97995
|
return "in-use";
|
|
97519
97996
|
} catch (err) {
|
|
97520
97997
|
if (err.code !== "ENOENT") {
|
|
@@ -97522,7 +97999,7 @@ function probePathInUse(path10) {
|
|
|
97522
97999
|
}
|
|
97523
98000
|
}
|
|
97524
98001
|
try {
|
|
97525
|
-
const out =
|
|
98002
|
+
const out = execFileSync29("lsof", ["-t", path10], {
|
|
97526
98003
|
stdio: ["ignore", "pipe", "ignore"]
|
|
97527
98004
|
}).toString().trim();
|
|
97528
98005
|
probeRan = true;
|
|
@@ -97537,7 +98014,7 @@ function probePathInUse(path10) {
|
|
|
97537
98014
|
}
|
|
97538
98015
|
function hasUncommittedChanges(_repoPath, worktreePath) {
|
|
97539
98016
|
try {
|
|
97540
|
-
const out =
|
|
98017
|
+
const out = execFileSync29("git", ["-C", worktreePath, "status", "--porcelain"], { stdio: "pipe" }).toString();
|
|
97541
98018
|
return out.trim().length > 0;
|
|
97542
98019
|
} catch {
|
|
97543
98020
|
return true;
|
|
@@ -97545,7 +98022,7 @@ function hasUncommittedChanges(_repoPath, worktreePath) {
|
|
|
97545
98022
|
}
|
|
97546
98023
|
function defaultRemoveWorktree(repoPath, worktreePath) {
|
|
97547
98024
|
try {
|
|
97548
|
-
|
|
98025
|
+
execFileSync29("git", ["worktree", "remove", "--force", worktreePath], {
|
|
97549
98026
|
cwd: repoPath,
|
|
97550
98027
|
stdio: "pipe"
|
|
97551
98028
|
});
|
|
@@ -97637,12 +98114,12 @@ function runReaper(nowMs, deps = {}) {
|
|
|
97637
98114
|
}
|
|
97638
98115
|
|
|
97639
98116
|
// src/worktree/gc.ts
|
|
97640
|
-
import { execFileSync as
|
|
98117
|
+
import { execFileSync as execFileSync30 } from "node:child_process";
|
|
97641
98118
|
import {
|
|
97642
98119
|
existsSync as existsSync96,
|
|
97643
98120
|
readFileSync as readFileSync85,
|
|
97644
98121
|
readdirSync as readdirSync32,
|
|
97645
|
-
statSync as
|
|
98122
|
+
statSync as statSync52,
|
|
97646
98123
|
renameSync as renameSync24,
|
|
97647
98124
|
mkdirSync as mkdirSync53,
|
|
97648
98125
|
rmSync as rmSync17
|
|
@@ -97730,7 +98207,7 @@ function isEphemeralPath(path10) {
|
|
|
97730
98207
|
}
|
|
97731
98208
|
return false;
|
|
97732
98209
|
}
|
|
97733
|
-
var defaultExec3 = (file, args, cwd) =>
|
|
98210
|
+
var defaultExec3 = (file, args, cwd) => execFileSync30(file, args, { cwd, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).toString();
|
|
97734
98211
|
function defaultPrSignal(repo, branch, exec) {
|
|
97735
98212
|
try {
|
|
97736
98213
|
const out = exec("gh", [
|
|
@@ -97769,7 +98246,7 @@ function planGc(roots, deps = {}) {
|
|
|
97769
98246
|
const exists = deps.existsSync ?? existsSync96;
|
|
97770
98247
|
const readDir = deps.readDir ?? ((p) => readdirSync32(p));
|
|
97771
98248
|
const readFile4 = deps.readFile ?? ((p) => readFileSync85(p, "utf8"));
|
|
97772
|
-
const stat3 = deps.stat ?? ((p) =>
|
|
98249
|
+
const stat3 = deps.stat ?? ((p) => statSync52(p));
|
|
97773
98250
|
const exec = deps.exec ?? defaultExec3;
|
|
97774
98251
|
const prSignal = deps.prSignal ?? ((repo, branch) => defaultPrSignal(repo, branch, exec));
|
|
97775
98252
|
const stamp = deps.dateStamp ?? "undated";
|
|
@@ -97963,7 +98440,7 @@ function listTrashEntries(nowMs, deps = {}) {
|
|
|
97963
98440
|
const p = join96(stampDir, name);
|
|
97964
98441
|
let mtimeMs = nowMs;
|
|
97965
98442
|
try {
|
|
97966
|
-
mtimeMs =
|
|
98443
|
+
mtimeMs = statSync52(p).mtimeMs;
|
|
97967
98444
|
} catch {}
|
|
97968
98445
|
out.push({ path: p, ageDays: (nowMs - mtimeMs) / 86400000 });
|
|
97969
98446
|
}
|
|
@@ -99238,6 +99715,54 @@ function redactToolCallParams(params) {
|
|
|
99238
99715
|
}
|
|
99239
99716
|
return { ...called, arguments: redactToolArguments(called.arguments) };
|
|
99240
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
|
+
}
|
|
99241
99766
|
function buildFallbackToolsList() {
|
|
99242
99767
|
const tools = Object.entries(FALLBACK_TOOL_TABLE).map(([name, [required, props]]) => ({
|
|
99243
99768
|
name,
|
|
@@ -99564,7 +100089,11 @@ class HindsightShim {
|
|
|
99564
100089
|
if (called?.name && SYNTHESIZED_TOOL_NAMES.includes(called.name)) {
|
|
99565
100090
|
return this.synthesizedCall(called.name, called.arguments);
|
|
99566
100091
|
}
|
|
99567
|
-
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);
|
|
99568
100097
|
try {
|
|
99569
100098
|
const res = await this.upstream.request("tools/call", forwarded, this.opts.toolsCallTimeoutMs ?? TOOLS_CALL_TIMEOUT_MS);
|
|
99570
100099
|
this.notifyIfRecovered();
|
|
@@ -99667,7 +100196,7 @@ function registerHindsightMcpShimCommand(program3) {
|
|
|
99667
100196
|
|
|
99668
100197
|
// src/cli/deliver-file.ts
|
|
99669
100198
|
init_client2();
|
|
99670
|
-
import { readFileSync as readFileSync87, statSync as
|
|
100199
|
+
import { readFileSync as readFileSync87, statSync as statSync53 } from "node:fs";
|
|
99671
100200
|
import { basename as basename13 } from "node:path";
|
|
99672
100201
|
|
|
99673
100202
|
// src/delivery/onedrive.ts
|
|
@@ -100006,7 +100535,7 @@ async function defaultResolveProvider() {
|
|
|
100006
100535
|
}
|
|
100007
100536
|
async function runDeliverFile(localPath, deps = {}) {
|
|
100008
100537
|
const agentName = safeAgentName(deps.agentName ?? process.env.SWITCHROOM_AGENT_NAME);
|
|
100009
|
-
const sizeOf = deps.fileSize ?? ((p) =>
|
|
100538
|
+
const sizeOf = deps.fileSize ?? ((p) => statSync53(p).size);
|
|
100010
100539
|
const read = deps.readFile ?? ((p) => new Uint8Array(readFileSync87(p)));
|
|
100011
100540
|
const resolveProvider = deps.resolveProvider ?? defaultResolveProvider;
|
|
100012
100541
|
let size;
|
|
@@ -100702,7 +101231,7 @@ init_agent_config();
|
|
|
100702
101231
|
|
|
100703
101232
|
// src/cli/agent-config-write.ts
|
|
100704
101233
|
init_agent_config();
|
|
100705
|
-
var
|
|
101234
|
+
var import_yaml22 = __toESM(require_dist(), 1);
|
|
100706
101235
|
|
|
100707
101236
|
// src/config/overlay-writer.ts
|
|
100708
101237
|
init_paths();
|
|
@@ -100715,7 +101244,7 @@ import {
|
|
|
100715
101244
|
readdirSync as readdirSync35,
|
|
100716
101245
|
readFileSync as readFileSync89,
|
|
100717
101246
|
renameSync as renameSync26,
|
|
100718
|
-
statSync as
|
|
101247
|
+
statSync as statSync54,
|
|
100719
101248
|
unlinkSync as unlinkSync20,
|
|
100720
101249
|
writeSync as writeSync10
|
|
100721
101250
|
} from "node:fs";
|
|
@@ -100759,7 +101288,7 @@ function withAgentLock(paths, fn) {
|
|
|
100759
101288
|
if (e.code !== "EEXIST")
|
|
100760
101289
|
throw err;
|
|
100761
101290
|
try {
|
|
100762
|
-
const age = Date.now() -
|
|
101291
|
+
const age = Date.now() - statSync54(paths.lockPath).mtimeMs;
|
|
100763
101292
|
if (age > 30000) {
|
|
100764
101293
|
unlinkSync20(paths.lockPath);
|
|
100765
101294
|
continue;
|
|
@@ -100903,7 +101432,7 @@ init_overlay_schema();
|
|
|
100903
101432
|
init_schema();
|
|
100904
101433
|
init_cron_unit_name();
|
|
100905
101434
|
init_lifecycle();
|
|
100906
|
-
var
|
|
101435
|
+
var import_yaml21 = __toESM(require_dist(), 1);
|
|
100907
101436
|
|
|
100908
101437
|
// src/scheduler/cron-gap.ts
|
|
100909
101438
|
function expandMinuteTerm(term) {
|
|
@@ -100993,7 +101522,7 @@ function violatesMinInterval(cron) {
|
|
|
100993
101522
|
function dryRunReconcile(input) {
|
|
100994
101523
|
let parsed;
|
|
100995
101524
|
try {
|
|
100996
|
-
parsed =
|
|
101525
|
+
parsed = import_yaml21.parse(input.yamlText);
|
|
100997
101526
|
} catch (err) {
|
|
100998
101527
|
return {
|
|
100999
101528
|
ok: false,
|
|
@@ -101192,7 +101721,7 @@ function denyPendingScheduleEntry(opts) {
|
|
|
101192
101721
|
|
|
101193
101722
|
// src/cli/agent-config-write.ts
|
|
101194
101723
|
import { existsSync as existsSync101, readFileSync as readFileSync91 } from "node:fs";
|
|
101195
|
-
import { execFileSync as
|
|
101724
|
+
import { execFileSync as execFileSync31 } from "node:child_process";
|
|
101196
101725
|
|
|
101197
101726
|
// src/scheduler/schedule-report.ts
|
|
101198
101727
|
var TIER_WEIGHT = { poll: 0, action: 0, cheap: 1, main: 5 };
|
|
@@ -101341,7 +101870,7 @@ function parseOverlayCronAndName(e) {
|
|
|
101341
101870
|
if (headerMatch)
|
|
101342
101871
|
name = headerMatch[1];
|
|
101343
101872
|
try {
|
|
101344
|
-
const doc =
|
|
101873
|
+
const doc = import_yaml22.parse(e.raw);
|
|
101345
101874
|
const first = doc?.schedule?.[0];
|
|
101346
101875
|
if (first) {
|
|
101347
101876
|
if (typeof first.cron === "string")
|
|
@@ -101377,7 +101906,7 @@ function scheduleAdd(opts) {
|
|
|
101377
101906
|
]
|
|
101378
101907
|
};
|
|
101379
101908
|
const yamlText = (() => {
|
|
101380
|
-
const body =
|
|
101909
|
+
const body = import_yaml22.stringify(doc);
|
|
101381
101910
|
const header = opts.name ? `# name: ${opts.name}
|
|
101382
101911
|
` : "";
|
|
101383
101912
|
return header + body;
|
|
@@ -101513,7 +102042,7 @@ function scheduleAddOrStage(opts) {
|
|
|
101513
102042
|
]
|
|
101514
102043
|
};
|
|
101515
102044
|
const yamlText = (opts.name ? `# name: ${opts.name}
|
|
101516
|
-
` : "") +
|
|
102045
|
+
` : "") + import_yaml22.stringify(doc);
|
|
101517
102046
|
const summary = (() => {
|
|
101518
102047
|
const parts = [`cron=${opts.cronExpr}`];
|
|
101519
102048
|
if (opts.secrets?.length)
|
|
@@ -101563,7 +102092,7 @@ function scheduleRemove(opts) {
|
|
|
101563
102092
|
break;
|
|
101564
102093
|
}
|
|
101565
102094
|
try {
|
|
101566
|
-
const parsed =
|
|
102095
|
+
const parsed = import_yaml22.parse(e.raw);
|
|
101567
102096
|
if (parsed && parsed.name === opts.name) {
|
|
101568
102097
|
match = e;
|
|
101569
102098
|
break;
|
|
@@ -101788,7 +102317,7 @@ function registerAgentConfigWriteCommands(program3) {
|
|
|
101788
102317
|
blob = existsSync101(opts.jsonl) ? readFileSync91(opts.jsonl, "utf-8") : "";
|
|
101789
102318
|
} else {
|
|
101790
102319
|
try {
|
|
101791
|
-
blob =
|
|
102320
|
+
blob = execFileSync31("docker", ["exec", `switchroom-${agent}`, "cat", "/state/agent/scheduler.jsonl"], {
|
|
101792
102321
|
encoding: "utf-8",
|
|
101793
102322
|
stdio: ["ignore", "pipe", "ignore"]
|
|
101794
102323
|
});
|
|
@@ -101816,11 +102345,11 @@ function registerAgentConfigWriteCommands(program3) {
|
|
|
101816
102345
|
}
|
|
101817
102346
|
|
|
101818
102347
|
// src/cli/agent-config-skill-write.ts
|
|
101819
|
-
var
|
|
102348
|
+
var import_yaml23 = __toESM(require_dist(), 1);
|
|
101820
102349
|
import { existsSync as existsSync102 } from "node:fs";
|
|
101821
102350
|
init_reconcile_default_skills();
|
|
101822
102351
|
init_agent_config();
|
|
101823
|
-
var
|
|
102352
|
+
var import_yaml24 = __toESM(require_dist(), 1);
|
|
101824
102353
|
import { join as join103 } from "node:path";
|
|
101825
102354
|
var MAX_SKILLS_PER_AGENT = 20;
|
|
101826
102355
|
var V1_ALLOWED_SOURCE_PREFIX = "bundled:";
|
|
@@ -101870,7 +102399,7 @@ function countCurrentSkills(agent, opts) {
|
|
|
101870
102399
|
let total = 0;
|
|
101871
102400
|
for (const e of entries) {
|
|
101872
102401
|
try {
|
|
101873
|
-
const doc =
|
|
102402
|
+
const doc = import_yaml24.parse(e.raw);
|
|
101874
102403
|
total += (doc?.skills ?? []).length;
|
|
101875
102404
|
} catch {}
|
|
101876
102405
|
}
|
|
@@ -101900,7 +102429,7 @@ function skillInstall(opts) {
|
|
|
101900
102429
|
if (!existsSync102(skillPath)) {
|
|
101901
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.`);
|
|
101902
102431
|
}
|
|
101903
|
-
const yamlText =
|
|
102432
|
+
const yamlText = import_yaml23.stringify({ skills: [skillName] });
|
|
101904
102433
|
let path10;
|
|
101905
102434
|
try {
|
|
101906
102435
|
path10 = writeSkillsOverlayEntry(agent, slug, yamlText, { root: opts.root });
|
|
@@ -102060,6 +102589,7 @@ function registerAgentConfigSkillWriteCommands(program3) {
|
|
|
102060
102589
|
|
|
102061
102590
|
// src/cli/skill.ts
|
|
102062
102591
|
import {
|
|
102592
|
+
chmodSync as chmodSync17,
|
|
102063
102593
|
closeSync as closeSync20,
|
|
102064
102594
|
existsSync as existsSync103,
|
|
102065
102595
|
lstatSync as lstatSync12,
|
|
@@ -102071,7 +102601,7 @@ import {
|
|
|
102071
102601
|
realpathSync as realpathSync7,
|
|
102072
102602
|
renameSync as renameSync28,
|
|
102073
102603
|
rmSync as rmSync19,
|
|
102074
|
-
statSync as
|
|
102604
|
+
statSync as statSync55,
|
|
102075
102605
|
writeFileSync as writeFileSync43
|
|
102076
102606
|
} from "node:fs";
|
|
102077
102607
|
import { tmpdir as tmpdir6, homedir as homedir54 } from "node:os";
|
|
@@ -102079,7 +102609,7 @@ import { dirname as dirname41, join as join104, relative as relative4, resolve a
|
|
|
102079
102609
|
import { spawnSync as spawnSync21 } from "node:child_process";
|
|
102080
102610
|
|
|
102081
102611
|
// src/cli/skill-common.ts
|
|
102082
|
-
var
|
|
102612
|
+
var import_yaml25 = __toESM(require_dist(), 1);
|
|
102083
102613
|
var MAX_SKILLS_PER_AGENT2 = 20;
|
|
102084
102614
|
var MAX_FILE_BYTES = 256 * 1024;
|
|
102085
102615
|
var MAX_SKILL_BYTES = 2 * 1024 * 1024;
|
|
@@ -102184,7 +102714,7 @@ function validateSkillMd(content, expectedName) {
|
|
|
102184
102714
|
}
|
|
102185
102715
|
let parsed;
|
|
102186
102716
|
try {
|
|
102187
|
-
parsed =
|
|
102717
|
+
parsed = import_yaml25.parse(fmText);
|
|
102188
102718
|
} catch (e) {
|
|
102189
102719
|
return authorErr("E_SKILL_INVALID_FRONTMATTER", `frontmatter is not valid YAML: ${e.message}`);
|
|
102190
102720
|
}
|
|
@@ -102342,7 +102872,7 @@ function isTarballPath(p) {
|
|
|
102342
102872
|
}
|
|
102343
102873
|
function loadFromDir(dir) {
|
|
102344
102874
|
const abs = realpathSync7(dir);
|
|
102345
|
-
if (!
|
|
102875
|
+
if (!statSync55(abs).isDirectory()) {
|
|
102346
102876
|
fail4(`--from path is not a directory: ${dir}`);
|
|
102347
102877
|
}
|
|
102348
102878
|
const files = {};
|
|
@@ -102542,6 +103072,7 @@ function writePayload(poolDir, name, files) {
|
|
|
102542
103072
|
fail4(`refusing to overwrite symlink at ${target}; investigate manually`);
|
|
102543
103073
|
}
|
|
102544
103074
|
const staging = mkdtempSync5(join104(poolDir, `.skill-apply-stage-${name}-`));
|
|
103075
|
+
chmodSync17(staging, 493);
|
|
102545
103076
|
let oldRename = null;
|
|
102546
103077
|
try {
|
|
102547
103078
|
for (const [path10, content] of Object.entries(files)) {
|
|
@@ -102602,7 +103133,7 @@ function registerSkillCommand(program3) {
|
|
|
102602
103133
|
if (!existsSync103(fromPath)) {
|
|
102603
103134
|
fail4(`--from path does not exist: ${opts.from}`);
|
|
102604
103135
|
}
|
|
102605
|
-
const st =
|
|
103136
|
+
const st = statSync55(fromPath);
|
|
102606
103137
|
if (st.isDirectory()) {
|
|
102607
103138
|
files = loadFromDir(fromPath);
|
|
102608
103139
|
} else if (isTarballPath(fromPath)) {
|
|
@@ -102654,6 +103185,7 @@ function sumBytes(files) {
|
|
|
102654
103185
|
// src/cli/skill-personal.ts
|
|
102655
103186
|
init_esm();
|
|
102656
103187
|
import {
|
|
103188
|
+
chmodSync as chmodSync18,
|
|
102657
103189
|
closeSync as closeSync21,
|
|
102658
103190
|
existsSync as existsSync104,
|
|
102659
103191
|
lstatSync as lstatSync13,
|
|
@@ -102664,7 +103196,7 @@ import {
|
|
|
102664
103196
|
readdirSync as readdirSync38,
|
|
102665
103197
|
renameSync as renameSync29,
|
|
102666
103198
|
rmSync as rmSync20,
|
|
102667
|
-
statSync as
|
|
103199
|
+
statSync as statSync56,
|
|
102668
103200
|
utimesSync,
|
|
102669
103201
|
writeFileSync as writeFileSync44
|
|
102670
103202
|
} from "node:fs";
|
|
@@ -102748,6 +103280,7 @@ function mirrorToConfigRepo(agent, name, liveSkillDir) {
|
|
|
102748
103280
|
}
|
|
102749
103281
|
};
|
|
102750
103282
|
walk2(liveSkillDir, staging);
|
|
103283
|
+
chmodSync18(staging, 493);
|
|
102751
103284
|
if (existsSync104(dest)) {
|
|
102752
103285
|
const prior = join105(configSkillsRoot, `.${name}-prior-${Date.now()}`);
|
|
102753
103286
|
renameSync29(dest, prior);
|
|
@@ -102820,7 +103353,7 @@ function readStdinSync2() {
|
|
|
102820
103353
|
}
|
|
102821
103354
|
function loadFromDir2(dir) {
|
|
102822
103355
|
const abs = resolve59(dir);
|
|
102823
|
-
if (!
|
|
103356
|
+
if (!statSync56(abs).isDirectory()) {
|
|
102824
103357
|
fail5(`--from path is not a directory: ${dir}`);
|
|
102825
103358
|
}
|
|
102826
103359
|
const files = {};
|
|
@@ -102906,7 +103439,7 @@ function sweepTrash(agentsRoot, agent) {
|
|
|
102906
103439
|
continue;
|
|
102907
103440
|
const entPath = join105(trash, ent.name);
|
|
102908
103441
|
try {
|
|
102909
|
-
const st =
|
|
103442
|
+
const st = statSync56(entPath);
|
|
102910
103443
|
if (now - st.mtimeMs > TRASH_TTL_MS) {
|
|
102911
103444
|
rmSync20(entPath, { recursive: true, force: true });
|
|
102912
103445
|
}
|
|
@@ -102926,6 +103459,7 @@ function writePersonalSkill(targetDir, files) {
|
|
|
102926
103459
|
}
|
|
102927
103460
|
mkdirSync61(dirname42(targetDir), { recursive: true, mode: 493 });
|
|
102928
103461
|
const staging = mkdtempSync6(join105(dirname42(targetDir), `.skill-personal-stage-`));
|
|
103462
|
+
chmodSync18(staging, 493);
|
|
102929
103463
|
let oldRename = null;
|
|
102930
103464
|
try {
|
|
102931
103465
|
for (const [path10, content] of Object.entries(files)) {
|
|
@@ -103031,7 +103565,7 @@ function loadFiles(opts) {
|
|
|
103031
103565
|
if (!existsSync104(p)) {
|
|
103032
103566
|
fail5(`--from path does not exist: ${opts.from}`);
|
|
103033
103567
|
}
|
|
103034
|
-
const st =
|
|
103568
|
+
const st = statSync56(p);
|
|
103035
103569
|
if (st.isDirectory()) {
|
|
103036
103570
|
return loadFromDir2(p);
|
|
103037
103571
|
}
|
|
@@ -103252,7 +103786,7 @@ function listPersonalAction(opts) {
|
|
|
103252
103786
|
if (e.isFile()) {
|
|
103253
103787
|
fileCount += 1;
|
|
103254
103788
|
try {
|
|
103255
|
-
totalBytes +=
|
|
103789
|
+
totalBytes += statSync56(join105(sub, e.name)).size;
|
|
103256
103790
|
} catch {}
|
|
103257
103791
|
} else if (e.isDirectory()) {
|
|
103258
103792
|
walk2(join105(sub, e.name));
|
|
@@ -103369,8 +103903,8 @@ function registerSelfImproveProposeSkillCommand(program3) {
|
|
|
103369
103903
|
// src/cli/skill-search.ts
|
|
103370
103904
|
init_esm();
|
|
103371
103905
|
init_helpers();
|
|
103372
|
-
var
|
|
103373
|
-
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";
|
|
103374
103908
|
import { homedir as homedir57 } from "node:os";
|
|
103375
103909
|
import { join as join107, resolve as resolve60 } from "node:path";
|
|
103376
103910
|
var PERSONAL_PREFIX2 = "personal-";
|
|
@@ -103409,7 +103943,7 @@ function readSkillFrontmatter(skillDir) {
|
|
|
103409
103943
|
const fmText = rest.slice(0, endIdx);
|
|
103410
103944
|
let parsed;
|
|
103411
103945
|
try {
|
|
103412
|
-
parsed =
|
|
103946
|
+
parsed = import_yaml26.parse(fmText);
|
|
103413
103947
|
} catch (e) {
|
|
103414
103948
|
return { error: `yaml parse: ${e.message}` };
|
|
103415
103949
|
}
|
|
@@ -103421,7 +103955,7 @@ function readSkillFrontmatter(skillDir) {
|
|
|
103421
103955
|
function statSkillMd(skillDir) {
|
|
103422
103956
|
const mdPath = join107(skillDir, "SKILL.md");
|
|
103423
103957
|
try {
|
|
103424
|
-
const st =
|
|
103958
|
+
const st = statSync57(mdPath);
|
|
103425
103959
|
return { size: st.size, mtime: st.mtime.toISOString() };
|
|
103426
103960
|
} catch {
|
|
103427
103961
|
return null;
|
|
@@ -103445,7 +103979,7 @@ function listPersonalSkills(agent, agentsRoot = defaultAgentsRoot()) {
|
|
|
103445
103979
|
continue;
|
|
103446
103980
|
const dirPath = join107(skillsDir, ent);
|
|
103447
103981
|
try {
|
|
103448
|
-
if (!
|
|
103982
|
+
if (!statSync57(dirPath).isDirectory())
|
|
103449
103983
|
continue;
|
|
103450
103984
|
} catch {
|
|
103451
103985
|
continue;
|
|
@@ -103485,7 +104019,7 @@ function listSharedSkills(sharedRoot = defaultSharedRoot2()) {
|
|
|
103485
104019
|
continue;
|
|
103486
104020
|
const dirPath = join107(sharedRoot, ent);
|
|
103487
104021
|
try {
|
|
103488
|
-
if (!
|
|
104022
|
+
if (!statSync57(dirPath).isDirectory())
|
|
103489
104023
|
continue;
|
|
103490
104024
|
} catch {
|
|
103491
104025
|
continue;
|
|
@@ -103521,7 +104055,7 @@ function listBundledSkills(bundledRoot = defaultBundledRoot2()) {
|
|
|
103521
104055
|
continue;
|
|
103522
104056
|
const dirPath = join107(bundledRoot, ent);
|
|
103523
104057
|
try {
|
|
103524
|
-
if (!
|
|
104058
|
+
if (!statSync57(dirPath).isDirectory())
|
|
103525
104059
|
continue;
|
|
103526
104060
|
} catch {
|
|
103527
104061
|
continue;
|
|
@@ -103669,7 +104203,7 @@ import {
|
|
|
103669
104203
|
mkdirSync as mkdirSync62,
|
|
103670
104204
|
readdirSync as readdirSync40,
|
|
103671
104205
|
writeFileSync as writeFileSync45,
|
|
103672
|
-
statSync as
|
|
104206
|
+
statSync as statSync58,
|
|
103673
104207
|
lstatSync as lstatSync14,
|
|
103674
104208
|
realpathSync as realpathSync8,
|
|
103675
104209
|
copyFileSync as copyFileSync14
|
|
@@ -103781,6 +104315,10 @@ services:
|
|
|
103781
104315
|
# HAProxy writes its runtime state/pidfile under /run; read_only rootfs
|
|
103782
104316
|
# otherwise makes it crash-loop on boot.
|
|
103783
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
|
|
103784
104322
|
cap_drop:
|
|
103785
104323
|
- ALL
|
|
103786
104324
|
cap_add:
|
|
@@ -104182,7 +104720,7 @@ function doStatus() {
|
|
|
104182
104720
|
continue;
|
|
104183
104721
|
const sockPath = join108(dir, name, "sock");
|
|
104184
104722
|
if (existsSync107(sockPath)) {
|
|
104185
|
-
const st =
|
|
104723
|
+
const st = statSync58(sockPath);
|
|
104186
104724
|
if ((st.mode & 61440) === 49152) {
|
|
104187
104725
|
entries.push(`${name} \u2192 ${sockPath}`);
|
|
104188
104726
|
}
|
|
@@ -104273,7 +104811,7 @@ init_source();
|
|
|
104273
104811
|
init_helpers();
|
|
104274
104812
|
init_operator_uid();
|
|
104275
104813
|
init_compose();
|
|
104276
|
-
import { chownSync as
|
|
104814
|
+
import { chownSync as chownSync11, existsSync as existsSync108, mkdirSync as mkdirSync63, writeFileSync as writeFileSync46, copyFileSync as copyFileSync15 } from "node:fs";
|
|
104277
104815
|
import { homedir as homedir59 } from "node:os";
|
|
104278
104816
|
import { join as join109 } from "node:path";
|
|
104279
104817
|
import { spawnSync as spawnSync26 } from "node:child_process";
|
|
@@ -104453,10 +104991,10 @@ async function doInstall2(opts, program3) {
|
|
|
104453
104991
|
writeFileSync46(composePath, yaml, "utf8");
|
|
104454
104992
|
try {
|
|
104455
104993
|
if (typeof process.geteuid === "function" && process.geteuid() === 0) {
|
|
104456
|
-
|
|
104457
|
-
|
|
104994
|
+
chownSync11(dir, operatorUid, operatorUid);
|
|
104995
|
+
chownSync11(composePath, operatorUid, operatorUid);
|
|
104458
104996
|
if (bak)
|
|
104459
|
-
|
|
104997
|
+
chownSync11(bak, operatorUid, operatorUid);
|
|
104460
104998
|
}
|
|
104461
104999
|
} catch {}
|
|
104462
105000
|
console.log(source_default.green(` \u2713 Wrote ${composePath}`));
|
|
@@ -105073,7 +105611,7 @@ init_install_cron();
|
|
|
105073
105611
|
|
|
105074
105612
|
// src/hindsight-watch/probe.ts
|
|
105075
105613
|
import { spawnSync as spawnSync28 } from "node:child_process";
|
|
105076
|
-
import { readFileSync as readFileSync100, readdirSync as readdirSync44, statSync as
|
|
105614
|
+
import { readFileSync as readFileSync100, readdirSync as readdirSync44, statSync as statSync60 } from "node:fs";
|
|
105077
105615
|
import { homedir as homedir62 } from "node:os";
|
|
105078
105616
|
import { resolve as resolve62 } from "node:path";
|
|
105079
105617
|
|
|
@@ -105201,7 +105739,7 @@ function readLlmSignals(series) {
|
|
|
105201
105739
|
}
|
|
105202
105740
|
|
|
105203
105741
|
// src/hindsight-watch/recall-log.ts
|
|
105204
|
-
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";
|
|
105205
105743
|
import { join as join111 } from "node:path";
|
|
105206
105744
|
var RECALL_WINDOW_ROWS = 200;
|
|
105207
105745
|
var RECALL_MAX_TAIL_BYTES = 1024 * 1024;
|
|
@@ -105216,7 +105754,7 @@ function readRecallLogTail2(path10, windowRows = RECALL_WINDOW_ROWS) {
|
|
|
105216
105754
|
let truncatedHead = false;
|
|
105217
105755
|
let fd;
|
|
105218
105756
|
try {
|
|
105219
|
-
const size =
|
|
105757
|
+
const size = statSync59(path10).size;
|
|
105220
105758
|
const start = Math.max(0, size - RECALL_MAX_TAIL_BYTES);
|
|
105221
105759
|
truncatedHead = start > 0;
|
|
105222
105760
|
const length = size - start;
|
|
@@ -105448,7 +105986,7 @@ function readDropCount(hindsightDir) {
|
|
|
105448
105986
|
const path10 = resolve62(hindsightDir, "pending-drops.json");
|
|
105449
105987
|
let parsed;
|
|
105450
105988
|
try {
|
|
105451
|
-
if (
|
|
105989
|
+
if (statSync60(path10).size > DROPS_LEDGER_MAX_BYTES)
|
|
105452
105990
|
return 0;
|
|
105453
105991
|
parsed = JSON.parse(readFileSync100(path10, "utf8"));
|
|
105454
105992
|
} catch {
|
|
@@ -105473,6 +106011,7 @@ function probeSpool(agentsDir = resolveAgentsDir2()) {
|
|
|
105473
106011
|
let dead = 0;
|
|
105474
106012
|
let evicted = 0;
|
|
105475
106013
|
let drops = 0;
|
|
106014
|
+
let duplicates = 0;
|
|
105476
106015
|
let agents = 0;
|
|
105477
106016
|
for (const name of names) {
|
|
105478
106017
|
const hindsightDir = resolve62(agentsDir, name, "home", ".hindsight");
|
|
@@ -105501,9 +106040,15 @@ function probeSpool(agentsDir = resolveAgentsDir2()) {
|
|
|
105501
106040
|
dead++;
|
|
105502
106041
|
}
|
|
105503
106042
|
} catch {}
|
|
106043
|
+
try {
|
|
106044
|
+
for (const f of readdirSync44(resolve62(hindsightDir, "pending-duplicate"))) {
|
|
106045
|
+
if (f.endsWith(".json"))
|
|
106046
|
+
duplicates++;
|
|
106047
|
+
}
|
|
106048
|
+
} catch {}
|
|
105504
106049
|
drops += readDropCount(hindsightDir);
|
|
105505
106050
|
}
|
|
105506
|
-
return { pending, dead, evicted, drops, agents };
|
|
106051
|
+
return { pending, dead, evicted, drops, duplicates, agents };
|
|
105507
106052
|
}
|
|
105508
106053
|
var PSQL_BOOTSTRAP_SH = `
|
|
105509
106054
|
set -e
|
|
@@ -105680,6 +106225,7 @@ async function probeOnce(opts = {}) {
|
|
|
105680
106225
|
dead: spool.dead,
|
|
105681
106226
|
evicted: spool.evicted,
|
|
105682
106227
|
drops: spool.drops,
|
|
106228
|
+
duplicates: spool.duplicates,
|
|
105683
106229
|
restartCount: container.restartCount,
|
|
105684
106230
|
startedAt: container.startedAt,
|
|
105685
106231
|
health: container.health,
|
|
@@ -105786,7 +106332,9 @@ function evaluateRetainLoss(ring) {
|
|
|
105786
106332
|
["dropped retain", last.drops - prev.drops]
|
|
105787
106333
|
];
|
|
105788
106334
|
const risen = channels.filter(([, added]) => added > 0);
|
|
105789
|
-
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}`;
|
|
105790
106338
|
if (risen.length > 0) {
|
|
105791
106339
|
return {
|
|
105792
106340
|
signal,
|
|
@@ -105798,7 +106346,8 @@ function evaluateRetainLoss(ring) {
|
|
|
105798
106346
|
dropsAdded: last.drops - prev.drops,
|
|
105799
106347
|
dead: last.dead,
|
|
105800
106348
|
evicted: last.evicted,
|
|
105801
|
-
drops: last.drops
|
|
106349
|
+
drops: last.drops,
|
|
106350
|
+
duplicates: dup ?? 0
|
|
105802
106351
|
}
|
|
105803
106352
|
};
|
|
105804
106353
|
}
|
|
@@ -105812,7 +106361,8 @@ function evaluateRetainLoss(ring) {
|
|
|
105812
106361
|
dropsAdded: 0,
|
|
105813
106362
|
dead: last.dead,
|
|
105814
106363
|
evicted: last.evicted,
|
|
105815
|
-
drops: last.drops
|
|
106364
|
+
drops: last.drops,
|
|
106365
|
+
duplicates: dup ?? 0
|
|
105816
106366
|
}
|
|
105817
106367
|
};
|
|
105818
106368
|
}
|
|
@@ -106002,7 +106552,7 @@ function evaluateConsolidationQueueAge(ring) {
|
|
|
106002
106552
|
return {
|
|
106003
106553
|
signal,
|
|
106004
106554
|
state: "ok",
|
|
106005
|
-
detail: "
|
|
106555
|
+
detail: "no pending/processing operations (failures are not counted here \u2014 " + "see consolidation-failure-streak)",
|
|
106006
106556
|
measured: { pending: 0, oldestAgeS: 0 }
|
|
106007
106557
|
};
|
|
106008
106558
|
}
|
|
@@ -106482,8 +107032,27 @@ function runInstallCron(cronUser) {
|
|
|
106482
107032
|
`) + " This path needs root; re-run under sudo with `--cron-user <operator>`.\n");
|
|
106483
107033
|
return 1;
|
|
106484
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
|
+
}
|
|
106485
107052
|
const verb = res.status === "installed" ? "installed" : "already up to date";
|
|
106486
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}
|
|
106487
107056
|
` + ` Verify with: switchroom doctor | switchroom hindsight-watch --dry-run
|
|
106488
107057
|
`);
|
|
106489
107058
|
return 0;
|