switchroom 0.21.14 → 0.21.16

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 (36) hide show
  1. package/bin/rules-sentinel-hook.sh +101 -0
  2. package/dist/agent-scheduler/index.js +7 -2
  3. package/dist/auth-broker/index.js +7 -2
  4. package/dist/cli/notion-write-pretool.mjs +7 -2
  5. package/dist/cli/switchroom.js +2601 -1009
  6. package/dist/host-control/main.js +8 -3
  7. package/dist/vault/approvals/kernel-server.js +7 -2
  8. package/dist/vault/broker/server.js +7 -2
  9. package/package.json +1 -1
  10. package/profiles/_base/start.sh.hbs +9 -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 +11 -6
  14. package/vendor/hindsight-memory/hooks/hooks.json +10 -0
  15. package/vendor/hindsight-memory/scripts/lib/client.py +14 -0
  16. package/vendor/hindsight-memory/scripts/lib/config.py +22 -0
  17. package/vendor/hindsight-memory/scripts/lib/directives.py +45 -7
  18. package/vendor/hindsight-memory/scripts/lib/recall_buffer.py +236 -0
  19. package/vendor/hindsight-memory/scripts/lib/watermark.py +27 -0
  20. package/vendor/hindsight-memory/scripts/prefetch.py +156 -0
  21. package/vendor/hindsight-memory/scripts/recall.py +520 -5
  22. package/vendor/hindsight-memory/scripts/reconcile_tail.py +4 -12
  23. package/vendor/hindsight-memory/scripts/retain.py +167 -28
  24. package/vendor/hindsight-memory/scripts/tests/test_config_retain_tool_calls_env.py +98 -0
  25. package/vendor/hindsight-memory/scripts/tests/test_directives.py +52 -0
  26. package/vendor/hindsight-memory/scripts/tests/test_incremental_sweep.py +293 -0
  27. package/vendor/hindsight-memory/scripts/tests/test_prefetch_pipeline.py +247 -0
  28. package/vendor/hindsight-memory/scripts/tests/test_profile_capture_nudge.py +335 -0
  29. package/vendor/hindsight-memory/scripts/tests/test_recall_buffer.py +143 -0
  30. package/vendor/hindsight-memory/scripts/tests/test_recall_buffer_join.py +193 -0
  31. package/vendor/hindsight-memory/scripts/tests/test_recall_cap_truncation.py +133 -0
  32. package/vendor/hindsight-memory/scripts/tests/test_recall_junk_gate.py +168 -0
  33. package/vendor/hindsight-memory/scripts/tests/test_recall_no_score_floor.py +124 -0
  34. package/vendor/hindsight-memory/scripts/tests/test_recall_query_timestamp.py +376 -0
  35. package/vendor/hindsight-memory/scripts/tests/test_retain_delta.py +304 -0
  36. 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,12 +11118,14 @@ 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)."),
11121
11122
  disposition: exports_external.object({
11122
11123
  skepticism: exports_external.number().int().min(1).max(5).optional().describe("How much the bank doubts unverified claims (1-5; engine default 3)."),
11123
11124
  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)."),
11124
11125
  empathy: exports_external.number().int().min(1).max(5).optional().describe("How much the bank weights emotional/relational context (1-5; engine default 3).")
11125
11126
  }).optional().describe("Personality traits (1-5 each) steering how this bank frames recall, " + "reflect, and observation synthesis — a coach leans empathy-high, a " + "lawyer/analyst leans skepticism/literalism-high. Maps to the engine's " + "flat `disposition_skepticism`/`_literalism`/`_empathy` fields. " + "Cascade: per-key merge (an agent overrides individual traits and " + "inherits the rest, matching `recall`)."),
11126
11127
  directive_capture_nudge: exports_external.boolean().optional().describe("Deterministic directive-capture nudge (issue #2848 Stage B). When " + "on (switchroom default true — Stage A measured a ~55% miss rate on " + "durable corrections), the auto-recall hook regex-detects correction " + '/ standing-rule-shaped inbound ("always/never …", "from now on …", ' + `"stop doing …", a stated preference, "that's wrong, it's …") and ` + "appends a terse advisory to the turn's context telling the model to " + "persist the rule with mcp__hindsight__create_directive if it IS " + "durable. Detection is pure regex — the model does the judgment " + "in-session and calls create_directive itself (no model callsite, no " + "silent hook-side write). Set false to disable per-agent. " + "Cascade: override (per-agent wins over default)."),
