@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,158 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, test } from "bun:test";
|
|
2
|
+
|
|
3
|
+
import { _setOverridesForTesting } from "../config/assistant-feature-flags.js";
|
|
4
|
+
import type { AssistantConfig } from "../config/schema.js";
|
|
5
|
+
import { PROVIDER_CATALOG } from "../providers/model-catalog.js";
|
|
6
|
+
import { getVisibleProviderCatalog } from "../providers/provider-catalog-visibility.js";
|
|
7
|
+
|
|
8
|
+
beforeEach(() => {
|
|
9
|
+
_setOverridesForTesting({});
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
afterEach(() => {
|
|
13
|
+
_setOverridesForTesting({});
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
/** Minimal AssistantConfig stub for feature-flag resolution. */
|
|
17
|
+
function makeConfig(): AssistantConfig {
|
|
18
|
+
return {} as AssistantConfig;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
describe("getVisibleProviderCatalog", () => {
|
|
22
|
+
test("hides openai-compatible endpoints by default", () => {
|
|
23
|
+
_setOverridesForTesting({});
|
|
24
|
+
|
|
25
|
+
const visible = getVisibleProviderCatalog(makeConfig());
|
|
26
|
+
|
|
27
|
+
expect(visible.find((p) => p.id === "openai-compatible")).toBeUndefined();
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
test("shows openai-compatible endpoints when its flag is enabled", () => {
|
|
31
|
+
_setOverridesForTesting({ "openai-compatible-endpoints": true });
|
|
32
|
+
|
|
33
|
+
const visible = getVisibleProviderCatalog(makeConfig());
|
|
34
|
+
|
|
35
|
+
expect(visible.find((p) => p.id === "openai-compatible")).toBeDefined();
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
test("returns the full catalog when all feature flags are enabled", () => {
|
|
39
|
+
const allFlags: Record<string, boolean> = {};
|
|
40
|
+
for (const entry of PROVIDER_CATALOG) {
|
|
41
|
+
if (entry.featureFlag) allFlags[entry.featureFlag] = true;
|
|
42
|
+
for (const model of entry.models) {
|
|
43
|
+
if (model.featureFlag) allFlags[model.featureFlag] = true;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
_setOverridesForTesting(allFlags);
|
|
47
|
+
|
|
48
|
+
const visible = getVisibleProviderCatalog(makeConfig());
|
|
49
|
+
expect(visible.length).toBe(PROVIDER_CATALOG.length);
|
|
50
|
+
expect(visible.map((p) => p.id)).toEqual(PROVIDER_CATALOG.map((p) => p.id));
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
test("hides a provider whose featureFlag is disabled", () => {
|
|
54
|
+
const allFlags: Record<string, boolean> = {};
|
|
55
|
+
for (const entry of PROVIDER_CATALOG) {
|
|
56
|
+
if (entry.featureFlag) allFlags[entry.featureFlag] = true;
|
|
57
|
+
for (const model of entry.models) {
|
|
58
|
+
if (model.featureFlag) allFlags[model.featureFlag] = true;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
_setOverridesForTesting({ ...allFlags, "test-provider-flag": false });
|
|
62
|
+
|
|
63
|
+
const original = [...PROVIDER_CATALOG];
|
|
64
|
+
PROVIDER_CATALOG.push({
|
|
65
|
+
id: "__test_flagged_provider__",
|
|
66
|
+
displayName: "Test Flagged Provider",
|
|
67
|
+
models: [
|
|
68
|
+
{
|
|
69
|
+
id: "test-model",
|
|
70
|
+
displayName: "Test Model",
|
|
71
|
+
},
|
|
72
|
+
],
|
|
73
|
+
defaultModel: "test-model",
|
|
74
|
+
featureFlag: "test-provider-flag",
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
try {
|
|
78
|
+
const visible = getVisibleProviderCatalog(makeConfig());
|
|
79
|
+
expect(
|
|
80
|
+
visible.find((p) => p.id === "__test_flagged_provider__"),
|
|
81
|
+
).toBeUndefined();
|
|
82
|
+
expect(visible.length).toBe(original.length);
|
|
83
|
+
} finally {
|
|
84
|
+
PROVIDER_CATALOG.length = 0;
|
|
85
|
+
PROVIDER_CATALOG.push(...original);
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
test("hides a model whose featureFlag is disabled but keeps the provider", () => {
|
|
90
|
+
_setOverridesForTesting({ "test-model-flag": false });
|
|
91
|
+
|
|
92
|
+
const original = [...PROVIDER_CATALOG];
|
|
93
|
+
PROVIDER_CATALOG.push({
|
|
94
|
+
id: "__test_provider_with_flagged_model__",
|
|
95
|
+
displayName: "Test Provider",
|
|
96
|
+
models: [
|
|
97
|
+
{
|
|
98
|
+
id: "visible-model",
|
|
99
|
+
displayName: "Visible Model",
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
id: "flagged-model",
|
|
103
|
+
displayName: "Flagged Model",
|
|
104
|
+
featureFlag: "test-model-flag",
|
|
105
|
+
},
|
|
106
|
+
],
|
|
107
|
+
defaultModel: "visible-model",
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
try {
|
|
111
|
+
const visible = getVisibleProviderCatalog(makeConfig());
|
|
112
|
+
const provider = visible.find(
|
|
113
|
+
(p) => p.id === "__test_provider_with_flagged_model__",
|
|
114
|
+
);
|
|
115
|
+
expect(provider).toBeDefined();
|
|
116
|
+
expect(provider!.models.map((m) => m.id)).toEqual(["visible-model"]);
|
|
117
|
+
} finally {
|
|
118
|
+
PROVIDER_CATALOG.length = 0;
|
|
119
|
+
PROVIDER_CATALOG.push(...original);
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
test("hides a provider entirely when all its models are flagged off", () => {
|
|
124
|
+
_setOverridesForTesting({
|
|
125
|
+
"flag-a": false,
|
|
126
|
+
"flag-b": false,
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
const original = [...PROVIDER_CATALOG];
|
|
130
|
+
PROVIDER_CATALOG.push({
|
|
131
|
+
id: "__test_all_models_flagged__",
|
|
132
|
+
displayName: "All Flagged",
|
|
133
|
+
models: [
|
|
134
|
+
{
|
|
135
|
+
id: "model-a",
|
|
136
|
+
displayName: "Model A",
|
|
137
|
+
featureFlag: "flag-a",
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
id: "model-b",
|
|
141
|
+
displayName: "Model B",
|
|
142
|
+
featureFlag: "flag-b",
|
|
143
|
+
},
|
|
144
|
+
],
|
|
145
|
+
defaultModel: "model-a",
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
try {
|
|
149
|
+
const visible = getVisibleProviderCatalog(makeConfig());
|
|
150
|
+
expect(
|
|
151
|
+
visible.find((p) => p.id === "__test_all_models_flagged__"),
|
|
152
|
+
).toBeUndefined();
|
|
153
|
+
} finally {
|
|
154
|
+
PROVIDER_CATALOG.length = 0;
|
|
155
|
+
PROVIDER_CATALOG.push(...original);
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
});
|
|
@@ -753,6 +753,117 @@ describe("RetryProvider — streaming response handling", () => {
|
|
|
753
753
|
expect(callCount).toBe(2);
|
|
754
754
|
});
|
|
755
755
|
|
|
756
|
+
test("retries transport-aborted stream (Anthropic 'Request was aborted' with no abortReason)", async () => {
|
|
757
|
+
let callCount = 0;
|
|
758
|
+
const inner: Provider = {
|
|
759
|
+
name: "retry-transport-abort",
|
|
760
|
+
async sendMessage() {
|
|
761
|
+
callCount++;
|
|
762
|
+
if (callCount <= 1) {
|
|
763
|
+
// Mirrors the ProviderError shape produced by the catch-site in
|
|
764
|
+
// providers/anthropic/client.ts when the SDK reports
|
|
765
|
+
// ``Anthropic.APIError(status === undefined, message: "Request
|
|
766
|
+
// was aborted.")`` and the daemon's AbortController was NOT the
|
|
767
|
+
// cause (i.e. abortReason is undefined). Empirically the #1
|
|
768
|
+
// daemon error by a factor of 5x — 1,344 events in 4d on the
|
|
769
|
+
// SSE chat path, all of which used to surface as a 45s blank
|
|
770
|
+
// screen on the web client via LUM-1431.
|
|
771
|
+
throw new ProviderError(
|
|
772
|
+
"Anthropic API error: Request was aborted.",
|
|
773
|
+
"anthropic",
|
|
774
|
+
undefined,
|
|
775
|
+
);
|
|
776
|
+
}
|
|
777
|
+
return successResponse();
|
|
778
|
+
},
|
|
779
|
+
};
|
|
780
|
+
const provider = new RetryProvider(inner);
|
|
781
|
+
await provider.sendMessage(MESSAGES);
|
|
782
|
+
expect(callCount).toBe(2);
|
|
783
|
+
});
|
|
784
|
+
|
|
785
|
+
test("does NOT retry caller-aborted stream (abortReason set short-circuits retry)", async () => {
|
|
786
|
+
let callCount = 0;
|
|
787
|
+
const abortError = new ProviderError(
|
|
788
|
+
"Anthropic API error: Request was aborted.",
|
|
789
|
+
"anthropic",
|
|
790
|
+
undefined,
|
|
791
|
+
// Tagging abortReason exactly matches what the catch-site does when
|
|
792
|
+
// signal.aborted was true at the moment of failure — i.e. the
|
|
793
|
+
// daemon (or the user) cancelled the request, not the transport.
|
|
794
|
+
// The retry layer must respect this and surface the error
|
|
795
|
+
// immediately without consuming retry budget.
|
|
796
|
+
{ abortReason: "user-cancelled" },
|
|
797
|
+
);
|
|
798
|
+
const inner: Provider = {
|
|
799
|
+
name: "caller-abort",
|
|
800
|
+
async sendMessage() {
|
|
801
|
+
callCount++;
|
|
802
|
+
throw abortError;
|
|
803
|
+
},
|
|
804
|
+
};
|
|
805
|
+
const provider = new RetryProvider(inner);
|
|
806
|
+
await expect(provider.sendMessage(MESSAGES)).rejects.toBe(abortError);
|
|
807
|
+
expect(callCount).toBe(1);
|
|
808
|
+
});
|
|
809
|
+
|
|
810
|
+
test("does NOT retry inner-timeout stream (deterministic 30min deadline failure)", async () => {
|
|
811
|
+
let callCount = 0;
|
|
812
|
+
// When the inner streamTimeoutMs fires, the catch-site rewrites the
|
|
813
|
+
// message to "Anthropic stream timed out after Xs (inner
|
|
814
|
+
// streamTimeoutMs)" instead of "Request was aborted." That rewrite is
|
|
815
|
+
// what allows this branch to bypass the transport-abort retry —
|
|
816
|
+
// retrying a 30min-deadline failure would almost certainly hit the
|
|
817
|
+
// same deadline on the next attempt and waste retry budget.
|
|
818
|
+
const innerTimeoutError = new ProviderError(
|
|
819
|
+
"Anthropic API error: Anthropic stream timed out after 1800s (inner streamTimeoutMs)",
|
|
820
|
+
"anthropic",
|
|
821
|
+
undefined,
|
|
822
|
+
);
|
|
823
|
+
const inner: Provider = {
|
|
824
|
+
name: "inner-timeout",
|
|
825
|
+
async sendMessage() {
|
|
826
|
+
callCount++;
|
|
827
|
+
throw innerTimeoutError;
|
|
828
|
+
},
|
|
829
|
+
};
|
|
830
|
+
const provider = new RetryProvider(inner);
|
|
831
|
+
await expect(provider.sendMessage(MESSAGES)).rejects.toBe(
|
|
832
|
+
innerTimeoutError,
|
|
833
|
+
);
|
|
834
|
+
expect(callCount).toBe(1);
|
|
835
|
+
});
|
|
836
|
+
|
|
837
|
+
test("does NOT retry OpenAI/Gemini-shaped 'Request was aborted' (no inner-timeout rewrite at those catch-sites)", async () => {
|
|
838
|
+
// The OpenAI chat-completions, OpenAI responses, and Gemini catch-sites
|
|
839
|
+
// format their errors as `"<Provider> API error (undefined): Request
|
|
840
|
+
// was aborted."` (note the `(undefined)` parenthetical that the
|
|
841
|
+
// Anthropic catch-site intentionally omits) and — unlike the Anthropic
|
|
842
|
+
// catch-site — they do NOT rewrite their inner-streamTimeoutMs
|
|
843
|
+
// deadline failures. A provider-agnostic transport-abort predicate
|
|
844
|
+
// would burn three retries on what is by construction a deterministic
|
|
845
|
+
// 30-minute deadline failure that will fire again on every attempt.
|
|
846
|
+
// Scoping the predicate to the Anthropic message prefix avoids that
|
|
847
|
+
// wasted retry budget for non-Anthropic providers until their
|
|
848
|
+
// catch-sites grow the same `innerTimeoutFired` distinction.
|
|
849
|
+
const openaiAbortError = new ProviderError(
|
|
850
|
+
"OpenAI API error (undefined): Request was aborted.",
|
|
851
|
+
"openai",
|
|
852
|
+
undefined,
|
|
853
|
+
);
|
|
854
|
+
let callCount = 0;
|
|
855
|
+
const inner: Provider = {
|
|
856
|
+
name: "openai-aborted-stream",
|
|
857
|
+
async sendMessage() {
|
|
858
|
+
callCount++;
|
|
859
|
+
throw openaiAbortError;
|
|
860
|
+
},
|
|
861
|
+
};
|
|
862
|
+
const provider = new RetryProvider(inner);
|
|
863
|
+
await expect(provider.sendMessage(MESSAGES)).rejects.toBe(openaiAbortError);
|
|
864
|
+
expect(callCount).toBe(1);
|
|
865
|
+
});
|
|
866
|
+
|
|
756
867
|
test("events accumulate across retries (each attempt delivers events independently)", async () => {
|
|
757
868
|
let callCount = 0;
|
|
758
869
|
const inner: Provider = {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { beforeEach, describe, expect, mock, test } from "bun:test";
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { PLATFORM_PROVIDER_META } from "../providers/platform-proxy/constants.js";
|
|
4
4
|
import { credentialKey } from "../security/credential-key.js";
|
|
5
5
|
|
|
6
6
|
// ---------------------------------------------------------------------------
|
|
@@ -113,7 +113,7 @@ const DIRECT_OR_MANAGED_PROVIDER_KEYS: string[] = [
|
|
|
113
113
|
"fireworks",
|
|
114
114
|
"openrouter",
|
|
115
115
|
];
|
|
116
|
-
const MANAGED_FALLBACK_PROVIDERS: string[] = ["anthropic", "gemini", "openai"];
|
|
116
|
+
const MANAGED_FALLBACK_PROVIDERS: string[] = ["anthropic", "gemini", "openai", "fireworks"];
|
|
117
117
|
|
|
118
118
|
function enableManagedProxy() {
|
|
119
119
|
mockPlatformBaseUrl = PLATFORM_BASE;
|
|
@@ -209,20 +209,19 @@ describe("managed proxy integration — credential precedence", () => {
|
|
|
209
209
|
},
|
|
210
210
|
);
|
|
211
211
|
|
|
212
|
-
test("managed bootstrap registers anthropic, openai, and
|
|
212
|
+
test("managed bootstrap registers anthropic, openai, gemini, and fireworks", async () => {
|
|
213
213
|
enableManagedProxy();
|
|
214
214
|
mockProviderKeys = {};
|
|
215
215
|
await initializeProviders(makeProvidersConfig("anthropic", "test-model"));
|
|
216
216
|
expect(listProviders()).toEqual(
|
|
217
|
-
expect.arrayContaining(["anthropic", "openai", "gemini"]),
|
|
217
|
+
expect.arrayContaining(["anthropic", "openai", "gemini", "fireworks"]),
|
|
218
218
|
);
|
|
219
|
-
expect(listProviders()).toHaveLength(
|
|
219
|
+
expect(listProviders()).toHaveLength(4);
|
|
220
220
|
expect(getProviderRoutingSource("anthropic")).toBe("managed-proxy");
|
|
221
221
|
expect(getProviderRoutingSource("openai")).toBe("managed-proxy");
|
|
222
222
|
expect(getProviderRoutingSource("gemini")).toBe("managed-proxy");
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
}
|
|
223
|
+
expect(getProviderRoutingSource("fireworks")).toBe("managed-proxy");
|
|
224
|
+
expect(getProviderRoutingSource("openrouter")).toBeUndefined();
|
|
226
225
|
});
|
|
227
226
|
|
|
228
227
|
test("managed anthropic uses anthropic proxy path", async () => {
|
|
@@ -376,7 +375,7 @@ describe("managed proxy integration — credential precedence", () => {
|
|
|
376
375
|
});
|
|
377
376
|
|
|
378
377
|
describe("mixed: some user keys + managed fallback fills gaps", () => {
|
|
379
|
-
test("user key for anthropic routes direct and managed fallback fills openai and
|
|
378
|
+
test("user key for anthropic routes direct and managed fallback fills openai, gemini, and fireworks", async () => {
|
|
380
379
|
enableManagedProxy();
|
|
381
380
|
setUserKeysFor("anthropic");
|
|
382
381
|
await initializeProviders(makeProvidersConfig("anthropic", "test-model"));
|
|
@@ -387,13 +386,13 @@ describe("managed proxy integration — credential precedence", () => {
|
|
|
387
386
|
expect(getProviderRoutingSource("openai")).toBe("managed-proxy");
|
|
388
387
|
expect(registered).toContain("gemini");
|
|
389
388
|
expect(getProviderRoutingSource("gemini")).toBe("managed-proxy");
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
389
|
+
expect(registered).toContain("fireworks");
|
|
390
|
+
expect(getProviderRoutingSource("fireworks")).toBe("managed-proxy");
|
|
391
|
+
expect(registered).not.toContain("openrouter");
|
|
392
|
+
expect(getProviderRoutingSource("openrouter")).toBeUndefined();
|
|
394
393
|
});
|
|
395
394
|
|
|
396
|
-
test("user key for openai routes direct while anthropic and
|
|
395
|
+
test("user key for openai routes direct while anthropic, gemini, and fireworks still bootstrap via managed proxy", async () => {
|
|
397
396
|
enableManagedProxy();
|
|
398
397
|
setUserKeysFor("openai");
|
|
399
398
|
await initializeProviders(makeProvidersConfig("openai", "test-model"));
|
|
@@ -404,11 +403,10 @@ describe("managed proxy integration — credential precedence", () => {
|
|
|
404
403
|
expect(getProviderRoutingSource("anthropic")).toBe("managed-proxy");
|
|
405
404
|
expect(registered).toContain("gemini");
|
|
406
405
|
expect(getProviderRoutingSource("gemini")).toBe("managed-proxy");
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
}
|
|
406
|
+
expect(registered).toContain("fireworks");
|
|
407
|
+
expect(getProviderRoutingSource("fireworks")).toBe("managed-proxy");
|
|
408
|
+
expect(registered).not.toContain("openrouter");
|
|
409
|
+
expect(getProviderRoutingSource("openrouter")).toBeUndefined();
|
|
412
410
|
});
|
|
413
411
|
});
|
|
414
412
|
});
|
|
@@ -436,7 +434,7 @@ describe("managed proxy integration — ollama exclusion", () => {
|
|
|
436
434
|
});
|
|
437
435
|
|
|
438
436
|
test("ollama metadata is marked as non-managed", () => {
|
|
439
|
-
const meta =
|
|
437
|
+
const meta = PLATFORM_PROVIDER_META.ollama;
|
|
440
438
|
expect(meta).toBeDefined();
|
|
441
439
|
expect(meta.managed).toBe(false);
|
|
442
440
|
expect(meta.proxyPath).toBeUndefined();
|
|
@@ -476,9 +474,9 @@ describe("config mode flip → provider reinit", () => {
|
|
|
476
474
|
});
|
|
477
475
|
|
|
478
476
|
describe("managed proxy integration — constants integrity", () => {
|
|
479
|
-
test("anthropic, openai, and
|
|
480
|
-
for (const provider of ["anthropic", "openai", "gemini"]) {
|
|
481
|
-
const meta =
|
|
477
|
+
test("anthropic, openai, gemini, and fireworks have metadata with managed=true and a proxyPath", () => {
|
|
478
|
+
for (const provider of ["anthropic", "openai", "gemini", "fireworks"]) {
|
|
479
|
+
const meta = PLATFORM_PROVIDER_META[provider];
|
|
482
480
|
expect(meta).toBeDefined();
|
|
483
481
|
expect(meta.managed).toBe(true);
|
|
484
482
|
expect(meta.proxyPath).toBeTruthy();
|
|
@@ -487,27 +485,31 @@ describe("managed proxy integration — constants integrity", () => {
|
|
|
487
485
|
});
|
|
488
486
|
|
|
489
487
|
test("anthropic routes through anthropic proxy path", () => {
|
|
490
|
-
expect(
|
|
488
|
+
expect(PLATFORM_PROVIDER_META.anthropic.proxyPath).toBe(
|
|
491
489
|
"/v1/runtime-proxy/anthropic",
|
|
492
490
|
);
|
|
493
491
|
});
|
|
494
492
|
|
|
495
493
|
test("gemini routes through gemini proxy path", () => {
|
|
496
|
-
expect(
|
|
494
|
+
expect(PLATFORM_PROVIDER_META.gemini.proxyPath).toBe(
|
|
497
495
|
"/v1/runtime-proxy/gemini",
|
|
498
496
|
);
|
|
499
497
|
});
|
|
500
498
|
|
|
501
499
|
test("openai routes through openai proxy path", () => {
|
|
502
|
-
expect(
|
|
500
|
+
expect(PLATFORM_PROVIDER_META.openai.proxyPath).toBe(
|
|
503
501
|
"/v1/runtime-proxy/openai",
|
|
504
502
|
);
|
|
505
503
|
});
|
|
506
504
|
|
|
507
|
-
test("fireworks
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
505
|
+
test("fireworks routes through fireworks proxy path", () => {
|
|
506
|
+
expect(PLATFORM_PROVIDER_META.fireworks.proxyPath).toBe(
|
|
507
|
+
"/v1/runtime-proxy/fireworks",
|
|
508
|
+
);
|
|
509
|
+
});
|
|
510
|
+
|
|
511
|
+
test("openrouter is not managed proxy capable", () => {
|
|
512
|
+
expect(PLATFORM_PROVIDER_META.openrouter.managed).toBe(false);
|
|
513
|
+
expect(PLATFORM_PROVIDER_META.openrouter.proxyPath).toBeUndefined();
|
|
512
514
|
});
|
|
513
515
|
});
|
|
@@ -3,6 +3,7 @@ import { join } from "node:path";
|
|
|
3
3
|
import { afterEach, beforeEach, describe, expect, mock, test } from "bun:test";
|
|
4
4
|
|
|
5
5
|
const TEST_DIR = process.env.VELLUM_WORKSPACE_DIR!;
|
|
6
|
+
const mockRefreshSkillCapabilityMemories = mock(() => {});
|
|
6
7
|
|
|
7
8
|
mock.module("../util/logger.js", () => ({
|
|
8
9
|
getLogger: () =>
|
|
@@ -11,6 +12,11 @@ mock.module("../util/logger.js", () => ({
|
|
|
11
12
|
}),
|
|
12
13
|
}));
|
|
13
14
|
|
|
15
|
+
mock.module("../daemon/skill-memory-refresh.js", () => ({
|
|
16
|
+
refreshSkillCapabilityMemories: mockRefreshSkillCapabilityMemories,
|
|
17
|
+
}));
|
|
18
|
+
|
|
19
|
+
import { loadSkillCatalog } from "../config/skills.js";
|
|
14
20
|
import { executeScaffoldManagedSkill } from "../tools/skills/scaffold-managed.js";
|
|
15
21
|
import type { ToolContext } from "../tools/types.js";
|
|
16
22
|
|
|
@@ -24,6 +30,7 @@ function makeContext(): ToolContext {
|
|
|
24
30
|
|
|
25
31
|
beforeEach(() => {
|
|
26
32
|
mkdirSync(join(TEST_DIR, "skills"), { recursive: true });
|
|
33
|
+
mockRefreshSkillCapabilityMemories.mockClear();
|
|
27
34
|
});
|
|
28
35
|
|
|
29
36
|
afterEach(() => {
|
|
@@ -31,7 +38,29 @@ afterEach(() => {
|
|
|
31
38
|
});
|
|
32
39
|
|
|
33
40
|
describe("scaffold_managed_skill tool", () => {
|
|
34
|
-
test("
|
|
41
|
+
test("keeps legacy index control as a deprecated no-op schema field", () => {
|
|
42
|
+
const tools = JSON.parse(
|
|
43
|
+
readFileSync(
|
|
44
|
+
join(
|
|
45
|
+
import.meta.dirname,
|
|
46
|
+
"../config/bundled-skills/skill-management/TOOLS.json",
|
|
47
|
+
),
|
|
48
|
+
"utf-8",
|
|
49
|
+
),
|
|
50
|
+
);
|
|
51
|
+
const scaffoldTool = tools.tools.find(
|
|
52
|
+
(tool: { name: string }) => tool.name === "scaffold_managed_skill",
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
expect(scaffoldTool).toBeDefined();
|
|
56
|
+
expect(scaffoldTool.input_schema.properties.add_to_index).toEqual({
|
|
57
|
+
type: "boolean",
|
|
58
|
+
description:
|
|
59
|
+
"Deprecated no-op compatibility field. Skills are discovered from top-level SKILL.md files.",
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
test("creates a valid skill discovered from its SKILL.md directory", async () => {
|
|
35
64
|
const result = await executeScaffoldManagedSkill(
|
|
36
65
|
{
|
|
37
66
|
skill_id: "test-skill",
|
|
@@ -46,18 +75,40 @@ describe("scaffold_managed_skill tool", () => {
|
|
|
46
75
|
const parsed = JSON.parse(result.content);
|
|
47
76
|
expect(parsed.created).toBe(true);
|
|
48
77
|
expect(parsed.skill_id).toBe("test-skill");
|
|
49
|
-
expect(parsed
|
|
78
|
+
expect(parsed).not.toHaveProperty("index_updated");
|
|
50
79
|
|
|
51
80
|
const skillFile = join(TEST_DIR, "skills", "test-skill", "SKILL.md");
|
|
52
81
|
expect(existsSync(skillFile)).toBe(true);
|
|
53
82
|
const content = readFileSync(skillFile, "utf-8");
|
|
54
83
|
expect(content).toContain('name: "Test Skill"');
|
|
55
84
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
85
|
+
expect(existsSync(join(TEST_DIR, "skills", "SKILLS.md"))).toBe(false);
|
|
86
|
+
|
|
87
|
+
const catalog = loadSkillCatalog();
|
|
88
|
+
const skill = catalog.find((s) => s.id === "test-skill");
|
|
89
|
+
expect(skill).toBeDefined();
|
|
90
|
+
expect(skill!.name).toBe("Test Skill");
|
|
91
|
+
expect(mockRefreshSkillCapabilityMemories).toHaveBeenCalledTimes(1);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
test("accepts legacy add_to_index input without returning index metadata", async () => {
|
|
95
|
+
const result = await executeScaffoldManagedSkill(
|
|
96
|
+
{
|
|
97
|
+
skill_id: "legacy-input",
|
|
98
|
+
name: "Legacy Input",
|
|
99
|
+
description: "A test skill",
|
|
100
|
+
body_markdown: "Do the thing.",
|
|
101
|
+
add_to_index: true,
|
|
102
|
+
},
|
|
103
|
+
makeContext(),
|
|
59
104
|
);
|
|
60
|
-
|
|
105
|
+
|
|
106
|
+
expect(result.isError).toBe(false);
|
|
107
|
+
const parsed = JSON.parse(result.content);
|
|
108
|
+
expect(parsed.created).toBe(true);
|
|
109
|
+
expect(parsed).not.toHaveProperty("index_updated");
|
|
110
|
+
expect(existsSync(join(TEST_DIR, "skills", "SKILLS.md"))).toBe(false);
|
|
111
|
+
expect(mockRefreshSkillCapabilityMemories).toHaveBeenCalledTimes(1);
|
|
61
112
|
});
|
|
62
113
|
|
|
63
114
|
test("rejects duplicate unless overwrite=true", async () => {
|
|
@@ -258,7 +309,7 @@ describe("scaffold_managed_skill tool", () => {
|
|
|
258
309
|
expect(result.content).toContain("traversal");
|
|
259
310
|
});
|
|
260
311
|
|
|
261
|
-
test("e2e: scaffold child then parent with includes, verify
|
|
312
|
+
test("e2e: scaffold child then parent with includes, verify file discovery", async () => {
|
|
262
313
|
const childResult = await executeScaffoldManagedSkill(
|
|
263
314
|
{
|
|
264
315
|
skill_id: "e2e-child",
|
|
@@ -288,11 +339,12 @@ describe("scaffold_managed_skill tool", () => {
|
|
|
288
339
|
expect(parentContent).toContain(" includes:");
|
|
289
340
|
expect(parentContent).toContain(" - e2e-child");
|
|
290
341
|
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
);
|
|
295
|
-
|
|
296
|
-
expect(
|
|
342
|
+
expect(existsSync(join(TEST_DIR, "skills", "SKILLS.md"))).toBe(false);
|
|
343
|
+
|
|
344
|
+
const catalog = loadSkillCatalog();
|
|
345
|
+
expect(catalog.find((s) => s.id === "e2e-child")).toBeDefined();
|
|
346
|
+
const parent = catalog.find((s) => s.id === "e2e-parent");
|
|
347
|
+
expect(parent).toBeDefined();
|
|
348
|
+
expect(parent!.includes).toEqual(["e2e-child"]);
|
|
297
349
|
});
|
|
298
350
|
});
|
|
@@ -39,8 +39,11 @@ mock.module("../daemon/conversation-store.js", () => ({
|
|
|
39
39
|
},
|
|
40
40
|
}));
|
|
41
41
|
|
|
42
|
+
import { SYNC_TAGS } from "../daemon/message-types/sync.js";
|
|
42
43
|
import { getDb } from "../memory/db-connection.js";
|
|
43
44
|
import { initializeDb } from "../memory/db-init.js";
|
|
45
|
+
import type { AssistantEvent } from "../runtime/assistant-event.js";
|
|
46
|
+
import { assistantEventHub } from "../runtime/assistant-event-hub.js";
|
|
44
47
|
import { ROUTES } from "../runtime/routes/schedule-routes.js";
|
|
45
48
|
import type { RouteDefinition } from "../runtime/routes/types.js";
|
|
46
49
|
import {
|
|
@@ -71,6 +74,15 @@ function findRoute(endpoint: string, method: string): RouteDefinition {
|
|
|
71
74
|
return route;
|
|
72
75
|
}
|
|
73
76
|
|
|
77
|
+
async function waitFor(predicate: () => boolean): Promise<void> {
|
|
78
|
+
const deadline = Date.now() + 500;
|
|
79
|
+
while (Date.now() < deadline) {
|
|
80
|
+
if (predicate()) return;
|
|
81
|
+
await new Promise((resolve) => setTimeout(resolve, 5));
|
|
82
|
+
}
|
|
83
|
+
throw new Error("Timed out waiting for schedule route event");
|
|
84
|
+
}
|
|
85
|
+
|
|
74
86
|
describe("schedule run-now trust propagation", () => {
|
|
75
87
|
beforeEach(() => {
|
|
76
88
|
clearTables();
|
|
@@ -218,6 +230,41 @@ describe("GET /schedules — default defer exclusion", () => {
|
|
|
218
230
|
expect(result.schedules).toHaveLength(1);
|
|
219
231
|
expect(result.schedules[0].id).toBe(agent.id);
|
|
220
232
|
});
|
|
233
|
+
|
|
234
|
+
test("mutation routes emit schedule sync invalidation", async () => {
|
|
235
|
+
const received: AssistantEvent[] = [];
|
|
236
|
+
const subscription = assistantEventHub.subscribe({
|
|
237
|
+
type: "process",
|
|
238
|
+
callback: (event) => {
|
|
239
|
+
received.push(event);
|
|
240
|
+
},
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
try {
|
|
244
|
+
const agent = createSchedule({
|
|
245
|
+
name: "Agent schedule",
|
|
246
|
+
cronExpression: "* * * * *",
|
|
247
|
+
message: "hello",
|
|
248
|
+
syntax: "cron",
|
|
249
|
+
});
|
|
250
|
+
await waitFor(() => received.length >= 1);
|
|
251
|
+
received.length = 0;
|
|
252
|
+
|
|
253
|
+
const route = findRoute("schedules/:id/toggle", "POST");
|
|
254
|
+
route.handler({
|
|
255
|
+
pathParams: { id: agent.id },
|
|
256
|
+
body: { enabled: false },
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
await waitFor(() => received.length >= 1);
|
|
260
|
+
expect(received[0].message).toEqual({
|
|
261
|
+
type: "sync_changed",
|
|
262
|
+
tags: [SYNC_TAGS.assistantSchedules],
|
|
263
|
+
});
|
|
264
|
+
} finally {
|
|
265
|
+
subscription.dispose();
|
|
266
|
+
}
|
|
267
|
+
});
|
|
221
268
|
});
|
|
222
269
|
|
|
223
270
|
// ── schedules/:id/runs limit handling ─────────────────────────────────────
|
|
@@ -388,9 +435,9 @@ describe("POST /schedules — create", () => {
|
|
|
388
435
|
});
|
|
389
436
|
|
|
390
437
|
test("rejects missing required fields", () => {
|
|
391
|
-
expect(() =>
|
|
392
|
-
"
|
|
393
|
-
);
|
|
438
|
+
expect(() =>
|
|
439
|
+
postCreate({ expression: "* * * * *", message: "hi" }),
|
|
440
|
+
).toThrow("name is required");
|
|
394
441
|
expect(() => postCreate({ name: "x", message: "hi" })).toThrow(
|
|
395
442
|
"expression is required",
|
|
396
443
|
);
|