@vellumai/assistant 0.8.0 → 0.8.2
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/AGENTS.md +11 -0
- package/ARCHITECTURE.md +2 -7
- package/Dockerfile +80 -5
- package/README.md +2 -2
- package/bun.lock +11 -1
- package/docker-entrypoint.sh +21 -0
- package/docker-init-apt-root.sh +94 -0
- package/docker-kata-apt-env.sh +39 -0
- package/docs/plugins.md +88 -47
- package/docs/skills.md +9 -7
- package/eslint-rules/__tests__/cli-no-daemon-internals.test.ts +420 -0
- package/eslint-rules/cli-no-daemon-internals.js +283 -0
- package/eslint.config.mjs +12 -0
- package/examples/plugins/echo/README.md +27 -27
- package/examples/plugins/echo/package.json +3 -0
- package/examples/plugins/echo/register.ts +31 -31
- package/knip.json +2 -1
- package/node_modules/@vellumai/skill-host-contracts/src/client.ts +10 -1
- package/node_modules/@vellumai/slack-text/src/index.test.ts +114 -14
- package/node_modules/@vellumai/slack-text/src/index.ts +82 -18
- package/openapi.yaml +4462 -991
- package/package.json +5 -1
- package/scripts/generate-openapi.ts +135 -14
- package/scripts/sync-llm-catalog.ts +165 -0
- package/scripts/sync-web-search-catalog.ts +129 -0
- package/src/__tests__/actor-trust-resolver-address-fallback.test.ts +169 -0
- package/src/__tests__/agent-image-optimize.test.ts +11 -3
- package/src/__tests__/agent-loop-override-profile.test.ts +26 -1
- package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +131 -0
- package/src/__tests__/anthropic-provider.test.ts +137 -2
- package/src/__tests__/app-builder-tool-scripts.test.ts +9 -3
- package/src/__tests__/app-control-flow.test.ts +7 -0
- package/src/__tests__/app-executors.test.ts +220 -4
- package/src/__tests__/assistant-events-sse-shed.test.ts +232 -0
- package/src/__tests__/auto-analysis-end-to-end.test.ts +35 -0
- package/src/__tests__/avatar-identity-sync.test.ts +87 -0
- package/src/__tests__/background-workers-disk-pressure.test.ts +11 -22
- package/src/__tests__/btw-routes.test.ts +1 -0
- package/src/__tests__/bundled-asset.test.ts +6 -6
- package/src/__tests__/call-site-routing-provider.test.ts +172 -45
- package/src/__tests__/cancel-resolves-conversation-key.test.ts +44 -3
- package/src/__tests__/channel-availability-routes.test.ts +206 -0
- package/src/__tests__/channel-delivery-store.test.ts +289 -1
- package/src/__tests__/channel-policy.test.ts +12 -0
- package/src/__tests__/checker.test.ts +89 -0
- package/src/__tests__/circuit-breaker-pipeline.test.ts +0 -1
- package/src/__tests__/clawhub.test.ts +75 -16
- package/src/__tests__/cli-memory-v2-reembed-skills.test.ts +35 -7
- package/src/__tests__/compact-event-conversation-id-guard.test.ts +33 -5
- package/src/__tests__/compaction-strip-metadata-clear.test.ts +26 -1
- package/src/__tests__/compactor-tail-resolution.test.ts +41 -0
- package/src/__tests__/config-loader-backfill.test.ts +526 -102
- package/src/__tests__/config-loader-corrupt.test.ts +68 -0
- package/src/__tests__/config-loader-platform-defaults.test.ts +77 -23
- package/src/__tests__/config-schema-cmd.test.ts +63 -29
- package/src/__tests__/config-schema.test.ts +35 -3
- package/src/__tests__/config-set-platform-guard.test.ts +75 -152
- package/src/__tests__/config-set-route.test.ts +278 -0
- package/src/__tests__/config-sounds-sync.test.ts +97 -0
- package/src/__tests__/config-watcher-skill-reseed.test.ts +453 -0
- package/src/__tests__/config-watcher.test.ts +6 -0
- package/src/__tests__/contacts-tools.test.ts +51 -199
- package/src/__tests__/context-search-agent-protocol.test.ts +21 -2
- package/src/__tests__/context-search-agent-runner.test.ts +22 -138
- package/src/__tests__/context-search-conversations-source.test.ts +159 -18
- package/src/__tests__/context-search-fanout.test.ts +20 -157
- package/src/__tests__/context-search-memory-v2-source.test.ts +3 -4
- package/src/__tests__/context-search-types.test.ts +7 -2
- package/src/__tests__/context-search-workspace-source.test.ts +7 -0
- package/src/__tests__/context-token-estimator.test.ts +1 -0
- package/src/__tests__/conversation-abort-tool-results.test.ts +4 -1
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +1 -0
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +93 -92
- package/src/__tests__/conversation-agent-loop.test.ts +2 -0
- package/src/__tests__/conversation-crud-inference-profile.test.ts +100 -0
- package/src/__tests__/conversation-error.test.ts +80 -3
- package/src/__tests__/conversation-fork-crud.test.ts +323 -1
- package/src/__tests__/conversation-inference-profile-route.test.ts +54 -18
- package/src/__tests__/conversation-init.benchmark.test.ts +1 -0
- package/src/__tests__/conversation-lifecycle.test.ts +297 -0
- package/src/__tests__/conversation-message-sync-tags.test.ts +97 -0
- package/src/__tests__/conversation-pairing.test.ts +54 -0
- package/src/__tests__/conversation-process-app-control-preactivation.test.ts +100 -1
- package/src/__tests__/conversation-process-callsite.test.ts +25 -2
- package/src/__tests__/conversation-provider-retry-repair.test.ts +5 -1
- package/src/__tests__/conversation-queue.test.ts +4 -1
- package/src/__tests__/conversation-runtime-assembly.test.ts +80 -13
- package/src/__tests__/conversation-slash-commands.test.ts +194 -2
- package/src/__tests__/conversation-slash-queue.test.ts +59 -1
- package/src/__tests__/conversation-slash-unknown.test.ts +4 -1
- package/src/__tests__/conversation-surfaces-app-control.test.ts +323 -3
- package/src/__tests__/conversation-surfaces-table-action.test.ts +360 -0
- package/src/__tests__/conversation-sync-tags.test.ts +235 -0
- package/src/__tests__/conversation-workspace-injection.test.ts +5 -1
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +5 -1
- package/src/__tests__/credential-security-invariants.test.ts +8 -8
- package/src/__tests__/daemon-credential-client.test.ts +56 -1
- package/src/__tests__/db-activation-state-fk-cascade.test.ts +132 -0
- package/src/__tests__/db-conversation-inference-profile-migration.test.ts +37 -0
- package/src/__tests__/db-memory-graph-event-date-repair.test.ts +43 -20
- package/src/__tests__/db-proxy-transaction.test.ts +206 -0
- package/src/__tests__/db-slack-external-content-normalization.test.ts +301 -0
- package/src/__tests__/delete-managed-skill-tool.test.ts +55 -13
- package/src/__tests__/disk-pressure-tools.test.ts +1 -0
- package/src/__tests__/dm-backfill.test.ts +121 -10
- package/src/__tests__/document-tool-security.test.ts +258 -0
- package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +0 -1
- package/src/__tests__/edit-propagation.test.ts +33 -0
- package/src/__tests__/empty-response-pipeline.test.ts +0 -4
- package/src/__tests__/external-plugin-loader.test.ts +482 -0
- package/src/__tests__/filing-service.test.ts +163 -3
- package/src/__tests__/fixtures/mock-chrome-extension.ts +5 -0
- package/src/__tests__/gateway-only-guard.test.ts +0 -1
- package/src/__tests__/get-skill-detail-audit.test.ts +0 -4
- package/src/__tests__/graph-extraction-event-date.test.ts +34 -0
- package/src/__tests__/handlers-skills-memory-v2-reseed.test.ts +42 -69
- package/src/__tests__/heartbeat-disk-pressure.test.ts +21 -8
- package/src/__tests__/heartbeat-service.test.ts +50 -233
- package/src/__tests__/helpers/tar-fixtures.ts +39 -0
- package/src/__tests__/helpers/wait-for.ts +21 -0
- package/src/__tests__/history-repair-pipeline.test.ts +0 -3
- package/src/__tests__/history-repair.test.ts +162 -0
- package/src/__tests__/host-app-control-proxy.test.ts +365 -1
- package/src/__tests__/host-app-control-routes.test.ts +247 -1
- package/src/__tests__/host-browser-proxy.test.ts +416 -20
- package/src/__tests__/host-browser-routes.test.ts +325 -33
- package/src/__tests__/host-proxy-preactivation.test.ts +211 -0
- package/src/__tests__/image-credentials.test.ts +1 -1
- package/src/__tests__/inbound-slack-persistence.test.ts +2 -0
- package/src/__tests__/inference-no-mode-boot-e2e.test.ts +246 -0
- package/src/__tests__/inference-profile-reaper.test.ts +156 -0
- package/src/__tests__/inference-profile-session-handler.test.ts +410 -0
- package/src/__tests__/inference-profile-session-ipc.test.ts +248 -0
- package/src/__tests__/injector-chain.test.ts +10 -8
- package/src/__tests__/inline-skill-load-permissions.test.ts +6 -1
- package/src/__tests__/install-skill-routing.test.ts +157 -39
- package/src/__tests__/lifecycle-memory-v2-seed.test.ts +107 -3
- package/src/__tests__/list-messages-page-latest.test.ts +55 -0
- package/src/__tests__/llm-call-pipeline.test.ts +0 -3
- package/src/__tests__/llm-callsite-catalog.test.ts +20 -1
- package/src/__tests__/llm-catalog-parity.test.ts +190 -2
- package/src/__tests__/llm-request-log-source-clickhouse.test.ts +222 -0
- package/src/__tests__/llm-request-log-source-factory.test.ts +100 -0
- package/src/__tests__/llm-resolver.test.ts +46 -0
- package/src/__tests__/llm-usage-store.test.ts +114 -0
- package/src/__tests__/managed-profile-guard.test.ts +145 -14
- package/src/__tests__/managed-skill-lifecycle.test.ts +109 -18
- package/src/__tests__/managed-store.test.ts +84 -192
- package/src/__tests__/mcp-auth-routes.test.ts +1 -0
- package/src/__tests__/mcp-cli.test.ts +182 -220
- package/src/__tests__/mcp-health-check.test.ts +56 -27
- package/src/__tests__/media-generate-image.test.ts +1 -1
- package/src/__tests__/memory-jobs-worker-lanes.test.ts +18 -11
- package/src/__tests__/memory-retrieval-pipeline.test.ts +0 -2
- package/src/__tests__/message-complete-display-id.test.ts +175 -0
- package/src/__tests__/messages-after-tiebreaker.test.ts +122 -0
- package/src/__tests__/notification-platform-adapter.test.ts +229 -0
- package/src/__tests__/oauth-cli.test.ts +38 -2009
- package/src/__tests__/oauth-commands-routes.test.ts +863 -0
- package/src/__tests__/oauth-connect-routes.test.ts +174 -11
- package/src/__tests__/oauth-provider-profiles.test.ts +9 -0
- package/src/__tests__/oauth-providers-routes.test.ts +14 -10
- package/src/__tests__/openai-provider.test.ts +24 -0
- package/src/__tests__/openai-responses-cutover-guard.test.ts +48 -19
- package/src/__tests__/openai-responses-provider.test.ts +17 -0
- package/src/__tests__/overflow-reduce-pipeline.test.ts +0 -2
- package/src/__tests__/persistence-pipeline.test.ts +0 -2
- package/src/__tests__/{managed-proxy-context.test.ts → platform-proxy-context.test.ts} +1 -1
- package/src/__tests__/platform.test.ts +2 -0
- package/src/__tests__/plugin-api-shim.test.ts +125 -0
- package/src/__tests__/plugin-bootstrap.test.ts +41 -38
- package/src/__tests__/plugin-external-api.test.ts +68 -0
- package/src/__tests__/plugin-registry.test.ts +0 -77
- package/src/__tests__/plugin-route-contribution.test.ts +31 -4
- package/src/__tests__/plugin-skill-contribution.test.ts +0 -2
- package/src/__tests__/plugin-tool-contribution.test.ts +47 -18
- package/src/__tests__/plugin-types.test.ts +15 -23
- package/src/__tests__/process-message-background-slack.test.ts +53 -0
- package/src/__tests__/process-message-display-content.test.ts +421 -0
- package/src/__tests__/profile-entry-status.test.ts +43 -0
- package/src/__tests__/provider-catalog-visibility.test.ts +142 -0
- package/src/__tests__/provider-error-scenarios.test.ts +111 -0
- package/src/__tests__/{provider-managed-proxy-integration.test.ts → provider-platform-proxy-integration.test.ts} +20 -12
- package/src/__tests__/provider-registry-ollama.test.ts +12 -4
- package/src/__tests__/provider-send-message-override-profile.test.ts +10 -4
- package/src/__tests__/relay-server.test.ts +118 -0
- package/src/__tests__/retry-thinking-tool-choice.test.ts +15 -0
- package/src/__tests__/scaffold-managed-skill-tool.test.ts +65 -13
- package/src/__tests__/schedule-retry.test.ts +56 -4
- package/src/__tests__/schedule-routes.test.ts +151 -0
- package/src/__tests__/schedule-store.test.ts +94 -0
- package/src/__tests__/scheduler-disk-pressure.test.ts +0 -4
- package/src/__tests__/scheduler-recurrence.test.ts +87 -34
- package/src/__tests__/scheduler-reuse-conversation.test.ts +208 -5
- package/src/__tests__/scheduler-wake.test.ts +0 -63
- package/src/__tests__/schema-transforms.test.ts +20 -0
- package/src/__tests__/search-skills-unified.test.ts +0 -5
- package/src/__tests__/secret-allowlist.test.ts +1 -0
- package/src/__tests__/{secret-routes-managed-proxy.test.ts → secret-routes-platform-proxy.test.ts} +12 -4
- package/src/__tests__/server-history-render.test.ts +43 -0
- package/src/__tests__/shell-credential-ref.test.ts +95 -3
- package/src/__tests__/shell-tool-proxy-mode.test.ts +14 -0
- package/src/__tests__/skill-load-feature-flag.test.ts +1 -12
- package/src/__tests__/skill-load-tool.test.ts +29 -93
- package/src/__tests__/skill-memory.test.ts +23 -3
- package/src/__tests__/skills-file-content-endpoint.test.ts +9 -38
- package/src/__tests__/skills-files-catalog-fallback.test.ts +0 -3
- package/src/__tests__/skills-install-extract.test.ts +49 -38
- package/src/__tests__/skills-install-staging.test.ts +159 -0
- package/src/__tests__/skills-uninstall.test.ts +9 -41
- package/src/__tests__/skills.test.ts +51 -58
- package/src/__tests__/slack-channel-config.test.ts +9 -0
- package/src/__tests__/subagent-call-site-routing.test.ts +78 -16
- package/src/__tests__/subagent-tool-filtering.test.ts +50 -0
- package/src/__tests__/suggestion-routes.test.ts +3 -3
- package/src/__tests__/sync-message-contract.test.ts +63 -0
- package/src/__tests__/system-prompt.test.ts +737 -63
- package/src/__tests__/task-scheduler.test.ts +88 -23
- package/src/__tests__/terminal-tools.test.ts +28 -1
- package/src/__tests__/thread-backfill.test.ts +557 -27
- package/src/__tests__/title-generate-pipeline.test.ts +0 -13
- package/src/__tests__/token-estimate-pipeline.test.ts +0 -3
- package/src/__tests__/tool-error-pipeline.test.ts +0 -3
- package/src/__tests__/tool-execute-pipeline.test.ts +0 -5
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +1 -1
- package/src/__tests__/tool-executor.test.ts +16 -4
- package/src/__tests__/tool-result-truncate-pipeline.test.ts +0 -12
- package/src/__tests__/turn-events-store.test.ts +256 -0
- package/src/__tests__/twilio-routes.test.ts +4 -0
- package/src/__tests__/update-bulletin-job.test.ts +96 -193
- package/src/__tests__/usage-cli.test.ts +11 -73
- package/src/__tests__/user-plugin-loader.test.ts +143 -5
- package/src/__tests__/vercel-config.test.ts +168 -0
- package/src/__tests__/voice-session-bridge.test.ts +198 -0
- package/src/__tests__/web-search-catalog-parity.test.ts +108 -0
- package/src/__tests__/web-search.test.ts +303 -2
- package/src/__tests__/workspace-migration-039-drop-legacy-llm-keys.test.ts +1 -21
- package/src/__tests__/workspace-migration-057-repair-stale-gemini-model-ids.test.ts +170 -0
- package/src/__tests__/workspace-migration-069-seed-onboarding-threads.test.ts +53 -20
- package/src/__tests__/workspace-migration-072-seed-reply-suggestion-callsite.test.ts +241 -0
- package/src/__tests__/workspace-migration-073-repair-recall-callsite-empty-profile.test.ts +153 -0
- package/src/__tests__/workspace-migration-076-drop-services-inference-mode.test.ts +211 -0
- package/src/__tests__/workspace-migration-077-seed-memory-router-callsite.test.ts +174 -0
- package/src/__tests__/workspace-migration-079-home-feed-notification-only.test.ts +323 -0
- package/src/__tests__/workspace-migration-080-restrict-vercel-api-token-metadata.test.ts +299 -0
- package/src/__tests__/workspace-migration-081-backfill-bash-allowed-tools.test.ts +410 -0
- package/src/__tests__/workspace-migration-082-backfill-managed-profile-labels.test.ts +268 -0
- package/src/__tests__/workspace-migration-085-memory-v2-bm25-b-reembed-disabled-v2-pages.test.ts +220 -0
- package/src/__tests__/workspace-migration-086-revert-stale-gemini-mis-rewrites.test.ts +269 -0
- package/src/__tests__/workspace-migration-remove-legacy-skills-index.test.ts +309 -0
- package/src/__tests__/workspace-migration-unify-llm-callsite-configs.test.ts +3 -3
- package/src/__tests__/workspace-migrations-runner.test.ts +111 -3
- package/src/__tests__/workspace-release-notes-feature-flag-guard.test.ts +115 -0
- package/src/acp/__tests__/helpers/which-stub.ts +4 -2
- package/src/acp/resolve-agent.test.ts +25 -0
- package/src/acp/resolve-agent.ts +13 -2
- package/src/acp/session-manager.ts +14 -0
- package/src/agent/image-optimize.ts +13 -5
- package/src/approvals/guardian-request-resolvers.ts +32 -87
- package/src/calls/relay-server.ts +35 -0
- package/src/calls/relay-setup-router.ts +36 -0
- package/src/calls/types.ts +1 -0
- package/src/calls/voice-session-bridge.ts +74 -36
- package/src/channels/config.ts +14 -1
- package/src/channels/types.ts +109 -0
- package/src/cli/AGENTS.md +164 -4
- package/src/cli/__tests__/notifications.test.ts +54 -0
- package/src/cli/__tests__/unknown-command.test.ts +24 -0
- package/src/cli/commands/__tests__/avatar.test.ts +540 -0
- package/src/cli/commands/__tests__/backup.test.ts +236 -776
- package/src/cli/commands/__tests__/cache.test.ts +1 -1
- package/src/cli/commands/__tests__/changelog.test.ts +578 -0
- package/src/cli/commands/__tests__/channel-verification-sessions.test.ts +503 -0
- package/src/cli/commands/__tests__/conversations-import.test.ts +515 -0
- package/src/cli/commands/__tests__/domain-register.test.ts +140 -167
- package/src/cli/commands/__tests__/domain-status.test.ts +137 -76
- package/src/cli/commands/__tests__/email-attachment.test.ts +314 -337
- package/src/cli/commands/__tests__/email-core.test.ts +579 -0
- package/src/cli/commands/__tests__/image-generation.test.ts +87 -824
- package/src/cli/commands/__tests__/inference-send.test.ts +30 -266
- package/src/cli/commands/__tests__/inference-session.test.ts +423 -0
- package/src/cli/commands/__tests__/memory-v2.test.ts +81 -110
- package/src/cli/commands/__tests__/schedules.test.ts +491 -0
- package/src/cli/commands/__tests__/skills.test.ts +563 -0
- package/src/cli/commands/__tests__/status.test.ts +249 -0
- package/src/cli/commands/__tests__/stt.test.ts +320 -0
- package/src/cli/commands/__tests__/tts-synthesize.test.ts +4 -603
- package/src/cli/commands/__tests__/tts.test.ts +321 -0
- package/src/cli/commands/__tests__/webhooks.test.ts +86 -511
- package/src/cli/commands/attachment.ts +8 -3
- package/src/cli/commands/audit.ts +95 -64
- package/src/cli/commands/auth.ts +61 -58
- package/src/cli/commands/avatar.ts +276 -390
- package/src/cli/commands/backup.ts +409 -505
- package/src/cli/commands/bash.ts +9 -5
- package/src/cli/commands/browser.ts +28 -9
- package/src/cli/commands/cache.ts +9 -4
- package/src/cli/commands/changelog.ts +478 -0
- package/src/cli/commands/channel-verification-sessions.ts +238 -317
- package/src/cli/commands/clients.ts +8 -3
- package/src/cli/commands/completions.ts +9 -9
- package/src/cli/commands/config.ts +102 -72
- package/src/cli/commands/contacts.ts +575 -696
- package/src/cli/commands/conversations-defer.ts +17 -69
- package/src/cli/commands/conversations-import.ts +90 -253
- package/src/cli/commands/conversations.ts +429 -434
- package/src/cli/commands/credential-execution.ts +9 -6
- package/src/cli/commands/credentials.ts +456 -736
- package/src/cli/commands/default-action.ts +10 -53
- package/src/cli/commands/domain.ts +128 -206
- package/src/cli/commands/email.ts +606 -794
- package/src/cli/commands/gateway.ts +8 -1
- package/src/cli/commands/image-generation.ts +157 -205
- package/src/cli/commands/inference-providers.ts +352 -0
- package/src/cli/commands/inference-session.ts +415 -0
- package/src/cli/commands/inference.ts +87 -65
- package/src/cli/commands/keys.ts +8 -3
- package/src/cli/commands/mcp.ts +103 -287
- package/src/cli/commands/memory-v2.ts +162 -516
- package/src/cli/commands/notifications.ts +342 -304
- package/src/cli/commands/oauth/apps.ts +292 -261
- package/src/cli/commands/oauth/connect.ts +176 -297
- package/src/cli/commands/oauth/disconnect.ts +16 -215
- package/src/cli/commands/oauth/index.ts +49 -45
- package/src/cli/commands/oauth/mode.ts +43 -199
- package/src/cli/commands/oauth/ping.ts +17 -125
- package/src/cli/commands/oauth/providers.ts +732 -921
- package/src/cli/commands/oauth/request.ts +60 -350
- package/src/cli/commands/oauth/shared.ts +11 -121
- package/src/cli/commands/oauth/status.ts +31 -121
- package/src/cli/commands/oauth/token.ts +13 -55
- package/src/cli/commands/pending.ts +19 -10
- package/src/cli/commands/platform/__tests__/callback-routes-list.test.ts +133 -183
- package/src/cli/commands/platform/__tests__/connect.test.ts +66 -181
- package/src/cli/commands/platform/__tests__/disconnect.test.ts +71 -227
- package/src/cli/commands/platform/__tests__/status.test.ts +169 -287
- package/src/cli/commands/platform/connect.ts +16 -80
- package/src/cli/commands/platform/disconnect.ts +14 -112
- package/src/cli/commands/platform/index.ts +177 -246
- package/src/cli/commands/plugins.ts +185 -0
- package/src/cli/commands/routes.ts +153 -336
- package/src/cli/commands/schedules.ts +391 -0
- package/src/cli/commands/sequence.ts +316 -360
- package/src/cli/commands/skills.ts +449 -671
- package/src/cli/commands/status.ts +58 -37
- package/src/cli/commands/stt.ts +94 -262
- package/src/cli/commands/task.ts +14 -40
- package/src/cli/commands/telemetry.ts +40 -0
- package/src/cli/commands/trust.ts +8 -3
- package/src/cli/commands/tts.ts +162 -167
- package/src/cli/commands/ui.ts +35 -42
- package/src/cli/commands/usage.ts +188 -126
- package/src/cli/commands/watchers.ts +8 -3
- package/src/cli/commands/webhooks.ts +99 -193
- package/src/cli/lib/__tests__/cli-colors.test.ts +48 -0
- package/src/cli/lib/__tests__/confirm-prompt.test.ts +159 -0
- package/src/cli/lib/__tests__/install-from-github.test.ts +355 -0
- package/src/cli/lib/__tests__/list-installed-plugins.test.ts +154 -0
- package/src/cli/lib/__tests__/register-command.test.ts +85 -0
- package/src/cli/lib/__tests__/uninstall-plugin.test.ts +124 -0
- package/src/cli/lib/__tests__/unknown-command.test.ts +106 -0
- package/src/cli/lib/cli-colors.ts +12 -0
- package/src/cli/lib/confirm-prompt.ts +79 -0
- package/src/cli/lib/daemon-credential-client.ts +4 -5
- package/src/cli/lib/install-from-github.ts +304 -0
- package/src/cli/lib/list-installed-plugins.ts +137 -0
- package/src/cli/lib/nested-value.ts +44 -0
- package/src/cli/lib/open-browser.ts +36 -0
- package/src/cli/lib/register-command.ts +19 -0
- package/src/cli/lib/time-ago.ts +34 -0
- package/src/cli/lib/uninstall-plugin.ts +82 -0
- package/src/cli/lib/unknown-command.ts +111 -0
- package/src/cli/program.ts +40 -6
- package/src/cli/utils/__tests__/conversation-id.test.ts +66 -0
- package/src/cli/utils/__tests__/parse-duration.test.ts +49 -0
- package/src/cli/utils/conversation-id.ts +30 -0
- package/src/cli/utils/parse-duration.ts +41 -0
- package/src/config/acp-defaults.test.ts +5 -1
- package/src/config/acp-defaults.ts +11 -4
- package/src/config/bundled-skills/acp/TOOLS.json +2 -2
- package/src/config/bundled-skills/app-builder/SKILL.md +23 -21
- package/src/config/bundled-skills/app-builder/TOOLS.json +7 -0
- package/src/config/bundled-skills/app-control/TOOLS.json +32 -0
- package/src/config/bundled-skills/computer-use/TOOLS.json +15 -52
- package/src/config/bundled-skills/contacts/SKILL.md +12 -45
- package/src/config/bundled-skills/contacts/TOOLS.json +0 -57
- package/src/config/bundled-skills/document/SKILL.md +23 -3
- package/src/config/bundled-skills/document/TOOLS.json +53 -0
- package/src/config/bundled-skills/document/tools/document-delete.ts +12 -0
- package/src/config/bundled-skills/document/tools/document-list.ts +12 -0
- package/src/config/bundled-skills/document/tools/document-read.ts +12 -0
- package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +0 -12
- package/src/config/bundled-skills/messaging/tools/messaging-send.ts +0 -58
- package/src/config/bundled-skills/skill-management/SKILL.md +2 -2
- package/src/config/bundled-skills/skill-management/TOOLS.json +7 -7
- package/src/config/bundled-tool-registry.ts +6 -2
- package/src/config/feature-flag-registry.json +57 -1
- package/src/config/llm-resolver.ts +16 -1
- package/src/config/loader.ts +140 -52
- package/src/config/raw-config-utils.ts +2 -30
- package/src/config/schema.ts +8 -7
- package/src/config/schemas/__tests__/llm-request-logs.test.ts +36 -0
- package/src/config/schemas/__tests__/memory-v2.test.ts +49 -0
- package/src/config/schemas/call-site-catalog.ts +29 -7
- package/src/config/schemas/channels.ts +8 -0
- package/src/config/schemas/compaction.ts +28 -0
- package/src/config/schemas/heartbeat.ts +9 -0
- package/src/config/schemas/llm-request-logs.ts +81 -0
- package/src/config/schemas/llm.ts +55 -2
- package/src/config/schemas/memory-retrieval.ts +18 -0
- package/src/config/schemas/memory-retrospective.ts +48 -0
- package/src/config/schemas/memory-v2.ts +32 -1
- package/src/config/schemas/memory.ts +4 -0
- package/src/config/schemas/services.ts +15 -12
- package/src/config/schemas/tools.ts +14 -0
- package/src/config/seed-inference-profiles.ts +195 -134
- package/src/config/skills.ts +3 -96
- package/src/contacts/contact-store.ts +0 -61
- package/src/context/compactor.ts +1047 -0
- package/src/context/token-estimator.ts +2 -2
- package/src/context/window-manager.ts +197 -1334
- package/src/credential-execution/managed-catalog.ts +37 -0
- package/src/credential-health/credential-health-service.ts +280 -19
- package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +113 -0
- package/src/daemon/__tests__/conversation-tool-setup-exclude.test.ts +138 -0
- package/src/daemon/__tests__/conversation-tool-setup.test.ts +183 -4
- package/src/daemon/__tests__/daemon-skill-host.test.ts +10 -4
- package/src/daemon/approval-generators.ts +26 -30
- package/src/daemon/config-watcher.ts +94 -29
- package/src/daemon/conversation-agent-loop-handlers.ts +24 -0
- package/src/daemon/conversation-agent-loop.ts +293 -103
- package/src/daemon/conversation-error.ts +188 -33
- package/src/daemon/conversation-lifecycle.ts +80 -26
- package/src/daemon/conversation-messaging.ts +25 -6
- package/src/daemon/conversation-process.ts +85 -31
- package/src/daemon/conversation-runtime-assembly.ts +30 -6
- package/src/daemon/conversation-slash.ts +184 -25
- package/src/daemon/conversation-store.ts +24 -10
- package/src/daemon/conversation-surfaces.ts +76 -12
- package/src/daemon/conversation-tool-setup.ts +63 -21
- package/src/daemon/conversation.ts +81 -10
- package/src/daemon/external-plugins-bootstrap.ts +231 -185
- package/src/daemon/first-greeting.ts +22 -2
- package/src/daemon/guardian-action-generators.ts +7 -22
- package/src/daemon/handlers/config-model.ts +13 -130
- package/src/daemon/handlers/config-slack-channel.ts +25 -10
- package/src/daemon/handlers/config-vercel.ts +3 -1
- package/src/daemon/handlers/shared.ts +14 -5
- package/src/daemon/handlers/skills.ts +166 -84
- package/src/daemon/history-repair.ts +61 -7
- package/src/daemon/host-app-control-proxy.ts +129 -29
- package/src/daemon/host-bash-proxy.ts +85 -158
- package/src/daemon/host-browser-proxy.ts +96 -35
- package/src/daemon/host-proxy-base.ts +13 -1
- package/src/daemon/host-proxy-preactivation.ts +25 -1
- package/src/daemon/identity-helpers.ts +19 -0
- package/src/daemon/lifecycle.ts +79 -70
- package/src/daemon/meet-host-supervisor.ts +20 -19
- package/src/daemon/memory-v2-startup.ts +58 -2
- package/src/daemon/message-protocol.ts +7 -0
- package/src/daemon/message-types/bookmarks.ts +18 -0
- package/src/daemon/message-types/conversations.ts +37 -9
- package/src/daemon/message-types/messages.ts +70 -1
- package/src/daemon/message-types/subagents.ts +1 -0
- package/src/daemon/message-types/sync.ts +61 -0
- package/src/daemon/pkb-reminder-builder.test.ts +54 -13
- package/src/daemon/pkb-reminder-builder.ts +21 -7
- package/src/daemon/plugin-source-watcher.ts +146 -0
- package/src/daemon/process-message.ts +77 -26
- package/src/daemon/server.ts +34 -20
- package/src/daemon/shutdown-handlers.ts +0 -2
- package/src/daemon/skill-memory-refresh.ts +29 -0
- package/src/daemon/tool-setup-types.ts +9 -0
- package/src/daemon/tool-side-effects.ts +6 -4
- package/src/daemon/wake-target-adapter.ts +11 -0
- package/src/documents/document-store.ts +221 -3
- package/src/embedded/plugin-api.ts +40 -0
- package/src/export/transcript-formatter.ts +61 -2
- package/src/filing/filing-service.ts +79 -53
- package/src/heartbeat/__tests__/heartbeat-service.test.ts +444 -0
- package/src/heartbeat/heartbeat-run-store.ts +3 -1
- package/src/heartbeat/heartbeat-service.ts +189 -127
- package/src/home/__tests__/feed-types.test.ts +99 -127
- package/src/home/__tests__/feed-writer.test.ts +77 -278
- package/src/home/__tests__/post-connect-feed.test.ts +9 -12
- package/src/home/feed-types.ts +41 -73
- package/src/home/feed-writer.ts +25 -156
- package/src/home/post-connect-feed.ts +2 -3
- package/src/index.ts +18 -1
- package/src/ipc/__tests__/cli-ipc.test.ts +2 -0
- package/src/ipc/__tests__/email-ipc.test.ts +506 -0
- package/src/ipc/__tests__/exit-helper.test.ts +104 -0
- package/src/ipc/__tests__/streaming-client.test.ts +237 -0
- package/src/ipc/__tests__/streaming-framing.test.ts +142 -0
- package/src/ipc/assistant-server.ts +55 -6
- package/src/ipc/cli-client.ts +370 -50
- package/src/ipc/routes/db-proxy-transaction.ts +151 -0
- package/src/ipc/skill-routes/__tests__/events-ipc.test.ts +60 -0
- package/src/ipc/skill-routes/events.ts +30 -3
- package/src/live-voice/__tests__/live-voice-session-manager.test.ts +46 -0
- package/src/live-voice/__tests__/live-voice-stt.test.ts +57 -0
- package/src/live-voice/__tests__/runtime-websocket-shell.test.ts +1 -0
- package/src/live-voice/live-voice-session-manager.ts +11 -4
- package/src/live-voice/live-voice-session.ts +14 -6
- package/src/mcp/client.ts +20 -4
- package/src/media/image-credentials.ts +3 -3
- package/src/memory/__tests__/bookmark-crud.test.ts +264 -0
- package/src/memory/__tests__/bookmark-schema.test.ts +181 -0
- package/src/memory/__tests__/conversation-queries.test.ts +263 -0
- package/src/memory/__tests__/conversation-types.test.ts +36 -0
- package/src/memory/__tests__/find-most-recent-retrospective-for.test.ts +130 -0
- package/src/memory/__tests__/jobs-worker-v2-graph-trigger-embed.test.ts +113 -0
- package/src/memory/__tests__/memory-retrospective-enqueue.test.ts +177 -0
- package/src/memory/__tests__/memory-retrospective-job.test.ts +328 -0
- package/src/memory/__tests__/memory-retrospective-startup-cleanup.test.ts +318 -0
- package/src/memory/__tests__/memory-retrospective-trigger-check.test.ts +90 -0
- package/src/memory/__tests__/memory-v2-activation-log-store.test.ts +69 -0
- package/src/memory/__tests__/memory-v2-concept-frequency.test.ts +3 -0
- package/src/memory/__tests__/message-content.test.ts +35 -0
- package/src/memory/bookmark-crud.ts +211 -0
- package/src/memory/context-search/__tests__/agent-runner-redaction.test.ts +31 -9
- package/src/memory/context-search/agent-protocol.ts +5 -1
- package/src/memory/context-search/agent-runner.ts +60 -85
- package/src/memory/context-search/limits.ts +1 -4
- package/src/memory/context-search/search.ts +23 -113
- package/src/memory/context-search/sources/conversations.ts +80 -8
- package/src/memory/context-search/sources/memory-v2.ts +39 -14
- package/src/memory/context-search/sources/memory.ts +7 -0
- package/src/memory/context-search/sources/workspace.ts +17 -10
- package/src/memory/context-search/types.ts +1 -1
- package/src/memory/conversation-bootstrap.ts +11 -0
- package/src/memory/conversation-crud.ts +368 -22
- package/src/memory/conversation-queries.ts +116 -12
- package/src/memory/conversation-title-service.ts +1 -0
- package/src/memory/conversation-types.ts +16 -0
- package/src/memory/db-init.ts +20 -0
- package/src/memory/delivery-crud.ts +152 -5
- package/src/memory/embedding-backend.ts +6 -5
- package/src/memory/embedding-runtime-manager.ts +1 -2
- package/src/memory/external-conversation-store.ts +66 -5
- package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +66 -9
- package/src/memory/graph/__tests__/remember-description.test.ts +55 -0
- package/src/memory/graph/conversation-graph-memory.ts +92 -5
- package/src/memory/graph/extraction.ts +4 -0
- package/src/memory/graph/graph-memory-state-store.ts +16 -3
- package/src/memory/graph/tool-handlers.ts +17 -7
- package/src/memory/graph/tools.ts +45 -6
- package/src/memory/indexer.ts +51 -29
- package/src/memory/jobs/__tests__/embed-concept-page.test.ts +86 -15
- package/src/memory/jobs/embed-concept-page.ts +65 -20
- package/src/memory/jobs-store.ts +51 -1
- package/src/memory/jobs-worker.ts +57 -3
- package/src/memory/llm-request-log-source-clickhouse.ts +324 -0
- package/src/memory/llm-request-log-source-local.ts +26 -0
- package/src/memory/llm-request-log-source.ts +64 -0
- package/src/memory/llm-request-log-store.ts +1 -1
- package/src/memory/llm-usage-store.ts +125 -5
- package/src/memory/memory-retrospective-constants.ts +13 -0
- package/src/memory/memory-retrospective-enqueue.ts +114 -0
- package/src/memory/memory-retrospective-job.ts +351 -0
- package/src/memory/memory-retrospective-startup-cleanup.ts +175 -0
- package/src/memory/memory-retrospective-state.ts +162 -0
- package/src/memory/memory-retrospective-trigger-check.ts +91 -0
- package/src/memory/memory-v2-activation-log-store.ts +49 -5
- package/src/memory/memory-v2-concept-frequency.ts +4 -0
- package/src/memory/message-content.ts +38 -1
- package/src/memory/migrations/109-external-conversation-bindings.ts +15 -4
- package/src/memory/migrations/227-add-conversation-inference-profile.ts +6 -1
- package/src/memory/migrations/228-rename-inference-profile-snake-case.ts +20 -7
- package/src/memory/migrations/229-delete-private-conversations.test.ts +107 -1
- package/src/memory/migrations/229-delete-private-conversations.ts +19 -0
- package/src/memory/migrations/231-repair-memory-graph-event-dates.ts +16 -2
- package/src/memory/migrations/240-conversation-inference-profile-session.ts +25 -0
- package/src/memory/migrations/241-activation-state-fk-cascade.ts +50 -0
- package/src/memory/migrations/242-message-bookmarks.ts +38 -0
- package/src/memory/migrations/243-provider-connections.ts +68 -0
- package/src/memory/migrations/244-provider-connection-status-label.ts +23 -0
- package/src/memory/migrations/245-memory-retrospective-state.ts +36 -0
- package/src/memory/migrations/246-backfill-provider-connection-label.ts +81 -0
- package/src/memory/migrations/247-external-conversation-binding-thread-id.ts +78 -0
- package/src/memory/migrations/248-create-onboarding-events.ts +21 -0
- package/src/memory/migrations/249-normalize-slack-external-content.ts +240 -0
- package/src/memory/migrations/__tests__/244-provider-connection-status-label.test.ts +84 -0
- package/src/memory/migrations/__tests__/245-memory-retrospective-state.test.ts +125 -0
- package/src/memory/migrations/__tests__/246-backfill-provider-connection-label.test.ts +192 -0
- package/src/memory/migrations/index.ts +13 -0
- package/src/memory/migrations/registry.ts +8 -0
- package/src/memory/onboarding-events-store.ts +106 -0
- package/src/memory/published-pages-store.ts +16 -0
- package/src/memory/schema/bookmarks.ts +36 -0
- package/src/memory/schema/calls.ts +1 -0
- package/src/memory/schema/conversations.ts +2 -0
- package/src/memory/schema/index.ts +2 -0
- package/src/memory/schema/inference.ts +27 -0
- package/src/memory/schema/infrastructure.ts +12 -0
- package/src/memory/schema/memory-core.ts +9 -0
- package/src/memory/search/semantic.ts +1 -4
- package/src/memory/turn-events-store.ts +127 -2
- package/src/memory/v2/__tests__/__snapshots__/prompts-router.test.ts.snap +27 -0
- package/src/memory/v2/__tests__/activation-store.test.ts +5 -5
- package/src/memory/v2/__tests__/activation.test.ts +11 -12
- package/src/memory/v2/__tests__/backfill-jobs.test.ts +38 -21
- package/src/memory/v2/__tests__/consolidation-job.test.ts +123 -135
- package/src/memory/v2/__tests__/edge-index.test.ts +1 -1
- package/src/memory/v2/__tests__/frontmatter-sweep.test.ts +111 -0
- package/src/memory/v2/__tests__/injection.test.ts +726 -18
- package/src/memory/v2/__tests__/migration.test.ts +94 -3
- package/src/memory/v2/__tests__/page-index.test.ts +360 -0
- package/src/memory/v2/__tests__/page-store.test.ts +14 -1
- package/src/memory/v2/__tests__/prompts-router.test.ts +309 -0
- package/src/memory/v2/__tests__/qdrant.test.ts +138 -3
- package/src/memory/v2/__tests__/reranker.test.ts +4 -4
- package/src/memory/v2/__tests__/router.test.ts +531 -0
- package/src/memory/v2/__tests__/sim.test.ts +45 -1
- package/src/memory/v2/__tests__/skill-store.test.ts +445 -11
- package/src/memory/v2/__tests__/static-context.test.ts +7 -22
- package/src/memory/v2/__tests__/sweep-job.test.ts +95 -0
- package/src/memory/v2/activation-store.ts +34 -5
- package/src/memory/v2/activation.ts +40 -27
- package/src/memory/v2/backfill-jobs.ts +17 -84
- package/src/memory/v2/consolidation-job.ts +85 -78
- package/src/memory/v2/frontmatter-sweep.ts +91 -0
- package/src/memory/v2/injection.ts +466 -109
- package/src/memory/v2/migration.ts +147 -20
- package/src/memory/v2/page-index.ts +221 -0
- package/src/memory/v2/page-store.ts +3 -0
- package/src/memory/v2/prompts/consolidation.ts +9 -7
- package/src/memory/v2/prompts/router.ts +195 -0
- package/src/memory/v2/prompts/sweep.ts +2 -2
- package/src/memory/v2/qdrant.ts +234 -93
- package/src/memory/v2/reranker.ts +14 -7
- package/src/memory/v2/router.ts +323 -0
- package/src/memory/v2/sim.ts +25 -12
- package/src/memory/v2/skill-store.ts +204 -30
- package/src/memory/v2/static-context.ts +16 -9
- package/src/memory/v2/sweep-job.ts +122 -96
- package/src/memory/v2/types.ts +10 -6
- package/src/memory/validation.ts +13 -0
- package/src/messaging/providers/slack/__tests__/adapter-token-routing.test.ts +45 -5
- package/src/messaging/providers/slack/__tests__/download.test.ts +231 -0
- package/src/messaging/providers/slack/adapter.ts +43 -5
- package/src/messaging/providers/slack/client.ts +27 -0
- package/src/messaging/providers/slack/deep-link.ts +65 -0
- package/src/messaging/providers/slack/download.ts +104 -0
- package/src/messaging/providers/slack/message-metadata.test.ts +32 -0
- package/src/messaging/providers/slack/message-metadata.ts +27 -0
- package/src/messaging/providers/slack/render-transcript.test.ts +134 -0
- package/src/messaging/providers/slack/render-transcript.ts +69 -5
- package/src/messaging/providers/slack/types.ts +20 -1
- package/src/notifications/__tests__/emit-signal-home-feed.test.ts +182 -0
- package/src/notifications/__tests__/home-feed-side-effect.test.ts +199 -0
- package/src/notifications/__tests__/signal-registry.test.ts +17 -0
- package/src/notifications/adapters/platform.ts +171 -0
- package/src/notifications/conversation-pairing.ts +4 -3
- package/src/notifications/copy-composer.ts +15 -0
- package/src/notifications/decision-engine.ts +2 -1
- package/src/notifications/destination-resolver.ts +21 -0
- package/src/notifications/emit-signal.ts +48 -2
- package/src/notifications/home-feed-side-effect.ts +165 -0
- package/src/notifications/signal.ts +8 -1
- package/src/oauth/connection-resolver.ts +8 -4
- package/src/oauth/platform-connection.ts +6 -2
- package/src/oauth/seed-providers.ts +10 -1
- package/src/permissions/checker.ts +14 -0
- package/src/permissions/ipc-risk-types.ts +3 -0
- package/src/permissions/question-prompter.test.ts +416 -0
- package/src/permissions/question-prompter.ts +294 -0
- package/src/platform/client.test.ts +1 -1
- package/src/platform/client.ts +1 -1
- package/src/plugin-api/constants.ts +26 -0
- package/src/plugin-api/index.ts +46 -0
- package/src/plugin-api/package.json +12 -0
- package/src/plugin-api/types.ts +144 -0
- package/src/plugins/defaults/circuit-breaker.ts +0 -5
- package/src/plugins/defaults/compaction.ts +0 -4
- package/src/plugins/defaults/empty-response.ts +0 -2
- package/src/plugins/defaults/history-repair.ts +0 -2
- package/src/plugins/defaults/injectors.ts +55 -6
- package/src/plugins/defaults/llm-call.ts +0 -2
- package/src/plugins/defaults/memory-retrieval.ts +0 -1
- package/src/plugins/defaults/overflow-reduce.ts +0 -1
- package/src/plugins/defaults/persistence.ts +0 -2
- package/src/plugins/defaults/title-generate.ts +0 -5
- package/src/plugins/defaults/token-estimate.ts +0 -2
- package/src/plugins/defaults/tool-error.ts +0 -7
- package/src/plugins/defaults/tool-execute.ts +0 -2
- package/src/plugins/defaults/tool-result-truncate.ts +0 -4
- package/src/plugins/ensure-plugin-api-shim.ts +96 -0
- package/src/plugins/external-api.ts +104 -0
- package/src/plugins/external-plugin-loader.ts +367 -0
- package/src/plugins/feature-gate.ts +22 -0
- package/src/plugins/pipeline.ts +37 -0
- package/src/plugins/registry.ts +48 -80
- package/src/plugins/types.ts +74 -53
- package/src/plugins/user-loader.ts +85 -43
- package/src/proactive-artifact/aux-message-injector.ts +11 -0
- package/src/proactive-artifact/job.test.ts +49 -9
- package/src/proactive-artifact/job.ts +4 -0
- package/src/proactive-artifact/trigger-state.test.ts +9 -0
- package/src/proactive-artifact/trigger-state.ts +4 -0
- package/src/prompts/__tests__/system-prompt.test.ts +117 -0
- package/src/prompts/__tests__/task-progress-hint-section.test.ts +99 -0
- package/src/prompts/normalize-onboarding.ts +27 -0
- package/src/prompts/sections.ts +302 -0
- package/src/prompts/system-prompt.ts +72 -154
- package/src/prompts/templates/BOOTSTRAP.md +17 -1
- package/src/prompts/templates/system-sections.ts +173 -0
- package/src/prompts/update-bulletin-job.ts +61 -73
- package/src/providers/__tests__/dispatch-connection-routing.test.ts +279 -0
- package/src/providers/__tests__/inference.test.ts +303 -0
- package/src/providers/__tests__/provider-env-vars.test.ts +6 -0
- package/src/providers/__tests__/provider-secret-catalog.test.ts +6 -0
- package/src/providers/__tests__/retry-callsite.test.ts +14 -32
- package/src/providers/__tests__/satellite-connection-routing.test.ts +510 -0
- package/src/providers/__tests__/search-provider-catalog.test.ts +80 -0
- package/src/providers/anthropic/client.ts +123 -54
- package/src/providers/call-site-routing.ts +94 -16
- package/src/providers/connection-resolution.ts +170 -0
- package/src/providers/inference/__tests__/connections-status-label.test.ts +250 -0
- package/src/providers/inference/adapter-factory.ts +210 -0
- package/src/providers/inference/auth.ts +112 -0
- package/src/providers/inference/backfill.ts +196 -0
- package/src/providers/inference/connections.ts +401 -0
- package/src/providers/inference/resolve-auth.ts +73 -0
- package/src/providers/model-catalog.ts +386 -6
- package/src/providers/openai/chat-completions-provider.ts +10 -2
- package/src/providers/openai/responses-provider.ts +4 -2
- package/src/providers/openrouter/client.ts +7 -0
- package/src/providers/{managed-proxy → platform-proxy}/constants.ts +4 -1
- package/src/providers/{managed-proxy → platform-proxy}/context.ts +3 -3
- package/src/providers/provider-availability.ts +17 -2
- package/src/providers/provider-catalog-visibility.ts +36 -0
- package/src/providers/provider-env-vars.ts +17 -7
- package/src/providers/provider-secret-catalog.ts +49 -30
- package/src/providers/provider-send-message.ts +41 -20
- package/src/providers/registry.ts +151 -159
- package/src/providers/retry.ts +65 -11
- package/src/providers/search-provider-catalog.ts +121 -0
- package/src/runtime/AGENTS.md +18 -5
- package/src/runtime/__tests__/agent-wake.test.ts +152 -0
- package/src/runtime/__tests__/background-job-runner.test.ts +357 -0
- package/src/runtime/__tests__/pre-first-message-gate.test.ts +82 -0
- package/src/runtime/actor-trust-resolver.ts +32 -10
- package/src/runtime/agent-wake.ts +64 -7
- package/src/runtime/assistant-event-hub.ts +3 -85
- package/src/runtime/auth/route-policy.ts +311 -9
- package/src/runtime/auth/same-actor.ts +2 -0
- package/src/runtime/background-job-runner.ts +339 -0
- package/src/runtime/btw-sidechain.ts +3 -0
- package/src/runtime/http-router.ts +36 -1
- package/src/runtime/http-server.ts +31 -5
- package/src/runtime/http-types.ts +21 -0
- package/src/runtime/middleware/__tests__/request-logger.test.ts +162 -0
- package/src/runtime/middleware/request-logger.ts +62 -1
- package/src/runtime/migrations/origin-mode.ts +1 -1
- package/src/runtime/pending-interactions.ts +1 -0
- package/src/runtime/pre-first-message-gate.ts +83 -0
- package/src/runtime/routes/__tests__/backup-routes.test.ts +8 -1
- package/src/runtime/routes/__tests__/bookmark-routes.test.ts +268 -0
- package/src/runtime/routes/__tests__/connection-routes-vs-cli-parity.test.ts +142 -0
- package/src/runtime/routes/__tests__/conversation-management-routes.test.ts +319 -0
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +280 -4
- package/src/runtime/routes/__tests__/home-feed-routes.test.ts +15 -136
- package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +736 -0
- package/src/runtime/routes/__tests__/memory-v2-routes.test.ts +4 -4
- package/src/runtime/routes/__tests__/question-routes.test.ts +395 -0
- package/src/runtime/routes/__tests__/stt-routes.test.ts +5 -1
- package/src/runtime/routes/__tests__/surface-action-routes.test.ts +384 -0
- package/src/runtime/routes/__tests__/tts-routes.test.ts +70 -3
- package/src/runtime/routes/acp-routes-list.test.ts +143 -0
- package/src/runtime/routes/acp-routes.ts +12 -8
- package/src/runtime/routes/app-management-routes.ts +228 -3
- package/src/runtime/routes/approval-routes.ts +0 -18
- package/src/runtime/routes/audit-routes.ts +43 -0
- package/src/runtime/routes/auth-routes.ts +72 -0
- package/src/runtime/routes/avatar-routes.ts +273 -20
- package/src/runtime/routes/backup-routes.ts +406 -2
- package/src/runtime/routes/bookmark-routes.ts +156 -0
- package/src/runtime/routes/btw-routes.ts +5 -1
- package/src/runtime/routes/channel-availability-routes.ts +121 -0
- package/src/runtime/routes/channel-verification-routes.ts +2 -1
- package/src/runtime/routes/contact-routes.ts +0 -160
- package/src/runtime/routes/conversation-cli-routes.ts +233 -0
- package/src/runtime/routes/conversation-list-routes.ts +3 -20
- package/src/runtime/routes/conversation-management-routes.ts +47 -85
- package/src/runtime/routes/conversation-query-routes.ts +350 -97
- package/src/runtime/routes/conversation-routes.ts +121 -21
- package/src/runtime/routes/conversations-import-routes.ts +229 -0
- package/src/runtime/routes/credential-routes.ts +540 -0
- package/src/runtime/routes/debug-routes.ts +2 -2
- package/src/runtime/routes/document-pdf-renderer.ts +5 -1
- package/src/runtime/routes/documents-routes.ts +25 -86
- package/src/runtime/routes/domain-routes.ts +167 -0
- package/src/runtime/routes/email-routes.ts +603 -0
- package/src/runtime/routes/errors.ts +2 -2
- package/src/runtime/routes/events-routes.ts +192 -0
- package/src/runtime/routes/group-routes.ts +5 -0
- package/src/runtime/routes/home-feed-routes.ts +6 -78
- package/src/runtime/routes/host-app-control-routes.ts +44 -2
- package/src/runtime/routes/host-browser-routes.ts +103 -22
- package/src/runtime/routes/http-adapter.ts +2 -0
- package/src/runtime/routes/identity-routes.ts +5 -0
- package/src/runtime/routes/image-generation-routes.ts +99 -0
- package/src/runtime/routes/inbound-conversation.ts +28 -8
- package/src/runtime/routes/inbound-message-handler.ts +236 -41
- package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +248 -1
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +118 -7
- package/src/runtime/routes/inbound-stages/edit-intercept.ts +17 -4
- package/src/runtime/routes/inbound-stages/guardian-reply-intercept.test.ts +156 -0
- package/src/runtime/routes/inbound-stages/guardian-reply-intercept.ts +22 -4
- package/src/runtime/routes/index.ts +42 -0
- package/src/runtime/routes/inference-profile-session-handler.ts +285 -0
- package/src/runtime/routes/inference-profile-session-reaper.ts +84 -0
- package/src/runtime/routes/inference-profile-session-routes.ts +146 -0
- package/src/runtime/routes/inference-provider-connection-routes.ts +361 -0
- package/src/runtime/routes/inference-send-routes.ts +115 -0
- package/src/runtime/routes/integrations/slack/share.ts +4 -52
- package/src/runtime/routes/integrations/slack/token.ts +43 -0
- package/src/runtime/routes/integrations/twilio.ts +7 -13
- package/src/runtime/routes/mcp-auth-routes.ts +283 -9
- package/src/runtime/routes/memory-v2-routes.ts +13 -398
- package/src/runtime/routes/notification-routes.ts +3 -1
- package/src/runtime/routes/oauth-apps.ts +112 -7
- package/src/runtime/routes/oauth-commands-routes.ts +1097 -0
- package/src/runtime/routes/oauth-connect-routes.ts +67 -5
- package/src/runtime/routes/oauth-lifecycle-routes.ts +43 -0
- package/src/runtime/routes/oauth-providers.ts +298 -8
- package/src/runtime/routes/platform-routes.ts +336 -0
- package/src/runtime/routes/playground/inject-failures.ts +2 -1
- package/src/runtime/routes/playground/reset-circuit.ts +2 -1
- package/src/runtime/routes/playground/state.ts +2 -1
- package/src/runtime/routes/publish-routes.ts +221 -0
- package/src/runtime/routes/question-routes.ts +259 -0
- package/src/runtime/routes/rename-conversation-routes.ts +2 -33
- package/src/runtime/routes/schedule-routes.ts +79 -0
- package/src/runtime/routes/sequence-routes.ts +291 -0
- package/src/runtime/routes/settings-routes.ts +2 -10
- package/src/runtime/routes/skills-routes.ts +31 -1
- package/src/runtime/routes/stt-routes.ts +240 -3
- package/src/runtime/routes/subagents-routes.ts +57 -18
- package/src/runtime/routes/surface-action-routes.ts +43 -7
- package/src/runtime/routes/telemetry-routes.ts +27 -0
- package/src/runtime/routes/tts-routes.ts +93 -1
- package/src/runtime/routes/types.ts +32 -0
- package/src/runtime/routes/user-routes-cli.ts +243 -0
- package/src/runtime/routes/webhook-routes.ts +165 -0
- package/src/runtime/routes/workspace-routes.test.ts +43 -0
- package/src/runtime/routes/workspace-routes.ts +28 -0
- package/src/runtime/services/conversation-serializer.ts +39 -7
- package/src/runtime/sync/resource-sync-events.ts +117 -0
- package/src/runtime/sync/sync-publisher.test.ts +105 -0
- package/src/runtime/sync/sync-publisher.ts +21 -0
- package/src/schedule/schedule-store.ts +27 -2
- package/src/schedule/scheduler.ts +208 -123
- package/src/security/__tests__/provider-key-env-fallback.test.ts +12 -6
- package/src/security/__tests__/untrusted-content.test.ts +86 -0
- package/src/security/secret-patterns.ts +3 -0
- package/src/security/untrusted-content.ts +93 -8
- package/src/sequence/engine.ts +38 -40
- package/src/skills/catalog-files.ts +1 -1
- package/src/skills/catalog-install.ts +233 -116
- package/src/skills/clawhub.ts +70 -13
- package/src/skills/managed-store.ts +4 -119
- package/src/skills/skillssh-registry.ts +27 -48
- package/src/subagent/manager.ts +28 -15
- package/src/telemetry/types.ts +113 -1
- package/src/telemetry/usage-telemetry-reporter.test.ts +312 -5
- package/src/telemetry/usage-telemetry-reporter.ts +113 -7
- package/src/tools/apps/executors.ts +58 -7
- package/src/tools/ask-question/ask-question-tool.test.ts +509 -0
- package/src/tools/ask-question/ask-question-tool.ts +304 -0
- package/src/tools/browser/__tests__/browser-execution-acquire.test.ts +206 -0
- package/src/tools/browser/browser-execution.ts +29 -14
- package/src/tools/browser/cdp-client/__tests__/factory.test.ts +174 -0
- package/src/tools/browser/cdp-client/cdp-inspect/__tests__/ws-transport.test.ts +16 -13
- package/src/tools/browser/cdp-client/extension-cdp-client.ts +24 -1
- package/src/tools/browser/cdp-client/factory.ts +66 -5
- package/src/tools/browser/runtime-check.ts +77 -0
- package/src/tools/computer-use/definitions.ts +3 -3
- package/src/tools/credentials/vault.ts +1 -1
- package/src/tools/document/document-tool.ts +124 -1
- package/src/tools/filesystem/edit.ts +1 -1
- package/src/tools/filesystem/list.ts +1 -1
- package/src/tools/filesystem/read.ts +1 -1
- package/src/tools/filesystem/write.ts +5 -2
- package/src/tools/host-filesystem/transfer.ts +1 -1
- package/src/tools/host-terminal/host-shell.ts +1 -1
- package/src/tools/memory/register.test.ts +3 -3
- package/src/tools/memory/register.ts +9 -1
- package/src/tools/network/__tests__/web-search.test.ts +156 -0
- package/src/tools/network/web-search.ts +280 -37
- package/src/tools/permission-checker.ts +14 -6
- package/src/tools/registry.ts +17 -7
- package/src/tools/schedule/create.ts +2 -2
- package/src/tools/schema-transforms.ts +7 -2
- package/src/tools/side-effects.ts +1 -0
- package/src/tools/skills/delete-managed.ts +4 -4
- package/src/tools/skills/execute.ts +1 -1
- package/src/tools/skills/scaffold-managed.ts +3 -2
- package/src/tools/subagent/notify-parent.ts +1 -1
- package/src/tools/subagent/spawn.ts +3 -3
- package/src/tools/system/request-permission.ts +2 -2
- package/src/tools/terminal/safe-env.ts +60 -1
- package/src/tools/terminal/shell.ts +44 -0
- package/src/tools/tool-manifest.ts +2 -0
- package/src/tools/types.ts +72 -21
- package/src/tools/ui-surface/definitions.ts +6 -5
- package/src/tts/__tests__/provider-adapters.test.ts +76 -2
- package/src/tts/providers/elevenlabs-provider.ts +75 -1
- package/src/types/onboarding-context.ts +2 -0
- package/src/usage/attribution.ts +3 -2
- package/src/util/errors.ts +17 -0
- package/src/util/platform.ts +10 -0
- package/src/util/pricing.ts +86 -160
- package/src/watcher/__tests__/engine.test.ts +323 -0
- package/src/watcher/constants.ts +7 -0
- package/src/watcher/engine.ts +94 -90
- package/src/workspace/migrations/046-seed-conversation-starters-callsite.ts +6 -9
- package/src/workspace/migrations/054-seed-recall-callsite.ts +10 -1
- package/src/workspace/migrations/057-repair-stale-gemini-model-ids.ts +94 -5
- package/src/workspace/migrations/069-seed-onboarding-threads.ts +8 -2
- package/src/workspace/migrations/072-seed-reply-suggestion-callsite.ts +117 -0
- package/src/workspace/migrations/073-repair-recall-callsite-empty-profile.ts +95 -0
- package/src/workspace/migrations/074-drop-deprecated-secret-detection-keys.ts +117 -0
- package/src/workspace/migrations/075-memory-v2-bm25-b-default-reembed.ts +61 -0
- package/src/workspace/migrations/076-drop-services-inference-mode.ts +62 -0
- package/src/workspace/migrations/077-seed-memory-router-callsite.ts +89 -0
- package/src/workspace/migrations/078-release-notes-tavily-web-search.ts +66 -0
- package/src/workspace/migrations/079-home-feed-notification-only.ts +197 -0
- package/src/workspace/migrations/080-restrict-vercel-api-token-metadata.ts +182 -0
- package/src/workspace/migrations/081-backfill-bash-allowed-tools-for-injection-credentials.ts +160 -0
- package/src/workspace/migrations/082-backfill-managed-profile-labels.ts +154 -0
- package/src/workspace/migrations/083-system-prompt-prefix-to-file.ts +191 -0
- package/src/workspace/migrations/084-remove-legacy-skills-index.ts +276 -0
- package/src/workspace/migrations/085-memory-v2-bm25-b-reembed-disabled-v2-pages.ts +137 -0
- package/src/workspace/migrations/086-revert-stale-gemini-mis-rewrites.ts +198 -0
- package/src/workspace/migrations/registry.ts +30 -0
- package/src/workspace/migrations/runner.ts +46 -5
- package/src/workspace/migrations/types.ts +17 -3
- package/src/workspace/provider-commit-message-generator.ts +3 -2
- package/examples/plugins/echo/bun.lock +0 -25
- package/src/__tests__/context-search-pkb-source.test.ts +0 -498
- package/src/__tests__/context-window-manager.test.ts +0 -2093
- package/src/__tests__/credentials-cli.test.ts +0 -1225
- package/src/__tests__/memory-admin-recall.test.ts +0 -213
- package/src/approvals/__tests__/guardian-feed-event.test.ts +0 -303
- package/src/cli/commands/__tests__/email-download.test.ts +0 -260
- package/src/cli/commands/__tests__/email-list.test.ts +0 -216
- package/src/cli/commands/__tests__/email-register.test.ts +0 -186
- package/src/cli/commands/__tests__/email-send.test.ts +0 -416
- package/src/cli/commands/__tests__/email-status.test.ts +0 -185
- package/src/cli/commands/__tests__/email-unregister.test.ts +0 -168
- package/src/cli/commands/__tests__/routes.test.ts +0 -562
- package/src/cli/commands/__tests__/stt-transcribe.test.ts +0 -454
- package/src/cli/commands/autonomy.ts +0 -365
- package/src/cli/commands/memory.ts +0 -424
- package/src/cli/commands/oauth/__tests__/connect.test.ts +0 -947
- package/src/cli/commands/oauth/__tests__/disconnect.test.ts +0 -686
- package/src/cli/commands/oauth/__tests__/mode.test.ts +0 -632
- package/src/cli/commands/oauth/__tests__/ping.test.ts +0 -631
- package/src/cli/commands/oauth/__tests__/providers-delete.test.ts +0 -573
- package/src/cli/commands/oauth/__tests__/providers-register.test.ts +0 -330
- package/src/cli/commands/oauth/__tests__/providers-update.test.ts +0 -521
- package/src/cli/commands/oauth/__tests__/status.test.ts +0 -551
- package/src/cli/commands/oauth/__tests__/token.test.ts +0 -420
- package/src/cli/lib/daemon-avatar-client.ts +0 -37
- package/src/config/bundled-skills/contacts/tools/contact-upsert.ts +0 -87
- package/src/config/bundled-skills/messaging/tools/__tests__/messaging-feed-events.test.ts +0 -207
- package/src/context/__tests__/compact-prompt.test.ts +0 -63
- package/src/context/prompts/compact.md +0 -26
- package/src/daemon/__tests__/conversation-feed-event.test.ts +0 -304
- package/src/heartbeat/__tests__/heartbeat-feed-event.test.ts +0 -233
- package/src/home/__tests__/assistant-feed-authoring.test.ts +0 -156
- package/src/home/__tests__/emit-feed-event.test.ts +0 -169
- package/src/home/__tests__/feed-population-integration.test.ts +0 -312
- package/src/home/__tests__/feed-scheduler.test.ts +0 -222
- package/src/home/__tests__/phase5-exit-criteria.test.ts +0 -229
- package/src/home/__tests__/platform-gmail-digest.test.ts +0 -222
- package/src/home/__tests__/rollup-producer.test.ts +0 -507
- package/src/home/assistant-feed-authoring.ts +0 -135
- package/src/home/emit-feed-event.ts +0 -169
- package/src/home/feed-scheduler.ts +0 -281
- package/src/home/platform-gmail-digest.ts +0 -163
- package/src/home/rewrite-command-preview.ts +0 -66
- package/src/home/rewrite-feed-title.ts +0 -58
- package/src/home/rollup-producer.ts +0 -426
- package/src/memory/admin.ts +0 -326
- package/src/memory/context-search/sources/pkb.ts +0 -476
- package/src/memory/graph/compaction.ts +0 -299
- package/src/prompts/__tests__/build-cli-reference-section.test.ts +0 -37
- /package/src/cli/{commands → lib}/cache-fs.ts +0 -0
|
@@ -41,6 +41,7 @@ import {
|
|
|
41
41
|
} from "bun:test";
|
|
42
42
|
|
|
43
43
|
import { drizzle } from "drizzle-orm/bun-sqlite";
|
|
44
|
+
import { z } from "zod";
|
|
44
45
|
|
|
45
46
|
import { makeMockLogger } from "../../../__tests__/helpers/mock-logger.js";
|
|
46
47
|
import type { AssistantConfig } from "../../../config/types.js";
|
|
@@ -153,6 +154,11 @@ mock.module("../skill-store.js", () => ({
|
|
|
153
154
|
isSkillSlug: (slug: string) => slug.startsWith("skills/"),
|
|
154
155
|
SKILL_SLUG_PREFIX: "skills/",
|
|
155
156
|
skillSlugFor: (id: string) => `skills/${id}`,
|
|
157
|
+
// PR 4 added `listSkillEntries`; `page-index.ts` (transitively imported
|
|
158
|
+
// via `page-store.ts` and `skill-store.ts`) consumes it at module-init
|
|
159
|
+
// time. Tests stage skill content via `skillState.entries`; expose them
|
|
160
|
+
// here so the page-index loader sees a consistent view.
|
|
161
|
+
listSkillEntries: () => Array.from(skillState.entries.values()),
|
|
156
162
|
}));
|
|
157
163
|
|
|
158
164
|
// ---------------------------------------------------------------------------
|
|
@@ -179,6 +185,93 @@ mock.module("../../memory-v2-activation-log-store.js", () => ({
|
|
|
179
185
|
},
|
|
180
186
|
}));
|
|
181
187
|
|
|
188
|
+
// ---------------------------------------------------------------------------
|
|
189
|
+
// Page-store mock — pass-through with optional per-slug failure injection
|
|
190
|
+
// ---------------------------------------------------------------------------
|
|
191
|
+
//
|
|
192
|
+
// Most tests want the real `readPage` (it walks the temp workspace seeded in
|
|
193
|
+
// `beforeAll`). The error-isolation tests stage a slug whose `readPage` call
|
|
194
|
+
// must throw — typically a Zod validation error mimicking the real-world
|
|
195
|
+
// "unrecognized frontmatter key" failure that motivated this work. Tests
|
|
196
|
+
// stage entries via `pageStoreState.failingSlugs` and reset in `resetState`.
|
|
197
|
+
//
|
|
198
|
+
// Bun's `mock.module` mutates the module's exports object in place, so
|
|
199
|
+
// `realPageStore.readPage` AFTER the mock applies would resolve to the mock
|
|
200
|
+
// itself — calling it would recurse. We capture the original function value
|
|
201
|
+
// (not a property lookup) before installing the mock so the pass-through
|
|
202
|
+
// path has a real reference to the underlying implementation.
|
|
203
|
+
|
|
204
|
+
const realPageStoreModule = await import("../page-store.js");
|
|
205
|
+
const realReadPage = realPageStoreModule.readPage;
|
|
206
|
+
const pageStoreState = {
|
|
207
|
+
failingSlugs: new Map<string, Error>(),
|
|
208
|
+
};
|
|
209
|
+
mock.module("../page-store.js", () => ({
|
|
210
|
+
...realPageStoreModule,
|
|
211
|
+
readPage: async (workspaceDir: string, slug: string) => {
|
|
212
|
+
const err = pageStoreState.failingSlugs.get(slug);
|
|
213
|
+
if (err) throw err;
|
|
214
|
+
return realReadPage(workspaceDir, slug);
|
|
215
|
+
},
|
|
216
|
+
}));
|
|
217
|
+
|
|
218
|
+
// ---------------------------------------------------------------------------
|
|
219
|
+
// Router mock — programmable per-call result
|
|
220
|
+
// ---------------------------------------------------------------------------
|
|
221
|
+
//
|
|
222
|
+
// PR 10 wires `runRouter` into `injectMemoryV2Block` behind the
|
|
223
|
+
// `memory.v2.router.enabled` flag. The activation-mode tests above never
|
|
224
|
+
// flip the flag, so the default mock returns a no-op result and the router
|
|
225
|
+
// branch is never exercised. Router-mode tests set `routerState.nextResult`
|
|
226
|
+
// to stage a deterministic outcome before each call.
|
|
227
|
+
|
|
228
|
+
interface RouterResultStub {
|
|
229
|
+
selectedSlugs: string[];
|
|
230
|
+
failureReason: string | null;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
const routerState = {
|
|
234
|
+
nextResult: null as RouterResultStub | null,
|
|
235
|
+
callCount: 0,
|
|
236
|
+
};
|
|
237
|
+
|
|
238
|
+
mock.module("../router.js", () => ({
|
|
239
|
+
runRouter: async () => {
|
|
240
|
+
routerState.callCount++;
|
|
241
|
+
return (
|
|
242
|
+
routerState.nextResult ?? {
|
|
243
|
+
selectedSlugs: [],
|
|
244
|
+
failureReason: null,
|
|
245
|
+
}
|
|
246
|
+
);
|
|
247
|
+
},
|
|
248
|
+
}));
|
|
249
|
+
|
|
250
|
+
// ---------------------------------------------------------------------------
|
|
251
|
+
// Activation-store mock — pass-through with optional `save` failure injection
|
|
252
|
+
// ---------------------------------------------------------------------------
|
|
253
|
+
//
|
|
254
|
+
// One regression test forces `save` to throw to exercise the
|
|
255
|
+
// `injectMemoryV2Block` outer try/finally — telemetry must still be flushed
|
|
256
|
+
// (with `mode: "errored"`) and the error must propagate. Default behavior
|
|
257
|
+
// delegates to the real activation-store so the rest of the suite stays
|
|
258
|
+
// untouched. Same pre-mock function-capture trick as `readPage` above.
|
|
259
|
+
|
|
260
|
+
const realActivationStoreModule = await import("../activation-store.js");
|
|
261
|
+
const realSave = realActivationStoreModule.save;
|
|
262
|
+
const activationStoreState = {
|
|
263
|
+
saveShouldThrow: false,
|
|
264
|
+
};
|
|
265
|
+
mock.module("../activation-store.js", () => ({
|
|
266
|
+
...realActivationStoreModule,
|
|
267
|
+
save: async (...args: Parameters<typeof realSave>) => {
|
|
268
|
+
if (activationStoreState.saveShouldThrow) {
|
|
269
|
+
throw new Error("simulated activation-store save failure");
|
|
270
|
+
}
|
|
271
|
+
return realSave(...args);
|
|
272
|
+
},
|
|
273
|
+
}));
|
|
274
|
+
|
|
182
275
|
// ---------------------------------------------------------------------------
|
|
183
276
|
// Workspace + DB setup
|
|
184
277
|
// ---------------------------------------------------------------------------
|
|
@@ -299,8 +392,10 @@ function makeConfig(
|
|
|
299
392
|
epsilon: number;
|
|
300
393
|
dense_weight: number;
|
|
301
394
|
sparse_weight: number;
|
|
395
|
+
router: { enabled: boolean; max_page_ids?: number };
|
|
302
396
|
}> = {},
|
|
303
397
|
): AssistantConfig {
|
|
398
|
+
const { router, ...rest } = overrides;
|
|
304
399
|
return {
|
|
305
400
|
memory: {
|
|
306
401
|
v2: {
|
|
@@ -314,7 +409,8 @@ function makeConfig(
|
|
|
314
409
|
epsilon: 0.01,
|
|
315
410
|
dense_weight: 1.0,
|
|
316
411
|
sparse_weight: 0.0,
|
|
317
|
-
...
|
|
412
|
+
router: { enabled: false, max_page_ids: 25, ...(router ?? {}) },
|
|
413
|
+
...rest,
|
|
318
414
|
},
|
|
319
415
|
},
|
|
320
416
|
} as unknown as AssistantConfig;
|
|
@@ -390,6 +486,10 @@ function resetState(): void {
|
|
|
390
486
|
skillState.entries.clear();
|
|
391
487
|
telemetryState.recordCalls.length = 0;
|
|
392
488
|
telemetryState.recordShouldThrow = false;
|
|
489
|
+
pageStoreState.failingSlugs.clear();
|
|
490
|
+
activationStoreState.saveShouldThrow = false;
|
|
491
|
+
routerState.nextResult = null;
|
|
492
|
+
routerState.callCount = 0;
|
|
393
493
|
// The qdrant module caches its client; the cached client may be a
|
|
394
494
|
// MockQdrantClient instance from a sibling test file. Reset to force a
|
|
395
495
|
// fresh `new QdrantClient()` against this file's mock.
|
|
@@ -712,11 +812,13 @@ describe("injectMemoryV2Block", () => {
|
|
|
712
812
|
});
|
|
713
813
|
|
|
714
814
|
test("persists sparse state — only slugs above epsilon survive", async () => {
|
|
715
|
-
// Carol scores high; alice
|
|
716
|
-
//
|
|
815
|
+
// Carol scores high; alice essentially zero. After saving, only carol
|
|
816
|
+
// should appear in the persisted state map. denseScore is the raw
|
|
817
|
+
// Qdrant cosine in [-1, 1]; alice uses -1 so the post `(x+1)/2`
|
|
818
|
+
// unit-mapping pins her fused score to 0 — below epsilon.
|
|
717
819
|
stageTurn([
|
|
718
820
|
{ slug: "carol-jazz", denseScore: 1.0 },
|
|
719
|
-
{ slug: "alice-vscode", denseScore:
|
|
821
|
+
{ slug: "alice-vscode", denseScore: -1.0 },
|
|
720
822
|
]);
|
|
721
823
|
await injectMemoryV2Block({
|
|
722
824
|
database: db,
|
|
@@ -777,17 +879,17 @@ describe("injectMemoryV2Block", () => {
|
|
|
777
879
|
// Unified pool — skills as `skills/<id>` slugs
|
|
778
880
|
// ---------------------------------------------------------------------------
|
|
779
881
|
|
|
780
|
-
test("renders a
|
|
882
|
+
test("renders a retrieved skills/<id> slug under Skills You Can Use", async () => {
|
|
781
883
|
// No concept-page candidates this turn — the only ANN hit is a skill
|
|
782
884
|
// slug. The render path branches on `skills/` prefix: it pulls the
|
|
783
885
|
// entry from the skill-store cache (mocked) and emits the bullet under
|
|
784
886
|
// the `### Skills You Can Use` subsection.
|
|
785
|
-
stageTurn([{ slug: "skills/
|
|
887
|
+
stageTurn([{ slug: "skills/retrieved-skill", denseScore: 0.9 }]);
|
|
786
888
|
stageSkills([
|
|
787
889
|
{
|
|
788
|
-
id: "
|
|
890
|
+
id: "retrieved-skill",
|
|
789
891
|
content:
|
|
790
|
-
'The "
|
|
892
|
+
'The "Retrieved Skill" skill (retrieved-skill) is available. Helps with retrieved skills.',
|
|
791
893
|
},
|
|
792
894
|
]);
|
|
793
895
|
|
|
@@ -802,16 +904,18 @@ describe("injectMemoryV2Block", () => {
|
|
|
802
904
|
config: makeConfig(),
|
|
803
905
|
});
|
|
804
906
|
|
|
805
|
-
expect(result.toInject).toEqual(["skills/
|
|
907
|
+
expect(result.toInject).toEqual(["skills/retrieved-skill"]);
|
|
806
908
|
expect(result.block).not.toBeNull();
|
|
807
909
|
expect(result.block).not.toContain("<memory>");
|
|
808
910
|
expect(result.block).not.toContain("</memory>");
|
|
809
911
|
expect(result.block).not.toContain("## What I Remember Right Now");
|
|
810
912
|
expect(result.block).not.toContain("# memory/concepts/alice-vscode.md");
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
'- The "
|
|
913
|
+
const headerIdx = result.block!.indexOf("### Skills You Can Use");
|
|
914
|
+
const skillIdx = result.block!.indexOf(
|
|
915
|
+
'- The "Retrieved Skill" skill (retrieved-skill) is available. Helps with retrieved skills. → use skill_load to activate',
|
|
814
916
|
);
|
|
917
|
+
expect(headerIdx).toBeGreaterThan(-1);
|
|
918
|
+
expect(skillIdx).toBeGreaterThan(headerIdx);
|
|
815
919
|
});
|
|
816
920
|
|
|
817
921
|
test("renders concept-page sections before the skills subsection in mixed blocks", async () => {
|
|
@@ -907,9 +1011,10 @@ describe("injectMemoryV2Block", () => {
|
|
|
907
1011
|
|
|
908
1012
|
test("skill slugs whose entry is missing from the cache are dropped silently", async () => {
|
|
909
1013
|
// The skill ranks into top-K but the in-process cache no longer knows
|
|
910
|
-
// its content (skill uninstalled mid-run
|
|
911
|
-
//
|
|
912
|
-
//
|
|
1014
|
+
// its content (skill uninstalled mid-run, or a startup race where the
|
|
1015
|
+
// Qdrant row landed before the skill cache was seeded). The render path
|
|
1016
|
+
// drops it without surfacing it as a `missingSlugs` page-missing event —
|
|
1017
|
+
// that status is reserved for on-disk concept pages, not catalog-derived
|
|
913
1018
|
// skill entries.
|
|
914
1019
|
stageTurn([{ slug: "skills/missing-skill", denseScore: 0.9 }]);
|
|
915
1020
|
// No `stageSkills` call — cache stays empty.
|
|
@@ -925,10 +1030,16 @@ describe("injectMemoryV2Block", () => {
|
|
|
925
1030
|
config: makeConfig(),
|
|
926
1031
|
});
|
|
927
1032
|
|
|
928
|
-
//
|
|
929
|
-
//
|
|
930
|
-
|
|
1033
|
+
// The skill is excluded from `toInject` (and `everInjected`) so future
|
|
1034
|
+
// per-turn runs re-attempt the attach once the cache is populated.
|
|
1035
|
+
// `block` collapses to null because the only candidate was a cache miss.
|
|
1036
|
+
expect(result.toInject).toEqual([]);
|
|
931
1037
|
expect(result.block).toBeNull();
|
|
1038
|
+
|
|
1039
|
+
// Persisted `everInjected` must not record the missing skill — that
|
|
1040
|
+
// would block retry on a later turn until compaction-driven eviction.
|
|
1041
|
+
const persisted = await hydrate(db, "conv-1");
|
|
1042
|
+
expect(persisted!.everInjected).toEqual([]);
|
|
932
1043
|
});
|
|
933
1044
|
|
|
934
1045
|
test("returns null when both concept pages and skills are empty", async () => {
|
|
@@ -1287,4 +1398,601 @@ describe("injectMemoryV2Block", () => {
|
|
|
1287
1398
|
{ slug: "alice-vscode", turn: 1 },
|
|
1288
1399
|
]);
|
|
1289
1400
|
});
|
|
1401
|
+
|
|
1402
|
+
// ---------------------------------------------------------------------------
|
|
1403
|
+
// Per-page error isolation + on-throw telemetry
|
|
1404
|
+
// ---------------------------------------------------------------------------
|
|
1405
|
+
|
|
1406
|
+
test("one slug's page-read failing isolates the error — other slugs still render and the corrupt slug records `status: corrupt`", async () => {
|
|
1407
|
+
// Two slugs rank into top-K together. Carol's page reads cleanly; alice's
|
|
1408
|
+
// `readPage` throws a ZodError mimicking the real "unrecognized
|
|
1409
|
+
// frontmatter key" failure that motivated this work. Before the fix, the
|
|
1410
|
+
// bare `Promise.all` rejected and the entire turn lost its block AND its
|
|
1411
|
+
// activation log row. With per-page isolation, carol still renders and
|
|
1412
|
+
// the activation log row marks alice as `corrupt` (telemetry remains
|
|
1413
|
+
// observable for triage).
|
|
1414
|
+
const zodErr = z.object({ x: z.string() }).safeParse({ x: 1 }).error!;
|
|
1415
|
+
pageStoreState.failingSlugs.set("alice-vscode", zodErr);
|
|
1416
|
+
stageTurn([
|
|
1417
|
+
{ slug: "alice-vscode", denseScore: 0.95 },
|
|
1418
|
+
{ slug: "carol-jazz", denseScore: 0.9 },
|
|
1419
|
+
]);
|
|
1420
|
+
|
|
1421
|
+
const result = await injectMemoryV2Block({
|
|
1422
|
+
database: db,
|
|
1423
|
+
conversationId: "conv-1",
|
|
1424
|
+
currentTurn: 1,
|
|
1425
|
+
userMessage: "music and editors",
|
|
1426
|
+
assistantMessage: "",
|
|
1427
|
+
nowText: "Now",
|
|
1428
|
+
messageId: "msg-1",
|
|
1429
|
+
config: makeConfig(),
|
|
1430
|
+
});
|
|
1431
|
+
|
|
1432
|
+
// (a) Block is non-null and contains content from the OTHER slug; alice
|
|
1433
|
+
// is dropped from the rendered block but does not poison the batch.
|
|
1434
|
+
expect(result.block).not.toBeNull();
|
|
1435
|
+
expect(result.block).toContain("# memory/concepts/carol-jazz.md");
|
|
1436
|
+
expect(result.block).not.toContain("# memory/concepts/alice-vscode.md");
|
|
1437
|
+
|
|
1438
|
+
// (b) Activation log row exists with carol `injected` and alice
|
|
1439
|
+
// `corrupt`. Status `corrupt` is reserved for read-time throws and is
|
|
1440
|
+
// distinct from `page_missing` (which is null-return / file vanished).
|
|
1441
|
+
expect(telemetryState.recordCalls.length).toBe(1);
|
|
1442
|
+
const row = telemetryState.recordCalls[0] as {
|
|
1443
|
+
mode: string;
|
|
1444
|
+
concepts: Array<{ slug: string; status: string }>;
|
|
1445
|
+
};
|
|
1446
|
+
expect(row.mode).toBe("per-turn");
|
|
1447
|
+
const byslug = new Map(row.concepts.map((c) => [c.slug, c]));
|
|
1448
|
+
expect(byslug.get("alice-vscode")!.status).toBe("corrupt");
|
|
1449
|
+
expect(byslug.get("carol-jazz")!.status).toBe("injected");
|
|
1450
|
+
|
|
1451
|
+
// (c) Both slugs land in `toInject` and `everInjected` — same handling
|
|
1452
|
+
// as `page_missing` (see the phantom-slug test): the slug was attempted
|
|
1453
|
+
// this turn, telemetry records the outcome, and we don't keep re-trying
|
|
1454
|
+
// a stale Qdrant / edge-index entry on every subsequent turn.
|
|
1455
|
+
expect(new Set(result.toInject)).toEqual(
|
|
1456
|
+
new Set(["alice-vscode", "carol-jazz"]),
|
|
1457
|
+
);
|
|
1458
|
+
const persisted = await hydrate(db, "conv-1");
|
|
1459
|
+
const everInjectedSlugs = persisted!.everInjected.map((e) => e.slug);
|
|
1460
|
+
expect(new Set(everInjectedSlugs)).toEqual(
|
|
1461
|
+
new Set(["alice-vscode", "carol-jazz"]),
|
|
1462
|
+
);
|
|
1463
|
+
});
|
|
1464
|
+
|
|
1465
|
+
test("a throw before renderInjectionBlock still flushes telemetry as `mode: errored` and re-throws", async () => {
|
|
1466
|
+
// The activation-state save throws — the most realistic non-render
|
|
1467
|
+
// failure mode (transient SQLite write error mid-injection). The
|
|
1468
|
+
// `injectMemoryV2Block` outer try/finally must (a) flush an activation
|
|
1469
|
+
// log row tagged `mode: "errored"` so silent failures stay observable
|
|
1470
|
+
// in the database, and (b) re-throw so callers (e.g. `prepareMemory`'s
|
|
1471
|
+
// outer catch) see the original error and can degrade gracefully.
|
|
1472
|
+
activationStoreState.saveShouldThrow = true;
|
|
1473
|
+
stageTurn([{ slug: "alice-vscode", denseScore: 0.9 }]);
|
|
1474
|
+
|
|
1475
|
+
let threw: unknown = undefined;
|
|
1476
|
+
try {
|
|
1477
|
+
await injectMemoryV2Block({
|
|
1478
|
+
database: db,
|
|
1479
|
+
conversationId: "conv-1",
|
|
1480
|
+
currentTurn: 1,
|
|
1481
|
+
userMessage: "Alice's editor",
|
|
1482
|
+
assistantMessage: "",
|
|
1483
|
+
nowText: "Now",
|
|
1484
|
+
messageId: "msg-1",
|
|
1485
|
+
config: makeConfig(),
|
|
1486
|
+
});
|
|
1487
|
+
} catch (err) {
|
|
1488
|
+
threw = err;
|
|
1489
|
+
}
|
|
1490
|
+
|
|
1491
|
+
// The original error propagates to the caller.
|
|
1492
|
+
expect(threw).toBeInstanceOf(Error);
|
|
1493
|
+
expect((threw as Error).message).toContain(
|
|
1494
|
+
"simulated activation-store save failure",
|
|
1495
|
+
);
|
|
1496
|
+
|
|
1497
|
+
// A telemetry row was still written, tagged `errored`. `concepts` is
|
|
1498
|
+
// empty because the throw fired before the row-builder ran — that's
|
|
1499
|
+
// expected and documented as part of the contract.
|
|
1500
|
+
expect(telemetryState.recordCalls.length).toBe(1);
|
|
1501
|
+
const row = telemetryState.recordCalls[0] as {
|
|
1502
|
+
mode: string;
|
|
1503
|
+
conversationId: string;
|
|
1504
|
+
turn: number;
|
|
1505
|
+
concepts: unknown[];
|
|
1506
|
+
};
|
|
1507
|
+
expect(row.mode).toBe("errored");
|
|
1508
|
+
expect(row.conversationId).toBe("conv-1");
|
|
1509
|
+
expect(row.turn).toBe(1);
|
|
1510
|
+
expect(row.concepts).toEqual([]);
|
|
1511
|
+
});
|
|
1512
|
+
|
|
1513
|
+
test("activation pipeline routes through `finalizeInjection` — telemetry shape and config snapshot match the contract", async () => {
|
|
1514
|
+
// Pure-refactor regression check: `injectMemoryV2Block` now delegates the
|
|
1515
|
+
// tail (state save + render + telemetry finalization + log write) to a
|
|
1516
|
+
// private `finalizeInjection` helper. This test asserts the helper is
|
|
1517
|
+
// exercised by verifying `recordMemoryV2ActivationLog` is called with the
|
|
1518
|
+
// same arg shape as before — same conversationId/turn/mode, same config
|
|
1519
|
+
// snapshot, and a fully populated concept row whose status was finalized
|
|
1520
|
+
// to `"injected"` on the freshly-attached slug.
|
|
1521
|
+
stageTurn([{ slug: "alice-vscode", denseScore: 0.9 }]);
|
|
1522
|
+
|
|
1523
|
+
const result = await injectMemoryV2Block({
|
|
1524
|
+
database: db,
|
|
1525
|
+
conversationId: "conv-finalize",
|
|
1526
|
+
currentTurn: 7,
|
|
1527
|
+
userMessage: "Alice's editor",
|
|
1528
|
+
assistantMessage: "",
|
|
1529
|
+
nowText: "Now",
|
|
1530
|
+
messageId: "msg-finalize",
|
|
1531
|
+
config: makeConfig(),
|
|
1532
|
+
});
|
|
1533
|
+
|
|
1534
|
+
// The helper rendered + persisted just like the original tail did.
|
|
1535
|
+
expect(result.block).toContain("alice-vscode");
|
|
1536
|
+
expect(result.toInject).toEqual(["alice-vscode"]);
|
|
1537
|
+
|
|
1538
|
+
expect(telemetryState.recordCalls.length).toBe(1);
|
|
1539
|
+
const row = telemetryState.recordCalls[0] as {
|
|
1540
|
+
conversationId: string;
|
|
1541
|
+
turn: number;
|
|
1542
|
+
mode: string;
|
|
1543
|
+
concepts: Array<{
|
|
1544
|
+
slug: string;
|
|
1545
|
+
status: string;
|
|
1546
|
+
finalActivation: number;
|
|
1547
|
+
}>;
|
|
1548
|
+
config: {
|
|
1549
|
+
d: number;
|
|
1550
|
+
c_user: number;
|
|
1551
|
+
c_assistant: number;
|
|
1552
|
+
c_now: number;
|
|
1553
|
+
k: number;
|
|
1554
|
+
hops: number;
|
|
1555
|
+
top_k: number;
|
|
1556
|
+
epsilon: number;
|
|
1557
|
+
};
|
|
1558
|
+
};
|
|
1559
|
+
expect(row.conversationId).toBe("conv-finalize");
|
|
1560
|
+
expect(row.turn).toBe(7);
|
|
1561
|
+
expect(row.mode).toBe("per-turn");
|
|
1562
|
+
// Config snapshot must include all eight tunables — proves the helper is
|
|
1563
|
+
// pulling from `config.memory.v2` rather than synthesizing a partial.
|
|
1564
|
+
expect(Object.keys(row.config).sort()).toEqual(
|
|
1565
|
+
[
|
|
1566
|
+
"c_assistant",
|
|
1567
|
+
"c_now",
|
|
1568
|
+
"c_user",
|
|
1569
|
+
"d",
|
|
1570
|
+
"epsilon",
|
|
1571
|
+
"hops",
|
|
1572
|
+
"k",
|
|
1573
|
+
"top_k",
|
|
1574
|
+
].sort(),
|
|
1575
|
+
);
|
|
1576
|
+
// Status finalization ran inside the helper — alice was selected and
|
|
1577
|
+
// rendered, so its row reads `injected`.
|
|
1578
|
+
const aliceRow = row.concepts.find((c) => c.slug === "alice-vscode");
|
|
1579
|
+
expect(aliceRow?.status).toBe("injected");
|
|
1580
|
+
});
|
|
1581
|
+
|
|
1582
|
+
// ---------------------------------------------------------------------------
|
|
1583
|
+
// Router mode (flag-gated)
|
|
1584
|
+
// ---------------------------------------------------------------------------
|
|
1585
|
+
|
|
1586
|
+
describe("router mode", () => {
|
|
1587
|
+
test("flag-on: router-selected slugs render and append to everInjected", async () => {
|
|
1588
|
+
// Router picks alice. The activation pipeline never runs — we don't
|
|
1589
|
+
// stage any qdrant responses here, and that's intentional. The
|
|
1590
|
+
// candidate set comes straight from the router's `selectedSlugs`.
|
|
1591
|
+
routerState.nextResult = {
|
|
1592
|
+
selectedSlugs: ["alice-vscode"],
|
|
1593
|
+
failureReason: null,
|
|
1594
|
+
};
|
|
1595
|
+
|
|
1596
|
+
const result = await injectMemoryV2Block({
|
|
1597
|
+
database: db,
|
|
1598
|
+
conversationId: "conv-router-1",
|
|
1599
|
+
currentTurn: 1,
|
|
1600
|
+
userMessage: "Tell me about Alice",
|
|
1601
|
+
assistantMessage: "",
|
|
1602
|
+
nowText: "Now",
|
|
1603
|
+
messageId: "msg-1",
|
|
1604
|
+
config: makeConfig({ router: { enabled: true } }),
|
|
1605
|
+
});
|
|
1606
|
+
|
|
1607
|
+
expect(routerState.callCount).toBe(1);
|
|
1608
|
+
expect(result.toInject).toEqual(["alice-vscode"]);
|
|
1609
|
+
expect(result.block).not.toBeNull();
|
|
1610
|
+
expect(result.block).toContain("# memory/concepts/alice-vscode.md");
|
|
1611
|
+
|
|
1612
|
+
const persisted = await hydrate(db, "conv-router-1");
|
|
1613
|
+
expect(persisted!.everInjected).toEqual([
|
|
1614
|
+
{ slug: "alice-vscode", turn: 1 },
|
|
1615
|
+
]);
|
|
1616
|
+
// Router mode persists an empty sparse activation map — the router
|
|
1617
|
+
// does not compute spreading-activation scores.
|
|
1618
|
+
expect(persisted!.state).toEqual({});
|
|
1619
|
+
|
|
1620
|
+
// Telemetry: success rows get `mode: "router"` and `source: "router"`,
|
|
1621
|
+
// with all activation fields zeroed.
|
|
1622
|
+
expect(telemetryState.recordCalls.length).toBe(1);
|
|
1623
|
+
const row = telemetryState.recordCalls[0] as {
|
|
1624
|
+
mode: string;
|
|
1625
|
+
concepts: Array<{
|
|
1626
|
+
slug: string;
|
|
1627
|
+
source: string;
|
|
1628
|
+
status: string;
|
|
1629
|
+
finalActivation: number;
|
|
1630
|
+
ownActivation: number;
|
|
1631
|
+
}>;
|
|
1632
|
+
};
|
|
1633
|
+
expect(row.mode).toBe("router");
|
|
1634
|
+
const aliceRow = row.concepts.find((c) => c.slug === "alice-vscode");
|
|
1635
|
+
expect(aliceRow).toBeDefined();
|
|
1636
|
+
expect(aliceRow!.source).toBe("router");
|
|
1637
|
+
expect(aliceRow!.status).toBe("injected");
|
|
1638
|
+
expect(aliceRow!.finalActivation).toBe(0);
|
|
1639
|
+
expect(aliceRow!.ownActivation).toBe(0);
|
|
1640
|
+
});
|
|
1641
|
+
|
|
1642
|
+
test("flag-on: router failure logs warn, writes mode:`errored` telemetry, returns null block", async () => {
|
|
1643
|
+
routerState.nextResult = {
|
|
1644
|
+
selectedSlugs: [],
|
|
1645
|
+
failureReason: "api_error",
|
|
1646
|
+
};
|
|
1647
|
+
|
|
1648
|
+
const result = await injectMemoryV2Block({
|
|
1649
|
+
database: db,
|
|
1650
|
+
conversationId: "conv-router-fail",
|
|
1651
|
+
currentTurn: 3,
|
|
1652
|
+
userMessage: "anything",
|
|
1653
|
+
assistantMessage: "ok",
|
|
1654
|
+
nowText: "Now",
|
|
1655
|
+
messageId: "msg-fail",
|
|
1656
|
+
config: makeConfig({ router: { enabled: true } }),
|
|
1657
|
+
});
|
|
1658
|
+
|
|
1659
|
+
expect(result.block).toBeNull();
|
|
1660
|
+
expect(result.toInject).toEqual([]);
|
|
1661
|
+
|
|
1662
|
+
// Stub state still advanced.
|
|
1663
|
+
const persisted = await hydrate(db, "conv-router-fail");
|
|
1664
|
+
expect(persisted).not.toBeNull();
|
|
1665
|
+
expect(persisted!.currentTurn).toBe(3);
|
|
1666
|
+
expect(persisted!.messageId).toBe("msg-fail");
|
|
1667
|
+
expect(persisted!.state).toEqual({});
|
|
1668
|
+
expect(persisted!.everInjected).toEqual([]);
|
|
1669
|
+
|
|
1670
|
+
// Single telemetry row with `mode: "errored"` (not `"router"`).
|
|
1671
|
+
expect(telemetryState.recordCalls.length).toBe(1);
|
|
1672
|
+
const row = telemetryState.recordCalls[0] as {
|
|
1673
|
+
mode: string;
|
|
1674
|
+
conversationId: string;
|
|
1675
|
+
turn: number;
|
|
1676
|
+
concepts: unknown[];
|
|
1677
|
+
};
|
|
1678
|
+
expect(row.mode).toBe("errored");
|
|
1679
|
+
expect(row.conversationId).toBe("conv-router-fail");
|
|
1680
|
+
expect(row.turn).toBe(3);
|
|
1681
|
+
expect(row.concepts).toEqual([]);
|
|
1682
|
+
});
|
|
1683
|
+
|
|
1684
|
+
test("flag-on: router-failure path swallows a save() error and returns block:null instead of throwing", async () => {
|
|
1685
|
+
// PR 30176 refactored router-failure handling to delegate to
|
|
1686
|
+
// `finalizeInjection`. That regressed the prior inline log-and-continue
|
|
1687
|
+
// semantics on the router-failure path: a transient SQLite write
|
|
1688
|
+
// throwing during the stub-state save now aborted the whole turn
|
|
1689
|
+
// because `finalizeInjection`'s try/catch re-threw caughtErr at the end.
|
|
1690
|
+
//
|
|
1691
|
+
// This test stages exactly that scenario — router returns
|
|
1692
|
+
// `failureReason: api_error` AND `save()` throws — and asserts the
|
|
1693
|
+
// turn completes with `{ block: null, toInject: [] }` rather than
|
|
1694
|
+
// propagating the SQLite error to `prepareMemory`.
|
|
1695
|
+
routerState.nextResult = {
|
|
1696
|
+
selectedSlugs: [],
|
|
1697
|
+
failureReason: "api_error",
|
|
1698
|
+
};
|
|
1699
|
+
activationStoreState.saveShouldThrow = true;
|
|
1700
|
+
|
|
1701
|
+
let threw: unknown = undefined;
|
|
1702
|
+
let result: Awaited<ReturnType<typeof injectMemoryV2Block>> | undefined;
|
|
1703
|
+
try {
|
|
1704
|
+
result = await injectMemoryV2Block({
|
|
1705
|
+
database: db,
|
|
1706
|
+
conversationId: "conv-router-fail-save-throws",
|
|
1707
|
+
currentTurn: 5,
|
|
1708
|
+
userMessage: "anything",
|
|
1709
|
+
assistantMessage: "ok",
|
|
1710
|
+
nowText: "Now",
|
|
1711
|
+
messageId: "msg-fail-save",
|
|
1712
|
+
config: makeConfig({ router: { enabled: true } }),
|
|
1713
|
+
});
|
|
1714
|
+
} catch (err) {
|
|
1715
|
+
threw = err;
|
|
1716
|
+
}
|
|
1717
|
+
|
|
1718
|
+
expect(threw).toBeUndefined();
|
|
1719
|
+
expect(result).toBeDefined();
|
|
1720
|
+
expect(result!.block).toBeNull();
|
|
1721
|
+
expect(result!.toInject).toEqual([]);
|
|
1722
|
+
|
|
1723
|
+
// Telemetry still flushes with `mode: "errored"` so the failure stays
|
|
1724
|
+
// observable — the same row the inline pre-refactor path emitted.
|
|
1725
|
+
expect(telemetryState.recordCalls.length).toBe(1);
|
|
1726
|
+
const row = telemetryState.recordCalls[0] as {
|
|
1727
|
+
mode: string;
|
|
1728
|
+
concepts: unknown[];
|
|
1729
|
+
};
|
|
1730
|
+
expect(row.mode).toBe("errored");
|
|
1731
|
+
expect(row.concepts).toEqual([]);
|
|
1732
|
+
});
|
|
1733
|
+
|
|
1734
|
+
test("flag-on: router abstention (empty selectedSlugs, no failure) writes mode:`router` row with no injected pages", async () => {
|
|
1735
|
+
routerState.nextResult = {
|
|
1736
|
+
selectedSlugs: [],
|
|
1737
|
+
failureReason: null,
|
|
1738
|
+
};
|
|
1739
|
+
|
|
1740
|
+
const result = await injectMemoryV2Block({
|
|
1741
|
+
database: db,
|
|
1742
|
+
conversationId: "conv-router-abstain",
|
|
1743
|
+
currentTurn: 1,
|
|
1744
|
+
userMessage: "small talk",
|
|
1745
|
+
assistantMessage: "",
|
|
1746
|
+
nowText: "Now",
|
|
1747
|
+
messageId: "msg-abstain",
|
|
1748
|
+
config: makeConfig({ router: { enabled: true } }),
|
|
1749
|
+
});
|
|
1750
|
+
|
|
1751
|
+
expect(result.block).toBeNull();
|
|
1752
|
+
expect(result.toInject).toEqual([]);
|
|
1753
|
+
|
|
1754
|
+
// No prior everInjected to dedup against, so toInject is empty and
|
|
1755
|
+
// nothing renders. State still advanced.
|
|
1756
|
+
const persisted = await hydrate(db, "conv-router-abstain");
|
|
1757
|
+
expect(persisted!.everInjected).toEqual([]);
|
|
1758
|
+
expect(persisted!.currentTurn).toBe(1);
|
|
1759
|
+
|
|
1760
|
+
// Telemetry: `mode: "router"` row with zero injected pages.
|
|
1761
|
+
expect(telemetryState.recordCalls.length).toBe(1);
|
|
1762
|
+
const row = telemetryState.recordCalls[0] as {
|
|
1763
|
+
mode: string;
|
|
1764
|
+
concepts: Array<{ slug: string; status: string }>;
|
|
1765
|
+
};
|
|
1766
|
+
expect(row.mode).toBe("router");
|
|
1767
|
+
const injectedCount = row.concepts.filter(
|
|
1768
|
+
(c) => c.status === "injected",
|
|
1769
|
+
).length;
|
|
1770
|
+
expect(injectedCount).toBe(0);
|
|
1771
|
+
});
|
|
1772
|
+
|
|
1773
|
+
test("flag-on: router-selected slug whose page is missing on disk records `page_missing` and is NOT added to everInjected", async () => {
|
|
1774
|
+
routerState.nextResult = {
|
|
1775
|
+
selectedSlugs: ["phantom-router-slug"],
|
|
1776
|
+
failureReason: null,
|
|
1777
|
+
};
|
|
1778
|
+
|
|
1779
|
+
const result = await injectMemoryV2Block({
|
|
1780
|
+
database: db,
|
|
1781
|
+
conversationId: "conv-router-missing",
|
|
1782
|
+
currentTurn: 1,
|
|
1783
|
+
userMessage: "phantom",
|
|
1784
|
+
assistantMessage: "",
|
|
1785
|
+
nowText: "Now",
|
|
1786
|
+
messageId: "msg-missing",
|
|
1787
|
+
config: makeConfig({ router: { enabled: true } }),
|
|
1788
|
+
});
|
|
1789
|
+
|
|
1790
|
+
// No backing page → block collapses to null.
|
|
1791
|
+
expect(result.block).toBeNull();
|
|
1792
|
+
// toInject mirrors `newlyInjected` from `finalizeInjection` — the
|
|
1793
|
+
// missing slug still flowed through `slugsToRender` so it's recorded
|
|
1794
|
+
// here (matching the activation-mode phantom-slug contract).
|
|
1795
|
+
expect(result.toInject).toEqual(["phantom-router-slug"]);
|
|
1796
|
+
|
|
1797
|
+
// Activation-mode parity: the phantom slug DOES land in everInjected
|
|
1798
|
+
// so we don't infinite-retry it. (This matches the behavior the
|
|
1799
|
+
// existing `returns null block when toInject slugs all reference
|
|
1800
|
+
// missing pages` test asserts for activation mode.)
|
|
1801
|
+
const persisted = await hydrate(db, "conv-router-missing");
|
|
1802
|
+
expect(persisted!.everInjected).toEqual([
|
|
1803
|
+
{ slug: "phantom-router-slug", turn: 1 },
|
|
1804
|
+
]);
|
|
1805
|
+
|
|
1806
|
+
// Telemetry: `status: "page_missing"` for the phantom slug.
|
|
1807
|
+
expect(telemetryState.recordCalls.length).toBe(1);
|
|
1808
|
+
const row = telemetryState.recordCalls[0] as {
|
|
1809
|
+
mode: string;
|
|
1810
|
+
concepts: Array<{ slug: string; status: string; source: string }>;
|
|
1811
|
+
};
|
|
1812
|
+
expect(row.mode).toBe("router");
|
|
1813
|
+
const phantom = row.concepts.find(
|
|
1814
|
+
(c) => c.slug === "phantom-router-slug",
|
|
1815
|
+
);
|
|
1816
|
+
expect(phantom).toBeDefined();
|
|
1817
|
+
expect(phantom!.status).toBe("page_missing");
|
|
1818
|
+
expect(phantom!.source).toBe("router");
|
|
1819
|
+
});
|
|
1820
|
+
|
|
1821
|
+
test("flag-on: router re-picking a prior-everInjected slug does NOT re-render it; non-overlapping picks render and append to everInjected", async () => {
|
|
1822
|
+
// Turn 1: router picks alice. Standard append.
|
|
1823
|
+
routerState.nextResult = {
|
|
1824
|
+
selectedSlugs: ["alice-vscode"],
|
|
1825
|
+
failureReason: null,
|
|
1826
|
+
};
|
|
1827
|
+
const turn1 = await injectMemoryV2Block({
|
|
1828
|
+
database: db,
|
|
1829
|
+
conversationId: "conv-router-dedup",
|
|
1830
|
+
currentTurn: 1,
|
|
1831
|
+
userMessage: "Tell me about Alice",
|
|
1832
|
+
assistantMessage: "",
|
|
1833
|
+
nowText: "Now",
|
|
1834
|
+
messageId: "msg-1",
|
|
1835
|
+
config: makeConfig({ router: { enabled: true } }),
|
|
1836
|
+
});
|
|
1837
|
+
expect(turn1.toInject).toEqual(["alice-vscode"]);
|
|
1838
|
+
|
|
1839
|
+
// Turn 2: router re-picks alice (the "re-anchor" prompt branch) AND
|
|
1840
|
+
// adds bob. The block must NOT contain alice's body — her cached
|
|
1841
|
+
// attachment from turn 1 is still on the prior user message — but
|
|
1842
|
+
// must contain bob's.
|
|
1843
|
+
telemetryState.recordCalls.length = 0;
|
|
1844
|
+
routerState.nextResult = {
|
|
1845
|
+
selectedSlugs: ["alice-vscode", "bob-coffee"],
|
|
1846
|
+
failureReason: null,
|
|
1847
|
+
};
|
|
1848
|
+
const turn2 = await injectMemoryV2Block({
|
|
1849
|
+
database: db,
|
|
1850
|
+
conversationId: "conv-router-dedup",
|
|
1851
|
+
currentTurn: 2,
|
|
1852
|
+
userMessage: "And Bob?",
|
|
1853
|
+
assistantMessage: "Sure",
|
|
1854
|
+
nowText: "Now",
|
|
1855
|
+
messageId: "msg-2",
|
|
1856
|
+
config: makeConfig({ router: { enabled: true } }),
|
|
1857
|
+
});
|
|
1858
|
+
|
|
1859
|
+
// Re-picked alice was deduped; only bob is freshly injected.
|
|
1860
|
+
expect(turn2.toInject).toEqual(["bob-coffee"]);
|
|
1861
|
+
expect(turn2.block).not.toBeNull();
|
|
1862
|
+
expect(turn2.block).toContain("# memory/concepts/bob-coffee.md");
|
|
1863
|
+
expect(turn2.block).toContain("Bob takes his coffee");
|
|
1864
|
+
expect(turn2.block).not.toContain("VS Code");
|
|
1865
|
+
expect(turn2.block).not.toContain("# memory/concepts/alice-vscode.md");
|
|
1866
|
+
|
|
1867
|
+
// everInjected only gained bob — alice was already there.
|
|
1868
|
+
const persisted = await hydrate(db, "conv-router-dedup");
|
|
1869
|
+
expect(persisted!.everInjected).toEqual([
|
|
1870
|
+
{ slug: "alice-vscode", turn: 1 },
|
|
1871
|
+
{ slug: "bob-coffee", turn: 2 },
|
|
1872
|
+
]);
|
|
1873
|
+
});
|
|
1874
|
+
|
|
1875
|
+
test("flag-on: telemetry distinguishes `source: router` (router picks) from `source: carry_over` (prior-everInjected slugs the router did not re-pick)", async () => {
|
|
1876
|
+
// Turn 1: seed everInjected with alice.
|
|
1877
|
+
routerState.nextResult = {
|
|
1878
|
+
selectedSlugs: ["alice-vscode"],
|
|
1879
|
+
failureReason: null,
|
|
1880
|
+
};
|
|
1881
|
+
await injectMemoryV2Block({
|
|
1882
|
+
database: db,
|
|
1883
|
+
conversationId: "conv-router-source",
|
|
1884
|
+
currentTurn: 1,
|
|
1885
|
+
userMessage: "Alice",
|
|
1886
|
+
assistantMessage: "",
|
|
1887
|
+
nowText: "Now",
|
|
1888
|
+
messageId: "msg-1",
|
|
1889
|
+
config: makeConfig({ router: { enabled: true } }),
|
|
1890
|
+
});
|
|
1891
|
+
telemetryState.recordCalls.length = 0;
|
|
1892
|
+
|
|
1893
|
+
// Turn 2: router picks bob only. alice is still in everInjected but
|
|
1894
|
+
// not re-picked — her telemetry row must read `source: carry_over`,
|
|
1895
|
+
// not `source: router`.
|
|
1896
|
+
routerState.nextResult = {
|
|
1897
|
+
selectedSlugs: ["bob-coffee"],
|
|
1898
|
+
failureReason: null,
|
|
1899
|
+
};
|
|
1900
|
+
await injectMemoryV2Block({
|
|
1901
|
+
database: db,
|
|
1902
|
+
conversationId: "conv-router-source",
|
|
1903
|
+
currentTurn: 2,
|
|
1904
|
+
userMessage: "Bob",
|
|
1905
|
+
assistantMessage: "",
|
|
1906
|
+
nowText: "Now",
|
|
1907
|
+
messageId: "msg-2",
|
|
1908
|
+
config: makeConfig({ router: { enabled: true } }),
|
|
1909
|
+
});
|
|
1910
|
+
|
|
1911
|
+
expect(telemetryState.recordCalls.length).toBe(1);
|
|
1912
|
+
const row = telemetryState.recordCalls[0] as {
|
|
1913
|
+
mode: string;
|
|
1914
|
+
concepts: Array<{ slug: string; source: string; status: string }>;
|
|
1915
|
+
};
|
|
1916
|
+
expect(row.mode).toBe("router");
|
|
1917
|
+
const aliceRow = row.concepts.find((c) => c.slug === "alice-vscode");
|
|
1918
|
+
const bobRow = row.concepts.find((c) => c.slug === "bob-coffee");
|
|
1919
|
+
expect(aliceRow).toBeDefined();
|
|
1920
|
+
expect(bobRow).toBeDefined();
|
|
1921
|
+
expect(aliceRow!.source).toBe("carry_over");
|
|
1922
|
+
expect(aliceRow!.status).toBe("in_context");
|
|
1923
|
+
expect(bobRow!.source).toBe("router");
|
|
1924
|
+
expect(bobRow!.status).toBe("injected");
|
|
1925
|
+
});
|
|
1926
|
+
|
|
1927
|
+
test("flag-off (default): activation pipeline still runs unchanged", async () => {
|
|
1928
|
+
// Regression check — with the router flag explicitly off (the
|
|
1929
|
+
// production default), `runRouter` must never be called and the
|
|
1930
|
+
// activation pipeline drives the selection just like before.
|
|
1931
|
+
stageTurn([{ slug: "alice-vscode", denseScore: 0.9 }]);
|
|
1932
|
+
routerState.nextResult = {
|
|
1933
|
+
selectedSlugs: ["should-not-be-used"],
|
|
1934
|
+
failureReason: null,
|
|
1935
|
+
};
|
|
1936
|
+
|
|
1937
|
+
const result = await injectMemoryV2Block({
|
|
1938
|
+
database: db,
|
|
1939
|
+
conversationId: "conv-flag-off",
|
|
1940
|
+
currentTurn: 1,
|
|
1941
|
+
userMessage: "Alice's editor",
|
|
1942
|
+
assistantMessage: "",
|
|
1943
|
+
nowText: "Now",
|
|
1944
|
+
messageId: "msg-1",
|
|
1945
|
+
config: makeConfig({ router: { enabled: false } }),
|
|
1946
|
+
});
|
|
1947
|
+
|
|
1948
|
+
// Router was not called.
|
|
1949
|
+
expect(routerState.callCount).toBe(0);
|
|
1950
|
+
// Activation pipeline produced its normal result.
|
|
1951
|
+
expect(result.toInject).toEqual(["alice-vscode"]);
|
|
1952
|
+
expect(result.block).toContain("# memory/concepts/alice-vscode.md");
|
|
1953
|
+
|
|
1954
|
+
// Telemetry row carries the activation mode, not router.
|
|
1955
|
+
expect(telemetryState.recordCalls.length).toBe(1);
|
|
1956
|
+
const row = telemetryState.recordCalls[0] as { mode: string };
|
|
1957
|
+
expect(row.mode).toBe("per-turn");
|
|
1958
|
+
});
|
|
1959
|
+
|
|
1960
|
+
test("flag-on + mode='context-load': router runs (everInjected was cleared by onCompacted so dedupe is a no-op; abstention is accepted as the trade-off)", async () => {
|
|
1961
|
+
// Context-load is the full top-K bootstrap fired after compaction or
|
|
1962
|
+
// a fresh conversation reload. The earlier worry about the router's
|
|
1963
|
+
// `everInjected` dedupe filtering out post-compaction restorations
|
|
1964
|
+
// doesn't apply: `ConversationGraphMemory.onCompacted` calls
|
|
1965
|
+
// `evictCompactedTurnsV2` which empties the list before this code
|
|
1966
|
+
// runs. Router abstention here means no v2 pages this turn — that's
|
|
1967
|
+
// preferable to letting the activation graph pick something arbitrary.
|
|
1968
|
+
routerState.nextResult = {
|
|
1969
|
+
selectedSlugs: ["alice-vscode"],
|
|
1970
|
+
failureReason: null,
|
|
1971
|
+
};
|
|
1972
|
+
|
|
1973
|
+
const result = await injectMemoryV2Block({
|
|
1974
|
+
database: db,
|
|
1975
|
+
conversationId: "conv-context-load-router-on",
|
|
1976
|
+
currentTurn: 1,
|
|
1977
|
+
userMessage: "Tell me about Alice",
|
|
1978
|
+
assistantMessage: "",
|
|
1979
|
+
nowText: "Now",
|
|
1980
|
+
messageId: "msg-1",
|
|
1981
|
+
mode: "context-load",
|
|
1982
|
+
config: makeConfig({ router: { enabled: true } }),
|
|
1983
|
+
});
|
|
1984
|
+
|
|
1985
|
+
// Router was called on context-load too.
|
|
1986
|
+
expect(routerState.callCount).toBe(1);
|
|
1987
|
+
|
|
1988
|
+
// Router's picks were rendered.
|
|
1989
|
+
expect(result.toInject).toEqual(["alice-vscode"]);
|
|
1990
|
+
expect(result.block).toContain("# memory/concepts/alice-vscode.md");
|
|
1991
|
+
|
|
1992
|
+
// Telemetry row reflects the router mode, not the activation mode.
|
|
1993
|
+
expect(telemetryState.recordCalls.length).toBe(1);
|
|
1994
|
+
const row = telemetryState.recordCalls[0] as { mode: string };
|
|
1995
|
+
expect(row.mode).toBe("router");
|
|
1996
|
+
});
|
|
1997
|
+
});
|
|
1290
1998
|
});
|