@vellumai/assistant 0.10.9 → 0.10.10-dev.202607162206.d08e98e
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/ARCHITECTURE.md +1 -1
- package/Dockerfile +8 -0
- package/docs/activation-funnel-telemetry.md +13 -7
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/package.json +1 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/__tests__/redacted-credential.test.ts +200 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/redacted-credential.ts +226 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/package.json +1 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/__tests__/redacted-credential.test.ts +200 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/redacted-credential.ts +226 -0
- package/node_modules/@vellumai/service-contracts/package.json +1 -0
- package/node_modules/@vellumai/service-contracts/src/__tests__/redacted-credential.test.ts +200 -0
- package/node_modules/@vellumai/service-contracts/src/redacted-credential.ts +226 -0
- package/openapi.yaml +530 -107
- package/package.json +1 -1
- package/scripts/generate-openapi.ts +8 -0
- package/src/__tests__/activation-early-marking.test.ts +6 -5
- package/src/__tests__/agent-loop-override-profile.test.ts +22 -25
- package/src/__tests__/agent-wake-override-profile.test.ts +21 -48
- package/src/__tests__/app-builder-tool-scripts.test.ts +0 -1
- package/src/__tests__/app-bundler.test.ts +4 -12
- package/src/__tests__/app-executors.test.ts +2 -49
- package/src/__tests__/app-routes-csp.test.ts +114 -146
- package/src/__tests__/auth-fallback-events-store.test.ts +8 -1
- package/src/__tests__/build-persisted-content.test.ts +96 -0
- package/src/__tests__/bundle-scanner.test.ts +27 -1
- package/src/__tests__/call-controller.test.ts +291 -0
- package/src/__tests__/call-site-routing-connection-auto-resolve.test.ts +165 -0
- package/src/__tests__/chat-credential-redaction.test.ts +1395 -0
- package/src/__tests__/chat-reveal-guard-priming.test.ts +791 -0
- package/src/__tests__/compaction.benchmark.test.ts +2 -1
- package/src/__tests__/compactor-image-manifest-trust.test.ts +50 -0
- package/src/__tests__/config-loader-backfill.test.ts +9 -4
- package/src/__tests__/config-schema-cmd.test.ts +10 -11
- package/src/__tests__/config-schema.test.ts +190 -257
- package/src/__tests__/conversation-agent-loop-fatal-cleanup.test.ts +208 -0
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +5 -19
- package/src/__tests__/conversation-agent-loop.test.ts +22 -9
- package/src/__tests__/conversation-error.test.ts +31 -0
- package/src/__tests__/conversation-load-history-repair.test.ts +110 -0
- package/src/__tests__/conversation-process-callsite.test.ts +12 -19
- package/src/__tests__/conversation-routes-slash-commands.test.ts +7 -21
- package/src/__tests__/conversation-summarize-route.test.ts +36 -44
- package/src/__tests__/conversation-surfaces-action-delivery.test.ts +1 -0
- package/src/__tests__/conversation-surfaces-activation-emit.test.ts +7 -7
- package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +0 -1
- package/src/__tests__/conversation-tool-setup-attribution.test.ts +0 -1
- package/src/__tests__/conversation-usage.test.ts +4 -12
- package/src/__tests__/credential-routes.test.ts +250 -0
- package/src/__tests__/credential-security-invariants.test.ts +3 -0
- package/src/__tests__/db-migration-rollback.test.ts +22 -0
- package/src/__tests__/empty-response-hook.test.ts +186 -1
- package/src/__tests__/external-plugin-loader.test.ts +11 -5
- package/src/__tests__/heartbeat-service.test.ts +0 -28
- package/src/__tests__/host-shell-tool.test.ts +2 -0
- package/src/__tests__/inactive-tool-error-messages.test.ts +2 -2
- package/src/__tests__/inference-no-mode-boot-e2e.test.ts +52 -8
- package/src/__tests__/internal-telemetry-routes.test.ts +23 -4
- package/src/__tests__/invite-routes-http.test.ts +12 -16
- package/src/__tests__/list-all-apps.test.ts +0 -4
- package/src/__tests__/llm-context-resolution.test.ts +32 -56
- package/src/__tests__/llm-request-log-turn-query.test.ts +109 -0
- package/src/__tests__/llm-resolver-override-or-default.test.ts +3 -52
- package/src/__tests__/llm-resolver.test.ts +342 -602
- package/src/__tests__/llm-schema.test.ts +79 -37
- package/src/__tests__/max-tokens-continue-hook.test.ts +19 -0
- package/src/__tests__/media-stream-output.test.ts +259 -3
- package/src/__tests__/media-stream-server-integration.test.ts +22 -1
- package/src/__tests__/media-stream-stt-session.test.ts +47 -0
- package/src/__tests__/memory-jobs-worker-cleanup-cadence.test.ts +33 -0
- package/src/__tests__/memory-recall-log-store.test.ts +47 -13
- package/src/__tests__/mock-gateway-ipc.ts +46 -1
- package/src/__tests__/mtime-cache.test.ts +61 -0
- package/src/__tests__/navigate-settings-tab.test.ts +2 -0
- package/src/__tests__/normalize-onboarding.test.ts +33 -0
- package/src/__tests__/onboarding-persona-write.test.ts +26 -0
- package/src/__tests__/plugin-api-resolve-credential.test.ts +140 -0
- package/src/__tests__/plugin-app-serve-routes.test.ts +166 -14
- package/src/__tests__/plugin-import-boundary-reverse-guard.test.ts +2 -0
- package/src/__tests__/post-turn-tool-result-truncation.test.ts +38 -0
- package/src/__tests__/provider-commit-message-generator.test.ts +27 -20
- package/src/__tests__/provider-connections-backfill.test.ts +138 -0
- package/src/__tests__/provider-platform-proxy-integration.test.ts +10 -31
- package/src/__tests__/provider-registry-ollama.test.ts +8 -18
- package/src/__tests__/provider-send-message-override-profile.test.ts +23 -23
- package/src/__tests__/provider-usage-tracking.test.ts +9 -19
- package/src/__tests__/prune-old-conversations-job.test.ts +12 -0
- package/src/__tests__/published-app-updater.test.ts +22 -16
- package/src/__tests__/registry.test.ts +5 -16
- package/src/__tests__/retry-openrouter-only-normalization.test.ts +22 -23
- package/src/__tests__/retry-thinking-adaptive-only.test.ts +43 -48
- package/src/__tests__/retry-thinking-tool-choice.test.ts +57 -66
- package/src/__tests__/retry-verbosity-normalization.test.ts +24 -23
- package/src/__tests__/reveal-success-registry.test.ts +123 -0
- package/src/__tests__/run-conversation-turn-persistence.test.ts +130 -0
- package/src/__tests__/secret-fixtures.ts +9 -0
- package/src/__tests__/server-history-render.test.ts +28 -0
- package/src/__tests__/skills.test.ts +9 -4
- package/src/__tests__/slack-share-routes.test.ts +0 -1
- package/src/__tests__/stt-stream-session.test.ts +6 -5
- package/src/__tests__/subagent-call-site-routing.test.ts +69 -95
- package/src/__tests__/subagent-disposal.test.ts +2 -0
- package/src/__tests__/subagent-fork-notifications.test.ts +2 -0
- package/src/__tests__/subagent-fork-spawn.test.ts +2 -0
- package/src/__tests__/subagent-manager-notify.test.ts +2 -0
- package/src/__tests__/subagent-role-registry.test.ts +37 -0
- package/src/__tests__/subagent-spawn-and-await.test.ts +1 -0
- package/src/__tests__/subagent-terminal-message.test.ts +50 -0
- package/src/__tests__/subagent-tool-gate-mode.test.ts +78 -5
- package/src/__tests__/surface-completion-nudge-hook.test.ts +19 -0
- package/src/__tests__/telemetry-routes.test.ts +99 -19
- package/src/__tests__/tool-audit.test.ts +34 -4
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +1 -1
- package/src/__tests__/tool-profiler.test.ts +72 -1
- package/src/__tests__/tool-result-spool.test.ts +49 -4
- package/src/__tests__/tool-side-effects-slack-dm.test.ts +0 -1
- package/src/__tests__/ui-channel-variants.test.ts +108 -0
- package/src/__tests__/ui-shape-teaching.test.ts +255 -0
- package/src/__tests__/usage-attribution.test.ts +18 -41
- package/src/__tests__/user-plugin-loader.test.ts +4 -4
- package/src/__tests__/voice-config-update.test.ts +46 -0
- package/src/__tests__/voice-session-bridge.test.ts +216 -84
- package/src/__tests__/workspace-migration-131-drop-web-fetch-mode.test.ts +120 -0
- package/src/agent/loop.ts +4 -3
- package/src/api/events/open-conversation.test.ts +64 -0
- package/src/api/events/open-conversation.ts +33 -0
- package/src/api/index.ts +6 -0
- package/src/api/responses/conversation-message.ts +5 -0
- package/src/apps/app-store.ts +25 -35
- package/src/bundler/app-bundler.ts +34 -48
- package/src/bundler/app-compiler.ts +39 -4
- package/src/bundler/bundle-scanner.ts +13 -0
- package/src/bundler/manifest.ts +1 -1
- package/src/calls/__tests__/voice-session-bridge.test.ts +47 -0
- package/src/calls/call-constants.ts +5 -0
- package/src/calls/call-controller.ts +100 -32
- package/src/calls/call-transport.ts +9 -0
- package/src/calls/media-stream-output.ts +107 -4
- package/src/calls/media-stream-server.ts +29 -9
- package/src/calls/media-stream-stt-session.ts +7 -1
- package/src/calls/media-turn-detector.ts +11 -1
- package/src/calls/voice-session-bridge.ts +84 -63
- package/src/cli/commands/__tests__/inference-providers.test.ts +270 -33
- package/src/cli/commands/__tests__/notifications.test.ts +24 -3
- package/src/cli/commands/config.help.ts +6 -6
- package/src/cli/commands/credentials.help.ts +13 -0
- package/src/cli/commands/credentials.ts +6 -1
- package/src/cli/commands/email.help.ts +7 -0
- package/src/cli/commands/email.ts +35 -1
- package/src/cli/commands/inference-providers.ts +168 -107
- package/src/cli/commands/inference.help.ts +118 -46
- package/src/cli/commands/memory/index.help.ts +23 -0
- package/src/cli/commands/memory/nodes.ts +146 -0
- package/src/cli/commands/notifications.help.ts +10 -10
- package/src/cli/commands/oauth/connect-surface-guidance.test.ts +40 -0
- package/src/cli/commands/oauth/connect-surface-guidance.ts +54 -0
- package/src/cli/commands/oauth/connect.test.ts +126 -0
- package/src/cli/commands/oauth/connect.ts +29 -6
- package/src/cli/commands/oauth/index.help.ts +7 -1
- package/src/cli/commands/oauth/status.test.ts +69 -3
- package/src/cli/commands/oauth/status.ts +50 -12
- package/src/cli/commands/plugins.help.ts +13 -2
- package/src/cli/commands/plugins.ts +61 -7
- package/src/cli/commands/telemetry.help.ts +13 -0
- package/src/cli/commands/telemetry.ts +45 -5
- package/src/cli/lib/__tests__/plugin-catalog-local.test.ts +8 -2
- package/src/cli/lib/__tests__/upgrade-plugin.test.ts +213 -9
- package/src/cli/lib/bundled-marketplace.json +93 -36
- package/src/cli/lib/inspect-plugin.ts +5 -1
- package/src/cli/lib/install-from-github.ts +68 -4
- package/src/cli/lib/plugin-catalog-local.ts +6 -2
- package/src/cli/lib/plugin-fingerprint.ts +3 -3
- package/src/cli/lib/upgrade-plugin.ts +236 -35
- package/src/config/__tests__/default-profile-catalog.test.ts +8 -12
- package/src/config/__tests__/plugin-resident-skill-discovery.test.ts +137 -0
- package/src/config/__tests__/profile-materialization.test.ts +1 -88
- package/src/config/bundled-skills/AGENTS.md +3 -30
- package/src/config/bundled-skills/app-builder/SKILL.md +5 -3
- package/src/config/bundled-skills/app-builder/TOOLS.json +23 -0
- package/src/config/bundled-skills/app-builder/tools/app-open.ts +32 -0
- package/src/config/bundled-skills/messaging/tools/messaging-send.ts +1 -1
- package/src/config/bundled-skills/phone-calls/references/CONFIG.md +7 -7
- package/src/config/bundled-skills/schedule/SKILL.md +1 -1
- package/src/config/bundled-skills/schedule/TOOLS.json +1 -1
- package/src/config/bundled-skills/settings/TOOLS.json +3 -1
- package/src/config/bundled-skills/settings/tools/navigate-settings-tab.ts +2 -0
- package/src/config/bundled-skills/settings/tools/voice-config-update.ts +18 -10
- package/src/config/bundled-skills/subagent/SKILL.md +2 -0
- package/src/config/bundled-skills/subagent/TOOLS.json +1 -1
- package/src/config/call-site-defaults.ts +3 -3
- package/src/config/feature-flag-registry.json +24 -39
- package/src/config/llm-resolver.ts +88 -475
- package/src/config/profile-materialization.ts +11 -11
- package/src/config/schema.ts +93 -127
- package/src/config/schemas/__tests__/live-voice.test.ts +24 -6
- package/src/config/schemas/__tests__/stt.test.ts +31 -3
- package/src/config/schemas/live-voice.ts +10 -4
- package/src/config/schemas/llm.ts +52 -15
- package/src/config/schemas/memory-lifecycle.ts +1 -1
- package/src/config/schemas/memory-retrospective.ts +8 -0
- package/src/config/schemas/memory-v2.ts +2 -2
- package/src/config/schemas/memory-v3.ts +1 -1
- package/src/config/schemas/services.ts +6 -3
- package/src/config/schemas/stt.ts +38 -21
- package/src/config/schemas/tts.ts +13 -18
- package/src/config/skills.ts +31 -21
- package/src/context/compactor.ts +44 -7
- package/src/context/post-turn-tool-result-truncation.ts +4 -2
- package/src/context/tool-result-spool.ts +26 -5
- package/src/conversations/__tests__/message-consolidation.test.ts +48 -0
- package/src/conversations/message-consolidation.ts +22 -2
- package/src/daemon/__tests__/conversation-tool-setup.test.ts +5 -12
- package/src/daemon/app-source-watcher.ts +17 -23
- package/src/daemon/chat-credential-redaction.ts +1365 -0
- package/src/daemon/conversation-agent-loop-handlers.ts +627 -34
- package/src/daemon/conversation-agent-loop.ts +71 -31
- package/src/daemon/conversation-error.ts +36 -14
- package/src/daemon/conversation-process.ts +22 -0
- package/src/daemon/conversation-store.ts +35 -0
- package/src/daemon/conversation-surfaces.ts +25 -25
- package/src/daemon/conversation-tool-setup.ts +33 -7
- package/src/daemon/conversation.ts +55 -5
- package/src/daemon/handlers/shared.ts +33 -2
- package/src/daemon/lifecycle.ts +4 -4
- package/src/daemon/message-types/conversations.ts +6 -17
- package/src/daemon/providers-setup.ts +8 -0
- package/src/daemon/tool-setup-types.ts +7 -1
- package/src/daemon/wake-conversation-ops.ts +10 -1
- package/src/hooks/types.ts +9 -0
- package/src/ipc/__tests__/email-ipc.test.ts +90 -0
- package/src/ipc/gateway-client.test.ts +59 -0
- package/src/ipc/gateway-client.ts +70 -27
- package/src/live-voice/__tests__/live-voice-events.test.ts +14 -2
- package/src/live-voice/__tests__/live-voice-integration.test.ts +116 -2
- package/src/live-voice/__tests__/live-voice-vad.test.ts +804 -13
- package/src/live-voice/__tests__/protocol.test.ts +122 -0
- package/src/live-voice/live-voice-session.ts +443 -40
- package/src/live-voice/protocol.ts +143 -1
- package/src/monitoring/__tests__/plugin-source-watch.test.ts +3 -1
- package/src/monitoring/plugin-source-watch.ts +3 -62
- package/src/notifications/README.md +1 -1
- package/src/permissions/checker.ts +8 -4
- package/src/persistence/__tests__/db-init-migrations-ok.test.ts +26 -0
- package/src/persistence/conversation-crud.ts +104 -2
- package/src/persistence/db-init.ts +14 -3
- package/src/persistence/job-handlers/cleanup.ts +15 -7
- package/src/persistence/llm-request-log-store.ts +88 -52
- package/src/persistence/migrations/298-move-memory-jobs-to-memory-db.ts +7 -31
- package/src/persistence/migrations/305-drop-contact-acl-columns.ts +3 -2
- package/src/persistence/migrations/326-move-injection-events-to-memory-db.ts +8 -34
- package/src/persistence/migrations/336-move-memory-v2-activation-logs-to-memory-db.ts +90 -0
- package/src/persistence/migrations/337-move-memory-recall-logs-to-memory-db.ts +114 -0
- package/src/persistence/migrations/338-move-memory-v3-selections-to-memory-db.ts +84 -0
- package/src/persistence/migrations/339-move-activation-sessions-to-memory-db.ts +52 -0
- package/src/persistence/migrations/__tests__/run-migrations.test.ts +155 -0
- package/src/persistence/migrations/helpers/relocation.ts +44 -1
- package/src/persistence/migrations/run-migrations.ts +25 -1
- package/src/persistence/schema/infrastructure.ts +9 -0
- package/src/persistence/schema/memory-core.ts +3 -0
- package/src/persistence/schema/memory-injection.ts +2 -0
- package/src/persistence/steps.ts +36 -0
- package/src/platform/client.test.ts +1 -44
- package/src/platform/client.ts +10 -20
- package/src/platform/consent-cache.test.ts +89 -23
- package/src/platform/consent-cache.ts +71 -33
- package/src/plugin-api/constants.ts +12 -0
- package/src/plugin-api/conversation-turn.ts +37 -14
- package/src/plugin-api/index.ts +11 -1
- package/src/plugin-api/resolve-credential.ts +75 -0
- package/src/plugin-api/vision-support.test.ts +8 -19
- package/src/plugin-api/vision-support.ts +26 -17
- package/src/plugins/collect-source-versions.ts +77 -0
- package/src/plugins/defaults/compaction/compact.ts +6 -0
- package/src/plugins/defaults/compaction/window-manager.ts +9 -0
- package/src/plugins/defaults/empty-response/hooks/post-model-call.ts +18 -24
- package/src/plugins/defaults/empty-response/hooks/user-prompt-submit.ts +38 -0
- package/src/plugins/defaults/empty-response/refusal-quarantine.ts +99 -0
- package/src/plugins/defaults/image-fallback/__tests__/caption-cache-persistence.test.ts +7 -3
- package/src/plugins/defaults/index.ts +8 -1
- package/src/plugins/defaults/max-tokens-continue/hooks/post-model-call.ts +4 -1
- package/src/plugins/defaults/memory/__tests__/activation-session-store.test.ts +48 -6
- package/src/plugins/defaults/memory/__tests__/db-memory-attach.test.ts +28 -22
- package/src/plugins/defaults/memory/__tests__/memory-log-stores-degraded.test.ts +148 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +80 -8
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-prompt.test.ts +202 -0
- package/src/plugins/defaults/memory/__tests__/memory-v2-activation-log-store.test.ts +64 -25
- package/src/plugins/defaults/memory/__tests__/memory-v2-concept-frequency.test.ts +24 -11
- package/src/plugins/defaults/memory/__tests__/prompt-override.test.ts +70 -6
- package/src/plugins/defaults/memory/__tests__/table-relocation.test.ts +278 -0
- package/src/plugins/defaults/memory/activation-session-store.ts +27 -20
- package/src/plugins/defaults/memory/context-search/sources/memory-v2.ts +2 -9
- package/src/plugins/defaults/memory/context-search/sources/workspace.ts +1 -8
- package/src/plugins/defaults/memory/graph/retriever.test.ts +6 -6
- package/src/plugins/defaults/memory/graph/store.ts +114 -0
- package/src/plugins/defaults/memory/graph/tool-handlers.ts +36 -1
- package/src/plugins/defaults/memory/graph/tools.ts +47 -13
- package/src/plugins/defaults/memory/graph-topology/build-memory-graph.ts +16 -35
- package/src/plugins/defaults/memory/jobs-worker.ts +10 -1
- package/src/plugins/defaults/memory/memory-db.ts +3 -2
- package/src/plugins/defaults/memory/memory-recall-log-store.ts +129 -66
- package/src/plugins/defaults/memory/memory-retrospective-constants.ts +8 -0
- package/src/plugins/defaults/memory/memory-retrospective-job.ts +11 -122
- package/src/plugins/defaults/memory/memory-retrospective-prompt.ts +216 -0
- package/src/plugins/defaults/memory/memory-v2-activation-log-store.ts +84 -46
- package/src/plugins/defaults/memory/memory-v2-concept-frequency.ts +42 -32
- package/src/plugins/defaults/memory/path-containment.ts +21 -0
- package/src/plugins/defaults/memory/prompt-override.ts +51 -13
- package/src/plugins/defaults/memory/tools.test.ts +34 -0
- package/src/plugins/defaults/memory/tools.ts +12 -2
- package/src/plugins/defaults/memory/v2/__tests__/harness-compare.test.ts +19 -15
- package/src/plugins/defaults/memory/v2/__tests__/harness-oracle.test.ts +24 -19
- package/src/plugins/defaults/memory/v2/__tests__/harness-replay-input.test.ts +19 -15
- package/src/plugins/defaults/memory/v2/__tests__/injection.test.ts +22 -2
- package/src/plugins/defaults/memory/v2/__tests__/prompts-consolidation.test.ts +5 -3
- package/src/plugins/defaults/memory/v2/harness/oracle.ts +59 -41
- package/src/plugins/defaults/memory/v2/harness/replay-input.ts +29 -25
- package/src/plugins/defaults/memory/v2/migration.ts +46 -16
- package/src/plugins/defaults/memory/v2/prompts/consolidation.ts +4 -0
- package/src/plugins/defaults/memory/v3/__tests__/carry-integration.test.ts +17 -15
- package/src/plugins/defaults/memory/v3/__tests__/gate.test.ts +24 -22
- package/src/plugins/defaults/memory/v3/__tests__/injection.test.ts +9 -5
- package/src/plugins/defaults/memory/v3/__tests__/orchestrate.test.ts +277 -10
- package/src/plugins/defaults/memory/v3/__tests__/selection-log-store.test.ts +57 -5
- package/src/plugins/defaults/memory/v3/__tests__/shadow-integration.test.ts +9 -7
- package/src/plugins/defaults/memory/v3/__tests__/shadow-plugin.test.ts +38 -7
- package/src/plugins/defaults/memory/v3/hot-set.test.ts +36 -18
- package/src/plugins/defaults/memory/v3/hot-set.ts +12 -14
- package/src/plugins/defaults/memory/v3/learned-edges.test.ts +47 -27
- package/src/plugins/defaults/memory/v3/learned-edges.ts +12 -14
- package/src/plugins/defaults/memory/v3/orchestrate.ts +139 -22
- package/src/plugins/defaults/memory/v3/prune.test.ts +10 -3
- package/src/plugins/defaults/memory/v3/prune.ts +17 -11
- package/src/plugins/defaults/memory/v3/selection-log-store.ts +23 -11
- package/src/plugins/defaults/memory/v3/shadow-plugin.ts +70 -53
- package/src/plugins/defaults/surface-completion-nudge/hooks/post-model-call.ts +9 -6
- package/src/plugins/external-plugin-loader.ts +15 -15
- package/src/plugins/mtime-cache.ts +47 -0
- package/src/plugins/pipeline.ts +9 -1
- package/src/plugins/plugin-execution-context.ts +44 -0
- package/src/plugins/plugin-tree-walk.ts +31 -24
- package/src/plugins/source-fingerprint.ts +3 -4
- package/src/prompts/normalize-onboarding.ts +12 -0
- package/src/prompts/persona-resolver.ts +8 -0
- package/src/prompts/templates/BOOTSTRAP-ACTIVATION-RAIL.md +1 -1
- package/src/providers/__tests__/dispatch-connection-routing.test.ts +6 -9
- package/src/providers/__tests__/registry-native-web-search.test.ts +4 -10
- package/src/providers/__tests__/retry-callsite.test.ts +235 -215
- package/src/providers/__tests__/satellite-connection-routing.test.ts +8 -16
- package/src/providers/atlascloud/client.ts +10 -49
- package/src/providers/baseten/client.ts +43 -0
- package/src/providers/call-site-routing.ts +26 -13
- package/src/providers/connection-resolution.ts +9 -11
- package/src/providers/fetch-provider-catalog.ts +4 -2
- package/src/providers/inference/__tests__/adapter-factory-openai-compatible.test.ts +29 -4
- package/src/providers/inference/__tests__/connection-availability-keyless.test.ts +78 -0
- package/src/providers/inference/adapter-factory.ts +27 -6
- package/src/providers/inference/auth.ts +29 -1
- package/src/providers/inference/backfill.ts +61 -10
- package/src/providers/inference/connection-availability.ts +3 -2
- package/src/providers/inference/resolve-auth.ts +9 -1
- package/src/providers/model-catalog.ts +37 -0
- package/src/providers/openai/__tests__/api-error-normalization.test.ts +24 -2
- package/src/providers/openai/api-key-validation.ts +70 -0
- package/src/providers/retry.ts +2 -0
- package/src/providers/types.ts +11 -4
- package/src/providers/vellum-model-routing.test.ts +26 -0
- package/src/providers/vellum-model-routing.ts +30 -0
- package/src/providers/voice-error-copy.ts +47 -0
- package/src/runtime/agent-wake.ts +40 -24
- package/src/runtime/for-chat-mint-registry.ts +118 -0
- package/src/runtime/reveal-nonce.ts +49 -0
- package/src/runtime/reveal-success-registry.ts +306 -0
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +88 -3
- package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +161 -30
- package/src/runtime/routes/__tests__/migration-vellum-metadata-reconcile.test.ts +7 -0
- package/src/runtime/routes/__tests__/schedule-worker-routes.test.ts +15 -0
- package/src/runtime/routes/app-management-routes.ts +152 -45
- package/src/runtime/routes/app-routes.ts +16 -77
- package/src/runtime/routes/canned-message-complete.ts +16 -16
- package/src/runtime/routes/conversation-management-routes.ts +17 -14
- package/src/runtime/routes/conversation-query-routes.ts +54 -6
- package/src/runtime/routes/conversation-routes.ts +48 -2
- package/src/runtime/routes/credential-routes.ts +116 -3
- package/src/runtime/routes/email-routes.ts +17 -1
- package/src/runtime/routes/inbound-stages/transcribe-audio.test.ts +33 -5
- package/src/runtime/routes/inbound-stages/transcribe-audio.ts +6 -5
- package/src/runtime/routes/inference-provider-connection-routes.ts +77 -28
- package/src/runtime/routes/inference-send-routes.ts +1 -1
- package/src/runtime/routes/internal-telemetry-routes.ts +7 -6
- package/src/runtime/routes/plugins-routes.ts +37 -6
- package/src/runtime/routes/publish-routes.ts +15 -18
- package/src/runtime/routes/schedule-worker-routes.ts +9 -0
- package/src/runtime/routes/secret-routes.ts +10 -0
- package/src/runtime/routes/telemetry-routes.ts +149 -45
- package/src/schedule/__tests__/schedule-timezone.test.ts +101 -0
- package/src/schedule/__tests__/worker-watchdog.test.ts +209 -0
- package/src/schedule/schedule-store.ts +12 -1
- package/src/schedule/schedule-timezone.ts +63 -0
- package/src/schedule/scheduler.ts +100 -6
- package/src/schedule/worker-control.ts +19 -0
- package/src/security/auth-fallback-events-store.ts +7 -6
- package/src/security/secret-scanner.ts +26 -1
- package/src/services/published-app-updater.ts +6 -11
- package/src/stt/stt-stream-session.ts +36 -16
- package/src/subagent/manager.ts +34 -8
- package/src/telemetry/AGENTS.md +30 -1
- package/src/telemetry/__tests__/config-setting-snapshot.test.ts +18 -0
- package/src/telemetry/__tests__/outbox-test-harness.ts +5 -3
- package/src/telemetry/config-setting-snapshot.ts +44 -10
- package/src/telemetry/telemetry-event-sources.test.ts +124 -30
- package/src/telemetry/telemetry-event-sources.ts +137 -83
- package/src/telemetry/telemetry-events-outbox.test.ts +46 -1
- package/src/telemetry/telemetry-events-outbox.ts +60 -9
- package/src/telemetry/telemetry-wire-source.json +1 -1
- package/src/telemetry/telemetry-wire-validation.ts +39 -2
- package/src/telemetry/telemetry-wire.generated.ts +8 -0
- package/src/telemetry/tool-audit.ts +15 -9
- package/src/telemetry/tool-executed-events-store.test.ts +1 -1
- package/src/telemetry/turn-events-store.ts +20 -0
- package/src/telemetry/types.ts +45 -12
- package/src/telemetry/usage-telemetry-reporter.test.ts +294 -25
- package/src/telemetry/usage-telemetry-reporter.ts +117 -18
- package/src/telemetry/watchdog-direct-emit.test.ts +11 -3
- package/src/telemetry/watchdog-direct-emit.ts +11 -6
- package/src/tools/apps/executors.ts +11 -36
- package/src/tools/executor.ts +14 -1
- package/src/tools/host-terminal/host-shell.ts +6 -2
- package/src/tools/network/__tests__/web-fetch-firecrawl.test.ts +1 -1
- package/src/tools/network/__tests__/web-fetch-metadata.test.ts +25 -0
- package/src/tools/network/web-fetch.ts +6 -2
- package/src/tools/skills/sandbox-runner.ts +5 -2
- package/src/tools/subagent/spawn.ts +7 -11
- package/src/tools/terminal/shell.ts +5 -2
- package/src/tools/tool-manifest.ts +0 -2
- package/src/tools/tool-profiler.ts +37 -6
- package/src/tools/ui-surface/channel-variants.ts +101 -0
- package/src/tools/ui-surface/definitions.ts +23 -67
- package/src/tools/ui-surface/surface-shape-docs.ts +229 -0
- package/src/tts/__tests__/provider-adapters.test.ts +18 -0
- package/src/tts/provider-catalog.ts +2 -4
- package/src/tts/providers/deepgram-provider.ts +10 -1
- package/src/types/onboarding-context.ts +8 -0
- package/src/usage/attribution.ts +18 -112
- package/src/{config/bundled-skills/messaging/tools/gmail-mime-helpers.ts → util/mime-type.ts} +4 -1
- package/src/util/provider-error-patterns.ts +7 -1
- package/src/util/worker-process.ts +105 -0
- package/src/watcher/__tests__/telemetry.test.ts +17 -4
- package/src/watcher/telemetry.ts +5 -5
- package/src/workspace/migrations/131-drop-web-fetch-mode.ts +61 -0
- package/src/workspace/migrations/registry.ts +2 -0
- package/src/workspace/provider-commit-message-generator.ts +6 -4
- package/src/__tests__/app-open-proxy.test.ts +0 -67
- package/src/onboarding/onboarding-research-events-store.test.ts +0 -230
- package/src/onboarding/onboarding-research-events-store.ts +0 -104
- package/src/runtime/routes/assets/vellum-design-system.css +0 -2236
- package/src/tools/apps/definitions.ts +0 -73
- package/src/tools/apps/open-proxy.ts +0 -43
|
@@ -1,18 +1,23 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Tests for the frecency hot-set lane (`hot-set.ts`) and its config schema.
|
|
3
3
|
*
|
|
4
|
-
* `computeHotSet`
|
|
5
|
-
* each test
|
|
6
|
-
* (
|
|
4
|
+
* `computeHotSet` reads `memory_v3_selections` over the dedicated memory
|
|
5
|
+
* connection: each test installs an in-memory SQLite db into the `memory`
|
|
6
|
+
* singleton slot (where the accessor resolves its connection), seeds it with
|
|
7
|
+
* selection rows, and asserts the decayed-frequency ranking.
|
|
7
8
|
*/
|
|
8
9
|
|
|
9
10
|
import { Database } from "bun:sqlite";
|
|
10
|
-
import { beforeEach, describe, expect, test } from "bun:test";
|
|
11
|
+
import { afterEach, beforeEach, describe, expect, test } from "bun:test";
|
|
11
12
|
|
|
12
13
|
import { drizzle } from "drizzle-orm/bun-sqlite";
|
|
13
14
|
|
|
14
15
|
import { MemoryV3ConfigSchema } from "../../../../config/schemas/memory-v3.js";
|
|
15
|
-
import {
|
|
16
|
+
import {
|
|
17
|
+
clearStoredDb,
|
|
18
|
+
setStoredDb,
|
|
19
|
+
} from "../../../../persistence/db-singleton.js";
|
|
20
|
+
import { ensureMemoryV3SelectionsSchema } from "../../../../persistence/migrations/338-move-memory-v3-selections-to-memory-db.js";
|
|
16
21
|
import * as schema from "../../../../persistence/schema/index.js";
|
|
17
22
|
import { computeHotSet, type HotSetOptions } from "./hot-set.js";
|
|
18
23
|
|
|
@@ -20,12 +25,15 @@ const HALF_LIFE_MS = 1000;
|
|
|
20
25
|
const NOW = 100_000;
|
|
21
26
|
|
|
22
27
|
let sqlite: Database;
|
|
23
|
-
let db: ReturnType<typeof drizzle<typeof schema>>;
|
|
24
28
|
|
|
25
29
|
beforeEach(() => {
|
|
26
30
|
sqlite = new Database(":memory:");
|
|
27
|
-
|
|
28
|
-
|
|
31
|
+
ensureMemoryV3SelectionsSchema(sqlite);
|
|
32
|
+
setStoredDb("memory", drizzle(sqlite, { schema }), () => sqlite.close());
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
afterEach(() => {
|
|
36
|
+
clearStoredDb("memory");
|
|
29
37
|
});
|
|
30
38
|
|
|
31
39
|
let nextTurn = 0;
|
|
@@ -40,16 +48,13 @@ function seed(slug: string, createdAts: number[]): void {
|
|
|
40
48
|
}
|
|
41
49
|
|
|
42
50
|
function hotSet(overrides: Partial<HotSetOptions> = {}) {
|
|
43
|
-
return computeHotSet(
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
...overrides,
|
|
51
|
-
},
|
|
52
|
-
);
|
|
51
|
+
return computeHotSet({
|
|
52
|
+
k: 10,
|
|
53
|
+
halfLifeMs: HALF_LIFE_MS,
|
|
54
|
+
now: NOW,
|
|
55
|
+
excludeSlugs: new Set<string>(),
|
|
56
|
+
...overrides,
|
|
57
|
+
});
|
|
53
58
|
}
|
|
54
59
|
|
|
55
60
|
describe("computeHotSet", () => {
|
|
@@ -111,6 +116,19 @@ describe("computeHotSet", () => {
|
|
|
111
116
|
seed("page-a", [NOW + 50 * HALF_LIFE_MS]);
|
|
112
117
|
expect(hotSet()[0]!.score).toBeCloseTo(1, 10);
|
|
113
118
|
});
|
|
119
|
+
|
|
120
|
+
test("degrades to an empty hot set when the memory database is unavailable", () => {
|
|
121
|
+
seed("page-a", [NOW]);
|
|
122
|
+
// Simulate unavailability through the singleton slot instead of
|
|
123
|
+
// `mock.module`, which is process-global and leaks into sibling test
|
|
124
|
+
// files. The accessor extracts the raw connection from the stored
|
|
125
|
+
// handle's `$client`, so a handle without one makes `getMemorySqlite()`
|
|
126
|
+
// resolve to null, the same contract computeHotSet sees when the
|
|
127
|
+
// dedicated open fails.
|
|
128
|
+
clearStoredDb("memory");
|
|
129
|
+
setStoredDb("memory", { $client: null }, () => {});
|
|
130
|
+
expect(hotSet()).toEqual([]);
|
|
131
|
+
});
|
|
114
132
|
});
|
|
115
133
|
|
|
116
134
|
describe("MemoryV3ConfigSchema hotSet", () => {
|
|
@@ -14,17 +14,9 @@
|
|
|
14
14
|
* the hot lane never duplicates core.
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
|
-
import {
|
|
18
|
-
type DrizzleDb,
|
|
19
|
-
getSqliteFrom,
|
|
20
|
-
} from "../../../../persistence/db-connection.js";
|
|
17
|
+
import { memorySqliteOrNull } from "../memory-db.js";
|
|
21
18
|
import type { Slug } from "./types.js";
|
|
22
19
|
|
|
23
|
-
export interface HotSetDeps {
|
|
24
|
-
/** Handle to the database containing `memory_v3_selections`. */
|
|
25
|
-
db: DrizzleDb;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
20
|
export interface HotSetOptions {
|
|
29
21
|
/** Maximum number of slugs returned. */
|
|
30
22
|
k: number;
|
|
@@ -52,15 +44,21 @@ interface SelectionAgeRow {
|
|
|
52
44
|
* Deterministic for fixed inputs: ties break score desc, then slug asc. The
|
|
53
45
|
* decay sum runs in TS over `(slug, created_at)` pairs — the selections table
|
|
54
46
|
* is small enough that reading it directly beats pushing the math into SQL.
|
|
47
|
+
*
|
|
48
|
+
* Reads `memory_v3_selections` over the dedicated memory connection.
|
|
49
|
+
* Fail-soft: an unavailable memory database yields an empty hot set — the
|
|
50
|
+
* lane simply contributes nothing to the stable prefix.
|
|
55
51
|
*/
|
|
56
|
-
export function computeHotSet(
|
|
57
|
-
deps: HotSetDeps,
|
|
58
|
-
opts: HotSetOptions,
|
|
59
|
-
): HotSetEntry[] {
|
|
52
|
+
export function computeHotSet(opts: HotSetOptions): HotSetEntry[] {
|
|
60
53
|
const { k, halfLifeMs, now, excludeSlugs } = opts;
|
|
61
54
|
if (k <= 0) return [];
|
|
62
55
|
|
|
63
|
-
const
|
|
56
|
+
const raw = memorySqliteOrNull("computeHotSet");
|
|
57
|
+
if (!raw) {
|
|
58
|
+
return [];
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const rows = raw
|
|
64
62
|
.query(
|
|
65
63
|
/*sql*/ `
|
|
66
64
|
SELECT slug, created_at FROM memory_v3_selections
|
|
@@ -1,18 +1,23 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Tests for the learned-edge lane (`learned-edges.ts`).
|
|
3
3
|
*
|
|
4
|
-
* `computeLearnedEdgeGraph`
|
|
5
|
-
* each test
|
|
6
|
-
*
|
|
4
|
+
* `computeLearnedEdgeGraph` reads `memory_v3_selections` over the dedicated
|
|
5
|
+
* memory connection: each test installs an in-memory SQLite db into the
|
|
6
|
+
* `memory` singleton slot (where the accessor resolves its connection), seeds
|
|
7
|
+
* it with selection rows, and asserts the NPMI association graph. Rows
|
|
7
8
|
* sharing a `(conversation_id, created_at)` form one selector call.
|
|
8
9
|
*/
|
|
9
10
|
|
|
10
11
|
import { Database } from "bun:sqlite";
|
|
11
|
-
import { beforeEach, describe, expect, test } from "bun:test";
|
|
12
|
+
import { afterEach, beforeEach, describe, expect, test } from "bun:test";
|
|
12
13
|
|
|
13
14
|
import { drizzle } from "drizzle-orm/bun-sqlite";
|
|
14
15
|
|
|
15
|
-
import {
|
|
16
|
+
import {
|
|
17
|
+
clearStoredDb,
|
|
18
|
+
setStoredDb,
|
|
19
|
+
} from "../../../../persistence/db-singleton.js";
|
|
20
|
+
import { ensureMemoryV3SelectionsSchema } from "../../../../persistence/migrations/338-move-memory-v3-selections-to-memory-db.js";
|
|
16
21
|
import * as schema from "../../../../persistence/schema/index.js";
|
|
17
22
|
import {
|
|
18
23
|
computeLearnedEdgeGraph,
|
|
@@ -23,12 +28,15 @@ const HALF_LIFE_MS = 1_000_000;
|
|
|
23
28
|
const NOW = 100_000;
|
|
24
29
|
|
|
25
30
|
let sqlite: Database;
|
|
26
|
-
let db: ReturnType<typeof drizzle<typeof schema>>;
|
|
27
31
|
|
|
28
32
|
beforeEach(() => {
|
|
29
33
|
sqlite = new Database(":memory:");
|
|
30
|
-
|
|
31
|
-
|
|
34
|
+
ensureMemoryV3SelectionsSchema(sqlite);
|
|
35
|
+
setStoredDb("memory", drizzle(sqlite, { schema }), () => sqlite.close());
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
afterEach(() => {
|
|
39
|
+
clearStoredDb("memory");
|
|
32
40
|
});
|
|
33
41
|
|
|
34
42
|
let nextTurn = 0;
|
|
@@ -44,25 +52,22 @@ function seedCall(slugs: string[], createdAt = NOW, conv = "conv-1"): void {
|
|
|
44
52
|
}
|
|
45
53
|
|
|
46
54
|
function graphOf(overrides: Partial<LearnedEdgesOptions> = {}) {
|
|
47
|
-
return computeLearnedEdgeGraph(
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
...overrides,
|
|
64
|
-
},
|
|
65
|
-
);
|
|
55
|
+
return computeLearnedEdgeGraph({
|
|
56
|
+
halfLifeMs: HALF_LIFE_MS,
|
|
57
|
+
minCount: 2,
|
|
58
|
+
npmiFloor: 0.2,
|
|
59
|
+
maxPerPage: 6,
|
|
60
|
+
now: NOW,
|
|
61
|
+
windowMs: NOW,
|
|
62
|
+
knownSlugs: new Set([
|
|
63
|
+
"page-a",
|
|
64
|
+
"page-b",
|
|
65
|
+
"page-c",
|
|
66
|
+
"page-d",
|
|
67
|
+
"skills/widget",
|
|
68
|
+
]),
|
|
69
|
+
...overrides,
|
|
70
|
+
});
|
|
66
71
|
}
|
|
67
72
|
|
|
68
73
|
const peersOf = (
|
|
@@ -180,4 +185,19 @@ describe("computeLearnedEdgeGraph", () => {
|
|
|
180
185
|
const graph = graphOf({ windowMs: 10_000 });
|
|
181
186
|
expect(graph.adjacency.size).toBe(0);
|
|
182
187
|
});
|
|
188
|
+
|
|
189
|
+
test("degrades to an empty graph when the memory database is unavailable", () => {
|
|
190
|
+
// Simulate unavailability through the singleton slot instead of
|
|
191
|
+
// `mock.module`, which is process-global and leaks into sibling test
|
|
192
|
+
// files. The accessor extracts the raw connection from the stored
|
|
193
|
+
// handle's `$client`, so a handle without one makes `getMemorySqlite()`
|
|
194
|
+
// resolve to null, the same contract computeLearnedEdgeGraph sees when
|
|
195
|
+
// the dedicated open fails.
|
|
196
|
+
clearStoredDb("memory");
|
|
197
|
+
setStoredDb("memory", { $client: null }, () => {});
|
|
198
|
+
|
|
199
|
+
const graph = graphOf();
|
|
200
|
+
expect(graph.adjacency.size).toBe(0);
|
|
201
|
+
expect(graph.hubs.size).toBe(0);
|
|
202
|
+
});
|
|
183
203
|
});
|
|
@@ -38,18 +38,10 @@
|
|
|
38
38
|
* the persistence; nothing else is stored.
|
|
39
39
|
*/
|
|
40
40
|
|
|
41
|
-
import {
|
|
42
|
-
type DrizzleDb,
|
|
43
|
-
getSqliteFrom,
|
|
44
|
-
} from "../../../../persistence/db-connection.js";
|
|
41
|
+
import { memorySqliteOrNull } from "../memory-db.js";
|
|
45
42
|
import type { EdgeGraph } from "./edge.js";
|
|
46
43
|
import type { Slug } from "./types.js";
|
|
47
44
|
|
|
48
|
-
export interface LearnedEdgesDeps {
|
|
49
|
-
/** Handle to the database containing `memory_v3_selections`. */
|
|
50
|
-
db: DrizzleDb;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
45
|
export interface LearnedEdgesOptions {
|
|
54
46
|
/** Decay half-life in milliseconds: a selector call this old contributes
|
|
55
47
|
* half the weight of one made now. */
|
|
@@ -86,11 +78,12 @@ interface SelectionRow {
|
|
|
86
78
|
* Deterministic for fixed inputs: per-page neighbors order by NPMI desc, then
|
|
87
79
|
* peer slug asc. Symmetric by construction — an edge contributes to BOTH
|
|
88
80
|
* endpoints' neighbor lists (each independently subject to `maxPerPage`).
|
|
81
|
+
*
|
|
82
|
+
* Reads `memory_v3_selections` over the dedicated memory connection.
|
|
83
|
+
* Fail-soft: an unavailable memory database yields an empty graph — the lane
|
|
84
|
+
* simply surfaces no learned associations.
|
|
89
85
|
*/
|
|
90
|
-
export function computeLearnedEdgeGraph(
|
|
91
|
-
deps: LearnedEdgesDeps,
|
|
92
|
-
opts: LearnedEdgesOptions,
|
|
93
|
-
): EdgeGraph {
|
|
86
|
+
export function computeLearnedEdgeGraph(opts: LearnedEdgesOptions): EdgeGraph {
|
|
94
87
|
const { halfLifeMs, minCount, npmiFloor, maxPerPage, now, windowMs } = opts;
|
|
95
88
|
const empty: EdgeGraph = {
|
|
96
89
|
adjacency: new Map(),
|
|
@@ -99,7 +92,12 @@ export function computeLearnedEdgeGraph(
|
|
|
99
92
|
};
|
|
100
93
|
if (maxPerPage <= 0) return empty;
|
|
101
94
|
|
|
102
|
-
const
|
|
95
|
+
const raw = memorySqliteOrNull("computeLearnedEdgeGraph");
|
|
96
|
+
if (!raw) {
|
|
97
|
+
return empty;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const rows = raw
|
|
103
101
|
.query(
|
|
104
102
|
/*sql*/ `
|
|
105
103
|
SELECT conversation_id, slug, created_at FROM memory_v3_selections
|
|
@@ -89,9 +89,18 @@ const log = getLogger("memory-v3-injection-gate");
|
|
|
89
89
|
export const MEMORY_V3_INJECTION_GATE_CHECK_NAME = "memory_v3_injection_gate";
|
|
90
90
|
|
|
91
91
|
/** Record one injection-gate run to usage telemetry. `detail` keys are the
|
|
92
|
-
* platform-side contract (snake_case
|
|
93
|
-
*
|
|
94
|
-
*
|
|
92
|
+
* platform-side contract (snake_case; scores, reason codes, and corpus-size
|
|
93
|
+
* counts only — never page titles, slugs, or conversation content). Never
|
|
94
|
+
* throws: the gate is pass-open by design, and a telemetry failure must not
|
|
95
|
+
* cost the turn its memory either.
|
|
96
|
+
*
|
|
97
|
+
* `detail.scored` says whether checkV3Gate actually weighed scores this run, as
|
|
98
|
+
* opposed to the run taking a pass-open shortcut (dense off, dense unavailable,
|
|
99
|
+
* gate threw). Pass rate is only meaningful over scored runs — the shortcuts are
|
|
100
|
+
* all unconditional passes and would otherwise inflate it. It is derivable from
|
|
101
|
+
* `reason`, but the daemon owns the reason set and adds to it freely, so it is
|
|
102
|
+
* reported explicitly rather than left for the platform to re-derive from a
|
|
103
|
+
* hardcoded list that would silently miscount the next reason we add. */
|
|
95
104
|
function recordGateRun(detail: Record<string, unknown>): void {
|
|
96
105
|
try {
|
|
97
106
|
recordWatchdogEvent({
|
|
@@ -105,6 +114,35 @@ function recordGateRun(detail: Record<string, unknown>): void {
|
|
|
105
114
|
}
|
|
106
115
|
}
|
|
107
116
|
|
|
117
|
+
/** `check_name` of the `watchdog` telemetry event recorded once per orchestrated
|
|
118
|
+
* turn, carrying what the SELECTOR did. Keep this string stable — the platform's
|
|
119
|
+
* memory admin analytics filter on it. */
|
|
120
|
+
export const MEMORY_V3_SELECTION_CHECK_NAME = "memory_v3_selection";
|
|
121
|
+
|
|
122
|
+
/** Record one turn's selection outcome. Deliberately a SEPARATE event from
|
|
123
|
+
* {@link MEMORY_V3_INJECTION_GATE_CHECK_NAME} rather than extra keys on it: the
|
|
124
|
+
* gate records at decision time, before selection has run, so folding the
|
|
125
|
+
* outcome in would mean deferring the gate event until after `selectPool` — and
|
|
126
|
+
* losing every gate run whenever selection throws, which is exactly the
|
|
127
|
+
* incident we would want the gate telemetry for. This event carries
|
|
128
|
+
* `gate_reason` so the two still group together without a join.
|
|
129
|
+
*
|
|
130
|
+
* `value` is the selection count. Never throws, same as the gate counter. */
|
|
131
|
+
function recordSelectionRun(
|
|
132
|
+
value: number,
|
|
133
|
+
detail: Record<string, unknown>,
|
|
134
|
+
): void {
|
|
135
|
+
try {
|
|
136
|
+
recordWatchdogEvent({
|
|
137
|
+
checkName: MEMORY_V3_SELECTION_CHECK_NAME,
|
|
138
|
+
value,
|
|
139
|
+
detail,
|
|
140
|
+
});
|
|
141
|
+
} catch {
|
|
142
|
+
// Same contract as recordGateRun: telemetry must never cost a turn.
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
108
146
|
/** Default number of BM25 needle articles to fold into the pool. */
|
|
109
147
|
export const DEFAULT_NEEDLE_K = 12;
|
|
110
148
|
/** Default number of dense-lane articles to fold into the pool. */
|
|
@@ -182,6 +220,12 @@ export interface OrchestrateDeps {
|
|
|
182
220
|
* `enabled`, assembled in observeTurn). Omitted/disabled → the gate never
|
|
183
221
|
* runs and every turn proceeds to selectPool as before. */
|
|
184
222
|
gateConfig?: V3GateConfig;
|
|
223
|
+
/** Real concept-page count at lane build — the same corpus-size signal
|
|
224
|
+
* `resolveV3Tuning` switches the lean/full profile on. Reported with each
|
|
225
|
+
* gate run so the reason distribution can be read against
|
|
226
|
+
* `MEMORY_V3_FULL_PROFILE_MIN_PAGES`; omitted drops the field from the
|
|
227
|
+
* telemetry detail (the gate itself never reads it). */
|
|
228
|
+
realConceptPageCount?: number;
|
|
185
229
|
}
|
|
186
230
|
|
|
187
231
|
/** A finder-lane candidate: the slug, the descriptor that justified it, and
|
|
@@ -430,29 +474,90 @@ export async function orchestrate(
|
|
|
430
474
|
//
|
|
431
475
|
// The gate is dense-gated: it only runs when the live dense lane produced hits
|
|
432
476
|
// (`liveDensed.length > 0`). In healthy operation dense returns top-k hits for
|
|
433
|
-
// any query, so zero live dense hits means dense is unavailable
|
|
434
|
-
//
|
|
435
|
-
//
|
|
436
|
-
//
|
|
437
|
-
//
|
|
438
|
-
//
|
|
439
|
-
//
|
|
477
|
+
// any query, so zero live dense hits means dense is unavailable, NOT low
|
|
478
|
+
// relevance. checkV3Gate reads only finder scores and cannot tell those apart,
|
|
479
|
+
// so without live dense signal we pass open rather than suppress all memory
|
|
480
|
+
// (not even the core/hot/fresh prefix) on every lexically-weak turn.
|
|
481
|
+
//
|
|
482
|
+
// Two very different situations reach that pass-open, and they carry distinct
|
|
483
|
+
// reason codes because only one of them is a problem:
|
|
484
|
+
// - dense_disabled: `denseK === 0`, so the lane never ran. The lean
|
|
485
|
+
// new-user profile (`MEMORY_V3_NEW_USER_TUNING`) sets it
|
|
486
|
+
// for sub-threshold corpora, and that same profile sets
|
|
487
|
+
// `selectorEnabled: false` — so there is no selectPool
|
|
488
|
+
// call for the gate to save and passing open is free.
|
|
489
|
+
// Expected, benign, and the bulk of gate runs.
|
|
490
|
+
// - dense_unavailable: the lane ran and yielded nothing live — a degraded
|
|
491
|
+
// embedding backend, a Qdrant error (`denseLaneScored`
|
|
492
|
+
// swallows these to `[]`), or only stale deleted-page
|
|
493
|
+
// points. Dense SHOULD have scored this turn and did
|
|
494
|
+
// not, so this one is worth alerting on.
|
|
495
|
+
//
|
|
496
|
+
// Every run carries the corpus size, so the reason mix can be read against the
|
|
497
|
+
// profile threshold rather than guessed at: `dense_disabled` is sub-threshold
|
|
498
|
+
// by construction, and whether those assistants sit at 1 page or 9 is the
|
|
499
|
+
// difference between "empty" and "about to cross".
|
|
500
|
+
const recordGate = (detail: Record<string, unknown>): void =>
|
|
501
|
+
recordGateRun(
|
|
502
|
+
deps.realConceptPageCount === undefined
|
|
503
|
+
? detail
|
|
504
|
+
: { ...detail, real_concept_page_count: deps.realConceptPageCount },
|
|
505
|
+
);
|
|
506
|
+
|
|
507
|
+
// This turn's gate decision, carried to the selection event so the two group
|
|
508
|
+
// together without a join. Null when the gate never ran (disabled/omitted).
|
|
509
|
+
let gateOutcome: { reason: string; pass: boolean } | null = null;
|
|
510
|
+
|
|
511
|
+
// What the SELECTOR did with the pool the gate let through — the outcome the
|
|
512
|
+
// gate's own pass rate cannot see. A passed gate only means selectPool got to
|
|
513
|
+
// run; the selector is told to return `[]` when no candidate is relevant, so
|
|
514
|
+
// it is the real injection decision and the pass rate is an upper bound on it.
|
|
515
|
+
//
|
|
516
|
+
// `selector_ran` marks the turns where the selector actually JUDGED the pool,
|
|
517
|
+
// and is the filter that keeps a relevance rate honest. Three ways a turn can
|
|
518
|
+
// report zero selections without the selector having been asked, all of which
|
|
519
|
+
// must stay out of that rate:
|
|
520
|
+
// - the lean profile sets `selectorEnabled: false`, so
|
|
521
|
+
// `selectAllPoolCandidates` returns the whole pool untouched (would read
|
|
522
|
+
// as a 100% hit rate),
|
|
523
|
+
// - a closed gate hard-skips selection entirely (a 0%),
|
|
524
|
+
// - the pool is empty, and `selectPool` returns `[]` before it ever reaches
|
|
525
|
+
// the provider (also a 0%).
|
|
526
|
+
// The last is why `poolSize` decides this rather than each call site: an empty
|
|
527
|
+
// pool is not a judgment that nothing was relevant, and no caller has to
|
|
528
|
+
// remember that.
|
|
529
|
+
const recordSelection = (
|
|
530
|
+
selections: SelectedPage[],
|
|
531
|
+
poolSize: number,
|
|
532
|
+
): void => {
|
|
533
|
+
const detail: Record<string, unknown> = {
|
|
534
|
+
gate_reason: gateOutcome?.reason ?? null,
|
|
535
|
+
gate_pass: gateOutcome?.pass ?? null,
|
|
536
|
+
selector_ran: deps.selectorEnabled !== false && poolSize > 0,
|
|
537
|
+
selected_count: selections.length,
|
|
538
|
+
pool_size: poolSize,
|
|
539
|
+
};
|
|
540
|
+
if (deps.realConceptPageCount !== undefined) {
|
|
541
|
+
detail.real_concept_page_count = deps.realConceptPageCount;
|
|
542
|
+
}
|
|
543
|
+
recordSelectionRun(selections.length, detail);
|
|
544
|
+
};
|
|
440
545
|
if (deps.gateConfig?.enabled) {
|
|
441
546
|
if (liveDensed.length === 0) {
|
|
442
|
-
|
|
443
|
-
// to [] / only stale deleted-page points returned). That means dense is
|
|
444
|
-
// unavailable, not low relevance, so pass open — but record it so rollout
|
|
445
|
-
// telemetry sees these turns instead of silently skipping the gate.
|
|
547
|
+
const reason = denseEnabled ? "dense_unavailable" : "dense_disabled";
|
|
446
548
|
log.info(
|
|
447
549
|
{
|
|
448
550
|
conversationId: turn.conversationId,
|
|
449
551
|
turnNumber: turn.turnNumber,
|
|
450
|
-
reason
|
|
552
|
+
reason,
|
|
451
553
|
pass: true,
|
|
452
554
|
},
|
|
453
|
-
|
|
555
|
+
denseEnabled
|
|
556
|
+
? "memory-v3 injection gate: dense lane unavailable, passing open"
|
|
557
|
+
: "memory-v3 injection gate: dense lane disabled, passing open",
|
|
454
558
|
);
|
|
455
|
-
|
|
559
|
+
gateOutcome = { reason, pass: true };
|
|
560
|
+
recordGate({ pass: true, reason, scored: false });
|
|
456
561
|
} else {
|
|
457
562
|
let gate: V3GateResult | null = null;
|
|
458
563
|
try {
|
|
@@ -469,7 +574,8 @@ export async function orchestrate(
|
|
|
469
574
|
},
|
|
470
575
|
"memory-v3 injection gate threw; passing open (proceeding with selection)",
|
|
471
576
|
);
|
|
472
|
-
|
|
577
|
+
gateOutcome = { reason: "gate_error", pass: true };
|
|
578
|
+
recordGate({ pass: true, reason: "gate_error", scored: false });
|
|
473
579
|
}
|
|
474
580
|
if (gate) {
|
|
475
581
|
log.info(
|
|
@@ -480,9 +586,11 @@ export async function orchestrate(
|
|
|
480
586
|
},
|
|
481
587
|
"memory-v3 injection gate decision",
|
|
482
588
|
);
|
|
483
|
-
|
|
589
|
+
gateOutcome = { reason: gate.reason, pass: gate.pass };
|
|
590
|
+
recordGate({
|
|
484
591
|
pass: gate.pass,
|
|
485
592
|
reason: gate.reason,
|
|
593
|
+
scored: true,
|
|
486
594
|
top_dense_score: gate.topDenseScore,
|
|
487
595
|
top_norm_sparse_score: gate.topNormSparseScore,
|
|
488
596
|
checked_articles: gate.checkedArticles,
|
|
@@ -505,10 +613,18 @@ export async function orchestrate(
|
|
|
505
613
|
// explicitly configured with `selectorEnabled: false` AND
|
|
506
614
|
// `denseK > 0` (the dense-gated gate only runs with dense hits; the
|
|
507
615
|
// new-user profile sets `denseK: 0`, so the gate never runs for it).
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
616
|
+
const stableOnly = buildStable();
|
|
617
|
+
const bypassed = await runSelection({
|
|
618
|
+
stable: stableOnly,
|
|
619
|
+
finder: [],
|
|
620
|
+
});
|
|
621
|
+
recordSelection(bypassed, stableOnly.length);
|
|
622
|
+
return closed(bypassed);
|
|
511
623
|
}
|
|
624
|
+
// Hard skip: the selector is never consulted, so this is a zero
|
|
625
|
+
// selection BY CONSTRUCTION, not a judgment that nothing was relevant.
|
|
626
|
+
// `selector_ran: false` keeps it out of any relevance rate.
|
|
627
|
+
recordSelection([], 0);
|
|
512
628
|
return closed([]);
|
|
513
629
|
}
|
|
514
630
|
}
|
|
@@ -602,6 +718,7 @@ export async function orchestrate(
|
|
|
602
718
|
// scaffold; `undefined` falls through to the bundled default.
|
|
603
719
|
const pool = { stable, finder: finderTail };
|
|
604
720
|
const selections = await runSelection(pool);
|
|
721
|
+
recordSelection(selections, stable.length + finderTail.length);
|
|
605
722
|
|
|
606
723
|
return {
|
|
607
724
|
selections,
|
|
@@ -31,8 +31,8 @@ import { afterAll, beforeEach, describe, expect, mock, test } from "bun:test";
|
|
|
31
31
|
import type { Message } from "@vellumai/plugin-api";
|
|
32
32
|
import { drizzle } from "drizzle-orm/bun-sqlite";
|
|
33
33
|
|
|
34
|
-
import { migrateAddMemoryV3Selections } from "../../../../persistence/migrations/268-add-memory-v3-selections.js";
|
|
35
34
|
import { migrateAddMemoryV3EverInjected } from "../../../../persistence/migrations/277-add-memory-v3-ever-injected.js";
|
|
35
|
+
import { ensureMemoryV3SelectionsSchema } from "../../../../persistence/migrations/338-move-memory-v3-selections-to-memory-db.js";
|
|
36
36
|
import * as schema from "../../../../persistence/schema/index.js";
|
|
37
37
|
import { wrapMemoryBlock } from "../memory-marker.js";
|
|
38
38
|
|
|
@@ -48,12 +48,15 @@ let pruneConfig: {
|
|
|
48
48
|
} | null = null;
|
|
49
49
|
|
|
50
50
|
let testSqlite: Database;
|
|
51
|
+
// `planPrune`'s recency ranking reads `memory_v3_selections` over the
|
|
52
|
+
// dedicated memory connection, resolved via `getMemorySqlite` — stubbed to a
|
|
53
|
+
// second in-memory DB carrying the relocated table's schema.
|
|
54
|
+
let memorySqlite: Database;
|
|
51
55
|
let testDb = makeDb();
|
|
52
56
|
function makeDb() {
|
|
53
57
|
testSqlite = new Database(":memory:");
|
|
54
58
|
const db = drizzle(testSqlite, { schema });
|
|
55
59
|
migrateAddMemoryV3EverInjected(db);
|
|
56
|
-
migrateAddMemoryV3Selections(db);
|
|
57
60
|
// Minimal `messages` shape — `collectPersistedV3Cards` reads only
|
|
58
61
|
// `conversation_id` and `metadata`.
|
|
59
62
|
testSqlite.run(/*sql*/ `
|
|
@@ -66,6 +69,8 @@ function makeDb() {
|
|
|
66
69
|
created_at INTEGER NOT NULL
|
|
67
70
|
)
|
|
68
71
|
`);
|
|
72
|
+
memorySqlite = new Database(":memory:");
|
|
73
|
+
ensureMemoryV3SelectionsSchema(memorySqlite);
|
|
69
74
|
return db;
|
|
70
75
|
}
|
|
71
76
|
|
|
@@ -76,6 +81,8 @@ mock.module("../../../../persistence/db-connection.js", () => ({
|
|
|
76
81
|
pruneMockActive
|
|
77
82
|
? testSqlite
|
|
78
83
|
: realDb.getSqliteFrom(db as Parameters<typeof realDb.getSqliteFrom>[0]),
|
|
84
|
+
getMemorySqlite: () =>
|
|
85
|
+
pruneMockActive ? memorySqlite : realDb.getMemorySqlite(),
|
|
79
86
|
}));
|
|
80
87
|
|
|
81
88
|
// Memory code resolves its config through the plugin's own accessor
|
|
@@ -120,7 +127,7 @@ function insertSelection(
|
|
|
120
127
|
slug: string,
|
|
121
128
|
createdAt: number,
|
|
122
129
|
): void {
|
|
123
|
-
|
|
130
|
+
memorySqlite
|
|
124
131
|
.query(
|
|
125
132
|
/*sql*/ `
|
|
126
133
|
INSERT OR REPLACE INTO memory_v3_selections
|
|
@@ -63,6 +63,7 @@ import type { ContentBlock, Message } from "@vellumai/plugin-api";
|
|
|
63
63
|
import { getDb, getSqliteFrom } from "../../../../persistence/db-connection.js";
|
|
64
64
|
import { getMemoryConfig } from "../config.js";
|
|
65
65
|
import { getLogger } from "../logging.js";
|
|
66
|
+
import { memorySqliteOrNull } from "../memory-db.js";
|
|
66
67
|
import { unwrapMemoryBlock, wrapMemoryBlock } from "../memory-marker.js";
|
|
67
68
|
import {
|
|
68
69
|
INJECTED_CONCEPT_HEADER_REGEX,
|
|
@@ -210,12 +211,14 @@ export interface PruneDeps {
|
|
|
210
211
|
*
|
|
211
212
|
* The footprint and the candidates both range over the ACTIVE injected slugs.
|
|
212
213
|
* Candidates are ranked by last selection recency — `MAX(created_at)` per
|
|
213
|
-
* slug from `memory_v3_selections
|
|
214
|
-
*
|
|
215
|
-
*
|
|
216
|
-
*
|
|
217
|
-
*
|
|
218
|
-
*
|
|
214
|
+
* slug from `memory_v3_selections` (read over the dedicated memory
|
|
215
|
+
* connection; an unavailable memory database reads as no selection rows),
|
|
216
|
+
* falling back to the store's `injected_at` for slugs with no selection rows
|
|
217
|
+
* (e.g. rows copied by a full fork) — taken oldest-first until the footprint
|
|
218
|
+
* is at `targetResidentBytes`. Core/hot lane members are exempt, and
|
|
219
|
+
* zero-byte rows (capability slugs, truncated-fork seeds: dedup-only, no byte
|
|
220
|
+
* accounting) are skipped — pruning them frees nothing while discarding
|
|
221
|
+
* inherited context.
|
|
219
222
|
*/
|
|
220
223
|
export function planPrune(
|
|
221
224
|
deps: PruneDeps,
|
|
@@ -225,15 +228,18 @@ export function planPrune(
|
|
|
225
228
|
const resident = activeEntries.reduce((sum, e) => sum + e.bytes, 0);
|
|
226
229
|
if (resident <= deps.maxResidentBytes) return null;
|
|
227
230
|
|
|
228
|
-
const
|
|
229
|
-
|
|
230
|
-
|
|
231
|
+
const memoryRaw = memorySqliteOrNull("planPrune");
|
|
232
|
+
const selectionRows = memoryRaw
|
|
233
|
+
? (memoryRaw
|
|
234
|
+
.query(
|
|
235
|
+
/*sql*/ `
|
|
231
236
|
SELECT slug, MAX(created_at) AS lastSelectedAt FROM memory_v3_selections
|
|
232
237
|
WHERE conversation_id = ?
|
|
233
238
|
GROUP BY slug
|
|
234
239
|
`,
|
|
235
|
-
|
|
236
|
-
|
|
240
|
+
)
|
|
241
|
+
.all(conversationId) as Array<{ slug: string; lastSelectedAt: number }>)
|
|
242
|
+
: [];
|
|
237
243
|
const lastSelectedAt = new Map(
|
|
238
244
|
selectionRows.map((row) => [row.slug, row.lastSelectedAt]),
|
|
239
245
|
);
|