@vellumai/assistant 0.8.1 → 0.8.2
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 +2 -7
- package/Dockerfile +75 -1
- package/bun.lock +11 -1
- package/docker-entrypoint.sh +5 -0
- package/docker-init-apt-root.sh +94 -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 +325 -3
- 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-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 +41 -0
- 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 +1 -0
- 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 +2 -0
- 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-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 +76 -9
- 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__/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 +60 -36
- package/src/__tests__/filing-service.test.ts +140 -0
- package/src/__tests__/get-skill-detail-audit.test.ts +0 -4
- package/src/__tests__/handlers-skills-memory-v2-reseed.test.ts +43 -62
- 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 +266 -10
- 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-chain.test.ts +10 -8
- package/src/__tests__/install-skill-routing.test.ts +155 -37
- package/src/__tests__/lifecycle-memory-v2-seed.test.ts +92 -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-catalog-parity.test.ts +55 -13
- package/src/__tests__/llm-request-log-source-clickhouse.test.ts +34 -0
- package/src/__tests__/llm-request-log-source-factory.test.ts +29 -53
- package/src/__tests__/llm-usage-store.test.ts +114 -0
- package/src/__tests__/managed-profile-guard.test.ts +31 -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__/oauth-commands-routes.test.ts +168 -16
- package/src/__tests__/oauth-provider-profiles.test.ts +9 -0
- package/src/__tests__/openai-provider.test.ts +24 -0
- package/src/__tests__/openai-responses-cutover-guard.test.ts +17 -9
- 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} +1 -1
- 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 +142 -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} +8 -8
- 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__/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 +737 -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-remove-legacy-skills-index.test.ts +309 -0
- package/src/__tests__/workspace-migrations-runner.test.ts +111 -3
- package/src/acp/resolve-agent.ts +1 -1
- package/src/agent/image-optimize.ts +13 -5
- package/src/calls/voice-session-bridge.ts +61 -42
- package/src/channels/types.ts +108 -0
- package/src/cli/__tests__/unknown-command.test.ts +24 -0
- package/src/cli/commands/__tests__/changelog.test.ts +304 -319
- package/src/cli/commands/__tests__/schedules.test.ts +491 -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 +329 -317
- package/src/cli/commands/plugins.ts +185 -0
- package/src/cli/commands/schedules.ts +391 -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__/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 +304 -0
- package/src/cli/lib/list-installed-plugins.ts +137 -0
- package/src/cli/lib/uninstall-plugin.ts +82 -0
- package/src/cli/lib/unknown-command.ts +111 -0
- package/src/cli/program.ts +38 -2
- package/src/config/bundled-skills/app-builder/SKILL.md +23 -21
- 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/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/feature-flag-registry.json +41 -1
- package/src/config/loader.ts +64 -38
- package/src/config/schema.ts +7 -10
- package/src/config/schemas/__tests__/llm-request-logs.test.ts +36 -0
- package/src/config/schemas/channels.ts +8 -0
- package/src/config/schemas/compaction.ts +28 -0
- package/src/config/schemas/heartbeat.ts +9 -0
- package/src/config/schemas/llm-request-logs.ts +31 -7
- package/src/config/schemas/llm.ts +3 -0
- package/src/config/schemas/memory-retrieval.ts +18 -0
- package/src/config/schemas/tools.ts +14 -0
- package/src/config/skills.ts +3 -96
- package/src/context/compactor.ts +1047 -0
- package/src/context/token-estimator.ts +2 -2
- 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 +34 -0
- 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.ts +169 -9
- 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 +16 -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 -1
- package/src/daemon/external-plugins-bootstrap.ts +217 -181
- package/src/daemon/first-greeting.ts +22 -2
- package/src/daemon/handlers/config-model.ts +6 -5
- package/src/daemon/handlers/config-slack-channel.ts +15 -3
- 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 +98 -23
- package/src/daemon/lifecycle.ts +45 -35
- package/src/daemon/meet-host-supervisor.ts +5 -4
- package/src/daemon/memory-v2-startup.ts +49 -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/subagents.ts +1 -0
- package/src/daemon/message-types/sync.ts +1 -0
- package/src/daemon/pkb-reminder-builder.test.ts +1 -1
- package/src/daemon/pkb-reminder-builder.ts +1 -1
- package/src/daemon/plugin-source-watcher.ts +146 -0
- package/src/daemon/process-message.ts +21 -3
- package/src/daemon/server.ts +11 -2
- package/src/daemon/skill-memory-refresh.ts +29 -0
- package/src/documents/document-store.ts +221 -3
- package/src/embedded/plugin-api.ts +40 -0
- package/src/filing/filing-service.ts +39 -0
- package/src/heartbeat/__tests__/heartbeat-service.test.ts +91 -6
- package/src/heartbeat/heartbeat-run-store.ts +2 -1
- package/src/heartbeat/heartbeat-service.ts +41 -0
- package/src/home/__tests__/feed-types.test.ts +40 -0
- package/src/home/feed-types.ts +22 -0
- package/src/home/post-connect-feed.ts +1 -0
- package/src/index.ts +18 -1
- 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 +263 -0
- package/src/memory/__tests__/jobs-worker-v2-graph-trigger-embed.test.ts +113 -0
- 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 +110 -10
- package/src/memory/db-init.ts +6 -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 +66 -9
- package/src/memory/graph/conversation-graph-memory.ts +31 -15
- package/src/memory/graph/tools.ts +3 -3
- package/src/memory/indexer.ts +34 -29
- 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 +17 -10
- package/src/memory/llm-request-log-source.ts +19 -52
- package/src/memory/llm-usage-store.ts +125 -5
- 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/index.ts +6 -0
- package/src/memory/migrations/registry.ts +8 -0
- package/src/memory/onboarding-events-store.ts +106 -0
- package/src/memory/schema/bookmarks.ts +0 -2
- package/src/memory/schema/calls.ts +1 -0
- package/src/memory/schema/inference.ts +1 -3
- package/src/memory/schema/infrastructure.ts +12 -0
- package/src/memory/turn-events-store.ts +127 -2
- package/src/memory/v2/__tests__/activation.test.ts +0 -8
- package/src/memory/v2/__tests__/injection.test.ts +98 -8
- 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/injection.ts +32 -6
- package/src/memory/v2/migration.ts +49 -19
- package/src/memory/v2/page-index.ts +35 -5
- 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/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/conversation-pairing.ts +2 -1
- package/src/notifications/decision-engine.ts +2 -1
- package/src/notifications/emit-signal.ts +20 -1
- package/src/notifications/home-feed-side-effect.ts +54 -0
- package/src/notifications/signal.ts +3 -1
- package/src/oauth/connection-resolver.ts +8 -4
- package/src/oauth/platform-connection.ts +6 -2
- 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 +36 -3
- 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 +105 -32
- 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 +31 -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 +12 -0
- package/src/prompts/__tests__/task-progress-hint-section.test.ts +99 -0
- package/src/prompts/normalize-onboarding.ts +27 -0
- package/src/prompts/sections.ts +302 -0
- package/src/prompts/system-prompt.ts +63 -166
- package/src/prompts/templates/BOOTSTRAP.md +17 -1
- package/src/prompts/templates/system-sections.ts +173 -0
- package/src/providers/__tests__/inference.test.ts +22 -7
- package/src/providers/anthropic/client.ts +28 -28
- package/src/providers/connection-resolution.ts +7 -0
- package/src/providers/inference/adapter-factory.ts +41 -4
- package/src/providers/inference/connections.ts +74 -29
- package/src/providers/inference/resolve-auth.ts +12 -4
- package/src/providers/model-catalog.ts +294 -12
- package/src/providers/openai/chat-completions-provider.ts +10 -2
- package/src/providers/openrouter/client.ts +7 -0
- package/src/providers/{managed-proxy → platform-proxy}/constants.ts +4 -1
- 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 +36 -0
- package/src/providers/registry.ts +22 -14
- package/src/providers/retry.ts +47 -1
- package/src/runtime/__tests__/agent-wake.test.ts +152 -0
- package/src/runtime/agent-wake.ts +42 -14
- package/src/runtime/auth/route-policy.ts +8 -1
- package/src/runtime/btw-sidechain.ts +2 -0
- package/src/runtime/http-types.ts +19 -0
- 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__/conversation-management-routes.test.ts +5 -1
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +107 -20
- 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 +121 -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 +40 -35
- package/src/runtime/routes/conversation-routes.ts +90 -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 +6 -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 +65 -21
- 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/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 +57 -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 +15 -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/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 +72 -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/registry.ts +8 -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/context/__tests__/compact-prompt.test.ts +0 -63
- package/src/context/prompts/compact.md +0 -26
- package/src/prompts/__tests__/build-cli-reference-section.test.ts +0 -37
- /package/src/__tests__/{secret-routes-managed-proxy.test.ts → secret-routes-platform-proxy.test.ts} +0 -0
|
@@ -22,6 +22,8 @@ import { beforeEach, describe, expect, mock, test } from "bun:test";
|
|
|
22
22
|
interface MockProviderRow {
|
|
23
23
|
provider: string;
|
|
24
24
|
managedServiceConfigKey: string | null;
|
|
25
|
+
baseUrl: string | null;
|
|
26
|
+
injectionTemplates: string | null;
|
|
25
27
|
pingUrl: string | null;
|
|
26
28
|
pingMethod: string | null;
|
|
27
29
|
pingHeaders: string | null;
|
|
@@ -31,6 +33,8 @@ interface MockProviderRow {
|
|
|
31
33
|
const baseProvider: MockProviderRow = {
|
|
32
34
|
provider: "google",
|
|
33
35
|
managedServiceConfigKey: "google-oauth",
|
|
36
|
+
baseUrl: "https://api.google.com",
|
|
37
|
+
injectionTemplates: null,
|
|
34
38
|
pingUrl: null,
|
|
35
39
|
pingMethod: null,
|
|
36
40
|
pingHeaders: null,
|
|
@@ -45,7 +49,10 @@ let mockApps: Record<string, unknown> = {};
|
|
|
45
49
|
let mockTokenValue = "tok-fake";
|
|
46
50
|
let platformAvailable = true;
|
|
47
51
|
let platformAssistantId: string | null = "assistant-1";
|
|
48
|
-
let mockFetchImpl: (
|
|
52
|
+
let mockFetchImpl: (
|
|
53
|
+
path: string,
|
|
54
|
+
init?: RequestInit,
|
|
55
|
+
) => Promise<{
|
|
49
56
|
ok: boolean;
|
|
50
57
|
status: number;
|
|
51
58
|
json: () => Promise<unknown>;
|
|
@@ -61,6 +68,7 @@ let mockResolveResponse: {
|
|
|
61
68
|
headers: Record<string, string>;
|
|
62
69
|
body: unknown;
|
|
63
70
|
} = { status: 200, headers: {}, body: { ok: true } };
|
|
71
|
+
let mockResolveRequests: unknown[] = [];
|
|
64
72
|
|
|
65
73
|
const mockDisconnectOAuthProvider = mock(() => Promise.resolve());
|
|
66
74
|
const mockSaveRawConfig = mock(() => undefined);
|
|
@@ -102,7 +110,10 @@ mock.module("../oauth/oauth-store.js", () => ({
|
|
|
102
110
|
|
|
103
111
|
mock.module("../oauth/connection-resolver.js", () => ({
|
|
104
112
|
resolveOAuthConnection: async (_provider: string) => ({
|
|
105
|
-
request: async () =>
|
|
113
|
+
request: async (req: unknown) => {
|
|
114
|
+
mockResolveRequests.push(req);
|
|
115
|
+
return mockResolveResponse;
|
|
116
|
+
},
|
|
106
117
|
}),
|
|
107
118
|
}));
|
|
108
119
|
|
|
@@ -119,17 +130,19 @@ mock.module("../platform/client.js", () => ({
|
|
|
119
130
|
}));
|
|
120
131
|
|
|
121
132
|
mock.module("../security/token-manager.js", () => ({
|
|
122
|
-
withValidToken: async <T
|
|
123
|
-
|
|
124
|
-
fn: (t: string) => Promise<T>,
|
|
125
|
-
) => fn(mockTokenValue),
|
|
133
|
+
withValidToken: async <T>(_provider: string, fn: (t: string) => Promise<T>) =>
|
|
134
|
+
fn(mockTokenValue),
|
|
126
135
|
}));
|
|
127
136
|
|
|
128
137
|
mock.module("../config/loader.js", () => ({
|
|
129
138
|
getConfig: () => ({ services: {} }),
|
|
130
139
|
loadRawConfig: () => ({ services: {} }),
|
|
131
140
|
saveRawConfig: mockSaveRawConfig,
|
|
132
|
-
setNestedValue: (
|
|
141
|
+
setNestedValue: (
|
|
142
|
+
obj: Record<string, unknown>,
|
|
143
|
+
path: string,
|
|
144
|
+
value: unknown,
|
|
145
|
+
) => {
|
|
133
146
|
const parts = path.split(".");
|
|
134
147
|
let cur: Record<string, unknown> = obj;
|
|
135
148
|
for (let i = 0; i < parts.length - 1; i++) {
|
|
@@ -152,7 +165,11 @@ mock.module("../config/schemas/services.js", () => ({
|
|
|
152
165
|
},
|
|
153
166
|
}));
|
|
154
167
|
|
|
155
|
-
import {
|
|
168
|
+
import {
|
|
169
|
+
BadRequestError,
|
|
170
|
+
InternalError,
|
|
171
|
+
NotFoundError,
|
|
172
|
+
} from "../runtime/routes/errors.js";
|
|
156
173
|
import { ROUTES } from "../runtime/routes/oauth-commands-routes.js";
|
|
157
174
|
import type { RouteHandlerArgs } from "../runtime/routes/types.js";
|
|
158
175
|
|
|
@@ -194,6 +211,7 @@ beforeEach(() => {
|
|
|
194
211
|
text: async () => "",
|
|
195
212
|
});
|
|
196
213
|
mockResolveResponse = { status: 200, headers: {}, body: { ok: true } };
|
|
214
|
+
mockResolveRequests = [];
|
|
197
215
|
mockDisconnectOAuthProvider.mockClear();
|
|
198
216
|
mockSaveRawConfig.mockClear();
|
|
199
217
|
});
|
|
@@ -430,7 +448,10 @@ describe("GET oauth/status", () => {
|
|
|
430
448
|
];
|
|
431
449
|
const result = (await getRoute("GET", "oauth/status").handler(
|
|
432
450
|
makeArgs({ queryParams: { provider: "google" } }),
|
|
433
|
-
)) as {
|
|
451
|
+
)) as {
|
|
452
|
+
mode: string;
|
|
453
|
+
connections: Array<{ id: string; grantedScopes: string[] }>;
|
|
454
|
+
};
|
|
434
455
|
expect(result.mode).toBe("byo");
|
|
435
456
|
expect(result.connections).toHaveLength(1);
|
|
436
457
|
expect(result.connections[0]!.id).toBe("conn-1");
|
|
@@ -507,7 +528,11 @@ describe("POST oauth/ping", () => {
|
|
|
507
528
|
...baseProvider,
|
|
508
529
|
pingUrl: "https://api.google.com/v1/me",
|
|
509
530
|
};
|
|
510
|
-
mockResolveResponse = {
|
|
531
|
+
mockResolveResponse = {
|
|
532
|
+
status: 401,
|
|
533
|
+
headers: {},
|
|
534
|
+
body: { error: "unauthorized" },
|
|
535
|
+
};
|
|
511
536
|
const result = (await getRoute("POST", "oauth/ping").handler(
|
|
512
537
|
makeArgs({ body: { provider: "google" } }),
|
|
513
538
|
)) as { ok: boolean; status: number; hint?: string };
|
|
@@ -543,7 +568,9 @@ describe("POST oauth/token", () => {
|
|
|
543
568
|
// No active connections registered for google
|
|
544
569
|
await expect(
|
|
545
570
|
getRoute("POST", "oauth/token").handler(
|
|
546
|
-
makeArgs({
|
|
571
|
+
makeArgs({
|
|
572
|
+
body: { provider: "google", account: "missing@example.com" },
|
|
573
|
+
}),
|
|
547
574
|
),
|
|
548
575
|
).rejects.toBeInstanceOf(NotFoundError);
|
|
549
576
|
});
|
|
@@ -578,6 +605,130 @@ describe("POST oauth/request", () => {
|
|
|
578
605
|
expect(result.body).toEqual({ hello: "world" });
|
|
579
606
|
});
|
|
580
607
|
|
|
608
|
+
test("rejects absolute URL host outside provider base host when no injection templates exist", async () => {
|
|
609
|
+
await expect(
|
|
610
|
+
getRoute("POST", "oauth/request").handler(
|
|
611
|
+
makeArgs({
|
|
612
|
+
body: { provider: "google", url: "https://attacker.example/v1/me" },
|
|
613
|
+
}),
|
|
614
|
+
),
|
|
615
|
+
).rejects.toBeInstanceOf(BadRequestError);
|
|
616
|
+
expect(mockResolveRequests).toHaveLength(0);
|
|
617
|
+
});
|
|
618
|
+
|
|
619
|
+
test("rejects protocol downgrade for absolute OAuth request URLs", async () => {
|
|
620
|
+
await expect(
|
|
621
|
+
getRoute("POST", "oauth/request").handler(
|
|
622
|
+
makeArgs({
|
|
623
|
+
body: { provider: "google", url: "http://api.google.com/v1/me" },
|
|
624
|
+
}),
|
|
625
|
+
),
|
|
626
|
+
).rejects.toBeInstanceOf(BadRequestError);
|
|
627
|
+
expect(mockResolveRequests).toHaveLength(0);
|
|
628
|
+
});
|
|
629
|
+
|
|
630
|
+
test("rejects absolute URL host outside provider injection templates", async () => {
|
|
631
|
+
mockProviders.slack_channel = {
|
|
632
|
+
...baseProvider,
|
|
633
|
+
provider: "slack_channel",
|
|
634
|
+
managedServiceConfigKey: null,
|
|
635
|
+
baseUrl: "https://slack.com/api",
|
|
636
|
+
injectionTemplates: JSON.stringify([
|
|
637
|
+
{
|
|
638
|
+
hostPattern: "slack.com",
|
|
639
|
+
injectionType: "header",
|
|
640
|
+
headerName: "Authorization",
|
|
641
|
+
valuePrefix: "Bearer ",
|
|
642
|
+
},
|
|
643
|
+
]),
|
|
644
|
+
};
|
|
645
|
+
|
|
646
|
+
await expect(
|
|
647
|
+
getRoute("POST", "oauth/request").handler(
|
|
648
|
+
makeArgs({
|
|
649
|
+
body: {
|
|
650
|
+
provider: "slack_channel",
|
|
651
|
+
url: "https://attacker.example/api/auth.test",
|
|
652
|
+
},
|
|
653
|
+
}),
|
|
654
|
+
),
|
|
655
|
+
).rejects.toBeInstanceOf(BadRequestError);
|
|
656
|
+
expect(mockResolveRequests).toHaveLength(0);
|
|
657
|
+
});
|
|
658
|
+
|
|
659
|
+
test("allows absolute URL host matching provider injection templates", async () => {
|
|
660
|
+
mockProviders.slack_channel = {
|
|
661
|
+
...baseProvider,
|
|
662
|
+
provider: "slack_channel",
|
|
663
|
+
managedServiceConfigKey: null,
|
|
664
|
+
baseUrl: "https://slack.com/api",
|
|
665
|
+
injectionTemplates: JSON.stringify([
|
|
666
|
+
{
|
|
667
|
+
hostPattern: "slack.com",
|
|
668
|
+
injectionType: "header",
|
|
669
|
+
headerName: "Authorization",
|
|
670
|
+
valuePrefix: "Bearer ",
|
|
671
|
+
},
|
|
672
|
+
]),
|
|
673
|
+
};
|
|
674
|
+
|
|
675
|
+
await getRoute("POST", "oauth/request").handler(
|
|
676
|
+
makeArgs({
|
|
677
|
+
body: {
|
|
678
|
+
provider: "slack_channel",
|
|
679
|
+
url: "https://slack.com/api/auth.test?team=T123",
|
|
680
|
+
},
|
|
681
|
+
}),
|
|
682
|
+
);
|
|
683
|
+
|
|
684
|
+
expect(mockResolveRequests).toEqual([
|
|
685
|
+
{
|
|
686
|
+
method: "GET",
|
|
687
|
+
path: "/api/auth.test",
|
|
688
|
+
query: { team: "T123" },
|
|
689
|
+
baseUrl: "https://slack.com",
|
|
690
|
+
},
|
|
691
|
+
]);
|
|
692
|
+
});
|
|
693
|
+
|
|
694
|
+
test("allows cross-host absolute URLs declared by provider injection templates", async () => {
|
|
695
|
+
mockProviders.google = {
|
|
696
|
+
...baseProvider,
|
|
697
|
+
baseUrl: "https://gmail.googleapis.com/gmail/v1/users/me",
|
|
698
|
+
injectionTemplates: JSON.stringify([
|
|
699
|
+
{
|
|
700
|
+
hostPattern: "gmail.googleapis.com",
|
|
701
|
+
injectionType: "header",
|
|
702
|
+
headerName: "Authorization",
|
|
703
|
+
valuePrefix: "Bearer ",
|
|
704
|
+
},
|
|
705
|
+
{
|
|
706
|
+
hostPattern: "www.googleapis.com",
|
|
707
|
+
injectionType: "header",
|
|
708
|
+
headerName: "Authorization",
|
|
709
|
+
valuePrefix: "Bearer ",
|
|
710
|
+
},
|
|
711
|
+
]),
|
|
712
|
+
};
|
|
713
|
+
|
|
714
|
+
await getRoute("POST", "oauth/request").handler(
|
|
715
|
+
makeArgs({
|
|
716
|
+
body: {
|
|
717
|
+
provider: "google",
|
|
718
|
+
url: "https://www.googleapis.com/calendar/v3/calendars",
|
|
719
|
+
},
|
|
720
|
+
}),
|
|
721
|
+
);
|
|
722
|
+
|
|
723
|
+
expect(mockResolveRequests).toEqual([
|
|
724
|
+
{
|
|
725
|
+
method: "GET",
|
|
726
|
+
path: "/calendar/v3/calendars",
|
|
727
|
+
baseUrl: "https://www.googleapis.com",
|
|
728
|
+
},
|
|
729
|
+
]);
|
|
730
|
+
});
|
|
731
|
+
|
|
581
732
|
test("attaches reconnect hint on 401 response", async () => {
|
|
582
733
|
mockResolveResponse = { status: 401, headers: {}, body: { error: "no" } };
|
|
583
734
|
const result = (await getRoute("POST", "oauth/request").handler(
|
|
@@ -681,10 +832,7 @@ describe("GET oauth/managed-connect/poll", () => {
|
|
|
681
832
|
],
|
|
682
833
|
text: async () => "",
|
|
683
834
|
});
|
|
684
|
-
const result = (await getRoute(
|
|
685
|
-
"GET",
|
|
686
|
-
"oauth/managed-connect/poll",
|
|
687
|
-
).handler(
|
|
835
|
+
const result = (await getRoute("GET", "oauth/managed-connect/poll").handler(
|
|
688
836
|
makeArgs({ queryParams: { provider: "google" } }),
|
|
689
837
|
)) as {
|
|
690
838
|
ok: boolean;
|
|
@@ -696,7 +844,11 @@ describe("GET oauth/managed-connect/poll", () => {
|
|
|
696
844
|
};
|
|
697
845
|
expect(result.ok).toBe(true);
|
|
698
846
|
expect(result.connections).toEqual([
|
|
699
|
-
{
|
|
847
|
+
{
|
|
848
|
+
id: "conn-1",
|
|
849
|
+
account_label: "alice@example.com",
|
|
850
|
+
scopes_granted: ["email"],
|
|
851
|
+
},
|
|
700
852
|
]);
|
|
701
853
|
});
|
|
702
854
|
|
|
@@ -21,6 +21,15 @@ initializeDb();
|
|
|
21
21
|
seedOAuthProviders();
|
|
22
22
|
|
|
23
23
|
describe("oauth provider profiles (DB-seeded)", () => {
|
|
24
|
+
test("google provider row includes Drive in default scopes", () => {
|
|
25
|
+
const provider = getProvider("google");
|
|
26
|
+
|
|
27
|
+
expect(provider).toBeDefined();
|
|
28
|
+
expect(JSON.parse(provider!.defaultScopes)).toContain(
|
|
29
|
+
"https://www.googleapis.com/auth/drive",
|
|
30
|
+
);
|
|
31
|
+
});
|
|
32
|
+
|
|
24
33
|
test("google provider row contains bearer injection templates for 3 Google API hosts", () => {
|
|
25
34
|
const provider = getProvider("google");
|
|
26
35
|
|
|
@@ -1344,6 +1344,30 @@ describe("OpenRouterProvider reasoning", () => {
|
|
|
1344
1344
|
expect(lastCreateParams!.reasoning).toEqual({ enabled: false });
|
|
1345
1345
|
});
|
|
1346
1346
|
|
|
1347
|
+
test("sends OpenRouter app-attribution headers on OpenAI-compatible requests", async () => {
|
|
1348
|
+
const provider = new OpenRouterProvider("or-key", "x-ai/grok-4");
|
|
1349
|
+
await provider.sendMessage([userMsg("hi")], undefined, undefined, {
|
|
1350
|
+
config: {
|
|
1351
|
+
usageAttributionHeaders: {
|
|
1352
|
+
"Vellum-Organization-Id": "org-123",
|
|
1353
|
+
},
|
|
1354
|
+
},
|
|
1355
|
+
});
|
|
1356
|
+
|
|
1357
|
+
expect(lastCreateOptions?.headers).toEqual(
|
|
1358
|
+
expect.objectContaining({
|
|
1359
|
+
"HTTP-Referer": "https://www.vellum.ai",
|
|
1360
|
+
"X-OpenRouter-Title": "Vellum Assistant",
|
|
1361
|
+
"X-OpenRouter-Categories": "personal-agent,cli-agent",
|
|
1362
|
+
"Vellum-Organization-Id": "org-123",
|
|
1363
|
+
}),
|
|
1364
|
+
);
|
|
1365
|
+
expect(lastCreateParams).not.toHaveProperty("HTTP-Referer");
|
|
1366
|
+
expect(lastCreateParams).not.toHaveProperty("X-OpenRouter-Title");
|
|
1367
|
+
expect(lastCreateParams).not.toHaveProperty("X-OpenRouter-Categories");
|
|
1368
|
+
expect(lastCreateParams).not.toHaveProperty("usageAttributionHeaders");
|
|
1369
|
+
});
|
|
1370
|
+
|
|
1347
1371
|
test("RetryProvider + OpenRouterProvider enables thinking end-to-end", async () => {
|
|
1348
1372
|
const provider = new OpenRouterProvider("or-key", "x-ai/grok-4");
|
|
1349
1373
|
const retry = new RetryProvider(provider);
|
|
@@ -91,18 +91,26 @@ describe("OpenAI Responses API cutover guard", () => {
|
|
|
91
91
|
).toBe(true);
|
|
92
92
|
|
|
93
93
|
// The factory must NOT instantiate OpenAIChatCompletionsProvider or
|
|
94
|
-
// OpenAIProvider (the backward-compatible alias)
|
|
95
|
-
//
|
|
96
|
-
//
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
94
|
+
// OpenAIProvider (the backward-compatible alias) inside the `openai:`
|
|
95
|
+
// factory entry. Other entries (e.g. `zai:`, `deepseek:`, `minimax:`)
|
|
96
|
+
// may legitimately use OpenAIChatCompletionsProvider since that's the
|
|
97
|
+
// OpenAI Chat Completions transport for third-party endpoints.
|
|
98
|
+
const openaiEntryRegion =
|
|
99
|
+
/(?:^|\s)openai\s*:\s*\([^)]*\)\s*=>\s*[\s\S]{0,400}?(?=\}\s*,\s*[a-z-]+\s*:|\}\s*;)/m.exec(
|
|
100
|
+
source,
|
|
101
|
+
)?.[0] ?? "";
|
|
102
|
+
const chatCompletionsInstantiationsInOpenAi = [
|
|
103
|
+
...openaiEntryRegion.matchAll(
|
|
104
|
+
/new\s+OpenAIChatCompletionsProvider\s*\(/g,
|
|
105
|
+
),
|
|
106
|
+
...openaiEntryRegion.matchAll(/new\s+OpenAIProvider\s*\(/g),
|
|
100
107
|
];
|
|
101
108
|
expect(
|
|
102
|
-
|
|
109
|
+
chatCompletionsInstantiationsInOpenAi.length,
|
|
103
110
|
[
|
|
104
|
-
"
|
|
105
|
-
"OpenAIProvider (legacy alias).
|
|
111
|
+
"ADAPTER_FACTORIES['openai'] must NOT instantiate",
|
|
112
|
+
"OpenAIChatCompletionsProvider or OpenAIProvider (legacy alias).",
|
|
113
|
+
"Use OpenAIResponsesProvider for openai.",
|
|
106
114
|
].join("\n"),
|
|
107
115
|
).toBe(0);
|
|
108
116
|
|
|
@@ -453,7 +453,6 @@ describe("overflow-reduce pipeline", () => {
|
|
|
453
453
|
manifest: {
|
|
454
454
|
name: "spy-overflow",
|
|
455
455
|
version: "0.0.1",
|
|
456
|
-
requires: { pluginRuntime: "v1", overflowReduceApi: "v1" },
|
|
457
456
|
},
|
|
458
457
|
middleware: { overflowReduce: spy },
|
|
459
458
|
};
|
|
@@ -512,7 +511,6 @@ describe("overflow-reduce pipeline", () => {
|
|
|
512
511
|
manifest: {
|
|
513
512
|
name: "short-circuit-overflow",
|
|
514
513
|
version: "0.0.1",
|
|
515
|
-
requires: { pluginRuntime: "v1", overflowReduceApi: "v1" },
|
|
516
514
|
},
|
|
517
515
|
middleware: { overflowReduce: shortCircuit },
|
|
518
516
|
});
|
|
@@ -267,7 +267,6 @@ describe("persistence pipeline", () => {
|
|
|
267
267
|
manifest: {
|
|
268
268
|
name: "mock-persistence",
|
|
269
269
|
version: "0.0.1",
|
|
270
|
-
requires: { pluginRuntime: "v1" },
|
|
271
270
|
},
|
|
272
271
|
middleware: { persistence: redirect },
|
|
273
272
|
};
|
|
@@ -352,7 +351,6 @@ describe("persistence pipeline", () => {
|
|
|
352
351
|
manifest: {
|
|
353
352
|
name: "late-user-plugin",
|
|
354
353
|
version: "0.0.1",
|
|
355
|
-
requires: { pluginRuntime: "v1" },
|
|
356
354
|
},
|
|
357
355
|
middleware: { persistence: userMiddleware },
|
|
358
356
|
});
|
|
@@ -32,7 +32,7 @@ import {
|
|
|
32
32
|
hasManagedProxyPrereqs,
|
|
33
33
|
managedFallbackEnabledFor,
|
|
34
34
|
resolveManagedProxyContext,
|
|
35
|
-
} from "../providers/
|
|
35
|
+
} from "../providers/platform-proxy/context.js";
|
|
36
36
|
|
|
37
37
|
describe("resolveManagedProxyContext", () => {
|
|
38
38
|
beforeEach(() => {
|
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
getWorkspaceConfigPath,
|
|
17
17
|
getWorkspaceDir,
|
|
18
18
|
getWorkspaceHooksDir,
|
|
19
|
+
getWorkspacePluginsDir,
|
|
19
20
|
getWorkspacePromptPath,
|
|
20
21
|
getWorkspaceSkillsDir,
|
|
21
22
|
getXdgVellumConfigDirName,
|
|
@@ -161,6 +162,7 @@ describe("workspace path primitives", () => {
|
|
|
161
162
|
expect(getWorkspaceConfigPath()).toBe(join(ws, "config.json"));
|
|
162
163
|
expect(getWorkspaceSkillsDir()).toBe(join(ws, "skills"));
|
|
163
164
|
expect(getWorkspaceHooksDir()).toBe(join(ws, "hooks"));
|
|
165
|
+
expect(getWorkspacePluginsDir()).toBe(join(ws, "plugins"));
|
|
164
166
|
expect(getWorkspacePromptPath("IDENTITY.md")).toBe(join(ws, "IDENTITY.md"));
|
|
165
167
|
expect(getWorkspacePromptPath("SOUL.md")).toBe(join(ws, "SOUL.md"));
|
|
166
168
|
});
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Smoke tests for the workspace-level `@vellumai/plugin-api` shim.
|
|
3
|
+
*
|
|
4
|
+
* - shim files are materialized at `<workspaceDir>/node_modules/@vellumai/plugin-api/`
|
|
5
|
+
* - the shim's index.js re-binds each runtime export from globalThis
|
|
6
|
+
* - the shim is idempotent across re-runs
|
|
7
|
+
* - a fake plugin in `<workspaceDir>/plugins/<name>/` can resolve the
|
|
8
|
+
* bare `@vellumai/plugin-api` specifier via Node-style walk-up,
|
|
9
|
+
* proving the end-to-end import path works for real user plugins
|
|
10
|
+
*
|
|
11
|
+
* As plugin-api's runtime surface grows in follow-up PRs, the shim's
|
|
12
|
+
* generated export list expands automatically — the test below covers
|
|
13
|
+
* the generator (`buildShimSource`) directly so we don't need to
|
|
14
|
+
* update assertions every time an export is added.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { mkdir, mkdtemp, readFile, writeFile } from "node:fs/promises";
|
|
18
|
+
import { tmpdir } from "node:os";
|
|
19
|
+
import { join } from "node:path";
|
|
20
|
+
import { describe, expect, test } from "bun:test";
|
|
21
|
+
|
|
22
|
+
import {
|
|
23
|
+
PLUGIN_API_EXPORTS,
|
|
24
|
+
PLUGIN_API_REGISTRY_KEY,
|
|
25
|
+
} from "../embedded/plugin-api.js";
|
|
26
|
+
import {
|
|
27
|
+
buildShimSource,
|
|
28
|
+
ensurePluginApiShim,
|
|
29
|
+
} from "../plugins/ensure-plugin-api-shim.js";
|
|
30
|
+
|
|
31
|
+
const SHIM_REL_PATH = "node_modules/@vellumai/plugin-api";
|
|
32
|
+
|
|
33
|
+
describe("buildShimSource", () => {
|
|
34
|
+
test("emits a globalThis trampoline + one binding per export", () => {
|
|
35
|
+
const source = buildShimSource(
|
|
36
|
+
["foo", "bar"],
|
|
37
|
+
Symbol.for("vellum.plugin-api.v1"),
|
|
38
|
+
);
|
|
39
|
+
expect(source).toBe(
|
|
40
|
+
`const api = globalThis[Symbol.for("vellum.plugin-api.v1")];\n` +
|
|
41
|
+
`export const foo = api.foo;\n` +
|
|
42
|
+
`export const bar = api.bar;\n`,
|
|
43
|
+
);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
test("handles an empty export list (today's types-only surface)", () => {
|
|
47
|
+
const source = buildShimSource(
|
|
48
|
+
[],
|
|
49
|
+
Symbol.for("vellum.plugin-api.v1"),
|
|
50
|
+
);
|
|
51
|
+
expect(source).toBe(
|
|
52
|
+
`const api = globalThis[Symbol.for("vellum.plugin-api.v1")];\n`,
|
|
53
|
+
);
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
describe("ensurePluginApiShim", () => {
|
|
58
|
+
test("creates a resolvable @vellumai/plugin-api package under workspaceDir", async () => {
|
|
59
|
+
const workspaceDir = await mkdtemp(join(tmpdir(), "plugin-api-shim-"));
|
|
60
|
+
await ensurePluginApiShim({ workspaceDir });
|
|
61
|
+
|
|
62
|
+
const shimDir = join(workspaceDir, SHIM_REL_PATH);
|
|
63
|
+
const indexJs = await readFile(join(shimDir, "index.js"), "utf8");
|
|
64
|
+
expect(indexJs).toBe(buildShimSource());
|
|
65
|
+
|
|
66
|
+
const pkg = JSON.parse(
|
|
67
|
+
await readFile(join(shimDir, "package.json"), "utf8"),
|
|
68
|
+
);
|
|
69
|
+
expect(pkg.name).toBe("@vellumai/plugin-api");
|
|
70
|
+
expect(pkg.type).toBe("module");
|
|
71
|
+
expect(pkg.main).toBe("./index.js");
|
|
72
|
+
expect(typeof pkg.version).toBe("string");
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
test("is idempotent — re-running yields the same shim contents", async () => {
|
|
76
|
+
const workspaceDir = await mkdtemp(join(tmpdir(), "plugin-api-shim-"));
|
|
77
|
+
await ensurePluginApiShim({ workspaceDir });
|
|
78
|
+
const first = await readFile(
|
|
79
|
+
join(workspaceDir, SHIM_REL_PATH, "index.js"),
|
|
80
|
+
"utf8",
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
await ensurePluginApiShim({ workspaceDir });
|
|
84
|
+
const second = await readFile(
|
|
85
|
+
join(workspaceDir, SHIM_REL_PATH, "index.js"),
|
|
86
|
+
"utf8",
|
|
87
|
+
);
|
|
88
|
+
expect(second).toBe(first);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
test("globalThis is populated with the plugin-api namespace", () => {
|
|
92
|
+
// Importing the embed wrapper has the side effect of installing the
|
|
93
|
+
// namespace on globalThis. By the time this test runs (any earlier
|
|
94
|
+
// test in the file has already imported it), the registry must be
|
|
95
|
+
// populated.
|
|
96
|
+
const namespace = (globalThis as Record<symbol, unknown>)[
|
|
97
|
+
PLUGIN_API_REGISTRY_KEY
|
|
98
|
+
];
|
|
99
|
+
expect(namespace).toBeDefined();
|
|
100
|
+
// Exports list is non-null but may be empty until runtime exports
|
|
101
|
+
// migrate in later PRs.
|
|
102
|
+
expect(Array.isArray(PLUGIN_API_EXPORTS)).toBe(true);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
test("a fake user plugin can resolve @vellumai/plugin-api via Node-style walk-up", async () => {
|
|
106
|
+
const workspaceDir = await mkdtemp(join(tmpdir(), "plugin-api-shim-"));
|
|
107
|
+
await ensurePluginApiShim({ workspaceDir });
|
|
108
|
+
|
|
109
|
+
const pluginDir = join(workspaceDir, "plugins", "fake-plugin");
|
|
110
|
+
await mkdir(pluginDir, { recursive: true });
|
|
111
|
+
await writeFile(
|
|
112
|
+
join(pluginDir, "register.js"),
|
|
113
|
+
`import * as api from "@vellumai/plugin-api";\nexport { api };\n`,
|
|
114
|
+
);
|
|
115
|
+
|
|
116
|
+
// Resolution walks up: plugins/fake-plugin → plugins → workspaceDir
|
|
117
|
+
// → workspaceDir/node_modules/@vellumai/plugin-api → shim → globalThis
|
|
118
|
+
// → plugin-api namespace. If any link in that chain is broken, this
|
|
119
|
+
// import throws.
|
|
120
|
+
const mod: { api: Record<string, unknown> } = await import(
|
|
121
|
+
join(pluginDir, "register.js")
|
|
122
|
+
);
|
|
123
|
+
expect(mod.api).toBeDefined();
|
|
124
|
+
});
|
|
125
|
+
});
|
|
@@ -43,7 +43,6 @@ import {
|
|
|
43
43
|
import { runShutdownHooks } from "../daemon/shutdown-registry.js";
|
|
44
44
|
import { RiskLevel } from "../permissions/types.js";
|
|
45
45
|
import {
|
|
46
|
-
ASSISTANT_API_VERSIONS,
|
|
47
46
|
getInjectors,
|
|
48
47
|
getMiddlewaresFor,
|
|
49
48
|
registerPlugin,
|
|
@@ -84,7 +83,6 @@ function buildPlugin(
|
|
|
84
83
|
onShutdown?: () => Promise<void>;
|
|
85
84
|
} = {},
|
|
86
85
|
options: {
|
|
87
|
-
requires?: Record<string, string>;
|
|
88
86
|
requiresCredential?: string[];
|
|
89
87
|
requiresFlag?: string[];
|
|
90
88
|
} = {},
|
|
@@ -111,7 +109,6 @@ function buildPlugin(
|
|
|
111
109
|
manifest: {
|
|
112
110
|
name,
|
|
113
111
|
version: "0.0.1",
|
|
114
|
-
requires: options.requires ?? { pluginRuntime: "v1" },
|
|
115
112
|
...(options.requiresCredential
|
|
116
113
|
? { requiresCredential: options.requiresCredential }
|
|
117
114
|
: {}),
|
|
@@ -161,10 +158,6 @@ describe("plugin bootstrap", () => {
|
|
|
161
158
|
);
|
|
162
159
|
expect(existsSync(ctx.pluginStorageDir)).toBe(true);
|
|
163
160
|
expect(ctx.assistantVersion).toBe("9.9.9-test");
|
|
164
|
-
// apiVersions must surface the canonical capability table from the
|
|
165
|
-
// registry so plugins can negotiate at runtime.
|
|
166
|
-
expect(ctx.apiVersions).toBe(ASSISTANT_API_VERSIONS);
|
|
167
|
-
expect(ctx.apiVersions.pluginRuntime).toEqual(["v1"]);
|
|
168
161
|
});
|
|
169
162
|
|
|
170
163
|
test("credential resolution: init receives the resolved value under credentials[key]", async () => {
|
|
@@ -215,29 +208,15 @@ describe("plugin bootstrap", () => {
|
|
|
215
208
|
expect(msg).toContain("absent-key");
|
|
216
209
|
});
|
|
217
210
|
|
|
218
|
-
test("version mismatch:
|
|
219
|
-
//
|
|
220
|
-
//
|
|
221
|
-
//
|
|
222
|
-
// single authoritative point of
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
);
|
|
228
|
-
|
|
229
|
-
let caught: unknown;
|
|
230
|
-
try {
|
|
231
|
-
registerPlugin(plugin);
|
|
232
|
-
} catch (err) {
|
|
233
|
-
caught = err;
|
|
234
|
-
}
|
|
235
|
-
expect(caught).toBeInstanceOf(PluginExecutionError);
|
|
236
|
-
const msg = (caught as PluginExecutionError).message;
|
|
237
|
-
expect(msg).toContain("from-the-future");
|
|
238
|
-
expect(msg).toContain("pluginRuntime");
|
|
239
|
-
expect(msg).toContain("v99");
|
|
240
|
-
expect((caught as PluginExecutionError).pluginName).toBe("from-the-future");
|
|
211
|
+
test("version mismatch: external plugin loader rejects when peerDependency unsatisfied", async () => {
|
|
212
|
+
// Host-compat negotiation lives in the external-plugin loader against
|
|
213
|
+
// `peerDependencies["@vellumai/plugin-api"]`. The registry no longer
|
|
214
|
+
// re-validates a manifest-level `requires` block — the loader is the
|
|
215
|
+
// single authoritative point. End-to-end coverage of the loader path
|
|
216
|
+
// lives in `external-plugin-loader.test.ts`; this test asserts the
|
|
217
|
+
// bootstrap doesn't gain its own validation surface.
|
|
218
|
+
const plugin = buildPlugin("compat-claim-checked-upstream");
|
|
219
|
+
expect(() => registerPlugin(plugin)).not.toThrow();
|
|
241
220
|
});
|
|
242
221
|
|
|
243
222
|
test("plugin init throw: bootstrap throws a PluginExecutionError naming the plugin", async () => {
|
|
@@ -395,13 +374,8 @@ describe("plugin bootstrap", () => {
|
|
|
395
374
|
{
|
|
396
375
|
name: "gated-off-tool",
|
|
397
376
|
description: "should not be registered",
|
|
398
|
-
category: "plugin-test",
|
|
399
377
|
defaultRiskLevel: RiskLevel.Low,
|
|
400
|
-
|
|
401
|
-
name: "gated-off-tool",
|
|
402
|
-
description: "should not be registered",
|
|
403
|
-
input_schema: { type: "object", properties: {}, required: [] },
|
|
404
|
-
}),
|
|
378
|
+
input_schema: { type: "object", properties: {}, required: [] },
|
|
405
379
|
execute: async () => ({ content: "nope", isError: false }),
|
|
406
380
|
},
|
|
407
381
|
],
|