@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
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
* - Edit mode with --source (source images passed through)
|
|
12
12
|
* - --variants passed through
|
|
13
13
|
* - --model override
|
|
14
|
+
* - Provider dispatch (gemini vs openai)
|
|
14
15
|
*/
|
|
15
16
|
|
|
16
17
|
import { existsSync, readFileSync } from "node:fs";
|
|
@@ -29,15 +30,12 @@ let mockConfig = {
|
|
|
29
30
|
services: {
|
|
30
31
|
"image-generation": {
|
|
31
32
|
mode: "your-own" as "managed" | "your-own",
|
|
32
|
-
provider: "gemini" as
|
|
33
|
+
provider: "gemini" as "gemini" | "openai",
|
|
33
34
|
model: "gemini-3.1-flash-image-preview",
|
|
34
35
|
},
|
|
35
36
|
},
|
|
36
37
|
};
|
|
37
38
|
|
|
38
|
-
/** Result returned by buildManagedBaseUrl() */
|
|
39
|
-
let mockManagedBaseUrl: string | undefined = undefined;
|
|
40
|
-
|
|
41
39
|
/** Context returned by resolveManagedProxyContext() */
|
|
42
40
|
let mockManagedProxyContext = {
|
|
43
41
|
enabled: false,
|
|
@@ -45,8 +43,8 @@ let mockManagedProxyContext = {
|
|
|
45
43
|
assistantApiKey: "",
|
|
46
44
|
};
|
|
47
45
|
|
|
48
|
-
/** Key returned by getProviderKeyAsync() */
|
|
49
|
-
let
|
|
46
|
+
/** Key returned by getProviderKeyAsync(). Keyed by provider. */
|
|
47
|
+
let mockProviderKeys: Record<string, string | undefined> = {};
|
|
50
48
|
|
|
51
49
|
/** Result returned by generateImage() */
|
|
52
50
|
let mockGenerateResult: {
|
|
@@ -68,6 +66,7 @@ let mockGenerateError: Error | undefined = undefined;
|
|
|
68
66
|
|
|
69
67
|
/** Captured generateImage call args */
|
|
70
68
|
let lastGenerateCall: {
|
|
69
|
+
provider: unknown;
|
|
71
70
|
credentials: unknown;
|
|
72
71
|
request: unknown;
|
|
73
72
|
} | null = null;
|
|
@@ -82,24 +81,35 @@ mock.module("../../../config/loader.js", () => ({
|
|
|
82
81
|
}));
|
|
83
82
|
|
|
84
83
|
mock.module("../../../providers/managed-proxy/context.js", () => ({
|
|
85
|
-
buildManagedBaseUrl: async () => mockManagedBaseUrl,
|
|
86
84
|
resolveManagedProxyContext: async () => mockManagedProxyContext,
|
|
87
85
|
}));
|
|
88
86
|
|
|
89
87
|
mock.module("../../../security/secure-keys.js", () => ({
|
|
90
|
-
getProviderKeyAsync: async () =>
|
|
88
|
+
getProviderKeyAsync: async (provider: string) => mockProviderKeys[provider],
|
|
91
89
|
}));
|
|
92
90
|
|
|
93
|
-
mock.module("../../../media/
|
|
94
|
-
generateImage: async (
|
|
95
|
-
|
|
91
|
+
mock.module("../../../media/image-service.js", () => ({
|
|
92
|
+
generateImage: async (
|
|
93
|
+
provider: unknown,
|
|
94
|
+
credentials: unknown,
|
|
95
|
+
request: unknown,
|
|
96
|
+
) => {
|
|
97
|
+
lastGenerateCall = { provider, credentials, request };
|
|
96
98
|
if (mockGenerateError) throw mockGenerateError;
|
|
97
99
|
return mockGenerateResult;
|
|
98
100
|
},
|
|
99
|
-
|
|
100
|
-
if (error instanceof Error)
|
|
101
|
+
mapImageGenError: (provider: unknown, error: unknown) => {
|
|
102
|
+
if (error instanceof Error)
|
|
103
|
+
return `Mapped error (${String(provider)}): ${error.message}`;
|
|
101
104
|
return "An unexpected error occurred during image generation.";
|
|
102
105
|
},
|
|
106
|
+
providerForModel: (model: string | undefined, fallback: string) => {
|
|
107
|
+
if (!model) return fallback;
|
|
108
|
+
if (model.startsWith("gpt-") || model.startsWith("dall-e-"))
|
|
109
|
+
return "openai";
|
|
110
|
+
if (model.startsWith("gemini-")) return "gemini";
|
|
111
|
+
return fallback;
|
|
112
|
+
},
|
|
103
113
|
}));
|
|
104
114
|
|
|
105
115
|
mock.module("../../../util/logger.js", () => ({
|
|
@@ -188,13 +198,12 @@ beforeEach(() => {
|
|
|
188
198
|
},
|
|
189
199
|
},
|
|
190
200
|
};
|
|
191
|
-
mockManagedBaseUrl = undefined;
|
|
192
201
|
mockManagedProxyContext = {
|
|
193
202
|
enabled: false,
|
|
194
203
|
platformBaseUrl: "",
|
|
195
204
|
assistantApiKey: "",
|
|
196
205
|
};
|
|
197
|
-
|
|
206
|
+
mockProviderKeys = {};
|
|
198
207
|
mockGenerateResult = {
|
|
199
208
|
images: [
|
|
200
209
|
{
|
|
@@ -221,6 +230,10 @@ describe("help text", () => {
|
|
|
221
230
|
expect(stdout).toContain("your-own");
|
|
222
231
|
expect(stdout).toContain("Examples:");
|
|
223
232
|
expect(stdout).toContain("generate");
|
|
233
|
+
// Both providers mentioned and gpt-image-2 listed as a supported model.
|
|
234
|
+
expect(stdout).toContain("Gemini");
|
|
235
|
+
expect(stdout).toContain("OpenAI");
|
|
236
|
+
expect(stdout).toContain("gpt-image-2");
|
|
224
237
|
});
|
|
225
238
|
|
|
226
239
|
test("image-generation generate --help renders argument docs and examples", async () => {
|
|
@@ -245,7 +258,7 @@ describe("help text", () => {
|
|
|
245
258
|
|
|
246
259
|
describe("required arguments", () => {
|
|
247
260
|
test("generate requires --prompt", async () => {
|
|
248
|
-
|
|
261
|
+
mockProviderKeys.gemini = "test-key";
|
|
249
262
|
const { exitCode } = await runCommand(["image-generation", "generate"]);
|
|
250
263
|
expect(exitCode).not.toBe(0);
|
|
251
264
|
});
|
|
@@ -258,7 +271,7 @@ describe("required arguments", () => {
|
|
|
258
271
|
describe("credential errors", () => {
|
|
259
272
|
test("exits with code 1 when no credentials in your-own mode", async () => {
|
|
260
273
|
mockConfig.services["image-generation"].mode = "your-own";
|
|
261
|
-
|
|
274
|
+
mockProviderKeys.gemini = undefined;
|
|
262
275
|
|
|
263
276
|
const { exitCode } = await runCommand([
|
|
264
277
|
"image-generation",
|
|
@@ -271,7 +284,6 @@ describe("credential errors", () => {
|
|
|
271
284
|
|
|
272
285
|
test("exits with code 1 when no credentials in managed mode", async () => {
|
|
273
286
|
mockConfig.services["image-generation"].mode = "managed";
|
|
274
|
-
mockManagedBaseUrl = undefined;
|
|
275
287
|
|
|
276
288
|
const { exitCode } = await runCommand([
|
|
277
289
|
"image-generation",
|
|
@@ -282,9 +294,10 @@ describe("credential errors", () => {
|
|
|
282
294
|
expect(exitCode).toBe(1);
|
|
283
295
|
});
|
|
284
296
|
|
|
285
|
-
test("--json outputs error when no credentials in your-own mode", async () => {
|
|
297
|
+
test("--json outputs error when no credentials in your-own mode (gemini)", async () => {
|
|
286
298
|
mockConfig.services["image-generation"].mode = "your-own";
|
|
287
|
-
|
|
299
|
+
mockConfig.services["image-generation"].provider = "gemini";
|
|
300
|
+
mockProviderKeys.gemini = undefined;
|
|
288
301
|
|
|
289
302
|
const { exitCode, stdout } = await runCommand([
|
|
290
303
|
"image-generation",
|
|
@@ -297,11 +310,34 @@ describe("credential errors", () => {
|
|
|
297
310
|
const parsed = JSON.parse(stdout.trim());
|
|
298
311
|
expect(parsed.ok).toBe(false);
|
|
299
312
|
expect(parsed.error).toContain("Gemini API key");
|
|
313
|
+
// CLI prepends the actionable `assistant keys set` command with the
|
|
314
|
+
// resolved provider — the shared hint is UI-neutral ("Settings") so
|
|
315
|
+
// without this prefix CLI users get no copy-pasteable next step.
|
|
316
|
+
expect(parsed.error).toContain("Run: assistant keys set gemini <key>");
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
test("--json outputs OpenAI-specific hint when provider=openai and no key set", async () => {
|
|
320
|
+
mockConfig.services["image-generation"].mode = "your-own";
|
|
321
|
+
mockConfig.services["image-generation"].provider = "openai";
|
|
322
|
+
mockConfig.services["image-generation"].model = "gpt-image-2";
|
|
323
|
+
mockProviderKeys.openai = undefined;
|
|
324
|
+
|
|
325
|
+
const { exitCode, stdout } = await runCommand([
|
|
326
|
+
"image-generation",
|
|
327
|
+
"generate",
|
|
328
|
+
"--prompt",
|
|
329
|
+
"A sunset",
|
|
330
|
+
"--json",
|
|
331
|
+
]);
|
|
332
|
+
expect(exitCode).toBe(1);
|
|
333
|
+
const parsed = JSON.parse(stdout.trim());
|
|
334
|
+
expect(parsed.ok).toBe(false);
|
|
335
|
+
expect(parsed.error).toContain("OpenAI API key");
|
|
336
|
+
expect(parsed.error).toContain("Run: assistant keys set openai <key>");
|
|
300
337
|
});
|
|
301
338
|
|
|
302
339
|
test("--json outputs error when no credentials in managed mode", async () => {
|
|
303
340
|
mockConfig.services["image-generation"].mode = "managed";
|
|
304
|
-
mockManagedBaseUrl = undefined;
|
|
305
341
|
|
|
306
342
|
const { exitCode, stdout } = await runCommand([
|
|
307
343
|
"image-generation",
|
|
@@ -313,7 +349,39 @@ describe("credential errors", () => {
|
|
|
313
349
|
expect(exitCode).toBe(1);
|
|
314
350
|
const parsed = JSON.parse(stdout.trim());
|
|
315
351
|
expect(parsed.ok).toBe(false);
|
|
352
|
+
// Base hint from image-credentials.ts is preserved.
|
|
316
353
|
expect(parsed.error).toContain("Managed proxy");
|
|
354
|
+
// CLI augments the hint with CLI-specific recovery guidance so users
|
|
355
|
+
// know how to resolve the problem from the CLI (the shared hint is
|
|
356
|
+
// tool-flavored and only mentions the Vellum app).
|
|
357
|
+
expect(parsed.error).toContain("assistant auth login");
|
|
358
|
+
expect(parsed.error).toContain(
|
|
359
|
+
"services.image-generation.mode to 'your-own'",
|
|
360
|
+
);
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
test("your-own mode hint is NOT augmented with CLI auth guidance", async () => {
|
|
364
|
+
// The CLI-specific `assistant auth login` guidance only makes sense when
|
|
365
|
+
// the user is trying to use managed mode. In your-own mode, the shared
|
|
366
|
+
// hint (pointing at Settings > Models & Services for the API key) is
|
|
367
|
+
// already actionable from the CLI perspective (the key is in secure
|
|
368
|
+
// storage, the user just hasn't set it). Augmenting here would confuse
|
|
369
|
+
// the user into thinking they need to authenticate to Vellum.
|
|
370
|
+
mockConfig.services["image-generation"].mode = "your-own";
|
|
371
|
+
mockConfig.services["image-generation"].provider = "gemini";
|
|
372
|
+
mockProviderKeys.gemini = undefined;
|
|
373
|
+
|
|
374
|
+
const { exitCode, stdout } = await runCommand([
|
|
375
|
+
"image-generation",
|
|
376
|
+
"generate",
|
|
377
|
+
"--prompt",
|
|
378
|
+
"A sunset",
|
|
379
|
+
"--json",
|
|
380
|
+
]);
|
|
381
|
+
expect(exitCode).toBe(1);
|
|
382
|
+
const parsed = JSON.parse(stdout.trim());
|
|
383
|
+
expect(parsed.ok).toBe(false);
|
|
384
|
+
expect(parsed.error).not.toContain("assistant auth login");
|
|
317
385
|
});
|
|
318
386
|
});
|
|
319
387
|
|
|
@@ -324,7 +392,7 @@ describe("credential errors", () => {
|
|
|
324
392
|
describe("credential resolution", () => {
|
|
325
393
|
test("your-own mode uses getProviderKeyAsync for direct credentials", async () => {
|
|
326
394
|
mockConfig.services["image-generation"].mode = "your-own";
|
|
327
|
-
|
|
395
|
+
mockProviderKeys.gemini = "test-gemini-key";
|
|
328
396
|
|
|
329
397
|
await runCommand([
|
|
330
398
|
"image-generation",
|
|
@@ -344,9 +412,8 @@ describe("credential resolution", () => {
|
|
|
344
412
|
expect(creds.apiKey).toBe("test-gemini-key");
|
|
345
413
|
});
|
|
346
414
|
|
|
347
|
-
test("managed mode uses
|
|
415
|
+
test("managed mode uses resolveManagedProxyContext", async () => {
|
|
348
416
|
mockConfig.services["image-generation"].mode = "managed";
|
|
349
|
-
mockManagedBaseUrl = "https://platform.example.com/proxy/gemini";
|
|
350
417
|
mockManagedProxyContext = {
|
|
351
418
|
enabled: true,
|
|
352
419
|
platformBaseUrl: "https://platform.example.com",
|
|
@@ -370,7 +437,160 @@ describe("credential resolution", () => {
|
|
|
370
437
|
};
|
|
371
438
|
expect(creds.type).toBe("managed-proxy");
|
|
372
439
|
expect(creds.assistantApiKey).toBe("managed-api-key");
|
|
373
|
-
expect(creds.baseUrl).toBe(
|
|
440
|
+
expect(creds.baseUrl).toBe(
|
|
441
|
+
"https://platform.example.com/v1/runtime-proxy/gemini",
|
|
442
|
+
);
|
|
443
|
+
});
|
|
444
|
+
});
|
|
445
|
+
|
|
446
|
+
// ---------------------------------------------------------------------------
|
|
447
|
+
// Provider dispatch
|
|
448
|
+
// ---------------------------------------------------------------------------
|
|
449
|
+
|
|
450
|
+
describe("provider dispatch", () => {
|
|
451
|
+
test("provider=gemini is forwarded to the dispatcher", async () => {
|
|
452
|
+
mockConfig.services["image-generation"].mode = "your-own";
|
|
453
|
+
mockConfig.services["image-generation"].provider = "gemini";
|
|
454
|
+
mockProviderKeys.gemini = "test-gemini-key";
|
|
455
|
+
const outDir = join(os.tmpdir(), `img-dispatch-gemini-${Date.now()}`);
|
|
456
|
+
|
|
457
|
+
const { exitCode } = await runCommand([
|
|
458
|
+
"image-generation",
|
|
459
|
+
"generate",
|
|
460
|
+
"--prompt",
|
|
461
|
+
"Test",
|
|
462
|
+
"--output-dir",
|
|
463
|
+
outDir,
|
|
464
|
+
]);
|
|
465
|
+
|
|
466
|
+
expect(exitCode).toBe(0);
|
|
467
|
+
expect(lastGenerateCall).toBeDefined();
|
|
468
|
+
expect(lastGenerateCall!.provider).toBe("gemini");
|
|
469
|
+
});
|
|
470
|
+
|
|
471
|
+
test("provider=openai with --model gpt-image-2 is forwarded to the dispatcher", async () => {
|
|
472
|
+
mockConfig.services["image-generation"].mode = "your-own";
|
|
473
|
+
mockConfig.services["image-generation"].provider = "openai";
|
|
474
|
+
mockConfig.services["image-generation"].model = "gpt-image-2";
|
|
475
|
+
mockProviderKeys.openai = "test-openai-key";
|
|
476
|
+
mockGenerateResult = {
|
|
477
|
+
images: [
|
|
478
|
+
{
|
|
479
|
+
mimeType: "image/png",
|
|
480
|
+
dataBase64: Buffer.from("fake-png-data").toString("base64"),
|
|
481
|
+
},
|
|
482
|
+
],
|
|
483
|
+
resolvedModel: "gpt-image-2",
|
|
484
|
+
};
|
|
485
|
+
const outDir = join(os.tmpdir(), `img-dispatch-openai-${Date.now()}`);
|
|
486
|
+
|
|
487
|
+
const { exitCode } = await runCommand([
|
|
488
|
+
"image-generation",
|
|
489
|
+
"generate",
|
|
490
|
+
"--prompt",
|
|
491
|
+
"Test",
|
|
492
|
+
"--model",
|
|
493
|
+
"gpt-image-2",
|
|
494
|
+
"--output-dir",
|
|
495
|
+
outDir,
|
|
496
|
+
]);
|
|
497
|
+
|
|
498
|
+
expect(exitCode).toBe(0);
|
|
499
|
+
expect(lastGenerateCall).toBeDefined();
|
|
500
|
+
expect(lastGenerateCall!.provider).toBe("openai");
|
|
501
|
+
const req = lastGenerateCall!.request as { model: string };
|
|
502
|
+
expect(req.model).toBe("gpt-image-2");
|
|
503
|
+
const creds = lastGenerateCall!.credentials as {
|
|
504
|
+
type: string;
|
|
505
|
+
apiKey: string;
|
|
506
|
+
};
|
|
507
|
+
expect(creds.type).toBe("direct");
|
|
508
|
+
expect(creds.apiKey).toBe("test-openai-key");
|
|
509
|
+
});
|
|
510
|
+
|
|
511
|
+
test("cross-provider override: config=gemini + --model gpt-image-2 dispatches to openai", async () => {
|
|
512
|
+
// Config still points at gemini (the user's Settings default), but the
|
|
513
|
+
// CLI caller explicitly picks gpt-image-2. The command must dispatch to
|
|
514
|
+
// OpenAI and resolve OpenAI credentials, not fall back to Gemini's
|
|
515
|
+
// default model.
|
|
516
|
+
mockConfig.services["image-generation"].mode = "your-own";
|
|
517
|
+
mockConfig.services["image-generation"].provider = "gemini";
|
|
518
|
+
mockConfig.services["image-generation"].model =
|
|
519
|
+
"gemini-3.1-flash-image-preview";
|
|
520
|
+
mockProviderKeys.openai = "test-openai-key";
|
|
521
|
+
mockGenerateResult = {
|
|
522
|
+
images: [
|
|
523
|
+
{
|
|
524
|
+
mimeType: "image/png",
|
|
525
|
+
dataBase64: Buffer.from("fake-png-data").toString("base64"),
|
|
526
|
+
},
|
|
527
|
+
],
|
|
528
|
+
resolvedModel: "gpt-image-2",
|
|
529
|
+
};
|
|
530
|
+
const outDir = join(os.tmpdir(), `img-cross-openai-${Date.now()}`);
|
|
531
|
+
|
|
532
|
+
const { exitCode } = await runCommand([
|
|
533
|
+
"image-generation",
|
|
534
|
+
"generate",
|
|
535
|
+
"--prompt",
|
|
536
|
+
"Test",
|
|
537
|
+
"--model",
|
|
538
|
+
"gpt-image-2",
|
|
539
|
+
"--output-dir",
|
|
540
|
+
outDir,
|
|
541
|
+
]);
|
|
542
|
+
|
|
543
|
+
expect(exitCode).toBe(0);
|
|
544
|
+
expect(lastGenerateCall).toBeDefined();
|
|
545
|
+
expect(lastGenerateCall!.provider).toBe("openai");
|
|
546
|
+
const req = lastGenerateCall!.request as { model: string };
|
|
547
|
+
expect(req.model).toBe("gpt-image-2");
|
|
548
|
+
const creds = lastGenerateCall!.credentials as {
|
|
549
|
+
type: string;
|
|
550
|
+
apiKey: string;
|
|
551
|
+
};
|
|
552
|
+
expect(creds.type).toBe("direct");
|
|
553
|
+
expect(creds.apiKey).toBe("test-openai-key");
|
|
554
|
+
});
|
|
555
|
+
|
|
556
|
+
test("cross-provider override: config=openai + --model gemini-3-pro-image-preview dispatches to gemini", async () => {
|
|
557
|
+
mockConfig.services["image-generation"].mode = "your-own";
|
|
558
|
+
mockConfig.services["image-generation"].provider = "openai";
|
|
559
|
+
mockConfig.services["image-generation"].model = "gpt-image-2";
|
|
560
|
+
mockProviderKeys.gemini = "test-gemini-key";
|
|
561
|
+
mockGenerateResult = {
|
|
562
|
+
images: [
|
|
563
|
+
{
|
|
564
|
+
mimeType: "image/png",
|
|
565
|
+
dataBase64: Buffer.from("fake-png-data").toString("base64"),
|
|
566
|
+
},
|
|
567
|
+
],
|
|
568
|
+
resolvedModel: "gemini-3-pro-image-preview",
|
|
569
|
+
};
|
|
570
|
+
const outDir = join(os.tmpdir(), `img-cross-gemini-${Date.now()}`);
|
|
571
|
+
|
|
572
|
+
const { exitCode } = await runCommand([
|
|
573
|
+
"image-generation",
|
|
574
|
+
"generate",
|
|
575
|
+
"--prompt",
|
|
576
|
+
"Test",
|
|
577
|
+
"--model",
|
|
578
|
+
"gemini-3-pro-image-preview",
|
|
579
|
+
"--output-dir",
|
|
580
|
+
outDir,
|
|
581
|
+
]);
|
|
582
|
+
|
|
583
|
+
expect(exitCode).toBe(0);
|
|
584
|
+
expect(lastGenerateCall).toBeDefined();
|
|
585
|
+
expect(lastGenerateCall!.provider).toBe("gemini");
|
|
586
|
+
const req = lastGenerateCall!.request as { model: string };
|
|
587
|
+
expect(req.model).toBe("gemini-3-pro-image-preview");
|
|
588
|
+
const creds = lastGenerateCall!.credentials as {
|
|
589
|
+
type: string;
|
|
590
|
+
apiKey: string;
|
|
591
|
+
};
|
|
592
|
+
expect(creds.type).toBe("direct");
|
|
593
|
+
expect(creds.apiKey).toBe("test-gemini-key");
|
|
374
594
|
});
|
|
375
595
|
});
|
|
376
596
|
|
|
@@ -380,7 +600,7 @@ describe("credential resolution", () => {
|
|
|
380
600
|
|
|
381
601
|
describe("generate mode", () => {
|
|
382
602
|
test("generates image and prints file path to stdout", async () => {
|
|
383
|
-
|
|
603
|
+
mockProviderKeys.gemini = "test-key";
|
|
384
604
|
const outDir = join(os.tmpdir(), `img-gen-test-${Date.now()}`);
|
|
385
605
|
|
|
386
606
|
const { exitCode, stdout } = await runCommand([
|
|
@@ -407,7 +627,7 @@ describe("generate mode", () => {
|
|
|
407
627
|
});
|
|
408
628
|
|
|
409
629
|
test("--json produces structured output with paths, MIME types, and sizes", async () => {
|
|
410
|
-
|
|
630
|
+
mockProviderKeys.gemini = "test-key";
|
|
411
631
|
mockGenerateResult = {
|
|
412
632
|
images: [
|
|
413
633
|
{
|
|
@@ -449,7 +669,7 @@ describe("generate mode", () => {
|
|
|
449
669
|
|
|
450
670
|
describe("edit mode", () => {
|
|
451
671
|
test("exits with code 1 when --mode edit is used without --source", async () => {
|
|
452
|
-
|
|
672
|
+
mockProviderKeys.gemini = "test-key";
|
|
453
673
|
|
|
454
674
|
const { exitCode, stdout } = await runCommand([
|
|
455
675
|
"image-generation",
|
|
@@ -470,7 +690,7 @@ describe("edit mode", () => {
|
|
|
470
690
|
});
|
|
471
691
|
|
|
472
692
|
test("passes source images to generateImage in edit mode", async () => {
|
|
473
|
-
|
|
693
|
+
mockProviderKeys.gemini = "test-key";
|
|
474
694
|
|
|
475
695
|
// Use a real temp file for the source image
|
|
476
696
|
const sourceDir = join(os.tmpdir(), `img-src-test-${Date.now()}`);
|
|
@@ -515,7 +735,7 @@ describe("edit mode", () => {
|
|
|
515
735
|
|
|
516
736
|
describe("variants", () => {
|
|
517
737
|
test("non-numeric --variants defaults to 1", async () => {
|
|
518
|
-
|
|
738
|
+
mockProviderKeys.gemini = "test-key";
|
|
519
739
|
const outDir = join(os.tmpdir(), `img-nan-variants-${Date.now()}`);
|
|
520
740
|
|
|
521
741
|
const { exitCode } = await runCommand([
|
|
@@ -536,7 +756,7 @@ describe("variants", () => {
|
|
|
536
756
|
});
|
|
537
757
|
|
|
538
758
|
test("--variants is passed through to generateImage", async () => {
|
|
539
|
-
|
|
759
|
+
mockProviderKeys.gemini = "test-key";
|
|
540
760
|
mockGenerateResult = {
|
|
541
761
|
images: [
|
|
542
762
|
{
|
|
@@ -587,7 +807,7 @@ describe("variants", () => {
|
|
|
587
807
|
|
|
588
808
|
describe("model override", () => {
|
|
589
809
|
test("--model overrides config model", async () => {
|
|
590
|
-
|
|
810
|
+
mockProviderKeys.gemini = "test-key";
|
|
591
811
|
const outDir = join(os.tmpdir(), `img-model-test-${Date.now()}`);
|
|
592
812
|
|
|
593
813
|
await runCommand([
|
|
@@ -607,7 +827,7 @@ describe("model override", () => {
|
|
|
607
827
|
});
|
|
608
828
|
|
|
609
829
|
test("falls back to config model when --model is not provided", async () => {
|
|
610
|
-
|
|
830
|
+
mockProviderKeys.gemini = "test-key";
|
|
611
831
|
mockConfig.services["image-generation"].model =
|
|
612
832
|
"gemini-3.1-flash-image-preview";
|
|
613
833
|
const outDir = join(os.tmpdir(), `img-model-fallback-${Date.now()}`);
|
|
@@ -633,7 +853,7 @@ describe("model override", () => {
|
|
|
633
853
|
|
|
634
854
|
describe("error handling", () => {
|
|
635
855
|
test("maps generateImage error and exits with code 1", async () => {
|
|
636
|
-
|
|
856
|
+
mockProviderKeys.gemini = "test-key";
|
|
637
857
|
mockGenerateError = new Error("API rate limit exceeded");
|
|
638
858
|
|
|
639
859
|
const { exitCode } = await runCommand([
|
|
@@ -647,7 +867,7 @@ describe("error handling", () => {
|
|
|
647
867
|
});
|
|
648
868
|
|
|
649
869
|
test("--json outputs mapped error on generateImage failure", async () => {
|
|
650
|
-
|
|
870
|
+
mockProviderKeys.gemini = "test-key";
|
|
651
871
|
mockGenerateError = new Error("Connection timeout");
|
|
652
872
|
|
|
653
873
|
const { exitCode, stdout } = await runCommand([
|
|
@@ -138,6 +138,14 @@ async function runCommand(
|
|
|
138
138
|
const stdoutChunks: string[] = [];
|
|
139
139
|
const stderrChunks: string[] = [];
|
|
140
140
|
|
|
141
|
+
// Mock isTTY to undefined so the stdin fallback path is reachable even
|
|
142
|
+
// when tests run from an interactive terminal (where isTTY === true).
|
|
143
|
+
const originalIsTTY = process.stdin.isTTY;
|
|
144
|
+
Object.defineProperty(process.stdin, "isTTY", {
|
|
145
|
+
value: undefined,
|
|
146
|
+
configurable: true,
|
|
147
|
+
});
|
|
148
|
+
|
|
141
149
|
process.stdout.write = ((chunk: unknown) => {
|
|
142
150
|
stdoutChunks.push(typeof chunk === "string" ? chunk : String(chunk));
|
|
143
151
|
return true;
|
|
@@ -164,6 +172,10 @@ async function runCommand(
|
|
|
164
172
|
} finally {
|
|
165
173
|
process.stdout.write = originalStdoutWrite;
|
|
166
174
|
process.stderr.write = originalStderrWrite;
|
|
175
|
+
Object.defineProperty(process.stdin, "isTTY", {
|
|
176
|
+
value: originalIsTTY,
|
|
177
|
+
configurable: true,
|
|
178
|
+
});
|
|
167
179
|
}
|
|
168
180
|
|
|
169
181
|
const exitCode = process.exitCode ?? 0;
|
|
@@ -128,6 +128,14 @@ async function runCommand(
|
|
|
128
128
|
const stdoutChunks: string[] = [];
|
|
129
129
|
const stderrChunks: string[] = [];
|
|
130
130
|
|
|
131
|
+
// Mock isTTY to undefined so the stdin fallback path is reachable even
|
|
132
|
+
// when tests run from an interactive terminal (where isTTY === true).
|
|
133
|
+
const originalIsTTY = process.stdin.isTTY;
|
|
134
|
+
Object.defineProperty(process.stdin, "isTTY", {
|
|
135
|
+
value: undefined,
|
|
136
|
+
configurable: true,
|
|
137
|
+
});
|
|
138
|
+
|
|
131
139
|
process.stdout.write = ((chunk: unknown) => {
|
|
132
140
|
stdoutChunks.push(typeof chunk === "string" ? chunk : String(chunk));
|
|
133
141
|
return true;
|
|
@@ -161,6 +169,10 @@ async function runCommand(
|
|
|
161
169
|
process.stdout.write = originalStdoutWrite;
|
|
162
170
|
process.stderr.write = originalStderrWrite;
|
|
163
171
|
process.exit = originalExit;
|
|
172
|
+
Object.defineProperty(process.stdin, "isTTY", {
|
|
173
|
+
value: originalIsTTY,
|
|
174
|
+
configurable: true,
|
|
175
|
+
});
|
|
164
176
|
}
|
|
165
177
|
|
|
166
178
|
const exitCode = process.exitCode ?? 0;
|
|
@@ -699,7 +699,7 @@ plaintext — plaintext only makes sense when the user owns physical access to
|
|
|
699
699
|
the medium (e.g. an external SSD).
|
|
700
700
|
|
|
701
701
|
Examples:
|
|
702
|
-
$ vellum backup enable --interval 6 --retention
|
|
702
|
+
$ vellum backup enable --interval 6 --retention 3
|
|
703
703
|
$ vellum backup destinations add /Volumes/BackupSSD/vellum --plaintext
|
|
704
704
|
$ vellum backup status
|
|
705
705
|
$ vellum backup list
|
|
@@ -717,7 +717,7 @@ Examples:
|
|
|
717
717
|
)
|
|
718
718
|
.option(
|
|
719
719
|
"--retention <n>",
|
|
720
|
-
"Snapshots to retain per destination (1-100). Defaults to
|
|
720
|
+
"Snapshots to retain per destination (1-100). Defaults to 3.",
|
|
721
721
|
)
|
|
722
722
|
.option(
|
|
723
723
|
"--no-offsite",
|