switchroom 0.17.6 → 0.17.10

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 (155) hide show
  1. package/dist/agent-scheduler/index.js +38 -4
  2. package/dist/auth-broker/index.js +302 -203
  3. package/dist/cli/notion-write-pretool.mjs +35 -2
  4. package/dist/cli/switchroom.js +1178 -576
  5. package/dist/host-control/main.js +148 -14
  6. package/dist/vault/approvals/kernel-server.js +140 -55
  7. package/dist/vault/broker/server.js +142 -57
  8. package/package.json +1 -1
  9. package/profiles/_base/start.sh.hbs +50 -6
  10. package/profiles/default/CLAUDE.md +116 -0
  11. package/skills/mental-model-curator/SKILL.md +162 -0
  12. package/telegram-plugin/bridge/bridge.ts +80 -1
  13. package/telegram-plugin/bridge/ipc-client.ts +19 -0
  14. package/telegram-plugin/bridge/permission-ledger.ts +61 -0
  15. package/telegram-plugin/consolidation-legibility.ts +279 -0
  16. package/telegram-plugin/dist/bridge/bridge.js +85 -1
  17. package/telegram-plugin/dist/gateway/gateway.js +2565 -610
  18. package/telegram-plugin/dist/server.js +86 -2
  19. package/telegram-plugin/feed-heartbeat-climb.ts +206 -0
  20. package/telegram-plugin/gateway/activity-card-store.ts +293 -0
  21. package/telegram-plugin/gateway/gateway.ts +1376 -82
  22. package/telegram-plugin/gateway/inbound-spool.ts +22 -0
  23. package/telegram-plugin/gateway/mental-model-propose-card.ts +69 -0
  24. package/telegram-plugin/gateway/mental-model-propose-diff.ts +171 -0
  25. package/telegram-plugin/gateway/mental-model-propose-inbound-builders.ts +147 -0
  26. package/telegram-plugin/gateway/mental-model-propose-resolve.ts +201 -0
  27. package/telegram-plugin/gateway/missed-approvals-card.ts +161 -0
  28. package/telegram-plugin/gateway/missed-approvals-store.ts +167 -0
  29. package/telegram-plugin/gateway/permission-rearm.ts +115 -0
  30. package/telegram-plugin/gateway/scoped-grant-store.ts +89 -0
  31. package/telegram-plugin/memory-legibility.ts +217 -0
  32. package/telegram-plugin/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -0
  33. package/telegram-plugin/scoped-approval.ts +59 -0
  34. package/telegram-plugin/silent-end.ts +78 -0
  35. package/telegram-plugin/subagent-watcher.ts +60 -6
  36. package/telegram-plugin/tests/activity-card-store.test.ts +436 -0
  37. package/telegram-plugin/tests/activity-card-wiring.test.ts +88 -0
  38. package/telegram-plugin/tests/consolidation-legibility.test.ts +224 -0
  39. package/telegram-plugin/tests/emission-authority-facade.test.ts +25 -10
  40. package/telegram-plugin/tests/feed-heartbeat-liveness-open.test.ts +33 -9
  41. package/telegram-plugin/tests/gateway-boot-marker-clear.test.ts +3 -3
  42. package/telegram-plugin/tests/inbound-spool.test.ts +105 -0
  43. package/telegram-plugin/tests/memory-legibility.test.ts +216 -0
  44. package/telegram-plugin/tests/mental-model-propose-callback-gate.test.ts +67 -0
  45. package/telegram-plugin/tests/mental-model-propose-card.test.ts +56 -0
  46. package/telegram-plugin/tests/mental-model-propose-diff.test.ts +201 -0
  47. package/telegram-plugin/tests/mental-model-propose-inbound-builders.test.ts +68 -0
  48. package/telegram-plugin/tests/mental-model-propose-resolve.test.ts +157 -0
  49. package/telegram-plugin/tests/missed-approvals-card.test.ts +145 -0
  50. package/telegram-plugin/tests/missed-approvals-store.test.ts +147 -0
  51. package/telegram-plugin/tests/missed-approvals-wiring.test.ts +89 -0
  52. package/telegram-plugin/tests/permission-ledger.test.ts +166 -0
  53. package/telegram-plugin/tests/permission-no-repeat-wiring.test.ts +1 -1
  54. package/telegram-plugin/tests/permission-rearm-wiring.test.ts +175 -0
  55. package/telegram-plugin/tests/permission-rearm.test.ts +126 -0
  56. package/telegram-plugin/tests/scoped-grant-persist.test.ts +223 -0
  57. package/telegram-plugin/tests/silent-end-transport.test.ts +290 -0
  58. package/telegram-plugin/tests/silent-turn-climb-transport.test.ts +337 -0
  59. package/telegram-plugin/tests/subagent-watcher.test.ts +139 -0
  60. package/telegram-plugin/tests/worktree-watch-cwds.test.ts +103 -0
  61. package/telegram-plugin/uat/assertions.ts +88 -4
  62. package/telegram-plugin/uat/feed-matcher.test.ts +69 -0
  63. package/telegram-plugin/uat/scenarios/fuzz-liveness-climb-dm.test.ts +155 -0
  64. package/telegram-plugin/uat/scenarios/jtbd-directive-capture-nudge-dm.test.ts +185 -0
  65. package/telegram-plugin/uat/scenarios/jtbd-liveness-climb-channel.test.ts +192 -0
  66. package/telegram-plugin/uat/scenarios/jtbd-liveness-climb-dm.test.ts +220 -0
  67. package/telegram-plugin/uat/scenarios/jtbd-liveness-narration-channel.test.ts +137 -0
  68. package/telegram-plugin/uat/scenarios/jtbd-liveness-narration-dm.test.ts +148 -0
  69. package/telegram-plugin/uat/scenarios/jtbd-memory-legibility-channel.test.ts +66 -0
  70. package/telegram-plugin/uat/scenarios/jtbd-memory-legibility-dm.test.ts +61 -0
  71. package/telegram-plugin/uat/scenarios/silent-end-recovery-channel.test.ts +136 -0
  72. package/telegram-plugin/uat/scenarios/silent-end-recovery-dm.test.ts +24 -2
  73. package/telegram-plugin/worktree-watch-cwds.ts +60 -0
  74. package/vendor/hindsight-memory/hooks/hooks.json +9 -0
  75. package/vendor/hindsight-memory/scripts/__pycache__/directive_verify.cpython-313.pyc +0 -0
  76. package/vendor/hindsight-memory/scripts/__pycache__/drain_pending.cpython-313.pyc +0 -0
  77. package/vendor/hindsight-memory/scripts/__pycache__/recall.cpython-313.pyc +0 -0
  78. package/vendor/hindsight-memory/scripts/__pycache__/retain.cpython-313.pyc +0 -0
  79. package/vendor/hindsight-memory/scripts/__pycache__/session_end.cpython-313.pyc +0 -0
  80. package/vendor/hindsight-memory/scripts/directive_verify.py +445 -0
  81. package/vendor/hindsight-memory/scripts/lib/__pycache__/__init__.cpython-313.pyc +0 -0
  82. package/vendor/hindsight-memory/scripts/lib/__pycache__/bank.cpython-313.pyc +0 -0
  83. package/vendor/hindsight-memory/scripts/lib/__pycache__/client.cpython-313.pyc +0 -0
  84. package/vendor/hindsight-memory/scripts/lib/__pycache__/config.cpython-313.pyc +0 -0
  85. package/vendor/hindsight-memory/scripts/lib/__pycache__/content.cpython-313.pyc +0 -0
  86. package/vendor/hindsight-memory/scripts/lib/__pycache__/daemon.cpython-313.pyc +0 -0
  87. package/vendor/hindsight-memory/scripts/lib/__pycache__/directives.cpython-313.pyc +0 -0
  88. package/vendor/hindsight-memory/scripts/lib/__pycache__/gateway_ipc.cpython-313.pyc +0 -0
  89. package/vendor/hindsight-memory/scripts/lib/__pycache__/llm.cpython-313.pyc +0 -0
  90. package/vendor/hindsight-memory/scripts/lib/__pycache__/pending.cpython-313.pyc +0 -0
  91. package/vendor/hindsight-memory/scripts/lib/__pycache__/state.cpython-313.pyc +0 -0
  92. package/vendor/hindsight-memory/scripts/lib/__pycache__/switchroom_envelope.cpython-313.pyc +0 -0
  93. package/vendor/hindsight-memory/scripts/lib/config.py +37 -0
  94. package/vendor/hindsight-memory/scripts/lib/directives.py +88 -0
  95. package/vendor/hindsight-memory/scripts/lib/switchroom_envelope.py +77 -0
  96. package/vendor/hindsight-memory/scripts/recall.py +153 -4
  97. package/vendor/hindsight-memory/scripts/retain.py +17 -0
  98. package/vendor/hindsight-memory/scripts/setup_hooks.py +9 -0
  99. package/vendor/hindsight-memory/scripts/tests/__pycache__/__init__.cpython-313.pyc +0 -0
  100. package/vendor/hindsight-memory/scripts/tests/__pycache__/test_config_client_casts.cpython-313-pytest-9.1.1.pyc +0 -0
  101. package/vendor/hindsight-memory/scripts/tests/__pycache__/test_config_client_casts.cpython-313.pyc +0 -0
  102. package/vendor/hindsight-memory/scripts/tests/__pycache__/test_directive_capture_nudge.cpython-313-pytest-9.1.1.pyc +0 -0
  103. package/vendor/hindsight-memory/scripts/tests/__pycache__/test_directive_capture_nudge.cpython-313.pyc +0 -0
  104. package/vendor/hindsight-memory/scripts/tests/__pycache__/test_directive_verify.cpython-313-pytest-9.1.1.pyc +0 -0
  105. package/vendor/hindsight-memory/scripts/tests/__pycache__/test_directive_verify.cpython-313.pyc +0 -0
  106. package/vendor/hindsight-memory/scripts/tests/__pycache__/test_directives.cpython-313-pytest-9.1.1.pyc +0 -0
  107. package/vendor/hindsight-memory/scripts/tests/__pycache__/test_directives.cpython-313.pyc +0 -0
  108. package/vendor/hindsight-memory/scripts/tests/__pycache__/test_gateway_ipc.cpython-313-pytest-9.1.1.pyc +0 -0
  109. package/vendor/hindsight-memory/scripts/tests/__pycache__/test_gateway_ipc.cpython-313.pyc +0 -0
  110. package/vendor/hindsight-memory/scripts/tests/__pycache__/test_recall_context_slice.cpython-313-pytest-9.1.1.pyc +0 -0
  111. package/vendor/hindsight-memory/scripts/tests/__pycache__/test_recall_context_slice.cpython-313.pyc +0 -0
  112. package/vendor/hindsight-memory/scripts/tests/__pycache__/test_recall_integration.cpython-313-pytest-9.1.1.pyc +0 -0
  113. package/vendor/hindsight-memory/scripts/tests/__pycache__/test_recall_integration.cpython-313.pyc +0 -0
  114. package/vendor/hindsight-memory/scripts/tests/__pycache__/test_recall_tag_filters.cpython-313-pytest-9.1.1.pyc +0 -0
  115. package/vendor/hindsight-memory/scripts/tests/__pycache__/test_recall_tag_filters.cpython-313.pyc +0 -0
  116. package/vendor/hindsight-memory/scripts/tests/__pycache__/test_recall_topic_filter.cpython-313-pytest-9.1.1.pyc +0 -0
  117. package/vendor/hindsight-memory/scripts/tests/__pycache__/test_recall_topic_filter.cpython-313.pyc +0 -0
  118. package/vendor/hindsight-memory/scripts/tests/__pycache__/test_recall_trivial_skip.cpython-313-pytest-9.1.1.pyc +0 -0
  119. package/vendor/hindsight-memory/scripts/tests/__pycache__/test_recall_trivial_skip.cpython-313.pyc +0 -0
  120. package/vendor/hindsight-memory/scripts/tests/__pycache__/test_retain_window.cpython-313-pytest-9.1.1.pyc +0 -0
  121. package/vendor/hindsight-memory/scripts/tests/__pycache__/test_retain_window.cpython-313.pyc +0 -0
  122. package/vendor/hindsight-memory/scripts/tests/__pycache__/test_sender_routing.cpython-313-pytest-9.1.1.pyc +0 -0
  123. package/vendor/hindsight-memory/scripts/tests/__pycache__/test_sender_routing.cpython-313.pyc +0 -0
  124. package/vendor/hindsight-memory/scripts/tests/__pycache__/test_switchroom_envelope.cpython-313-pytest-9.1.1.pyc +0 -0
  125. package/vendor/hindsight-memory/scripts/tests/test_directive_capture_nudge.py +185 -0
  126. package/vendor/hindsight-memory/scripts/tests/test_directive_verify.py +516 -0
  127. package/vendor/hindsight-memory/scripts/tests/test_directives.py +49 -0
  128. package/vendor/hindsight-memory/scripts/tests/test_retain_window.py +66 -1
  129. package/vendor/hindsight-memory/scripts/tests/test_switchroom_envelope.py +69 -0
  130. package/vendor/hindsight-memory/tests/__pycache__/conftest.cpython-313-pytest-9.0.3.pyc +0 -0
  131. package/vendor/hindsight-memory/tests/__pycache__/conftest.cpython-313-pytest-9.1.1.pyc +0 -0
  132. package/vendor/hindsight-memory/tests/__pycache__/test_bank.cpython-313-pytest-9.1.1.pyc +0 -0
  133. package/vendor/hindsight-memory/tests/__pycache__/test_bank.cpython-313.pyc +0 -0
  134. package/vendor/hindsight-memory/tests/__pycache__/test_client.cpython-313-pytest-9.1.1.pyc +0 -0
  135. package/vendor/hindsight-memory/tests/__pycache__/test_client.cpython-313.pyc +0 -0
  136. package/vendor/hindsight-memory/tests/__pycache__/test_config.cpython-313-pytest-9.0.3.pyc +0 -0
  137. package/vendor/hindsight-memory/tests/__pycache__/test_config.cpython-313-pytest-9.1.1.pyc +0 -0
  138. package/vendor/hindsight-memory/tests/__pycache__/test_config.cpython-313.pyc +0 -0
  139. package/vendor/hindsight-memory/tests/__pycache__/test_content.cpython-313-pytest-9.1.1.pyc +0 -0
  140. package/vendor/hindsight-memory/tests/__pycache__/test_content.cpython-313.pyc +0 -0
  141. package/vendor/hindsight-memory/tests/__pycache__/test_drain_pending.cpython-313-pytest-9.1.1.pyc +0 -0
  142. package/vendor/hindsight-memory/tests/__pycache__/test_drain_pending.cpython-313.pyc +0 -0
  143. package/vendor/hindsight-memory/tests/__pycache__/test_hooks.cpython-313-pytest-9.1.1.pyc +0 -0
  144. package/vendor/hindsight-memory/tests/__pycache__/test_hooks.cpython-313.pyc +0 -0
  145. package/vendor/hindsight-memory/tests/__pycache__/test_manifest.cpython-313-pytest-9.1.1.pyc +0 -0
  146. package/vendor/hindsight-memory/tests/__pycache__/test_manifest.cpython-313.pyc +0 -0
  147. package/vendor/hindsight-memory/tests/__pycache__/test_pending.cpython-313-pytest-9.1.1.pyc +0 -0
  148. package/vendor/hindsight-memory/tests/__pycache__/test_pending.cpython-313.pyc +0 -0
  149. package/vendor/hindsight-memory/tests/__pycache__/test_recall_exit_codes.cpython-313-pytest-9.1.1.pyc +0 -0
  150. package/vendor/hindsight-memory/tests/__pycache__/test_recall_exit_codes.cpython-313.pyc +0 -0
  151. package/vendor/hindsight-memory/tests/__pycache__/test_session_end_pending.cpython-313-pytest-9.1.1.pyc +0 -0
  152. package/vendor/hindsight-memory/tests/__pycache__/test_session_end_pending.cpython-313.pyc +0 -0
  153. package/vendor/hindsight-memory/tests/__pycache__/test_state.cpython-313-pytest-9.1.1.pyc +0 -0
  154. package/vendor/hindsight-memory/tests/__pycache__/test_state.cpython-313.pyc +0 -0
  155. package/vendor/hindsight-memory/tests/test_recall_exit_codes.py +49 -2
