@vellumai/assistant 0.6.5 → 0.6.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +9 -1
- package/ARCHITECTURE.md +15 -17
- package/Dockerfile +6 -4
- package/__tests__/permissions/gateway-threshold-reader.test.ts +283 -0
- package/docs/architecture/integrations.md +32 -39
- package/docs/architecture/memory.md +25 -30
- package/docs/architecture/security.md +7 -6
- package/docs/browser-use-architecture-phase2.md +63 -20
- package/docs/plugins.md +761 -0
- package/examples/plugins/echo/README.md +132 -0
- package/examples/plugins/echo/package.json +17 -0
- package/examples/plugins/echo/register.ts +187 -0
- package/node_modules/@vellumai/egress-proxy/src/types.ts +19 -0
- package/openapi.yaml +212 -68
- package/package.json +1 -1
- package/src/__tests__/app-compiler.test.ts +57 -0
- package/src/__tests__/approval-cascade.test.ts +7 -2
- package/src/__tests__/auto-analysis-end-to-end.test.ts +1 -0
- package/src/__tests__/avatar-generator.test.ts +4 -2
- package/src/__tests__/bundled-asset.test.ts +6 -6
- package/src/__tests__/catalog-cache.test.ts +69 -0
- package/src/__tests__/checker.test.ts +459 -171
- package/src/__tests__/circuit-breaker-pipeline.test.ts +406 -0
- package/src/__tests__/compaction-events.test.ts +501 -0
- package/src/__tests__/compaction-pipeline.test.ts +210 -0
- package/src/__tests__/compaction-strip-metadata-clear.test.ts +181 -0
- package/src/__tests__/compaction-timeout-recovery.test.ts +262 -0
- package/src/__tests__/config-model-image-provider.test.ts +110 -0
- package/src/__tests__/config-schema.test.ts +22 -9
- package/src/__tests__/config-watcher-cleanup-throttle.test.ts +0 -4
- package/src/__tests__/contacts-tools.test.ts +26 -0
- package/src/__tests__/context-overflow-policy.test.ts +7 -7
- package/src/__tests__/context-window-manager.test.ts +355 -4
- package/src/__tests__/conversation-abort-tool-results.test.ts +4 -1
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +26 -30
- package/src/__tests__/conversation-agent-loop.test.ts +30 -141
- package/src/__tests__/conversation-confirmation-signals.test.ts +6 -1
- package/src/__tests__/conversation-history-web-search.test.ts +1 -0
- package/src/__tests__/conversation-init.benchmark.test.ts +2 -16
- package/src/__tests__/conversation-pairing.test.ts +174 -10
- package/src/__tests__/conversation-pre-run-repair.test.ts +4 -1
- package/src/__tests__/conversation-process-callsite.test.ts +3 -0
- package/src/__tests__/conversation-provider-retry-repair.test.ts +16 -7
- package/src/__tests__/conversation-queue.test.ts +29 -14
- package/src/__tests__/conversation-routes-disk-view.test.ts +7 -6
- package/src/__tests__/conversation-runtime-assembly.test.ts +155 -110
- package/src/__tests__/conversation-runtime-workspace.test.ts +23 -38
- package/src/__tests__/conversation-seed-composer.test.ts +2 -2
- package/src/__tests__/conversation-slash-queue.test.ts +7 -2
- package/src/__tests__/conversation-slash-unknown.test.ts +25 -2
- package/src/__tests__/conversation-speed-override.test.ts +6 -1
- package/src/__tests__/conversation-title-service.test.ts +116 -0
- package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +41 -2
- package/src/__tests__/conversation-usage.test.ts +1 -1
- package/src/__tests__/conversation-workspace-cache-state.test.ts +4 -1
- package/src/__tests__/conversation-workspace-injection.test.ts +3 -0
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +4 -1
- package/src/__tests__/credential-health-service.test.ts +78 -9
- package/src/__tests__/credential-security-invariants.test.ts +2 -2
- package/src/__tests__/db-schedule-syntax-migration.test.ts +1 -0
- package/src/__tests__/empty-response-pipeline.test.ts +305 -0
- package/src/__tests__/extension-id-sync-guard.test.ts +3 -3
- package/src/__tests__/first-greeting.test.ts +247 -5
- package/src/__tests__/headless-browser-mode.test.ts +57 -0
- package/src/__tests__/history-repair-pipeline.test.ts +399 -0
- package/src/__tests__/host-browser-e2e-cloud.test.ts +307 -0
- package/src/__tests__/host-browser-e2e-self-hosted.test.ts +3 -3
- package/src/__tests__/host-proxy-interface.test.ts +36 -2
- package/src/__tests__/image-credentials.test.ts +137 -0
- package/src/__tests__/image-service-dispatcher.test.ts +186 -0
- package/src/__tests__/injector-chain.test.ts +526 -0
- package/src/__tests__/intent-routing.test.ts +0 -26
- package/src/__tests__/llm-call-pipeline.test.ts +285 -0
- package/src/__tests__/llm-schema.test.ts +1 -1
- package/src/__tests__/media-generate-image.test.ts +119 -13
- package/src/__tests__/memory-retrieval-pipeline.test.ts +401 -0
- package/src/__tests__/memory-upsert-concurrency.test.ts +1 -0
- package/src/__tests__/migration-import-from-url.test.ts +5 -68
- package/src/__tests__/model-intents.test.ts +4 -2
- package/src/__tests__/notification-broadcaster.test.ts +3 -3
- package/src/__tests__/notification-decision-strategy.test.ts +0 -11
- package/src/__tests__/notification-schedule-notify-dedup.test.ts +108 -0
- package/src/__tests__/oauth-apps-routes.test.ts +1 -1
- package/src/__tests__/oauth-cli.test.ts +14 -12
- package/src/__tests__/oauth-connect-orchestrator.test.ts +4 -13
- package/src/__tests__/oauth-provider-serializer.test.ts +6 -4
- package/src/__tests__/oauth-provider-visibility.test.ts +3 -5
- package/src/__tests__/oauth-providers-routes.test.ts +3 -2
- package/src/__tests__/oauth-store.test.ts +41 -76
- package/src/__tests__/onboarding-template-contract.test.ts +16 -64
- package/src/__tests__/openai-image-service.test.ts +368 -0
- package/src/__tests__/overflow-reduce-pipeline.test.ts +676 -0
- package/src/__tests__/permission-checker-host-gate.test.ts +0 -24
- package/src/__tests__/persist-onboarding-artifacts.test.ts +266 -0
- package/src/__tests__/persistence-pipeline.test.ts +377 -0
- package/src/__tests__/pipeline-runner.test.ts +565 -0
- package/src/__tests__/platform.test.ts +5 -2
- package/src/__tests__/plugin-bootstrap.test.ts +483 -0
- package/src/__tests__/plugin-registry.test.ts +273 -0
- package/src/__tests__/plugin-route-contribution.test.ts +288 -0
- package/src/__tests__/plugin-skill-contribution.test.ts +367 -0
- package/src/__tests__/plugin-tool-contribution.test.ts +286 -0
- package/src/__tests__/plugin-types.test.ts +320 -0
- package/src/__tests__/pricing.test.ts +44 -12
- package/src/__tests__/proxy-approval-callback.test.ts +69 -8
- package/src/__tests__/reaction-persistence.test.ts +1 -0
- package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +1 -0
- package/src/__tests__/registry.test.ts +0 -2
- package/src/__tests__/schedule-routes.test.ts +131 -1
- package/src/__tests__/scheduler-recurrence.test.ts +14 -70
- package/src/__tests__/scheduler-reuse-conversation.test.ts +10 -50
- package/src/__tests__/secret-detection-handler.test.ts +0 -10
- package/src/__tests__/shell-identity.test.ts +0 -134
- package/src/__tests__/suggestion-routes.test.ts +103 -4
- package/src/__tests__/task-memory-cleanup.test.ts +1 -0
- package/src/__tests__/task-scheduler.test.ts +3 -15
- package/src/__tests__/test-preload.ts +11 -0
- package/src/__tests__/title-generate-pipeline.test.ts +224 -0
- package/src/__tests__/token-estimate-pipeline.test.ts +431 -0
- package/src/__tests__/tool-error-pipeline.test.ts +244 -0
- package/src/__tests__/tool-execute-pipeline.test.ts +431 -0
- package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +0 -6
- package/src/__tests__/tool-executor-shell-integration.test.ts +7 -10
- package/src/__tests__/tool-executor.test.ts +141 -0
- package/src/__tests__/tool-result-truncate-pipeline.test.ts +356 -0
- package/src/__tests__/tool-result-truncation.test.ts +0 -110
- package/src/__tests__/user-plugin-loader.test.ts +191 -0
- package/src/__tests__/workspace-migration-046-seed-conversation-starters-callsite.test.ts +185 -0
- package/src/__tests__/workspace-migration-049-release-notes-default-sonnet.test.ts +100 -0
- package/src/__tests__/workspace-migration-050-seed-main-agent-opus-callsite.test.ts +171 -0
- package/src/__tests__/workspace-migration-051-seed-conversation-summarization-callsite.test.ts +252 -0
- package/src/__tests__/workspace-migration-remove-hooks.test.ts +99 -0
- package/src/__tests__/workspace-policy.test.ts +21 -3
- package/src/agent/loop.ts +340 -102
- package/src/approvals/__tests__/guardian-feed-event.test.ts +304 -0
- package/src/approvals/guardian-request-resolvers.ts +80 -0
- package/src/backup/__tests__/backup-worker.test.ts +2 -13
- package/src/backup/backup-worker.ts +3 -15
- package/src/bundler/app-compiler.ts +84 -1
- package/src/calls/call-state.ts +2 -2
- package/src/channels/__tests__/types.test.ts +3 -3
- package/src/channels/types.ts +6 -4
- package/src/cli/__tests__/notifications.test.ts +87 -211
- package/src/cli/commands/__tests__/backup.test.ts +1 -1
- package/src/cli/commands/__tests__/image-generation.test.ts +255 -35
- package/src/cli/commands/__tests__/inference-send.test.ts +12 -0
- package/src/cli/commands/__tests__/tts-synthesize.test.ts +12 -0
- package/src/cli/commands/backup.ts +2 -2
- package/src/cli/commands/clients.ts +138 -0
- package/src/cli/commands/completions.ts +2 -9
- package/src/cli/commands/conversations.ts +55 -7
- package/src/cli/commands/image-generation.ts +33 -34
- package/src/cli/commands/notifications.ts +68 -103
- package/src/cli/commands/oauth/__tests__/providers-register.test.ts +1 -1
- package/src/cli/commands/oauth/__tests__/providers-update.test.ts +1 -1
- package/src/cli/commands/oauth/connect.ts +2 -2
- package/src/cli/commands/oauth/providers.ts +176 -8
- package/src/cli/commands/oauth/status.ts +46 -36
- package/src/cli/commands/skills.ts +3 -4
- package/src/cli/program.ts +25 -29
- package/src/config/__tests__/backup-schema.test.ts +7 -2
- package/src/config/bundled-skills/app-builder/SKILL.md +2 -2
- package/src/config/bundled-skills/app-builder/references/WIDGETS.md +10 -10
- package/src/config/bundled-skills/contacts/tools/contact-merge.ts +66 -87
- package/src/config/bundled-skills/contacts/tools/contact-search.ts +28 -51
- package/src/config/bundled-skills/contacts/tools/contact-upsert.ts +22 -40
- package/src/config/bundled-skills/image-studio/SKILL.md +2 -1
- package/src/config/bundled-skills/image-studio/TOOLS.json +2 -1
- package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +23 -39
- package/src/config/bundled-skills/messaging/SKILL.md +3 -3
- package/src/config/bundled-skills/messaging/tools/__tests__/messaging-feed-events.test.ts +207 -0
- package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +12 -0
- package/src/config/bundled-skills/messaging/tools/messaging-send.ts +58 -0
- package/src/config/bundled-skills/schedule/SKILL.md +8 -3
- package/src/config/bundled-skills/schedule/TOOLS.json +15 -7
- package/src/config/bundled-skills/schedule/references/SCRIPT_MODE_PATTERNS.md +59 -0
- package/src/config/bundled-tool-registry.ts +0 -15
- package/src/config/feature-flag-registry.json +17 -1
- package/src/config/schema.ts +19 -0
- package/src/config/schemas/backup.ts +1 -1
- package/src/config/schemas/conversations.ts +16 -0
- package/src/config/schemas/llm.ts +2 -3
- package/src/config/schemas/security.ts +6 -6
- package/src/config/schemas/tts.ts +11 -0
- package/src/config/skill-state.ts +6 -2
- package/src/config/skills.ts +94 -5
- package/src/context/__tests__/compact-prompt.test.ts +27 -9
- package/src/context/prompts/compact.md +26 -12
- package/src/context/tool-result-truncation.ts +3 -63
- package/src/context/window-manager.ts +190 -16
- package/src/credential-health/credential-health-service.ts +19 -6
- package/src/daemon/__tests__/conversation-feed-event.test.ts +317 -0
- package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +4 -12
- package/src/daemon/__tests__/conversation-tool-setup.test.ts +14 -15
- package/src/daemon/config-watcher.ts +0 -2
- package/src/daemon/context-overflow-policy.ts +4 -13
- package/src/daemon/conversation-agent-loop-handlers.ts +83 -22
- package/src/daemon/conversation-agent-loop.ts +984 -683
- package/src/daemon/conversation-history.ts +10 -19
- package/src/daemon/conversation-lifecycle.ts +37 -19
- package/src/daemon/conversation-notifiers.ts +2 -110
- package/src/daemon/conversation-process.ts +14 -7
- package/src/daemon/conversation-runtime-assembly.ts +532 -411
- package/src/daemon/conversation-tool-setup.ts +41 -4
- package/src/daemon/conversation.ts +80 -35
- package/src/daemon/external-plugins-bootstrap.ts +478 -0
- package/src/daemon/first-greeting.ts +191 -14
- package/src/daemon/handlers/config-model.ts +11 -0
- package/src/daemon/handlers/skills.ts +5 -1
- package/src/daemon/lifecycle.ts +33 -68
- package/src/daemon/message-types/computer-use.ts +2 -34
- package/src/daemon/message-types/conversations.ts +49 -0
- package/src/daemon/message-types/messages.ts +12 -0
- package/src/daemon/server.ts +5 -3
- package/src/daemon/shutdown-handlers.ts +2 -12
- package/src/daemon/tool-side-effects.ts +14 -56
- package/src/heartbeat/__tests__/heartbeat-feed-event.test.ts +160 -0
- package/src/heartbeat/heartbeat-service.ts +24 -1
- package/src/home/__tests__/feed-population-integration.test.ts +312 -0
- package/src/home/emit-feed-event.ts +7 -0
- package/src/home/feed-types.ts +41 -2
- package/src/home/rewrite-command-preview.ts +66 -0
- package/src/ipc/__tests__/socket-path.test.ts +11 -50
- package/src/ipc/cli-client.ts +1 -1
- package/src/ipc/cli-server.ts +3 -3
- package/src/ipc/gateway-client.ts +4 -1
- package/src/ipc/routes/browser-context.ts +2 -0
- package/src/ipc/routes/browser.ts +1 -0
- package/src/ipc/routes/get-contact.ts +16 -0
- package/src/ipc/routes/index.ts +14 -0
- package/src/ipc/routes/list-clients.ts +31 -0
- package/src/ipc/routes/merge-contacts.ts +17 -0
- package/src/ipc/routes/notification.ts +133 -0
- package/src/ipc/routes/rename-conversation.ts +59 -0
- package/src/ipc/routes/search-contacts.ts +19 -0
- package/src/ipc/routes/upsert-contact.ts +25 -0
- package/src/ipc/socket-path.ts +14 -38
- package/src/media/app-icon-generator.ts +23 -46
- package/src/media/avatar-router.ts +26 -41
- package/src/media/gemini-image-service.ts +8 -41
- package/src/media/image-credentials.ts +73 -0
- package/src/media/image-service.ts +85 -0
- package/src/media/openai-image-service.ts +131 -0
- package/src/media/types.ts +46 -0
- package/src/memory/conversation-crud.ts +48 -18
- package/src/memory/conversation-queries.ts +57 -4
- package/src/memory/conversation-title-service.ts +25 -0
- package/src/memory/db-init.ts +8 -0
- package/src/memory/embedding-gemini.test.ts +41 -2
- package/src/memory/embedding-gemini.ts +6 -1
- package/src/memory/graph/bootstrap.test.ts +282 -0
- package/src/memory/graph/bootstrap.ts +8 -5
- package/src/memory/graph/extraction.ts +10 -2
- package/src/memory/graph/graph-search.test.ts +1 -0
- package/src/memory/graph/inspect.ts +2 -2
- package/src/memory/graph/retriever.ts +10 -3
- package/src/memory/migrations/041-approval-prompt-ts-tracker.ts +26 -0
- package/src/memory/migrations/149-oauth-tables.ts +1 -0
- package/src/memory/migrations/223-schedule-script-column.ts +11 -0
- package/src/memory/migrations/224-oauth-providers-managed-service-is-paid.ts +24 -0
- package/src/memory/migrations/225-oauth-providers-available-scopes.ts +13 -0
- package/src/memory/migrations/index.ts +4 -0
- package/src/memory/pkb/pkb-index.test.ts +1 -0
- package/src/memory/pkb/pkb-reconcile.test.ts +1 -0
- package/src/memory/pkb/pkb-search.test.ts +65 -4
- package/src/memory/pkb/pkb-search.ts +40 -18
- package/src/memory/qdrant-client.test.ts +60 -0
- package/src/memory/qdrant-client.ts +25 -0
- package/src/memory/schema/infrastructure.ts +1 -0
- package/src/memory/schema/oauth.ts +4 -1
- package/src/messaging/providers/slack/render-transcript.test.ts +77 -29
- package/src/messaging/providers/slack/render-transcript.ts +58 -0
- package/src/notifications/conversation-pairing.ts +78 -19
- package/src/notifications/copy-composer.ts +0 -5
- package/src/notifications/emit-signal.ts +1 -1
- package/src/notifications/signal.ts +1 -2
- package/src/oauth/AGENTS.md +1 -1
- package/src/oauth/__tests__/identity-verifier.test.ts +2 -1
- package/src/oauth/connect-orchestrator.ts +8 -34
- package/src/oauth/connect-types.ts +6 -10
- package/src/oauth/manual-token-connection.ts +23 -0
- package/src/oauth/oauth-store.ts +30 -14
- package/src/oauth/provider-serializer.ts +6 -1
- package/src/oauth/seed-providers.ts +56 -108
- package/src/outbound-proxy/http-forwarder.ts +9 -0
- package/src/permissions/approval-policy.test.ts +293 -18
- package/src/permissions/approval-policy.ts +110 -58
- package/src/permissions/arg-parser.test.ts +161 -0
- package/src/permissions/arg-parser.ts +141 -0
- package/src/permissions/bash-risk-classifier.test.ts +414 -2
- package/src/permissions/bash-risk-classifier.ts +303 -60
- package/src/permissions/checker.ts +157 -29
- package/src/permissions/command-registry.test.ts +239 -0
- package/src/permissions/command-registry.ts +234 -54
- package/src/permissions/defaults.ts +5 -4
- package/src/permissions/gateway-threshold-reader.ts +196 -0
- package/src/permissions/prompter.ts +4 -0
- package/src/permissions/risk-types.ts +61 -4
- package/src/permissions/schedule-risk-classifier.test.ts +129 -0
- package/src/permissions/schedule-risk-classifier.ts +85 -0
- package/src/permissions/shell-identity.ts +2 -42
- package/src/permissions/types.ts +2 -0
- package/src/permissions/workspace-policy.ts +8 -3
- package/src/plugins/defaults/circuit-breaker.ts +146 -0
- package/src/plugins/defaults/compaction.ts +145 -0
- package/src/plugins/defaults/empty-response.ts +126 -0
- package/src/plugins/defaults/history-repair.ts +85 -0
- package/src/plugins/defaults/index.ts +116 -0
- package/src/plugins/defaults/injectors.ts +491 -0
- package/src/plugins/defaults/llm-call.ts +82 -0
- package/src/plugins/defaults/memory-retrieval.ts +226 -0
- package/src/plugins/defaults/overflow-reduce.ts +181 -0
- package/src/plugins/defaults/persistence.ts +129 -0
- package/src/plugins/defaults/title-generate.ts +95 -0
- package/src/plugins/defaults/token-estimate.ts +104 -0
- package/src/plugins/defaults/tool-error.ts +126 -0
- package/src/plugins/defaults/tool-execute.ts +89 -0
- package/src/plugins/defaults/tool-result-truncate.ts +88 -0
- package/src/plugins/pipeline.ts +316 -0
- package/src/plugins/plugin-skill-contributions.ts +292 -0
- package/src/plugins/registry.ts +241 -0
- package/src/plugins/types.ts +1134 -0
- package/src/plugins/user-loader.ts +177 -0
- package/src/prompts/templates/BOOTSTRAP.md +27 -77
- package/src/providers/model-catalog.ts +52 -29
- package/src/providers/model-intents.ts +1 -1
- package/src/providers/openrouter/client.ts +5 -1
- package/src/providers/speech-to-text/deepgram-realtime.test.ts +61 -0
- package/src/providers/speech-to-text/deepgram-realtime.ts +57 -0
- package/src/providers/speech-to-text/xai-realtime.test.ts +72 -4
- package/src/providers/speech-to-text/xai-realtime.ts +39 -14
- package/src/runtime/AGENTS.md +25 -16
- package/src/runtime/__tests__/browser-extension-pair-routes.test.ts +3 -3
- package/src/runtime/__tests__/client-registry.test.ts +293 -0
- package/src/runtime/client-registry.ts +261 -0
- package/src/runtime/http-server.ts +77 -8
- package/src/runtime/http-types.ts +0 -2
- package/src/runtime/migrations/vbundle-builder.ts +1 -22
- package/src/runtime/routes/approval-prompt-ts-tracker.ts +51 -31
- package/src/runtime/routes/approval-routes.ts +17 -0
- package/src/runtime/routes/browser-extension-pair-routes.ts +27 -8
- package/src/runtime/routes/conversation-routes.ts +223 -116
- package/src/runtime/routes/inbound-message-handler.ts +88 -13
- package/src/runtime/routes/memory-item-routes.test.ts +1 -0
- package/src/runtime/routes/migration-routes.ts +0 -3
- package/src/runtime/routes/playground/__tests__/force-compact.test.ts +284 -0
- package/src/runtime/routes/playground/__tests__/guard.test.ts +80 -0
- package/src/runtime/routes/playground/__tests__/inject-failures.test.ts +294 -0
- package/src/runtime/routes/playground/__tests__/reset-circuit.test.ts +271 -0
- package/src/runtime/routes/playground/__tests__/seed-conversation.test.ts +202 -0
- package/src/runtime/routes/playground/__tests__/seeded-conversations.test.ts +309 -0
- package/src/runtime/routes/playground/__tests__/state.test.ts +224 -0
- package/src/runtime/routes/playground/conversation-not-found.ts +29 -0
- package/src/runtime/routes/playground/deps.ts +56 -0
- package/src/runtime/routes/playground/force-compact.ts +73 -0
- package/src/runtime/routes/playground/guard.ts +37 -0
- package/src/runtime/routes/playground/index.ts +28 -0
- package/src/runtime/routes/playground/inject-failures.ts +159 -0
- package/src/runtime/routes/playground/reset-circuit.ts +115 -0
- package/src/runtime/routes/playground/seed-conversation.ts +139 -0
- package/src/runtime/routes/playground/seeded-conversations.ts +78 -0
- package/src/runtime/routes/playground/state.ts +78 -0
- package/src/runtime/routes/schedule-routes.ts +89 -8
- package/src/runtime/skill-route-registry.ts +75 -15
- package/src/schedule/run-script.ts +68 -0
- package/src/schedule/schedule-store.ts +7 -1
- package/src/schedule/scheduler.ts +48 -8
- package/src/skills/catalog-cache.ts +12 -5
- package/src/tools/browser/__tests__/browser-status.test.ts +189 -0
- package/src/tools/browser/browser-execution.ts +88 -19
- package/src/tools/browser/cdp-client/__tests__/extension-cdp-client.test.ts +230 -0
- package/src/tools/browser/cdp-client/__tests__/factory.test.ts +146 -3
- package/src/tools/browser/cdp-client/extension-cdp-client.ts +54 -3
- package/src/tools/browser/cdp-client/factory.ts +15 -4
- package/src/tools/executor.ts +126 -74
- package/src/tools/network/script-proxy/session-manager.ts +37 -1
- package/src/tools/permission-checker.ts +98 -49
- package/src/tools/policy-context.ts +4 -0
- package/src/tools/registry.ts +140 -3
- package/src/tools/schedule/create.ts +23 -8
- package/src/tools/schedule/update.ts +3 -1
- package/src/tools/secret-detection-handler.ts +0 -51
- package/src/tools/system/avatar-generator.ts +6 -2
- package/src/tools/types.ts +28 -2
- package/src/util/platform.ts +7 -2
- package/src/util/pricing.ts +26 -3
- package/src/workspace/migrations/006-services-config.ts +2 -4
- package/src/workspace/migrations/022-move-hooks-to-workspace.ts +2 -3
- package/src/workspace/migrations/041-backfill-google-gmail-settings-scope.ts +3 -4
- package/src/workspace/migrations/046-seed-conversation-starters-callsite.ts +108 -0
- package/src/workspace/migrations/047-remove-watch-callsites.ts +54 -0
- package/src/workspace/migrations/048-remove-workspace-hooks.ts +81 -0
- package/src/workspace/migrations/049-release-notes-default-sonnet.ts +80 -0
- package/src/workspace/migrations/050-seed-main-agent-opus-callsite.ts +86 -0
- package/src/workspace/migrations/051-seed-conversation-summarization-callsite.ts +128 -0
- package/src/workspace/migrations/registry.ts +12 -0
- package/tsconfig.json +1 -1
- package/hook-templates/debug-prompt-logger/hook.json +0 -7
- package/hook-templates/debug-prompt-logger/run.sh +0 -66
- package/src/__tests__/compaction-circuit-breaker.test.ts +0 -336
- package/src/__tests__/context-overflow-approval.test.ts +0 -156
- package/src/__tests__/hooks-blocking.test.ts +0 -178
- package/src/__tests__/hooks-cli.test.ts +0 -182
- package/src/__tests__/hooks-config.test.ts +0 -108
- package/src/__tests__/hooks-discovery.test.ts +0 -211
- package/src/__tests__/hooks-integration.test.ts +0 -196
- package/src/__tests__/hooks-manager.test.ts +0 -226
- package/src/__tests__/hooks-runner.test.ts +0 -175
- package/src/__tests__/hooks-settings.test.ts +0 -160
- package/src/__tests__/hooks-templates.test.ts +0 -169
- package/src/__tests__/hooks-ts-runner.test.ts +0 -170
- package/src/__tests__/hooks-watch.test.ts +0 -112
- package/src/__tests__/notification-schedule-dedup.test.ts +0 -213
- package/src/__tests__/oauth-scope-policy.test.ts +0 -180
- package/src/__tests__/send-notification-tool.test.ts +0 -83
- package/src/cli/commands/shotgun.ts +0 -266
- package/src/config/bundled-skills/conversations/SKILL.md +0 -20
- package/src/config/bundled-skills/conversations/TOOLS.json +0 -23
- package/src/config/bundled-skills/conversations/tools/rename-conversation.ts +0 -88
- package/src/config/bundled-skills/heartbeat/SKILL.md +0 -43
- package/src/config/bundled-skills/notifications/SKILL.md +0 -40
- package/src/config/bundled-skills/notifications/TOOLS.json +0 -80
- package/src/config/bundled-skills/notifications/tools/send-notification.ts +0 -152
- package/src/config/bundled-skills/notifications/tools/shared.ts +0 -13
- package/src/config/bundled-skills/screen-watch/SKILL.md +0 -27
- package/src/config/bundled-skills/screen-watch/TOOLS.json +0 -35
- package/src/config/bundled-skills/screen-watch/tools/start-screen-watch.ts +0 -12
- package/src/config/bundled-skills/skills-catalog/SKILL.md +0 -84
- package/src/daemon/context-overflow-approval.ts +0 -52
- package/src/daemon/watch-handler.ts +0 -399
- package/src/hooks/cli.ts +0 -253
- package/src/hooks/config.ts +0 -100
- package/src/hooks/discovery.ts +0 -135
- package/src/hooks/manager.ts +0 -179
- package/src/hooks/runner.ts +0 -117
- package/src/hooks/templates.ts +0 -77
- package/src/hooks/types.ts +0 -75
- package/src/oauth/scope-policy.ts +0 -89
- package/src/runtime/gateway-internal-client.ts +0 -94
- package/src/runtime/routes/watch-routes.ts +0 -156
- package/src/signals/shotgun.ts +0 -203
- package/src/tools/watch/screen-watch.ts +0 -144
- package/src/tools/watch/watch-state.ts +0 -142
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit tests for the `llmCall` pipeline wrapping (PR 15).
|
|
3
|
+
*
|
|
4
|
+
* Exercises the three behaviors the plan calls out:
|
|
5
|
+
*
|
|
6
|
+
* 1. The default `llmCall` pipeline delegates to `provider.sendMessage(...)`
|
|
7
|
+
* and returns its response unchanged.
|
|
8
|
+
* 2. A spy middleware registered for `llmCall` observes the full argument
|
|
9
|
+
* payload before the provider is called.
|
|
10
|
+
* 3. A short-circuit middleware synthesizes a `ProviderResponse` and prevents
|
|
11
|
+
* the real `provider.sendMessage` from running.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { afterAll, beforeEach, describe, expect, test } from "bun:test";
|
|
15
|
+
|
|
16
|
+
import type { TrustContext } from "../daemon/conversation-runtime-assembly.js";
|
|
17
|
+
import { defaultLlmCallPlugin } from "../plugins/defaults/llm-call.js";
|
|
18
|
+
import { DEFAULT_TIMEOUTS, runPipeline } from "../plugins/pipeline.js";
|
|
19
|
+
import {
|
|
20
|
+
getMiddlewaresFor,
|
|
21
|
+
registerPlugin,
|
|
22
|
+
resetPluginRegistryForTests,
|
|
23
|
+
} from "../plugins/registry.js";
|
|
24
|
+
import type {
|
|
25
|
+
LLMCallArgs,
|
|
26
|
+
LLMCallResult,
|
|
27
|
+
Middleware,
|
|
28
|
+
Plugin,
|
|
29
|
+
TurnContext,
|
|
30
|
+
} from "../plugins/types.js";
|
|
31
|
+
import type {
|
|
32
|
+
Message,
|
|
33
|
+
Provider,
|
|
34
|
+
ProviderResponse,
|
|
35
|
+
ToolDefinition,
|
|
36
|
+
} from "../providers/types.js";
|
|
37
|
+
|
|
38
|
+
// ─── Fixtures ───────────────────────────────────────────────────────────────
|
|
39
|
+
|
|
40
|
+
const trust: TrustContext = {
|
|
41
|
+
sourceChannel: "vellum",
|
|
42
|
+
trustClass: "guardian",
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
function makeCtx(overrides: Partial<TurnContext> = {}): TurnContext {
|
|
46
|
+
return {
|
|
47
|
+
requestId: "req-test",
|
|
48
|
+
conversationId: "conv-test",
|
|
49
|
+
turnIndex: 0,
|
|
50
|
+
trust,
|
|
51
|
+
...overrides,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function makeResponse(
|
|
56
|
+
overrides: Partial<ProviderResponse> = {},
|
|
57
|
+
): ProviderResponse {
|
|
58
|
+
return {
|
|
59
|
+
content: [{ type: "text", text: "hello from provider" }],
|
|
60
|
+
model: "fake-model",
|
|
61
|
+
usage: {
|
|
62
|
+
inputTokens: 10,
|
|
63
|
+
outputTokens: 5,
|
|
64
|
+
},
|
|
65
|
+
stopReason: "end_turn",
|
|
66
|
+
...overrides,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
type FakeProviderCall = {
|
|
71
|
+
messages: Message[];
|
|
72
|
+
tools?: ToolDefinition[];
|
|
73
|
+
systemPrompt?: string;
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
function makeFakeProvider(
|
|
77
|
+
response: ProviderResponse = makeResponse(),
|
|
78
|
+
): Provider & { calls: FakeProviderCall[] } {
|
|
79
|
+
const calls: FakeProviderCall[] = [];
|
|
80
|
+
return {
|
|
81
|
+
name: "fake-provider",
|
|
82
|
+
calls,
|
|
83
|
+
async sendMessage(messages, tools, systemPrompt, _options) {
|
|
84
|
+
calls.push({ messages, tools, systemPrompt });
|
|
85
|
+
return response;
|
|
86
|
+
},
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function makeArgs(
|
|
91
|
+
provider: Provider,
|
|
92
|
+
overrides: Partial<LLMCallArgs> = {},
|
|
93
|
+
): LLMCallArgs {
|
|
94
|
+
return {
|
|
95
|
+
provider,
|
|
96
|
+
messages: [{ role: "user", content: [{ type: "text", text: "hi" }] }],
|
|
97
|
+
tools: undefined,
|
|
98
|
+
systemPrompt: "you are a helpful assistant",
|
|
99
|
+
options: { config: {} },
|
|
100
|
+
...overrides,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// The terminal passed into `runPipeline` matches the one in `agent/loop.ts`:
|
|
105
|
+
// it delegates straight to `args.provider.sendMessage(...)` with no
|
|
106
|
+
// transformation. Keeping it identical here means the test exercises the
|
|
107
|
+
// exact call shape the real loop uses.
|
|
108
|
+
const terminal = (args: LLMCallArgs): Promise<LLMCallResult> =>
|
|
109
|
+
args.provider.sendMessage(
|
|
110
|
+
args.messages,
|
|
111
|
+
args.tools,
|
|
112
|
+
args.systemPrompt,
|
|
113
|
+
args.options,
|
|
114
|
+
);
|
|
115
|
+
|
|
116
|
+
// ─── Tests ──────────────────────────────────────────────────────────────────
|
|
117
|
+
|
|
118
|
+
describe("llmCall pipeline", () => {
|
|
119
|
+
beforeEach(() => {
|
|
120
|
+
resetPluginRegistryForTests();
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
// Clear the registry on the way out too so later test files in the same
|
|
124
|
+
// `bun test` run don't inherit `llmCall` middleware from our final test.
|
|
125
|
+
// Bun runs files sequentially within a process; `beforeEach` only clears
|
|
126
|
+
// at the start of each case, leaving whatever the final test registered
|
|
127
|
+
// in place for the next file.
|
|
128
|
+
afterAll(() => {
|
|
129
|
+
resetPluginRegistryForTests();
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
test("default pipeline invokes provider.sendMessage and returns its response", async () => {
|
|
133
|
+
registerPlugin(defaultLlmCallPlugin);
|
|
134
|
+
|
|
135
|
+
const expected = makeResponse({ model: "expected-model" });
|
|
136
|
+
const provider = makeFakeProvider(expected);
|
|
137
|
+
const args = makeArgs(provider);
|
|
138
|
+
|
|
139
|
+
const result = await runPipeline<LLMCallArgs, LLMCallResult>(
|
|
140
|
+
"llmCall",
|
|
141
|
+
getMiddlewaresFor("llmCall"),
|
|
142
|
+
terminal,
|
|
143
|
+
args,
|
|
144
|
+
makeCtx(),
|
|
145
|
+
DEFAULT_TIMEOUTS.llmCall,
|
|
146
|
+
);
|
|
147
|
+
|
|
148
|
+
expect(result).toBe(expected);
|
|
149
|
+
expect(provider.calls).toHaveLength(1);
|
|
150
|
+
expect(provider.calls[0]!.messages).toBe(args.messages);
|
|
151
|
+
expect(provider.calls[0]!.systemPrompt).toBe("you are a helpful assistant");
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
test("spy middleware records the full invocation arguments", async () => {
|
|
155
|
+
const observed: LLMCallArgs[] = [];
|
|
156
|
+
const spyPlugin: Plugin = {
|
|
157
|
+
manifest: {
|
|
158
|
+
name: "spy-llm",
|
|
159
|
+
version: "0.0.1",
|
|
160
|
+
requires: { pluginRuntime: "v1" },
|
|
161
|
+
},
|
|
162
|
+
middleware: {
|
|
163
|
+
llmCall: async (args, next, _ctx) => {
|
|
164
|
+
observed.push(args);
|
|
165
|
+
return next(args);
|
|
166
|
+
},
|
|
167
|
+
},
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
registerPlugin(spyPlugin);
|
|
171
|
+
registerPlugin(defaultLlmCallPlugin);
|
|
172
|
+
|
|
173
|
+
const provider = makeFakeProvider();
|
|
174
|
+
const tools: ToolDefinition[] = [
|
|
175
|
+
{
|
|
176
|
+
name: "echo",
|
|
177
|
+
description: "echoes its input",
|
|
178
|
+
input_schema: { type: "object" },
|
|
179
|
+
},
|
|
180
|
+
];
|
|
181
|
+
const args = makeArgs(provider, { tools });
|
|
182
|
+
|
|
183
|
+
await runPipeline<LLMCallArgs, LLMCallResult>(
|
|
184
|
+
"llmCall",
|
|
185
|
+
getMiddlewaresFor("llmCall"),
|
|
186
|
+
terminal,
|
|
187
|
+
args,
|
|
188
|
+
makeCtx(),
|
|
189
|
+
DEFAULT_TIMEOUTS.llmCall,
|
|
190
|
+
);
|
|
191
|
+
|
|
192
|
+
expect(observed).toHaveLength(1);
|
|
193
|
+
expect(observed[0]!.provider).toBe(provider);
|
|
194
|
+
expect(observed[0]!.messages).toBe(args.messages);
|
|
195
|
+
expect(observed[0]!.tools).toBe(tools);
|
|
196
|
+
expect(observed[0]!.systemPrompt).toBe("you are a helpful assistant");
|
|
197
|
+
expect(provider.calls).toHaveLength(1);
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
test("default registered first does not shadow later-registered user middleware", async () => {
|
|
201
|
+
// Regression for a shadowing bug where `defaultLlmCallPlugin` called
|
|
202
|
+
// `provider.sendMessage` directly instead of `next(args)`. Because the
|
|
203
|
+
// default registers at module load (before `bootstrapPlugins()` loads
|
|
204
|
+
// user plugins), it sat at the outermost layer in production — any
|
|
205
|
+
// user-registered `llmCall` middleware would have been silently skipped.
|
|
206
|
+
// This test locks in the fix by registering the default FIRST (matching
|
|
207
|
+
// production ordering) and asserting a user-registered spy still runs.
|
|
208
|
+
const observed: LLMCallArgs[] = [];
|
|
209
|
+
const spyPlugin: Plugin = {
|
|
210
|
+
manifest: {
|
|
211
|
+
name: "spy-llm-after-default",
|
|
212
|
+
version: "0.0.1",
|
|
213
|
+
requires: { pluginRuntime: "v1" },
|
|
214
|
+
},
|
|
215
|
+
middleware: {
|
|
216
|
+
llmCall: async (args, next, _ctx) => {
|
|
217
|
+
observed.push(args);
|
|
218
|
+
return next(args);
|
|
219
|
+
},
|
|
220
|
+
},
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
registerPlugin(defaultLlmCallPlugin);
|
|
224
|
+
registerPlugin(spyPlugin);
|
|
225
|
+
|
|
226
|
+
const provider = makeFakeProvider();
|
|
227
|
+
const args = makeArgs(provider);
|
|
228
|
+
|
|
229
|
+
await runPipeline<LLMCallArgs, LLMCallResult>(
|
|
230
|
+
"llmCall",
|
|
231
|
+
getMiddlewaresFor("llmCall"),
|
|
232
|
+
terminal,
|
|
233
|
+
args,
|
|
234
|
+
makeCtx(),
|
|
235
|
+
DEFAULT_TIMEOUTS.llmCall,
|
|
236
|
+
);
|
|
237
|
+
|
|
238
|
+
expect(observed).toHaveLength(1);
|
|
239
|
+
expect(observed[0]!.provider).toBe(provider);
|
|
240
|
+
expect(provider.calls).toHaveLength(1);
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
test("short-circuit middleware prevents the real provider call", async () => {
|
|
244
|
+
const synthetic = makeResponse({
|
|
245
|
+
model: "synthetic-model",
|
|
246
|
+
content: [{ type: "text", text: "synthesized" }],
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
const shortCircuit: Middleware<LLMCallArgs, LLMCallResult> = async (
|
|
250
|
+
_args,
|
|
251
|
+
_next,
|
|
252
|
+
_ctx,
|
|
253
|
+
) => synthetic;
|
|
254
|
+
|
|
255
|
+
const shortCircuitPlugin: Plugin = {
|
|
256
|
+
manifest: {
|
|
257
|
+
name: "short-circuit-llm",
|
|
258
|
+
version: "0.0.1",
|
|
259
|
+
requires: { pluginRuntime: "v1" },
|
|
260
|
+
},
|
|
261
|
+
middleware: { llmCall: shortCircuit },
|
|
262
|
+
};
|
|
263
|
+
|
|
264
|
+
registerPlugin(shortCircuitPlugin);
|
|
265
|
+
registerPlugin(defaultLlmCallPlugin);
|
|
266
|
+
|
|
267
|
+
const provider = makeFakeProvider();
|
|
268
|
+
const args = makeArgs(provider);
|
|
269
|
+
|
|
270
|
+
const result = await runPipeline<LLMCallArgs, LLMCallResult>(
|
|
271
|
+
"llmCall",
|
|
272
|
+
getMiddlewaresFor("llmCall"),
|
|
273
|
+
terminal,
|
|
274
|
+
args,
|
|
275
|
+
makeCtx(),
|
|
276
|
+
DEFAULT_TIMEOUTS.llmCall,
|
|
277
|
+
);
|
|
278
|
+
|
|
279
|
+
expect(result).toBe(synthetic);
|
|
280
|
+
// The short-circuit middleware never calls `next`, so the terminal and
|
|
281
|
+
// every downstream middleware (including the default) are skipped and
|
|
282
|
+
// the provider is never contacted.
|
|
283
|
+
expect(provider.calls).toHaveLength(0);
|
|
284
|
+
});
|
|
285
|
+
});
|
|
@@ -10,14 +10,17 @@ import type { ToolContext } from "../tools/types.js";
|
|
|
10
10
|
// Mock dependencies for the tool wrapper
|
|
11
11
|
// ---------------------------------------------------------------------------
|
|
12
12
|
|
|
13
|
-
let
|
|
13
|
+
let mockGeminiKey: string | undefined = "test-gemini-key";
|
|
14
|
+
let mockOpenAIKey: string | undefined = "test-openai-key";
|
|
14
15
|
let mockImageGenMode: "your-own" | "managed" = "your-own";
|
|
16
|
+
let mockImageGenProvider: "gemini" | "openai" = "gemini";
|
|
15
17
|
let mockGenerateResult = {
|
|
16
18
|
images: [{ mimeType: "image/png", dataBase64: "generated-data" }],
|
|
17
19
|
text: "A beautiful image",
|
|
18
20
|
resolvedModel: "gemini-3.1-flash-image-preview",
|
|
19
21
|
};
|
|
20
22
|
let mockGenerateError: Error | null = null;
|
|
23
|
+
let lastGenerateProvider: unknown = null;
|
|
21
24
|
let lastGenerateCredentials: unknown = null;
|
|
22
25
|
|
|
23
26
|
mock.module("../config/loader.js", () => ({
|
|
@@ -31,7 +34,7 @@ mock.module("../config/loader.js", () => ({
|
|
|
31
34
|
},
|
|
32
35
|
"image-generation": {
|
|
33
36
|
mode: mockImageGenMode,
|
|
34
|
-
provider:
|
|
37
|
+
provider: mockImageGenProvider,
|
|
35
38
|
model: "gemini-3.1-flash-image-preview",
|
|
36
39
|
},
|
|
37
40
|
"web-search": { mode: "your-own", provider: "inference-provider-native" },
|
|
@@ -41,27 +44,33 @@ mock.module("../config/loader.js", () => ({
|
|
|
41
44
|
|
|
42
45
|
mock.module("../security/secure-keys.js", () => ({
|
|
43
46
|
getSecureKeyAsync: async (account: string) => {
|
|
44
|
-
if (account === "gemini") return
|
|
47
|
+
if (account === "gemini") return mockGeminiKey;
|
|
48
|
+
if (account === "openai") return mockOpenAIKey;
|
|
45
49
|
return undefined;
|
|
46
50
|
},
|
|
47
51
|
getProviderKeyAsync: async (provider: string) => {
|
|
48
|
-
if (provider === "gemini") return
|
|
52
|
+
if (provider === "gemini") return mockGeminiKey;
|
|
53
|
+
if (provider === "openai") return mockOpenAIKey;
|
|
49
54
|
return undefined;
|
|
50
55
|
},
|
|
51
56
|
}));
|
|
52
57
|
|
|
53
|
-
mock.module("../media/
|
|
58
|
+
mock.module("../media/image-service.js", () => ({
|
|
54
59
|
generateImage: async (
|
|
60
|
+
provider: unknown,
|
|
55
61
|
credentials: unknown,
|
|
56
62
|
_request: Record<string, unknown>,
|
|
57
63
|
) => {
|
|
64
|
+
lastGenerateProvider = provider;
|
|
58
65
|
lastGenerateCredentials = credentials;
|
|
59
66
|
if (mockGenerateError) throw mockGenerateError;
|
|
60
67
|
return mockGenerateResult;
|
|
61
68
|
},
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
69
|
+
mapImageGenError: (provider: unknown, error: unknown) => {
|
|
70
|
+
const providerLabel = provider === "openai" ? "OpenAI" : "Gemini";
|
|
71
|
+
if (error instanceof Error)
|
|
72
|
+
return `Mock ${providerLabel} error: ${error.message}`;
|
|
73
|
+
return `Mock ${providerLabel} unknown error`;
|
|
65
74
|
},
|
|
66
75
|
}));
|
|
67
76
|
|
|
@@ -97,14 +106,17 @@ const CONFIG_DIR = join(
|
|
|
97
106
|
// ---------------------------------------------------------------------------
|
|
98
107
|
|
|
99
108
|
beforeEach(() => {
|
|
100
|
-
|
|
109
|
+
mockGeminiKey = "test-gemini-key";
|
|
110
|
+
mockOpenAIKey = "test-openai-key";
|
|
101
111
|
mockImageGenMode = "your-own";
|
|
112
|
+
mockImageGenProvider = "gemini";
|
|
102
113
|
mockGenerateResult = {
|
|
103
114
|
images: [{ mimeType: "image/png", dataBase64: "generated-data" }],
|
|
104
115
|
text: "A beautiful image",
|
|
105
116
|
resolvedModel: "gemini-3.1-flash-image-preview",
|
|
106
117
|
};
|
|
107
118
|
mockGenerateError = null;
|
|
119
|
+
lastGenerateProvider = null;
|
|
108
120
|
lastGenerateCredentials = null;
|
|
109
121
|
mockManagedBaseUrl = undefined;
|
|
110
122
|
mockManagedProxyContext = {
|
|
@@ -127,7 +139,7 @@ describe("image-studio skill script wrapper", () => {
|
|
|
127
139
|
});
|
|
128
140
|
|
|
129
141
|
test("returns error when no API key and no managed proxy", async () => {
|
|
130
|
-
|
|
142
|
+
mockGeminiKey = undefined;
|
|
131
143
|
|
|
132
144
|
const result = await run({ prompt: "a cat" }, fakeContext);
|
|
133
145
|
|
|
@@ -148,6 +160,7 @@ describe("image-studio skill script wrapper", () => {
|
|
|
148
160
|
|
|
149
161
|
expect(result.isError).toBe(false);
|
|
150
162
|
expect(result.content).toContain("Generated 1 image");
|
|
163
|
+
expect(lastGenerateProvider).toBe("gemini");
|
|
151
164
|
expect(lastGenerateCredentials).toEqual({
|
|
152
165
|
type: "managed-proxy",
|
|
153
166
|
assistantApiKey: "managed-key-123",
|
|
@@ -157,7 +170,7 @@ describe("image-studio skill script wrapper", () => {
|
|
|
157
170
|
|
|
158
171
|
test("managed mode returns error when managed proxy is unavailable", async () => {
|
|
159
172
|
mockImageGenMode = "managed";
|
|
160
|
-
|
|
173
|
+
mockGeminiKey = "direct-key"; // should be ignored in managed mode
|
|
161
174
|
mockManagedBaseUrl = undefined;
|
|
162
175
|
|
|
163
176
|
const result = await run({ prompt: "a cat" }, fakeContext);
|
|
@@ -168,7 +181,7 @@ describe("image-studio skill script wrapper", () => {
|
|
|
168
181
|
|
|
169
182
|
test("your-own mode uses direct API key", async () => {
|
|
170
183
|
mockImageGenMode = "your-own";
|
|
171
|
-
|
|
184
|
+
mockGeminiKey = "direct-key";
|
|
172
185
|
mockManagedBaseUrl = "https://platform.example.com/v1/runtime-proxy/gemini";
|
|
173
186
|
mockManagedProxyContext = {
|
|
174
187
|
enabled: true,
|
|
@@ -178,12 +191,94 @@ describe("image-studio skill script wrapper", () => {
|
|
|
178
191
|
|
|
179
192
|
await run({ prompt: "a cat" }, fakeContext);
|
|
180
193
|
|
|
194
|
+
expect(lastGenerateProvider).toBe("gemini");
|
|
181
195
|
expect(lastGenerateCredentials).toEqual({
|
|
182
196
|
type: "direct",
|
|
183
197
|
apiKey: "direct-key",
|
|
184
198
|
});
|
|
185
199
|
});
|
|
186
200
|
|
|
201
|
+
test("openai provider dispatches to OpenAI with its key", async () => {
|
|
202
|
+
mockImageGenProvider = "openai";
|
|
203
|
+
mockOpenAIKey = "openai-direct-key";
|
|
204
|
+
|
|
205
|
+
const result = await run({ prompt: "a robot" }, fakeContext);
|
|
206
|
+
|
|
207
|
+
expect(result.isError).toBe(false);
|
|
208
|
+
expect(lastGenerateProvider).toBe("openai");
|
|
209
|
+
expect(lastGenerateCredentials).toEqual({
|
|
210
|
+
type: "direct",
|
|
211
|
+
apiKey: "openai-direct-key",
|
|
212
|
+
});
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
test("openai provider returns OpenAI-specific error hint when no key", async () => {
|
|
216
|
+
mockImageGenProvider = "openai";
|
|
217
|
+
mockOpenAIKey = undefined;
|
|
218
|
+
|
|
219
|
+
const result = await run({ prompt: "a robot" }, fakeContext);
|
|
220
|
+
|
|
221
|
+
expect(result.isError).toBe(true);
|
|
222
|
+
expect(result.content).toContain("OpenAI");
|
|
223
|
+
expect(result.content).not.toContain("No Gemini API key");
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
test("explicit model override routes to owning provider (gemini config → openai call)", async () => {
|
|
227
|
+
// Config says the user's default provider is gemini, but the LLM
|
|
228
|
+
// explicitly requests a gpt-* model. The tool must dispatch to OpenAI
|
|
229
|
+
// and resolve OpenAI credentials, not fall back to Gemini's default.
|
|
230
|
+
mockImageGenProvider = "gemini";
|
|
231
|
+
mockOpenAIKey = "openai-direct-key";
|
|
232
|
+
|
|
233
|
+
const result = await run(
|
|
234
|
+
{ prompt: "a robot", model: "gpt-image-2" },
|
|
235
|
+
fakeContext,
|
|
236
|
+
);
|
|
237
|
+
|
|
238
|
+
expect(result.isError).toBe(false);
|
|
239
|
+
expect(lastGenerateProvider).toBe("openai");
|
|
240
|
+
expect(lastGenerateCredentials).toEqual({
|
|
241
|
+
type: "direct",
|
|
242
|
+
apiKey: "openai-direct-key",
|
|
243
|
+
});
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
test("explicit model override routes to owning provider (openai config → gemini call)", async () => {
|
|
247
|
+
// The inverse: config says openai but the LLM asks for a gemini-* model.
|
|
248
|
+
mockImageGenProvider = "openai";
|
|
249
|
+
mockGeminiKey = "gemini-direct-key";
|
|
250
|
+
|
|
251
|
+
const result = await run(
|
|
252
|
+
{ prompt: "a cat", model: "gemini-3-pro-image-preview" },
|
|
253
|
+
fakeContext,
|
|
254
|
+
);
|
|
255
|
+
|
|
256
|
+
expect(result.isError).toBe(false);
|
|
257
|
+
expect(lastGenerateProvider).toBe("gemini");
|
|
258
|
+
expect(lastGenerateCredentials).toEqual({
|
|
259
|
+
type: "direct",
|
|
260
|
+
apiKey: "gemini-direct-key",
|
|
261
|
+
});
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
test("cross-provider override surfaces owning provider's credential error", async () => {
|
|
265
|
+
// Config: gemini (with a gemini key). LLM asks for gpt-image-2 but the
|
|
266
|
+
// OpenAI key is missing. The error hint must reference OpenAI, not
|
|
267
|
+
// Gemini, because the dispatch target is OpenAI.
|
|
268
|
+
mockImageGenProvider = "gemini";
|
|
269
|
+
mockGeminiKey = "test-gemini-key";
|
|
270
|
+
mockOpenAIKey = undefined;
|
|
271
|
+
|
|
272
|
+
const result = await run(
|
|
273
|
+
{ prompt: "a robot", model: "gpt-image-2" },
|
|
274
|
+
fakeContext,
|
|
275
|
+
);
|
|
276
|
+
|
|
277
|
+
expect(result.isError).toBe(true);
|
|
278
|
+
expect(result.content).toContain("OpenAI");
|
|
279
|
+
expect(result.content).not.toContain("No Gemini API key");
|
|
280
|
+
});
|
|
281
|
+
|
|
187
282
|
test("returns generated image with contentBlocks", async () => {
|
|
188
283
|
const result = await run({ prompt: "a sunset" }, fakeContext);
|
|
189
284
|
|
|
@@ -225,7 +320,17 @@ describe("image-studio skill script wrapper", () => {
|
|
|
225
320
|
const result = await run({ prompt: "a cat" }, fakeContext);
|
|
226
321
|
|
|
227
322
|
expect(result.isError).toBe(true);
|
|
228
|
-
expect(result.content).toContain("Mock error: API failure");
|
|
323
|
+
expect(result.content).toContain("Mock Gemini error: API failure");
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
test("openai generation error uses OpenAI-specific mapping", async () => {
|
|
327
|
+
mockImageGenProvider = "openai";
|
|
328
|
+
mockGenerateError = new Error("openai failure");
|
|
329
|
+
|
|
330
|
+
const result = await run({ prompt: "a cat" }, fakeContext);
|
|
331
|
+
|
|
332
|
+
expect(result.isError).toBe(true);
|
|
333
|
+
expect(result.content).toContain("Mock OpenAI error: openai failure");
|
|
229
334
|
});
|
|
230
335
|
|
|
231
336
|
test("reads source images from file paths on disk", async () => {
|
|
@@ -330,6 +435,7 @@ describe("image-studio TOOLS.json manifest", () => {
|
|
|
330
435
|
expect(props.model.enum).toEqual([
|
|
331
436
|
"gemini-3.1-flash-image-preview",
|
|
332
437
|
"gemini-3-pro-image-preview",
|
|
438
|
+
"gpt-image-2",
|
|
333
439
|
]);
|
|
334
440
|
expect(props.variants.type).toBe("number");
|
|
335
441
|
});
|