switchroom 0.21.15 → 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.
Files changed (37) hide show
  1. package/bin/rules-sentinel-hook.sh +101 -0
  2. package/dist/agent-scheduler/index.js +6 -0
  3. package/dist/auth-broker/index.js +6 -0
  4. package/dist/cli/notion-write-pretool.mjs +6 -0
  5. package/dist/cli/switchroom.js +2086 -871
  6. package/dist/host-control/main.js +7 -1
  7. package/dist/vault/approvals/kernel-server.js +6 -0
  8. package/dist/vault/broker/server.js +6 -0
  9. package/package.json +1 -1
  10. package/profiles/_base/start.sh.hbs +11 -0
  11. package/profiles/_shared/delegation-golden-rule.md.hbs +2 -0
  12. package/skills/mental-model-curator/SKILL.md +187 -56
  13. package/telegram-plugin/dist/gateway/gateway.js +10 -4
  14. package/vendor/hindsight-memory/hooks/hooks.json +19 -0
  15. package/vendor/hindsight-memory/scripts/directive_verify.py +43 -1
  16. package/vendor/hindsight-memory/scripts/lib/client.py +35 -0
  17. package/vendor/hindsight-memory/scripts/lib/config.py +47 -0
  18. package/vendor/hindsight-memory/scripts/lib/orientation.py +248 -0
  19. package/vendor/hindsight-memory/scripts/lib/recall_buffer.py +265 -0
  20. package/vendor/hindsight-memory/scripts/orientation.py +195 -0
  21. package/vendor/hindsight-memory/scripts/prefetch.py +166 -0
  22. package/vendor/hindsight-memory/scripts/recall.py +323 -5
  23. package/vendor/hindsight-memory/scripts/retain.py +132 -49
  24. package/vendor/hindsight-memory/scripts/setup_hooks.py +10 -1
  25. package/vendor/hindsight-memory/scripts/tests/fixtures/rules-block.golden.md +9 -0
  26. package/vendor/hindsight-memory/scripts/tests/test_orientation_hook.py +283 -0
  27. package/vendor/hindsight-memory/scripts/tests/test_orientation_logic.py +176 -0
  28. package/vendor/hindsight-memory/scripts/tests/test_prefetch_invalidation.py +329 -0
  29. package/vendor/hindsight-memory/scripts/tests/test_prefetch_pipeline.py +247 -0
  30. package/vendor/hindsight-memory/scripts/tests/test_recall_buffer.py +143 -0
  31. package/vendor/hindsight-memory/scripts/tests/test_recall_buffer_join.py +193 -0
  32. package/vendor/hindsight-memory/scripts/tests/test_recall_cap_truncation.py +133 -0
  33. package/vendor/hindsight-memory/scripts/tests/test_recall_directive_suppression.py +328 -0
  34. package/vendor/hindsight-memory/scripts/tests/test_recall_junk_gate.py +168 -0
  35. package/vendor/hindsight-memory/scripts/tests/test_recall_no_score_floor.py +124 -0
  36. package/vendor/hindsight-memory/scripts/tests/test_retain_delta.py +304 -0
  37. package/vendor/hindsight-memory/scripts/tests/test_retain_stop_hook_prefetch_gate.py +109 -0