@@ -15850,8 +15850,35 @@ var AgentMemorySchema = exports_external.object({
15850
15850
  auto_recall: exports_external.boolean().default(true).describe("Auto-search memories before each response"),
15851
15851
  file: exports_external.boolean().default(true).describe("Maintain a curated workspace MEMORY.md file (seeded once, " + "auto-loaded every turn). Set false for hindsight-only memory: " + "the file is not seeded or re-created, so once migrated into " + "Hindsight and deleted it stays gone. Recall + directives carry " + "the memory instead. Cascade: override (per-agent wins over default)."),
15852
15852
  isolation: exports_external.enum(["default", "strict"]).default("default").describe("strict = never shared cross-agent, default = eligible for reflect"),
15853
- bank_mission: exports_external.string().optional().describe("Bank-level mission statement used during recall to contextualize results"),
15854
- retain_mission: exports_external.string().optional().describe("Instructions for the fact extraction LLM during retain"),
15853
+ bank_mission: exports_external.string().optional().describe("Bank-level mission statement used during recall to contextualize " + "results. NOTE: this is an alias for the Hindsight engine's " + "`reflect_mission` field (verified live: switchroom's bank_mission " + "lands in `config.reflect_mission`). Prefer `reflect_mission` going " + "forward; `bank_mission` is retained for back-compat. If both are " + "set, `reflect_mission` wins. Cascade: override."),
15854
+ reflect_mission: exports_external.string().optional().describe("Mission/context steering Hindsight Reflect operations (the bank's " + "'who am I / what matters' framing applied during recall). The " + "engine-accurate name for what `bank_mission` sets. Cascade: override."),
15855
+ retain_mission: exports_external.string().optional().describe("Instructions for the fact extraction LLM during retain. Cascade: override."),
15856
+ mental_models: exports_external.array(exports_external.object({
15857
+ name: exports_external.string().min(1).describe("Stable model name (identity key for idempotent ensure). Two " + "declarations with the same name in one agent are rejected."),
15858
+ source_query: exports_external.string().min(1).max(2000).describe("The reflection query the model answers, semantically " + "refreshed from the bank's content. Capped at 2000 chars — a " + "standing reflection query, not a document; the ceiling also " + "bounds what an agent-proposed model can smuggle past the " + "operator approval card."),
15859
+ refresh_after_consolidation: exports_external.boolean().optional().describe("Refresh this model after each consolidation. Defaults OFF — " + "refresh adds bounded background model-spend + timeout risk " + "(RFC Phase 5), so it is opt-in per model."),
15860
+ max_tokens: exports_external.number().int().positive().max(8192).optional().describe("Cap on the synthesized model's token size. Upper-bounded at " + "8192 — a mental model is a standing summary, not a corpus; the " + "ceiling also caps what an agent-proposed model can request.")
15861
+ })).superRefine((models, ctx) => {
15862
+ const seen = new Set;
15863
+ for (let i = 0;i < models.length; i++) {
15864
+ const key = models[i].name;
15865
+ if (seen.has(key)) {
15866
+ ctx.addIssue({
15867
+ code: exports_external.ZodIssueCode.custom,
15868
+ path: [i, "name"],
15869
+ message: `duplicate mental_models name "${key}" — model names must be ` + `unique within an agent (they are the idempotent-ensure key)`
15870
+ });
15871
+ }
15872
+ seen.add(key);
15873
+ }
15874
+ }).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)."),
15875
+ 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."),
15876
+ disposition: exports_external.object({
15877
+ skepticism: exports_external.number().int().min(1).max(5).optional().describe("How much the bank doubts unverified claims (1-5; engine default 3)."),
15878
+ 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)."),
15879
+ empathy: exports_external.number().int().min(1).max(5).optional().describe("How much the bank weights emotional/relational context (1-5; engine default 3).")
15880
+ }).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`)."),
15881
+ 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)."),
15855
15882
  recall: exports_external.object({
15856
15883
  max_memories: exports_external.number().int().min(0).optional().describe("Cap on the number of memories injected into the prompt by " + "auto-recall, regardless of token budget. Plugin default is 12. " + "0 disables the cap (all memories Hindsight returns are injected)."),
15857
15884
  cache_ttl_secs: exports_external.number().int().min(0).optional().describe("Per-session recall cache TTL in seconds. When > 0, identical " + "(prompt, bank) within the same session reuse the cached recall " + "result instead of round-tripping to Hindsight. 0 disables. " + "Default is 600 (10 min) for switchroom-managed agents."),
@@ -16116,6 +16143,7 @@ var profileFields = {
16116
16143
  auto_recall: exports_external.boolean().optional(),
16117
16144
  file: exports_external.boolean().optional(),
16118
16145
  isolation: exports_external.enum(["default", "strict"]).optional(),
16146
+ directive_capture_nudge: exports_external.boolean().optional(),
16119
16147
  recall: exports_external.object({
16120
16148
  max_memories: exports_external.number().int().min(0).optional(),
16121
16149
  cache_ttl_secs: exports_external.number().int().min(0).optional(),
@@ -16770,6 +16798,11 @@ function mergeAgentConfig(defaultsIn, agentIn) {
16770
16798
  continue;
16771
16799
  if (k === "recall" && base.recall && typeof v === "object" && v !== null && !Array.isArray(v)) {
16772
16800
  combined[k] = { ...base.recall, ...v };
16801
+ } else if (k === "disposition" && base.disposition && typeof v === "object" && v !== null && !Array.isArray(v)) {
16802
+ combined[k] = {
16803
+ ...base.disposition,
16804
+ ...v
16805
+ };
16773
16806
  } else {
16774
16807
  combined[k] = v;
16775
16808
  }
@@ -17222,8 +17255,31 @@ function isReservedAgentName(name) {
17222
17255
  return RESERVED_AGENT_NAMES.has(name);
17223
17256
  }
17224
17257
 
17258
+ // src/setup/hindsight.ts
17259
+ var HINDSIGHT_DEFAULT_API_PORT = 18888;
17260
+ var HINDSIGHT_DEFAULT_MCP_URL = `http://127.0.0.1:${HINDSIGHT_DEFAULT_API_PORT}/mcp/`;
17261
+ var HINDSIGHT_DEFAULT_API_BASE_URL = HINDSIGHT_DEFAULT_MCP_URL.replace(/\/mcp\/?$/, "");
17262
+ var HINDSIGHT_CONSUMER_NAME = "hindsight";
17263
+ var HINDSIGHT_IMAGE_REPO = "ghcr.io/switchroom/switchroom-hindsight";
17264
+ var HINDSIGHT_IMAGE = `${HINDSIGHT_IMAGE_REPO}:latest`;
17265
+ var HINDSIGHT_BROKER_SOCK_VOLUME = `auth-broker-${HINDSIGHT_CONSUMER_NAME}-sock`;
17266
+ var HINDSIGHT_HEALTHCHECK_PY = 'import urllib.request,sys; sys.exit(0 if urllib.request.urlopen("http://localhost:8888/health",timeout=4).getcode()==200 else 1)';
17267
+ var HINDSIGHT_HEALTHCHECK_CMD = `python3 -c '${HINDSIGHT_HEALTHCHECK_PY}'`;
17268
+
17225
17269
  // src/memory/hindsight.ts