11128
+ profile_capture_nudge: exports_external.boolean().optional().describe("Deterministic operator-profile capture nudge (RFC phase4 P3, serves " + 'the "save memories about him" want). When on (switchroom default ' + "true), the auto-recall hook regex-detects a first-person durable " + 'self-statement by the operator ("I prefer …", "my … is …", "I ' + 'always …", "remind me that I …") and appends a terse advisory to ' + "the turn's context telling the model to persist it with an explicit " + "mcp__hindsight__retain tagged `profile:ken` into the agent's OWN bank " + "(not a shared/cross-agent person bank). Detection is pure regex — the " + "model does the judgment in-session and calls retain itself (no model " + "callsite, no silent hook-side write). Set false to disable per-agent. " + "Cascade: override (per-agent wins over default)."),
11127
11129
  anti_confabulation_directive: AntiConfabulationDirectiveSchema,
11128
11130
  observation_scopes: ObservationScopesSchema,
11129
11131
  observation_scope_strategy: ObservationScopeStrategySchema,
@@ -11172,7 +11174,8 @@ var AgentMemorySchema = exports_external.object({
11172
11174
  }).optional().describe("Auto-recall tuning knobs"),
11173
11175
  retain: exports_external.object({
11174
11176
  every_n_turns: exports_external.number().int().min(1).optional().describe("How often the Stop hook fires auto-retention, in turns. The " + "vendor plugin default is 10 (a short session can end before " + "retention ever fires); switchroom's scaffold default is 3. " + "Lower = more frequent, smaller retains + tighter crash " + "durability (at 1, every turn is retained before a restart can " + "lose it); higher = fewer, larger retains + less LLM churn. " + "Raised from the historical 1 to 3 because the local reasoning " + "consolidation model (Ollama gpt-oss-20b) ran away on the large " + "overlapping every-turn payloads. Set to 1 for the old " + "every-turn crash-durability guarantee. Min 1. Cascade: " + "per-field merge (agent wins over default)."),
11175
- overlap_turns: exports_external.number().int().min(0).optional().describe("Extra recent turns included in each chunked retain window on " + "top of `every_n_turns`, so window = overlap_turns + " + "every_n_turns recent turns. Vendor default is 2; switchroom's " + "scaffold default is 1 (smaller payloads for the local " + "reasoning consolidation model). Higher = more redundant " + "context re-sent per fire. Min 0. Cascade: per-field merge.")
11177
+ overlap_turns: exports_external.number().int().min(0).optional().describe("Extra recent turns included in each chunked retain window on " + "top of `every_n_turns`, so window = overlap_turns + " + "every_n_turns recent turns. Vendor default is 2; switchroom's " + "scaffold default is 1 (smaller payloads for the local " + "reasoning consolidation model). Higher = more redundant " + "context re-sent per fire. Min 0. Cascade: per-field merge."),
11178
+ tool_calls: exports_external.boolean().optional().describe("Whether auto-retain stores assistant tool_use blocks (with their " + "entire input dict) and tool_result content (up to 2000 chars) " + "alongside the human/assistant turns. Vendor + switchroom default " + "is true — the fleet behaves byte-identically until an operator " + "sets this false. Tool inputs/results are a large fraction of " + "retained volume (RFC memory-redesign P4) but also where evidence " + "lives (a commit SHA, a failing test's output, the diff that " + "fixed something), so the tradeoff is real and per-agent. false is " + "not a new mode: sidechains already force it off " + "(subagent_retain.py). Set false to drop tool exhaust from an " + "agent's retains. Cascade: per-field merge (agent wins over " + "default).")
11176
11179
  }).optional().describe("Auto-retain (Stop-hook consolidation) cadence knobs")
11177
11180
  }).optional();
