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.
- package/dist/agent-scheduler/index.js +38 -4
- package/dist/auth-broker/index.js +302 -203
- package/dist/cli/notion-write-pretool.mjs +35 -2
- package/dist/cli/switchroom.js +1178 -576
- package/dist/host-control/main.js +148 -14
- package/dist/vault/approvals/kernel-server.js +140 -55
- package/dist/vault/broker/server.js +142 -57
- package/package.json +1 -1
- package/profiles/_base/start.sh.hbs +50 -6
- package/profiles/default/CLAUDE.md +116 -0
- package/skills/mental-model-curator/SKILL.md +162 -0
- package/telegram-plugin/bridge/bridge.ts +80 -1
- package/telegram-plugin/bridge/ipc-client.ts +19 -0
- package/telegram-plugin/bridge/permission-ledger.ts +61 -0
- package/telegram-plugin/consolidation-legibility.ts +279 -0
- package/telegram-plugin/dist/bridge/bridge.js +85 -1
- package/telegram-plugin/dist/gateway/gateway.js +2565 -610
- package/telegram-plugin/dist/server.js +86 -2
- package/telegram-plugin/feed-heartbeat-climb.ts +206 -0
- package/telegram-plugin/gateway/activity-card-store.ts +293 -0
- package/telegram-plugin/gateway/gateway.ts +1376 -82
- package/telegram-plugin/gateway/inbound-spool.ts +22 -0
- package/telegram-plugin/gateway/mental-model-propose-card.ts +69 -0
- package/telegram-plugin/gateway/mental-model-propose-diff.ts +171 -0
- package/telegram-plugin/gateway/mental-model-propose-inbound-builders.ts +147 -0
- package/telegram-plugin/gateway/mental-model-propose-resolve.ts +201 -0
- package/telegram-plugin/gateway/missed-approvals-card.ts +161 -0
- package/telegram-plugin/gateway/missed-approvals-store.ts +167 -0
- package/telegram-plugin/gateway/permission-rearm.ts +115 -0
- package/telegram-plugin/gateway/scoped-grant-store.ts +89 -0
- package/telegram-plugin/memory-legibility.ts +217 -0
- package/telegram-plugin/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -0
- package/telegram-plugin/scoped-approval.ts +59 -0
- package/telegram-plugin/silent-end.ts +78 -0
- package/telegram-plugin/subagent-watcher.ts +60 -6
- package/telegram-plugin/tests/activity-card-store.test.ts +436 -0
- package/telegram-plugin/tests/activity-card-wiring.test.ts +88 -0
- package/telegram-plugin/tests/consolidation-legibility.test.ts +224 -0
- package/telegram-plugin/tests/emission-authority-facade.test.ts +25 -10
- package/telegram-plugin/tests/feed-heartbeat-liveness-open.test.ts +33 -9
- package/telegram-plugin/tests/gateway-boot-marker-clear.test.ts +3 -3
- package/telegram-plugin/tests/inbound-spool.test.ts +105 -0
- package/telegram-plugin/tests/memory-legibility.test.ts +216 -0
- package/telegram-plugin/tests/mental-model-propose-callback-gate.test.ts +67 -0
- package/telegram-plugin/tests/mental-model-propose-card.test.ts +56 -0
- package/telegram-plugin/tests/mental-model-propose-diff.test.ts +201 -0
- package/telegram-plugin/tests/mental-model-propose-inbound-builders.test.ts +68 -0
- package/telegram-plugin/tests/mental-model-propose-resolve.test.ts +157 -0
- package/telegram-plugin/tests/missed-approvals-card.test.ts +145 -0
- package/telegram-plugin/tests/missed-approvals-store.test.ts +147 -0
- package/telegram-plugin/tests/missed-approvals-wiring.test.ts +89 -0
- package/telegram-plugin/tests/permission-ledger.test.ts +166 -0
- package/telegram-plugin/tests/permission-no-repeat-wiring.test.ts +1 -1
- package/telegram-plugin/tests/permission-rearm-wiring.test.ts +175 -0
- package/telegram-plugin/tests/permission-rearm.test.ts +126 -0
- package/telegram-plugin/tests/scoped-grant-persist.test.ts +223 -0
- package/telegram-plugin/tests/silent-end-transport.test.ts +290 -0
- package/telegram-plugin/tests/silent-turn-climb-transport.test.ts +337 -0
- package/telegram-plugin/tests/subagent-watcher.test.ts +139 -0
- package/telegram-plugin/tests/worktree-watch-cwds.test.ts +103 -0
- package/telegram-plugin/uat/assertions.ts +88 -4
- package/telegram-plugin/uat/feed-matcher.test.ts +69 -0
- package/telegram-plugin/uat/scenarios/fuzz-liveness-climb-dm.test.ts +155 -0
- package/telegram-plugin/uat/scenarios/jtbd-directive-capture-nudge-dm.test.ts +185 -0
- package/telegram-plugin/uat/scenarios/jtbd-liveness-climb-channel.test.ts +192 -0
- package/telegram-plugin/uat/scenarios/jtbd-liveness-climb-dm.test.ts +220 -0
- package/telegram-plugin/uat/scenarios/jtbd-liveness-narration-channel.test.ts +137 -0
- package/telegram-plugin/uat/scenarios/jtbd-liveness-narration-dm.test.ts +148 -0
- package/telegram-plugin/uat/scenarios/jtbd-memory-legibility-channel.test.ts +66 -0
- package/telegram-plugin/uat/scenarios/jtbd-memory-legibility-dm.test.ts +61 -0
- package/telegram-plugin/uat/scenarios/silent-end-recovery-channel.test.ts +136 -0
- package/telegram-plugin/uat/scenarios/silent-end-recovery-dm.test.ts +24 -2
- package/telegram-plugin/worktree-watch-cwds.ts +60 -0
- package/vendor/hindsight-memory/hooks/hooks.json +9 -0
- package/vendor/hindsight-memory/scripts/__pycache__/directive_verify.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/__pycache__/drain_pending.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/__pycache__/recall.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/__pycache__/retain.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/__pycache__/session_end.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/directive_verify.py +445 -0
- package/vendor/hindsight-memory/scripts/lib/__pycache__/__init__.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/lib/__pycache__/bank.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/lib/__pycache__/client.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/lib/__pycache__/config.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/lib/__pycache__/content.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/lib/__pycache__/daemon.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/lib/__pycache__/directives.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/lib/__pycache__/gateway_ipc.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/lib/__pycache__/llm.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/lib/__pycache__/pending.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/lib/__pycache__/state.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/lib/__pycache__/switchroom_envelope.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/lib/config.py +37 -0
- package/vendor/hindsight-memory/scripts/lib/directives.py +88 -0
- package/vendor/hindsight-memory/scripts/lib/switchroom_envelope.py +77 -0
- package/vendor/hindsight-memory/scripts/recall.py +153 -4
- package/vendor/hindsight-memory/scripts/retain.py +17 -0
- package/vendor/hindsight-memory/scripts/setup_hooks.py +9 -0
- package/vendor/hindsight-memory/scripts/tests/__pycache__/__init__.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/tests/__pycache__/test_config_client_casts.cpython-313-pytest-9.1.1.pyc +0 -0
- package/vendor/hindsight-memory/scripts/tests/__pycache__/test_config_client_casts.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/tests/__pycache__/test_directive_capture_nudge.cpython-313-pytest-9.1.1.pyc +0 -0
- package/vendor/hindsight-memory/scripts/tests/__pycache__/test_directive_capture_nudge.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/tests/__pycache__/test_directive_verify.cpython-313-pytest-9.1.1.pyc +0 -0
- package/vendor/hindsight-memory/scripts/tests/__pycache__/test_directive_verify.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/tests/__pycache__/test_directives.cpython-313-pytest-9.1.1.pyc +0 -0
- package/vendor/hindsight-memory/scripts/tests/__pycache__/test_directives.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/tests/__pycache__/test_gateway_ipc.cpython-313-pytest-9.1.1.pyc +0 -0
- package/vendor/hindsight-memory/scripts/tests/__pycache__/test_gateway_ipc.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/tests/__pycache__/test_recall_context_slice.cpython-313-pytest-9.1.1.pyc +0 -0
- package/vendor/hindsight-memory/scripts/tests/__pycache__/test_recall_context_slice.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/tests/__pycache__/test_recall_integration.cpython-313-pytest-9.1.1.pyc +0 -0
- package/vendor/hindsight-memory/scripts/tests/__pycache__/test_recall_integration.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/tests/__pycache__/test_recall_tag_filters.cpython-313-pytest-9.1.1.pyc +0 -0
- package/vendor/hindsight-memory/scripts/tests/__pycache__/test_recall_tag_filters.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/tests/__pycache__/test_recall_topic_filter.cpython-313-pytest-9.1.1.pyc +0 -0
- package/vendor/hindsight-memory/scripts/tests/__pycache__/test_recall_topic_filter.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/tests/__pycache__/test_recall_trivial_skip.cpython-313-pytest-9.1.1.pyc +0 -0
- package/vendor/hindsight-memory/scripts/tests/__pycache__/test_recall_trivial_skip.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/tests/__pycache__/test_retain_window.cpython-313-pytest-9.1.1.pyc +0 -0
- package/vendor/hindsight-memory/scripts/tests/__pycache__/test_retain_window.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/tests/__pycache__/test_sender_routing.cpython-313-pytest-9.1.1.pyc +0 -0
- package/vendor/hindsight-memory/scripts/tests/__pycache__/test_sender_routing.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/tests/__pycache__/test_switchroom_envelope.cpython-313-pytest-9.1.1.pyc +0 -0
- package/vendor/hindsight-memory/scripts/tests/test_directive_capture_nudge.py +185 -0
- package/vendor/hindsight-memory/scripts/tests/test_directive_verify.py +516 -0
- package/vendor/hindsight-memory/scripts/tests/test_directives.py +49 -0
- package/vendor/hindsight-memory/scripts/tests/test_retain_window.py +66 -1
- package/vendor/hindsight-memory/scripts/tests/test_switchroom_envelope.py +69 -0
- package/vendor/hindsight-memory/tests/__pycache__/conftest.cpython-313-pytest-9.0.3.pyc +0 -0
- package/vendor/hindsight-memory/tests/__pycache__/conftest.cpython-313-pytest-9.1.1.pyc +0 -0
- package/vendor/hindsight-memory/tests/__pycache__/test_bank.cpython-313-pytest-9.1.1.pyc +0 -0
- package/vendor/hindsight-memory/tests/__pycache__/test_bank.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/tests/__pycache__/test_client.cpython-313-pytest-9.1.1.pyc +0 -0
- package/vendor/hindsight-memory/tests/__pycache__/test_client.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/tests/__pycache__/test_config.cpython-313-pytest-9.0.3.pyc +0 -0
- package/vendor/hindsight-memory/tests/__pycache__/test_config.cpython-313-pytest-9.1.1.pyc +0 -0
- package/vendor/hindsight-memory/tests/__pycache__/test_config.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/tests/__pycache__/test_content.cpython-313-pytest-9.1.1.pyc +0 -0
- package/vendor/hindsight-memory/tests/__pycache__/test_content.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/tests/__pycache__/test_drain_pending.cpython-313-pytest-9.1.1.pyc +0 -0
- package/vendor/hindsight-memory/tests/__pycache__/test_drain_pending.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/tests/__pycache__/test_hooks.cpython-313-pytest-9.1.1.pyc +0 -0
- package/vendor/hindsight-memory/tests/__pycache__/test_hooks.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/tests/__pycache__/test_manifest.cpython-313-pytest-9.1.1.pyc +0 -0
- package/vendor/hindsight-memory/tests/__pycache__/test_manifest.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/tests/__pycache__/test_pending.cpython-313-pytest-9.1.1.pyc +0 -0
- package/vendor/hindsight-memory/tests/__pycache__/test_pending.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/tests/__pycache__/test_recall_exit_codes.cpython-313-pytest-9.1.1.pyc +0 -0
- package/vendor/hindsight-memory/tests/__pycache__/test_recall_exit_codes.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/tests/__pycache__/test_session_end_pending.cpython-313-pytest-9.1.1.pyc +0 -0
- package/vendor/hindsight-memory/tests/__pycache__/test_session_end_pending.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/tests/__pycache__/test_state.cpython-313-pytest-9.1.1.pyc +0 -0
- package/vendor/hindsight-memory/tests/__pycache__/test_state.cpython-313.pyc +0 -0
- 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
|
-
|
|
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
|
|
12003
|
-
var
|
|
12004
|
-
|
|
12005
|
-
|
|
12006
|
-
|
|
12007
|
-
|
|
12008
|
-
|
|
12009
|
-
|
|
12010
|
-
|
|
12011
|
-
|
|
12012
|
-
|
|
12013
|
-
|
|
12014
|
-
|
|
12015
|
-
|
|
12016
|
-
|
|
12017
|
-
|
|
12018
|
-
|
|
12019
|
-
|
|
12020
|
-
|
|
12021
|
-
|
|
12022
|
-
|
|
12023
|
-
|
|
12024
|
-
|
|
12025
|
-
|
|
12026
|
-
|
|
12027
|
-
|
|
12028
|
-
|
|
12029
|
-
|
|
12030
|
-
|
|
12031
|
-
|
|
12032
|
-
|
|
12033
|
-
|
|
12034
|
-
|
|
12035
|
-
|
|
12036
|
-
|
|
12037
|
-
|
|
12038
|
-
|
|
12039
|
-
|
|
12040
|
-
|
|
12041
|
-
|
|
12042
|
-
|
|
12043
|
-
|
|
12044
|
-
|
|
12045
|
-
|
|
12046
|
-
|
|
12047
|
-
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
|
16793
|
-
if (
|
|
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.
|
|
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
|
-
|
|
74
|
-
|
|
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
|
-
|
|
908
|
-
|
|
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
|
+
|