@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
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Generates app icons using the
|
|
2
|
+
* Generates app icons using the configured image-generation provider.
|
|
3
3
|
*
|
|
4
4
|
* Called as an async side-effect after app creation — never blocks
|
|
5
5
|
* the main app_create flow. Icons are saved to the app's directory
|
|
@@ -11,25 +11,18 @@ import { join } from "node:path";
|
|
|
11
11
|
|
|
12
12
|
import { getConfig } from "../config/loader.js";
|
|
13
13
|
import { getAppDirPath } from "../memory/app-store.js";
|
|
14
|
-
import {
|
|
15
|
-
buildManagedBaseUrl,
|
|
16
|
-
resolveManagedProxyContext,
|
|
17
|
-
} from "../providers/managed-proxy/context.js";
|
|
18
|
-
import { getProviderKeyAsync } from "../security/secure-keys.js";
|
|
19
14
|
import { getLogger } from "../util/logger.js";
|
|
20
|
-
import {
|
|
21
|
-
|
|
22
|
-
type ImageGenCredentials,
|
|
23
|
-
mapGeminiError,
|
|
24
|
-
} from "./gemini-image-service.js";
|
|
15
|
+
import { resolveImageGenCredentials } from "./image-credentials.js";
|
|
16
|
+
import { generateImage, mapImageGenError } from "./image-service.js";
|
|
25
17
|
|
|
26
18
|
const log = getLogger("app-icon-generator");
|
|
27
19
|
|
|
28
20
|
/**
|
|
29
21
|
* Generate an app icon and save it to `~/.vellum/apps/{appId}/icon.png`.
|
|
30
22
|
*
|
|
31
|
-
* Uses
|
|
32
|
-
* Silently no-ops if no
|
|
23
|
+
* Uses the configured image-generation provider when credentials are
|
|
24
|
+
* available. Silently no-ops if no credentials are configured or
|
|
25
|
+
* generation fails.
|
|
33
26
|
*/
|
|
34
27
|
export async function generateAppIcon(
|
|
35
28
|
appId: string,
|
|
@@ -37,34 +30,15 @@ export async function generateAppIcon(
|
|
|
37
30
|
appDescription?: string,
|
|
38
31
|
): Promise<void> {
|
|
39
32
|
const config = getConfig();
|
|
40
|
-
const
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
if (imageGenMode === "managed") {
|
|
46
|
-
const managedBaseUrl = await buildManagedBaseUrl("gemini");
|
|
47
|
-
if (managedBaseUrl) {
|
|
48
|
-
const ctx = await resolveManagedProxyContext();
|
|
49
|
-
credentials = {
|
|
50
|
-
type: "managed-proxy",
|
|
51
|
-
assistantApiKey: ctx.assistantApiKey,
|
|
52
|
-
baseUrl: managedBaseUrl,
|
|
53
|
-
};
|
|
54
|
-
}
|
|
55
|
-
} else {
|
|
56
|
-
const apiKey = await getProviderKeyAsync("gemini");
|
|
57
|
-
if (apiKey) {
|
|
58
|
-
credentials = { type: "direct", apiKey };
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
|
|
33
|
+
const svc = config.services["image-generation"];
|
|
34
|
+
const { credentials, errorHint } = await resolveImageGenCredentials({
|
|
35
|
+
provider: svc.provider,
|
|
36
|
+
mode: svc.mode,
|
|
37
|
+
});
|
|
62
38
|
if (!credentials) {
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
: "No Gemini API key configured";
|
|
67
|
-
log.debug(`${reason} — skipping app icon generation`);
|
|
39
|
+
log.debug(
|
|
40
|
+
`${errorHint ?? "Image generation is not configured"} — skipping app icon generation`,
|
|
41
|
+
);
|
|
68
42
|
return;
|
|
69
43
|
}
|
|
70
44
|
|
|
@@ -90,16 +64,19 @@ export async function generateAppIcon(
|
|
|
90
64
|
"- Modern aesthetic similar to Apple's design language";
|
|
91
65
|
|
|
92
66
|
try {
|
|
93
|
-
log.info({ appId, appName }, "Generating app icon
|
|
67
|
+
log.info({ appId, appName, provider: svc.provider }, "Generating app icon");
|
|
94
68
|
|
|
95
|
-
const result = await generateImage(credentials, {
|
|
69
|
+
const result = await generateImage(svc.provider, credentials, {
|
|
96
70
|
prompt,
|
|
97
71
|
mode: "generate",
|
|
98
|
-
model:
|
|
72
|
+
model: svc.model,
|
|
99
73
|
});
|
|
100
74
|
|
|
101
75
|
if (result.images.length === 0) {
|
|
102
|
-
log.warn(
|
|
76
|
+
log.warn(
|
|
77
|
+
{ appId, provider: svc.provider },
|
|
78
|
+
"Provider returned no image for app icon",
|
|
79
|
+
);
|
|
103
80
|
return;
|
|
104
81
|
}
|
|
105
82
|
|
|
@@ -111,9 +88,9 @@ export async function generateAppIcon(
|
|
|
111
88
|
|
|
112
89
|
log.info({ appId, iconPath }, "App icon saved");
|
|
113
90
|
} catch (error) {
|
|
114
|
-
const message =
|
|
91
|
+
const message = mapImageGenError(svc.provider, error);
|
|
115
92
|
log.warn(
|
|
116
|
-
{ appId, error: message },
|
|
93
|
+
{ appId, provider: svc.provider, error: message },
|
|
117
94
|
"App icon generation failed — skipping",
|
|
118
95
|
);
|
|
119
96
|
}
|
|
@@ -1,60 +1,45 @@
|
|
|
1
1
|
import { getConfig } from "../config/loader.js";
|
|
2
|
-
import {
|
|
3
|
-
buildManagedBaseUrl,
|
|
4
|
-
resolveManagedProxyContext,
|
|
5
|
-
} from "../providers/managed-proxy/context.js";
|
|
6
|
-
import { getProviderKeyAsync } from "../security/secure-keys.js";
|
|
7
2
|
import { ConfigError, ProviderError } from "../util/errors.js";
|
|
8
|
-
import {
|
|
9
|
-
|
|
10
|
-
type ImageGenCredentials,
|
|
11
|
-
} from "./gemini-image-service.js";
|
|
3
|
+
import { resolveImageGenCredentials } from "./image-credentials.js";
|
|
4
|
+
import { generateImage, mapImageGenError } from "./image-service.js";
|
|
12
5
|
|
|
13
6
|
export async function generateAvatar(
|
|
14
7
|
prompt: string,
|
|
15
8
|
): Promise<{ imageBase64: string; mimeType: string }> {
|
|
16
9
|
const config = getConfig();
|
|
17
|
-
const
|
|
10
|
+
const svc = config.services["image-generation"];
|
|
18
11
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
const managedBaseUrl = await buildManagedBaseUrl("gemini");
|
|
24
|
-
if (managedBaseUrl) {
|
|
25
|
-
const ctx = await resolveManagedProxyContext();
|
|
26
|
-
credentials = {
|
|
27
|
-
type: "managed-proxy",
|
|
28
|
-
assistantApiKey: ctx.assistantApiKey,
|
|
29
|
-
baseUrl: managedBaseUrl,
|
|
30
|
-
};
|
|
31
|
-
}
|
|
32
|
-
} else {
|
|
33
|
-
const geminiKey = await getProviderKeyAsync("gemini");
|
|
34
|
-
if (geminiKey) {
|
|
35
|
-
credentials = { type: "direct", apiKey: geminiKey };
|
|
36
|
-
}
|
|
37
|
-
}
|
|
12
|
+
const { credentials, errorHint } = await resolveImageGenCredentials({
|
|
13
|
+
provider: svc.provider,
|
|
14
|
+
mode: svc.mode,
|
|
15
|
+
});
|
|
38
16
|
|
|
39
17
|
if (!credentials) {
|
|
40
|
-
|
|
41
|
-
imageGenMode === "managed"
|
|
42
|
-
? "Managed proxy is not available. Please log in to Vellum or switch to Your Own mode."
|
|
43
|
-
: "Gemini API key is not configured. Please set your Gemini API key in Settings > Models & Services.";
|
|
44
|
-
throw new ConfigError(hint);
|
|
18
|
+
throw new ConfigError(errorHint ?? "Image generation is not configured.");
|
|
45
19
|
}
|
|
46
20
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
21
|
+
let result;
|
|
22
|
+
try {
|
|
23
|
+
result = await generateImage(svc.provider, credentials, {
|
|
24
|
+
prompt,
|
|
25
|
+
mode: "generate",
|
|
26
|
+
model: svc.model,
|
|
27
|
+
});
|
|
28
|
+
} catch (error) {
|
|
29
|
+
// Re-throw with a provider-aware, user-friendly message so callers
|
|
30
|
+
// (e.g. avatar-generator) don't need provider context to surface a
|
|
31
|
+
// useful error.
|
|
32
|
+
throw new ProviderError(
|
|
33
|
+
mapImageGenError(svc.provider, error),
|
|
34
|
+
svc.provider,
|
|
35
|
+
);
|
|
36
|
+
}
|
|
52
37
|
|
|
53
38
|
const image = result.images[0];
|
|
54
39
|
if (!image) {
|
|
55
40
|
throw new ProviderError(
|
|
56
|
-
"
|
|
57
|
-
|
|
41
|
+
"Image generation returned no images.",
|
|
42
|
+
svc.provider,
|
|
58
43
|
);
|
|
59
44
|
}
|
|
60
45
|
|
|
@@ -1,45 +1,13 @@
|
|
|
1
1
|
import { ApiError, GoogleGenAI } from "@google/genai";
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
model?: string;
|
|
12
|
-
/** Number of output variants (1-4, default 1) */
|
|
13
|
-
variants?: number;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
/** Credentials for direct Gemini API access. */
|
|
17
|
-
interface DirectCredentials {
|
|
18
|
-
type: "direct";
|
|
19
|
-
apiKey: string;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
/** Credentials for managed proxy access (platform translates to Vertex AI). */
|
|
23
|
-
interface ManagedProxyCredentials {
|
|
24
|
-
type: "managed-proxy";
|
|
25
|
-
assistantApiKey: string;
|
|
26
|
-
baseUrl: string;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export type ImageGenCredentials = DirectCredentials | ManagedProxyCredentials;
|
|
30
|
-
|
|
31
|
-
interface GeneratedImage {
|
|
32
|
-
mimeType: string;
|
|
33
|
-
dataBase64: string;
|
|
34
|
-
/** Short title derived from the model's text response, if available. */
|
|
35
|
-
title?: string;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
interface ImageGenerationResult {
|
|
39
|
-
images: GeneratedImage[];
|
|
40
|
-
text?: string;
|
|
41
|
-
resolvedModel: string;
|
|
42
|
-
}
|
|
3
|
+
import {
|
|
4
|
+
type GeneratedImage,
|
|
5
|
+
type ImageGenCredentials,
|
|
6
|
+
type ImageGenerationRequest,
|
|
7
|
+
type ImageGenerationResult,
|
|
8
|
+
type ManagedProxyCredentials,
|
|
9
|
+
MAX_VARIANTS,
|
|
10
|
+
} from "./types.js";
|
|
43
11
|
|
|
44
12
|
// --- Constants ---
|
|
45
13
|
|
|
@@ -48,7 +16,6 @@ const ALLOWED_MODELS = new Set([
|
|
|
48
16
|
"gemini-3.1-flash-image-preview",
|
|
49
17
|
"gemini-3-pro-image-preview",
|
|
50
18
|
]);
|
|
51
|
-
const MAX_VARIANTS = 4;
|
|
52
19
|
|
|
53
20
|
// --- Error mapping ---
|
|
54
21
|
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared credential resolver for image-generation call sites (image-studio
|
|
3
|
+
* tool, CLI `image-generation` command, app-icon generator).
|
|
4
|
+
*
|
|
5
|
+
* Each call site picks between the managed-proxy path (routes through the
|
|
6
|
+
* platform) and the "your own" path (direct provider API key). This module
|
|
7
|
+
* resolves either path and returns a provider-aware error hint when
|
|
8
|
+
* credentials are unavailable.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { MANAGED_PROVIDER_META } from "../providers/managed-proxy/constants.js";
|
|
12
|
+
import { resolveManagedProxyContext } from "../providers/managed-proxy/context.js";
|
|
13
|
+
import { getProviderKeyAsync } from "../security/secure-keys.js";
|
|
14
|
+
import type { ImageGenCredentials, ImageGenProvider } from "./types.js";
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Resolve credentials for an image-generation request.
|
|
18
|
+
*
|
|
19
|
+
* - `mode === "managed"`: returns managed-proxy credentials when the
|
|
20
|
+
* platform URL and assistant API key are both configured, otherwise
|
|
21
|
+
* returns a hint telling the user to log in or switch modes.
|
|
22
|
+
* - `mode === "your-own"`: returns direct credentials when the provider
|
|
23
|
+
* API key is present in secure storage (or the env-var fallback),
|
|
24
|
+
* otherwise returns a provider-aware hint pointing at Settings.
|
|
25
|
+
*/
|
|
26
|
+
export async function resolveImageGenCredentials(opts: {
|
|
27
|
+
provider: ImageGenProvider;
|
|
28
|
+
mode: "managed" | "your-own";
|
|
29
|
+
}): Promise<{ credentials?: ImageGenCredentials; errorHint?: string }> {
|
|
30
|
+
const { provider, mode } = opts;
|
|
31
|
+
|
|
32
|
+
if (mode === "managed") {
|
|
33
|
+
// Resolve platform URL + assistant API key from a single snapshot so
|
|
34
|
+
// baseUrl and assistantApiKey can't diverge if the credential is cleared
|
|
35
|
+
// between lookups.
|
|
36
|
+
const meta = MANAGED_PROVIDER_META[provider];
|
|
37
|
+
const ctx = await resolveManagedProxyContext();
|
|
38
|
+
if (
|
|
39
|
+
!meta?.managed ||
|
|
40
|
+
!meta.proxyPath ||
|
|
41
|
+
!ctx.enabled ||
|
|
42
|
+
!ctx.assistantApiKey
|
|
43
|
+
) {
|
|
44
|
+
return {
|
|
45
|
+
errorHint:
|
|
46
|
+
"Managed proxy is not available. Please log in to Vellum or switch to Your Own mode.",
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
return {
|
|
50
|
+
credentials: {
|
|
51
|
+
type: "managed-proxy",
|
|
52
|
+
assistantApiKey: ctx.assistantApiKey,
|
|
53
|
+
baseUrl: `${ctx.platformBaseUrl}${meta.proxyPath}`,
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// mode === "your-own"
|
|
59
|
+
const apiKey = await getProviderKeyAsync(provider);
|
|
60
|
+
if (apiKey) {
|
|
61
|
+
return { credentials: { type: "direct", apiKey } };
|
|
62
|
+
}
|
|
63
|
+
return { errorHint: providerKeyHint(provider) };
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function providerKeyHint(provider: ImageGenProvider): string {
|
|
67
|
+
switch (provider) {
|
|
68
|
+
case "gemini":
|
|
69
|
+
return "No Gemini API key configured. Please set your Gemini API key in Settings > Models & Services.";
|
|
70
|
+
case "openai":
|
|
71
|
+
return "No OpenAI API key configured. Please set your OpenAI API key in Settings > Models & Services.";
|
|
72
|
+
}
|
|
73
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import {
|
|
2
|
+
generateImage as generateImageGemini,
|
|
3
|
+
mapGeminiError,
|
|
4
|
+
} from "./gemini-image-service.js";
|
|
5
|
+
import { generateImageOpenAI, mapOpenAIError } from "./openai-image-service.js";
|
|
6
|
+
import type {
|
|
7
|
+
ImageGenCredentials,
|
|
8
|
+
ImageGenerationRequest,
|
|
9
|
+
ImageGenerationResult,
|
|
10
|
+
ImageGenProvider,
|
|
11
|
+
} from "./types.js";
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Dispatch image generation to the provider-specific implementation.
|
|
15
|
+
*/
|
|
16
|
+
export function generateImage(
|
|
17
|
+
provider: ImageGenProvider,
|
|
18
|
+
credentials: ImageGenCredentials,
|
|
19
|
+
request: ImageGenerationRequest,
|
|
20
|
+
): Promise<ImageGenerationResult> {
|
|
21
|
+
switch (provider) {
|
|
22
|
+
case "openai":
|
|
23
|
+
return generateImageOpenAI(credentials, request);
|
|
24
|
+
case "gemini":
|
|
25
|
+
return generateImageGemini(credentials, request);
|
|
26
|
+
default: {
|
|
27
|
+
const _exhaustive: never = provider;
|
|
28
|
+
throw new Error(`Unknown image generation provider: ${_exhaustive}`);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Dispatch error mapping to the provider-specific implementation.
|
|
35
|
+
*/
|
|
36
|
+
export function mapImageGenError(
|
|
37
|
+
provider: ImageGenProvider,
|
|
38
|
+
error: unknown,
|
|
39
|
+
): string {
|
|
40
|
+
switch (provider) {
|
|
41
|
+
case "openai":
|
|
42
|
+
return mapOpenAIError(error);
|
|
43
|
+
case "gemini":
|
|
44
|
+
return mapGeminiError(error);
|
|
45
|
+
default: {
|
|
46
|
+
const _exhaustive: never = provider;
|
|
47
|
+
throw new Error(`Unknown image generation provider: ${_exhaustive}`);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Derive the owning provider from an explicit model ID.
|
|
54
|
+
*
|
|
55
|
+
* When a caller (LLM tool invocation, CLI `--model` flag) passes an explicit
|
|
56
|
+
* `model` argument, the request should dispatch to the provider that owns
|
|
57
|
+
* that model — not to the user's configured Settings provider. Without this,
|
|
58
|
+
* asking for `gpt-image-2` while `services["image-generation"].provider` is
|
|
59
|
+
* `gemini` silently falls back to the Gemini default model.
|
|
60
|
+
*
|
|
61
|
+
* Model prefix mapping:
|
|
62
|
+
* - `gpt-*` or `dall-e-*` → `openai`
|
|
63
|
+
* - `gemini-*` → `gemini`
|
|
64
|
+
* - anything else (or `undefined`) → the provided `fallback`
|
|
65
|
+
*/
|
|
66
|
+
export function providerForModel(
|
|
67
|
+
model: unknown,
|
|
68
|
+
fallback: ImageGenProvider,
|
|
69
|
+
): ImageGenProvider {
|
|
70
|
+
// `model` originates from an LLM tool call's `input.model`, which is a
|
|
71
|
+
// `Record<string, unknown>` without runtime validation — so a non-string
|
|
72
|
+
// value (e.g. `{"model": 123}`) must not crash `.startsWith`.
|
|
73
|
+
if (typeof model !== "string" || !model) return fallback;
|
|
74
|
+
if (model.startsWith("gpt-") || model.startsWith("dall-e-")) return "openai";
|
|
75
|
+
if (model.startsWith("gemini-")) return "gemini";
|
|
76
|
+
return fallback;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export type {
|
|
80
|
+
GeneratedImage,
|
|
81
|
+
ImageGenCredentials,
|
|
82
|
+
ImageGenerationRequest,
|
|
83
|
+
ImageGenerationResult,
|
|
84
|
+
ImageGenProvider,
|
|
85
|
+
} from "./types.js";
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import OpenAI from "openai";
|
|
2
|
+
import { toFile } from "openai/uploads";
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
type GeneratedImage,
|
|
6
|
+
type ImageGenCredentials,
|
|
7
|
+
type ImageGenerationRequest,
|
|
8
|
+
type ImageGenerationResult,
|
|
9
|
+
MAX_VARIANTS,
|
|
10
|
+
} from "./types.js";
|
|
11
|
+
|
|
12
|
+
// --- Constants ---
|
|
13
|
+
|
|
14
|
+
const DEFAULT_MODEL = "gpt-image-2";
|
|
15
|
+
const ALLOWED_MODELS = new Set(["gpt-image-2"]);
|
|
16
|
+
|
|
17
|
+
// --- Error mapping ---
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Map an error raised by the OpenAI Images API to a user-friendly string.
|
|
21
|
+
* Mirrors the status-code branches of `mapGeminiError` in
|
|
22
|
+
* `./gemini-image-service.ts`.
|
|
23
|
+
*/
|
|
24
|
+
export function mapOpenAIError(error: unknown): string {
|
|
25
|
+
if (error instanceof OpenAI.APIError) {
|
|
26
|
+
const status = error.status;
|
|
27
|
+
if (status === 400) {
|
|
28
|
+
return "The image request was invalid. Please check your prompt and try again.";
|
|
29
|
+
}
|
|
30
|
+
if (status === 401 || status === 403) {
|
|
31
|
+
return "Authentication failed. Please check your OpenAI API key.";
|
|
32
|
+
}
|
|
33
|
+
if (status === 429) {
|
|
34
|
+
return "Rate limit exceeded. Please wait a moment and try again.";
|
|
35
|
+
}
|
|
36
|
+
if (status !== undefined && status >= 500) {
|
|
37
|
+
return "The OpenAI service is temporarily unavailable. Please try again later.";
|
|
38
|
+
}
|
|
39
|
+
return `OpenAI API error (status ${status}). Please try again.`;
|
|
40
|
+
}
|
|
41
|
+
if (error instanceof Error) {
|
|
42
|
+
return `Image generation failed: ${error.message}`;
|
|
43
|
+
}
|
|
44
|
+
return "An unexpected error occurred during image generation.";
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// --- Title derivation ---
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Derive a short filename-safe title from the first 6 words of the prompt.
|
|
51
|
+
* Uses the same sanitization regex as `extractTitle` in
|
|
52
|
+
* `./gemini-image-service.ts`.
|
|
53
|
+
*/
|
|
54
|
+
function deriveTitleFromPrompt(prompt: string): string | undefined {
|
|
55
|
+
const firstWords = prompt.trim().split(/\s+/).slice(0, 6).join(" ");
|
|
56
|
+
if (!firstWords) return undefined;
|
|
57
|
+
const sanitized = firstWords
|
|
58
|
+
.replace(/[^\w\s-]/g, "")
|
|
59
|
+
.replace(/\s+/g, "-")
|
|
60
|
+
.toLowerCase()
|
|
61
|
+
.slice(0, 60);
|
|
62
|
+
return sanitized.length > 0 ? sanitized : undefined;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// --- Core function ---
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Generate or edit an image via the OpenAI Images API (`gpt-image-2`).
|
|
69
|
+
*
|
|
70
|
+
* The OpenAI Images API does not return commentary text, so the returned
|
|
71
|
+
* `text` field is always `undefined`. A title is derived from the prompt
|
|
72
|
+
* instead and attached to every returned image.
|
|
73
|
+
*/
|
|
74
|
+
export async function generateImageOpenAI(
|
|
75
|
+
credentials: ImageGenCredentials,
|
|
76
|
+
request: ImageGenerationRequest,
|
|
77
|
+
): Promise<ImageGenerationResult> {
|
|
78
|
+
const model =
|
|
79
|
+
request.model && ALLOWED_MODELS.has(request.model)
|
|
80
|
+
? request.model
|
|
81
|
+
: DEFAULT_MODEL;
|
|
82
|
+
|
|
83
|
+
const variants = Math.max(1, Math.min(request.variants ?? 1, MAX_VARIANTS));
|
|
84
|
+
|
|
85
|
+
const client =
|
|
86
|
+
credentials.type === "managed-proxy"
|
|
87
|
+
? new OpenAI({
|
|
88
|
+
apiKey: credentials.assistantApiKey,
|
|
89
|
+
baseURL: credentials.baseUrl,
|
|
90
|
+
})
|
|
91
|
+
: new OpenAI({ apiKey: credentials.apiKey });
|
|
92
|
+
|
|
93
|
+
const title = deriveTitleFromPrompt(request.prompt);
|
|
94
|
+
|
|
95
|
+
let response: { data?: Array<{ b64_json?: string }> };
|
|
96
|
+
|
|
97
|
+
if (request.mode === "edit" && request.sourceImages) {
|
|
98
|
+
const files = await Promise.all(
|
|
99
|
+
request.sourceImages.map((img) =>
|
|
100
|
+
toFile(Buffer.from(img.dataBase64, "base64"), "input.png", {
|
|
101
|
+
type: img.mimeType,
|
|
102
|
+
}),
|
|
103
|
+
),
|
|
104
|
+
);
|
|
105
|
+
response = (await client.images.edit({
|
|
106
|
+
model,
|
|
107
|
+
prompt: request.prompt,
|
|
108
|
+
image: files,
|
|
109
|
+
n: variants,
|
|
110
|
+
})) as { data?: Array<{ b64_json?: string }> };
|
|
111
|
+
} else {
|
|
112
|
+
response = (await client.images.generate({
|
|
113
|
+
model,
|
|
114
|
+
prompt: request.prompt,
|
|
115
|
+
n: variants,
|
|
116
|
+
})) as { data?: Array<{ b64_json?: string }> };
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const images: GeneratedImage[] = [];
|
|
120
|
+
for (const entry of response.data ?? []) {
|
|
121
|
+
if (!entry.b64_json) continue;
|
|
122
|
+
const img: GeneratedImage = {
|
|
123
|
+
mimeType: "image/png",
|
|
124
|
+
dataBase64: entry.b64_json,
|
|
125
|
+
};
|
|
126
|
+
if (title) img.title = title;
|
|
127
|
+
images.push(img);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return { images, text: undefined, resolvedModel: model };
|
|
131
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
export type ImageGenProvider = "gemini" | "openai";
|
|
2
|
+
|
|
3
|
+
export interface DirectCredentials {
|
|
4
|
+
type: "direct";
|
|
5
|
+
apiKey: string;
|
|
6
|
+
}
|
|
7
|
+
export interface ManagedProxyCredentials {
|
|
8
|
+
type: "managed-proxy";
|
|
9
|
+
assistantApiKey: string;
|
|
10
|
+
baseUrl: string;
|
|
11
|
+
}
|
|
12
|
+
export type ImageGenCredentials = DirectCredentials | ManagedProxyCredentials;
|
|
13
|
+
|
|
14
|
+
export interface ImageGenerationRequest {
|
|
15
|
+
prompt: string;
|
|
16
|
+
mode: "generate" | "edit";
|
|
17
|
+
sourceImages?: Array<{ mimeType: string; dataBase64: string }>;
|
|
18
|
+
model?: string;
|
|
19
|
+
variants?: number;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface GeneratedImage {
|
|
23
|
+
mimeType: string;
|
|
24
|
+
dataBase64: string;
|
|
25
|
+
title?: string;
|
|
26
|
+
}
|
|
27
|
+
export interface ImageGenerationResult {
|
|
28
|
+
images: GeneratedImage[];
|
|
29
|
+
text?: string;
|
|
30
|
+
resolvedModel: string;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export const MAX_VARIANTS = 4;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Derive the image-generation provider from a model identifier by prefix.
|
|
37
|
+
* Canonical mapping shared between the runtime model setter
|
|
38
|
+
* (`setImageGenModel`) and workspace migration 006-services-config so the
|
|
39
|
+
* two cannot drift. Unknown models fall through to "gemini".
|
|
40
|
+
*/
|
|
41
|
+
export function providerForImageModelPrefix(model: string): ImageGenProvider {
|
|
42
|
+
if (model.startsWith("gpt") || model.startsWith("dall-e")) {
|
|
43
|
+
return "openai";
|
|
44
|
+
}
|
|
45
|
+
return "gemini";
|
|
46
|
+
}
|