11178
11181
  var HookEntrySchema = exports_external.object({
@@ -11560,6 +11563,7 @@ var profileFields = {
11560
11563
  isolation: exports_external.enum(["default", "strict"]).optional(),
11561
11564
  profile: exports_external.string().optional(),
11562
11565
  directive_capture_nudge: exports_external.boolean().optional(),
11566
+ profile_capture_nudge: exports_external.boolean().optional(),
11563
11567
  anti_confabulation_directive: AntiConfabulationDirectiveSchema,
11564
11568
  observation_scopes: ObservationScopesSchema,
11565
11569
  observation_scope_strategy: ObservationScopeStrategySchema,
@@ -11608,7 +11612,8 @@ var profileFields = {
11608
11612
  }).optional(),
11609
11613
  retain: exports_external.object({
11610
11614
  every_n_turns: exports_external.number().int().min(1).optional(),
11611
- overlap_turns: exports_external.number().int().min(0).optional()
11615
+ overlap_turns: exports_external.number().int().min(0).optional(),
11616
+ tool_calls: exports_external.boolean().optional()
11612
11617
  }).optional(),
11613
11618
  bank_mission: exports_external.string().optional(),
11614
11619
  reflect_mission: exports_external.string().optional(),
@@ -11143,12 +11143,14 @@ 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)."),
11146
11147
  disposition: exports_external.object({
11147
11148
  skepticism: exports_external.number().int().min(1).max(5).optional().describe("How much the bank doubts unverified claims (1-5; engine default 3)."),
11148
11149
  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)."),
11149
11150
  empathy: exports_external.number().int().min(1).max(5).optional().describe("How much the bank weights emotional/relational context (1-5; engine default 3).")
11150
11151
  }).optional().describe("Personality traits (1-5 each) steering how this bank frames recall, " + "reflect, and observation synthesis — a coach leans empathy-high, a " + "lawyer/analyst leans skepticism/literalism-high. Maps to the engine's " + "flat `disposition_skepticism`/`_literalism`/`_empathy` fields. " + "Cascade: per-key merge (an agent overrides individual traits and " + "inherits the rest, matching `recall`)."),
11151
11152
  directive_capture_nudge: exports_external.boolean().optional().describe("Deterministic directive-capture nudge (issue #2848 Stage B). When " + "on (switchroom default true — Stage A measured a ~55% miss rate on " + "durable corrections), the auto-recall hook regex-detects correction " + '/ standing-rule-shaped inbound ("always/never …", "from now on …", ' + `"stop doing …", a stated preference, "that's wrong, it's …") and ` + "appends a terse advisory to the turn's context telling the model to " + "persist the rule with mcp__hindsight__create_directive if it IS " + "durable. Detection is pure regex — the model does the judgment " + "in-session and calls create_directive itself (no model callsite, no " + "silent hook-side write). Set false to disable per-agent. " + "Cascade: override (per-agent wins over default)."),
11153
+ profile_capture_nudge: exports_external.boolean().optional().describe("Deterministic operator-profile capture nudge (RFC phase4 P3, serves " + 'the "save memories about him" want). When on (switchroom default ' + "true), the auto-recall hook regex-detects a first-person durable " + 'self-statement by the operator ("I prefer …", "my … is …", "I ' + 'always …", "remind me that I …") and appends a terse advisory to ' + "the turn's context telling the model to persist it with an explicit " + "mcp__hindsight__retain tagged `profile:ken` into the agent's OWN bank " + "(not a shared/cross-agent person bank). Detection is pure regex — the " + "model does the judgment in-session and calls retain itself (no model " + "callsite, no silent hook-side write). Set false to disable per-agent. " + "Cascade: override (per-agent wins over default)."),
11152
11154
  anti_confabulation_directive: AntiConfabulationDirectiveSchema,
11153
11155
  observation_scopes: ObservationScopesSchema,
11154
11156
  observation_scope_strategy: ObservationScopeStrategySchema,
@@ -11197,7 +11199,8 @@ var init_schema = __esm(() => {
11197
11199
  }).optional().describe("Auto-recall tuning knobs"),
11198
11200
  retain: exports_external.object({
11199
11201
  every_n_turns: exports_external.number().int().min(1).optional().describe("How often the Stop hook fires auto-retention, in turns. The " + "vendor plugin default is 10 (a short session can end before " + "retention ever fires); switchroom's scaffold default is 3. " + "Lower = more frequent, smaller retains + tighter crash " + "durability (at 1, every turn is retained before a restart can " + "lose it); higher = fewer, larger retains + less LLM churn. " + "Raised from the historical 1 to 3 because the local reasoning " + "consolidation model (Ollama gpt-oss-20b) ran away on the large " + "overlapping every-turn payloads. Set to 1 for the old " + "every-turn crash-durability guarantee. Min 1. Cascade: " + "per-field merge (agent wins over default)."),
11200
- overlap_turns: exports_external.number().int().min(0).optional().describe("Extra recent turns included in each chunked retain window on " + "top of `every_n_turns`, so window = overlap_turns + " + "every_n_turns recent turns. Vendor default is 2; switchroom's " + "scaffold default is 1 (smaller payloads for the local " + "reasoning consolidation model). Higher = more redundant " + "context re-sent per fire. Min 0. Cascade: per-field merge.")
11202
+ overlap_turns: exports_external.number().int().min(0).optional().describe("Extra recent turns included in each chunked retain window on " + "top of `every_n_turns`, so window = overlap_turns + " + "every_n_turns recent turns. Vendor default is 2; switchroom's " + "scaffold default is 1 (smaller payloads for the local " + "reasoning consolidation model). Higher = more redundant " + "context re-sent per fire. Min 0. Cascade: per-field merge."),
11203
+ tool_calls: exports_external.boolean().optional().describe("Whether auto-retain stores assistant tool_use blocks (with their " + "entire input dict) and tool_result content (up to 2000 chars) " + "alongside the human/assistant turns. Vendor + switchroom default " + "is true — the fleet behaves byte-identically until an operator " + "sets this false. Tool inputs/results are a large fraction of " + "retained volume (RFC memory-redesign P4) but also where evidence " + "lives (a commit SHA, a failing test's output, the diff that " + "fixed something), so the tradeoff is real and per-agent. false is " + "not a new mode: sidechains already force it off " + "(subagent_retain.py). Set false to drop tool exhaust from an " + "agent's retains. Cascade: per-field merge (agent wins over " + "default).")
11201
11204
  }).optional().describe("Auto-retain (Stop-hook consolidation) cadence knobs")
