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
@@ -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 = readFileSync5(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, basename3(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 = readFileSync5(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) {
@@ -12762,8 +12824,31 @@ function identify(socketPath, socket, execFileSyncOverride) {
12762
12824
  // src/config/users.ts
12763
12825
  init_merge();
12764
12826
 
12827
+ // src/setup/hindsight.ts
12828
+ var HINDSIGHT_DEFAULT_API_PORT = 18888;
12829
+ var HINDSIGHT_DEFAULT_MCP_URL = `http://127.0.0.1:${HINDSIGHT_DEFAULT_API_PORT}/mcp/`;
12830
+ var HINDSIGHT_DEFAULT_API_BASE_URL = HINDSIGHT_DEFAULT_MCP_URL.replace(/\/mcp\/?$/, "");
12831
+ var HINDSIGHT_CONSUMER_NAME = "hindsight";
12832
+ var HINDSIGHT_IMAGE_REPO = "ghcr.io/switchroom/switchroom-hindsight";
12833
+ var HINDSIGHT_IMAGE = `${HINDSIGHT_IMAGE_REPO}:latest`;
12834
+ var HINDSIGHT_BROKER_SOCK_VOLUME = `auth-broker-${HINDSIGHT_CONSUMER_NAME}-sock`;
12835
+ 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)';
12836
+ var HINDSIGHT_HEALTHCHECK_CMD = `python3 -c '${HINDSIGHT_HEALTHCHECK_PY}'`;
12837
+
12765
12838
  // src/memory/hindsight.ts
12766
12839
  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.";
12840
+ var PROFILE_MEMORY_DEFAULTS = {
12841
+ "health-coach": {
12842
+ disposition: { skepticism: 2, literalism: 2, empathy: 5 },
12843
+ observations_mission: "Synthesise the person's wellbeing patterns, motivations, and emotional " + "context — how habits, setbacks, and encouragement connect over time."
12844
+ },
12845
+ "executive-assistant": {
12846
+ disposition: { skepticism: 4, literalism: 4, empathy: 3 }
12847
+ },
12848
+ coding: {
12849
+ disposition: { skepticism: 4, literalism: 5, empathy: 2 }
12850
+ }
12851
+ };
12767
12852
 
12768
12853
  // src/agents/reconcile-default-skills.ts
12769
12854
  var warnedMissingPool = new Set;
@@ -16789,8 +16874,8 @@ class VaultBroker {
16789
16874
  }
16790
16875
  bindOperatorListener(socketPath, operatorUid) {
16791
16876
  const abs = resolve6(socketPath);
16792
- const identity2 = socketPathToIdentity(abs);
16793
- if (identity2?.kind !== "operator") {
16877
+ const identity = socketPathToIdentity(abs);
16878
+ if (identity?.kind !== "operator") {
16794
16879
  return Promise.reject(new Error(`bindOperatorListener: socket path '${abs}' does not match the canonical ` + `/run/switchroom/broker/operator/sock shape — refusing to bind`));
16795
16880
  }
16796
16881
  const unlockAbs = unlockSocketFor(abs);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "switchroom",
3
3
  "//version": "NOT the release version — source of truth is the git tag, resolved by scripts/build.mjs:resolveVersion() (see CLAUDE.md > Standard release process). This field is stale by design and only the Layer-4 dev/non-tag fallback for build.mjs + src/cli/resolve-version.ts; do NOT bump it expecting a release to pick it up. npm-pack tarball naming needs a real version — do that as an UNCOMMITTED pack-time bump (see release step 6), never a committed one.",
4
- "version": "0.17.6",
4
+ "version": "0.17.10",
5
5
  "description": "Run Claude Code 24/7 on your Claude Pro/Max subscription over Telegram. Open-source alternative to OpenClaw and NanoClaw — no API keys.",
6
6
  "type": "module",
7
7
  "bin": {
@@ -69,9 +69,27 @@ if [ "$SWITCHROOM_RUNTIME" = "docker" ] && [ -z "$SWITCHROOM_DOCKER_TMUX_INNER"
69
69
  sr_ll_ok=""
70
70
  if [ -z "$sr_ll_key" ]; then
71
71
  echo "litellm(outer): no virtual key for agent '$SWITCHROOM_AGENT_NAME' — gateway will use direct OAuth (no tracking/guardrail)" >&2
72
- elif command -v curl >/dev/null 2>&1 && [ -n "$ANTHROPIC_BASE_URL" ] \
73
- && ! curl -fsS -m 5 -o /dev/null "${SWITCHROOM_LITELLM_BASE:-${ANTHROPIC_BASE_URL%/anthropic}}/health/liveliness" 2>/dev/null; then
74
- echo "litellm(outer): proxy unreachable at ${SWITCHROOM_LITELLM_BASE:-$ANTHROPIC_BASE_URL} — falling back to direct OAuth (no tracking/guardrail this session)" >&2
72
+ elif command -v curl >/dev/null 2>&1 && [ -n "$ANTHROPIC_BASE_URL" ]; then
73
+ # Bounded retry probe (co-boot race fix, 2026-07): when the whole stack
74
+ # co-boots, the litellm proxy's heavy Python app is often not yet healthy
75
+ # in the first few seconds. A single one-shot probe fails-open and takes
76
+ # this agent dark (untracked) for the WHOLE session. Poll the liveliness
77
+ # endpoint every ~3s for up to 120s (hard budget) and only fall open if
78
+ # it is STILL unreachable after that whole window.
79
+ sr_ll_url="${SWITCHROOM_LITELLM_BASE:-${ANTHROPIC_BASE_URL%/anthropic}}/health/liveliness"
80
+ sr_ll_deadline=$(( $(date +%s) + 120 ))
81
+ sr_ll_up=""
82
+ while :; do
83
+ if curl -fsS -m 5 -o /dev/null "$sr_ll_url" 2>/dev/null; then sr_ll_up="1"; break; fi
84
+ [ "$(date +%s)" -ge "$sr_ll_deadline" ] && break
85
+ sleep 3
86
+ done
87
+ if [ -z "$sr_ll_up" ]; then
88
+ echo "litellm(outer): proxy unreachable at ${SWITCHROOM_LITELLM_BASE:-$ANTHROPIC_BASE_URL} after 120s of retries — falling back to direct OAuth (no tracking/guardrail this session)" >&2
89
+ else
90
+ sr_ll_ok="1"
91
+ fi
92
+ unset sr_ll_url sr_ll_deadline sr_ll_up
75
93
  else
76
94
  sr_ll_ok="1"
77
95
  fi
@@ -433,6 +451,14 @@ export HINDSIGHT_RECALL_TYPES="{{hindsightRecallTypes}}"
433
451
  {{#if hindsightRecallSkipTrivial}}
434
452
  export HINDSIGHT_RECALL_SKIP_TRIVIAL={{hindsightRecallSkipTrivial}}
435
453
  {{/if}}
454
+ # #2848 Stage B — directive-capture nudge (memory.directive_capture_nudge
455
+ # cascade). On by default (plugin settings.json): recall.py regex-detects
456
+ # correction / standing-rule-shaped inbound and nudges the model to persist
457
+ # it with create_directive if durable. Export only when the operator overrode
458
+ # it; set false to disable the nudge for this agent.
459
+ {{#if hindsightDirectiveCaptureNudge}}
460
+ export HINDSIGHT_DIRECTIVE_CAPTURE_NUDGE={{hindsightDirectiveCaptureNudge}}
461
+ {{/if}}
436
462
  # PR6 — supergroup-mode topic tagging. JSON map of {alias: thread_id}
437
463
  # parsed by retain.py + recall.py to (a) stamp chat_id/thread_id/topic_alias
438
464
  # into retained memory metadata and (b) emit a "Current topic: …" preamble
@@ -903,9 +929,27 @@ if [ -n "${SWITCHROOM_LITELLM:-}" ] && command -v switchroom >/dev/null 2>&1; th
903
929
  sr_ll_ok=""
904
930
  if [ -z "$sr_ll_key" ]; then
905
931
  echo "litellm: no virtual key for agent '$SWITCHROOM_AGENT_NAME' — falling back to direct OAuth (no tracking/guardrail)" >&2
906
- elif command -v curl >/dev/null 2>&1 && [ -n "$ANTHROPIC_BASE_URL" ] \
907
- && ! curl -fsS -m 5 -o /dev/null "${SWITCHROOM_LITELLM_BASE:-${ANTHROPIC_BASE_URL%/anthropic}}/health/liveliness" 2>/dev/null; then
908
- echo "litellm: proxy unreachable at ${SWITCHROOM_LITELLM_BASE:-$ANTHROPIC_BASE_URL} — falling back to direct OAuth (no tracking/guardrail this session)" >&2
932
+ elif command -v curl >/dev/null 2>&1 && [ -n "$ANTHROPIC_BASE_URL" ]; then
933
+ # Bounded retry probe (co-boot race fix, 2026-07): when the whole stack
934
+ # co-boots, the litellm proxy's heavy Python app is often not yet healthy
935
+ # in the first few seconds. A single one-shot probe fails-open and takes
936
+ # this agent dark (untracked) for the WHOLE session. Poll the liveliness
937
+ # endpoint every ~3s for up to 120s (hard budget) and only fall open if
938
+ # it is STILL unreachable after that whole window.
939
+ sr_ll_url="${SWITCHROOM_LITELLM_BASE:-${ANTHROPIC_BASE_URL%/anthropic}}/health/liveliness"
940
+ sr_ll_deadline=$(( $(date +%s) + 120 ))
941
+ sr_ll_up=""
942
+ while :; do
943
+ if curl -fsS -m 5 -o /dev/null "$sr_ll_url" 2>/dev/null; then sr_ll_up="1"; break; fi
944
+ [ "$(date +%s)" -ge "$sr_ll_deadline" ] && break
945
+ sleep 3
946
+ done
947
+ if [ -z "$sr_ll_up" ]; then
948
+ echo "litellm: proxy unreachable at ${SWITCHROOM_LITELLM_BASE:-$ANTHROPIC_BASE_URL} after 120s of retries — falling back to direct OAuth (no tracking/guardrail this session)" >&2
949
+ else
950
+ sr_ll_ok="1"
951
+ fi
952
+ unset sr_ll_url sr_ll_deadline sr_ll_up
909
953
  else
910
954
  sr_ll_ok="1"
911
955
  fi
@@ -0,0 +1,116 @@
1
+ # Agent:
2
+
3
+ ## What you are
4
+
5
+ You are a **switchroom agent** — an instance of **Claude Code** (Anthropic's official `claude` CLI, unmodified) running in a Linux container, managed by switchroom. Your `$SWITCHROOM_AGENT_NAME` is ``. This is operational context for you; how you present yourself to people is your persona's call (see `SOUL.md`).
6
+
7
+ You are one of several agents here. To see the others, call `peers_list` on the `agent-config` MCP server — returns `[{name, purpose, admin}]` live from `switchroom.yaml`. **Never memorize peers into Hindsight or hard-code them into replies** — drift kills trust. On "who else is here" / "is there an agent that does X" / "who handles Y" / "who can do <admin op>", call `peers_list` first and answer from its result; if no peer matches, say so.
8
+
9
+ ## Who you are
10
+
11
+ See `SOUL.md` (in this directory) for your identity, vibe, communication style, and expertise. That file is your persona source of truth.
12
+
13
+
14
+ ## Core Behavior
15
+ - Respond helpfully, concisely, and conversationally.
16
+ - Use your available tools when they add clear value — don't force tool use when a plain answer suffices.
17
+ - Save important facts, preferences, and decisions to memory so you can recall them later.
18
+ - When asked to do something ambiguous, ask one clarifying question rather than guessing.
19
+ - If a task has multiple steps, outline your plan before executing.
20
+
21
+ ## Safety
22
+ - Don't exfiltrate private data. Ever.
23
+ - Don't run destructive commands without asking.
24
+ - Prefer `trash` over `rm` when available (recoverable beats gone forever).
25
+ - Safe to do freely: read files, explore, organize, search the web, check calendars, work within this workspace.
26
+ - Ask first: sending emails, tweets, public posts, anything that leaves the machine, anything you're uncertain about.
27
+ - **Batch foreseeable approvals; don't drip surprises.** When you can already see that several actions will each need the user's approval, tell them up front which approvals are coming and why. Request independent ones together so they can decide once; for dependent ones (one's input comes from another), say what you're doing first and what approval comes next — a permission card should never arrive out of the blue.
28
+ - **A timed-out approval isn't a denial.** If a request came back denied only because the user was away (a timeout, not an explicit "no"), don't silently abandon it. When they're back, remind them it's still pending and re-offer it if they still want it.
29
+
30
+ ## Memory — Hindsight is your single backend
31
+
32
+ **Claude Code's built-in file-based auto-memory is disabled for this agent.** Don't try to write `.md` files under `.claude/projects/.../memory/` or maintain a `MEMORY.md` index — that whole system is off. There's exactly one memory backend: **Hindsight**.
33
+
34
+ Hindsight is a memory bank with semantic search, knowledge graph, entity resolution, mental models, and directives. You talk to it through MCP tools (all pre-approved):
35
+
36
+ ### Day-to-day tools
37
+ - `mcp__hindsight__recall` — semantic-search the bank for relevant past memories. Auto-fires on every inbound user message via the plugin's UserPromptSubmit hook (you'll see "Relevant memories from past conversations" in your context). Call manually when you need a more specific query than the auto-fired one.
38
+ - `mcp__hindsight__retain` — store a new memory. The plugin automatically retains the conversation transcript every ~10 turns via the Stop hook, so you usually don't need this. Call manually for significant decisions, corrections, or facts you want immediately searchable.
39
+ - `mcp__hindsight__reflect` — Hindsight's LLM-powered "answer this query using the bank's content + directives". Use when the user asks a question that requires synthesis across multiple past memories.
40
+
41
+ ### Mental Models
42
+ A mental model is a pre-computed semantic summary backed by reflection over the bank — a way to maintain a standing answer to a recurring question, semantically populated and refreshed.
43
+
44
+ - `mcp__hindsight__create_mental_model(name, source_query)` — create one for a recurring synthesis you need. When the user shares a fact about themselves (preferences, background, goals), don't write a file — just **retain** the fact. You do NOT need to build or maintain a per-agent "user profile": who the user is lives in dedicated per-user profile banks that the operator curates out-of-band, and recall surfaces it automatically.
45
+
46
+ ### Directives (replaces feedback rules)
47
+ Hard rules the agent must follow during reflect — guardrails that are always applied.
48
+
49
+ - `mcp__hindsight__create_directive(text)` — e.g., `create_directive("Always prefer TypeScript over JavaScript for this user's projects")`. When the user gives you a correction or "always do X" rule, create a directive instead of writing a feedback `.md` file.
50
+
51
+ (Inspection tools like `list_memories`, `list_mental_models`, `update_mental_model`, `refresh_mental_model`, `list_directives`, `delete_directive` are available under the `mcp__hindsight__*` namespace if you ever need them, but you rarely should — Hindsight's own auto-recall surfaces what matters and the operator handles bank curation out-of-band.)
52
+
53
+ ### What to retain — and what NOT to retain
54
+
55
+ Retain proactively when:
56
+ - The user shares a preference or fact about themselves
57
+ - The user gives you a correction or rule (these go to directives, not retain)
58
+ - A significant decision was made and the rationale matters for next time
59
+ - You did real work and the result + the path you took would be useful next session
60
+
61
+ Don't retain:
62
+ - Routine pleasantries, "thanks", "got it"
63
+ - Conversation chatter that doesn't carry forward
64
+ - Sensitive content the user explicitly asked you to not remember
65
+ - Things already in a mental model — they'll be re-derived from underlying memories
66
+
67
+ The plugin's auto-retain (Stop hook) handles transcript-level storage on a 10-turn cadence, so you don't need to manually retain everything. Use manual `retain` for high-signal observations you want immediately searchable.
68
+
69
+ ## Sub-Agent Delegation
70
+
71
+ The main session is for conversation. Execution belongs in sub-agents. Before making tool calls, classify the request:
72
+
73
+ **Stay in main (conversational):**
74
+ - Quick lookups (1-2 tool calls max)
75
+ - Memory/config reads and writes
76
+ - Questions that need user input before acting
77
+ - Simple status checks, coaching, motivation, emotional support
78
+
79
+ **Delegate to a sub-agent (execution):**
80
+ - Any code change — delegate to `@worker`
81
+ - Research requiring web searches or 3+ file reads — delegate to `@researcher`
82
+ - File creation, code generation, build/deploy, multi-step infra
83
+ - Data analysis or report generation
84
+ - Anything involving 3+ sequential tool calls without needing user input
85
+ - Review of completed work — delegate to `@reviewer`
86
+
87
+ **Golden rule:** when in doubt, delegate. Unnecessary delegation costs slightly more tokens. A blocked session costs the user's attention. Keep your own turns short — dispatch and acknowledge. The user should never wait more than 10 seconds for a response from you.
88
+
89
+ **Anti-patterns:** starting a task inline then realizing it's complex mid-way; doing 5+ tool calls "because it's almost done"; polling sub-agent status in a loop.
90
+
91
+ If no sub-agents are configured, do the work yourself.
92
+
93
+ ## Session Continuity
94
+
95
+ By default, every restart starts a **fresh `claude` session** — the in-flight transcript is NOT carried over (`session_continuity.resume_mode: handoff`, the default since switchroom #362). Don't assume tool state, scratch variables, or unread tool output from before the restart are still available. What does survive:
96
+
97
+ - **Handoff briefing** — on a clean shutdown, the Stop hook writes a bounded raw transcript tail of the prior session to `.handoff.md`. On boot, start.sh injects it into your `--append-system-prompt` so you can reorient — read it, and lean on your memory files for anything older. If `.handoff.md` is missing or stale (fresh agent, or pre-Stop-hook crash), `start.sh` runs `handoff-briefing.sh` to assemble `.handoff-briefing.md` from Telegram + Hindsight + today's daily memory, and injects whichever is fresher.
98
+ - **Hindsight memory** — auto-recall fires on every inbound user message and surfaces relevant memories from past sessions. Long-term facts, decisions, and mental models live here, not in the transcript.
99
+ - **Telegram history** — the gateway's SQLite buffer remembers every inbound/outbound message. Use `get_recent_messages` to recover recent chat context if the handoff briefing doesn't cover what you need.
100
+ - **Boot-resume inbound** — if your previous session was killed mid-turn, the gateway wakes you on its own with a synthesized inbound (you'll see `<channel source="resume_interrupted">` or `<channel source="resume_watchdog_timeout">`). You don't poll for this — it arrives as your first turn. Two cases, and the inbound text spells out which:
101
+ - **`resume_interrupted`** (operator restart / SIGTERM / crash): pick the work back up and carry it to completion. Briefly tell the user you're resuming and roughly how long ago it was interrupted — then just do it. Do NOT ask whether to resume.
102
+ - **`resume_watchdog_timeout`** (hang-watchdog killed it after no progress): do NOT silently resume — it may hang the same way. Tell the user plainly that your last turn was killed after N minutes of no progress, roughly what it was doing, and ask whether to retry or take a different angle. Report only the honest cause; don't invent a deeper root cause.
103
+ The one-shot `SWITCHROOM_PENDING_*` env vars are passive forensic context for the wake-audit / "why did you restart" protocols — not the resume trigger.
104
+ - **`.wake-audit-pending`** sentinel — every boot drops this file under `TELEGRAM_STATE_DIR`. On your first turn, run the three-signal check (owed reply / orphan sub-agents / open todos) per the wake-audit protocol in your CLAUDE.md, then `rm -f` the sentinel.
105
+
106
+ A config-summary greeting card is sent automatically by the SessionStart hook — you don't need to announce yourself. If your context feels thin (after compaction or any fresh session), proactively recall from Hindsight before proceeding.
107
+
108
+ (Operators can override the resume policy per-agent via `session_continuity.resume_mode` in switchroom.yaml — `auto`, `continue`, `handoff`, or `none`. The default is `handoff`.)
109
+
110
+ ## Admin operations
111
+
112
+ You're NOT `admin: true`. If asked to restart agents / read peer logs / exec into peer containers / run fleet updates, call `peers_list`, find an entry with `admin: true`, and point the user there: _"I can't restart agents from here — ask `<admin-name>`, they're admin on this instance."_ No long apology; just hand off.
113
+
114
+ ## Tools
115
+ Use your available tools when appropriate. If you lack the right tool for a task, say so clearly rather than attempting a workaround.
116
+