@vellumai/assistant 0.6.5 → 0.6.6
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 +9 -1
- package/ARCHITECTURE.md +15 -17
- package/Dockerfile +6 -4
- package/__tests__/permissions/gateway-threshold-reader.test.ts +283 -0
- package/docs/architecture/integrations.md +32 -39
- package/docs/architecture/memory.md +25 -30
- package/docs/architecture/security.md +7 -6
- package/docs/browser-use-architecture-phase2.md +63 -20
- package/docs/plugins.md +761 -0
- package/examples/plugins/echo/README.md +132 -0
- package/examples/plugins/echo/package.json +17 -0
- package/examples/plugins/echo/register.ts +187 -0
- package/node_modules/@vellumai/egress-proxy/src/types.ts +19 -0
- package/openapi.yaml +212 -68
- package/package.json +1 -1
- package/src/__tests__/app-compiler.test.ts +57 -0
- package/src/__tests__/approval-cascade.test.ts +7 -2
- package/src/__tests__/auto-analysis-end-to-end.test.ts +1 -0
- package/src/__tests__/avatar-generator.test.ts +4 -2
- package/src/__tests__/bundled-asset.test.ts +6 -6
- package/src/__tests__/catalog-cache.test.ts +69 -0
- package/src/__tests__/checker.test.ts +459 -171
- package/src/__tests__/circuit-breaker-pipeline.test.ts +406 -0
- package/src/__tests__/compaction-events.test.ts +501 -0
- package/src/__tests__/compaction-pipeline.test.ts +210 -0
- package/src/__tests__/compaction-strip-metadata-clear.test.ts +181 -0
- package/src/__tests__/compaction-timeout-recovery.test.ts +262 -0
- package/src/__tests__/config-model-image-provider.test.ts +110 -0
- package/src/__tests__/config-schema.test.ts +22 -9
- package/src/__tests__/config-watcher-cleanup-throttle.test.ts +0 -4
- package/src/__tests__/contacts-tools.test.ts +26 -0
- package/src/__tests__/context-overflow-policy.test.ts +7 -7
- package/src/__tests__/context-window-manager.test.ts +355 -4
- package/src/__tests__/conversation-abort-tool-results.test.ts +4 -1
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +26 -30
- package/src/__tests__/conversation-agent-loop.test.ts +30 -141
- package/src/__tests__/conversation-confirmation-signals.test.ts +6 -1
- package/src/__tests__/conversation-history-web-search.test.ts +1 -0
- package/src/__tests__/conversation-init.benchmark.test.ts +2 -16
- package/src/__tests__/conversation-pairing.test.ts +174 -10
- package/src/__tests__/conversation-pre-run-repair.test.ts +4 -1
- package/src/__tests__/conversation-process-callsite.test.ts +3 -0
- package/src/__tests__/conversation-provider-retry-repair.test.ts +16 -7
- package/src/__tests__/conversation-queue.test.ts +29 -14
- package/src/__tests__/conversation-routes-disk-view.test.ts +7 -6
- package/src/__tests__/conversation-runtime-assembly.test.ts +155 -110
- package/src/__tests__/conversation-runtime-workspace.test.ts +23 -38
- package/src/__tests__/conversation-seed-composer.test.ts +2 -2
- package/src/__tests__/conversation-slash-queue.test.ts +7 -2
- package/src/__tests__/conversation-slash-unknown.test.ts +25 -2
- package/src/__tests__/conversation-speed-override.test.ts +6 -1
- package/src/__tests__/conversation-title-service.test.ts +116 -0
- package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +41 -2
- package/src/__tests__/conversation-usage.test.ts +1 -1
- package/src/__tests__/conversation-workspace-cache-state.test.ts +4 -1
- package/src/__tests__/conversation-workspace-injection.test.ts +3 -0
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +4 -1
- package/src/__tests__/credential-health-service.test.ts +78 -9
- package/src/__tests__/credential-security-invariants.test.ts +2 -2
- package/src/__tests__/db-schedule-syntax-migration.test.ts +1 -0
- package/src/__tests__/empty-response-pipeline.test.ts +305 -0
- package/src/__tests__/extension-id-sync-guard.test.ts +3 -3
- package/src/__tests__/first-greeting.test.ts +247 -5
- package/src/__tests__/headless-browser-mode.test.ts +57 -0
- package/src/__tests__/history-repair-pipeline.test.ts +399 -0
- package/src/__tests__/host-browser-e2e-cloud.test.ts +307 -0
- package/src/__tests__/host-browser-e2e-self-hosted.test.ts +3 -3
- package/src/__tests__/host-proxy-interface.test.ts +36 -2
- package/src/__tests__/image-credentials.test.ts +137 -0
- package/src/__tests__/image-service-dispatcher.test.ts +186 -0
- package/src/__tests__/injector-chain.test.ts +526 -0
- package/src/__tests__/intent-routing.test.ts +0 -26
- package/src/__tests__/llm-call-pipeline.test.ts +285 -0
- package/src/__tests__/llm-schema.test.ts +1 -1
- package/src/__tests__/media-generate-image.test.ts +119 -13
- package/src/__tests__/memory-retrieval-pipeline.test.ts +401 -0
- package/src/__tests__/memory-upsert-concurrency.test.ts +1 -0
- package/src/__tests__/migration-import-from-url.test.ts +5 -68
- package/src/__tests__/model-intents.test.ts +4 -2
- package/src/__tests__/notification-broadcaster.test.ts +3 -3
- package/src/__tests__/notification-decision-strategy.test.ts +0 -11
- package/src/__tests__/notification-schedule-notify-dedup.test.ts +108 -0
- package/src/__tests__/oauth-apps-routes.test.ts +1 -1
- package/src/__tests__/oauth-cli.test.ts +14 -12
- package/src/__tests__/oauth-connect-orchestrator.test.ts +4 -13
- package/src/__tests__/oauth-provider-serializer.test.ts +6 -4
- package/src/__tests__/oauth-provider-visibility.test.ts +3 -5
- package/src/__tests__/oauth-providers-routes.test.ts +3 -2
- package/src/__tests__/oauth-store.test.ts +41 -76
- package/src/__tests__/onboarding-template-contract.test.ts +16 -64
- package/src/__tests__/openai-image-service.test.ts +368 -0
- package/src/__tests__/overflow-reduce-pipeline.test.ts +676 -0
- package/src/__tests__/permission-checker-host-gate.test.ts +0 -24
- package/src/__tests__/persist-onboarding-artifacts.test.ts +266 -0
- package/src/__tests__/persistence-pipeline.test.ts +377 -0
- package/src/__tests__/pipeline-runner.test.ts +565 -0
- package/src/__tests__/platform.test.ts +5 -2
- package/src/__tests__/plugin-bootstrap.test.ts +483 -0
- package/src/__tests__/plugin-registry.test.ts +273 -0
- package/src/__tests__/plugin-route-contribution.test.ts +288 -0
- package/src/__tests__/plugin-skill-contribution.test.ts +367 -0
- package/src/__tests__/plugin-tool-contribution.test.ts +286 -0
- package/src/__tests__/plugin-types.test.ts +320 -0
- package/src/__tests__/pricing.test.ts +44 -12
- package/src/__tests__/proxy-approval-callback.test.ts +69 -8
- package/src/__tests__/reaction-persistence.test.ts +1 -0
- package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +1 -0
- package/src/__tests__/registry.test.ts +0 -2
- package/src/__tests__/schedule-routes.test.ts +131 -1
- package/src/__tests__/scheduler-recurrence.test.ts +14 -70
- package/src/__tests__/scheduler-reuse-conversation.test.ts +10 -50
- package/src/__tests__/secret-detection-handler.test.ts +0 -10
- package/src/__tests__/shell-identity.test.ts +0 -134
- package/src/__tests__/suggestion-routes.test.ts +103 -4
- package/src/__tests__/task-memory-cleanup.test.ts +1 -0
- package/src/__tests__/task-scheduler.test.ts +3 -15
- package/src/__tests__/test-preload.ts +11 -0
- package/src/__tests__/title-generate-pipeline.test.ts +224 -0
- package/src/__tests__/token-estimate-pipeline.test.ts +431 -0
- package/src/__tests__/tool-error-pipeline.test.ts +244 -0
- package/src/__tests__/tool-execute-pipeline.test.ts +431 -0
- package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +0 -6
- package/src/__tests__/tool-executor-shell-integration.test.ts +7 -10
- package/src/__tests__/tool-executor.test.ts +141 -0
- package/src/__tests__/tool-result-truncate-pipeline.test.ts +356 -0
- package/src/__tests__/tool-result-truncation.test.ts +0 -110
- package/src/__tests__/user-plugin-loader.test.ts +191 -0
- package/src/__tests__/workspace-migration-046-seed-conversation-starters-callsite.test.ts +185 -0
- package/src/__tests__/workspace-migration-049-release-notes-default-sonnet.test.ts +100 -0
- package/src/__tests__/workspace-migration-050-seed-main-agent-opus-callsite.test.ts +171 -0
- package/src/__tests__/workspace-migration-051-seed-conversation-summarization-callsite.test.ts +252 -0
- package/src/__tests__/workspace-migration-remove-hooks.test.ts +99 -0
- package/src/__tests__/workspace-policy.test.ts +21 -3
- package/src/agent/loop.ts +340 -102
- package/src/approvals/__tests__/guardian-feed-event.test.ts +304 -0
- package/src/approvals/guardian-request-resolvers.ts +80 -0
- package/src/backup/__tests__/backup-worker.test.ts +2 -13
- package/src/backup/backup-worker.ts +3 -15
- package/src/bundler/app-compiler.ts +84 -1
- package/src/calls/call-state.ts +2 -2
- package/src/channels/__tests__/types.test.ts +3 -3
- package/src/channels/types.ts +6 -4
- package/src/cli/__tests__/notifications.test.ts +87 -211
- package/src/cli/commands/__tests__/backup.test.ts +1 -1
- package/src/cli/commands/__tests__/image-generation.test.ts +255 -35
- package/src/cli/commands/__tests__/inference-send.test.ts +12 -0
- package/src/cli/commands/__tests__/tts-synthesize.test.ts +12 -0
- package/src/cli/commands/backup.ts +2 -2
- package/src/cli/commands/clients.ts +138 -0
- package/src/cli/commands/completions.ts +2 -9
- package/src/cli/commands/conversations.ts +55 -7
- package/src/cli/commands/image-generation.ts +33 -34
- package/src/cli/commands/notifications.ts +68 -103
- package/src/cli/commands/oauth/__tests__/providers-register.test.ts +1 -1
- package/src/cli/commands/oauth/__tests__/providers-update.test.ts +1 -1
- package/src/cli/commands/oauth/connect.ts +2 -2
- package/src/cli/commands/oauth/providers.ts +176 -8
- package/src/cli/commands/oauth/status.ts +46 -36
- package/src/cli/commands/skills.ts +3 -4
- package/src/cli/program.ts +25 -29
- package/src/config/__tests__/backup-schema.test.ts +7 -2
- package/src/config/bundled-skills/app-builder/SKILL.md +2 -2
- package/src/config/bundled-skills/app-builder/references/WIDGETS.md +10 -10
- package/src/config/bundled-skills/contacts/tools/contact-merge.ts +66 -87
- package/src/config/bundled-skills/contacts/tools/contact-search.ts +28 -51
- package/src/config/bundled-skills/contacts/tools/contact-upsert.ts +22 -40
- package/src/config/bundled-skills/image-studio/SKILL.md +2 -1
- package/src/config/bundled-skills/image-studio/TOOLS.json +2 -1
- package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +23 -39
- package/src/config/bundled-skills/messaging/SKILL.md +3 -3
- package/src/config/bundled-skills/messaging/tools/__tests__/messaging-feed-events.test.ts +207 -0
- package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +12 -0
- package/src/config/bundled-skills/messaging/tools/messaging-send.ts +58 -0
- package/src/config/bundled-skills/schedule/SKILL.md +8 -3
- package/src/config/bundled-skills/schedule/TOOLS.json +15 -7
- package/src/config/bundled-skills/schedule/references/SCRIPT_MODE_PATTERNS.md +59 -0
- package/src/config/bundled-tool-registry.ts +0 -15
- package/src/config/feature-flag-registry.json +17 -1
- package/src/config/schema.ts +19 -0
- package/src/config/schemas/backup.ts +1 -1
- package/src/config/schemas/conversations.ts +16 -0
- package/src/config/schemas/llm.ts +2 -3
- package/src/config/schemas/security.ts +6 -6
- package/src/config/schemas/tts.ts +11 -0
- package/src/config/skill-state.ts +6 -2
- package/src/config/skills.ts +94 -5
- package/src/context/__tests__/compact-prompt.test.ts +27 -9
- package/src/context/prompts/compact.md +26 -12
- package/src/context/tool-result-truncation.ts +3 -63
- package/src/context/window-manager.ts +190 -16
- package/src/credential-health/credential-health-service.ts +19 -6
- package/src/daemon/__tests__/conversation-feed-event.test.ts +317 -0
- package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +4 -12
- package/src/daemon/__tests__/conversation-tool-setup.test.ts +14 -15
- package/src/daemon/config-watcher.ts +0 -2
- package/src/daemon/context-overflow-policy.ts +4 -13
- package/src/daemon/conversation-agent-loop-handlers.ts +83 -22
- package/src/daemon/conversation-agent-loop.ts +984 -683
- package/src/daemon/conversation-history.ts +10 -19
- package/src/daemon/conversation-lifecycle.ts +37 -19
- package/src/daemon/conversation-notifiers.ts +2 -110
- package/src/daemon/conversation-process.ts +14 -7
- package/src/daemon/conversation-runtime-assembly.ts +532 -411
- package/src/daemon/conversation-tool-setup.ts +41 -4
- package/src/daemon/conversation.ts +80 -35
- package/src/daemon/external-plugins-bootstrap.ts +478 -0
- package/src/daemon/first-greeting.ts +191 -14
- package/src/daemon/handlers/config-model.ts +11 -0
- package/src/daemon/handlers/skills.ts +5 -1
- package/src/daemon/lifecycle.ts +33 -68
- package/src/daemon/message-types/computer-use.ts +2 -34
- package/src/daemon/message-types/conversations.ts +49 -0
- package/src/daemon/message-types/messages.ts +12 -0
- package/src/daemon/server.ts +5 -3
- package/src/daemon/shutdown-handlers.ts +2 -12
- package/src/daemon/tool-side-effects.ts +14 -56
- package/src/heartbeat/__tests__/heartbeat-feed-event.test.ts +160 -0
- package/src/heartbeat/heartbeat-service.ts +24 -1
- package/src/home/__tests__/feed-population-integration.test.ts +312 -0
- package/src/home/emit-feed-event.ts +7 -0
- package/src/home/feed-types.ts +41 -2
- package/src/home/rewrite-command-preview.ts +66 -0
- package/src/ipc/__tests__/socket-path.test.ts +11 -50
- package/src/ipc/cli-client.ts +1 -1
- package/src/ipc/cli-server.ts +3 -3
- package/src/ipc/gateway-client.ts +4 -1
- package/src/ipc/routes/browser-context.ts +2 -0
- package/src/ipc/routes/browser.ts +1 -0
- package/src/ipc/routes/get-contact.ts +16 -0
- package/src/ipc/routes/index.ts +14 -0
- package/src/ipc/routes/list-clients.ts +31 -0
- package/src/ipc/routes/merge-contacts.ts +17 -0
- package/src/ipc/routes/notification.ts +133 -0
- package/src/ipc/routes/rename-conversation.ts +59 -0
- package/src/ipc/routes/search-contacts.ts +19 -0
- package/src/ipc/routes/upsert-contact.ts +25 -0
- package/src/ipc/socket-path.ts +14 -38
- package/src/media/app-icon-generator.ts +23 -46
- package/src/media/avatar-router.ts +26 -41
- package/src/media/gemini-image-service.ts +8 -41
- package/src/media/image-credentials.ts +73 -0
- package/src/media/image-service.ts +85 -0
- package/src/media/openai-image-service.ts +131 -0
- package/src/media/types.ts +46 -0
- package/src/memory/conversation-crud.ts +48 -18
- package/src/memory/conversation-queries.ts +57 -4
- package/src/memory/conversation-title-service.ts +25 -0
- package/src/memory/db-init.ts +8 -0
- package/src/memory/embedding-gemini.test.ts +41 -2
- package/src/memory/embedding-gemini.ts +6 -1
- package/src/memory/graph/bootstrap.test.ts +282 -0
- package/src/memory/graph/bootstrap.ts +8 -5
- package/src/memory/graph/extraction.ts +10 -2
- package/src/memory/graph/graph-search.test.ts +1 -0
- package/src/memory/graph/inspect.ts +2 -2
- package/src/memory/graph/retriever.ts +10 -3
- package/src/memory/migrations/041-approval-prompt-ts-tracker.ts +26 -0
- package/src/memory/migrations/149-oauth-tables.ts +1 -0
- package/src/memory/migrations/223-schedule-script-column.ts +11 -0
- package/src/memory/migrations/224-oauth-providers-managed-service-is-paid.ts +24 -0
- package/src/memory/migrations/225-oauth-providers-available-scopes.ts +13 -0
- package/src/memory/migrations/index.ts +4 -0
- package/src/memory/pkb/pkb-index.test.ts +1 -0
- package/src/memory/pkb/pkb-reconcile.test.ts +1 -0
- package/src/memory/pkb/pkb-search.test.ts +65 -4
- package/src/memory/pkb/pkb-search.ts +40 -18
- package/src/memory/qdrant-client.test.ts +60 -0
- package/src/memory/qdrant-client.ts +25 -0
- package/src/memory/schema/infrastructure.ts +1 -0
- package/src/memory/schema/oauth.ts +4 -1
- package/src/messaging/providers/slack/render-transcript.test.ts +77 -29
- package/src/messaging/providers/slack/render-transcript.ts +58 -0
- package/src/notifications/conversation-pairing.ts +78 -19
- package/src/notifications/copy-composer.ts +0 -5
- package/src/notifications/emit-signal.ts +1 -1
- package/src/notifications/signal.ts +1 -2
- package/src/oauth/AGENTS.md +1 -1
- package/src/oauth/__tests__/identity-verifier.test.ts +2 -1
- package/src/oauth/connect-orchestrator.ts +8 -34
- package/src/oauth/connect-types.ts +6 -10
- package/src/oauth/manual-token-connection.ts +23 -0
- package/src/oauth/oauth-store.ts +30 -14
- package/src/oauth/provider-serializer.ts +6 -1
- package/src/oauth/seed-providers.ts +56 -108
- package/src/outbound-proxy/http-forwarder.ts +9 -0
- package/src/permissions/approval-policy.test.ts +293 -18
- package/src/permissions/approval-policy.ts +110 -58
- package/src/permissions/arg-parser.test.ts +161 -0
- package/src/permissions/arg-parser.ts +141 -0
- package/src/permissions/bash-risk-classifier.test.ts +414 -2
- package/src/permissions/bash-risk-classifier.ts +303 -60
- package/src/permissions/checker.ts +157 -29
- package/src/permissions/command-registry.test.ts +239 -0
- package/src/permissions/command-registry.ts +234 -54
- package/src/permissions/defaults.ts +5 -4
- package/src/permissions/gateway-threshold-reader.ts +196 -0
- package/src/permissions/prompter.ts +4 -0
- package/src/permissions/risk-types.ts +61 -4
- package/src/permissions/schedule-risk-classifier.test.ts +129 -0
- package/src/permissions/schedule-risk-classifier.ts +85 -0
- package/src/permissions/shell-identity.ts +2 -42
- package/src/permissions/types.ts +2 -0
- package/src/permissions/workspace-policy.ts +8 -3
- package/src/plugins/defaults/circuit-breaker.ts +146 -0
- package/src/plugins/defaults/compaction.ts +145 -0
- package/src/plugins/defaults/empty-response.ts +126 -0
- package/src/plugins/defaults/history-repair.ts +85 -0
- package/src/plugins/defaults/index.ts +116 -0
- package/src/plugins/defaults/injectors.ts +491 -0
- package/src/plugins/defaults/llm-call.ts +82 -0
- package/src/plugins/defaults/memory-retrieval.ts +226 -0
- package/src/plugins/defaults/overflow-reduce.ts +181 -0
- package/src/plugins/defaults/persistence.ts +129 -0
- package/src/plugins/defaults/title-generate.ts +95 -0
- package/src/plugins/defaults/token-estimate.ts +104 -0
- package/src/plugins/defaults/tool-error.ts +126 -0
- package/src/plugins/defaults/tool-execute.ts +89 -0
- package/src/plugins/defaults/tool-result-truncate.ts +88 -0
- package/src/plugins/pipeline.ts +316 -0
- package/src/plugins/plugin-skill-contributions.ts +292 -0
- package/src/plugins/registry.ts +241 -0
- package/src/plugins/types.ts +1134 -0
- package/src/plugins/user-loader.ts +177 -0
- package/src/prompts/templates/BOOTSTRAP.md +27 -77
- package/src/providers/model-catalog.ts +52 -29
- package/src/providers/model-intents.ts +1 -1
- package/src/providers/openrouter/client.ts +5 -1
- package/src/providers/speech-to-text/deepgram-realtime.test.ts +61 -0
- package/src/providers/speech-to-text/deepgram-realtime.ts +57 -0
- package/src/providers/speech-to-text/xai-realtime.test.ts +72 -4
- package/src/providers/speech-to-text/xai-realtime.ts +39 -14
- package/src/runtime/AGENTS.md +25 -16
- package/src/runtime/__tests__/browser-extension-pair-routes.test.ts +3 -3
- package/src/runtime/__tests__/client-registry.test.ts +293 -0
- package/src/runtime/client-registry.ts +261 -0
- package/src/runtime/http-server.ts +77 -8
- package/src/runtime/http-types.ts +0 -2
- package/src/runtime/migrations/vbundle-builder.ts +1 -22
- package/src/runtime/routes/approval-prompt-ts-tracker.ts +51 -31
- package/src/runtime/routes/approval-routes.ts +17 -0
- package/src/runtime/routes/browser-extension-pair-routes.ts +27 -8
- package/src/runtime/routes/conversation-routes.ts +223 -116
- package/src/runtime/routes/inbound-message-handler.ts +88 -13
- package/src/runtime/routes/memory-item-routes.test.ts +1 -0
- package/src/runtime/routes/migration-routes.ts +0 -3
- package/src/runtime/routes/playground/__tests__/force-compact.test.ts +284 -0
- package/src/runtime/routes/playground/__tests__/guard.test.ts +80 -0
- package/src/runtime/routes/playground/__tests__/inject-failures.test.ts +294 -0
- package/src/runtime/routes/playground/__tests__/reset-circuit.test.ts +271 -0
- package/src/runtime/routes/playground/__tests__/seed-conversation.test.ts +202 -0
- package/src/runtime/routes/playground/__tests__/seeded-conversations.test.ts +309 -0
- package/src/runtime/routes/playground/__tests__/state.test.ts +224 -0
- package/src/runtime/routes/playground/conversation-not-found.ts +29 -0
- package/src/runtime/routes/playground/deps.ts +56 -0
- package/src/runtime/routes/playground/force-compact.ts +73 -0
- package/src/runtime/routes/playground/guard.ts +37 -0
- package/src/runtime/routes/playground/index.ts +28 -0
- package/src/runtime/routes/playground/inject-failures.ts +159 -0
- package/src/runtime/routes/playground/reset-circuit.ts +115 -0
- package/src/runtime/routes/playground/seed-conversation.ts +139 -0
- package/src/runtime/routes/playground/seeded-conversations.ts +78 -0
- package/src/runtime/routes/playground/state.ts +78 -0
- package/src/runtime/routes/schedule-routes.ts +89 -8
- package/src/runtime/skill-route-registry.ts +75 -15
- package/src/schedule/run-script.ts +68 -0
- package/src/schedule/schedule-store.ts +7 -1
- package/src/schedule/scheduler.ts +48 -8
- package/src/skills/catalog-cache.ts +12 -5
- package/src/tools/browser/__tests__/browser-status.test.ts +189 -0
- package/src/tools/browser/browser-execution.ts +88 -19
- package/src/tools/browser/cdp-client/__tests__/extension-cdp-client.test.ts +230 -0
- package/src/tools/browser/cdp-client/__tests__/factory.test.ts +146 -3
- package/src/tools/browser/cdp-client/extension-cdp-client.ts +54 -3
- package/src/tools/browser/cdp-client/factory.ts +15 -4
- package/src/tools/executor.ts +126 -74
- package/src/tools/network/script-proxy/session-manager.ts +37 -1
- package/src/tools/permission-checker.ts +98 -49
- package/src/tools/policy-context.ts +4 -0
- package/src/tools/registry.ts +140 -3
- package/src/tools/schedule/create.ts +23 -8
- package/src/tools/schedule/update.ts +3 -1
- package/src/tools/secret-detection-handler.ts +0 -51
- package/src/tools/system/avatar-generator.ts +6 -2
- package/src/tools/types.ts +28 -2
- package/src/util/platform.ts +7 -2
- package/src/util/pricing.ts +26 -3
- package/src/workspace/migrations/006-services-config.ts +2 -4
- package/src/workspace/migrations/022-move-hooks-to-workspace.ts +2 -3
- package/src/workspace/migrations/041-backfill-google-gmail-settings-scope.ts +3 -4
- package/src/workspace/migrations/046-seed-conversation-starters-callsite.ts +108 -0
- package/src/workspace/migrations/047-remove-watch-callsites.ts +54 -0
- package/src/workspace/migrations/048-remove-workspace-hooks.ts +81 -0
- package/src/workspace/migrations/049-release-notes-default-sonnet.ts +80 -0
- package/src/workspace/migrations/050-seed-main-agent-opus-callsite.ts +86 -0
- package/src/workspace/migrations/051-seed-conversation-summarization-callsite.ts +128 -0
- package/src/workspace/migrations/registry.ts +12 -0
- package/tsconfig.json +1 -1
- package/hook-templates/debug-prompt-logger/hook.json +0 -7
- package/hook-templates/debug-prompt-logger/run.sh +0 -66
- package/src/__tests__/compaction-circuit-breaker.test.ts +0 -336
- package/src/__tests__/context-overflow-approval.test.ts +0 -156
- package/src/__tests__/hooks-blocking.test.ts +0 -178
- package/src/__tests__/hooks-cli.test.ts +0 -182
- package/src/__tests__/hooks-config.test.ts +0 -108
- package/src/__tests__/hooks-discovery.test.ts +0 -211
- package/src/__tests__/hooks-integration.test.ts +0 -196
- package/src/__tests__/hooks-manager.test.ts +0 -226
- package/src/__tests__/hooks-runner.test.ts +0 -175
- package/src/__tests__/hooks-settings.test.ts +0 -160
- package/src/__tests__/hooks-templates.test.ts +0 -169
- package/src/__tests__/hooks-ts-runner.test.ts +0 -170
- package/src/__tests__/hooks-watch.test.ts +0 -112
- package/src/__tests__/notification-schedule-dedup.test.ts +0 -213
- package/src/__tests__/oauth-scope-policy.test.ts +0 -180
- package/src/__tests__/send-notification-tool.test.ts +0 -83
- package/src/cli/commands/shotgun.ts +0 -266
- package/src/config/bundled-skills/conversations/SKILL.md +0 -20
- package/src/config/bundled-skills/conversations/TOOLS.json +0 -23
- package/src/config/bundled-skills/conversations/tools/rename-conversation.ts +0 -88
- package/src/config/bundled-skills/heartbeat/SKILL.md +0 -43
- package/src/config/bundled-skills/notifications/SKILL.md +0 -40
- package/src/config/bundled-skills/notifications/TOOLS.json +0 -80
- package/src/config/bundled-skills/notifications/tools/send-notification.ts +0 -152
- package/src/config/bundled-skills/notifications/tools/shared.ts +0 -13
- package/src/config/bundled-skills/screen-watch/SKILL.md +0 -27
- package/src/config/bundled-skills/screen-watch/TOOLS.json +0 -35
- package/src/config/bundled-skills/screen-watch/tools/start-screen-watch.ts +0 -12
- package/src/config/bundled-skills/skills-catalog/SKILL.md +0 -84
- package/src/daemon/context-overflow-approval.ts +0 -52
- package/src/daemon/watch-handler.ts +0 -399
- package/src/hooks/cli.ts +0 -253
- package/src/hooks/config.ts +0 -100
- package/src/hooks/discovery.ts +0 -135
- package/src/hooks/manager.ts +0 -179
- package/src/hooks/runner.ts +0 -117
- package/src/hooks/templates.ts +0 -77
- package/src/hooks/types.ts +0 -75
- package/src/oauth/scope-policy.ts +0 -89
- package/src/runtime/gateway-internal-client.ts +0 -94
- package/src/runtime/routes/watch-routes.ts +0 -156
- package/src/signals/shotgun.ts +0 -203
- package/src/tools/watch/screen-watch.ts +0 -144
- package/src/tools/watch/watch-state.ts +0 -142
|
@@ -21,7 +21,7 @@ import { getWorkspaceDir } from "../../util/platform.js";
|
|
|
21
21
|
import { getMemoryCheckpoint, setMemoryCheckpoint } from "../checkpoints.js";
|
|
22
22
|
import { getDb, rawAll, rawGet, rawRun } from "../db.js";
|
|
23
23
|
import { enqueueMemoryJob, hasActiveJobOfType } from "../jobs-store.js";
|
|
24
|
-
import { initQdrantClient } from "../qdrant-client.js";
|
|
24
|
+
import { initQdrantClient, resolveQdrantUrl } from "../qdrant-client.js";
|
|
25
25
|
import { conversations, memoryGraphNodes, memorySegments } from "../schema.js";
|
|
26
26
|
import { runGraphExtraction } from "./extraction.js";
|
|
27
27
|
import { countNodes } from "./store.js";
|
|
@@ -71,7 +71,7 @@ export async function bootstrapFromHistory(
|
|
|
71
71
|
// Initialize Qdrant client for inline embedding
|
|
72
72
|
try {
|
|
73
73
|
initQdrantClient({
|
|
74
|
-
url: config
|
|
74
|
+
url: resolveQdrantUrl(config),
|
|
75
75
|
collection: config.memory.qdrant.collection,
|
|
76
76
|
vectorSize: config.memory.qdrant.vectorSize,
|
|
77
77
|
onDisk: config.memory.qdrant.onDisk ?? true,
|
|
@@ -384,6 +384,10 @@ const KIND_TO_PREFIX: Record<string, string> = {
|
|
|
384
384
|
* schema. ORM-based inserts include every column in the schema definition,
|
|
385
385
|
* so adding a column in a later migration would cause this migration to
|
|
386
386
|
* fail with "table has no column named …" on upgrade paths.
|
|
387
|
+
*
|
|
388
|
+
* The INSERT intentionally omits columns added by later migrations (e.g.
|
|
389
|
+
* `image_refs` from migration 205) since they default to NULL and
|
|
390
|
+
* including them would couple this migration to those later schema changes.
|
|
387
391
|
*/
|
|
388
392
|
export function migrateToolCreatedItems(): void {
|
|
389
393
|
if (getMemoryCheckpoint(MIGRATE_ITEMS_CHECKPOINT)) return;
|
|
@@ -446,8 +450,8 @@ export function migrateToolCreatedItems(): void {
|
|
|
446
450
|
event_date, emotional_charge, fidelity, confidence, significance,
|
|
447
451
|
stability, reinforcement_count, last_reinforced,
|
|
448
452
|
source_conversations, source_type, narrative_role, part_of_story,
|
|
449
|
-
|
|
450
|
-
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?,
|
|
453
|
+
scope_id
|
|
454
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
451
455
|
id,
|
|
452
456
|
content,
|
|
453
457
|
"semantic",
|
|
@@ -466,7 +470,6 @@ export function migrateToolCreatedItems(): void {
|
|
|
466
470
|
"direct",
|
|
467
471
|
null,
|
|
468
472
|
null,
|
|
469
|
-
null,
|
|
470
473
|
row.scope_id || "default",
|
|
471
474
|
);
|
|
472
475
|
|
|
@@ -1066,10 +1066,18 @@ export async function runGraphExtraction(
|
|
|
1066
1066
|
);
|
|
1067
1067
|
|
|
1068
1068
|
// 7. Handle supersession (inherit durability before applying diff)
|
|
1069
|
+
// TODO: full supersession is not yet implemented. When it lands, iterate
|
|
1070
|
+
// BOTH `diff.createEdges` (existing → existing) AND `deferredEdges`
|
|
1071
|
+
// (new → existing, the typical supersession case).
|
|
1072
|
+
// Tracked by https://github.com/vellum-ai/vellum-assistant/pull/27057 (Devin).
|
|
1069
1073
|
for (const edge of diff.createEdges) {
|
|
1070
1074
|
if (edge.relationship === "supersedes") {
|
|
1071
|
-
//
|
|
1072
|
-
|
|
1075
|
+
// Placeholder — see TODO above.
|
|
1076
|
+
}
|
|
1077
|
+
}
|
|
1078
|
+
for (const de of deferredEdges) {
|
|
1079
|
+
if (de.relationship === "supersedes") {
|
|
1080
|
+
// Placeholder — see TODO above.
|
|
1073
1081
|
}
|
|
1074
1082
|
}
|
|
1075
1083
|
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
|
|
15
15
|
import { getConfig } from "../../config/loader.js";
|
|
16
16
|
import { initializeDb } from "../db-init.js";
|
|
17
|
-
import { initQdrantClient } from "../qdrant-client.js";
|
|
17
|
+
import { initQdrantClient, resolveQdrantUrl } from "../qdrant-client.js";
|
|
18
18
|
import {
|
|
19
19
|
countNodes,
|
|
20
20
|
getEdgesForNode,
|
|
@@ -29,7 +29,7 @@ initializeDb();
|
|
|
29
29
|
const config = getConfig();
|
|
30
30
|
try {
|
|
31
31
|
initQdrantClient({
|
|
32
|
-
url: config
|
|
32
|
+
url: resolveQdrantUrl(config),
|
|
33
33
|
collection: config.memory.qdrant.collection,
|
|
34
34
|
vectorSize: config.memory.qdrant.vectorSize,
|
|
35
35
|
onDisk: config.memory.qdrant.onDisk ?? true,
|
|
@@ -877,9 +877,16 @@ export interface TurnRetrievalResult {
|
|
|
877
877
|
*/
|
|
878
878
|
queryVector?: number[];
|
|
879
879
|
/**
|
|
880
|
-
*
|
|
881
|
-
*
|
|
882
|
-
*
|
|
880
|
+
* Sparse (TF-IDF) vector of the user's last message, computed once per
|
|
881
|
+
* turn and reused across every return path of `retrieveForTurn`. Surfaced
|
|
882
|
+
* so downstream callers (e.g. the PKB hint retriever in
|
|
883
|
+
* `applyRuntimeInjections`) can pair it with `queryVector` to run a
|
|
884
|
+
* hybrid dense+sparse query — RRF fusion pulls in lexical matches
|
|
885
|
+
* (exact filenames, proper nouns, uncommon tokens) that pure dense
|
|
886
|
+
* embeddings wash out. Computed locally (no embedding-service call), so
|
|
887
|
+
* it survives even when the dense embed fails via the circuit breaker.
|
|
888
|
+
* `undefined` when the user's last message is empty/whitespace-only or
|
|
889
|
+
* yields no TF-IDF tokens.
|
|
883
890
|
*/
|
|
884
891
|
sparseVector?: QdrantSparseVector;
|
|
885
892
|
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { DrizzleDb } from "../db-connection.js";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Tracker for approval prompt message timestamps.
|
|
5
|
+
*
|
|
6
|
+
* Stores the (channel, chat_id, ts) tuples for delivered guardian approval
|
|
7
|
+
* prompts so only reactions on a known prompt can resolve a pending
|
|
8
|
+
* request. Persistence (rather than in-memory state) is required because
|
|
9
|
+
* the guardian approval TTL is 30 minutes, which can span a daemon
|
|
10
|
+
* restart between prompt delivery and the user's reaction.
|
|
11
|
+
*/
|
|
12
|
+
export function createApprovalPromptTsTrackerTable(database: DrizzleDb): void {
|
|
13
|
+
database.run(/*sql*/ `
|
|
14
|
+
CREATE TABLE IF NOT EXISTS approval_prompt_ts_tracker (
|
|
15
|
+
channel TEXT NOT NULL,
|
|
16
|
+
chat_id TEXT NOT NULL,
|
|
17
|
+
ts TEXT NOT NULL,
|
|
18
|
+
expires_at INTEGER NOT NULL,
|
|
19
|
+
PRIMARY KEY (channel, chat_id, ts)
|
|
20
|
+
)
|
|
21
|
+
`);
|
|
22
|
+
|
|
23
|
+
database.run(
|
|
24
|
+
/*sql*/ `CREATE INDEX IF NOT EXISTS idx_approval_prompt_ts_tracker_expires ON approval_prompt_ts_tracker(expires_at)`,
|
|
25
|
+
);
|
|
26
|
+
}
|
|
@@ -15,6 +15,7 @@ export function createOAuthTables(database: DrizzleDb): void {
|
|
|
15
15
|
base_url TEXT,
|
|
16
16
|
default_scopes TEXT NOT NULL DEFAULT '[]',
|
|
17
17
|
scope_policy TEXT NOT NULL DEFAULT '{}',
|
|
18
|
+
available_scopes TEXT,
|
|
18
19
|
extra_params TEXT,
|
|
19
20
|
callback_transport TEXT,
|
|
20
21
|
loopback_port INTEGER,
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { DrizzleDb } from "../db-connection.js";
|
|
2
|
+
import { getSqliteFrom } from "../db-connection.js";
|
|
3
|
+
|
|
4
|
+
export function migrateScheduleScriptColumn(database: DrizzleDb): void {
|
|
5
|
+
const raw = getSqliteFrom(database);
|
|
6
|
+
try {
|
|
7
|
+
raw.exec(`ALTER TABLE cron_jobs ADD COLUMN script TEXT`);
|
|
8
|
+
} catch {
|
|
9
|
+
// Column already exists — nothing to do.
|
|
10
|
+
}
|
|
11
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { DrizzleDb } from "../db-connection.js";
|
|
2
|
+
import { getSqliteFrom } from "../db-connection.js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Add the `managed_service_is_paid` column to `oauth_providers`.
|
|
6
|
+
*
|
|
7
|
+
* Flags whether a provider's managed OAuth variant requires a paid plan
|
|
8
|
+
* upstream (e.g. Twitter). Defaults to 0 (false) for all existing rows.
|
|
9
|
+
*
|
|
10
|
+
* Mirrors the pattern from migration 178. The raw.exec() call below is
|
|
11
|
+
* Drizzle's SQLite statement runner — NOT a shell exec.
|
|
12
|
+
*/
|
|
13
|
+
export function migrateOAuthProvidersManagedServiceIsPaid(
|
|
14
|
+
database: DrizzleDb,
|
|
15
|
+
): void {
|
|
16
|
+
const raw = getSqliteFrom(database);
|
|
17
|
+
try {
|
|
18
|
+
raw.exec(
|
|
19
|
+
/*sql*/ `ALTER TABLE oauth_providers ADD COLUMN managed_service_is_paid INTEGER NOT NULL DEFAULT 0`,
|
|
20
|
+
);
|
|
21
|
+
} catch {
|
|
22
|
+
// Column already exists — nothing to do.
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { DrizzleDb } from "../db-connection.js";
|
|
2
|
+
import { getSqliteFrom } from "../db-connection.js";
|
|
3
|
+
|
|
4
|
+
export function migrateOAuthProvidersAvailableScopes(
|
|
5
|
+
database: DrizzleDb,
|
|
6
|
+
): void {
|
|
7
|
+
const raw = getSqliteFrom(database);
|
|
8
|
+
try {
|
|
9
|
+
raw.exec(`ALTER TABLE oauth_providers ADD COLUMN available_scopes TEXT`);
|
|
10
|
+
} catch {
|
|
11
|
+
// Column already exists — nothing to do.
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -42,6 +42,7 @@ export { migrateVoiceInviteColumns } from "./037-voice-invite-columns.js";
|
|
|
42
42
|
export { createActorTokenRecordsTable } from "./038-actor-token-records.js";
|
|
43
43
|
export { createActorRefreshTokenRecordsTable } from "./039-actor-refresh-token-records.js";
|
|
44
44
|
export { migrateInviteCodeHashColumn } from "./040-invite-code-hash-column.js";
|
|
45
|
+
export { createApprovalPromptTsTrackerTable } from "./041-approval-prompt-ts-tracker.js";
|
|
45
46
|
export { createCoreTables } from "./100-core-tables.js";
|
|
46
47
|
export { createWatchersAndLogsTables } from "./101-watchers-and-logs.js";
|
|
47
48
|
export { addCoreColumns } from "./102-alter-table-columns.js";
|
|
@@ -167,6 +168,9 @@ export {
|
|
|
167
168
|
} from "./220-normalize-user-file-by-principal.js";
|
|
168
169
|
export { migrateConversationsArchivedAt } from "./221-conversations-archived-at.js";
|
|
169
170
|
export { migrateStripPlaceholderSentinelsFromMessages } from "./222-strip-placeholder-sentinels-from-messages.js";
|
|
171
|
+
export { migrateScheduleScriptColumn } from "./223-schedule-script-column.js";
|
|
172
|
+
export { migrateOAuthProvidersManagedServiceIsPaid } from "./224-oauth-providers-managed-service-is-paid.js";
|
|
173
|
+
export { migrateOAuthProvidersAvailableScopes } from "./225-oauth-providers-available-scopes.js";
|
|
170
174
|
export {
|
|
171
175
|
MIGRATION_REGISTRY,
|
|
172
176
|
type MigrationRegistryEntry,
|
|
@@ -25,6 +25,8 @@ type ScoredPoint = { id: string; score: number; payload: Payload };
|
|
|
25
25
|
|
|
26
26
|
let hybridResults: ScoredPoint[] = [];
|
|
27
27
|
let denseResults: ScoredPoint[] = [];
|
|
28
|
+
let hybridThrows: Error | null = null;
|
|
29
|
+
let denseThrows: Error | null = null;
|
|
28
30
|
|
|
29
31
|
mock.module("../qdrant-circuit-breaker.js", () => ({
|
|
30
32
|
isQdrantBreakerOpen: () => breakerOpen,
|
|
@@ -41,6 +43,7 @@ mock.module("../qdrant-client.js", () => ({
|
|
|
41
43
|
prefetchLimit?: number;
|
|
42
44
|
}) => {
|
|
43
45
|
hybridSearchCalls.push(params);
|
|
46
|
+
if (hybridThrows) throw hybridThrows;
|
|
44
47
|
return hybridResults;
|
|
45
48
|
},
|
|
46
49
|
search: async (
|
|
@@ -49,9 +52,11 @@ mock.module("../qdrant-client.js", () => ({
|
|
|
49
52
|
filter?: Record<string, unknown>,
|
|
50
53
|
) => {
|
|
51
54
|
searchCalls.push({ vector, limit, filter });
|
|
55
|
+
if (denseThrows) throw denseThrows;
|
|
52
56
|
return denseResults;
|
|
53
57
|
},
|
|
54
58
|
}),
|
|
59
|
+
resolveQdrantUrl: () => "http://127.0.0.1:6333",
|
|
55
60
|
}));
|
|
56
61
|
|
|
57
62
|
const { searchPkbFiles } = await import("./pkb-search.js");
|
|
@@ -63,6 +68,8 @@ describe("searchPkbFiles", () => {
|
|
|
63
68
|
searchCalls.length = 0;
|
|
64
69
|
hybridResults = [];
|
|
65
70
|
denseResults = [];
|
|
71
|
+
hybridThrows = null;
|
|
72
|
+
denseThrows = null;
|
|
66
73
|
});
|
|
67
74
|
|
|
68
75
|
test("filter payload targets pkb_file (hybrid path runs both queries)", async () => {
|
|
@@ -236,7 +243,7 @@ describe("searchPkbFiles", () => {
|
|
|
236
243
|
expect(other?.hybridScore).toBeUndefined();
|
|
237
244
|
});
|
|
238
245
|
|
|
239
|
-
test("
|
|
246
|
+
test("hybrid-only hits (no dense match) are dropped so they can't evict dense-qualified paths before the slice", async () => {
|
|
240
247
|
denseResults = [
|
|
241
248
|
{
|
|
242
249
|
id: "a",
|
|
@@ -248,6 +255,10 @@ describe("searchPkbFiles", () => {
|
|
|
248
255
|
},
|
|
249
256
|
},
|
|
250
257
|
];
|
|
258
|
+
// `b` appears only in the hybrid response (outside the dense prefetch).
|
|
259
|
+
// It has no cosine score, so it can never pass a downstream threshold
|
|
260
|
+
// and must not be surfaced — otherwise it could crowd out dense-qualified
|
|
261
|
+
// hits before the caller gates on denseScore.
|
|
251
262
|
hybridResults = [
|
|
252
263
|
{
|
|
253
264
|
id: "a",
|
|
@@ -275,12 +286,62 @@ describe("searchPkbFiles", () => {
|
|
|
275
286
|
10,
|
|
276
287
|
);
|
|
277
288
|
|
|
289
|
+
expect(results).toHaveLength(1);
|
|
278
290
|
const a = results.find((r) => r.path === "/notes/a.md");
|
|
279
|
-
const b = results.find((r) => r.path === "/notes/b.md");
|
|
280
291
|
expect(a?.denseScore).toBe(0.8);
|
|
281
292
|
expect(a?.hybridScore).toBe(0.03);
|
|
282
|
-
expect(b
|
|
283
|
-
|
|
293
|
+
expect(results.find((r) => r.path === "/notes/b.md")).toBeUndefined();
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
test("hybrid failure (transient) falls back to dense-only results", async () => {
|
|
297
|
+
denseResults = [
|
|
298
|
+
{
|
|
299
|
+
id: "a",
|
|
300
|
+
score: 0.8,
|
|
301
|
+
payload: {
|
|
302
|
+
target_type: "pkb_file",
|
|
303
|
+
target_id: "t-1",
|
|
304
|
+
path: "/notes/a.md",
|
|
305
|
+
},
|
|
306
|
+
},
|
|
307
|
+
];
|
|
308
|
+
hybridThrows = new Error("qdrant hybrid transient failure");
|
|
309
|
+
|
|
310
|
+
const results = await searchPkbFiles(
|
|
311
|
+
[0.1],
|
|
312
|
+
{ indices: [1], values: [1] },
|
|
313
|
+
10,
|
|
314
|
+
);
|
|
315
|
+
|
|
316
|
+
expect(results).toHaveLength(1);
|
|
317
|
+
expect(results[0]?.path).toBe("/notes/a.md");
|
|
318
|
+
expect(results[0]?.denseScore).toBe(0.8);
|
|
319
|
+
expect(results[0]?.hybridScore).toBeUndefined();
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
test("dense failure (transient) falls back to empty results even if hybrid succeeded", async () => {
|
|
323
|
+
denseThrows = new Error("qdrant dense transient failure");
|
|
324
|
+
hybridResults = [
|
|
325
|
+
{
|
|
326
|
+
id: "a",
|
|
327
|
+
score: 0.03,
|
|
328
|
+
payload: {
|
|
329
|
+
target_type: "pkb_file",
|
|
330
|
+
target_id: "t-1",
|
|
331
|
+
path: "/notes/a.md",
|
|
332
|
+
},
|
|
333
|
+
},
|
|
334
|
+
];
|
|
335
|
+
|
|
336
|
+
// Without a dense cosine score there is nothing to gate on, so the
|
|
337
|
+
// hybrid-only fallback surfaces no results.
|
|
338
|
+
const results = await searchPkbFiles(
|
|
339
|
+
[0.1],
|
|
340
|
+
{ indices: [1], values: [1] },
|
|
341
|
+
10,
|
|
342
|
+
);
|
|
343
|
+
|
|
344
|
+
expect(results).toEqual([]);
|
|
284
345
|
});
|
|
285
346
|
|
|
286
347
|
test("empty Qdrant response yields []", async () => {
|
|
@@ -28,9 +28,10 @@ const log = getLogger("pkb-search");
|
|
|
28
28
|
*
|
|
29
29
|
* PKB files are chunked at index time, so a single path can match on
|
|
30
30
|
* multiple chunks. Scores collapse to the highest per path, per query.
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
31
|
+
* Only paths with a dense cosine score are returned (hybrid-only matches
|
|
32
|
+
* are dropped because their implicit `denseScore = 0` can never satisfy a
|
|
33
|
+
* caller's threshold, and keeping them would evict dense-qualifying hits
|
|
34
|
+
* from the pre-slice top-`limit` window).
|
|
34
35
|
*/
|
|
35
36
|
export async function searchPkbFiles(
|
|
36
37
|
queryVector: number[],
|
|
@@ -77,29 +78,50 @@ export async function searchPkbFiles(
|
|
|
77
78
|
)
|
|
78
79
|
: Promise.resolve([]);
|
|
79
80
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
81
|
+
// Silence any hybrid rejection so a short-circuit on dense failure below
|
|
82
|
+
// does not surface it as an unhandledRejection. We still `await` the
|
|
83
|
+
// original promise in the non-short-circuit path to observe its outcome.
|
|
84
|
+
hybridPromise.catch(() => {});
|
|
85
|
+
|
|
86
|
+
// Dense is the required signal — only paths with a dense cosine score are
|
|
87
|
+
// merged, so a dense rejection guarantees `[]` regardless of hybrid. Return
|
|
88
|
+
// immediately rather than blocking on hybrid latency.
|
|
89
|
+
let denseResults: QdrantSearchResult[];
|
|
90
|
+
try {
|
|
91
|
+
denseResults = await densePromise;
|
|
92
|
+
} catch (err) {
|
|
93
|
+
log.warn({ err }, "Dense PKB search failed; returning empty results");
|
|
94
|
+
return [];
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
let hybridResults: QdrantSearchResult[] = [];
|
|
98
|
+
if (useHybrid) {
|
|
99
|
+
try {
|
|
100
|
+
hybridResults = await hybridPromise;
|
|
101
|
+
} catch (err) {
|
|
102
|
+
log.warn(
|
|
103
|
+
{ err },
|
|
104
|
+
"Hybrid PKB search failed; falling back to dense-only results",
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
84
108
|
|
|
85
109
|
const denseByPath = collapseByPath(denseResults);
|
|
86
110
|
const hybridByPath = useHybrid ? collapseByPath(hybridResults) : new Map();
|
|
87
111
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
112
|
+
// Only surface paths that have a dense cosine score. Hybrid-only hits
|
|
113
|
+
// (paths that appeared in the hybrid query but fell outside the dense
|
|
114
|
+
// prefetch) would have `denseScore = 0`, which is guaranteed to fail any
|
|
115
|
+
// caller's cosine threshold — and because they'd sort ahead of dense
|
|
116
|
+
// matches (hybrid-first ranking), they'd evict dense-qualifying hits from
|
|
117
|
+
// the top-`limit` window before callers ever get a chance to gate on
|
|
118
|
+
// denseScore. Dropping them keeps gating meaningful with the pre-slice.
|
|
93
119
|
const merged: PkbSearchResult[] = [];
|
|
94
|
-
for (const path of
|
|
95
|
-
const denseScore = denseByPath.get(path);
|
|
120
|
+
for (const [path, denseScore] of denseByPath) {
|
|
96
121
|
const hybridScore = hybridByPath.get(path);
|
|
97
|
-
// A path that only shows up in the hybrid query (past the dense prefetch
|
|
98
|
-
// limit) has no cosine score to gate on. Carry denseScore = 0 so callers
|
|
99
|
-
// see it but can filter it out with their threshold.
|
|
100
122
|
merged.push({
|
|
101
123
|
path,
|
|
102
|
-
denseScore
|
|
124
|
+
denseScore,
|
|
103
125
|
...(useHybrid && hybridScore !== undefined ? { hybridScore } : {}),
|
|
104
126
|
});
|
|
105
127
|
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, test } from "bun:test";
|
|
2
|
+
|
|
3
|
+
import { applyNestedDefaults } from "../config/loader.js";
|
|
4
|
+
import type { AssistantConfig } from "../config/types.js";
|
|
5
|
+
import { resolveQdrantUrl } from "./qdrant-client.js";
|
|
6
|
+
|
|
7
|
+
const DEFAULT_CONFIG: AssistantConfig = applyNestedDefaults({});
|
|
8
|
+
|
|
9
|
+
describe("resolveQdrantUrl", () => {
|
|
10
|
+
const savedPort = process.env.QDRANT_HTTP_PORT;
|
|
11
|
+
const savedUrl = process.env.QDRANT_URL;
|
|
12
|
+
|
|
13
|
+
beforeEach(() => {
|
|
14
|
+
delete process.env.QDRANT_HTTP_PORT;
|
|
15
|
+
delete process.env.QDRANT_URL;
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
afterEach(() => {
|
|
19
|
+
if (savedPort === undefined) delete process.env.QDRANT_HTTP_PORT;
|
|
20
|
+
else process.env.QDRANT_HTTP_PORT = savedPort;
|
|
21
|
+
if (savedUrl === undefined) delete process.env.QDRANT_URL;
|
|
22
|
+
else process.env.QDRANT_URL = savedUrl;
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
test("falls back to config when no env vars are set", () => {
|
|
26
|
+
expect(resolveQdrantUrl(DEFAULT_CONFIG)).toBe("http://127.0.0.1:6333");
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
test("honours QDRANT_URL when set", () => {
|
|
30
|
+
process.env.QDRANT_URL = "http://qdrant.example.com:6333";
|
|
31
|
+
expect(resolveQdrantUrl(DEFAULT_CONFIG)).toBe(
|
|
32
|
+
"http://qdrant.example.com:6333",
|
|
33
|
+
);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
test("QDRANT_HTTP_PORT wins over QDRANT_URL", () => {
|
|
37
|
+
process.env.QDRANT_URL = "http://qdrant.example.com:6333";
|
|
38
|
+
process.env.QDRANT_HTTP_PORT = "20200";
|
|
39
|
+
expect(resolveQdrantUrl(DEFAULT_CONFIG)).toBe("http://127.0.0.1:20200");
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
test("QDRANT_HTTP_PORT wins over config default", () => {
|
|
43
|
+
process.env.QDRANT_HTTP_PORT = "20200";
|
|
44
|
+
expect(resolveQdrantUrl(DEFAULT_CONFIG)).toBe("http://127.0.0.1:20200");
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
test("respects a non-default config URL when no env is set", () => {
|
|
48
|
+
const config: AssistantConfig = {
|
|
49
|
+
...DEFAULT_CONFIG,
|
|
50
|
+
memory: {
|
|
51
|
+
...DEFAULT_CONFIG.memory,
|
|
52
|
+
qdrant: {
|
|
53
|
+
...DEFAULT_CONFIG.memory.qdrant,
|
|
54
|
+
url: "http://custom-host:9999",
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
expect(resolveQdrantUrl(config)).toBe("http://custom-host:9999");
|
|
59
|
+
});
|
|
60
|
+
});
|
|
@@ -1,10 +1,35 @@
|
|
|
1
1
|
import { QdrantClient as QdrantRestClient } from "@qdrant/js-client-rest";
|
|
2
2
|
import { v4 as uuid } from "uuid";
|
|
3
3
|
|
|
4
|
+
import { getQdrantHttpPortEnv, getQdrantUrlEnv } from "../config/env.js";
|
|
5
|
+
import type { AssistantConfig } from "../config/types.js";
|
|
4
6
|
import { getLogger } from "../util/logger.js";
|
|
5
7
|
|
|
6
8
|
const log = getLogger("qdrant-client");
|
|
7
9
|
|
|
10
|
+
/**
|
|
11
|
+
* Resolve the Qdrant base URL for this process.
|
|
12
|
+
*
|
|
13
|
+
* Precedence (highest first):
|
|
14
|
+
* 1. `QDRANT_HTTP_PORT` — a locally-spawned Qdrant sidecar on 127.0.0.1:<port>.
|
|
15
|
+
* Set by the CLI when spawning the daemon with a non-default port
|
|
16
|
+
* (multi-local instances).
|
|
17
|
+
* 2. `QDRANT_URL` — an external Qdrant instance (K8s sidecar, remote URL).
|
|
18
|
+
* 3. `config.memory.qdrant.url` — static config (defaults to
|
|
19
|
+
* `http://127.0.0.1:6333`).
|
|
20
|
+
*
|
|
21
|
+
* Every caller that constructs a Qdrant URL should route through this helper
|
|
22
|
+
* so the precedence stays consistent across the daemon, CLI, and any
|
|
23
|
+
* background jobs that re-read the config.
|
|
24
|
+
*/
|
|
25
|
+
export function resolveQdrantUrl(config: AssistantConfig): string {
|
|
26
|
+
const port = getQdrantHttpPortEnv();
|
|
27
|
+
if (port) return `http://127.0.0.1:${port}`;
|
|
28
|
+
const url = getQdrantUrlEnv();
|
|
29
|
+
if (url) return url;
|
|
30
|
+
return config.memory.qdrant.url;
|
|
31
|
+
}
|
|
32
|
+
|
|
8
33
|
export interface QdrantSparseVector {
|
|
9
34
|
indices: number[];
|
|
10
35
|
values: number[];
|
|
@@ -28,6 +28,7 @@ export const cronJobs = sqliteTable("cron_jobs", {
|
|
|
28
28
|
reuseConversation: integer("reuse_conversation", { mode: "boolean" })
|
|
29
29
|
.notNull()
|
|
30
30
|
.default(false), // reuse the same conversation across runs
|
|
31
|
+
script: text("script"), // shell command for script mode (nullable, only used when mode = 'script')
|
|
31
32
|
createdAt: integer("created_at").notNull(),
|
|
32
33
|
updatedAt: integer("updated_at").notNull(),
|
|
33
34
|
});
|
|
@@ -20,7 +20,7 @@ export const oauthProviders = sqliteTable("oauth_providers", {
|
|
|
20
20
|
userinfoUrl: text("userinfo_url"),
|
|
21
21
|
baseUrl: text("base_url"),
|
|
22
22
|
defaultScopes: text("default_scopes").notNull().default("[]"),
|
|
23
|
-
|
|
23
|
+
availableScopes: text("available_scopes"),
|
|
24
24
|
scopeSeparator: text("scope_separator").notNull().default(" "),
|
|
25
25
|
authorizeParams: text("extra_params"),
|
|
26
26
|
pingUrl: text("ping_url"),
|
|
@@ -30,6 +30,9 @@ export const oauthProviders = sqliteTable("oauth_providers", {
|
|
|
30
30
|
revokeUrl: text("revoke_url"),
|
|
31
31
|
revokeBodyTemplate: text("revoke_body_template"),
|
|
32
32
|
managedServiceConfigKey: text("managed_service_config_key"),
|
|
33
|
+
managedServiceIsPaid: integer("managed_service_is_paid", { mode: "boolean" })
|
|
34
|
+
.notNull()
|
|
35
|
+
.default(false),
|
|
33
36
|
displayLabel: text("display_name"),
|
|
34
37
|
description: text("description"),
|
|
35
38
|
dashboardUrl: text("dashboard_url"),
|