17226
17270
  var DEFAULT_RETAIN_MISSION = "Extract user preferences, ongoing projects, recurring commitments, " + "important context, and durable facts that should help across future " + "conversations. Skip one-off chatter and temporary task noise.";
17271
+ var PROFILE_MEMORY_DEFAULTS = {
17272
+ "health-coach": {
17273
+ disposition: { skepticism: 2, literalism: 2, empathy: 5 },
17274
+ observations_mission: "Synthesise the person's wellbeing patterns, motivations, and emotional " + "context — how habits, setbacks, and encouragement connect over time."
17275
+ },
17276
+ "executive-assistant": {
17277
+ disposition: { skepticism: 4, literalism: 4, empathy: 3 }
17278
+ },
17279
+ coding: {
17280
+ disposition: { skepticism: 4, literalism: 5, empathy: 2 }
17281
+ }
17282
+ };
17227
17283
 
17228
17284
  // src/agents/reconcile-default-skills.ts
17229
17285
  var warnedMissingPool = new Set;
@@ -22670,7 +22726,7 @@ import { existsSync as existsSync6, readFileSync as readFileSync4 } from "node:f
22670
22726
  import { dirname as dirname4, join as join2 } from "node:path";
22671
22727
 
22672
22728
  // src/build-info.ts
