switchroom 0.17.10 → 0.18.6

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 (149) hide show
  1. package/bin/workspace-dynamic-hook.sh +12 -13
  2. package/dist/agent-scheduler/index.js +29 -2
  3. package/dist/auth-broker/index.js +6163 -152
  4. package/dist/cli/notion-write-pretool.mjs +31 -3
  5. package/dist/cli/switchroom.js +695 -526
  6. package/dist/host-control/main.js +6184 -173
  7. package/dist/vault/approvals/kernel-server.js +5893 -165
  8. package/dist/vault/broker/server.js +6666 -921
  9. package/package.json +1 -1
  10. package/profiles/_base/settings.json.hbs +2 -2
  11. package/profiles/_base/start.sh.hbs +170 -21
  12. package/profiles/coding/CLAUDE.md.hbs +1 -1
  13. package/profiles/default/CLAUDE.md.hbs +2 -2
  14. package/profiles/executive-assistant/CLAUDE.md.hbs +1 -1
  15. package/profiles/health-coach/CLAUDE.md.hbs +1 -1
  16. package/skills/switchroom-release/SKILL.md +78 -0
  17. package/telegram-plugin/auth-snapshot-format.ts +37 -25
  18. package/telegram-plugin/context-exhaustion.ts +124 -0
  19. package/telegram-plugin/dist/gateway/gateway.js +25025 -9203
  20. package/telegram-plugin/gateway/activity-card-store.ts +76 -0
  21. package/telegram-plugin/gateway/gateway.ts +740 -106
  22. package/telegram-plugin/gateway/inbound-delivery-gate.ts +26 -0
  23. package/telegram-plugin/gateway/model-command.ts +70 -10
  24. package/telegram-plugin/gateway/resolve-person.ts +304 -0
  25. package/telegram-plugin/gateway/unhandled-rejection-policy.ts +21 -1
  26. package/telegram-plugin/hooks/silent-end-scan.mjs +164 -40
  27. package/telegram-plugin/operator-events.ts +21 -0
  28. package/telegram-plugin/package.json +6 -0
  29. package/telegram-plugin/pending-work-progress.ts +42 -7
  30. package/telegram-plugin/quota-bar-format.ts +360 -0
  31. package/telegram-plugin/quota-watch.ts +4 -6
  32. package/telegram-plugin/registry/turns-schema.test.ts +97 -0
  33. package/telegram-plugin/registry/turns-schema.ts +78 -0
  34. package/telegram-plugin/render/ir.ts +209 -0
  35. package/telegram-plugin/render/parse.ts +363 -0
  36. package/telegram-plugin/render/render.ts +440 -0
  37. package/telegram-plugin/render/rich-render.ts +72 -0
  38. package/telegram-plugin/stream-controller.ts +14 -3
  39. package/telegram-plugin/subagent-watcher.ts +27 -9
  40. package/telegram-plugin/tests/activity-card-store.test.ts +94 -0
  41. package/telegram-plugin/tests/auth-command-format2.test.ts +1 -1
  42. package/telegram-plugin/tests/auth-snapshot-format.test.ts +51 -16
  43. package/telegram-plugin/tests/claude-code-event-contract.test.ts +48 -0
  44. package/telegram-plugin/tests/feed-heartbeat-liveness-open.test.ts +11 -0
  45. package/telegram-plugin/tests/feed-survival.test.ts +39 -0
  46. package/telegram-plugin/tests/gateway-boot-marker-clear.test.ts +3 -3
  47. package/telegram-plugin/tests/gateway-session-model-relaunch.test.ts +81 -0
  48. package/telegram-plugin/tests/inbound-emit-after-intercepts.test.ts +82 -0
  49. package/telegram-plugin/tests/liveness-tracker.test.ts +228 -0
  50. package/telegram-plugin/tests/model-command.test.ts +193 -16
  51. package/telegram-plugin/tests/narrative-render.test.ts +125 -0
  52. package/telegram-plugin/tests/operator-events.test.ts +16 -0
  53. package/telegram-plugin/tests/orphaned-reply-rearm.test.ts +123 -163
  54. package/telegram-plugin/tests/pending-work-progress.test.ts +116 -3
  55. package/telegram-plugin/tests/quota-bar-format.test.ts +444 -0
  56. package/telegram-plugin/tests/quota-watch.test.ts +1 -4
  57. package/telegram-plugin/tests/rapid-fire-delivery-ordering.test.ts +149 -0
  58. package/telegram-plugin/tests/render/parse-torture.test.ts +136 -0
  59. package/telegram-plugin/tests/render/parse.test.ts +393 -0
  60. package/telegram-plugin/tests/render/render.test.ts +436 -0
  61. package/telegram-plugin/tests/render/rich-render.test.ts +85 -0
  62. package/telegram-plugin/tests/resolve-person.test.ts +290 -0
  63. package/telegram-plugin/tests/silent-end-interrupt-stop-integration.test.ts +53 -0
  64. package/telegram-plugin/tests/silent-end-interrupt-stop-scan.test.ts +138 -0
  65. package/telegram-plugin/tests/subagent-watcher.test.ts +61 -0
  66. package/telegram-plugin/tests/telegram-activity-visibility-integration.test.ts +155 -1
  67. package/telegram-plugin/tests/unhandled-rejection-policy.test.ts +19 -0
  68. package/telegram-plugin/tests/worker-activity-feed.test.ts +97 -0
  69. package/telegram-plugin/tests/worktree-watch-cwds.test.ts +98 -3
  70. package/telegram-plugin/turn-liveness-floor.ts +35 -1
  71. package/telegram-plugin/uat/scenarios/jtbd-rich-formatting-render-dm.test.ts +99 -7
  72. package/telegram-plugin/worker-activity-feed.ts +220 -15
  73. package/telegram-plugin/worktree-watch-cwds.ts +92 -17
  74. package/vendor/hindsight-memory/scripts/lib/client.py +11 -1
  75. package/vendor/hindsight-memory/scripts/lib/config.py +9 -2
  76. package/vendor/hindsight-memory/scripts/recall.py +64 -6
  77. package/vendor/hindsight-memory/scripts/tests/test_recall_integration.py +1 -0
  78. package/vendor/hindsight-memory/tests/test_client.py +43 -0
  79. package/vendor/hindsight-memory/tests/test_recall_precision.py +114 -0
  80. package/profiles/default/CLAUDE.md +0 -116
  81. package/telegram-plugin/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +0 -1
  82. package/vendor/hindsight-memory/scripts/__pycache__/directive_verify.cpython-313.pyc +0 -0
  83. package/vendor/hindsight-memory/scripts/__pycache__/drain_pending.cpython-313.pyc +0 -0
  84. package/vendor/hindsight-memory/scripts/__pycache__/recall.cpython-313.pyc +0 -0
  85. package/vendor/hindsight-memory/scripts/__pycache__/retain.cpython-313.pyc +0 -0
  86. package/vendor/hindsight-memory/scripts/__pycache__/session_end.cpython-313.pyc +0 -0
  87. package/vendor/hindsight-memory/scripts/lib/__pycache__/__init__.cpython-313.pyc +0 -0
  88. package/vendor/hindsight-memory/scripts/lib/__pycache__/bank.cpython-313.pyc +0 -0
  89. package/vendor/hindsight-memory/scripts/lib/__pycache__/client.cpython-313.pyc +0 -0
  90. package/vendor/hindsight-memory/scripts/lib/__pycache__/config.cpython-313.pyc +0 -0
  91. package/vendor/hindsight-memory/scripts/lib/__pycache__/content.cpython-313.pyc +0 -0
  92. package/vendor/hindsight-memory/scripts/lib/__pycache__/daemon.cpython-313.pyc +0 -0
  93. package/vendor/hindsight-memory/scripts/lib/__pycache__/directives.cpython-313.pyc +0 -0
  94. package/vendor/hindsight-memory/scripts/lib/__pycache__/gateway_ipc.cpython-313.pyc +0 -0
  95. package/vendor/hindsight-memory/scripts/lib/__pycache__/llm.cpython-313.pyc +0 -0
  96. package/vendor/hindsight-memory/scripts/lib/__pycache__/pending.cpython-313.pyc +0 -0
  97. package/vendor/hindsight-memory/scripts/lib/__pycache__/state.cpython-313.pyc +0 -0
  98. package/vendor/hindsight-memory/scripts/lib/__pycache__/switchroom_envelope.cpython-313.pyc +0 -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/tests/__pycache__/conftest.cpython-313-pytest-9.0.3.pyc +0 -0
  126. package/vendor/hindsight-memory/tests/__pycache__/conftest.cpython-313-pytest-9.1.1.pyc +0 -0
  127. package/vendor/hindsight-memory/tests/__pycache__/test_bank.cpython-313-pytest-9.1.1.pyc +0 -0
  128. package/vendor/hindsight-memory/tests/__pycache__/test_bank.cpython-313.pyc +0 -0
  129. package/vendor/hindsight-memory/tests/__pycache__/test_client.cpython-313-pytest-9.1.1.pyc +0 -0
  130. package/vendor/hindsight-memory/tests/__pycache__/test_client.cpython-313.pyc +0 -0
  131. package/vendor/hindsight-memory/tests/__pycache__/test_config.cpython-313-pytest-9.0.3.pyc +0 -0
  132. package/vendor/hindsight-memory/tests/__pycache__/test_config.cpython-313-pytest-9.1.1.pyc +0 -0
  133. package/vendor/hindsight-memory/tests/__pycache__/test_config.cpython-313.pyc +0 -0
  134. package/vendor/hindsight-memory/tests/__pycache__/test_content.cpython-313-pytest-9.1.1.pyc +0 -0
  135. package/vendor/hindsight-memory/tests/__pycache__/test_content.cpython-313.pyc +0 -0
  136. package/vendor/hindsight-memory/tests/__pycache__/test_drain_pending.cpython-313-pytest-9.1.1.pyc +0 -0
  137. package/vendor/hindsight-memory/tests/__pycache__/test_drain_pending.cpython-313.pyc +0 -0
  138. package/vendor/hindsight-memory/tests/__pycache__/test_hooks.cpython-313-pytest-9.1.1.pyc +0 -0
  139. package/vendor/hindsight-memory/tests/__pycache__/test_hooks.cpython-313.pyc +0 -0
  140. package/vendor/hindsight-memory/tests/__pycache__/test_manifest.cpython-313-pytest-9.1.1.pyc +0 -0
  141. package/vendor/hindsight-memory/tests/__pycache__/test_manifest.cpython-313.pyc +0 -0
  142. package/vendor/hindsight-memory/tests/__pycache__/test_pending.cpython-313-pytest-9.1.1.pyc +0 -0
  143. package/vendor/hindsight-memory/tests/__pycache__/test_pending.cpython-313.pyc +0 -0
  144. package/vendor/hindsight-memory/tests/__pycache__/test_recall_exit_codes.cpython-313-pytest-9.1.1.pyc +0 -0
  145. package/vendor/hindsight-memory/tests/__pycache__/test_recall_exit_codes.cpython-313.pyc +0 -0
  146. package/vendor/hindsight-memory/tests/__pycache__/test_session_end_pending.cpython-313-pytest-9.1.1.pyc +0 -0
  147. package/vendor/hindsight-memory/tests/__pycache__/test_session_end_pending.cpython-313.pyc +0 -0
  148. package/vendor/hindsight-memory/tests/__pycache__/test_state.cpython-313-pytest-9.1.1.pyc +0 -0
  149. package/vendor/hindsight-memory/tests/__pycache__/test_state.cpython-313.pyc +0 -0
