switchroom 0.19.38 → 0.19.40
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 +10 -1
- package/dist/auth-broker/index.js +15 -6
- package/dist/cli/notion-write-pretool.mjs +10 -1
- package/dist/cli/switchroom.js +1141 -566
- package/dist/host-control/main.js +220 -8
- package/dist/vault/approvals/kernel-server.js +15 -6
- package/dist/vault/broker/server.js +15 -6
- package/package.json +1 -1
- package/profiles/_base/start.sh.hbs +10 -0
- package/telegram-plugin/dist/bridge/bridge.js +8 -0
- package/telegram-plugin/dist/gateway/gateway.js +450 -206
- package/telegram-plugin/dist/server.js +8 -0
- package/telegram-plugin/gateway/backstop-delivery.ts +48 -0
- package/telegram-plugin/gateway/compaction-marker.ts +84 -0
- package/telegram-plugin/gateway/gateway.ts +3 -3
- package/telegram-plugin/gateway/handback-preturn-signal.ts +16 -0
- package/telegram-plugin/gateway/liveness-wiring.ts +15 -0
- package/telegram-plugin/gateway/outbound-send-path.ts +20 -0
- package/telegram-plugin/gateway/outbox-sweep.ts +116 -18
- package/telegram-plugin/gateway/silence-poke-session-event.ts +13 -0
- package/telegram-plugin/gateway/stream-render.ts +277 -18
- package/telegram-plugin/hooks/compaction-marker-precompact.mjs +70 -0
- package/telegram-plugin/hooks/hooks.json +11 -0
- package/telegram-plugin/hooks/tool-label-pretool.mjs +119 -1
- package/telegram-plugin/session-tail.ts +20 -0
- package/telegram-plugin/silence-poke.ts +28 -0
- package/telegram-plugin/tests/activity-ever-opened-sticky.test.ts +19 -12
- package/telegram-plugin/tests/fixtures/pretool-main-2.1.219.json +15 -0
- package/telegram-plugin/tests/fixtures/pretool-subagent-2.1.219.json +16 -0
- package/telegram-plugin/tests/outbox-delivery.test.ts +38 -1
- package/telegram-plugin/tests/outbox-flush-ack-claim-race.test.ts +213 -0
- package/telegram-plugin/tests/outbox-reply-then-recap-e2e.test.ts +1 -1
- package/telegram-plugin/tests/outbox-sweep-flood-breaker.test.ts +4 -4
- package/telegram-plugin/tests/outbox-sweep-listen-button.test.ts +71 -8
- package/telegram-plugin/tests/queued-card-surface.test.ts +262 -0
- package/telegram-plugin/tests/send-reply-golden.test.ts +47 -0
- package/telegram-plugin/tests/sidechain-label-filter-pretool.test.ts +272 -0
- package/telegram-plugin/tests/silence-poke-compaction.test.ts +222 -0
- package/vendor/hindsight-memory/scripts/lib/client.py +7 -4
- package/vendor/hindsight-memory/scripts/lib/config.py +151 -0
- package/vendor/hindsight-memory/scripts/retain.py +19 -15
- package/vendor/hindsight-memory/scripts/tests/test_backfill.py +14 -1
- package/vendor/hindsight-memory/scripts/tests/test_observation_scopes.py +192 -3
- package/vendor/hindsight-memory/scripts/tests/test_reconcile_durability.py +18 -2
- package/vendor/hindsight-memory/scripts/tests/test_subagent_retain.py +57 -4
|
@@ -10977,7 +10977,7 @@ var init_zod = __esm(() => {
|
|
|
10977
10977
|
});
|
|
10978
10978
|
|
|
10979
10979
|
// src/memory/observation-scopes.ts
|
|
10980
|
-
var OBSERVATION_SCOPES;
|
|
10980
|
+
var OBSERVATION_SCOPES, OBSERVATION_SCOPE_STRATEGIES;
|
|
10981
10981
|
var init_observation_scopes = __esm(() => {
|
|
10982
10982
|
OBSERVATION_SCOPES = [
|
|
10983
10983
|
"per_tag",
|
|
@@ -10985,10 +10985,16 @@ var init_observation_scopes = __esm(() => {
|
|
|
10985
10985
|
"all_combinations",
|
|
10986
10986
|
"shared"
|
|
10987
10987
|
];
|
|
10988
|
+
OBSERVATION_SCOPE_STRATEGIES = [
|
|
10989
|
+
"curated",
|
|
10990
|
+
"shared",
|
|
10991
|
+
"combined",
|
|
10992
|
+
"off"
|
|
10993
|
+
];
|
|
10988
10994
|
});
|
|
10989
10995
|
|
|
10990
10996
|
// src/config/schema.ts
|
|
10991
|
-
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;
|
|
10997
|
+
var CodeRepoEntrySchema, AgentBindMountSchema, HttpDiffPollSchema, PollSpecSchema, TelegramMessageActionSchema, WebhookActionSchema, ActionSpecSchema, ScheduleEntrySchema, AgentSoulSchema, AgentToolsSchema, ObservationScopesSchema, ObservationScopeStrategySchema, 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;
|
|
10992
10998
|
var init_schema = __esm(() => {
|
|
10993
10999
|
init_zod();
|
|
10994
11000
|
init_observation_scopes();
|
|
@@ -11103,7 +11109,8 @@ var init_schema = __esm(() => {
|
|
|
11103
11109
|
allow: exports_external.array(exports_external.string()).default([]).describe("Allowed tools (use ['all'] for unrestricted)"),
|
|
11104
11110
|
deny: exports_external.array(exports_external.string()).default([]).describe("Denied tools (overrides allow)")
|
|
11105
11111
|
}).optional();
|
|
11106
|
-
ObservationScopesSchema = exports_external.enum(OBSERVATION_SCOPES).optional().describe("
|
|
11112
|
+
ObservationScopesSchema = exports_external.enum(OBSERVATION_SCOPES).optional().describe("Operator PIN for the per-row observation scope, overriding the " + 'default `curated` strategy (see docs/configuration.md). "shared" ' + "forces Hindsight's consolidation to write EVERY observation into ONE " + "global untagged scope — what several agents pooling one bank want so " + "their observations merge rather than sitting in parallel silos. " + '"combined" is the pin form of opting out (restores the pre-#4035 ' + "engine default). UNSET does NOT mean 'nothing on the wire': the " + "`curated` strategy still computes a scope per retain. 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).");
|
|
11113
|
+
ObservationScopeStrategySchema = exports_external.enum(OBSERVATION_SCOPE_STRATEGIES).optional().describe("Per-retain strategy that decides how the observation scope is " + 'computed when no `observation_scopes` pin is set. "curated" ' + "(the default since #4035) strips volatile per-session provenance " + "tags from each retain's consolidation scope — keeping the stable " + "semantic ones on the source fact — so an agent's observations dedup " + "and merge across sessions instead of pocketing one-per-session. " + '"shared" sends every retain to the one global untagged scope. ' + '"combined" / "off" opt OUT: no per-row scope goes on the wire, ' + "restoring the pre-feature engine default (byte-identical to an " + "unconfigured client). OMITTED BY DEFAULT: unset leaves the plugin's " + "own default (`curated`) in force. A manual `observation_scopes` pin " + "wins over this strategy. Accepted values: " + `${OBSERVATION_SCOPE_STRATEGIES.join(", ")}. ` + "Cascade: override (per-agent wins over default).");
|
|
11107
11114
|
AgentMemorySchema = exports_external.object({
|
|
11108
11115
|
collection: exports_external.string().describe("Hindsight collection name for this agent"),
|
|
11109
11116
|
auto_recall: exports_external.boolean().default(true).describe("Auto-search memories before each response"),
|
|
@@ -11139,6 +11146,7 @@ var init_schema = __esm(() => {
|
|
|
11139
11146
|
}).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`)."),
|
|
11140
11147
|
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)."),
|
|
11141
11148
|
observation_scopes: ObservationScopesSchema,
|
|
11149
|
+
observation_scope_strategy: ObservationScopeStrategySchema,
|
|
11142
11150
|
recall: exports_external.object({
|
|
11143
11151
|
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)."),
|
|
11144
11152
|
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."),
|
|
@@ -11539,6 +11547,7 @@ var init_schema = __esm(() => {
|
|
|
11539
11547
|
isolation: exports_external.enum(["default", "strict"]).optional(),
|
|
11540
11548
|
directive_capture_nudge: exports_external.boolean().optional(),
|
|
11541
11549
|
observation_scopes: ObservationScopesSchema,
|
|
11550
|
+
observation_scope_strategy: ObservationScopeStrategySchema,
|
|
11542
11551
|
recall: exports_external.object({
|
|
11543
11552
|
max_memories: exports_external.number().int().min(0).optional(),
|
|
11544
11553
|
cache_ttl_secs: exports_external.number().int().min(0).optional(),
|
|
@@ -21311,7 +21320,7 @@ function allocateAgentUid(name) {
|
|
|
21311
21320
|
}
|
|
21312
21321
|
|
|
21313
21322
|
// src/build-info.ts
|
|
21314
|
-
var VERSION = "0.19.
|
|
21323
|
+
var VERSION = "0.19.40";
|
|
21315
21324
|
|
|
21316
21325
|
// src/setup/hindsight-recall-tunables.ts
|
|
21317
21326
|
var RECALL_DEADLINE_HEADROOM_SECONDS = 2;
|
|
@@ -21440,8 +21449,8 @@ var HINDSIGHT_PG_MEM_LIMIT_MIB_FOR_DERIVATION = 16 * 1024;
|
|
|
21440
21449
|
var HINDSIGHT_PG_APP_ANON_MIB = 2560;
|
|
21441
21450
|
var HINDSIGHT_PG_PAGE_CACHE_FLOOR_MIB = 2048;
|
|
21442
21451
|
var HINDSIGHT_PG_SHARED_BUFFERS_BUDGET_MIB = HINDSIGHT_PG_MEM_LIMIT_MIB_FOR_DERIVATION - HINDSIGHT_PG_APP_ANON_MIB - HINDSIGHT_PG_PAGE_CACHE_FLOOR_MIB;
|
|
21443
|
-
var HINDSIGHT_PG_DEFAULT_SHARED_BUFFERS_MIB =
|
|
21444
|
-
var HINDSIGHT_PG_DEFAULT_EFFECTIVE_CACHE_SIZE_MIB =
|
|
21452
|
+
var HINDSIGHT_PG_DEFAULT_SHARED_BUFFERS_MIB = 6144;
|
|
21453
|
+
var HINDSIGHT_PG_DEFAULT_EFFECTIVE_CACHE_SIZE_MIB = 12288;
|
|
21445
21454
|
function pgMib(mib) {
|
|
21446
21455
|
return `${mib}MB`;
|
|
21447
21456
|
}
|
|
@@ -21485,7 +21494,7 @@ var HINDSIGHT_DEFAULT_RERANKER_MAX_CANDIDATES = 150;
|
|
|
21485
21494
|
var HINDSIGHT_DEFAULT_RERANKER_LOCAL_MAX_CONCURRENT = 4;
|
|
21486
21495
|
var HINDSIGHT_DEFAULT_RECALL_MAX_CONCURRENT = 8;
|
|
21487
21496
|
var HINDSIGHT_DEFAULT_REFLECT_WALL_TIMEOUT_S = 600;
|
|
21488
|
-
var HINDSIGHT_DEFAULT_CONSOLIDATION_MAX_MEMORIES_PER_ROUND =
|
|
21497
|
+
var HINDSIGHT_DEFAULT_CONSOLIDATION_MAX_MEMORIES_PER_ROUND = 250;
|
|
21489
21498
|
var HINDSIGHT_DEFAULT_CONSOLIDATION_SLOT_LIMIT = 6;
|
|
21490
21499
|
var HINDSIGHT_DEFAULT_CONSOLIDATION_MAX_SLOTS = 1;
|
|
21491
21500
|
var HINDSIGHT_DEFAULT_RECENCY_DECAY_FUNCTION = "exponential";
|
|
@@ -28405,6 +28414,10 @@ function parseRolloutPhaseLine(line) {
|
|
|
28405
28414
|
"agent-done",
|
|
28406
28415
|
"persist-pin",
|
|
28407
28416
|
"web-refresh",
|
|
28417
|
+
"web-refresh-done",
|
|
28418
|
+
"hindsight-refresh",
|
|
28419
|
+
"hindsight-refresh-done",
|
|
28420
|
+
"hindsight-skipped",
|
|
28408
28421
|
"hostd-web-deferred",
|
|
28409
28422
|
"self-bump",
|
|
28410
28423
|
"self-bump-done"
|
|
@@ -29385,6 +29398,14 @@ function phaseLine(s) {
|
|
|
29385
29398
|
return "persisting pin";
|
|
29386
29399
|
case "web-refresh":
|
|
29387
29400
|
return "refreshing web dashboard (webd install)";
|
|
29401
|
+
case "web-refresh-done":
|
|
29402
|
+
return "web dashboard refreshed";
|
|
29403
|
+
case "hindsight-refresh":
|
|
29404
|
+
return "refreshing hindsight (memory backend recreate)";
|
|
29405
|
+
case "hindsight-refresh-done":
|
|
29406
|
+
return "hindsight refreshed";
|
|
29407
|
+
case "hindsight-skipped":
|
|
29408
|
+
return "hindsight — no container on this host; skipped";
|
|
29388
29409
|
case "hostd-web-deferred":
|
|
29389
29410
|
return "hostd refresh deferred (run host-side)";
|
|
29390
29411
|
case "self-bump":
|
|
@@ -29452,6 +29473,33 @@ function checklistLines(s, compact) {
|
|
|
29452
29473
|
}
|
|
29453
29474
|
return lines;
|
|
29454
29475
|
}
|
|
29476
|
+
function singletonGlyph(status) {
|
|
29477
|
+
switch (status) {
|
|
29478
|
+
case "updated":
|
|
29479
|
+
return "✓";
|
|
29480
|
+
case "updating":
|
|
29481
|
+
return "⏳";
|
|
29482
|
+
case "deferred":
|
|
29483
|
+
return "⧗";
|
|
29484
|
+
case "skipped":
|
|
29485
|
+
return "○";
|
|
29486
|
+
case "failed":
|
|
29487
|
+
return "✗";
|
|
29488
|
+
default:
|
|
29489
|
+
return "·";
|
|
29490
|
+
}
|
|
29491
|
+
}
|
|
29492
|
+
function singletonLines(s) {
|
|
29493
|
+
const singletons = s.singletons ?? [];
|
|
29494
|
+
if (singletons.length === 0)
|
|
29495
|
+
return [];
|
|
29496
|
+
const lines = ["**Singletons / shared:**"];
|
|
29497
|
+
for (const g of singletons) {
|
|
29498
|
+
const detail = g.detail ? ` — ${g.detail}` : "";
|
|
29499
|
+
lines.push(`- ${singletonGlyph(g.status)} \`${g.name}\`${detail}`);
|
|
29500
|
+
}
|
|
29501
|
+
return lines;
|
|
29502
|
+
}
|
|
29455
29503
|
function etaLine(s) {
|
|
29456
29504
|
if (s.m === undefined)
|
|
29457
29505
|
return null;
|
|
@@ -29485,6 +29533,7 @@ function renderWith(s, compact) {
|
|
|
29485
29533
|
const rolled = s.rolled ?? [];
|
|
29486
29534
|
const rolledCount = rolled.length;
|
|
29487
29535
|
const checklist = checklistLines(s, compact);
|
|
29536
|
+
const singletons = singletonLines(s);
|
|
29488
29537
|
const elapsedMs = s.elapsedMs ?? (s.startedAtMs !== undefined && s.nowMs !== undefined ? s.nowMs - s.startedAtMs : undefined);
|
|
29489
29538
|
if (s.terminal === "completed") {
|
|
29490
29539
|
const parts2 = [headerLine(s, "✅")];
|
|
@@ -29495,6 +29544,8 @@ function renderWith(s, compact) {
|
|
|
29495
29544
|
parts2.push(summary);
|
|
29496
29545
|
if (checklist.length > 0)
|
|
29497
29546
|
parts2.push("", ...checklist);
|
|
29547
|
+
if (singletons.length > 0)
|
|
29548
|
+
parts2.push("", ...singletons);
|
|
29498
29549
|
if (s.deferred !== false)
|
|
29499
29550
|
parts2.push("", ...deferredLines(s.target));
|
|
29500
29551
|
return parts2.join(`
|
|
@@ -29512,6 +29563,8 @@ function renderWith(s, compact) {
|
|
|
29512
29563
|
parts2.push("", `Re-running the roll will NOT fix this — the agents are already on ` + `target. Finish the stale component(s), then \`switchroom update --check\`.`);
|
|
29513
29564
|
if (checklist.length > 0)
|
|
29514
29565
|
parts2.push("", ...checklist);
|
|
29566
|
+
if (singletons.length > 0)
|
|
29567
|
+
parts2.push("", ...singletons);
|
|
29515
29568
|
return parts2.join(`
|
|
29516
29569
|
`);
|
|
29517
29570
|
}
|
|
@@ -29525,12 +29578,16 @@ function renderWith(s, compact) {
|
|
|
29525
29578
|
parts2.push(summary);
|
|
29526
29579
|
if (checklist.length > 0)
|
|
29527
29580
|
parts2.push("", ...checklist);
|
|
29581
|
+
if (singletons.length > 0)
|
|
29582
|
+
parts2.push("", ...singletons);
|
|
29528
29583
|
return parts2.join(`
|
|
29529
29584
|
`);
|
|
29530
29585
|
}
|
|
29531
29586
|
const parts = [headerLine(s, "⏳"), `**Phase:** ${phaseLine(s)}`];
|
|
29532
29587
|
if (checklist.length > 0)
|
|
29533
29588
|
parts.push("", ...checklist);
|
|
29589
|
+
if (singletons.length > 0)
|
|
29590
|
+
parts.push("", ...singletons);
|
|
29534
29591
|
const footer = [];
|
|
29535
29592
|
if (s.m !== undefined)
|
|
29536
29593
|
footer.push(`${rolledCount}/${s.m} rolled`);
|
|
@@ -31446,6 +31503,8 @@ ${pinNote}`.trim();
|
|
|
31446
31503
|
const drifted = entry.drifted ?? [];
|
|
31447
31504
|
const notes = entry.failed_step === "verify-components" ? [
|
|
31448
31505
|
`Unattended auto-update roll to ${entry.pin ?? "?"} rolled ` + `${(entry.rolled ?? []).length} agent(s) but did NOT converge the ` + `whole host: ${drifted.join(", ") || "one or more components"} ` + `still behind. Re-running the roll will not fix this — run the ` + `per-component install named in the rollout warnings, then ` + `\`switchroom update --check\` to confirm.`
|
|
31506
|
+
] : entry.failed_step === "ensure-banks" ? [
|
|
31507
|
+
`Unattended auto-update roll to ${entry.pin ?? "?"} rolled ` + `${(entry.rolled ?? []).length} agent(s) but could NOT create the ` + `Hindsight bank for: ${drifted.join(", ") || "one or more agents"}. ` + `Re-running the roll will not fix this — restart the named agent(s) ` + `to re-run bank creation now that Hindsight is back up.`
|
|
31449
31508
|
] : [
|
|
31450
31509
|
`Unattended auto-update roll to ${entry.pin ?? "?"} FAILED at ` + `${entry.failed_step ?? "unknown step"}` + `${entry.failed_agent ? ` (agent ${entry.failed_agent})` : ""}. ` + `Unattended retries of this version are disabled; fix the cause, ` + `then roll manually (\`switchroom rollout --pin ${entry.pin ?? "vX.Y.Z"}\`).`
|
|
31451
31510
|
];
|
|
@@ -31473,6 +31532,10 @@ ${pinNote}`.trim();
|
|
|
31473
31532
|
notes.push(`No automatic rollback: the agent fleet reached ` + `${entry.pin ?? "the target"} and the pin is committed — the gap is ` + `${(entry.drifted ?? []).join(", ") || "a component"}, not the fleet. ` + `Rolling back would move working agents backwards; converge the ` + `named component(s) forward instead.`);
|
|
31474
31533
|
return notes;
|
|
31475
31534
|
}
|
|
31535
|
+
if (entry.failed_step === "ensure-banks") {
|
|
31536
|
+
notes.push(`No automatic rollback: the agent fleet reached ` + `${entry.pin ?? "the target"} and the pin is committed — the gap is a ` + `missing Hindsight bank for ${(entry.drifted ?? []).join(", ") || "an agent"}, ` + `not the fleet. Restart the named agent(s) to re-create the bank; do ` + `not roll back.`);
|
|
31537
|
+
return notes;
|
|
31538
|
+
}
|
|
31476
31539
|
const beforeAnyAgent = entry.failed_step === "apply" || entry.failed_step === "restart-agent" && rolledCount === 0;
|
|
31477
31540
|
if (!beforeAnyAgent) {
|
|
31478
31541
|
notes.push(rolledCount > 0 ? `No automatic rollback: ${rolledCount} agent(s) already confirmed on ` + `${entry.pin ?? "the target"} past a green canary ` + `(${(entry.rolled ?? []).join(", ")}). The fleet is mixed — ` + `finish the roll or roll back manually.` : `No automatic rollback: the roll stopped at ` + `${entry.failed_step ?? "an unknown step"} before any change ` + `landed — nothing to revert. Verify host-side if unsure.`);
|
|
@@ -32314,6 +32377,26 @@ class SocketRolloutNarrationRelay {
|
|
|
32314
32377
|
}
|
|
32315
32378
|
|
|
32316
32379
|
// src/host-control/rollout-narrator.ts
|
|
32380
|
+
var SINGLETON_WEB = "switchroom-web";
|
|
32381
|
+
var SINGLETON_HINDSIGHT = "hindsight";
|
|
32382
|
+
var SINGLETON_HOSTD = "hostd";
|
|
32383
|
+
var SINGLETON_SHARED = "shared services (approval-kernel · auth-broker · vault-broker · voice)";
|
|
32384
|
+
function initialSingletons() {
|
|
32385
|
+
return [
|
|
32386
|
+
{ name: SINGLETON_WEB, status: "pending" },
|
|
32387
|
+
{ name: SINGLETON_HINDSIGHT, status: "pending" },
|
|
32388
|
+
{
|
|
32389
|
+
name: SINGLETON_HOSTD,
|
|
32390
|
+
status: "deferred",
|
|
32391
|
+
detail: "host-side (agent-invoked roll cannot recreate its own hostd)"
|
|
32392
|
+
},
|
|
32393
|
+
{
|
|
32394
|
+
name: SINGLETON_SHARED,
|
|
32395
|
+
status: "pending",
|
|
32396
|
+
detail: "self-heal with the first agent restart"
|
|
32397
|
+
}
|
|
32398
|
+
];
|
|
32399
|
+
}
|
|
32317
32400
|
var DEFAULT_DEBOUNCE_MS = 1500;
|
|
32318
32401
|
var MAX_POST_ATTEMPTS = 2;
|
|
32319
32402
|
|
|
@@ -32366,6 +32449,68 @@ class LogTailRolloutNarrator {
|
|
|
32366
32449
|
break;
|
|
32367
32450
|
}
|
|
32368
32451
|
}
|
|
32452
|
+
static patchSingleton(st, name, patch) {
|
|
32453
|
+
const row = st.singletons.find((g) => g.name === name);
|
|
32454
|
+
if (row)
|
|
32455
|
+
Object.assign(row, patch);
|
|
32456
|
+
}
|
|
32457
|
+
trackSingletonProgress(st, phase) {
|
|
32458
|
+
const patch = LogTailRolloutNarrator.patchSingleton;
|
|
32459
|
+
switch (phase.phase) {
|
|
32460
|
+
case "canary-pass":
|
|
32461
|
+
case "agent-done": {
|
|
32462
|
+
const shared = st.singletons.find((g) => g.name === SINGLETON_SHARED);
|
|
32463
|
+
if (shared && shared.status === "pending") {
|
|
32464
|
+
shared.status = "updating";
|
|
32465
|
+
shared.detail = "recreated with the first agent restart — verified at roll end";
|
|
32466
|
+
}
|
|
32467
|
+
break;
|
|
32468
|
+
}
|
|
32469
|
+
case "web-refresh":
|
|
32470
|
+
patch(st, SINGLETON_WEB, { status: "updating", detail: "webd install…" });
|
|
32471
|
+
break;
|
|
32472
|
+
case "web-refresh-done":
|
|
32473
|
+
patch(st, SINGLETON_WEB, { status: "updated", detail: undefined });
|
|
32474
|
+
break;
|
|
32475
|
+
case "hindsight-refresh":
|
|
32476
|
+
patch(st, SINGLETON_HINDSIGHT, {
|
|
32477
|
+
status: "updating",
|
|
32478
|
+
detail: "memory setup --recreate…"
|
|
32479
|
+
});
|
|
32480
|
+
break;
|
|
32481
|
+
case "hindsight-refresh-done":
|
|
32482
|
+
patch(st, SINGLETON_HINDSIGHT, { status: "updated", detail: undefined });
|
|
32483
|
+
break;
|
|
32484
|
+
case "hindsight-skipped":
|
|
32485
|
+
patch(st, SINGLETON_HINDSIGHT, {
|
|
32486
|
+
status: "skipped",
|
|
32487
|
+
detail: "no hindsight container on this host"
|
|
32488
|
+
});
|
|
32489
|
+
break;
|
|
32490
|
+
case "self-bump":
|
|
32491
|
+
patch(st, SINGLETON_HOSTD, {
|
|
32492
|
+
status: "updating",
|
|
32493
|
+
detail: "self-bump to the target…"
|
|
32494
|
+
});
|
|
32495
|
+
break;
|
|
32496
|
+
case "self-bump-done":
|
|
32497
|
+
patch(st, SINGLETON_HOSTD, {
|
|
32498
|
+
status: "updated",
|
|
32499
|
+
detail: "self-bump; template regen stays host-side"
|
|
32500
|
+
});
|
|
32501
|
+
break;
|
|
32502
|
+
case "hostd-web-deferred": {
|
|
32503
|
+
const hostd = st.singletons.find((g) => g.name === SINGLETON_HOSTD);
|
|
32504
|
+
if (hostd && hostd.status !== "updated") {
|
|
32505
|
+
hostd.status = "deferred";
|
|
32506
|
+
hostd.detail = "host-side";
|
|
32507
|
+
}
|
|
32508
|
+
break;
|
|
32509
|
+
}
|
|
32510
|
+
default:
|
|
32511
|
+
break;
|
|
32512
|
+
}
|
|
32513
|
+
}
|
|
32369
32514
|
static seqFor(phase) {
|
|
32370
32515
|
switch (phase.phase) {
|
|
32371
32516
|
case "self-bump":
|
|
@@ -32385,7 +32530,12 @@ class LogTailRolloutNarrator {
|
|
|
32385
32530
|
case "agent-done":
|
|
32386
32531
|
return 2 * (phase.n ?? 1) + 1;
|
|
32387
32532
|
case "web-refresh":
|
|
32388
|
-
|
|
32533
|
+
case "web-refresh-done":
|
|
32534
|
+
return Number.MAX_SAFE_INTEGER - 4;
|
|
32535
|
+
case "hindsight-refresh":
|
|
32536
|
+
case "hindsight-refresh-done":
|
|
32537
|
+
case "hindsight-skipped":
|
|
32538
|
+
return Number.MAX_SAFE_INTEGER - 3;
|
|
32389
32539
|
case "hostd-web-deferred":
|
|
32390
32540
|
return Number.MAX_SAFE_INTEGER - 1;
|
|
32391
32541
|
default:
|
|
@@ -32402,6 +32552,7 @@ class LogTailRolloutNarrator {
|
|
|
32402
32552
|
return;
|
|
32403
32553
|
st.lastAppliedSeq = seq;
|
|
32404
32554
|
this.trackAgentProgress(st, phase);
|
|
32555
|
+
this.trackSingletonProgress(st, phase);
|
|
32405
32556
|
const doneNames = st.agents.filter((a) => a.status === "done").map((a) => a.name);
|
|
32406
32557
|
const rolledSoFar = entry.rolled && entry.rolled.length > 0 ? entry.rolled : doneNames;
|
|
32407
32558
|
st.render = {
|
|
@@ -32413,6 +32564,7 @@ class LogTailRolloutNarrator {
|
|
|
32413
32564
|
...phase.agent !== undefined ? { agent: phase.agent } : {},
|
|
32414
32565
|
rolled: rolledSoFar,
|
|
32415
32566
|
agents: st.agents,
|
|
32567
|
+
singletons: st.singletons,
|
|
32416
32568
|
requestId: entry.request_id,
|
|
32417
32569
|
...entry.prior_pin ? { fromVersion: entry.prior_pin } : {},
|
|
32418
32570
|
startedAtMs: entry.started_at
|
|
@@ -32435,6 +32587,7 @@ class LogTailRolloutNarrator {
|
|
|
32435
32587
|
a.status = "pending";
|
|
32436
32588
|
}
|
|
32437
32589
|
}
|
|
32590
|
+
this.reconcileSingletonsAtTerminal(st, entry);
|
|
32438
32591
|
st.render = {
|
|
32439
32592
|
target: entry.pin ?? st.render.target,
|
|
32440
32593
|
rolled: entry.rolled ?? st.render.rolled,
|
|
@@ -32444,6 +32597,7 @@ class LogTailRolloutNarrator {
|
|
|
32444
32597
|
...entry.got !== undefined ? { got: entry.got } : {},
|
|
32445
32598
|
...entry.drifted && entry.drifted.length > 0 ? { drifted: entry.drifted } : {},
|
|
32446
32599
|
...st.agents.length > 0 ? { agents: st.agents } : {},
|
|
32600
|
+
...st.singletons.length > 0 ? { singletons: st.singletons } : {},
|
|
32447
32601
|
...st.render.m !== undefined ? { m: st.render.m } : {},
|
|
32448
32602
|
requestId: entry.request_id,
|
|
32449
32603
|
...entry.prior_pin ?? st.render.fromVersion ? { fromVersion: entry.prior_pin ?? st.render.fromVersion } : {},
|
|
@@ -32461,6 +32615,63 @@ class LogTailRolloutNarrator {
|
|
|
32461
32615
|
this.opts.log?.(`onTerminal threw (non-fatal): ${e.message}`);
|
|
32462
32616
|
}
|
|
32463
32617
|
}
|
|
32618
|
+
reconcileSingletonsAtTerminal(st, entry) {
|
|
32619
|
+
const completed = entry.result === "completed";
|
|
32620
|
+
const drifted = new Set(entry.drifted ?? []);
|
|
32621
|
+
const driftedHits = (row) => {
|
|
32622
|
+
switch (row) {
|
|
32623
|
+
case SINGLETON_WEB:
|
|
32624
|
+
return [...drifted].filter((d) => d === "switchroom-web");
|
|
32625
|
+
case SINGLETON_HINDSIGHT:
|
|
32626
|
+
return [...drifted].filter((d) => d === "switchroom-hindsight");
|
|
32627
|
+
case SINGLETON_HOSTD:
|
|
32628
|
+
return [...drifted].filter((d) => d === "switchroom-hostd" || d === "switchroom-hindsight-autoheal");
|
|
32629
|
+
case SINGLETON_SHARED:
|
|
32630
|
+
return [...drifted].filter((d) => d.includes("broker") || d.includes("kernel") || d.includes("voice"));
|
|
32631
|
+
default:
|
|
32632
|
+
return [];
|
|
32633
|
+
}
|
|
32634
|
+
};
|
|
32635
|
+
for (const g of st.singletons) {
|
|
32636
|
+
const hits = driftedHits(g.name);
|
|
32637
|
+
if (hits.length > 0) {
|
|
32638
|
+
g.status = "failed";
|
|
32639
|
+
g.detail = `still behind (${hits.join(", ")}) — see the roll's warnings`;
|
|
32640
|
+
continue;
|
|
32641
|
+
}
|
|
32642
|
+
if (entry.failed_step === "refresh-hindsight" && g.name === SINGLETON_HINDSIGHT) {
|
|
32643
|
+
g.status = "failed";
|
|
32644
|
+
g.detail = "recreate FAILED — memory backend down; run `switchroom memory setup`";
|
|
32645
|
+
continue;
|
|
32646
|
+
}
|
|
32647
|
+
if (!completed) {
|
|
32648
|
+
if (g.status === "updating" && g.name !== SINGLETON_SHARED) {
|
|
32649
|
+
g.detail = "outcome unknown — the roll stopped; see warnings";
|
|
32650
|
+
}
|
|
32651
|
+
continue;
|
|
32652
|
+
}
|
|
32653
|
+
switch (g.name) {
|
|
32654
|
+
case SINGLETON_WEB:
|
|
32655
|
+
case SINGLETON_HINDSIGHT:
|
|
32656
|
+
if (g.status === "updated")
|
|
32657
|
+
break;
|
|
32658
|
+
if (g.status === "updating") {
|
|
32659
|
+
g.status = "updated";
|
|
32660
|
+
g.detail = "verified by the end-of-roll component check";
|
|
32661
|
+
} else if (g.status === "pending") {
|
|
32662
|
+
g.status = "skipped";
|
|
32663
|
+
g.detail = "no refresh observed in this roll — see warnings";
|
|
32664
|
+
}
|
|
32665
|
+
break;
|
|
32666
|
+
case SINGLETON_SHARED:
|
|
32667
|
+
g.status = "updated";
|
|
32668
|
+
g.detail = "self-healed with the first agent restart; end-of-roll check passed";
|
|
32669
|
+
break;
|
|
32670
|
+
default:
|
|
32671
|
+
break;
|
|
32672
|
+
}
|
|
32673
|
+
}
|
|
32674
|
+
}
|
|
32464
32675
|
ensureState(entry) {
|
|
32465
32676
|
let st = this.states.get(entry.request_id);
|
|
32466
32677
|
if (!st) {
|
|
@@ -32474,6 +32685,7 @@ class LogTailRolloutNarrator {
|
|
|
32474
32685
|
postFailed: false,
|
|
32475
32686
|
render: { target: entry.pin ?? "" },
|
|
32476
32687
|
agents: [],
|
|
32688
|
+
singletons: initialSingletons(),
|
|
32477
32689
|
frozen: false,
|
|
32478
32690
|
timer: null,
|
|
32479
32691
|
pendingEditAfterPost: false
|
|
@@ -4417,7 +4417,7 @@ var init_protocol = __esm(() => {
|
|
|
4417
4417
|
});
|
|
4418
4418
|
|
|
4419
4419
|
// src/memory/observation-scopes.ts
|
|
4420
|
-
var OBSERVATION_SCOPES;
|
|
4420
|
+
var OBSERVATION_SCOPES, OBSERVATION_SCOPE_STRATEGIES;
|
|
4421
4421
|
var init_observation_scopes = __esm(() => {
|
|
4422
4422
|
OBSERVATION_SCOPES = [
|
|
4423
4423
|
"per_tag",
|
|
@@ -4425,10 +4425,16 @@ var init_observation_scopes = __esm(() => {
|
|
|
4425
4425
|
"all_combinations",
|
|
4426
4426
|
"shared"
|
|
4427
4427
|
];
|
|
4428
|
+
OBSERVATION_SCOPE_STRATEGIES = [
|
|
4429
|
+
"curated",
|
|
4430
|
+
"shared",
|
|
4431
|
+
"combined",
|
|
4432
|
+
"off"
|
|
4433
|
+
];
|
|
4428
4434
|
});
|
|
4429
4435
|
|
|
4430
4436
|
// src/config/schema.ts
|
|
4431
|
-
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;
|
|
4437
|
+
var CodeRepoEntrySchema, AgentBindMountSchema, HttpDiffPollSchema, PollSpecSchema, TelegramMessageActionSchema, WebhookActionSchema, ActionSpecSchema, ScheduleEntrySchema, AgentSoulSchema, AgentToolsSchema, ObservationScopesSchema, ObservationScopeStrategySchema, 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;
|
|
4432
4438
|
var init_schema = __esm(() => {
|
|
4433
4439
|
init_zod();
|
|
4434
4440
|
init_observation_scopes();
|
|
@@ -4543,7 +4549,8 @@ var init_schema = __esm(() => {
|
|
|
4543
4549
|
allow: exports_external.array(exports_external.string()).default([]).describe("Allowed tools (use ['all'] for unrestricted)"),
|
|
4544
4550
|
deny: exports_external.array(exports_external.string()).default([]).describe("Denied tools (overrides allow)")
|
|
4545
4551
|
}).optional();
|
|
4546
|
-
ObservationScopesSchema = exports_external.enum(OBSERVATION_SCOPES).optional().describe("
|
|
4552
|
+
ObservationScopesSchema = exports_external.enum(OBSERVATION_SCOPES).optional().describe("Operator PIN for the per-row observation scope, overriding the " + 'default `curated` strategy (see docs/configuration.md). "shared" ' + "forces Hindsight's consolidation to write EVERY observation into ONE " + "global untagged scope — what several agents pooling one bank want so " + "their observations merge rather than sitting in parallel silos. " + '"combined" is the pin form of opting out (restores the pre-#4035 ' + "engine default). UNSET does NOT mean 'nothing on the wire': the " + "`curated` strategy still computes a scope per retain. 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).");
|
|
4553
|
+
ObservationScopeStrategySchema = exports_external.enum(OBSERVATION_SCOPE_STRATEGIES).optional().describe("Per-retain strategy that decides how the observation scope is " + 'computed when no `observation_scopes` pin is set. "curated" ' + "(the default since #4035) strips volatile per-session provenance " + "tags from each retain's consolidation scope — keeping the stable " + "semantic ones on the source fact — so an agent's observations dedup " + "and merge across sessions instead of pocketing one-per-session. " + '"shared" sends every retain to the one global untagged scope. ' + '"combined" / "off" opt OUT: no per-row scope goes on the wire, ' + "restoring the pre-feature engine default (byte-identical to an " + "unconfigured client). OMITTED BY DEFAULT: unset leaves the plugin's " + "own default (`curated`) in force. A manual `observation_scopes` pin " + "wins over this strategy. Accepted values: " + `${OBSERVATION_SCOPE_STRATEGIES.join(", ")}. ` + "Cascade: override (per-agent wins over default).");
|
|
4547
4554
|
AgentMemorySchema = exports_external.object({
|
|
4548
4555
|
collection: exports_external.string().describe("Hindsight collection name for this agent"),
|
|
4549
4556
|
auto_recall: exports_external.boolean().default(true).describe("Auto-search memories before each response"),
|
|
@@ -4579,6 +4586,7 @@ var init_schema = __esm(() => {
|
|
|
4579
4586
|
}).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`)."),
|
|
4580
4587
|
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)."),
|
|
4581
4588
|
observation_scopes: ObservationScopesSchema,
|
|
4589
|
+
observation_scope_strategy: ObservationScopeStrategySchema,
|
|
4582
4590
|
recall: exports_external.object({
|
|
4583
4591
|
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)."),
|
|
4584
4592
|
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."),
|
|
@@ -4979,6 +4987,7 @@ var init_schema = __esm(() => {
|
|
|
4979
4987
|
isolation: exports_external.enum(["default", "strict"]).optional(),
|
|
4980
4988
|
directive_capture_nudge: exports_external.boolean().optional(),
|
|
4981
4989
|
observation_scopes: ObservationScopesSchema,
|
|
4990
|
+
observation_scope_strategy: ObservationScopeStrategySchema,
|
|
4982
4991
|
recall: exports_external.object({
|
|
4983
4992
|
max_memories: exports_external.number().int().min(0).optional(),
|
|
4984
4993
|
cache_ttl_secs: exports_external.number().int().min(0).optional(),
|
|
@@ -19337,8 +19346,8 @@ var HINDSIGHT_PG_MEM_LIMIT_MIB_FOR_DERIVATION = 16 * 1024;
|
|
|
19337
19346
|
var HINDSIGHT_PG_APP_ANON_MIB = 2560;
|
|
19338
19347
|
var HINDSIGHT_PG_PAGE_CACHE_FLOOR_MIB = 2048;
|
|
19339
19348
|
var HINDSIGHT_PG_SHARED_BUFFERS_BUDGET_MIB = HINDSIGHT_PG_MEM_LIMIT_MIB_FOR_DERIVATION - HINDSIGHT_PG_APP_ANON_MIB - HINDSIGHT_PG_PAGE_CACHE_FLOOR_MIB;
|
|
19340
|
-
var HINDSIGHT_PG_DEFAULT_SHARED_BUFFERS_MIB =
|
|
19341
|
-
var HINDSIGHT_PG_DEFAULT_EFFECTIVE_CACHE_SIZE_MIB =
|
|
19349
|
+
var HINDSIGHT_PG_DEFAULT_SHARED_BUFFERS_MIB = 6144;
|
|
19350
|
+
var HINDSIGHT_PG_DEFAULT_EFFECTIVE_CACHE_SIZE_MIB = 12288;
|
|
19342
19351
|
function pgMib(mib) {
|
|
19343
19352
|
return `${mib}MB`;
|
|
19344
19353
|
}
|
|
@@ -19382,7 +19391,7 @@ var HINDSIGHT_DEFAULT_RERANKER_MAX_CANDIDATES = 150;
|
|
|
19382
19391
|
var HINDSIGHT_DEFAULT_RERANKER_LOCAL_MAX_CONCURRENT = 4;
|
|
19383
19392
|
var HINDSIGHT_DEFAULT_RECALL_MAX_CONCURRENT = 8;
|
|
19384
19393
|
var HINDSIGHT_DEFAULT_REFLECT_WALL_TIMEOUT_S = 600;
|
|
19385
|
-
var HINDSIGHT_DEFAULT_CONSOLIDATION_MAX_MEMORIES_PER_ROUND =
|
|
19394
|
+
var HINDSIGHT_DEFAULT_CONSOLIDATION_MAX_MEMORIES_PER_ROUND = 250;
|
|
19386
19395
|
var HINDSIGHT_DEFAULT_CONSOLIDATION_SLOT_LIMIT = 6;
|
|
19387
19396
|
var HINDSIGHT_DEFAULT_CONSOLIDATION_MAX_SLOTS = 1;
|
|
19388
19397
|
var HINDSIGHT_DEFAULT_RECENCY_DECAY_FUNCTION = "exponential";
|
|
@@ -4013,7 +4013,7 @@ var init_zod = __esm(() => {
|
|
|
4013
4013
|
});
|
|
4014
4014
|
|
|
4015
4015
|
// src/memory/observation-scopes.ts
|
|
4016
|
-
var OBSERVATION_SCOPES;
|
|
4016
|
+
var OBSERVATION_SCOPES, OBSERVATION_SCOPE_STRATEGIES;
|
|
4017
4017
|
var init_observation_scopes = __esm(() => {
|
|
4018
4018
|
OBSERVATION_SCOPES = [
|
|
4019
4019
|
"per_tag",
|
|
@@ -4021,10 +4021,16 @@ var init_observation_scopes = __esm(() => {
|
|
|
4021
4021
|
"all_combinations",
|
|
4022
4022
|
"shared"
|
|
4023
4023
|
];
|
|
4024
|
+
OBSERVATION_SCOPE_STRATEGIES = [
|
|
4025
|
+
"curated",
|
|
4026
|
+
"shared",
|
|
4027
|
+
"combined",
|
|
4028
|
+
"off"
|
|
4029
|
+
];
|
|
4024
4030
|
});
|
|
4025
4031
|
|
|
4026
4032
|
// src/config/schema.ts
|
|
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;
|
|
4033
|
+
var CodeRepoEntrySchema, AgentBindMountSchema, HttpDiffPollSchema, PollSpecSchema, TelegramMessageActionSchema, WebhookActionSchema, ActionSpecSchema, ScheduleEntrySchema, AgentSoulSchema, AgentToolsSchema, ObservationScopesSchema, ObservationScopeStrategySchema, 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;
|
|
4028
4034
|
var init_schema = __esm(() => {
|
|
4029
4035
|
init_zod();
|
|
4030
4036
|
init_observation_scopes();
|
|
@@ -4139,7 +4145,8 @@ var init_schema = __esm(() => {
|
|
|
4139
4145
|
allow: exports_external.array(exports_external.string()).default([]).describe("Allowed tools (use ['all'] for unrestricted)"),
|
|
4140
4146
|
deny: exports_external.array(exports_external.string()).default([]).describe("Denied tools (overrides allow)")
|
|
4141
4147
|
}).optional();
|
|
4142
|
-
ObservationScopesSchema = exports_external.enum(OBSERVATION_SCOPES).optional().describe("
|
|
4148
|
+
ObservationScopesSchema = exports_external.enum(OBSERVATION_SCOPES).optional().describe("Operator PIN for the per-row observation scope, overriding the " + 'default `curated` strategy (see docs/configuration.md). "shared" ' + "forces Hindsight's consolidation to write EVERY observation into ONE " + "global untagged scope — what several agents pooling one bank want so " + "their observations merge rather than sitting in parallel silos. " + '"combined" is the pin form of opting out (restores the pre-#4035 ' + "engine default). UNSET does NOT mean 'nothing on the wire': the " + "`curated` strategy still computes a scope per retain. 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).");
|
|
4149
|
+
ObservationScopeStrategySchema = exports_external.enum(OBSERVATION_SCOPE_STRATEGIES).optional().describe("Per-retain strategy that decides how the observation scope is " + 'computed when no `observation_scopes` pin is set. "curated" ' + "(the default since #4035) strips volatile per-session provenance " + "tags from each retain's consolidation scope — keeping the stable " + "semantic ones on the source fact — so an agent's observations dedup " + "and merge across sessions instead of pocketing one-per-session. " + '"shared" sends every retain to the one global untagged scope. ' + '"combined" / "off" opt OUT: no per-row scope goes on the wire, ' + "restoring the pre-feature engine default (byte-identical to an " + "unconfigured client). OMITTED BY DEFAULT: unset leaves the plugin's " + "own default (`curated`) in force. A manual `observation_scopes` pin " + "wins over this strategy. Accepted values: " + `${OBSERVATION_SCOPE_STRATEGIES.join(", ")}. ` + "Cascade: override (per-agent wins over default).");
|
|
4143
4150
|
AgentMemorySchema = exports_external.object({
|
|
4144
4151
|
collection: exports_external.string().describe("Hindsight collection name for this agent"),
|
|
4145
4152
|
auto_recall: exports_external.boolean().default(true).describe("Auto-search memories before each response"),
|
|
@@ -4175,6 +4182,7 @@ var init_schema = __esm(() => {
|
|
|
4175
4182
|
}).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`)."),
|
|
4176
4183
|
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
4184
|
observation_scopes: ObservationScopesSchema,
|
|
4185
|
+
observation_scope_strategy: ObservationScopeStrategySchema,
|
|
4178
4186
|
recall: exports_external.object({
|
|
4179
4187
|
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)."),
|
|
4180
4188
|
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."),
|
|
@@ -4575,6 +4583,7 @@ var init_schema = __esm(() => {
|
|
|
4575
4583
|
isolation: exports_external.enum(["default", "strict"]).optional(),
|
|
4576
4584
|
directive_capture_nudge: exports_external.boolean().optional(),
|
|
4577
4585
|
observation_scopes: ObservationScopesSchema,
|
|
4586
|
+
observation_scope_strategy: ObservationScopeStrategySchema,
|
|
4578
4587
|
recall: exports_external.object({
|
|
4579
4588
|
max_memories: exports_external.number().int().min(0).optional(),
|
|
4580
4589
|
cache_ttl_secs: exports_external.number().int().min(0).optional(),
|
|
@@ -19750,8 +19759,8 @@ var HINDSIGHT_PG_MEM_LIMIT_MIB_FOR_DERIVATION = 16 * 1024;
|
|
|
19750
19759
|
var HINDSIGHT_PG_APP_ANON_MIB = 2560;
|
|
19751
19760
|
var HINDSIGHT_PG_PAGE_CACHE_FLOOR_MIB = 2048;
|
|
19752
19761
|
var HINDSIGHT_PG_SHARED_BUFFERS_BUDGET_MIB = HINDSIGHT_PG_MEM_LIMIT_MIB_FOR_DERIVATION - HINDSIGHT_PG_APP_ANON_MIB - HINDSIGHT_PG_PAGE_CACHE_FLOOR_MIB;
|
|
19753
|
-
var HINDSIGHT_PG_DEFAULT_SHARED_BUFFERS_MIB =
|
|
19754
|
-
var HINDSIGHT_PG_DEFAULT_EFFECTIVE_CACHE_SIZE_MIB =
|
|
19762
|
+
var HINDSIGHT_PG_DEFAULT_SHARED_BUFFERS_MIB = 6144;
|
|
19763
|
+
var HINDSIGHT_PG_DEFAULT_EFFECTIVE_CACHE_SIZE_MIB = 12288;
|
|
19755
19764
|
function pgMib(mib) {
|
|
19756
19765
|
return `${mib}MB`;
|
|
19757
19766
|
}
|
|
@@ -19795,7 +19804,7 @@ var HINDSIGHT_DEFAULT_RERANKER_MAX_CANDIDATES = 150;
|
|
|
19795
19804
|
var HINDSIGHT_DEFAULT_RERANKER_LOCAL_MAX_CONCURRENT = 4;
|
|
19796
19805
|
var HINDSIGHT_DEFAULT_RECALL_MAX_CONCURRENT = 8;
|
|
19797
19806
|
var HINDSIGHT_DEFAULT_REFLECT_WALL_TIMEOUT_S = 600;
|
|
19798
|
-
var HINDSIGHT_DEFAULT_CONSOLIDATION_MAX_MEMORIES_PER_ROUND =
|
|
19807
|
+
var HINDSIGHT_DEFAULT_CONSOLIDATION_MAX_MEMORIES_PER_ROUND = 250;
|
|
19799
19808
|
var HINDSIGHT_DEFAULT_CONSOLIDATION_SLOT_LIMIT = 6;
|
|
19800
19809
|
var HINDSIGHT_DEFAULT_CONSOLIDATION_MAX_SLOTS = 1;
|
|
19801
19810
|
var HINDSIGHT_DEFAULT_RECENCY_DECAY_FUNCTION = "exponential";
|
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.40",
|
|
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": {
|
|
@@ -1018,6 +1018,16 @@ export HINDSIGHT_DIRECTIVE_CAPTURE_NUDGE={{hindsightDirectiveCaptureNudge}}
|
|
|
1018
1018
|
{{#if hindsightObservationScopesQ}}
|
|
1019
1019
|
export HINDSIGHT_OBSERVATION_SCOPES={{{hindsightObservationScopesQ}}}
|
|
1020
1020
|
{{/if}}
|
|
1021
|
+
# Per-retain observation-scope STRATEGY (memory.observation_scope_strategy
|
|
1022
|
+
# cascade). Selects curated-vs-not: "curated" (plugin default) strips volatile
|
|
1023
|
+
# per-session provenance tags so observations dedup across sessions; "shared"
|
|
1024
|
+
# forces the one global untagged scope; "combined"/"off" opt out (no per-row
|
|
1025
|
+
# scope on the wire, pre-feature engine default). Export ONLY when the operator
|
|
1026
|
+
# set it: unset leaves the plugin's on-by-default `curated` in force. A pinned
|
|
1027
|
+
# per-row scope (memory.observation_scopes) still wins over this in the resolver.
|
|
1028
|
+
{{#if hindsightObservationScopeStrategyQ}}
|
|
1029
|
+
export HINDSIGHT_OBSERVATION_SCOPE_STRATEGY={{{hindsightObservationScopeStrategyQ}}}
|
|
1030
|
+
{{/if}}
|
|
1021
1031
|
# PR6 — supergroup-mode topic tagging. JSON map of {alias: thread_id}
|
|
1022
1032
|
# parsed by retain.py + recall.py to (a) stamp chat_id/thread_id/topic_alias
|
|
1023
1033
|
# into retained memory metadata and (b) emit a "Current topic: …" preamble
|
|
@@ -23543,6 +23543,14 @@ function projectTranscriptLine(line) {
|
|
|
23543
23543
|
{ kind: "turn_end", durationMs: obj.durationMs ?? 0 }
|
|
23544
23544
|
];
|
|
23545
23545
|
}
|
|
23546
|
+
if (type === "system" && obj.subtype === "compact_boundary") {
|
|
23547
|
+
const meta = obj.compactMetadata;
|
|
23548
|
+
return [{
|
|
23549
|
+
kind: "compact_boundary",
|
|
23550
|
+
trigger: typeof meta?.trigger === "string" ? meta.trigger : null,
|
|
23551
|
+
compactDurationMs: typeof meta?.durationMs === "number" ? meta.durationMs : null
|
|
23552
|
+
}];
|
|
23553
|
+
}
|
|
23546
23554
|
return [];
|
|
23547
23555
|
}
|
|
23548
23556
|
function projectSubagentLine(line, agentId, state) {
|