@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
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { describe, expect, test } from "bun:test";
|
|
2
2
|
|
|
3
|
+
import { CALL_SITE_DEFAULTS } from "../config/call-site-defaults.js";
|
|
3
4
|
import { getLLMCallSiteLabel } from "../config/llm-callsite-catalog.js";
|
|
4
5
|
import { CALL_SITE_CATALOG } from "../config/schemas/call-site-catalog.js";
|
|
5
6
|
import { LLMCallSiteEnum, LLMSchema } from "../config/schemas/llm.js";
|
|
@@ -50,4 +51,28 @@ describe("LLM call-site catalog", () => {
|
|
|
50
51
|
});
|
|
51
52
|
expect(parsed.callSites.memoryRouter?.model).toBe("claude-sonnet-4-6");
|
|
52
53
|
});
|
|
54
|
+
|
|
55
|
+
test("CALL_SITE_DEFAULTS covers every LLMCallSite enum value", () => {
|
|
56
|
+
const defaultIds = new Set(Object.keys(CALL_SITE_DEFAULTS));
|
|
57
|
+
const missing = LLMCallSiteEnum.options.filter(
|
|
58
|
+
(id) => !defaultIds.has(id),
|
|
59
|
+
);
|
|
60
|
+
expect(missing).toEqual([]);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
test("CALL_SITE_DEFAULTS contains no unknown call-site keys", () => {
|
|
64
|
+
const enumIds = new Set<string>(LLMCallSiteEnum.options);
|
|
65
|
+
const extra = Object.keys(CALL_SITE_DEFAULTS).filter(
|
|
66
|
+
(id) => !enumIds.has(id),
|
|
67
|
+
);
|
|
68
|
+
expect(extra).toEqual([]);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
test("every CALL_SITE_DEFAULTS entry has a profile field", () => {
|
|
72
|
+
for (const [, config] of Object.entries(CALL_SITE_DEFAULTS)) {
|
|
73
|
+
expect(config.profile).toBeDefined();
|
|
74
|
+
expect(typeof config.profile).toBe("string");
|
|
75
|
+
expect(config.profile!.length).toBeGreaterThan(0);
|
|
76
|
+
}
|
|
77
|
+
});
|
|
53
78
|
});
|
|
@@ -2,8 +2,8 @@ import { readFileSync } from "node:fs";
|
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
import { describe, expect, test } from "bun:test";
|
|
4
4
|
|
|
5
|
-
import { MANAGED_PROVIDER_META } from "../providers/managed-proxy/constants.js";
|
|
6
5
|
import { PROVIDER_CATALOG } from "../providers/model-catalog.js";
|
|
6
|
+
import { PLATFORM_PROVIDER_META } from "../providers/platform-proxy/constants.js";
|
|
7
7
|
import { resolvePricing, resolvePricingForUsage } from "../util/pricing.js";
|
|
8
8
|
|
|
9
9
|
/**
|
|
@@ -29,6 +29,15 @@ function getRepoRoot(): string {
|
|
|
29
29
|
return join(process.cwd(), "..");
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
+
const META_JSON_PATH = join(getRepoRoot(), "meta", "llm-provider-catalog.json");
|
|
33
|
+
const SWIFTPM_MIRROR_PATH = join(
|
|
34
|
+
getRepoRoot(),
|
|
35
|
+
"clients",
|
|
36
|
+
"shared",
|
|
37
|
+
"Resources",
|
|
38
|
+
"llm-provider-catalog.json",
|
|
39
|
+
);
|
|
40
|
+
|
|
32
41
|
interface ClientCatalogCredentialsGuide {
|
|
33
42
|
description: string;
|
|
34
43
|
url: string;
|
|
@@ -71,7 +80,7 @@ interface ClientCatalogEntry {
|
|
|
71
80
|
envVar?: string;
|
|
72
81
|
apiKeyPlaceholder?: string;
|
|
73
82
|
credentialsGuide?: ClientCatalogCredentialsGuide;
|
|
74
|
-
|
|
83
|
+
supportsPlatformAuth?: boolean;
|
|
75
84
|
defaultModel: string;
|
|
76
85
|
models: ClientCatalogModel[];
|
|
77
86
|
}
|
|
@@ -82,8 +91,7 @@ interface ClientCatalog {
|
|
|
82
91
|
}
|
|
83
92
|
|
|
84
93
|
function loadClientCatalog(): ClientCatalog {
|
|
85
|
-
const
|
|
86
|
-
const raw = readFileSync(catalogPath, "utf-8");
|
|
94
|
+
const raw = readFileSync(META_JSON_PATH, "utf-8");
|
|
87
95
|
return JSON.parse(raw);
|
|
88
96
|
}
|
|
89
97
|
|
|
@@ -124,8 +132,8 @@ describe("LLM catalog parity: daemon vs client", () => {
|
|
|
124
132
|
expect(clientEntry.setupHint).toBe(daemonEntry.setupHint);
|
|
125
133
|
expect(clientEntry.envVar).toBe(daemonEntry.envVar);
|
|
126
134
|
expect(clientEntry.apiKeyPlaceholder).toBe(daemonEntry.apiKeyPlaceholder);
|
|
127
|
-
expect(clientEntry.
|
|
128
|
-
daemonEntry.
|
|
135
|
+
expect(clientEntry.supportsPlatformAuth).toBe(
|
|
136
|
+
daemonEntry.supportsPlatformAuth,
|
|
129
137
|
);
|
|
130
138
|
expect(clientEntry.credentialsGuide).toEqual(
|
|
131
139
|
daemonEntry.credentialsGuide,
|
|
@@ -134,16 +142,16 @@ describe("LLM catalog parity: daemon vs client", () => {
|
|
|
134
142
|
}
|
|
135
143
|
});
|
|
136
144
|
|
|
137
|
-
test("
|
|
138
|
-
// The catalog field is derived from
|
|
145
|
+
test("supportsPlatformAuth mirrors PLATFORM_PROVIDER_META", () => {
|
|
146
|
+
// The catalog field is derived from PLATFORM_PROVIDER_META at build
|
|
139
147
|
// time. This test guards against future hand-edits to model-catalog.ts
|
|
140
|
-
// that would let the two drift. Adding a provider to
|
|
148
|
+
// that would let the two drift. Adding a provider to PLATFORM_PROVIDER_META
|
|
141
149
|
// must auto-propagate; flipping `managed: true` to `false` (or vice
|
|
142
150
|
// versa) must propagate too.
|
|
143
151
|
for (const entry of PROVIDER_CATALOG) {
|
|
144
152
|
const expectedSupportsManagedAuth =
|
|
145
|
-
|
|
146
|
-
expect(entry.
|
|
153
|
+
PLATFORM_PROVIDER_META[entry.id]?.managed === true;
|
|
154
|
+
expect(entry.supportsPlatformAuth).toBe(expectedSupportsManagedAuth);
|
|
147
155
|
}
|
|
148
156
|
});
|
|
149
157
|
|
|
@@ -200,6 +208,9 @@ describe("LLM catalog parity: daemon vs client", () => {
|
|
|
200
208
|
|
|
201
209
|
test("every provider's defaultModel exists in its models list", () => {
|
|
202
210
|
for (const entry of PROVIDER_CATALOG) {
|
|
211
|
+
// Providers with an empty models list (e.g. openai-compatible) use
|
|
212
|
+
// per-connection model identifiers instead of a static catalog.
|
|
213
|
+
if (entry.models.length === 0) continue;
|
|
203
214
|
const found = entry.models.some((m) => m.id === entry.defaultModel);
|
|
204
215
|
expect(
|
|
205
216
|
found,
|
|
@@ -208,6 +219,26 @@ describe("LLM catalog parity: daemon vs client", () => {
|
|
|
208
219
|
}
|
|
209
220
|
});
|
|
210
221
|
|
|
222
|
+
test("cache pricing rates are positive when defined", () => {
|
|
223
|
+
for (const entry of PROVIDER_CATALOG) {
|
|
224
|
+
for (const model of entry.models) {
|
|
225
|
+
if (!model.pricing) continue;
|
|
226
|
+
if (model.pricing.cacheReadPer1mTokens !== undefined) {
|
|
227
|
+
expect(
|
|
228
|
+
model.pricing.cacheReadPer1mTokens,
|
|
229
|
+
`${entry.id}/${model.id} cacheReadPer1mTokens must be positive`,
|
|
230
|
+
).toBeGreaterThan(0);
|
|
231
|
+
}
|
|
232
|
+
if (model.pricing.cacheWritePer1mTokens !== undefined) {
|
|
233
|
+
expect(
|
|
234
|
+
model.pricing.cacheWritePer1mTokens,
|
|
235
|
+
`${entry.id}/${model.id} cacheWritePer1mTokens must be positive`,
|
|
236
|
+
).toBeGreaterThan(0);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
});
|
|
241
|
+
|
|
211
242
|
test("every model default context is capped by its context window", () => {
|
|
212
243
|
const json = loadClientCatalog();
|
|
213
244
|
|
|
@@ -383,8 +414,7 @@ describe("LLM catalog parity: daemon vs client", () => {
|
|
|
383
414
|
`${provider.id}/${model.id} unpriced at tier ${tier.inputTokenThreshold}`,
|
|
384
415
|
).toBe("priced");
|
|
385
416
|
const resolvedRate =
|
|
386
|
-
(result.estimatedCostUsd ?? 0) *
|
|
387
|
-
(TOKENS_PER_MILLION / probeTokens);
|
|
417
|
+
(result.estimatedCostUsd ?? 0) * (TOKENS_PER_MILLION / probeTokens);
|
|
388
418
|
expect(
|
|
389
419
|
resolvedRate,
|
|
390
420
|
`${provider.id}/${model.id} input rate drift at tier ${tier.inputTokenThreshold}`,
|
|
@@ -407,4 +437,19 @@ describe("LLM catalog parity: daemon vs client", () => {
|
|
|
407
437
|
longContextMode: "native-model",
|
|
408
438
|
});
|
|
409
439
|
});
|
|
440
|
+
|
|
441
|
+
// -----------------------------------------------------------------------
|
|
442
|
+
// Mirror byte-equality
|
|
443
|
+
// -----------------------------------------------------------------------
|
|
444
|
+
|
|
445
|
+
test("SwiftPM mirror is byte-identical to meta/ copy", () => {
|
|
446
|
+
// `sync-llm-catalog.ts` writes both files from the same serializer; this
|
|
447
|
+
// guard catches any case where one copy is regenerated without the other.
|
|
448
|
+
// Byte equality is required because SwiftPM bundles the resource verbatim
|
|
449
|
+
// into `VellumAssistantShared` while the meta/ JSON is consumed as a
|
|
450
|
+
// cross-package artifact (web codegen, etc.).
|
|
451
|
+
const metaBytes = readFileSync(META_JSON_PATH);
|
|
452
|
+
const swiftPmBytes = readFileSync(SWIFTPM_MIRROR_PATH);
|
|
453
|
+
expect(swiftPmBytes.equals(metaBytes)).toBe(true);
|
|
454
|
+
});
|
|
410
455
|
});
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for `setAgentLoopExitReasonOnLatestLog` and the
|
|
3
|
+
* `agentLoopExitReason` field on the LogRow type. The helper stamps the
|
|
4
|
+
* reason onto the most-recent `llm_request_logs` row for a conversation,
|
|
5
|
+
* which is how downstream tooling distinguishes "loop kept going" (null)
|
|
6
|
+
* from "loop exited because X" (specific reason) on a per-row basis.
|
|
7
|
+
*/
|
|
8
|
+
import { beforeEach, describe, expect, mock, test } from "bun:test";
|
|
9
|
+
|
|
10
|
+
mock.module("../util/logger.js", () => ({
|
|
11
|
+
getLogger: () =>
|
|
12
|
+
new Proxy({} as Record<string, unknown>, {
|
|
13
|
+
get: () => () => {},
|
|
14
|
+
}),
|
|
15
|
+
}));
|
|
16
|
+
|
|
17
|
+
mock.module("../config/loader.js", () => ({
|
|
18
|
+
getConfig: () => ({
|
|
19
|
+
ui: {},
|
|
20
|
+
model: "test",
|
|
21
|
+
provider: "test",
|
|
22
|
+
memory: { enabled: false },
|
|
23
|
+
rateLimit: { maxRequestsPerMinute: 0 },
|
|
24
|
+
secretDetection: { enabled: false },
|
|
25
|
+
}),
|
|
26
|
+
}));
|
|
27
|
+
|
|
28
|
+
import { getDb } from "../memory/db-connection.js";
|
|
29
|
+
import { initializeDb } from "../memory/db-init.js";
|
|
30
|
+
import {
|
|
31
|
+
getRequestLogById,
|
|
32
|
+
recordRequestLog,
|
|
33
|
+
setAgentLoopExitReasonOnLatestLog,
|
|
34
|
+
} from "../memory/llm-request-log-store.js";
|
|
35
|
+
import { llmRequestLogs } from "../memory/schema.js";
|
|
36
|
+
|
|
37
|
+
initializeDb();
|
|
38
|
+
|
|
39
|
+
function resetLogs(): void {
|
|
40
|
+
const db = getDb();
|
|
41
|
+
db.delete(llmRequestLogs).run();
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
describe("setAgentLoopExitReasonOnLatestLog", () => {
|
|
45
|
+
beforeEach(resetLogs);
|
|
46
|
+
|
|
47
|
+
test("recordRequestLog leaves agentLoopExitReason NULL", () => {
|
|
48
|
+
const id = recordRequestLog("conv-1", '{"req":1}', '{"res":1}');
|
|
49
|
+
const row = getRequestLogById(id);
|
|
50
|
+
expect(row).not.toBeNull();
|
|
51
|
+
expect(row!.agentLoopExitReason).toBeNull();
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
test("stamps the reason onto the most-recent log for the conversation", () => {
|
|
55
|
+
const first = recordRequestLog("conv-1", '{"req":1}', '{"res":1}');
|
|
56
|
+
// Ensure createdAt strict ordering — `recordRequestLog` uses
|
|
57
|
+
// `Date.now()` and bun-sqlite is fast enough that two consecutive
|
|
58
|
+
// inserts can share a millisecond. Sleep a tick to disambiguate.
|
|
59
|
+
Bun.sleepSync(2);
|
|
60
|
+
const second = recordRequestLog("conv-1", '{"req":2}', '{"res":2}');
|
|
61
|
+
|
|
62
|
+
setAgentLoopExitReasonOnLatestLog("conv-1", "no_tool_calls");
|
|
63
|
+
|
|
64
|
+
expect(getRequestLogById(first)?.agentLoopExitReason).toBeNull();
|
|
65
|
+
expect(getRequestLogById(second)?.agentLoopExitReason).toBe(
|
|
66
|
+
"no_tool_calls",
|
|
67
|
+
);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
test("scopes the stamp to the given conversation only", () => {
|
|
71
|
+
const a = recordRequestLog("conv-a", '{"req":1}', '{"res":1}');
|
|
72
|
+
Bun.sleepSync(2);
|
|
73
|
+
const b = recordRequestLog("conv-b", '{"req":1}', '{"res":1}');
|
|
74
|
+
|
|
75
|
+
setAgentLoopExitReasonOnLatestLog("conv-a", "yield_to_user");
|
|
76
|
+
|
|
77
|
+
expect(getRequestLogById(a)?.agentLoopExitReason).toBe("yield_to_user");
|
|
78
|
+
// conv-b is later overall but belongs to a different conversation —
|
|
79
|
+
// must stay NULL.
|
|
80
|
+
expect(getRequestLogById(b)?.agentLoopExitReason).toBeNull();
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
test("no-op when conversation has no logs", () => {
|
|
84
|
+
expect(() =>
|
|
85
|
+
setAgentLoopExitReasonOnLatestLog("conv-missing", "error"),
|
|
86
|
+
).not.toThrow();
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
test("does not clobber a previous run's reason when the current run never landed a row", () => {
|
|
90
|
+
// Previous run: completes, lands a log, gets stamped.
|
|
91
|
+
const prev = recordRequestLog("conv-1", '{"prev_req":1}', '{"prev_res":1}');
|
|
92
|
+
setAgentLoopExitReasonOnLatestLog("conv-1", "no_tool_calls");
|
|
93
|
+
expect(getRequestLogById(prev)?.agentLoopExitReason).toBe("no_tool_calls");
|
|
94
|
+
|
|
95
|
+
// Current run aborts pre-call (or similar) before any LLM call lands.
|
|
96
|
+
// The helper must NOT overwrite the previous run's row.
|
|
97
|
+
setAgentLoopExitReasonOnLatestLog("conv-1", "aborted_pre_call");
|
|
98
|
+
expect(getRequestLogById(prev)?.agentLoopExitReason).toBe("no_tool_calls");
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
test("stamps the current run's newest row even when a prior row is already stamped", () => {
|
|
102
|
+
// Prior run already stamped.
|
|
103
|
+
const prev = recordRequestLog("conv-1", '{"prev_req":1}', '{"prev_res":1}');
|
|
104
|
+
setAgentLoopExitReasonOnLatestLog("conv-1", "no_tool_calls");
|
|
105
|
+
|
|
106
|
+
// Current run lands a new log, then exits.
|
|
107
|
+
Bun.sleepSync(2);
|
|
108
|
+
const current = recordRequestLog("conv-1", '{"cur_req":1}', '{"cur_res":1}');
|
|
109
|
+
setAgentLoopExitReasonOnLatestLog("conv-1", "yield_to_user");
|
|
110
|
+
|
|
111
|
+
expect(getRequestLogById(prev)?.agentLoopExitReason).toBe("no_tool_calls");
|
|
112
|
+
expect(getRequestLogById(current)?.agentLoopExitReason).toBe(
|
|
113
|
+
"yield_to_user",
|
|
114
|
+
);
|
|
115
|
+
});
|
|
116
|
+
});
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit tests for `buildProviderErrorResponsePayload` — the shared serializer
|
|
3
|
+
* used by `handleProviderError` (daemon) and the wake-path `onEvent` to
|
|
4
|
+
* record provider-rejected LLM calls in `llm_request_logs`.
|
|
5
|
+
*
|
|
6
|
+
* The serializer's job: take an arbitrary thrown `Error`, return a
|
|
7
|
+
* structured `{ error: {...} }` object whose fields are queryable in the
|
|
8
|
+
* LLM inspector and that round-trips cleanly through `JSON.stringify`.
|
|
9
|
+
* The `error` key wrap is load-bearing — it mirrors a successful row's
|
|
10
|
+
* `usage.rawResponse` shape so an inspector consumer can branch on
|
|
11
|
+
* `responsePayload.error` vs the success shape without re-parsing.
|
|
12
|
+
*
|
|
13
|
+
* Coverage:
|
|
14
|
+
* - `ProviderError` with full metadata (provider, statusCode, retryAfterMs).
|
|
15
|
+
* - `ProviderError` without optional metadata.
|
|
16
|
+
* - Non-provider `AssistantError` (carries `code` but not provider fields).
|
|
17
|
+
* - Plain `Error` (degrades to `{name, message}`).
|
|
18
|
+
* - Custom `Error` subclass with overridden `name` is preserved.
|
|
19
|
+
*
|
|
20
|
+
* Each test stringifies and re-parses the payload so the on-disk shape
|
|
21
|
+
* (what eventually lands in the `responsePayload` column) is what we
|
|
22
|
+
* assert on, not the JS object identity.
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
import { describe, expect, test } from "bun:test";
|
|
26
|
+
|
|
27
|
+
import { buildProviderErrorResponsePayload } from "../memory/llm-request-log-store.js";
|
|
28
|
+
import {
|
|
29
|
+
AssistantError,
|
|
30
|
+
ErrorCode,
|
|
31
|
+
ProviderError,
|
|
32
|
+
} from "../util/errors.js";
|
|
33
|
+
|
|
34
|
+
function persisted(err: Error): { error: Record<string, unknown> } {
|
|
35
|
+
// Round-trip through JSON to assert on the actual stored shape, not the
|
|
36
|
+
// in-memory object reference.
|
|
37
|
+
return JSON.parse(
|
|
38
|
+
JSON.stringify(buildProviderErrorResponsePayload(err)),
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
describe("buildProviderErrorResponsePayload", () => {
|
|
43
|
+
test("ProviderError with statusCode + retryAfterMs serializes every queryable field", () => {
|
|
44
|
+
const err = new ProviderError(
|
|
45
|
+
"Anthropic API error (429): rate limited",
|
|
46
|
+
"anthropic",
|
|
47
|
+
429,
|
|
48
|
+
{ retryAfterMs: 1500 },
|
|
49
|
+
);
|
|
50
|
+
const got = persisted(err);
|
|
51
|
+
expect(got).toEqual({
|
|
52
|
+
error: {
|
|
53
|
+
name: "ProviderError",
|
|
54
|
+
message: "Anthropic API error (429): rate limited",
|
|
55
|
+
code: ErrorCode.PROVIDER_ERROR,
|
|
56
|
+
provider: "anthropic",
|
|
57
|
+
statusCode: 429,
|
|
58
|
+
retryAfterMs: 1500,
|
|
59
|
+
},
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
test("ProviderError without optional metadata omits statusCode + retryAfterMs", () => {
|
|
64
|
+
const err = new ProviderError(
|
|
65
|
+
"Gemini API error: surprise internal state",
|
|
66
|
+
"gemini",
|
|
67
|
+
);
|
|
68
|
+
const got = persisted(err);
|
|
69
|
+
expect(got).toEqual({
|
|
70
|
+
error: {
|
|
71
|
+
name: "ProviderError",
|
|
72
|
+
message: "Gemini API error: surprise internal state",
|
|
73
|
+
code: ErrorCode.PROVIDER_ERROR,
|
|
74
|
+
provider: "gemini",
|
|
75
|
+
},
|
|
76
|
+
});
|
|
77
|
+
// Explicit assertion: omitted fields aren't present as `null` either —
|
|
78
|
+
// the inspector should be able to test `'statusCode' in error` reliably.
|
|
79
|
+
expect("statusCode" in got.error).toBe(false);
|
|
80
|
+
expect("retryAfterMs" in got.error).toBe(false);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
test("non-provider AssistantError carries the ErrorCode but no provider fields", () => {
|
|
84
|
+
// Tool errors / permission denials are technically also AssistantErrors;
|
|
85
|
+
// we just want to confirm the generic AssistantError branch produces a
|
|
86
|
+
// sensible row rather than silently degrading to a plain Error shape.
|
|
87
|
+
const err = new AssistantError(
|
|
88
|
+
"internal state corrupted",
|
|
89
|
+
ErrorCode.INTERNAL_ERROR,
|
|
90
|
+
);
|
|
91
|
+
const got = persisted(err);
|
|
92
|
+
expect(got).toEqual({
|
|
93
|
+
error: {
|
|
94
|
+
name: "AssistantError",
|
|
95
|
+
message: "internal state corrupted",
|
|
96
|
+
code: ErrorCode.INTERNAL_ERROR,
|
|
97
|
+
},
|
|
98
|
+
});
|
|
99
|
+
expect("provider" in got.error).toBe(false);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
test("plain Error degrades to {name, message} with no code/provider noise", () => {
|
|
103
|
+
const err = new Error("connection reset");
|
|
104
|
+
const got = persisted(err);
|
|
105
|
+
expect(got).toEqual({
|
|
106
|
+
error: {
|
|
107
|
+
name: "Error",
|
|
108
|
+
message: "connection reset",
|
|
109
|
+
},
|
|
110
|
+
});
|
|
111
|
+
expect("code" in got.error).toBe(false);
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
test("custom Error subclass with overridden name is preserved", () => {
|
|
115
|
+
class TimeoutError extends Error {
|
|
116
|
+
constructor(message: string) {
|
|
117
|
+
super(message);
|
|
118
|
+
this.name = "TimeoutError";
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
const got = persisted(new TimeoutError("provider timed out after 60s"));
|
|
122
|
+
expect(got).toEqual({
|
|
123
|
+
error: {
|
|
124
|
+
name: "TimeoutError",
|
|
125
|
+
message: "provider timed out after 60s",
|
|
126
|
+
},
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
test("ProviderError with statusCode 0 is still recorded (not coerced to undefined)", () => {
|
|
131
|
+
// Defensive: `if (err.statusCode !== undefined)` correctly admits 0.
|
|
132
|
+
// A raw `if (err.statusCode)` would drop it, so the test guards against
|
|
133
|
+
// a regression to truthy-checking.
|
|
134
|
+
const err = new ProviderError("weird provider", "fake", 0);
|
|
135
|
+
const got = persisted(err);
|
|
136
|
+
expect(got.error.statusCode).toBe(0);
|
|
137
|
+
});
|
|
138
|
+
});
|
|
@@ -83,6 +83,7 @@ const SAMPLE_ROW = {
|
|
|
83
83
|
response_payload: '{"bar":2}',
|
|
84
84
|
// ClickHouse emits Int64 as a quoted string under JSONEachRow by default.
|
|
85
85
|
created_at: "1778465138786",
|
|
86
|
+
agent_loop_exit_reason: "no_tool_calls",
|
|
86
87
|
};
|
|
87
88
|
|
|
88
89
|
describe("ClickHouseLlmRequestLogSource", () => {
|
|
@@ -102,6 +103,7 @@ describe("ClickHouseLlmRequestLogSource", () => {
|
|
|
102
103
|
requestPayload: '{"foo":1}',
|
|
103
104
|
responsePayload: '{"bar":2}',
|
|
104
105
|
createdAt: 1778465138786,
|
|
106
|
+
agentLoopExitReason: "no_tool_calls",
|
|
105
107
|
});
|
|
106
108
|
});
|
|
107
109
|
|
|
@@ -162,6 +164,40 @@ describe("ClickHouseLlmRequestLogSource", () => {
|
|
|
162
164
|
expect(rows).toEqual([]);
|
|
163
165
|
});
|
|
164
166
|
|
|
167
|
+
test("getRequestLogsByMessageId binds message ids via parameterized placeholders", async () => {
|
|
168
|
+
// Regression for ATL-537. `getAssistantMessageIdsInTurn` returns the
|
|
169
|
+
// caller-supplied id straight through when the message lookup misses,
|
|
170
|
+
// so the value passed in here is what the IN clause receives. The id
|
|
171
|
+
// ends in a backslash on purpose: with the old inline-literal approach
|
|
172
|
+
// (quote-doubling only), ClickHouse would honor `\'` as an escaped
|
|
173
|
+
// quote and the string literal would break out of the IN clause.
|
|
174
|
+
// The fix binds each id as a typed `{id_N:String}` parameter, so the
|
|
175
|
+
// hostile content flows as data, never syntax.
|
|
176
|
+
const recorder: FakeFetchCall[] = [];
|
|
177
|
+
const malicious = "foo\\";
|
|
178
|
+
const src = makeSource({
|
|
179
|
+
body: "",
|
|
180
|
+
recorder,
|
|
181
|
+
resolveTurnMessageIds: () => [malicious, "msg-b"],
|
|
182
|
+
});
|
|
183
|
+
await src.getRequestLogsByMessageId(malicious);
|
|
184
|
+
expect(recorder).toHaveLength(1);
|
|
185
|
+
const call = recorder[0]!;
|
|
186
|
+
const parsed = new URL(call.url);
|
|
187
|
+
expect(parsed.searchParams.get("param_assistant_id")).toBe(
|
|
188
|
+
"asst-fixture-001",
|
|
189
|
+
);
|
|
190
|
+
expect(parsed.searchParams.get("param_id_0")).toBe(malicious);
|
|
191
|
+
expect(parsed.searchParams.get("param_id_1")).toBe("msg-b");
|
|
192
|
+
const body = String(call.init?.body ?? "");
|
|
193
|
+
expect(body).toContain("message_id IN ({id_0:String},{id_1:String})");
|
|
194
|
+
// No inline single-quoted literal should appear for the IN clause:
|
|
195
|
+
// if any caller-supplied id surfaces unbound in the SQL, that's the
|
|
196
|
+
// injection surface the regression test guards against.
|
|
197
|
+
expect(body).not.toContain(`'${malicious}'`);
|
|
198
|
+
expect(body).not.toContain(`'msg-b'`);
|
|
199
|
+
});
|
|
200
|
+
|
|
165
201
|
test("missing clickhouse:url credential surfaces a clear error", async () => {
|
|
166
202
|
const src = new ClickHouseLlmRequestLogSource(DEFAULT_CONFIG, {
|
|
167
203
|
resolveUrl: async () => null,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { afterAll,
|
|
1
|
+
import { afterAll, describe, expect, mock, test } from "bun:test";
|
|
2
2
|
|
|
3
3
|
function makeLoggerStub(): Record<string, unknown> {
|
|
4
4
|
const stub: Record<string, unknown> = {};
|
|
@@ -22,19 +22,10 @@ mock.module("../util/logger.js", () => ({
|
|
|
22
22
|
}));
|
|
23
23
|
|
|
24
24
|
// Configurable config stub. Each test sets `currentConfig` before calling
|
|
25
|
-
// the factory. The ClickHouse source is dynamic-imported, so its
|
|
26
|
-
// runs lazily; we don't need to mock conversation-crud here
|
|
27
|
-
// the factory
|
|
28
|
-
const LOCAL_DEFAULT_CONFIG = {
|
|
29
|
-
llmRequestLogs: {
|
|
30
|
-
readSource: "local",
|
|
31
|
-
clickhouse: {
|
|
32
|
-
database: "default",
|
|
33
|
-
table: "llm_request_logs",
|
|
34
|
-
user: "default",
|
|
35
|
-
},
|
|
36
|
-
},
|
|
37
|
-
};
|
|
25
|
+
// the factory. The ClickHouse source is dynamic-imported, so its
|
|
26
|
+
// constructor runs lazily; we don't need to mock conversation-crud here
|
|
27
|
+
// because none of the factory tests exercise the CH read path.
|
|
28
|
+
const LOCAL_DEFAULT_CONFIG = { llmRequestLogs: { readSource: "local" } };
|
|
38
29
|
let currentConfig: unknown = LOCAL_DEFAULT_CONFIG;
|
|
39
30
|
mock.module("../config/loader.js", () => ({
|
|
40
31
|
getConfig: () => currentConfig,
|
|
@@ -43,32 +34,18 @@ mock.module("../config/loader.js", () => ({
|
|
|
43
34
|
// Bun's `mock.module()` persists process-wide; reset to the safe local
|
|
44
35
|
// default after this file runs so other test files that touch this module
|
|
45
36
|
// (or transitively call `getConfig()` through the factory) don't pick up
|
|
46
|
-
// a stale `readSource=clickhouse` value.
|
|
47
|
-
// cache so the next caller resolves fresh against the reset config.
|
|
37
|
+
// a stale `readSource=clickhouse` value.
|
|
48
38
|
afterAll(() => {
|
|
49
39
|
currentConfig = LOCAL_DEFAULT_CONFIG;
|
|
50
|
-
invalidateLlmRequestLogSourceCache();
|
|
51
40
|
});
|
|
52
41
|
|
|
53
|
-
import {
|
|
54
|
-
getLlmRequestLogSource,
|
|
55
|
-
invalidateLlmRequestLogSourceCache,
|
|
56
|
-
} from "../memory/llm-request-log-source.js";
|
|
42
|
+
import { getLlmRequestLogSource } from "../memory/llm-request-log-source.js";
|
|
57
43
|
import { ClickHouseLlmRequestLogSource } from "../memory/llm-request-log-source-clickhouse.js";
|
|
58
44
|
import { LocalLlmRequestLogSource } from "../memory/llm-request-log-source-local.js";
|
|
59
45
|
|
|
60
46
|
describe("getLlmRequestLogSource factory", () => {
|
|
61
|
-
beforeEach(() => {
|
|
62
|
-
invalidateLlmRequestLogSourceCache();
|
|
63
|
-
});
|
|
64
|
-
|
|
65
47
|
test("returns LocalLlmRequestLogSource when readSource is 'local'", async () => {
|
|
66
|
-
currentConfig = {
|
|
67
|
-
llmRequestLogs: {
|
|
68
|
-
readSource: "local",
|
|
69
|
-
clickhouse: { database: "default", table: "llm_request_logs", user: "default" },
|
|
70
|
-
},
|
|
71
|
-
};
|
|
48
|
+
currentConfig = { llmRequestLogs: { readSource: "local" } };
|
|
72
49
|
const src = await getLlmRequestLogSource();
|
|
73
50
|
expect(src).toBeInstanceOf(LocalLlmRequestLogSource);
|
|
74
51
|
});
|
|
@@ -83,42 +60,41 @@ describe("getLlmRequestLogSource factory", () => {
|
|
|
83
60
|
currentConfig = {
|
|
84
61
|
llmRequestLogs: {
|
|
85
62
|
readSource: "clickhouse",
|
|
86
|
-
clickhouse: {
|
|
63
|
+
clickhouse: {
|
|
64
|
+
database: "default",
|
|
65
|
+
table: "llm_request_logs",
|
|
66
|
+
user: "default",
|
|
67
|
+
},
|
|
87
68
|
},
|
|
88
69
|
};
|
|
89
70
|
const src = await getLlmRequestLogSource();
|
|
90
71
|
expect(src).toBeInstanceOf(ClickHouseLlmRequestLogSource);
|
|
91
72
|
});
|
|
92
73
|
|
|
93
|
-
test("
|
|
94
|
-
currentConfig = {
|
|
95
|
-
llmRequestLogs: {
|
|
96
|
-
readSource: "local",
|
|
97
|
-
clickhouse: { database: "default", table: "llm_request_logs", user: "default" },
|
|
98
|
-
},
|
|
99
|
-
};
|
|
74
|
+
test("instantiates a fresh source on every call (no module-level cache)", async () => {
|
|
75
|
+
currentConfig = { llmRequestLogs: { readSource: "local" } };
|
|
100
76
|
const first = await getLlmRequestLogSource();
|
|
101
77
|
const second = await getLlmRequestLogSource();
|
|
102
|
-
expect(second).toBe(first);
|
|
78
|
+
expect(second).not.toBe(first);
|
|
79
|
+
expect(second).toBeInstanceOf(LocalLlmRequestLogSource);
|
|
103
80
|
});
|
|
104
81
|
|
|
105
|
-
test("
|
|
106
|
-
currentConfig = {
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
},
|
|
111
|
-
};
|
|
112
|
-
const first = await getLlmRequestLogSource();
|
|
113
|
-
invalidateLlmRequestLogSourceCache();
|
|
82
|
+
test("picks up live config changes without an invalidation hook", async () => {
|
|
83
|
+
currentConfig = { llmRequestLogs: { readSource: "local" } };
|
|
84
|
+
const before = await getLlmRequestLogSource();
|
|
85
|
+
expect(before).toBeInstanceOf(LocalLlmRequestLogSource);
|
|
86
|
+
|
|
114
87
|
currentConfig = {
|
|
115
88
|
llmRequestLogs: {
|
|
116
89
|
readSource: "clickhouse",
|
|
117
|
-
clickhouse: {
|
|
90
|
+
clickhouse: {
|
|
91
|
+
database: "default",
|
|
92
|
+
table: "llm_request_logs",
|
|
93
|
+
user: "default",
|
|
94
|
+
},
|
|
118
95
|
},
|
|
119
96
|
};
|
|
120
|
-
const
|
|
121
|
-
expect(
|
|
122
|
-
expect(second).toBeInstanceOf(ClickHouseLlmRequestLogSource);
|
|
97
|
+
const after = await getLlmRequestLogSource();
|
|
98
|
+
expect(after).toBeInstanceOf(ClickHouseLlmRequestLogSource);
|
|
123
99
|
});
|
|
124
100
|
});
|