@vellumai/assistant 0.10.4 → 0.10.5-dev.202607022329.d89bf2f
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ARCHITECTURE.md +94 -91
- package/Dockerfile +5 -16
- package/README.md +7 -10
- package/bun.lock +0 -4
- package/docs/architecture/security.md +20 -20
- package/docs/stt-provider-onboarding.md +4 -5
- package/docs/trusted-contact-access.md +7 -7
- package/eslint-rules/cli-no-daemon-internals.js +4 -0
- package/eslint.config.mjs +7 -1
- package/knip.json +0 -1
- package/node_modules/@vellumai/gateway-client/src/__tests__/contact-read-contracts.test.ts +36 -0
- package/node_modules/@vellumai/gateway-client/src/__tests__/invite-contract.test.ts +233 -0
- package/node_modules/@vellumai/gateway-client/src/__tests__/trust-verdict-contract.test.ts +40 -0
- package/node_modules/@vellumai/gateway-client/src/gateway-ipc-contracts.ts +20 -8
- package/node_modules/@vellumai/gateway-client/src/inbound-contract.ts +10 -0
- package/node_modules/@vellumai/gateway-client/src/index.ts +33 -0
- package/node_modules/@vellumai/gateway-client/src/invite-contract.ts +216 -0
- package/node_modules/@vellumai/gateway-client/src/outbound-contract.ts +91 -0
- package/node_modules/@vellumai/gateway-client/src/trust-verdict-contract.ts +39 -3
- package/node_modules/@vellumai/service-contracts/src/__tests__/ingress.test.ts +0 -8
- package/node_modules/@vellumai/service-contracts/src/__tests__/package-boundary.test.ts +1 -5
- package/node_modules/@vellumai/service-contracts/src/twilio-ingress.ts +3 -14
- package/openapi.yaml +1073 -225
- package/package.json +1 -4
- package/scripts/test.sh +35 -13
- package/src/__tests__/access-request-seed-content-blocks.test.ts +60 -4
- package/src/__tests__/acp-session.test.ts +4 -1
- package/src/__tests__/actor-trust-resolver-address-fallback.test.ts +0 -6
- package/src/__tests__/add-message-preassigned-id.test.ts +56 -0
- package/src/__tests__/agent-loop-set-system-prompt.test.ts +139 -0
- package/src/__tests__/app-conversation-ids-backfill.test.ts +3 -0
- package/src/__tests__/app-source-watcher.test.ts +11 -0
- package/src/__tests__/approval-interception-trust-gates.test.ts +148 -3
- package/src/__tests__/assistant-attachments.test.ts +62 -4
- package/src/__tests__/assistant-id-boundary-guard.test.ts +0 -1
- package/src/__tests__/attachments-store-heic-normalize.test.ts +230 -0
- package/src/__tests__/attachments-store.test.ts +3 -0
- package/src/__tests__/background-shell-bash.test.ts +34 -0
- package/src/__tests__/background-shell-host-bash.test.ts +12 -0
- package/src/__tests__/background-tool-registry.test.ts +97 -0
- package/src/__tests__/background-tool-routes.test.ts +72 -1
- package/src/__tests__/background-workers-disk-pressure.test.ts +2 -1
- package/src/__tests__/btw-routes.test.ts +57 -3
- package/src/__tests__/call-controller.test.ts +474 -84
- package/src/__tests__/call-domain.test.ts +48 -1
- package/src/__tests__/call-pointer-messages.test.ts +57 -19
- package/src/__tests__/call-pointer-no-hardcoded-copy.guard.test.ts +2 -6
- package/src/__tests__/call-routes-http.test.ts +15 -14
- package/src/__tests__/call-setup-flow-invite.test.ts +568 -0
- package/src/__tests__/call-setup-flow-midcall-trust.test.ts +231 -0
- package/src/__tests__/call-setup-flow-name-capture.test.ts +754 -0
- package/src/__tests__/call-setup-flow-verification.test.ts +925 -0
- package/src/__tests__/call-setup-flow.test.ts +311 -0
- package/src/__tests__/cancel-resolves-conversation-key.test.ts +0 -1
- package/src/__tests__/channel-guardian.test.ts +4 -7
- package/src/__tests__/channel-readiness-service.test.ts +0 -6
- package/src/__tests__/channel-readiness-slack-remote.test.ts +0 -4
- package/src/__tests__/channel-retry-sweep.test.ts +76 -64
- package/src/__tests__/channel-setup-panel-ack.test.ts +184 -0
- package/src/__tests__/clear-all-lexical.test.ts +90 -0
- package/src/__tests__/compactor-media-strip.test.ts +177 -0
- package/src/__tests__/config-loader-backfill.test.ts +5 -5
- package/src/__tests__/config-schema.test.ts +17 -0
- package/src/__tests__/contact-store-interaction-info.test.ts +27 -70
- package/src/__tests__/contacts-relay-reads.test.ts +188 -23
- package/src/__tests__/contacts-tools.test.ts +42 -1
- package/src/__tests__/contacts-write.test.ts +181 -0
- package/src/__tests__/context-search-conversations-source.test.ts +220 -81
- package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +1 -0
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +1 -0
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +1 -0
- package/src/__tests__/conversation-agent-loop.test.ts +39 -0
- package/src/__tests__/conversation-analysis-routes.test.ts +1 -1
- package/src/__tests__/conversation-app-control-instantiation.test.ts +2 -0
- package/src/__tests__/conversation-clear-safety.test.ts +0 -1
- package/src/__tests__/conversation-crud-enabled-plugins.test.ts +60 -0
- package/src/__tests__/conversation-delete-schedule-cleanup.test.ts +0 -1
- package/src/__tests__/conversation-disk-view.test.ts +1 -0
- package/src/__tests__/conversation-enabledplugins-route.test.ts +184 -0
- package/src/__tests__/conversation-error.test.ts +0 -18
- package/src/__tests__/conversation-fork-crud.test.ts +67 -0
- package/src/__tests__/conversation-history-web-search.test.ts +7 -137
- package/src/__tests__/conversation-initial-prompt.test.ts +110 -0
- package/src/__tests__/conversation-loop-db-lock-resilience.test.ts +191 -0
- package/src/__tests__/conversation-process-callsite.test.ts +37 -0
- package/src/__tests__/conversation-queue.test.ts +149 -0
- package/src/__tests__/conversation-routes-enabled-plugins.test.ts +392 -0
- package/src/__tests__/conversation-routes-guardian-reply.test.ts +84 -0
- package/src/__tests__/conversation-routes-hidden-queue.test.ts +407 -0
- package/src/__tests__/conversation-runtime-assembly.test.ts +36 -16
- package/src/__tests__/conversation-skill-tools.test.ts +73 -0
- package/src/__tests__/conversation-wipe.test.ts +20 -5
- package/src/__tests__/credential-prompt-route.test.ts +69 -0
- package/src/__tests__/credential-security-invariants.test.ts +1 -3
- package/src/__tests__/db-drop-messages-fts.test.ts +115 -0
- package/src/__tests__/deterministic-verification-control-plane.test.ts +1 -62
- package/src/__tests__/edit-propagation.test.ts +56 -2
- package/src/__tests__/empty-state-greeting-cache.test.ts +9 -0
- package/src/__tests__/gateway-only-enforcement.test.ts +15 -110
- package/src/__tests__/guardian-expiry-notifier.test.ts +1 -0
- package/src/__tests__/guardian-question-mode.test.ts +57 -0
- package/src/__tests__/guardian-verification-voice-binding.test.ts +1 -1
- package/src/__tests__/guardian-wait-controller.test.ts +619 -0
- package/src/__tests__/handlers-skills-memory-v2-reseed.test.ts +33 -2
- package/src/__tests__/heic-fixture.ts +55 -0
- package/src/__tests__/helpers/seed-contact-channel.ts +0 -2
- package/src/__tests__/http-conversation-lineage.test.ts +6 -1
- package/src/__tests__/image-conversion.test.ts +170 -0
- package/src/__tests__/inbound-invite-redemption.test.ts +72 -341
- package/src/__tests__/introduction-card-resolver.test.ts +354 -0
- package/src/__tests__/invite-redeemed-ipc.test.ts +97 -0
- package/src/__tests__/invite-routes-http.test.ts +246 -420
- package/src/__tests__/job-handler-registration-guard.test.ts +104 -0
- package/src/__tests__/lexical-index-dual-write.test.ts +426 -0
- package/src/__tests__/lifecycle-memory-v2-seed.test.ts +2 -2
- package/src/__tests__/list-messages-attachments.test.ts +159 -0
- package/src/__tests__/list-messages-background-tool-completion.test.ts +141 -0
- package/src/__tests__/list-messages-queued.test.ts +25 -0
- package/src/__tests__/managed-store.test.ts +129 -1
- package/src/__tests__/media-stream-audio-transcode.test.ts +113 -0
- package/src/__tests__/media-stream-output.test.ts +350 -19
- package/src/__tests__/media-stream-server-integration.test.ts +943 -169
- package/src/__tests__/media-stream-stt-session.test.ts +498 -1
- package/src/__tests__/memory-jobs-worker-lanes.test.ts +2 -2
- package/src/__tests__/model-intents.test.ts +1 -1
- package/src/__tests__/mtime-cache.test.ts +19 -0
- package/src/__tests__/non-member-access-request.test.ts +349 -3
- package/src/__tests__/notification-decision-fallback.test.ts +104 -8
- package/src/__tests__/notification-decision-strategy.test.ts +57 -8
- package/src/__tests__/onboarding-template-contract.test.ts +27 -43
- package/src/__tests__/openai-provider.test.ts +33 -0
- package/src/__tests__/openai-responses-provider.test.ts +40 -0
- package/src/__tests__/persistence-layering-guard.test.ts +29 -20
- package/src/__tests__/persona-resolver.test.ts +56 -2
- package/src/__tests__/personal-memory-auth-bypass.test.ts +62 -0
- package/src/__tests__/phone.test.ts +1 -15
- package/src/__tests__/plugin-bootstrap.test.ts +25 -196
- package/src/__tests__/plugin-disabled-state.test.ts +33 -0
- package/src/__tests__/plugin-effective-enabled-set.test.ts +227 -0
- package/src/__tests__/plugin-event-hub-facade.test.ts +323 -0
- package/src/__tests__/plugin-import-boundary-guard.test.ts +156 -42
- package/src/__tests__/plugin-injector-contribution.test.ts +4 -20
- package/src/__tests__/plugin-pipeline.test.ts +63 -1
- package/src/__tests__/plugin-route-contribution.test.ts +62 -115
- package/src/__tests__/plugin-tool-contribution.test.ts +0 -22
- package/src/__tests__/plugin-types.test.ts +0 -1
- package/src/__tests__/pointer-conversation-trust.test.ts +38 -6
- package/src/__tests__/pre-model-call-sanitize.test.ts +1 -1
- package/src/__tests__/prune-old-tool-invocations-job.test.ts +97 -0
- package/src/__tests__/public-ingress-urls.test.ts +0 -48
- package/src/__tests__/qdrant-collection-migration.test.ts +145 -2
- package/src/__tests__/reaction-intercept-member-verdict-warm.test.ts +0 -1
- package/src/__tests__/resolve-trust-class.test.ts +28 -10
- package/src/__tests__/scaffold-managed-skill-tool.test.ts +137 -0
- package/src/__tests__/schedule-retry.test.ts +38 -14
- package/src/__tests__/schedule-routes-workflow-validation.test.ts +3 -3
- package/src/__tests__/schedule-routes.test.ts +14 -6
- package/src/__tests__/schedule-store.test.ts +3 -1
- package/src/__tests__/scheduler-disk-pressure.test.ts +17 -6
- package/src/__tests__/scheduler-recurrence.test.ts +57 -107
- package/src/__tests__/scheduler-reuse-conversation.test.ts +51 -46
- package/src/__tests__/scheduler-wake.test.ts +13 -23
- package/src/__tests__/scheduler-worker-standdown.test.ts +220 -0
- package/src/__tests__/secret-routes-platform-proxy.test.ts +1 -1
- package/src/__tests__/shell-observability.test.ts +1 -0
- package/src/__tests__/skill-boundary-guard.test.ts +3 -4
- package/src/__tests__/skill-load-plugin-scope.test.ts +238 -0
- package/src/__tests__/skill-projection-feature-flag.test.ts +2 -0
- package/src/__tests__/skill-script-runner-host.test.ts +53 -0
- package/src/__tests__/skill-script-runner-sandbox.test.ts +5 -2
- package/src/__tests__/skill-tool-factory.test.ts +73 -1
- package/src/__tests__/slack-app-setup-skill-regression.test.ts +37 -14
- package/src/__tests__/slack-inbound-verification.test.ts +36 -0
- package/src/__tests__/slack-notification-approval-card.test.ts +89 -2
- package/src/__tests__/slack-reaction-canonical-approval.test.ts +51 -1
- package/src/__tests__/stt-hints.test.ts +2 -256
- package/src/__tests__/subagent-allowlist-validation.test.ts +47 -0
- package/src/__tests__/subagent-call-site-routing.test.ts +72 -0
- package/src/__tests__/subagent-fork-notifications.test.ts +14 -2
- package/src/__tests__/subagent-persistence.test.ts +122 -0
- package/src/__tests__/subagent-spawn-and-await.test.ts +1 -0
- package/src/__tests__/subagent-terminal-message.test.ts +99 -0
- package/src/__tests__/subagent-tool-gate-mode.test.ts +78 -1
- package/src/__tests__/system-prompt.test.ts +17 -118
- package/src/__tests__/task-scheduler.test.ts +68 -89
- package/src/__tests__/telephony-credential-preflight.test.ts +312 -0
- package/src/__tests__/telephony-tts-guard.test.ts +538 -0
- package/src/__tests__/title-generate-hook.test.ts +16 -0
- package/src/__tests__/tool-approval-handler.test.ts +87 -1
- package/src/__tests__/tool-approval-seed-content-blocks.test.ts +45 -20
- package/src/__tests__/tool-grant-request-escalation.test.ts +39 -0
- package/src/__tests__/trust-context-guards.test.ts +21 -10
- package/src/__tests__/trusted-contact-multichannel.test.ts +4 -0
- package/src/__tests__/twilio-provider.test.ts +17 -17
- package/src/__tests__/twilio-routes-twiml.test.ts +6 -543
- package/src/__tests__/twilio-routes.test.ts +257 -387
- package/src/__tests__/twilio-validation.test.ts +1 -1
- package/src/__tests__/unified-turn-context-client-os.test.ts +45 -0
- package/src/__tests__/usage-cache-backfill-migration.test.ts +5 -0
- package/src/__tests__/voice-approval-resolver-relay.test.ts +150 -0
- package/src/__tests__/voice-ingress-preflight.test.ts +61 -0
- package/src/__tests__/voice-quality.test.ts +2 -2
- package/src/__tests__/voice-scoped-grant-consumer.test.ts +1 -7
- package/src/__tests__/voice-session-bridge-processing-wait.test.ts +27 -0
- package/src/__tests__/voice-session-bridge.test.ts +1 -10
- package/src/__tests__/workspace-git-service.test.ts +1 -1
- package/src/__tests__/workspace-migration-013-repair-conversation-disk-view.test.ts +1 -0
- package/src/__tests__/workspace-migration-118-seed-now-md.test.ts +120 -0
- package/src/__tests__/workspace-migration-120-revise-onboarding-threads.test.ts +175 -0
- package/src/__tests__/workspace-migration-121-seed-default-user-guardrails.test.ts +118 -0
- package/src/__tests__/workspace-migration-122-relocate-default-user-boundary.test.ts +119 -0
- package/src/__tests__/workspace-migration-123-swap-quality-profile-to-fable.test.ts +191 -0
- package/src/__tests__/workspace-migration-backfill-installation-id.test.ts +235 -241
- package/src/acp/__tests__/prepare-agent-env.test.ts +0 -97
- package/src/acp/__tests__/session-manager-persistence.test.ts +2 -0
- package/src/acp/__tests__/session-manager-resume.test.ts +8 -0
- package/src/acp/__tests__/session-manager-usage.test.ts +6 -0
- package/src/acp/__tests__/session-manager.test.ts +6 -0
- package/src/acp/agent-process.test.ts +116 -0
- package/src/acp/agent-process.ts +71 -0
- package/src/acp/auto-install.ts +12 -8
- package/src/acp/failure-error.test.ts +109 -0
- package/src/acp/failure-error.ts +111 -0
- package/src/acp/prepare-agent-env.ts +0 -12
- package/src/acp/resolve-agent.test.ts +6 -45
- package/src/acp/resolve-agent.ts +2 -4
- package/src/acp/session-manager.test.ts +312 -0
- package/src/acp/session-manager.ts +102 -46
- package/src/agent/ax-tree-compaction.test.ts +4 -1
- package/src/agent/image-optimize.ts +17 -127
- package/src/agent/loop.ts +45 -192
- package/src/api/events/background-tool-completed.test.ts +51 -0
- package/src/api/events/background-tool-completed.ts +29 -0
- package/src/api/events/background-tool-started.test.ts +36 -0
- package/src/api/events/background-tool-started.ts +28 -0
- package/src/api/events/conversation-error.ts +0 -1
- package/src/api/events/open-panel.ts +33 -0
- package/src/api/events/subagent-status-changed.ts +4 -1
- package/src/api/index.ts +19 -0
- package/src/api/responses/conversation-message.ts +34 -0
- package/src/approvals/guardian-decision-primitive.ts +74 -24
- package/src/approvals/guardian-request-resolvers.ts +450 -122
- package/src/apps/app-store.ts +1 -0
- package/src/apps/shared-app-links-store.ts +1 -0
- package/src/avatar/identity-avatar.ts +3 -7
- package/src/background-wake/wake-intent-hooks.test.ts +1 -1
- package/src/calls/__tests__/fish-audio-client.test.ts +73 -0
- package/src/calls/__tests__/gateway-invite-reader.test.ts +170 -0
- package/src/calls/__tests__/relay-setup-router.test.ts +126 -102
- package/src/calls/call-controller.ts +134 -48
- package/src/calls/call-domain.ts +18 -42
- package/src/calls/call-pointer-messages.ts +51 -67
- package/src/calls/call-setup-flow-types.ts +76 -0
- package/src/calls/call-setup-flow.ts +1743 -0
- package/src/calls/call-speech-output.ts +87 -23
- package/src/calls/call-store.ts +4 -0
- package/src/calls/call-transport.ts +22 -36
- package/src/calls/fish-audio-client.ts +3 -0
- package/src/calls/gateway-invite-reader.ts +98 -0
- package/src/calls/guardian-wait-controller.ts +451 -0
- package/src/calls/media-stream-audio-transcode.ts +82 -2
- package/src/calls/media-stream-output.ts +142 -36
- package/src/calls/media-stream-server.ts +297 -178
- package/src/calls/media-stream-stt-session.ts +393 -50
- package/src/calls/media-turn-detector.ts +4 -6
- package/src/calls/relay-access-wait.ts +3 -4
- package/src/calls/relay-setup-router.ts +22 -43
- package/src/calls/relay-verification.ts +17 -24
- package/src/calls/resolve-call-tts-provider.ts +96 -29
- package/src/calls/stt-hints.ts +24 -87
- package/src/calls/telephony-credential-preflight.ts +159 -0
- package/src/calls/telephony-tts-capability.ts +183 -0
- package/src/calls/tts-call-strategy.ts +18 -20
- package/src/calls/twilio-provider.ts +3 -2
- package/src/calls/twilio-routes.ts +47 -351
- package/src/calls/types.ts +5 -3
- package/src/calls/voice-ingress-preflight.ts +9 -0
- package/src/calls/voice-quality.ts +13 -18
- package/src/calls/voice-session-bridge.ts +25 -35
- package/src/channels/__tests__/types.test.ts +28 -0
- package/src/channels/channel-verification-sessions.ts +7 -2
- package/src/channels/config.ts +0 -42
- package/src/channels/slack-thread-store.ts +12 -0
- package/src/channels/types.ts +23 -0
- package/src/cli/commands/__tests__/conversations-slack.test.ts +1 -1
- package/src/cli/commands/__tests__/schedules.test.ts +83 -34
- package/src/cli/commands/__tests__/status.test.ts +42 -2
- package/src/cli/commands/contacts.ts +2 -2
- package/src/cli/commands/credentials.ts +17 -0
- package/src/cli/commands/monitoring.ts +191 -0
- package/src/cli/commands/schedules-worker.ts +175 -0
- package/src/cli/commands/schedules.ts +169 -43
- package/src/cli/commands/status.ts +12 -1
- package/src/cli/commands/tools.ts +5 -1
- package/src/cli/lib/__tests__/plugin-catalog-cache.test.ts +1 -0
- package/src/cli/lib/__tests__/search-plugins.test.ts +29 -1
- package/src/cli/lib/__tests__/toggle-plugin.test.ts +7 -0
- package/src/cli/lib/search-plugins.ts +6 -0
- package/src/cli/lib/toggle-plugin.ts +21 -2
- package/src/cli/program.ts +3 -1
- package/src/config/__tests__/deployment-context-defaults.test.ts +23 -0
- package/src/config/acp-defaults.test.ts +0 -10
- package/src/config/acp-defaults.ts +0 -6
- package/src/config/bundled-skills/acp/SKILL.md +13 -45
- package/src/config/bundled-skills/acp/TOOLS.json +3 -3
- package/src/config/bundled-skills/app-builder/SKILL.md +3 -3
- package/src/config/bundled-skills/app-builder/references/RESPONSIVE.md +9 -9
- package/src/config/bundled-skills/contacts/tools/contact-merge.ts +1 -1
- package/src/config/bundled-skills/contacts/tools/contact-search.ts +1 -1
- package/src/config/bundled-skills/phone-calls/SKILL.md +1 -1
- package/src/config/bundled-skills/phone-calls/references/CONFIG.md +9 -9
- package/src/config/bundled-skills/phone-calls/references/TROUBLESHOOTING.md +10 -7
- package/src/config/bundled-skills/skill-management/TOOLS.json +10 -0
- package/src/config/call-site-defaults.ts +0 -2
- package/src/config/feature-flag-registry.json +16 -8
- package/src/config/loader.ts +11 -0
- package/src/config/schema.ts +9 -1
- package/src/config/schemas/__tests__/memory-v2.test.ts +2 -2
- package/src/config/schemas/__tests__/memory-v3.test.ts +64 -0
- package/src/config/schemas/call-site-catalog.ts +0 -12
- package/src/config/schemas/calls.ts +8 -0
- package/src/config/schemas/elevenlabs.ts +1 -1
- package/src/config/schemas/llm.ts +0 -2
- package/src/config/schemas/memory-v2.ts +1 -1
- package/src/config/schemas/memory-v3.ts +82 -0
- package/src/config/schemas/monitoring.ts +61 -0
- package/src/config/schemas/schedules.ts +35 -0
- package/src/config/seed-inference-profiles.ts +2 -2
- package/src/config/skills-plugin-filter.test.ts +96 -0
- package/src/config/skills.ts +26 -0
- package/src/contacts/__tests__/member-write-relay.test.ts +156 -4
- package/src/contacts/canonical-guardian-store.ts +5 -0
- package/src/contacts/contact-store.ts +81 -35
- package/src/contacts/contacts-write.ts +50 -25
- package/src/contacts/member-write-relay.ts +115 -31
- package/src/contacts/types.ts +0 -8
- package/src/context/compactor.ts +29 -15
- package/src/context/outbound-sanitize.ts +202 -0
- package/src/daemon/__tests__/abort-null-controller.test.ts +167 -0
- package/src/daemon/__tests__/conversation-tool-setup-plugin-scope.test.ts +122 -0
- package/src/daemon/__tests__/embedding-reconcile.test.ts +99 -16
- package/src/daemon/__tests__/wake-conversation-ops-completion.test.ts +107 -0
- package/src/daemon/abort-watchdog.ts +7 -0
- package/src/daemon/app-source-watcher.ts +9 -0
- package/src/daemon/assistant-attachments.ts +42 -0
- package/src/daemon/conversation-agent-loop-handlers.ts +256 -147
- package/src/daemon/conversation-agent-loop.ts +61 -68
- package/src/daemon/conversation-error.ts +1 -15
- package/src/daemon/conversation-history.ts +32 -236
- package/src/daemon/conversation-initial-prompt.ts +77 -0
- package/src/daemon/conversation-lifecycle.ts +25 -1
- package/src/daemon/conversation-messaging.ts +2 -2
- package/src/daemon/conversation-plugin-scope.ts +38 -0
- package/src/daemon/conversation-process.ts +50 -14
- package/src/daemon/conversation-runtime-assembly.ts +36 -6
- package/src/daemon/conversation-skill-tools.ts +19 -3
- package/src/daemon/conversation-store.ts +3 -3
- package/src/daemon/conversation-surfaces.ts +171 -0
- package/src/daemon/conversation-tool-setup.test.ts +95 -0
- package/src/daemon/conversation-tool-setup.ts +116 -1
- package/src/daemon/conversation-turn-finalize.ts +211 -0
- package/src/daemon/conversation.ts +92 -1
- package/src/daemon/disk-pressure-policy.ts +2 -1
- package/src/daemon/event-loop-watchdog.ts +22 -6
- package/src/daemon/external-plugins-bootstrap.ts +47 -134
- package/src/daemon/handlers/__tests__/config-a2a-accept.test.ts +1 -1
- package/src/daemon/handlers/__tests__/config-a2a-complete.test.ts +26 -14
- package/src/daemon/handlers/__tests__/config-a2a-invite.test.ts +27 -14
- package/src/daemon/handlers/__tests__/config-channels.test.ts +0 -5
- package/src/daemon/handlers/config-a2a.ts +7 -14
- package/src/daemon/handlers/config-channels.ts +2 -10
- package/src/daemon/handlers/conversation-history.ts +3 -1
- package/src/daemon/handlers/conversations.ts +0 -52
- package/src/daemon/handlers/shared.ts +4 -24
- package/src/daemon/handlers/skills.ts +4 -1
- package/src/daemon/host-cu-proxy.ts +1 -1
- package/src/daemon/lifecycle.ts +59 -472
- package/src/daemon/message-protocol.ts +3 -3
- package/src/daemon/message-provenance.ts +12 -14
- package/src/daemon/message-types/background-tools.ts +28 -0
- package/src/daemon/message-types/contacts.ts +2 -10
- package/src/daemon/message-types/conversations.ts +10 -0
- package/src/daemon/message-types/integrations.ts +2 -0
- package/src/daemon/message-types/surfaces.ts +1 -0
- package/src/daemon/message-types/sync.ts +1 -0
- package/src/daemon/pointer-conversation-trust.ts +23 -9
- package/src/daemon/pointer-turn-runner.ts +174 -0
- package/src/daemon/providers-setup.ts +16 -0
- package/src/daemon/shutdown-handlers.ts +139 -47
- package/src/daemon/skill-memory-refresh.ts +2 -2
- package/src/daemon/sqlite-corruption-watchdog.test.ts +217 -0
- package/src/daemon/sqlite-corruption-watchdog.ts +155 -0
- package/src/daemon/tool-setup-types.ts +8 -0
- package/src/daemon/trust-context.ts +21 -6
- package/src/daemon/turn-latency-tracker.test.ts +93 -0
- package/src/daemon/turn-latency-tracker.ts +38 -0
- package/src/daemon/wake-conversation-ops.ts +10 -8
- package/src/documents/document-comments-store.test.ts +7 -1
- package/src/documents/document-comments-store.ts +8 -0
- package/src/documents/document-store.ts +15 -0
- package/src/export/__tests__/transcript-formatter.test.ts +11 -0
- package/src/export/transcript-formatter.ts +49 -67
- package/src/home/__tests__/feed-writer.test.ts +247 -1
- package/src/home/conversation-starter-checkpoints.ts +1 -0
- package/src/home/feed-writer.ts +61 -0
- package/src/home/job-handlers/conversation-starters.ts +1 -0
- package/src/hooks/hook-loader.ts +12 -0
- package/src/hooks/registry.ts +32 -4
- package/src/inbound/public-ingress-urls.ts +0 -17
- package/src/ipc/__tests__/contacts-info-ipc.test.ts +285 -0
- package/src/ipc/assistant-server.ts +28 -7
- package/src/ipc/routes/__tests__/contacts-mirror-apply-atomicity.test.ts +62 -0
- package/src/ipc/routes/__tests__/contacts-mirror-apply.test.ts +272 -0
- package/src/ipc/routes/__tests__/contacts-mirror-ipc-routes.test.ts +322 -0
- package/src/ipc/routes/__tests__/invite-ipc-routes.test.ts +60 -4
- package/src/ipc/routes/contacts-info-ipc-routes.ts +239 -0
- package/src/ipc/routes/contacts-mirror-ipc-routes.ts +202 -0
- package/src/ipc/routes/db-proxy.ts +5 -2
- package/src/ipc/routes/invite-ipc-routes.ts +53 -39
- package/src/jobs/worker.ts +9 -2
- package/src/live-voice/__tests__/live-voice-archive.test.ts +10 -1
- package/src/live-voice/live-voice-archive.ts +5 -0
- package/src/messaging/providers/__tests__/transport-dispatch.test.ts +22 -6
- package/src/messaging/providers/channel-transport.ts +6 -0
- package/src/messaging/providers/index.ts +3 -0
- package/src/messaging/providers/slack/api.test.ts +49 -1
- package/src/messaging/providers/slack/api.ts +110 -0
- package/src/messaging/providers/slack/send.test.ts +85 -0
- package/src/messaging/providers/slack/send.ts +100 -28
- package/src/messaging/providers/slack/transport.ts +7 -13
- package/src/messaging/providers/whatsapp/send.ts +29 -9
- package/src/monitoring/__tests__/control.test.ts +235 -0
- package/src/monitoring/__tests__/sample-ring-buffer.test.ts +96 -0
- package/src/monitoring/control.ts +105 -0
- package/src/monitoring/resource-sampler.ts +258 -0
- package/src/monitoring/sample-ring-buffer.ts +108 -0
- package/src/monitoring/worker.ts +94 -0
- package/src/notifications/README.md +1 -1
- package/src/notifications/access-request-copy.ts +94 -23
- package/src/notifications/adapters/slack.ts +89 -21
- package/src/notifications/approval-card-builder.ts +33 -18
- package/src/notifications/approval-card-data.ts +48 -15
- package/src/notifications/broadcaster.ts +25 -16
- package/src/notifications/decision-engine.ts +131 -32
- package/src/notifications/guardian-question-mode.ts +58 -10
- package/src/permissions/approval-policy.ts +8 -1
- package/src/permissions/checker.ts +2 -0
- package/src/permissions/gateway-threshold-reader.ts +7 -6
- package/src/permissions/ipc-risk-types.ts +1 -0
- package/src/permissions/secret-prompter.ts +11 -3
- package/src/permissions/types.ts +2 -2
- package/src/persistence/__tests__/conversation-queries-search.test.ts +397 -0
- package/src/persistence/__tests__/conversation-search-lexical.test.ts +103 -0
- package/src/persistence/__tests__/jobs-worker-memory-disabled.test.ts +95 -0
- package/src/persistence/__tests__/slow-query-log.test.ts +266 -0
- package/src/persistence/__tests__/slow-sync-log.test.ts +155 -0
- package/src/persistence/a2a-invite-store.ts +205 -0
- package/src/persistence/attachments-store.ts +94 -10
- package/src/persistence/checkpoints.ts +45 -0
- package/src/persistence/conversation-crud.ts +453 -260
- package/src/persistence/conversation-display-order-migration.ts +8 -2
- package/src/persistence/conversation-group-migration.test.ts +8 -0
- package/src/persistence/conversation-group-migration.ts +9 -0
- package/src/persistence/conversation-queries.ts +244 -125
- package/src/persistence/conversation-row-batch-delete.ts +9 -5
- package/src/persistence/conversation-search-lexical.ts +49 -0
- package/src/persistence/db-async-query.ts +42 -0
- package/src/persistence/db-connection.ts +13 -0
- package/src/persistence/delivery-channels.ts +0 -82
- package/src/persistence/delivery-crud.ts +27 -6
- package/src/persistence/embeddings/__tests__/embedding-identity.test.ts +18 -5
- package/src/persistence/embeddings/__tests__/messages-lexical-index.test.ts +334 -0
- package/src/persistence/embeddings/embedding-identity.ts +17 -5
- package/src/persistence/embeddings/embedding-runtime-manager.ts +31 -0
- package/src/persistence/embeddings/messages-lexical-index.ts +396 -0
- package/src/persistence/embeddings/qdrant-client.ts +81 -5
- package/src/persistence/group-crud.ts +13 -1
- package/src/persistence/job-handlers/__tests__/message-lexical-backfill.test.ts +506 -0
- package/src/persistence/job-handlers/__tests__/message-lexical-enqueue.test.ts +164 -0
- package/src/persistence/job-handlers/__tests__/message-lexical.test.ts +259 -0
- package/src/persistence/job-handlers/cleanup.ts +57 -9
- package/src/persistence/job-handlers/message-lexical-backfill.ts +187 -0
- package/src/persistence/job-handlers/message-lexical.ts +251 -0
- package/src/persistence/jobs-store.ts +102 -8
- package/src/persistence/jobs-worker.ts +68 -28
- package/src/persistence/llm-request-log-store.ts +35 -19
- package/src/persistence/llm-usage-store.ts +11 -0
- package/src/persistence/memory-lifecycle-hooks.test.ts +65 -0
- package/src/persistence/memory-lifecycle-hooks.ts +55 -1
- package/src/persistence/migrations/028-call-session-mode.ts +1 -1
- package/src/persistence/migrations/116-messages-fts.ts +8 -1
- package/src/persistence/migrations/311-create-subagents-table.ts +50 -0
- package/src/persistence/migrations/312-drop-inbox-conversation-state-table.ts +15 -0
- package/src/persistence/migrations/313-drop-messages-fts.ts +17 -0
- package/src/persistence/migrations/314-add-conversation-enabled-plugins.ts +25 -0
- package/src/persistence/migrations/315-create-a2a-invites.ts +58 -0
- package/src/persistence/migrations/316-drop-contact-channels-invite-id.ts +28 -0
- package/src/persistence/migrations/317-canonical-guardian-requester-signals.ts +24 -0
- package/src/persistence/migrations/318-drop-contact-channel-telemetry.ts +32 -0
- package/src/persistence/migrations/__tests__/315-create-a2a-invites.test.ts +161 -0
- package/src/persistence/migrations/__tests__/316-drop-contact-channels-invite-id.test.ts +156 -0
- package/src/persistence/migrations/__tests__/318-drop-contact-channel-telemetry.test.ts +134 -0
- package/src/persistence/migrations/__tests__/migration-prefix-guard.test.ts +99 -0
- package/src/persistence/raw-query.ts +50 -11
- package/src/persistence/schema/a2a.ts +14 -0
- package/src/persistence/schema/contacts.ts +0 -62
- package/src/persistence/schema/conversation-groups.ts +12 -0
- package/src/persistence/schema/conversations.ts +2 -0
- package/src/persistence/schema/documents.ts +65 -0
- package/src/persistence/schema/guardian.ts +4 -0
- package/src/persistence/schema/index.ts +4 -0
- package/src/persistence/schema/memory-injection.ts +62 -0
- package/src/persistence/schema/workflows.ts +55 -0
- package/src/persistence/slow-query-log.ts +437 -0
- package/src/persistence/slow-sync-log.ts +204 -0
- package/src/persistence/steps.ts +16 -0
- package/src/persistence/subagent-store.ts +125 -0
- package/src/persistence/worker-control.ts +45 -238
- package/src/plugin-api/event-hub-facade.ts +172 -0
- package/src/plugin-api/index.ts +9 -1
- package/src/plugin-api/types.ts +40 -9
- package/src/plugins/defaults/default-plugin-names.ts +60 -0
- package/src/plugins/defaults/index.ts +19 -29
- package/src/plugins/defaults/memory/__tests__/conversation-queries.test.ts +46 -67
- package/src/plugins/defaults/memory/__tests__/db-async-query.test.ts +36 -1
- package/src/plugins/defaults/memory/__tests__/find-most-recent-retrospective-for.test.ts +2 -4
- package/src/plugins/defaults/memory/__tests__/jobs-worker-v2-graph-trigger-embed.test.ts +2 -2
- package/src/plugins/defaults/memory/__tests__/jobs-worker-v2-schedule.test.ts +9 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +7 -4
- package/src/plugins/defaults/memory/config.ts +11 -0
- package/src/plugins/defaults/memory/context-search/agent-runner.ts +1 -1
- package/src/plugins/defaults/memory/context-search/sources/conversations.ts +124 -113
- package/src/plugins/defaults/memory/embeddings.ts +77 -0
- package/src/plugins/defaults/memory/find-most-recent-retrospective-for.ts +66 -0
- package/src/plugins/defaults/memory/frontmatter.ts +50 -0
- package/src/plugins/defaults/memory/graph/bootstrap.test.ts +36 -10
- package/src/plugins/defaults/memory/graph/bootstrap.ts +6 -5
- package/src/plugins/defaults/memory/graph/consolidation.ts +3 -5
- package/src/plugins/defaults/memory/graph/extraction.ts +3 -6
- package/src/plugins/defaults/memory/graph/graph-search.ts +12 -23
- package/src/plugins/defaults/memory/graph/narrative.ts +3 -5
- package/src/plugins/defaults/memory/graph/pattern-scan.ts +3 -5
- package/src/plugins/defaults/memory/graph/retriever.test.ts +1 -6
- package/src/plugins/defaults/memory/graph/retriever.ts +5 -10
- package/src/plugins/defaults/memory/graph/tools.ts +1 -1
- package/src/plugins/defaults/memory/hooks/init.ts +31 -5
- package/src/plugins/defaults/memory/indexer.ts +2 -3
- package/src/plugins/defaults/memory/injectors.ts +2 -2
- package/src/plugins/defaults/memory/job-handler-registration.ts +96 -0
- package/src/plugins/defaults/memory/job-handlers/backfill.ts +4 -26
- package/src/plugins/defaults/memory/job-handlers/embedding.test.ts +6 -19
- package/src/plugins/defaults/memory/job-handlers/embedding.ts +9 -22
- package/src/plugins/defaults/memory/job-handlers/index-maintenance.ts +2 -3
- package/src/plugins/defaults/memory/job-handlers.ts +13 -12
- package/src/plugins/defaults/memory/jobs/embed-concept-page.ts +1 -1
- package/src/plugins/defaults/memory/llm-helpers.ts +76 -0
- package/src/plugins/defaults/memory/memory-retrospective-job.ts +8 -5
- package/src/plugins/defaults/memory/memory-retrospective-startup-cleanup.ts +2 -2
- package/src/plugins/defaults/memory/memory-v2-concept-frequency.ts +2 -0
- package/src/plugins/defaults/memory/persistence-hooks.ts +46 -1
- package/src/plugins/defaults/memory/pkb/pkb-index.ts +1 -5
- package/src/plugins/defaults/memory/pkb/pkb-search.ts +2 -2
- package/src/plugins/defaults/memory/routes/__tests__/memory-v2-simulate-route.test.ts +4 -6
- package/src/plugins/defaults/memory/routes/memory-item-routes.ts +1 -1
- package/src/plugins/defaults/memory/search/semantic.ts +2 -2
- package/src/plugins/defaults/memory/startup.ts +244 -0
- package/src/plugins/defaults/memory/task-memory-cleanup.ts +8 -0
- package/src/{daemon → plugins/defaults/memory/v2}/__tests__/memory-v2-startup.test.ts +6 -6
- package/src/plugins/defaults/memory/v2/__tests__/migration.test.ts +2 -13
- package/src/plugins/defaults/memory/v2/__tests__/router.test.ts +4 -5
- package/src/plugins/defaults/memory/v2/__tests__/sweep-job.test.ts +1 -8
- package/src/plugins/defaults/memory/v2/activation.ts +2 -4
- package/src/plugins/defaults/memory/v2/cli-command-store.ts +2 -4
- package/src/plugins/defaults/memory/v2/frontmatter-sweep.ts +1 -1
- package/src/{daemon → plugins/defaults/memory/v2}/memory-v2-startup.test.ts +8 -8
- package/src/{daemon → plugins/defaults/memory/v2}/memory-v2-startup.ts +20 -22
- package/src/plugins/defaults/memory/v2/migration.ts +2 -5
- package/src/plugins/defaults/memory/v2/page-store.ts +1 -1
- package/src/plugins/defaults/memory/v2/qdrant.ts +2 -3
- package/src/plugins/defaults/memory/v2/router.ts +5 -5
- package/src/plugins/defaults/memory/v2/sim.ts +2 -4
- package/src/plugins/defaults/memory/v2/skill-store.ts +2 -4
- package/src/plugins/defaults/memory/v2/sweep-job.ts +6 -6
- package/src/plugins/defaults/memory/v3/__tests__/carry-integration.test.ts +11 -9
- package/src/plugins/defaults/memory/v3/__tests__/dense.test.ts +107 -2
- package/src/plugins/defaults/memory/v3/__tests__/gate-flag.test.ts +69 -0
- package/src/plugins/defaults/memory/v3/__tests__/gate.test.ts +181 -0
- package/src/plugins/defaults/memory/v3/__tests__/live-integration.test.ts +7 -3
- package/src/plugins/defaults/memory/v3/__tests__/orchestrate.test.ts +345 -8
- package/src/plugins/defaults/memory/v3/__tests__/pool-select.test.ts +1 -4
- package/src/plugins/defaults/memory/v3/__tests__/section-needle.test.ts +64 -0
- package/src/plugins/defaults/memory/v3/__tests__/shadow-integration.test.ts +18 -13
- package/src/plugins/defaults/memory/v3/__tests__/shadow-plugin.test.ts +62 -1
- package/src/plugins/defaults/memory/v3/card.ts +1 -4
- package/src/plugins/defaults/memory/v3/dense.ts +60 -25
- package/src/plugins/defaults/memory/v3/edge.ts +1 -1
- package/src/plugins/defaults/memory/v3/gate-flag.ts +20 -0
- package/src/plugins/defaults/memory/v3/gate.ts +158 -0
- package/src/plugins/defaults/memory/v3/maintain-job.ts +2 -4
- package/src/plugins/defaults/memory/v3/orchestrate.ts +186 -25
- package/src/plugins/defaults/memory/v3/pool-select.test.ts +7 -9
- package/src/plugins/defaults/memory/v3/pool-select.ts +6 -6
- package/src/plugins/defaults/memory/v3/prune.ts +2 -2
- package/src/plugins/defaults/memory/v3/section-dense-store.ts +8 -11
- package/src/plugins/defaults/memory/v3/section-needle.ts +27 -7
- package/src/plugins/defaults/memory/v3/shadow-plugin.ts +10 -0
- package/src/plugins/defaults/memory/v3-eval/eval-packets.ts +1 -4
- package/src/plugins/defaults/title-generate/hooks/user-prompt-submit.ts +5 -0
- package/src/plugins/defaults/turn-context/injectors.ts +1 -0
- package/src/plugins/defaults/turn-context/unified-turn-context.ts +12 -0
- package/src/plugins/injector-registry.ts +13 -2
- package/src/plugins/mtime-cache.ts +36 -47
- package/src/plugins/pipeline.ts +15 -1
- package/src/plugins/types.ts +14 -23
- package/src/prompts/__tests__/system-prompt.test.ts +170 -32
- package/src/prompts/persona-resolver.ts +13 -9
- package/src/prompts/system-prompt.ts +30 -9
- package/src/prompts/templates/BOOTSTRAP.md +16 -119
- package/src/prompts/templates/SOUL.md +43 -13
- package/src/prompts/templates/system-sections.ts +32 -27
- package/src/prompts/templates/users/default.md +19 -0
- package/src/providers/__tests__/placeholder-sentinels.test.ts +56 -0
- package/src/providers/inference/__tests__/base-url-route-validation.test.ts +96 -1
- package/src/providers/model-intents.ts +2 -2
- package/src/providers/openai/chat-completions-provider.ts +14 -1
- package/src/providers/openai/responses-provider.ts +18 -0
- package/src/providers/placeholder-sentinels.ts +18 -8
- package/src/providers/retry.ts +1 -0
- package/src/providers/speech-to-text/__tests__/provider-catalog.test.ts +0 -64
- package/src/providers/speech-to-text/__tests__/resolve.test.ts +92 -79
- package/src/providers/speech-to-text/deepgram-realtime.test.ts +98 -0
- package/src/providers/speech-to-text/deepgram-realtime.ts +62 -3
- package/src/providers/speech-to-text/provider-catalog.ts +0 -89
- package/src/providers/speech-to-text/resolve.ts +55 -0
- package/src/providers/tool-progress-events.test.ts +50 -0
- package/src/providers/tool-progress-events.ts +99 -0
- package/src/providers/types.ts +1 -1
- package/src/providers/vellum-model-routing.test.ts +51 -0
- package/src/providers/vellum-model-routing.ts +84 -0
- package/src/runtime/AGENTS.md +5 -4
- package/src/runtime/__tests__/agent-wake.test.ts +158 -1
- package/src/runtime/__tests__/channel-verification-service.test.ts +2 -2
- package/src/runtime/__tests__/trust-verdict-consumer.test.ts +37 -8
- package/src/runtime/access-request-helper.ts +154 -3
- package/src/runtime/actor-trust-resolver.ts +7 -21
- package/src/runtime/agent-wake.ts +41 -1
- package/src/runtime/assistant-event.ts +86 -12
- package/src/runtime/auth/__tests__/route-policy.test.ts +31 -0
- package/src/runtime/channel-approval-types.ts +52 -2
- package/src/runtime/channel-approvals.ts +23 -8
- package/src/runtime/channel-invite-transport.ts +2 -2
- package/src/runtime/channel-invite-transports/voice.ts +1 -1
- package/src/runtime/channel-readiness-service.ts +3 -5
- package/src/runtime/channel-retry-sweep.ts +18 -24
- package/src/runtime/channel-verification-service.ts +9 -11
- package/src/runtime/finalize-event-delivery.ts +30 -16
- package/src/runtime/guardian-action-service.ts +10 -11
- package/src/runtime/guardian-reply-router.ts +122 -21
- package/src/runtime/http-server.ts +44 -101
- package/src/runtime/introduction-policy.test.ts +214 -0
- package/src/runtime/introduction-policy.ts +284 -0
- package/src/runtime/invite-service.ts +104 -361
- package/src/runtime/local-actor-identity.test.ts +38 -0
- package/src/runtime/local-actor-identity.ts +14 -0
- package/src/runtime/middleware/__tests__/rate-limiter.test.ts +22 -0
- package/src/runtime/middleware/rate-limiter.ts +30 -0
- package/src/runtime/middleware/twilio-validation.ts +5 -7
- package/src/runtime/migrations/__tests__/gcs-signed-url.test.ts +28 -0
- package/src/runtime/migrations/origin-mode.ts +6 -6
- package/src/runtime/pre-first-message-gate.ts +1 -0
- package/src/runtime/routes/__tests__/channel-verification-revoke.test.ts +6 -35
- package/src/runtime/routes/__tests__/consolidation-routes.test.ts +24 -3
- package/src/runtime/routes/__tests__/contact-routes.test.ts +80 -15
- package/src/runtime/routes/__tests__/conversation-list-routes.test.ts +7 -1
- package/src/runtime/routes/__tests__/global-search-routes.test.ts +0 -3
- package/src/runtime/routes/__tests__/home-feed-routes.test.ts +142 -1
- package/src/runtime/routes/__tests__/invite-relay-routes.test.ts +119 -30
- package/src/runtime/routes/__tests__/migration-import-allowed-hosts-env.test.ts +70 -0
- package/src/runtime/routes/__tests__/monitoring-routes.test.ts +194 -0
- package/src/runtime/routes/__tests__/plugins-routes.test.ts +755 -29
- package/src/runtime/routes/__tests__/retrospective-routes.test.ts +4 -0
- package/src/runtime/routes/__tests__/schedule-routes-create.test.ts +70 -0
- package/src/runtime/routes/__tests__/schedule-worker-routes.test.ts +161 -0
- package/src/runtime/routes/__tests__/surface-action-routes.test.ts +1 -1
- package/src/runtime/routes/__tests__/surface-content-routes.test.ts +1 -1
- package/src/runtime/routes/acp-routes.ts +15 -3
- package/src/runtime/routes/app-management-routes.ts +4 -7
- package/src/runtime/routes/approval-routes.ts +4 -0
- package/src/runtime/routes/attachment-routes.ts +54 -3
- package/src/runtime/routes/avatar-routes.ts +0 -4
- package/src/runtime/routes/background-tool-routes.ts +34 -5
- package/src/runtime/routes/btw-routes.ts +32 -4
- package/src/runtime/routes/channel-route-shared.ts +18 -14
- package/src/runtime/routes/channel-verification-routes.ts +0 -3
- package/src/runtime/routes/contact-routes.ts +298 -117
- package/src/runtime/routes/conversation-list-routes.ts +6 -0
- package/src/runtime/routes/conversation-management-routes.ts +74 -37
- package/src/runtime/routes/conversation-query-routes.ts +5 -2
- package/src/runtime/routes/conversation-routes.ts +222 -82
- package/src/runtime/routes/conversations-import-routes.ts +8 -1
- package/src/runtime/routes/credential-prompt-routes.ts +20 -5
- package/src/runtime/routes/debug-routes.ts +1 -0
- package/src/runtime/routes/documents-routes.ts +5 -2
- package/src/runtime/routes/empty-state-greeting-cache.ts +37 -10
- package/src/runtime/routes/global-search-routes.ts +2 -1
- package/src/runtime/routes/guardian-approval-interception.ts +51 -0
- package/src/runtime/routes/home-feed-routes.ts +60 -1
- package/src/runtime/routes/identity-routes.ts +4 -87
- package/src/runtime/routes/inbound-message-handler.ts +82 -41
- package/src/runtime/routes/inbound-stages/acl-enforcement.test.ts +430 -57
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +302 -572
- package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +76 -270
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +21 -116
- package/src/runtime/routes/inbound-stages/edit-intercept.ts +9 -1
- package/src/runtime/routes/inbound-stages/escalation-intercept.test.ts +158 -0
- package/src/runtime/routes/inbound-stages/escalation-intercept.ts +22 -1
- package/src/runtime/routes/index.ts +6 -0
- package/src/runtime/routes/inference-provider-connection-routes.ts +22 -16
- package/src/runtime/routes/internal-twilio-routes.ts +0 -15
- package/src/runtime/routes/messages-lexical-routes.ts +75 -0
- package/src/runtime/routes/migration-routes.ts +69 -6
- package/src/runtime/routes/monitoring-routes.ts +237 -0
- package/src/runtime/routes/plugins-routes.ts +329 -8
- package/src/runtime/routes/schedule-routes.ts +49 -8
- package/src/runtime/routes/schedule-worker-routes.ts +210 -0
- package/src/runtime/routes/secret-routes.ts +1 -1
- package/src/runtime/routes/surface-conversation-resolver.ts +1 -0
- package/src/runtime/services/__tests__/analyze-conversation.test.ts +54 -9
- package/src/runtime/services/__tests__/auto-analysis-enqueue.test.ts +30 -6
- package/src/runtime/services/__tests__/conversation-analyze-job.test.ts +43 -8
- package/src/runtime/services/__tests__/conversation-serializer.test.ts +20 -0
- package/src/runtime/services/analyze-conversation.ts +14 -4
- package/src/runtime/services/conversation-serializer.ts +5 -0
- package/src/runtime/slack-no-response.ts +47 -0
- package/src/runtime/slack-reply-session.test.ts +633 -0
- package/src/runtime/slack-reply-session.ts +382 -0
- package/src/runtime/slack-task-progress.ts +95 -0
- package/src/runtime/sync/resource-sync-events.ts +25 -0
- package/src/runtime/tool-grant-request-helper.ts +17 -1
- package/src/runtime/trust-class.test.ts +141 -0
- package/src/runtime/trust-class.ts +121 -0
- package/src/runtime/trust-verdict-consumer.ts +7 -6
- package/src/schedule/__tests__/run-script.test.ts +21 -0
- package/src/schedule/__tests__/worker-control.test.ts +241 -0
- package/src/schedule/run-script.ts +13 -6
- package/src/schedule/schedule-usage-store.ts +1 -0
- package/src/schedule/scheduler.ts +178 -127
- package/src/schedule/worker-control.ts +118 -0
- package/src/schedule/worker.ts +131 -0
- package/src/skills/__tests__/categories-cache.test.ts +38 -0
- package/src/skills/categories-cache.ts +40 -5
- package/src/skills/managed-store.ts +41 -6
- package/src/skills/validate-input.ts +47 -0
- package/src/stt/types.ts +5 -9
- package/src/subagent/manager.ts +267 -39
- package/src/subagent/types.ts +6 -2
- package/src/subagent/validate-allowlists.ts +65 -0
- package/src/telemetry/watchdog-direct-emit.test.ts +109 -0
- package/src/telemetry/watchdog-direct-emit.ts +89 -0
- package/src/tools/acp/list-agents.test.ts +0 -6
- package/src/tools/background-tool-registry.ts +61 -0
- package/src/tools/document/document-comment-tool.test.ts +4 -0
- package/src/tools/host-terminal/host-shell.test.ts +397 -0
- package/src/tools/host-terminal/host-shell.ts +203 -14
- package/src/tools/skills/find-similar-skills.test.ts +152 -1
- package/src/tools/skills/find-similar-skills.ts +30 -4
- package/src/tools/skills/load.ts +36 -0
- package/src/tools/skills/scaffold-managed.ts +66 -34
- package/src/tools/skills/skill-script-runner.ts +17 -0
- package/src/tools/skills/skill-tool-factory.ts +5 -1
- package/src/tools/terminal/safe-env.ts +3 -0
- package/src/tools/terminal/shell.test.ts +177 -0
- package/src/tools/terminal/shell.ts +98 -4
- package/src/tools/tool-approval-handler.ts +106 -29
- package/{node_modules/@vellumai/skill-host-contracts/src → src/tools}/tool-types.ts +16 -17
- package/src/tools/types.ts +29 -17
- package/src/tools/ui-surface/definitions.ts +3 -1
- package/src/tts/__tests__/provider-adapters.test.ts +37 -0
- package/src/tts/__tests__/provider-catalog.test.ts +21 -0
- package/src/tts/provider-catalog.ts +40 -9
- package/src/tts/providers/fish-audio-provider.ts +19 -10
- package/src/tts/types.ts +3 -4
- package/src/util/__tests__/process-tree.test.ts +10 -2
- package/src/util/__tests__/text-spacing.test.ts +53 -0
- package/src/util/cgroup-memory.ts +142 -0
- package/src/util/image-conversion.ts +281 -0
- package/src/util/phone.ts +1 -8
- package/src/util/platform.ts +33 -0
- package/src/util/process-tree.ts +10 -2
- package/src/util/text-spacing.ts +53 -0
- package/src/util/worker-process.ts +271 -0
- package/src/workflows/journal-store.ts +16 -1
- package/src/workflows/library.test.ts +35 -20
- package/src/workflows/library.ts +105 -66
- package/src/workspace/git-service.ts +1 -0
- package/src/workspace/migrations/011-backfill-installation-id.ts +83 -31
- package/src/workspace/migrations/041-backfill-google-gmail-settings-scope.ts +41 -22
- package/src/workspace/migrations/070-memory-v2-summary-schema-rebuild.ts +1 -1
- package/src/workspace/migrations/118-seed-now-md.ts +64 -0
- package/src/workspace/migrations/119-strip-persisted-memory-v3-tuning-defaults.ts +169 -0
- package/src/workspace/migrations/120-revise-onboarding-threads.ts +47 -0
- package/src/workspace/migrations/121-seed-default-user-guardrails.ts +82 -0
- package/src/workspace/migrations/122-relocate-default-user-boundary.ts +111 -0
- package/src/workspace/migrations/123-swap-quality-profile-to-fable.ts +85 -0
- package/src/workspace/migrations/__tests__/119-strip-persisted-memory-v3-tuning-defaults.test.ts +181 -0
- package/src/workspace/migrations/registry.ts +12 -0
- package/drizzle/0000_dizzy_maggott.sql +0 -301
- package/drizzle/meta/0000_snapshot.json +0 -1933
- package/drizzle/meta/_journal.json +0 -13
- package/node_modules/@vellumai/skill-host-contracts/__tests__/client.test.ts +0 -887
- package/node_modules/@vellumai/skill-host-contracts/bun.lock +0 -24
- package/node_modules/@vellumai/skill-host-contracts/package.json +0 -18
- package/node_modules/@vellumai/skill-host-contracts/src/assistant-event.ts +0 -97
- package/node_modules/@vellumai/skill-host-contracts/src/client.ts +0 -1349
- package/node_modules/@vellumai/skill-host-contracts/src/index.ts +0 -6
- package/node_modules/@vellumai/skill-host-contracts/src/runtime-mode.ts +0 -11
- package/node_modules/@vellumai/skill-host-contracts/src/server-message.ts +0 -32
- package/node_modules/@vellumai/skill-host-contracts/src/skill-host.ts +0 -333
- package/node_modules/@vellumai/skill-host-contracts/tsconfig.json +0 -20
- package/node_modules/@vellumai/skill-host-contracts/tsconfig.test.json +0 -12
- package/src/__tests__/invite-redemption-service.test.ts +0 -1180
- package/src/__tests__/invite-service-ipc.test.ts +0 -350
- package/src/__tests__/job-handler-registry-guard.test.ts +0 -125
- package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +0 -203
- package/src/__tests__/relay-server.test.ts +0 -6153
- package/src/__tests__/telephony-stt-routing.test.ts +0 -329
- package/src/__tests__/voice-invite-redemption.test.ts +0 -702
- package/src/calls/relay-server.ts +0 -2463
- package/src/calls/telephony-stt-routing.ts +0 -145
- package/src/contacts/__tests__/contacts-write-revoke-relay.test.ts +0 -128
- package/src/daemon/__tests__/daemon-skill-host.test.ts +0 -273
- package/src/daemon/daemon-skill-host.ts +0 -262
- package/src/daemon/message-types/meet.ts +0 -143
- package/src/daemon/shutdown-registry.ts +0 -40
- package/src/ipc/__tests__/skill-server-bidirectional.test.ts +0 -253
- package/src/ipc/__tests__/skill-server.test.ts +0 -182
- package/src/ipc/skill-ipc-types.ts +0 -54
- package/src/ipc/skill-routes/__tests__/config.test.ts +0 -146
- package/src/ipc/skill-routes/__tests__/events-ipc.test.ts +0 -448
- package/src/ipc/skill-routes/__tests__/identity.test.ts +0 -62
- package/src/ipc/skill-routes/__tests__/log.test.ts +0 -133
- package/src/ipc/skill-routes/__tests__/memory.test.ts +0 -203
- package/src/ipc/skill-routes/__tests__/platform.test.ts +0 -111
- package/src/ipc/skill-routes/__tests__/providers.test.ts +0 -265
- package/src/ipc/skill-routes/__tests__/registries.test.ts +0 -400
- package/src/ipc/skill-routes/config.ts +0 -47
- package/src/ipc/skill-routes/events.ts +0 -147
- package/src/ipc/skill-routes/identity.ts +0 -21
- package/src/ipc/skill-routes/index.ts +0 -37
- package/src/ipc/skill-routes/log.ts +0 -40
- package/src/ipc/skill-routes/memory.ts +0 -92
- package/src/ipc/skill-routes/platform.ts +0 -39
- package/src/ipc/skill-routes/providers.ts +0 -162
- package/src/ipc/skill-routes/registries.ts +0 -338
- package/src/ipc/skill-server.ts +0 -831
- package/src/ipc/skill-socket-path.ts +0 -20
- package/src/jobs/register-job-handlers.ts +0 -74
- package/src/persistence/invite-store.ts +0 -481
- package/src/plugins/job-handler-registry.ts +0 -94
- package/src/providers/cache-control.ts +0 -26
- package/src/runtime/__tests__/runtime-mode.test.ts +0 -62
- package/src/runtime/invite-redemption-service.ts +0 -828
- package/src/runtime/invite-redemption-templates.ts +0 -46
- package/src/runtime/runtime-mode.ts +0 -40
- package/src/runtime/slack-dm-text-delivery.ts +0 -177
- package/src/util/voice-code.ts +0 -31
|
@@ -15,7 +15,10 @@ function isDuplicateColumnError(err: unknown): boolean {
|
|
|
15
15
|
|
|
16
16
|
function ensureDisplayOrderColumns(): void {
|
|
17
17
|
try {
|
|
18
|
-
rawRun(
|
|
18
|
+
rawRun(
|
|
19
|
+
"conversation:migrateDisplayOrder",
|
|
20
|
+
"ALTER TABLE conversations ADD COLUMN display_order INTEGER",
|
|
21
|
+
);
|
|
19
22
|
} catch (err) {
|
|
20
23
|
if (!isDuplicateColumnError(err)) {
|
|
21
24
|
log.error({ err }, "Failed to add display_order column");
|
|
@@ -23,7 +26,10 @@ function ensureDisplayOrderColumns(): void {
|
|
|
23
26
|
}
|
|
24
27
|
}
|
|
25
28
|
try {
|
|
26
|
-
rawRun(
|
|
29
|
+
rawRun(
|
|
30
|
+
"conversation:migrateIsPinned",
|
|
31
|
+
"ALTER TABLE conversations ADD COLUMN is_pinned INTEGER DEFAULT 0",
|
|
32
|
+
);
|
|
27
33
|
} catch (err) {
|
|
28
34
|
if (!isDuplicateColumnError(err)) {
|
|
29
35
|
log.error({ err }, "Failed to add is_pinned column");
|
|
@@ -36,16 +36,19 @@ describe("ensureGroupMigration — reflections cleanup", () => {
|
|
|
36
36
|
`);
|
|
37
37
|
try {
|
|
38
38
|
rawRun(
|
|
39
|
+
"test:addGroupIdColumn",
|
|
39
40
|
"ALTER TABLE conversations ADD COLUMN group_id TEXT REFERENCES conversation_groups(id) ON DELETE SET NULL",
|
|
40
41
|
);
|
|
41
42
|
} catch {
|
|
42
43
|
// column already present — ok
|
|
43
44
|
}
|
|
44
45
|
rawRun(
|
|
46
|
+
"test:seedReflectionsGroup",
|
|
45
47
|
"INSERT OR IGNORE INTO conversation_groups (id, name, sort_position, is_system_group) VALUES ('system:reflections', 'Reflections', 100, TRUE)",
|
|
46
48
|
);
|
|
47
49
|
const now = Math.floor(Date.now() / 1000);
|
|
48
50
|
rawRun(
|
|
51
|
+
"test:seedLegacyConv",
|
|
49
52
|
"INSERT INTO conversations (id, created_at, updated_at, group_id) VALUES (?, ?, ?, ?)",
|
|
50
53
|
"legacy-refl-1",
|
|
51
54
|
now,
|
|
@@ -58,6 +61,7 @@ describe("ensureGroupMigration — reflections cleanup", () => {
|
|
|
58
61
|
|
|
59
62
|
test("moves legacy system:reflections conversations to system:background", () => {
|
|
60
63
|
const legacy = rawGet<{ group_id: string | null }>(
|
|
64
|
+
"test:getLegacyGroupId",
|
|
61
65
|
"SELECT group_id FROM conversations WHERE id = 'legacy-refl-1'",
|
|
62
66
|
);
|
|
63
67
|
expect(legacy?.group_id).toBe("system:background");
|
|
@@ -65,6 +69,7 @@ describe("ensureGroupMigration — reflections cleanup", () => {
|
|
|
65
69
|
|
|
66
70
|
test("deletes the orphaned system:reflections group row", () => {
|
|
67
71
|
const refl = rawGet<{ id: string }>(
|
|
72
|
+
"test:getReflectionsGroup",
|
|
68
73
|
"SELECT id FROM conversation_groups WHERE id = 'system:reflections'",
|
|
69
74
|
);
|
|
70
75
|
expect(refl).toBeNull();
|
|
@@ -72,6 +77,7 @@ describe("ensureGroupMigration — reflections cleanup", () => {
|
|
|
72
77
|
|
|
73
78
|
test("seeds the current system groups without system:reflections", () => {
|
|
74
79
|
const systemIds = rawAll<{ id: string }>(
|
|
80
|
+
"test:listSystemGroups",
|
|
75
81
|
"SELECT id FROM conversation_groups WHERE id LIKE 'system:%' ORDER BY id",
|
|
76
82
|
).map((r) => r.id);
|
|
77
83
|
expect(systemIds).toEqual([
|
|
@@ -84,6 +90,7 @@ describe("ensureGroupMigration — reflections cleanup", () => {
|
|
|
84
90
|
|
|
85
91
|
test("records the cleanup sentinel so the step is idempotent", () => {
|
|
86
92
|
const sentinel = rawGet<{ id: string }>(
|
|
93
|
+
"test:getSentinel",
|
|
87
94
|
"SELECT id FROM conversation_groups WHERE id = '_reflections_group_deleted_complete'",
|
|
88
95
|
);
|
|
89
96
|
expect(sentinel?.id).toBe("_reflections_group_deleted_complete");
|
|
@@ -92,6 +99,7 @@ describe("ensureGroupMigration — reflections cleanup", () => {
|
|
|
92
99
|
test("re-running ensureGroupMigration does not recreate the stale group", () => {
|
|
93
100
|
expect(() => ensureGroupMigration()).not.toThrow();
|
|
94
101
|
const refl = rawGet<{ id: string }>(
|
|
102
|
+
"test:getReflectionsGroup",
|
|
95
103
|
"SELECT id FROM conversation_groups WHERE id = 'system:reflections'",
|
|
96
104
|
);
|
|
97
105
|
expect(refl).toBeNull();
|
|
@@ -59,6 +59,7 @@ export function ensureGroupMigration(): void {
|
|
|
59
59
|
// only swallow duplicate-column errors, log+throw everything else.
|
|
60
60
|
try {
|
|
61
61
|
rawRun(
|
|
62
|
+
"group:migrateAddGroupId",
|
|
62
63
|
"ALTER TABLE conversations ADD COLUMN group_id TEXT REFERENCES conversation_groups(id) ON DELETE SET NULL",
|
|
63
64
|
);
|
|
64
65
|
} catch (err) {
|
|
@@ -84,18 +85,22 @@ export function ensureGroupMigration(): void {
|
|
|
84
85
|
// transaction so a crash between the shift and the sentinel can't cause
|
|
85
86
|
// repeated drift on restart.
|
|
86
87
|
const sortShiftDone = rawGet<{ id: string }>(
|
|
88
|
+
"group:migrateSortShiftGuard",
|
|
87
89
|
"SELECT id FROM conversation_groups WHERE id = '_sort_shift_complete'",
|
|
88
90
|
);
|
|
89
91
|
if (!sortShiftDone) {
|
|
90
92
|
try {
|
|
91
93
|
rawExec("BEGIN");
|
|
92
94
|
rawRun(
|
|
95
|
+
"group:migrateSortShift:shift",
|
|
93
96
|
"UPDATE conversation_groups SET sort_position = sort_position + 1 WHERE is_system_group = 0 AND sort_position >= 3",
|
|
94
97
|
);
|
|
95
98
|
rawRun(
|
|
99
|
+
"group:migrateSortShift:setAll",
|
|
96
100
|
"UPDATE conversation_groups SET sort_position = 3 WHERE id = 'system:all' AND sort_position != 3",
|
|
97
101
|
);
|
|
98
102
|
rawRun(
|
|
103
|
+
"group:migrateSortShift:marker",
|
|
99
104
|
`INSERT OR IGNORE INTO conversation_groups (id, name, sort_position, is_system_group, created_at, updated_at)
|
|
100
105
|
VALUES ('_sort_shift_complete', '_sort_shift_complete', -1, TRUE, ${now}, ${now})`,
|
|
101
106
|
);
|
|
@@ -115,6 +120,7 @@ export function ensureGroupMigration(): void {
|
|
|
115
120
|
// would backfill it back into system:background). A persistent marker row in
|
|
116
121
|
// conversation_groups tracks whether backfill has already completed.
|
|
117
122
|
const backfillDone = rawGet<{ id: string }>(
|
|
123
|
+
"group:migrateBackfillGuard",
|
|
118
124
|
"SELECT id FROM conversation_groups WHERE id = '_backfill_complete'",
|
|
119
125
|
);
|
|
120
126
|
|
|
@@ -200,6 +206,7 @@ export function ensureGroupMigration(): void {
|
|
|
200
206
|
// Uses its own sentinel so it runs exactly once, even on existing installations
|
|
201
207
|
// where the original backfill already completed.
|
|
202
208
|
const allBackfillDone = rawGet<{ id: string }>(
|
|
209
|
+
"group:migrateBackfillAllGuard",
|
|
203
210
|
"SELECT id FROM conversation_groups WHERE id = '_backfill_all_complete'",
|
|
204
211
|
);
|
|
205
212
|
|
|
@@ -226,6 +233,7 @@ export function ensureGroupMigration(): void {
|
|
|
226
233
|
|
|
227
234
|
// 6. One-time migration: move auto-analysis conversations from system:reflections to system:background
|
|
228
235
|
const reflectionsMigrateDone = rawGet<{ id: string }>(
|
|
236
|
+
"group:migrateReflectionsGuard",
|
|
229
237
|
"SELECT id FROM conversation_groups WHERE id = '_reflections_to_background_complete'",
|
|
230
238
|
);
|
|
231
239
|
|
|
@@ -262,6 +270,7 @@ export function ensureGroupMigration(): void {
|
|
|
262
270
|
// place causes the macOS sidebar to render an empty duplicate "Reflections"
|
|
263
271
|
// entry with a fallback folder icon alongside the Background sub-group.
|
|
264
272
|
const reflectionsGroupDeleted = rawGet<{ id: string }>(
|
|
273
|
+
"group:migrateReflDeleteGuard",
|
|
265
274
|
"SELECT id FROM conversation_groups WHERE id = '_reflections_group_deleted_complete'",
|
|
266
275
|
);
|
|
267
276
|
|
|
@@ -7,10 +7,12 @@ import {
|
|
|
7
7
|
wrapUntrustedContent,
|
|
8
8
|
} from "../security/untrusted-content.js";
|
|
9
9
|
import { getLogger } from "../util/logger.js";
|
|
10
|
+
import { isLexicalBackfillComplete } from "./checkpoints.js";
|
|
10
11
|
import type { ConversationRow } from "./conversation-crud.js";
|
|
11
12
|
import { parseConversation } from "./conversation-crud.js";
|
|
12
13
|
import { ensureDisplayOrderMigration } from "./conversation-display-order-migration.js";
|
|
13
14
|
import { ensureGroupMigration } from "./conversation-group-migration.js";
|
|
15
|
+
import { searchMessageIdsLexical } from "./conversation-search-lexical.js";
|
|
14
16
|
import type { ConversationType } from "./conversation-types.js";
|
|
15
17
|
import { getDb } from "./db-connection.js";
|
|
16
18
|
import { rawAll } from "./raw-query.js";
|
|
@@ -19,34 +21,54 @@ import { conversations, messages } from "./schema/index.js";
|
|
|
19
21
|
const log = getLogger("conversation-store");
|
|
20
22
|
|
|
21
23
|
/**
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
+
* Max distinct visible conversations {@link searchConversations} collects from
|
|
25
|
+
* content matches before merging with title matches and the final
|
|
26
|
+
* `updated_at`-ordered `LIMIT`. Both backends cap on distinct conversations (not
|
|
27
|
+
* matching messages) so one chatty conversation can't crowd others out; the FTS
|
|
28
|
+
* path enforces this in SQL (`SELECT DISTINCT conversation_id … LIMIT`).
|
|
24
29
|
*/
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
30
|
+
const CONVERSATION_SEARCH_DISTINCT_LIMIT = 1000;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Message-candidate over-fetch for the Qdrant lexical backend.
|
|
34
|
+
*
|
|
35
|
+
* Qdrant ranks at message grain and has no visibility/archived filtering, so a
|
|
36
|
+
* naive cap at {@link CONVERSATION_SEARCH_DISTINCT_LIMIT} *messages* could be
|
|
37
|
+
* consumed entirely by one chatty visible conversation or by private/archived
|
|
38
|
+
* candidates, starving other matching visible conversations — unlike the FTS
|
|
39
|
+
* path, which caps on distinct conversations *after* the visibility JOIN.
|
|
40
|
+
*
|
|
41
|
+
* To approximate that behavior we over-fetch this many message candidates, then
|
|
42
|
+
* filter/dedupe to distinct visible conversations in code, so the effective cap
|
|
43
|
+
* lands on {@link CONVERSATION_SEARCH_DISTINCT_LIMIT} distinct visible
|
|
44
|
+
* conversations. A few thousand candidates reliably yields far more than the
|
|
45
|
+
* caller's `limit` (default 20) distinct visible conversations except in
|
|
46
|
+
* pathological mega-conversation cases.
|
|
47
|
+
*
|
|
48
|
+
* This is a proportionate over-fetch, NOT a hard parity guarantee: a query
|
|
49
|
+
* whose top ~{@link QDRANT_SEARCH_CANDIDATE_LIMIT} candidates all fall in a
|
|
50
|
+
* handful of conversations can still under-surface. The stronger fix — Qdrant
|
|
51
|
+
* `search_groups` (group_by `conversation_id`) or true score-ordered
|
|
52
|
+
* pagination — is deferred to pre-flip hardening if Gate A shows regressions.
|
|
53
|
+
*
|
|
54
|
+
* Kept well under bun:sqlite's ~32k bound-parameter ceiling so the
|
|
55
|
+
* `WHERE m.id IN (…)` visibility query stays a single statement.
|
|
56
|
+
*/
|
|
57
|
+
const QDRANT_SEARCH_CANDIDATE_LIMIT = 5000;
|
|
40
58
|
|
|
41
|
-
|
|
59
|
+
/**
|
|
60
|
+
* Whether `text` yields at least one usable lexical token (≥2 chars of
|
|
61
|
+
* `[a-z0-9_]`). Queries with none — non-ASCII, single-char, or
|
|
62
|
+
* punctuation-only input like "你", "é", "C++" — cannot be served by content
|
|
63
|
+
* matching: the sparse encoder would still emit noisy 1-char tokens for them,
|
|
64
|
+
* so the read sites skip the lexical index entirely and fall back to their
|
|
65
|
+
* no-token behavior (title matches only / no evidence).
|
|
66
|
+
*/
|
|
67
|
+
export function hasLexicalTokens(text: string): boolean {
|
|
68
|
+
return text
|
|
42
69
|
.toLowerCase()
|
|
43
70
|
.split(/[^a-z0-9_]+/g)
|
|
44
|
-
.
|
|
45
|
-
.filter((token) => token.length >= 2);
|
|
46
|
-
if (tokens.length === 0) return null;
|
|
47
|
-
const unique = [...new Set(tokens)].slice(0, 24);
|
|
48
|
-
// Space-separated quoted tokens are implicit AND in FTS5.
|
|
49
|
-
return unique.map((token) => `"${token.replace(/"/g, '""')}"`).join(" ");
|
|
71
|
+
.some((token) => token.trim().length >= 2);
|
|
50
72
|
}
|
|
51
73
|
|
|
52
74
|
/**
|
|
@@ -259,7 +281,9 @@ export function getMessageRoleStatsByConversation(
|
|
|
259
281
|
conversationIds: string[],
|
|
260
282
|
role: string = "assistant",
|
|
261
283
|
): Map<string, { count: number; lastAt: number }> {
|
|
262
|
-
if (conversationIds.length === 0)
|
|
284
|
+
if (conversationIds.length === 0) {
|
|
285
|
+
return new Map();
|
|
286
|
+
}
|
|
263
287
|
const db = getDb();
|
|
264
288
|
const rows = db
|
|
265
289
|
.select({
|
|
@@ -353,6 +377,7 @@ export function listConversationsByTitlePrefix(
|
|
|
353
377
|
created_at: number;
|
|
354
378
|
}
|
|
355
379
|
const rows = rawAll<Row>(
|
|
380
|
+
"conversation:listByTitlePrefix",
|
|
356
381
|
`SELECT c.id, c.title,
|
|
357
382
|
(SELECT COUNT(*) FROM messages WHERE conversation_id = c.id) AS message_count,
|
|
358
383
|
c.created_at
|
|
@@ -410,7 +435,9 @@ export function isLastUserMessageToolResult(conversationId: string): boolean {
|
|
|
410
435
|
.limit(1)
|
|
411
436
|
.get();
|
|
412
437
|
|
|
413
|
-
if (!lastUserMsg)
|
|
438
|
+
if (!lastUserMsg) {
|
|
439
|
+
return false;
|
|
440
|
+
}
|
|
414
441
|
|
|
415
442
|
try {
|
|
416
443
|
const parsed = JSON.parse(lastUserMsg.content);
|
|
@@ -448,82 +475,187 @@ export interface ConversationSearchResult {
|
|
|
448
475
|
}>;
|
|
449
476
|
}
|
|
450
477
|
|
|
478
|
+
interface ConversationSearchMsgRow {
|
|
479
|
+
id: string;
|
|
480
|
+
role: string;
|
|
481
|
+
content: string;
|
|
482
|
+
created_at: number;
|
|
483
|
+
}
|
|
484
|
+
|
|
451
485
|
/**
|
|
452
|
-
*
|
|
453
|
-
*
|
|
454
|
-
*
|
|
455
|
-
* conversations with their relevant messages, ordered by most recently updated.
|
|
486
|
+
* SQL `LIKE` pattern escaping the three wildcard metacharacters (`\`, `%`, `_`)
|
|
487
|
+
* so a literal user query matches as literal text under `ESCAPE '\\'`. Wrapped
|
|
488
|
+
* in `%...%` for a substring match.
|
|
456
489
|
*/
|
|
457
|
-
|
|
490
|
+
function likeContainsPattern(query: string): string {
|
|
491
|
+
return `%${query
|
|
492
|
+
.replace(/\\/g, "\\\\")
|
|
493
|
+
.replace(/%/g, "\\%")
|
|
494
|
+
.replace(/_/g, "\\_")}%`;
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
/**
|
|
498
|
+
* Whether the sparse Qdrant `messages_lexical` index — the only source of
|
|
499
|
+
* message-content matches — is a safe read source. Content matching is
|
|
500
|
+
* unavailable (title matches only) until the one-time upgrade backfill has
|
|
501
|
+
* fully drained: a partially populated collection would silently miss older
|
|
502
|
+
* content (an empty result — not a throw). Indexing itself is unconditional
|
|
503
|
+
* host infrastructure, so completion is the only gate; the recall read site
|
|
504
|
+
* applies the same one via the shared {@link isLexicalBackfillComplete}.
|
|
505
|
+
*/
|
|
506
|
+
function isMessageContentSearchAvailable(): boolean {
|
|
507
|
+
return isLexicalBackfillComplete();
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
/**
|
|
511
|
+
* Full-text search across message content.
|
|
512
|
+
*
|
|
513
|
+
* Message-content candidates come from the sparse `messages_lexical` Qdrant
|
|
514
|
+
* index (BM25-style), filtered by the visibility/archived SQL predicates and
|
|
515
|
+
* merged with a `LIKE` match on conversation titles; matching conversations
|
|
516
|
+
* return with their relevant messages, ordered by most recently updated.
|
|
517
|
+
*
|
|
518
|
+
* Content matching is index-only — there is no `messages.content` scan
|
|
519
|
+
* fallback and no other content source. Only the title arm can match while
|
|
520
|
+
* the index is not a safe read source ({@link isMessageContentSearchAvailable}),
|
|
521
|
+
* for a query that tokenizes to nothing under the shared tokenizer (non-ASCII
|
|
522
|
+
* or single-char input like "你", "é", "C++"), or when the Qdrant lexical
|
|
523
|
+
* lookup fails (logged). An unindexed or unreachable index yields fewer
|
|
524
|
+
* results, by design.
|
|
525
|
+
*
|
|
526
|
+
* The content cap lands on distinct conversations, not matching messages:
|
|
527
|
+
* Qdrant ranks at message grain, so the read over-fetches a candidate pool
|
|
528
|
+
* ({@link QDRANT_SEARCH_CANDIDATE_LIMIT}) and dedupes to
|
|
529
|
+
* {@link CONVERSATION_SEARCH_DISTINCT_LIMIT} distinct visible conversations
|
|
530
|
+
* in score order (see the constants' docstrings).
|
|
531
|
+
*/
|
|
532
|
+
export async function searchConversations(
|
|
458
533
|
query: string,
|
|
459
534
|
opts?: { limit?: number; maxMessagesPerConversation?: number },
|
|
460
|
-
): ConversationSearchResult[] {
|
|
461
|
-
if (!query.trim())
|
|
535
|
+
): Promise<ConversationSearchResult[]> {
|
|
536
|
+
if (!query.trim()) {
|
|
537
|
+
return [];
|
|
538
|
+
}
|
|
462
539
|
|
|
463
540
|
ensureGroupMigration();
|
|
464
541
|
const db = getDb();
|
|
542
|
+
const trimmed = query.trim();
|
|
465
543
|
const limit = opts?.limit ?? 20;
|
|
466
544
|
const maxMsgsPerConv = opts?.maxMessagesPerConversation ?? 3;
|
|
467
545
|
|
|
468
|
-
const
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
//
|
|
481
|
-
//
|
|
482
|
-
//
|
|
483
|
-
//
|
|
484
|
-
|
|
485
|
-
|
|
546
|
+
const hasTokens = hasLexicalTokens(trimmed);
|
|
547
|
+
const contentSearchAvailable = isMessageContentSearchAvailable();
|
|
548
|
+
|
|
549
|
+
// LIKE pattern for title matching (message-content indexes don't cover titles).
|
|
550
|
+
const titlePattern = likeContainsPattern(query);
|
|
551
|
+
|
|
552
|
+
// Collect conversation IDs from message-content matches and title LIKE
|
|
553
|
+
// matches, then merge them to produce the final set of matching
|
|
554
|
+
// conversations. Content paths LIMIT on distinct conversation_id to prevent a
|
|
555
|
+
// single conversation with many matching messages from crowding out others.
|
|
556
|
+
const contentConvIds = new Set<string>();
|
|
557
|
+
|
|
558
|
+
// When the Qdrant backend answers, `qdrantCandidatesByConv` maps each
|
|
559
|
+
// conversation to its candidate message ids so the per-conversation message
|
|
560
|
+
// fetch reuses the single lexical round-trip instead of issuing a second one.
|
|
561
|
+
// On a lexical-lookup failure it stays null, so the content arm contributes
|
|
562
|
+
// nothing and only title matches surface.
|
|
563
|
+
let qdrantCandidatesByConv: Map<string, string[]> | null = null;
|
|
564
|
+
|
|
565
|
+
if (hasTokens && contentSearchAvailable) {
|
|
566
|
+
// The lexical index is written asynchronously by the memory job queue, so
|
|
567
|
+
// for a brief window after an edit it can lag SQLite: a search for a term
|
|
568
|
+
// just removed from a message may still return that message id (or miss one
|
|
569
|
+
// just added). This is an accepted async-indexing tradeoff — transient, and
|
|
570
|
+
// results are re-sorted by `updated_at` — not re-verified against live
|
|
571
|
+
// content here.
|
|
572
|
+
let candidates: Awaited<ReturnType<typeof searchMessageIdsLexical>> = [];
|
|
486
573
|
try {
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
FROM messages_fts f
|
|
491
|
-
JOIN messages m ON m.id = f.message_id
|
|
492
|
-
JOIN conversations c ON c.id = m.conversation_id
|
|
493
|
-
WHERE messages_fts MATCH ? AND ${standardListingVisibilitySql("c")} AND c.archived_at IS NULL
|
|
494
|
-
LIMIT 1000
|
|
495
|
-
`,
|
|
496
|
-
ftsMatch,
|
|
574
|
+
candidates = await searchMessageIdsLexical(
|
|
575
|
+
trimmed,
|
|
576
|
+
QDRANT_SEARCH_CANDIDATE_LIMIT,
|
|
497
577
|
);
|
|
498
|
-
for (const row of ftsRows) ftsConvIds.add(row.conversation_id);
|
|
499
578
|
} catch (err) {
|
|
500
579
|
log.warn(
|
|
501
580
|
{ err, query: query.slice(0, 80) },
|
|
502
|
-
"searchConversations:
|
|
581
|
+
"searchConversations: Qdrant lexical query failed — returning title matches only",
|
|
503
582
|
);
|
|
504
583
|
}
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
584
|
+
|
|
585
|
+
if (candidates.length > 0) {
|
|
586
|
+
const candidateIds = candidates.map((c) => c.messageId);
|
|
587
|
+
// Filter the lexical candidates down to visible, non-archived
|
|
588
|
+
// conversations in SQL (Qdrant has no visibility filtering). No SQL
|
|
589
|
+
// LIMIT here: the row count is already bounded by the candidate pool
|
|
590
|
+
// (≤ QDRANT_SEARCH_CANDIDATE_LIMIT), and capping in SQL would cap on
|
|
591
|
+
// messages, not distinct conversations. The distinct-conversation cap is
|
|
592
|
+
// applied below in Qdrant score order.
|
|
593
|
+
interface CandidateRow {
|
|
594
|
+
id: string;
|
|
595
|
+
conversation_id: string;
|
|
596
|
+
}
|
|
597
|
+
const placeholders = candidateIds.map(() => "?").join(",");
|
|
598
|
+
const visibleRows = rawAll<CandidateRow>(
|
|
599
|
+
"conversation:searchConversations:visibleCandidates",
|
|
600
|
+
`
|
|
601
|
+
SELECT m.id, m.conversation_id
|
|
602
|
+
FROM messages m
|
|
603
|
+
JOIN conversations c ON c.id = m.conversation_id
|
|
604
|
+
WHERE m.id IN (${placeholders}) AND ${standardListingVisibilitySql("c")} AND c.archived_at IS NULL
|
|
605
|
+
`,
|
|
606
|
+
...candidateIds,
|
|
607
|
+
);
|
|
608
|
+
const visibleConvByMessageId = new Map<string, string>();
|
|
609
|
+
for (const row of visibleRows) {
|
|
610
|
+
visibleConvByMessageId.set(row.id, row.conversation_id);
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
// Walk candidates in Qdrant score order (highest first). Bucket each
|
|
614
|
+
// visible candidate's message id under its conversation and stop
|
|
615
|
+
// collecting NEW conversations once CONVERSATION_SEARCH_DISTINCT_LIMIT
|
|
616
|
+
// distinct visible conversations are seen — so one chatty conversation
|
|
617
|
+
// can't starve others, matching the FTS distinct-conversation cap.
|
|
618
|
+
qdrantCandidatesByConv = new Map();
|
|
619
|
+
for (const candidate of candidates) {
|
|
620
|
+
const convId = visibleConvByMessageId.get(candidate.messageId);
|
|
621
|
+
if (!convId) {
|
|
622
|
+
continue;
|
|
623
|
+
}
|
|
624
|
+
const bucket = qdrantCandidatesByConv.get(convId);
|
|
625
|
+
if (bucket) {
|
|
626
|
+
bucket.push(candidate.messageId);
|
|
627
|
+
} else {
|
|
628
|
+
if (
|
|
629
|
+
qdrantCandidatesByConv.size >= CONVERSATION_SEARCH_DISTINCT_LIMIT
|
|
630
|
+
) {
|
|
631
|
+
continue;
|
|
632
|
+
}
|
|
633
|
+
qdrantCandidatesByConv.set(convId, [candidate.messageId]);
|
|
634
|
+
contentConvIds.add(convId);
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
} else if (hasTokens) {
|
|
639
|
+
// The lexical index is not a safe read source (memory disabled, or the
|
|
640
|
+
// one-time upgrade backfill still draining) — the query can only match by
|
|
641
|
+
// conversation title below. The breadcrumb explains an otherwise
|
|
642
|
+
// surprising lack of content matches.
|
|
643
|
+
log.info(
|
|
644
|
+
{ query: query.slice(0, 80) },
|
|
645
|
+
"searchConversations: lexical index unavailable — title matches only",
|
|
646
|
+
);
|
|
647
|
+
} else {
|
|
648
|
+
// The query tokenized to nothing (non-ASCII, single-char, etc. — "你",
|
|
649
|
+
// "é", "C++"). Content matching is index-only, so such queries can only
|
|
650
|
+
// match by conversation title below. The breadcrumb explains an otherwise
|
|
651
|
+
// surprising empty result.
|
|
652
|
+
log.info(
|
|
653
|
+
{ query: query.slice(0, 80) },
|
|
654
|
+
"searchConversations: query produced no lexical tokens — title matches only",
|
|
522
655
|
);
|
|
523
|
-
for (const row of likeRows) ftsConvIds.add(row.conversation_id);
|
|
524
656
|
}
|
|
525
657
|
|
|
526
|
-
// Title-only matches (
|
|
658
|
+
// Title-only matches (message-content indexes don't cover conversation titles).
|
|
527
659
|
const titleMatchConvs = db
|
|
528
660
|
.select({ id: conversations.id })
|
|
529
661
|
.from(conversations)
|
|
@@ -535,12 +667,16 @@ export function searchConversations(
|
|
|
535
667
|
),
|
|
536
668
|
)
|
|
537
669
|
.all();
|
|
538
|
-
for (const row of titleMatchConvs)
|
|
670
|
+
for (const row of titleMatchConvs) {
|
|
671
|
+
contentConvIds.add(row.id);
|
|
672
|
+
}
|
|
539
673
|
|
|
540
|
-
if (
|
|
674
|
+
if (contentConvIds.size === 0) {
|
|
675
|
+
return [];
|
|
676
|
+
}
|
|
541
677
|
|
|
542
678
|
// Fetch the matching conversation rows, ordered by updatedAt, capped at limit.
|
|
543
|
-
const convIds = [...
|
|
679
|
+
const convIds = [...contentConvIds];
|
|
544
680
|
const placeholders = convIds.map(() => "?").join(",");
|
|
545
681
|
interface ConvRow {
|
|
546
682
|
id: string;
|
|
@@ -548,6 +684,7 @@ export function searchConversations(
|
|
|
548
684
|
updated_at: number;
|
|
549
685
|
}
|
|
550
686
|
const matchingConversations = rawAll<ConvRow>(
|
|
687
|
+
"conversation:searchConversations:matchingConversations",
|
|
551
688
|
`SELECT id, title, updated_at FROM conversations
|
|
552
689
|
WHERE id IN (${placeholders})
|
|
553
690
|
ORDER BY updated_at DESC
|
|
@@ -556,57 +693,39 @@ export function searchConversations(
|
|
|
556
693
|
limit,
|
|
557
694
|
);
|
|
558
695
|
|
|
559
|
-
if (matchingConversations.length === 0)
|
|
696
|
+
if (matchingConversations.length === 0) {
|
|
697
|
+
return [];
|
|
698
|
+
}
|
|
560
699
|
|
|
561
700
|
const results: ConversationSearchResult[] = [];
|
|
562
701
|
|
|
563
702
|
for (const conv of matchingConversations) {
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
703
|
+
// Conversations without content candidates — title-only matches, every
|
|
704
|
+
// match on a no-token query, and all matches while content search is
|
|
705
|
+
// unavailable or the Qdrant lookup failed — carry no content excerpts:
|
|
706
|
+
// `matchingMsgs` stays empty.
|
|
707
|
+
let matchingMsgs: ConversationSearchMsgRow[] = [];
|
|
708
|
+
if (hasTokens && contentSearchAvailable) {
|
|
709
|
+
// Reuse the lexical candidates already fetched above — no second Qdrant
|
|
710
|
+
// round-trip. Select this conversation's candidate message rows by id,
|
|
711
|
+
// ordered oldest-first.
|
|
712
|
+
const candidateIds = qdrantCandidatesByConv?.get(conv.id) ?? [];
|
|
713
|
+
if (candidateIds.length > 0) {
|
|
714
|
+
const msgPlaceholders = candidateIds.map(() => "?").join(",");
|
|
715
|
+
matchingMsgs = rawAll<ConversationSearchMsgRow>(
|
|
716
|
+
"conversation:searchConversations:matchingMessages",
|
|
574
717
|
`
|
|
575
|
-
SELECT
|
|
576
|
-
FROM
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
ORDER BY m.created_at ASC
|
|
718
|
+
SELECT id, role, content, created_at
|
|
719
|
+
FROM messages
|
|
720
|
+
WHERE conversation_id = ? AND id IN (${msgPlaceholders})
|
|
721
|
+
ORDER BY created_at ASC
|
|
580
722
|
LIMIT ?
|
|
581
723
|
`,
|
|
582
|
-
ftsMatch,
|
|
583
724
|
conv.id,
|
|
725
|
+
...candidateIds,
|
|
584
726
|
maxMsgsPerConv,
|
|
585
727
|
);
|
|
586
|
-
} catch (err) {
|
|
587
|
-
log.warn(
|
|
588
|
-
{ err, conversationId: conv.id },
|
|
589
|
-
"searchConversations: FTS per-conversation query failed",
|
|
590
|
-
);
|
|
591
728
|
}
|
|
592
|
-
} else if (query.trim()) {
|
|
593
|
-
// LIKE fallback for non-ASCII / short-token queries.
|
|
594
|
-
const msgLikePattern = `%${query
|
|
595
|
-
.replace(/\\/g, "\\\\")
|
|
596
|
-
.replace(/%/g, "\\%")
|
|
597
|
-
.replace(/_/g, "\\_")}%`;
|
|
598
|
-
matchingMsgs = rawAll<MsgRow>(
|
|
599
|
-
`
|
|
600
|
-
SELECT id, role, content, created_at
|
|
601
|
-
FROM messages
|
|
602
|
-
WHERE conversation_id = ? AND content LIKE ? ESCAPE '\\'
|
|
603
|
-
ORDER BY created_at ASC
|
|
604
|
-
LIMIT ?
|
|
605
|
-
`,
|
|
606
|
-
conv.id,
|
|
607
|
-
msgLikePattern,
|
|
608
|
-
maxMsgsPerConv,
|
|
609
|
-
);
|
|
610
729
|
}
|
|
611
730
|
|
|
612
731
|
results.push({
|
|
@@ -45,12 +45,16 @@ import {
|
|
|
45
45
|
/**
|
|
46
46
|
* Default batch size for the chunked delete. Each batch is one `DELETE` that
|
|
47
47
|
* auto-commits, so this bounds how long the subprocess holds the write lock
|
|
48
|
-
* before releasing it to in-process writers
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
48
|
+
* before releasing it to in-process writers; the worst-case wait for a
|
|
49
|
+
* contending foreground write is one batch, and delete latency doesn't matter
|
|
50
|
+
* (nobody waits on a background GC).
|
|
51
|
+
*
|
|
52
|
+
* A deleted row costs a plain b-tree delete plus any FK cascade deletes —
|
|
53
|
+
* cheap and roughly proportional to the row count, with no per-row trigger
|
|
54
|
+
* work — so a batch this size still holds the lock only briefly while
|
|
55
|
+
* amortizing the per-batch subprocess spawn cost.
|
|
52
56
|
*/
|
|
53
|
-
export const DEFAULT_DELETE_BATCH_SIZE =
|
|
57
|
+
export const DEFAULT_DELETE_BATCH_SIZE = 200;
|
|
54
58
|
|
|
55
59
|
/**
|
|
56
60
|
* Pause inserted between batch subprocess calls. Without it the delete releases
|