@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
|
@@ -406,29 +406,77 @@ function normalizeInstructionText(value: string): string {
|
|
|
406
406
|
.trim();
|
|
407
407
|
}
|
|
408
408
|
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
requestCode: string,
|
|
412
|
-
mode: GuardianQuestionInstructionMode,
|
|
413
|
-
): string {
|
|
414
|
-
const escapedCode = escapeRegExp(requestCode);
|
|
415
|
-
const approvalInstructionPattern = new RegExp(
|
|
409
|
+
function buildApprovalInstructionPattern(escapedCode: string): RegExp {
|
|
410
|
+
return new RegExp(
|
|
416
411
|
`(?:Reference\\s+code:\\s*${escapedCode}\\.?\\s*)?Reply\\s+"${escapedCode}\\s+approve"\\s+or\\s+"${escapedCode}\\s+reject"\\.?`,
|
|
417
412
|
"ig",
|
|
418
413
|
);
|
|
419
|
-
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
function buildAnswerInstructionPattern(escapedCode: string): RegExp {
|
|
417
|
+
return new RegExp(
|
|
420
418
|
`(?:Reference\\s+code:\\s*${escapedCode}\\.?\\s*)?Reply\\s+"${escapedCode}\\s+<your\\s+answer>"\\.?`,
|
|
421
419
|
"ig",
|
|
422
420
|
);
|
|
421
|
+
}
|
|
423
422
|
|
|
423
|
+
export function stripConflictingGuardianRequestInstructions(
|
|
424
|
+
text: string,
|
|
425
|
+
requestCode: string,
|
|
426
|
+
mode: GuardianQuestionInstructionMode,
|
|
427
|
+
): string {
|
|
428
|
+
const escapedCode = escapeRegExp(requestCode);
|
|
424
429
|
const next =
|
|
425
430
|
mode === "answer"
|
|
426
|
-
? text.replace(
|
|
427
|
-
: text.replace(
|
|
431
|
+
? text.replace(buildApprovalInstructionPattern(escapedCode), "")
|
|
432
|
+
: text.replace(buildAnswerInstructionPattern(escapedCode), "");
|
|
433
|
+
|
|
434
|
+
return normalizeInstructionText(next);
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
/**
|
|
438
|
+
* Remove every request-code reply instruction (both modes) plus bare
|
|
439
|
+
* "Reference code: X." / "Approval code: X." mentions from copy destined
|
|
440
|
+
* for a surface that renders interactive Approve/Reject buttons, where
|
|
441
|
+
* code-reply instructions are redundant noise.
|
|
442
|
+
*/
|
|
443
|
+
export function stripGuardianRequestCodeInstructions(
|
|
444
|
+
text: string,
|
|
445
|
+
requestCode: string,
|
|
446
|
+
): string {
|
|
447
|
+
const escapedCode = escapeRegExp(requestCode);
|
|
448
|
+
const next = text
|
|
449
|
+
.replace(buildApprovalInstructionPattern(escapedCode), "")
|
|
450
|
+
.replace(buildAnswerInstructionPattern(escapedCode), "")
|
|
451
|
+
.replace(
|
|
452
|
+
new RegExp(`(?:Reference|Approval)\\s+code:\\s*${escapedCode}\\.?`, "ig"),
|
|
453
|
+
"",
|
|
454
|
+
);
|
|
428
455
|
|
|
429
456
|
return normalizeInstructionText(next);
|
|
430
457
|
}
|
|
431
458
|
|
|
459
|
+
/**
|
|
460
|
+
* Parse a guardian.question payload that renders channel-native
|
|
461
|
+
* Approve/Reject actions on button-capable channels: it parses strictly,
|
|
462
|
+
* resolves to approval mode, and carries the requestId the action
|
|
463
|
+
* callbacks target. Returns `null` otherwise — those payloads render as
|
|
464
|
+
* plain text and rely on request-code replies.
|
|
465
|
+
*/
|
|
466
|
+
export function parseInteractiveApprovalPayload(
|
|
467
|
+
payload: Record<string, unknown>,
|
|
468
|
+
): GuardianQuestionPayload | null {
|
|
469
|
+
const parsed = parseGuardianQuestionPayload(payload);
|
|
470
|
+
if (!parsed) {
|
|
471
|
+
return null;
|
|
472
|
+
}
|
|
473
|
+
const { mode } = resolveGuardianInstructionModeFromPayload(parsed);
|
|
474
|
+
if (mode !== "approval") {
|
|
475
|
+
return null;
|
|
476
|
+
}
|
|
477
|
+
return nonEmpty(parsed.requestId) ? parsed : null;
|
|
478
|
+
}
|
|
479
|
+
|
|
432
480
|
/**
|
|
433
481
|
* Resolve guardian reply instruction mode from a raw context payload.
|
|
434
482
|
*
|
|
@@ -7,6 +7,13 @@ import { RiskLevel } from "./types.js";
|
|
|
7
7
|
/** Execution context for per-context threshold resolution. */
|
|
8
8
|
export type ExecutionContext = "conversation" | "background" | "headless";
|
|
9
9
|
|
|
10
|
+
/**
|
|
11
|
+
* Auto-approve threshold: the highest risk level that is approved without
|
|
12
|
+
* prompting. Single source of truth for the threshold vocabulary — the
|
|
13
|
+
* gateway threshold reader and the sensitive-tool gate reuse this type.
|
|
14
|
+
*/
|
|
15
|
+
export type AutoApproveThreshold = "none" | "low" | "medium" | "high";
|
|
16
|
+
|
|
10
17
|
/** Contextual information that an approval policy uses to reach a decision. */
|
|
11
18
|
export interface ApprovalContext {
|
|
12
19
|
riskLevel: RiskLevel;
|
|
@@ -34,7 +41,7 @@ export interface ApprovalContext {
|
|
|
34
41
|
* - "medium": auto-approve Low and Medium risk
|
|
35
42
|
* - "high": auto-approve everything unconditionally
|
|
36
43
|
*/
|
|
37
|
-
autoApproveUpTo?:
|
|
44
|
+
autoApproveUpTo?: AutoApproveThreshold;
|
|
38
45
|
}
|
|
39
46
|
|
|
40
47
|
// ── Ordinal maps for threshold comparison ─────────────────────────────────────
|
|
@@ -27,6 +27,7 @@ import {
|
|
|
27
27
|
getWorkspacePluginsDir,
|
|
28
28
|
getWorkspaceRoutesDir,
|
|
29
29
|
getWorkspaceToolsDir,
|
|
30
|
+
getWorkspaceWorkflowsDir,
|
|
30
31
|
} from "../util/platform.js";
|
|
31
32
|
import {
|
|
32
33
|
type ApprovalContext,
|
|
@@ -311,6 +312,7 @@ function buildFileContext(): FileContext {
|
|
|
311
312
|
pluginsDir: resolveRealPath(getWorkspacePluginsDir()),
|
|
312
313
|
toolsDir: resolveRealPath(getWorkspaceToolsDir()),
|
|
313
314
|
routesDir: resolveRealPath(getWorkspaceRoutesDir()),
|
|
315
|
+
workflowsDir: resolveRealPath(getWorkspaceWorkflowsDir()),
|
|
314
316
|
actorTokenSigningKeyPath: join(protectedDir, "actor-token-signing-key"),
|
|
315
317
|
skillSourceDirs: getSkillRoots(config.skills.load.extraDirs).map(
|
|
316
318
|
resolveRealPath,
|
|
@@ -9,14 +9,15 @@
|
|
|
9
9
|
|
|
10
10
|
import { ipcCall } from "../ipc/gateway-client.js";
|
|
11
11
|
import { getLogger } from "../util/logger.js";
|
|
12
|
-
import type {
|
|
12
|
+
import type {
|
|
13
|
+
AutoApproveThreshold,
|
|
14
|
+
ExecutionContext,
|
|
15
|
+
} from "./approval-policy.js";
|
|
13
16
|
|
|
14
17
|
const log = getLogger("gateway-threshold-reader");
|
|
15
18
|
|
|
16
19
|
// ── Types ────────────────────────────────────────────────────────────────────
|
|
17
20
|
|
|
18
|
-
type Threshold = "none" | "low" | "medium" | "high";
|
|
19
|
-
|
|
20
21
|
interface GlobalThresholds {
|
|
21
22
|
interactive: string;
|
|
22
23
|
autonomous: string;
|
|
@@ -163,7 +164,7 @@ function mapExecutionContextToField(
|
|
|
163
164
|
return "autonomous";
|
|
164
165
|
}
|
|
165
166
|
|
|
166
|
-
function isValidThreshold(value: string): value is
|
|
167
|
+
function isValidThreshold(value: string): value is AutoApproveThreshold {
|
|
167
168
|
return (
|
|
168
169
|
value === "none" ||
|
|
169
170
|
value === "low" ||
|
|
@@ -188,7 +189,7 @@ function isValidThreshold(value: string): value is Threshold {
|
|
|
188
189
|
export async function getAutoApproveThreshold(
|
|
189
190
|
conversationId: string | undefined,
|
|
190
191
|
executionContext?: ExecutionContext,
|
|
191
|
-
): Promise<
|
|
192
|
+
): Promise<AutoApproveThreshold> {
|
|
192
193
|
const ctx: ExecutionContext = executionContext ?? "conversation";
|
|
193
194
|
|
|
194
195
|
// For conversation context with a conversationId, try per-conversation override first
|
|
@@ -315,7 +316,7 @@ async function fetchGlobalThresholds(): Promise<GlobalThresholds> {
|
|
|
315
316
|
export async function refreshAutoApproveThreshold(
|
|
316
317
|
conversationId: string | undefined,
|
|
317
318
|
executionContext?: ExecutionContext,
|
|
318
|
-
): Promise<
|
|
319
|
+
): Promise<AutoApproveThreshold | null> {
|
|
319
320
|
const ctx: ExecutionContext = executionContext ?? "conversation";
|
|
320
321
|
|
|
321
322
|
if (ctx === "conversation" && conversationId) {
|
|
@@ -18,6 +18,14 @@ export interface SecretPromptResult {
|
|
|
18
18
|
delivery: SecretDelivery;
|
|
19
19
|
/** When set, the prompt could not be delivered and the value is null due to a delivery failure (not user cancellation). */
|
|
20
20
|
error?: "unsupported_channel";
|
|
21
|
+
/**
|
|
22
|
+
* Why `value` is null. `"cancelled"` = the user explicitly dismissed the
|
|
23
|
+
* prompt (a valid flow, not a failure); `"timed_out"` = no response within
|
|
24
|
+
* the permission-timeout window. Only meaningful when `value` is null and
|
|
25
|
+
* `error` is unset. Lets callers distinguish a deliberate cancel from a
|
|
26
|
+
* genuine failure instead of treating both as an error.
|
|
27
|
+
*/
|
|
28
|
+
reason?: "cancelled" | "timed_out";
|
|
21
29
|
}
|
|
22
30
|
|
|
23
31
|
export interface SecretPrompterChannelContext {
|
|
@@ -149,9 +157,9 @@ export class SecretPrompter {
|
|
|
149
157
|
value === undefined ? "cancelled" : "answered",
|
|
150
158
|
);
|
|
151
159
|
this.ownedIds.delete(requestId);
|
|
152
|
-
(
|
|
153
|
-
|
|
154
|
-
);
|
|
160
|
+
(
|
|
161
|
+
interaction?.rpcResolve as ((v: SecretPromptResult) => void) | undefined
|
|
162
|
+
)?.({ value: value ?? null, delivery: delivery ?? "store" });
|
|
155
163
|
}
|
|
156
164
|
|
|
157
165
|
dispose(): void {
|
package/src/permissions/types.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
export type {
|
|
2
2
|
AllowlistOption,
|
|
3
3
|
ScopeOption,
|
|
4
|
-
} from "
|
|
5
|
-
export { RiskLevel } from "
|
|
4
|
+
} from "../tools/tool-types.js";
|
|
5
|
+
export { RiskLevel } from "../tools/tool-types.js";
|
|
6
6
|
|
|
7
7
|
export type ApprovalMode = "prompted" | "auto" | "blocked" | "unknown";
|
|
8
8
|
|
|
@@ -0,0 +1,397 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Read-path tests for {@link searchConversations}.
|
|
3
|
+
*
|
|
4
|
+
* Message-content candidates come exclusively from the Qdrant lexical index
|
|
5
|
+
* (mocked here), filtered by the visibility/archived SQL and merged with the
|
|
6
|
+
* title `LIKE` arm. Content matching is index-only (no `messages.content`
|
|
7
|
+
* scan fallback): a Qdrant lookup failure and a non-tokenizable query both
|
|
8
|
+
* leave title matches as the only arm, and while content search is
|
|
9
|
+
* unavailable (memory disabled / backfill not yet drained) the lexical index
|
|
10
|
+
* is never consulted and only titles can match.
|
|
11
|
+
*
|
|
12
|
+
* The lexical index is mocked at the `conversation-search-lexical` seam so no
|
|
13
|
+
* real Qdrant is required; a real SQLite DB backs the visibility/archived SQL.
|
|
14
|
+
*/
|
|
15
|
+
import { afterAll, beforeEach, describe, expect, mock, test } from "bun:test";
|
|
16
|
+
|
|
17
|
+
import type { MessageLexicalSearchResult } from "../embeddings/messages-lexical-index.js";
|
|
18
|
+
|
|
19
|
+
mock.module("../../util/logger.js", () => ({
|
|
20
|
+
getLogger: () =>
|
|
21
|
+
new Proxy({} as Record<string, unknown>, {
|
|
22
|
+
get: () => () => {},
|
|
23
|
+
}),
|
|
24
|
+
}));
|
|
25
|
+
|
|
26
|
+
// The lexical candidate source is the single seam this cutover swaps in. Mock
|
|
27
|
+
// it directly (returning caller-controlled candidate ids/scores) and, per test,
|
|
28
|
+
// override it to throw to exercise the Qdrant-error degrade path. Spreading the
|
|
29
|
+
// real module keeps its other exports intact for any test sharing the process.
|
|
30
|
+
const searchMessageIdsLexicalMock = mock(
|
|
31
|
+
async (
|
|
32
|
+
_query: string,
|
|
33
|
+
_limit: number,
|
|
34
|
+
_opts?: { conversationId?: string },
|
|
35
|
+
): Promise<MessageLexicalSearchResult[]> => [],
|
|
36
|
+
);
|
|
37
|
+
const actualLexical = await import("../conversation-search-lexical.js");
|
|
38
|
+
mock.module("../conversation-search-lexical.js", () => ({
|
|
39
|
+
...actualLexical,
|
|
40
|
+
searchMessageIdsLexical: searchMessageIdsLexicalMock,
|
|
41
|
+
}));
|
|
42
|
+
|
|
43
|
+
// Message-content search is host infrastructure, independent of the memory
|
|
44
|
+
// feature. Control `isMemoryEnabled` so one test can flip it `false` and
|
|
45
|
+
// prove reads ignore it. Spread the real module to preserve its other exports
|
|
46
|
+
// (many modules import from `jobs-store`).
|
|
47
|
+
let memoryEnabled = true;
|
|
48
|
+
const actualJobsStore = await import("../jobs-store.js");
|
|
49
|
+
mock.module("../jobs-store.js", () => ({
|
|
50
|
+
...actualJobsStore,
|
|
51
|
+
isMemoryEnabled: () => memoryEnabled,
|
|
52
|
+
}));
|
|
53
|
+
|
|
54
|
+
import {
|
|
55
|
+
deleteMemoryCheckpoint,
|
|
56
|
+
LEXICAL_BACKFILL_COMPLETE_KEY,
|
|
57
|
+
setMemoryCheckpoint,
|
|
58
|
+
} from "../checkpoints.js";
|
|
59
|
+
import { createConversation } from "../conversation-crud.js";
|
|
60
|
+
import { searchConversations } from "../conversation-queries.js";
|
|
61
|
+
import { getDb } from "../db-connection.js";
|
|
62
|
+
import { initializeDb } from "../db-init.js";
|
|
63
|
+
import { rawRun } from "../raw-query.js";
|
|
64
|
+
|
|
65
|
+
await initializeDb();
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Mark the messages lexical-index backfill complete. Content search is gated
|
|
69
|
+
* on this: an upgraded instance whose backfill has not finished never reads
|
|
70
|
+
* from a partially-populated `messages_lexical` (title matches only). The
|
|
71
|
+
* tests below index candidates as if the backfill has drained, so they set
|
|
72
|
+
* this marker; the gate itself is covered by its own test.
|
|
73
|
+
*/
|
|
74
|
+
function markBackfillComplete(): void {
|
|
75
|
+
setMemoryCheckpoint(LEXICAL_BACKFILL_COMPLETE_KEY, "1");
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function resetTables(): void {
|
|
79
|
+
const db = getDb();
|
|
80
|
+
db.run(`DELETE FROM messages`);
|
|
81
|
+
db.run(`DELETE FROM conversations`);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Insert a message row directly. `id` is caller-supplied so tests can wire the
|
|
86
|
+
* exact ids the mocked lexical index returns as candidates.
|
|
87
|
+
*/
|
|
88
|
+
function insertMessage(
|
|
89
|
+
id: string,
|
|
90
|
+
conversationId: string,
|
|
91
|
+
content: string,
|
|
92
|
+
createdAt = 1000,
|
|
93
|
+
): void {
|
|
94
|
+
rawRun(
|
|
95
|
+
"test:insertMessage",
|
|
96
|
+
"INSERT INTO messages (id, conversation_id, role, content, created_at) VALUES (?, ?, ?, ?, ?)",
|
|
97
|
+
id,
|
|
98
|
+
conversationId,
|
|
99
|
+
"user",
|
|
100
|
+
content,
|
|
101
|
+
createdAt,
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function setConversationType(conversationId: string, type: string): void {
|
|
106
|
+
rawRun(
|
|
107
|
+
"test:setConversationType",
|
|
108
|
+
"UPDATE conversations SET conversation_type = ? WHERE id = ?",
|
|
109
|
+
type,
|
|
110
|
+
conversationId,
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function archive(conversationId: string): void {
|
|
115
|
+
rawRun(
|
|
116
|
+
"test:archiveConversation",
|
|
117
|
+
"UPDATE conversations SET archived_at = ? WHERE id = ?",
|
|
118
|
+
Date.now(),
|
|
119
|
+
conversationId,
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/** Make the mocked lexical index return exactly these ids as candidates. */
|
|
124
|
+
function lexicalReturns(ids: string[]): void {
|
|
125
|
+
searchMessageIdsLexicalMock.mockImplementation(async () =>
|
|
126
|
+
ids.map((messageId, i) => ({ messageId, score: 1 - i * 0.01 })),
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
afterAll(() => {
|
|
131
|
+
mock.restore();
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
describe("searchConversations · qdrant lexical index", () => {
|
|
135
|
+
beforeEach(() => {
|
|
136
|
+
resetTables();
|
|
137
|
+
memoryEnabled = true;
|
|
138
|
+
// Content search is available once the index is populated: memory enabled
|
|
139
|
+
// + backfill complete. These tests exercise that post-backfill path.
|
|
140
|
+
markBackfillComplete();
|
|
141
|
+
searchMessageIdsLexicalMock.mockClear();
|
|
142
|
+
searchMessageIdsLexicalMock.mockImplementation(async () => []);
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
test("sources content candidates exclusively from the lexical index", async () => {
|
|
146
|
+
const conv = createConversation("Notes");
|
|
147
|
+
insertMessage("m-1", conv.id, "the flux capacitor needs recalibration");
|
|
148
|
+
// A message matching the query text that the lexical index does NOT
|
|
149
|
+
// return: if the query still surfaces it, candidates are coming from
|
|
150
|
+
// somewhere other than the index.
|
|
151
|
+
insertMessage("m-2", conv.id, "flux capacitor decoy", 2000);
|
|
152
|
+
|
|
153
|
+
lexicalReturns(["m-1"]);
|
|
154
|
+
|
|
155
|
+
const results = await searchConversations("flux capacitor");
|
|
156
|
+
|
|
157
|
+
expect(searchMessageIdsLexicalMock).toHaveBeenCalledTimes(1);
|
|
158
|
+
// The query text is passed through; the limit is the wide candidate
|
|
159
|
+
// over-fetch (asserted precisely in its own test below).
|
|
160
|
+
expect(searchMessageIdsLexicalMock.mock.calls[0]![0]).toBe(
|
|
161
|
+
"flux capacitor",
|
|
162
|
+
);
|
|
163
|
+
|
|
164
|
+
expect(results.map((r) => r.conversationId)).toEqual([conv.id]);
|
|
165
|
+
// Only the candidate the lexical index returned is surfaced as a match.
|
|
166
|
+
expect(results[0]!.matchingMessages.map((m) => m.messageId)).toEqual([
|
|
167
|
+
"m-1",
|
|
168
|
+
]);
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
test("does not issue a second lexical round-trip for per-conversation messages", async () => {
|
|
172
|
+
const convA = createConversation("A");
|
|
173
|
+
const convB = createConversation("B");
|
|
174
|
+
insertMessage("a-1", convA.id, "shared token alpha", 1000);
|
|
175
|
+
insertMessage("b-1", convB.id, "shared token beta", 1000);
|
|
176
|
+
|
|
177
|
+
lexicalReturns(["a-1", "b-1"]);
|
|
178
|
+
|
|
179
|
+
const results = await searchConversations("shared token");
|
|
180
|
+
|
|
181
|
+
// Exactly one lexical call total — the per-conversation message rows are
|
|
182
|
+
// selected from the already-fetched candidate set, not a fresh query.
|
|
183
|
+
expect(searchMessageIdsLexicalMock).toHaveBeenCalledTimes(1);
|
|
184
|
+
expect(results.map((r) => r.conversationId).sort()).toEqual(
|
|
185
|
+
[convA.id, convB.id].sort(),
|
|
186
|
+
);
|
|
187
|
+
for (const r of results) {
|
|
188
|
+
expect(r.matchingMessages).toHaveLength(1);
|
|
189
|
+
}
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
test("over-fetches a wide message-candidate pool (cap on distinct conversations, not messages)", async () => {
|
|
193
|
+
const conv = createConversation("Notes");
|
|
194
|
+
insertMessage("m-1", conv.id, "flux capacitor");
|
|
195
|
+
lexicalReturns(["m-1"]);
|
|
196
|
+
|
|
197
|
+
await searchConversations("flux capacitor");
|
|
198
|
+
|
|
199
|
+
// The candidate limit must be far larger than the caller's result `limit`
|
|
200
|
+
// (default 20) so the effective cap lands on distinct visible
|
|
201
|
+
// conversations after dedup, not raw messages. A single chatty
|
|
202
|
+
// conversation must not be able to consume the whole candidate budget.
|
|
203
|
+
const requestedLimit = searchMessageIdsLexicalMock.mock.calls[0]![1];
|
|
204
|
+
expect(requestedLimit).toBeGreaterThanOrEqual(5000);
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
test("a chatty conversation does not starve other distinct visible conversations", async () => {
|
|
208
|
+
// `chatty` has many matching messages; `other` has a single one. If the
|
|
209
|
+
// cap were on messages (and chatty's messages ranked first), `other` could
|
|
210
|
+
// be crowded out. The distinct-conversation cap must surface both.
|
|
211
|
+
const chatty = createConversation("Chatty");
|
|
212
|
+
const other = createConversation("Other");
|
|
213
|
+
|
|
214
|
+
const chattyIds: string[] = [];
|
|
215
|
+
for (let i = 0; i < 50; i++) {
|
|
216
|
+
const id = `chatty-${i}`;
|
|
217
|
+
insertMessage(id, chatty.id, `flux capacitor mention ${i}`, 1000 + i);
|
|
218
|
+
chattyIds.push(id);
|
|
219
|
+
}
|
|
220
|
+
insertMessage("other-1", other.id, "flux capacitor once", 999);
|
|
221
|
+
|
|
222
|
+
// Rank ALL of chatty's messages ahead of other's single message — the
|
|
223
|
+
// worst case for a message-level cap.
|
|
224
|
+
lexicalReturns([...chattyIds, "other-1"]);
|
|
225
|
+
|
|
226
|
+
const results = await searchConversations("flux capacitor");
|
|
227
|
+
|
|
228
|
+
expect(searchMessageIdsLexicalMock).toHaveBeenCalledTimes(1);
|
|
229
|
+
// Both distinct visible conversations surface despite chatty dominating
|
|
230
|
+
// the candidate ranking.
|
|
231
|
+
expect(results.map((r) => r.conversationId).sort()).toEqual(
|
|
232
|
+
[chatty.id, other.id].sort(),
|
|
233
|
+
);
|
|
234
|
+
// Per-conversation message rows are still capped (default 3) and sourced
|
|
235
|
+
// from the single round-trip.
|
|
236
|
+
const chattyResult = results.find((r) => r.conversationId === chatty.id)!;
|
|
237
|
+
expect(chattyResult.matchingMessages.length).toBeLessThanOrEqual(3);
|
|
238
|
+
const otherResult = results.find((r) => r.conversationId === other.id)!;
|
|
239
|
+
expect(otherResult.matchingMessages.map((m) => m.messageId)).toEqual([
|
|
240
|
+
"other-1",
|
|
241
|
+
]);
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
test("applies the same visibility filtering (excludes non-surfaced background)", async () => {
|
|
245
|
+
const background = createConversation({
|
|
246
|
+
title: "bg-run",
|
|
247
|
+
conversationType: "background",
|
|
248
|
+
});
|
|
249
|
+
insertMessage("bg-1", background.id, "flux capacitor in the background");
|
|
250
|
+
|
|
251
|
+
lexicalReturns(["bg-1"]);
|
|
252
|
+
|
|
253
|
+
// Candidate exists in the index, but the conversation is a non-surfaced
|
|
254
|
+
// background row — the visibility SQL must filter it out.
|
|
255
|
+
expect(await searchConversations("flux capacitor")).toEqual([]);
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
test("applies the same archived filtering (excludes archived conversations)", async () => {
|
|
259
|
+
const conv = createConversation("Archived notes");
|
|
260
|
+
insertMessage("arch-1", conv.id, "flux capacitor archived");
|
|
261
|
+
archive(conv.id);
|
|
262
|
+
|
|
263
|
+
lexicalReturns(["arch-1"]);
|
|
264
|
+
|
|
265
|
+
expect(await searchConversations("flux capacitor")).toEqual([]);
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
test("excludes private conversations even when the index returns their messages", async () => {
|
|
269
|
+
const priv = createConversation("Private notes");
|
|
270
|
+
setConversationType(priv.id, "private");
|
|
271
|
+
insertMessage("p-1", priv.id, "flux capacitor secret");
|
|
272
|
+
|
|
273
|
+
lexicalReturns(["p-1"]);
|
|
274
|
+
|
|
275
|
+
expect(await searchConversations("flux capacitor")).toEqual([]);
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
test("title-only matches still work without any lexical candidates", async () => {
|
|
279
|
+
const conv = createConversation("Quarterly metrics rollup");
|
|
280
|
+
|
|
281
|
+
// Index returns nothing for the content query; the title LIKE arm still
|
|
282
|
+
// finds the conversation.
|
|
283
|
+
lexicalReturns([]);
|
|
284
|
+
|
|
285
|
+
const results = await searchConversations("Quarterly metrics");
|
|
286
|
+
|
|
287
|
+
expect(results.map((r) => r.conversationId)).toEqual([conv.id]);
|
|
288
|
+
// No content candidates → no matching messages, just the title hit.
|
|
289
|
+
expect(results[0]!.matchingMessages).toEqual([]);
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
test("returns title matches only when the lexical lookup throws", async () => {
|
|
293
|
+
// Content matching is index-only: a lexical failure is logged and the
|
|
294
|
+
// content arm contributes nothing — no messages.content scan recovers it.
|
|
295
|
+
const contentOnly = createConversation("Degrade notes");
|
|
296
|
+
insertMessage("c-1", contentOnly.id, "flux capacitor mentioned in content");
|
|
297
|
+
const titleMatch = createConversation("Flux capacitor planning");
|
|
298
|
+
|
|
299
|
+
searchMessageIdsLexicalMock.mockImplementation(async () => {
|
|
300
|
+
throw new Error("qdrant unreachable");
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
const results = await searchConversations("flux capacitor");
|
|
304
|
+
|
|
305
|
+
expect(searchMessageIdsLexicalMock).toHaveBeenCalledTimes(1);
|
|
306
|
+
// The content-only conversation is dropped; the title-matched conversation
|
|
307
|
+
// still surfaces, without content excerpts.
|
|
308
|
+
expect(results.map((r) => r.conversationId)).toEqual([titleMatch.id]);
|
|
309
|
+
expect(results[0]!.matchingMessages).toEqual([]);
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
test("serves lexical content matches even when memory is disabled", async () => {
|
|
313
|
+
// Message-content search is host infrastructure: indexing runs and reads
|
|
314
|
+
// serve regardless of the memory feature's state, so disabling memory
|
|
315
|
+
// must not degrade search to title-only.
|
|
316
|
+
memoryEnabled = false;
|
|
317
|
+
const conv = createConversation("Notes");
|
|
318
|
+
insertMessage("m-1", conv.id, "flux capacitor in content only");
|
|
319
|
+
lexicalReturns(["m-1"]);
|
|
320
|
+
|
|
321
|
+
const results = await searchConversations("flux capacitor");
|
|
322
|
+
|
|
323
|
+
expect(searchMessageIdsLexicalMock).toHaveBeenCalledTimes(1);
|
|
324
|
+
expect(results.map((r) => r.conversationId)).toEqual([conv.id]);
|
|
325
|
+
expect(results[0]!.matchingMessages.map((m) => m.messageId)).toEqual([
|
|
326
|
+
"m-1",
|
|
327
|
+
]);
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
test("returns title matches only until the backfill completion checkpoint is set", async () => {
|
|
331
|
+
// On an upgraded instance the historical messages are indexed by a
|
|
332
|
+
// background backfill. Until it drains, the lexical collection is only
|
|
333
|
+
// partially populated and a read would silently miss older content. With
|
|
334
|
+
// the completion checkpoint UNSET, the index must not be consulted at
|
|
335
|
+
// all; only titles can match.
|
|
336
|
+
deleteMemoryCheckpoint(LEXICAL_BACKFILL_COMPLETE_KEY);
|
|
337
|
+
const contentOnly = createConversation("Notes");
|
|
338
|
+
insertMessage("m-1", contentOnly.id, "flux capacitor in content only");
|
|
339
|
+
const titleMatch = createConversation("Flux capacitor planning");
|
|
340
|
+
lexicalReturns(["m-1"]);
|
|
341
|
+
|
|
342
|
+
const results = await searchConversations("flux capacitor");
|
|
343
|
+
|
|
344
|
+
expect(searchMessageIdsLexicalMock).not.toHaveBeenCalled();
|
|
345
|
+
expect(results.map((r) => r.conversationId)).toEqual([titleMatch.id]);
|
|
346
|
+
expect(results[0]!.matchingMessages).toEqual([]);
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
test("includes a surfaced background conversation via lexical candidates", async () => {
|
|
350
|
+
// Surfacing flips a background conversation into the visible listing set,
|
|
351
|
+
// so its content matches must surface like any standard conversation's.
|
|
352
|
+
const surfaced = createConversation({
|
|
353
|
+
title: "bg-run",
|
|
354
|
+
conversationType: "background",
|
|
355
|
+
});
|
|
356
|
+
rawRun(
|
|
357
|
+
"test:setSurfaced",
|
|
358
|
+
"UPDATE conversations SET surfaced_at = ? WHERE id = ?",
|
|
359
|
+
Date.now(),
|
|
360
|
+
surfaced.id,
|
|
361
|
+
);
|
|
362
|
+
insertMessage("sb-1", surfaced.id, "flux capacitor surfaced background");
|
|
363
|
+
|
|
364
|
+
lexicalReturns(["sb-1"]);
|
|
365
|
+
|
|
366
|
+
const results = await searchConversations("flux capacitor");
|
|
367
|
+
|
|
368
|
+
expect(results.map((r) => r.conversationId)).toEqual([surfaced.id]);
|
|
369
|
+
expect(results[0]!.matchingMessages.map((m) => m.messageId)).toEqual([
|
|
370
|
+
"sb-1",
|
|
371
|
+
]);
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
test("non-tokenizable queries return title matches only, without hitting the index", async () => {
|
|
375
|
+
// Single-char / non-ASCII queries produce no tokens, so neither FTS nor the
|
|
376
|
+
// sparse encoder yields terms. Content matching is index-only, so only the
|
|
377
|
+
// title LIKE arm can match such queries.
|
|
378
|
+
const contentOnly = createConversation("Symbols");
|
|
379
|
+
insertMessage("s-1", contentOnly.id, "review the C§ draft");
|
|
380
|
+
const titleMatch = createConversation("C§ symbol reference");
|
|
381
|
+
|
|
382
|
+
const results = await searchConversations("C§");
|
|
383
|
+
|
|
384
|
+
expect(searchMessageIdsLexicalMock).not.toHaveBeenCalled();
|
|
385
|
+
expect(results.map((r) => r.conversationId)).toEqual([titleMatch.id]);
|
|
386
|
+
expect(results[0]!.matchingMessages).toEqual([]);
|
|
387
|
+
});
|
|
388
|
+
|
|
389
|
+
test("returns [] when the index yields no candidates and nothing matches by title", async () => {
|
|
390
|
+
const conv = createConversation("Unrelated");
|
|
391
|
+
insertMessage("u-1", conv.id, "totally different content");
|
|
392
|
+
|
|
393
|
+
lexicalReturns([]);
|
|
394
|
+
|
|
395
|
+
expect(await searchConversations("flux capacitor")).toEqual([]);
|
|
396
|
+
});
|
|
397
|
+
});
|