switchroom 0.19.26 → 0.19.28
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/bin/git-agent-attribution-hook.sh +144 -0
- package/dist/agent-scheduler/index.js +60 -2
- package/dist/auth-broker/index.js +244 -13
- package/dist/cli/autoaccept-poll.js +225 -17
- package/dist/cli/notion-write-pretool.mjs +60 -2
- package/dist/cli/switchroom.js +2843 -1220
- package/dist/host-control/main.js +245 -14
- package/dist/vault/approvals/kernel-server.js +242 -13
- package/dist/vault/broker/server.js +242 -13
- package/package.json +7 -2
- package/profiles/_base/cron-session.sh.hbs +8 -0
- package/profiles/_base/start.sh.hbs +175 -15
- package/telegram-plugin/card-layout.ts +328 -0
- package/telegram-plugin/dist/bridge/bridge.js +94 -1
- package/telegram-plugin/dist/gateway/gateway.js +2544 -1182
- package/telegram-plugin/dist/server.js +97 -1
- package/telegram-plugin/edit-flood-fuse.ts +841 -57
- package/telegram-plugin/flood-429-ledger.ts +526 -0
- package/telegram-plugin/flood-circuit-breaker.ts +18 -0
- package/telegram-plugin/gateway/callback-query-handlers.ts +6 -0
- package/telegram-plugin/gateway/flood-reply-queue.ts +168 -0
- package/telegram-plugin/gateway/gateway.ts +67 -70
- package/telegram-plugin/gateway/mcp-failure-hook.ts +74 -0
- package/telegram-plugin/gateway/narrative-lane.ts +14 -0
- package/telegram-plugin/gateway/outbound-send-path.ts +36 -0
- package/telegram-plugin/gateway/outbox-sweep.ts +183 -6
- package/telegram-plugin/gateway/pinned-message-handler.ts +12 -16
- package/telegram-plugin/gateway/status-pin-retarget.ts +72 -36
- package/telegram-plugin/gateway/status-pin-store.ts +58 -9
- package/telegram-plugin/gateway/worker-pin-reaper.ts +56 -7
- package/telegram-plugin/inline-keyboard-callbacks.ts +202 -21
- package/telegram-plugin/llm-error-present.ts +61 -2
- package/telegram-plugin/mcp-credential-failure.ts +459 -0
- package/telegram-plugin/model-unavailable.ts +8 -0
- package/telegram-plugin/operator-events.ts +110 -5
- package/telegram-plugin/outbound-class.ts +81 -0
- package/telegram-plugin/provider-credit.ts +237 -0
- package/telegram-plugin/scripts/bun-test-ci.sh +36 -6
- package/telegram-plugin/send-gate.ts +24 -2
- package/telegram-plugin/status-no-truncate.ts +10 -48
- package/telegram-plugin/status-pin-driver.ts +33 -45
- package/telegram-plugin/status-pin.ts +18 -1
- package/telegram-plugin/tests/card-golden.test.ts +69 -0
- package/telegram-plugin/tests/card-lifecycle-render.test.ts +362 -0
- package/telegram-plugin/tests/card-type-distinguishability.test.ts +187 -164
- package/telegram-plugin/tests/card-variants.golden.txt +211 -0
- package/telegram-plugin/tests/card-variants.ts +366 -0
- package/telegram-plugin/tests/edit-flood-fuse-ban-awareness.test.ts +373 -0
- package/telegram-plugin/tests/edit-flood-fuse-default-deny.test.ts +319 -0
- package/telegram-plugin/tests/edit-flood-fuse-reply-reserve.test.ts +340 -0
- package/telegram-plugin/tests/edit-flood-fuse.test.ts +11 -2
- package/telegram-plugin/tests/feed-edit-rate-ceiling.test.ts +462 -0
- package/telegram-plugin/tests/finalize-callback-flood-policy.test.ts +298 -0
- package/telegram-plugin/tests/finalize-callback.test.ts +41 -8
- package/telegram-plugin/tests/fixtures/real-429-stream.ts +220 -0
- package/telegram-plugin/tests/flood-429-ledger.test.ts +278 -0
- package/telegram-plugin/tests/flood-429-recorder-wiring.test.ts +128 -0
- package/telegram-plugin/tests/flood-reply-queue.test.ts +418 -0
- package/telegram-plugin/tests/mcp-credential-failure.test.ts +310 -0
- package/telegram-plugin/tests/outbox-sweep-flood-breaker.test.ts +221 -0
- package/telegram-plugin/tests/pinned-card-collapse.test.ts +19 -24
- package/telegram-plugin/tests/pinned-message-handler.test.ts +15 -15
- package/telegram-plugin/tests/provider-credit-402.test.ts +243 -0
- package/telegram-plugin/tests/status-pin-api.test.ts +11 -11
- package/telegram-plugin/tests/status-pin-boot-recovery.test.ts +36 -37
- package/telegram-plugin/tests/status-pin-lifecycle.test.ts +602 -0
- package/telegram-plugin/tests/status-pin-retarget.test.ts +90 -62
- package/telegram-plugin/tests/status-pin-service-message-suppression.test.ts +7 -3
- package/telegram-plugin/tests/status-pin-store.test.ts +109 -60
- package/telegram-plugin/tests/status-pin.test.ts +56 -5
- package/telegram-plugin/tests/test-runner-coverage.test.ts +133 -0
- package/telegram-plugin/tests/worker-activity-feed.test.ts +12 -10
- package/telegram-plugin/tests/worker-feed-coalesce.test.ts +23 -29
- package/telegram-plugin/tests/worker-feed-pin-persistence.test.ts +56 -59
- package/telegram-plugin/tests/worker-feed-terminal-edit-class.test.ts +335 -0
- package/telegram-plugin/tests/worker-visibility-prose-silent-harness.test.ts +1 -1
- package/telegram-plugin/tool-activity-summary.ts +239 -365
- package/telegram-plugin/uat/assertions.ts +22 -11
- package/telegram-plugin/uat/feed-matcher.test.ts +24 -17
- package/telegram-plugin/worker-activity-feed.ts +105 -47
- package/vendor/hindsight-memory/CLAUDE.md +45 -0
- package/vendor/hindsight-memory/scripts/drain_pending.py +433 -11
- package/vendor/hindsight-memory/scripts/lib/config.py +33 -0
- package/vendor/hindsight-memory/scripts/lib/pending.py +193 -28
- package/vendor/hindsight-memory/scripts/recall.py +176 -7
- package/vendor/hindsight-memory/scripts/tests/test_config_recall_passthrough_env.py +170 -0
- package/vendor/hindsight-memory/scripts/tests/test_drain_circuit_breaker.py +401 -0
- package/vendor/hindsight-memory/scripts/tests/test_drain_serialisation.py +286 -0
- package/vendor/hindsight-memory/scripts/tests/test_pending_drops.py +817 -8
- package/vendor/hindsight-memory/scripts/tests/test_recall_min_score.py +464 -0
- package/vendor/hindsight-memory/settings.json +1 -1
- package/vendor/hindsight-memory/tests/test_hooks.py +11 -2
|
@@ -16756,10 +16756,37 @@ var AgentMemorySchema = exports_external.object({
|
|
|
16756
16756
|
request_timeout_seconds: exports_external.number().int().min(1).optional().describe("Per-bank HTTP read timeout, in seconds, for one recall " + "request. Even parallelised, each bank carries its own deadline " + "so ONE hung bank returns empty instead of consuming the shared " + "deadline and starving its siblings. The plugin default is 12 " + "(raised from a hardcoded 8 in #3757, which fired on 96.8% of " + "one agent's own-bank recalls). Switchroom defaults it to the " + "effective `parallel_deadline_seconds` instead — 10 at the " + "shipped ceiling — because the shared fan-out deadline is " + "already the tighter outer guard, so a per-bank value above it " + "can never bind. An explicitly configured value above the " + "effective deadline is clamped down to it, and the clamp is " + "reported."),
|
|
16757
16757
|
own_bank_min_slots: exports_external.number().int().min(0).optional().describe("Slots inside `max_memories` reserved as a FLOOR for the agent's " + "own bank when recall fans out to more than one bank. The merged " + "set is sorted globally by relevance and head-sliced, which is " + "winner-take-all across banks: when both banks return more " + "candidates than the cap, one bank's score distribution can fill " + "every slot and the agent gets a dossier about its operator with " + "none of its own session memory. A floor, not a quota: at most " + "this many slots, only if the own bank returned that many, and " + "only up to HALF the cap shared with `additional_bank_min_slots` " + "— the rest is always won on pure relevance, so composition still " + "moves with the scores. Fixes score-based crowd-out only; a " + "timed-out bank returns no candidates and reservation is a no-op " + "there. 0 disables (default). Switchroom-managed agents use 2 " + "against the fleet-deployed cap of 6."),
|
|
16758
16758
|
additional_bank_min_slots: exports_external.number().int().min(0).optional().describe("Slots inside `max_memories` reserved as a FLOOR for the " + "additional (profile / shared / sender) banks. Symmetric with " + "`own_bank_min_slots` — same floor-not-quota semantics, and the " + "two share the same half-of-cap reservation budget. When they sum " + "above that budget the own-bank floor is honoured first. 0 " + "disables (default). Switchroom-managed agents use 1 against the " + "fleet-deployed cap of 6. Observe `injected_own_bank_count` / " + "`injected_additional_bank_count` via " + "`switchroom memory recall-log`."),
|
|
16759
|
+
min_score: exports_external.number().min(0).optional().describe("Absolute floor on a memory's engine relevance score " + "(`scores.final`) for it to be injected. 0 disables (default, " + "and the shipped fleet behaviour). Exists for one measured " + "failure: when the agent's own bank times out, recall still " + "injects side-bank residue under the banner 'Relevant memories " + "from past conversations' — 98.4% of degraded turns have a best " + "injected score below 0.01, against 28.4% of healthy ones. Six " + "noise memories are worse than none, because the agent cannot " + "tell them apart. Below-floor results are dropped BEFORE " + "rendering, and when the floor empties the set the turn says so " + "rather than going silent. Do NOT read this as a general " + "precision control: `scores.final` is not calibrated across " + "queries, and #3761 measured that an unconditional 0.01 floor " + "empties ~28% of HEALTHY recalls — which is why " + "`min_score_scope` defaults to degraded turns only. Observe " + "`dropped_below_min_score` via `switchroom memory recall-log`."),
|
|
16760
|
+
min_score_scope: exports_external.enum(["degraded", "all"]).optional().describe('Which turns `min_score` binds on. "degraded" (default) — only ' + "turns where the agent's OWN bank timed out or was unreachable, " + "the population where a below-floor score actually predicts " + "noise and where the agent already receives the degraded-recall " + 'disclosure. "all" — every turn; only for an operator who has ' + "measured their own bank's score distribution, since it " + "re-creates the empty-recall failure of #3541 at any floor " + "calibrated on degraded data. No effect while `min_score` is 0."),
|
|
16759
16761
|
types: exports_external.array(exports_external.string()).optional().describe("Hindsight fact types to recall. Switchroom default is " + '["world", "experience", "observation"] — the synthesized ' + "`observation` tier is on by default. Set to " + '["world", "experience"] to opt out of observation-backed ' + "recall for this agent (or fleet-wide under defaults)."),
|
|
16760
16762
|
additional_banks: exports_external.array(exports_external.string()).optional().describe("Extra Hindsight banks to recall from on every turn, merged into " + "the agent's own bank results — e.g. a shared operator/household " + "profile bank authored via `switchroom memory profile`. Each is " + "recalled with the `request_timeout_seconds` per-bank timeout " + "(defaults to the effective `parallel_deadline_seconds`, 10s at " + "the shipped ceiling) and is non-fatal on failure. Stays " + "within the single tenant: all banks are the operator's data, in " + "the operator's Hindsight instance (see the `single-tenant` " + "invariant). Defaults to [] (no extra banks)."),
|
|
16761
16763
|
sender_banks: exports_external.record(exports_external.string(), exports_external.string()).optional().describe("Per-speaker recall routing: a map of Telegram sender → extra " + "recall bank. When a message arrives, the agent also recalls the " + "speaker's bank (matched by Telegram username — a leading @ is " + "optional — or numeric user_id), merged " + "into its own results — so each trusted user gets their own " + "profile context. Additive recall scoping within the single " + "tenant: never an access boundary (who may drive an agent stays " + "the per-agent user assignment in `access.allowFrom`). Author the " + "banks via `switchroom memory profile`."),
|
|
16762
16764
|
skip_trivial: exports_external.boolean().optional().describe("Skip recall on plausibly-stateless trivial turns (time/date/" + "greeting). Switchroom default true — saves the recall arm + " + "injected tokens on turns that never need memory, guarded so it " + "never skips a turn that references user/project/session state. " + "Set false to always run recall."),
|
|
16765
|
+
budget: exports_external.enum(["low", "mid", "high"]).optional().describe('How hard Hindsight searches. "low" (switchroom default) = ' + 'vector retrieval only, ~1-2s. "mid" adds the LLM rerank pass ' + "and measured ~5s of hook latency on real fleet turns — the " + "second-largest contributor to perceived dead air after model " + 'TTFT. "high" is thorough and slower still. Raise it for an ' + "agent whose recall quality matters more than its reply latency " + "(a research or audit role); leave it at low for chat."),
|
|
16766
|
+
max_tokens: exports_external.number().int().min(1).optional().describe("Token budget for the injected memory block. Default 1024. This " + "is the TOKEN bound; `max_memories` is the separate COUNT bound " + "and the tighter of the two wins. Raise it only alongside " + "`max_memories` — on its own it buys nothing once the count cap " + "binds."),
|
|
16767
|
+
prefer_observations: exports_external.boolean().optional().describe("Bias recall toward the synthesized `observation` tier, " + "backfilling the slots freed by superseded raw facts for denser " + "coverage inside the same budget. Default true. Set false to " + "rank raw `world`/`experience` facts on equal footing — useful " + "when auditing what the consolidation engine actually stored, or " + "if a bank's observations are stale."),
|
|
16768
|
+
context_turns: exports_external.number().int().min(1).optional().describe("How many recent human turns are composed into the recall query. " + 'Default 2, so a bare follow-up ("and the port?") embeds with ' + "its antecedent instead of recalling on the pronoun alone. 1 = " + "the latest turn only. Raising it costs BM25 terms, which is the " + "real recall cost driver — `query_max_tokens` still bounds the " + "result, so a large value mostly shifts which terms survive."),
|
|
16769
|
+
roles: exports_external.array(exports_external.string().min(1)).min(1).optional().describe("Transcript roles the multi-turn composition may draw from. " + 'Default ["user", "assistant"]. Set ["user"] to compose ' + "the query from the human's words only — worth trying when an " + "agent's own verbose replies are dominating the query terms. No " + "effect while `context_turns` is 1."),
|
|
16770
|
+
prompt_preamble: exports_external.string().min(1).optional().describe("The banner rendered above injected memories. The agent reads " + "this line as the instruction for how to treat the block, so it " + "is a behaviour knob, not cosmetics. Default tells the model to " + "prioritise recent memories on conflict and ignore irrelevant " + "ones. Override to tighten that framing for a specialised agent."),
|
|
16771
|
+
tags: exports_external.array(exports_external.string().min(1)).optional().describe("Restrict recall to memories carrying these tags. Default [] = " + "no filter (match everything). This is a HARD filter applied " + "server-side — a memory without the tags cannot surface at any " + "score — so it is for a genuinely scoped agent, not for " + "ranking. Use `tag_weights` when you want a preference rather " + "than an exclusion."),
|
|
16772
|
+
tags_match: exports_external.enum(["any", "all", "any_strict", "all_strict"]).optional().describe('How `tags` combine. "any" (default) = at least one; ' + '"all" = every tag. The `_strict` forms additionally require ' + "the memory to actually carry the tags rather than merely rank " + "for them. No effect while `tags` and `tag_groups` are empty."),
|
|
16773
|
+
tag_groups: exports_external.union([
|
|
16774
|
+
exports_external.array(exports_external.array(exports_external.string().min(1))),
|
|
16775
|
+
exports_external.record(exports_external.string(), exports_external.array(exports_external.string().min(1)))
|
|
16776
|
+
]).optional().describe("Tag filtering with grouping — either an OR-of-ANDs list " + '([["a","b"],["c"]] = (a AND b) OR c) or a named ' + "{group: [tags]} map. Default unset (no grouping). Use when a " + "flat `tags` + `tags_match` cannot express the scope you need."),
|
|
16777
|
+
tag_weights: exports_external.record(exports_external.string(), exports_external.number().min(0)).optional().describe("Per-tag multipliers applied to `scores.final` just before the " + "final sort — a DEMOTION/PROMOTION, never a drop, so a " + "down-weighted memory still surfaces when it is the only " + "relevant hit. MERGED over switchroom's seed " + '({"sidechain": 0.8}, which ranks delegated sub-agent ' + "process-memories just under first-party ones), so setting one " + "unrelated weight does not silently undo it; pass " + "`sidechain: 1.0` to neutralise the seed. Reach for this when " + "recall_log shows one class of memory crowding the block."),
|
|
16778
|
+
additional_bank_filters: exports_external.record(exports_external.string(), exports_external.object({
|
|
16779
|
+
tags: exports_external.array(exports_external.string().min(1)).optional(),
|
|
16780
|
+
tags_match: exports_external.enum(["any", "all", "any_strict", "all_strict"]).optional(),
|
|
16781
|
+
tag_groups: exports_external.union([
|
|
16782
|
+
exports_external.array(exports_external.array(exports_external.string().min(1))),
|
|
16783
|
+
exports_external.record(exports_external.string(), exports_external.array(exports_external.string().min(1)))
|
|
16784
|
+
]).optional()
|
|
16785
|
+
}).strict()).optional().describe("Per-bank overrides of the tag filters above, keyed by bank id " + "(applies to `additional_banks` AND to sender banks). Default " + "{} = every extra bank inherits the global filters. Use it to " + "scope a shared bank — e.g. recall only `profile`-tagged " + "memories from the operator's profile bank while leaving the " + "agent's own bank unfiltered."),
|
|
16786
|
+
transcript_fallback: exports_external.boolean().optional().describe("When every bank returns zero results AND no bank hit its " + "deadline, grep the current session's transcript tail for turns " + "matching the query and inject them as a clearly-labelled " + "lower-confidence block. Default true — it covers the window " + "between an abrupt kill and the next boot reconciliation, where " + "the fact layer was never told about the lost turns. Set false " + "if you never want un-consolidated transcript text in context."),
|
|
16787
|
+
transcript_tail_bytes: exports_external.number().int().min(0).optional().describe("Bytes of the session transcript read from the tail for the " + "multi-turn query composition. Default 262144 (256 KiB), which " + "keeps the per-turn read O(1) on a session log that can grow to " + "many MB. 0 = read the whole file (the pre-bound behaviour, and " + "the rollback lever if a composition ever needs older turns)."),
|
|
16788
|
+
max_query_chars: exports_external.number().int().min(1).optional().describe("Character bound on the composed recall query, applied before " + "`query_max_tokens` shapes it. Default 800. Truncation preserves " + "the latest turn and drops the oldest context first. Lower it " + "for an agent whose turns are long pasted payloads."),
|
|
16789
|
+
parallel: exports_external.boolean().optional().describe("Run the directives fetch and every bank recall concurrently " + "under one shared deadline, so total latency is the SLOWEST slot " + "rather than their SUM. Default true. false restores the serial " + "path — the rollback lever if the parallel path ever misbehaves; " + "expect multi-bank recall latency to add up."),
|
|
16763
16790
|
topic_filter_mode: exports_external.enum(["soft-preamble", "hard-filter"]).optional().describe("Supergroup-mode cross-topic memory behaviour. Default " + "(unset) → soft-preamble: recall returns memories from all " + "topics, and a 'Current topic: …' preamble tells the model " + "to self-scope. hard-filter: drop any recalled memory whose " + "metadata.thread_id differs from the active inbound's topic. " + "Flip to hard-filter when the recall_log shows binding " + "failures (model surfacing the right memory but applying " + "it to the wrong topic).")
|
|
16764
16791
|
}).optional().describe("Auto-recall tuning knobs"),
|
|
16765
16792
|
retain: exports_external.object({
|
|
@@ -16962,7 +16989,10 @@ var LiteLLMConfigSchema = exports_external.object({
|
|
|
16962
16989
|
admin_key: exports_external.string().optional().describe("LiteLLM master/admin key used at apply time to provision the team + " + "virtual key. Supports a vault reference (e.g. " + "'vault:litellm/master-key') — resolution happens at apply time via " + "the vault-broker. Never injected into the agent container."),
|
|
16963
16990
|
team: exports_external.string().optional().describe("LiteLLM team alias the per-agent key is created under. Defaults to " + "'switchroom' (applied in code, not as a schema default)."),
|
|
16964
16991
|
small_fast_model: exports_external.string().optional().describe("Model id exported as ANTHROPIC_SMALL_FAST_MODEL for the claude CLI's " + "background/fast lane, e.g. 'claude-haiku-4-5-20251001'."),
|
|
16965
|
-
tags: exports_external.record(exports_external.string(), exports_external.string()).optional().describe("Extra key/value metadata tags attached to the provisioned LiteLLM " + "virtual key. Merged per-key across cascade layers (agent wins).")
|
|
16992
|
+
tags: exports_external.record(exports_external.string(), exports_external.string()).optional().describe("Extra key/value metadata tags attached to the provisioned LiteLLM " + "virtual key. Merged per-key across cascade layers (agent wins)."),
|
|
16993
|
+
max_budget: exports_external.number().positive().optional().describe("HARD spend cap in USD for this agent's virtual key over one " + "`budget_duration` window. LiteLLM refuses the request once the key's " + "tracked spend exceeds it, so a runaway loop costs at most this much " + "before it is stopped. Defaults to " + "DEFAULT_KEY_MAX_BUDGET_USD (see src/litellm/budget.ts) — deliberately " + "conservative; raise it per-agent rather than removing it. Set 0 or " + "omit `budget_duration` at your own risk: an uncapped key is only as " + "bounded as the upstream account balance."),
|
|
16994
|
+
soft_budget: exports_external.number().positive().optional().describe("ADVISORY spend threshold in USD. LiteLLM keeps serving past it and " + "raises a budget alert instead. Must be < max_budget. NOTE: LiteLLM " + "accepts soft_budget only on POST /key/generate (GenerateKeyRequest); " + "UpdateKeyRequest does NOT carry it, so changing this value only takes " + "effect on a key that is (re)generated, not on an existing one."),
|
|
16995
|
+
budget_duration: exports_external.string().regex(/^\d+(s|m|h|d|mo)$/, "budget_duration must be a LiteLLM duration like '30d', '24h', '1mo'").optional().describe("Rolling window the budget resets on, in LiteLLM duration syntax " + "('30d', '24h', '1mo'). Defaults to DEFAULT_KEY_BUDGET_DURATION. " + "WITHOUT a duration LiteLLM treats max_budget as a LIFETIME cap that " + "never resets — the key silently dies for good once it is hit.")
|
|
16966
16996
|
}).optional().describe("LiteLLM routing config — opt-in per-agent virtual-key auto-provisioning " + "+ routing env. Default OFF. See LiteLLMConfigSchema doc for the full flow.");
|
|
16967
16997
|
var HindsightPerOpLlmSchema = exports_external.object({
|
|
16968
16998
|
model: exports_external.string().min(1).optional().describe("Per-op model (upstream `HINDSIGHT_API_<OP>_LLM_MODEL`). Absent → " + "inherit the global `hindsight.llm.model`."),
|
|
@@ -16972,6 +17002,7 @@ var HindsightPerOpLlmSchema = exports_external.object({
|
|
|
16972
17002
|
context_window: exports_external.number().int().positive().optional().describe("Context window (tokens) of the backend serving THIS op. NOT an " + "upstream env var — switchroom derives the op's token budget " + "(consolidation batch size / max-completion caps / reflect " + "max-context cap) from it so a single call can never overflow the " + "window. Absent → inherit " + "`hindsight.llm.context_window`, else a per-provider default " + "(conservative for non-`claude-code` providers, which usually mean " + "a local llama.cpp/Ollama slot; a self-hosted `base_url` — loopback, " + "RFC1918, `.local`/`.internal` — forces the conservative default too, " + "regardless of the provider NAME, since the endpoint is where the " + "traffic actually terminates). All three lanes (`retain`, " + "`reflect`, `consolidation`) are budgeted independently.")
|
|
16973
17003
|
}).describe("Per-operation LLM override. Every field optional; an unset field (or " + "an omitted op block) inherits the global `hindsight.llm.*`, which is " + "already the engine's fallback — switchroom emits only the vars set.");
|
|
16974
17004
|
var HindsightConfigSchema = exports_external.object({
|
|
17005
|
+
gpu: exports_external.boolean().optional().describe("Force GPU passthrough for the hindsight container on (`true`) or off " + "(`false`), overriding host autodetection in BOTH directions. Absent " + "(the default) → autodetect from the persisted host-capabilities " + "verdict (`~/.switchroom/host-capabilities.json`), which enables " + "`--gpus all` only when that file proves BOTH a GPU and the nvidia " + "container toolkit. Set `true` when that verdict is wrong or unreadable " + "and you know the host has a working toolkit — switchroom cannot verify " + "it for you, and `docker run --gpus all` hard-fails container create on " + "a host without one. Set `false` to pin the container to CPU on a GPU " + "host. This is also the declarative opt-out for the recreate-time GPU " + "drop guard (`switchroom memory setup --recreate` refuses to silently " + "turn a GPU container into a CPU one). `--gpu`/`--no-gpu` on `memory " + "setup` override this for a single run."),
|
|
16975
17006
|
llm: exports_external.object({
|
|
16976
17007
|
provider: exports_external.string().min(1).optional().describe("Hindsight LLM provider (upstream `HINDSIGHT_API_LLM_PROVIDER`). " + "Defaults to `claude-code` (subscription-honest, broker-fed OAuth). " + "Any litellm-routable provider the upstream image supports is valid. " + "Serves as the GLOBAL default for every op absent a per-op override."),
|
|
16977
17008
|
model: exports_external.string().min(1).optional().describe("Hindsight LLM model (upstream `HINDSIGHT_API_LLM_MODEL`). Defaults " + "to HINDSIGHT_DEFAULT_MODEL. Any model your LiteLLM proxy can route " + "is valid, e.g. `openrouter/z-ai/glm-5.2` when routing through the " + "fleet proxy. With provider=claude-code this value is ALSO exported " + "as `ANTHROPIC_MODEL` to the claude subprocess. Serves as the GLOBAL " + "default for every op absent a per-op override."),
|
|
@@ -16980,7 +17011,7 @@ var HindsightConfigSchema = exports_external.object({
|
|
|
16980
17011
|
reflect: HindsightPerOpLlmSchema.optional().describe("Per-op override for the `reflect` LLM op (synthesis / mental-model " + "refresh). Emits `HINDSIGHT_API_REFLECT_LLM_*`. Absent → uses global."),
|
|
16981
17012
|
consolidation: HindsightPerOpLlmSchema.optional().describe("Per-op override for the `consolidation` LLM op (background memory " + "merge). Emits `HINDSIGHT_API_CONSOLIDATION_LLM_*`. Absent → global.")
|
|
16982
17013
|
}).optional().describe("LLM knob for the hindsight container. The flat `provider`/`model` set " + "the global default (backward-compatible); optional `retain`/`reflect`/" + "`consolidation` blocks override individual ops. All fields optional; " + "unset fields fall back to the hard-coded defaults."),
|
|
16983
|
-
env: exports_external.record(exports_external.union([exports_external.string(), exports_external.number(), exports_external.boolean()])).optional().describe("Operator overrides for switchroom's capability-gated Hindsight " + "performance defaults. Only the keys switchroom actually manages are " + "honoured (`HINDSIGHT_PERF_ENV_KEYS` in " + "src/setup/hindsight-perf-defaults.ts: RERANKER_LOCAL_FP16, " + "RERANKER_LOCAL_BATCH_SIZE, LLM_MAX_CONCURRENT, " + "RETAIN/CONSOLIDATION_LLM_MAX_CONCURRENT, LLM_STRICT_SCHEMA, " + "LLM_MAX_RETRIES, CONSOLIDATION_LLM_PARALLELISM, " + "MAX_OBSERVATIONS_PER_SCOPE, " + "RECALL_MAX_CANDIDATES_PER_SOURCE, LINK_EXPANSION_PER_ENTITY_LIMIT, " + "LINK_EXPANSION_TIMEOUT, LLM_REASONING_EFFORT, " + "RERANKER_LOCAL_BUCKET_BATCHING, RERANKER_MAX_CANDIDATES, " + "RERANKER_LOCAL_MAX_CONCURRENT, RECALL_MAX_CONCURRENT, " + "REFLECT_WALL_TIMEOUT, WORKER_CONSOLIDATION_MAX_SLOTS, " + "WORKER_CONSOLIDATION_SLOT_LIMIT, " + "CONSOLIDATION_MAX_MEMORIES_PER_ROUND), the override-only keys " + "switchroom manages but ships NO default for " + "(`HINDSIGHT_PERF_OVERRIDE_ONLY_KEYS`: " + "HINDSIGHT_API_WORKER_CONSOLIDATION_BANK_PRIORITY — a per-deployment " + "`bank-pattern:priority,...` map; unset means upstream's flat " + "created_at FIFO across banks; and " + "HINDSIGHT_CE_DECISIVE_RELATIVE_GAP — the rollback knob for " + "switchroom's CE-saturation damping patch, a float; >= ~0.65 backs the " + "damping out entirely, unset means the patch's own derived gap), plus the " + "embedded-PostgreSQL (pg0) sizing keys switchroom manages in " + "src/setup/hindsight-pg-defaults.ts (`HINDSIGHT_PG_ENV_KEYS`: " + "SWITCHROOM_HINDSIGHT_PG_EFFECTIVE_CACHE_SIZE, " + "SWITCHROOM_HINDSIGHT_PG_SHARED_BUFFERS — a postgres size string such " + "as `4GB`, or the sentinel `off` to leave pg0's own default for that " + "one knob). A value set here " + "REPLACES switchroom's default and is emitted even when the gating " + "capability is absent, so an operator can always force a knob. Other " + "`HINDSIGHT_API_*` keys are deliberately IGNORED — a blanket " + "passthrough would collide with the vars startHindsight() derives " + "itself (HINDSIGHT_API_PORT, the retain token/deadline budget).")
|
|
17014
|
+
env: exports_external.record(exports_external.union([exports_external.string(), exports_external.number(), exports_external.boolean()])).optional().describe("Operator overrides for switchroom's capability-gated Hindsight " + "performance defaults. Only the keys switchroom actually manages are " + "honoured (`HINDSIGHT_PERF_ENV_KEYS` in " + "src/setup/hindsight-perf-defaults.ts: RERANKER_LOCAL_FP16, " + "RERANKER_LOCAL_BATCH_SIZE, LLM_MAX_CONCURRENT, " + "RETAIN/CONSOLIDATION_LLM_MAX_CONCURRENT, LLM_STRICT_SCHEMA, " + "LLM_MAX_RETRIES, CONSOLIDATION_LLM_PARALLELISM, " + "MAX_OBSERVATIONS_PER_SCOPE, " + "RECALL_MAX_CANDIDATES_PER_SOURCE, LINK_EXPANSION_PER_ENTITY_LIMIT, " + "LINK_EXPANSION_TIMEOUT, LLM_REASONING_EFFORT, " + "RERANKER_LOCAL_BUCKET_BATCHING, RERANKER_MAX_CANDIDATES, " + "RERANKER_LOCAL_MAX_CONCURRENT, RECALL_MAX_CONCURRENT, " + "REFLECT_WALL_TIMEOUT, WORKER_CONSOLIDATION_MAX_SLOTS, " + "WORKER_CONSOLIDATION_SLOT_LIMIT, " + "CONSOLIDATION_MAX_MEMORIES_PER_ROUND, RECENCY_DECAY_FUNCTION, " + "RECENCY_DECAY_HALFLIFE_DAYS — switchroom defaults recall's recency " + "curve to `exponential` with a 30-day half-life so a fact retained " + "today outranks a stale one, instead of upstream's near-flat " + "linear/365-day window), the override-only keys " + "switchroom manages but ships NO default for " + "(`HINDSIGHT_PERF_OVERRIDE_ONLY_KEYS`: " + "HINDSIGHT_API_WORKER_CONSOLIDATION_BANK_PRIORITY — a per-deployment " + "`bank-pattern:priority,...` map; unset means upstream's flat " + "created_at FIFO across banks; and " + "HINDSIGHT_CE_DECISIVE_RELATIVE_GAP — the rollback knob for " + "switchroom's CE-saturation damping patch, a float; >= ~0.65 backs the " + "damping out entirely, unset means the patch's own derived gap; and " + "HINDSIGHT_API_RECENCY_DECAY_LINEAR_WINDOW_DAYS — only read when the " + "decay function is `linear`, so switchroom ships no default for it but " + "still honours an operator who flips the function back; and " + "HINDSIGHT_API_WORKER_MAX_SLOTS — the worker poller's TOTAL in-flight " + "task budget, the pool WORKER_CONSOLIDATION_MAX_SLOTS reserves out of; " + "unset means upstream's own default), plus the " + "embedded-PostgreSQL (pg0) sizing keys switchroom manages in " + "src/setup/hindsight-pg-defaults.ts (`HINDSIGHT_PG_ENV_KEYS`: " + "SWITCHROOM_HINDSIGHT_PG_EFFECTIVE_CACHE_SIZE, " + "SWITCHROOM_HINDSIGHT_PG_SHARED_BUFFERS — a postgres size string such " + "as `4GB`, or the sentinel `off` to leave pg0's own default for that " + "one knob). A value set here " + "REPLACES switchroom's default and is emitted even when the gating " + "capability is absent, so an operator can always force a knob. Other " + "`HINDSIGHT_API_*` keys are deliberately IGNORED — a blanket " + "passthrough would collide with the vars startHindsight() derives " + "itself (HINDSIGHT_API_PORT, the retain token/deadline budget).")
|
|
16984
17015
|
});
|
|
16985
17016
|
var MicrosoftWorkspaceConfigSchema = exports_external.object({
|
|
16986
17017
|
microsoft_client_id: exports_external.string().min(1).optional().describe("Microsoft OAuth application (client) ID from Entra portal " + "(literal string or vault reference e.g. " + "'vault:microsoft-oauth-client-id'). OPTIONAL — omit it to use " + "switchroom's shipped default Microsoft app (zero-config). " + "Set it only to bring your own Entra app (BYO)."),
|
|
@@ -17123,6 +17154,33 @@ var profileFields = {
|
|
|
17123
17154
|
request_timeout_seconds: exports_external.number().int().min(1).optional(),
|
|
17124
17155
|
own_bank_min_slots: exports_external.number().int().min(0).optional(),
|
|
17125
17156
|
additional_bank_min_slots: exports_external.number().int().min(0).optional(),
|
|
17157
|
+
min_score: exports_external.number().min(0).optional(),
|
|
17158
|
+
min_score_scope: exports_external.enum(["degraded", "all"]).optional(),
|
|
17159
|
+
budget: exports_external.enum(["low", "mid", "high"]).optional(),
|
|
17160
|
+
max_tokens: exports_external.number().int().min(1).optional(),
|
|
17161
|
+
prefer_observations: exports_external.boolean().optional(),
|
|
17162
|
+
context_turns: exports_external.number().int().min(1).optional(),
|
|
17163
|
+
roles: exports_external.array(exports_external.string().min(1)).min(1).optional(),
|
|
17164
|
+
prompt_preamble: exports_external.string().min(1).optional(),
|
|
17165
|
+
tags: exports_external.array(exports_external.string().min(1)).optional(),
|
|
17166
|
+
tags_match: exports_external.enum(["any", "all", "any_strict", "all_strict"]).optional(),
|
|
17167
|
+
tag_groups: exports_external.union([
|
|
17168
|
+
exports_external.array(exports_external.array(exports_external.string().min(1))),
|
|
17169
|
+
exports_external.record(exports_external.string(), exports_external.array(exports_external.string().min(1)))
|
|
17170
|
+
]).optional(),
|
|
17171
|
+
tag_weights: exports_external.record(exports_external.string(), exports_external.number().min(0)).optional(),
|
|
17172
|
+
additional_bank_filters: exports_external.record(exports_external.string(), exports_external.object({
|
|
17173
|
+
tags: exports_external.array(exports_external.string().min(1)).optional(),
|
|
17174
|
+
tags_match: exports_external.enum(["any", "all", "any_strict", "all_strict"]).optional(),
|
|
17175
|
+
tag_groups: exports_external.union([
|
|
17176
|
+
exports_external.array(exports_external.array(exports_external.string().min(1))),
|
|
17177
|
+
exports_external.record(exports_external.string(), exports_external.array(exports_external.string().min(1)))
|
|
17178
|
+
]).optional()
|
|
17179
|
+
}).strict()).optional(),
|
|
17180
|
+
transcript_fallback: exports_external.boolean().optional(),
|
|
17181
|
+
transcript_tail_bytes: exports_external.number().int().min(0).optional(),
|
|
17182
|
+
max_query_chars: exports_external.number().int().min(1).optional(),
|
|
17183
|
+
parallel: exports_external.boolean().optional(),
|
|
17126
17184
|
additional_banks: exports_external.array(exports_external.string()).optional(),
|
|
17127
17185
|
sender_banks: exports_external.record(exports_external.string(), exports_external.string()).optional()
|
|
17128
17186
|
}).optional()
|
|
@@ -18413,6 +18471,9 @@ function assertPositive(value, label) {
|
|
|
18413
18471
|
}
|
|
18414
18472
|
}
|
|
18415
18473
|
|
|
18474
|
+
// src/setup/host-capabilities.ts
|
|
18475
|
+
var _warnedReads = new Set;
|
|
18476
|
+
|
|
18416
18477
|
// src/setup/hindsight-pg-defaults.ts
|
|
18417
18478
|
var HINDSIGHT_PG_MEM_LIMIT_MIB_FOR_DERIVATION = 8 * 1024;
|
|
18418
18479
|
var HINDSIGHT_PG_APP_ANON_MIB = 2560;
|
|
@@ -18442,7 +18503,13 @@ var HINDSIGHT_DEFAULT_LINK_EXPANSION_TIMEOUT_S = 2;
|
|
|
18442
18503
|
var HINDSIGHT_DEFAULT_LLM_REASONING_EFFORT = "low";
|
|
18443
18504
|
var HINDSIGHT_DEFAULT_LLM_MAX_CONCURRENT = 4;
|
|
18444
18505
|
var HINDSIGHT_DEFAULT_RETAIN_LLM_MAX_CONCURRENT = 1;
|
|
18445
|
-
|
|
18506
|
+
function hindsightConsolidationLlmMaxConcurrentDefault(globalMaxConcurrent = HINDSIGHT_DEFAULT_LLM_MAX_CONCURRENT, retainMaxConcurrent = HINDSIGHT_DEFAULT_RETAIN_LLM_MAX_CONCURRENT) {
|
|
18507
|
+
const globalCap = Number.isFinite(globalMaxConcurrent) && globalMaxConcurrent >= 1 ? Math.floor(globalMaxConcurrent) : HINDSIGHT_DEFAULT_LLM_MAX_CONCURRENT;
|
|
18508
|
+
const retainCap = Number.isFinite(retainMaxConcurrent) && retainMaxConcurrent >= 0 ? Math.floor(retainMaxConcurrent) : HINDSIGHT_DEFAULT_RETAIN_LLM_MAX_CONCURRENT;
|
|
18509
|
+
const headroomBound = Math.max(1, globalCap - 1);
|
|
18510
|
+
return Math.min(headroomBound, Math.max(1, globalCap - retainCap - 1));
|
|
18511
|
+
}
|
|
18512
|
+
var HINDSIGHT_DEFAULT_CONSOLIDATION_LLM_MAX_CONCURRENT = hindsightConsolidationLlmMaxConcurrentDefault();
|
|
18446
18513
|
var HINDSIGHT_DEFAULT_RERANKER_LOCAL_FP16 = "true";
|
|
18447
18514
|
var HINDSIGHT_DEFAULT_RERANKER_LOCAL_BATCH_SIZE = 128;
|
|
18448
18515
|
var HINDSIGHT_DEFAULT_LLM_STRICT_SCHEMA = "true";
|
|
@@ -18457,6 +18524,8 @@ var HINDSIGHT_DEFAULT_REFLECT_WALL_TIMEOUT_S = 600;
|
|
|
18457
18524
|
var HINDSIGHT_DEFAULT_CONSOLIDATION_MAX_MEMORIES_PER_ROUND = 500;
|
|
18458
18525
|
var HINDSIGHT_DEFAULT_CONSOLIDATION_SLOT_LIMIT = 6;
|
|
18459
18526
|
var HINDSIGHT_DEFAULT_CONSOLIDATION_MAX_SLOTS = 1;
|
|
18527
|
+
var HINDSIGHT_DEFAULT_RECENCY_DECAY_FUNCTION = "exponential";
|
|
18528
|
+
var HINDSIGHT_DEFAULT_RECENCY_DECAY_HALFLIFE_DAYS = 30;
|
|
18460
18529
|
var HINDSIGHT_PERF_DEFAULTS_UNGATED = [
|
|
18461
18530
|
[
|
|
18462
18531
|
"HINDSIGHT_API_RECALL_MAX_CANDIDATES_PER_SOURCE",
|
|
@@ -18510,6 +18579,14 @@ var HINDSIGHT_PERF_DEFAULTS_UNGATED = [
|
|
|
18510
18579
|
[
|
|
18511
18580
|
"HINDSIGHT_API_CONSOLIDATION_MAX_MEMORIES_PER_ROUND",
|
|
18512
18581
|
String(HINDSIGHT_DEFAULT_CONSOLIDATION_MAX_MEMORIES_PER_ROUND)
|
|
18582
|
+
],
|
|
18583
|
+
[
|
|
18584
|
+
"HINDSIGHT_API_RECENCY_DECAY_FUNCTION",
|
|
18585
|
+
HINDSIGHT_DEFAULT_RECENCY_DECAY_FUNCTION
|
|
18586
|
+
],
|
|
18587
|
+
[
|
|
18588
|
+
"HINDSIGHT_API_RECENCY_DECAY_HALFLIFE_DAYS",
|
|
18589
|
+
String(HINDSIGHT_DEFAULT_RECENCY_DECAY_HALFLIFE_DAYS)
|
|
18513
18590
|
]
|
|
18514
18591
|
];
|
|
18515
18592
|
var HINDSIGHT_PERF_DEFAULTS_GPU = [
|
|
@@ -18534,7 +18611,9 @@ var HINDSIGHT_PERF_DEFAULTS_LOCAL_LLM = [
|
|
|
18534
18611
|
];
|
|
18535
18612
|
var HINDSIGHT_PERF_OVERRIDE_ONLY_KEYS = new Set([
|
|
18536
18613
|
"HINDSIGHT_API_WORKER_CONSOLIDATION_BANK_PRIORITY",
|
|
18537
|
-
"HINDSIGHT_CE_DECISIVE_RELATIVE_GAP"
|
|
18614
|
+
"HINDSIGHT_CE_DECISIVE_RELATIVE_GAP",
|
|
18615
|
+
"HINDSIGHT_API_RECENCY_DECAY_LINEAR_WINDOW_DAYS",
|
|
18616
|
+
"HINDSIGHT_API_WORKER_MAX_SLOTS"
|
|
18538
18617
|
]);
|
|
18539
18618
|
var HINDSIGHT_PERF_ENV_KEYS = new Set([
|
|
18540
18619
|
...[
|
|
@@ -18575,7 +18654,52 @@ var HINDSIGHT_HEALTHCHECK_CMD = `python3 -c '${HINDSIGHT_HEALTHCHECK_PY}'`;
|
|
|
18575
18654
|
var DOCKER_PROBE_TIMEOUT_MS = 60 * 1000;
|
|
18576
18655
|
|
|
18577
18656
|
// src/memory/hindsight.ts
|
|
18578
|
-
var DEFAULT_RETAIN_MISSION =
|
|
18657
|
+
var DEFAULT_RETAIN_MISSION = `Extract durable facts that will still be true and useful weeks from now: user preferences and standing rules, ongoing projects and recurring commitments, technical and architectural decisions with their rationale, and people/tool relationships. A preference revealed by a request is durable — record the preference (what the user likes, wants, or always does), not the request itself.
|
|
18658
|
+
` + `
|
|
18659
|
+
` + `A TOOL RESULT IS NOT A FACT. Before extracting, ask: is the subject of this
|
|
18660
|
+
` + `candidate a file path, a command/process/agent/session id, a temp directory, or
|
|
18661
|
+
` + `the location where some output was written? If yes, drop it — it is transcript
|
|
18662
|
+
` + `exhaust, not memory.
|
|
18663
|
+
` + `
|
|
18664
|
+
` + `NEVER extract:
|
|
18665
|
+
` + `- Tool results verbatim or paraphrased. Concretely, never produce a fact whose
|
|
18666
|
+
` + ` text resembles any of these: "File created successfully at /path/to/file",
|
|
18667
|
+
` + ` "A background command with ID bctz4yskm is running, and its output will be
|
|
18668
|
+
` + ` written to /tmp/...", "Async agent a745598ba84e71df1 was launched successfully
|
|
18669
|
+
` + ` and is running in the background", "User executed a Bash command to sleep for
|
|
18670
|
+
` + ` 200 seconds", "The assistant used grep to locate 'truncateSync' in src/foo.ts".
|
|
18671
|
+
` + `- Anything mentioning a path under /tmp, a scratchpad directory, or a .tmp file.
|
|
18672
|
+
` + `- Agent tool-use traces or narration of what the assistant did (e.g. "the
|
|
18673
|
+
` + ` assistant used X to query Y", "ran a search", "sent the message").
|
|
18674
|
+
` + `- In-flight workflow/process narration (a sub-task started, paused, or is still
|
|
18675
|
+
` + ` running) — retain the outcome only once the task completes or a decision is made.
|
|
18676
|
+
` + `- Operation, request, batch, agent, command or session IDs, UUIDs, hashes, or error codes.
|
|
18677
|
+
` + `- Slash commands the user typed and their effects (e.g. "User issued /clear to
|
|
18678
|
+
` + ` reset assistant state").
|
|
18679
|
+
` + `- Hindsight's own errors, retries, backlogs, or internal state — the memory
|
|
18680
|
+
` + ` system's self-reports are not memories.
|
|
18681
|
+
` + `- Restatements of the user's current request or the task in progress.
|
|
18682
|
+
` + `- Volatile state written as a timeless assertion. A version, count, size,
|
|
18683
|
+
` + ` backlog, status, or any "X is running Y" / "X is at Y" / "X is currently Y"
|
|
18684
|
+
` + ` claim is true only at the instant it was said. Concretely, never produce a
|
|
18685
|
+
` + ` fact whose text resembles any of these: "Switchroom fleet is running image
|
|
18686
|
+
` + ` version v0.18.19", "The switchroom repo is at /path/to/fleet, version
|
|
18687
|
+
` + ` v0.19.5", "Bank overlord has 43155 pending consolidations", "The build is
|
|
18688
|
+
` + ` currently green". If the claim is worth keeping, put the date INSIDE the
|
|
18689
|
+
` + ` fact text ("As of 2026-07-19 the fleet was running v0.18.19"); if you
|
|
18690
|
+
` + ` cannot date it, drop it. An undated one is recalled forever as though it
|
|
18691
|
+
` + ` were still true, which is worse than not remembering it at all.
|
|
18692
|
+
` + `- Transient state (unread counts, build status, what is running right now) unless
|
|
18693
|
+
` + ` the fact is explicitly dated, in which case record it as a dated observation.
|
|
18694
|
+
` + `- Greetings, acknowledgements, and routine operational chatter.
|
|
18695
|
+
` + `
|
|
18696
|
+
` + `If a candidate fact matches an exclusion, drop it rather than rewording it. If
|
|
18697
|
+
` + "nothing durable remains, return an empty facts list.";
|
|
18698
|
+
var SUPERSEDED_RETAIN_MISSIONS = [
|
|
18699
|
+
"Extract technical decisions, architectural choices, user preferences, project context, and people/tool relationships. Ignore routine greetings and transient operational details.",
|
|
18700
|
+
"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.",
|
|
18701
|
+
"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, " + "including in-flight workflow/process narration (a sub-task started, " + "paused, or is still running) — only retain the outcome once a task " + "actually completes or a decision is made.",
|
|
18702
|
+
"Extract durable facts that will still be true and useful weeks from now: " + "user preferences and standing rules, ongoing projects and recurring " + "commitments, technical and architectural decisions with their rationale, " + "and people/tool relationships. A preference revealed by a request is " + "durable — record the preference (what the user likes, wants, or always " + `does), not the request itself.
|
|
18579
18703
|
|
|
18580
18704
|
` + `NEVER extract:
|
|
18581
18705
|
` + "- Agent tool-use traces or narration of what the assistant did (e.g. " + `"the assistant used X to query Y", "ran a search", "sent the message").
|
|
@@ -18586,22 +18710,108 @@ var DEFAULT_RETAIN_MISSION = "Extract durable facts that will still be true and
|
|
|
18586
18710
|
` + "- Transient state (unread counts, build status, what is running right now) " + "unless the fact is explicitly dated, in which case record it as a dated " + `observation.
|
|
18587
18711
|
` + `- Greetings, acknowledgements, and routine operational chatter.
|
|
18588
18712
|
|
|
18589
|
-
` + "If a candidate fact matches an exclusion, drop it rather than rewording " + "it. If nothing durable remains, return an empty facts list."
|
|
18590
|
-
|
|
18591
|
-
|
|
18592
|
-
|
|
18593
|
-
|
|
18713
|
+
` + "If a candidate fact matches an exclusion, drop it rather than rewording " + "it. If nothing durable remains, return an empty facts list.",
|
|
18714
|
+
`Extract durable facts that will still be true and useful weeks from now: user preferences and standing rules, ongoing projects and recurring commitments, technical and architectural decisions with their rationale, and people/tool relationships. A preference revealed by a request is durable — record the preference (what the user likes, wants, or always does), not the request itself.
|
|
18715
|
+
` + `
|
|
18716
|
+
` + `A TOOL RESULT IS NOT A FACT. Before extracting, ask: is the subject of this
|
|
18717
|
+
` + `candidate a file path, a command/process/agent/session id, a temp directory, or
|
|
18718
|
+
` + `the location where some output was written? If yes, drop it — it is transcript
|
|
18719
|
+
` + `exhaust, not memory.
|
|
18720
|
+
` + `
|
|
18721
|
+
` + `NEVER extract:
|
|
18722
|
+
` + `- Tool results verbatim or paraphrased. Concretely, never produce a fact whose
|
|
18723
|
+
` + ` text resembles any of these: "File created successfully at /path/to/file",
|
|
18724
|
+
` + ` "A background command with ID bctz4yskm is running, and its output will be
|
|
18725
|
+
` + ` written to /tmp/...", "Async agent a745598ba84e71df1 was launched successfully
|
|
18726
|
+
` + ` and is running in the background", "User executed a Bash command to sleep for
|
|
18727
|
+
` + ` 200 seconds", "The assistant used grep to locate 'truncateSync' in src/foo.ts".
|
|
18728
|
+
` + `- Anything mentioning a path under /tmp, a scratchpad directory, or a .tmp file.
|
|
18729
|
+
` + `- Agent tool-use traces or narration of what the assistant did (e.g. "the
|
|
18730
|
+
` + ` assistant used X to query Y", "ran a search", "sent the message").
|
|
18731
|
+
` + `- In-flight workflow/process narration (a sub-task started, paused, or is still
|
|
18732
|
+
` + ` running) — retain the outcome only once the task completes or a decision is made.
|
|
18733
|
+
` + `- Operation, request, batch, agent, command or session IDs, UUIDs, hashes, or error codes.
|
|
18734
|
+
` + `- Slash commands the user typed and their effects (e.g. "User issued /clear to
|
|
18735
|
+
` + ` reset assistant state").
|
|
18736
|
+
` + `- Hindsight's own errors, retries, backlogs, or internal state — the memory
|
|
18737
|
+
` + ` system's self-reports are not memories.
|
|
18738
|
+
` + `- Restatements of the user's current request or the task in progress.
|
|
18739
|
+
` + `- Transient state (unread counts, build status, what is running right now) unless
|
|
18740
|
+
` + ` the fact is explicitly dated, in which case record it as a dated observation.
|
|
18741
|
+
` + `- Greetings, acknowledgements, and routine operational chatter.
|
|
18742
|
+
` + `
|
|
18743
|
+
` + `If a candidate fact matches an exclusion, drop it rather than rewording it. If
|
|
18744
|
+
` + "nothing durable remains, return an empty facts list."
|
|
18745
|
+
];
|
|
18746
|
+
var DEFAULT_OBSERVATIONS_MISSION = `Synthesise durable, standing knowledge about the people, projects, and systems this agent works with: preferences and standing rules, roles and relationships, skills and recurring patterns, technical and operational decisions with their rationale, and the state of long-running work once it lands.
|
|
18747
|
+
` + `
|
|
18748
|
+
` + `The test is durability, not notability: an observation must still be worth reading weeks from now. A single dated event belongs in an observation only when it establishes or changes a standing fact.
|
|
18749
|
+
` + `
|
|
18750
|
+
` + `Do NOT synthesise observations from:
|
|
18751
|
+
` + `- Transcript exhaust — tool calls and their results, file paths, temp or scratchpad directories, where some output was written, or narration of what an assistant did.
|
|
18752
|
+
` + `- Identifiers with no standing meaning: session, agent, request, batch or command IDs, UUIDs, hashes, error codes.
|
|
18753
|
+
` + `- In-flight process narration — a task started, paused, or still running. Record the outcome once it lands, not the running state.
|
|
18754
|
+
` + `- The memory system's own errors, retries, backlogs, or internal state.
|
|
18755
|
+
` + `- Transient state (what is running right now, unread counts, build status) unless the fact is explicitly dated, in which case record it as dated.
|
|
18756
|
+
` + `
|
|
18757
|
+
` + "If the new facts contain nothing durable, record nothing rather than synthesising a weak observation.";
|
|
18758
|
+
var SUPERSEDED_OBSERVATIONS_MISSIONS = [
|
|
18759
|
+
"Synthesise the person's wellbeing patterns, motivations, and emotional " + "context — how habits, setbacks, and encouragement connect over time."
|
|
18594
18760
|
];
|
|
18595
18761
|
var PROFILE_MEMORY_DEFAULTS = {
|
|
18596
18762
|
"health-coach": {
|
|
18597
18763
|
disposition: { skepticism: 2, literalism: 2, empathy: 5 },
|
|
18598
|
-
observations_mission:
|
|
18764
|
+
observations_mission: `You consolidate the memory of a health and fitness coach working with one person. This bank records how that person actually lives and trains.
|
|
18765
|
+
` + `
|
|
18766
|
+
` + `Synthesise into durable observations:
|
|
18767
|
+
` + `- Goals, targets, and the plan currently in force, with the reasoning behind each.
|
|
18768
|
+
` + `- Training, nutrition, sleep, and alcohol patterns as they hold over weeks — what the person reliably does, not what they did once.
|
|
18769
|
+
` + `- Constraints that shape the plan: injuries, medical guidance, schedule, equipment, foods and sessions they refuse.
|
|
18770
|
+
` + `- Motivations, and what actually helps or backfires when they slip.
|
|
18771
|
+
` + `- Trends in the numbers: direction and range over time, not any single reading.
|
|
18772
|
+
` + `- Corrections to earlier beliefs, recorded explicitly as corrections.
|
|
18773
|
+
` + `
|
|
18774
|
+
` + `A single day's log, weight reading, or session is evidence, not an observation. Record one only when it establishes or changes a standing pattern, target, or constraint — then fold it into the observation for that pattern and say what changed and roughly when.
|
|
18775
|
+
` + `
|
|
18776
|
+
` + `Granularity: one observation per habit, target, constraint, or trend. Aggregate repeated daily evidence into the observation for that pattern rather than creating one per day, and keep training, nutrition, and sleep as separate observations.
|
|
18777
|
+
` + `
|
|
18778
|
+
` + "Word observations as the person's own pattern and framing, never as a verdict on them."
|
|
18599
18779
|
},
|
|
18600
18780
|
"executive-assistant": {
|
|
18601
|
-
disposition: { skepticism: 4, literalism: 4, empathy: 3 }
|
|
18781
|
+
disposition: { skepticism: 4, literalism: 4, empathy: 3 },
|
|
18782
|
+
observations_mission: `You consolidate the memory of an executive assistant working for one person. This bank records that person's commitments, people, and standing arrangements.
|
|
18783
|
+
` + `
|
|
18784
|
+
` + `Synthesise into durable observations:
|
|
18785
|
+
` + `- Standing rules and preferences: how they want things scheduled, written, and filed, and when they want to be interrupted.
|
|
18786
|
+
` + `- People and organisations, and the relationship: who they are, what they are involved in, how to reach them.
|
|
18787
|
+
` + `- Recurring commitments and routines, and the constraints around them.
|
|
18788
|
+
` + `- Obligations and their state: what was promised to whom, the deadline, and what is still outstanding.
|
|
18789
|
+
` + `- Decisions made and decisions deferred, with the reasoning and the trade accepted.
|
|
18790
|
+
` + `- Corrections to earlier beliefs, recorded explicitly as corrections.
|
|
18791
|
+
` + `
|
|
18792
|
+
` + `Live commitment state IS durable knowledge here, not ephemeral chatter. An outstanding obligation, a travel window, an unanswered request, a "currently X" arrangement — these are precisely what this agent must recall later. Keep them, and embed the date inside the observation text so a later reader can judge staleness. Do not drop them as ephemeral.
|
|
18793
|
+
` + `
|
|
18794
|
+
` + `Granularity: one observation per person, arrangement, or obligation. Aggregate repeated mentions into that observation rather than creating siblings; never merge two different people or two different commitments into one.
|
|
18795
|
+
` + `
|
|
18796
|
+
` + "Do not synthesise from transcript exhaust: tool calls and their results, message or event identifiers, or narration of what the assistant did."
|
|
18602
18797
|
},
|
|
18603
18798
|
coding: {
|
|
18604
|
-
disposition: { skepticism: 4, literalism: 5, empathy: 2 }
|
|
18799
|
+
disposition: { skepticism: 4, literalism: 5, empathy: 2 },
|
|
18800
|
+
observations_mission: `You consolidate the memory of a software-engineering agent. This bank records real work on real codebases.
|
|
18801
|
+
` + `
|
|
18802
|
+
` + `Synthesise into durable observations:
|
|
18803
|
+
` + `- Architecture and design decisions, each with its rationale and the trade accepted.
|
|
18804
|
+
` + `- Root causes, with the evidence chain, and negative results — what was ruled out matters as much as what was found.
|
|
18805
|
+
` + `- How the repository works: build, test, and lint commands, conventions, CI gates, and where things live.
|
|
18806
|
+
` + `- Outcomes of code work: issue and PR numbers, what changed, whether it merged, what review found.
|
|
18807
|
+
` + `- The user's standing rules, preferences, and corrections to this agent's behaviour.
|
|
18808
|
+
` + `- Corrections to earlier beliefs, recorded explicitly as corrections.
|
|
18809
|
+
` + `
|
|
18810
|
+
` + `Repository and service state IS durable knowledge here, not ephemeral chatter. Versions, a failing gate, an open PR, a measured number, a "currently X" claim — these are precisely what this agent must recall later. Keep them, and embed the date inside the observation text so a later reader can judge staleness. Do not drop them as ephemeral.
|
|
18811
|
+
` + `
|
|
18812
|
+
` + `Granularity: one observation per distinct decision, cause, convention, or work item. Aggregate repeated evidence about the same one into that observation rather than creating siblings; never merge two separate decisions into a single summary.
|
|
18813
|
+
` + `
|
|
18814
|
+
` + "Do not synthesise from transcript exhaust: tool calls and their results, scratch paths, session or request identifiers, or narration of what the agent did. Prefer specific and falsifiable — naming the file, the number, the commit, or the decision beats summarising the topic."
|
|
18605
18815
|
}
|
|
18606
18816
|
};
|
|
18607
18817
|
|
|
@@ -18974,6 +19184,27 @@ var OPERATOR_SOCKET_PATH = join3(homedir3(), ".switchroom", "broker-operator", "
|
|
|
18974
19184
|
// src/vault/resolver.ts
|
|
18975
19185
|
var materializedDirs = new Set;
|
|
18976
19186
|
|
|
19187
|
+
// src/setup/hindsight-recall-passthrough.ts
|
|
19188
|
+
var HINDSIGHT_RECALL_TAG_WEIGHT_SEED = Object.freeze({ sidechain: 0.8 });
|
|
19189
|
+
var HINDSIGHT_RECALL_PROMPT_PREAMBLE_DEFAULT = "Relevant memories from past conversations (prioritize recent when " + "conflicting). Only use memories that are directly useful to continue " + "this conversation; ignore the rest:";
|
|
19190
|
+
var RECALL_PASSTHROUGH_DEFAULTS = Object.freeze({
|
|
19191
|
+
budget: "low",
|
|
19192
|
+
maxTokens: 1024,
|
|
19193
|
+
preferObservations: true,
|
|
19194
|
+
contextTurns: 2,
|
|
19195
|
+
roles: ["user", "assistant"],
|
|
19196
|
+
promptPreamble: HINDSIGHT_RECALL_PROMPT_PREAMBLE_DEFAULT,
|
|
19197
|
+
tags: [],
|
|
19198
|
+
tagsMatch: "any",
|
|
19199
|
+
tagGroups: {},
|
|
19200
|
+
tagWeights: HINDSIGHT_RECALL_TAG_WEIGHT_SEED,
|
|
19201
|
+
additionalBankFilters: {},
|
|
19202
|
+
transcriptFallback: true,
|
|
19203
|
+
transcriptTailBytes: 262144,
|
|
19204
|
+
maxQueryChars: 800,
|
|
19205
|
+
parallel: true
|
|
19206
|
+
});
|
|
19207
|
+
|
|
18977
19208
|
// src/agents/scaffold.ts
|
|
18978
19209
|
var REPO_ROOT = resolve5(import.meta.dirname, "../..");
|
|
18979
19210
|
var CLAUDE_MD_YOURS_PLACEHOLDER = "This space is yours. Add per-agent rules, exceptions, or context the " + "Switchroom template doesn't capture. Everything above the marker line is " + "regenerated on every apply; this section is preserved.";
|