@vellumai/assistant 0.5.5 → 0.5.7
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/.env.example +16 -2
- package/ARCHITECTURE.md +6 -75
- package/Dockerfile +4 -5
- package/README.md +0 -2
- package/bun.lock +0 -414
- package/docs/architecture/keychain-broker.md +45 -240
- package/docs/architecture/security.md +0 -17
- package/docs/credential-execution-service.md +2 -2
- package/node_modules/@vellumai/ces-contracts/package.json +1 -0
- package/node_modules/@vellumai/ces-contracts/src/rpc.ts +119 -0
- package/node_modules/@vellumai/credential-storage/package.json +1 -0
- package/node_modules/@vellumai/egress-proxy/package.json +1 -0
- package/package.json +2 -3
- package/src/__tests__/actor-token-service.test.ts +1 -2
- package/src/__tests__/assistant-feature-flags-integration.test.ts +30 -29
- package/src/__tests__/browser-skill-endstate.test.ts +6 -5
- package/src/__tests__/btw-routes.test.ts +0 -39
- package/src/__tests__/call-domain.test.ts +0 -128
- package/src/__tests__/ces-rpc-credential-backend.test.ts +199 -0
- package/src/__tests__/channel-approval-routes.test.ts +0 -5
- package/src/__tests__/channel-readiness-service.test.ts +1 -60
- package/src/__tests__/checker.test.ts +4 -2
- package/src/__tests__/cli-command-risk-guard.test.ts +112 -0
- package/src/__tests__/config-schema-cmd.test.ts +0 -1
- package/src/__tests__/config-schema.test.ts +3 -3
- package/src/__tests__/context-window-manager.test.ts +78 -0
- package/src/__tests__/conversation-attention-telegram.test.ts +0 -5
- package/src/__tests__/conversation-init.benchmark.test.ts +0 -2
- package/src/__tests__/conversation-skill-tools.test.ts +0 -54
- package/src/__tests__/conversation-title-service.test.ts +117 -1
- package/src/__tests__/credential-execution-feature-gates.test.ts +28 -14
- package/src/__tests__/credential-execution-managed-contract.test.ts +33 -18
- package/src/__tests__/credential-security-e2e.test.ts +0 -66
- package/src/__tests__/credential-security-invariants.test.ts +4 -45
- package/src/__tests__/credentials-cli.test.ts +78 -0
- package/src/__tests__/db-migration-rollback.test.ts +2015 -1
- package/src/__tests__/docker-signing-key-bootstrap.test.ts +98 -0
- package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +6 -4
- package/src/__tests__/guardian-routing-state.test.ts +0 -5
- package/src/__tests__/host-shell-tool.test.ts +6 -7
- package/src/__tests__/http-user-message-parity.test.ts +3 -103
- package/src/__tests__/inbound-invite-redemption.test.ts +0 -4
- package/src/__tests__/inline-skill-load-permissions.test.ts +6 -8
- package/src/__tests__/intent-routing.test.ts +0 -13
- package/src/__tests__/jobs-store-qdrant-breaker.test.ts +178 -0
- package/src/__tests__/keychain-broker-client.test.ts +161 -22
- package/src/__tests__/memory-jobs-worker-backoff.test.ts +150 -0
- package/src/__tests__/memory-regressions.test.ts +8 -30
- package/src/__tests__/migration-export-http.test.ts +2 -2
- package/src/__tests__/migration-import-commit-http.test.ts +2 -2
- package/src/__tests__/migration-import-preflight-http.test.ts +2 -2
- package/src/__tests__/migration-validate-http.test.ts +2 -2
- package/src/__tests__/non-member-access-request.test.ts +0 -5
- package/src/__tests__/notification-decision-fallback.test.ts +4 -0
- package/src/__tests__/notification-decision-identity.test.ts +4 -0
- package/src/__tests__/permission-types.test.ts +1 -0
- package/src/__tests__/provider-managed-proxy-integration.test.ts +5 -6
- package/src/__tests__/qdrant-manager.test.ts +28 -2
- package/src/__tests__/registry.test.ts +0 -6
- package/src/__tests__/require-fresh-approval.test.ts +4 -0
- package/src/__tests__/runtime-attachment-metadata.test.ts +0 -4
- package/src/__tests__/secret-routes-managed-proxy.test.ts +0 -4
- package/src/__tests__/secure-keys.test.ts +83 -263
- package/src/__tests__/shell-identity.test.ts +96 -6
- package/src/__tests__/skill-feature-flags-integration.test.ts +22 -14
- package/src/__tests__/skill-feature-flags.test.ts +46 -45
- package/src/__tests__/skill-load-feature-flag.test.ts +7 -10
- package/src/__tests__/skill-load-inline-command.test.ts +8 -12
- package/src/__tests__/skill-load-inline-includes.test.ts +6 -10
- package/src/__tests__/skill-load-tool.test.ts +0 -2
- package/src/__tests__/skill-projection-feature-flag.test.ts +33 -29
- package/src/__tests__/skills.test.ts +0 -2
- package/src/__tests__/slack-inbound-verification.test.ts +0 -4
- package/src/__tests__/suggestion-routes.test.ts +1 -32
- package/src/__tests__/system-prompt.test.ts +0 -1
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +4 -0
- package/src/__tests__/tool-executor-shell-integration.test.ts +5 -3
- package/src/__tests__/tool-executor.test.ts +4 -0
- package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +0 -5
- package/src/__tests__/trusted-contact-multichannel.test.ts +0 -4
- package/src/__tests__/update-bulletin.test.ts +0 -2
- package/src/__tests__/vellum-self-knowledge-inline-command.test.ts +6 -9
- package/src/__tests__/voice-scoped-grant-consumer.test.ts +0 -6
- package/src/__tests__/workspace-migration-015-migrate-credentials-to-keychain.test.ts +252 -0
- package/src/__tests__/workspace-migration-016-migrate-credentials-from-keychain.test.ts +218 -0
- package/src/__tests__/workspace-migration-down-functions.test.ts +1009 -0
- package/src/__tests__/workspace-migrations-runner.test.ts +114 -0
- package/src/calls/audio-store.test.ts +97 -0
- package/src/calls/audio-store.ts +205 -0
- package/src/calls/call-controller.ts +85 -7
- package/src/calls/call-domain.ts +3 -0
- package/src/calls/call-store.ts +10 -3
- package/src/calls/fish-audio-client.ts +117 -0
- package/src/calls/relay-server.ts +27 -0
- package/src/calls/twilio-routes.ts +2 -1
- package/src/calls/types.ts +1 -0
- package/src/calls/voice-ingress-preflight.ts +0 -42
- package/src/calls/voice-quality.ts +26 -5
- package/src/calls/voice-session-bridge.ts +6 -12
- package/src/cli/commands/config.ts +1 -4
- package/src/cli/commands/conversations.ts +0 -18
- package/src/cli/commands/credentials.ts +34 -4
- package/src/cli/commands/oauth/index.ts +7 -0
- package/src/cli/commands/oauth/platform.ts +179 -0
- package/src/cli/commands/platform.ts +3 -3
- package/src/config/assistant-feature-flags.ts +186 -5
- package/src/config/bundled-skills/messaging/SKILL.md +5 -5
- package/src/config/bundled-skills/phone-calls/TOOLS.json +4 -0
- package/src/config/bundled-skills/settings/TOOLS.json +2 -2
- package/src/config/bundled-skills/settings/tools/voice-config-update.ts +42 -0
- package/src/config/bundled-tool-registry.ts +1 -11
- package/src/config/env-registry.ts +1 -1
- package/src/config/env.ts +16 -16
- package/src/config/feature-flag-registry.json +48 -16
- package/src/config/loader.ts +98 -31
- package/src/config/schema.ts +4 -25
- package/src/config/schemas/calls.ts +13 -0
- package/src/config/schemas/fish-audio.ts +39 -0
- package/src/config/schemas/memory.ts +0 -4
- package/src/config/schemas/platform.ts +1 -1
- package/src/config/schemas/security.ts +4 -4
- package/src/config/types.ts +0 -1
- package/src/contacts/contact-store.ts +39 -0
- package/src/contacts/types.ts +2 -0
- package/src/context/window-manager.ts +53 -2
- package/src/credential-execution/approval-bridge.ts +1 -0
- package/src/credential-execution/executable-discovery.ts +28 -4
- package/src/credential-execution/feature-gates.ts +16 -0
- package/src/credential-execution/process-manager.ts +38 -0
- package/src/daemon/assistant-attachments.ts +9 -0
- package/src/daemon/config-watcher.ts +6 -4
- package/src/daemon/conversation-agent-loop.ts +0 -60
- package/src/daemon/conversation-memory.ts +0 -117
- package/src/daemon/conversation-runtime-assembly.ts +0 -2
- package/src/daemon/conversation-tool-setup.ts +0 -105
- package/src/daemon/conversation.ts +10 -1
- package/src/daemon/handlers/config-vercel.ts +92 -0
- package/src/daemon/handlers/conversations.ts +0 -11
- package/src/daemon/handlers/skills.ts +2 -15
- package/src/daemon/install-symlink.ts +195 -0
- package/src/daemon/lifecycle.ts +229 -96
- package/src/daemon/message-types/conversations.ts +3 -4
- package/src/daemon/message-types/diagnostics.ts +3 -22
- package/src/daemon/message-types/messages.ts +0 -2
- package/src/daemon/message-types/upgrades.ts +8 -0
- package/src/daemon/server.ts +30 -92
- package/src/events/domain-events.ts +2 -1
- package/src/followups/followup-store.ts +5 -2
- package/src/inbound/platform-callback-registration.ts +3 -3
- package/src/instrument.ts +8 -5
- package/src/memory/conversation-crud.ts +0 -236
- package/src/memory/conversation-title-service.ts +76 -11
- package/src/memory/db-init.ts +15 -11
- package/src/memory/indexer.ts +15 -106
- package/src/memory/items-extractor.ts +15 -1
- package/src/memory/job-handlers/conversation-starters.ts +4 -1
- package/src/memory/job-handlers/embedding.ts +0 -79
- package/src/memory/job-utils.ts +1 -1
- package/src/memory/jobs-store.ts +30 -13
- package/src/memory/jobs-worker.ts +31 -27
- package/src/memory/migrations/001-job-deferrals.ts +19 -0
- package/src/memory/migrations/004-entity-relation-dedup.ts +10 -0
- package/src/memory/migrations/005-fingerprint-scope-unique.ts +76 -0
- package/src/memory/migrations/006-scope-salted-fingerprints.ts +50 -0
- package/src/memory/migrations/007-assistant-id-to-self.ts +10 -0
- package/src/memory/migrations/008-remove-assistant-id-columns.ts +34 -0
- package/src/memory/migrations/009-llm-usage-events-drop-assistant-id.ts +26 -0
- package/src/memory/migrations/014-backfill-inbox-thread-state.ts +10 -0
- package/src/memory/migrations/015-drop-active-search-index.ts +17 -0
- package/src/memory/migrations/019-notification-tables-schema-migration.ts +12 -0
- package/src/memory/migrations/020-rename-macos-ios-channel-to-vellum.ts +121 -0
- package/src/memory/migrations/024-embedding-vector-blob.ts +74 -0
- package/src/memory/migrations/026a-embeddings-nullable-vector-json.ts +82 -0
- package/src/memory/migrations/036-normalize-phone-identities.ts +11 -0
- package/src/memory/migrations/116-messages-fts.ts +106 -1
- package/src/memory/migrations/126-backfill-guardian-principal-id.ts +52 -0
- package/src/memory/migrations/127-guardian-principal-id-not-null.ts +77 -0
- package/src/memory/migrations/134-contacts-notes-column.ts +13 -0
- package/src/memory/migrations/135-backfill-contact-interaction-stats.ts +20 -0
- package/src/memory/migrations/136-drop-assistant-id-columns.ts +52 -0
- package/src/memory/migrations/140-backfill-usage-cache-accounting.ts +13 -0
- package/src/memory/migrations/141-rename-verification-table.ts +54 -0
- package/src/memory/migrations/142-rename-verification-session-id-column.ts +25 -0
- package/src/memory/migrations/143-rename-guardian-verification-values.ts +35 -0
- package/src/memory/migrations/144-rename-voice-to-phone.ts +136 -0
- package/src/memory/migrations/145-drop-accounts-table.ts +32 -0
- package/src/memory/migrations/147-migrate-reminders-to-schedules.ts +14 -1
- package/src/memory/migrations/148-drop-reminders-table.ts +35 -1
- package/src/memory/migrations/150-oauth-apps-client-secret-path.ts +69 -1
- package/src/memory/migrations/162-guardian-timestamps-epoch-ms.ts +290 -0
- package/src/memory/migrations/169-rename-gmail-provider-key-to-google.ts +51 -1
- package/src/memory/migrations/174-rename-thread-starters-table.ts +47 -1
- package/src/memory/migrations/176-drop-capability-card-state.ts +13 -0
- package/src/memory/migrations/180-backfill-inline-attachments-to-disk.ts +16 -0
- package/src/memory/migrations/181-rename-thread-starters-checkpoints.ts +28 -1
- package/src/memory/migrations/189-drop-simplified-memory.ts +42 -0
- package/src/memory/migrations/190-call-session-skip-disclosure.ts +15 -0
- package/src/memory/migrations/191-backfill-audio-attachment-mime-types.ts +64 -0
- package/src/memory/migrations/192-contacts-user-file-column.ts +15 -0
- package/src/memory/migrations/index.ts +5 -3
- package/src/memory/migrations/registry.ts +90 -0
- package/src/memory/migrations/validate-migration-state.ts +137 -11
- package/src/memory/qdrant-circuit-breaker.ts +9 -0
- package/src/memory/qdrant-client.ts +4 -6
- package/src/memory/qdrant-manager.ts +64 -7
- package/src/memory/schema/calls.ts +1 -0
- package/src/memory/schema/contacts.ts +1 -0
- package/src/memory/schema/conversations.ts +0 -3
- package/src/memory/schema/index.ts +0 -2
- package/src/messaging/draft-store.ts +2 -2
- package/src/notifications/decision-engine.ts +4 -1
- package/src/oauth/connection-resolver.ts +6 -4
- package/src/permissions/checker.ts +0 -38
- package/src/permissions/defaults.ts +3 -3
- package/src/permissions/shell-identity.ts +76 -22
- package/src/permissions/trust-client.ts +2 -13
- package/src/permissions/trust-store.ts +8 -3
- package/src/permissions/types.ts +4 -2
- package/src/platform/client.ts +35 -7
- package/src/prompts/persona-resolver.ts +138 -0
- package/src/prompts/system-prompt.ts +36 -4
- package/src/prompts/templates/users/default.md +1 -0
- package/src/providers/registry.ts +27 -40
- package/src/runtime/auth/__tests__/credential-service.test.ts +0 -1
- package/src/runtime/auth/__tests__/external-assistant-id.test.ts +13 -68
- package/src/runtime/auth/external-assistant-id.ts +13 -59
- package/src/runtime/auth/route-policy.ts +29 -1
- package/src/runtime/auth/token-service.ts +53 -15
- package/src/runtime/channel-readiness-service.ts +1 -16
- package/src/runtime/http-server.ts +29 -2
- package/src/runtime/middleware/error-handler.ts +1 -9
- package/src/runtime/routes/audio-routes.ts +40 -0
- package/src/runtime/routes/btw-routes.ts +0 -17
- package/src/runtime/routes/conversation-management-routes.ts +0 -36
- package/src/runtime/routes/conversation-query-routes.ts +106 -2
- package/src/runtime/routes/conversation-routes.ts +4 -43
- package/src/runtime/routes/diagnostics-routes.ts +1 -477
- package/src/runtime/routes/identity-routes.ts +18 -29
- package/src/runtime/routes/inbound-stages/secret-ingress-check.ts +4 -33
- package/src/runtime/routes/inbound-stages/transcribe-audio.test.ts +1 -1
- package/src/runtime/routes/integrations/vercel.ts +89 -0
- package/src/runtime/routes/log-export-routes.ts +5 -0
- package/src/runtime/routes/memory-item-routes.test.ts +221 -3
- package/src/runtime/routes/memory-item-routes.ts +144 -4
- package/src/runtime/routes/migration-rollback-routes.ts +209 -0
- package/src/runtime/routes/migration-routes.ts +17 -1
- package/src/runtime/routes/notification-routes.ts +58 -0
- package/src/runtime/routes/schedule-routes.ts +65 -0
- package/src/runtime/routes/settings-routes.ts +41 -1
- package/src/runtime/routes/tts-routes.ts +86 -0
- package/src/runtime/routes/upgrade-broadcast-routes.ts +175 -0
- package/src/runtime/routes/workspace-commit-routes.ts +62 -0
- package/src/runtime/routes/workspace-routes.test.ts +22 -1
- package/src/runtime/routes/workspace-routes.ts +1 -1
- package/src/runtime/routes/workspace-utils.ts +86 -2
- package/src/schedule/schedule-store.ts +0 -21
- package/src/security/ces-credential-client.ts +59 -22
- package/src/security/ces-rpc-credential-backend.ts +85 -0
- package/src/security/credential-backend.ts +12 -88
- package/src/security/keychain-broker-client.ts +10 -2
- package/src/security/secure-keys.ts +94 -113
- package/src/skills/catalog-install.ts +13 -7
- package/src/skills/inline-command-render.ts +5 -1
- package/src/skills/inline-command-runner.ts +30 -2
- package/src/telemetry/usage-telemetry-reporter.ts +4 -2
- package/src/tools/calls/call-start.ts +1 -0
- package/src/tools/executor.ts +0 -4
- package/src/tools/memory/handlers.ts +1 -129
- package/src/tools/network/script-proxy/session-manager.ts +19 -4
- package/src/tools/network/web-fetch.ts +3 -1
- package/src/tools/permission-checker.ts +18 -0
- package/src/tools/skills/execute.ts +1 -1
- package/src/tools/skills/load.ts +9 -2
- package/src/tools/types.ts +0 -8
- package/src/util/errors.ts +0 -12
- package/src/util/platform.ts +8 -55
- package/src/util/xml.ts +8 -0
- package/src/workspace/git-service.ts +5 -2
- package/src/workspace/heartbeat-service.ts +5 -24
- package/src/workspace/migrations/001-avatar-rename.ts +15 -0
- package/src/workspace/migrations/003-seed-device-id.ts +17 -1
- package/src/workspace/migrations/004-extract-collect-usage-data.ts +33 -0
- package/src/workspace/migrations/005-add-send-diagnostics.ts +3 -0
- package/src/workspace/migrations/006-services-config.ts +49 -0
- package/src/workspace/migrations/007-web-search-provider-rename.ts +27 -0
- package/src/workspace/migrations/008-voice-timeout-and-max-steps.ts +3 -0
- package/src/workspace/migrations/009-backfill-conversation-disk-view.ts +4 -0
- package/src/workspace/migrations/010-app-dir-rename.ts +78 -0
- package/src/workspace/migrations/011-backfill-installation-id.ts +11 -0
- package/src/workspace/migrations/012-rename-conversation-disk-view-dirs.ts +44 -0
- package/src/workspace/migrations/013-repair-conversation-disk-view.ts +5 -0
- package/src/workspace/migrations/015-migrate-credentials-to-keychain.ts +153 -0
- package/src/workspace/migrations/016-extract-feature-flags-to-protected.ts +156 -0
- package/src/workspace/migrations/016-migrate-credentials-from-keychain.ts +150 -0
- package/src/workspace/migrations/017-seed-persona-dirs.ts +95 -0
- package/src/workspace/migrations/migrate-to-workspace-volume.ts +23 -1
- package/src/workspace/migrations/registry.ts +8 -0
- package/src/workspace/migrations/runner.ts +106 -2
- package/src/workspace/migrations/types.ts +4 -0
- package/src/__tests__/archive-recall.test.ts +0 -560
- package/src/__tests__/claude-code-skill-regression.test.ts +0 -206
- package/src/__tests__/claude-code-tool-profiles.test.ts +0 -99
- package/src/__tests__/conversation-memory-dirty-tail.test.ts +0 -150
- package/src/__tests__/conversation-switch-memory-reduction.test.ts +0 -474
- package/src/__tests__/db-memory-archive-migration.test.ts +0 -372
- package/src/__tests__/db-memory-brief-state-migration.test.ts +0 -213
- package/src/__tests__/db-memory-reducer-checkpoints.test.ts +0 -273
- package/src/__tests__/diagnostics-export.test.ts +0 -288
- package/src/__tests__/local-gateway-health.test.ts +0 -209
- package/src/__tests__/memory-brief-open-loops.test.ts +0 -530
- package/src/__tests__/memory-brief-time.test.ts +0 -285
- package/src/__tests__/memory-brief-wrapper.test.ts +0 -311
- package/src/__tests__/memory-chunk-archive.test.ts +0 -400
- package/src/__tests__/memory-chunk-dual-write.test.ts +0 -453
- package/src/__tests__/memory-episode-archive.test.ts +0 -370
- package/src/__tests__/memory-episode-dual-write.test.ts +0 -626
- package/src/__tests__/memory-observation-archive.test.ts +0 -375
- package/src/__tests__/memory-observation-dual-write.test.ts +0 -318
- package/src/__tests__/memory-reducer-job.test.ts +0 -538
- package/src/__tests__/memory-reducer-scheduling.test.ts +0 -473
- package/src/__tests__/memory-reducer-store.test.ts +0 -728
- package/src/__tests__/memory-reducer-types.test.ts +0 -707
- package/src/__tests__/memory-reducer.test.ts +0 -704
- package/src/__tests__/memory-simplified-config.test.ts +0 -281
- package/src/__tests__/secret-ingress-handler.test.ts +0 -120
- package/src/__tests__/simplified-memory-e2e.test.ts +0 -666
- package/src/__tests__/simplified-memory-runtime.test.ts +0 -616
- package/src/__tests__/swarm-conversation-integration.test.ts +0 -358
- package/src/__tests__/swarm-dag-pathological.test.ts +0 -547
- package/src/__tests__/swarm-orchestrator.test.ts +0 -463
- package/src/__tests__/swarm-plan-validator.test.ts +0 -384
- package/src/__tests__/swarm-recursion.test.ts +0 -197
- package/src/__tests__/swarm-router-planner.test.ts +0 -234
- package/src/__tests__/swarm-tool.test.ts +0 -185
- package/src/__tests__/swarm-worker-backend.test.ts +0 -144
- package/src/__tests__/swarm-worker-runner.test.ts +0 -288
- package/src/commands/__tests__/cc-command-registry.test.ts +0 -396
- package/src/commands/cc-command-registry.ts +0 -248
- package/src/config/bundled-skills/claude-code/SKILL.md +0 -53
- package/src/config/bundled-skills/claude-code/TOOLS.json +0 -47
- package/src/config/bundled-skills/claude-code/tools/claude-code.ts +0 -12
- package/src/config/bundled-skills/orchestration/SKILL.md +0 -33
- package/src/config/bundled-skills/orchestration/TOOLS.json +0 -35
- package/src/config/bundled-skills/orchestration/tools/swarm-delegate.ts +0 -12
- package/src/config/schemas/memory-simplified.ts +0 -101
- package/src/config/schemas/swarm.ts +0 -82
- package/src/logfire.ts +0 -135
- package/src/memory/archive-recall.ts +0 -516
- package/src/memory/archive-store.ts +0 -400
- package/src/memory/brief-formatting.ts +0 -33
- package/src/memory/brief-open-loops.ts +0 -266
- package/src/memory/brief-time.ts +0 -162
- package/src/memory/brief.ts +0 -75
- package/src/memory/job-handlers/backfill-simplified-memory.ts +0 -462
- package/src/memory/job-handlers/reduce-conversation-memory.ts +0 -229
- package/src/memory/migrations/185-memory-brief-state.ts +0 -52
- package/src/memory/migrations/186-memory-archive.ts +0 -109
- package/src/memory/migrations/187-memory-reducer-checkpoints.ts +0 -19
- package/src/memory/reducer-scheduler.ts +0 -242
- package/src/memory/reducer-store.ts +0 -271
- package/src/memory/reducer-types.ts +0 -106
- package/src/memory/reducer.ts +0 -467
- package/src/memory/schema/memory-archive.ts +0 -121
- package/src/memory/schema/memory-brief.ts +0 -55
- package/src/runtime/local-gateway-health.ts +0 -275
- package/src/security/secret-ingress.ts +0 -68
- package/src/swarm/backend-claude-code.ts +0 -225
- package/src/swarm/checkpoint.ts +0 -137
- package/src/swarm/graph-utils.ts +0 -53
- package/src/swarm/index.ts +0 -55
- package/src/swarm/limits.ts +0 -66
- package/src/swarm/orchestrator.ts +0 -424
- package/src/swarm/plan-validator.ts +0 -117
- package/src/swarm/router-planner.ts +0 -162
- package/src/swarm/router-prompts.ts +0 -39
- package/src/swarm/synthesizer.ts +0 -81
- package/src/swarm/types.ts +0 -72
- package/src/swarm/worker-backend.ts +0 -131
- package/src/swarm/worker-prompts.ts +0 -80
- package/src/swarm/worker-runner.ts +0 -170
- package/src/tools/claude-code/claude-code.ts +0 -610
- package/src/tools/swarm/delegate.ts +0 -205
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Route handlers for Vercel integration config endpoints.
|
|
3
|
+
*
|
|
4
|
+
* GET /v1/integrations/vercel/config — check if a Vercel API token is stored
|
|
5
|
+
* POST /v1/integrations/vercel/config — set or delete token (dispatched via action field)
|
|
6
|
+
* DELETE /v1/integrations/vercel/config — delete the stored Vercel API token
|
|
7
|
+
*
|
|
8
|
+
* The Swift client sends all mutations as POST with an `action` field
|
|
9
|
+
* ("set" or "delete") rather than using HTTP verbs directly.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import {
|
|
13
|
+
deleteVercelConfig,
|
|
14
|
+
getVercelConfig,
|
|
15
|
+
setVercelConfig,
|
|
16
|
+
} from "../../../daemon/handlers/config-vercel.js";
|
|
17
|
+
import type { RouteDefinition } from "../../http-router.js";
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* GET /v1/integrations/vercel/config
|
|
21
|
+
*/
|
|
22
|
+
export async function handleGetVercelConfig(): Promise<Response> {
|
|
23
|
+
const result = await getVercelConfig();
|
|
24
|
+
return Response.json(result);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* POST /v1/integrations/vercel/config
|
|
29
|
+
*
|
|
30
|
+
* Body: { action: "set" | "delete"; apiToken?: string }
|
|
31
|
+
*
|
|
32
|
+
* The Swift client uses POST for both set and delete operations,
|
|
33
|
+
* distinguished by the `action` field.
|
|
34
|
+
*/
|
|
35
|
+
export async function handlePostVercelConfig(req: Request): Promise<Response> {
|
|
36
|
+
const body = (await req.json()) as {
|
|
37
|
+
action?: "get" | "set" | "delete";
|
|
38
|
+
apiToken?: string;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
switch (body.action) {
|
|
42
|
+
case "delete": {
|
|
43
|
+
const result = await deleteVercelConfig();
|
|
44
|
+
return Response.json(result);
|
|
45
|
+
}
|
|
46
|
+
case "get": {
|
|
47
|
+
const result = await getVercelConfig();
|
|
48
|
+
return Response.json(result);
|
|
49
|
+
}
|
|
50
|
+
case "set":
|
|
51
|
+
default: {
|
|
52
|
+
const result = await setVercelConfig(body.apiToken);
|
|
53
|
+
const status = result.success ? 200 : 400;
|
|
54
|
+
return Response.json(result, { status });
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* DELETE /v1/integrations/vercel/config
|
|
61
|
+
*/
|
|
62
|
+
export async function handleDeleteVercelConfig(): Promise<Response> {
|
|
63
|
+
const result = await deleteVercelConfig();
|
|
64
|
+
return Response.json(result);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// ---------------------------------------------------------------------------
|
|
68
|
+
// Route definitions
|
|
69
|
+
// ---------------------------------------------------------------------------
|
|
70
|
+
|
|
71
|
+
export function vercelRouteDefinitions(): RouteDefinition[] {
|
|
72
|
+
return [
|
|
73
|
+
{
|
|
74
|
+
endpoint: "integrations/vercel/config",
|
|
75
|
+
method: "GET",
|
|
76
|
+
handler: () => handleGetVercelConfig(),
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
endpoint: "integrations/vercel/config",
|
|
80
|
+
method: "POST",
|
|
81
|
+
handler: async ({ req }) => handlePostVercelConfig(req),
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
endpoint: "integrations/vercel/config",
|
|
85
|
+
method: "DELETE",
|
|
86
|
+
handler: async () => handleDeleteVercelConfig(),
|
|
87
|
+
},
|
|
88
|
+
];
|
|
89
|
+
}
|
|
@@ -37,6 +37,7 @@ import {
|
|
|
37
37
|
getWorkspaceConfigPath,
|
|
38
38
|
getWorkspaceDir,
|
|
39
39
|
} from "../../util/platform.js";
|
|
40
|
+
import { APP_VERSION, COMMIT_SHA } from "../../version.js";
|
|
40
41
|
import { httpError } from "../http-errors.js";
|
|
41
42
|
import type { RouteDefinition } from "../http-router.js";
|
|
42
43
|
|
|
@@ -264,12 +265,16 @@ async function handleExport(body: ExportRequestBody): Promise<Response> {
|
|
|
264
265
|
? {
|
|
265
266
|
type: "conversation-export" as const,
|
|
266
267
|
conversationId,
|
|
268
|
+
assistantVersion: APP_VERSION,
|
|
269
|
+
commitSha: COMMIT_SHA,
|
|
267
270
|
...(startTime !== undefined ? { startTime } : {}),
|
|
268
271
|
...(endTime !== undefined ? { endTime } : {}),
|
|
269
272
|
exportedAt: new Date().toISOString(),
|
|
270
273
|
}
|
|
271
274
|
: {
|
|
272
275
|
type: "global-export" as const,
|
|
276
|
+
assistantVersion: APP_VERSION,
|
|
277
|
+
commitSha: COMMIT_SHA,
|
|
273
278
|
exportedAt: new Date().toISOString(),
|
|
274
279
|
};
|
|
275
280
|
writeFileSync(
|
|
@@ -37,13 +37,55 @@ mock.module("../../util/logger.js", () => ({
|
|
|
37
37
|
}),
|
|
38
38
|
}));
|
|
39
39
|
|
|
40
|
-
// Stub config loader
|
|
40
|
+
// Stub config loader — returns a config with memory enabled by default
|
|
41
41
|
mock.module("../../config/loader.js", () => ({
|
|
42
|
-
loadConfig: () =>
|
|
43
|
-
getConfig: () =>
|
|
42
|
+
loadConfig: () => mockConfig,
|
|
43
|
+
getConfig: () => mockConfig,
|
|
44
44
|
invalidateConfigCache: () => {},
|
|
45
45
|
}));
|
|
46
46
|
|
|
47
|
+
// ── Controllable mocks for semantic search ─────────────────────────────
|
|
48
|
+
const mockConfig: unknown = {};
|
|
49
|
+
|
|
50
|
+
let mockBackendStatus: {
|
|
51
|
+
enabled: boolean;
|
|
52
|
+
provider: string | null;
|
|
53
|
+
model: string | null;
|
|
54
|
+
} = { enabled: false, provider: null, model: null };
|
|
55
|
+
|
|
56
|
+
const mockEmbedResult: {
|
|
57
|
+
provider: string;
|
|
58
|
+
model: string;
|
|
59
|
+
vectors: number[][];
|
|
60
|
+
} = { provider: "local", model: "test", vectors: [[0.1, 0.2, 0.3]] };
|
|
61
|
+
|
|
62
|
+
let mockHybridSearchResults: Array<{
|
|
63
|
+
id: string;
|
|
64
|
+
score: number;
|
|
65
|
+
payload: Record<string, unknown>;
|
|
66
|
+
}> = [];
|
|
67
|
+
|
|
68
|
+
mock.module("../../memory/embedding-backend.js", () => ({
|
|
69
|
+
getMemoryBackendStatus: async () => mockBackendStatus,
|
|
70
|
+
embedWithBackend: async () => mockEmbedResult,
|
|
71
|
+
generateSparseEmbedding: () => ({
|
|
72
|
+
indices: [0, 1, 2],
|
|
73
|
+
values: [0.5, 0.3, 0.2],
|
|
74
|
+
}),
|
|
75
|
+
}));
|
|
76
|
+
|
|
77
|
+
mock.module("../../memory/qdrant-client.js", () => ({
|
|
78
|
+
getQdrantClient: () => ({
|
|
79
|
+
hybridSearch: async () => [...mockHybridSearchResults],
|
|
80
|
+
searchWithFilter: async () => [...mockHybridSearchResults],
|
|
81
|
+
}),
|
|
82
|
+
initQdrantClient: () => {},
|
|
83
|
+
}));
|
|
84
|
+
|
|
85
|
+
mock.module("../../memory/qdrant-circuit-breaker.js", () => ({
|
|
86
|
+
withQdrantBreaker: async (fn: () => Promise<unknown>) => fn(),
|
|
87
|
+
}));
|
|
88
|
+
|
|
47
89
|
import { and, eq } from "drizzle-orm";
|
|
48
90
|
|
|
49
91
|
import { getDb, initializeDb, resetDb } from "../../memory/db.js";
|
|
@@ -392,6 +434,182 @@ describe("Memory Item Routes", () => {
|
|
|
392
434
|
const res = await handler(ctx);
|
|
393
435
|
expect(res.status).toBe(400);
|
|
394
436
|
});
|
|
437
|
+
|
|
438
|
+
// ── Semantic / hybrid search ──────────────────────────────────────
|
|
439
|
+
|
|
440
|
+
test("uses semantic search when embedding backend is available", async () => {
|
|
441
|
+
insertItem({
|
|
442
|
+
id: "i1",
|
|
443
|
+
kind: "preference",
|
|
444
|
+
subject: "dark mode",
|
|
445
|
+
statement: "User prefers dark mode",
|
|
446
|
+
});
|
|
447
|
+
insertItem({
|
|
448
|
+
id: "i2",
|
|
449
|
+
kind: "identity",
|
|
450
|
+
subject: "name",
|
|
451
|
+
statement: "User name is Alice",
|
|
452
|
+
});
|
|
453
|
+
|
|
454
|
+
// Enable semantic search
|
|
455
|
+
mockBackendStatus = {
|
|
456
|
+
enabled: true,
|
|
457
|
+
provider: "local",
|
|
458
|
+
model: "test",
|
|
459
|
+
};
|
|
460
|
+
// Qdrant returns i2 first (higher relevance), then i1
|
|
461
|
+
mockHybridSearchResults = [
|
|
462
|
+
{
|
|
463
|
+
id: "pt-2",
|
|
464
|
+
score: 0.95,
|
|
465
|
+
payload: { target_type: "item", target_id: "i2" },
|
|
466
|
+
},
|
|
467
|
+
{
|
|
468
|
+
id: "pt-1",
|
|
469
|
+
score: 0.7,
|
|
470
|
+
payload: { target_type: "item", target_id: "i1" },
|
|
471
|
+
},
|
|
472
|
+
];
|
|
473
|
+
|
|
474
|
+
const ctx = makeCtx({ search: "alice" });
|
|
475
|
+
const res = await handler(ctx);
|
|
476
|
+
const body = (await res.json()) as {
|
|
477
|
+
items: Array<{ id: string }>;
|
|
478
|
+
total: number;
|
|
479
|
+
};
|
|
480
|
+
|
|
481
|
+
// Results in Qdrant relevance order (i2 first)
|
|
482
|
+
expect(body.items.length).toBe(2);
|
|
483
|
+
expect(body.items[0].id).toBe("i2");
|
|
484
|
+
expect(body.items[1].id).toBe("i1");
|
|
485
|
+
expect(body.total).toBe(2);
|
|
486
|
+
|
|
487
|
+
// Reset
|
|
488
|
+
mockBackendStatus = { enabled: false, provider: null, model: null };
|
|
489
|
+
mockHybridSearchResults = [];
|
|
490
|
+
});
|
|
491
|
+
|
|
492
|
+
test("falls back to SQL LIKE when backend is unavailable", async () => {
|
|
493
|
+
insertItem({
|
|
494
|
+
id: "i1",
|
|
495
|
+
kind: "preference",
|
|
496
|
+
subject: "dark mode",
|
|
497
|
+
statement: "User prefers dark mode",
|
|
498
|
+
});
|
|
499
|
+
insertItem({
|
|
500
|
+
id: "i2",
|
|
501
|
+
kind: "identity",
|
|
502
|
+
subject: "name",
|
|
503
|
+
statement: "User name is Alice",
|
|
504
|
+
});
|
|
505
|
+
|
|
506
|
+
// Backend unavailable
|
|
507
|
+
mockBackendStatus = { enabled: false, provider: null, model: null };
|
|
508
|
+
mockHybridSearchResults = [];
|
|
509
|
+
|
|
510
|
+
const ctx = makeCtx({ search: "dark" });
|
|
511
|
+
const res = await handler(ctx);
|
|
512
|
+
const body = (await res.json()) as {
|
|
513
|
+
items: Array<{ id: string }>;
|
|
514
|
+
total: number;
|
|
515
|
+
};
|
|
516
|
+
|
|
517
|
+
// SQL LIKE fallback finds "dark" in subject/statement
|
|
518
|
+
expect(body.total).toBe(1);
|
|
519
|
+
expect(body.items[0].id).toBe("i1");
|
|
520
|
+
});
|
|
521
|
+
|
|
522
|
+
test("semantic search respects pagination", async () => {
|
|
523
|
+
insertItem({
|
|
524
|
+
id: "i1",
|
|
525
|
+
kind: "preference",
|
|
526
|
+
subject: "s1",
|
|
527
|
+
statement: "first item",
|
|
528
|
+
});
|
|
529
|
+
insertItem({
|
|
530
|
+
id: "i2",
|
|
531
|
+
kind: "preference",
|
|
532
|
+
subject: "s2",
|
|
533
|
+
statement: "second item",
|
|
534
|
+
});
|
|
535
|
+
insertItem({
|
|
536
|
+
id: "i3",
|
|
537
|
+
kind: "preference",
|
|
538
|
+
subject: "s3",
|
|
539
|
+
statement: "third item",
|
|
540
|
+
});
|
|
541
|
+
|
|
542
|
+
mockBackendStatus = {
|
|
543
|
+
enabled: true,
|
|
544
|
+
provider: "local",
|
|
545
|
+
model: "test",
|
|
546
|
+
};
|
|
547
|
+
mockHybridSearchResults = [
|
|
548
|
+
{
|
|
549
|
+
id: "pt-1",
|
|
550
|
+
score: 0.9,
|
|
551
|
+
payload: { target_type: "item", target_id: "i1" },
|
|
552
|
+
},
|
|
553
|
+
{
|
|
554
|
+
id: "pt-2",
|
|
555
|
+
score: 0.8,
|
|
556
|
+
payload: { target_type: "item", target_id: "i2" },
|
|
557
|
+
},
|
|
558
|
+
{
|
|
559
|
+
id: "pt-3",
|
|
560
|
+
score: 0.7,
|
|
561
|
+
payload: { target_type: "item", target_id: "i3" },
|
|
562
|
+
},
|
|
563
|
+
];
|
|
564
|
+
|
|
565
|
+
// Request page 2 (offset=1, limit=1)
|
|
566
|
+
const ctx = makeCtx({ search: "item", limit: "1", offset: "1" });
|
|
567
|
+
const res = await handler(ctx);
|
|
568
|
+
const body = (await res.json()) as {
|
|
569
|
+
items: Array<{ id: string }>;
|
|
570
|
+
total: number;
|
|
571
|
+
};
|
|
572
|
+
|
|
573
|
+
expect(body.items.length).toBe(1);
|
|
574
|
+
expect(body.items[0].id).toBe("i2"); // second by relevance
|
|
575
|
+
expect(body.total).toBe(3);
|
|
576
|
+
|
|
577
|
+
// Reset
|
|
578
|
+
mockBackendStatus = { enabled: false, provider: null, model: null };
|
|
579
|
+
mockHybridSearchResults = [];
|
|
580
|
+
});
|
|
581
|
+
|
|
582
|
+
test("falls back to SQL when semantic returns empty results", async () => {
|
|
583
|
+
insertItem({
|
|
584
|
+
id: "i1",
|
|
585
|
+
kind: "preference",
|
|
586
|
+
subject: "dark mode",
|
|
587
|
+
statement: "User prefers dark mode",
|
|
588
|
+
});
|
|
589
|
+
|
|
590
|
+
mockBackendStatus = {
|
|
591
|
+
enabled: true,
|
|
592
|
+
provider: "local",
|
|
593
|
+
model: "test",
|
|
594
|
+
};
|
|
595
|
+
// Qdrant returns nothing
|
|
596
|
+
mockHybridSearchResults = [];
|
|
597
|
+
|
|
598
|
+
const ctx = makeCtx({ search: "dark" });
|
|
599
|
+
const res = await handler(ctx);
|
|
600
|
+
const body = (await res.json()) as {
|
|
601
|
+
items: Array<{ id: string }>;
|
|
602
|
+
total: number;
|
|
603
|
+
};
|
|
604
|
+
|
|
605
|
+
// Falls through to SQL LIKE
|
|
606
|
+
expect(body.total).toBe(1);
|
|
607
|
+
expect(body.items[0].id).toBe("i1");
|
|
608
|
+
|
|
609
|
+
// Reset
|
|
610
|
+
mockBackendStatus = { enabled: false, provider: null, model: null };
|
|
611
|
+
mockHybridSearchResults = [];
|
|
612
|
+
});
|
|
395
613
|
});
|
|
396
614
|
|
|
397
615
|
// =========================================================================
|
|
@@ -11,18 +11,29 @@
|
|
|
11
11
|
import { and, asc, count, desc, eq, inArray, like, ne, or } from "drizzle-orm";
|
|
12
12
|
import { v4 as uuid } from "uuid";
|
|
13
13
|
|
|
14
|
+
import { getConfig } from "../../config/loader.js";
|
|
14
15
|
import { getDb } from "../../memory/db.js";
|
|
16
|
+
import {
|
|
17
|
+
embedWithBackend,
|
|
18
|
+
generateSparseEmbedding,
|
|
19
|
+
getMemoryBackendStatus,
|
|
20
|
+
} from "../../memory/embedding-backend.js";
|
|
15
21
|
import { computeMemoryFingerprint } from "../../memory/fingerprint.js";
|
|
16
22
|
import { enqueueMemoryJob } from "../../memory/jobs-store.js";
|
|
23
|
+
import { withQdrantBreaker } from "../../memory/qdrant-circuit-breaker.js";
|
|
24
|
+
import { getQdrantClient } from "../../memory/qdrant-client.js";
|
|
17
25
|
import {
|
|
18
26
|
conversations,
|
|
19
27
|
memoryEmbeddings,
|
|
20
28
|
memoryItems,
|
|
21
29
|
} from "../../memory/schema.js";
|
|
30
|
+
import { getLogger } from "../../util/logger.js";
|
|
22
31
|
import { truncate } from "../../util/truncate.js";
|
|
23
32
|
import { httpError } from "../http-errors.js";
|
|
24
33
|
import type { RouteContext, RouteDefinition } from "../http-router.js";
|
|
25
34
|
|
|
35
|
+
const log = getLogger("memory-item-routes");
|
|
36
|
+
|
|
26
37
|
// ---------------------------------------------------------------------------
|
|
27
38
|
// Constants
|
|
28
39
|
// ---------------------------------------------------------------------------
|
|
@@ -34,6 +45,7 @@ const VALID_KINDS = [
|
|
|
34
45
|
"decision",
|
|
35
46
|
"constraint",
|
|
36
47
|
"event",
|
|
48
|
+
"capability",
|
|
37
49
|
] as const;
|
|
38
50
|
|
|
39
51
|
type MemoryItemKind = (typeof VALID_KINDS)[number];
|
|
@@ -116,11 +128,80 @@ function buildConversationTitleMap(
|
|
|
116
128
|
return map;
|
|
117
129
|
}
|
|
118
130
|
|
|
131
|
+
// ---------------------------------------------------------------------------
|
|
132
|
+
// Semantic search helper
|
|
133
|
+
// ---------------------------------------------------------------------------
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Attempt hybrid semantic search for memory items via Qdrant.
|
|
137
|
+
* Returns ordered item IDs + total count on success, or `null` when
|
|
138
|
+
* the embedding backend / Qdrant is unavailable (caller falls back to SQL).
|
|
139
|
+
*/
|
|
140
|
+
async function searchItemsSemantic(
|
|
141
|
+
query: string,
|
|
142
|
+
fetchLimit: number,
|
|
143
|
+
kindFilter: string | null,
|
|
144
|
+
statusFilter: string,
|
|
145
|
+
): Promise<{ ids: string[]; total: number } | null> {
|
|
146
|
+
try {
|
|
147
|
+
const config = getConfig();
|
|
148
|
+
const backendStatus = await getMemoryBackendStatus(config);
|
|
149
|
+
if (!backendStatus.provider) return null;
|
|
150
|
+
|
|
151
|
+
const embedded = await embedWithBackend(config, [query]);
|
|
152
|
+
const queryVector = embedded.vectors[0];
|
|
153
|
+
if (!queryVector) return null;
|
|
154
|
+
|
|
155
|
+
const sparse = generateSparseEmbedding(query);
|
|
156
|
+
const sparseVector = { indices: sparse.indices, values: sparse.values };
|
|
157
|
+
|
|
158
|
+
// Build Qdrant filter — items only, exclude sentinel
|
|
159
|
+
const mustConditions: Array<Record<string, unknown>> = [
|
|
160
|
+
{ key: "target_type", match: { value: "item" } },
|
|
161
|
+
];
|
|
162
|
+
if (statusFilter && statusFilter !== "all") {
|
|
163
|
+
mustConditions.push({ key: "status", match: { value: statusFilter } });
|
|
164
|
+
}
|
|
165
|
+
if (kindFilter) {
|
|
166
|
+
mustConditions.push({ key: "kind", match: { value: kindFilter } });
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const filter = {
|
|
170
|
+
must: mustConditions,
|
|
171
|
+
must_not: [
|
|
172
|
+
{ key: "_meta", match: { value: true } },
|
|
173
|
+
],
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
const qdrant = getQdrantClient();
|
|
177
|
+
const results = await withQdrantBreaker(() =>
|
|
178
|
+
qdrant.hybridSearch({
|
|
179
|
+
denseVector: queryVector,
|
|
180
|
+
sparseVector,
|
|
181
|
+
filter,
|
|
182
|
+
limit: fetchLimit,
|
|
183
|
+
prefetchLimit: fetchLimit,
|
|
184
|
+
}),
|
|
185
|
+
);
|
|
186
|
+
|
|
187
|
+
const ids = results.map((r) => r.payload.target_id);
|
|
188
|
+
|
|
189
|
+
// Use the vector search result count as the pagination total.
|
|
190
|
+
// A DB-wide COUNT would include items with no embedding yet (lagging) and
|
|
191
|
+
// items irrelevant to the search query, inflating the total and causing
|
|
192
|
+
// clients to paginate into empty pages.
|
|
193
|
+
return { ids, total: ids.length };
|
|
194
|
+
} catch (err) {
|
|
195
|
+
log.warn({ err }, "Semantic memory search failed, falling back to SQL");
|
|
196
|
+
return null;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
119
200
|
// ---------------------------------------------------------------------------
|
|
120
201
|
// GET /v1/memory-items
|
|
121
202
|
// ---------------------------------------------------------------------------
|
|
122
203
|
|
|
123
|
-
export function handleListMemoryItems(url: URL): Response {
|
|
204
|
+
export async function handleListMemoryItems(url: URL): Promise<Response> {
|
|
124
205
|
const kindParam = url.searchParams.get("kind");
|
|
125
206
|
const statusParam = url.searchParams.get("status") ?? "active";
|
|
126
207
|
const searchParam = url.searchParams.get("search");
|
|
@@ -155,10 +236,69 @@ export function handleListMemoryItems(url: URL): Response {
|
|
|
155
236
|
|
|
156
237
|
const db = getDb();
|
|
157
238
|
|
|
158
|
-
//
|
|
239
|
+
// ── Semantic search path ────────────────────────────────────────────
|
|
240
|
+
// When a search query is present, try Qdrant hybrid search first.
|
|
241
|
+
// Falls back to SQL LIKE when embeddings / Qdrant are unavailable.
|
|
242
|
+
if (searchParam) {
|
|
243
|
+
const semanticResult = await searchItemsSemantic(
|
|
244
|
+
searchParam,
|
|
245
|
+
limitParam + offsetParam,
|
|
246
|
+
kindParam,
|
|
247
|
+
statusParam,
|
|
248
|
+
);
|
|
249
|
+
|
|
250
|
+
if (semanticResult && semanticResult.ids.length > 0) {
|
|
251
|
+
// Slice for pagination
|
|
252
|
+
const pageIds = semanticResult.ids.slice(
|
|
253
|
+
offsetParam,
|
|
254
|
+
offsetParam + limitParam,
|
|
255
|
+
);
|
|
256
|
+
|
|
257
|
+
if (pageIds.length === 0) {
|
|
258
|
+
return Response.json({ items: [], total: semanticResult.total });
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// Re-apply the same DB-side filters used in the SQL path as defense-
|
|
262
|
+
// in-depth against stale Qdrant payloads leaking deleted/mismatched rows.
|
|
263
|
+
const hydrationConditions = [
|
|
264
|
+
inArray(memoryItems.id, pageIds),
|
|
265
|
+
];
|
|
266
|
+
if (statusParam && statusParam !== "all") {
|
|
267
|
+
hydrationConditions.push(eq(memoryItems.status, statusParam));
|
|
268
|
+
}
|
|
269
|
+
if (kindParam) {
|
|
270
|
+
hydrationConditions.push(eq(memoryItems.kind, kindParam));
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
const rows = db
|
|
274
|
+
.select()
|
|
275
|
+
.from(memoryItems)
|
|
276
|
+
.where(and(...hydrationConditions))
|
|
277
|
+
.all();
|
|
278
|
+
|
|
279
|
+
// Preserve Qdrant relevance ordering
|
|
280
|
+
const idOrder = new Map(pageIds.map((id, i) => [id, i]));
|
|
281
|
+
rows.sort((a, b) => (idOrder.get(a.id) ?? 0) - (idOrder.get(b.id) ?? 0));
|
|
282
|
+
|
|
283
|
+
const titleMap = buildConversationTitleMap(
|
|
284
|
+
db,
|
|
285
|
+
rows.map((i) => i.scopeId),
|
|
286
|
+
);
|
|
287
|
+
const enrichedItems = rows.map((item) => ({
|
|
288
|
+
...item,
|
|
289
|
+
scopeLabel: resolveScopeLabel(item.scopeId, titleMap),
|
|
290
|
+
}));
|
|
291
|
+
|
|
292
|
+
return Response.json({
|
|
293
|
+
items: enrichedItems,
|
|
294
|
+
total: semanticResult.total,
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
// semanticResult was null (Qdrant unavailable) or empty — fall through to SQL
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// ── SQL path (default or fallback) ──────────────────────────────────
|
|
159
301
|
const conditions = [];
|
|
160
|
-
// Hide system-managed capability memories (skill announcements) from the UI
|
|
161
|
-
conditions.push(ne(memoryItems.kind, "capability"));
|
|
162
302
|
if (statusParam && statusParam !== "all") {
|
|
163
303
|
conditions.push(eq(memoryItems.status, statusParam));
|
|
164
304
|
}
|