@@ -0,0 +1,101 @@
1
+ #!/bin/bash
2
+ # SessionStart hook — Memory v2 M1 rules-block tamper sentinel
3
+ # (carve-M1.md §3, red-team-M1.md §B/§C, MINOR fixes #6/#7).
4
+ #
5
+ # Recomputes the rules block's sha256 sentinel + the mutation-log hash
6
+ # chain and compares against what's embedded in the agent's own
7
+ # CLAUDE.md. Purely advisory in M1 (dark build — no `memory.rules_block`
8
+ # agent exists live yet): this hook exists so the wiring, the read-only
9
+ # discipline, and the fast no-op path are all proven out and tested
10
+ # BEFORE M3 flips the flag on any real agent.
11
+ #
12
+ # STRICT DISCIPLINE (red-team §C, root-owned-overlay EACCES hazard):
13
+ # - READ-ONLY. This script must NEVER write to the agent's tree. A
14
+ # root-invoked write here would leave a root-owned file behind and
15
+ # EACCES the agent's own subsequent writes to it (the exact failure
16
+ # class that dropped clerk's crons for weeks — see this repo's
17
+ # CLAUDE.md "root-tier host access" section). `switchroom memory
18
+ # rule verify` (the CLI path) is the only writer-adjacent tool, and
19
+ # even IT never writes — verify is read-only too.
20
+ # - Prints to STDOUT, not stderr. SessionStart hook stdout is added to
21
+ # the model's context (unlike PreCompact); a stderr message would be
22
+ # silently dropped by Claude Code's hook contract, defeating the
23
+ # entire point of a tamper *sentinel* — see this file's own header
24
+ # precedent in bin/timezone-hook.sh for the additionalContext
25
+ # mechanism this hook reuses (NOT because "no additionalContext
26
+ # emitter exists" — one already does, in timezone-hook.sh and
27
+ # scaffold.ts; this hook simply needs the same JSON shape).
28
+ # - No-ops FAST and SILENTLY when the rules-block markers are absent
29
+ # (the M1/M3-flag-off dark case, and the pre-first-rule case even
30
+ # with the flag on) — never print a divergence notice about a block
31
+ # that was never created.
32
+ #
33
+ # Wiring note (MINOR fix #7): unlike working-state-reload-hook.sh
34
+ # (matcher: "compact" — fires ONLY on compaction), this hook is wired
35
+ # with NO matcher, so it fires on every SessionStart source (startup,
36
+ # resume, clear, fork, compact). That is a deliberate divergence: a
37
+ # tamper sentinel that only checked itself after compaction would miss
38
+ # tampering that happened between a fresh boot and the first compaction
39
+ # (the common case for a short-lived session). The check itself is a
40
+ # single local file read + a hash compare — cheap enough to run on every
41
+ # boot without a latency concern (contrast working-state-reload's
42
+ # network-hop briefing, which specifically justified restricting itself
43
+ # to the compact matcher).
44
+ #
45
+ # Exit code: 0 always. A hook that exits non-zero BLOCKS the turn in
46
+ # Claude Code; a tamper signal must degrade to a visible notice, never
47
+ # a hard stop (the same "failure modes are silent" doctrine as
48
+ # timezone-hook.sh).
49
+
50
+ set -u
51
+
52
+ CLAUDE_MD="${CLAUDE_PROJECT_DIR:-}/CLAUDE.md"
53
+
54
+ if [ -z "${CLAUDE_PROJECT_DIR:-}" ] || [ ! -f "$CLAUDE_MD" ]; then
55
+ exit 0
56
+ fi
57
+
58
+ # Fast no-op: markers absent (dark build / no rules yet). grep -q is a
59
+ # single pass over the file, no subshell fork per line.
60
+ if ! grep -q '<!-- switchroom:rules:begin -->' "$CLAUDE_MD" 2>/dev/null; then
61
+ exit 0
62
+ fi
63
+
64
+ # Delegate the actual sentinel recompute to the CLI verb this module
65
+ # already ships (`switchroom memory rule verify <agent>`), which reuses
66
+ # rules-store.ts's verifyIntegrity — one recompute implementation, not
67
+ # two (a bash reimplementation of the sha256-over-canonical-JSON
68
+ # encoding would be a second place for the two encodings to drift).
69
+ # `switchroom` is expected on PATH inside the agent container image;
70
+ # if it's missing, degrade silently rather than error the turn.
71
+ if ! command -v switchroom >/dev/null 2>&1; then
72
+ exit 0
73
+ fi
74
+
75
+ AGENT_NAME="$(basename "${CLAUDE_PROJECT_DIR:-}")"
76
+ VERIFY_OUT="$(switchroom memory rule verify "$AGENT_NAME" 2>&1)"
77
+ VERIFY_EXIT=$?
78
+
79
+ # Exit-code contract (memory-rules.ts `verify`):
80
+ # 0 = clean; 2 = GENUINE TAMPER; any other non-zero = an ENVIRONMENT
81
+ # failure (config unreadable, agent-dir mismatch, withConfigError exit 1,
82
+ # `switchroom` bug). Only code 2 warrants injecting a tamper notice into
83
+ # the model's context — an environment failure that hard-injected "tamper
84
+ # FAILED" would cry wolf on every broken boot and erode the signal. Degrade
85
+ # silently on everything that is not an unambiguous tamper.
86
+ if [ "$VERIFY_EXIT" -ne 2 ]; then
87
+ exit 0
88
+ fi
89
+
90
+ MSG="Rules-block tamper sentinel FAILED for \"$AGENT_NAME\": ${VERIFY_OUT}"
91
+
92
+ if command -v jq >/dev/null 2>&1; then
93
+ jq -cn --arg msg "$MSG" \
94
+ '{hookSpecificOutput:{hookEventName:"SessionStart",additionalContext:$msg}}'
95
+ else
96
+ ESCAPED=${MSG//\\/\\\\}
97
+ ESCAPED=${ESCAPED//\"/\\\"}
98
+ printf '{"hookSpecificOutput":{"hookEventName":"SessionStart","additionalContext":"%s"}}\n' "$ESCAPED"
99
+ fi
100
+
101
+ exit 0
@@ -11118,6 +11118,11 @@ var AgentMemorySchema = exports_external.object({
11118
11118
  }
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
+ 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."),
11121
11126
  disposition: exports_external.object({
11122
11127
  skepticism: exports_external.number().int().min(1).max(5).optional().describe("How much the bank doubts unverified claims (1-5; engine default 3)."),
11123
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)."),
@@ -11563,6 +11568,7 @@ var profileFields = {
11563
11568
  profile: exports_external.string().optional(),
11564
11569
  directive_capture_nudge: exports_external.boolean().optional(),
11565
11570
  profile_capture_nudge: exports_external.boolean().optional(),
11571
+ orientation_cadence_hours: exports_external.number().int().min(1).optional(),
11566
11572
  anti_confabulation_directive: AntiConfabulationDirectiveSchema,
11567
11573
  observation_scopes: ObservationScopesSchema,
11568
11574
  observation_scope_strategy: ObservationScopeStrategySchema,
@@ -11143,6 +11143,11 @@ var init_schema = __esm(() => {
11143
11143
  }
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
+ 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."),
11146
11151
  disposition: exports_external.object({
11147
11152
  skepticism: exports_external.number().int().min(1).max(5).optional().describe("How much the bank doubts unverified claims (1-5; engine default 3)."),
11148
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)."),
@@ -11588,6 +11593,7 @@ var init_schema = __esm(() => {
11588
11593
  profile: exports_external.string().optional(),
11589
11594
  directive_capture_nudge: exports_external.boolean().optional(),
11590
11595
  profile_capture_nudge: exports_external.boolean().optional(),
11596
+ orientation_cadence_hours: exports_external.number().int().min(1).optional(),
11591
11597
  anti_confabulation_directive: AntiConfabulationDirectiveSchema,
11592
11598
  observation_scopes: ObservationScopesSchema,
11593
11599
  observation_scope_strategy: ObservationScopeStrategySchema,
@@ -11879,6 +11879,11 @@ var AgentMemorySchema = exports_external.object({
11879
11879
  }
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
+ 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."),
11882
11887
  disposition: exports_external.object({
11883
11888
  skepticism: exports_external.number().int().min(1).max(5).optional().describe("How much the bank doubts unverified claims (1-5; engine default 3)."),
11884
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)."),
@@ -12324,6 +12329,7 @@ var profileFields = {
12324
12329
  profile: exports_external.string().optional(),
12325
12330
  directive_capture_nudge: exports_external.boolean().optional(),
12326
12331
  profile_capture_nudge: exports_external.boolean().optional(),
12332
+ orientation_cadence_hours: exports_external.number().int().min(1).optional(),
12327
12333
  anti_confabulation_directive: AntiConfabulationDirectiveSchema,
12328
12334
  observation_scopes: ObservationScopesSchema,
12329
12335
  observation_scope_strategy: ObservationScopeStrategySchema,