@vellumai/assistant 0.8.0 → 0.8.1
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 +11 -0
- package/Dockerfile +5 -4
- package/README.md +2 -2
- package/docker-entrypoint.sh +16 -0
- package/eslint-rules/__tests__/cli-no-daemon-internals.test.ts +420 -0
- package/eslint-rules/cli-no-daemon-internals.js +283 -0
- package/eslint.config.mjs +12 -0
- package/knip.json +2 -1
- package/node_modules/@vellumai/skill-host-contracts/src/client.ts +10 -1
- package/openapi.yaml +4847 -1698
- package/package.json +3 -1
- package/scripts/generate-openapi.ts +52 -4
- package/scripts/sync-llm-catalog.ts +165 -0
- package/scripts/sync-web-search-catalog.ts +107 -0
- package/src/__tests__/actor-trust-resolver-address-fallback.test.ts +169 -0
- package/src/__tests__/agent-loop-override-profile.test.ts +26 -1
- package/src/__tests__/anthropic-provider.test.ts +92 -2
- package/src/__tests__/app-control-flow.test.ts +7 -0
- package/src/__tests__/assistant-events-sse-shed.test.ts +232 -0
- package/src/__tests__/avatar-identity-sync.test.ts +87 -0
- package/src/__tests__/background-workers-disk-pressure.test.ts +11 -22
- package/src/__tests__/btw-routes.test.ts +1 -0
- package/src/__tests__/call-site-routing-provider.test.ts +172 -45
- package/src/__tests__/cancel-resolves-conversation-key.test.ts +44 -3
- package/src/__tests__/channel-policy.test.ts +12 -0
- package/src/__tests__/checker.test.ts +89 -0
- package/src/__tests__/cli-memory-v2-reembed-skills.test.ts +35 -7
- package/src/__tests__/compact-event-conversation-id-guard.test.ts +33 -5
- package/src/__tests__/compaction-strip-metadata-clear.test.ts +26 -1
- package/src/__tests__/config-loader-backfill.test.ts +526 -102
- package/src/__tests__/config-loader-corrupt.test.ts +68 -0
- package/src/__tests__/config-loader-platform-defaults.test.ts +77 -23
- package/src/__tests__/config-schema-cmd.test.ts +63 -29
- package/src/__tests__/config-schema.test.ts +14 -3
- package/src/__tests__/config-set-platform-guard.test.ts +75 -152
- package/src/__tests__/config-set-route.test.ts +198 -0
- package/src/__tests__/config-watcher.test.ts +6 -0
- package/src/__tests__/contacts-tools.test.ts +51 -199
- package/src/__tests__/context-search-agent-protocol.test.ts +21 -2
- package/src/__tests__/context-search-agent-runner.test.ts +22 -138
- package/src/__tests__/context-search-conversations-source.test.ts +42 -16
- package/src/__tests__/context-search-fanout.test.ts +20 -157
- package/src/__tests__/context-search-memory-v2-source.test.ts +3 -3
- package/src/__tests__/context-search-types.test.ts +7 -2
- package/src/__tests__/context-window-manager.test.ts +389 -1
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +1 -0
- package/src/__tests__/conversation-crud-inference-profile.test.ts +100 -0
- package/src/__tests__/conversation-error.test.ts +38 -0
- package/src/__tests__/conversation-fork-crud.test.ts +241 -1
- package/src/__tests__/conversation-inference-profile-route.test.ts +14 -14
- package/src/__tests__/conversation-init.benchmark.test.ts +1 -0
- package/src/__tests__/conversation-lifecycle.test.ts +124 -0
- package/src/__tests__/conversation-process-app-control-preactivation.test.ts +100 -1
- package/src/__tests__/conversation-process-callsite.test.ts +21 -1
- package/src/__tests__/conversation-runtime-assembly.test.ts +4 -4
- package/src/__tests__/conversation-slash-commands.test.ts +194 -2
- package/src/__tests__/conversation-surfaces-app-control.test.ts +323 -3
- package/src/__tests__/credential-security-invariants.test.ts +5 -6
- package/src/__tests__/daemon-credential-client.test.ts +56 -1
- package/src/__tests__/db-activation-state-fk-cascade.test.ts +132 -0
- package/src/__tests__/db-conversation-inference-profile-migration.test.ts +37 -0
- package/src/__tests__/db-memory-graph-event-date-repair.test.ts +43 -20
- package/src/__tests__/db-proxy-transaction.test.ts +206 -0
- package/src/__tests__/external-plugin-loader.test.ts +458 -0
- package/src/__tests__/filing-service.test.ts +23 -3
- package/src/__tests__/fixtures/mock-chrome-extension.ts +5 -0
- package/src/__tests__/gateway-only-guard.test.ts +0 -1
- package/src/__tests__/graph-extraction-event-date.test.ts +34 -0
- package/src/__tests__/handlers-skills-memory-v2-reseed.test.ts +0 -8
- package/src/__tests__/heartbeat-disk-pressure.test.ts +21 -8
- package/src/__tests__/heartbeat-service.test.ts +50 -233
- package/src/__tests__/history-repair.test.ts +89 -0
- package/src/__tests__/host-app-control-proxy.test.ts +109 -1
- package/src/__tests__/host-app-control-routes.test.ts +247 -1
- package/src/__tests__/host-browser-proxy.test.ts +416 -20
- package/src/__tests__/host-browser-routes.test.ts +325 -33
- package/src/__tests__/host-proxy-preactivation.test.ts +211 -0
- package/src/__tests__/inference-no-mode-boot-e2e.test.ts +246 -0
- package/src/__tests__/inference-profile-reaper.test.ts +154 -0
- package/src/__tests__/inference-profile-session-handler.test.ts +398 -0
- package/src/__tests__/inference-profile-session-ipc.test.ts +236 -0
- package/src/__tests__/inline-skill-load-permissions.test.ts +6 -1
- package/src/__tests__/install-skill-routing.test.ts +2 -2
- package/src/__tests__/lifecycle-memory-v2-seed.test.ts +15 -0
- package/src/__tests__/llm-callsite-catalog.test.ts +20 -1
- package/src/__tests__/llm-catalog-parity.test.ts +146 -0
- package/src/__tests__/llm-request-log-source-clickhouse.test.ts +188 -0
- package/src/__tests__/llm-request-log-source-factory.test.ts +124 -0
- package/src/__tests__/llm-resolver.test.ts +46 -0
- package/src/__tests__/managed-profile-guard.test.ts +131 -2
- package/src/__tests__/mcp-auth-routes.test.ts +1 -0
- package/src/__tests__/mcp-cli.test.ts +182 -220
- package/src/__tests__/mcp-health-check.test.ts +56 -27
- package/src/__tests__/memory-jobs-worker-lanes.test.ts +18 -11
- package/src/__tests__/message-complete-display-id.test.ts +175 -0
- package/src/__tests__/notification-platform-adapter.test.ts +229 -0
- package/src/__tests__/oauth-cli.test.ts +38 -2009
- package/src/__tests__/oauth-commands-routes.test.ts +711 -0
- package/src/__tests__/oauth-connect-routes.test.ts +174 -11
- package/src/__tests__/oauth-providers-routes.test.ts +14 -10
- package/src/__tests__/openai-responses-cutover-guard.test.ts +33 -12
- package/src/__tests__/openai-responses-provider.test.ts +17 -0
- package/src/__tests__/plugin-bootstrap.test.ts +31 -2
- package/src/__tests__/plugin-route-contribution.test.ts +31 -3
- package/src/__tests__/plugin-tool-contribution.test.ts +31 -3
- package/src/__tests__/plugin-types.test.ts +13 -11
- package/src/__tests__/process-message-background-slack.test.ts +46 -0
- package/src/__tests__/profile-entry-status.test.ts +43 -0
- package/src/__tests__/provider-managed-proxy-integration.test.ts +12 -4
- package/src/__tests__/provider-registry-ollama.test.ts +12 -4
- package/src/__tests__/provider-send-message-override-profile.test.ts +10 -4
- package/src/__tests__/relay-server.test.ts +118 -0
- package/src/__tests__/retry-thinking-tool-choice.test.ts +15 -0
- package/src/__tests__/schedule-retry.test.ts +56 -4
- package/src/__tests__/schedule-routes.test.ts +104 -0
- package/src/__tests__/scheduler-disk-pressure.test.ts +0 -4
- package/src/__tests__/scheduler-recurrence.test.ts +87 -34
- package/src/__tests__/scheduler-reuse-conversation.test.ts +161 -5
- package/src/__tests__/scheduler-wake.test.ts +0 -63
- package/src/__tests__/secret-allowlist.test.ts +1 -0
- package/src/__tests__/secret-routes-managed-proxy.test.ts +12 -4
- package/src/__tests__/shell-credential-ref.test.ts +95 -3
- package/src/__tests__/shell-tool-proxy-mode.test.ts +14 -0
- package/src/__tests__/skill-load-feature-flag.test.ts +1 -0
- package/src/__tests__/skill-load-tool.test.ts +2 -4
- package/src/__tests__/subagent-call-site-routing.test.ts +78 -16
- package/src/__tests__/suggestion-routes.test.ts +3 -3
- package/src/__tests__/sync-message-contract.test.ts +63 -0
- package/src/__tests__/task-scheduler.test.ts +88 -23
- package/src/__tests__/update-bulletin-job.test.ts +96 -193
- package/src/__tests__/usage-cli.test.ts +11 -73
- package/src/__tests__/user-plugin-loader.test.ts +145 -0
- package/src/__tests__/vercel-config.test.ts +168 -0
- package/src/__tests__/web-search-catalog-parity.test.ts +86 -0
- package/src/__tests__/web-search.test.ts +303 -2
- package/src/__tests__/workspace-migration-039-drop-legacy-llm-keys.test.ts +1 -21
- package/src/__tests__/workspace-migration-057-repair-stale-gemini-model-ids.test.ts +58 -0
- package/src/__tests__/workspace-migration-069-seed-onboarding-threads.test.ts +53 -20
- package/src/__tests__/workspace-migration-072-seed-reply-suggestion-callsite.test.ts +191 -0
- package/src/__tests__/workspace-migration-076-drop-services-inference-mode.test.ts +211 -0
- package/src/__tests__/workspace-migration-077-seed-memory-router-callsite.test.ts +174 -0
- package/src/__tests__/workspace-migration-079-home-feed-notification-only.test.ts +323 -0
- package/src/__tests__/workspace-migration-080-restrict-vercel-api-token-metadata.test.ts +299 -0
- package/src/__tests__/workspace-migration-081-backfill-bash-allowed-tools.test.ts +410 -0
- package/src/__tests__/workspace-migration-082-backfill-managed-profile-labels.test.ts +268 -0
- package/src/__tests__/workspace-migration-unify-llm-callsite-configs.test.ts +3 -3
- package/src/__tests__/workspace-release-notes-feature-flag-guard.test.ts +115 -0
- package/src/acp/__tests__/helpers/which-stub.ts +4 -2
- package/src/acp/resolve-agent.test.ts +25 -0
- package/src/acp/resolve-agent.ts +13 -2
- package/src/acp/session-manager.ts +14 -0
- package/src/approvals/guardian-request-resolvers.ts +32 -87
- package/src/calls/relay-server.ts +35 -0
- package/src/calls/relay-setup-router.ts +36 -0
- package/src/calls/types.ts +1 -0
- package/src/calls/voice-session-bridge.ts +23 -4
- package/src/channels/config.ts +14 -1
- package/src/channels/types.ts +1 -0
- package/src/cli/AGENTS.md +164 -4
- package/src/cli/__tests__/notifications.test.ts +54 -0
- package/src/cli/commands/__tests__/avatar.test.ts +540 -0
- package/src/cli/commands/__tests__/backup.test.ts +236 -776
- package/src/cli/commands/__tests__/cache.test.ts +1 -1
- package/src/cli/commands/__tests__/changelog.test.ts +593 -0
- package/src/cli/commands/__tests__/channel-verification-sessions.test.ts +503 -0
- package/src/cli/commands/__tests__/conversations-import.test.ts +515 -0
- package/src/cli/commands/__tests__/domain-register.test.ts +140 -167
- package/src/cli/commands/__tests__/domain-status.test.ts +137 -76
- package/src/cli/commands/__tests__/email-attachment.test.ts +314 -337
- package/src/cli/commands/__tests__/email-core.test.ts +579 -0
- package/src/cli/commands/__tests__/image-generation.test.ts +87 -824
- package/src/cli/commands/__tests__/inference-send.test.ts +30 -266
- package/src/cli/commands/__tests__/inference-session.test.ts +423 -0
- package/src/cli/commands/__tests__/memory-v2.test.ts +81 -110
- package/src/cli/commands/__tests__/skills.test.ts +563 -0
- package/src/cli/commands/__tests__/status.test.ts +249 -0
- package/src/cli/commands/__tests__/stt.test.ts +320 -0
- package/src/cli/commands/__tests__/tts-synthesize.test.ts +4 -603
- package/src/cli/commands/__tests__/tts.test.ts +321 -0
- package/src/cli/commands/__tests__/webhooks.test.ts +86 -511
- package/src/cli/commands/attachment.ts +8 -3
- package/src/cli/commands/audit.ts +95 -64
- package/src/cli/commands/auth.ts +61 -58
- package/src/cli/commands/avatar.ts +276 -390
- package/src/cli/commands/backup.ts +409 -505
- package/src/cli/commands/bash.ts +9 -5
- package/src/cli/commands/browser.ts +28 -9
- package/src/cli/commands/cache.ts +9 -4
- package/src/cli/commands/changelog.ts +414 -0
- package/src/cli/commands/channel-verification-sessions.ts +238 -317
- package/src/cli/commands/clients.ts +8 -3
- package/src/cli/commands/completions.ts +9 -9
- package/src/cli/commands/config.ts +102 -72
- package/src/cli/commands/contacts.ts +575 -696
- package/src/cli/commands/conversations-defer.ts +17 -69
- package/src/cli/commands/conversations-import.ts +90 -253
- package/src/cli/commands/conversations.ts +346 -436
- package/src/cli/commands/credential-execution.ts +9 -6
- package/src/cli/commands/credentials.ts +456 -736
- package/src/cli/commands/domain.ts +128 -206
- package/src/cli/commands/email.ts +606 -794
- package/src/cli/commands/gateway.ts +8 -1
- package/src/cli/commands/image-generation.ts +157 -205
- package/src/cli/commands/inference-providers.ts +352 -0
- package/src/cli/commands/inference-session.ts +415 -0
- package/src/cli/commands/inference.ts +87 -65
- package/src/cli/commands/keys.ts +8 -3
- package/src/cli/commands/mcp.ts +103 -287
- package/src/cli/commands/memory-v2.ts +162 -516
- package/src/cli/commands/notifications.ts +33 -7
- package/src/cli/commands/oauth/apps.ts +292 -261
- package/src/cli/commands/oauth/connect.ts +176 -297
- package/src/cli/commands/oauth/disconnect.ts +16 -215
- package/src/cli/commands/oauth/index.ts +49 -45
- package/src/cli/commands/oauth/mode.ts +43 -199
- package/src/cli/commands/oauth/ping.ts +17 -125
- package/src/cli/commands/oauth/providers.ts +732 -921
- package/src/cli/commands/oauth/request.ts +60 -350
- package/src/cli/commands/oauth/shared.ts +11 -121
- package/src/cli/commands/oauth/status.ts +31 -121
- package/src/cli/commands/oauth/token.ts +13 -55
- package/src/cli/commands/pending.ts +19 -10
- package/src/cli/commands/platform/__tests__/callback-routes-list.test.ts +133 -183
- package/src/cli/commands/platform/__tests__/connect.test.ts +66 -181
- package/src/cli/commands/platform/__tests__/disconnect.test.ts +71 -227
- package/src/cli/commands/platform/__tests__/status.test.ts +169 -287
- package/src/cli/commands/platform/connect.ts +16 -80
- package/src/cli/commands/platform/disconnect.ts +14 -112
- package/src/cli/commands/platform/index.ts +177 -246
- package/src/cli/commands/routes.ts +153 -336
- package/src/cli/commands/sequence.ts +316 -360
- package/src/cli/commands/skills.ts +449 -671
- package/src/cli/commands/status.ts +58 -37
- package/src/cli/commands/stt.ts +94 -262
- package/src/cli/commands/task.ts +14 -40
- package/src/cli/commands/trust.ts +8 -3
- package/src/cli/commands/tts.ts +162 -167
- package/src/cli/commands/ui.ts +35 -42
- package/src/cli/commands/usage.ts +188 -126
- package/src/cli/commands/watchers.ts +8 -3
- package/src/cli/commands/webhooks.ts +99 -193
- package/src/cli/lib/__tests__/register-command.test.ts +85 -0
- package/src/cli/lib/daemon-credential-client.ts +4 -5
- package/src/cli/lib/nested-value.ts +44 -0
- package/src/cli/lib/open-browser.ts +36 -0
- package/src/cli/lib/register-command.ts +19 -0
- package/src/cli/lib/time-ago.ts +34 -0
- package/src/cli/program.ts +2 -4
- package/src/cli/utils/__tests__/conversation-id.test.ts +66 -0
- package/src/cli/utils/__tests__/parse-duration.test.ts +49 -0
- package/src/cli/utils/conversation-id.ts +30 -0
- package/src/cli/utils/parse-duration.ts +41 -0
- package/src/config/acp-defaults.test.ts +5 -1
- package/src/config/acp-defaults.ts +11 -4
- package/src/config/bundled-skills/acp/TOOLS.json +2 -2
- package/src/config/bundled-skills/app-control/TOOLS.json +32 -0
- package/src/config/bundled-skills/contacts/SKILL.md +12 -45
- package/src/config/bundled-skills/contacts/TOOLS.json +0 -57
- package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +0 -12
- package/src/config/bundled-skills/messaging/tools/messaging-send.ts +0 -58
- package/src/config/bundled-tool-registry.ts +0 -2
- package/src/config/feature-flag-registry.json +16 -0
- package/src/config/llm-resolver.ts +16 -1
- package/src/config/loader.ts +76 -14
- package/src/config/raw-config-utils.ts +2 -30
- package/src/config/schema.ts +4 -0
- package/src/config/schemas/__tests__/memory-v2.test.ts +49 -0
- package/src/config/schemas/call-site-catalog.ts +29 -7
- package/src/config/schemas/llm-request-logs.ts +57 -0
- package/src/config/schemas/llm.ts +52 -2
- package/src/config/schemas/memory-retrospective.ts +48 -0
- package/src/config/schemas/memory-v2.ts +32 -1
- package/src/config/schemas/memory.ts +4 -0
- package/src/config/schemas/services.ts +15 -12
- package/src/config/seed-inference-profiles.ts +195 -134
- package/src/contacts/contact-store.ts +0 -61
- package/src/context/window-manager.ts +191 -5
- package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +79 -0
- package/src/daemon/__tests__/conversation-tool-setup.test.ts +109 -4
- package/src/daemon/__tests__/daemon-skill-host.test.ts +10 -4
- package/src/daemon/approval-generators.ts +23 -29
- package/src/daemon/config-watcher.ts +2 -0
- package/src/daemon/conversation-agent-loop-handlers.ts +24 -0
- package/src/daemon/conversation-agent-loop.ts +127 -97
- package/src/daemon/conversation-error.ts +21 -0
- package/src/daemon/conversation-lifecycle.ts +46 -5
- package/src/daemon/conversation-process.ts +36 -19
- package/src/daemon/conversation-runtime-assembly.ts +14 -5
- package/src/daemon/conversation-slash.ts +175 -23
- package/src/daemon/conversation-store.ts +17 -10
- package/src/daemon/conversation-surfaces.ts +76 -12
- package/src/daemon/conversation-tool-setup.ts +24 -14
- package/src/daemon/conversation.ts +48 -9
- package/src/daemon/external-plugins-bootstrap.ts +18 -8
- package/src/daemon/guardian-action-generators.ts +7 -22
- package/src/daemon/handlers/config-model.ts +8 -126
- package/src/daemon/handlers/config-slack-channel.ts +10 -7
- package/src/daemon/handlers/config-vercel.ts +3 -1
- package/src/daemon/handlers/skills.ts +84 -5
- package/src/daemon/history-repair.ts +33 -6
- package/src/daemon/host-app-control-proxy.ts +44 -19
- package/src/daemon/host-bash-proxy.ts +85 -158
- package/src/daemon/host-browser-proxy.ts +96 -35
- package/src/daemon/host-proxy-base.ts +13 -1
- package/src/daemon/host-proxy-preactivation.ts +25 -1
- package/src/daemon/identity-helpers.ts +19 -0
- package/src/daemon/lifecycle.ts +42 -43
- package/src/daemon/meet-host-supervisor.ts +15 -15
- package/src/daemon/memory-v2-startup.ts +9 -2
- package/src/daemon/message-protocol.ts +6 -0
- package/src/daemon/message-types/bookmarks.ts +18 -0
- package/src/daemon/message-types/conversations.ts +12 -9
- package/src/daemon/message-types/messages.ts +9 -1
- package/src/daemon/message-types/sync.ts +60 -0
- package/src/daemon/pkb-reminder-builder.test.ts +54 -13
- package/src/daemon/pkb-reminder-builder.ts +21 -7
- package/src/daemon/process-message.ts +56 -23
- package/src/daemon/server.ts +23 -18
- package/src/daemon/shutdown-handlers.ts +0 -2
- package/src/daemon/tool-setup-types.ts +9 -0
- package/src/daemon/tool-side-effects.ts +6 -4
- package/src/daemon/wake-target-adapter.ts +11 -0
- package/src/export/transcript-formatter.ts +61 -2
- package/src/filing/filing-service.ts +40 -53
- package/src/heartbeat/__tests__/heartbeat-service.test.ts +359 -0
- package/src/heartbeat/heartbeat-run-store.ts +2 -1
- package/src/heartbeat/heartbeat-service.ts +148 -127
- package/src/home/__tests__/feed-types.test.ts +63 -131
- package/src/home/__tests__/feed-writer.test.ts +77 -278
- package/src/home/__tests__/post-connect-feed.test.ts +9 -12
- package/src/home/feed-types.ts +19 -73
- package/src/home/feed-writer.ts +25 -156
- package/src/home/post-connect-feed.ts +1 -3
- package/src/ipc/__tests__/cli-ipc.test.ts +2 -0
- package/src/ipc/__tests__/email-ipc.test.ts +506 -0
- package/src/ipc/__tests__/exit-helper.test.ts +104 -0
- package/src/ipc/__tests__/streaming-client.test.ts +237 -0
- package/src/ipc/__tests__/streaming-framing.test.ts +142 -0
- package/src/ipc/assistant-server.ts +55 -6
- package/src/ipc/cli-client.ts +370 -50
- package/src/ipc/routes/db-proxy-transaction.ts +151 -0
- package/src/ipc/skill-routes/__tests__/events-ipc.test.ts +60 -0
- package/src/ipc/skill-routes/events.ts +30 -3
- package/src/live-voice/__tests__/live-voice-session-manager.test.ts +46 -0
- package/src/live-voice/__tests__/runtime-websocket-shell.test.ts +1 -0
- package/src/live-voice/live-voice-session-manager.ts +11 -4
- package/src/live-voice/live-voice-session.ts +14 -6
- package/src/memory/__tests__/bookmark-crud.test.ts +258 -0
- package/src/memory/__tests__/bookmark-schema.test.ts +181 -0
- package/src/memory/__tests__/conversation-types.test.ts +36 -0
- package/src/memory/__tests__/find-most-recent-retrospective-for.test.ts +130 -0
- package/src/memory/__tests__/memory-retrospective-enqueue.test.ts +177 -0
- package/src/memory/__tests__/memory-retrospective-job.test.ts +328 -0
- package/src/memory/__tests__/memory-retrospective-startup-cleanup.test.ts +213 -0
- package/src/memory/__tests__/memory-retrospective-trigger-check.test.ts +90 -0
- package/src/memory/__tests__/memory-v2-activation-log-store.test.ts +69 -0
- package/src/memory/__tests__/memory-v2-concept-frequency.test.ts +3 -0
- package/src/memory/bookmark-crud.ts +179 -0
- package/src/memory/context-search/__tests__/agent-runner-redaction.test.ts +31 -9
- package/src/memory/context-search/agent-protocol.ts +5 -1
- package/src/memory/context-search/agent-runner.ts +60 -85
- package/src/memory/context-search/limits.ts +1 -4
- package/src/memory/context-search/search.ts +23 -113
- package/src/memory/context-search/sources/conversations.ts +18 -6
- package/src/memory/context-search/sources/memory-v2.ts +39 -14
- package/src/memory/context-search/sources/memory.ts +7 -0
- package/src/memory/context-search/sources/workspace.ts +13 -10
- package/src/memory/context-search/types.ts +1 -1
- package/src/memory/conversation-bootstrap.ts +11 -0
- package/src/memory/conversation-crud.ts +312 -10
- package/src/memory/conversation-queries.ts +9 -5
- package/src/memory/conversation-title-service.ts +1 -0
- package/src/memory/conversation-types.ts +16 -0
- package/src/memory/db-init.ts +14 -0
- package/src/memory/embedding-backend.ts +2 -1
- package/src/memory/embedding-runtime-manager.ts +1 -2
- package/src/memory/graph/__tests__/remember-description.test.ts +55 -0
- package/src/memory/graph/conversation-graph-memory.ts +76 -5
- package/src/memory/graph/extraction.ts +4 -0
- package/src/memory/graph/graph-memory-state-store.ts +16 -3
- package/src/memory/graph/tool-handlers.ts +17 -7
- package/src/memory/graph/tools.ts +44 -5
- package/src/memory/indexer.ts +17 -0
- package/src/memory/jobs/__tests__/embed-concept-page.test.ts +13 -15
- package/src/memory/jobs/embed-concept-page.ts +45 -9
- package/src/memory/jobs-store.ts +51 -1
- package/src/memory/jobs-worker.ts +52 -3
- package/src/memory/llm-request-log-source-clickhouse.ts +317 -0
- package/src/memory/llm-request-log-source-local.ts +26 -0
- package/src/memory/llm-request-log-source.ts +97 -0
- package/src/memory/llm-request-log-store.ts +1 -1
- package/src/memory/memory-retrospective-constants.ts +13 -0
- package/src/memory/memory-retrospective-enqueue.ts +114 -0
- package/src/memory/memory-retrospective-job.ts +351 -0
- package/src/memory/memory-retrospective-startup-cleanup.ts +108 -0
- package/src/memory/memory-retrospective-state.ts +162 -0
- package/src/memory/memory-retrospective-trigger-check.ts +91 -0
- package/src/memory/memory-v2-activation-log-store.ts +49 -5
- package/src/memory/memory-v2-concept-frequency.ts +4 -0
- package/src/memory/message-content.ts +38 -1
- package/src/memory/migrations/227-add-conversation-inference-profile.ts +6 -1
- package/src/memory/migrations/228-rename-inference-profile-snake-case.ts +20 -7
- package/src/memory/migrations/229-delete-private-conversations.test.ts +70 -1
- package/src/memory/migrations/229-delete-private-conversations.ts +12 -0
- package/src/memory/migrations/231-repair-memory-graph-event-dates.ts +16 -2
- package/src/memory/migrations/240-conversation-inference-profile-session.ts +25 -0
- package/src/memory/migrations/241-activation-state-fk-cascade.ts +50 -0
- package/src/memory/migrations/242-message-bookmarks.ts +38 -0
- package/src/memory/migrations/243-provider-connections.ts +68 -0
- package/src/memory/migrations/244-provider-connection-status-label.ts +23 -0
- package/src/memory/migrations/245-memory-retrospective-state.ts +36 -0
- package/src/memory/migrations/246-backfill-provider-connection-label.ts +81 -0
- package/src/memory/migrations/__tests__/244-provider-connection-status-label.test.ts +84 -0
- package/src/memory/migrations/__tests__/245-memory-retrospective-state.test.ts +125 -0
- package/src/memory/migrations/__tests__/246-backfill-provider-connection-label.test.ts +192 -0
- package/src/memory/migrations/index.ts +7 -0
- package/src/memory/published-pages-store.ts +16 -0
- package/src/memory/schema/bookmarks.ts +38 -0
- package/src/memory/schema/conversations.ts +2 -0
- package/src/memory/schema/index.ts +2 -0
- package/src/memory/schema/inference.ts +29 -0
- package/src/memory/schema/memory-core.ts +9 -0
- package/src/memory/search/semantic.ts +1 -4
- package/src/memory/v2/__tests__/__snapshots__/prompts-router.test.ts.snap +27 -0
- package/src/memory/v2/__tests__/activation-store.test.ts +5 -5
- package/src/memory/v2/__tests__/activation.test.ts +11 -4
- package/src/memory/v2/__tests__/backfill-jobs.test.ts +38 -21
- package/src/memory/v2/__tests__/consolidation-job.test.ts +123 -135
- package/src/memory/v2/__tests__/edge-index.test.ts +1 -1
- package/src/memory/v2/__tests__/frontmatter-sweep.test.ts +111 -0
- package/src/memory/v2/__tests__/injection.test.ts +628 -10
- package/src/memory/v2/__tests__/migration.test.ts +7 -3
- package/src/memory/v2/__tests__/page-index.test.ts +277 -0
- package/src/memory/v2/__tests__/page-store.test.ts +14 -1
- package/src/memory/v2/__tests__/prompts-router.test.ts +257 -0
- package/src/memory/v2/__tests__/qdrant.test.ts +72 -0
- package/src/memory/v2/__tests__/reranker.test.ts +4 -4
- package/src/memory/v2/__tests__/router.test.ts +516 -0
- package/src/memory/v2/__tests__/sim.test.ts +45 -1
- package/src/memory/v2/__tests__/skill-store.test.ts +58 -3
- package/src/memory/v2/__tests__/static-context.test.ts +7 -22
- package/src/memory/v2/__tests__/sweep-job.test.ts +95 -0
- package/src/memory/v2/activation-store.ts +34 -5
- package/src/memory/v2/activation.ts +40 -27
- package/src/memory/v2/backfill-jobs.ts +17 -84
- package/src/memory/v2/consolidation-job.ts +85 -78
- package/src/memory/v2/frontmatter-sweep.ts +91 -0
- package/src/memory/v2/injection.ts +440 -109
- package/src/memory/v2/migration.ts +117 -20
- package/src/memory/v2/page-index.ts +191 -0
- package/src/memory/v2/page-store.ts +3 -0
- package/src/memory/v2/prompts/consolidation.ts +9 -7
- package/src/memory/v2/prompts/router.ts +192 -0
- package/src/memory/v2/qdrant.ts +100 -87
- package/src/memory/v2/reranker.ts +14 -7
- package/src/memory/v2/router.ts +322 -0
- package/src/memory/v2/sim.ts +25 -12
- package/src/memory/v2/skill-store.ts +118 -29
- package/src/memory/v2/static-context.ts +16 -9
- package/src/memory/v2/sweep-job.ts +122 -96
- package/src/memory/v2/types.ts +10 -6
- package/src/memory/validation.ts +13 -0
- package/src/notifications/__tests__/emit-signal-home-feed.test.ts +182 -0
- package/src/notifications/__tests__/home-feed-side-effect.test.ts +199 -0
- package/src/notifications/__tests__/signal-registry.test.ts +17 -0
- package/src/notifications/adapters/platform.ts +171 -0
- package/src/notifications/conversation-pairing.ts +2 -2
- package/src/notifications/copy-composer.ts +15 -0
- package/src/notifications/destination-resolver.ts +21 -0
- package/src/notifications/emit-signal.ts +28 -1
- package/src/notifications/home-feed-side-effect.ts +111 -0
- package/src/notifications/signal.ts +5 -0
- package/src/permissions/checker.ts +12 -0
- package/src/permissions/ipc-risk-types.ts +2 -0
- package/src/plugin-api/index.ts +13 -0
- package/src/plugin-api/package.json +12 -0
- package/src/plugin-api/types.ts +62 -0
- package/src/plugins/defaults/injectors.ts +19 -3
- package/src/plugins/external-plugin-loader.ts +294 -0
- package/src/plugins/types.ts +46 -30
- package/src/plugins/user-loader.ts +64 -41
- package/src/proactive-artifact/job.test.ts +12 -4
- package/src/proactive-artifact/job.ts +4 -0
- package/src/proactive-artifact/trigger-state.test.ts +9 -0
- package/src/proactive-artifact/trigger-state.ts +4 -0
- package/src/prompts/__tests__/system-prompt.test.ts +105 -0
- package/src/prompts/system-prompt.ts +22 -1
- package/src/prompts/update-bulletin-job.ts +61 -73
- package/src/providers/__tests__/dispatch-connection-routing.test.ts +279 -0
- package/src/providers/__tests__/inference.test.ts +288 -0
- package/src/providers/__tests__/provider-env-vars.test.ts +6 -0
- package/src/providers/__tests__/provider-secret-catalog.test.ts +6 -0
- package/src/providers/__tests__/retry-callsite.test.ts +14 -32
- package/src/providers/__tests__/satellite-connection-routing.test.ts +510 -0
- package/src/providers/__tests__/search-provider-catalog.test.ts +80 -0
- package/src/providers/anthropic/client.ts +95 -26
- package/src/providers/call-site-routing.ts +94 -16
- package/src/providers/connection-resolution.ts +163 -0
- package/src/providers/inference/__tests__/connections-status-label.test.ts +250 -0
- package/src/providers/inference/adapter-factory.ts +173 -0
- package/src/providers/inference/auth.ts +112 -0
- package/src/providers/inference/backfill.ts +196 -0
- package/src/providers/inference/connections.ts +356 -0
- package/src/providers/inference/resolve-auth.ts +65 -0
- package/src/providers/model-catalog.ts +104 -6
- package/src/providers/openai/responses-provider.ts +4 -2
- package/src/providers/provider-env-vars.ts +17 -7
- package/src/providers/provider-secret-catalog.ts +49 -30
- package/src/providers/provider-send-message.ts +41 -20
- package/src/providers/registry.ts +143 -159
- package/src/providers/retry.ts +18 -10
- package/src/providers/search-provider-catalog.ts +121 -0
- package/src/runtime/AGENTS.md +18 -5
- package/src/runtime/__tests__/background-job-runner.test.ts +357 -0
- package/src/runtime/__tests__/pre-first-message-gate.test.ts +82 -0
- package/src/runtime/actor-trust-resolver.ts +32 -10
- package/src/runtime/agent-wake.ts +35 -6
- package/src/runtime/assistant-event-hub.ts +3 -85
- package/src/runtime/auth/route-policy.ts +303 -8
- package/src/runtime/auth/same-actor.ts +2 -0
- package/src/runtime/background-job-runner.ts +339 -0
- package/src/runtime/btw-sidechain.ts +1 -0
- package/src/runtime/http-router.ts +36 -1
- package/src/runtime/http-server.ts +31 -5
- package/src/runtime/http-types.ts +2 -0
- package/src/runtime/middleware/__tests__/request-logger.test.ts +162 -0
- package/src/runtime/middleware/request-logger.ts +62 -1
- package/src/runtime/pre-first-message-gate.ts +83 -0
- package/src/runtime/routes/__tests__/backup-routes.test.ts +8 -1
- package/src/runtime/routes/__tests__/bookmark-routes.test.ts +251 -0
- package/src/runtime/routes/__tests__/connection-routes-vs-cli-parity.test.ts +142 -0
- package/src/runtime/routes/__tests__/conversation-management-routes.test.ts +315 -0
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +189 -0
- package/src/runtime/routes/__tests__/home-feed-routes.test.ts +15 -136
- package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +736 -0
- package/src/runtime/routes/__tests__/memory-v2-routes.test.ts +4 -4
- package/src/runtime/routes/__tests__/stt-routes.test.ts +5 -1
- package/src/runtime/routes/__tests__/surface-action-routes.test.ts +384 -0
- package/src/runtime/routes/__tests__/tts-routes.test.ts +6 -2
- package/src/runtime/routes/acp-routes.ts +10 -8
- package/src/runtime/routes/app-management-routes.ts +228 -3
- package/src/runtime/routes/approval-routes.ts +0 -18
- package/src/runtime/routes/audit-routes.ts +43 -0
- package/src/runtime/routes/auth-routes.ts +72 -0
- package/src/runtime/routes/avatar-routes.ts +273 -20
- package/src/runtime/routes/backup-routes.ts +406 -2
- package/src/runtime/routes/bookmark-routes.ts +154 -0
- package/src/runtime/routes/channel-verification-routes.ts +2 -1
- package/src/runtime/routes/contact-routes.ts +0 -160
- package/src/runtime/routes/conversation-cli-routes.ts +192 -0
- package/src/runtime/routes/conversation-management-routes.ts +30 -43
- package/src/runtime/routes/conversation-query-routes.ts +334 -86
- package/src/runtime/routes/conversation-routes.ts +31 -10
- package/src/runtime/routes/conversations-import-routes.ts +229 -0
- package/src/runtime/routes/credential-routes.ts +540 -0
- package/src/runtime/routes/debug-routes.ts +2 -2
- package/src/runtime/routes/document-pdf-renderer.ts +5 -1
- package/src/runtime/routes/domain-routes.ts +167 -0
- package/src/runtime/routes/email-routes.ts +603 -0
- package/src/runtime/routes/errors.ts +2 -2
- package/src/runtime/routes/events-routes.ts +192 -0
- package/src/runtime/routes/home-feed-routes.ts +6 -78
- package/src/runtime/routes/host-app-control-routes.ts +44 -2
- package/src/runtime/routes/host-browser-routes.ts +103 -22
- package/src/runtime/routes/http-adapter.ts +2 -0
- package/src/runtime/routes/identity-routes.ts +5 -0
- package/src/runtime/routes/image-generation-routes.ts +99 -0
- package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +137 -1
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +87 -7
- package/src/runtime/routes/inbound-stages/guardian-reply-intercept.test.ts +156 -0
- package/src/runtime/routes/inbound-stages/guardian-reply-intercept.ts +22 -4
- package/src/runtime/routes/index.ts +36 -0
- package/src/runtime/routes/inference-profile-session-handler.ts +312 -0
- package/src/runtime/routes/inference-profile-session-reaper.ts +98 -0
- package/src/runtime/routes/inference-profile-session-routes.ts +146 -0
- package/src/runtime/routes/inference-provider-connection-routes.ts +317 -0
- package/src/runtime/routes/inference-send-routes.ts +115 -0
- package/src/runtime/routes/integrations/twilio.ts +1 -0
- package/src/runtime/routes/mcp-auth-routes.ts +283 -9
- package/src/runtime/routes/memory-v2-routes.ts +13 -398
- package/src/runtime/routes/notification-routes.ts +2 -0
- package/src/runtime/routes/oauth-apps.ts +112 -7
- package/src/runtime/routes/oauth-commands-routes.ts +1007 -0
- package/src/runtime/routes/oauth-connect-routes.ts +67 -5
- package/src/runtime/routes/oauth-providers.ts +298 -8
- package/src/runtime/routes/platform-routes.ts +336 -0
- package/src/runtime/routes/playground/inject-failures.ts +2 -1
- package/src/runtime/routes/playground/reset-circuit.ts +2 -1
- package/src/runtime/routes/playground/state.ts +2 -1
- package/src/runtime/routes/publish-routes.ts +221 -0
- package/src/runtime/routes/schedule-routes.ts +82 -0
- package/src/runtime/routes/sequence-routes.ts +291 -0
- package/src/runtime/routes/settings-routes.ts +2 -10
- package/src/runtime/routes/skills-routes.ts +31 -1
- package/src/runtime/routes/stt-routes.ts +240 -3
- package/src/runtime/routes/surface-action-routes.ts +43 -7
- package/src/runtime/routes/tts-routes.ts +67 -0
- package/src/runtime/routes/types.ts +32 -0
- package/src/runtime/routes/user-routes-cli.ts +243 -0
- package/src/runtime/routes/webhook-routes.ts +165 -0
- package/src/runtime/sync/resource-sync-events.ts +25 -0
- package/src/runtime/sync/sync-publisher.test.ts +105 -0
- package/src/runtime/sync/sync-publisher.ts +21 -0
- package/src/schedule/scheduler.ts +200 -123
- package/src/security/__tests__/provider-key-env-fallback.test.ts +12 -6
- package/src/security/secret-patterns.ts +3 -0
- package/src/sequence/engine.ts +38 -40
- package/src/subagent/manager.ts +20 -15
- package/src/tools/browser/__tests__/browser-execution-acquire.test.ts +206 -0
- package/src/tools/browser/browser-execution.ts +15 -4
- package/src/tools/browser/cdp-client/__tests__/factory.test.ts +174 -0
- package/src/tools/browser/cdp-client/cdp-inspect/__tests__/ws-transport.test.ts +16 -13
- package/src/tools/browser/cdp-client/extension-cdp-client.ts +24 -1
- package/src/tools/browser/cdp-client/factory.ts +66 -5
- package/src/tools/browser/runtime-check.ts +77 -0
- package/src/tools/memory/register.test.ts +3 -3
- package/src/tools/memory/register.ts +9 -1
- package/src/tools/network/__tests__/web-search.test.ts +156 -0
- package/src/tools/network/web-search.ts +280 -37
- package/src/tools/permission-checker.ts +13 -5
- package/src/tools/subagent/spawn.ts +3 -3
- package/src/tools/terminal/shell.ts +44 -0
- package/src/usage/attribution.ts +3 -2
- package/src/util/pricing.ts +86 -160
- package/src/watcher/__tests__/engine.test.ts +301 -0
- package/src/watcher/constants.ts +7 -0
- package/src/watcher/engine.ts +90 -90
- package/src/workspace/migrations/046-seed-conversation-starters-callsite.ts +6 -9
- package/src/workspace/migrations/054-seed-recall-callsite.ts +10 -1
- package/src/workspace/migrations/057-repair-stale-gemini-model-ids.ts +28 -4
- package/src/workspace/migrations/069-seed-onboarding-threads.ts +8 -2
- package/src/workspace/migrations/072-seed-reply-suggestion-callsite.ts +104 -0
- package/src/workspace/migrations/073-repair-recall-callsite-empty-profile.ts +93 -0
- package/src/workspace/migrations/074-drop-deprecated-secret-detection-keys.ts +117 -0
- package/src/workspace/migrations/075-memory-v2-bm25-b-default-reembed.ts +61 -0
- package/src/workspace/migrations/076-drop-services-inference-mode.ts +62 -0
- package/src/workspace/migrations/077-seed-memory-router-callsite.ts +89 -0
- package/src/workspace/migrations/078-release-notes-tavily-web-search.ts +66 -0
- package/src/workspace/migrations/079-home-feed-notification-only.ts +197 -0
- package/src/workspace/migrations/080-restrict-vercel-api-token-metadata.ts +182 -0
- package/src/workspace/migrations/081-backfill-bash-allowed-tools-for-injection-credentials.ts +160 -0
- package/src/workspace/migrations/082-backfill-managed-profile-labels.ts +154 -0
- package/src/workspace/migrations/registry.ts +22 -0
- package/src/workspace/migrations/runner.ts +13 -2
- package/src/workspace/migrations/types.ts +13 -3
- package/src/workspace/provider-commit-message-generator.ts +3 -2
- package/src/__tests__/context-search-pkb-source.test.ts +0 -498
- package/src/__tests__/credentials-cli.test.ts +0 -1225
- package/src/__tests__/memory-admin-recall.test.ts +0 -213
- package/src/approvals/__tests__/guardian-feed-event.test.ts +0 -303
- package/src/cli/commands/__tests__/email-download.test.ts +0 -260
- package/src/cli/commands/__tests__/email-list.test.ts +0 -216
- package/src/cli/commands/__tests__/email-register.test.ts +0 -186
- package/src/cli/commands/__tests__/email-send.test.ts +0 -416
- package/src/cli/commands/__tests__/email-status.test.ts +0 -185
- package/src/cli/commands/__tests__/email-unregister.test.ts +0 -168
- package/src/cli/commands/__tests__/routes.test.ts +0 -562
- package/src/cli/commands/__tests__/stt-transcribe.test.ts +0 -454
- package/src/cli/commands/autonomy.ts +0 -365
- package/src/cli/commands/memory.ts +0 -424
- package/src/cli/commands/oauth/__tests__/connect.test.ts +0 -947
- package/src/cli/commands/oauth/__tests__/disconnect.test.ts +0 -686
- package/src/cli/commands/oauth/__tests__/mode.test.ts +0 -632
- package/src/cli/commands/oauth/__tests__/ping.test.ts +0 -631
- package/src/cli/commands/oauth/__tests__/providers-delete.test.ts +0 -573
- package/src/cli/commands/oauth/__tests__/providers-register.test.ts +0 -330
- package/src/cli/commands/oauth/__tests__/providers-update.test.ts +0 -521
- package/src/cli/commands/oauth/__tests__/status.test.ts +0 -551
- package/src/cli/commands/oauth/__tests__/token.test.ts +0 -420
- package/src/cli/lib/daemon-avatar-client.ts +0 -37
- package/src/config/bundled-skills/contacts/tools/contact-upsert.ts +0 -87
- package/src/config/bundled-skills/messaging/tools/__tests__/messaging-feed-events.test.ts +0 -207
- package/src/daemon/__tests__/conversation-feed-event.test.ts +0 -304
- package/src/heartbeat/__tests__/heartbeat-feed-event.test.ts +0 -233
- package/src/home/__tests__/assistant-feed-authoring.test.ts +0 -156
- package/src/home/__tests__/emit-feed-event.test.ts +0 -169
- package/src/home/__tests__/feed-population-integration.test.ts +0 -312
- package/src/home/__tests__/feed-scheduler.test.ts +0 -222
- package/src/home/__tests__/phase5-exit-criteria.test.ts +0 -229
- package/src/home/__tests__/platform-gmail-digest.test.ts +0 -222
- package/src/home/__tests__/rollup-producer.test.ts +0 -507
- package/src/home/assistant-feed-authoring.ts +0 -135
- package/src/home/emit-feed-event.ts +0 -169
- package/src/home/feed-scheduler.ts +0 -281
- package/src/home/platform-gmail-digest.ts +0 -163
- package/src/home/rewrite-command-preview.ts +0 -66
- package/src/home/rewrite-feed-title.ts +0 -58
- package/src/home/rollup-producer.ts +0 -426
- package/src/memory/admin.ts +0 -326
- package/src/memory/context-search/sources/pkb.ts +0 -476
- package/src/memory/graph/compaction.ts +0 -299
- /package/src/cli/{commands → lib}/cache-fs.ts +0 -0
|
@@ -203,7 +203,7 @@ describe("embedConceptPageJob — happy path", () => {
|
|
|
203
203
|
test("reads the page, embeds it, and upserts to the v2 collection", async () => {
|
|
204
204
|
await writePage(tmpWorkspace, {
|
|
205
205
|
slug: "alice-prefers-vs-code",
|
|
206
|
-
frontmatter: { edges: [], ref_files: [] },
|
|
206
|
+
frontmatter: { edges: [], ref_files: [], ref_urls: [] },
|
|
207
207
|
body: "Alice prefers VS Code over Vim.\nShe ships at end of day.\n",
|
|
208
208
|
});
|
|
209
209
|
|
|
@@ -231,7 +231,7 @@ describe("embedConceptPageJob — happy path", () => {
|
|
|
231
231
|
test("populates the SQLite embedding cache row keyed on (concept_page, slug)", async () => {
|
|
232
232
|
await writePage(tmpWorkspace, {
|
|
233
233
|
slug: "bob-uses-zsh",
|
|
234
|
-
frontmatter: { edges: [], ref_files: [] },
|
|
234
|
+
frontmatter: { edges: [], ref_files: [], ref_urls: [] },
|
|
235
235
|
body: "Bob uses zsh.\n",
|
|
236
236
|
});
|
|
237
237
|
|
|
@@ -257,6 +257,7 @@ describe("embedConceptPageJob — summary embedding", () => {
|
|
|
257
257
|
frontmatter: {
|
|
258
258
|
edges: [],
|
|
259
259
|
ref_files: [],
|
|
260
|
+
ref_urls: [],
|
|
260
261
|
summary: "A short prose summary that retrieval indexes separately.",
|
|
261
262
|
},
|
|
262
263
|
body: "Long-form body content.\n",
|
|
@@ -282,7 +283,7 @@ describe("embedConceptPageJob — summary embedding", () => {
|
|
|
282
283
|
test("skips summary embedding when the page has no summary in frontmatter", async () => {
|
|
283
284
|
await writePage(tmpWorkspace, {
|
|
284
285
|
slug: "legacy-page",
|
|
285
|
-
frontmatter: { edges: [], ref_files: [] },
|
|
286
|
+
frontmatter: { edges: [], ref_files: [], ref_urls: [] },
|
|
286
287
|
body: "Body only — no summary in frontmatter.\n",
|
|
287
288
|
});
|
|
288
289
|
|
|
@@ -303,6 +304,7 @@ describe("embedConceptPageJob — summary embedding", () => {
|
|
|
303
304
|
frontmatter: {
|
|
304
305
|
edges: [],
|
|
305
306
|
ref_files: [],
|
|
307
|
+
ref_urls: [],
|
|
306
308
|
summary: " ",
|
|
307
309
|
},
|
|
308
310
|
body: "Body content.\n",
|
|
@@ -324,14 +326,12 @@ describe("embedConceptPageJob — summary embedding", () => {
|
|
|
324
326
|
frontmatter: {
|
|
325
327
|
edges: [],
|
|
326
328
|
ref_files: [],
|
|
329
|
+
ref_urls: [],
|
|
327
330
|
summary: "First version of the summary.",
|
|
328
331
|
},
|
|
329
332
|
body: "Stable body that never changes.\n",
|
|
330
333
|
});
|
|
331
|
-
await embedConceptPageJob(
|
|
332
|
-
makeJob({ slug: "cached-summary" }),
|
|
333
|
-
TEST_CONFIG,
|
|
334
|
-
);
|
|
334
|
+
await embedConceptPageJob(makeJob({ slug: "cached-summary" }), TEST_CONFIG);
|
|
335
335
|
// Body + summary batched into a single backend call on first run.
|
|
336
336
|
expect(embedWithBackendCalls).toHaveLength(1);
|
|
337
337
|
expect(embedWithBackendCalls[0].inputs).toHaveLength(2);
|
|
@@ -344,14 +344,12 @@ describe("embedConceptPageJob — summary embedding", () => {
|
|
|
344
344
|
frontmatter: {
|
|
345
345
|
edges: [],
|
|
346
346
|
ref_files: [],
|
|
347
|
+
ref_urls: [],
|
|
347
348
|
summary: "Second version of the summary, different wording.",
|
|
348
349
|
},
|
|
349
350
|
body: "Stable body that never changes.\n",
|
|
350
351
|
});
|
|
351
|
-
await embedConceptPageJob(
|
|
352
|
-
makeJob({ slug: "cached-summary" }),
|
|
353
|
-
TEST_CONFIG,
|
|
354
|
-
);
|
|
352
|
+
await embedConceptPageJob(makeJob({ slug: "cached-summary" }), TEST_CONFIG);
|
|
355
353
|
// One additional backend call with only the summary text — body hit the cache.
|
|
356
354
|
expect(embedWithBackendCalls).toHaveLength(2);
|
|
357
355
|
expect(embedWithBackendCalls[1].inputs).toHaveLength(1);
|
|
@@ -362,7 +360,7 @@ describe("embedConceptPageJob — cache hit", () => {
|
|
|
362
360
|
test("reuses the cached dense vector when content hash matches", async () => {
|
|
363
361
|
await writePage(tmpWorkspace, {
|
|
364
362
|
slug: "alice-prefers-vs-code",
|
|
365
|
-
frontmatter: { edges: [], ref_files: [] },
|
|
363
|
+
frontmatter: { edges: [], ref_files: [], ref_urls: [] },
|
|
366
364
|
body: "Stable content.\n",
|
|
367
365
|
});
|
|
368
366
|
|
|
@@ -387,7 +385,7 @@ describe("embedConceptPageJob — cache hit", () => {
|
|
|
387
385
|
test("re-embeds when the body changes (content hash mismatch)", async () => {
|
|
388
386
|
await writePage(tmpWorkspace, {
|
|
389
387
|
slug: "alice-prefers-vs-code",
|
|
390
|
-
frontmatter: { edges: [], ref_files: [] },
|
|
388
|
+
frontmatter: { edges: [], ref_files: [], ref_urls: [] },
|
|
391
389
|
body: "First content.\n",
|
|
392
390
|
});
|
|
393
391
|
await embedConceptPageJob(
|
|
@@ -398,7 +396,7 @@ describe("embedConceptPageJob — cache hit", () => {
|
|
|
398
396
|
// Rewrite with different body.
|
|
399
397
|
await writePage(tmpWorkspace, {
|
|
400
398
|
slug: "alice-prefers-vs-code",
|
|
401
|
-
frontmatter: { edges: [], ref_files: [] },
|
|
399
|
+
frontmatter: { edges: [], ref_files: [], ref_urls: [] },
|
|
402
400
|
body: "Second content (different).\n",
|
|
403
401
|
});
|
|
404
402
|
await embedConceptPageJob(
|
|
@@ -453,7 +451,7 @@ describe("enqueueEmbedConceptPageJob", () => {
|
|
|
453
451
|
test("round-trip: enqueued job dispatches through embedConceptPageJob", async () => {
|
|
454
452
|
await writePage(tmpWorkspace, {
|
|
455
453
|
slug: "round-trip-slug",
|
|
456
|
-
frontmatter: { edges: [], ref_files: [] },
|
|
454
|
+
frontmatter: { edges: [], ref_files: [], ref_urls: [] },
|
|
457
455
|
body: "Round-trip body.\n",
|
|
458
456
|
});
|
|
459
457
|
|
|
@@ -163,21 +163,54 @@ export async function embedConceptPageJob(
|
|
|
163
163
|
slots.push("summary");
|
|
164
164
|
}
|
|
165
165
|
|
|
166
|
-
let bodyDense: number[] | undefined = bodyCacheHit
|
|
166
|
+
let bodyDense: number[] | undefined = bodyCacheHit
|
|
167
|
+
? bodyCache!.dense
|
|
168
|
+
: undefined;
|
|
167
169
|
let summaryDense: number[] | undefined = summaryCacheHit
|
|
168
170
|
? summaryCache!.dense
|
|
169
171
|
: undefined;
|
|
170
172
|
let writeProvider = cacheProvider;
|
|
171
173
|
let writeModel = cacheModel;
|
|
174
|
+
let bodyFresh = false;
|
|
175
|
+
let summaryFresh = false;
|
|
172
176
|
if (toEmbed.length > 0) {
|
|
173
|
-
|
|
177
|
+
let embedded = await embedWithBackend(config, toEmbed);
|
|
178
|
+
let appliedSlots = slots;
|
|
179
|
+
// Backend rotation between `getMemoryBackendStatus()` and
|
|
180
|
+
// `embedWithBackend()` would tag the cached half with the old
|
|
181
|
+
// provider/model and the fresh half with the new — writing both into
|
|
182
|
+
// one Qdrant point mixes embedding spaces. Re-embed every slot fresh
|
|
183
|
+
// when we detect the rotation so the point's named vectors share one
|
|
184
|
+
// identity.
|
|
185
|
+
const rotated =
|
|
186
|
+
(bodyCacheHit || summaryCacheHit) &&
|
|
187
|
+
(embedded.provider !== cacheProvider || embedded.model !== cacheModel);
|
|
188
|
+
if (rotated) {
|
|
189
|
+
const allTexts: Array<{ type: "text"; text: string }> = [
|
|
190
|
+
{ type: "text", text },
|
|
191
|
+
];
|
|
192
|
+
const allSlots: Slot[] = ["body"];
|
|
193
|
+
if (hasSummary) {
|
|
194
|
+
allTexts.push({ type: "text", text: summaryText });
|
|
195
|
+
allSlots.push("summary");
|
|
196
|
+
}
|
|
197
|
+
embedded = await embedWithBackend(config, allTexts);
|
|
198
|
+
appliedSlots = allSlots;
|
|
199
|
+
bodyDense = undefined;
|
|
200
|
+
summaryDense = undefined;
|
|
201
|
+
}
|
|
174
202
|
writeProvider = embedded.provider;
|
|
175
203
|
writeModel = embedded.model;
|
|
176
|
-
for (let i = 0; i <
|
|
204
|
+
for (let i = 0; i < appliedSlots.length; i++) {
|
|
177
205
|
const vector = embedded.vectors[i];
|
|
178
206
|
if (!vector) continue;
|
|
179
|
-
if (
|
|
180
|
-
|
|
207
|
+
if (appliedSlots[i] === "body") {
|
|
208
|
+
bodyDense = vector;
|
|
209
|
+
bodyFresh = true;
|
|
210
|
+
} else {
|
|
211
|
+
summaryDense = vector;
|
|
212
|
+
summaryFresh = true;
|
|
213
|
+
}
|
|
181
214
|
}
|
|
182
215
|
}
|
|
183
216
|
// Body embedding is the ground truth — without it the page can't surface.
|
|
@@ -205,9 +238,12 @@ export async function embedConceptPageJob(
|
|
|
205
238
|
const now = Date.now();
|
|
206
239
|
// Persist freshly embedded vectors for cross-restart reuse. On cache hit
|
|
207
240
|
// the existing row already has identical content + hash, so the write
|
|
208
|
-
// would be a no-op — skip it.
|
|
209
|
-
//
|
|
210
|
-
|
|
241
|
+
// would be a no-op — skip it. Backend rotation flips a cache hit into a
|
|
242
|
+
// fresh embed (see `rotated` above); the `*Fresh` flags capture that so
|
|
243
|
+
// the new vector overwrites the now-stale cache row under the new
|
|
244
|
+
// provider/model identity. Best-effort: write failure is not fatal, we
|
|
245
|
+
// still want the Qdrant upsert below to fire.
|
|
246
|
+
if (bodyFresh) {
|
|
211
247
|
writeEmbeddingCache(db, {
|
|
212
248
|
slug,
|
|
213
249
|
cacheId: slug,
|
|
@@ -218,7 +254,7 @@ export async function embedConceptPageJob(
|
|
|
218
254
|
now,
|
|
219
255
|
});
|
|
220
256
|
}
|
|
221
|
-
if (hasSummary &&
|
|
257
|
+
if (hasSummary && summaryFresh && summaryDense && summaryContentHash) {
|
|
222
258
|
writeEmbeddingCache(db, {
|
|
223
259
|
slug,
|
|
224
260
|
cacheId: summaryCacheId,
|
package/src/memory/jobs-store.ts
CHANGED
|
@@ -42,7 +42,8 @@ export type MemoryJobType =
|
|
|
42
42
|
| "memory_v2_consolidate"
|
|
43
43
|
| "memory_v2_migrate"
|
|
44
44
|
| "memory_v2_reembed"
|
|
45
|
-
| "memory_v2_activation_recompute"
|
|
45
|
+
| "memory_v2_activation_recompute"
|
|
46
|
+
| "memory_retrospective";
|
|
46
47
|
|
|
47
48
|
export const EMBED_JOB_TYPES: MemoryJobType[] = [
|
|
48
49
|
"embed_segment",
|
|
@@ -52,6 +53,7 @@ export const EMBED_JOB_TYPES: MemoryJobType[] = [
|
|
|
52
53
|
"embed_graph_node",
|
|
53
54
|
"embed_pkb_file",
|
|
54
55
|
"graph_trigger_embed",
|
|
56
|
+
"embed_concept_page",
|
|
55
57
|
];
|
|
56
58
|
|
|
57
59
|
export const SLOW_LLM_JOB_TYPES: MemoryJobType[] = [
|
|
@@ -65,6 +67,7 @@ export const SLOW_LLM_JOB_TYPES: MemoryJobType[] = [
|
|
|
65
67
|
"memory_v2_sweep",
|
|
66
68
|
"memory_v2_consolidate",
|
|
67
69
|
"memory_v2_migrate",
|
|
70
|
+
"memory_retrospective",
|
|
68
71
|
"backfill",
|
|
69
72
|
"graph_bootstrap",
|
|
70
73
|
];
|
|
@@ -251,6 +254,53 @@ export function upsertAutoAnalysisJob(
|
|
|
251
254
|
}
|
|
252
255
|
}
|
|
253
256
|
|
|
257
|
+
/**
|
|
258
|
+
* Upsert a pending `memory_retrospective` job keyed by `conversationId`. All
|
|
259
|
+
* four retrospective triggers (interval, message_count, compaction,
|
|
260
|
+
* lifecycle) collapse into a single pending row per conversation — rapid
|
|
261
|
+
* triggers coalesce instead of double-firing. The `runAfter` parameter on a
|
|
262
|
+
* follow-up enqueue overwrites the existing row's `runAfter` so a sooner
|
|
263
|
+
* trigger can pull a later-scheduled job earlier; a later-scheduled trigger
|
|
264
|
+
* does NOT push a sooner-scheduled row further out (consumer takes the
|
|
265
|
+
* minimum). The trigger metadata is intentionally not retained — it is only
|
|
266
|
+
* useful for observability at enqueue time.
|
|
267
|
+
*/
|
|
268
|
+
export function upsertMemoryRetrospectiveJob(
|
|
269
|
+
payload: { conversationId: string },
|
|
270
|
+
runAfter: number = Date.now(),
|
|
271
|
+
dbOverride?: Parameters<ReturnType<typeof getDb>["transaction"]>[0] extends (
|
|
272
|
+
tx: infer T,
|
|
273
|
+
) => unknown
|
|
274
|
+
? T
|
|
275
|
+
: never,
|
|
276
|
+
): void {
|
|
277
|
+
const db = dbOverride ?? getDb();
|
|
278
|
+
const existing = db
|
|
279
|
+
.select()
|
|
280
|
+
.from(memoryJobs)
|
|
281
|
+
.where(
|
|
282
|
+
and(
|
|
283
|
+
eq(memoryJobs.type, "memory_retrospective"),
|
|
284
|
+
eq(memoryJobs.status, "pending"),
|
|
285
|
+
sql`json_extract(${memoryJobs.payload}, '$.conversationId') = ${payload.conversationId}`,
|
|
286
|
+
),
|
|
287
|
+
)
|
|
288
|
+
.get();
|
|
289
|
+
if (existing) {
|
|
290
|
+
// Take the minimum of the existing and incoming runAfter so the earliest
|
|
291
|
+
// trigger always wins. A later trigger never pushes work further out.
|
|
292
|
+
const nextRunAfter = Math.min(existing.runAfter, runAfter);
|
|
293
|
+
if (nextRunAfter !== existing.runAfter) {
|
|
294
|
+
db.update(memoryJobs)
|
|
295
|
+
.set({ runAfter: nextRunAfter, updatedAt: Date.now() })
|
|
296
|
+
.where(eq(memoryJobs.id, existing.id))
|
|
297
|
+
.run();
|
|
298
|
+
}
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
301
|
+
enqueueMemoryJob("memory_retrospective", payload, runAfter, dbOverride);
|
|
302
|
+
}
|
|
303
|
+
|
|
254
304
|
/**
|
|
255
305
|
* Check whether a pending or running job of the given type already exists.
|
|
256
306
|
* Used to prevent duplicate enqueues for long-running maintenance jobs.
|
|
@@ -67,6 +67,8 @@ import {
|
|
|
67
67
|
resetRunningJobsToPending,
|
|
68
68
|
SLOW_LLM_JOB_TYPES,
|
|
69
69
|
} from "./jobs-store.js";
|
|
70
|
+
import { memoryRetrospectiveJob } from "./memory-retrospective-job.js";
|
|
71
|
+
import { sweepOrphanMemoryRetrospectiveConversations } from "./memory-retrospective-startup-cleanup.js";
|
|
70
72
|
import { QdrantCircuitOpenError } from "./qdrant-circuit-breaker.js";
|
|
71
73
|
import {
|
|
72
74
|
memoryV2ActivationRecomputeJob,
|
|
@@ -78,6 +80,26 @@ import { memoryV2SweepJob } from "./v2/sweep-job.js";
|
|
|
78
80
|
|
|
79
81
|
const log = getLogger("memory-jobs-worker");
|
|
80
82
|
|
|
83
|
+
/**
|
|
84
|
+
* V1 job types that read or write the v1 Qdrant collection via
|
|
85
|
+
* `getQdrantClient()`. When `memory.v2.enabled` is true, the v1 client is
|
|
86
|
+
* intentionally left uninitialized in `lifecycle.ts`, so these handlers would
|
|
87
|
+
* throw `BackendUnavailableError` and accumulate as a deferred backlog. Stale
|
|
88
|
+
* rows from indexer.ts and other unguarded enqueue sites must short-circuit
|
|
89
|
+
* here for the same reason `graph_extract` does below.
|
|
90
|
+
*/
|
|
91
|
+
const V1_QDRANT_JOB_TYPES = new Set<MemoryJobType>([
|
|
92
|
+
"embed_segment",
|
|
93
|
+
"embed_summary",
|
|
94
|
+
"embed_media",
|
|
95
|
+
"embed_attachment",
|
|
96
|
+
"embed_graph_node",
|
|
97
|
+
"embed_pkb_file",
|
|
98
|
+
"graph_trigger_embed",
|
|
99
|
+
"rebuild_index",
|
|
100
|
+
"delete_qdrant_vectors",
|
|
101
|
+
]);
|
|
102
|
+
|
|
81
103
|
/**
|
|
82
104
|
* Job types whose handlers have been removed. Existing rows may still sit in
|
|
83
105
|
* the database — the worker completes them silently instead of throwing.
|
|
@@ -111,6 +133,19 @@ export function startMemoryJobsWorker(): MemoryJobsWorker {
|
|
|
111
133
|
log.info({ recovered }, "Recovered stale running memory jobs");
|
|
112
134
|
}
|
|
113
135
|
|
|
136
|
+
// After running-job recovery (so legitimate in-flight retries aren't
|
|
137
|
+
// swept), clean up orphan memory-retrospective background conversations
|
|
138
|
+
// left behind by daemon crashes mid-job. Best-effort — never block worker
|
|
139
|
+
// startup on cleanup failures.
|
|
140
|
+
try {
|
|
141
|
+
sweepOrphanMemoryRetrospectiveConversations();
|
|
142
|
+
} catch (err) {
|
|
143
|
+
log.warn(
|
|
144
|
+
{ err },
|
|
145
|
+
"Memory-retrospective startup cleanup failed; continuing worker startup",
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
|
|
114
149
|
let stopped = false;
|
|
115
150
|
let tickRunning = false;
|
|
116
151
|
let timer: ReturnType<typeof setTimeout>;
|
|
@@ -124,16 +159,24 @@ export function startMemoryJobsWorker(): MemoryJobsWorker {
|
|
|
124
159
|
enableScheduledCleanup: true,
|
|
125
160
|
});
|
|
126
161
|
if (processed > 0) {
|
|
127
|
-
|
|
162
|
+
// Per-tick claim budget equals the lane caps, so when a tick
|
|
163
|
+
// processed work the next tick must run immediately to drain any
|
|
164
|
+
// remaining backlog. Holding the 1.5s floor between ticks would cap
|
|
165
|
+
// sustained throughput at lane-cap jobs per 1.5s and starve large
|
|
166
|
+
// backlogs of short jobs.
|
|
167
|
+
currentIntervalMs = 0;
|
|
128
168
|
} else {
|
|
129
169
|
currentIntervalMs = Math.min(
|
|
130
|
-
currentIntervalMs * 2,
|
|
170
|
+
Math.max(currentIntervalMs * 2, POLL_INTERVAL_MIN_MS),
|
|
131
171
|
POLL_INTERVAL_MAX_MS,
|
|
132
172
|
);
|
|
133
173
|
}
|
|
134
174
|
} catch (err) {
|
|
135
175
|
log.error({ err }, "Memory worker tick failed");
|
|
136
|
-
currentIntervalMs = Math.min(
|
|
176
|
+
currentIntervalMs = Math.min(
|
|
177
|
+
Math.max(currentIntervalMs * 2, POLL_INTERVAL_MIN_MS),
|
|
178
|
+
POLL_INTERVAL_MAX_MS,
|
|
179
|
+
);
|
|
137
180
|
} finally {
|
|
138
181
|
tickRunning = false;
|
|
139
182
|
}
|
|
@@ -462,6 +505,9 @@ async function processJob(
|
|
|
462
505
|
job: MemoryJob,
|
|
463
506
|
config: AssistantConfig,
|
|
464
507
|
): Promise<void> {
|
|
508
|
+
if (config.memory.v2.enabled && V1_QDRANT_JOB_TYPES.has(job.type)) {
|
|
509
|
+
return;
|
|
510
|
+
}
|
|
465
511
|
switch (job.type) {
|
|
466
512
|
case "embed_segment":
|
|
467
513
|
await embedSegmentJob(job, config);
|
|
@@ -555,6 +601,9 @@ async function processJob(
|
|
|
555
601
|
case "memory_v2_activation_recompute":
|
|
556
602
|
await memoryV2ActivationRecomputeJob(job, config);
|
|
557
603
|
return;
|
|
604
|
+
case "memory_retrospective":
|
|
605
|
+
await memoryRetrospectiveJob(job, config);
|
|
606
|
+
return;
|
|
558
607
|
|
|
559
608
|
default: {
|
|
560
609
|
const rawType = (job as { type: string }).type;
|
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ClickHouse-backed LLM request log read source.
|
|
3
|
+
*
|
|
4
|
+
* Reads from the ClickHouse mirror (populated out-of-band by the
|
|
5
|
+
* `mirror-llm-logs-to-clickhouse` cron). Scoped to the running
|
|
6
|
+
* assistant's own `assistant_id` — never cross-assistant. URL and
|
|
7
|
+
* password are resolved lazily from the credential store
|
|
8
|
+
* (`clickhouse:url`, `clickhouse:password`); database/table/user come
|
|
9
|
+
* from workspace config.
|
|
10
|
+
*
|
|
11
|
+
* Known limitation: the mirror is INSERT-only. A row inserted locally
|
|
12
|
+
* with `message_id = NULL` and backfilled later will appear in
|
|
13
|
+
* ClickHouse with `message_id = ''` forever. Reads via this source for
|
|
14
|
+
* the most-recent ~minute of activity therefore have lower fidelity
|
|
15
|
+
* than the local source. Acceptable for the "internal use while we
|
|
16
|
+
* finetune prompts" use case; revisit when mirror updates are added.
|
|
17
|
+
*/
|
|
18
|
+
import type { LlmRequestLogsClickHouseConfig } from "../config/schemas/llm-request-logs.js";
|
|
19
|
+
import { credentialKey } from "../security/credential-key.js";
|
|
20
|
+
import { getSecureKeyAsync } from "../security/secure-keys.js";
|
|
21
|
+
import { getLogger } from "../util/logger.js";
|
|
22
|
+
import {
|
|
23
|
+
getAssistantMessageIdsInTurn,
|
|
24
|
+
getMessageById,
|
|
25
|
+
messageMetadataSchema,
|
|
26
|
+
} from "./conversation-crud.js";
|
|
27
|
+
import type { LlmRequestLogSource } from "./llm-request-log-source.js";
|
|
28
|
+
import type { LogRow } from "./llm-request-log-store.js";
|
|
29
|
+
|
|
30
|
+
const log = getLogger("clickhouse-llm-request-log-source");
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Read a credential and normalize `undefined` → `null`. The credential
|
|
34
|
+
* resolver factories on this class are typed `() => Promise<string | null>`;
|
|
35
|
+
* `getSecureKeyAsync` returns `Promise<string | undefined>`. Keep the
|
|
36
|
+
* coercion in one place so TypeScript stays happy without per-call casts.
|
|
37
|
+
*/
|
|
38
|
+
async function readCredentialOrNull(
|
|
39
|
+
service: string,
|
|
40
|
+
field: string,
|
|
41
|
+
): Promise<string | null> {
|
|
42
|
+
const value = await getSecureKeyAsync(credentialKey(service, field));
|
|
43
|
+
return value ?? null;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Wire-format row returned by ClickHouse for our query columns. Note
|
|
48
|
+
* that `created_at` arrives as a string because Int64 is emitted as a
|
|
49
|
+
* quoted string under the default `output_format_json_quote_64bit_integers=1`
|
|
50
|
+
* setting; we coerce to `number` in `toLogRow`.
|
|
51
|
+
*/
|
|
52
|
+
interface ClickHouseRow {
|
|
53
|
+
id: string;
|
|
54
|
+
conversation_id: string;
|
|
55
|
+
message_id: string;
|
|
56
|
+
provider: string;
|
|
57
|
+
request_payload: string;
|
|
58
|
+
response_payload: string;
|
|
59
|
+
created_at: string;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** Injectable fetch override for tests. Defaults to globalThis.fetch. */
|
|
63
|
+
export type ClickHouseFetch = typeof fetch;
|
|
64
|
+
|
|
65
|
+
/** Minimal subset of the SQLite message row the fork-source fallback needs. */
|
|
66
|
+
export interface ClickHouseMessageRow {
|
|
67
|
+
metadata: string | null;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export interface ClickHouseLlmRequestLogSourceDeps {
|
|
71
|
+
/** Override the credential read for `clickhouse:url`. */
|
|
72
|
+
resolveUrl?: () => Promise<string | null>;
|
|
73
|
+
/** Override the credential read for `clickhouse:password`. */
|
|
74
|
+
resolvePassword?: () => Promise<string | null>;
|
|
75
|
+
/** Override the credential read for `vellum:platform_assistant_id`. */
|
|
76
|
+
resolveAssistantId?: () => Promise<string | null>;
|
|
77
|
+
/** Override the turn-id resolver (default: `getAssistantMessageIdsInTurn`). */
|
|
78
|
+
resolveTurnMessageIds?: (messageId: string) => string[];
|
|
79
|
+
/** Override the message lookup (default: `getMessageById`). */
|
|
80
|
+
resolveMessage?: (messageId: string) => ClickHouseMessageRow | null;
|
|
81
|
+
/** Override fetch for testing. */
|
|
82
|
+
fetchImpl?: ClickHouseFetch;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export class ClickHouseLlmRequestLogSource implements LlmRequestLogSource {
|
|
86
|
+
private cachedUrl: string | null = null;
|
|
87
|
+
private cachedPassword: string | null = null;
|
|
88
|
+
private cachedAssistantId: string | null = null;
|
|
89
|
+
|
|
90
|
+
private readonly resolveUrl: () => Promise<string | null>;
|
|
91
|
+
private readonly resolvePassword: () => Promise<string | null>;
|
|
92
|
+
private readonly resolveAssistantId: () => Promise<string | null>;
|
|
93
|
+
private readonly resolveTurnMessageIds: (messageId: string) => string[];
|
|
94
|
+
private readonly resolveMessage: (
|
|
95
|
+
messageId: string,
|
|
96
|
+
) => ClickHouseMessageRow | null;
|
|
97
|
+
private readonly fetchImpl: ClickHouseFetch;
|
|
98
|
+
|
|
99
|
+
constructor(
|
|
100
|
+
private readonly config: LlmRequestLogsClickHouseConfig,
|
|
101
|
+
deps: ClickHouseLlmRequestLogSourceDeps = {},
|
|
102
|
+
) {
|
|
103
|
+
this.resolveUrl =
|
|
104
|
+
deps.resolveUrl ?? (() => readCredentialOrNull("clickhouse", "url"));
|
|
105
|
+
this.resolvePassword =
|
|
106
|
+
deps.resolvePassword ??
|
|
107
|
+
(() => readCredentialOrNull("clickhouse", "password"));
|
|
108
|
+
this.resolveAssistantId =
|
|
109
|
+
deps.resolveAssistantId ??
|
|
110
|
+
(() => readCredentialOrNull("vellum", "platform_assistant_id"));
|
|
111
|
+
this.resolveTurnMessageIds =
|
|
112
|
+
deps.resolveTurnMessageIds ?? getAssistantMessageIdsInTurn;
|
|
113
|
+
this.resolveMessage = deps.resolveMessage ?? getMessageById;
|
|
114
|
+
this.fetchImpl = deps.fetchImpl ?? globalThis.fetch.bind(globalThis);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
async getRequestLogById(logId: string): Promise<LogRow | null> {
|
|
118
|
+
const aid = await this.assistantId();
|
|
119
|
+
const sql = `SELECT
|
|
120
|
+
id,
|
|
121
|
+
conversation_id,
|
|
122
|
+
message_id,
|
|
123
|
+
provider,
|
|
124
|
+
request_payload,
|
|
125
|
+
response_payload,
|
|
126
|
+
toUnixTimestamp64Milli(created_at) AS created_at
|
|
127
|
+
FROM ${this.tableRef()}
|
|
128
|
+
WHERE assistant_id = {assistant_id:String}
|
|
129
|
+
AND id = {log_id:String}
|
|
130
|
+
ORDER BY created_at DESC
|
|
131
|
+
LIMIT 1
|
|
132
|
+
FORMAT JSONEachRow`;
|
|
133
|
+
const rows = await this.exec(sql, { assistant_id: aid, log_id: logId });
|
|
134
|
+
return rows[0] ? this.toLogRow(rows[0]) : null;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
async getRequestLogsByMessageId(messageId: string): Promise<LogRow[]> {
|
|
138
|
+
const turnIds = this.resolveTurnMessageIds(messageId);
|
|
139
|
+
let rows = await this.selectByMessageIds(turnIds);
|
|
140
|
+
|
|
141
|
+
if (rows.length === 0) {
|
|
142
|
+
// Fork-source fallback. Mirror behavior of the local source: when no
|
|
143
|
+
// logs match the queried message's turn, see if it was forked from
|
|
144
|
+
// another and resolve that source's turn. The fork relationship lives
|
|
145
|
+
// in local SQLite (message.metadata.forkSourceMessageId), not CH.
|
|
146
|
+
const message = this.resolveMessage(messageId);
|
|
147
|
+
if (message?.metadata) {
|
|
148
|
+
try {
|
|
149
|
+
const parsed = messageMetadataSchema.safeParse(
|
|
150
|
+
JSON.parse(message.metadata),
|
|
151
|
+
);
|
|
152
|
+
const sourceMessageId =
|
|
153
|
+
parsed.success &&
|
|
154
|
+
typeof parsed.data.forkSourceMessageId === "string"
|
|
155
|
+
? parsed.data.forkSourceMessageId
|
|
156
|
+
: null;
|
|
157
|
+
if (sourceMessageId && sourceMessageId !== messageId) {
|
|
158
|
+
const sourceTurnIds = this.resolveTurnMessageIds(sourceMessageId);
|
|
159
|
+
rows = await this.selectByMessageIds(sourceTurnIds);
|
|
160
|
+
}
|
|
161
|
+
} catch {
|
|
162
|
+
// metadata not JSON / schema mismatch — no fork fallback, return []
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
return rows.sort(
|
|
168
|
+
(a, b) => a.createdAt - b.createdAt || a.id.localeCompare(b.id),
|
|
169
|
+
);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
private async selectByMessageIds(ids: string[]): Promise<LogRow[]> {
|
|
173
|
+
if (ids.length === 0) return [];
|
|
174
|
+
const aid = await this.assistantId();
|
|
175
|
+
// ClickHouse Array(String) URL encoding is fiddly; the message IDs are
|
|
176
|
+
// server-generated UUIDs (or safe internal strings), so inline the
|
|
177
|
+
// literal after escaping single quotes. No SQL-injection surface — the
|
|
178
|
+
// values originate from our own SQLite messages table.
|
|
179
|
+
const idLiteral =
|
|
180
|
+
"[" +
|
|
181
|
+
ids.map((id) => `'${id.replace(/'/g, "''")}'`).join(",") +
|
|
182
|
+
"]";
|
|
183
|
+
const sql = `SELECT
|
|
184
|
+
id,
|
|
185
|
+
conversation_id,
|
|
186
|
+
message_id,
|
|
187
|
+
provider,
|
|
188
|
+
request_payload,
|
|
189
|
+
response_payload,
|
|
190
|
+
toUnixTimestamp64Milli(created_at) AS created_at
|
|
191
|
+
FROM ${this.tableRef()}
|
|
192
|
+
WHERE assistant_id = {assistant_id:String}
|
|
193
|
+
AND message_id IN ${idLiteral}
|
|
194
|
+
ORDER BY created_at ASC, id ASC
|
|
195
|
+
LIMIT 1 BY id
|
|
196
|
+
FORMAT JSONEachRow`;
|
|
197
|
+
const rows = await this.exec(sql, { assistant_id: aid });
|
|
198
|
+
return rows.map((r) => this.toLogRow(r));
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
private tableRef(): string {
|
|
202
|
+
// Database is set via the `database=` URL param in `exec`, so we only
|
|
203
|
+
// need to quote the table identifier here. Backtick-quote both to
|
|
204
|
+
// tolerate non-default names with special characters.
|
|
205
|
+
return `\`${this.config.table.replace(/`/g, "``")}\``;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
private async exec(
|
|
209
|
+
sql: string,
|
|
210
|
+
params: Record<string, string>,
|
|
211
|
+
): Promise<ClickHouseRow[]> {
|
|
212
|
+
const baseUrl = await this.url();
|
|
213
|
+
const password = await this.password();
|
|
214
|
+
|
|
215
|
+
let target: URL;
|
|
216
|
+
try {
|
|
217
|
+
target = new URL(baseUrl);
|
|
218
|
+
} catch (err) {
|
|
219
|
+
throw new Error(
|
|
220
|
+
`clickhouse:url is not a valid URL: ${err instanceof Error ? err.message : String(err)}`,
|
|
221
|
+
);
|
|
222
|
+
}
|
|
223
|
+
target.searchParams.set("database", this.config.database);
|
|
224
|
+
for (const [k, v] of Object.entries(params)) {
|
|
225
|
+
target.searchParams.set(`param_${k}`, v);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
const auth =
|
|
229
|
+
"Basic " +
|
|
230
|
+
Buffer.from(`${this.config.user}:${password}`, "utf8").toString("base64");
|
|
231
|
+
|
|
232
|
+
const res = await this.fetchImpl(target.toString(), {
|
|
233
|
+
method: "POST",
|
|
234
|
+
headers: { Authorization: auth, "Content-Type": "text/plain" },
|
|
235
|
+
body: sql,
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
if (!res.ok) {
|
|
239
|
+
const body = await res.text().catch(() => "");
|
|
240
|
+
log.error(
|
|
241
|
+
{ status: res.status, table: this.config.table, bodySnippet: body.slice(0, 200) },
|
|
242
|
+
"ClickHouse query failed",
|
|
243
|
+
);
|
|
244
|
+
throw new Error(
|
|
245
|
+
`ClickHouse query failed (HTTP ${res.status}): ${body.slice(0, 500)}`,
|
|
246
|
+
);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
const text = await res.text();
|
|
250
|
+
if (text.trim().length === 0) return [];
|
|
251
|
+
|
|
252
|
+
const rows: ClickHouseRow[] = [];
|
|
253
|
+
for (const line of text.split("\n")) {
|
|
254
|
+
const trimmed = line.trim();
|
|
255
|
+
if (trimmed.length === 0) continue;
|
|
256
|
+
try {
|
|
257
|
+
rows.push(JSON.parse(trimmed) as ClickHouseRow);
|
|
258
|
+
} catch (err) {
|
|
259
|
+
throw new Error(
|
|
260
|
+
`Failed to parse ClickHouse JSONEachRow line: ${err instanceof Error ? err.message : String(err)}`,
|
|
261
|
+
);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
return rows;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
private toLogRow(row: ClickHouseRow): LogRow {
|
|
268
|
+
return {
|
|
269
|
+
id: row.id,
|
|
270
|
+
conversationId: row.conversation_id,
|
|
271
|
+
// The mirror writes empty-string for missing message_id/provider
|
|
272
|
+
// because the CH table columns have `DEFAULT ''` (Nullable adds
|
|
273
|
+
// overhead). Map empty back to null to match the local LogRow shape.
|
|
274
|
+
messageId: row.message_id === "" ? null : row.message_id,
|
|
275
|
+
provider: row.provider === "" ? null : row.provider,
|
|
276
|
+
requestPayload: row.request_payload,
|
|
277
|
+
responsePayload: row.response_payload,
|
|
278
|
+
createdAt: Number(row.created_at),
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
private async assistantId(): Promise<string> {
|
|
283
|
+
if (this.cachedAssistantId) return this.cachedAssistantId;
|
|
284
|
+
const val = await this.resolveAssistantId();
|
|
285
|
+
if (!val) {
|
|
286
|
+
throw new Error(
|
|
287
|
+
"vellum:platform_assistant_id credential is required when readSource=clickhouse",
|
|
288
|
+
);
|
|
289
|
+
}
|
|
290
|
+
this.cachedAssistantId = val;
|
|
291
|
+
return val;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
private async url(): Promise<string> {
|
|
295
|
+
if (this.cachedUrl) return this.cachedUrl;
|
|
296
|
+
const val = await this.resolveUrl();
|
|
297
|
+
if (!val) {
|
|
298
|
+
throw new Error(
|
|
299
|
+
"clickhouse:url credential is required when readSource=clickhouse",
|
|
300
|
+
);
|
|
301
|
+
}
|
|
302
|
+
this.cachedUrl = val;
|
|
303
|
+
return val;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
private async password(): Promise<string> {
|
|
307
|
+
if (this.cachedPassword) return this.cachedPassword;
|
|
308
|
+
const val = await this.resolvePassword();
|
|
309
|
+
if (!val) {
|
|
310
|
+
throw new Error(
|
|
311
|
+
"clickhouse:password credential is required when readSource=clickhouse",
|
|
312
|
+
);
|
|
313
|
+
}
|
|
314
|
+
this.cachedPassword = val;
|
|
315
|
+
return val;
|
|
316
|
+
}
|
|
317
|
+
}
|