22673
- var VERSION = "0.17.6";
22729
+ var VERSION = "0.17.10";
22674
22730
 
22675
22731
  // src/cli/resolve-version.ts
22676
22732
  function readPackageVersion() {
@@ -22800,14 +22856,6 @@ function latestRolloutRowForRequest(raw, requestId) {
22800
22856
  return latest;
22801
22857
  }
22802
22858
 
22803
- // src/setup/hindsight.ts
22804
- var HINDSIGHT_CONSUMER_NAME = "hindsight";
22805
- var HINDSIGHT_IMAGE_REPO = "ghcr.io/switchroom/switchroom-hindsight";
22806
- var HINDSIGHT_IMAGE = `${HINDSIGHT_IMAGE_REPO}:latest`;
22807
- var HINDSIGHT_BROKER_SOCK_VOLUME = `auth-broker-${HINDSIGHT_CONSUMER_NAME}-sock`;
22808
- var HINDSIGHT_HEALTHCHECK_PY = 'import urllib.request,sys; sys.exit(0 if urllib.request.urlopen("http://localhost:8888/health",timeout=4).getcode()==200 else 1)';
22809
- var HINDSIGHT_HEALTHCHECK_CMD = `python3 -c '${HINDSIGHT_HEALTHCHECK_PY}'`;
22810
-
22811
22859
  // src/cli/rollout.ts
22812
22860
  function isVersionAssertable(target) {
22813
22861
  return /^v?\d+\.\d+\.\d+$/.test(target.trim());
@@ -23537,6 +23585,92 @@ function stripCallerAllow(cfg, caller) {
23537
23585
  }
23538
23586
  return clone;
23539
23587
  }
23588
+ function assertSelfScopedMentalModelEdit(beforeContent, afterContent, caller) {
23589
+ let before;
23590
+ let after;
23591
+ try {
23592
+ before = toObject(import_yaml5.parseDocument(beforeContent, { merge: false, strict: false }).toJS());
23593
+ after = toObject(import_yaml5.parseDocument(afterContent, { merge: false, strict: false }).toJS());
23594
+ } catch (e) {
23595
+ return { ok: false, detail: `self-scope: config did not parse (${e.message})` };
23596
+ }
23597
+ const beforeModels = readCallerMentalModels(before, caller);
23598
+ const afterByName = new Map;
23599
+ for (const m of readCallerMentalModels(after, caller)) {
23600
+ const n = m && typeof m === "object" ? m.name : undefined;
23601
+ if (typeof n === "string" && !afterByName.has(n))
23602
+ afterByName.set(n, m);
23603
+ }
23604
+ for (const m of beforeModels) {
23605
+ const n = m && typeof m === "object" ? m.name : undefined;
23606
+ if (typeof n !== "string")
23607
+ continue;
23608
+ if (!afterByName.has(n)) {
23609
+ return {
23610
+ ok: false,
23611
+ detail: `self-scope: edit removes existing mental model "${n}" from agents.${caller}.memory.mental_models`
23612
+ };
23613
+ }
23614
+ if (!isDeepStrictEqual(m, afterByName.get(n))) {
23615
+ return {
23616
+ ok: false,
23617
+ detail: `self-scope: edit rewrites existing mental model "${n}" ` + `(append-only: an existing declared model is immutable — only NEW models may be added)`
23618
+ };
23619
+ }
23620
+ }
23621
+ const beforeStripped = stripCallerMentalModels(before, caller);
23622
+ const afterStripped = stripCallerMentalModels(after, caller);
23623
+ if (!isDeepStrictEqual(beforeStripped, afterStripped)) {
23624
+ return {
23625
+ ok: false,
23626
+ detail: `self-scope: edit changes config outside agents.${caller}.memory.mental_models ` + `(a non-admin agent may only append its own declared mental models)`
23627
+ };
23628
+ }
23629
+ return { ok: true };
23630
+ }
23631
+ function admitSelfScopedNonAdminEdit(beforeContent, afterContent, caller) {
23632
+ const allowScope = assertSelfScopedAllowEdit(beforeContent, afterContent, caller);
23633
+ if (allowScope.ok)
23634
+ return { ok: true, via: "tools.allow" };
23635
+ const mentalModelScope = assertSelfScopedMentalModelEdit(beforeContent, afterContent, caller);
23636
+ if (mentalModelScope.ok)
23637
+ return { ok: true, via: "mental_models" };
23638
+ return {
23639
+ ok: false,
23640
+ detail: allowScope.detail,
23641
+ mentalModelDetail: mentalModelScope.detail
23642
+ };
23643
+ }
23644
+ function readCallerMentalModels(cfg, caller) {
23645
+ const agents = cfg.agents;
23646
+ if (!agents || typeof agents !== "object")
23647
+ return [];
23648
+ const agent = agents[caller];
23649
+ if (!agent || typeof agent !== "object")
23650
+ return [];
23651
+ const memory = agent.memory;
23652
+ if (!memory || typeof memory !== "object")
23653
+ return [];
23654
+ const models = memory.mental_models;
23655
+ return Array.isArray(models) ? models : [];
23656
+ }
23657
+ function stripCallerMentalModels(cfg, caller) {
23658
+ const clone = structuredClone(cfg);
23659
+ const agents = clone.agents;
23660
+ if (!agents || typeof agents !== "object")
23661
+ return clone;
23662
+ const agent = agents[caller];
23663
+ if (!agent || typeof agent !== "object")
23664
+ return clone;
23665
+ const memory = agent.memory;
23666
+ if (!memory || typeof memory !== "object")
23667
+ return clone;
23668
+ delete memory.mental_models;
23669
+ if (Object.keys(memory).length === 0) {
23670
+ delete agent.memory;
23671
+ }
23672
+ return clone;
23673
+ }
23540
23674
 
23541
23675
  // src/host-control/config-blast-radius.ts
23542
23676
  var import_yaml6 = __toESM(require_dist(), 1);
@@ -24699,9 +24833,9 @@ class HostdServer {
24699
24833
  } catch {
24700
24834
  beforeContent = "";
24701
24835
  }
24702
- const scope = assertSelfScopedAllowEdit(beforeContent, verdict.postApplyContent, caller.name);
24703
- if (!scope.ok) {
24704
- return err("E_NOT_SELF_SCOPED", scope.detail).why("non-admin agents may only add rules to their own " + "agents.<self>.tools.allow via config_propose_edit").fixBadInput("unified_diff").op("config_propose_edit").caller("agent").agentName(caller.name).asDenied().build(req.request_id, Date.now() - started);
24836
+ const admission = admitSelfScopedNonAdminEdit(beforeContent, verdict.postApplyContent, caller.name);
24837
+ if (!admission.ok) {
24838
+ return err("E_NOT_SELF_SCOPED", admission.detail).why("non-admin agents may only add rules to their own " + "agents.<self>.tools.allow OR append their own " + "agents.<self>.memory.mental_models via config_propose_edit " + `(mental-model check: ${admission.mentalModelDetail})`).fixBadInput("unified_diff").op("config_propose_edit").caller("agent").agentName(caller.name).asDenied().build(req.request_id, Date.now() - started);
24705
24839
  }
24706
24840
  }
24707
24841
  if (!this.opts.approvalGateway) {
@@ -1,5 +1,34 @@
1
1
  import { createRequire } from "node:module";
2
+ var __create = Object.create;
3
+ var __getProtoOf = Object.getPrototypeOf;
2
4
  var __defProp = Object.defineProperty;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ function __accessProp(key) {
8
+ return this[key];
9
+ }
10
+ var __toESMCache_node;
11
+ var __toESMCache_esm;
12
+ var __toESM = (mod, isNodeMode, target) => {
13
+ var canCache = mod != null && typeof mod === "object";
14
+ if (canCache) {
15
+ var cache = isNodeMode ? __toESMCache_node ??= new WeakMap : __toESMCache_esm ??= new WeakMap;
16
+ var cached = cache.get(mod);
17
+ if (cached)
18
+ return cached;
19
+ }
20
+ target = mod != null ? __create(__getProtoOf(mod)) : {};
21
+ const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
22
+ for (let key of __getOwnPropNames(mod))
23
+ if (!__hasOwnProp.call(to, key))
24
+ __defProp(to, key, {
25
+ get: __accessProp.bind(mod, key),
26
+ enumerable: true
27
+ });
28
+ if (canCache)
29
+ cache.set(mod, to);
30
+ return to;
31
+ };
3
32
  var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
4
33
  var __returnValue = (v) => v;
5
34
  function __exportSetter(name, newValue) {
@@ -4098,8 +4127,35 @@ var init_schema = __esm(() => {
4098
4127
  auto_recall: exports_external.boolean().default(true).describe("Auto-search memories before each response"),
4099
4128
  file: exports_external.boolean().default(true).describe("Maintain a curated workspace MEMORY.md file (seeded once, " + "auto-loaded every turn). Set false for hindsight-only memory: " + "the file is not seeded or re-created, so once migrated into " + "Hindsight and deleted it stays gone. Recall + directives carry " + "the memory instead. Cascade: override (per-agent wins over default)."),
4100
4129
  isolation: exports_external.enum(["default", "strict"]).default("default").describe("strict = never shared cross-agent, default = eligible for reflect"),
4101
- bank_mission: exports_external.string().optional().describe("Bank-level mission statement used during recall to contextualize results"),
4102
- retain_mission: exports_external.string().optional().describe("Instructions for the fact extraction LLM during retain"),
4130
+ bank_mission: exports_external.string().optional().describe("Bank-level mission statement used during recall to contextualize " + "results. NOTE: this is an alias for the Hindsight engine's " + "`reflect_mission` field (verified live: switchroom's bank_mission " + "lands in `config.reflect_mission`). Prefer `reflect_mission` going " + "forward; `bank_mission` is retained for back-compat. If both are " + "set, `reflect_mission` wins. Cascade: override."),
4131
+ reflect_mission: exports_external.string().optional().describe("Mission/context steering Hindsight Reflect operations (the bank's " + "'who am I / what matters' framing applied during recall). The " + "engine-accurate name for what `bank_mission` sets. Cascade: override."),
4132
+ retain_mission: exports_external.string().optional().describe("Instructions for the fact extraction LLM during retain. Cascade: override."),
4133
+ mental_models: exports_external.array(exports_external.object({
4134
+ name: exports_external.string().min(1).describe("Stable model name (identity key for idempotent ensure). Two " + "declarations with the same name in one agent are rejected."),
4135
+ source_query: exports_external.string().min(1).max(2000).describe("The reflection query the model answers, semantically " + "refreshed from the bank's content. Capped at 2000 chars — a " + "standing reflection query, not a document; the ceiling also " + "bounds what an agent-proposed model can smuggle past the " + "operator approval card."),
4136
+ refresh_after_consolidation: exports_external.boolean().optional().describe("Refresh this model after each consolidation. Defaults OFF — " + "refresh adds bounded background model-spend + timeout risk " + "(RFC Phase 5), so it is opt-in per model."),
4137
+ max_tokens: exports_external.number().int().positive().max(8192).optional().describe("Cap on the synthesized model's token size. Upper-bounded at " + "8192 — a mental model is a standing summary, not a corpus; the " + "ceiling also caps what an agent-proposed model can request.")
4138
+ })).superRefine((models, ctx) => {
4139
+ const seen = new Set;
4140
+ for (let i = 0;i < models.length; i++) {
4141
+ const key = models[i].name;
4142
+ if (seen.has(key)) {
4143
+ ctx.addIssue({
4144
+ code: exports_external.ZodIssueCode.custom,
4145
+ path: [i, "name"],
4146
+ message: `duplicate mental_models name "${key}" — model names must be ` + `unique within an agent (they are the idempotent-ensure key)`
4147
+ });
4148
+ }
4149
+ seen.add(key);
4150
+ }
4151
+ }).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)."),
4152
+ 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."),
4153
+ disposition: exports_external.object({
4154
+ skepticism: exports_external.number().int().min(1).max(5).optional().describe("How much the bank doubts unverified claims (1-5; engine default 3)."),
4155
+ 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)."),
4156
+ empathy: exports_external.number().int().min(1).max(5).optional().describe("How much the bank weights emotional/relational context (1-5; engine default 3).")
4157
+ }).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`)."),
4158
+ 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)."),
4103
4159
  recall: exports_external.object({
4104
4160
  max_memories: exports_external.number().int().min(0).optional().describe("Cap on the number of memories injected into the prompt by " + "auto-recall, regardless of token budget. Plugin default is 12. " + "0 disables the cap (all memories Hindsight returns are injected)."),
4105
4161
  cache_ttl_secs: exports_external.number().int().min(0).optional().describe("Per-session recall cache TTL in seconds. When > 0, identical " + "(prompt, bank) within the same session reuse the cached recall " + "result instead of round-tripping to Hindsight. 0 disables. " + "Default is 600 (10 min) for switchroom-managed agents."),
@@ -4364,6 +4420,7 @@ var init_schema = __esm(() => {
4364
4420
  auto_recall: exports_external.boolean().optional(),
4365
4421
  file: exports_external.boolean().optional(),
4366
4422
  isolation: exports_external.enum(["default", "strict"]).optional(),
4423
+ directive_capture_nudge: exports_external.boolean().optional(),
4367
4424
  recall: exports_external.object({
4368
4425
  max_memories: exports_external.number().int().min(0).optional(),
4369
4426
  cache_ttl_secs: exports_external.number().int().min(0).optional(),
@@ -4827,6 +4884,11 @@ function mergeAgentConfig(defaultsIn, agentIn) {
4827
4884
  continue;
4828
4885
  if (k === "recall" && base.recall && typeof v === "object" && v !== null && !Array.isArray(v)) {
4829
4886
  combined[k] = { ...base.recall, ...v };
4887
+ } else if (k === "disposition" && base.disposition && typeof v === "object" && v !== null && !Array.isArray(v)) {
4888
+ combined[k] = {
4889
+ ...base.disposition,
4890
+ ...v
4891
+ };
4830
4892
  } else {
4831
4893
  combined[k] = v;
4832
4894
  }
@@ -11999,52 +12061,52 @@ var require_public_api = __commonJS((exports) => {
11999
12061
  });
12000
12062
 
12001
12063
  // node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/index.js
12002
- var composer, Document, Schema, errors2, Alias, identity, Pair, Scalar, YAMLMap, YAMLSeq, cst, lexer, lineCounter, parser, publicApi, visit, $Composer, $Document, $Schema, $YAMLError, $YAMLParseError, $YAMLWarning, $Alias, $isAlias, $isCollection, $isDocument, $isMap, $isNode, $isPair, $isScalar, $isSeq, $Pair, $Scalar, $YAMLMap, $YAMLSeq, $Lexer, $LineCounter, $Parser, $parse, $parseAllDocuments, $parseDocument, $stringify, $visit, $visitAsync;
12003
- var init_dist = __esm(() => {
12004
- composer = require_composer();
12005
- Document = require_Document();
12006
- Schema = require_Schema();
12007
- errors2 = require_errors();
12008
- Alias = require_Alias();
12009
- identity = require_identity();
12010
- Pair = require_Pair();
12011
- Scalar = require_Scalar();
12012
- YAMLMap = require_YAMLMap();
12013
- YAMLSeq = require_YAMLSeq();
12014
- cst = require_cst();
12015
- lexer = require_lexer();
12016
- lineCounter = require_line_counter();
12017
- parser = require_parser();
12018
- publicApi = require_public_api();
12019
- visit = require_visit();
12020
- $Composer = composer.Composer;
12021
- $Document = Document.Document;
12022
- $Schema = Schema.Schema;
12023
- $YAMLError = errors2.YAMLError;
12024
- $YAMLParseError = errors2.YAMLParseError;
12025
- $YAMLWarning = errors2.YAMLWarning;
12026
- $Alias = Alias.Alias;
12027
- $isAlias = identity.isAlias;
12028
- $isCollection = identity.isCollection;
12029
- $isDocument = identity.isDocument;
12030
- $isMap = identity.isMap;
12031
- $isNode = identity.isNode;
12032
- $isPair = identity.isPair;
12033
- $isScalar = identity.isScalar;
12034
- $isSeq = identity.isSeq;
12035
- $Pair = Pair.Pair;
12036
- $Scalar = Scalar.Scalar;
12037
- $YAMLMap = YAMLMap.YAMLMap;
12038
- $YAMLSeq = YAMLSeq.YAMLSeq;
12039
- $Lexer = lexer.Lexer;
12040
- $LineCounter = lineCounter.LineCounter;
12041
- $Parser = parser.Parser;
12042
- $parse = publicApi.parse;
12043
- $parseAllDocuments = publicApi.parseAllDocuments;
12044
- $parseDocument = publicApi.parseDocument;
12045
- $stringify = publicApi.stringify;
12046
- $visit = visit.visit;
12047
- $visitAsync = visit.visitAsync;
12064
+ var require_dist = __commonJS((exports) => {
12065
+ var composer = require_composer();
12066
+ var Document = require_Document();
12067
+ var Schema = require_Schema();
12068
+ var errors2 = require_errors();
12069
+ var Alias = require_Alias();
12070
+ var identity = require_identity();
12071
+ var Pair = require_Pair();
12072
+ var Scalar = require_Scalar();
12073
+ var YAMLMap = require_YAMLMap();
12074
+ var YAMLSeq = require_YAMLSeq();
12075
+ var cst = require_cst();
12076
+ var lexer = require_lexer();
12077
+ var lineCounter = require_line_counter();
12078
+ var parser = require_parser();
12079
+ var publicApi = require_public_api();
12080
+ var visit = require_visit();
12081
+ exports.Composer = composer.Composer;
12082
+ exports.Document = Document.Document;
12083
+ exports.Schema = Schema.Schema;
12084
+ exports.YAMLError = errors2.YAMLError;
12085
+ exports.YAMLParseError = errors2.YAMLParseError;
12086
+ exports.YAMLWarning = errors2.YAMLWarning;
12087
+ exports.Alias = Alias.Alias;
12088
+ exports.isAlias = identity.isAlias;
12089
+ exports.isCollection = identity.isCollection;
12090
+ exports.isDocument = identity.isDocument;
12091
+ exports.isMap = identity.isMap;
12092
+ exports.isNode = identity.isNode;
12093
+ exports.isPair = identity.isPair;
12094
+ exports.isScalar = identity.isScalar;
12095
+ exports.isSeq = identity.isSeq;
12096
+ exports.Pair = Pair.Pair;
12097
+ exports.Scalar = Scalar.Scalar;
12098
+ exports.YAMLMap = YAMLMap.YAMLMap;
12099
+ exports.YAMLSeq = YAMLSeq.YAMLSeq;
12100
+ exports.CST = cst;
12101
+ exports.Lexer = lexer.Lexer;
12102
+ exports.LineCounter = lineCounter.LineCounter;
12103
+ exports.Parser = parser.Parser;
12104
+ exports.parse = publicApi.parse;
12105
+ exports.parseAllDocuments = publicApi.parseAllDocuments;
12106
+ exports.parseDocument = publicApi.parseDocument;
12107
+ exports.stringify = publicApi.stringify;
12108
+ exports.visit = visit.visit;
12109
+ exports.visitAsync = visit.visitAsync;
12048
12110
  });
12049
12111
 
12050
12112
  // src/config/overlay-schema.ts
@@ -12124,7 +12186,7 @@ function applyAgentOverlays(config) {
12124
12186
  for (const file of files) {
12125
12187
  try {
12126
12188
  const raw = readFileSync(file, "utf-8");
12127
- const parsed = $parse(raw);
12189
+ const parsed = import_yaml.parse(raw);
12128
12190
  const doc = OverlayDocSchema.parse(parsed);
12129
12191
  const title = deriveOverlayTitle(raw, basename(file));
12130
12192
  for (const entry of doc.schedule ?? []) {
@@ -12165,7 +12227,7 @@ function applyAgentOverlays(config) {
12165
12227
  for (const file of skillFiles) {
12166
12228
  try {
12167
12229
  const raw = readFileSync(file, "utf-8");
12168
- const parsed = $parse(raw);
12230
+ const parsed = import_yaml.parse(raw);
12169
12231
  const doc = OverlayDocSchema.parse(parsed);
12170
12232
  for (const skillName of doc.skills ?? []) {
12171
12233
  if (seen.has(skillName))
@@ -12192,12 +12254,12 @@ function applyAgentOverlays(config) {
12192
12254
  }
12193
12255
  return { config, warnings };
12194
12256
  }
12195
- var OVERLAY_SOURCE, OVERLAY_TITLE;
12257
+ var import_yaml, OVERLAY_SOURCE, OVERLAY_TITLE;
12196
12258
  var init_overlay_loader = __esm(() => {
12197
- init_dist();
12198
12259
  init_zod();
12199
12260
  init_overlay_schema();
12200
12261
  init_paths();
12262
+ import_yaml = __toESM(require_dist(), 1);
12201
12263
  OVERLAY_SOURCE = Symbol.for("switchroom.config.overlay-source");
12202
12264
  OVERLAY_TITLE = Symbol.for("switchroom.config.overlay-title");
12203
12265
  });
@@ -12327,7 +12389,7 @@ function loadConfig(configPath) {
12327
12389
  }
12328
12390
  let parsed;
12329
12391
  try {
12330
- parsed = $parse(raw);
12392
+ parsed = import_yaml2.parse(raw);
12331
12393
  } catch (err) {
12332
12394
  throw new ConfigError(`Invalid YAML in ${filePath}`, [
12333
12395
  ` ${err.message}`
@@ -12393,14 +12455,14 @@ function resolveAgentsDir(config) {
12393
12455
  function resolvePath(pathStr) {
12394
12456
  return resolveDualPath(pathStr);
12395
12457
  }
12396
- var ConfigError;
12458
+ var import_yaml2, ConfigError;
12397
12459
  var init_loader = __esm(() => {
12398
- init_dist();
12399
12460
  init_zod();
12400
12461
  init_schema();
12401
12462
  init_paths();
12402
12463
  init_overlay_loader();
12403
12464
  init_merge();
12465
+ import_yaml2 = __toESM(require_dist(), 1);
12404
12466
  ConfigError = class ConfigError extends Error {
12405
12467
  details;
12406
12468
  constructor(message, details) {
@@ -13195,8 +13257,31 @@ var CONTAINER_DEFAULT_UTC_ZONES = new Set([
13195
13257
  // src/config/users.ts
13196
13258
  init_merge();
13197
13259
 
13260
+ // src/setup/hindsight.ts
13261
+ var HINDSIGHT_DEFAULT_API_PORT = 18888;
13262
+ var HINDSIGHT_DEFAULT_MCP_URL = `http://127.0.0.1:${HINDSIGHT_DEFAULT_API_PORT}/mcp/`;
13263
+ var HINDSIGHT_DEFAULT_API_BASE_URL = HINDSIGHT_DEFAULT_MCP_URL.replace(/\/mcp\/?$/, "");
13264
+ var HINDSIGHT_CONSUMER_NAME = "hindsight";
13265
+ var HINDSIGHT_IMAGE_REPO = "ghcr.io/switchroom/switchroom-hindsight";
13266
+ var HINDSIGHT_IMAGE = `${HINDSIGHT_IMAGE_REPO}:latest`;
13267
+ var HINDSIGHT_BROKER_SOCK_VOLUME = `auth-broker-${HINDSIGHT_CONSUMER_NAME}-sock`;
13268
+ var HINDSIGHT_HEALTHCHECK_PY = 'import urllib.request,sys; sys.exit(0 if urllib.request.urlopen("http://localhost:8888/health",timeout=4).getcode()==200 else 1)';
13269
+ var HINDSIGHT_HEALTHCHECK_CMD = `python3 -c '${HINDSIGHT_HEALTHCHECK_PY}'`;
13270
+
13198
13271
  // src/memory/hindsight.ts
13199
13272
  var DEFAULT_RETAIN_MISSION = "Extract user preferences, ongoing projects, recurring commitments, " + "important context, and durable facts that should help across future " + "conversations. Skip one-off chatter and temporary task noise.";
13273
+ var PROFILE_MEMORY_DEFAULTS = {
13274
+ "health-coach": {
13275
+ disposition: { skepticism: 2, literalism: 2, empathy: 5 },
13276
+ observations_mission: "Synthesise the person's wellbeing patterns, motivations, and emotional " + "context — how habits, setbacks, and encouragement connect over time."
13277
+ },
13278
+ "executive-assistant": {
13279
+ disposition: { skepticism: 4, literalism: 4, empathy: 3 }
13280
+ },
13281
+ coding: {
13282
+ disposition: { skepticism: 4, literalism: 5, empathy: 2 }
13283
+ }
13284
+ };
13200
13285
 
13201
13286
  // src/agents/reconcile-default-skills.ts
13202
13287
  var warnedMissingPool = new Set;