switchroom 0.21.16 → 0.21.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent-scheduler/index.js +5 -0
- package/dist/auth-broker/index.js +5 -0
- package/dist/cli/notion-write-pretool.mjs +5 -0
- package/dist/cli/switchroom.js +163 -31
- package/dist/host-control/main.js +6 -1
- package/dist/vault/approvals/kernel-server.js +5 -0
- package/dist/vault/broker/server.js +5 -0
- package/package.json +1 -1
- package/profiles/_base/start.sh.hbs +11 -0
- package/telegram-plugin/dist/gateway/gateway.js +9 -4
- package/vendor/hindsight-memory/hooks/hooks.json +9 -0
- package/vendor/hindsight-memory/scripts/directive_verify.py +43 -1
- package/vendor/hindsight-memory/scripts/lib/client.py +35 -0
- package/vendor/hindsight-memory/scripts/lib/config.py +47 -0
- package/vendor/hindsight-memory/scripts/lib/orientation.py +248 -0
- package/vendor/hindsight-memory/scripts/lib/recall_buffer.py +29 -0
- package/vendor/hindsight-memory/scripts/orientation.py +195 -0
- package/vendor/hindsight-memory/scripts/prefetch.py +10 -0
- package/vendor/hindsight-memory/scripts/recall.py +144 -11
- package/vendor/hindsight-memory/scripts/setup_hooks.py +10 -1
- package/vendor/hindsight-memory/scripts/tests/fixtures/rules-block.golden.md +9 -0
- package/vendor/hindsight-memory/scripts/tests/test_orientation_hook.py +283 -0
- package/vendor/hindsight-memory/scripts/tests/test_orientation_logic.py +176 -0
- package/vendor/hindsight-memory/scripts/tests/test_prefetch_invalidation.py +329 -0
- package/vendor/hindsight-memory/scripts/tests/test_recall_directive_suppression.py +328 -0
|
@@ -11119,6 +11119,10 @@ var AgentMemorySchema = exports_external.object({
|
|
|
11119
11119
|
}).optional().describe("Operator-declared, per-specialist Hindsight mental models (RFC " + "Phase 5). Named, opt-in curated reflections this agent's bank should " + "carry — e.g. a coach's 'training-plan-state' or a lawyer's " + "'open-matters'. Ensured idempotently at scaffold/reconcile: NOTHING " + "is created unless declared here (zero declarations = zero models, " + "matching post-#2447 behaviour), and no fixed identity model is " + "reintroduced — 'who the user is' stays owned by dedicated profile " + "banks (users.*.profile_bank), never a per-agent model. Per-agent " + "ONLY: intentionally not accepted at the defaults/profile tier, so a " + "model can never be fleet-seeded — each specialist opts in on its own " + "(the invariant-clean inverse of the retired blind auto-seeding)."),
|
|
11120
11120
|
observations_mission: exports_external.string().optional().describe("Steers what the observation-consolidation LLM synthesises from raw " + "facts (the higher-order 'what patterns matter' lens). Cascade: override."),
|
|
11121
11121
|
rules_block: exports_external.boolean().default(false).describe("Memory v2 M3 go-live flag (M1 only DEFINES this, default false; " + "M3 flips it per agent). When true, the sanctioned rules/index " + "blocks render live in this agent's CLAUDE.md (marker-delimited, " + "below the `# --- Yours ---` line) AND the permission deny for " + "direct Edit/Write of the agent's own CLAUDE.md is seeded — the " + "flag couples deny + tools together so a live deny never orphans " + "the invited free-text edit path (the `memory_edit_yours` verb is " + "the only sanctioned writer once flipped). Unset/false ⇒ byte-" + "identical to pre-M1 behaviour: no blocks, no deny, dark build. " + "Cascade: override (per-agent wins over default; never fleet-" + "seeded — each agent's flip is a deliberate M3 rollout step)."),
|
|
11122
|
+
orientation: exports_external.boolean().optional().describe("Memory v2 M5 go-live flag (Surface B: orientation-at-boot), " + "effective default false — dark build (unset ⇒ OFF, applied by the " + "scaffold resolver, NOT a Zod .default() — a hard default here would " + "shadow the per-agent value in the cascade the way it does every other " + "memory knob). When true, the orientation SessionStart " + "hook injects this agent's cron-refreshed `orientation` mental model " + "into context at boot AND re-seats it after every compaction (E-88), " + "deterministically and with zero tool call. Unset/false ⇒ byte-" + "identical to pre-M5 behaviour: the hook no-ops before any bank " + "resolve or network call. Per-agent ONLY (never fleet-seeded — a " + "flip is a deliberate canary/rollout step gated on the agent's " + "m2-residue measurement + operator-created model, carve-M5 §7). " + "Cascade: override (per-agent wins over default)."),
|
|
11123
|
+
orientation_reinject_turns: exports_external.number().int().min(0).optional().describe("Memory v2 M5 optional per-turn re-inject cadence, effective default " + "0 = off (unset ⇒ 0, applied by the scaffold resolver, not a Zod " + ".default() — same cascade-shadowing reason as `orientation`). At 0 the " + "briefing is injected only at SessionStart + compaction (the cheap " + "path). N>0 " + "would additionally re-inject the orientation briefing every N turns; " + "priced at ~55M tok/30d at every-turn, which is WHY it defaults off " + "(carve-M5 §2/§4). Only late-session context loss should ever set it, " + "per-agent, measured. A stripped/absent value fails to 0 (fail-safe " + "AND fail-cheap). Per-agent ONLY. Cascade: override."),
|
|
11124
|
+
orientation_cadence_hours: exports_external.number().int().min(1).optional().describe("Memory v2 M5 per-agent refresh cadence tier (hours), effective " + "default 48 (unset ⇒ 48, applied by the scaffold resolver, not a Zod " + ".default() — a hard default would shadow a `defaults.memory." + "orientation_cadence_hours` fleet value in the cascade). " + "The fast-churning agents (klanker, overlord) set 24; everyone else " + "inherits 48 (carve-M5 §6, Ken's locked tiered cadence). Drives BOTH " + "the out-of-session refresh scheduler AND the staleness guard's " + "per-tier thresholds (stale prefix at 1.5× cadence, cold degrade at " + "3×) — so a fixed 36h can never mislabel a 48h-tier agent as stale a " + "day early. Modelled as an explicit int (not a hard-coded name list) " + "so the tiering is data-driven and auditable. A cost knob, not an " + "enablement knob: a stripped/absent value failing to 48 is safe " + "(the cheaper tier). Cascade: override (per-agent wins over default); " + "unlike `orientation`/`_reinject_turns` it IS accepted at the " + "defaults/profile tier."),
|
|
11125
|
+
inject_directives: exports_external.boolean().optional().describe("Memory v2 M3 Surface-A directive-injection switch (default TRUE — " + "on-by-default via the plugin settings.json stamp + config.py " + "DEFAULTS). When left unset the agent keeps injecting the " + "`<active_directives>` block on every turn, byte-identical to pre-M3. " + "Set FALSE to SUPPRESS that injection for this agent — its standing " + "rules are then served from the live rules block (memory.rules_block) " + "instead of re-injected directives, which is the change that collapses " + "the always-on directive-token spend (E-41). The suppression is " + "fail-safe and re-checked every turn in recall.py: it fires ONLY when " + "a non-empty rules block is physically present in the agent's " + "CLAUDE.md — a false flag with an empty/absent block keeps injecting " + "AND emits a degraded-canary notice, so a zero-standing-rules turn is " + "unreachable. Requires memory.rules_block=true first (the block must " + "be live before injection is turned off — enforce the ordering with " + "`switchroom memory flip`, never a bare flag flip). Coordinates with " + "M4's memory.recall prefetch (memoryPrefetchEnabled): the same guard " + "gates all three recall.py emit sites so a flipped agent leaks no " + "directive on a cache/prefetch hit. Cascade: override, per-agent ONLY " + "(never fleet-seeded from the defaults/profile tier — each flip is a " + "deliberate staggered M3 rollout step, same darkness safeguard as " + "rules_block). Default-true is the fail-safe direction under the " + "Zod-strip footgun: a stripped/misread key keeps the agent injecting."),
|
|
11122
11126
|
disposition: exports_external.object({
|
|
11123
11127
|
skepticism: exports_external.number().int().min(1).max(5).optional().describe("How much the bank doubts unverified claims (1-5; engine default 3)."),
|
|
11124
11128
|
literalism: exports_external.number().int().min(1).max(5).optional().describe("How literally the bank reads statements vs inferring intent (1-5; engine default 3)."),
|
|
@@ -11564,6 +11568,7 @@ var profileFields = {
|
|
|
11564
11568
|
profile: exports_external.string().optional(),
|
|
11565
11569
|
directive_capture_nudge: exports_external.boolean().optional(),
|
|
11566
11570
|
profile_capture_nudge: exports_external.boolean().optional(),
|
|
11571
|
+
orientation_cadence_hours: exports_external.number().int().min(1).optional(),
|
|
11567
11572
|
anti_confabulation_directive: AntiConfabulationDirectiveSchema,
|
|
11568
11573
|
observation_scopes: ObservationScopesSchema,
|
|
11569
11574
|
observation_scope_strategy: ObservationScopeStrategySchema,
|
|
@@ -11144,6 +11144,10 @@ var init_schema = __esm(() => {
|
|
|
11144
11144
|
}).optional().describe("Operator-declared, per-specialist Hindsight mental models (RFC " + "Phase 5). Named, opt-in curated reflections this agent's bank should " + "carry — e.g. a coach's 'training-plan-state' or a lawyer's " + "'open-matters'. Ensured idempotently at scaffold/reconcile: NOTHING " + "is created unless declared here (zero declarations = zero models, " + "matching post-#2447 behaviour), and no fixed identity model is " + "reintroduced — 'who the user is' stays owned by dedicated profile " + "banks (users.*.profile_bank), never a per-agent model. Per-agent " + "ONLY: intentionally not accepted at the defaults/profile tier, so a " + "model can never be fleet-seeded — each specialist opts in on its own " + "(the invariant-clean inverse of the retired blind auto-seeding)."),
|
|
11145
11145
|
observations_mission: exports_external.string().optional().describe("Steers what the observation-consolidation LLM synthesises from raw " + "facts (the higher-order 'what patterns matter' lens). Cascade: override."),
|
|
11146
11146
|
rules_block: exports_external.boolean().default(false).describe("Memory v2 M3 go-live flag (M1 only DEFINES this, default false; " + "M3 flips it per agent). When true, the sanctioned rules/index " + "blocks render live in this agent's CLAUDE.md (marker-delimited, " + "below the `# --- Yours ---` line) AND the permission deny for " + "direct Edit/Write of the agent's own CLAUDE.md is seeded — the " + "flag couples deny + tools together so a live deny never orphans " + "the invited free-text edit path (the `memory_edit_yours` verb is " + "the only sanctioned writer once flipped). Unset/false ⇒ byte-" + "identical to pre-M1 behaviour: no blocks, no deny, dark build. " + "Cascade: override (per-agent wins over default; never fleet-" + "seeded — each agent's flip is a deliberate M3 rollout step)."),
|
|
11147
|
+
orientation: exports_external.boolean().optional().describe("Memory v2 M5 go-live flag (Surface B: orientation-at-boot), " + "effective default false — dark build (unset ⇒ OFF, applied by the " + "scaffold resolver, NOT a Zod .default() — a hard default here would " + "shadow the per-agent value in the cascade the way it does every other " + "memory knob). When true, the orientation SessionStart " + "hook injects this agent's cron-refreshed `orientation` mental model " + "into context at boot AND re-seats it after every compaction (E-88), " + "deterministically and with zero tool call. Unset/false ⇒ byte-" + "identical to pre-M5 behaviour: the hook no-ops before any bank " + "resolve or network call. Per-agent ONLY (never fleet-seeded — a " + "flip is a deliberate canary/rollout step gated on the agent's " + "m2-residue measurement + operator-created model, carve-M5 §7). " + "Cascade: override (per-agent wins over default)."),
|
|
11148
|
+
orientation_reinject_turns: exports_external.number().int().min(0).optional().describe("Memory v2 M5 optional per-turn re-inject cadence, effective default " + "0 = off (unset ⇒ 0, applied by the scaffold resolver, not a Zod " + ".default() — same cascade-shadowing reason as `orientation`). At 0 the " + "briefing is injected only at SessionStart + compaction (the cheap " + "path). N>0 " + "would additionally re-inject the orientation briefing every N turns; " + "priced at ~55M tok/30d at every-turn, which is WHY it defaults off " + "(carve-M5 §2/§4). Only late-session context loss should ever set it, " + "per-agent, measured. A stripped/absent value fails to 0 (fail-safe " + "AND fail-cheap). Per-agent ONLY. Cascade: override."),
|
|
11149
|
+
orientation_cadence_hours: exports_external.number().int().min(1).optional().describe("Memory v2 M5 per-agent refresh cadence tier (hours), effective " + "default 48 (unset ⇒ 48, applied by the scaffold resolver, not a Zod " + ".default() — a hard default would shadow a `defaults.memory." + "orientation_cadence_hours` fleet value in the cascade). " + "The fast-churning agents (klanker, overlord) set 24; everyone else " + "inherits 48 (carve-M5 §6, Ken's locked tiered cadence). Drives BOTH " + "the out-of-session refresh scheduler AND the staleness guard's " + "per-tier thresholds (stale prefix at 1.5× cadence, cold degrade at " + "3×) — so a fixed 36h can never mislabel a 48h-tier agent as stale a " + "day early. Modelled as an explicit int (not a hard-coded name list) " + "so the tiering is data-driven and auditable. A cost knob, not an " + "enablement knob: a stripped/absent value failing to 48 is safe " + "(the cheaper tier). Cascade: override (per-agent wins over default); " + "unlike `orientation`/`_reinject_turns` it IS accepted at the " + "defaults/profile tier."),
|
|
11150
|
+
inject_directives: exports_external.boolean().optional().describe("Memory v2 M3 Surface-A directive-injection switch (default TRUE — " + "on-by-default via the plugin settings.json stamp + config.py " + "DEFAULTS). When left unset the agent keeps injecting the " + "`<active_directives>` block on every turn, byte-identical to pre-M3. " + "Set FALSE to SUPPRESS that injection for this agent — its standing " + "rules are then served from the live rules block (memory.rules_block) " + "instead of re-injected directives, which is the change that collapses " + "the always-on directive-token spend (E-41). The suppression is " + "fail-safe and re-checked every turn in recall.py: it fires ONLY when " + "a non-empty rules block is physically present in the agent's " + "CLAUDE.md — a false flag with an empty/absent block keeps injecting " + "AND emits a degraded-canary notice, so a zero-standing-rules turn is " + "unreachable. Requires memory.rules_block=true first (the block must " + "be live before injection is turned off — enforce the ordering with " + "`switchroom memory flip`, never a bare flag flip). Coordinates with " + "M4's memory.recall prefetch (memoryPrefetchEnabled): the same guard " + "gates all three recall.py emit sites so a flipped agent leaks no " + "directive on a cache/prefetch hit. Cascade: override, per-agent ONLY " + "(never fleet-seeded from the defaults/profile tier — each flip is a " + "deliberate staggered M3 rollout step, same darkness safeguard as " + "rules_block). Default-true is the fail-safe direction under the " + "Zod-strip footgun: a stripped/misread key keeps the agent injecting."),
|
|
11147
11151
|
disposition: exports_external.object({
|
|
11148
11152
|
skepticism: exports_external.number().int().min(1).max(5).optional().describe("How much the bank doubts unverified claims (1-5; engine default 3)."),
|
|
11149
11153
|
literalism: exports_external.number().int().min(1).max(5).optional().describe("How literally the bank reads statements vs inferring intent (1-5; engine default 3)."),
|
|
@@ -11589,6 +11593,7 @@ var init_schema = __esm(() => {
|
|
|
11589
11593
|
profile: exports_external.string().optional(),
|
|
11590
11594
|
directive_capture_nudge: exports_external.boolean().optional(),
|
|
11591
11595
|
profile_capture_nudge: exports_external.boolean().optional(),
|
|
11596
|
+
orientation_cadence_hours: exports_external.number().int().min(1).optional(),
|
|
11592
11597
|
anti_confabulation_directive: AntiConfabulationDirectiveSchema,
|
|
11593
11598
|
observation_scopes: ObservationScopesSchema,
|
|
11594
11599
|
observation_scope_strategy: ObservationScopeStrategySchema,
|
|
@@ -11880,6 +11880,10 @@ var AgentMemorySchema = exports_external.object({
|
|
|
11880
11880
|
}).optional().describe("Operator-declared, per-specialist Hindsight mental models (RFC " + "Phase 5). Named, opt-in curated reflections this agent's bank should " + "carry \u2014 e.g. a coach's 'training-plan-state' or a lawyer's " + "'open-matters'. Ensured idempotently at scaffold/reconcile: NOTHING " + "is created unless declared here (zero declarations = zero models, " + "matching post-#2447 behaviour), and no fixed identity model is " + "reintroduced \u2014 'who the user is' stays owned by dedicated profile " + "banks (users.*.profile_bank), never a per-agent model. Per-agent " + "ONLY: intentionally not accepted at the defaults/profile tier, so a " + "model can never be fleet-seeded \u2014 each specialist opts in on its own " + "(the invariant-clean inverse of the retired blind auto-seeding)."),
|
|
11881
11881
|
observations_mission: exports_external.string().optional().describe("Steers what the observation-consolidation LLM synthesises from raw " + "facts (the higher-order 'what patterns matter' lens). Cascade: override."),
|
|
11882
11882
|
rules_block: exports_external.boolean().default(false).describe("Memory v2 M3 go-live flag (M1 only DEFINES this, default false; " + "M3 flips it per agent). When true, the sanctioned rules/index " + "blocks render live in this agent's CLAUDE.md (marker-delimited, " + "below the `# --- Yours ---` line) AND the permission deny for " + "direct Edit/Write of the agent's own CLAUDE.md is seeded \u2014 the " + "flag couples deny + tools together so a live deny never orphans " + "the invited free-text edit path (the `memory_edit_yours` verb is " + "the only sanctioned writer once flipped). Unset/false \u21d2 byte-" + "identical to pre-M1 behaviour: no blocks, no deny, dark build. " + "Cascade: override (per-agent wins over default; never fleet-" + "seeded \u2014 each agent's flip is a deliberate M3 rollout step)."),
|
|
11883
|
+
orientation: exports_external.boolean().optional().describe("Memory v2 M5 go-live flag (Surface B: orientation-at-boot), " + "effective default false \u2014 dark build (unset \u21d2 OFF, applied by the " + "scaffold resolver, NOT a Zod .default() \u2014 a hard default here would " + "shadow the per-agent value in the cascade the way it does every other " + "memory knob). When true, the orientation SessionStart " + "hook injects this agent's cron-refreshed `orientation` mental model " + "into context at boot AND re-seats it after every compaction (E-88), " + "deterministically and with zero tool call. Unset/false \u21d2 byte-" + "identical to pre-M5 behaviour: the hook no-ops before any bank " + "resolve or network call. Per-agent ONLY (never fleet-seeded \u2014 a " + "flip is a deliberate canary/rollout step gated on the agent's " + "m2-residue measurement + operator-created model, carve-M5 \u00a77). " + "Cascade: override (per-agent wins over default)."),
|
|
11884
|
+
orientation_reinject_turns: exports_external.number().int().min(0).optional().describe("Memory v2 M5 optional per-turn re-inject cadence, effective default " + "0 = off (unset \u21d2 0, applied by the scaffold resolver, not a Zod " + ".default() \u2014 same cascade-shadowing reason as `orientation`). At 0 the " + "briefing is injected only at SessionStart + compaction (the cheap " + "path). N>0 " + "would additionally re-inject the orientation briefing every N turns; " + "priced at ~55M tok/30d at every-turn, which is WHY it defaults off " + "(carve-M5 \u00a72/\u00a74). Only late-session context loss should ever set it, " + "per-agent, measured. A stripped/absent value fails to 0 (fail-safe " + "AND fail-cheap). Per-agent ONLY. Cascade: override."),
|
|
11885
|
+
orientation_cadence_hours: exports_external.number().int().min(1).optional().describe("Memory v2 M5 per-agent refresh cadence tier (hours), effective " + "default 48 (unset \u21d2 48, applied by the scaffold resolver, not a Zod " + ".default() \u2014 a hard default would shadow a `defaults.memory." + "orientation_cadence_hours` fleet value in the cascade). " + "The fast-churning agents (klanker, overlord) set 24; everyone else " + "inherits 48 (carve-M5 \u00a76, Ken's locked tiered cadence). Drives BOTH " + "the out-of-session refresh scheduler AND the staleness guard's " + "per-tier thresholds (stale prefix at 1.5\u00d7 cadence, cold degrade at " + "3\u00d7) \u2014 so a fixed 36h can never mislabel a 48h-tier agent as stale a " + "day early. Modelled as an explicit int (not a hard-coded name list) " + "so the tiering is data-driven and auditable. A cost knob, not an " + "enablement knob: a stripped/absent value failing to 48 is safe " + "(the cheaper tier). Cascade: override (per-agent wins over default); " + "unlike `orientation`/`_reinject_turns` it IS accepted at the " + "defaults/profile tier."),
|
|
11886
|
+
inject_directives: exports_external.boolean().optional().describe("Memory v2 M3 Surface-A directive-injection switch (default TRUE \u2014 " + "on-by-default via the plugin settings.json stamp + config.py " + "DEFAULTS). When left unset the agent keeps injecting the " + "`<active_directives>` block on every turn, byte-identical to pre-M3. " + "Set FALSE to SUPPRESS that injection for this agent \u2014 its standing " + "rules are then served from the live rules block (memory.rules_block) " + "instead of re-injected directives, which is the change that collapses " + "the always-on directive-token spend (E-41). The suppression is " + "fail-safe and re-checked every turn in recall.py: it fires ONLY when " + "a non-empty rules block is physically present in the agent's " + "CLAUDE.md \u2014 a false flag with an empty/absent block keeps injecting " + "AND emits a degraded-canary notice, so a zero-standing-rules turn is " + "unreachable. Requires memory.rules_block=true first (the block must " + "be live before injection is turned off \u2014 enforce the ordering with " + "`switchroom memory flip`, never a bare flag flip). Coordinates with " + "M4's memory.recall prefetch (memoryPrefetchEnabled): the same guard " + "gates all three recall.py emit sites so a flipped agent leaks no " + "directive on a cache/prefetch hit. Cascade: override, per-agent ONLY " + "(never fleet-seeded from the defaults/profile tier \u2014 each flip is a " + "deliberate staggered M3 rollout step, same darkness safeguard as " + "rules_block). Default-true is the fail-safe direction under the " + "Zod-strip footgun: a stripped/misread key keeps the agent injecting."),
|
|
11883
11887
|
disposition: exports_external.object({
|
|
11884
11888
|
skepticism: exports_external.number().int().min(1).max(5).optional().describe("How much the bank doubts unverified claims (1-5; engine default 3)."),
|
|
11885
11889
|
literalism: exports_external.number().int().min(1).max(5).optional().describe("How literally the bank reads statements vs inferring intent (1-5; engine default 3)."),
|
|
@@ -12325,6 +12329,7 @@ var profileFields = {
|
|
|
12325
12329
|
profile: exports_external.string().optional(),
|
|
12326
12330
|
directive_capture_nudge: exports_external.boolean().optional(),
|
|
12327
12331
|
profile_capture_nudge: exports_external.boolean().optional(),
|
|
12332
|
+
orientation_cadence_hours: exports_external.number().int().min(1).optional(),
|
|
12328
12333
|
anti_confabulation_directive: AntiConfabulationDirectiveSchema,
|
|
12329
12334
|
observation_scopes: ObservationScopesSchema,
|
|
12330
12335
|
observation_scope_strategy: ObservationScopeStrategySchema,
|
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.21.
|
|
2123
|
+
var VERSION = "0.21.17", COMMIT_SHA = "ac61272c", COMMIT_DATE = "2026-08-17T16:42:58Z";
|
|
2124
2124
|
|
|
2125
2125
|
// src/cli/resolve-version.ts
|
|
2126
2126
|
import { existsSync, readFileSync } from "node:fs";
|
|
@@ -13745,6 +13745,10 @@ var init_schema = __esm(() => {
|
|
|
13745
13745
|
}).optional().describe("Operator-declared, per-specialist Hindsight mental models (RFC " + "Phase 5). Named, opt-in curated reflections this agent's bank should " + "carry \u2014 e.g. a coach's 'training-plan-state' or a lawyer's " + "'open-matters'. Ensured idempotently at scaffold/reconcile: NOTHING " + "is created unless declared here (zero declarations = zero models, " + "matching post-#2447 behaviour), and no fixed identity model is " + "reintroduced \u2014 'who the user is' stays owned by dedicated profile " + "banks (users.*.profile_bank), never a per-agent model. Per-agent " + "ONLY: intentionally not accepted at the defaults/profile tier, so a " + "model can never be fleet-seeded \u2014 each specialist opts in on its own " + "(the invariant-clean inverse of the retired blind auto-seeding)."),
|
|
13746
13746
|
observations_mission: exports_external.string().optional().describe("Steers what the observation-consolidation LLM synthesises from raw " + "facts (the higher-order 'what patterns matter' lens). Cascade: override."),
|
|
13747
13747
|
rules_block: exports_external.boolean().default(false).describe("Memory v2 M3 go-live flag (M1 only DEFINES this, default false; " + "M3 flips it per agent). When true, the sanctioned rules/index " + "blocks render live in this agent's CLAUDE.md (marker-delimited, " + "below the `# --- Yours ---` line) AND the permission deny for " + "direct Edit/Write of the agent's own CLAUDE.md is seeded \u2014 the " + "flag couples deny + tools together so a live deny never orphans " + "the invited free-text edit path (the `memory_edit_yours` verb is " + "the only sanctioned writer once flipped). Unset/false \u21d2 byte-" + "identical to pre-M1 behaviour: no blocks, no deny, dark build. " + "Cascade: override (per-agent wins over default; never fleet-" + "seeded \u2014 each agent's flip is a deliberate M3 rollout step)."),
|
|
13748
|
+
orientation: exports_external.boolean().optional().describe("Memory v2 M5 go-live flag (Surface B: orientation-at-boot), " + "effective default false \u2014 dark build (unset \u21d2 OFF, applied by the " + "scaffold resolver, NOT a Zod .default() \u2014 a hard default here would " + "shadow the per-agent value in the cascade the way it does every other " + "memory knob). When true, the orientation SessionStart " + "hook injects this agent's cron-refreshed `orientation` mental model " + "into context at boot AND re-seats it after every compaction (E-88), " + "deterministically and with zero tool call. Unset/false \u21d2 byte-" + "identical to pre-M5 behaviour: the hook no-ops before any bank " + "resolve or network call. Per-agent ONLY (never fleet-seeded \u2014 a " + "flip is a deliberate canary/rollout step gated on the agent's " + "m2-residue measurement + operator-created model, carve-M5 \u00a77). " + "Cascade: override (per-agent wins over default)."),
|
|
13749
|
+
orientation_reinject_turns: exports_external.number().int().min(0).optional().describe("Memory v2 M5 optional per-turn re-inject cadence, effective default " + "0 = off (unset \u21d2 0, applied by the scaffold resolver, not a Zod " + ".default() \u2014 same cascade-shadowing reason as `orientation`). At 0 the " + "briefing is injected only at SessionStart + compaction (the cheap " + "path). N>0 " + "would additionally re-inject the orientation briefing every N turns; " + "priced at ~55M tok/30d at every-turn, which is WHY it defaults off " + "(carve-M5 \u00a72/\u00a74). Only late-session context loss should ever set it, " + "per-agent, measured. A stripped/absent value fails to 0 (fail-safe " + "AND fail-cheap). Per-agent ONLY. Cascade: override."),
|
|
13750
|
+
orientation_cadence_hours: exports_external.number().int().min(1).optional().describe("Memory v2 M5 per-agent refresh cadence tier (hours), effective " + "default 48 (unset \u21d2 48, applied by the scaffold resolver, not a Zod " + ".default() \u2014 a hard default would shadow a `defaults.memory." + "orientation_cadence_hours` fleet value in the cascade). " + "The fast-churning agents (klanker, overlord) set 24; everyone else " + "inherits 48 (carve-M5 \u00a76, Ken's locked tiered cadence). Drives BOTH " + "the out-of-session refresh scheduler AND the staleness guard's " + "per-tier thresholds (stale prefix at 1.5\u00d7 cadence, cold degrade at " + "3\u00d7) \u2014 so a fixed 36h can never mislabel a 48h-tier agent as stale a " + "day early. Modelled as an explicit int (not a hard-coded name list) " + "so the tiering is data-driven and auditable. A cost knob, not an " + "enablement knob: a stripped/absent value failing to 48 is safe " + "(the cheaper tier). Cascade: override (per-agent wins over default); " + "unlike `orientation`/`_reinject_turns` it IS accepted at the " + "defaults/profile tier."),
|
|
13751
|
+
inject_directives: exports_external.boolean().optional().describe("Memory v2 M3 Surface-A directive-injection switch (default TRUE \u2014 " + "on-by-default via the plugin settings.json stamp + config.py " + "DEFAULTS). When left unset the agent keeps injecting the " + "`<active_directives>` block on every turn, byte-identical to pre-M3. " + "Set FALSE to SUPPRESS that injection for this agent \u2014 its standing " + "rules are then served from the live rules block (memory.rules_block) " + "instead of re-injected directives, which is the change that collapses " + "the always-on directive-token spend (E-41). The suppression is " + "fail-safe and re-checked every turn in recall.py: it fires ONLY when " + "a non-empty rules block is physically present in the agent's " + "CLAUDE.md \u2014 a false flag with an empty/absent block keeps injecting " + "AND emits a degraded-canary notice, so a zero-standing-rules turn is " + "unreachable. Requires memory.rules_block=true first (the block must " + "be live before injection is turned off \u2014 enforce the ordering with " + "`switchroom memory flip`, never a bare flag flip). Coordinates with " + "M4's memory.recall prefetch (memoryPrefetchEnabled): the same guard " + "gates all three recall.py emit sites so a flipped agent leaks no " + "directive on a cache/prefetch hit. Cascade: override, per-agent ONLY " + "(never fleet-seeded from the defaults/profile tier \u2014 each flip is a " + "deliberate staggered M3 rollout step, same darkness safeguard as " + "rules_block). Default-true is the fail-safe direction under the " + "Zod-strip footgun: a stripped/misread key keeps the agent injecting."),
|
|
13748
13752
|
disposition: exports_external.object({
|
|
13749
13753
|
skepticism: exports_external.number().int().min(1).max(5).optional().describe("How much the bank doubts unverified claims (1-5; engine default 3)."),
|
|
13750
13754
|
literalism: exports_external.number().int().min(1).max(5).optional().describe("How literally the bank reads statements vs inferring intent (1-5; engine default 3)."),
|
|
@@ -14190,6 +14194,7 @@ var init_schema = __esm(() => {
|
|
|
14190
14194
|
profile: exports_external.string().optional(),
|
|
14191
14195
|
directive_capture_nudge: exports_external.boolean().optional(),
|
|
14192
14196
|
profile_capture_nudge: exports_external.boolean().optional(),
|
|
14197
|
+
orientation_cadence_hours: exports_external.number().int().min(1).optional(),
|
|
14193
14198
|
anti_confabulation_directive: AntiConfabulationDirectiveSchema,
|
|
14194
14199
|
observation_scopes: ObservationScopesSchema,
|
|
14195
14200
|
observation_scope_strategy: ObservationScopeStrategySchema,
|
|
@@ -19562,6 +19567,9 @@ function generateHindsightMcpConfig(collection, memoryConfig, opts = {}) {
|
|
|
19562
19567
|
}
|
|
19563
19568
|
};
|
|
19564
19569
|
}
|
|
19570
|
+
const grantedBanks = [
|
|
19571
|
+
...new Set((opts.knowledgeExtraBanks ?? []).filter((b) => b && b !== collection))
|
|
19572
|
+
];
|
|
19565
19573
|
return {
|
|
19566
19574
|
type: "stdio",
|
|
19567
19575
|
command: HINDSIGHT_SHIM_CLI_PATH,
|
|
@@ -19569,6 +19577,7 @@ function generateHindsightMcpConfig(collection, memoryConfig, opts = {}) {
|
|
|
19569
19577
|
env: {
|
|
19570
19578
|
HINDSIGHT_MCP_URL: url,
|
|
19571
19579
|
HINDSIGHT_BANK_ID: collection,
|
|
19580
|
+
...grantedBanks.length > 0 ? { HINDSIGHT_KNOWLEDGE_EXTRA_BANKS: grantedBanks.join(",") } : {},
|
|
19572
19581
|
HINDSIGHT_SHIM_CACHE_DIR: `${HINDSIGHT_SHIM_AGENT_HOME}/.hindsight-shim`,
|
|
19573
19582
|
HOME: HINDSIGHT_SHIM_AGENT_HOME,
|
|
19574
19583
|
...opts.reflectBudget ? { HINDSIGHT_SHIM_REFLECT_BUDGET: opts.reflectBudget } : {},
|
|
@@ -28007,9 +28016,11 @@ function getHindsightSettingsEntry(agentName, config) {
|
|
|
28007
28016
|
}
|
|
28008
28017
|
const collection = getCollectionForAgent(agentName, config);
|
|
28009
28018
|
const resolved = resolveAgentConfig(config.defaults, config.profiles, config.agents[agentName] ?? {});
|
|
28019
|
+
const { additionalBanks } = resolveUsers(config, agentName);
|
|
28010
28020
|
const mcpConfig = generateHindsightMcpConfig(collection, memoryConfig, {
|
|
28011
28021
|
reflectBudget: resolved.memory?.reflect_budget,
|
|
28012
|
-
reflectMaxTokens: resolved.memory?.reflect_max_tokens
|
|
28022
|
+
reflectMaxTokens: resolved.memory?.reflect_max_tokens,
|
|
28023
|
+
knowledgeExtraBanks: additionalBanks
|
|
28013
28024
|
});
|
|
28014
28025
|
return { key: "hindsight", value: { ...mcpConfig, alwaysLoad: false } };
|
|
28015
28026
|
}
|
|
@@ -28102,6 +28113,7 @@ function getBuiltinDefaultSkillEntries() {
|
|
|
28102
28113
|
var GOOGLE_WORKSPACE_MCP_PINNED_SHA = "db6212992738d4380a94fb6940140bcebf5d861d", MICROSOFT_WORKSPACE_MCP_PINNED_VERSION = "0.113.0", MICROSOFT_WORKSPACE_MCP_PACKAGE = "@softeria/ms-365-mcp-server", NOTION_MCP_PINNED_VERSION = "1.8.1", NOTION_MCP_PACKAGE = "@notionhq/notion-mcp-server", NOTION_TOKEN_ENV = "NOTION_TOKEN";
|
|
28103
28114
|
var init_scaffold_integration = __esm(() => {
|
|
28104
28115
|
init_merge();
|
|
28116
|
+
init_users();
|
|
28105
28117
|
init_hindsight2();
|
|
28106
28118
|
});
|
|
28107
28119
|
|
|
@@ -30493,6 +30505,7 @@ function installHindsightPlugin(agentName, agentDir, switchroomConfig, resolvedA
|
|
|
30493
30505
|
const additionalBanks = resolveUsers(switchroomConfig, agentName).additionalBanks;
|
|
30494
30506
|
const retainConfig = resolveHindsightRetainConfig(switchroomConfig, agentName, resolvedAgentConfig);
|
|
30495
30507
|
const observationScopeSettings = resolveHindsightObservationScopeSettings(switchroomConfig, agentName, resolvedAgentConfig);
|
|
30508
|
+
const orientationSettings = resolveHindsightOrientationSettings(switchroomConfig, agentName, resolvedAgentConfig);
|
|
30496
30509
|
const recallConfig = resolveHindsightRecallConfig(switchroomConfig, agentName, resolvedAgentConfig);
|
|
30497
30510
|
const recallTunables = resolveHindsightRecallTunables(recallConfig);
|
|
30498
30511
|
const recallCaps = resolveHindsightRecallCaps(recallConfig);
|
|
@@ -30500,7 +30513,7 @@ function installHindsightPlugin(agentName, agentDir, switchroomConfig, resolvedA
|
|
|
30500
30513
|
try {
|
|
30501
30514
|
const vendorSettingsPath = join15(sourcePath, "settings.json");
|
|
30502
30515
|
if (existsSync20(vendorSettingsPath)) {
|
|
30503
|
-
const expectedSettings = renderHindsightSettingsOverrides(readFileSync17(vendorSettingsPath, "utf-8"), additionalBanks, retainConfig, recallTunables, recallCaps, observationScopeSettings);
|
|
30516
|
+
const expectedSettings = renderHindsightSettingsOverrides(readFileSync17(vendorSettingsPath, "utf-8"), additionalBanks, retainConfig, recallTunables, recallCaps, observationScopeSettings, orientationSettings);
|
|
30504
30517
|
if (expectedSettings != null)
|
|
30505
30518
|
contentOverrides["settings.json"] = expectedSettings;
|
|
30506
30519
|
}
|
|
@@ -30518,7 +30531,7 @@ function installHindsightPlugin(agentName, agentDir, switchroomConfig, resolvedA
|
|
|
30518
30531
|
}
|
|
30519
30532
|
copyDirRecursive2(sourcePath, destPath);
|
|
30520
30533
|
}
|
|
30521
|
-
applyHindsightSettingsOverrides(destPath, additionalBanks, retainConfig, recallTunables, recallCaps, observationScopeSettings);
|
|
30534
|
+
applyHindsightSettingsOverrides(destPath, additionalBanks, retainConfig, recallTunables, recallCaps, observationScopeSettings, orientationSettings);
|
|
30522
30535
|
applyHindsightHooksOverrides(destPath, recallTunables);
|
|
30523
30536
|
const bankId = agentMemory?.collection ?? agentName;
|
|
30524
30537
|
const mcpUrl = memory.config?.url ?? HINDSIGHT_DEFAULT_MCP_URL;
|
|
@@ -30547,6 +30560,15 @@ function resolveHindsightObservationScopeSettings(switchroomConfig, agentName, r
|
|
|
30547
30560
|
observationScopeStrategy: memory?.observation_scope_strategy
|
|
30548
30561
|
};
|
|
30549
30562
|
}
|
|
30563
|
+
function resolveHindsightOrientationSettings(switchroomConfig, agentName, resolvedAgentConfig) {
|
|
30564
|
+
const memory = resolvedAgentConfig ? resolvedAgentConfig.memory : switchroomConfig ? resolveAgentConfig(switchroomConfig.defaults, switchroomConfig.profiles, switchroomConfig.agents[agentName] ?? {})?.memory : undefined;
|
|
30565
|
+
return {
|
|
30566
|
+
enabled: memory?.orientation === true,
|
|
30567
|
+
model: "orientation",
|
|
30568
|
+
cadenceHours: typeof memory?.orientation_cadence_hours === "number" ? memory.orientation_cadence_hours : 48,
|
|
30569
|
+
reinjectTurns: typeof memory?.orientation_reinject_turns === "number" ? memory.orientation_reinject_turns : 0
|
|
30570
|
+
};
|
|
30571
|
+
}
|
|
30550
30572
|
function resolveHindsightRecallConfig(switchroomConfig, agentName, resolvedAgentConfig) {
|
|
30551
30573
|
if (resolvedAgentConfig) {
|
|
30552
30574
|
return resolvedAgentConfig.memory?.recall;
|
|
@@ -30581,7 +30603,7 @@ function defaultRecallOwnBankMinSlots(maxMemories) {
|
|
|
30581
30603
|
function defaultRecallAdditionalBankMinSlots(maxMemories) {
|
|
30582
30604
|
return recallSlotFloorFromCap(maxMemories, RECALL_ADDITIONAL_BANK_SLOT_DIVISOR);
|
|
30583
30605
|
}
|
|
30584
|
-
function applyHindsightSettingsOverrides(pluginDestPath, additionalBanks, retainConfig, recallTunables, recallCaps, observationScopeSettings) {
|
|
30606
|
+
function applyHindsightSettingsOverrides(pluginDestPath, additionalBanks, retainConfig, recallTunables, recallCaps, observationScopeSettings, orientationSettings) {
|
|
30585
30607
|
const settingsPath = join15(pluginDestPath, "settings.json");
|
|
30586
30608
|
if (!existsSync20(settingsPath))
|
|
30587
30609
|
return;
|
|
@@ -30591,12 +30613,12 @@ function applyHindsightSettingsOverrides(pluginDestPath, additionalBanks, retain
|
|
|
30591
30613
|
} catch {
|
|
30592
30614
|
return;
|
|
30593
30615
|
}
|
|
30594
|
-
const next = renderHindsightSettingsOverrides(raw, additionalBanks, retainConfig, recallTunables, recallCaps, observationScopeSettings);
|
|
30616
|
+
const next = renderHindsightSettingsOverrides(raw, additionalBanks, retainConfig, recallTunables, recallCaps, observationScopeSettings, orientationSettings);
|
|
30595
30617
|
if (next == null)
|
|
30596
30618
|
return;
|
|
30597
30619
|
writeFileSyncIfChanged(settingsPath, next);
|
|
30598
30620
|
}
|
|
30599
|
-
function renderHindsightSettingsOverrides(raw, additionalBanks, retainConfig, recallTunables, recallCaps, observationScopeSettings) {
|
|
30621
|
+
function renderHindsightSettingsOverrides(raw, additionalBanks, retainConfig, recallTunables, recallCaps, observationScopeSettings, orientationSettings) {
|
|
30600
30622
|
let settings;
|
|
30601
30623
|
try {
|
|
30602
30624
|
settings = JSON.parse(raw);
|
|
@@ -30618,6 +30640,7 @@ function renderHindsightSettingsOverrides(raw, additionalBanks, retainConfig, re
|
|
|
30618
30640
|
settings.recallSkipTrivial = true;
|
|
30619
30641
|
settings.directiveCaptureNudge = true;
|
|
30620
30642
|
settings.profileCaptureNudge = true;
|
|
30643
|
+
settings.injectDirectives = true;
|
|
30621
30644
|
settings.recallTagWeights = { ...HINDSIGHT_RECALL_TAG_WEIGHT_SEED };
|
|
30622
30645
|
if (additionalBanks.length > 0) {
|
|
30623
30646
|
settings.recallAdditionalBanks = [...additionalBanks];
|
|
@@ -30630,6 +30653,10 @@ function renderHindsightSettingsOverrides(raw, additionalBanks, retainConfig, re
|
|
|
30630
30653
|
if (typeof observationScopeStrategy === "string" && observationScopeStrategy.length > 0) {
|
|
30631
30654
|
settings.observationScopeStrategy = observationScopeStrategy;
|
|
30632
30655
|
}
|
|
30656
|
+
settings.memoryOrientationEnabled = orientationSettings.enabled;
|
|
30657
|
+
settings.memoryOrientationModel = orientationSettings.model;
|
|
30658
|
+
settings.memoryOrientationCadenceHours = orientationSettings.cadenceHours;
|
|
30659
|
+
settings.memoryOrientationReinjectTurns = orientationSettings.reinjectTurns;
|
|
30633
30660
|
return JSON.stringify(settings, null, 2) + `
|
|
30634
30661
|
`;
|
|
30635
30662
|
}
|
|
@@ -30672,6 +30699,7 @@ function buildWorkspaceContext(args) {
|
|
|
30672
30699
|
hindsightRecallSkipTrivial,
|
|
30673
30700
|
hindsightDirectiveCaptureNudge,
|
|
30674
30701
|
hindsightProfileCaptureNudge,
|
|
30702
|
+
hindsightInjectDirectives,
|
|
30675
30703
|
hindsightObservationScopes,
|
|
30676
30704
|
hindsightObservationScopeStrategy,
|
|
30677
30705
|
hindsightTopicAliasesJson,
|
|
@@ -30728,6 +30756,7 @@ function buildWorkspaceContext(args) {
|
|
|
30728
30756
|
hindsightRecallSkipTrivial,
|
|
30729
30757
|
hindsightDirectiveCaptureNudge,
|
|
30730
30758
|
hindsightProfileCaptureNudge,
|
|
30759
|
+
hindsightInjectDirectives,
|
|
30731
30760
|
hindsightObservationScopesQ: hindsightObservationScopes ? shellSingleQuote(hindsightObservationScopes) : undefined,
|
|
30732
30761
|
hindsightObservationScopeStrategyQ: hindsightObservationScopeStrategy ? shellSingleQuote(hindsightObservationScopeStrategy) : undefined,
|
|
30733
30762
|
hindsightTopicAliasesJsonQ: hindsightTopicAliasesJson ? shellSingleQuote(hindsightTopicAliasesJson) : undefined,
|
|
@@ -31004,6 +31033,7 @@ function scaffoldAgent(name, agentConfigRaw, agentsDir, telegramConfig, switchro
|
|
|
31004
31033
|
const hindsightRecallSkipTrivial = agentConfig.memory?.recall?.skip_trivial === undefined ? undefined : String(agentConfig.memory.recall.skip_trivial);
|
|
31005
31034
|
const hindsightDirectiveCaptureNudge = agentConfig.memory?.directive_capture_nudge === undefined ? undefined : String(agentConfig.memory.directive_capture_nudge);
|
|
31006
31035
|
const hindsightProfileCaptureNudge = agentConfig.memory?.profile_capture_nudge === undefined ? undefined : String(agentConfig.memory.profile_capture_nudge);
|
|
31036
|
+
const hindsightInjectDirectives = agentConfig.memory?.inject_directives === undefined ? undefined : String(agentConfig.memory.inject_directives);
|
|
31007
31037
|
const hindsightObservationScopes = agentConfig.memory?.observation_scopes;
|
|
31008
31038
|
const hindsightObservationScopeStrategy = agentConfig.memory?.observation_scope_strategy;
|
|
31009
31039
|
const topicAliases = agentConfig.channels?.telegram?.topic_aliases;
|
|
@@ -31042,6 +31072,7 @@ function scaffoldAgent(name, agentConfigRaw, agentsDir, telegramConfig, switchro
|
|
|
31042
31072
|
hindsightRecallSkipTrivial,
|
|
31043
31073
|
hindsightDirectiveCaptureNudge,
|
|
31044
31074
|
hindsightProfileCaptureNudge,
|
|
31075
|
+
hindsightInjectDirectives,
|
|
31045
31076
|
hindsightObservationScopes,
|
|
31046
31077
|
hindsightObservationScopeStrategy,
|
|
31047
31078
|
hindsightTopicAliasesJson,
|
|
@@ -32031,6 +32062,7 @@ function reconcileAgentInner(name, agentConfigRaw, agentsDir, telegramConfig, sw
|
|
|
32031
32062
|
const hindsightRecallSkipTrivial = agentConfig.memory?.recall?.skip_trivial === undefined ? undefined : String(agentConfig.memory.recall.skip_trivial);
|
|
32032
32063
|
const hindsightDirectiveCaptureNudge = agentConfig.memory?.directive_capture_nudge === undefined ? undefined : String(agentConfig.memory.directive_capture_nudge);
|
|
32033
32064
|
const hindsightProfileCaptureNudge = agentConfig.memory?.profile_capture_nudge === undefined ? undefined : String(agentConfig.memory.profile_capture_nudge);
|
|
32065
|
+
const hindsightInjectDirectives = agentConfig.memory?.inject_directives === undefined ? undefined : String(agentConfig.memory.inject_directives);
|
|
32034
32066
|
const hindsightObservationScopes = agentConfig.memory?.observation_scopes;
|
|
32035
32067
|
const hindsightObservationScopeStrategy = agentConfig.memory?.observation_scope_strategy;
|
|
32036
32068
|
const topicAliases = agentConfig.channels?.telegram?.topic_aliases;
|
|
@@ -32081,6 +32113,7 @@ function reconcileAgentInner(name, agentConfigRaw, agentsDir, telegramConfig, sw
|
|
|
32081
32113
|
hindsightRecallSkipTrivial,
|
|
32082
32114
|
hindsightDirectiveCaptureNudge,
|
|
32083
32115
|
hindsightProfileCaptureNudge,
|
|
32116
|
+
hindsightInjectDirectives,
|
|
32084
32117
|
hindsightObservationScopesQ: hindsightObservationScopes ? shellSingleQuote(hindsightObservationScopes) : undefined,
|
|
32085
32118
|
hindsightObservationScopeStrategyQ: hindsightObservationScopeStrategy ? shellSingleQuote(hindsightObservationScopeStrategy) : undefined,
|
|
32086
32119
|
hindsightTopicAliasesJsonQ: hindsightTopicAliasesJson ? shellSingleQuote(hindsightTopicAliasesJson) : undefined,
|
|
@@ -32519,6 +32552,7 @@ ${body}
|
|
|
32519
32552
|
hindsightRecallSkipTrivial,
|
|
32520
32553
|
hindsightDirectiveCaptureNudge,
|
|
32521
32554
|
hindsightProfileCaptureNudge,
|
|
32555
|
+
hindsightInjectDirectives,
|
|
32522
32556
|
hindsightObservationScopes,
|
|
32523
32557
|
hindsightObservationScopeStrategy,
|
|
32524
32558
|
hindsightTopicAliasesJson,
|
|
@@ -66507,6 +66541,51 @@ function readClaudeMdSafe(agentsDir, agentName, readFileFn) {
|
|
|
66507
66541
|
return null;
|
|
66508
66542
|
}
|
|
66509
66543
|
}
|
|
66544
|
+
function runDirectiveFlipChecks(config, deps = {}) {
|
|
66545
|
+
const readFileFn = deps.readFileFn ?? ((p) => readFileSync65(p, "utf-8"));
|
|
66546
|
+
let agentsDir = deps.agentsDir;
|
|
66547
|
+
if (agentsDir === undefined) {
|
|
66548
|
+
try {
|
|
66549
|
+
agentsDir = resolveAgentsDir(config);
|
|
66550
|
+
} catch {
|
|
66551
|
+
agentsDir = undefined;
|
|
66552
|
+
}
|
|
66553
|
+
}
|
|
66554
|
+
const results = [];
|
|
66555
|
+
for (const [agentName, agentConfig] of Object.entries(config.agents)) {
|
|
66556
|
+
const resolved = resolveAgentConfig(config.defaults, config.profiles, agentConfig);
|
|
66557
|
+
if (resolved.memory?.inject_directives !== false)
|
|
66558
|
+
continue;
|
|
66559
|
+
const name = `agent ${agentName} directive flip (inject_directives=false)`;
|
|
66560
|
+
if (resolved.memory?.rules_block !== true) {
|
|
66561
|
+
results.push({
|
|
66562
|
+
name,
|
|
66563
|
+
status: "fail",
|
|
66564
|
+
detail: "inject_directives is false but memory.rules_block is not true \u2014 the " + "rules block that must carry the migrated directives is not even " + "enabled. recall.py fails safe (keeps injecting), but fix the config: " + "enable rules_block and migrate, or re-enable inject_directives.",
|
|
66565
|
+
fix: `Set memory.rules_block: true for ${agentName} (and migrate its directives) before flipping.`
|
|
66566
|
+
});
|
|
66567
|
+
continue;
|
|
66568
|
+
}
|
|
66569
|
+
const claudeMd = readClaudeMdSafe(agentsDir, agentName, readFileFn);
|
|
66570
|
+
const parsed = claudeMd ? parseRulesBlock(claudeMd) : null;
|
|
66571
|
+
const ruleCount = parsed?.rules.length ?? 0;
|
|
66572
|
+
if (ruleCount === 0) {
|
|
66573
|
+
results.push({
|
|
66574
|
+
name,
|
|
66575
|
+
status: "fail",
|
|
66576
|
+
detail: claudeMd === null ? "inject_directives is false but the agent's CLAUDE.md could not be " + "read to confirm a rules block carries the migrated directives." : "inject_directives is false but the CLAUDE.md rules block is empty " + "(no `- **R-\u2026` rule lines). Every directive would be suppressed with " + "nothing carrying its guarantee. recall.py fails safe at runtime, but " + "fix the config: migrate the directives into the rules block, or " + "re-enable inject_directives.",
|
|
66577
|
+
fix: `Migrate ${agentName}'s directives into the rules block (memory rule add) or re-enable inject_directives.`
|
|
66578
|
+
});
|
|
66579
|
+
} else {
|
|
66580
|
+
results.push({
|
|
66581
|
+
name,
|
|
66582
|
+
status: "ok",
|
|
66583
|
+
detail: `flipped, and the rules block carries ${ruleCount} rule(s) \u2014 directive injection safely suppressed.`
|
|
66584
|
+
});
|
|
66585
|
+
}
|
|
66586
|
+
}
|
|
66587
|
+
return results;
|
|
66588
|
+
}
|
|
66510
66589
|
async function runRulesBlockChecks(config, engineUrl, deps = {}) {
|
|
66511
66590
|
const readFileFn = deps.readFileFn ?? ((p) => readFileSync65(p, "utf-8"));
|
|
66512
66591
|
let agentsDir = deps.agentsDir;
|
|
@@ -68797,6 +68876,7 @@ async function checkHindsight(config) {
|
|
|
68797
68876
|
results.push(...await checkBankIngestHealth(config, url, { includeConsolidationBacklog: true }));
|
|
68798
68877
|
results.push(...await checkBankObservationsMissions(config, url));
|
|
68799
68878
|
results.push(...await runRulesBlockChecks(config, url));
|
|
68879
|
+
results.push(...runDirectiveFlipChecks(config));
|
|
68800
68880
|
results.push(...await checkBankConfigApiReachable(config, url));
|
|
68801
68881
|
results.push(await checkObservationScopeSaturation(config, url));
|
|
68802
68882
|
const memoryAgentsDir = resolveAgentsDir(config);
|
|
@@ -126158,6 +126238,19 @@ function clampKnowledgeSearchLimit(limit) {
|
|
|
126158
126238
|
}
|
|
126159
126239
|
return Math.min(KNOWLEDGE_SEARCH_LIMIT_MAX, Math.max(KNOWLEDGE_SEARCH_LIMIT_MIN, Math.trunc(limit)));
|
|
126160
126240
|
}
|
|
126241
|
+
|
|
126242
|
+
class KnowledgeBankNotGrantedError extends Error {
|
|
126243
|
+
requested;
|
|
126244
|
+
ownBankId;
|
|
126245
|
+
grantedBanks;
|
|
126246
|
+
constructor(requested, ownBankId, grantedBanks) {
|
|
126247
|
+
super(grantedBanks.length > 0 ? `bank_id '${requested}' is not a bank you can read. You may read your ` + `own bank ('${ownBankId}') or an operator-granted bank ` + `(${grantedBanks.map((b) => `'${b}'`).join(", ")}); '${requested}' ` + `is none of these. Omit bank_id to read your own memory bank.` : `bank_id '${requested}' is not your own bank ('${ownBankId}'), and ` + `this agent has not been granted any other bank to read. Omit ` + `bank_id to read your own memory bank.`);
|
|
126248
|
+
this.requested = requested;
|
|
126249
|
+
this.ownBankId = ownBankId;
|
|
126250
|
+
this.grantedBanks = grantedBanks;
|
|
126251
|
+
this.name = "KnowledgeBankNotGrantedError";
|
|
126252
|
+
}
|
|
126253
|
+
}
|
|
126161
126254
|
var KNOWLEDGE_ADMIN_TIMEOUT_MS = 15000;
|
|
126162
126255
|
var KNOWLEDGE_PAGE_ID_PATTERN = /^[A-Za-z0-9_-]+$/;
|
|
126163
126256
|
|
|
@@ -126169,11 +126262,22 @@ class KnowledgeAdmin {
|
|
|
126169
126262
|
get fetchImpl() {
|
|
126170
126263
|
return this.opts.fetchImpl ?? fetch;
|
|
126171
126264
|
}
|
|
126172
|
-
|
|
126265
|
+
resolveBank(requested) {
|
|
126266
|
+
const own = this.opts.bankId;
|
|
126267
|
+
if (requested === undefined || requested === "" || requested === own) {
|
|
126268
|
+
return own;
|
|
126269
|
+
}
|
|
126270
|
+
if ((this.opts.extraBanks ?? []).includes(requested))
|
|
126271
|
+
return requested;
|
|
126272
|
+
throw new KnowledgeBankNotGrantedError(requested, own, [
|
|
126273
|
+
...this.opts.extraBanks ?? []
|
|
126274
|
+
]);
|
|
126275
|
+
}
|
|
126276
|
+
knowledgeBasePath(bankId) {
|
|
126173
126277
|
const base = this.opts.apiBaseUrl.replace(/\/+$/, "");
|
|
126174
|
-
return `${base}/v1/default/banks/${encodeURIComponent(
|
|
126278
|
+
return `${base}/v1/default/banks/${encodeURIComponent(bankId)}/knowledge-base`;
|
|
126175
126279
|
}
|
|
126176
|
-
async get(url) {
|
|
126280
|
+
async get(url, bankId) {
|
|
126177
126281
|
const ctl = new AbortController;
|
|
126178
126282
|
const timer = setTimeout(() => ctl.abort(), this.opts.timeoutMs ?? KNOWLEDGE_ADMIN_TIMEOUT_MS);
|
|
126179
126283
|
try {
|
|
@@ -126181,7 +126285,7 @@ class KnowledgeAdmin {
|
|
|
126181
126285
|
method: "GET",
|
|
126182
126286
|
headers: {
|
|
126183
126287
|
accept: "application/json",
|
|
126184
|
-
...
|
|
126288
|
+
...bankId ? { "X-Bank-Id": bankId } : {}
|
|
126185
126289
|
},
|
|
126186
126290
|
signal: ctl.signal
|
|
126187
126291
|
});
|
|
@@ -126190,13 +126294,14 @@ class KnowledgeAdmin {
|
|
|
126190
126294
|
}
|
|
126191
126295
|
}
|
|
126192
126296
|
async search(args) {
|
|
126297
|
+
const bank = this.resolveBank(args.bankId);
|
|
126193
126298
|
const params = new URLSearchParams({
|
|
126194
126299
|
q: args.query,
|
|
126195
126300
|
limit: String(clampKnowledgeSearchLimit(args.limit))
|
|
126196
126301
|
});
|
|
126197
|
-
const res = await this.get(`${this.knowledgeBasePath()}/search?${params}
|
|
126302
|
+
const res = await this.get(`${this.knowledgeBasePath(bank)}/search?${params}`, bank);
|
|
126198
126303
|
if (!res.ok) {
|
|
126199
|
-
throw new Error(`searching knowledge pages in bank '${
|
|
126304
|
+
throw new Error(`searching knowledge pages in bank '${bank}' failed: HTTP ${res.status}`);
|
|
126200
126305
|
}
|
|
126201
126306
|
const parsed = await res.json();
|
|
126202
126307
|
const results = Array.isArray(parsed.results) ? parsed.results : [];
|
|
@@ -126209,23 +126314,25 @@ class KnowledgeAdmin {
|
|
|
126209
126314
|
if (!KNOWLEDGE_PAGE_ID_PATTERN.test(args.page_id)) {
|
|
126210
126315
|
throw new Error(`'${args.page_id}' is not a knowledge page id. Ids look like ` + `'kp-<hex>' (letters, digits, '-' and '_' only) and come from ` + `search_knowledge_pages or get_knowledge_tree.`);
|
|
126211
126316
|
}
|
|
126212
|
-
const
|
|
126317
|
+
const bank = this.resolveBank(args.bankId);
|
|
126318
|
+
const res = await this.get(`${this.knowledgeBasePath(bank)}/pages/${encodeURIComponent(args.page_id)}`, bank);
|
|
126213
126319
|
if (res.status === 404) {
|
|
126214
|
-
throw new Error(`no knowledge page '${args.page_id}' in bank '${
|
|
126320
|
+
throw new Error(`no knowledge page '${args.page_id}' in bank '${bank}'. ` + `Page ids come from search_knowledge_pages or get_knowledge_tree.`);
|
|
126215
126321
|
}
|
|
126216
126322
|
if (!res.ok) {
|
|
126217
|
-
throw new Error(`reading knowledge page '${args.page_id}' in bank ` + `'${
|
|
126323
|
+
throw new Error(`reading knowledge page '${args.page_id}' in bank ` + `'${bank}' failed: HTTP ${res.status}`);
|
|
126218
126324
|
}
|
|
126219
126325
|
const page = await res.json();
|
|
126220
126326
|
if (typeof page.markdown !== "string") {
|
|
126221
|
-
throw new Error(`knowledge page '${args.page_id}' in bank '${
|
|
126327
|
+
throw new Error(`knowledge page '${args.page_id}' in bank '${bank}' came ` + `back with no markdown body \u2014 the response had no string 'markdown' ` + `field, so there is nothing to read.`);
|
|
126222
126328
|
}
|
|
126223
126329
|
return page;
|
|
126224
126330
|
}
|
|
126225
|
-
async tree() {
|
|
126226
|
-
const
|
|
126331
|
+
async tree(args = {}) {
|
|
126332
|
+
const bank = this.resolveBank(args.bankId);
|
|
126333
|
+
const res = await this.get(`${this.knowledgeBasePath(bank)}/tree`, bank);
|
|
126227
126334
|
if (!res.ok) {
|
|
126228
|
-
throw new Error(`listing the knowledge tree in bank '${
|
|
126335
|
+
throw new Error(`listing the knowledge tree in bank '${bank}' failed: HTTP ${res.status}`);
|
|
126229
126336
|
}
|
|
126230
126337
|
const parsed = await res.json();
|
|
126231
126338
|
return { roots: Array.isArray(parsed.roots) ? parsed.roots : [] };
|
|
@@ -126278,6 +126385,10 @@ var FALLBACK_TOOL_TABLE = {
|
|
|
126278
126385
|
update_memory: [["memory_id"], ["bank_id", "context", "entities", "fact_type", "memory_id", "occurred_end", "occurred_start", "text"]],
|
|
126279
126386
|
update_mental_model: [["mental_model_id"], ["bank_id", "max_tokens", "mental_model_id", "name", "source_query", "tags", "trigger_refresh_after_consolidation"]]
|
|
126280
126387
|
};
|
|
126388
|
+
var KNOWLEDGE_BANK_SELECTOR_PROP = {
|
|
126389
|
+
type: "string",
|
|
126390
|
+
description: "Optional. Which memory bank to read. Omit to read YOUR OWN bank (the " + "default). You may name another bank ONLY if your operator has granted " + "it to you (e.g. a shared repo knowledge bank); naming any other bank is " + "rejected."
|
|
126391
|
+
};
|
|
126281
126392
|
var SYNTHESIZED_TOOL_TABLE = {
|
|
126282
126393
|
deactivate_directive: {
|
|
126283
126394
|
description: "Retire one of YOUR OWN standing directives by setting is_active=false, " + "so it stops being injected as a hard rule. Reversible with " + "reactivate_directive; the directive and its text are preserved. Pass " + "superseded_by when another directive replaces this one \u2014 both get " + "provenance tags recording the supersession. Operates only on your own " + "memory bank and changes nothing but the active flag and those tags.",
|
|
@@ -126304,7 +126415,7 @@ var SYNTHESIZED_TOOL_TABLE = {
|
|
|
126304
126415
|
}
|
|
126305
126416
|
},
|
|
126306
126417
|
search_knowledge_pages: {
|
|
126307
|
-
description: "Search
|
|
126418
|
+
description: "Search knowledge pages \u2014 the curated, continuously-refreshed " + "summaries of what a bank knows \u2014 with hybrid full-text + semantic " + "search. Reach for this before re-deriving a standing answer from raw " + "recall: a page is already synthesized, where recall returns fragments. " + "Returns ranked hits with a relevance snippet and a page id; read the " + "whole page with get_knowledge_page. Read-only. Defaults to your own " + "bank; pass bank_id to search an operator-granted bank. An empty " + "knowledge base returns no results, not an error.",
|
|
126308
126419
|
required: ["query"],
|
|
126309
126420
|
props: {
|
|
126310
126421
|
query: {
|
|
@@ -126316,23 +126427,27 @@ var SYNTHESIZED_TOOL_TABLE = {
|
|
|
126316
126427
|
minimum: KNOWLEDGE_SEARCH_LIMIT_MIN,
|
|
126317
126428
|
maximum: KNOWLEDGE_SEARCH_LIMIT_MAX,
|
|
126318
126429
|
description: `Maximum hits to return (${KNOWLEDGE_SEARCH_LIMIT_MIN}-` + `${KNOWLEDGE_SEARCH_LIMIT_MAX}, default ${KNOWLEDGE_SEARCH_LIMIT_DEFAULT}).`
|
|
126319
|
-
}
|
|
126430
|
+
},
|
|
126431
|
+
bank_id: KNOWLEDGE_BANK_SELECTOR_PROP
|
|
126320
126432
|
}
|
|
126321
126433
|
},
|
|
126322
126434
|
get_knowledge_page: {
|
|
126323
|
-
description: "Read one
|
|
126435
|
+
description: "Read one knowledge page in full, by the page id returned by " + "search_knowledge_pages or get_knowledge_tree. Returns the complete " + "markdown document (YAML frontmatter + synthesized body). Prefer this " + "over rebuilding the same understanding from scratch. Read-only: it " + "returns the page as it currently stands and never triggers a refresh. " + "Defaults to your own bank; pass bank_id (matching the tree/search call " + "the id came from) to read from an operator-granted bank.",
|
|
126324
126436
|
required: ["page_id"],
|
|
126325
126437
|
props: {
|
|
126326
126438
|
page_id: {
|
|
126327
126439
|
type: "string",
|
|
126328
126440
|
description: "Id of the page to read, from search_knowledge_pages or " + "get_knowledge_tree."
|
|
126329
|
-
}
|
|
126441
|
+
},
|
|
126442
|
+
bank_id: KNOWLEDGE_BANK_SELECTOR_PROP
|
|
126330
126443
|
}
|
|
126331
126444
|
},
|
|
126332
126445
|
get_knowledge_tree: {
|
|
126333
|
-
description: "List
|
|
126446
|
+
description: "List a knowledge base as a folder/page tree \u2014 every page's id, " + "name, source query, tags and staleness flag. Call this to see what " + "a bank already knows before reading anything else; is_stale=true " + "means the page MAY be behind newer memories, not that it is wrong. " + "Read-only. Defaults to your own bank; pass bank_id to list an " + "operator-granted bank.",
|
|
126334
126447
|
required: [],
|
|
126335
|
-
props: {
|
|
126448
|
+
props: {
|
|
126449
|
+
bank_id: KNOWLEDGE_BANK_SELECTOR_PROP
|
|
126450
|
+
}
|
|
126336
126451
|
}
|
|
126337
126452
|
};
|
|
126338
126453
|
var SYNTHESIZED_TOOL_NAMES = Object.keys(SYNTHESIZED_TOOL_TABLE);
|
|
@@ -126936,6 +127051,7 @@ class HindsightShim {
|
|
|
126936
127051
|
this.knowledgeAdminCache = new KnowledgeAdmin({
|
|
126937
127052
|
apiBaseUrl: this.opts.apiBaseUrl ?? this.opts.url.replace(/\/mcp\/?$/, ""),
|
|
126938
127053
|
bankId: this.opts.bankId,
|
|
127054
|
+
...this.opts.extraBanks ? { extraBanks: this.opts.extraBanks } : {},
|
|
126939
127055
|
...this.opts.fetchImpl ? { fetchImpl: this.opts.fetchImpl } : {}
|
|
126940
127056
|
});
|
|
126941
127057
|
}
|
|
@@ -126979,6 +127095,15 @@ class HindsightShim {
|
|
|
126979
127095
|
if (!this.opts.bankId) {
|
|
126980
127096
|
return fail4(`${name} is unavailable: this agent has no HINDSIGHT_BANK_ID, so ` + "there is no bank to pin it to.");
|
|
126981
127097
|
}
|
|
127098
|
+
if (typeof clean3.bank_id === "string") {
|
|
127099
|
+
const requested = clean3.bank_id;
|
|
127100
|
+
const granted = requested === this.opts.bankId || (this.opts.extraBanks ?? []).includes(requested);
|
|
127101
|
+
if (!granted) {
|
|
127102
|
+
return fail4(new KnowledgeBankNotGrantedError(requested, this.opts.bankId, [
|
|
127103
|
+
...this.opts.extraBanks ?? []
|
|
127104
|
+
]).message);
|
|
127105
|
+
}
|
|
127106
|
+
}
|
|
126982
127107
|
const preflight = await this.contractPin.preflight(name);
|
|
126983
127108
|
if (!preflight.ok)
|
|
126984
127109
|
return fail4(preflight.text);
|
|
@@ -126986,6 +127111,9 @@ class HindsightShim {
|
|
|
126986
127111
|
const text = await this.runSynthesized(name, clean3);
|
|
126987
127112
|
return { content: [{ type: "text", text }], isError: false };
|
|
126988
127113
|
} catch (err) {
|
|
127114
|
+
if (err instanceof KnowledgeBankNotGrantedError) {
|
|
127115
|
+
return fail4(err.message);
|
|
127116
|
+
}
|
|
126989
127117
|
if (err instanceof DirectivePairInconsistentError) {
|
|
126990
127118
|
this.log(`[hindsight-shim] ${err.message}`);
|
|
126991
127119
|
}
|
|
@@ -127004,23 +127132,25 @@ class HindsightShim {
|
|
|
127004
127132
|
case "search_knowledge_pages": {
|
|
127005
127133
|
const res = await this.knowledgeAdmin.search({
|
|
127006
127134
|
query: args.query,
|
|
127007
|
-
...typeof args.limit === "number" ? { limit: args.limit } : {}
|
|
127135
|
+
...typeof args.limit === "number" ? { limit: args.limit } : {},
|
|
127136
|
+
...typeof args.bank_id === "string" ? { bankId: args.bank_id } : {}
|
|
127008
127137
|
});
|
|
127009
127138
|
if (res.results.length === 0) {
|
|
127010
|
-
return "No knowledge pages matched that query in your memory bank. " + "The bank may have no pages yet \u2014 get_knowledge_tree lists what " + "exists.";
|
|
127139
|
+
return "No knowledge pages matched that query in " + (typeof args.bank_id === "string" ? `bank '${args.bank_id}'. ` : "your memory bank. ") + "The bank may have no pages yet \u2014 get_knowledge_tree lists what " + "exists.";
|
|
127011
127140
|
}
|
|
127012
127141
|
return capKnowledgeResponse(JSON.stringify({ total: res.total, results: res.results }, null, 2));
|
|
127013
127142
|
}
|
|
127014
127143
|
case "get_knowledge_page": {
|
|
127015
127144
|
const page = await this.knowledgeAdmin.getPage({
|
|
127016
|
-
page_id: args.page_id
|
|
127145
|
+
page_id: args.page_id,
|
|
127146
|
+
...typeof args.bank_id === "string" ? { bankId: args.bank_id } : {}
|
|
127017
127147
|
});
|
|
127018
127148
|
return capKnowledgeResponse(page.markdown);
|
|
127019
127149
|
}
|
|
127020
127150
|
case "get_knowledge_tree": {
|
|
127021
|
-
const tree = await this.knowledgeAdmin.tree();
|
|
127151
|
+
const tree = await this.knowledgeAdmin.tree(typeof args.bank_id === "string" ? { bankId: args.bank_id } : {});
|
|
127022
127152
|
if (tree.roots.length === 0) {
|
|
127023
|
-
return "Your knowledge base has no pages yet. Pages are synthesized
|
|
127153
|
+
return typeof args.bank_id === "string" ? `Bank '${args.bank_id}' has no knowledge pages yet.` : "Your knowledge base has no pages yet. Pages are synthesized " + "from mental models \u2014 propose one to start it.";
|
|
127024
127154
|
}
|
|
127025
127155
|
return renderKnowledgeTree(tree.roots);
|
|
127026
127156
|
}
|
|
@@ -127134,9 +127264,11 @@ class HindsightShim {
|
|
|
127134
127264
|
}
|
|
127135
127265
|
function resolveShimOptionsFromEnv(env2) {
|
|
127136
127266
|
const home2 = env2.HOME && env2.HOME !== "/" ? env2.HOME : tmpdir6();
|
|
127267
|
+
const extraBanks = (env2.HINDSIGHT_KNOWLEDGE_EXTRA_BANKS ?? "").split(",").map((b) => b.trim()).filter((b) => b.length > 0 && b !== (env2.HINDSIGHT_BANK_ID || ""));
|
|
127137
127268
|
return {
|
|
127138
127269
|
url: env2.HINDSIGHT_MCP_URL || HINDSIGHT_DEFAULT_MCP_URL,
|
|
127139
127270
|
bankId: env2.HINDSIGHT_BANK_ID || "",
|
|
127271
|
+
...extraBanks.length > 0 ? { extraBanks } : {},
|
|
127140
127272
|
cacheDir: env2.HINDSIGHT_SHIM_CACHE_DIR || join117(home2, ".hindsight-shim")
|
|
127141
127273
|
};
|
|
127142
127274
|
}
|