@@ -274,6 +274,49 @@ class TestHindsightClientRecallTagFilters:
274
274
  assert "tag_groups" not in captured["body"]
275
275
 
276
276
 
277
+ class TestHindsightClientPreferObservations:
278
+ """Switchroom Phase-1 — prefer_observations forwarded in the recall body."""
279
+
280
+ def test_forwards_prefer_observations_true(self):
281
+ c = HindsightClient("http://localhost:9077")
282
+ captured = {}
283
+
284
+ def fake_open(req, timeout=None):
285
+ captured["body"] = json.loads(req.data.decode())
286
+ return FakeResp({"results": []})
287
+
288
+ with patch("urllib.request.urlopen", side_effect=fake_open):
289
+ c.recall("bank", "query", prefer_observations=True)
290
+
291
+ assert captured["body"]["prefer_observations"] is True
292
+
293
+ def test_forwards_prefer_observations_false(self):
294
+ c = HindsightClient("http://localhost:9077")
295
+ captured = {}
296
+
297
+ def fake_open(req, timeout=None):
298
+ captured["body"] = json.loads(req.data.decode())
299
+ return FakeResp({"results": []})
300
+
301
+ with patch("urllib.request.urlopen", side_effect=fake_open):
302
+ c.recall("bank", "query", prefer_observations=False)
303
+
304
+ assert captured["body"]["prefer_observations"] is False
305
+
306
+ def test_omits_prefer_observations_when_unset(self):
307
+ c = HindsightClient("http://localhost:9077")
308
+ captured = {}
309
+
310
+ def fake_open(req, timeout=None):
311
+ captured["body"] = json.loads(req.data.decode())
312
+ return FakeResp({"results": []})
313
+
314
+ with patch("urllib.request.urlopen", side_effect=fake_open):
315
+ c.recall("bank", "query")
316
+
317
+ assert "prefer_observations" not in captured["body"]
318
+
319
+
277
320
  class TestRequestTimeoutOverride:
