switchroom 0.19.28 → 0.19.30
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 +12 -1
- package/dist/auth-broker/index.js +14 -2
- package/dist/cli/notion-write-pretool.mjs +14 -1
- package/dist/cli/switchroom.js +1041 -374
- package/dist/host-control/main.js +28 -10
- package/dist/vault/approvals/kernel-server.js +19 -3
- package/dist/vault/broker/server.js +19 -3
- package/package.json +1 -1
- package/profiles/_base/start.sh.hbs +9 -0
- package/telegram-plugin/dist/gateway/gateway.js +23 -7
- package/vendor/hindsight-memory/CHANGELOG.md +50 -0
- package/vendor/hindsight-memory/scripts/backfill_transcripts.py +1 -0
- package/vendor/hindsight-memory/scripts/drain_pending.py +4 -0
- package/vendor/hindsight-memory/scripts/lib/client.py +14 -0
- package/vendor/hindsight-memory/scripts/lib/config.py +91 -0
- package/vendor/hindsight-memory/scripts/lib/pending.py +6 -0
- package/vendor/hindsight-memory/scripts/reconcile_tail.py +1 -0
- package/vendor/hindsight-memory/scripts/retain.py +41 -1
- package/vendor/hindsight-memory/scripts/subagent_retain.py +1 -0
- package/vendor/hindsight-memory/scripts/tests/test_backfill.py +23 -1
- package/vendor/hindsight-memory/scripts/tests/test_backfill_from_logs.py +5 -1
- package/vendor/hindsight-memory/scripts/tests/test_observation_scopes.py +325 -0
- package/vendor/hindsight-memory/scripts/tests/test_reconcile_durability.py +160 -1
- package/vendor/hindsight-memory/scripts/tests/test_subagent_retain.py +40 -2
|
@@ -19073,6 +19073,16 @@ import { resolve as resolve3 } from "node:path";
|
|
|
19073
19073
|
|
|
19074
19074
|
// src/config/schema.ts
|
|
19075
19075
|
init_zod();
|
|
19076
|
+
|
|
19077
|
+
// src/memory/observation-scopes.ts
|
|
19078
|
+
var OBSERVATION_SCOPES = [
|
|
19079
|
+
"per_tag",
|
|
19080
|
+
"combined",
|
|
19081
|
+
"all_combinations",
|
|
19082
|
+
"shared"
|
|
19083
|
+
];
|
|
19084
|
+
|
|
19085
|
+
// src/config/schema.ts
|
|
19076
19086
|
var CodeRepoEntrySchema = exports_external.object({
|
|
19077
19087
|
name: exports_external.string().describe("Short alias used when claiming (e.g. 'switchroom')"),
|
|
19078
19088
|
source: exports_external.string().describe("Absolute or home-relative path to the repo (e.g. ~/code/switchroom)"),
|
|
@@ -19184,6 +19194,7 @@ var AgentToolsSchema = exports_external.object({
|
|
|
19184
19194
|
allow: exports_external.array(exports_external.string()).default([]).describe("Allowed tools (use ['all'] for unrestricted)"),
|
|
19185
19195
|
deny: exports_external.array(exports_external.string()).default([]).describe("Denied tools (overrides allow)")
|
|
19186
19196
|
}).optional();
|
|
19197
|
+
var ObservationScopesSchema = exports_external.enum(OBSERVATION_SCOPES).optional().describe("Per-row observation scope stamped on every memory this agent " + `retains. "shared" makes Hindsight's consolidation write the ` + "resulting observations into ONE global untagged scope instead of a " + "scope per tag — what several agents pooling one bank need so their " + "observations actually merge rather than sitting in parallel " + "per-tag silos. OMITTED BY DEFAULT: unset means the field never goes " + "on the wire and the engine's own default stands, which is the " + "shipped behaviour. Applies to every retain path (Stop hook, " + "sidechain, boot reconcile, queue drain, backfill, session-handoff " + "mirror) and is carried on the queued payload, so a retain that fails " + "now and drains later still lands in this scope. Accepted values: " + `${OBSERVATION_SCOPES.join(", ")}. ` + "Cascade: override (per-agent wins over default).");
|
|
19187
19198
|
var AgentMemorySchema = exports_external.object({
|
|
19188
19199
|
collection: exports_external.string().describe("Hindsight collection name for this agent"),
|
|
19189
19200
|
auto_recall: exports_external.boolean().default(true).describe("Auto-search memories before each response"),
|
|
@@ -19218,6 +19229,7 @@ var AgentMemorySchema = exports_external.object({
|
|
|
19218
19229
|
empathy: exports_external.number().int().min(1).max(5).optional().describe("How much the bank weights emotional/relational context (1-5; engine default 3).")
|
|
19219
19230
|
}).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`)."),
|
|
19220
19231
|
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)."),
|
|
19232
|
+
observation_scopes: ObservationScopesSchema,
|
|
19221
19233
|
recall: exports_external.object({
|
|
19222
19234
|
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)."),
|
|
19223
19235
|
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."),
|
|
@@ -19483,7 +19495,7 @@ var HindsightConfigSchema = exports_external.object({
|
|
|
19483
19495
|
reflect: HindsightPerOpLlmSchema.optional().describe("Per-op override for the `reflect` LLM op (synthesis / mental-model " + "refresh). Emits `HINDSIGHT_API_REFLECT_LLM_*`. Absent → uses global."),
|
|
19484
19496
|
consolidation: HindsightPerOpLlmSchema.optional().describe("Per-op override for the `consolidation` LLM op (background memory " + "merge). Emits `HINDSIGHT_API_CONSOLIDATION_LLM_*`. Absent → global.")
|
|
19485
19497
|
}).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."),
|
|
19486
|
-
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).")
|
|
19498
|
+
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; and " + "HINDSIGHT_API_WORKER_RETAIN_MAX_SLOTS — the reserved slot FLOOR for the " + "retain (memory write) lane, carved from that same total; unset means " + "upstream's own 0, i.e. no floor and retain competes for the shared " + "pool), 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).")
|
|
19487
19499
|
});
|
|
19488
19500
|
var MicrosoftWorkspaceConfigSchema = exports_external.object({
|
|
19489
19501
|
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)."),
|
|
@@ -19616,6 +19628,7 @@ var profileFields = {
|
|
|
19616
19628
|
file: exports_external.boolean().optional(),
|
|
19617
19629
|
isolation: exports_external.enum(["default", "strict"]).optional(),
|
|
19618
19630
|
directive_capture_nudge: exports_external.boolean().optional(),
|
|
19631
|
+
observation_scopes: ObservationScopesSchema,
|
|
19619
19632
|
recall: exports_external.object({
|
|
19620
19633
|
max_memories: exports_external.number().int().min(0).optional(),
|
|
19621
19634
|
cache_ttl_secs: exports_external.number().int().min(0).optional(),
|
|
@@ -20832,16 +20845,18 @@ function needsSelfBump(ownVersion, pin) {
|
|
|
20832
20845
|
return cmp !== null && cmp < 0;
|
|
20833
20846
|
}
|
|
20834
20847
|
function bumpHostdComposeImageTag(yaml, pin) {
|
|
20835
|
-
const re = /^(\s*image:\s*)(\S*switchroom-hostd):(\S+)\
|
|
20836
|
-
const
|
|
20837
|
-
if (
|
|
20848
|
+
const re = /^(\s*image:\s*)(\S*switchroom-hostd):(\S+)[ \t]*$/gm;
|
|
20849
|
+
const matches = [...yaml.matchAll(re)];
|
|
20850
|
+
if (matches.length === 0)
|
|
20838
20851
|
return null;
|
|
20839
|
-
const
|
|
20840
|
-
const
|
|
20852
|
+
const first = matches[0];
|
|
20853
|
+
const oldImageRef = `${first[2]}:${first[3]}`;
|
|
20854
|
+
const newImageRef = `${first[2]}:${pin}`;
|
|
20841
20855
|
return {
|
|
20842
|
-
yaml: yaml.replace(re, `$
|
|
20856
|
+
yaml: yaml.replace(re, (_m, indent, repo) => `${indent}${repo}:${pin}`),
|
|
20843
20857
|
oldImageRef,
|
|
20844
|
-
newImageRef
|
|
20858
|
+
newImageRef,
|
|
20859
|
+
bumpedCount: matches.length
|
|
20845
20860
|
};
|
|
20846
20861
|
}
|
|
20847
20862
|
function encodePendingRolloutMarker(m) {
|
|
@@ -21195,7 +21210,7 @@ function allocateAgentUid(name) {
|
|
|
21195
21210
|
}
|
|
21196
21211
|
|
|
21197
21212
|
// src/build-info.ts
|
|
21198
|
-
var VERSION = "0.19.
|
|
21213
|
+
var VERSION = "0.19.30";
|
|
21199
21214
|
|
|
21200
21215
|
// src/setup/hindsight-recall-tunables.ts
|
|
21201
21216
|
var RECALL_DEADLINE_HEADROOM_SECONDS = 2;
|
|
@@ -21433,7 +21448,8 @@ var HINDSIGHT_PERF_OVERRIDE_ONLY_KEYS = new Set([
|
|
|
21433
21448
|
"HINDSIGHT_API_WORKER_CONSOLIDATION_BANK_PRIORITY",
|
|
21434
21449
|
"HINDSIGHT_CE_DECISIVE_RELATIVE_GAP",
|
|
21435
21450
|
"HINDSIGHT_API_RECENCY_DECAY_LINEAR_WINDOW_DAYS",
|
|
21436
|
-
"HINDSIGHT_API_WORKER_MAX_SLOTS"
|
|
21451
|
+
"HINDSIGHT_API_WORKER_MAX_SLOTS",
|
|
21452
|
+
"HINDSIGHT_API_WORKER_RETAIN_MAX_SLOTS"
|
|
21437
21453
|
]);
|
|
21438
21454
|
var HINDSIGHT_PERF_ENV_KEYS = new Set([
|
|
21439
21455
|
...[
|
|
@@ -30055,6 +30071,8 @@ class HostdServer {
|
|
|
30055
30071
|
};
|
|
30056
30072
|
copyFileSync3(composePath, bakPath);
|
|
30057
30073
|
writeFileSync6(composePath, bumped.yaml, "utf8");
|
|
30074
|
+
process.stderr.write(`hostd: self-bump rewrote ${bumped.bumpedCount} switchroom-hostd ` + `image line(s) → ${bumped.newImageRef}
|
|
30075
|
+
`);
|
|
30058
30076
|
writeFileSync6(markerPath, encodePendingRolloutMarker(marker), {
|
|
30059
30077
|
mode: 384
|
|
30060
30078
|
});
|
|
@@ -4012,10 +4012,22 @@ var init_zod = __esm(() => {
|
|
|
4012
4012
|
init_external();
|
|
4013
4013
|
});
|
|
4014
4014
|
|
|
4015
|
+
// src/memory/observation-scopes.ts
|
|
4016
|
+
var OBSERVATION_SCOPES;
|
|
4017
|
+
var init_observation_scopes = __esm(() => {
|
|
4018
|
+
OBSERVATION_SCOPES = [
|
|
4019
|
+
"per_tag",
|
|
4020
|
+
"combined",
|
|
4021
|
+
"all_combinations",
|
|
4022
|
+
"shared"
|
|
4023
|
+
];
|
|
4024
|
+
});
|
|
4025
|
+
|
|
4015
4026
|
// src/config/schema.ts
|
|
4016
|
-
var CodeRepoEntrySchema, AgentBindMountSchema, HttpDiffPollSchema, PollSpecSchema, TelegramMessageActionSchema, WebhookActionSchema, ActionSpecSchema, ScheduleEntrySchema, AgentSoulSchema, AgentToolsSchema, AgentMemorySchema, HookEntrySchema, AgentHooksSchema, SubagentSchema, SessionSchema, SessionContinuitySchema, webhookDispatchRule, TelegramChannelSchema, ChannelsSchema, TIMEZONE_REGEX, ApproverIdSchema, GoogleWorkspaceTierSchema, GoogleWorkspaceConfigSchema, LiteLLMConfigSchema, HindsightPerOpLlmSchema, HindsightConfigSchema, MicrosoftWorkspaceConfigSchema, NotionWorkspaceConfigSchema, AgentGoogleWorkspaceConfigSchema, MicrosoftAccountEmailSchema, MicrosoftToolTokenSchema, MicrosoftAccountBindingSchema, AgentMicrosoftWorkspaceConfigSchema, AgentNotionWorkspaceConfigSchema, ReactionsSchema, ReactionDispatchSchema, releaseBlockFields, ReleaseBlock, RootReleaseBlock, NetworkIsolationSchema, servesField, knowsField, profileFields, ProfileSchema, _omitExtends, defaultsFields, AgentDefaultsSchema, AgentSchema, TelegramConfigSchema, MemoryBackendConfigSchema, VaultConfigSchema, QuotaConfigSchema, AutoReleaseCheckSchema, HostControlConfigSchema, WebServiceConfigSchema, FleetHealthConfigSchema, HostdConfigSchema, CronEgressSchema, CronConfigSchema, UserSchema, SwitchroomConfigSchema;
|
|
4027
|
+
var CodeRepoEntrySchema, AgentBindMountSchema, HttpDiffPollSchema, PollSpecSchema, TelegramMessageActionSchema, WebhookActionSchema, ActionSpecSchema, ScheduleEntrySchema, AgentSoulSchema, AgentToolsSchema, ObservationScopesSchema, AgentMemorySchema, HookEntrySchema, AgentHooksSchema, SubagentSchema, SessionSchema, SessionContinuitySchema, webhookDispatchRule, TelegramChannelSchema, ChannelsSchema, TIMEZONE_REGEX, ApproverIdSchema, GoogleWorkspaceTierSchema, GoogleWorkspaceConfigSchema, LiteLLMConfigSchema, HindsightPerOpLlmSchema, HindsightConfigSchema, MicrosoftWorkspaceConfigSchema, NotionWorkspaceConfigSchema, AgentGoogleWorkspaceConfigSchema, MicrosoftAccountEmailSchema, MicrosoftToolTokenSchema, MicrosoftAccountBindingSchema, AgentMicrosoftWorkspaceConfigSchema, AgentNotionWorkspaceConfigSchema, ReactionsSchema, ReactionDispatchSchema, releaseBlockFields, ReleaseBlock, RootReleaseBlock, NetworkIsolationSchema, servesField, knowsField, profileFields, ProfileSchema, _omitExtends, defaultsFields, AgentDefaultsSchema, AgentSchema, TelegramConfigSchema, MemoryBackendConfigSchema, VaultConfigSchema, QuotaConfigSchema, AutoReleaseCheckSchema, HostControlConfigSchema, WebServiceConfigSchema, FleetHealthConfigSchema, HostdConfigSchema, CronEgressSchema, CronConfigSchema, UserSchema, SwitchroomConfigSchema;
|
|
4017
4028
|
var init_schema = __esm(() => {
|
|
4018
4029
|
init_zod();
|
|
4030
|
+
init_observation_scopes();
|
|
4019
4031
|
CodeRepoEntrySchema = exports_external.object({
|
|
4020
4032
|
name: exports_external.string().describe("Short alias used when claiming (e.g. 'switchroom')"),
|
|
4021
4033
|
source: exports_external.string().describe("Absolute or home-relative path to the repo (e.g. ~/code/switchroom)"),
|
|
@@ -4127,6 +4139,7 @@ var init_schema = __esm(() => {
|
|
|
4127
4139
|
allow: exports_external.array(exports_external.string()).default([]).describe("Allowed tools (use ['all'] for unrestricted)"),
|
|
4128
4140
|
deny: exports_external.array(exports_external.string()).default([]).describe("Denied tools (overrides allow)")
|
|
4129
4141
|
}).optional();
|
|
4142
|
+
ObservationScopesSchema = exports_external.enum(OBSERVATION_SCOPES).optional().describe("Per-row observation scope stamped on every memory this agent " + `retains. "shared" makes Hindsight's consolidation write the ` + "resulting observations into ONE global untagged scope instead of a " + "scope per tag — what several agents pooling one bank need so their " + "observations actually merge rather than sitting in parallel " + "per-tag silos. OMITTED BY DEFAULT: unset means the field never goes " + "on the wire and the engine's own default stands, which is the " + "shipped behaviour. Applies to every retain path (Stop hook, " + "sidechain, boot reconcile, queue drain, backfill, session-handoff " + "mirror) and is carried on the queued payload, so a retain that fails " + "now and drains later still lands in this scope. Accepted values: " + `${OBSERVATION_SCOPES.join(", ")}. ` + "Cascade: override (per-agent wins over default).");
|
|
4130
4143
|
AgentMemorySchema = exports_external.object({
|
|
4131
4144
|
collection: exports_external.string().describe("Hindsight collection name for this agent"),
|
|
4132
4145
|
auto_recall: exports_external.boolean().default(true).describe("Auto-search memories before each response"),
|
|
@@ -4161,6 +4174,7 @@ var init_schema = __esm(() => {
|
|
|
4161
4174
|
empathy: exports_external.number().int().min(1).max(5).optional().describe("How much the bank weights emotional/relational context (1-5; engine default 3).")
|
|
4162
4175
|
}).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`)."),
|
|
4163
4176
|
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)."),
|
|
4177
|
+
observation_scopes: ObservationScopesSchema,
|
|
4164
4178
|
recall: exports_external.object({
|
|
4165
4179
|
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)."),
|
|
4166
4180
|
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."),
|
|
@@ -4426,7 +4440,7 @@ var init_schema = __esm(() => {
|
|
|
4426
4440
|
reflect: HindsightPerOpLlmSchema.optional().describe("Per-op override for the `reflect` LLM op (synthesis / mental-model " + "refresh). Emits `HINDSIGHT_API_REFLECT_LLM_*`. Absent → uses global."),
|
|
4427
4441
|
consolidation: HindsightPerOpLlmSchema.optional().describe("Per-op override for the `consolidation` LLM op (background memory " + "merge). Emits `HINDSIGHT_API_CONSOLIDATION_LLM_*`. Absent → global.")
|
|
4428
4442
|
}).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."),
|
|
4429
|
-
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).")
|
|
4443
|
+
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; and " + "HINDSIGHT_API_WORKER_RETAIN_MAX_SLOTS — the reserved slot FLOOR for the " + "retain (memory write) lane, carved from that same total; unset means " + "upstream's own 0, i.e. no floor and retain competes for the shared " + "pool), 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).")
|
|
4430
4444
|
});
|
|
4431
4445
|
MicrosoftWorkspaceConfigSchema = exports_external.object({
|
|
4432
4446
|
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)."),
|
|
@@ -4559,6 +4573,7 @@ var init_schema = __esm(() => {
|
|
|
4559
4573
|
file: exports_external.boolean().optional(),
|
|
4560
4574
|
isolation: exports_external.enum(["default", "strict"]).optional(),
|
|
4561
4575
|
directive_capture_nudge: exports_external.boolean().optional(),
|
|
4576
|
+
observation_scopes: ObservationScopesSchema,
|
|
4562
4577
|
recall: exports_external.object({
|
|
4563
4578
|
max_memories: exports_external.number().int().min(0).optional(),
|
|
4564
4579
|
cache_ttl_secs: exports_external.number().int().min(0).optional(),
|
|
@@ -19410,7 +19425,8 @@ var HINDSIGHT_PERF_OVERRIDE_ONLY_KEYS = new Set([
|
|
|
19410
19425
|
"HINDSIGHT_API_WORKER_CONSOLIDATION_BANK_PRIORITY",
|
|
19411
19426
|
"HINDSIGHT_CE_DECISIVE_RELATIVE_GAP",
|
|
19412
19427
|
"HINDSIGHT_API_RECENCY_DECAY_LINEAR_WINDOW_DAYS",
|
|
19413
|
-
"HINDSIGHT_API_WORKER_MAX_SLOTS"
|
|
19428
|
+
"HINDSIGHT_API_WORKER_MAX_SLOTS",
|
|
19429
|
+
"HINDSIGHT_API_WORKER_RETAIN_MAX_SLOTS"
|
|
19414
19430
|
]);
|
|
19415
19431
|
var HINDSIGHT_PERF_ENV_KEYS = new Set([
|
|
19416
19432
|
...[
|
|
@@ -4012,10 +4012,22 @@ var init_zod = __esm(() => {
|
|
|
4012
4012
|
init_external();
|
|
4013
4013
|
});
|
|
4014
4014
|
|
|
4015
|
+
// src/memory/observation-scopes.ts
|
|
4016
|
+
var OBSERVATION_SCOPES;
|
|
4017
|
+
var init_observation_scopes = __esm(() => {
|
|
4018
|
+
OBSERVATION_SCOPES = [
|
|
4019
|
+
"per_tag",
|
|
4020
|
+
"combined",
|
|
4021
|
+
"all_combinations",
|
|
4022
|
+
"shared"
|
|
4023
|
+
];
|
|
4024
|
+
});
|
|
4025
|
+
|
|
4015
4026
|
// src/config/schema.ts
|
|
4016
|
-
var CodeRepoEntrySchema, AgentBindMountSchema, HttpDiffPollSchema, PollSpecSchema, TelegramMessageActionSchema, WebhookActionSchema, ActionSpecSchema, ScheduleEntrySchema, AgentSoulSchema, AgentToolsSchema, AgentMemorySchema, HookEntrySchema, AgentHooksSchema, SubagentSchema, SessionSchema, SessionContinuitySchema, webhookDispatchRule, TelegramChannelSchema, ChannelsSchema, TIMEZONE_REGEX, ApproverIdSchema, GoogleWorkspaceTierSchema, GoogleWorkspaceConfigSchema, LiteLLMConfigSchema, HindsightPerOpLlmSchema, HindsightConfigSchema, MicrosoftWorkspaceConfigSchema, NotionWorkspaceConfigSchema, AgentGoogleWorkspaceConfigSchema, MicrosoftAccountEmailSchema, MicrosoftToolTokenSchema, MicrosoftAccountBindingSchema, AgentMicrosoftWorkspaceConfigSchema, AgentNotionWorkspaceConfigSchema, ReactionsSchema, ReactionDispatchSchema, releaseBlockFields, ReleaseBlock, RootReleaseBlock, NetworkIsolationSchema, servesField, knowsField, profileFields, ProfileSchema, _omitExtends, defaultsFields, AgentDefaultsSchema, AgentSchema, TelegramConfigSchema, MemoryBackendConfigSchema, VaultConfigSchema, QuotaConfigSchema, AutoReleaseCheckSchema, HostControlConfigSchema, WebServiceConfigSchema, FleetHealthConfigSchema, HostdConfigSchema, CronEgressSchema, CronConfigSchema, UserSchema, SwitchroomConfigSchema;
|
|
4027
|
+
var CodeRepoEntrySchema, AgentBindMountSchema, HttpDiffPollSchema, PollSpecSchema, TelegramMessageActionSchema, WebhookActionSchema, ActionSpecSchema, ScheduleEntrySchema, AgentSoulSchema, AgentToolsSchema, ObservationScopesSchema, AgentMemorySchema, HookEntrySchema, AgentHooksSchema, SubagentSchema, SessionSchema, SessionContinuitySchema, webhookDispatchRule, TelegramChannelSchema, ChannelsSchema, TIMEZONE_REGEX, ApproverIdSchema, GoogleWorkspaceTierSchema, GoogleWorkspaceConfigSchema, LiteLLMConfigSchema, HindsightPerOpLlmSchema, HindsightConfigSchema, MicrosoftWorkspaceConfigSchema, NotionWorkspaceConfigSchema, AgentGoogleWorkspaceConfigSchema, MicrosoftAccountEmailSchema, MicrosoftToolTokenSchema, MicrosoftAccountBindingSchema, AgentMicrosoftWorkspaceConfigSchema, AgentNotionWorkspaceConfigSchema, ReactionsSchema, ReactionDispatchSchema, releaseBlockFields, ReleaseBlock, RootReleaseBlock, NetworkIsolationSchema, servesField, knowsField, profileFields, ProfileSchema, _omitExtends, defaultsFields, AgentDefaultsSchema, AgentSchema, TelegramConfigSchema, MemoryBackendConfigSchema, VaultConfigSchema, QuotaConfigSchema, AutoReleaseCheckSchema, HostControlConfigSchema, WebServiceConfigSchema, FleetHealthConfigSchema, HostdConfigSchema, CronEgressSchema, CronConfigSchema, UserSchema, SwitchroomConfigSchema;
|
|
4017
4028
|
var init_schema = __esm(() => {
|
|
4018
4029
|
init_zod();
|
|
4030
|
+
init_observation_scopes();
|
|
4019
4031
|
CodeRepoEntrySchema = exports_external.object({
|
|
4020
4032
|
name: exports_external.string().describe("Short alias used when claiming (e.g. 'switchroom')"),
|
|
4021
4033
|
source: exports_external.string().describe("Absolute or home-relative path to the repo (e.g. ~/code/switchroom)"),
|
|
@@ -4127,6 +4139,7 @@ var init_schema = __esm(() => {
|
|
|
4127
4139
|
allow: exports_external.array(exports_external.string()).default([]).describe("Allowed tools (use ['all'] for unrestricted)"),
|
|
4128
4140
|
deny: exports_external.array(exports_external.string()).default([]).describe("Denied tools (overrides allow)")
|
|
4129
4141
|
}).optional();
|
|
4142
|
+
ObservationScopesSchema = exports_external.enum(OBSERVATION_SCOPES).optional().describe("Per-row observation scope stamped on every memory this agent " + `retains. "shared" makes Hindsight's consolidation write the ` + "resulting observations into ONE global untagged scope instead of a " + "scope per tag — what several agents pooling one bank need so their " + "observations actually merge rather than sitting in parallel " + "per-tag silos. OMITTED BY DEFAULT: unset means the field never goes " + "on the wire and the engine's own default stands, which is the " + "shipped behaviour. Applies to every retain path (Stop hook, " + "sidechain, boot reconcile, queue drain, backfill, session-handoff " + "mirror) and is carried on the queued payload, so a retain that fails " + "now and drains later still lands in this scope. Accepted values: " + `${OBSERVATION_SCOPES.join(", ")}. ` + "Cascade: override (per-agent wins over default).");
|
|
4130
4143
|
AgentMemorySchema = exports_external.object({
|
|
4131
4144
|
collection: exports_external.string().describe("Hindsight collection name for this agent"),
|
|
4132
4145
|
auto_recall: exports_external.boolean().default(true).describe("Auto-search memories before each response"),
|
|
@@ -4161,6 +4174,7 @@ var init_schema = __esm(() => {
|
|
|
4161
4174
|
empathy: exports_external.number().int().min(1).max(5).optional().describe("How much the bank weights emotional/relational context (1-5; engine default 3).")
|
|
4162
4175
|
}).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`)."),
|
|
4163
4176
|
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)."),
|
|
4177
|
+
observation_scopes: ObservationScopesSchema,
|
|
4164
4178
|
recall: exports_external.object({
|
|
4165
4179
|
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)."),
|
|
4166
4180
|
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."),
|
|
@@ -4426,7 +4440,7 @@ var init_schema = __esm(() => {
|
|
|
4426
4440
|
reflect: HindsightPerOpLlmSchema.optional().describe("Per-op override for the `reflect` LLM op (synthesis / mental-model " + "refresh). Emits `HINDSIGHT_API_REFLECT_LLM_*`. Absent → uses global."),
|
|
4427
4441
|
consolidation: HindsightPerOpLlmSchema.optional().describe("Per-op override for the `consolidation` LLM op (background memory " + "merge). Emits `HINDSIGHT_API_CONSOLIDATION_LLM_*`. Absent → global.")
|
|
4428
4442
|
}).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."),
|
|
4429
|
-
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).")
|
|
4443
|
+
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; and " + "HINDSIGHT_API_WORKER_RETAIN_MAX_SLOTS — the reserved slot FLOOR for the " + "retain (memory write) lane, carved from that same total; unset means " + "upstream's own 0, i.e. no floor and retain competes for the shared " + "pool), 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).")
|
|
4430
4444
|
});
|
|
4431
4445
|
MicrosoftWorkspaceConfigSchema = exports_external.object({
|
|
4432
4446
|
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)."),
|
|
@@ -4559,6 +4573,7 @@ var init_schema = __esm(() => {
|
|
|
4559
4573
|
file: exports_external.boolean().optional(),
|
|
4560
4574
|
isolation: exports_external.enum(["default", "strict"]).optional(),
|
|
4561
4575
|
directive_capture_nudge: exports_external.boolean().optional(),
|
|
4576
|
+
observation_scopes: ObservationScopesSchema,
|
|
4562
4577
|
recall: exports_external.object({
|
|
4563
4578
|
max_memories: exports_external.number().int().min(0).optional(),
|
|
4564
4579
|
cache_ttl_secs: exports_external.number().int().min(0).optional(),
|
|
@@ -19083,7 +19098,8 @@ var HINDSIGHT_PERF_OVERRIDE_ONLY_KEYS = new Set([
|
|
|
19083
19098
|
"HINDSIGHT_API_WORKER_CONSOLIDATION_BANK_PRIORITY",
|
|
19084
19099
|
"HINDSIGHT_CE_DECISIVE_RELATIVE_GAP",
|
|
19085
19100
|
"HINDSIGHT_API_RECENCY_DECAY_LINEAR_WINDOW_DAYS",
|
|
19086
|
-
"HINDSIGHT_API_WORKER_MAX_SLOTS"
|
|
19101
|
+
"HINDSIGHT_API_WORKER_MAX_SLOTS",
|
|
19102
|
+
"HINDSIGHT_API_WORKER_RETAIN_MAX_SLOTS"
|
|
19087
19103
|
]);
|
|
19088
19104
|
var HINDSIGHT_PERF_ENV_KEYS = new Set([
|
|
19089
19105
|
...[
|
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.19.
|
|
4
|
+
"version": "0.19.30",
|
|
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": {
|
|
@@ -999,6 +999,15 @@ export HINDSIGHT_RECALL_SKIP_TRIVIAL={{hindsightRecallSkipTrivial}}
|
|
|
999
999
|
{{#if hindsightDirectiveCaptureNudge}}
|
|
1000
1000
|
export HINDSIGHT_DIRECTIVE_CAPTURE_NUDGE={{hindsightDirectiveCaptureNudge}}
|
|
1001
1001
|
{{/if}}
|
|
1002
|
+
# Per-row observation scope (memory.observation_scopes cascade). Stamped on
|
|
1003
|
+
# every retain — Stop hook, sidechain, boot reconcile, queue drain, backfill.
|
|
1004
|
+
# "shared" makes consolidation pool this agent's observations into ONE global
|
|
1005
|
+
# untagged scope instead of a scope per tag. Export ONLY when the operator set
|
|
1006
|
+
# it: unset means the field never goes on the wire and the engine default
|
|
1007
|
+
# stands, which is the shipped behaviour.
|
|
1008
|
+
{{#if hindsightObservationScopesQ}}
|
|
1009
|
+
export HINDSIGHT_OBSERVATION_SCOPES={{{hindsightObservationScopesQ}}}
|
|
1010
|
+
{{/if}}
|
|
1002
1011
|
# PR6 — supergroup-mode topic tagging. JSON map of {alias: thread_id}
|
|
1003
1012
|
# parsed by retain.py + recall.py to (a) stamp chat_id/thread_id/topic_alias
|
|
1004
1013
|
# into retained memory metadata and (b) emit a "Current topic: …" preamble
|
|
@@ -21552,10 +21552,22 @@ var require_dist = __commonJS((exports) => {
|
|
|
21552
21552
|
exports.visitAsync = visit.visitAsync;
|
|
21553
21553
|
});
|
|
21554
21554
|
|
|
21555
|
+
// ../src/memory/observation-scopes.ts
|
|
21556
|
+
var OBSERVATION_SCOPES;
|
|
21557
|
+
var init_observation_scopes = __esm(() => {
|
|
21558
|
+
OBSERVATION_SCOPES = [
|
|
21559
|
+
"per_tag",
|
|
21560
|
+
"combined",
|
|
21561
|
+
"all_combinations",
|
|
21562
|
+
"shared"
|
|
21563
|
+
];
|
|
21564
|
+
});
|
|
21565
|
+
|
|
21555
21566
|
// ../src/config/schema.ts
|
|
21556
|
-
var CodeRepoEntrySchema, AgentBindMountSchema, HttpDiffPollSchema, PollSpecSchema, TelegramMessageActionSchema, WebhookActionSchema, ActionSpecSchema, ScheduleEntrySchema, AgentSoulSchema, AgentToolsSchema, AgentMemorySchema, HookEntrySchema, AgentHooksSchema, SubagentSchema, SessionSchema, SessionContinuitySchema, webhookDispatchRule, TelegramChannelSchema, ChannelsSchema, TIMEZONE_REGEX, ApproverIdSchema, GoogleWorkspaceTierSchema, GoogleWorkspaceConfigSchema, LiteLLMConfigSchema, HindsightPerOpLlmSchema, HindsightConfigSchema, MicrosoftWorkspaceConfigSchema, NotionWorkspaceConfigSchema, AgentGoogleWorkspaceConfigSchema, MicrosoftAccountEmailSchema, MicrosoftToolTokenSchema, MicrosoftAccountBindingSchema, AgentMicrosoftWorkspaceConfigSchema, AgentNotionWorkspaceConfigSchema, ReactionsSchema, ReactionDispatchSchema, releaseBlockFields, ReleaseBlock, RootReleaseBlock, NetworkIsolationSchema, servesField, knowsField, profileFields, ProfileSchema, _omitExtends, defaultsFields, AgentDefaultsSchema, AgentSchema, TelegramConfigSchema, MemoryBackendConfigSchema, VaultConfigSchema, QuotaConfigSchema, AutoReleaseCheckSchema, HostControlConfigSchema, WebServiceConfigSchema, FleetHealthConfigSchema, HostdConfigSchema, CronEgressSchema, CronConfigSchema, UserSchema, SwitchroomConfigSchema;
|
|
21567
|
+
var CodeRepoEntrySchema, AgentBindMountSchema, HttpDiffPollSchema, PollSpecSchema, TelegramMessageActionSchema, WebhookActionSchema, ActionSpecSchema, ScheduleEntrySchema, AgentSoulSchema, AgentToolsSchema, ObservationScopesSchema, AgentMemorySchema, HookEntrySchema, AgentHooksSchema, SubagentSchema, SessionSchema, SessionContinuitySchema, webhookDispatchRule, TelegramChannelSchema, ChannelsSchema, TIMEZONE_REGEX, ApproverIdSchema, GoogleWorkspaceTierSchema, GoogleWorkspaceConfigSchema, LiteLLMConfigSchema, HindsightPerOpLlmSchema, HindsightConfigSchema, MicrosoftWorkspaceConfigSchema, NotionWorkspaceConfigSchema, AgentGoogleWorkspaceConfigSchema, MicrosoftAccountEmailSchema, MicrosoftToolTokenSchema, MicrosoftAccountBindingSchema, AgentMicrosoftWorkspaceConfigSchema, AgentNotionWorkspaceConfigSchema, ReactionsSchema, ReactionDispatchSchema, releaseBlockFields, ReleaseBlock, RootReleaseBlock, NetworkIsolationSchema, servesField, knowsField, profileFields, ProfileSchema, _omitExtends, defaultsFields, AgentDefaultsSchema, AgentSchema, TelegramConfigSchema, MemoryBackendConfigSchema, VaultConfigSchema, QuotaConfigSchema, AutoReleaseCheckSchema, HostControlConfigSchema, WebServiceConfigSchema, FleetHealthConfigSchema, HostdConfigSchema, CronEgressSchema, CronConfigSchema, UserSchema, SwitchroomConfigSchema;
|
|
21557
21568
|
var init_schema = __esm(() => {
|
|
21558
21569
|
init_zod();
|
|
21570
|
+
init_observation_scopes();
|
|
21559
21571
|
CodeRepoEntrySchema = exports_external.object({
|
|
21560
21572
|
name: exports_external.string().describe("Short alias used when claiming (e.g. 'switchroom')"),
|
|
21561
21573
|
source: exports_external.string().describe("Absolute or home-relative path to the repo (e.g. ~/code/switchroom)"),
|
|
@@ -21667,6 +21679,7 @@ var init_schema = __esm(() => {
|
|
|
21667
21679
|
allow: exports_external.array(exports_external.string()).default([]).describe("Allowed tools (use ['all'] for unrestricted)"),
|
|
21668
21680
|
deny: exports_external.array(exports_external.string()).default([]).describe("Denied tools (overrides allow)")
|
|
21669
21681
|
}).optional();
|
|
21682
|
+
ObservationScopesSchema = exports_external.enum(OBSERVATION_SCOPES).optional().describe("Per-row observation scope stamped on every memory this agent " + `retains. "shared" makes Hindsight's consolidation write the ` + "resulting observations into ONE global untagged scope instead of a " + "scope per tag \u2014 what several agents pooling one bank need so their " + "observations actually merge rather than sitting in parallel " + "per-tag silos. OMITTED BY DEFAULT: unset means the field never goes " + "on the wire and the engine's own default stands, which is the " + "shipped behaviour. Applies to every retain path (Stop hook, " + "sidechain, boot reconcile, queue drain, backfill, session-handoff " + "mirror) and is carried on the queued payload, so a retain that fails " + "now and drains later still lands in this scope. Accepted values: " + `${OBSERVATION_SCOPES.join(", ")}. ` + "Cascade: override (per-agent wins over default).");
|
|
21670
21683
|
AgentMemorySchema = exports_external.object({
|
|
21671
21684
|
collection: exports_external.string().describe("Hindsight collection name for this agent"),
|
|
21672
21685
|
auto_recall: exports_external.boolean().default(true).describe("Auto-search memories before each response"),
|
|
@@ -21701,6 +21714,7 @@ var init_schema = __esm(() => {
|
|
|
21701
21714
|
empathy: exports_external.number().int().min(1).max(5).optional().describe("How much the bank weights emotional/relational context (1-5; engine default 3).")
|
|
21702
21715
|
}).optional().describe("Personality traits (1-5 each) steering how this bank frames recall, " + "reflect, and observation synthesis \u2014 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`)."),
|
|
21703
21716
|
directive_capture_nudge: exports_external.boolean().optional().describe("Deterministic directive-capture nudge (issue #2848 Stage B). When " + "on (switchroom default true \u2014 Stage A measured a ~55% miss rate on " + "durable corrections), the auto-recall hook regex-detects correction " + '/ standing-rule-shaped inbound ("always/never \u2026", "from now on \u2026", ' + `"stop doing \u2026", a stated preference, "that's wrong, it's \u2026") 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 \u2014 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)."),
|
|
21717
|
+
observation_scopes: ObservationScopesSchema,
|
|
21704
21718
|
recall: exports_external.object({
|
|
21705
21719
|
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)."),
|
|
21706
21720
|
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."),
|
|
@@ -21966,7 +21980,7 @@ var init_schema = __esm(() => {
|
|
|
21966
21980
|
reflect: HindsightPerOpLlmSchema.optional().describe("Per-op override for the `reflect` LLM op (synthesis / mental-model " + "refresh). Emits `HINDSIGHT_API_REFLECT_LLM_*`. Absent \u2192 uses global."),
|
|
21967
21981
|
consolidation: HindsightPerOpLlmSchema.optional().describe("Per-op override for the `consolidation` LLM op (background memory " + "merge). Emits `HINDSIGHT_API_CONSOLIDATION_LLM_*`. Absent \u2192 global.")
|
|
21968
21982
|
}).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."),
|
|
21969
|
-
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 \u2014 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 \u2014 a per-deployment " + "`bank-pattern:priority,...` map; unset means upstream's flat " + "created_at FIFO across banks; and " + "HINDSIGHT_CE_DECISIVE_RELATIVE_GAP \u2014 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 \u2014 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 \u2014 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 \u2014 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 \u2014 a blanket " + "passthrough would collide with the vars startHindsight() derives " + "itself (HINDSIGHT_API_PORT, the retain token/deadline budget).")
|
|
21983
|
+
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 \u2014 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 \u2014 a per-deployment " + "`bank-pattern:priority,...` map; unset means upstream's flat " + "created_at FIFO across banks; and " + "HINDSIGHT_CE_DECISIVE_RELATIVE_GAP \u2014 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 \u2014 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 \u2014 the worker poller's TOTAL in-flight " + "task budget, the pool WORKER_CONSOLIDATION_MAX_SLOTS reserves out of; " + "unset means upstream's own default; and " + "HINDSIGHT_API_WORKER_RETAIN_MAX_SLOTS \u2014 the reserved slot FLOOR for the " + "retain (memory write) lane, carved from that same total; unset means " + "upstream's own 0, i.e. no floor and retain competes for the shared " + "pool), 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 \u2014 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 \u2014 a blanket " + "passthrough would collide with the vars startHindsight() derives " + "itself (HINDSIGHT_API_PORT, the retain token/deadline budget).")
|
|
21970
21984
|
});
|
|
21971
21985
|
MicrosoftWorkspaceConfigSchema = exports_external.object({
|
|
21972
21986
|
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 \u2014 omit it to use " + "switchroom's shipped default Microsoft app (zero-config). " + "Set it only to bring your own Entra app (BYO)."),
|
|
@@ -22099,6 +22113,7 @@ var init_schema = __esm(() => {
|
|
|
22099
22113
|
file: exports_external.boolean().optional(),
|
|
22100
22114
|
isolation: exports_external.enum(["default", "strict"]).optional(),
|
|
22101
22115
|
directive_capture_nudge: exports_external.boolean().optional(),
|
|
22116
|
+
observation_scopes: ObservationScopesSchema,
|
|
22102
22117
|
recall: exports_external.object({
|
|
22103
22118
|
max_memories: exports_external.number().int().min(0).optional(),
|
|
22104
22119
|
cache_ttl_secs: exports_external.number().int().min(0).optional(),
|
|
@@ -84524,7 +84539,8 @@ var HINDSIGHT_PERF_OVERRIDE_ONLY_KEYS = new Set([
|
|
|
84524
84539
|
"HINDSIGHT_API_WORKER_CONSOLIDATION_BANK_PRIORITY",
|
|
84525
84540
|
"HINDSIGHT_CE_DECISIVE_RELATIVE_GAP",
|
|
84526
84541
|
"HINDSIGHT_API_RECENCY_DECAY_LINEAR_WINDOW_DAYS",
|
|
84527
|
-
"HINDSIGHT_API_WORKER_MAX_SLOTS"
|
|
84542
|
+
"HINDSIGHT_API_WORKER_MAX_SLOTS",
|
|
84543
|
+
"HINDSIGHT_API_WORKER_RETAIN_MAX_SLOTS"
|
|
84528
84544
|
]);
|
|
84529
84545
|
var HINDSIGHT_PERF_ENV_KEYS = new Set([
|
|
84530
84546
|
...[
|
|
@@ -98169,10 +98185,10 @@ function startOutboxSweep(deps) {
|
|
|
98169
98185
|
}
|
|
98170
98186
|
|
|
98171
98187
|
// ../src/build-info.ts
|
|
98172
|
-
var VERSION2 = "0.19.
|
|
98173
|
-
var COMMIT_SHA = "
|
|
98174
|
-
var COMMIT_DATE = "2026-07-
|
|
98175
|
-
var LATEST_PR =
|
|
98188
|
+
var VERSION2 = "0.19.30";
|
|
98189
|
+
var COMMIT_SHA = "efe8e2a7";
|
|
98190
|
+
var COMMIT_DATE = "2026-07-28T18:24:56Z";
|
|
98191
|
+
var LATEST_PR = 3923;
|
|
98176
98192
|
var COMMITS_AHEAD_OF_TAG = 0;
|
|
98177
98193
|
|
|
98178
98194
|
// gateway/boot-version.ts
|
|
@@ -2,6 +2,56 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
### Added (switchroom divergence)
|
|
6
|
+
|
|
7
|
+
- **Per-row `observation_scopes` on every retain.** Hindsight accepts and
|
|
8
|
+
stores an `observation_scopes` field per retained item; `"shared"` makes
|
|
9
|
+
consolidation write that item's observations into ONE global untagged scope
|
|
10
|
+
instead of a scope per tag. The plugin had no way to send it, so a bank
|
|
11
|
+
several agents write into could not pool their observations. Added as an
|
|
12
|
+
explicit `observation_scopes` kwarg on `HindsightClient.retain()` /
|
|
13
|
+
`_retain_one()` (`scripts/lib/client.py`), sourced from the new
|
|
14
|
+
`observationScopes` config key (`scripts/lib/config.py`, default `None`,
|
|
15
|
+
overridable by `HINDSIGHT_OBSERVATION_SCOPES`, which switchroom's
|
|
16
|
+
`start.sh` exports only when the operator set
|
|
17
|
+
`memory.observation_scopes`).
|
|
18
|
+
|
|
19
|
+
`build_retain_payload` (`scripts/retain.py`) puts the resolved value on the
|
|
20
|
+
payload, and all five hand-enumerated `client.retain()` callsites forward it:
|
|
21
|
+
`retain.py` (Stop hook), `subagent_retain.py` (sidechain),
|
|
22
|
+
`reconcile_tail.py` (boot reconcile), `drain_pending.py` (queue drain) and
|
|
23
|
+
`backfill_transcripts.py`. Each enumerates kwargs rather than splatting the
|
|
24
|
+
payload, so a missed one silently keeps writing per-tag scopes — every path
|
|
25
|
+
is pinned by a test.
|
|
26
|
+
|
|
27
|
+
Two durability properties are load-bearing. The scope rides ON the queued
|
|
28
|
+
payload (`lib/pending` copies the payload wholesale), so a retain that fails
|
|
29
|
+
now and drains hours later lands in the scope it was written for rather than
|
|
30
|
+
whatever config says at drain time. And `drain_pending._retry_one` reads it
|
|
31
|
+
with `entry.get(...)`: entries queued by a build that predates this field are
|
|
32
|
+
on disk right now, and a `KeyError` there would strand the last on-disk copy
|
|
33
|
+
of a turn (the #3244 silent-loss shape).
|
|
34
|
+
|
|
35
|
+
Default behaviour is unchanged and asserted as such: with the config unset
|
|
36
|
+
the kwarg is `None` and `_retain_one` omits the key from the request body
|
|
37
|
+
entirely rather than sending a null, so the body is byte-identical to a
|
|
38
|
+
pre-change client. Split retains carry the same scope on every part.
|
|
39
|
+
Tests: `scripts/tests/test_observation_scopes.py`, plus per-path pins in
|
|
40
|
+
`test_reconcile_durability.py`, `test_subagent_retain.py` and
|
|
41
|
+
`test_backfill.py`.
|
|
42
|
+
|
|
43
|
+
An off-list value RAISES rather than reaching the wire
|
|
44
|
+
(`resolve_observation_scopes` in `scripts/lib/config.py`, called from
|
|
45
|
+
`build_retain_payload`). Switchroom's config schema is the primary gate, but
|
|
46
|
+
it cannot see a hand-edited `settings.json` or a raw
|
|
47
|
+
`HINDSIGHT_OBSERVATION_SCOPES` export — and a scope is invisible after the
|
|
48
|
+
write, so a value quietly ignored today reads as a bank whose observations
|
|
49
|
+
never merged months later. Empty/whitespace stays UNSET, matching the
|
|
50
|
+
existing "an empty export hands authority back to the config file" idiom.
|
|
51
|
+
The accepted tuple (`OBSERVATION_SCOPES_VALUES`) is paired with
|
|
52
|
+
`src/memory/observation-scopes.ts` on the switchroom side; widening the set
|
|
53
|
+
means widening both.
|
|
54
|
+
|
|
5
55
|
### Changed (switchroom divergence)
|
|
6
56
|
|
|
7
57
|
- **`MAX_DIRECTIVES` 15 → 30, and truncation is no longer SILENT**
|
|
@@ -474,6 +474,7 @@ class Backfill:
|
|
|
474
474
|
tags=payload["tags"],
|
|
475
475
|
timeout=15,
|
|
476
476
|
async_processing=False, # commit-before-ack (daemon contract 2)
|
|
477
|
+
observation_scopes=payload.get("observation_scopes"),
|
|
477
478
|
)
|
|
478
479
|
except Exception as e:
|
|
479
480
|
debug_log(self.config, f"backfill: POST failed for {built['document_id']}: {e}")
|
|
@@ -600,6 +600,10 @@ def _retry_one(entry: dict, timeout: int) -> None:
|
|
|
600
600
|
tags=entry.get("tags"),
|
|
601
601
|
timeout=timeout,
|
|
602
602
|
async_processing=False,
|
|
603
|
+
# `.get` (not `[...]`): entries queued by a pre-#observation_scopes build
|
|
604
|
+
# are on disk RIGHT NOW and carry no such key. They must drain, not
|
|
605
|
+
# KeyError — a crash here loses the queue entry's only copy of a turn.
|
|
606
|
+
observation_scopes=entry.get("observation_scopes"),
|
|
603
607
|
)
|
|
604
608
|
|
|
605
609
|
|
|
@@ -177,9 +177,17 @@ class HindsightClient:
|
|
|
177
177
|
tags: Optional[list] = None,
|
|
178
178
|
timeout: int = 15,
|
|
179
179
|
async_processing: bool = True,
|
|
180
|
+
observation_scopes: Optional[str] = None,
|
|
180
181
|
) -> dict:
|
|
181
182
|
"""Retain content into a bank's memory.
|
|
182
183
|
|
|
184
|
+
``observation_scopes`` is a per-row Hindsight field controlling which
|
|
185
|
+
observation scope consolidation writes this item's observations into.
|
|
186
|
+
``"shared"`` puts them in ONE global untagged scope instead of a scope
|
|
187
|
+
per tag. ``None`` (the default) omits the field entirely, so the
|
|
188
|
+
engine's own default stands and the wire body is byte-identical to a
|
|
189
|
+
pre-``observation_scopes`` client.
|
|
190
|
+
|
|
183
191
|
By default posts with ``async=true`` so the server processes extraction
|
|
184
192
|
in the background (a 200 is an ack-of-receipt, not proof of durable
|
|
185
193
|
persistence). The context field helps Hindsight cluster memories by
|
|
@@ -253,6 +261,7 @@ class HindsightClient:
|
|
|
253
261
|
tags=tags,
|
|
254
262
|
timeout=part_timeout,
|
|
255
263
|
async_processing=async_processing,
|
|
264
|
+
observation_scopes=observation_scopes,
|
|
256
265
|
)
|
|
257
266
|
if total > 1 and isinstance(response, dict):
|
|
258
267
|
response = dict(response)
|
|
@@ -269,6 +278,7 @@ class HindsightClient:
|
|
|
269
278
|
tags: Optional[list],
|
|
270
279
|
timeout: int,
|
|
271
280
|
async_processing: bool,
|
|
281
|
+
observation_scopes: Optional[str] = None,
|
|
272
282
|
) -> dict:
|
|
273
283
|
"""POST exactly one retain item. Raises on any HTTP/transport error."""
|
|
274
284
|
path = f"/v1/default/banks/{urllib.parse.quote(bank_id, safe='')}/memories"
|
|
@@ -281,6 +291,10 @@ class HindsightClient:
|
|
|
281
291
|
item["context"] = context
|
|
282
292
|
if tags:
|
|
283
293
|
item["tags"] = tags
|
|
294
|
+
# Omitted entirely when unset — the engine default must stay in force
|
|
295
|
+
# and the body must match a pre-observation_scopes client byte for byte.
|
|
296
|
+
if observation_scopes:
|
|
297
|
+
item["observation_scopes"] = observation_scopes
|
|
284
298
|
body = {
|
|
285
299
|
"items": [item],
|
|
286
300
|
"async": bool(async_processing),
|