@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
|
@@ -568,6 +568,313 @@ describe("macOS message ingress with connected extension", () => {
|
|
|
568
568
|
});
|
|
569
569
|
});
|
|
570
570
|
|
|
571
|
+
// ── macOS SSE bridge ingress (no extension registry) ────────────────
|
|
572
|
+
//
|
|
573
|
+
// Exercises the cloud-hosted + desktop SSE bridge path for macOS turns
|
|
574
|
+
// WITHOUT relying on the ChromeExtensionRegistry. This validates the
|
|
575
|
+
// native macOS host-browser proxy path where `host_browser_request`
|
|
576
|
+
// frames travel through `assistantEventHub` (SSE) rather than the
|
|
577
|
+
// extension WebSocket.
|
|
578
|
+
//
|
|
579
|
+
// In production, this path is used when:
|
|
580
|
+
// - The macOS desktop client is connected to the assistant via SSE
|
|
581
|
+
// - The user does NOT have the Chrome extension installed
|
|
582
|
+
// - The desktop client receives `host_browser_request` frames via SSE,
|
|
583
|
+
// executes CDP commands against the local Chrome, and POSTs results
|
|
584
|
+
// back to `/v1/host-browser-result`
|
|
585
|
+
//
|
|
586
|
+
// The test constructs a HostBrowserProxy wired to a mock SSE sender
|
|
587
|
+
// (simulating the `onEvent` hub publisher) and a mock macOS client that
|
|
588
|
+
// observes the sent frames and returns results via POST.
|
|
589
|
+
|
|
590
|
+
describe("macOS SSE bridge ingress (no extension registry)", () => {
|
|
591
|
+
let server: RuntimeHttpServer;
|
|
592
|
+
let port: number;
|
|
593
|
+
let runtimeBaseUrl: string;
|
|
594
|
+
|
|
595
|
+
beforeEach(async () => {
|
|
596
|
+
const db = getDb();
|
|
597
|
+
db.run("DELETE FROM contact_channels");
|
|
598
|
+
db.run("DELETE FROM contacts");
|
|
599
|
+
pendingInteractions.clear();
|
|
600
|
+
__resetChromeExtensionRegistryForTests();
|
|
601
|
+
|
|
602
|
+
port = 20200 + Math.floor(Math.random() * 200);
|
|
603
|
+
runtimeBaseUrl = `http://127.0.0.1:${port}`;
|
|
604
|
+
server = new RuntimeHttpServer({ port });
|
|
605
|
+
await server.start();
|
|
606
|
+
});
|
|
607
|
+
|
|
608
|
+
afterEach(async () => {
|
|
609
|
+
await server?.stop();
|
|
610
|
+
pendingInteractions.clear();
|
|
611
|
+
__resetChromeExtensionRegistryForTests();
|
|
612
|
+
});
|
|
613
|
+
|
|
614
|
+
/**
|
|
615
|
+
* Create a HostBrowserProxy wired to a mock SSE sender. The sender
|
|
616
|
+
* captures `host_browser_request` frames and simulates what the macOS
|
|
617
|
+
* desktop client does: execute the CDP command locally and POST the
|
|
618
|
+
* result back to `/v1/host-browser-result`.
|
|
619
|
+
*
|
|
620
|
+
* Unlike `createBoundProxy` (which routes through the extension
|
|
621
|
+
* registry), this helper routes through a direct function call —
|
|
622
|
+
* simulating the `onEvent` SSE hub publisher path.
|
|
623
|
+
*/
|
|
624
|
+
function createSseBoundProxy(
|
|
625
|
+
conversationId: string,
|
|
626
|
+
token: string,
|
|
627
|
+
): {
|
|
628
|
+
proxy: HostBrowserProxy;
|
|
629
|
+
conversation: Conversation;
|
|
630
|
+
sentFrames: Array<{ type: string; [key: string]: unknown }>;
|
|
631
|
+
} {
|
|
632
|
+
let proxyRef: HostBrowserProxy | null = null;
|
|
633
|
+
const conversation = {
|
|
634
|
+
resolveHostBrowser(
|
|
635
|
+
requestId: string,
|
|
636
|
+
response: { content: string; isError: boolean },
|
|
637
|
+
) {
|
|
638
|
+
proxyRef?.resolve(requestId, response);
|
|
639
|
+
},
|
|
640
|
+
} as unknown as Conversation;
|
|
641
|
+
|
|
642
|
+
const sentFrames: Array<{ type: string; [key: string]: unknown }> = [];
|
|
643
|
+
|
|
644
|
+
// The SSE sender simulates what `assistantEventHub.publish` does in
|
|
645
|
+
// production: it delivers the message to the connected SSE client.
|
|
646
|
+
// Here we capture the frame and immediately simulate the macOS client
|
|
647
|
+
// handling it — executing a mock CDP command and POSTing the result
|
|
648
|
+
// back to the runtime.
|
|
649
|
+
const sseSender = (msg: ServerMessage) => {
|
|
650
|
+
const frame = msg as { type: string; [key: string]: unknown };
|
|
651
|
+
sentFrames.push(frame);
|
|
652
|
+
|
|
653
|
+
if (frame.type === "host_browser_request") {
|
|
654
|
+
const requestId = frame.requestId as string;
|
|
655
|
+
|
|
656
|
+
// Register the pending interaction (in production this happens
|
|
657
|
+
// in makeHubPublisher inside conversation-routes.ts).
|
|
658
|
+
pendingInteractions.register(requestId, {
|
|
659
|
+
conversation,
|
|
660
|
+
conversationId,
|
|
661
|
+
kind: "host_browser",
|
|
662
|
+
});
|
|
663
|
+
|
|
664
|
+
// Simulate the macOS desktop client processing the CDP command
|
|
665
|
+
// and POSTing the result back to the runtime.
|
|
666
|
+
const cdpMethod = frame.cdpMethod as string;
|
|
667
|
+
let content: string;
|
|
668
|
+
let isError = false;
|
|
669
|
+
if (cdpMethod === "Browser.getVersion") {
|
|
670
|
+
content = JSON.stringify({
|
|
671
|
+
product: "Chrome/macOS-SSE-Test",
|
|
672
|
+
protocolVersion: "1.3",
|
|
673
|
+
revision: "@macos-sse",
|
|
674
|
+
userAgent: "Mozilla/5.0 (macOS SSE bridge e2e fixture)",
|
|
675
|
+
jsVersion: "0.0.0-macos-sse",
|
|
676
|
+
});
|
|
677
|
+
} else if (cdpMethod === "Runtime.evaluate") {
|
|
678
|
+
content = JSON.stringify({ result: { value: "complete" } });
|
|
679
|
+
} else {
|
|
680
|
+
content = `mock macOS client: unsupported cdpMethod "${cdpMethod}"`;
|
|
681
|
+
isError = true;
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
// POST result asynchronously (simulating the real macOS client).
|
|
685
|
+
void fetch(`${runtimeBaseUrl}/v1/host-browser-result`, {
|
|
686
|
+
method: "POST",
|
|
687
|
+
headers: {
|
|
688
|
+
"Content-Type": "application/json",
|
|
689
|
+
Authorization: `Bearer ${token}`,
|
|
690
|
+
},
|
|
691
|
+
body: JSON.stringify({ requestId, content, isError }),
|
|
692
|
+
})
|
|
693
|
+
.then((res) => res.body?.cancel())
|
|
694
|
+
.catch(() => {});
|
|
695
|
+
}
|
|
696
|
+
};
|
|
697
|
+
|
|
698
|
+
const proxy = new HostBrowserProxy(sseSender);
|
|
699
|
+
proxyRef = proxy;
|
|
700
|
+
return { proxy, conversation, sentFrames };
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
test("happy path: Browser.getVersion round-trips through the SSE bridge without extension registry", async () => {
|
|
704
|
+
const guardianId = `test-guardian-macos-sse-${crypto.randomUUID()}`;
|
|
705
|
+
const token = mintActorToken(guardianId);
|
|
706
|
+
|
|
707
|
+
const { proxy, sentFrames } = createSseBoundProxy(
|
|
708
|
+
"conv-macos-sse-happy",
|
|
709
|
+
token,
|
|
710
|
+
);
|
|
711
|
+
|
|
712
|
+
const result = await proxy.request(
|
|
713
|
+
{ cdpMethod: "Browser.getVersion" },
|
|
714
|
+
"conv-macos-sse-happy",
|
|
715
|
+
);
|
|
716
|
+
|
|
717
|
+
// The request completed via the SSE bridge path, not the extension registry.
|
|
718
|
+
expect(result.isError).toBe(false);
|
|
719
|
+
expect(result.content).toContain("Chrome/macOS-SSE-Test");
|
|
720
|
+
|
|
721
|
+
// The SSE sender received exactly one host_browser_request frame.
|
|
722
|
+
const requests = sentFrames.filter(
|
|
723
|
+
(f) => f.type === "host_browser_request",
|
|
724
|
+
);
|
|
725
|
+
expect(requests).toHaveLength(1);
|
|
726
|
+
expect(requests[0].cdpMethod).toBe("Browser.getVersion");
|
|
727
|
+
expect(requests[0].conversationId).toBe("conv-macos-sse-happy");
|
|
728
|
+
|
|
729
|
+
// The extension registry should NOT have been involved — no entries exist.
|
|
730
|
+
expect(getChromeExtensionRegistry().get(guardianId)).toBeUndefined();
|
|
731
|
+
|
|
732
|
+
proxy.dispose();
|
|
733
|
+
});
|
|
734
|
+
|
|
735
|
+
test("abort: SSE-bridged request resolves to 'Aborted' when signal fires", async () => {
|
|
736
|
+
const guardianId = `test-guardian-macos-sse-abort-${crypto.randomUUID()}`;
|
|
737
|
+
const _token = mintActorToken(guardianId);
|
|
738
|
+
|
|
739
|
+
// Use a CDP handler that hangs forever so we can abort mid-flight.
|
|
740
|
+
const sentFrames: Array<{ type: string; [key: string]: unknown }> = [];
|
|
741
|
+
let proxyRef: HostBrowserProxy | null = null;
|
|
742
|
+
const conversation = {
|
|
743
|
+
resolveHostBrowser(
|
|
744
|
+
requestId: string,
|
|
745
|
+
response: { content: string; isError: boolean },
|
|
746
|
+
) {
|
|
747
|
+
proxyRef?.resolve(requestId, response);
|
|
748
|
+
},
|
|
749
|
+
} as unknown as Conversation;
|
|
750
|
+
|
|
751
|
+
const hangingSender = (msg: ServerMessage) => {
|
|
752
|
+
const frame = msg as { type: string; [key: string]: unknown };
|
|
753
|
+
sentFrames.push(frame);
|
|
754
|
+
if (frame.type === "host_browser_request") {
|
|
755
|
+
const requestId = frame.requestId as string;
|
|
756
|
+
pendingInteractions.register(requestId, {
|
|
757
|
+
conversation,
|
|
758
|
+
conversationId: "conv-macos-sse-abort",
|
|
759
|
+
kind: "host_browser",
|
|
760
|
+
});
|
|
761
|
+
// Simulate a macOS client that never responds (hangs).
|
|
762
|
+
}
|
|
763
|
+
};
|
|
764
|
+
|
|
765
|
+
const proxy = new HostBrowserProxy(hangingSender);
|
|
766
|
+
proxyRef = proxy;
|
|
767
|
+
|
|
768
|
+
const controller = new AbortController();
|
|
769
|
+
const resultPromise = proxy.request(
|
|
770
|
+
{ cdpMethod: "Browser.getVersion" },
|
|
771
|
+
"conv-macos-sse-abort",
|
|
772
|
+
controller.signal,
|
|
773
|
+
);
|
|
774
|
+
|
|
775
|
+
// Wait for the SSE sender to observe the request.
|
|
776
|
+
await waitFor(() => sentFrames.length === 1);
|
|
777
|
+
|
|
778
|
+
controller.abort();
|
|
779
|
+
const result = await resultPromise;
|
|
780
|
+
|
|
781
|
+
expect(result.content).toBe("Aborted");
|
|
782
|
+
expect(result.isError).toBe(true);
|
|
783
|
+
|
|
784
|
+
// The cancel frame should have been sent through the SSE sender.
|
|
785
|
+
const cancels = sentFrames.filter((f) => f.type === "host_browser_cancel");
|
|
786
|
+
expect(cancels).toHaveLength(1);
|
|
787
|
+
|
|
788
|
+
proxy.dispose();
|
|
789
|
+
});
|
|
790
|
+
|
|
791
|
+
test("timeout: SSE-bridged request surfaces timeout when macOS client never responds", async () => {
|
|
792
|
+
const guardianId = `test-guardian-macos-sse-timeout-${crypto.randomUUID()}`;
|
|
793
|
+
const _token = mintActorToken(guardianId);
|
|
794
|
+
|
|
795
|
+
const sentFrames: Array<{ type: string; [key: string]: unknown }> = [];
|
|
796
|
+
let proxyRef: HostBrowserProxy | null = null;
|
|
797
|
+
const conversation = {
|
|
798
|
+
resolveHostBrowser(
|
|
799
|
+
requestId: string,
|
|
800
|
+
response: { content: string; isError: boolean },
|
|
801
|
+
) {
|
|
802
|
+
proxyRef?.resolve(requestId, response);
|
|
803
|
+
},
|
|
804
|
+
} as unknown as Conversation;
|
|
805
|
+
|
|
806
|
+
const hangingSender = (msg: ServerMessage) => {
|
|
807
|
+
const frame = msg as { type: string; [key: string]: unknown };
|
|
808
|
+
sentFrames.push(frame);
|
|
809
|
+
if (frame.type === "host_browser_request") {
|
|
810
|
+
const requestId = frame.requestId as string;
|
|
811
|
+
pendingInteractions.register(requestId, {
|
|
812
|
+
conversation,
|
|
813
|
+
conversationId: "conv-macos-sse-timeout",
|
|
814
|
+
kind: "host_browser",
|
|
815
|
+
});
|
|
816
|
+
// Never respond — simulate unresponsive macOS client.
|
|
817
|
+
}
|
|
818
|
+
};
|
|
819
|
+
|
|
820
|
+
const proxy = new HostBrowserProxy(hangingSender);
|
|
821
|
+
proxyRef = proxy;
|
|
822
|
+
|
|
823
|
+
const result = await proxy.request(
|
|
824
|
+
{ cdpMethod: "Browser.getVersion", timeout_seconds: 0.05 },
|
|
825
|
+
"conv-macos-sse-timeout",
|
|
826
|
+
);
|
|
827
|
+
|
|
828
|
+
expect(result.isError).toBe(true);
|
|
829
|
+
expect(result.content).toContain("timed out");
|
|
830
|
+
|
|
831
|
+
// The SSE sender received the request frame (confirming the timeout
|
|
832
|
+
// is from the proxy timer, not a send failure).
|
|
833
|
+
const requests = sentFrames.filter(
|
|
834
|
+
(f) => f.type === "host_browser_request",
|
|
835
|
+
);
|
|
836
|
+
expect(requests).toHaveLength(1);
|
|
837
|
+
expect(requests[0].cdpMethod).toBe("Browser.getVersion");
|
|
838
|
+
|
|
839
|
+
proxy.dispose();
|
|
840
|
+
});
|
|
841
|
+
|
|
842
|
+
test("multiple sequential commands round-trip through the SSE bridge", async () => {
|
|
843
|
+
const guardianId = `test-guardian-macos-sse-seq-${crypto.randomUUID()}`;
|
|
844
|
+
const token = mintActorToken(guardianId);
|
|
845
|
+
|
|
846
|
+
const { proxy, sentFrames } = createSseBoundProxy(
|
|
847
|
+
"conv-macos-sse-seq",
|
|
848
|
+
token,
|
|
849
|
+
);
|
|
850
|
+
|
|
851
|
+
// First command: Browser.getVersion
|
|
852
|
+
const result1 = await proxy.request(
|
|
853
|
+
{ cdpMethod: "Browser.getVersion" },
|
|
854
|
+
"conv-macos-sse-seq",
|
|
855
|
+
);
|
|
856
|
+
expect(result1.isError).toBe(false);
|
|
857
|
+
expect(result1.content).toContain("Chrome/macOS-SSE-Test");
|
|
858
|
+
|
|
859
|
+
// Second command: Runtime.evaluate
|
|
860
|
+
const result2 = await proxy.request(
|
|
861
|
+
{ cdpMethod: "Runtime.evaluate", cdpParams: { expression: "1+1" } },
|
|
862
|
+
"conv-macos-sse-seq",
|
|
863
|
+
);
|
|
864
|
+
expect(result2.isError).toBe(false);
|
|
865
|
+
|
|
866
|
+
// Both requests went through the SSE sender.
|
|
867
|
+
const requests = sentFrames.filter(
|
|
868
|
+
(f) => f.type === "host_browser_request",
|
|
869
|
+
);
|
|
870
|
+
expect(requests).toHaveLength(2);
|
|
871
|
+
expect(requests[0].cdpMethod).toBe("Browser.getVersion");
|
|
872
|
+
expect(requests[1].cdpMethod).toBe("Runtime.evaluate");
|
|
873
|
+
|
|
874
|
+
proxy.dispose();
|
|
875
|
+
});
|
|
876
|
+
});
|
|
877
|
+
|
|
571
878
|
// ── Local wait helpers ──────────────────────────────────────────────
|
|
572
879
|
|
|
573
880
|
async function waitFor(
|
|
@@ -46,7 +46,7 @@ import {
|
|
|
46
46
|
verifyHostBrowserCapability,
|
|
47
47
|
} from "../runtime/capability-tokens.js";
|
|
48
48
|
import {
|
|
49
|
-
|
|
49
|
+
getAllowedExtensionOrigins,
|
|
50
50
|
handleBrowserExtensionPair,
|
|
51
51
|
} from "../runtime/routes/browser-extension-pair-routes.js";
|
|
52
52
|
|
|
@@ -81,10 +81,10 @@ const HELPER_BINARY = resolveHelperBinary();
|
|
|
81
81
|
const HELPER_EXISTS = existsSync(HELPER_BINARY);
|
|
82
82
|
|
|
83
83
|
const ALLOWED_ORIGIN = (() => {
|
|
84
|
-
const first = Array.from(
|
|
84
|
+
const first = Array.from(getAllowedExtensionOrigins())[0];
|
|
85
85
|
if (!first) {
|
|
86
86
|
throw new Error(
|
|
87
|
-
"
|
|
87
|
+
"getAllowedExtensionOrigins() must contain at least one extension origin for tests",
|
|
88
88
|
);
|
|
89
89
|
}
|
|
90
90
|
return first;
|
|
@@ -42,11 +42,11 @@ describe("supportsHostProxy (runtime)", () => {
|
|
|
42
42
|
expect(supportsHostProxy("chrome-extension", "host_cu")).toBe(false);
|
|
43
43
|
});
|
|
44
44
|
|
|
45
|
-
test("capability form grants
|
|
45
|
+
test("capability form grants all four capabilities to macOS including host_browser", () => {
|
|
46
46
|
expect(supportsHostProxy("macos", "host_bash")).toBe(true);
|
|
47
47
|
expect(supportsHostProxy("macos", "host_file")).toBe(true);
|
|
48
48
|
expect(supportsHostProxy("macos", "host_cu")).toBe(true);
|
|
49
|
-
expect(supportsHostProxy("macos", "host_browser")).toBe(
|
|
49
|
+
expect(supportsHostProxy("macos", "host_browser")).toBe(true);
|
|
50
50
|
});
|
|
51
51
|
|
|
52
52
|
test("capability form rejects everything for non-host-proxy interfaces", () => {
|
|
@@ -163,3 +163,37 @@ describe("isHostProxyTransport", () => {
|
|
|
163
163
|
expect(isHostProxyTransport(transport)).toBe(false);
|
|
164
164
|
});
|
|
165
165
|
});
|
|
166
|
+
|
|
167
|
+
// ---------------------------------------------------------------------------
|
|
168
|
+
// macOS host_browser capability — regression guards
|
|
169
|
+
// ---------------------------------------------------------------------------
|
|
170
|
+
|
|
171
|
+
describe("macOS host_browser capability", () => {
|
|
172
|
+
test("macOS supports host_browser via supportsHostProxy capability check", () => {
|
|
173
|
+
// macOS is now host-browser-capable, enabling host_browser_request
|
|
174
|
+
// frames to be sent to the desktop client via SSE or extension registry.
|
|
175
|
+
expect(supportsHostProxy("macos", "host_browser")).toBe(true);
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
test("macOS still passes the no-arg host-proxy check (full desktop proxy)", () => {
|
|
179
|
+
// The no-arg form gates computer-use preactivation and full proxy restore.
|
|
180
|
+
// macOS must still pass this check.
|
|
181
|
+
expect(supportsHostProxy("macos")).toBe(true);
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
test("non-macOS non-extension interfaces remain host_browser-ineligible", () => {
|
|
185
|
+
const ineligible: InterfaceId[] = [
|
|
186
|
+
"ios",
|
|
187
|
+
"cli",
|
|
188
|
+
"telegram",
|
|
189
|
+
"phone",
|
|
190
|
+
"vellum",
|
|
191
|
+
"whatsapp",
|
|
192
|
+
"slack",
|
|
193
|
+
"email",
|
|
194
|
+
];
|
|
195
|
+
for (const id of ineligible) {
|
|
196
|
+
expect(supportsHostProxy(id, "host_browser")).toBe(false);
|
|
197
|
+
}
|
|
198
|
+
});
|
|
199
|
+
});
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, mock, test } from "bun:test";
|
|
2
|
+
|
|
3
|
+
// ---------------------------------------------------------------------------
|
|
4
|
+
// Module mocks — must appear before importing the module under test
|
|
5
|
+
// ---------------------------------------------------------------------------
|
|
6
|
+
|
|
7
|
+
let mockProviderKey: string | undefined;
|
|
8
|
+
let mockPlatformBaseUrl = "";
|
|
9
|
+
let mockAssistantApiKey = "";
|
|
10
|
+
|
|
11
|
+
mock.module("../security/secure-keys.js", () => ({
|
|
12
|
+
getProviderKeyAsync: async (_provider: string) => mockProviderKey,
|
|
13
|
+
}));
|
|
14
|
+
|
|
15
|
+
mock.module("../providers/managed-proxy/context.js", () => ({
|
|
16
|
+
resolveManagedProxyContext: async () => ({
|
|
17
|
+
enabled: !!mockPlatformBaseUrl && !!mockAssistantApiKey,
|
|
18
|
+
platformBaseUrl: mockPlatformBaseUrl,
|
|
19
|
+
assistantApiKey: mockAssistantApiKey,
|
|
20
|
+
}),
|
|
21
|
+
}));
|
|
22
|
+
|
|
23
|
+
// Import after mocks
|
|
24
|
+
import { resolveImageGenCredentials } from "../media/image-credentials.js";
|
|
25
|
+
|
|
26
|
+
describe("resolveImageGenCredentials", () => {
|
|
27
|
+
beforeEach(() => {
|
|
28
|
+
mockProviderKey = undefined;
|
|
29
|
+
mockPlatformBaseUrl = "";
|
|
30
|
+
mockAssistantApiKey = "";
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
describe("managed mode", () => {
|
|
34
|
+
test("returns managed-proxy credentials when context is enabled", async () => {
|
|
35
|
+
mockPlatformBaseUrl = "https://platform.example.com";
|
|
36
|
+
mockAssistantApiKey = "sk-assistant-key";
|
|
37
|
+
|
|
38
|
+
const result = await resolveImageGenCredentials({
|
|
39
|
+
provider: "gemini",
|
|
40
|
+
mode: "managed",
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
expect(result.errorHint).toBeUndefined();
|
|
44
|
+
expect(result.credentials).toEqual({
|
|
45
|
+
type: "managed-proxy",
|
|
46
|
+
assistantApiKey: "sk-assistant-key",
|
|
47
|
+
baseUrl: "https://platform.example.com/v1/runtime-proxy/gemini",
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
test("returns errorHint mentioning 'log in to Vellum' when platform URL is missing", async () => {
|
|
52
|
+
mockPlatformBaseUrl = "";
|
|
53
|
+
mockAssistantApiKey = "sk-assistant-key";
|
|
54
|
+
|
|
55
|
+
const result = await resolveImageGenCredentials({
|
|
56
|
+
provider: "gemini",
|
|
57
|
+
mode: "managed",
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
expect(result.credentials).toBeUndefined();
|
|
61
|
+
expect(result.errorHint).toBeDefined();
|
|
62
|
+
expect(result.errorHint).toContain("log in to Vellum");
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
test("returns errorHint when assistant API key is empty (TOCTOU-safe)", async () => {
|
|
66
|
+
mockPlatformBaseUrl = "https://platform.example.com";
|
|
67
|
+
mockAssistantApiKey = "";
|
|
68
|
+
|
|
69
|
+
const result = await resolveImageGenCredentials({
|
|
70
|
+
provider: "gemini",
|
|
71
|
+
mode: "managed",
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
expect(result.credentials).toBeUndefined();
|
|
75
|
+
expect(result.errorHint).toBeDefined();
|
|
76
|
+
expect(result.errorHint).toContain("log in to Vellum");
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
describe("your-own mode", () => {
|
|
81
|
+
test("returns direct credentials for gemini when key is present", async () => {
|
|
82
|
+
mockProviderKey = "gemini-api-key";
|
|
83
|
+
|
|
84
|
+
const result = await resolveImageGenCredentials({
|
|
85
|
+
provider: "gemini",
|
|
86
|
+
mode: "your-own",
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
expect(result.errorHint).toBeUndefined();
|
|
90
|
+
expect(result.credentials).toEqual({
|
|
91
|
+
type: "direct",
|
|
92
|
+
apiKey: "gemini-api-key",
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
test("returns errorHint mentioning 'Gemini API key' when no key is set", async () => {
|
|
97
|
+
mockProviderKey = undefined;
|
|
98
|
+
|
|
99
|
+
const result = await resolveImageGenCredentials({
|
|
100
|
+
provider: "gemini",
|
|
101
|
+
mode: "your-own",
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
expect(result.credentials).toBeUndefined();
|
|
105
|
+
expect(result.errorHint).toBeDefined();
|
|
106
|
+
expect(result.errorHint).toContain("Gemini API key");
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
test("returns direct credentials for openai when key is present", async () => {
|
|
110
|
+
mockProviderKey = "openai-api-key";
|
|
111
|
+
|
|
112
|
+
const result = await resolveImageGenCredentials({
|
|
113
|
+
provider: "openai",
|
|
114
|
+
mode: "your-own",
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
expect(result.errorHint).toBeUndefined();
|
|
118
|
+
expect(result.credentials).toEqual({
|
|
119
|
+
type: "direct",
|
|
120
|
+
apiKey: "openai-api-key",
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
test("returns errorHint mentioning 'OpenAI API key' when no key is set", async () => {
|
|
125
|
+
mockProviderKey = undefined;
|
|
126
|
+
|
|
127
|
+
const result = await resolveImageGenCredentials({
|
|
128
|
+
provider: "openai",
|
|
129
|
+
mode: "your-own",
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
expect(result.credentials).toBeUndefined();
|
|
133
|
+
expect(result.errorHint).toBeDefined();
|
|
134
|
+
expect(result.errorHint).toContain("OpenAI API key");
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
});
|