switchroom 0.19.17 → 0.19.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/run-hook.sh +148 -0
- package/bin/workspace-dynamic-hook.sh +147 -38
- package/dist/agent-scheduler/index.js +13 -4
- package/dist/auth-broker/index.js +32 -5
- package/dist/cli/drive-write-pretool.mjs +48 -5
- package/dist/cli/ms-365-write-pretool.mjs +40 -2
- package/dist/cli/notion-write-pretool.mjs +13 -4
- package/dist/cli/switchroom.js +10614 -8104
- package/dist/host-control/main.js +12849 -11446
- package/dist/vault/approvals/kernel-server.js +90 -12
- package/dist/vault/broker/server.js +277 -94
- package/package.json +5 -3
- package/profiles/_base/start.sh.hbs +69 -5
- package/profiles/coding/CLAUDE.md.hbs +1 -1
- package/profiles/default/CLAUDE.md.hbs +3 -3
- package/profiles/executive-assistant/CLAUDE.md.hbs +1 -1
- package/profiles/health-coach/CLAUDE.md.hbs +1 -1
- package/skills/mental-model-curator/SKILL.md +8 -6
- package/telegram-plugin/bridge/bridge.ts +25 -19
- package/telegram-plugin/bridge/mcp-instructions.ts +87 -0
- package/telegram-plugin/dist/bridge/bridge.js +28 -20
- package/telegram-plugin/dist/gateway/gateway.js +2077 -1087
- package/telegram-plugin/dist/server.js +32 -20
- package/telegram-plugin/gateway/always-allow-persist-queue.ts +97 -11
- package/telegram-plugin/gateway/boot-card.ts +5 -1
- package/telegram-plugin/gateway/boot-probes.ts +113 -0
- package/telegram-plugin/gateway/config-approval-handler.test.ts +54 -0
- package/telegram-plugin/gateway/config-approval-handler.ts +16 -1
- package/telegram-plugin/gateway/disconnect-flush.ts +17 -0
- package/telegram-plugin/gateway/gateway.ts +43 -1
- package/telegram-plugin/gateway/handback-preturn-signal.ts +61 -7
- package/telegram-plugin/gateway/ipc-protocol.ts +5 -0
- package/telegram-plugin/gateway/ipc-server.ts +13 -0
- package/telegram-plugin/gateway/liveness-wiring.ts +125 -5
- package/telegram-plugin/gateway/missed-approvals-store.ts +66 -17
- package/telegram-plugin/gateway/obligation-ledger.ts +84 -4
- package/telegram-plugin/gateway/pending-card-store.ts +46 -16
- package/telegram-plugin/gateway/resume-inbound-builder.ts +13 -4
- package/telegram-plugin/gateway/scoped-grant-store.ts +39 -14
- package/telegram-plugin/gateway/store-file.ts +244 -0
- package/telegram-plugin/gateway/stream-render.ts +24 -5
- package/telegram-plugin/hooks/secret-guard-pretool.mjs +249 -76
- package/telegram-plugin/hooks/tool-label-pretool.mjs +88 -2
- package/telegram-plugin/registry/turns-schema.test.ts +8 -3
- package/telegram-plugin/registry/turns-schema.ts +40 -12
- package/telegram-plugin/runtime-metrics.ts +14 -0
- package/telegram-plugin/silence-poke.ts +138 -0
- package/telegram-plugin/tests/boot-probe-drift.test.ts +152 -0
- package/telegram-plugin/tests/bridge-tool-parity.test.ts +95 -0
- package/telegram-plugin/tests/gateway-disconnect-flush.test.ts +32 -0
- package/telegram-plugin/tests/handback-preturn-signal.test.ts +62 -0
- package/telegram-plugin/tests/helpers/liveness-wiring-fixture.ts +178 -0
- package/telegram-plugin/tests/ipc-server-validate-config-approval.test.ts +95 -0
- package/telegram-plugin/tests/mcp-instructions-budget.test.ts +184 -0
- package/telegram-plugin/tests/multitopic-routing-wiring.test.ts +22 -2
- package/telegram-plugin/tests/obligation-determinism.test.ts +114 -3
- package/telegram-plugin/tests/obligation-ledger.test.ts +310 -0
- package/telegram-plugin/tests/registry-turns.test.ts +13 -0
- package/telegram-plugin/tests/resume-inbound-builder.test.ts +15 -0
- package/telegram-plugin/tests/secret-guard-pretool.test.ts +347 -16
- package/telegram-plugin/tests/silence-poke-orphan-reap.test.ts +392 -0
- package/telegram-plugin/tests/silence-poke-teardown-notice.test.ts +301 -0
- package/telegram-plugin/tests/store-atomic-write.test.ts +411 -0
- package/telegram-plugin/tests/stream-render-golden.test.ts +103 -1
- package/telegram-plugin/tests/tool-activity-summary.test.ts +9 -2
- package/telegram-plugin/tests/tool-label-pretool.test.ts +94 -0
- package/telegram-plugin/tests/tts-normalize.test.ts +43 -0
- package/telegram-plugin/tests/voice-normalize-text.test.ts +212 -3
- package/telegram-plugin/tests/worker-feed-repeat-steps.test.ts +147 -0
- package/telegram-plugin/tts-normalize.ts +6 -4
- package/telegram-plugin/voice-normalize-text.ts +168 -11
- package/telegram-plugin/worker-activity-feed.ts +51 -1
- package/vendor/hindsight-memory/CHANGELOG.md +73 -0
- package/vendor/hindsight-memory/scripts/drain_pending.py +668 -56
- package/vendor/hindsight-memory/scripts/lib/client.py +124 -0
- package/vendor/hindsight-memory/scripts/lib/config.py +8 -3
- package/vendor/hindsight-memory/scripts/lib/directives.py +62 -4
- package/vendor/hindsight-memory/scripts/lib/pending.py +865 -33
- package/vendor/hindsight-memory/scripts/lib/retain_split.py +449 -0
- package/vendor/hindsight-memory/scripts/recall.py +257 -12
- package/vendor/hindsight-memory/scripts/retain.py +12 -6
- package/vendor/hindsight-memory/scripts/session_start.py +48 -0
- package/vendor/hindsight-memory/scripts/tests/test_client_document_exists.py +470 -0
- package/vendor/hindsight-memory/scripts/tests/test_directives.py +80 -9
- package/vendor/hindsight-memory/scripts/tests/test_pending_drops.py +2121 -0
- package/vendor/hindsight-memory/scripts/tests/test_recall_integration.py +362 -18
- package/vendor/hindsight-memory/scripts/tests/test_retain_split.py +430 -0
- package/vendor/hindsight-memory/scripts/tests/test_session_start_version_skew.py +204 -0
- package/vendor/hindsight-memory/settings.json +1 -1
- package/vendor/hindsight-memory/tests/test_drain_pending.py +102 -6
- package/vendor/hindsight-memory/tests/test_pending.py +32 -7
|
@@ -4013,7 +4013,7 @@ var init_zod = __esm(() => {
|
|
|
4013
4013
|
});
|
|
4014
4014
|
|
|
4015
4015
|
// 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, ReleaseBlock, NetworkIsolationSchema, servesField, knowsField, profileFields, ProfileSchema, _omitExtends, defaultsFields, AgentDefaultsSchema, AgentSchema, TelegramConfigSchema, MemoryBackendConfigSchema, VaultConfigSchema, QuotaConfigSchema, AutoReleaseCheckSchema, HostControlConfigSchema, WebServiceConfigSchema, FleetHealthConfigSchema, HostdConfigSchema, CronEgressSchema, CronConfigSchema, UserSchema, SwitchroomConfigSchema;
|
|
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;
|
|
4017
4017
|
var init_schema = __esm(() => {
|
|
4018
4018
|
init_zod();
|
|
4019
4019
|
CodeRepoEntrySchema = exports_external.object({
|
|
@@ -4164,7 +4164,7 @@ var init_schema = __esm(() => {
|
|
|
4164
4164
|
recall: exports_external.object({
|
|
4165
4165
|
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
4166
|
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."),
|
|
4167
|
-
min_overlap: exports_external.number().min(0).max(1).optional().describe("Minimum
|
|
4167
|
+
min_overlap: exports_external.number().min(0).max(1).optional().describe("Minimum containment token overlap [0.0–1.0] between the user " + "prompt and a memory's text for the memory to be injected. " + "A cheap FLOOR that removes candidates with (near-)zero lexical " + "relationship to the prompt — NOT a precision control: at the " + "recommended 0.10 the gate is near-passthrough, and the " + "effective precision control is the engine rerank plus the " + "max_memories head-slice. 0.0 disables (default — current " + "behaviour). Use 0.10; values at or above 0.20 measurably " + "starve recall — on production replay 0.20 leaves ~41.9% of " + "turns with NO memories at all, re-creating the bug #3541 " + "fixed. Observe the `overlap_dropped` field via " + "`switchroom memory recall-log`."),
|
|
4168
4168
|
types: exports_external.array(exports_external.string()).optional().describe("Hindsight fact types to recall. Switchroom default is " + '["world", "experience", "observation"] — the synthesized ' + "`observation` tier is on by default. Set to " + '["world", "experience"] to opt out of observation-backed ' + "recall for this agent (or fleet-wide under defaults)."),
|
|
4169
4169
|
additional_banks: exports_external.array(exports_external.string()).optional().describe("Extra Hindsight banks to recall from on every turn, merged into " + "the agent's own bank results — e.g. a shared operator/household " + "profile bank authored via `switchroom memory profile`. Each is " + "recalled with an 8s timeout and is non-fatal on failure. Stays " + "within the single tenant: all banks are the operator's data, in " + "the operator's Hindsight instance (see the `single-tenant` " + "invariant). Defaults to [] (no extra banks)."),
|
|
4170
4170
|
sender_banks: exports_external.record(exports_external.string(), exports_external.string()).optional().describe("Per-speaker recall routing: a map of Telegram sender → extra " + "recall bank. When a message arrives, the agent also recalls the " + "speaker's bank (matched by Telegram username — a leading @ is " + "optional — or numeric user_id), merged " + "into its own results — so each trusted user gets their own " + "profile context. Additive recall scoping within the single " + "tenant: never an access boundary (who may drive an agent stays " + "the per-agent user assignment in `access.allowFrom`). Author the " + "banks via `switchroom memory profile`."),
|
|
@@ -4476,9 +4476,16 @@ var init_schema = __esm(() => {
|
|
|
4476
4476
|
enabled: exports_external.boolean().optional().describe("Master switch for the reaction-dispatch path. Default false — " + "with no reaction_dispatch block, reactions are persisted (and may " + "feed the `reactions` feedback path) but are NEVER dispatched as " + "event-driven inbound turns."),
|
|
4477
4477
|
emojis: exports_external.array(exports_external.string()).optional().describe('Emoji allowlist that triggers a `<channel event="reaction">` ' + "inbound turn when reacted to any message. Default [] (nothing " + "fires). Cascade mode: REPLACE (not union) — a layer's list " + "replaces lower layers entirely so an operator can narrow per-agent.")
|
|
4478
4478
|
}).optional();
|
|
4479
|
-
|
|
4479
|
+
releaseBlockFields = {
|
|
4480
4480
|
channel: exports_external.enum(["dev", "rc", "latest"]).optional(),
|
|
4481
4481
|
pin: exports_external.string().regex(/^(sha-[0-9a-f]{7,40}|v\d+\.\d+\.\d+)$/).optional()
|
|
4482
|
+
};
|
|
4483
|
+
ReleaseBlock = exports_external.object(releaseBlockFields).strict().refine((r) => !(r.channel && r.pin), {
|
|
4484
|
+
message: "release.channel and release.pin are mutually exclusive"
|
|
4485
|
+
});
|
|
4486
|
+
RootReleaseBlock = exports_external.object({
|
|
4487
|
+
...releaseBlockFields,
|
|
4488
|
+
auto_update: exports_external.boolean().optional().describe("Opt-in unattended fleet auto-update (KEN-131). When true, hostd's " + "release watcher polls the published release version and, on a new " + "release, drives the EXISTING staggered canary rollout " + "(`switchroom rollout --pin vX.Y.Z`) unattended: canary-first, " + "per-agent version assert, durable pin persisted only after the " + "canary is green, abort + operator alert card on canary failure, " + "and compose rollback on a failed apply. Default false — with it " + "unset/false, behaviour is unchanged.")
|
|
4482
4489
|
}).strict().refine((r) => !(r.channel && r.pin), {
|
|
4483
4490
|
message: "release.channel and release.pin are mutually exclusive"
|
|
4484
4491
|
});
|
|
@@ -4722,6 +4729,7 @@ var init_schema = __esm(() => {
|
|
|
4722
4729
|
enabled: exports_external.boolean().default(false).describe("When true, hostd polls the remote release tag every " + "`interval_minutes` and applies + restarts the fleet when a new " + "release is detected. Default false — opt-in."),
|
|
4723
4730
|
interval_minutes: exports_external.number().int().min(5).max(1440).default(5).describe("Poll interval in minutes. Floor of 5m matches the agent-config " + "cron rate limit; ceiling of 1440m (24h) is a sanity cap."),
|
|
4724
4731
|
apply_on_detect: exports_external.boolean().default(true).describe("When false, hostd logs `release_detected` but does NOT call " + "update_apply / restart all. Useful for dogfooding the detector " + "without rolling the fleet."),
|
|
4732
|
+
notify_on_detect: exports_external.boolean().default(false).describe("KEN-129 — operator-in-the-loop update prompt. Only consulted " + "when apply_on_detect is false (auto-apply supersedes notify): " + "a newly detected release posts ONE operator approval card " + "('fleet is behind — tap to apply') via an admin agent's " + "gateway; Approve runs hostd's update_apply path (fleet-" + "mutation-locked, durable status rows, get_status-pollable). " + "Dedup on release id: the last-notified id persists in " + "~/.switchroom/release-notify-state.json, so a card that " + "reached the operator is never re-posted for the same release."),
|
|
4725
4733
|
image_ref: exports_external.string().default("ghcr.io/switchroom/switchroom-agent:latest").describe("Image reference whose remote digest is compared to the local " + "image digest. Defaults to the agent image's :latest tag, which " + "is the canonical signal that a release has been promoted.")
|
|
4726
4734
|
});
|
|
4727
4735
|
HostControlConfigSchema = exports_external.object({
|
|
@@ -4764,7 +4772,7 @@ var init_schema = __esm(() => {
|
|
|
4764
4772
|
timezone: exports_external.string().regex(TIMEZONE_REGEX, "timezone must be an IANA zone name like 'Australia/Melbourne' or 'UTC'").optional().describe("Global default IANA timezone applied to every agent unless the " + "agent (or its profile) declares its own. See the per-agent " + "timezone field for the full cascade and auto-detection fallback.")
|
|
4765
4773
|
}),
|
|
4766
4774
|
telegram: TelegramConfigSchema,
|
|
4767
|
-
release:
|
|
4775
|
+
release: RootReleaseBlock.optional().describe("Fleet-wide default release-channel pin / pointer for the update " + "flow. Either `channel` (dev|rc|latest) or `pin` (sha-<hex>|v<semver>) " + "— mutually exclusive. Per-agent `release` REPLACES this entirely " + "(except `auto_update`, which is root-only — a fleet property read by " + "hostd's release watcher, never per-agent)."),
|
|
4768
4776
|
memory: MemoryBackendConfigSchema.optional(),
|
|
4769
4777
|
hindsight: HindsightConfigSchema.optional().describe("Fleet-singleton Hindsight (memory backend) configuration. Currently " + "just the LLM knob (provider + model) used for retain/reflect/" + "consolidation. Both fields optional; when unset the container falls " + "back to provider=claude-code + the hard-coded HINDSIGHT_DEFAULT_MODEL " + "so nothing changes for operators who don't set it. Read at container " + "launch by startHindsight() (src/setup/hindsight.ts) — takes effect on " + "the next `switchroom apply` / `memory setup --recreate`."),
|
|
4770
4778
|
vault: VaultConfigSchema.optional(),
|
|
@@ -18538,7 +18546,8 @@ var ApprovalDecisionMetaSchema = exports_external.object({
|
|
|
18538
18546
|
ttl_expires_at: exports_external.number().nullable(),
|
|
18539
18547
|
last_used_at: exports_external.number().nullable(),
|
|
18540
18548
|
revoked_at: exports_external.number().nullable(),
|
|
18541
|
-
revoke_reason: exports_external.string().nullable()
|
|
18549
|
+
revoke_reason: exports_external.string().nullable(),
|
|
18550
|
+
origin: exports_external.enum(["agent", "operator"]).optional()
|
|
18542
18551
|
});
|
|
18543
18552
|
var OkApprovalLookupResponseSchema = exports_external.object({
|
|
18544
18553
|
ok: exports_external.literal(true),
|
|
@@ -18702,6 +18711,7 @@ function migrateApprovalSchema(db) {
|
|
|
18702
18711
|
var DEFAULT_MAX_TTL_LIFETIME_MS = 7 * 24 * 60 * 60 * 1000;
|
|
18703
18712
|
|
|
18704
18713
|
// src/vault/approvals/kernel.ts
|
|
18714
|
+
var ALLOW_ONCE_CONSUMED_REASON = "allow_once_consumed";
|
|
18705
18715
|
function generateRequestId() {
|
|
18706
18716
|
return randomBytes(16).toString("hex");
|
|
18707
18717
|
}
|
|
@@ -18849,6 +18859,38 @@ function evaluateDecisionRow(db, row, currentCanonical, opts, now) {
|
|
|
18849
18859
|
if (decision.decision === "deny") {
|
|
18850
18860
|
return { state: "denied", decision };
|
|
18851
18861
|
}
|
|
18862
|
+
if (decision.decision === "allow_once") {
|
|
18863
|
+
const burn = db.run(`UPDATE approval_decisions
|
|
18864
|
+
SET last_used_at = ?, revoked_at = ?, revoke_reason = ?
|
|
18865
|
+
WHERE id = ? AND revoked_at IS NULL`, [now, now, ALLOW_ONCE_CONSUMED_REASON, decision.id]);
|
|
18866
|
+
if ((burn.changes ?? 0) === 0) {
|
|
18867
|
+
audit(db, "deny", {
|
|
18868
|
+
agent_unit: decision.agent_unit,
|
|
18869
|
+
scope: decision.scope,
|
|
18870
|
+
action: decision.action,
|
|
18871
|
+
decision_id: decision.id,
|
|
18872
|
+
context: { reason: ALLOW_ONCE_CONSUMED_REASON }
|
|
18873
|
+
});
|
|
18874
|
+
return { state: "denied", decision };
|
|
18875
|
+
}
|
|
18876
|
+
decision.last_used_at = now;
|
|
18877
|
+
decision.revoked_at = now;
|
|
18878
|
+
decision.revoke_reason = ALLOW_ONCE_CONSUMED_REASON;
|
|
18879
|
+
audit(db, "match", {
|
|
18880
|
+
agent_unit: decision.agent_unit,
|
|
18881
|
+
scope: decision.scope,
|
|
18882
|
+
action: decision.action,
|
|
18883
|
+
decision_id: decision.id
|
|
18884
|
+
});
|
|
18885
|
+
audit(db, "revoke", {
|
|
18886
|
+
agent_unit: decision.agent_unit,
|
|
18887
|
+
scope: decision.scope,
|
|
18888
|
+
action: decision.action,
|
|
18889
|
+
decision_id: decision.id,
|
|
18890
|
+
context: { actor: "kernel", reason: ALLOW_ONCE_CONSUMED_REASON }
|
|
18891
|
+
});
|
|
18892
|
+
return { state: "granted", decision };
|
|
18893
|
+
}
|
|
18852
18894
|
if (decision.decision === "allow_ttl") {
|
|
18853
18895
|
const maxLifetime = opts.max_ttl_lifetime_ms ?? DEFAULT_MAX_TTL_LIFETIME_MS;
|
|
18854
18896
|
const hardCap = decision.granted_at + maxLifetime;
|
|
@@ -18917,7 +18959,8 @@ function consumeNonce(db, request_id, now = Date.now()) {
|
|
|
18917
18959
|
}
|
|
18918
18960
|
function recordDecision(db, input, now = Date.now()) {
|
|
18919
18961
|
const id = randomUUID();
|
|
18920
|
-
const
|
|
18962
|
+
const maxLifetime = input.max_ttl_lifetime_ms ?? DEFAULT_MAX_TTL_LIFETIME_MS;
|
|
18963
|
+
const ttl_expires_at = input.decision === "allow_ttl" && input.ttl_ms ? now + Math.min(input.ttl_ms, maxLifetime) : null;
|
|
18921
18964
|
const canonical = canonicalizeApproverSet(input.approver_set);
|
|
18922
18965
|
db.run(`INSERT INTO approval_decisions
|
|
18923
18966
|
(id, agent_unit, scope, action, decision,
|
|
@@ -19115,9 +19158,26 @@ var HINDSIGHT_BROKER_SOCK_VOLUME = `auth-broker-${HINDSIGHT_CONSUMER_NAME}-sock`
|
|
|
19115
19158
|
var HINDSIGHT_CREDS_MIRROR_VOLUME = `consumer-creds-${HINDSIGHT_CONSUMER_NAME}`;
|
|
19116
19159
|
var HINDSIGHT_HEALTHCHECK_PY = 'import urllib.request,sys; sys.exit(0 if urllib.request.urlopen("http://localhost:8888/health",timeout=4).getcode()==200 else 1)';
|
|
19117
19160
|
var HINDSIGHT_HEALTHCHECK_CMD = `python3 -c '${HINDSIGHT_HEALTHCHECK_PY}'`;
|
|
19161
|
+
var DOCKER_PROBE_TIMEOUT_MS = 60 * 1000;
|
|
19118
19162
|
|
|
19119
19163
|
// src/memory/hindsight.ts
|
|
19120
|
-
var DEFAULT_RETAIN_MISSION = "Extract
|
|
19164
|
+
var DEFAULT_RETAIN_MISSION = "Extract durable facts that will still be true and useful weeks from now: " + "user preferences and standing rules, ongoing projects and recurring " + "commitments, technical and architectural decisions with their rationale, " + "and people/tool relationships. A preference revealed by a request is " + "durable — record the preference (what the user likes, wants, or always " + `does), not the request itself.
|
|
19165
|
+
|
|
19166
|
+
` + `NEVER extract:
|
|
19167
|
+
` + "- Agent tool-use traces or narration of what the assistant did (e.g. " + `"the assistant used X to query Y", "ran a search", "sent the message").
|
|
19168
|
+
` + "- In-flight workflow/process narration (a sub-task started, paused, or is " + "still running) — retain the outcome only once the task completes or a " + `decision is made.
|
|
19169
|
+
` + `- Operation, request, batch or session IDs, UUIDs, hashes, or error codes.
|
|
19170
|
+
` + "- Hindsight's own errors, retries, backlogs, or internal state — the " + `memory system's self-reports are not memories.
|
|
19171
|
+
` + `- Restatements of the user's current request or the task in progress.
|
|
19172
|
+
` + "- Transient state (unread counts, build status, what is running right now) " + "unless the fact is explicitly dated, in which case record it as a dated " + `observation.
|
|
19173
|
+
` + `- Greetings, acknowledgements, and routine operational chatter.
|
|
19174
|
+
|
|
19175
|
+
` + "If a candidate fact matches an exclusion, drop it rather than rewording " + "it. If nothing durable remains, return an empty facts list.";
|
|
19176
|
+
var SUPERSEDED_RETAIN_MISSIONS = [
|
|
19177
|
+
"Extract technical decisions, architectural choices, user preferences, project context, and people/tool relationships. Ignore routine greetings and transient operational details.",
|
|
19178
|
+
"Extract user preferences, ongoing projects, recurring commitments, " + "important context, and durable facts that should help across future " + "conversations. Skip one-off chatter and temporary task noise.",
|
|
19179
|
+
"Extract user preferences, ongoing projects, recurring commitments, " + "important context, and durable facts that should help across future " + "conversations. Skip one-off chatter and temporary task noise, " + "including in-flight workflow/process narration (a sub-task started, " + "paused, or is still running) — only retain the outcome once a task " + "actually completes or a decision is made."
|
|
19180
|
+
];
|
|
19121
19181
|
var PROFILE_MEMORY_DEFAULTS = {
|
|
19122
19182
|
"health-coach": {
|
|
19123
19183
|
disposition: { skepticism: 2, literalism: 2, empathy: 5 },
|
|
@@ -19172,6 +19232,7 @@ init_paths();
|
|
|
19172
19232
|
// src/agents/scaffold.ts
|
|
19173
19233
|
var REPO_ROOT = resolve5(import.meta.dirname, "../..");
|
|
19174
19234
|
var CLAUDE_MD_YOURS_PLACEHOLDER = "This space is yours. Add per-agent rules, exceptions, or context the " + "Switchroom template doesn't capture. Everything above the marker line is " + "regenerated on every apply; this section is preserved.";
|
|
19235
|
+
var pendingBankOps = new Set;
|
|
19175
19236
|
var SWITCHROOM_OWNED_SETTINGS_KEYS = new Set([
|
|
19176
19237
|
"permissions",
|
|
19177
19238
|
"mcpServers",
|
|
@@ -19360,6 +19421,9 @@ function handleConnection(socket, agent, db, isOperator = false) {
|
|
|
19360
19421
|
}
|
|
19361
19422
|
var KERNEL_OPERATOR_NAME = "operator";
|
|
19362
19423
|
var OPERATOR_ALLOWED_OPS = new Set(["approval_list"]);
|
|
19424
|
+
function listenerOrigin(isOperator) {
|
|
19425
|
+
return isOperator ? "operator" : "agent";
|
|
19426
|
+
}
|
|
19363
19427
|
function handleRequest(socket, req, agent, db, peerUid, isOperator = false) {
|
|
19364
19428
|
if (isOperator && !OPERATOR_ALLOWED_OPS.has(req.op)) {
|
|
19365
19429
|
socket.write(encodeResponse(errorResponse("DENIED", `kernel operator socket is read-only: '${req.op}' is not permitted (only ${[...OPERATOR_ALLOWED_OPS].join(", ")})`)));
|
|
@@ -19420,7 +19484,8 @@ function handleRequest(socket, req, agent, db, peerUid, isOperator = false) {
|
|
|
19420
19484
|
ttl_expires_at: r.decision.ttl_expires_at,
|
|
19421
19485
|
last_used_at: r.decision.last_used_at,
|
|
19422
19486
|
revoked_at: r.decision.revoked_at,
|
|
19423
|
-
revoke_reason: r.decision.revoke_reason
|
|
19487
|
+
revoke_reason: r.decision.revoke_reason,
|
|
19488
|
+
origin: r.decision.origin
|
|
19424
19489
|
} : null;
|
|
19425
19490
|
socket.write(encodeResponse({ ok: true, state: r.state, decision }));
|
|
19426
19491
|
return;
|
|
@@ -19447,7 +19512,8 @@ function handleRequest(socket, req, agent, db, peerUid, isOperator = false) {
|
|
|
19447
19512
|
ttl_expires_at: r.decision.ttl_expires_at,
|
|
19448
19513
|
last_used_at: r.decision.last_used_at,
|
|
19449
19514
|
revoked_at: r.decision.revoked_at,
|
|
19450
|
-
revoke_reason: r.decision.revoke_reason
|
|
19515
|
+
revoke_reason: r.decision.revoke_reason,
|
|
19516
|
+
origin: r.decision.origin
|
|
19451
19517
|
} : null;
|
|
19452
19518
|
socket.write(encodeResponse({ ok: true, state: r.state, decision }));
|
|
19453
19519
|
return;
|
|
@@ -19509,7 +19575,8 @@ function handleRequest(socket, req, agent, db, peerUid, isOperator = false) {
|
|
|
19509
19575
|
decision: req.decision,
|
|
19510
19576
|
approver_set: req.approver_set,
|
|
19511
19577
|
granted_by_user_id: req.granted_by_user_id,
|
|
19512
|
-
ttl_ms: req.ttl_ms ?? undefined
|
|
19578
|
+
ttl_ms: req.ttl_ms ?? undefined,
|
|
19579
|
+
origin: listenerOrigin(isOperator)
|
|
19513
19580
|
});
|
|
19514
19581
|
socket.write(encodeResponse({ ok: true, decision_id }));
|
|
19515
19582
|
return;
|
|
@@ -19528,7 +19595,8 @@ function handleRequest(socket, req, agent, db, peerUid, isOperator = false) {
|
|
|
19528
19595
|
decision: req.decision,
|
|
19529
19596
|
approver_set: req.approver_set,
|
|
19530
19597
|
granted_by_user_id: req.granted_by_user_id,
|
|
19531
|
-
ttl_ms: req.ttl_ms ?? undefined
|
|
19598
|
+
ttl_ms: req.ttl_ms ?? undefined,
|
|
19599
|
+
origin: listenerOrigin(isOperator)
|
|
19532
19600
|
});
|
|
19533
19601
|
if (!res.consumed) {
|
|
19534
19602
|
socket.write(encodeResponse({ ok: true, consumed: false }));
|
|
@@ -19546,7 +19614,16 @@ function handleRequest(socket, req, agent, db, peerUid, isOperator = false) {
|
|
|
19546
19614
|
return;
|
|
19547
19615
|
}
|
|
19548
19616
|
if (req.op === "approval_list") {
|
|
19549
|
-
|
|
19617
|
+
let listFilter = { agent_unit: req.agent_unit };
|
|
19618
|
+
if (!isOperator) {
|
|
19619
|
+
const acl = checkApprovalAclByAgent(agent, req.agent_unit ?? agent);
|
|
19620
|
+
if (!acl.allow) {
|
|
19621
|
+
socket.write(encodeResponse(errorResponse("DENIED", acl.reason)));
|
|
19622
|
+
return;
|
|
19623
|
+
}
|
|
19624
|
+
listFilter = { agent_unit: agent };
|
|
19625
|
+
}
|
|
19626
|
+
const decisions = listDecisions(db, listFilter);
|
|
19550
19627
|
const meta = decisions.map((d) => ({
|
|
19551
19628
|
id: d.id,
|
|
19552
19629
|
agent_unit: d.agent_unit,
|
|
@@ -19708,6 +19785,7 @@ if (import.meta.url === `file://${process.argv[1]}` && /(?:^|[/\\])(?:vault[/\\]
|
|
|
19708
19785
|
export {
|
|
19709
19786
|
openKernelDb,
|
|
19710
19787
|
main,
|
|
19788
|
+
listenerOrigin,
|
|
19711
19789
|
bootstrap,
|
|
19712
19790
|
KERNEL_OPERATOR_NAME
|
|
19713
19791
|
};
|