@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
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Asserts `listConsolidationRuns` maps background-conversation rows tagged
|
|
3
|
+
* with `source = MEMORY_V2_CONSOLIDATION_SOURCE` into the heartbeat-runs
|
|
4
|
+
* response shape, derives `status` / `finishedAt` / `durationMs` from
|
|
5
|
+
* **assistant-message presence** (not `lastMessageAt`), and clamps the
|
|
6
|
+
* `limit` query param.
|
|
7
|
+
*
|
|
8
|
+
* Synthetic-field semantics covered here:
|
|
9
|
+
* - `id` and `conversationId` both equal the conversation row's id.
|
|
10
|
+
* - `scheduledFor` and `startedAt` both equal `conversation.createdAt`
|
|
11
|
+
* (no separate schedule timestamp on the row).
|
|
12
|
+
* - `finishedAt` is the `createdAt` of the LATEST assistant message,
|
|
13
|
+
* NOT `conversation.lastMessageAt` — the kickoff user prompt bumps
|
|
14
|
+
* `lastMessageAt` before the agent runs, so it cannot be used as a
|
|
15
|
+
* completion signal.
|
|
16
|
+
* - `durationMs` is `finishedAt − startedAt` when both are present, else
|
|
17
|
+
* null.
|
|
18
|
+
* - `status` is `"ok"` when the conversation has at least one assistant
|
|
19
|
+
* message (positive evidence the agent emitted output) and `"running"`
|
|
20
|
+
* otherwise — including the case where only the kickoff user prompt
|
|
21
|
+
* has been persisted.
|
|
22
|
+
* - `skipReason` and `error` are always null — the conversation row
|
|
23
|
+
* alone cannot distinguish a clean run from a mid-flight crash even
|
|
24
|
+
* once assistant output exists.
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
import { beforeEach, describe, expect, mock, test } from "bun:test";
|
|
28
|
+
|
|
29
|
+
mock.module("../../../util/logger.js", () => ({
|
|
30
|
+
getLogger: () =>
|
|
31
|
+
new Proxy({} as Record<string, unknown>, {
|
|
32
|
+
get: () => () => {},
|
|
33
|
+
}),
|
|
34
|
+
}));
|
|
35
|
+
|
|
36
|
+
import { createConversation } from "../../../memory/conversation-crud.js";
|
|
37
|
+
import { getDb } from "../../../memory/db-connection.js";
|
|
38
|
+
import { initializeDb } from "../../../memory/db-init.js";
|
|
39
|
+
import { rawRun } from "../../../memory/raw-query.js";
|
|
40
|
+
import { ROUTES } from "../consolidation-routes.js";
|
|
41
|
+
import type { RouteDefinition } from "../types.js";
|
|
42
|
+
|
|
43
|
+
initializeDb();
|
|
44
|
+
|
|
45
|
+
function resetTables(): void {
|
|
46
|
+
const db = getDb();
|
|
47
|
+
db.run(`DELETE FROM messages`);
|
|
48
|
+
db.run(`DELETE FROM conversations`);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function findHandler(operationId: string): RouteDefinition["handler"] {
|
|
52
|
+
const route = ROUTES.find((r) => r.operationId === operationId);
|
|
53
|
+
if (!route) throw new Error(`Route ${operationId} not found`);
|
|
54
|
+
return route.handler;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function insertMessage(
|
|
58
|
+
conversationId: string,
|
|
59
|
+
role: string,
|
|
60
|
+
createdAt: number,
|
|
61
|
+
): void {
|
|
62
|
+
rawRun(
|
|
63
|
+
"INSERT INTO messages (id, conversation_id, role, content, created_at) VALUES (?, ?, ?, ?, ?)",
|
|
64
|
+
`msg-${conversationId}-${role}-${createdAt}`,
|
|
65
|
+
conversationId,
|
|
66
|
+
role,
|
|
67
|
+
"x",
|
|
68
|
+
createdAt,
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
interface RunRecord {
|
|
73
|
+
id: string;
|
|
74
|
+
scheduledFor: number;
|
|
75
|
+
startedAt: number | null;
|
|
76
|
+
finishedAt: number | null;
|
|
77
|
+
durationMs: number | null;
|
|
78
|
+
status: "ok" | "running";
|
|
79
|
+
skipReason: string | null;
|
|
80
|
+
error: string | null;
|
|
81
|
+
conversationId: string | null;
|
|
82
|
+
createdAt: number;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
interface ListRunsResponse {
|
|
86
|
+
runs: RunRecord[];
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
describe("listConsolidationRuns handler", () => {
|
|
90
|
+
beforeEach(() => {
|
|
91
|
+
resetTables();
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
test("returns only conversations sourced from memory_v2_consolidation", async () => {
|
|
95
|
+
createConversation({ title: "c1", source: "memory_v2_consolidation" });
|
|
96
|
+
createConversation({ title: "h1", source: "heartbeat" });
|
|
97
|
+
createConversation({ title: "u1", source: "user" });
|
|
98
|
+
|
|
99
|
+
const handler = findHandler("listConsolidationRuns");
|
|
100
|
+
const result = (await handler({})) as ListRunsResponse;
|
|
101
|
+
|
|
102
|
+
expect(result.runs).toHaveLength(1);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
test("synthesizes status='ok' with finishedAt from latest assistant message", async () => {
|
|
106
|
+
const conv = createConversation({
|
|
107
|
+
title: "c1",
|
|
108
|
+
source: "memory_v2_consolidation",
|
|
109
|
+
});
|
|
110
|
+
rawRun(
|
|
111
|
+
"UPDATE conversations SET created_at = ? WHERE id = ?",
|
|
112
|
+
1000,
|
|
113
|
+
conv.id,
|
|
114
|
+
);
|
|
115
|
+
// Kickoff user prompt at t=1100 (bumps lastMessageAt — must NOT be
|
|
116
|
+
// mistaken for completion).
|
|
117
|
+
insertMessage(conv.id, "user", 1100);
|
|
118
|
+
// Agent's first assistant turn at t=2000.
|
|
119
|
+
insertMessage(conv.id, "assistant", 2000);
|
|
120
|
+
// Agent's final assistant turn at t=2500.
|
|
121
|
+
insertMessage(conv.id, "assistant", 2500);
|
|
122
|
+
|
|
123
|
+
const handler = findHandler("listConsolidationRuns");
|
|
124
|
+
const result = (await handler({})) as ListRunsResponse;
|
|
125
|
+
|
|
126
|
+
expect(result.runs).toHaveLength(1);
|
|
127
|
+
const run = result.runs[0]!;
|
|
128
|
+
expect(run.id).toBe(conv.id);
|
|
129
|
+
expect(run.conversationId).toBe(conv.id);
|
|
130
|
+
expect(run.status).toBe("ok");
|
|
131
|
+
expect(run.scheduledFor).toBe(1000);
|
|
132
|
+
expect(run.startedAt).toBe(1000);
|
|
133
|
+
// finishedAt = createdAt of LATEST assistant message (2500), NOT
|
|
134
|
+
// the conversation's lastMessageAt (which sqlite triggers may or may
|
|
135
|
+
// not have updated here — irrelevant to this endpoint).
|
|
136
|
+
expect(run.finishedAt).toBe(2500);
|
|
137
|
+
expect(run.durationMs).toBe(1500);
|
|
138
|
+
expect(run.createdAt).toBe(1000);
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
test("synthesizes status='running' when conversation has no assistant message", async () => {
|
|
142
|
+
createConversation({ title: "c1", source: "memory_v2_consolidation" });
|
|
143
|
+
|
|
144
|
+
const handler = findHandler("listConsolidationRuns");
|
|
145
|
+
const result = (await handler({})) as ListRunsResponse;
|
|
146
|
+
|
|
147
|
+
expect(result.runs).toHaveLength(1);
|
|
148
|
+
const run = result.runs[0]!;
|
|
149
|
+
expect(run.status).toBe("running");
|
|
150
|
+
expect(run.finishedAt).toBeNull();
|
|
151
|
+
expect(run.durationMs).toBeNull();
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
test("status stays 'running' when only the kickoff user prompt exists (Codex bug regression guard)", async () => {
|
|
155
|
+
// Regression guard for the original `status from lastMessageAt`
|
|
156
|
+
// heuristic. `processMessage` persists the background kickoff prompt as
|
|
157
|
+
// a user message BEFORE the agent runs, which bumps
|
|
158
|
+
// `conversation.lastMessageAt`. A run that timed out / threw before
|
|
159
|
+
// emitting any assistant turn must still report status='running' (or
|
|
160
|
+
// an explicit failure status once one exists) — never 'ok'.
|
|
161
|
+
const conv = createConversation({
|
|
162
|
+
title: "c1",
|
|
163
|
+
source: "memory_v2_consolidation",
|
|
164
|
+
});
|
|
165
|
+
rawRun(
|
|
166
|
+
"UPDATE conversations SET created_at = ?, last_message_at = ? WHERE id = ?",
|
|
167
|
+
1000,
|
|
168
|
+
1100,
|
|
169
|
+
conv.id,
|
|
170
|
+
);
|
|
171
|
+
insertMessage(conv.id, "user", 1100);
|
|
172
|
+
|
|
173
|
+
const handler = findHandler("listConsolidationRuns");
|
|
174
|
+
const result = (await handler({})) as ListRunsResponse;
|
|
175
|
+
|
|
176
|
+
expect(result.runs).toHaveLength(1);
|
|
177
|
+
const run = result.runs[0]!;
|
|
178
|
+
expect(run.status).toBe("running");
|
|
179
|
+
expect(run.finishedAt).toBeNull();
|
|
180
|
+
expect(run.durationMs).toBeNull();
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
test("skipReason and error are always null (not derivable from conversation row)", async () => {
|
|
184
|
+
const conv = createConversation({
|
|
185
|
+
title: "c1",
|
|
186
|
+
source: "memory_v2_consolidation",
|
|
187
|
+
});
|
|
188
|
+
insertMessage(conv.id, "assistant", 2000);
|
|
189
|
+
|
|
190
|
+
const handler = findHandler("listConsolidationRuns");
|
|
191
|
+
const result = (await handler({})) as ListRunsResponse;
|
|
192
|
+
|
|
193
|
+
expect(result.runs[0]!.skipReason).toBeNull();
|
|
194
|
+
expect(result.runs[0]!.error).toBeNull();
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
test("orders runs by createdAt descending", async () => {
|
|
198
|
+
const a = createConversation({
|
|
199
|
+
title: "a",
|
|
200
|
+
source: "memory_v2_consolidation",
|
|
201
|
+
});
|
|
202
|
+
const b = createConversation({
|
|
203
|
+
title: "b",
|
|
204
|
+
source: "memory_v2_consolidation",
|
|
205
|
+
});
|
|
206
|
+
const c = createConversation({
|
|
207
|
+
title: "c",
|
|
208
|
+
source: "memory_v2_consolidation",
|
|
209
|
+
});
|
|
210
|
+
rawRun("UPDATE conversations SET created_at = ? WHERE id = ?", 1000, a.id);
|
|
211
|
+
rawRun("UPDATE conversations SET created_at = ? WHERE id = ?", 3000, b.id);
|
|
212
|
+
rawRun("UPDATE conversations SET created_at = ? WHERE id = ?", 2000, c.id);
|
|
213
|
+
|
|
214
|
+
const handler = findHandler("listConsolidationRuns");
|
|
215
|
+
const result = (await handler({})) as ListRunsResponse;
|
|
216
|
+
|
|
217
|
+
expect(result.runs.map((r) => r.id)).toEqual([b.id, c.id, a.id]);
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
test("limit defaults to 20, clamps to [1, 100], and falls back on non-numeric input", async () => {
|
|
221
|
+
for (let i = 0; i < 5; i++) {
|
|
222
|
+
createConversation({
|
|
223
|
+
title: `c${i}`,
|
|
224
|
+
source: "memory_v2_consolidation",
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
const handler = findHandler("listConsolidationRuns");
|
|
229
|
+
|
|
230
|
+
// Default — all 5 returned (under the 20 default).
|
|
231
|
+
const def = (await handler({})) as ListRunsResponse;
|
|
232
|
+
expect(def.runs).toHaveLength(5);
|
|
233
|
+
|
|
234
|
+
// Explicit limit honored.
|
|
235
|
+
const lim2 = (await handler({
|
|
236
|
+
queryParams: { limit: "2" },
|
|
237
|
+
})) as ListRunsResponse;
|
|
238
|
+
expect(lim2.runs).toHaveLength(2);
|
|
239
|
+
|
|
240
|
+
// Negative clamps to 1.
|
|
241
|
+
const neg = (await handler({
|
|
242
|
+
queryParams: { limit: "-5" },
|
|
243
|
+
})) as ListRunsResponse;
|
|
244
|
+
expect(neg.runs).toHaveLength(1);
|
|
245
|
+
|
|
246
|
+
// Zero clamps to 1.
|
|
247
|
+
const zero = (await handler({
|
|
248
|
+
queryParams: { limit: "0" },
|
|
249
|
+
})) as ListRunsResponse;
|
|
250
|
+
expect(zero.runs).toHaveLength(1);
|
|
251
|
+
|
|
252
|
+
// Non-numeric falls back to the default (20 → all 5 here).
|
|
253
|
+
const bad = (await handler({
|
|
254
|
+
queryParams: { limit: "garbage" },
|
|
255
|
+
})) as ListRunsResponse;
|
|
256
|
+
expect(bad.runs).toHaveLength(5);
|
|
257
|
+
});
|
|
258
|
+
});
|
|
@@ -23,6 +23,7 @@ mock.module("../../assistant-event-hub.js", () => ({
|
|
|
23
23
|
publish: async () => {},
|
|
24
24
|
subscribe: () => () => {},
|
|
25
25
|
},
|
|
26
|
+
broadcastMessage: () => {},
|
|
26
27
|
}));
|
|
27
28
|
|
|
28
29
|
import { getDb } from "../../../memory/db-connection.js";
|
|
@@ -114,7 +115,10 @@ function seedConversation(id: string): void {
|
|
|
114
115
|
describe("PUT /v1/conversations/:id/inference-profile", () => {
|
|
115
116
|
beforeEach(() => {
|
|
116
117
|
clearConversations();
|
|
117
|
-
configLlmProfiles = {
|
|
118
|
+
configLlmProfiles = {
|
|
119
|
+
fast: { model: "model-a" },
|
|
120
|
+
slow: { model: "model-b" },
|
|
121
|
+
};
|
|
118
122
|
});
|
|
119
123
|
|
|
120
124
|
test("PUT with ttlSeconds=600 → response includes sessionId (UUID), expiresAt, ttlSeconds=600", async () => {
|
|
@@ -14,6 +14,12 @@ import {
|
|
|
14
14
|
|
|
15
15
|
let rawConfigFixture: Record<string, unknown> = {};
|
|
16
16
|
let savedRawConfig: Record<string, unknown> | null = null;
|
|
17
|
+
// Counters / spies so tests can assert that `commitConfigWrite` ran its
|
|
18
|
+
// post-write side effects. Each `replaceProfileRoute.handler` call that
|
|
19
|
+
// hits `commitConfigWrite` should bump these once.
|
|
20
|
+
let invalidateConfigCacheCalls = 0;
|
|
21
|
+
let initializeProvidersCalls = 0;
|
|
22
|
+
let clearEmbeddingBackendCacheCalls = 0;
|
|
17
23
|
|
|
18
24
|
mock.module("../../../config/loader.js", () => ({
|
|
19
25
|
loadRawConfig: () => structuredClone(rawConfigFixture),
|
|
@@ -26,6 +32,28 @@ mock.module("../../../config/loader.js", () => ({
|
|
|
26
32
|
) => {
|
|
27
33
|
Object.assign(target, overrides);
|
|
28
34
|
},
|
|
35
|
+
// `commitConfigWrite` (used by `handleReplaceInferenceProfile`) pulls
|
|
36
|
+
// in `getConfig` for the provider reinit's config arg and
|
|
37
|
+
// `invalidateConfigCache` so the next caller sees the fresh write.
|
|
38
|
+
// Stub both: getConfig returns whatever was last saved (or the fixture
|
|
39
|
+
// if nothing has been saved yet) and the cache-invalidation function
|
|
40
|
+
// is a counter so we can assert it fired.
|
|
41
|
+
getConfig: () => structuredClone(savedRawConfig ?? rawConfigFixture),
|
|
42
|
+
invalidateConfigCache: () => {
|
|
43
|
+
invalidateConfigCacheCalls += 1;
|
|
44
|
+
},
|
|
45
|
+
}));
|
|
46
|
+
|
|
47
|
+
mock.module("../../../providers/registry.js", () => ({
|
|
48
|
+
initializeProviders: async () => {
|
|
49
|
+
initializeProvidersCalls += 1;
|
|
50
|
+
},
|
|
51
|
+
}));
|
|
52
|
+
|
|
53
|
+
mock.module("../../../memory/embedding-backend.js", () => ({
|
|
54
|
+
clearEmbeddingBackendCache: () => {
|
|
55
|
+
clearEmbeddingBackendCacheCalls += 1;
|
|
56
|
+
},
|
|
29
57
|
}));
|
|
30
58
|
|
|
31
59
|
import type { ConversationCreateType } from "../../../memory/conversation-crud.js";
|
|
@@ -43,6 +71,10 @@ import {
|
|
|
43
71
|
memoryV2ActivationLogs,
|
|
44
72
|
messages,
|
|
45
73
|
} from "../../../memory/schema.js";
|
|
74
|
+
import {
|
|
75
|
+
createConnection,
|
|
76
|
+
getConnection,
|
|
77
|
+
} from "../../../providers/inference/connections.js";
|
|
46
78
|
import { ROUTES } from "../conversation-query-routes.js";
|
|
47
79
|
|
|
48
80
|
// Local subset: this test only exercises a single concept row.
|
|
@@ -288,6 +320,9 @@ describe("GET /v1/messages/:id/llm-context — conversationTotalEstimatedCostUsd
|
|
|
288
320
|
describe("PUT /v1/config/llm/profiles/:name", () => {
|
|
289
321
|
beforeEach(() => {
|
|
290
322
|
savedRawConfig = null;
|
|
323
|
+
invalidateConfigCacheCalls = 0;
|
|
324
|
+
initializeProvidersCalls = 0;
|
|
325
|
+
clearEmbeddingBackendCacheCalls = 0;
|
|
291
326
|
rawConfigFixture = {
|
|
292
327
|
llm: {
|
|
293
328
|
profiles: {
|
|
@@ -313,8 +348,8 @@ describe("PUT /v1/config/llm/profiles/:name", () => {
|
|
|
313
348
|
};
|
|
314
349
|
});
|
|
315
350
|
|
|
316
|
-
test("owns contextWindow maxInputTokens while preserving non-UI profile leaves", () => {
|
|
317
|
-
const result = replaceProfileRoute.handler({
|
|
351
|
+
test("owns contextWindow maxInputTokens while preserving non-UI profile leaves", async () => {
|
|
352
|
+
const result = await replaceProfileRoute.handler({
|
|
318
353
|
pathParams: { name: "custom" },
|
|
319
354
|
body: {
|
|
320
355
|
provider: "openai",
|
|
@@ -343,8 +378,8 @@ describe("PUT /v1/config/llm/profiles/:name", () => {
|
|
|
343
378
|
expect(savedProfile.openrouter).toEqual({ only: ["anthropic"] });
|
|
344
379
|
});
|
|
345
380
|
|
|
346
|
-
test("writes only the replacement contextWindow maxInputTokens override", () => {
|
|
347
|
-
const result = replaceProfileRoute.handler({
|
|
381
|
+
test("writes only the replacement contextWindow maxInputTokens override", async () => {
|
|
382
|
+
const result = await replaceProfileRoute.handler({
|
|
348
383
|
pathParams: { name: "custom" },
|
|
349
384
|
body: {
|
|
350
385
|
provider: "openai",
|
|
@@ -375,8 +410,8 @@ describe("PUT /v1/config/llm/profiles/:name", () => {
|
|
|
375
410
|
expect(savedProfile.openrouter).toEqual({ only: ["anthropic"] });
|
|
376
411
|
});
|
|
377
412
|
|
|
378
|
-
test("writes provider_connection when present in body", () => {
|
|
379
|
-
const result = replaceProfileRoute.handler({
|
|
413
|
+
test("writes provider_connection when present in body", async () => {
|
|
414
|
+
const result = await replaceProfileRoute.handler({
|
|
380
415
|
pathParams: { name: "custom" },
|
|
381
416
|
body: {
|
|
382
417
|
provider: "openai",
|
|
@@ -396,7 +431,7 @@ describe("PUT /v1/config/llm/profiles/:name", () => {
|
|
|
396
431
|
expect(savedProfile.provider_connection).toBe("personal-openai");
|
|
397
432
|
});
|
|
398
433
|
|
|
399
|
-
test("
|
|
434
|
+
test("auto-derives provider_connection when omitted from body (Any active)", async () => {
|
|
400
435
|
// Seed an existing binding so the test starts from a non-empty state.
|
|
401
436
|
(
|
|
402
437
|
rawConfigFixture.llm as {
|
|
@@ -404,14 +439,63 @@ describe("PUT /v1/config/llm/profiles/:name", () => {
|
|
|
404
439
|
}
|
|
405
440
|
).profiles.custom.provider_connection = "stale-openai";
|
|
406
441
|
|
|
407
|
-
const result = replaceProfileRoute.handler({
|
|
442
|
+
const result = await replaceProfileRoute.handler({
|
|
408
443
|
pathParams: { name: "custom" },
|
|
409
444
|
body: {
|
|
410
445
|
provider: "openai",
|
|
411
446
|
model: "gpt-5.5",
|
|
412
447
|
// provider_connection deliberately omitted — the UI cleared the
|
|
413
|
-
// picker back to "Any active"
|
|
414
|
-
//
|
|
448
|
+
// picker back to "Any active". The route auto-derives an active
|
|
449
|
+
// connection for the provider to prevent stale inheritance during
|
|
450
|
+
// config deep-merge.
|
|
451
|
+
},
|
|
452
|
+
});
|
|
453
|
+
|
|
454
|
+
expect(result).toEqual({ ok: true });
|
|
455
|
+
const savedProfile = (
|
|
456
|
+
savedRawConfig?.llm as {
|
|
457
|
+
profiles: Record<string, Record<string, unknown>>;
|
|
458
|
+
}
|
|
459
|
+
).profiles.custom;
|
|
460
|
+
|
|
461
|
+
// The canonical "openai-managed" connection exists in the test DB;
|
|
462
|
+
// the route auto-derives it when the UI omits provider_connection.
|
|
463
|
+
expect(savedProfile.provider_connection).toBe("openai-managed");
|
|
464
|
+
});
|
|
465
|
+
|
|
466
|
+
test("auto-derives provider_connection for BYOK provider (Any active)", async () => {
|
|
467
|
+
// Seed a fireworks connection in the DB.
|
|
468
|
+
createConnection(getDb(), {
|
|
469
|
+
name: "fireworks",
|
|
470
|
+
provider: "fireworks",
|
|
471
|
+
auth: { type: "api_key", credential: "fireworks:api_key" },
|
|
472
|
+
});
|
|
473
|
+
|
|
474
|
+
const result = await replaceProfileRoute.handler({
|
|
475
|
+
pathParams: { name: "custom" },
|
|
476
|
+
body: {
|
|
477
|
+
provider: "fireworks",
|
|
478
|
+
model: "accounts/fireworks/models/llama-v3p1-8b-instruct",
|
|
479
|
+
},
|
|
480
|
+
});
|
|
481
|
+
|
|
482
|
+
expect(result).toEqual({ ok: true });
|
|
483
|
+
const savedProfile = (
|
|
484
|
+
savedRawConfig?.llm as {
|
|
485
|
+
profiles: Record<string, Record<string, unknown>>;
|
|
486
|
+
}
|
|
487
|
+
).profiles.custom;
|
|
488
|
+
|
|
489
|
+
expect(savedProfile.provider).toBe("fireworks");
|
|
490
|
+
expect(savedProfile.provider_connection).toBe("fireworks");
|
|
491
|
+
});
|
|
492
|
+
|
|
493
|
+
test("auto-creates provider_connection when no connection exists for provider", async () => {
|
|
494
|
+
const result = await replaceProfileRoute.handler({
|
|
495
|
+
pathParams: { name: "custom" },
|
|
496
|
+
body: {
|
|
497
|
+
provider: "openrouter",
|
|
498
|
+
model: "anthropic/claude-sonnet-4-6",
|
|
415
499
|
},
|
|
416
500
|
});
|
|
417
501
|
|
|
@@ -422,7 +506,16 @@ describe("PUT /v1/config/llm/profiles/:name", () => {
|
|
|
422
506
|
}
|
|
423
507
|
).profiles.custom;
|
|
424
508
|
|
|
425
|
-
expect(savedProfile.
|
|
509
|
+
expect(savedProfile.provider).toBe("openrouter");
|
|
510
|
+
expect(savedProfile.provider_connection).toBe("openrouter-personal");
|
|
511
|
+
|
|
512
|
+
const conn = getConnection(getDb(), "openrouter-personal");
|
|
513
|
+
expect(conn).not.toBeNull();
|
|
514
|
+
expect(conn!.provider).toBe("openrouter");
|
|
515
|
+
expect(conn!.auth).toEqual({
|
|
516
|
+
type: "api_key",
|
|
517
|
+
credential: "credential/openrouter/api_key",
|
|
518
|
+
});
|
|
426
519
|
});
|
|
427
520
|
|
|
428
521
|
describe("managed profile guard", () => {
|
|
@@ -439,8 +532,8 @@ describe("PUT /v1/config/llm/profiles/:name", () => {
|
|
|
439
532
|
};
|
|
440
533
|
});
|
|
441
534
|
|
|
442
|
-
test("allows label edit on managed profile, preserving seed fields", () => {
|
|
443
|
-
const result = replaceProfileRoute.handler({
|
|
535
|
+
test("allows label edit on managed profile, preserving seed fields", async () => {
|
|
536
|
+
const result = await replaceProfileRoute.handler({
|
|
444
537
|
pathParams: { name: "balanced" },
|
|
445
538
|
body: { label: "My Balanced" },
|
|
446
539
|
});
|
|
@@ -459,8 +552,8 @@ describe("PUT /v1/config/llm/profiles/:name", () => {
|
|
|
459
552
|
expect(savedProfile.source).toBe("managed");
|
|
460
553
|
});
|
|
461
554
|
|
|
462
|
-
test("allows status edit on managed profile", () => {
|
|
463
|
-
const result = replaceProfileRoute.handler({
|
|
555
|
+
test("allows status edit on managed profile", async () => {
|
|
556
|
+
const result = await replaceProfileRoute.handler({
|
|
464
557
|
pathParams: { name: "balanced" },
|
|
465
558
|
body: { status: "disabled" },
|
|
466
559
|
});
|
|
@@ -476,8 +569,8 @@ describe("PUT /v1/config/llm/profiles/:name", () => {
|
|
|
476
569
|
expect(savedProfile.provider).toBe("anthropic");
|
|
477
570
|
});
|
|
478
571
|
|
|
479
|
-
test("allows label+status edit together", () => {
|
|
480
|
-
const result = replaceProfileRoute.handler({
|
|
572
|
+
test("allows label+status edit together", async () => {
|
|
573
|
+
const result = await replaceProfileRoute.handler({
|
|
481
574
|
pathParams: { name: "balanced" },
|
|
482
575
|
body: { label: "Renamed", status: "disabled" },
|
|
483
576
|
});
|
|
@@ -493,25 +586,81 @@ describe("PUT /v1/config/llm/profiles/:name", () => {
|
|
|
493
586
|
expect(savedProfile.status).toBe("disabled");
|
|
494
587
|
});
|
|
495
588
|
|
|
496
|
-
test("rejects provider edit on managed profile with disallowed-keys error", () => {
|
|
497
|
-
|
|
589
|
+
test("rejects provider edit on managed profile with disallowed-keys error", async () => {
|
|
590
|
+
// The handler is `async`, so synchronous BadRequest throws still
|
|
591
|
+
// surface as a rejected promise; assert via `.rejects.toThrow`.
|
|
592
|
+
await expect(
|
|
498
593
|
replaceProfileRoute.handler({
|
|
499
594
|
pathParams: { name: "balanced" },
|
|
500
595
|
body: { provider: "openai", model: "gpt-5" },
|
|
501
596
|
}),
|
|
502
|
-
).toThrow(
|
|
597
|
+
).rejects.toThrow(
|
|
598
|
+
/Cannot edit managed profile "balanced" fields \[provider, model\]/,
|
|
599
|
+
);
|
|
503
600
|
});
|
|
504
601
|
|
|
505
|
-
test("rejects mixed allowed+disallowed fields", () => {
|
|
602
|
+
test("rejects mixed allowed+disallowed fields", async () => {
|
|
506
603
|
// label is allowed but maxTokens is not — must reject without partially
|
|
507
604
|
// applying label, so saver should never be invoked.
|
|
508
|
-
expect(
|
|
605
|
+
await expect(
|
|
509
606
|
replaceProfileRoute.handler({
|
|
510
607
|
pathParams: { name: "balanced" },
|
|
511
608
|
body: { label: "Try", maxTokens: 999 },
|
|
512
609
|
}),
|
|
513
|
-
).toThrow(
|
|
610
|
+
).rejects.toThrow(
|
|
611
|
+
/Cannot edit managed profile "balanced" fields \[maxTokens\]/,
|
|
612
|
+
);
|
|
514
613
|
expect(savedRawConfig).toBeNull();
|
|
614
|
+
// Reject path skips commitConfigWrite entirely — no provider reinit
|
|
615
|
+
// or cache invalidation should fire on a guard rejection.
|
|
616
|
+
expect(initializeProvidersCalls).toBe(0);
|
|
617
|
+
expect(invalidateConfigCacheCalls).toBe(0);
|
|
618
|
+
expect(clearEmbeddingBackendCacheCalls).toBe(0);
|
|
619
|
+
});
|
|
620
|
+
});
|
|
621
|
+
|
|
622
|
+
describe("commitConfigWrite side effects", () => {
|
|
623
|
+
test("status flip on managed profile triggers provider reinit + cache invalidation", async () => {
|
|
624
|
+
// Seed a managed profile that the user will disable. commitConfigWrite
|
|
625
|
+
// must reinit the provider registry so the status change is reflected
|
|
626
|
+
// in the running daemon immediately, not at the next watcher tick.
|
|
627
|
+
(rawConfigFixture.llm as { profiles: Record<string, unknown> }).profiles[
|
|
628
|
+
"balanced"
|
|
629
|
+
] = {
|
|
630
|
+
source: "managed",
|
|
631
|
+
provider: "anthropic",
|
|
632
|
+
model: "claude-sonnet-4-6",
|
|
633
|
+
label: "Balanced",
|
|
634
|
+
status: "active",
|
|
635
|
+
};
|
|
636
|
+
|
|
637
|
+
const result = await replaceProfileRoute.handler({
|
|
638
|
+
pathParams: { name: "balanced" },
|
|
639
|
+
body: { status: "disabled" },
|
|
640
|
+
});
|
|
641
|
+
|
|
642
|
+
expect(result).toEqual({ ok: true });
|
|
643
|
+
expect(initializeProvidersCalls).toBe(1);
|
|
644
|
+
expect(invalidateConfigCacheCalls).toBe(1);
|
|
645
|
+
expect(clearEmbeddingBackendCacheCalls).toBe(1);
|
|
646
|
+
});
|
|
647
|
+
|
|
648
|
+
test("custom profile provider swap triggers provider reinit + cache invalidation", async () => {
|
|
649
|
+
// Custom profile path: provider/model swap on a user-owned profile.
|
|
650
|
+
// Same side-effect contract — registry must reinit so the new
|
|
651
|
+
// provider is wired into the running daemon without restart.
|
|
652
|
+
const result = await replaceProfileRoute.handler({
|
|
653
|
+
pathParams: { name: "custom" },
|
|
654
|
+
body: {
|
|
655
|
+
provider: "openai",
|
|
656
|
+
model: "gpt-5.5",
|
|
657
|
+
},
|
|
658
|
+
});
|
|
659
|
+
|
|
660
|
+
expect(result).toEqual({ ok: true });
|
|
661
|
+
expect(initializeProvidersCalls).toBe(1);
|
|
662
|
+
expect(invalidateConfigCacheCalls).toBe(1);
|
|
663
|
+
expect(clearEmbeddingBackendCacheCalls).toBe(1);
|
|
515
664
|
});
|
|
516
665
|
});
|
|
517
666
|
});
|