278
321
  """Upstream 55ef70679 — the constructor override replaces the per-call
279
322
  timeout that recall/retain/_request would otherwise use. When unset,
@@ -0,0 +1,114 @@
1
+ """Retrieval-precision measurement for the Switchroom Phase-1 memory changes.
2
+
3
+ Per the RFC's outcome-UAT rule (reference/rfcs/hindsight-memory-reimagined.md,
4
+ "Measurement"), the bank-starvation fix ships behind a check that compares
5
+ recalled-set relevance before/after the sort. These tests assert the two
6
+ load-bearing properties of the fix:
7
+
8
+ 1. The merged multi-bank result set is ordered by the engine's real
9
+ relevance score (`scores.final`) descending.
10
+ 2. A high-relevance memory from an ADDITIONAL bank is not starved by the
11
+ count cap when the own bank supplies enough lower-relevance hits to
12
+ fill it — the pre-fix bug (append-then-slice) would drop it.
13
+
14
+ `_sort_by_final_score` is the in-place sort applied just before the cap in
15
+ `process_recall`; the cap itself is a plain head-slice, reproduced here so the
16
+ before/after relevance of the capped set is directly comparable.
17
+ """
18
+
19
+ from recall import _result_final_score, _sort_by_final_score
20
+
21
+
22
+ def _mem(mem_id, final, bank):
23
+ """A recall result as the engine returns it: text + a scores object."""
24
+ return {
25
+ "id": mem_id,
26
+ "text": f"memory {mem_id} from {bank}",
27
+ "bank": bank,
28
+ "scores": {"final": final, "semantic": final, "keyword": final},
29
+ }
30
+
31
+
32
+ def _capped_relevance(results, cap):
33
+ """Mirror process_recall: head-slice at the cap, return kept scores."""
34
+ kept = results[:cap] if cap > 0 else results
35
+ return [_result_final_score(m) for m in kept]
36
+
37
+
38
+ class TestFinalScoreExtraction:
39
+ def test_reads_final_score(self):
40
+ assert _result_final_score(_mem("a", 0.9, "own")) == 0.9
41
+
42
+ def test_missing_scores_sorts_last(self):
43
+ assert _result_final_score({"id": "x", "text": "t"}) == float("-inf")
44
+
45
+ def test_null_scores_sorts_last(self):
46
+ assert _result_final_score({"id": "x", "scores": None}) == float("-inf")
47
+
48
+ def test_missing_final_sorts_last(self):
49
+ assert _result_final_score({"id": "x", "scores": {"semantic": 0.5}}) == float("-inf")
50
+
51
+ def test_bool_is_not_a_score(self):
52
+ # True is an int subclass; it must not be mistaken for a 1.0 score.
53
+ assert _result_final_score({"id": "x", "scores": {"final": True}}) == float("-inf")
54
+
55
+
56
+ class TestSortByFinalScore:
57
+ def test_orders_descending(self):
58
+ results = [_mem("a", 0.2, "own"), _mem("b", 0.9, "own"), _mem("c", 0.5, "own")]
59
+ _sort_by_final_score(results)
60
+ assert [m["id"] for m in results] == ["b", "c", "a"]
61
+
62
+ def test_stable_on_ties_preserves_own_bank_first(self):
63
+ # Own-bank appears before additional-bank in insertion order; on a
64
+ # score tie the stable sort must keep own-bank ahead.
65
+ results = [_mem("own1", 0.5, "own"), _mem("extra1", 0.5, "profile")]
66
+ _sort_by_final_score(results)
67
+ assert [m["id"] for m in results] == ["own1", "extra1"]
68
+
69
+ def test_scoreless_entries_sink_to_the_bottom(self):
70
+ results = [{"id": "noscore", "text": "t"}, _mem("scored", 0.1, "own")]
71
+ _sort_by_final_score(results)
72
+ assert results[0]["id"] == "scored"
73
+
74
+
75
+ class TestCrossBankStarvationRegression:
76
+ """The bug the fix targets: a relevant additional-bank memory dropped at
77
+ the cap because own-bank hits were appended first."""
78
+
79
+ def test_high_relevance_additional_bank_memory_survives_cap(self):
80
+ # Own bank fills the cap with mediocre hits; the profile bank has the
81
+ # single most relevant memory. Pre-fix (append own, then extra, then
82
+ # head-slice) the profile hit lands at index 2 and is sliced off.
83
+ cap = 2
84
+ own = [_mem("own_lo1", 0.30, "own"), _mem("own_lo2", 0.25, "own")]
85
+ extra = [_mem("profile_hi", 0.95, "profile")]
86
+ merged = own + extra # exactly the pre-fix append order
87
+
88
+ # Before: append-then-slice starves the profile bank.
89
+ pre_fix_ids = [m["id"] for m in merged[:cap]]
90
+ assert "profile_hi" not in pre_fix_ids
91
+
92
+ # After: sort by scores.final before the slice keeps the best memory.
93
+ _sort_by_final_score(merged)
94
+ post_fix_ids = [m["id"] for m in merged[:cap]]
95
+ assert "profile_hi" in post_fix_ids
96
+ assert post_fix_ids[0] == "profile_hi"
97
+
98
+ def test_capped_set_relevance_is_no_worse_after_sort(self):
99
+ # Measurement: summed relevance of the capped set must not decrease.
100
+ cap = 3
101
+ merged = [
102
+ _mem("own1", 0.4, "own"),
103
+ _mem("own2", 0.35, "own"),
104
+ _mem("own3", 0.3, "own"),
105
+ _mem("profile1", 0.9, "profile"),
106
+ _mem("shared1", 0.8, "shared"),
107
+ ]
108
+ before = sum(_capped_relevance(list(merged), cap))
109
+ _sort_by_final_score(merged)
110
+ after = sum(_capped_relevance(merged, cap))
111
+ assert after >= before
112
+ # And concretely, the two top additional-bank hits are now retained.
113
+ kept_ids = [m["id"] for m in merged[:cap]]
114
+ assert "profile1" in kept_ids and "shared1" in kept_ids
@@ -1,116 +0,0 @@
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
-
@@ -1 +0,0 @@
1
- {"version":"3.2.4","results":[[":tests/worker-activity-feed.test.ts",{"duration":604.6399409999995,"failed":true}]]}