11202
11205
  }).optional();
11203
11206
  HookEntrySchema = exports_external.object({
@@ -11585,6 +11588,7 @@ var init_schema = __esm(() => {
11585
11588
  isolation: exports_external.enum(["default", "strict"]).optional(),
11586
11589
  profile: exports_external.string().optional(),
11587
11590
  directive_capture_nudge: exports_external.boolean().optional(),
11591
+ profile_capture_nudge: exports_external.boolean().optional(),
11588
11592
  anti_confabulation_directive: AntiConfabulationDirectiveSchema,
11589
11593
  observation_scopes: ObservationScopesSchema,
11590
11594
  observation_scope_strategy: ObservationScopeStrategySchema,
@@ -11633,7 +11637,8 @@ var init_schema = __esm(() => {
11633
11637
  }).optional(),
11634
11638
  retain: exports_external.object({
11635
11639
  every_n_turns: exports_external.number().int().min(1).optional(),
11636
- overlap_turns: exports_external.number().int().min(0).optional()
11640
+ overlap_turns: exports_external.number().int().min(0).optional(),
11641
+ tool_calls: exports_external.boolean().optional()
11637
11642
  }).optional(),
11638
11643
  bank_mission: exports_external.string().optional(),
11639
11644
  reflect_mission: exports_external.string().optional(),
@@ -11879,12 +11879,14 @@ 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)."),
11882
11883
  disposition: exports_external.object({
11883
11884
  skepticism: exports_external.number().int().min(1).max(5).optional().describe("How much the bank doubts unverified claims (1-5; engine default 3)."),
11884
11885
  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)."),
11885
11886
  empathy: exports_external.number().int().min(1).max(5).optional().describe("How much the bank weights emotional/relational context (1-5; engine default 3).")
11886
11887
  }).optional().describe("Personality traits (1-5 each) steering how this bank frames recall, " + "reflect, and observation synthesis \u2014 a coach leans empathy-high, a " + "lawyer/analyst leans skepticism/literalism-high. Maps to the engine's " + "flat `disposition_skepticism`/`_literalism`/`_empathy` fields. " + "Cascade: per-key merge (an agent overrides individual traits and " + "inherits the rest, matching `recall`)."),
11887
11888
  directive_capture_nudge: exports_external.boolean().optional().describe("Deterministic directive-capture nudge (issue #2848 Stage B). When " + "on (switchroom default true \u2014 Stage A measured a ~55% miss rate on " + "durable corrections), the auto-recall hook regex-detects correction " + '/ standing-rule-shaped inbound ("always/never \u2026", "from now on \u2026", ' + `"stop doing \u2026", a stated preference, "that's wrong, it's \u2026") and ` + "appends a terse advisory to the turn's context telling the model to " + "persist the rule with mcp__hindsight__create_directive if it IS " + "durable. Detection is pure regex \u2014 the model does the judgment " + "in-session and calls create_directive itself (no model callsite, no " + "silent hook-side write). Set false to disable per-agent. " + "Cascade: override (per-agent wins over default)."),
11889
+ profile_capture_nudge: exports_external.boolean().optional().describe("Deterministic operator-profile capture nudge (RFC phase4 P3, serves " + 'the "save memories about him" want). When on (switchroom default ' + "true), the auto-recall hook regex-detects a first-person durable " + 'self-statement by the operator ("I prefer \u2026", "my \u2026 is \u2026", "I ' + 'always \u2026", "remind me that I \u2026") and appends a terse advisory to ' + "the turn's context telling the model to persist it with an explicit " + "mcp__hindsight__retain tagged `profile:ken` into the agent's OWN bank " + "(not a shared/cross-agent person bank). Detection is pure regex \u2014 the " + "model does the judgment in-session and calls retain itself (no model " + "callsite, no silent hook-side write). Set false to disable per-agent. " + "Cascade: override (per-agent wins over default)."),
11888
11890
  anti_confabulation_directive: AntiConfabulationDirectiveSchema,
11889
11891
  observation_scopes: ObservationScopesSchema,
11890
11892
  observation_scope_strategy: ObservationScopeStrategySchema,
@@ -11933,7 +11935,8 @@ var AgentMemorySchema = exports_external.object({
11933
11935
  }).optional().describe("Auto-recall tuning knobs"),
11934
11936
  retain: exports_external.object({
11935
11937
  every_n_turns: exports_external.number().int().min(1).optional().describe("How often the Stop hook fires auto-retention, in turns. The " + "vendor plugin default is 10 (a short session can end before " + "retention ever fires); switchroom's scaffold default is 3. " + "Lower = more frequent, smaller retains + tighter crash " + "durability (at 1, every turn is retained before a restart can " + "lose it); higher = fewer, larger retains + less LLM churn. " + "Raised from the historical 1 to 3 because the local reasoning " + "consolidation model (Ollama gpt-oss-20b) ran away on the large " + "overlapping every-turn payloads. Set to 1 for the old " + "every-turn crash-durability guarantee. Min 1. Cascade: " + "per-field merge (agent wins over default)."),
11936
- overlap_turns: exports_external.number().int().min(0).optional().describe("Extra recent turns included in each chunked retain window on " + "top of `every_n_turns`, so window = overlap_turns + " + "every_n_turns recent turns. Vendor default is 2; switchroom's " + "scaffold default is 1 (smaller payloads for the local " + "reasoning consolidation model). Higher = more redundant " + "context re-sent per fire. Min 0. Cascade: per-field merge.")
11938
+ overlap_turns: exports_external.number().int().min(0).optional().describe("Extra recent turns included in each chunked retain window on " + "top of `every_n_turns`, so window = overlap_turns + " + "every_n_turns recent turns. Vendor default is 2; switchroom's " + "scaffold default is 1 (smaller payloads for the local " + "reasoning consolidation model). Higher = more redundant " + "context re-sent per fire. Min 0. Cascade: per-field merge."),
11939
+ tool_calls: exports_external.boolean().optional().describe("Whether auto-retain stores assistant tool_use blocks (with their " + "entire input dict) and tool_result content (up to 2000 chars) " + "alongside the human/assistant turns. Vendor + switchroom default " + "is true \u2014 the fleet behaves byte-identically until an operator " + "sets this false. Tool inputs/results are a large fraction of " + "retained volume (RFC memory-redesign P4) but also where evidence " + "lives (a commit SHA, a failing test's output, the diff that " + "fixed something), so the tradeoff is real and per-agent. false is " + "not a new mode: sidechains already force it off " + "(subagent_retain.py). Set false to drop tool exhaust from an " + "agent's retains. Cascade: per-field merge (agent wins over " + "default).")
11937
11940
  }).optional().describe("Auto-retain (Stop-hook consolidation) cadence knobs")
11938
11941
  }).optional();
11939
11942
  var HookEntrySchema = exports_external.object({
@@ -12321,6 +12324,7 @@ var profileFields = {
12321
12324
  isolation: exports_external.enum(["default", "strict"]).optional(),
12322
12325
  profile: exports_external.string().optional(),
12323
12326
  directive_capture_nudge: exports_external.boolean().optional(),
12327
+ profile_capture_nudge: exports_external.boolean().optional(),
12324
12328
  anti_confabulation_directive: AntiConfabulationDirectiveSchema,
12325
12329
  observation_scopes: ObservationScopesSchema,
12326
12330
  observation_scope_strategy: ObservationScopeStrategySchema,
@@ -12369,7 +12373,8 @@ var profileFields = {
12369
12373
  }).optional(),
12370
12374
  retain: exports_external.object({
12371
12375
  every_n_turns: exports_external.number().int().min(1).optional(),
12372
- overlap_turns: exports_external.number().int().min(0).optional()
12376
+ overlap_turns: exports_external.number().int().min(0).optional(),
12377
+ tool_calls: exports_external.boolean().optional()
12373
12378
  }).optional(),
12374
12379
  bank_mission: exports_external.string().optional(),
12375
12380
  reflect_mission: exports_external.string().optional(),