@vellumai/assistant 0.8.1 → 0.8.3
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 +13 -19
- package/Dockerfile +75 -1
- package/bun.lock +11 -1
- package/docker-entrypoint.sh +17 -0
- package/docker-init-apt-root.sh +167 -0
- package/docker-kata-apt-env.sh +39 -0
- package/docs/plugins.md +88 -47
- package/docs/skills.md +9 -7
- package/examples/plugins/echo/README.md +27 -27
- package/examples/plugins/echo/package.json +3 -0
- package/examples/plugins/echo/register.ts +31 -31
- package/node_modules/@vellumai/slack-text/src/index.test.ts +114 -14
- package/node_modules/@vellumai/slack-text/src/index.ts +82 -18
- package/openapi.yaml +642 -5
- package/package.json +3 -1
- package/scripts/generate-openapi.ts +83 -10
- package/scripts/sync-llm-catalog.ts +2 -2
- package/scripts/sync-web-search-catalog.ts +47 -25
- package/src/__tests__/agent-image-optimize.test.ts +11 -3
- package/src/__tests__/agent-loop-exit-reason.test.ts +272 -0
- package/src/__tests__/agent-loop-provider-error-recording.test.ts +195 -0
- package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +131 -0
- package/src/__tests__/anthropic-provider.test.ts +45 -0
- package/src/__tests__/app-builder-tool-scripts.test.ts +9 -3
- package/src/__tests__/app-executors.test.ts +220 -4
- package/src/__tests__/auto-analysis-end-to-end.test.ts +35 -0
- package/src/__tests__/bundled-asset.test.ts +6 -6
- package/src/__tests__/channel-availability-routes.test.ts +206 -0
- package/src/__tests__/channel-delivery-store.test.ts +289 -1
- package/src/__tests__/circuit-breaker-pipeline.test.ts +0 -1
- package/src/__tests__/clawhub.test.ts +75 -16
- package/src/__tests__/compactor-tail-resolution.test.ts +147 -0
- package/src/__tests__/config-get-vision-flag.test.ts +136 -0
- package/src/__tests__/config-loader-backfill.test.ts +115 -18
- package/src/__tests__/config-schema.test.ts +21 -0
- package/src/__tests__/config-set-route.test.ts +80 -0
- package/src/__tests__/config-sounds-sync.test.ts +97 -0
- package/src/__tests__/config-watcher-skill-reseed.test.ts +453 -0
- package/src/__tests__/context-search-conversations-source.test.ts +117 -2
- package/src/__tests__/context-search-memory-v2-source.test.ts +0 -1
- package/src/__tests__/context-search-workspace-source.test.ts +7 -0
- package/src/__tests__/context-token-estimator.test.ts +31 -65
- package/src/__tests__/conversation-abort-tool-results.test.ts +4 -1
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +1 -0
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +92 -92
- package/src/__tests__/conversation-agent-loop.test.ts +59 -1
- package/src/__tests__/conversation-error.test.ts +42 -3
- package/src/__tests__/conversation-fork-crud.test.ts +82 -0
- package/src/__tests__/conversation-inference-profile-route.test.ts +40 -4
- package/src/__tests__/conversation-lifecycle.test.ts +173 -0
- package/src/__tests__/conversation-media-retry.test.ts +19 -8
- package/src/__tests__/conversation-message-sync-tags.test.ts +97 -0
- package/src/__tests__/conversation-pairing.test.ts +54 -0
- package/src/__tests__/conversation-process-callsite.test.ts +4 -1
- package/src/__tests__/conversation-provider-retry-repair.test.ts +5 -1
- package/src/__tests__/conversation-queue.test.ts +4 -1
- package/src/__tests__/conversation-runtime-assembly.test.ts +102 -13
- package/src/__tests__/conversation-slash-queue.test.ts +59 -1
- package/src/__tests__/conversation-slash-unknown.test.ts +4 -1
- package/src/__tests__/conversation-surfaces-table-action.test.ts +360 -0
- package/src/__tests__/conversation-sync-tags.test.ts +235 -0
- package/src/__tests__/conversation-workspace-injection.test.ts +5 -1
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +5 -1
- package/src/__tests__/credential-security-invariants.test.ts +3 -2
- package/src/__tests__/date-context.test.ts +45 -0
- package/src/__tests__/db-slack-external-content-normalization.test.ts +301 -0
- package/src/__tests__/delete-managed-skill-tool.test.ts +55 -13
- package/src/__tests__/disk-pressure-tools.test.ts +1 -0
- package/src/__tests__/dm-backfill.test.ts +121 -10
- package/src/__tests__/document-tool-security.test.ts +258 -0
- package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +0 -1
- package/src/__tests__/edit-propagation.test.ts +33 -0
- package/src/__tests__/empty-response-pipeline.test.ts +0 -4
- package/src/__tests__/external-plugin-loader.test.ts +151 -55
- package/src/__tests__/filing-service.test.ts +140 -0
- package/src/__tests__/get-skill-detail-audit.test.ts +0 -4
- package/src/__tests__/guardian-action-no-hardcoded-copy.test.ts +0 -1
- package/src/__tests__/guardian-dispatch.test.ts +1 -0
- package/src/__tests__/handlers-skills-memory-v2-reseed.test.ts +43 -62
- package/src/__tests__/heartbeat-service.test.ts +24 -164
- package/src/__tests__/helpers/channel-test-adapter.ts +0 -2
- package/src/__tests__/helpers/tar-fixtures.ts +39 -0
- package/src/__tests__/helpers/wait-for.ts +21 -0
- package/src/__tests__/history-repair-pipeline.test.ts +0 -3
- package/src/__tests__/history-repair.test.ts +73 -0
- package/src/__tests__/host-app-control-proxy.test.ts +507 -10
- package/src/__tests__/host-proxy-preactivation.test.ts +200 -13
- package/src/__tests__/image-credentials.test.ts +1 -1
- package/src/__tests__/inbound-slack-persistence.test.ts +2 -0
- package/src/__tests__/inference-no-mode-boot-e2e.test.ts +1 -1
- package/src/__tests__/inference-profile-reaper.test.ts +4 -2
- package/src/__tests__/inference-profile-session-handler.test.ts +18 -6
- package/src/__tests__/inference-profile-session-ipc.test.ts +17 -5
- package/src/__tests__/injector-background-turn.test.ts +153 -0
- package/src/__tests__/injector-chain.test.ts +15 -8
- package/src/__tests__/install-skill-routing.test.ts +155 -37
- package/src/__tests__/lifecycle-memory-v2-seed.test.ts +99 -3
- package/src/__tests__/list-messages-page-latest.test.ts +55 -0
- package/src/__tests__/llm-call-pipeline.test.ts +0 -3
- package/src/__tests__/llm-callsite-catalog.test.ts +25 -0
- package/src/__tests__/llm-catalog-parity.test.ts +58 -13
- package/src/__tests__/llm-request-log-agent-loop-exit-reason.test.ts +116 -0
- package/src/__tests__/llm-request-log-error-payload.test.ts +138 -0
- package/src/__tests__/llm-request-log-source-clickhouse.test.ts +36 -0
- package/src/__tests__/llm-request-log-source-factory.test.ts +29 -53
- package/src/__tests__/llm-resolver.test.ts +255 -2
- package/src/__tests__/llm-usage-store.test.ts +114 -0
- package/src/__tests__/managed-profile-guard.test.ts +41 -29
- package/src/__tests__/managed-skill-lifecycle.test.ts +109 -18
- package/src/__tests__/managed-store.test.ts +84 -192
- package/src/__tests__/media-generate-image.test.ts +1 -1
- package/src/__tests__/memory-retrieval-pipeline.test.ts +0 -2
- package/src/__tests__/messages-after-tiebreaker.test.ts +122 -0
- package/src/__tests__/notification-decision-fallback.test.ts +0 -91
- package/src/__tests__/notification-decision-strategy.test.ts +14 -31
- package/src/__tests__/notification-deep-link.test.ts +15 -0
- package/src/__tests__/notification-guardian-path.test.ts +1 -2
- package/src/__tests__/notification-platform-adapter.test.ts +5 -4
- package/src/__tests__/notification-telegram-adapter.test.ts +1 -0
- package/src/__tests__/notification-vellum-adapter.test.ts +113 -0
- package/src/__tests__/oauth-commands-routes.test.ts +168 -16
- package/src/__tests__/oauth-provider-profiles.test.ts +9 -0
- package/src/__tests__/openai-provider.test.ts +242 -3
- package/src/__tests__/openai-responses-cutover-guard.test.ts +17 -9
- package/src/__tests__/openrouter-provider-only.test.ts +51 -3
- package/src/__tests__/openrouter-token-estimation.test.ts +34 -25
- package/src/__tests__/overflow-reduce-pipeline.test.ts +0 -2
- package/src/__tests__/persistence-pipeline.test.ts +0 -2
- package/src/__tests__/{managed-proxy-context.test.ts → platform-proxy-context.test.ts} +7 -2
- package/src/__tests__/platform.test.ts +2 -0
- package/src/__tests__/plugin-api-shim.test.ts +125 -0
- package/src/__tests__/plugin-bootstrap.test.ts +10 -36
- package/src/__tests__/plugin-external-api.test.ts +68 -0
- package/src/__tests__/plugin-registry.test.ts +0 -77
- package/src/__tests__/plugin-route-contribution.test.ts +0 -1
- package/src/__tests__/plugin-skill-contribution.test.ts +0 -2
- package/src/__tests__/plugin-tool-contribution.test.ts +16 -15
- package/src/__tests__/plugin-types.test.ts +3 -13
- package/src/__tests__/process-message-background-slack.test.ts +8 -1
- package/src/__tests__/process-message-display-content.test.ts +421 -0
- package/src/__tests__/provider-catalog-visibility.test.ts +158 -0
- package/src/__tests__/provider-error-scenarios.test.ts +111 -0
- package/src/__tests__/{provider-managed-proxy-integration.test.ts → provider-platform-proxy-integration.test.ts} +33 -31
- package/src/__tests__/scaffold-managed-skill-tool.test.ts +65 -13
- package/src/__tests__/schedule-routes.test.ts +50 -3
- package/src/__tests__/schedule-store.test.ts +94 -0
- package/src/__tests__/scheduler-reuse-conversation.test.ts +54 -7
- package/src/__tests__/schema-transforms.test.ts +20 -0
- package/src/__tests__/search-skills-unified.test.ts +0 -5
- package/src/__tests__/{secret-routes-managed-proxy.test.ts → secret-routes-platform-proxy.test.ts} +1 -1
- package/src/__tests__/server-history-render.test.ts +43 -0
- package/src/__tests__/skill-load-feature-flag.test.ts +0 -12
- package/src/__tests__/skill-load-tool.test.ts +27 -89
- package/src/__tests__/skill-memory.test.ts +23 -3
- package/src/__tests__/skills-file-content-endpoint.test.ts +9 -38
- package/src/__tests__/skills-files-catalog-fallback.test.ts +0 -3
- package/src/__tests__/skills-install-extract.test.ts +49 -38
- package/src/__tests__/skills-install-staging.test.ts +159 -0
- package/src/__tests__/skills-uninstall.test.ts +9 -41
- package/src/__tests__/skills.test.ts +51 -58
- package/src/__tests__/slack-channel-config.test.ts +9 -0
- package/src/__tests__/subagent-tool-filtering.test.ts +50 -0
- package/src/__tests__/system-prompt.test.ts +670 -63
- package/src/__tests__/terminal-tools.test.ts +28 -1
- package/src/__tests__/thread-backfill.test.ts +557 -27
- package/src/__tests__/title-generate-pipeline.test.ts +0 -13
- package/src/__tests__/token-estimate-pipeline.test.ts +0 -3
- package/src/__tests__/tool-error-pipeline.test.ts +0 -3
- package/src/__tests__/tool-execute-pipeline.test.ts +0 -5
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +1 -1
- package/src/__tests__/tool-executor.test.ts +16 -4
- package/src/__tests__/tool-result-truncate-pipeline.test.ts +0 -12
- package/src/__tests__/turn-events-store.test.ts +256 -0
- package/src/__tests__/twilio-routes.test.ts +4 -0
- package/src/__tests__/user-plugin-loader.test.ts +0 -7
- package/src/__tests__/voice-session-bridge.test.ts +198 -0
- package/src/__tests__/web-search-catalog-parity.test.ts +32 -10
- package/src/__tests__/workspace-migration-057-repair-stale-gemini-model-ids.test.ts +115 -3
- package/src/__tests__/workspace-migration-072-seed-reply-suggestion-callsite.test.ts +50 -0
- package/src/__tests__/workspace-migration-073-repair-recall-callsite-empty-profile.test.ts +153 -0
- package/src/__tests__/workspace-migration-085-memory-v2-bm25-b-reembed-disabled-v2-pages.test.ts +220 -0
- package/src/__tests__/workspace-migration-086-revert-stale-gemini-mis-rewrites.test.ts +269 -0
- package/src/__tests__/workspace-migration-087-memory-router-balanced-profile.test.ts +228 -0
- package/src/__tests__/workspace-migration-remove-legacy-skills-index.test.ts +309 -0
- package/src/__tests__/workspace-migrations-runner.test.ts +111 -3
- package/src/a2a/__tests__/agent-card.test.ts +98 -0
- package/src/a2a/__tests__/e2e-a2a-channel.test.ts +597 -0
- package/src/a2a/__tests__/protocol-helpers.test.ts +113 -0
- package/src/a2a/__tests__/task-store.test.ts +246 -0
- package/src/a2a/agent-card.ts +58 -0
- package/src/a2a/feature-gate.ts +8 -0
- package/src/a2a/protocol-constants.ts +21 -0
- package/src/a2a/protocol-errors.ts +50 -0
- package/src/a2a/protocol-types.ts +162 -0
- package/src/a2a/task-store.ts +168 -0
- package/src/acp/resolve-agent.ts +1 -1
- package/src/agent/image-optimize.ts +13 -5
- package/src/agent/loop.ts +167 -18
- package/src/calls/voice-session-bridge.ts +61 -42
- package/src/channels/config.ts +9 -0
- package/src/channels/types.ts +122 -0
- package/src/cli/__tests__/unknown-command.test.ts +24 -0
- package/src/cli/commands/__tests__/changelog.test.ts +304 -319
- package/src/cli/{__tests__ → commands/__tests__}/notifications.test.ts +201 -28
- package/src/cli/commands/__tests__/schedules.test.ts +960 -0
- package/src/cli/commands/changelog.ts +106 -42
- package/src/cli/commands/conversations.ts +102 -17
- package/src/cli/commands/default-action.ts +10 -53
- package/src/cli/commands/notifications.ts +388 -346
- package/src/cli/commands/plugins.ts +252 -0
- package/src/cli/commands/schedules.ts +683 -0
- package/src/cli/commands/telemetry.ts +40 -0
- package/src/cli/lib/__tests__/cli-colors.test.ts +48 -0
- package/src/cli/lib/__tests__/confirm-prompt.test.ts +159 -0
- package/src/cli/lib/__tests__/install-from-github.test.ts +355 -0
- package/src/cli/lib/__tests__/list-installed-plugins.test.ts +154 -0
- package/src/cli/lib/__tests__/search-plugins.test.ts +261 -0
- package/src/cli/lib/__tests__/uninstall-plugin.test.ts +124 -0
- package/src/cli/lib/__tests__/unknown-command.test.ts +106 -0
- package/src/cli/lib/cli-colors.ts +12 -0
- package/src/cli/lib/confirm-prompt.ts +79 -0
- package/src/cli/lib/install-from-github.ts +303 -0
- package/src/cli/lib/list-installed-plugins.ts +137 -0
- package/src/cli/lib/search-plugins.ts +163 -0
- package/src/cli/lib/uninstall-plugin.ts +82 -0
- package/src/cli/lib/unknown-command.ts +111 -0
- package/src/cli/program.ts +52 -2
- package/src/config/assistant-feature-flags.ts +24 -54
- package/src/config/bundled-skills/app-builder/SKILL.md +140 -22
- package/src/config/bundled-skills/app-builder/TOOLS.json +7 -0
- package/src/config/bundled-skills/computer-use/TOOLS.json +15 -52
- package/src/config/bundled-skills/document/SKILL.md +23 -3
- package/src/config/bundled-skills/document/TOOLS.json +53 -0
- package/src/config/bundled-skills/document/tools/document-delete.ts +12 -0
- package/src/config/bundled-skills/document/tools/document-list.ts +12 -0
- package/src/config/bundled-skills/document/tools/document-read.ts +12 -0
- package/src/config/bundled-skills/phone-calls/SKILL.md +1 -1
- package/src/config/bundled-skills/skill-management/SKILL.md +2 -2
- package/src/config/bundled-skills/skill-management/TOOLS.json +7 -7
- package/src/config/bundled-tool-registry.ts +6 -0
- package/src/config/call-site-defaults.ts +105 -0
- package/src/config/feature-flag-registry.json +41 -9
- package/src/config/llm-resolver.ts +52 -1
- package/src/config/loader.ts +64 -38
- package/src/config/schema.ts +9 -10
- package/src/config/schemas/__tests__/llm-request-logs.test.ts +36 -0
- package/src/config/schemas/__tests__/memory-v2.test.ts +3 -3
- package/src/config/schemas/channels.ts +17 -0
- package/src/config/schemas/compaction.ts +28 -0
- package/src/config/schemas/conversations.ts +10 -0
- package/src/config/schemas/heartbeat.ts +23 -0
- package/src/config/schemas/llm-request-logs.ts +31 -7
- package/src/config/schemas/llm.ts +1 -0
- package/src/config/schemas/memory-retrieval.ts +18 -0
- package/src/config/schemas/memory-retrospective.ts +1 -1
- package/src/config/schemas/memory-v2.ts +4 -4
- package/src/config/schemas/memory.ts +3 -1
- package/src/config/schemas/tools.ts +14 -0
- package/src/config/seed-inference-profiles.ts +99 -29
- package/src/config/skills.ts +3 -96
- package/src/context/compactor.ts +1107 -0
- package/src/context/token-estimator.ts +34 -36
- package/src/context/window-manager.ts +197 -1520
- package/src/credential-execution/managed-catalog.ts +37 -0
- package/src/credential-health/credential-health-service.ts +280 -19
- package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +33 -18
- package/src/daemon/__tests__/conversation-tool-setup-exclude.test.ts +138 -0
- package/src/daemon/__tests__/conversation-tool-setup.test.ts +74 -0
- package/src/daemon/approval-generators.ts +8 -6
- package/src/daemon/config-watcher.ts +94 -31
- package/src/daemon/conversation-agent-loop-handlers.ts +78 -0
- package/src/daemon/conversation-agent-loop.ts +198 -11
- package/src/daemon/conversation-error.ts +171 -37
- package/src/daemon/conversation-lifecycle.ts +53 -40
- package/src/daemon/conversation-messaging.ts +25 -6
- package/src/daemon/conversation-process.ts +49 -12
- package/src/daemon/conversation-runtime-assembly.ts +25 -1
- package/src/daemon/conversation-slash.ts +12 -5
- package/src/daemon/conversation-store.ts +11 -4
- package/src/daemon/conversation-tool-setup.ts +39 -7
- package/src/daemon/conversation.ts +33 -8
- package/src/daemon/date-context.ts +40 -0
- package/src/daemon/external-plugins-bootstrap.ts +217 -181
- package/src/daemon/first-greeting.ts +22 -2
- package/src/daemon/guardian-action-generators.ts +1 -125
- package/src/daemon/handlers/__tests__/config-a2a-complete.test.ts +248 -0
- package/src/daemon/handlers/__tests__/config-a2a-invite.test.ts +154 -0
- package/src/daemon/handlers/__tests__/config-a2a-redeem.test.ts +133 -0
- package/src/daemon/handlers/__tests__/config-a2a.test.ts +95 -0
- package/src/daemon/handlers/config-a2a.ts +289 -0
- package/src/daemon/handlers/config-model.ts +6 -5
- package/src/daemon/handlers/config-slack-channel.ts +15 -3
- package/src/daemon/handlers/conversations.ts +1 -0
- package/src/daemon/handlers/shared.ts +14 -5
- package/src/daemon/handlers/skills.ts +111 -108
- package/src/daemon/history-repair.ts +28 -1
- package/src/daemon/host-app-control-proxy.ts +153 -27
- package/src/daemon/host-proxy-preactivation.ts +85 -18
- package/src/daemon/lifecycle.ts +89 -91
- package/src/daemon/meet-host-supervisor.ts +5 -4
- package/src/daemon/memory-v2-startup.ts +85 -0
- package/src/daemon/message-protocol.ts +1 -0
- package/src/daemon/message-types/conversations.ts +25 -0
- package/src/daemon/message-types/messages.ts +61 -0
- package/src/daemon/message-types/notifications.ts +21 -0
- package/src/daemon/message-types/subagents.ts +1 -0
- package/src/daemon/message-types/sync.ts +1 -0
- package/src/daemon/pkb-reminder-builder.test.ts +11 -54
- package/src/daemon/pkb-reminder-builder.ts +5 -20
- package/src/daemon/plugin-source-watcher.ts +146 -0
- package/src/daemon/process-message.ts +24 -3
- package/src/daemon/server.ts +11 -2
- package/src/daemon/skill-memory-refresh.ts +33 -0
- package/src/daemon/wake-target-adapter.ts +2 -0
- package/src/documents/document-store.ts +221 -3
- package/src/embedded/plugin-api.ts +40 -0
- package/src/export/__tests__/transcript-formatter.test.ts +121 -0
- package/src/export/transcript-formatter.ts +54 -20
- package/src/filing/filing-service.ts +39 -0
- package/src/heartbeat/__tests__/heartbeat-service.test.ts +135 -6
- package/src/heartbeat/heartbeat-run-store.ts +2 -1
- package/src/heartbeat/heartbeat-service.ts +73 -189
- package/src/home/__tests__/feed-types.test.ts +80 -0
- package/src/home/feed-types.ts +36 -2
- package/src/home/post-connect-feed.ts +1 -0
- package/src/index.ts +18 -1
- package/src/ipc/cli-client.ts +147 -45
- package/src/live-voice/__tests__/live-voice-stt.test.ts +57 -0
- package/src/mcp/client.ts +20 -4
- package/src/media/image-credentials.ts +3 -3
- package/src/memory/__tests__/bookmark-crud.test.ts +33 -27
- package/src/memory/__tests__/conversation-queries.test.ts +483 -0
- package/src/memory/__tests__/jobs-worker-v2-graph-trigger-embed.test.ts +113 -0
- package/src/memory/__tests__/memory-retrospective-enqueue.test.ts +2 -50
- package/src/memory/__tests__/memory-retrospective-job.test.ts +87 -4
- package/src/memory/__tests__/memory-retrospective-startup-cleanup.test.ts +119 -14
- package/src/memory/__tests__/message-content.test.ts +35 -0
- package/src/memory/bookmark-crud.ts +42 -10
- package/src/memory/context-search/sources/conversations.ts +62 -2
- package/src/memory/context-search/sources/workspace.ts +4 -0
- package/src/memory/conversation-crud.ts +63 -19
- package/src/memory/conversation-queries.ts +197 -11
- package/src/memory/conversation-title-service.ts +26 -4
- package/src/memory/db-init.ts +12 -0
- package/src/memory/delivery-crud.ts +152 -5
- package/src/memory/embedding-backend.ts +4 -4
- package/src/memory/external-conversation-store.ts +66 -5
- package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +150 -12
- package/src/memory/graph/conversation-graph-memory.ts +49 -21
- package/src/memory/graph/tools.ts +9 -40
- package/src/memory/indexer.ts +34 -29
- package/src/memory/invite-store.ts +53 -0
- package/src/memory/jobs/__tests__/embed-concept-page.test.ts +73 -0
- package/src/memory/jobs/embed-concept-page.ts +20 -11
- package/src/memory/jobs-worker.ts +6 -1
- package/src/memory/llm-request-log-source-clickhouse.ts +24 -12
- package/src/memory/llm-request-log-source.ts +19 -52
- package/src/memory/llm-request-log-store.ts +92 -1
- package/src/memory/llm-usage-store.ts +125 -5
- package/src/memory/memory-retrospective-enqueue.ts +1 -20
- package/src/memory/memory-retrospective-job.ts +33 -6
- package/src/memory/memory-retrospective-startup-cleanup.ts +72 -5
- package/src/memory/message-content.ts +1 -1
- package/src/memory/migrations/109-external-conversation-bindings.ts +15 -4
- package/src/memory/migrations/229-delete-private-conversations.test.ts +38 -1
- package/src/memory/migrations/229-delete-private-conversations.ts +7 -0
- package/src/memory/migrations/247-external-conversation-binding-thread-id.ts +78 -0
- package/src/memory/migrations/248-create-onboarding-events.ts +21 -0
- package/src/memory/migrations/249-normalize-slack-external-content.ts +240 -0
- package/src/memory/migrations/250-provider-connection-base-url-and-models.ts +28 -0
- package/src/memory/migrations/251-a2a-tasks.ts +49 -0
- package/src/memory/migrations/252-llm-request-log-agent-loop-exit-reason.ts +32 -0
- package/src/memory/migrations/index.ts +9 -0
- package/src/memory/migrations/registry.ts +16 -0
- package/src/memory/onboarding-events-store.ts +106 -0
- package/src/memory/schema/a2a.ts +15 -0
- package/src/memory/schema/bookmarks.ts +0 -2
- package/src/memory/schema/calls.ts +1 -0
- package/src/memory/schema/index.ts +1 -0
- package/src/memory/schema/inference.ts +3 -3
- package/src/memory/schema/infrastructure.ts +13 -0
- package/src/memory/turn-events-store.ts +127 -2
- package/src/memory/v2/__tests__/activation-store.test.ts +25 -23
- package/src/memory/v2/__tests__/activation.test.ts +0 -8
- package/src/memory/v2/__tests__/cli-command-store.test.ts +404 -0
- package/src/memory/v2/__tests__/frontmatter-sweep.test.ts +25 -4
- package/src/memory/v2/__tests__/injection.test.ts +288 -11
- package/src/memory/v2/__tests__/migration.test.ts +87 -0
- package/src/memory/v2/__tests__/page-index.test.ts +83 -0
- package/src/memory/v2/__tests__/prompts-router.test.ts +58 -6
- package/src/memory/v2/__tests__/qdrant.test.ts +66 -3
- package/src/memory/v2/__tests__/router.test.ts +15 -0
- package/src/memory/v2/__tests__/skill-store.test.ts +387 -8
- package/src/memory/v2/__tests__/static-context.test.ts +12 -1
- package/src/memory/v2/activation-store.ts +14 -16
- package/src/memory/v2/cli-command-content.ts +19 -0
- package/src/memory/v2/cli-command-store.ts +304 -0
- package/src/memory/v2/frontmatter-sweep.ts +7 -1
- package/src/memory/v2/injection.ts +81 -26
- package/src/memory/v2/migration.ts +49 -19
- package/src/memory/v2/page-index.ts +63 -8
- package/src/memory/v2/prompts/router.ts +11 -8
- package/src/memory/v2/prompts/sweep.ts +2 -2
- package/src/memory/v2/qdrant.ts +135 -7
- package/src/memory/v2/router.ts +9 -8
- package/src/memory/v2/skill-store.ts +120 -35
- package/src/memory/v2/static-context.ts +4 -4
- package/src/memory/v2/types.ts +23 -0
- package/src/messaging/providers/a2a/__tests__/deliver.test.ts +274 -0
- package/src/messaging/providers/a2a/deliver.ts +156 -0
- package/src/messaging/providers/gmail/client.ts +9 -2
- package/src/messaging/providers/index.ts +11 -2
- package/src/messaging/providers/slack/__tests__/adapter-token-routing.test.ts +45 -5
- package/src/messaging/providers/slack/__tests__/download.test.ts +231 -0
- package/src/messaging/providers/slack/adapter.ts +43 -5
- package/src/messaging/providers/slack/client.ts +27 -0
- package/src/messaging/providers/slack/deep-link.ts +65 -0
- package/src/messaging/providers/slack/download.ts +104 -0
- package/src/messaging/providers/slack/message-metadata.test.ts +32 -0
- package/src/messaging/providers/slack/message-metadata.ts +27 -0
- package/src/messaging/providers/slack/render-transcript.test.ts +134 -0
- package/src/messaging/providers/slack/render-transcript.ts +69 -5
- package/src/messaging/providers/slack/types.ts +20 -1
- package/src/notifications/__tests__/broadcaster.test.ts +203 -0
- package/src/notifications/__tests__/decision-engine.test.ts +283 -0
- package/src/notifications/__tests__/deterministic-checks.test.ts +286 -0
- package/src/notifications/__tests__/emit-signal-home-feed.test.ts +1 -0
- package/src/notifications/__tests__/home-feed-side-effect.test.ts +430 -7
- package/src/notifications/adapters/macos.ts +12 -2
- package/src/notifications/broadcaster.ts +29 -4
- package/src/notifications/conversation-pairing.ts +2 -1
- package/src/notifications/copy-composer.ts +17 -64
- package/src/notifications/decision-engine.ts +113 -45
- package/src/notifications/deterministic-checks.ts +96 -0
- package/src/notifications/emit-signal.ts +21 -1
- package/src/notifications/home-feed-side-effect.ts +138 -5
- package/src/notifications/signal.ts +3 -5
- package/src/notifications/types.ts +8 -0
- package/src/oauth/connection-resolver.ts +8 -4
- package/src/oauth/platform-connection.test.ts +43 -3
- package/src/oauth/platform-connection.ts +19 -6
- package/src/oauth/seed-providers.ts +10 -1
- package/src/permissions/checker.ts +2 -0
- package/src/permissions/ipc-risk-types.ts +1 -0
- package/src/permissions/question-prompter.test.ts +416 -0
- package/src/permissions/question-prompter.ts +294 -0
- package/src/platform/client.test.ts +1 -1
- package/src/platform/client.ts +1 -1
- package/src/plugin-api/constants.ts +26 -0
- package/src/plugin-api/index.ts +34 -1
- package/src/plugin-api/types.ts +104 -22
- package/src/plugins/defaults/circuit-breaker.ts +0 -5
- package/src/plugins/defaults/compaction.ts +0 -4
- package/src/plugins/defaults/empty-response.ts +0 -2
- package/src/plugins/defaults/history-repair.ts +0 -2
- package/src/plugins/defaults/injectors.ts +74 -22
- package/src/plugins/defaults/llm-call.ts +0 -2
- package/src/plugins/defaults/memory-retrieval.ts +0 -1
- package/src/plugins/defaults/overflow-reduce.ts +0 -1
- package/src/plugins/defaults/persistence.ts +0 -2
- package/src/plugins/defaults/title-generate.ts +0 -5
- package/src/plugins/defaults/token-estimate.ts +0 -2
- package/src/plugins/defaults/tool-error.ts +0 -7
- package/src/plugins/defaults/tool-execute.ts +0 -2
- package/src/plugins/defaults/tool-result-truncate.ts +0 -4
- package/src/plugins/ensure-plugin-api-shim.ts +96 -0
- package/src/plugins/external-api.ts +104 -0
- package/src/plugins/external-plugin-loader.ts +187 -42
- package/src/plugins/feature-gate.ts +22 -0
- package/src/plugins/pipeline.ts +37 -0
- package/src/plugins/registry.ts +48 -80
- package/src/plugins/types.ts +40 -26
- package/src/plugins/user-loader.ts +21 -2
- package/src/proactive-artifact/aux-message-injector.ts +11 -0
- package/src/proactive-artifact/job.test.ts +37 -5
- package/src/prompts/__tests__/system-prompt.test.ts +10 -43
- package/src/prompts/__tests__/task-progress-hint-section.test.ts +95 -0
- package/src/prompts/normalize-onboarding.ts +27 -0
- package/src/prompts/sections.ts +302 -0
- package/src/prompts/system-prompt.ts +63 -174
- package/src/prompts/templates/BOOTSTRAP.md +17 -1
- package/src/prompts/templates/system-sections.ts +164 -0
- package/src/providers/__tests__/inference.test.ts +24 -7
- package/src/providers/anthropic/client.ts +28 -28
- package/src/providers/call-site-routing.ts +24 -6
- package/src/providers/connection-resolution.ts +68 -11
- package/src/providers/inference/__tests__/adapter-factory-openai-compatible.test.ts +74 -0
- package/src/providers/inference/__tests__/connections-openai-compatible.test.ts +175 -0
- package/src/providers/inference/__tests__/connections-status-label.test.ts +15 -0
- package/src/providers/inference/adapter-factory.ts +32 -6
- package/src/providers/inference/auth.ts +12 -0
- package/src/providers/inference/backfill.ts +14 -1
- package/src/providers/inference/connections.ts +159 -34
- package/src/providers/inference/resolve-auth.ts +14 -4
- package/src/providers/model-catalog.ts +249 -12
- package/src/providers/model-intents.ts +3 -3
- package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +235 -0
- package/src/providers/openai/chat-completions-provider.ts +169 -8
- package/src/providers/openrouter/client.ts +49 -4
- package/src/providers/{managed-proxy → platform-proxy}/constants.ts +4 -2
- package/src/providers/{managed-proxy → platform-proxy}/context.ts +3 -3
- package/src/providers/provider-availability.ts +17 -2
- package/src/providers/provider-catalog-visibility.ts +38 -0
- package/src/providers/provider-send-message.ts +27 -12
- package/src/providers/registry.ts +52 -15
- package/src/providers/retry.ts +47 -1
- package/src/runtime/__tests__/agent-wake.test.ts +152 -0
- package/src/runtime/agent-wake.ts +103 -15
- package/src/runtime/auth/route-policy.ts +21 -1
- package/src/runtime/btw-sidechain.ts +2 -0
- package/src/runtime/http-server.ts +7 -16
- package/src/runtime/http-types.ts +19 -47
- package/src/runtime/migrations/origin-mode.ts +1 -1
- package/src/runtime/pending-interactions.ts +1 -0
- package/src/runtime/routes/__tests__/bookmark-routes.test.ts +17 -0
- package/src/runtime/routes/__tests__/consolidation-routes.test.ts +258 -0
- package/src/runtime/routes/__tests__/conversation-management-routes.test.ts +5 -1
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +172 -23
- package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +275 -44
- package/src/runtime/routes/__tests__/llm-call-sites-routes.test.ts +12 -0
- package/src/runtime/routes/__tests__/question-routes.test.ts +395 -0
- package/src/runtime/routes/__tests__/tts-routes.test.ts +64 -1
- package/src/runtime/routes/acp-routes-list.test.ts +143 -0
- package/src/runtime/routes/acp-routes.ts +5 -3
- package/src/runtime/routes/auth-routes.ts +1 -1
- package/src/runtime/routes/bookmark-routes.ts +5 -3
- package/src/runtime/routes/btw-routes.ts +5 -1
- package/src/runtime/routes/channel-availability-routes.ts +126 -0
- package/src/runtime/routes/consolidation-routes.ts +100 -0
- package/src/runtime/routes/conversation-cli-routes.ts +44 -3
- package/src/runtime/routes/conversation-list-routes.ts +3 -20
- package/src/runtime/routes/conversation-management-routes.ts +17 -42
- package/src/runtime/routes/conversation-query-routes.ts +99 -35
- package/src/runtime/routes/conversation-routes.ts +97 -11
- package/src/runtime/routes/documents-routes.ts +25 -86
- package/src/runtime/routes/group-routes.ts +5 -0
- package/src/runtime/routes/inbound-conversation.ts +28 -8
- package/src/runtime/routes/inbound-message-handler.ts +236 -41
- package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +111 -0
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +32 -1
- package/src/runtime/routes/inbound-stages/edit-intercept.ts +17 -4
- package/src/runtime/routes/index.ts +8 -0
- package/src/runtime/routes/inference-profile-session-handler.ts +17 -44
- package/src/runtime/routes/inference-profile-session-reaper.ts +7 -21
- package/src/runtime/routes/inference-provider-connection-routes.ts +199 -22
- package/src/runtime/routes/integrations/a2a.ts +235 -0
- package/src/runtime/routes/integrations/slack/share.ts +4 -52
- package/src/runtime/routes/integrations/slack/token.ts +43 -0
- package/src/runtime/routes/integrations/twilio.ts +6 -13
- package/src/runtime/routes/llm-call-sites-routes.ts +11 -1
- package/src/runtime/routes/notification-routes.ts +1 -1
- package/src/runtime/routes/oauth-commands-routes.ts +105 -15
- package/src/runtime/routes/oauth-lifecycle-routes.ts +43 -0
- package/src/runtime/routes/question-routes.ts +259 -0
- package/src/runtime/routes/rename-conversation-routes.ts +2 -33
- package/src/runtime/routes/schedule-routes.ts +4 -7
- package/src/runtime/routes/subagents-routes.ts +98 -18
- package/src/runtime/routes/telemetry-routes.ts +27 -0
- package/src/runtime/routes/tts-routes.ts +27 -2
- package/src/runtime/routes/workspace-routes.test.ts +43 -0
- package/src/runtime/routes/workspace-routes.ts +28 -0
- package/src/runtime/services/conversation-serializer.ts +39 -7
- package/src/runtime/sync/resource-sync-events.ts +93 -1
- package/src/schedule/schedule-store.ts +27 -2
- package/src/schedule/scheduler.ts +9 -1
- package/src/security/__tests__/untrusted-content.test.ts +86 -0
- package/src/security/untrusted-content.ts +93 -8
- package/src/skills/catalog-files.ts +1 -1
- package/src/skills/catalog-install.ts +233 -116
- package/src/skills/clawhub.ts +70 -13
- package/src/skills/managed-store.ts +4 -119
- package/src/skills/skillssh-registry.ts +27 -48
- package/src/subagent/manager.ts +17 -7
- package/src/telemetry/types.ts +113 -1
- package/src/telemetry/usage-telemetry-reporter.test.ts +312 -5
- package/src/telemetry/usage-telemetry-reporter.ts +113 -7
- package/src/tools/apps/executors.ts +58 -7
- package/src/tools/ask-question/ask-question-tool.test.ts +509 -0
- package/src/tools/ask-question/ask-question-tool.ts +304 -0
- package/src/tools/browser/browser-execution.ts +15 -11
- package/src/tools/computer-use/definitions.ts +3 -3
- package/src/tools/credentials/vault.ts +1 -1
- package/src/tools/document/document-tool.ts +124 -1
- package/src/tools/filesystem/edit.ts +1 -1
- package/src/tools/filesystem/list.ts +1 -1
- package/src/tools/filesystem/read.ts +1 -1
- package/src/tools/filesystem/write.ts +5 -2
- package/src/tools/host-filesystem/transfer.ts +1 -1
- package/src/tools/host-terminal/host-shell.ts +1 -1
- package/src/tools/memory/register.ts +1 -9
- package/src/tools/permission-checker.ts +1 -1
- package/src/tools/registry.ts +17 -7
- package/src/tools/schedule/create.ts +2 -2
- package/src/tools/schema-transforms.ts +7 -2
- package/src/tools/side-effects.ts +1 -0
- package/src/tools/skills/delete-managed.ts +4 -4
- package/src/tools/skills/execute.ts +1 -1
- package/src/tools/skills/scaffold-managed.ts +3 -2
- package/src/tools/subagent/notify-parent.ts +1 -1
- package/src/tools/system/request-permission.ts +2 -2
- package/src/tools/terminal/safe-env.ts +60 -1
- package/src/tools/tool-manifest.ts +2 -0
- package/src/tools/types.ts +107 -21
- package/src/tools/ui-surface/definitions.ts +6 -5
- package/src/tts/__tests__/provider-adapters.test.ts +76 -2
- package/src/tts/providers/elevenlabs-provider.ts +75 -1
- package/src/types/onboarding-context.ts +2 -0
- package/src/util/errors.ts +17 -0
- package/src/util/platform.ts +10 -0
- package/src/watcher/__tests__/engine.test.ts +22 -0
- package/src/watcher/engine.ts +6 -2
- package/src/workspace/migrations/057-repair-stale-gemini-model-ids.ts +80 -15
- package/src/workspace/migrations/072-seed-reply-suggestion-callsite.ts +35 -22
- package/src/workspace/migrations/073-repair-recall-callsite-empty-profile.ts +3 -1
- package/src/workspace/migrations/083-system-prompt-prefix-to-file.ts +191 -0
- package/src/workspace/migrations/084-remove-legacy-skills-index.ts +276 -0
- package/src/workspace/migrations/085-memory-v2-bm25-b-reembed-disabled-v2-pages.ts +137 -0
- package/src/workspace/migrations/086-revert-stale-gemini-mis-rewrites.ts +198 -0
- package/src/workspace/migrations/087-memory-router-balanced-profile.ts +91 -0
- package/src/workspace/migrations/registry.ts +10 -0
- package/src/workspace/migrations/runner.ts +39 -9
- package/src/workspace/migrations/types.ts +4 -0
- package/examples/plugins/echo/bun.lock +0 -25
- package/src/__tests__/context-window-manager.test.ts +0 -2481
- package/src/__tests__/guardian-action-conversation-turn.test.ts +0 -441
- package/src/context/__tests__/compact-prompt.test.ts +0 -63
- package/src/context/prompts/compact.md +0 -26
- package/src/memory/graph/__tests__/remember-description.test.ts +0 -55
- package/src/prompts/__tests__/build-cli-reference-section.test.ts +0 -37
- package/src/runtime/guardian-action-conversation-turn.ts +0 -99
|
@@ -5,11 +5,17 @@
|
|
|
5
5
|
* finding messages by source identifiers, and managing raw payload storage.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import { and, eq, isNotNull } from "drizzle-orm";
|
|
8
|
+
import { and, eq, isNotNull, or } from "drizzle-orm";
|
|
9
9
|
import { v4 as uuid } from "uuid";
|
|
10
10
|
|
|
11
|
+
import { readSlackMetadataFromMessageMetadata } from "../messaging/providers/slack/message-metadata.js";
|
|
11
12
|
import { DAEMON_INTERNAL_ASSISTANT_ID } from "../runtime/assistant-scope.js";
|
|
12
|
-
import {
|
|
13
|
+
import { selectSlackMetaCandidateMetadata } from "./conversation-crud.js";
|
|
14
|
+
import {
|
|
15
|
+
getConversationByKey,
|
|
16
|
+
getOrCreateConversation,
|
|
17
|
+
setConversationKeyIfAbsent,
|
|
18
|
+
} from "./conversation-key-store.js";
|
|
13
19
|
import { getDb } from "./db-connection.js";
|
|
14
20
|
import { channelInboundEvents, conversations } from "./schema.js";
|
|
15
21
|
|
|
@@ -23,6 +29,144 @@ export interface InboundResult {
|
|
|
23
29
|
export interface RecordInboundOptions {
|
|
24
30
|
sourceMessageId?: string;
|
|
25
31
|
assistantId?: string;
|
|
32
|
+
sourceThreadId?: string;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const SLACK_LEGACY_THREAD_EVIDENCE_BATCH_SIZE = 50;
|
|
36
|
+
const SLACK_LEGACY_THREAD_EVIDENCE_MAX_SCAN = 500;
|
|
37
|
+
|
|
38
|
+
function buildScopedConversationKeyForAssistant(
|
|
39
|
+
assistantId: string,
|
|
40
|
+
sourceChannel: string,
|
|
41
|
+
externalChatId: string,
|
|
42
|
+
sourceThreadId?: string | null,
|
|
43
|
+
): string {
|
|
44
|
+
const threadId = sourceThreadId?.trim();
|
|
45
|
+
if (sourceChannel === "slack" && threadId) {
|
|
46
|
+
return `asst:${assistantId}:${sourceChannel}:${externalChatId}:thread:${threadId}`;
|
|
47
|
+
}
|
|
48
|
+
return `asst:${assistantId}:${sourceChannel}:${externalChatId}`;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function buildScopedConversationKey(
|
|
52
|
+
sourceChannel: string,
|
|
53
|
+
externalChatId: string,
|
|
54
|
+
sourceThreadId?: string | null,
|
|
55
|
+
): string {
|
|
56
|
+
return buildScopedConversationKeyForAssistant(
|
|
57
|
+
DAEMON_INTERNAL_ASSISTANT_ID,
|
|
58
|
+
sourceChannel,
|
|
59
|
+
externalChatId,
|
|
60
|
+
sourceThreadId,
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function legacySlackConversationHasThreadEvidence(
|
|
65
|
+
conversationId: string,
|
|
66
|
+
externalChatId: string,
|
|
67
|
+
sourceThreadId: string,
|
|
68
|
+
): boolean {
|
|
69
|
+
const db = getDb();
|
|
70
|
+
const inboundEvidence = db
|
|
71
|
+
.select({ id: channelInboundEvents.id })
|
|
72
|
+
.from(channelInboundEvents)
|
|
73
|
+
.where(
|
|
74
|
+
and(
|
|
75
|
+
eq(channelInboundEvents.conversationId, conversationId),
|
|
76
|
+
eq(channelInboundEvents.sourceChannel, "slack"),
|
|
77
|
+
eq(channelInboundEvents.externalChatId, externalChatId),
|
|
78
|
+
or(
|
|
79
|
+
eq(channelInboundEvents.sourceMessageId, sourceThreadId),
|
|
80
|
+
eq(channelInboundEvents.externalMessageId, sourceThreadId),
|
|
81
|
+
),
|
|
82
|
+
),
|
|
83
|
+
)
|
|
84
|
+
.get();
|
|
85
|
+
|
|
86
|
+
if (inboundEvidence) {
|
|
87
|
+
return true;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
let offset = 0;
|
|
91
|
+
while (offset < SLACK_LEGACY_THREAD_EVIDENCE_MAX_SCAN) {
|
|
92
|
+
const remaining = SLACK_LEGACY_THREAD_EVIDENCE_MAX_SCAN - offset;
|
|
93
|
+
const batchLimit = Math.min(
|
|
94
|
+
SLACK_LEGACY_THREAD_EVIDENCE_BATCH_SIZE,
|
|
95
|
+
remaining,
|
|
96
|
+
);
|
|
97
|
+
const metadataRows = selectSlackMetaCandidateMetadata(
|
|
98
|
+
conversationId,
|
|
99
|
+
batchLimit,
|
|
100
|
+
offset,
|
|
101
|
+
{ includeFlatLegacy: true },
|
|
102
|
+
);
|
|
103
|
+
|
|
104
|
+
if (metadataRows.length === 0) return false;
|
|
105
|
+
for (const metadata of metadataRows) {
|
|
106
|
+
const slackMeta = readSlackMetadataFromMessageMetadata(metadata, {
|
|
107
|
+
allowFlatLegacy: true,
|
|
108
|
+
});
|
|
109
|
+
if (
|
|
110
|
+
slackMeta?.channelId === externalChatId &&
|
|
111
|
+
slackMeta.threadTs === sourceThreadId
|
|
112
|
+
) {
|
|
113
|
+
return true;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (metadataRows.length < batchLimit) return false;
|
|
118
|
+
offset += metadataRows.length;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return false;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function resolveInboundConversation(
|
|
125
|
+
assistantId: string,
|
|
126
|
+
sourceChannel: string,
|
|
127
|
+
externalChatId: string,
|
|
128
|
+
sourceThreadId?: string | null,
|
|
129
|
+
): { conversationId: string } {
|
|
130
|
+
const threadedKey = buildScopedConversationKeyForAssistant(
|
|
131
|
+
assistantId,
|
|
132
|
+
sourceChannel,
|
|
133
|
+
externalChatId,
|
|
134
|
+
sourceThreadId,
|
|
135
|
+
);
|
|
136
|
+
|
|
137
|
+
const threadId = sourceThreadId?.trim();
|
|
138
|
+
if (sourceChannel !== "slack" || !threadId) {
|
|
139
|
+
return getOrCreateConversation(threadedKey);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const threadedMapping = getConversationByKey(threadedKey);
|
|
143
|
+
if (threadedMapping) {
|
|
144
|
+
return { conversationId: threadedMapping.conversationId };
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const legacyKey = buildScopedConversationKeyForAssistant(
|
|
148
|
+
assistantId,
|
|
149
|
+
sourceChannel,
|
|
150
|
+
externalChatId,
|
|
151
|
+
null,
|
|
152
|
+
);
|
|
153
|
+
const legacyMapping = getConversationByKey(legacyKey);
|
|
154
|
+
if (
|
|
155
|
+
legacyMapping &&
|
|
156
|
+
legacySlackConversationHasThreadEvidence(
|
|
157
|
+
legacyMapping.conversationId,
|
|
158
|
+
externalChatId,
|
|
159
|
+
threadId,
|
|
160
|
+
)
|
|
161
|
+
) {
|
|
162
|
+
setConversationKeyIfAbsent(threadedKey, legacyMapping.conversationId);
|
|
163
|
+
const aliasedMapping = getConversationByKey(threadedKey);
|
|
164
|
+
if (aliasedMapping) {
|
|
165
|
+
return { conversationId: aliasedMapping.conversationId };
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
return getOrCreateConversation(threadedKey);
|
|
26
170
|
}
|
|
27
171
|
|
|
28
172
|
/**
|
|
@@ -62,8 +206,12 @@ export function recordInbound(
|
|
|
62
206
|
}
|
|
63
207
|
|
|
64
208
|
const assistantId = options?.assistantId ?? DAEMON_INTERNAL_ASSISTANT_ID;
|
|
65
|
-
const
|
|
66
|
-
|
|
209
|
+
const mapping = resolveInboundConversation(
|
|
210
|
+
assistantId,
|
|
211
|
+
sourceChannel,
|
|
212
|
+
externalChatId,
|
|
213
|
+
options?.sourceThreadId,
|
|
214
|
+
);
|
|
67
215
|
const now = Date.now();
|
|
68
216
|
const eventId = uuid();
|
|
69
217
|
|
|
@@ -176,4 +324,3 @@ export function clearPayload(eventId: string): void {
|
|
|
176
324
|
.where(eq(channelInboundEvents.id, eventId))
|
|
177
325
|
.run();
|
|
178
326
|
}
|
|
179
|
-
|
|
@@ -4,8 +4,8 @@ import { isAssistantFeatureFlagEnabled } from "../config/assistant-feature-flags
|
|
|
4
4
|
import { getOllamaBaseUrlEnv } from "../config/env.js";
|
|
5
5
|
import { resolveCallSiteConfig } from "../config/llm-resolver.js";
|
|
6
6
|
import type { AssistantConfig } from "../config/types.js";
|
|
7
|
-
import {
|
|
8
|
-
import { resolveManagedProxyContext } from "../providers/
|
|
7
|
+
import { PLATFORM_PROVIDER_META } from "../providers/platform-proxy/constants.js";
|
|
8
|
+
import { resolveManagedProxyContext } from "../providers/platform-proxy/context.js";
|
|
9
9
|
import { getProviderKeyAsync } from "../security/secure-keys.js";
|
|
10
10
|
import { getLogger } from "../util/logger.js";
|
|
11
11
|
import { GeminiEmbeddingBackend } from "./embedding-gemini.js";
|
|
@@ -311,7 +311,7 @@ export async function selectEmbeddingBackend(
|
|
|
311
311
|
) {
|
|
312
312
|
const proxyCtx = await resolveManagedProxyContext();
|
|
313
313
|
if (proxyCtx.enabled) {
|
|
314
|
-
const meta =
|
|
314
|
+
const meta = PLATFORM_PROVIDER_META["gemini"];
|
|
315
315
|
if (meta?.managed && meta.proxyPath) {
|
|
316
316
|
const managedBaseUrl = `${proxyCtx.platformBaseUrl}${meta.proxyPath}`;
|
|
317
317
|
const managedModel = config.memory.embeddings.geminiModel;
|
|
@@ -685,7 +685,7 @@ async function selectFallbackBackends(
|
|
|
685
685
|
) {
|
|
686
686
|
// Try managed proxy Gemini as fallback when no direct key exists.
|
|
687
687
|
const proxyCtx = await resolveManagedProxyContext();
|
|
688
|
-
const meta =
|
|
688
|
+
const meta = PLATFORM_PROVIDER_META["gemini"];
|
|
689
689
|
if (proxyCtx.enabled && meta?.managed && meta.proxyPath) {
|
|
690
690
|
const managedBaseUrl = `${proxyCtx.platformBaseUrl}${meta.proxyPath}`;
|
|
691
691
|
const managedModel = config.memory.embeddings.geminiModel;
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* list APIs.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
import { and, eq, inArray } from "drizzle-orm";
|
|
10
|
+
import { and, eq, inArray, isNull } from "drizzle-orm";
|
|
11
11
|
|
|
12
12
|
import { getDb } from "./db-connection.js";
|
|
13
13
|
import { externalConversationBindings } from "./schema.js";
|
|
@@ -16,6 +16,7 @@ export interface ExternalConversationBinding {
|
|
|
16
16
|
conversationId: string;
|
|
17
17
|
sourceChannel: string;
|
|
18
18
|
externalChatId: string;
|
|
19
|
+
externalThreadId?: string | null;
|
|
19
20
|
externalUserId?: string | null;
|
|
20
21
|
displayName?: string | null;
|
|
21
22
|
username?: string | null;
|
|
@@ -29,11 +30,19 @@ export interface UpsertBindingInput {
|
|
|
29
30
|
conversationId: string;
|
|
30
31
|
sourceChannel: string;
|
|
31
32
|
externalChatId: string;
|
|
33
|
+
externalThreadId?: string | null;
|
|
32
34
|
externalUserId?: string | null;
|
|
33
35
|
displayName?: string | null;
|
|
34
36
|
username?: string | null;
|
|
35
37
|
}
|
|
36
38
|
|
|
39
|
+
function normalizeExternalThreadId(
|
|
40
|
+
externalThreadId?: string | null,
|
|
41
|
+
): string | null {
|
|
42
|
+
const trimmed = externalThreadId?.trim();
|
|
43
|
+
return trimmed ? trimmed : null;
|
|
44
|
+
}
|
|
45
|
+
|
|
37
46
|
/**
|
|
38
47
|
* Insert or update an external conversation binding on conflict (conversationId).
|
|
39
48
|
* On conflict, updates channel metadata and timestamps.
|
|
@@ -41,12 +50,14 @@ export interface UpsertBindingInput {
|
|
|
41
50
|
export function upsertBinding(input: UpsertBindingInput): void {
|
|
42
51
|
const db = getDb();
|
|
43
52
|
const now = Date.now();
|
|
53
|
+
const externalThreadId = normalizeExternalThreadId(input.externalThreadId);
|
|
44
54
|
|
|
45
|
-
// If a stale binding exists for this
|
|
55
|
+
// If a stale binding exists for this channel/chat/thread tuple under a
|
|
46
56
|
// different conversationId, remove it first so the unique index is not violated.
|
|
47
|
-
const existing =
|
|
57
|
+
const existing = getBindingByChannelChatThread(
|
|
48
58
|
input.sourceChannel,
|
|
49
59
|
input.externalChatId,
|
|
60
|
+
externalThreadId,
|
|
50
61
|
);
|
|
51
62
|
if (existing && existing.conversationId !== input.conversationId) {
|
|
52
63
|
db.delete(externalConversationBindings)
|
|
@@ -64,6 +75,7 @@ export function upsertBinding(input: UpsertBindingInput): void {
|
|
|
64
75
|
conversationId: input.conversationId,
|
|
65
76
|
sourceChannel: input.sourceChannel,
|
|
66
77
|
externalChatId: input.externalChatId,
|
|
78
|
+
externalThreadId,
|
|
67
79
|
externalUserId: input.externalUserId ?? null,
|
|
68
80
|
displayName: input.displayName ?? null,
|
|
69
81
|
username: input.username ?? null,
|
|
@@ -76,6 +88,7 @@ export function upsertBinding(input: UpsertBindingInput): void {
|
|
|
76
88
|
set: {
|
|
77
89
|
sourceChannel: input.sourceChannel,
|
|
78
90
|
externalChatId: input.externalChatId,
|
|
91
|
+
externalThreadId,
|
|
79
92
|
externalUserId: input.externalUserId ?? null,
|
|
80
93
|
displayName: input.displayName ?? null,
|
|
81
94
|
username: input.username ?? null,
|
|
@@ -95,15 +108,18 @@ export function upsertOutboundBinding(input: {
|
|
|
95
108
|
conversationId: string;
|
|
96
109
|
sourceChannel: string;
|
|
97
110
|
externalChatId: string;
|
|
111
|
+
externalThreadId?: string | null;
|
|
98
112
|
}): void {
|
|
99
113
|
const db = getDb();
|
|
100
114
|
const now = Date.now();
|
|
115
|
+
const externalThreadId = normalizeExternalThreadId(input.externalThreadId);
|
|
101
116
|
|
|
102
|
-
// If a stale binding exists for this
|
|
117
|
+
// If a stale binding exists for this channel/chat/thread tuple under a
|
|
103
118
|
// different conversationId, remove it first so the unique index is not violated.
|
|
104
|
-
const existing =
|
|
119
|
+
const existing = getBindingByChannelChatThread(
|
|
105
120
|
input.sourceChannel,
|
|
106
121
|
input.externalChatId,
|
|
122
|
+
externalThreadId,
|
|
107
123
|
);
|
|
108
124
|
if (existing && existing.conversationId !== input.conversationId) {
|
|
109
125
|
db.delete(externalConversationBindings)
|
|
@@ -121,6 +137,7 @@ export function upsertOutboundBinding(input: {
|
|
|
121
137
|
conversationId: input.conversationId,
|
|
122
138
|
sourceChannel: input.sourceChannel,
|
|
123
139
|
externalChatId: input.externalChatId,
|
|
140
|
+
externalThreadId,
|
|
124
141
|
externalUserId: null,
|
|
125
142
|
displayName: null,
|
|
126
143
|
username: null,
|
|
@@ -133,6 +150,7 @@ export function upsertOutboundBinding(input: {
|
|
|
133
150
|
set: {
|
|
134
151
|
sourceChannel: input.sourceChannel,
|
|
135
152
|
externalChatId: input.externalChatId,
|
|
153
|
+
externalThreadId,
|
|
136
154
|
updatedAt: now,
|
|
137
155
|
lastOutboundAt: now,
|
|
138
156
|
},
|
|
@@ -161,8 +179,20 @@ export function getBindingByConversation(
|
|
|
161
179
|
export function getBindingByChannelChat(
|
|
162
180
|
sourceChannel: string,
|
|
163
181
|
externalChatId: string,
|
|
182
|
+
): ExternalConversationBinding | null {
|
|
183
|
+
return getBindingByChannelChatThread(sourceChannel, externalChatId, null);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Look up an external binding by channel + external chat ID + optional thread ID.
|
|
188
|
+
*/
|
|
189
|
+
export function getBindingByChannelChatThread(
|
|
190
|
+
sourceChannel: string,
|
|
191
|
+
externalChatId: string,
|
|
192
|
+
externalThreadId?: string | null,
|
|
164
193
|
): ExternalConversationBinding | null {
|
|
165
194
|
const db = getDb();
|
|
195
|
+
const normalizedThreadId = normalizeExternalThreadId(externalThreadId);
|
|
166
196
|
const row = db
|
|
167
197
|
.select()
|
|
168
198
|
.from(externalConversationBindings)
|
|
@@ -170,6 +200,12 @@ export function getBindingByChannelChat(
|
|
|
170
200
|
and(
|
|
171
201
|
eq(externalConversationBindings.sourceChannel, sourceChannel),
|
|
172
202
|
eq(externalConversationBindings.externalChatId, externalChatId),
|
|
203
|
+
normalizedThreadId
|
|
204
|
+
? eq(
|
|
205
|
+
externalConversationBindings.externalThreadId,
|
|
206
|
+
normalizedThreadId,
|
|
207
|
+
)
|
|
208
|
+
: isNull(externalConversationBindings.externalThreadId),
|
|
173
209
|
),
|
|
174
210
|
)
|
|
175
211
|
.get();
|
|
@@ -195,6 +231,31 @@ export function deleteBindingByChannelChat(
|
|
|
195
231
|
.run();
|
|
196
232
|
}
|
|
197
233
|
|
|
234
|
+
/**
|
|
235
|
+
* Remove an external binding by channel + external chat ID + thread ID.
|
|
236
|
+
*/
|
|
237
|
+
export function deleteBindingByChannelChatThread(
|
|
238
|
+
sourceChannel: string,
|
|
239
|
+
externalChatId: string,
|
|
240
|
+
externalThreadId: string,
|
|
241
|
+
): void {
|
|
242
|
+
const db = getDb();
|
|
243
|
+
const normalizedThreadId = normalizeExternalThreadId(externalThreadId);
|
|
244
|
+
if (!normalizedThreadId) {
|
|
245
|
+
deleteBindingByChannelChat(sourceChannel, externalChatId);
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
db.delete(externalConversationBindings)
|
|
249
|
+
.where(
|
|
250
|
+
and(
|
|
251
|
+
eq(externalConversationBindings.sourceChannel, sourceChannel),
|
|
252
|
+
eq(externalConversationBindings.externalChatId, externalChatId),
|
|
253
|
+
eq(externalConversationBindings.externalThreadId, normalizedThreadId),
|
|
254
|
+
),
|
|
255
|
+
)
|
|
256
|
+
.run();
|
|
257
|
+
}
|
|
258
|
+
|
|
198
259
|
/**
|
|
199
260
|
* Get bindings for multiple conversation IDs at once.
|
|
200
261
|
* Returns a map of conversationId -> binding for efficient lookup.
|
|
@@ -107,18 +107,20 @@ mock.module("@qdrant/js-client-rest", () => ({
|
|
|
107
107
|
QdrantClient: MockQdrantClient,
|
|
108
108
|
}));
|
|
109
109
|
|
|
110
|
+
const embedWithBackendMock = mock(async (_config, texts: string[]) => ({
|
|
111
|
+
provider: "local",
|
|
112
|
+
model: "test-model",
|
|
113
|
+
vectors: texts.map(() => [0.1, 0.2, 0.3]) as number[][],
|
|
114
|
+
}));
|
|
115
|
+
const generateSparseEmbeddingMock = mock((_text: string) => ({
|
|
116
|
+
indices: [1, 2, 3],
|
|
117
|
+
values: [0.5, 0.5, 0.5] as number[],
|
|
118
|
+
}));
|
|
110
119
|
const realEmbeddingBackend = await import("../../embedding-backend.js");
|
|
111
120
|
mock.module("../../embedding-backend.js", () => ({
|
|
112
121
|
...realEmbeddingBackend,
|
|
113
|
-
embedWithBackend:
|
|
114
|
-
|
|
115
|
-
model: "test-model",
|
|
116
|
-
vectors: [[0.1, 0.2, 0.3]] as number[][],
|
|
117
|
-
}),
|
|
118
|
-
generateSparseEmbedding: () => ({
|
|
119
|
-
indices: [1, 2, 3],
|
|
120
|
-
values: [0.5, 0.5, 0.5] as number[],
|
|
121
|
-
}),
|
|
122
|
+
embedWithBackend: embedWithBackendMock,
|
|
123
|
+
generateSparseEmbedding: generateSparseEmbeddingMock,
|
|
122
124
|
}));
|
|
123
125
|
|
|
124
126
|
const realQdrantClient = await import("../../qdrant-client.js");
|
|
@@ -175,7 +177,7 @@ const { migrateActivationState } =
|
|
|
175
177
|
await import("../../migrations/232-activation-state.js");
|
|
176
178
|
const schema = await import("../../schema.js");
|
|
177
179
|
const { _resetMemoryV2QdrantForTests } = await import("../../v2/qdrant.js");
|
|
178
|
-
const { hydrate: hydrateActivationState } =
|
|
180
|
+
const { hydrate: hydrateActivationState, save: saveActivationState } =
|
|
179
181
|
await import("../../v2/activation-store.js");
|
|
180
182
|
|
|
181
183
|
// The wiring layer calls `getDb()` to fetch the SQLite handle. We mock
|
|
@@ -213,9 +215,14 @@ function createTestDb(): DrizzleDb {
|
|
|
213
215
|
return db;
|
|
214
216
|
}
|
|
215
217
|
|
|
216
|
-
function makeConfig(v2Enabled: boolean): AssistantConfig {
|
|
218
|
+
function makeConfig(v2Enabled: boolean, memoryEnabled = true): AssistantConfig {
|
|
219
|
+
// Pin `router.enabled: false` so these tests exercise the activation
|
|
220
|
+
// pipeline. Router-mode coverage lives in `memory/v2/__tests__/injection.test.ts`.
|
|
217
221
|
return applyNestedDefaults({
|
|
218
|
-
memory: {
|
|
222
|
+
memory: {
|
|
223
|
+
enabled: memoryEnabled,
|
|
224
|
+
v2: { enabled: v2Enabled, router: { enabled: false } },
|
|
225
|
+
},
|
|
219
226
|
}) as AssistantConfig;
|
|
220
227
|
}
|
|
221
228
|
|
|
@@ -293,6 +300,8 @@ beforeEach(() => {
|
|
|
293
300
|
qdrantState.queryResponses.sparse.length = 0;
|
|
294
301
|
loadContextMemoryMock.mockClear();
|
|
295
302
|
retrieveForTurnMock.mockClear();
|
|
303
|
+
embedWithBackendMock.mockClear();
|
|
304
|
+
generateSparseEmbeddingMock.mockClear();
|
|
296
305
|
_resetMemoryV2QdrantForTests();
|
|
297
306
|
});
|
|
298
307
|
|
|
@@ -390,6 +399,59 @@ describe("ConversationGraphMemory.prepareMemory — v2 routing (per-turn path)",
|
|
|
390
399
|
expect(firstBlock.text).toContain("# memory/concepts/alice-vscode.md");
|
|
391
400
|
});
|
|
392
401
|
|
|
402
|
+
test("per-turn dense embedding is computed from combined assistant+user text", async () => {
|
|
403
|
+
// Short referential follow-ups ("do that one") carry no semantic signal
|
|
404
|
+
// on their own — the dense PKB query embedding must mirror v1's
|
|
405
|
+
// `retrieveForTurn` and combine the prior assistant turn so hint search
|
|
406
|
+
// still resolves what "that one" refers to. The sparse vector matches
|
|
407
|
+
// v1 by using the user message alone so lexical signal isn't diluted.
|
|
408
|
+
stageTurn([{ slug: "alice-vscode", denseScore: 0.9 }]);
|
|
409
|
+
|
|
410
|
+
const memory = makeMemory();
|
|
411
|
+
const config = makeConfig(true);
|
|
412
|
+
const assistantText =
|
|
413
|
+
"Alice prefers VS Code as her editor — she finds the extension ecosystem unmatched.";
|
|
414
|
+
const userText = "do that one";
|
|
415
|
+
const messages: Message[] = [
|
|
416
|
+
{
|
|
417
|
+
role: "user",
|
|
418
|
+
content: [
|
|
419
|
+
{ type: "text" as const, text: "what editors did we cover?" },
|
|
420
|
+
],
|
|
421
|
+
},
|
|
422
|
+
{
|
|
423
|
+
role: "assistant",
|
|
424
|
+
content: [{ type: "text" as const, text: assistantText }],
|
|
425
|
+
},
|
|
426
|
+
{ role: "user", content: [{ type: "text" as const, text: userText }] },
|
|
427
|
+
];
|
|
428
|
+
|
|
429
|
+
await memory.prepareMemory(
|
|
430
|
+
messages,
|
|
431
|
+
config,
|
|
432
|
+
new AbortController().signal,
|
|
433
|
+
noopEvent,
|
|
434
|
+
);
|
|
435
|
+
|
|
436
|
+
// v1's `retrieveForTurn` joins assistantLast + userLast with "\n\n" and
|
|
437
|
+
// embeds the combined string as the dense query vector. Assert the v2
|
|
438
|
+
// path makes the exact same embed call somewhere during this turn.
|
|
439
|
+
const expectedCombined = `${assistantText}\n\n${userText}`;
|
|
440
|
+
const matchingCall = embedWithBackendMock.mock.calls.find((call) => {
|
|
441
|
+
const texts = call[1] as string[];
|
|
442
|
+
return texts.length === 1 && texts[0] === expectedCombined;
|
|
443
|
+
});
|
|
444
|
+
expect(matchingCall).toBeDefined();
|
|
445
|
+
|
|
446
|
+
// Sparse embedding for the per-turn query uses userLast only.
|
|
447
|
+
expect(generateSparseEmbeddingMock.mock.calls).toContainEqual([userText]);
|
|
448
|
+
expect(
|
|
449
|
+
generateSparseEmbeddingMock.mock.calls.some((call) =>
|
|
450
|
+
(call[0] as string).includes(assistantText),
|
|
451
|
+
),
|
|
452
|
+
).toBe(false);
|
|
453
|
+
});
|
|
454
|
+
|
|
393
455
|
test("config on with empty Qdrant hits → no v2 block, v1 fallback skipped", async () => {
|
|
394
456
|
// No `stageTurn` call — every channel returns `{ points: [] }` so the
|
|
395
457
|
// candidate set is empty and `injectMemoryV2Block` returns block=null.
|
|
@@ -461,6 +523,50 @@ describe("ConversationGraphMemory.prepareMemory — v2 routing (context-load pat
|
|
|
461
523
|
});
|
|
462
524
|
});
|
|
463
525
|
|
|
526
|
+
describe("ConversationGraphMemory.prepareMemory — memory.enabled gate", () => {
|
|
527
|
+
test("memory.enabled=false short-circuits per-turn path: mode=none, no injection, v2/v1 not called", async () => {
|
|
528
|
+
stageTurn([{ slug: "alice-vscode", denseScore: 0.9 }]);
|
|
529
|
+
|
|
530
|
+
const memory = makeMemory();
|
|
531
|
+
const config = makeConfig(true, false);
|
|
532
|
+
const messages = makeMessages();
|
|
533
|
+
|
|
534
|
+
const result = await memory.prepareMemory(
|
|
535
|
+
messages,
|
|
536
|
+
config,
|
|
537
|
+
new AbortController().signal,
|
|
538
|
+
noopEvent,
|
|
539
|
+
);
|
|
540
|
+
|
|
541
|
+
expect(result.mode).toBe("none");
|
|
542
|
+
expect(result.injectedBlockText).toBeNull();
|
|
543
|
+
expect(result.runMessages).toEqual(messages);
|
|
544
|
+
expect(retrieveForTurnMock).not.toHaveBeenCalled();
|
|
545
|
+
expect(loadContextMemoryMock).not.toHaveBeenCalled();
|
|
546
|
+
});
|
|
547
|
+
|
|
548
|
+
test("memory.enabled=false short-circuits context-load path: mode=none, no injection, v2/v1 not called", async () => {
|
|
549
|
+
stageTurn([{ slug: "alice-vscode", denseScore: 0.9 }]);
|
|
550
|
+
|
|
551
|
+
const memory = new ConversationGraphMemory("conv-test-master-off");
|
|
552
|
+
const config = makeConfig(true, false);
|
|
553
|
+
const messages = makeMessages("first message of the conversation here");
|
|
554
|
+
|
|
555
|
+
const result = await memory.prepareMemory(
|
|
556
|
+
messages,
|
|
557
|
+
config,
|
|
558
|
+
new AbortController().signal,
|
|
559
|
+
noopEvent,
|
|
560
|
+
);
|
|
561
|
+
|
|
562
|
+
expect(result.mode).toBe("none");
|
|
563
|
+
expect(result.injectedBlockText).toBeNull();
|
|
564
|
+
expect(result.runMessages).toEqual(messages);
|
|
565
|
+
expect(loadContextMemoryMock).not.toHaveBeenCalled();
|
|
566
|
+
expect(retrieveForTurnMock).not.toHaveBeenCalled();
|
|
567
|
+
});
|
|
568
|
+
});
|
|
569
|
+
|
|
464
570
|
describe("ConversationGraphMemory.onCompacted — v2 activation eviction", () => {
|
|
465
571
|
test("clears everInjected so a previously-injected slug can re-attach", async () => {
|
|
466
572
|
// Without this wiring, `selectInjections` keeps subtracting the slug from
|
|
@@ -504,4 +610,36 @@ describe("ConversationGraphMemory.onCompacted — v2 activation eviction", () =>
|
|
|
504
610
|
"# memory/concepts/alice-vscode.md",
|
|
505
611
|
);
|
|
506
612
|
});
|
|
613
|
+
|
|
614
|
+
test("clears everInjected entries whose turn exceeds the tracker's currentTurn (zombie drift)", async () => {
|
|
615
|
+
// Regression: under the prior turn-bounded eviction, entries with `turn >
|
|
616
|
+
// tracker.currentTurn` survived `onCompacted` forever. This can happen
|
|
617
|
+
// after a non-graceful shutdown: `everInjected` is persisted every turn
|
|
618
|
+
// while the tracker snapshot is only persisted on graceful dispose, so a
|
|
619
|
+
// SIGKILL'd session followed by a reload restores the tracker from an
|
|
620
|
+
// older snapshot with a lower currentTurn while keeping the high-turn
|
|
621
|
+
// entries on disk.
|
|
622
|
+
const conversationId = "conv-test-zombie-drift";
|
|
623
|
+
const memory = new ConversationGraphMemory(conversationId);
|
|
624
|
+
|
|
625
|
+
// Seed the simulated post-crash state directly: tracker stays at
|
|
626
|
+
// currentTurn=0 (default for a fresh ConversationGraphMemory), while the
|
|
627
|
+
// persisted row carries everInjected entries from turns 10 and 20 (left
|
|
628
|
+
// over from a prior session that never disposed cleanly).
|
|
629
|
+
await saveActivationState(testDbHandle!, conversationId, {
|
|
630
|
+
messageId: "msg-zombie",
|
|
631
|
+
state: {},
|
|
632
|
+
everInjected: [
|
|
633
|
+
{ slug: "alice-vscode", turn: 10 },
|
|
634
|
+
{ slug: "bob-pkg-mgr", turn: 20 },
|
|
635
|
+
],
|
|
636
|
+
currentTurn: 0,
|
|
637
|
+
updatedAt: 1,
|
|
638
|
+
});
|
|
639
|
+
|
|
640
|
+
await memory.onCompacted(0);
|
|
641
|
+
|
|
642
|
+
const after = await hydrateActivationState(testDbHandle!, conversationId);
|
|
643
|
+
expect(after?.everInjected).toEqual([]);
|
|
644
|
+
});
|
|
507
645
|
});
|