@vellumai/assistant 0.11.3 → 0.11.4-dev.202608190019.b94dbf2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +9 -3
- package/ARCHITECTURE.md +32 -10
- package/docs/architecture/memory.md +43 -5
- package/docs/architecture/security.md +96 -152
- package/docs/architecture/turn-actor.md +70 -0
- package/docs/browser-use-architecture-phase2.md +128 -56
- package/docs/flux-turn-detection-spike.md +248 -0
- package/docs/guardian-request-flow.md +35 -0
- package/docs/stt-provider-onboarding.md +3 -1
- package/knip.json +3 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/package.json +1 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/__tests__/url-normalization.test.ts +135 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/channels.ts +11 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +60 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/url-normalization.ts +107 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/package.json +1 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/__tests__/url-normalization.test.ts +135 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/channels.ts +11 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +60 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/url-normalization.ts +107 -0
- package/node_modules/@vellumai/gateway-client/src/admission-policy-contract.ts +34 -0
- package/node_modules/@vellumai/gateway-client/src/gateway-ipc-contracts.ts +166 -1
- package/node_modules/@vellumai/gateway-client/src/index.ts +2 -0
- package/node_modules/@vellumai/service-contracts/package.json +1 -0
- package/node_modules/@vellumai/service-contracts/src/__tests__/url-normalization.test.ts +135 -0
- package/node_modules/@vellumai/service-contracts/src/channels.ts +11 -0
- package/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
- package/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +60 -0
- package/node_modules/@vellumai/service-contracts/src/url-normalization.ts +107 -0
- package/openapi.yaml +289 -113
- package/package.json +1 -1
- package/scripts/voice-ttft-spike.ts +3 -3
- package/scripts/write-plugin-api-shim.ts +10 -0
- package/src/__tests__/app-compiler.test.ts +38 -3
- package/src/__tests__/app-control-flow.test.ts +1 -0
- package/src/__tests__/approval-routes-http.test.ts +2 -2
- package/src/__tests__/assistant-feature-flag-guard.test.ts +25 -3
- package/src/__tests__/attachments-store.test.ts +21 -12
- package/src/__tests__/byok-default-profile-ensure.test.ts +17 -0
- package/src/__tests__/call-setup-flow-name-capture.test.ts +0 -1
- package/src/__tests__/call-site-routing-provider.test.ts +1 -1
- package/src/__tests__/channel-availability-routes.test.ts +14 -1
- package/src/__tests__/channel-capabilities-dedupe.test.ts +214 -0
- package/src/__tests__/channel-delivery-store.test.ts +14 -14
- package/src/__tests__/channel-inbound-disk-pressure.test.ts +4 -4
- package/src/__tests__/channel-setup-panel-ack.test.ts +1 -1
- package/src/__tests__/checker.test.ts +20 -314
- package/src/__tests__/cli-memory-v2-reembed-skills.test.ts +6 -2
- package/src/__tests__/compaction-events.test.ts +8 -10
- package/src/__tests__/config-loader-backfill.test.ts +3 -3
- package/src/__tests__/config-schema.test.ts +25 -10
- package/src/__tests__/context-search-memory-v2-source.test.ts +0 -1
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +8 -11
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +8 -11
- package/src/__tests__/conversation-agent-loop.test.ts +32 -21
- package/src/__tests__/conversation-attachments.test.ts +0 -1
- package/src/__tests__/conversation-attention-store.test.ts +63 -0
- package/src/__tests__/conversation-confirmation-signals.test.ts +112 -0
- package/src/__tests__/conversation-delete-schedule-cleanup.test.ts +0 -4
- package/src/__tests__/conversation-fork-crud.test.ts +69 -0
- package/src/__tests__/conversation-fork-referential.test.ts +67 -0
- package/src/__tests__/conversation-fork-retrospective.test.ts +24 -0
- package/src/__tests__/conversation-load-history-repair.test.ts +209 -0
- package/src/__tests__/conversation-notifiers-provenance.test.ts +59 -0
- package/src/__tests__/conversation-queue.test.ts +154 -4
- package/src/__tests__/conversation-routes-disk-view.test.ts +1 -1
- package/src/__tests__/conversation-routes-enabled-plugins.test.ts +1 -1
- package/src/__tests__/conversation-routes-guardian-reply.test.ts +9 -9
- package/src/__tests__/conversation-routes-hidden-queue.test.ts +1 -1
- package/src/__tests__/conversation-routes-slash-commands.test.ts +1 -1
- package/src/__tests__/conversation-runtime-assembly.test.ts +187 -102
- package/src/__tests__/conversation-runtime-workspace.test.ts +14 -10
- package/src/__tests__/conversation-slash-queue.test.ts +3 -0
- package/src/__tests__/conversation-surfaces-action-delivery.test.ts +1 -0
- package/src/__tests__/conversation-surfaces-activation-emit.test.ts +1 -0
- package/src/__tests__/conversation-surfaces-app-control.test.ts +1 -0
- package/src/__tests__/conversation-surfaces-app-open.test.ts +1 -1
- package/src/__tests__/conversation-surfaces-data-persist.test.ts +1 -1
- package/src/__tests__/conversation-surfaces-history-restored-completion.test.ts +21 -14
- package/src/__tests__/conversation-surfaces-queued-emit.test.ts +1 -0
- package/src/__tests__/conversation-surfaces-standalone-payloads.test.ts +1 -0
- package/src/__tests__/conversation-surfaces-standalone.test.ts +1 -0
- package/src/__tests__/conversation-surfaces-state-update.test.ts +1 -1
- package/src/__tests__/conversation-surfaces-table-action.test.ts +1 -1
- package/src/__tests__/conversation-surfaces-task-progress.test.ts +1 -1
- package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +1 -1
- package/src/__tests__/conversation-tool-setup-attribution.test.ts +1 -1
- package/src/__tests__/credential-prompt-route.test.ts +7 -10
- package/src/__tests__/credential-security-invariants.test.ts +1 -0
- package/src/__tests__/cu-unified-flow.test.ts +1 -0
- package/src/__tests__/custom-profile-ensure.test.ts +5 -1
- package/src/__tests__/default-plugin-names-guard.test.ts +12 -1
- package/src/__tests__/discord-access-request-privacy.test.ts +5 -1
- package/src/__tests__/discord-requester-notice-privacy.test.ts +3 -3
- package/src/__tests__/document-append-idempotency.test.ts +233 -0
- package/src/__tests__/document-sync-tags.test.ts +0 -75
- package/src/__tests__/dynamic-skill-background-guard.test.ts +0 -2
- package/src/__tests__/edit-propagation.test.ts +107 -11
- package/src/__tests__/file-ops-service.test.ts +163 -30
- package/src/__tests__/filesystem-tools.test.ts +23 -24
- package/src/__tests__/gateway-only-guard.test.ts +2 -5
- package/src/__tests__/helpers/gateway-classify-mock.ts +27 -6
- package/src/__tests__/helpers/mock-actor-context.ts +49 -0
- package/src/__tests__/helpers/mock-conversation.ts +13 -1
- package/src/__tests__/host-file-read-tool.test.ts +16 -19
- package/src/__tests__/http-user-message-parity.test.ts +1 -1
- package/src/__tests__/init-feature-flag-overrides.test.ts +49 -0
- package/src/__tests__/injector-chain.test.ts +63 -41
- package/src/__tests__/injector-disk-pressure.test.ts +11 -23
- package/src/__tests__/inline-skill-load-permissions.test.ts +47 -36
- package/src/__tests__/llm-context-resolution.test.ts +73 -1
- package/src/__tests__/llm-schema.test.ts +5 -2
- package/src/__tests__/managed-skill-lifecycle.test.ts +7 -0
- package/src/__tests__/mcp-list-plugin-servers.test.ts +250 -0
- package/src/__tests__/media-generate-image.test.ts +131 -21
- package/src/__tests__/memory-retrieval-hook.test.ts +100 -7
- package/src/__tests__/messages-read-boundary-guard.test.ts +134 -0
- package/src/__tests__/mtime-cache.test.ts +3 -1
- package/src/__tests__/non-member-access-request.test.ts +0 -20
- package/src/__tests__/outbound-slack-persistence.test.ts +40 -1
- package/src/__tests__/platform-bash-auto-approve.test.ts +0 -4
- package/src/__tests__/plugin-api-store-credential.test.ts +261 -0
- package/src/__tests__/plugin-api-webhook-url.test.ts +10 -7
- package/src/__tests__/plugin-disabled-state.test.ts +2 -0
- package/src/__tests__/plugin-effective-enabled-set.test.ts +55 -3
- package/src/__tests__/plugin-execution-context.test.ts +73 -0
- package/src/__tests__/plugin-import-boundary-guard.test.ts +5 -1
- package/src/__tests__/plugin-import-boundary-reverse-guard.test.ts +7 -4
- package/src/__tests__/plugin-secret-pattern-contribution.test.ts +1 -1
- package/src/__tests__/post-compaction-reinjection-idempotency.test.ts +14 -7
- package/src/__tests__/provider-commit-message-generator.test.ts +20 -0
- package/src/__tests__/proxy-approval-callback.test.ts +1 -0
- package/src/__tests__/qdrant-manager.test.ts +14 -1
- package/src/__tests__/reaction-persistence.test.ts +200 -7
- package/src/__tests__/require-fresh-approval.test.ts +0 -4
- package/src/__tests__/risk-classification-boundary-guard.test.ts +115 -0
- package/src/__tests__/run-conversation-turn-persistence.test.ts +460 -105
- package/src/__tests__/run-due-schedules.test.ts +21 -0
- package/src/__tests__/scaffold-managed-skill-tool.test.ts +187 -18
- package/src/__tests__/schedule-routes.test.ts +23 -0
- package/src/__tests__/schedule-store.test.ts +17 -0
- package/src/__tests__/scoped-approval-grants.test.ts +11 -6
- package/src/__tests__/secret-ingress-channel.test.ts +0 -1
- package/src/__tests__/secret-ingress-http.test.ts +1 -1
- package/src/__tests__/send-endpoint-busy.test.ts +3 -3
- package/src/__tests__/skills.test.ts +32 -0
- package/src/__tests__/slack-edit-ordering-characterization.test.ts +0 -1
- package/src/__tests__/starter-task-flow.test.ts +5 -4
- package/src/__tests__/subagent-call-site-routing.test.ts +31 -19
- package/src/__tests__/subagent-fork-prompt-role.test.ts +1 -1
- package/src/__tests__/subagent-spawn-and-await.test.ts +18 -17
- package/src/__tests__/subagent-tool-gate-mode.test.ts +1 -1
- package/src/__tests__/subagent-tools.test.ts +81 -101
- package/src/__tests__/surface-completion-in-flight-snapshot.test.ts +1 -0
- package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +1 -1
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +132 -5
- package/src/__tests__/tool-executor.test.ts +68 -39
- package/src/__tests__/turn-events-store.test.ts +43 -0
- package/src/__tests__/ui-choice-copy-surfaces.test.ts +1 -1
- package/src/__tests__/ui-shape-teaching.test.ts +33 -0
- package/src/__tests__/ui-visual-surface.test.ts +1 -1
- package/src/__tests__/ui-voice-picker-surface.test.ts +128 -0
- package/src/__tests__/ui-work-result-surface.test.ts +1 -1
- package/src/__tests__/user-plugin-loader.test.ts +3 -1
- package/src/__tests__/verification-control-plane-policy.test.ts +0 -2
- package/src/__tests__/visible-app-context.test.ts +16 -9
- package/src/__tests__/voice-config-update.test.ts +40 -0
- package/src/__tests__/voice-scoped-grant-consumer.test.ts +5 -3
- package/src/__tests__/voice-session-bridge.test.ts +85 -29
- package/src/__tests__/worker-entrypoint-guards.test.ts +54 -0
- package/src/__tests__/worker-plugin-surface.test.ts +77 -0
- package/src/__tests__/workspace-migration-142-consolidate-voice-front-door.test.ts +158 -0
- package/src/__tests__/workspace-migration-143-repair-deprecated-codex-model-id.test.ts +133 -0
- package/src/__tests__/workspace-migration-144-convert-stranded-subscription-openai-profiles.test.ts +316 -0
- package/src/__tests__/workspace-migration-145-collapse-profile-bindings-to-entries.test.ts +325 -0
- package/src/__tests__/workspace-migration-146-repair-retired-fireworks-deepseek-flash-model-id.test.ts +235 -0
- package/src/acp/__tests__/acp-claude-oauth.test.ts +10 -2
- package/src/acp/__tests__/auth-required.test.ts +161 -0
- package/src/acp/acp-claude-oauth.ts +19 -2
- package/src/acp/agent-process.test.ts +100 -0
- package/src/acp/agent-process.ts +29 -26
- package/src/acp/auth-required.ts +102 -0
- package/src/acp/session-manager.test.ts +119 -0
- package/src/acp/session-manager.ts +76 -3
- package/src/api/events/acp-auth-required.ts +55 -0
- package/src/api/events/host-file.ts +2 -2
- package/src/api/index.ts +7 -0
- package/src/api/surfaces.ts +12 -3
- package/src/apps/app-store.ts +3 -0
- package/src/bundler/package-resolver.ts +2 -30
- package/src/calls/__tests__/voice-session-bridge.test.ts +194 -11
- package/src/calls/__tests__/voice-triage-escalate.test.ts +102 -0
- package/src/calls/call-controller.ts +19 -3
- package/src/calls/call-setup-flow.ts +0 -1
- package/src/calls/media-stream-stt-session.ts +15 -0
- package/src/calls/voice-session-bridge.ts +115 -41
- package/src/calls/voice-triage-escalate.ts +105 -2
- package/src/channels/__tests__/plugin-channel-declarations.test.ts +161 -0
- package/src/channels/config.ts +13 -0
- package/src/channels/plugin-channel-declarations.ts +108 -0
- package/src/channels/types.ts +30 -0
- package/src/cli/AGENTS.md +5 -2
- package/src/cli/bundled-modules.ts +29 -0
- package/src/cli/commands/credentials.help.ts +2 -2
- package/src/cli/commands/db/repair.ts +4 -8
- package/src/cli/commands/domain.ts +6 -3
- package/src/cli/commands/email.ts +6 -3
- package/src/cli/commands/inference-providers.ts +1 -1
- package/src/cli/commands/keys.ts +8 -3
- package/src/cli/commands/mcp.help.ts +13 -4
- package/src/cli/commands/mcp.ts +9 -0
- package/src/cli/commands/memory/__tests__/memory-v2.test.ts +57 -7
- package/src/cli/commands/memory/__tests__/memory-v3.test.ts +128 -5
- package/src/cli/commands/memory/index.help.ts +84 -28
- package/src/cli/commands/memory/index.ts +2 -0
- package/src/cli/commands/memory/memory-v2.ts +58 -54
- package/src/cli/commands/memory/memory-v3.ts +64 -0
- package/src/cli/commands/memory/memory-validate.ts +18 -0
- package/src/cli/commands/plugins.ts +85 -36
- package/src/cli/commands/schedules.ts +35 -1
- package/src/cli/commands/stt.help.ts +27 -2
- package/src/cli/commands/trust.ts +3 -14
- package/src/cli/lib/__tests__/upgrade-plugin.test.ts +39 -0
- package/src/cli/lib/bundled-marketplace.json +14 -1
- package/src/cli/lib/upgrade-plugin.ts +42 -0
- package/src/config/__tests__/balanced-model-experiment.test.ts +278 -0
- package/src/config/__tests__/default-profile-catalog.test.ts +34 -2
- package/src/config/__tests__/default-provider.test.ts +6 -1
- package/src/config/__tests__/profile-materialization.test.ts +75 -19
- package/src/config/assistant-feature-flags.ts +36 -15
- package/src/config/balanced-model-experiment.ts +35 -0
- package/src/config/bundled-skills/acp/SKILL.md +6 -7
- package/src/config/bundled-skills/document-editor/SKILL.md +2 -2
- package/src/config/bundled-skills/document-editor/TOOLS.json +2 -2
- package/src/config/bundled-skills/image-studio/SKILL.md +5 -4
- package/src/config/bundled-skills/image-studio/TOOLS.json +1 -1
- package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +101 -0
- package/src/config/bundled-skills/media-processing/services/preprocess.ts +14 -4
- package/src/config/bundled-skills/settings/TOOLS.json +3 -3
- package/src/config/bundled-skills/settings/tools/navigate-settings-tab.test.ts +65 -0
- package/src/config/bundled-skills/settings/tools/navigate-settings-tab.ts +7 -1
- package/src/config/bundled-skills/settings/tools/shared.ts +16 -0
- package/src/config/bundled-skills/settings/tools/voice-config-update.ts +19 -1
- package/src/config/bundled-skills/skill-management/TOOLS.json +9 -3
- package/src/config/bundled-skills/subagent/SKILL.md +17 -12
- package/src/config/bundled-skills/subagent/TOOLS.json +4 -4
- package/src/config/bundled-skills/transcribe/tools/transcribe-media.test.ts +22 -1
- package/src/config/bundled-skills/transcribe/tools/transcribe-media.ts +9 -2
- package/src/config/call-site-defaults.ts +11 -5
- package/src/config/default-profile-catalog.ts +179 -16
- package/src/config/default-profile-names.ts +4 -1
- package/src/config/default-provider-resolution.ts +4 -0
- package/src/config/feature-flag-registry.json +12 -12
- package/src/config/llm-context-resolution.ts +11 -3
- package/src/config/llm-resolver.ts +28 -1
- package/src/config/profile-materialization.ts +70 -22
- package/src/config/schemas/__tests__/live-voice.test.ts +107 -4
- package/src/config/schemas/call-site-catalog.ts +4 -4
- package/src/config/schemas/live-voice.ts +57 -23
- package/src/config/schemas/llm.ts +59 -32
- package/src/config/schemas/mcp.ts +23 -0
- package/src/config/schemas/plugin-updates.ts +6 -2
- package/src/config/schemas/stt.ts +1 -0
- package/src/config/skills.ts +9 -2
- package/src/context/outbound-sanitize.ts +96 -1
- package/src/daemon/__tests__/conversation-surfaces-launch.test.ts +1 -1
- package/src/daemon/__tests__/plugin-mcp-reconcile.test.ts +82 -0
- package/src/daemon/conversation-agent-loop-handlers.ts +15 -10
- package/src/daemon/conversation-agent-loop.ts +31 -19
- package/src/daemon/conversation-messaging.ts +5 -1
- package/src/daemon/conversation-notifiers.ts +20 -10
- package/src/daemon/conversation-process.ts +20 -6
- package/src/daemon/conversation-runtime-assembly.ts +12 -6
- package/src/daemon/conversation-store.ts +14 -4
- package/src/daemon/conversation-surfaces.ts +75 -26
- package/src/daemon/conversation-tool-setup.ts +3 -6
- package/src/daemon/conversation.ts +154 -51
- package/src/daemon/doordash-steps.ts +2 -2
- package/src/daemon/lifecycle.ts +14 -1
- package/src/daemon/mcp-reload-service.ts +36 -6
- package/src/daemon/process-message.ts +13 -26
- package/src/daemon/providers-setup.ts +6 -3
- package/src/daemon/trust-context-types.ts +29 -0
- package/src/daemon/trust-context.ts +12 -7
- package/src/daemon/wake-conversation-ops.ts +3 -2
- package/src/daemon/windows-compiled-entry.ts +4 -0
- package/src/documents/document-store.ts +143 -240
- package/src/hooks/hook-loader.ts +3 -3
- package/src/hooks/registry.ts +103 -45
- package/src/hooks/types.ts +5 -0
- package/src/inbound/__tests__/oauth-callback-url.test.ts +83 -0
- package/src/inbound/oauth-callback-url.ts +61 -0
- package/src/ipc/gateway-client.test.ts +1 -1
- package/src/ipc/gateway-client.ts +14 -21
- package/src/ipc/gateway-flag-listener.ts +17 -3
- package/src/live-voice/__tests__/live-voice-agent-turn.test.ts +1 -104
- package/src/live-voice/__tests__/live-voice-events.test.ts +7 -8
- package/src/live-voice/__tests__/live-voice-flux-turn-end.test.ts +1050 -0
- package/src/live-voice/__tests__/live-voice-metrics.test.ts +115 -8
- package/src/live-voice/__tests__/live-voice-photo.test.ts +100 -0
- package/src/live-voice/__tests__/live-voice-progress.test.ts +60 -194
- package/src/live-voice/__tests__/live-voice-stt.test.ts +14 -0
- package/src/live-voice/__tests__/live-voice-triage-escalate.test.ts +29 -0
- package/src/live-voice/__tests__/live-voice-tts-session.test.ts +0 -483
- package/src/live-voice/__tests__/live-voice-vad.test.ts +0 -16
- package/src/live-voice/__tests__/progress-narration.test.ts +214 -0
- package/src/live-voice/live-voice-archive.ts +2 -0
- package/src/live-voice/live-voice-manager.ts +16 -3
- package/src/live-voice/live-voice-metrics.ts +57 -32
- package/src/live-voice/live-voice-photo.ts +1 -2
- package/src/live-voice/live-voice-session.ts +598 -323
- package/src/live-voice/progress-narration.ts +277 -0
- package/src/live-voice/protocol.ts +21 -1
- package/src/live-voice/windows-compiled-live-voice.ts +4 -0
- package/src/mcp/__tests__/effective-config.test.ts +238 -0
- package/src/mcp/__tests__/mcp-auth-orchestrator.test.ts +0 -1
- package/src/mcp/__tests__/mcp-oauth-client-registration.test.ts +200 -0
- package/src/mcp/__tests__/mcp-oauth-provider.test.ts +9 -9
- package/src/mcp/__tests__/plugin-server-credential-isolation.test.ts +95 -0
- package/src/mcp/client.ts +16 -11
- package/src/mcp/effective-config.ts +113 -0
- package/src/mcp/manager.ts +11 -6
- package/src/mcp/mcp-auth-orchestrator.ts +13 -22
- package/src/mcp/mcp-oauth-provider.ts +205 -240
- package/src/monitoring/__tests__/plugin-auto-update.test.ts +166 -3
- package/src/monitoring/control.ts +1 -0
- package/src/monitoring/db-integrity-sample.ts +4 -5
- package/src/monitoring/plugin-auto-update.ts +128 -24
- package/src/notifications/AGENTS.md +2 -0
- package/src/notifications/approval-card-data.ts +33 -0
- package/src/notifications/signal.ts +1 -0
- package/src/permissions/AGENTS.md +16 -0
- package/src/permissions/checker.test.ts +75 -122
- package/src/permissions/checker.ts +44 -560
- package/src/permissions/confirmation-guardian-request.test.ts +15 -11
- package/src/permissions/confirmation-guardian-request.ts +2 -2
- package/src/permissions/prompter.ts +1 -5
- package/src/permissions/question-guardian-request.test.ts +14 -6
- package/src/permissions/question-guardian-request.ts +1 -2
- package/src/persistence/attachments-store.ts +8 -1
- package/src/persistence/bookmark-crud.ts +3 -7
- package/src/persistence/conversation-attention-store.ts +16 -45
- package/src/persistence/conversation-crud.ts +33 -4
- package/src/persistence/conversation-lineage.ts +9 -0
- package/src/persistence/conversation-queries.ts +182 -52
- package/src/persistence/delivery-crud.ts +150 -36
- package/src/persistence/embeddings/qdrant-manager.ts +84 -49
- package/src/persistence/external-conversation-store.ts +32 -4
- package/src/persistence/llm-request-log-store.ts +4 -10
- package/src/persistence/llm-usage-store.ts +8 -3
- package/src/persistence/message-reads.test.ts +197 -0
- package/src/persistence/message-reads.ts +211 -0
- package/src/persistence/migrations/360-add-document-workspace-path.ts +5 -14
- package/src/persistence/migrations/366-chatgpt-subscription-row-identity.test.ts +120 -0
- package/src/persistence/migrations/366-chatgpt-subscription-row-identity.ts +62 -0
- package/src/persistence/real-user-turn-filter.ts +27 -3
- package/src/persistence/schema/documents.ts +4 -4
- package/src/persistence/steps.ts +9 -0
- package/src/plugin-api/__tests__/oauth-callback-url-export.test.ts +29 -0
- package/src/plugin-api/constants.ts +8 -0
- package/src/plugin-api/conversation-turn.ts +195 -5
- package/src/plugin-api/index.ts +38 -6
- package/src/plugin-api/resolve-credential.ts +3 -2
- package/src/plugin-api/store-credential.ts +146 -0
- package/src/plugin-api/vision-support.test.ts +1 -1
- package/src/plugin-api/webhook-url.ts +13 -11
- package/src/plugins/__tests__/mcp-servers.test.ts +371 -0
- package/src/plugins/defaults/main.ts +21 -7
- package/src/plugins/defaults/memory/AGENTS.md +15 -3
- package/src/plugins/defaults/memory/__tests__/buffer-format.test.ts +204 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-accounting.test.ts +72 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +4 -1
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-provider-path.test.ts +4 -1
- package/src/plugins/defaults/memory/__tests__/memory-tier-boundary-guard.test.ts +0 -1
- package/src/plugins/defaults/memory/buffer-format.ts +165 -0
- package/src/plugins/defaults/memory/context-search/sources/conversations.ts +6 -0
- package/src/plugins/defaults/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +77 -0
- package/src/plugins/defaults/memory/graph/conversation-graph-memory.ts +24 -6
- package/src/plugins/defaults/memory/graph/image-ref-utils.ts +3 -0
- package/src/plugins/defaults/memory/graph/tool-handlers.ts +1 -30
- package/src/plugins/defaults/memory/graph-topology/pending-buffer.test.ts +34 -0
- package/src/plugins/defaults/memory/graph-topology/pending-buffer.ts +17 -24
- package/src/plugins/defaults/memory/hooks/post-compact.ts +1 -4
- package/src/plugins/defaults/memory/hooks/user-prompt-submit.ts +53 -5
- package/src/plugins/defaults/memory/indexer.ts +3 -1
- package/src/plugins/defaults/memory/memory-retrospective-accounting.ts +19 -7
- package/src/plugins/defaults/memory/memory-retrospective-job.ts +4 -4
- package/src/plugins/defaults/memory/src/__tests__/memory-v3-gate-stats.test.ts +281 -0
- package/src/plugins/defaults/memory/src/memory-v2-routes.ts +12 -10
- package/src/plugins/defaults/memory/src/memory-v3-routes.ts +207 -0
- package/src/plugins/defaults/memory/substrate/__tests__/consolidation-job.test.ts +147 -0
- package/src/plugins/defaults/memory/substrate/__tests__/consolidation-prompt-flag-gating-guard.test.ts +8 -0
- package/src/plugins/defaults/memory/substrate/__tests__/edge-index.test.ts +0 -30
- package/src/plugins/defaults/memory/substrate/__tests__/ingest.test.ts +73 -1
- package/src/plugins/defaults/memory/substrate/__tests__/page-index.test.ts +37 -0
- package/src/plugins/defaults/memory/substrate/__tests__/page-links.test.ts +208 -0
- package/src/plugins/defaults/memory/substrate/__tests__/prompts-consolidation.test.ts +158 -1
- package/src/plugins/defaults/memory/substrate/__tests__/static-context.test.ts +195 -58
- package/src/plugins/defaults/memory/substrate/consolidation-job.ts +93 -24
- package/src/plugins/defaults/memory/substrate/edge-index.ts +0 -31
- package/src/plugins/defaults/memory/substrate/ingest.ts +59 -0
- package/src/plugins/defaults/memory/substrate/page-index.ts +35 -8
- package/src/plugins/defaults/memory/substrate/page-links.ts +133 -0
- package/src/plugins/defaults/memory/substrate/page-store.ts +10 -0
- package/src/plugins/defaults/memory/substrate/prompts/consolidation.ts +124 -34
- package/src/plugins/defaults/memory/substrate/skill-content.ts +8 -1
- package/src/plugins/defaults/memory/substrate/static-context.ts +160 -33
- package/src/plugins/defaults/memory/substrate/sweep-job.ts +2 -4
- package/src/plugins/defaults/memory/v1/graph/extraction.ts +3 -1
- package/src/plugins/defaults/memory/v3/__tests__/injection.test.ts +18 -0
- package/src/plugins/defaults/memory/v3/__tests__/shadow-plugin.test.ts +66 -1
- package/src/plugins/defaults/memory/v3/card.ts +9 -9
- package/src/plugins/defaults/memory/v3/core-set.test.ts +7 -0
- package/src/plugins/defaults/memory/v3/core-set.ts +5 -1
- package/src/plugins/defaults/memory/v3/edge.ts +13 -57
- package/src/plugins/defaults/memory/v3/injector.ts +8 -0
- package/src/plugins/defaults/memory/v3/prune.ts +2 -0
- package/src/plugins/defaults/memory/v3/selection-log-store.ts +2 -0
- package/src/plugins/defaults/memory/v3/shadow-plugin.ts +23 -9
- package/src/plugins/defaults/memory/worker-control.ts +1 -0
- package/src/plugins/defaults/memory/worker.ts +6 -3
- package/src/plugins/defaults/worker-entrypoints.ts +3 -0
- package/src/plugins/external-plugin-loader.ts +47 -0
- package/src/plugins/mcp-servers.ts +361 -0
- package/src/plugins/mtime-cache.ts +37 -46
- package/src/plugins/pipeline.ts +14 -7
- package/src/plugins/plugin-execution-context.ts +35 -11
- package/src/plugins/worker-plugin-surface.ts +33 -0
- package/src/prompts/templates/system-sections.ts +0 -7
- package/src/providers/__tests__/connection-model-compat.test.ts +1 -1
- package/src/providers/__tests__/context-overflow-error.test.ts +24 -0
- package/src/providers/__tests__/dispatch-connection-routing.test.ts +214 -2
- package/src/providers/__tests__/preflight-resolved-config.test.ts +57 -0
- package/src/providers/__tests__/retry-callsite.test.ts +25 -2
- package/src/providers/anthropic/__tests__/pause-turn-continuation.test.ts +170 -0
- package/src/providers/anthropic/client.ts +332 -241
- package/src/providers/call-site-routing.ts +30 -3
- package/src/providers/connection-resolution.ts +194 -11
- package/src/providers/inference/__tests__/adapter-factory-openai-compatible.test.ts +4 -0
- package/src/providers/inference/adapter-factory.ts +3 -0
- package/src/providers/inference/auth.ts +6 -0
- package/src/providers/inference/connection-availability.ts +24 -2
- package/src/providers/inference/connections.ts +2 -0
- package/src/providers/model-catalog.ts +3 -3
- package/src/providers/model-intents.ts +28 -8
- package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +163 -40
- package/src/providers/openai/__tests__/tool-choice-mapping.test.ts +125 -2
- package/src/providers/openai/chat-completions-provider.ts +133 -36
- package/src/providers/openai/codex-models.ts +2 -1
- package/src/providers/provider-send-message.ts +32 -3
- package/src/providers/speech-to-text/__tests__/deepgram-flux-frames.test.ts +433 -0
- package/src/providers/speech-to-text/__tests__/deepgram-flux-realtime.test.ts +625 -0
- package/src/providers/speech-to-text/__tests__/provider-catalog.test.ts +34 -0
- package/src/providers/speech-to-text/__tests__/resolve.test.ts +285 -6
- package/src/providers/speech-to-text/deepgram-flux-frames.ts +395 -0
- package/src/providers/speech-to-text/deepgram-flux-realtime.ts +679 -0
- package/src/providers/speech-to-text/provider-catalog.ts +99 -8
- package/src/providers/speech-to-text/resolve.ts +25 -2
- package/src/routes/control.ts +1 -0
- package/src/routes/route-host-client.ts +1 -0
- package/src/routes/route-host-protocol.ts +7 -0
- package/src/routes/worker.ts +48 -15
- package/src/runtime/AGENTS.md +16 -17
- package/src/runtime/access-request-helper.ts +9 -12
- package/src/runtime/agent-wake.ts +18 -15
- package/src/runtime/effective-capabilities.test.ts +43 -0
- package/src/runtime/effective-capabilities.ts +27 -0
- package/src/runtime/guardian-reply-router.ts +6 -2
- package/src/runtime/pre-first-message-gate.ts +4 -0
- package/src/runtime/routes/__tests__/acp-claude-auth-routes.test.ts +12 -4
- package/src/runtime/routes/__tests__/conversation-list-routes.test.ts +522 -2
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +52 -0
- package/src/runtime/routes/__tests__/default-provider-routes.test.ts +61 -0
- package/src/runtime/routes/__tests__/inference-profiles-routes.test.ts +44 -0
- package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +102 -1
- package/src/runtime/routes/__tests__/plugins-routes.test.ts +132 -35
- package/src/runtime/routes/__tests__/schedule-routes-disarm-reason.test.ts +215 -0
- package/src/runtime/routes/__tests__/stt-routes.test.ts +25 -0
- package/src/runtime/routes/__tests__/surface-action-routes.test.ts +55 -6
- package/src/runtime/routes/__tests__/user-route-dispatcher-host.test.ts +52 -1
- package/src/runtime/routes/__tests__/user-route-dispatcher.test.ts +133 -1
- package/src/runtime/routes/channel-availability-routes.ts +32 -14
- package/src/runtime/routes/channel-route-shared.ts +0 -6
- package/src/runtime/routes/chatgpt-subscription-auth-routes.ts +6 -6
- package/src/runtime/routes/conversation-list-routes.ts +181 -23
- package/src/runtime/routes/conversation-management-routes.ts +17 -12
- package/src/runtime/routes/conversation-query-routes.ts +40 -27
- package/src/runtime/routes/conversation-routes.ts +43 -29
- package/src/runtime/routes/credential-prompt-routes.ts +4 -7
- package/src/runtime/routes/credential-routes.ts +26 -82
- package/src/runtime/routes/default-provider-routes.ts +15 -0
- package/src/runtime/routes/documents-routes.ts +3 -222
- package/src/runtime/routes/inbound-message-handler.ts +21 -45
- package/src/runtime/routes/inbound-stages/acl-enforcement.test.ts +0 -1
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +0 -9
- package/src/runtime/routes/inbound-stages/admission-policy.ts +1 -17
- package/src/runtime/routes/inbound-stages/bootstrap-intercept.test.ts +0 -1
- package/src/runtime/routes/inbound-stages/bootstrap-intercept.ts +2 -3
- package/src/runtime/routes/inbound-stages/edit-intercept.ts +101 -79
- package/src/runtime/routes/inbound-stages/guardian-reply-intercept.test.ts +0 -1
- package/src/runtime/routes/inbound-stages/guardian-reply-intercept.ts +12 -7
- package/src/runtime/routes/inbound-stages/reaction-intercept.test.ts +91 -8
- package/src/runtime/routes/inbound-stages/reaction-intercept.ts +72 -79
- package/src/runtime/routes/inbound-stages/secret-ingress-check.ts +2 -3
- package/src/runtime/routes/inference-profiles-routes.ts +20 -11
- package/src/runtime/routes/inference-provider-connection-routes.ts +77 -15
- package/src/runtime/routes/log-export-routes.ts +3 -0
- package/src/runtime/routes/mcp-auth-routes.ts +148 -57
- package/src/runtime/routes/playground/__tests__/inject-failures.test.ts +2 -0
- package/src/runtime/routes/playground/__tests__/reset-circuit.test.ts +3 -0
- package/src/runtime/routes/playground/inject-failures.ts +2 -2
- package/src/runtime/routes/playground/reset-circuit.ts +1 -1
- package/src/runtime/routes/plugins-routes.ts +38 -8
- package/src/runtime/routes/schedule-routes.ts +94 -7
- package/src/runtime/routes/settings-routes.ts +6 -6
- package/src/runtime/routes/stt-routes.ts +31 -25
- package/src/runtime/routes/surface-action-routes.ts +20 -19
- package/src/runtime/routes/surface-conversation-resolver.ts +3 -0
- package/src/runtime/routes/user-route-dispatcher.ts +69 -15
- package/src/runtime/routes/user-route-import.ts +108 -0
- package/src/runtime/routes/user-route-resolution.ts +18 -1
- package/src/runtime/routes/workspace-routes.ts +0 -9
- package/src/runtime/routes/workspace-utils.ts +3 -13
- package/src/runtime/services/conversation-serializer.ts +7 -2
- package/src/schedule/__tests__/plugin-schedule-declarations.test.ts +68 -5
- package/src/schedule/__tests__/plugin-schedule-reconciler.test.ts +81 -0
- package/src/schedule/plugin-schedule-availability.ts +58 -0
- package/src/schedule/plugin-schedule-declarations.ts +23 -27
- package/src/schedule/plugin-schedule-reconciler.ts +12 -3
- package/src/schedule/schedule-store.ts +5 -1
- package/src/schedule/scheduler.ts +9 -4
- package/src/schedule/worker-control.ts +1 -0
- package/src/schedule/worker.ts +6 -0
- package/src/security/oauth2.ts +6 -22
- package/src/stt/__tests__/daemon-batch-transcriber.test.ts +22 -0
- package/src/stt/__tests__/types.test.ts +94 -0
- package/src/stt/daemon-batch-transcriber.ts +10 -0
- package/src/stt/stt-stream-session.ts +8 -4
- package/src/stt/types.ts +103 -0
- package/src/subagent/__tests__/consult-prompt.test.ts +26 -15
- package/src/subagent/consult-context.ts +11 -11
- package/src/subagent/consult-prompt.ts +26 -35
- package/src/subagent/manager.ts +21 -40
- package/src/subagent/notify.ts +7 -1
- package/src/subagent/types.ts +15 -12
- package/src/tools/__tests__/tool-input-schemas.test.ts +7 -7
- package/src/tools/acp/spawn.test.ts +97 -0
- package/src/tools/acp/spawn.ts +38 -4
- package/src/tools/credentials/ref-parse.ts +35 -0
- package/src/tools/credentials/resolve.ts +4 -10
- package/src/tools/credentials/store.ts +168 -0
- package/src/tools/document/document-tool.ts +12 -3
- package/src/tools/executor.ts +53 -15
- package/src/tools/filesystem/read.ts +27 -10
- package/src/tools/host-filesystem/read.ts +27 -15
- package/src/tools/network/url-safety.ts +9 -16
- package/src/tools/permission-checker.ts +43 -52
- package/src/tools/registry.ts +2 -1
- package/src/tools/shared/filesystem/file-ops-service.ts +63 -35
- package/src/tools/shared/filesystem/legacy-read-args.ts +22 -0
- package/src/tools/shared/filesystem/types.ts +5 -5
- package/src/tools/skills/scaffold-managed.ts +25 -7
- package/src/tools/subagent/spawn.ts +28 -88
- package/src/tools/tool-types.ts +26 -13
- package/src/tools/types.ts +6 -6
- package/src/tools/ui-surface/surface-shape-docs.ts +12 -1
- package/src/tools/workflows/run-workflow.ts +1 -2
- package/src/tts/__tests__/reasoning-tag-filter.test.ts +78 -0
- package/src/tts/reasoning-tag-filter.ts +89 -0
- package/src/util/__tests__/worker-process-command.test.ts +37 -0
- package/src/util/logger.ts +16 -0
- package/src/util/think-tag-stream.ts +95 -0
- package/src/util/worker-process.ts +37 -4
- package/src/windows-compiled-cli.ts +32 -0
- package/src/windows-compiled-entry.ts +4 -0
- package/src/windows-compiled-logger.ts +6 -0
- package/src/windows-compiled-worker-entry.ts +29 -0
- package/src/workspace/byok-default-profile-ensure.ts +76 -24
- package/src/workspace/custom-profile-ensure.ts +4 -24
- package/src/workspace/migrations/142-consolidate-voice-front-door.ts +70 -0
- package/src/workspace/migrations/143-repair-deprecated-codex-model-id.ts +134 -0
- package/src/workspace/migrations/144-convert-stranded-subscription-openai-profiles.ts +265 -0
- package/src/workspace/migrations/145-collapse-profile-bindings-to-entries.ts +328 -0
- package/src/workspace/migrations/146-repair-retired-fireworks-deepseek-flash-model-id.ts +195 -0
- package/src/workspace/migrations/__tests__/141-stt-english-default-to-multilingual.test.ts +0 -10
- package/src/workspace/migrations/registry.ts +10 -0
- package/src/workspace/provider-commit-message-generator.ts +7 -5
- package/src/__tests__/document-workspace-file.test.ts +0 -467
- package/src/live-voice/__tests__/front-decision.test.ts +0 -645
- package/src/live-voice/front-decision.ts +0 -476
- package/src/permissions/ipc-risk-types.ts +0 -143
- package/src/permissions/risk-types.ts +0 -76
- package/src/subagent/__tests__/consult-transcript.test.ts +0 -184
- package/src/subagent/consult-transcript.ts +0 -90
|
@@ -1,4 +1,14 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
and,
|
|
3
|
+
count,
|
|
4
|
+
desc,
|
|
5
|
+
eq,
|
|
6
|
+
getTableColumns,
|
|
7
|
+
isNull,
|
|
8
|
+
lt,
|
|
9
|
+
sql,
|
|
10
|
+
} from "drizzle-orm";
|
|
11
|
+
import type { SQLiteSelect } from "drizzle-orm/sqlite-core";
|
|
2
12
|
|
|
3
13
|
import {
|
|
4
14
|
parseExternalContentEnvelope,
|
|
@@ -26,11 +36,14 @@ import {
|
|
|
26
36
|
parseContentRef,
|
|
27
37
|
resolveMessageContentBlocks,
|
|
28
38
|
} from "./message-content-file.js";
|
|
39
|
+
import {
|
|
40
|
+
countMessagesByRoleForConversations,
|
|
41
|
+
latestUserMessageRawContent,
|
|
42
|
+
} from "./message-reads.js";
|
|
29
43
|
import { rawAll } from "./raw-query.js";
|
|
30
44
|
import {
|
|
31
45
|
conversationAssistantAttentionState,
|
|
32
46
|
conversations,
|
|
33
|
-
messages,
|
|
34
47
|
} from "./schema/index.js";
|
|
35
48
|
|
|
36
49
|
const log = getLogger("conversation-store");
|
|
@@ -186,7 +199,8 @@ function surfacedVisibilitySql(alias = "conversations"): string {
|
|
|
186
199
|
* explicitly surfaced. {@link standardListingVisibilitySql} already admits
|
|
187
200
|
* such rows through its surfaced and custom-group arms, so a caller that
|
|
188
201
|
* must distinguish "visible in the listing" from "user-facing foreground"
|
|
189
|
-
* (unread counting
|
|
202
|
+
* (unread counting, the section index, a `foregroundOnly` list read) applies
|
|
203
|
+
* this on top.
|
|
190
204
|
*
|
|
191
205
|
* The SQL twin of `isBackgroundConversation` in the web client's
|
|
192
206
|
* `utils/conversation-predicates.ts`.
|
|
@@ -260,6 +274,25 @@ export interface ConversationListFilter {
|
|
|
260
274
|
* section, and a UUID one custom group. Omit to span every group.
|
|
261
275
|
*/
|
|
262
276
|
groupId?: string;
|
|
277
|
+
/**
|
|
278
|
+
* Restrict to conversations whose latest assistant message the user has
|
|
279
|
+
* not seen: the same "unseen" predicate the unread count and the section
|
|
280
|
+
* index apply ({@link unseenAttentionStateConditions}), so every reader
|
|
281
|
+
* of "needs attention" agrees. Only `true` narrows; omit to span every
|
|
282
|
+
* conversation. A client that keeps no complete list can ask for exactly
|
|
283
|
+
* the rows its attention surfaces need instead of scanning for them.
|
|
284
|
+
*/
|
|
285
|
+
needsAttention?: true;
|
|
286
|
+
/**
|
|
287
|
+
* Restrict to user-facing foreground conversations: drop the automated
|
|
288
|
+
* background/scheduled rows the standard listing admits through its
|
|
289
|
+
* custom-group arm ({@link notBackgroundVisibilitySql}, the same predicate
|
|
290
|
+
* the unread count and the section index apply on top of visibility).
|
|
291
|
+
* Only `true` narrows; omit to keep every visible row. A client that
|
|
292
|
+
* needs the newest conversation a user can open can ask for it in one
|
|
293
|
+
* row instead of paging past runs it would skip.
|
|
294
|
+
*/
|
|
295
|
+
foregroundOnly?: true;
|
|
263
296
|
}
|
|
264
297
|
|
|
265
298
|
export interface ConversationListQuery extends ConversationListFilter {
|
|
@@ -324,21 +357,57 @@ function originChannelClause(originChannel: string) {
|
|
|
324
357
|
return eq(conversations.originChannel, originChannel);
|
|
325
358
|
}
|
|
326
359
|
|
|
360
|
+
/**
|
|
361
|
+
* The join between a conversation and its attention projection, one row per
|
|
362
|
+
* conversation in `conversation_assistant_attention_state`. Every reader of
|
|
363
|
+
* "unseen" joins on this: the list and count filters, the unread count, and
|
|
364
|
+
* the section index.
|
|
365
|
+
*/
|
|
366
|
+
const attentionJoin = eq(
|
|
367
|
+
conversationAssistantAttentionState.conversationId,
|
|
368
|
+
conversations.id,
|
|
369
|
+
);
|
|
370
|
+
|
|
327
371
|
function conversationListWhere(filter: ConversationListFilter) {
|
|
328
372
|
const {
|
|
329
373
|
conversationType = "standard",
|
|
330
374
|
archiveStatus = "active",
|
|
331
375
|
originChannel,
|
|
332
376
|
groupId,
|
|
377
|
+
needsAttention,
|
|
378
|
+
foregroundOnly,
|
|
333
379
|
} = filter;
|
|
334
380
|
return and(
|
|
335
381
|
conversationTypeClause(conversationType),
|
|
336
382
|
archiveStatusClause(archiveStatus) ?? undefined,
|
|
337
383
|
originChannel ? originChannelClause(originChannel) : undefined,
|
|
338
384
|
groupId ? groupIdClause(groupId) : undefined,
|
|
385
|
+
...(needsAttention ? unseenAttentionStateConditions() : []),
|
|
386
|
+
foregroundOnly ? sql.raw(notBackgroundVisibilitySql()) : undefined,
|
|
339
387
|
);
|
|
340
388
|
}
|
|
341
389
|
|
|
390
|
+
/**
|
|
391
|
+
* The joins {@link conversationListWhere}'s predicates need, applied to a
|
|
392
|
+
* list or count query over `conversations`. The attention predicate lives on
|
|
393
|
+
* `conversation_assistant_attention_state`, so `needsAttention` joins it.
|
|
394
|
+
* Inner, not left: a conversation with no
|
|
395
|
+
* attention row has no unseen message by definition. Joined only when the
|
|
396
|
+
* filter asks, so a query without the filter runs without the join and
|
|
397
|
+
* lists every conversation the predicates admit. One place for both
|
|
398
|
+
* {@link listConversations} and {@link countConversations}, so a page and
|
|
399
|
+
* its total always describe the same set.
|
|
400
|
+
*/
|
|
401
|
+
function withListFilterJoins<T extends SQLiteSelect>(
|
|
402
|
+
query: T,
|
|
403
|
+
filter: ConversationListFilter,
|
|
404
|
+
) {
|
|
405
|
+
if (!filter.needsAttention) {
|
|
406
|
+
return query;
|
|
407
|
+
}
|
|
408
|
+
return query.innerJoin(conversationAssistantAttentionState, attentionJoin);
|
|
409
|
+
}
|
|
410
|
+
|
|
342
411
|
export function listConversations(
|
|
343
412
|
query: ConversationListQuery = {},
|
|
344
413
|
): ConversationRow[] {
|
|
@@ -359,9 +428,13 @@ export function listConversations(
|
|
|
359
428
|
// distinguish a total order from a lucky one. The guarantee becomes
|
|
360
429
|
// observable, and gets its test, with the keyset pagination work.
|
|
361
430
|
const tiebreak = desc(conversations.id);
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
431
|
+
// Selecting the conversation columns explicitly keeps the row shape
|
|
432
|
+
// `parseConversation` maps identical whether or not the attention join is
|
|
433
|
+
// present.
|
|
434
|
+
return withListFilterJoins(
|
|
435
|
+
db.select(getTableColumns(conversations)).from(conversations).$dynamic(),
|
|
436
|
+
filter,
|
|
437
|
+
)
|
|
365
438
|
.where(conversationListWhere(filter))
|
|
366
439
|
.orderBy(recency, tiebreak)
|
|
367
440
|
.limit(limit ?? 100)
|
|
@@ -438,31 +511,7 @@ export function getMessageRoleStatsByConversation(
|
|
|
438
511
|
conversationIds: string[],
|
|
439
512
|
role: string = "assistant",
|
|
440
513
|
): Map<string, { count: number; lastAt: number }> {
|
|
441
|
-
|
|
442
|
-
return new Map();
|
|
443
|
-
}
|
|
444
|
-
const db = getDb();
|
|
445
|
-
const rows = db
|
|
446
|
-
.select({
|
|
447
|
-
conversationId: messages.conversationId,
|
|
448
|
-
count: sql<number>`COUNT(*)`.as("count"),
|
|
449
|
-
lastAt: sql<number>`MAX(${messages.createdAt})`.as("last_at"),
|
|
450
|
-
})
|
|
451
|
-
.from(messages)
|
|
452
|
-
.where(
|
|
453
|
-
and(
|
|
454
|
-
inArray(messages.conversationId, conversationIds),
|
|
455
|
-
eq(messages.role, role),
|
|
456
|
-
),
|
|
457
|
-
)
|
|
458
|
-
.groupBy(messages.conversationId)
|
|
459
|
-
.all();
|
|
460
|
-
return new Map(
|
|
461
|
-
rows.map((r) => [
|
|
462
|
-
r.conversationId,
|
|
463
|
-
{ count: Number(r.count), lastAt: Number(r.lastAt) },
|
|
464
|
-
]),
|
|
465
|
-
);
|
|
514
|
+
return countMessagesByRoleForConversations(conversationIds, role);
|
|
466
515
|
}
|
|
467
516
|
|
|
468
517
|
export function listPinnedConversations(
|
|
@@ -535,6 +584,8 @@ export function listConversationsByTitlePrefix(
|
|
|
535
584
|
const rows = rawAll<Row>(
|
|
536
585
|
"conversation:listByTitlePrefix",
|
|
537
586
|
`SELECT c.id, c.title,
|
|
587
|
+
-- Any-state count (streaming rows included): a list-surface size
|
|
588
|
+
-- hint where a transient +1 during a live turn is immaterial.
|
|
538
589
|
(SELECT COUNT(*) FROM messages WHERE conversation_id = c.id) AS message_count,
|
|
539
590
|
c.created_at
|
|
540
591
|
FROM conversations c
|
|
@@ -561,9 +612,10 @@ export function countConversations(
|
|
|
561
612
|
): number {
|
|
562
613
|
ensureGroupMigration();
|
|
563
614
|
const db = getDb();
|
|
564
|
-
const [{ total }] =
|
|
565
|
-
.select({ total: count() })
|
|
566
|
-
|
|
615
|
+
const [{ total }] = withListFilterJoins(
|
|
616
|
+
db.select({ total: count() }).from(conversations).$dynamic(),
|
|
617
|
+
filter,
|
|
618
|
+
)
|
|
567
619
|
.where(conversationListWhere(filter))
|
|
568
620
|
.all();
|
|
569
621
|
return total;
|
|
@@ -601,10 +653,7 @@ export function countUnreadConversations(): number {
|
|
|
601
653
|
const [{ total }] = db
|
|
602
654
|
.select({ total: count() })
|
|
603
655
|
.from(conversations)
|
|
604
|
-
.innerJoin(
|
|
605
|
-
conversationAssistantAttentionState,
|
|
606
|
-
eq(conversationAssistantAttentionState.conversationId, conversations.id),
|
|
607
|
-
)
|
|
656
|
+
.innerJoin(conversationAssistantAttentionState, attentionJoin)
|
|
608
657
|
.where(
|
|
609
658
|
and(
|
|
610
659
|
conversationTypeClause("standard"),
|
|
@@ -617,32 +666,109 @@ export function countUnreadConversations(): number {
|
|
|
617
666
|
return total;
|
|
618
667
|
}
|
|
619
668
|
|
|
669
|
+
/** Totals for one sidebar section: every member, and the unread among them. */
|
|
670
|
+
export interface ConversationSectionCount {
|
|
671
|
+
total: number;
|
|
672
|
+
unread: number;
|
|
673
|
+
}
|
|
674
|
+
|
|
620
675
|
/**
|
|
621
|
-
*
|
|
622
|
-
*
|
|
623
|
-
*
|
|
676
|
+
* Row counts for every non-empty sidebar section, with no rows fetched.
|
|
677
|
+
*
|
|
678
|
+
* Two axes, disjoint by construction because `group_id` is single-valued:
|
|
679
|
+
*
|
|
680
|
+
* - `groups`: rows filed somewhere, bucketed by `group_id`. Contains
|
|
681
|
+
* `'system:pinned'` and custom-group ids; the background/scheduled system
|
|
682
|
+
* buckets are excluded because no section renders them.
|
|
683
|
+
* - `channels`: rows filed nowhere, bucketed by effective origin channel,
|
|
684
|
+
* where NULL reads as {@link NATIVE_ORIGIN_CHANNEL} exactly as
|
|
685
|
+
* {@link originChannelClause} reads it. The native bucket is the Chats
|
|
686
|
+
* section; each other bucket is that channel's section.
|
|
687
|
+
*
|
|
688
|
+
* Only buckets with at least one row appear: GROUP BY cannot emit an empty
|
|
689
|
+
* bucket, and an empty section renders no card.
|
|
690
|
+
*
|
|
691
|
+
* `total` follows the standard-listing visibility
|
|
692
|
+
* ({@link conversationTypeClause}), active rows only, so it counts exactly
|
|
693
|
+
* the rows `GET /v1/conversations` would return for that section's filter.
|
|
694
|
+
* `unread` applies the same rules as {@link countUnreadConversations} on top
|
|
695
|
+
* (not an unsurfaced background/scheduled row, unseen per the attention
|
|
696
|
+
* projection), composed from the same predicates rather than restated, so
|
|
697
|
+
* the per-section numbers always sum against the global count.
|
|
624
698
|
*/
|
|
625
|
-
export
|
|
699
|
+
export interface ConversationSectionCounts {
|
|
700
|
+
groups: Array<{ groupId: string } & ConversationSectionCount>;
|
|
701
|
+
channels: Array<{ channel: string } & ConversationSectionCount>;
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
export function countConversationSections(): ConversationSectionCounts {
|
|
705
|
+
ensureGroupMigration();
|
|
626
706
|
const db = getDb();
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
707
|
+
|
|
708
|
+
/* `countUnreadConversations` gets the same effect with an INNER JOIN and
|
|
709
|
+
the unseen conditions in its WHERE; here every row must be counted and
|
|
710
|
+
only unread rows summed, so the join is LEFT and the conditions move
|
|
711
|
+
into the CASE. A row with no attention projection has NULL columns,
|
|
712
|
+
fails the conditions, and sums 0: identical membership. */
|
|
713
|
+
const unread = sql<number>`SUM(CASE WHEN ${and(
|
|
714
|
+
sql.raw(notBackgroundVisibilitySql()),
|
|
715
|
+
...unseenAttentionStateConditions(),
|
|
716
|
+
)} THEN 1 ELSE 0 END)`;
|
|
717
|
+
|
|
718
|
+
const groups = db
|
|
719
|
+
.select({
|
|
720
|
+
groupId: sql<string>`group_id`,
|
|
721
|
+
total: count(),
|
|
722
|
+
unread,
|
|
723
|
+
})
|
|
724
|
+
.from(conversations)
|
|
725
|
+
.leftJoin(conversationAssistantAttentionState, attentionJoin)
|
|
630
726
|
.where(
|
|
631
727
|
and(
|
|
632
|
-
|
|
633
|
-
|
|
728
|
+
conversationTypeClause("standard"),
|
|
729
|
+
isNull(conversations.archivedAt),
|
|
730
|
+
sql`(group_id = ${PINNED_GROUP_ID} OR (group_id IS NOT NULL AND group_id NOT LIKE 'system:%'))`,
|
|
634
731
|
),
|
|
635
732
|
)
|
|
636
|
-
.
|
|
637
|
-
.
|
|
638
|
-
|
|
733
|
+
.groupBy(sql`group_id`)
|
|
734
|
+
.all();
|
|
735
|
+
|
|
736
|
+
const effectiveChannel = sql<string>`COALESCE(${conversations.originChannel}, ${NATIVE_ORIGIN_CHANNEL})`;
|
|
737
|
+
const channels = db
|
|
738
|
+
.select({
|
|
739
|
+
channel: effectiveChannel,
|
|
740
|
+
total: count(),
|
|
741
|
+
unread,
|
|
742
|
+
})
|
|
743
|
+
.from(conversations)
|
|
744
|
+
.leftJoin(conversationAssistantAttentionState, attentionJoin)
|
|
745
|
+
.where(
|
|
746
|
+
and(
|
|
747
|
+
conversationTypeClause("standard"),
|
|
748
|
+
isNull(conversations.archivedAt),
|
|
749
|
+
groupIdClause(UNGROUPED_GROUP_ID),
|
|
750
|
+
),
|
|
751
|
+
)
|
|
752
|
+
.groupBy(effectiveChannel)
|
|
753
|
+
.all();
|
|
754
|
+
|
|
755
|
+
return { groups, channels };
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
/**
|
|
759
|
+
* Check whether the last user message in a conversation is a tool_result-only
|
|
760
|
+
* message (i.e., not a real user-typed message). This is used by undo() to
|
|
761
|
+
* determine if additional exchanges need to be deleted from the DB.
|
|
762
|
+
*/
|
|
763
|
+
export function isLastUserMessageToolResult(conversationId: string): boolean {
|
|
764
|
+
const lastUserContent = latestUserMessageRawContent(conversationId);
|
|
639
765
|
|
|
640
|
-
if (
|
|
766
|
+
if (lastUserContent === null) {
|
|
641
767
|
return false;
|
|
642
768
|
}
|
|
643
769
|
|
|
644
770
|
try {
|
|
645
|
-
const parsed = JSON.parse(
|
|
771
|
+
const parsed = JSON.parse(lastUserContent);
|
|
646
772
|
if (
|
|
647
773
|
Array.isArray(parsed) &&
|
|
648
774
|
parsed.length > 0 &&
|
|
@@ -816,6 +942,10 @@ export async function searchConversations(
|
|
|
816
942
|
if (!opts?.includeArchived) {
|
|
817
943
|
whereClauses.push(`c.archived_at IS NULL`);
|
|
818
944
|
}
|
|
945
|
+
// No completeness predicate: candidate ids come from the lexical
|
|
946
|
+
// index, which filters finalized = 1 at index time, so an unfinalized
|
|
947
|
+
// row cannot be a candidate. Do not copy this shape for id sets with
|
|
948
|
+
// different provenance.
|
|
819
949
|
const visibleRows = rawAll<CandidateRow>(
|
|
820
950
|
"conversation:searchConversations:visibleCandidates",
|
|
821
951
|
`
|
|
@@ -5,11 +5,11 @@
|
|
|
5
5
|
* finding messages by source identifiers, and managing raw payload storage.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import { and, desc, eq, isNotNull, ne, or, sql } from "drizzle-orm";
|
|
8
|
+
import { and, desc, eq, isNotNull, like, ne, or, sql } from "drizzle-orm";
|
|
9
9
|
import { v4 as uuid } from "uuid";
|
|
10
10
|
|
|
11
|
+
import type { ChannelId } from "../channels/types.js";
|
|
11
12
|
import { readSlackMetadataFromMessageMetadata } from "../messaging/providers/slack/message-metadata.js";
|
|
12
|
-
import { DAEMON_INTERNAL_ASSISTANT_ID } from "../runtime/assistant-scope.js";
|
|
13
13
|
import type { SlackInboundMessageMetadata } from "../runtime/http-types.js";
|
|
14
14
|
import { parseJsonSafe } from "../util/json.js";
|
|
15
15
|
import { isPlainObject } from "../util/object.js";
|
|
@@ -19,8 +19,14 @@ import {
|
|
|
19
19
|
getOrCreateConversation,
|
|
20
20
|
setConversationKeyIfAbsent,
|
|
21
21
|
} from "./conversation-key-store.js";
|
|
22
|
+
import type { NonScheduledConversationType } from "./conversation-types.js";
|
|
22
23
|
import { getDb } from "./db-connection.js";
|
|
23
|
-
import {
|
|
24
|
+
import {
|
|
25
|
+
channelInboundEvents,
|
|
26
|
+
conversationKeys,
|
|
27
|
+
conversations,
|
|
28
|
+
messages,
|
|
29
|
+
} from "./schema.js";
|
|
24
30
|
|
|
25
31
|
export interface InboundResult {
|
|
26
32
|
accepted: boolean;
|
|
@@ -31,13 +37,27 @@ export interface InboundResult {
|
|
|
31
37
|
|
|
32
38
|
export interface RecordInboundOptions {
|
|
33
39
|
sourceMessageId?: string;
|
|
34
|
-
assistantId?: string;
|
|
35
40
|
sourceThreadId?: string;
|
|
41
|
+
/**
|
|
42
|
+
* Record the event against this conversation instead of resolving one from
|
|
43
|
+
* the address. For events that belong to a message rather than to a chat: a
|
|
44
|
+
* reaction lives in the conversation of the message it was attached to, and
|
|
45
|
+
* resolving from its own address would mint a second one.
|
|
46
|
+
*/
|
|
47
|
+
conversationId?: string;
|
|
36
48
|
}
|
|
37
49
|
|
|
38
50
|
const SLACK_LEGACY_THREAD_EVIDENCE_BATCH_SIZE = 50;
|
|
39
51
|
const SLACK_LEGACY_THREAD_EVIDENCE_MAX_SCAN = 500;
|
|
40
52
|
|
|
53
|
+
/**
|
|
54
|
+
* Rows examined when locating a Slack message the assistant itself posted.
|
|
55
|
+
* Bounded on purpose: the scan runs on the inbound path while the gateway
|
|
56
|
+
* waits for its ack, and reactions land on recent messages, so a cap costs
|
|
57
|
+
* almost no recall and keeps the cost flat as the database grows.
|
|
58
|
+
*/
|
|
59
|
+
const SLACK_OUTBOUND_TS_MAX_SCAN = 400;
|
|
60
|
+
|
|
41
61
|
/**
|
|
42
62
|
* Channels where an inbound thread id scopes the conversation: a Slack thread
|
|
43
63
|
* or a Telegram private-chat topic each maps to its own conversation. A
|
|
@@ -45,30 +65,25 @@ const SLACK_LEGACY_THREAD_EVIDENCE_MAX_SCAN = 500;
|
|
|
45
65
|
*/
|
|
46
66
|
const THREAD_SCOPED_CHANNELS = new Set(["slack", "telegram"]);
|
|
47
67
|
|
|
48
|
-
|
|
49
|
-
|
|
68
|
+
/**
|
|
69
|
+
* Scope prefix on every conversation key.
|
|
70
|
+
*
|
|
71
|
+
* Part of the stored key format, so it is fixed: rows are written under
|
|
72
|
+
* `asst:self:` and stop resolving if it changes. The daemon is single-tenant
|
|
73
|
+
* and scopes all its storage to `self` (`DAEMON_INTERNAL_ASSISTANT_ID`).
|
|
74
|
+
*/
|
|
75
|
+
const CONVERSATION_KEY_SCOPE = "asst:self";
|
|
76
|
+
|
|
77
|
+
export function buildScopedConversationKey(
|
|
50
78
|
sourceChannel: string,
|
|
51
79
|
externalChatId: string,
|
|
52
80
|
sourceThreadId?: string | null,
|
|
53
81
|
): string {
|
|
54
82
|
const threadId = sourceThreadId?.trim();
|
|
55
83
|
if (THREAD_SCOPED_CHANNELS.has(sourceChannel) && threadId) {
|
|
56
|
-
return
|
|
84
|
+
return `${CONVERSATION_KEY_SCOPE}:${sourceChannel}:${externalChatId}:thread:${threadId}`;
|
|
57
85
|
}
|
|
58
|
-
return
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
export function buildScopedConversationKey(
|
|
62
|
-
sourceChannel: string,
|
|
63
|
-
externalChatId: string,
|
|
64
|
-
sourceThreadId?: string | null,
|
|
65
|
-
): string {
|
|
66
|
-
return buildScopedConversationKeyForAssistant(
|
|
67
|
-
DAEMON_INTERNAL_ASSISTANT_ID,
|
|
68
|
-
sourceChannel,
|
|
69
|
-
externalChatId,
|
|
70
|
-
sourceThreadId,
|
|
71
|
-
);
|
|
86
|
+
return `${CONVERSATION_KEY_SCOPE}:${sourceChannel}:${externalChatId}`;
|
|
72
87
|
}
|
|
73
88
|
|
|
74
89
|
function legacySlackConversationHasThreadEvidence(
|
|
@@ -135,14 +150,24 @@ function legacySlackConversationHasThreadEvidence(
|
|
|
135
150
|
return false;
|
|
136
151
|
}
|
|
137
152
|
|
|
138
|
-
|
|
139
|
-
|
|
153
|
+
/**
|
|
154
|
+
* Applied only where this call is what creates the conversation. An address
|
|
155
|
+
* that already resolves is an existing conversation, and neither its type nor
|
|
156
|
+
* where it came from is the current message's to restate.
|
|
157
|
+
*/
|
|
158
|
+
export interface ResolveInboundConversationOptions {
|
|
159
|
+
conversationType?: NonScheduledConversationType;
|
|
160
|
+
/** Channel this conversation originates on, for first-message attribution. */
|
|
161
|
+
origin?: ChannelId;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
export function resolveInboundConversation(
|
|
140
165
|
sourceChannel: string,
|
|
141
166
|
externalChatId: string,
|
|
142
167
|
sourceThreadId?: string | null,
|
|
168
|
+
opts?: ResolveInboundConversationOptions,
|
|
143
169
|
): { conversationId: string } {
|
|
144
|
-
const threadedKey =
|
|
145
|
-
assistantId,
|
|
170
|
+
const threadedKey = buildScopedConversationKey(
|
|
146
171
|
sourceChannel,
|
|
147
172
|
externalChatId,
|
|
148
173
|
sourceThreadId,
|
|
@@ -155,7 +180,7 @@ function resolveInboundConversation(
|
|
|
155
180
|
// other thread-scoped channel (Telegram topics) has no flat-key aliasing —
|
|
156
181
|
// a thread id always resolves the threaded key directly.
|
|
157
182
|
if (sourceChannel !== "slack" || !threadId) {
|
|
158
|
-
return getOrCreateConversation(threadedKey);
|
|
183
|
+
return getOrCreateConversation(threadedKey, opts);
|
|
159
184
|
}
|
|
160
185
|
|
|
161
186
|
const threadedMapping = getConversationByKey(threadedKey);
|
|
@@ -163,8 +188,7 @@ function resolveInboundConversation(
|
|
|
163
188
|
return { conversationId: threadedMapping.conversationId };
|
|
164
189
|
}
|
|
165
190
|
|
|
166
|
-
const legacyKey =
|
|
167
|
-
assistantId,
|
|
191
|
+
const legacyKey = buildScopedConversationKey(
|
|
168
192
|
sourceChannel,
|
|
169
193
|
externalChatId,
|
|
170
194
|
null,
|
|
@@ -185,7 +209,7 @@ function resolveInboundConversation(
|
|
|
185
209
|
}
|
|
186
210
|
}
|
|
187
211
|
|
|
188
|
-
return getOrCreateConversation(threadedKey);
|
|
212
|
+
return getOrCreateConversation(threadedKey, opts);
|
|
189
213
|
}
|
|
190
214
|
|
|
191
215
|
/**
|
|
@@ -236,7 +260,97 @@ export function findInboundConversationId(
|
|
|
236
260
|
/**
|
|
237
261
|
* Record an inbound channel event. Returns `duplicate: true` if this
|
|
238
262
|
* exact (channel, chat, message) combination was already seen.
|
|
263
|
+
*
|
|
264
|
+
* The dedup half of this is also implemented gateway-side, on the same
|
|
265
|
+
* triple, in `gateway/src/db/inbound-dedup-store.ts`. Deliberately, not by
|
|
266
|
+
* accident: the gateway claims a delivery before handing it over, so a
|
|
267
|
+
* vendor's retry costs no crossing, and this stays as the record that binds
|
|
268
|
+
* the conversation and tracks the reply. Both must key on the same three
|
|
269
|
+
* fields for either to mean anything.
|
|
239
270
|
*/
|
|
271
|
+
/**
|
|
272
|
+
* The inbound event already recorded for this address, if any. Read-only twin
|
|
273
|
+
* of the dedup check inside {@link recordInbound}, for callers that must know
|
|
274
|
+
* an event is a redelivery before doing work that recording would otherwise
|
|
275
|
+
* gate (routing a guardian decision, for one).
|
|
276
|
+
*/
|
|
277
|
+
export function findInboundEvent(
|
|
278
|
+
sourceChannel: string,
|
|
279
|
+
externalChatId: string,
|
|
280
|
+
externalMessageId: string,
|
|
281
|
+
): { eventId: string; conversationId: string } | null {
|
|
282
|
+
const row = getDb()
|
|
283
|
+
.select({
|
|
284
|
+
id: channelInboundEvents.id,
|
|
285
|
+
conversationId: channelInboundEvents.conversationId,
|
|
286
|
+
})
|
|
287
|
+
.from(channelInboundEvents)
|
|
288
|
+
.where(
|
|
289
|
+
and(
|
|
290
|
+
eq(channelInboundEvents.sourceChannel, sourceChannel),
|
|
291
|
+
eq(channelInboundEvents.externalChatId, externalChatId),
|
|
292
|
+
eq(channelInboundEvents.externalMessageId, externalMessageId),
|
|
293
|
+
),
|
|
294
|
+
)
|
|
295
|
+
.get();
|
|
296
|
+
return row ? { eventId: row.id, conversationId: row.conversationId } : null;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
* The conversation holding the Slack message with this `ts`, found by reading
|
|
301
|
+
* the `slackMeta` the assistant's own posts carry.
|
|
302
|
+
*
|
|
303
|
+
* `findMessageBySourceId` covers every message that arrived as an inbound
|
|
304
|
+
* event. It cannot see what the assistant posted, because an outbound reply
|
|
305
|
+
* opens no inbound event, so a reaction on the assistant's own message needs
|
|
306
|
+
* this. The search is confined to conversations already bound to the same
|
|
307
|
+
* Slack channel and to the most recent {@link SLACK_OUTBOUND_TS_MAX_SCAN}
|
|
308
|
+
* rows among them; beyond that it gives up and the caller drops the
|
|
309
|
+
* annotation, which is the same outcome as never finding it at all.
|
|
310
|
+
*/
|
|
311
|
+
export function findSlackConversationByMessageTs(
|
|
312
|
+
externalChatId: string,
|
|
313
|
+
channelTs: string,
|
|
314
|
+
): string | null {
|
|
315
|
+
const db = getDb();
|
|
316
|
+
const keyPrefix = `${CONVERSATION_KEY_SCOPE}:slack:${externalChatId}`;
|
|
317
|
+
const rows = db
|
|
318
|
+
.select({
|
|
319
|
+
conversationId: messages.conversationId,
|
|
320
|
+
metadata: messages.metadata,
|
|
321
|
+
})
|
|
322
|
+
.from(messages)
|
|
323
|
+
.innerJoin(
|
|
324
|
+
conversationKeys,
|
|
325
|
+
eq(conversationKeys.conversationId, messages.conversationId),
|
|
326
|
+
)
|
|
327
|
+
.where(
|
|
328
|
+
and(
|
|
329
|
+
or(
|
|
330
|
+
eq(conversationKeys.conversationKey, keyPrefix),
|
|
331
|
+
like(conversationKeys.conversationKey, `${keyPrefix}:thread:%`),
|
|
332
|
+
),
|
|
333
|
+
like(messages.metadata, '%"slackMeta"%'),
|
|
334
|
+
),
|
|
335
|
+
)
|
|
336
|
+
.orderBy(desc(messages.createdAt))
|
|
337
|
+
.limit(SLACK_OUTBOUND_TS_MAX_SCAN)
|
|
338
|
+
.all();
|
|
339
|
+
|
|
340
|
+
for (const row of rows) {
|
|
341
|
+
const slackMeta = readSlackMetadataFromMessageMetadata(row.metadata, {
|
|
342
|
+
allowFlatLegacy: true,
|
|
343
|
+
});
|
|
344
|
+
if (
|
|
345
|
+
slackMeta?.channelId === externalChatId &&
|
|
346
|
+
slackMeta.channelTs === channelTs
|
|
347
|
+
) {
|
|
348
|
+
return row.conversationId;
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
return null;
|
|
352
|
+
}
|
|
353
|
+
|
|
240
354
|
export function recordInbound(
|
|
241
355
|
sourceChannel: string,
|
|
242
356
|
externalChatId: string,
|
|
@@ -269,13 +383,13 @@ export function recordInbound(
|
|
|
269
383
|
};
|
|
270
384
|
}
|
|
271
385
|
|
|
272
|
-
const
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
386
|
+
const mapping = options?.conversationId
|
|
387
|
+
? { conversationId: options.conversationId }
|
|
388
|
+
: resolveInboundConversation(
|
|
389
|
+
sourceChannel,
|
|
390
|
+
externalChatId,
|
|
391
|
+
options?.sourceThreadId,
|
|
392
|
+
);
|
|
279
393
|
const now = Date.now();
|
|
280
394
|
const eventId = uuid();
|
|
281
395
|
|