@vellumai/assistant 0.6.2 → 0.6.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ARCHITECTURE.md +273 -10
- package/Dockerfile +2 -3
- package/bun.lock +41 -49
- package/bunfig.toml +3 -0
- package/docs/architecture/memory.md +1 -1
- package/docs/backup-troubleshooting.md +52 -0
- package/docs/browser-use-architecture-phase2.md +174 -0
- package/docs/stt-provider-onboarding.md +120 -0
- package/knip.json +12 -2
- package/node_modules/@vellumai/ces-contracts/bun.lock +8 -6
- package/node_modules/@vellumai/ces-contracts/package.json +3 -3
- package/node_modules/@vellumai/ces-contracts/src/rpc.ts +42 -0
- package/openapi.yaml +1111 -86
- package/package.json +40 -42
- package/scripts/generate-openapi.ts +0 -2
- package/scripts/test.sh +73 -18
- package/src/__tests__/acp-session.test.ts +43 -0
- package/src/__tests__/agent-image-optimize.test.ts +28 -0
- package/src/__tests__/agent-loop.test.ts +123 -0
- package/src/__tests__/anthropic-provider.test.ts +263 -10
- package/src/__tests__/app-builder-tool-scripts.test.ts +1 -0
- package/src/__tests__/app-executors.test.ts +1 -0
- package/src/__tests__/app-source-watcher.test.ts +37 -11
- package/src/__tests__/approval-routes-http.test.ts +178 -1
- package/src/__tests__/auto-analysis-end-to-end.test.ts +550 -0
- package/src/__tests__/auto-analysis-prompt.test.ts +50 -0
- package/src/__tests__/browser-fill-credential.test.ts +240 -94
- package/src/__tests__/browser-manager.test.ts +40 -27
- package/src/__tests__/browser-skill-baseline-tool-payload.test.ts +2 -2
- package/src/__tests__/browser-skill-endstate.test.ts +31 -7
- package/src/__tests__/btw-routes.test.ts +7 -0
- package/src/__tests__/call-controller.test.ts +581 -20
- package/src/__tests__/catalog-files.test.ts +1000 -0
- package/src/__tests__/channel-approvals.test.ts +53 -0
- package/src/__tests__/channel-invite-transport.test.ts +2 -2
- package/src/__tests__/channel-readiness-routes.test.ts +16 -20
- package/src/__tests__/channel-readiness-service.test.ts +12 -7
- package/src/__tests__/checker.test.ts +157 -10
- package/src/__tests__/clawhub-files.test.ts +347 -0
- package/src/__tests__/commit-message-enrichment-service.test.ts +36 -19
- package/src/__tests__/config-analysis.test.ts +100 -0
- package/src/__tests__/config-managed-gemini-defaults.test.ts +326 -0
- package/src/__tests__/config-schema-cmd.test.ts +2 -2
- package/src/__tests__/config-schema.test.ts +1248 -224
- package/src/__tests__/config-watcher-cleanup-throttle.test.ts +339 -0
- package/src/__tests__/config-watcher.test.ts +43 -8
- package/src/__tests__/confirmation-request-guardian-bridge.test.ts +23 -0
- package/src/__tests__/contact-store-user-file.test.ts +512 -0
- package/src/__tests__/contacts-write.test.ts +197 -0
- package/src/__tests__/context-overflow-approval.test.ts +16 -1
- package/src/__tests__/context-window-manager.test.ts +88 -0
- package/src/__tests__/conversation-abort-tool-results.test.ts +2 -0
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +2 -1
- package/src/__tests__/conversation-agent-loop.test.ts +99 -3
- package/src/__tests__/conversation-analysis-routes.test.ts +2 -2
- package/src/__tests__/conversation-attachments.test.ts +80 -4
- package/src/__tests__/conversation-confirmation-signals.test.ts +290 -0
- package/src/__tests__/conversation-error.test.ts +70 -0
- package/src/__tests__/conversation-fork-crud.test.ts +17 -0
- package/src/__tests__/conversation-history-web-search.test.ts +12 -4
- package/src/__tests__/conversation-host-access-routes.test.ts +229 -0
- package/src/__tests__/conversation-init.benchmark.test.ts +6 -1
- package/src/__tests__/conversation-inject-context.test.ts +103 -0
- package/src/__tests__/conversation-launcher-skill-regression.test.ts +51 -0
- package/src/__tests__/conversation-list-source.test.ts +145 -0
- package/src/__tests__/conversation-pre-run-repair.test.ts +2 -0
- package/src/__tests__/conversation-provider-retry-repair.test.ts +2 -0
- package/src/__tests__/conversation-queue.test.ts +946 -62
- package/src/__tests__/conversation-routes-disk-view.test.ts +275 -0
- package/src/__tests__/conversation-routes-guardian-reply.test.ts +16 -0
- package/src/__tests__/conversation-routes-slash-commands.test.ts +1 -0
- package/src/__tests__/conversation-runtime-assembly.test.ts +324 -46
- package/src/__tests__/conversation-skill-tools.test.ts +7 -4
- package/src/__tests__/conversation-slash-commands.test.ts +33 -0
- package/src/__tests__/conversation-slash-queue.test.ts +89 -18
- package/src/__tests__/conversation-slash-unknown.test.ts +2 -0
- package/src/__tests__/conversation-starter-routes.test.ts +126 -0
- package/src/__tests__/conversation-starters-cadence.test.ts +161 -0
- package/src/__tests__/conversation-store.test.ts +195 -0
- package/src/__tests__/conversation-tool-setup-batch-authorized.test.ts +226 -0
- package/src/__tests__/conversation-workspace-cache-state.test.ts +193 -0
- package/src/__tests__/conversation-workspace-injection.test.ts +2 -0
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +2 -0
- package/src/__tests__/credential-execution-approval-bridge.test.ts +32 -1
- package/src/__tests__/credential-health-service.test.ts +352 -0
- package/src/__tests__/credential-security-invariants.test.ts +6 -3
- package/src/__tests__/credential-vault-unit.test.ts +383 -7
- package/src/__tests__/credential-vault.test.ts +152 -13
- package/src/__tests__/credentials-cli.test.ts +42 -18
- package/src/__tests__/cross-provider-web-search.test.ts +146 -35
- package/src/__tests__/date-context.test.ts +4 -4
- package/src/__tests__/deterministic-verification-control-plane.test.ts +10 -1
- package/src/__tests__/device-id.test.ts +112 -0
- package/src/__tests__/docker-signing-key-bootstrap.test.ts +167 -4
- package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +1 -3
- package/src/__tests__/email-html-renderer.test.ts +71 -0
- package/src/__tests__/email-invite-adapter.test.ts +36 -32
- package/src/__tests__/embedding-managed-proxy-selection.test.ts +256 -0
- package/src/__tests__/emit-event-signal.test.ts +71 -0
- package/src/__tests__/extension-id-sync-guard.test.ts +222 -0
- package/src/__tests__/fixtures/mock-chrome-extension.ts +386 -0
- package/src/__tests__/gateway-only-enforcement.test.ts +206 -1
- package/src/__tests__/gateway-only-guard.test.ts +2 -0
- package/src/__tests__/gemini-provider.test.ts +66 -2
- package/src/__tests__/get-skill-detail-audit.test.ts +325 -0
- package/src/__tests__/gmail-archive-fallback.test.ts +193 -0
- package/src/__tests__/gmail-archive-gate.test.ts +246 -0
- package/src/__tests__/gmail-preferences.test.ts +117 -0
- package/src/__tests__/guardian-routing-invariants.test.ts +70 -2
- package/src/__tests__/headless-browser-interactions.test.ts +738 -359
- package/src/__tests__/headless-browser-mode.test.ts +614 -0
- package/src/__tests__/headless-browser-navigate.test.ts +528 -49
- package/src/__tests__/headless-browser-read-tools.test.ts +274 -100
- package/src/__tests__/headless-browser-snapshot.test.ts +250 -77
- package/src/__tests__/heartbeat-service.test.ts +70 -17
- package/src/__tests__/home-state-routes.test.ts +162 -0
- package/src/__tests__/host-bash-proxy.test.ts +145 -1
- package/src/__tests__/host-browser-e2e-cloud.test.ts +596 -0
- package/src/__tests__/host-browser-e2e-self-hosted-capability.test.ts +286 -0
- package/src/__tests__/host-browser-e2e-self-hosted.test.ts +374 -0
- package/src/__tests__/host-browser-event-routes.test.ts +350 -0
- package/src/__tests__/host-browser-proxy.test.ts +444 -0
- package/src/__tests__/host-browser-routes.test.ts +198 -0
- package/src/__tests__/host-browser-ws-events-e2e.test.ts +423 -0
- package/src/__tests__/host-cu-proxy.test.ts +166 -1
- package/src/__tests__/host-file-proxy.test.ts +185 -1
- package/src/__tests__/host-file-read-tool.test.ts +52 -0
- package/src/__tests__/host-proxy-interface.test.ts +165 -0
- package/src/__tests__/host-shell-tool.test.ts +1 -11
- package/src/__tests__/http-user-message-parity.test.ts +1 -0
- package/src/__tests__/identity-intro-cache.test.ts +40 -10
- package/src/__tests__/init-feature-flag-overrides.test.ts +38 -112
- package/src/__tests__/integration-status.test.ts +6 -7
- package/src/__tests__/jobs-store-upsert-debounced.test.ts +141 -0
- package/src/__tests__/list-messages-tool-merge.test.ts +37 -12
- package/src/__tests__/llm-context-normalization.test.ts +488 -0
- package/src/__tests__/llm-context-route-provider.test.ts +86 -5
- package/src/__tests__/llm-usage-store.test.ts +363 -0
- package/src/__tests__/mcp-client-auth.test.ts +40 -4
- package/src/__tests__/mcp-health-check.test.ts +10 -3
- package/src/__tests__/media-stream-output.test.ts +555 -0
- package/src/__tests__/media-stream-parser.test.ts +374 -0
- package/src/__tests__/media-stream-server-integration.test.ts +1234 -0
- package/src/__tests__/media-stream-stt-session.test.ts +588 -0
- package/src/__tests__/media-turn-detector.test.ts +440 -0
- package/src/__tests__/message-queue.test.ts +125 -0
- package/src/__tests__/migration-cross-version-compatibility.test.ts +3 -1
- package/src/__tests__/migration-export-http.test.ts +67 -8
- package/src/__tests__/migration-export-streaming.test.ts +66 -0
- package/src/__tests__/migration-import-commit-http.test.ts +109 -7
- package/src/__tests__/migration-import-preflight-http.test.ts +6 -5
- package/src/__tests__/migration-validate-http.test.ts +3 -3
- package/src/__tests__/mock-gateway-ipc.ts +151 -0
- package/src/__tests__/model-intents.test.ts +2 -2
- package/src/__tests__/native-host-marker-sync-guard.test.ts +157 -0
- package/src/__tests__/oauth-apps-routes.test.ts +18 -12
- package/src/__tests__/oauth-cli.test.ts +709 -60
- package/src/__tests__/oauth-connect-orchestrator.test.ts +118 -24
- package/src/__tests__/oauth-provider-seed-logos.test.ts +23 -0
- package/src/__tests__/oauth-provider-serializer.test.ts +147 -10
- package/src/__tests__/oauth-provider-visibility.test.ts +19 -21
- package/src/__tests__/oauth-providers-routes.test.ts +52 -14
- package/src/__tests__/oauth-store.test.ts +1465 -176
- package/src/__tests__/oauth2-gateway-transport.test.ts +460 -26
- package/src/__tests__/onboarding-template-contract.test.ts +81 -70
- package/src/__tests__/openai-provider.test.ts +178 -2
- package/src/__tests__/openai-responses-cutover-guard.test.ts +184 -0
- package/src/__tests__/openai-responses-provider.test.ts +1105 -0
- package/src/__tests__/openrouter-token-estimation.test.ts +100 -0
- package/src/__tests__/outlook-categories.test.ts +1 -1
- package/src/__tests__/outlook-client-automation.test.ts +1 -1
- package/src/__tests__/outlook-compose-tools.test.ts +1 -1
- package/src/__tests__/outlook-email-watcher.test.ts +1 -1
- package/src/__tests__/outlook-follow-up.test.ts +1 -1
- package/src/__tests__/outlook-messaging-provider.test.ts +2 -2
- package/src/__tests__/outlook-trash.test.ts +1 -1
- package/src/__tests__/outlook-unsubscribe.test.ts +32 -3
- package/src/__tests__/permission-checker-host-gate.test.ts +74 -14
- package/src/__tests__/permission-mode.test.ts +28 -56
- package/src/__tests__/persona-resolver.test.ts +251 -0
- package/src/__tests__/platform-bash-auto-approve.test.ts +4 -0
- package/src/__tests__/platform-callback-registration.test.ts +19 -0
- package/src/__tests__/platform.test.ts +92 -1
- package/src/__tests__/post-turn-tool-result-truncation.test.ts +343 -0
- package/src/__tests__/prechat-onboarding-contract.test.ts +267 -0
- package/src/__tests__/pricing.test.ts +174 -0
- package/src/__tests__/proxy-approval-callback.test.ts +18 -0
- package/src/__tests__/qdrant-manager.test.ts +29 -8
- package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +194 -0
- package/src/__tests__/relationship-state-contract.test.ts +175 -0
- package/src/__tests__/relay-server.test.ts +423 -5
- package/src/__tests__/require-fresh-approval.test.ts +40 -1
- package/src/__tests__/sanitize-config-for-transfer.test.ts +132 -0
- package/src/__tests__/schedule-routes.test.ts +162 -0
- package/src/__tests__/search-skills-unified.test.ts +118 -0
- package/src/__tests__/secret-detection-handler.test.ts +84 -0
- package/src/__tests__/secret-ingress-http.test.ts +1 -0
- package/src/__tests__/secret-scanner-executor.test.ts +4 -0
- package/src/__tests__/secure-keys.test.ts +107 -0
- package/src/__tests__/send-endpoint-busy.test.ts +8 -1
- package/src/__tests__/sequence-store.test.ts +1 -1
- package/src/__tests__/server-history-render.test.ts +49 -0
- package/src/__tests__/set-permission-mode.test.ts +13 -250
- package/src/__tests__/settings-routes.test.ts +201 -0
- package/src/__tests__/skill-load-feature-flag.test.ts +1 -0
- package/src/__tests__/skills-file-content-endpoint.test.ts +801 -0
- package/src/__tests__/skills-files-catalog-fallback.test.ts +738 -0
- package/src/__tests__/skills.test.ts +5 -2
- package/src/__tests__/skillssh-files.test.ts +446 -0
- package/src/__tests__/slack-block-formatting.test.ts +110 -0
- package/src/__tests__/slack-channel-config.test.ts +576 -16
- package/src/__tests__/stt-catalog-parity.test.ts +282 -0
- package/src/__tests__/stt-stream-session.test.ts +535 -0
- package/src/__tests__/subagent-detail.test.ts +44 -2
- package/src/__tests__/subagent-disposal.test.ts +1 -0
- package/src/__tests__/subagent-fork-notifications.test.ts +291 -0
- package/src/__tests__/subagent-fork-spawn.test.ts +384 -0
- package/src/__tests__/subagent-manager-notify.test.ts +1 -0
- package/src/__tests__/subagent-notify-parent.test.ts +1 -0
- package/src/__tests__/subagent-spawn-tool-fork.test.ts +411 -0
- package/src/__tests__/subagent-tools.test.ts +1 -0
- package/src/__tests__/subagent-types.test.ts +1 -0
- package/src/__tests__/system-prompt-ask-mode.test.ts +27 -71
- package/src/__tests__/system-prompt.test.ts +184 -27
- package/src/__tests__/task-scheduler.test.ts +32 -6
- package/src/__tests__/telegram-config.test.ts +10 -13
- package/src/__tests__/telephony-stt-routing.test.ts +329 -0
- package/src/__tests__/terminal-tools.test.ts +25 -5
- package/src/__tests__/test-preload.ts +18 -0
- package/src/__tests__/test-support/browser-skill-harness.ts +4 -1
- package/src/__tests__/tool-approval-handler.test.ts +73 -0
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +9 -5
- package/src/__tests__/tool-executor-shell-integration.test.ts +4 -0
- package/src/__tests__/tool-executor.test.ts +33 -24
- package/src/__tests__/tool-result-truncation.test.ts +36 -0
- package/src/__tests__/tool-side-effects-slack-dm.test.ts +22 -0
- package/src/__tests__/top-level-renderer.test.ts +73 -1
- package/src/__tests__/transport-hints-queue.test.ts +14 -29
- package/src/__tests__/trust-store.test.ts +7 -1
- package/src/__tests__/trusted-contact-approval-notifier.test.ts +1 -1
- package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +109 -0
- package/src/__tests__/tts-catalog-parity.test.ts +345 -0
- package/src/__tests__/twilio-routes-twiml.test.ts +512 -114
- package/src/__tests__/twilio-routes.test.ts +376 -0
- package/src/__tests__/unicode.test.ts +293 -0
- package/src/__tests__/update-bulletin-format.test.ts +59 -0
- package/src/__tests__/update-bulletin.test.ts +206 -5
- package/src/__tests__/usage-routes.test.ts +25 -4
- package/src/__tests__/user-reference.test.ts +46 -61
- package/src/__tests__/v2-consent-policy.test.ts +103 -0
- package/src/__tests__/verification-control-plane-policy.test.ts +4 -0
- package/src/__tests__/voice-config-update.test.ts +403 -0
- package/src/__tests__/voice-quality.test.ts +434 -19
- package/src/__tests__/workspace-heartbeat-service.test.ts +7 -0
- package/src/__tests__/workspace-migration-033-stt-service-explicit-config.test.ts +547 -0
- package/src/__tests__/workspace-migration-034-remove-calls-voice-transcription-provider.test.ts +596 -0
- package/src/__tests__/workspace-migration-drop-user-md.test.ts +368 -0
- package/src/__tests__/workspace-migration-meets.test.ts +244 -0
- package/src/__tests__/workspace-migration-seed-device-id.test.ts +14 -20
- package/src/__tests__/workspace-policy.test.ts +2 -0
- package/src/acp/client-handler.ts +30 -4
- package/src/agent/image-optimize.ts +24 -12
- package/src/agent/loop.ts +55 -9
- package/src/approvals/guardian-request-resolvers.ts +21 -15
- package/src/backup/__tests__/backup-key.test.ts +152 -0
- package/src/backup/__tests__/backup-worker.test.ts +767 -0
- package/src/backup/__tests__/list-snapshots.test.ts +87 -0
- package/src/backup/__tests__/local-writer.test.ts +218 -0
- package/src/backup/__tests__/offsite-writer.test.ts +641 -0
- package/src/backup/__tests__/paths.test.ts +300 -0
- package/src/backup/__tests__/restore.test.ts +498 -0
- package/src/backup/__tests__/snapshot-lock.test.ts +352 -0
- package/src/backup/__tests__/stream-crypt.test.ts +228 -0
- package/src/backup/backup-key.ts +137 -0
- package/src/backup/backup-worker.ts +459 -0
- package/src/backup/list-snapshots.ts +147 -0
- package/src/backup/local-writer.ts +133 -0
- package/src/backup/offsite-writer.ts +222 -0
- package/src/backup/paths.ts +226 -0
- package/src/backup/restore.ts +322 -0
- package/src/backup/snapshot-lock.ts +431 -0
- package/src/backup/stream-crypt.ts +263 -0
- package/src/browser-session/__tests__/manager.test.ts +297 -0
- package/src/browser-session/backends/cdp-inspect.ts +30 -0
- package/src/browser-session/backends/extension.ts +26 -0
- package/src/browser-session/backends/local.ts +24 -0
- package/src/browser-session/events.ts +164 -0
- package/src/browser-session/index.ts +27 -0
- package/src/browser-session/manager.ts +159 -0
- package/src/browser-session/types.ts +28 -0
- package/src/bundler/package-resolver.ts +4 -0
- package/src/calls/audio-store.ts +11 -5
- package/src/calls/call-controller.ts +226 -71
- package/src/calls/call-domain.ts +9 -0
- package/src/calls/call-speech-output.ts +190 -0
- package/src/calls/call-transport.ts +77 -0
- package/src/calls/media-stream-audio-transcode.ts +173 -0
- package/src/calls/media-stream-output.ts +660 -0
- package/src/calls/media-stream-parser.ts +300 -0
- package/src/calls/media-stream-protocol.ts +166 -0
- package/src/calls/media-stream-server.ts +592 -0
- package/src/calls/media-stream-stt-session.ts +460 -0
- package/src/calls/media-turn-detector.ts +230 -0
- package/src/calls/relay-server.ts +90 -75
- package/src/calls/resolve-call-tts-provider.ts +136 -0
- package/src/calls/telephony-stt-routing.ts +145 -0
- package/src/calls/tts-call-strategy.ts +161 -0
- package/src/calls/tts-text-sanitizer.ts +32 -16
- package/src/calls/twilio-routes.ts +281 -17
- package/src/calls/voice-quality.ts +78 -35
- package/src/calls/voice-session-bridge.ts +8 -1
- package/src/channels/__tests__/types.test.ts +134 -0
- package/src/channels/types.ts +69 -3
- package/src/cli/__tests__/run-assistant-command.ts +11 -1
- package/src/cli/commands/__tests__/backup.test.ts +1165 -0
- package/src/cli/commands/__tests__/domain-register.test.ts +234 -0
- package/src/cli/commands/__tests__/domain-status.test.ts +132 -0
- package/src/cli/commands/__tests__/email-attachment.test.ts +422 -0
- package/src/cli/commands/__tests__/email-download.test.ts +16 -1
- package/src/cli/commands/__tests__/email-list.test.ts +22 -4
- package/src/cli/commands/__tests__/email-register.test.ts +4 -4
- package/src/cli/commands/__tests__/email-send.test.ts +37 -4
- package/src/cli/commands/__tests__/email-status.test.ts +5 -1
- package/src/cli/commands/__tests__/email-unregister.test.ts +34 -5
- package/src/cli/commands/backup.ts +993 -0
- package/src/cli/commands/conversations.ts +77 -0
- package/src/cli/commands/credentials.ts +3 -4
- package/src/cli/commands/domain.ts +210 -0
- package/src/cli/commands/email.ts +273 -16
- package/src/cli/commands/mcp.ts +16 -4
- package/src/cli/commands/oauth/__tests__/connect.test.ts +56 -44
- package/src/cli/commands/oauth/__tests__/disconnect.test.ts +21 -21
- package/src/cli/commands/oauth/__tests__/mode.test.ts +17 -17
- package/src/cli/commands/oauth/__tests__/ping.test.ts +16 -16
- package/src/cli/commands/oauth/__tests__/providers-delete.test.ts +32 -33
- package/src/cli/commands/oauth/__tests__/providers-register.test.ts +330 -0
- package/src/cli/commands/oauth/__tests__/providers-update.test.ts +117 -12
- package/src/cli/commands/oauth/__tests__/status.test.ts +10 -10
- package/src/cli/commands/oauth/__tests__/token.test.ts +7 -7
- package/src/cli/commands/oauth/apps.ts +7 -4
- package/src/cli/commands/oauth/connect.ts +6 -3
- package/src/cli/commands/oauth/disconnect.ts +1 -1
- package/src/cli/commands/oauth/mode.ts +12 -3
- package/src/cli/commands/oauth/providers.ts +215 -36
- package/src/cli/commands/oauth/shared.ts +7 -6
- package/src/cli/commands/platform/__tests__/callback-routes-list.test.ts +254 -0
- package/src/cli/commands/platform/__tests__/connect.test.ts +6 -0
- package/src/cli/commands/platform/__tests__/disconnect.test.ts +7 -1
- package/src/cli/commands/platform/__tests__/status.test.ts +6 -0
- package/src/cli/commands/platform/index.ts +107 -10
- package/src/cli/commands/usage.ts +10 -9
- package/src/cli/lib/daemon-credential-client.ts +4 -0
- package/src/cli/program.ts +30 -4
- package/src/config/__tests__/backup-schema.test.ts +134 -0
- package/src/config/assistant-feature-flags.ts +61 -62
- package/src/config/bundled-skills/app-builder/SKILL.md +26 -249
- package/src/config/bundled-skills/app-builder/references/CUSTOM_ROUTES.md +141 -0
- package/src/config/bundled-skills/app-builder/references/INTERACTION_HOOKS.md +56 -0
- package/src/config/bundled-skills/app-builder/references/WIDGETS.md +125 -0
- package/src/config/bundled-skills/browser/SKILL.md +30 -5
- package/src/config/bundled-skills/browser/TOOLS.json +123 -0
- package/src/config/bundled-skills/browser/tools/browser-attach.ts +12 -0
- package/src/config/bundled-skills/browser/tools/browser-detach.ts +12 -0
- package/src/config/bundled-skills/browser/tools/browser-status.ts +12 -0
- package/src/config/bundled-skills/browser/tools/browser-wait-for-download.ts +17 -0
- package/src/config/bundled-skills/contacts/SKILL.md +5 -2
- package/src/config/bundled-skills/document/SKILL.md +4 -0
- package/src/config/bundled-skills/gmail/SKILL.md +54 -8
- package/src/config/bundled-skills/gmail/TOOLS.json +33 -3
- package/src/config/bundled-skills/gmail/tools/gmail-archive.ts +116 -9
- package/src/config/bundled-skills/gmail/tools/gmail-outreach-scan.ts +138 -11
- package/src/config/bundled-skills/gmail/tools/gmail-preferences-tool.ts +59 -0
- package/src/config/bundled-skills/gmail/tools/gmail-preferences.ts +82 -0
- package/src/config/bundled-skills/gmail/tools/gmail-sender-digest.ts +113 -17
- package/src/config/bundled-skills/gmail/tools/gmail-unsubscribe.ts +2 -2
- package/src/config/bundled-skills/media-processing/SKILL.md +3 -9
- package/src/config/bundled-skills/media-processing/TOOLS.json +1 -6
- package/src/config/bundled-skills/media-processing/__tests__/audio-transcribe.test.ts +125 -0
- package/src/config/bundled-skills/media-processing/__tests__/extract-keyframes.test.ts +181 -0
- package/src/config/bundled-skills/media-processing/__tests__/preprocess-audio.test.ts +141 -0
- package/src/config/bundled-skills/media-processing/services/audio-transcribe.ts +32 -87
- package/src/config/bundled-skills/media-processing/services/preprocess.ts +8 -4
- package/src/config/bundled-skills/media-processing/tools/extract-keyframes.ts +0 -10
- package/src/config/bundled-skills/messaging/SKILL.md +3 -3
- package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +2 -2
- package/src/config/bundled-skills/outlook/SKILL.md +9 -2
- package/src/config/bundled-skills/outlook/tools/outlook-unsubscribe.ts +2 -2
- package/src/config/bundled-skills/phone-calls/SKILL.md +2 -2
- package/src/config/bundled-skills/phone-calls/references/CONFIG.md +27 -18
- package/src/config/bundled-skills/phone-calls/references/TROUBLESHOOTING.md +3 -3
- package/src/config/bundled-skills/settings/TOOLS.json +3 -3
- package/src/config/bundled-skills/settings/tools/voice-config-update.ts +26 -22
- package/src/config/bundled-skills/slack/SKILL.md +1 -0
- package/src/config/bundled-skills/subagent/SKILL.md +21 -0
- package/src/config/bundled-skills/subagent/TOOLS.json +8 -4
- package/src/config/bundled-skills/tasks/SKILL.md +5 -0
- package/src/config/bundled-skills/transcribe/SKILL.md +9 -14
- package/src/config/bundled-skills/transcribe/TOOLS.json +2 -7
- package/src/config/bundled-skills/transcribe/tools/transcribe-media.test.ts +256 -0
- package/src/config/bundled-skills/transcribe/tools/transcribe-media.ts +38 -188
- package/src/config/bundled-tool-registry.ts +8 -0
- package/src/config/env-registry.ts +38 -0
- package/src/config/env.ts +49 -4
- package/src/config/feature-flag-registry.json +85 -14
- package/src/config/loader.ts +82 -13
- package/src/config/sanitize-for-transfer.ts +47 -0
- package/src/config/schema.ts +81 -15
- package/src/config/schemas/__tests__/stt.test.ts +43 -0
- package/src/config/schemas/analysis.ts +51 -0
- package/src/config/schemas/backup.ts +72 -0
- package/src/config/schemas/calls.ts +1 -26
- package/src/config/schemas/elevenlabs.ts +0 -59
- package/src/config/schemas/filing.ts +47 -7
- package/src/config/schemas/heartbeat.ts +27 -5
- package/src/config/schemas/host-browser.ts +112 -0
- package/src/config/schemas/inference.ts +1 -1
- package/src/config/schemas/memory-lifecycle.ts +14 -2
- package/src/config/schemas/memory-retrieval.ts +103 -0
- package/src/config/schemas/security.ts +0 -6
- package/src/config/schemas/services.ts +52 -0
- package/src/config/schemas/stt.ts +59 -0
- package/src/config/schemas/tts.ts +230 -0
- package/src/config/schemas/updates.ts +14 -0
- package/src/config/skills.ts +4 -0
- package/src/config/types.ts +4 -1
- package/src/contacts/contact-store.ts +56 -11
- package/src/contacts/contacts-write.ts +38 -1
- package/src/context/post-turn-tool-result-truncation.ts +177 -0
- package/src/context/tool-result-truncation.ts +2 -1
- package/src/context/window-manager.ts +61 -10
- package/src/credential-execution/approval-bridge.ts +49 -15
- package/src/credential-execution/executable-discovery.ts +12 -2
- package/src/credential-execution/process-manager.ts +33 -2
- package/src/credential-health/credential-health-service.ts +366 -0
- package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +324 -0
- package/src/daemon/__tests__/conversation-surfaces-launch.test.ts +497 -0
- package/src/daemon/__tests__/conversation-tool-setup.test.ts +195 -0
- package/src/daemon/__tests__/lifecycle-startup-ordering.test.ts +127 -0
- package/src/daemon/app-source-watcher.ts +35 -0
- package/src/daemon/config-watcher.ts +99 -5
- package/src/daemon/context-overflow-approval.ts +5 -0
- package/src/daemon/conversation-agent-loop-handlers.ts +23 -2
- package/src/daemon/conversation-agent-loop.ts +153 -42
- package/src/daemon/conversation-attachments.ts +40 -0
- package/src/daemon/conversation-error.ts +11 -0
- package/src/daemon/conversation-history.ts +40 -6
- package/src/daemon/conversation-launch.ts +220 -0
- package/src/daemon/conversation-lifecycle.ts +59 -9
- package/src/daemon/conversation-messaging.ts +37 -3
- package/src/daemon/conversation-notifiers.ts +5 -0
- package/src/daemon/conversation-process.ts +622 -13
- package/src/daemon/conversation-queue-manager.ts +24 -0
- package/src/daemon/conversation-runtime-assembly.ts +128 -36
- package/src/daemon/conversation-slash.ts +36 -0
- package/src/daemon/conversation-surfaces.ts +131 -40
- package/src/daemon/conversation-tool-setup.ts +99 -8
- package/src/daemon/conversation-usage.ts +7 -4
- package/src/daemon/conversation-workspace.ts +12 -0
- package/src/daemon/conversation.ts +292 -16
- package/src/daemon/date-context.ts +10 -10
- package/src/daemon/first-greeting.ts +3 -2
- package/src/daemon/handlers/config-slack-channel.ts +269 -94
- package/src/daemon/handlers/conversations.ts +13 -141
- package/src/daemon/handlers/shared.ts +80 -0
- package/src/daemon/handlers/skills.ts +483 -44
- package/src/daemon/host-bash-proxy.ts +48 -13
- package/src/daemon/host-browser-proxy.ts +192 -0
- package/src/daemon/host-cu-proxy.ts +36 -11
- package/src/daemon/host-file-proxy.ts +57 -9
- package/src/daemon/lifecycle.ts +179 -28
- package/src/daemon/message-protocol.ts +13 -0
- package/src/daemon/message-types/conversations.ts +89 -14
- package/src/daemon/message-types/home.ts +40 -0
- package/src/daemon/message-types/host-browser.ts +100 -0
- package/src/daemon/message-types/meet.ts +143 -0
- package/src/daemon/message-types/messages.ts +19 -5
- package/src/daemon/message-types/schedules.ts +34 -2
- package/src/daemon/message-types/skills.ts +26 -0
- package/src/daemon/message-types/subagents.ts +2 -0
- package/src/daemon/message-types/surfaces.ts +2 -0
- package/src/daemon/server.ts +439 -14
- package/src/daemon/shutdown-handlers.ts +32 -4
- package/src/daemon/shutdown-registry.ts +40 -0
- package/src/daemon/tool-side-effects.ts +15 -0
- package/src/daemon/transport-hints.ts +5 -24
- package/src/email/html-renderer.ts +76 -0
- package/src/heartbeat/heartbeat-service.ts +93 -7
- package/src/home/__tests__/assistant-feed-authoring.test.ts +156 -0
- package/src/home/__tests__/emit-feed-event.test.ts +169 -0
- package/src/home/__tests__/feed-scheduler.test.ts +194 -0
- package/src/home/__tests__/feed-types.test.ts +275 -0
- package/src/home/__tests__/feed-writer.test.ts +688 -0
- package/src/home/__tests__/phase5-exit-criteria.test.ts +212 -0
- package/src/home/__tests__/platform-gmail-digest.test.ts +222 -0
- package/src/home/__tests__/progress-formula.test.ts +213 -0
- package/src/home/__tests__/relationship-state-writer.test.ts +740 -0
- package/src/home/__tests__/rollup-producer.test.ts +398 -0
- package/src/home/assistant-feed-authoring.ts +124 -0
- package/src/home/emit-feed-event.ts +158 -0
- package/src/home/feed-scheduler.ts +247 -0
- package/src/home/feed-types.ts +181 -0
- package/src/home/feed-writer.ts +469 -0
- package/src/home/platform-gmail-digest.ts +163 -0
- package/src/home/progress-formula.ts +86 -0
- package/src/home/relationship-state-writer.ts +824 -0
- package/src/home/relationship-state.ts +143 -0
- package/src/home/rollup-producer.ts +384 -0
- package/src/hooks/runner.ts +7 -0
- package/src/inbound/platform-callback-registration.ts +30 -20
- package/src/inbound/public-ingress-urls.ts +12 -0
- package/src/instrument.ts +1 -1
- package/src/ipc/__tests__/cli-ipc.test.ts +200 -0
- package/src/ipc/cli-client.ts +151 -0
- package/src/ipc/cli-server.ts +234 -0
- package/src/ipc/gateway-client.ts +180 -0
- package/src/ipc/routes/index.ts +5 -0
- package/src/ipc/routes/wake-conversation.ts +19 -0
- package/src/mcp/client.ts +59 -24
- package/src/memory/__tests__/auto-analysis-enqueue.test.ts +356 -0
- package/src/memory/__tests__/auto-analysis-guard.test.ts +57 -0
- package/src/memory/__tests__/conversation-analyze-job.test.ts +232 -0
- package/src/memory/__tests__/find-analysis-conversation.test.ts +196 -0
- package/src/memory/app-store.ts +31 -1
- package/src/memory/attachments-store.ts +70 -0
- package/src/memory/auto-analysis-enqueue.ts +127 -0
- package/src/memory/auto-analysis-guard.ts +27 -0
- package/src/memory/cleanup-schedule-state.ts +37 -0
- package/src/memory/conversation-analyze-job.ts +73 -0
- package/src/memory/conversation-crud.ts +122 -0
- package/src/memory/conversation-disk-view.ts +7 -0
- package/src/memory/conversation-group-migration.ts +34 -2
- package/src/memory/conversation-queries.ts +6 -5
- package/src/memory/conversation-starters-cadence.ts +76 -0
- package/src/memory/conversation-title-service.ts +5 -2
- package/src/memory/db-init.ts +18 -0
- package/src/memory/db-maintenance.ts +108 -0
- package/src/memory/db.ts +1 -0
- package/src/memory/embedding-backend.test.ts +75 -0
- package/src/memory/embedding-backend.ts +131 -5
- package/src/memory/embedding-gemini.test.ts +54 -0
- package/src/memory/embedding-gemini.ts +20 -9
- package/src/memory/embedding-local.ts +176 -17
- package/src/memory/graph/consolidation.ts +10 -23
- package/src/memory/graph/conversation-graph-memory.ts +15 -0
- package/src/memory/graph/extraction-job.ts +15 -0
- package/src/memory/graph/extraction.test.ts +23 -0
- package/src/memory/graph/extraction.ts +8 -0
- package/src/memory/graph/retriever.ts +67 -40
- package/src/memory/graph/scoring.test.ts +186 -0
- package/src/memory/graph/scoring.ts +31 -1
- package/src/memory/graph/store.test.ts +7 -3
- package/src/memory/graph/store.ts +47 -12
- package/src/memory/graph/tools.ts +1 -1
- package/src/memory/group-crud.ts +6 -1
- package/src/memory/indexer.ts +95 -16
- package/src/memory/job-handlers/cleanup.ts +11 -8
- package/src/memory/job-handlers/conversation-starters.ts +16 -10
- package/src/memory/jobs-store.ts +64 -4
- package/src/memory/jobs-worker.ts +22 -9
- package/src/memory/llm-usage-store.ts +137 -60
- package/src/memory/migrations/213-oauth-providers-scope-separator.ts +13 -0
- package/src/memory/migrations/214-oauth-providers-refresh-url.ts +11 -0
- package/src/memory/migrations/215-oauth-providers-revoke.ts +14 -0
- package/src/memory/migrations/216-oauth-providers-token-auth-method.ts +30 -0
- package/src/memory/migrations/217-conversation-host-access.ts +40 -0
- package/src/memory/migrations/218-oauth-providers-logo-url.ts +11 -0
- package/src/memory/migrations/219-oauth-providers-token-exchange-body-format.ts +15 -0
- package/src/memory/migrations/220-normalize-user-file-by-principal.ts +190 -0
- package/src/memory/migrations/221-conversations-archived-at.ts +16 -0
- package/src/memory/migrations/index.ts +12 -0
- package/src/memory/migrations/registry.ts +16 -0
- package/src/memory/qdrant-manager.ts +43 -16
- package/src/memory/schema/conversations.ts +3 -0
- package/src/memory/schema/oauth.ts +21 -13
- package/src/memory/usage-buckets.ts +396 -0
- package/src/messaging/providers/gmail/client.ts +57 -6
- package/src/messaging/providers/slack/__tests__/adapter-token-routing.test.ts +282 -0
- package/src/messaging/providers/slack/adapter.ts +143 -38
- package/src/messaging/providers/slack/client.ts +16 -0
- package/src/messaging/providers/slack/types.ts +4 -0
- package/src/notifications/decision-engine.ts +3 -3
- package/src/notifications/signal.ts +5 -0
- package/src/oauth/AGENTS.md +76 -0
- package/src/oauth/__tests__/identity-verifier.test.ts +25 -19
- package/src/oauth/__tests__/seed-providers-managed.test.ts +32 -0
- package/src/oauth/byo-connection.test.ts +26 -9
- package/src/oauth/byo-connection.ts +10 -8
- package/src/oauth/connect-orchestrator.ts +25 -21
- package/src/oauth/connect-types.ts +3 -3
- package/src/oauth/connection-resolver.test.ts +17 -4
- package/src/oauth/connection-resolver.ts +22 -18
- package/src/oauth/connection.ts +3 -1
- package/src/oauth/manual-token-connection.ts +13 -13
- package/src/oauth/oauth-store.ts +223 -100
- package/src/oauth/platform-connection.test.ts +101 -3
- package/src/oauth/platform-connection.ts +56 -35
- package/src/oauth/provider-serializer.ts +31 -5
- package/src/oauth/revoke.ts +76 -0
- package/src/oauth/seed-providers.ts +133 -87
- package/src/oauth/token-persistence.ts +1 -1
- package/src/permissions/checker.ts +16 -6
- package/src/permissions/defaults.ts +49 -1
- package/src/permissions/permission-mode.ts +4 -11
- package/src/permissions/prompter.ts +13 -1
- package/src/permissions/trust-store.ts +3 -3
- package/src/permissions/v2-consent-policy.ts +87 -0
- package/src/permissions/workspace-policy.ts +3 -0
- package/src/platform/client.test.ts +10 -0
- package/src/platform/sync-identity.ts +129 -0
- package/src/prompts/persona-resolver.ts +126 -2
- package/src/prompts/system-prompt.ts +76 -38
- package/src/prompts/templates/BOOTSTRAP-REFERENCE.md +3 -65
- package/src/prompts/templates/BOOTSTRAP.md +59 -105
- package/src/prompts/templates/SOUL.md +3 -1
- package/src/prompts/templates/UPDATES.md +12 -0
- package/src/prompts/templates/channels/slack.md +20 -0
- package/src/prompts/update-bulletin-format.ts +26 -9
- package/src/prompts/update-bulletin.ts +34 -23
- package/src/prompts/user-reference.ts +20 -17
- package/src/providers/__tests__/provider-secret-catalog.test.ts +42 -0
- package/src/providers/anthropic/client.ts +157 -60
- package/src/providers/fireworks/client.ts +2 -2
- package/src/providers/gemini/client.ts +9 -1
- package/src/providers/model-catalog.ts +6 -0
- package/src/providers/model-intents.ts +4 -4
- package/src/providers/ollama/client.ts +2 -2
- package/src/providers/openai/chat-completions-provider.ts +474 -0
- package/src/providers/openai/client.ts +25 -440
- package/src/providers/openai/responses-provider.ts +502 -0
- package/src/providers/openrouter/client.ts +101 -4
- package/src/providers/provider-secret-catalog.ts +139 -0
- package/src/providers/registry.ts +2 -2
- package/src/providers/retry.ts +14 -3
- package/src/providers/speech-to-text/__tests__/provider-catalog.test.ts +251 -0
- package/src/providers/speech-to-text/__tests__/resolve.test.ts +828 -0
- package/src/providers/speech-to-text/deepgram-realtime.test.ts +980 -0
- package/src/providers/speech-to-text/deepgram-realtime.ts +767 -0
- package/src/providers/speech-to-text/deepgram.test.ts +332 -0
- package/src/providers/speech-to-text/deepgram.ts +115 -0
- package/src/providers/speech-to-text/google-gemini-live-stream.test.ts +743 -0
- package/src/providers/speech-to-text/google-gemini-live-stream.ts +625 -0
- package/src/providers/speech-to-text/google-gemini.test.ts +226 -0
- package/src/providers/speech-to-text/google-gemini.ts +101 -0
- package/src/providers/speech-to-text/openai-whisper-stream.test.ts +564 -0
- package/src/providers/speech-to-text/openai-whisper-stream.ts +381 -0
- package/src/providers/speech-to-text/openai-whisper.test.ts +1 -37
- package/src/providers/speech-to-text/openai-whisper.ts +63 -33
- package/src/providers/speech-to-text/provider-catalog.ts +306 -0
- package/src/providers/speech-to-text/resolve.ts +386 -6
- package/src/providers/types.ts +10 -1
- package/src/runtime/AGENTS.md +65 -0
- package/src/runtime/__tests__/agent-wake.test.ts +831 -0
- package/src/runtime/__tests__/browser-extension-pair-routes.test.ts +715 -0
- package/src/runtime/__tests__/capability-tokens.test.ts +258 -0
- package/src/runtime/__tests__/chrome-extension-registry.test.ts +518 -0
- package/src/runtime/__tests__/runtime-mode.test.ts +62 -0
- package/src/runtime/__tests__/slack-block-formatting.test.ts +481 -0
- package/src/runtime/agent-wake.ts +512 -0
- package/src/runtime/assistant-event-hub.ts +2 -2
- package/src/runtime/auth/__tests__/guard-tests.test.ts +1 -0
- package/src/runtime/auth/__tests__/middleware.test.ts +116 -1
- package/src/runtime/auth/__tests__/route-policy.test.ts +48 -0
- package/src/runtime/auth/middleware.ts +98 -0
- package/src/runtime/auth/route-policy.ts +33 -9
- package/src/runtime/auth/token-service.ts +56 -1
- package/src/runtime/btw-sidechain.ts +2 -0
- package/src/runtime/capability-tokens.ts +414 -0
- package/src/runtime/channel-approvals.ts +18 -5
- package/src/runtime/channel-invite-transport.ts +1 -1
- package/src/runtime/channel-invite-transports/email.ts +14 -6
- package/src/runtime/channel-readiness-service.ts +12 -22
- package/src/runtime/chrome-extension-registry.ts +368 -0
- package/src/runtime/confirmation-request-guardian-bridge.ts +6 -0
- package/src/runtime/guardian-decision-types.ts +7 -0
- package/src/runtime/http-server.ts +815 -75
- package/src/runtime/http-types.ts +6 -2
- package/src/runtime/migrations/__tests__/rebind-secrets-credentials.test.ts +172 -0
- package/src/runtime/migrations/__tests__/vbundle-builder-credentials.test.ts +276 -0
- package/src/runtime/migrations/__tests__/vbundle-import-credentials.test.ts +198 -0
- package/src/runtime/migrations/__tests__/vbundle-legacy-user-md.test.ts +360 -0
- package/src/runtime/migrations/migration-transport.ts +7 -0
- package/src/runtime/migrations/migration-wizard.ts +23 -2
- package/src/runtime/migrations/rebind-secrets-screen.ts +76 -15
- package/src/runtime/migrations/vbundle-builder.ts +145 -38
- package/src/runtime/migrations/vbundle-import-analyzer.ts +96 -1
- package/src/runtime/migrations/vbundle-importer.ts +89 -5
- package/src/runtime/pending-interactions.ts +18 -13
- package/src/runtime/routes/__tests__/backup-routes.test.ts +967 -0
- package/src/runtime/routes/__tests__/home-feed-routes.test.ts +507 -0
- package/src/runtime/routes/__tests__/migration-import-credential-filter.test.ts +208 -0
- package/src/runtime/routes/__tests__/stt-routes.test.ts +406 -0
- package/src/runtime/routes/__tests__/tts-routes.test.ts +474 -0
- package/src/runtime/routes/__tests__/user-route-dispatcher.test.ts +148 -17
- package/src/runtime/routes/app-management-routes.ts +12 -18
- package/src/runtime/routes/approval-routes.ts +90 -16
- package/src/runtime/routes/attachment-routes.test.ts +9 -3
- package/src/runtime/routes/attachment-routes.ts +216 -17
- package/src/runtime/routes/backup-routes.ts +519 -0
- package/src/runtime/routes/browser-extension-pair-routes.ts +556 -0
- package/src/runtime/routes/btw-routes.ts +8 -6
- package/src/runtime/routes/contact-routes.test.ts +298 -0
- package/src/runtime/routes/contact-routes.ts +132 -5
- package/src/runtime/routes/conversation-analysis-routes.ts +22 -141
- package/src/runtime/routes/conversation-management-routes.ts +223 -0
- package/src/runtime/routes/conversation-routes.ts +598 -103
- package/src/runtime/routes/conversation-starter-routes.ts +78 -16
- package/src/runtime/routes/filing-routes.ts +93 -0
- package/src/runtime/routes/guardian-action-routes.ts +24 -13
- package/src/runtime/routes/home-feed-routes.ts +334 -0
- package/src/runtime/routes/home-state-routes.ts +138 -0
- package/src/runtime/routes/host-browser-routes.ts +268 -0
- package/src/runtime/routes/host-file-routes.ts +9 -1
- package/src/runtime/routes/identity-intro-cache.ts +7 -3
- package/src/runtime/routes/identity-routes.ts +262 -33
- package/src/runtime/routes/inbound-stages/transcribe-audio.test.ts +46 -39
- package/src/runtime/routes/inbound-stages/transcribe-audio.ts +15 -15
- package/src/runtime/routes/integrations/slack/__tests__/channel.test.ts +137 -0
- package/src/runtime/routes/integrations/slack/__tests__/share.test.ts +179 -0
- package/src/runtime/routes/integrations/slack/channel.ts +11 -3
- package/src/runtime/routes/integrations/slack/share.ts +45 -7
- package/src/runtime/routes/llm-context-normalization.ts +303 -0
- package/src/runtime/routes/log-export-routes.ts +42 -22
- package/src/runtime/routes/memory-item-routes.test.ts +3 -2
- package/src/runtime/routes/memory-item-routes.ts +1 -7
- package/src/runtime/routes/migration-routes.ts +122 -2
- package/src/runtime/routes/oauth-apps.ts +15 -17
- package/src/runtime/routes/oauth-providers.ts +4 -0
- package/src/runtime/routes/schedule-routes.ts +24 -11
- package/src/runtime/routes/settings-routes.ts +31 -102
- package/src/runtime/routes/skills-routes.ts +128 -9
- package/src/runtime/routes/stt-routes.ts +233 -0
- package/src/runtime/routes/subagents-routes.ts +14 -10
- package/src/runtime/routes/surface-action-routes.ts +41 -2
- package/src/runtime/routes/tts-routes.ts +108 -24
- package/src/runtime/routes/usage-routes.ts +38 -9
- package/src/runtime/routes/user-route-dispatcher.ts +50 -5
- package/src/runtime/routes/user-routes.ts +13 -1
- package/src/runtime/routes/work-items-routes.ts +8 -1
- package/src/runtime/routes/workspace-routes.test.ts +22 -0
- package/src/runtime/routes/workspace-routes.ts +8 -1
- package/src/runtime/routes/workspace-utils.ts +2 -0
- package/src/runtime/runtime-mode.ts +33 -0
- package/src/runtime/services/__tests__/analyze-conversation.test.ts +444 -0
- package/src/runtime/services/__tests__/analyze-deps-singleton.test.ts +67 -0
- package/src/runtime/services/__tests__/auto-analysis-prompt.test.ts +53 -0
- package/src/runtime/services/__tests__/manual-analysis-prompt.test.ts +41 -0
- package/src/runtime/services/analyze-conversation.ts +344 -0
- package/src/runtime/services/analyze-deps-singleton.ts +32 -0
- package/src/runtime/services/auto-analysis-prompt.ts +55 -0
- package/src/runtime/skill-route-registry.ts +49 -0
- package/src/runtime/slack-block-formatting.ts +437 -10
- package/src/schedule/scheduler.ts +57 -5
- package/src/security/ces-credential-client.ts +20 -0
- package/src/security/ces-rpc-credential-backend.ts +17 -0
- package/src/security/credential-backend.ts +5 -0
- package/src/security/oauth2.ts +68 -29
- package/src/security/secure-keys.ts +143 -27
- package/src/security/token-manager.ts +31 -10
- package/src/sequence/engine.ts +23 -0
- package/src/sequence/types.ts +1 -1
- package/src/skills/catalog-files.ts +554 -0
- package/src/skills/category-inference.ts +122 -0
- package/src/skills/clawhub-files.ts +213 -0
- package/src/skills/clawhub.ts +84 -23
- package/src/skills/skill-file-provider.ts +40 -0
- package/src/skills/skillssh-files.ts +395 -0
- package/src/skills/skillssh-registry.ts +4 -4
- package/src/stt/__tests__/daemon-batch-transcriber.test.ts +392 -0
- package/src/stt/__tests__/types.test.ts +89 -0
- package/src/stt/daemon-batch-transcriber.ts +195 -0
- package/src/stt/stt-stream-session.ts +499 -0
- package/src/stt/types.ts +330 -0
- package/src/stt/wav-encoder.test.ts +373 -0
- package/src/stt/wav-encoder.ts +175 -0
- package/src/subagent/manager.ts +169 -40
- package/src/subagent/types.ts +19 -0
- package/src/tools/apps/executors.ts +11 -2
- package/src/tools/browser/__tests__/auth-detector.test.ts +202 -108
- package/src/tools/browser/__tests__/browser-mode.test.ts +119 -0
- package/src/tools/browser/__tests__/browser-status.test.ts +123 -0
- package/src/tools/browser/auth-detector.ts +43 -12
- package/src/tools/browser/browser-execution.ts +1787 -342
- package/src/tools/browser/browser-manager.ts +81 -12
- package/src/tools/browser/browser-mode-constants.ts +12 -0
- package/src/tools/browser/browser-mode.ts +92 -0
- package/src/tools/browser/browser-status-constants.ts +33 -0
- package/src/tools/browser/cdp-client/__tests__/accessibility-snapshot.test.ts +318 -0
- package/src/tools/browser/cdp-client/__tests__/cdp-dom-helpers.test.ts +1175 -0
- package/src/tools/browser/cdp-client/__tests__/cdp-inspect-client.test.ts +1263 -0
- package/src/tools/browser/cdp-client/__tests__/extension-cdp-client.test.ts +359 -0
- package/src/tools/browser/cdp-client/__tests__/factory.test.ts +1993 -0
- package/src/tools/browser/cdp-client/__tests__/fixtures/ax-tree-nested-frames.json +64 -0
- package/src/tools/browser/cdp-client/__tests__/fixtures/ax-tree-simple.json +69 -0
- package/src/tools/browser/cdp-client/__tests__/local-cdp-client.test.ts +310 -0
- package/src/tools/browser/cdp-client/__tests__/types.test.ts +96 -0
- package/src/tools/browser/cdp-client/accessibility-snapshot.ts +387 -0
- package/src/tools/browser/cdp-client/cdp-dom-helpers.ts +695 -0
- package/src/tools/browser/cdp-client/cdp-inspect/__tests__/discovery.test.ts +1007 -0
- package/src/tools/browser/cdp-client/cdp-inspect/__tests__/ws-transport.test.ts +580 -0
- package/src/tools/browser/cdp-client/cdp-inspect/discovery.ts +744 -0
- package/src/tools/browser/cdp-client/cdp-inspect/ws-transport.ts +579 -0
- package/src/tools/browser/cdp-client/cdp-inspect-client.ts +868 -0
- package/src/tools/browser/cdp-client/errors.ts +49 -0
- package/src/tools/browser/cdp-client/extension-cdp-client.ts +148 -0
- package/src/tools/browser/cdp-client/factory.ts +914 -0
- package/src/tools/browser/cdp-client/index.ts +28 -0
- package/src/tools/browser/cdp-client/local-cdp-client.ts +187 -0
- package/src/tools/browser/cdp-client/types.ts +120 -0
- package/src/tools/credentials/vault.ts +35 -6
- package/src/tools/filesystem/edit.ts +1 -1
- package/src/tools/filesystem/list.ts +1 -1
- package/src/tools/filesystem/read.ts +1 -1
- package/src/tools/filesystem/write.ts +2 -1
- package/src/tools/host-filesystem/edit.ts +1 -1
- package/src/tools/host-filesystem/read.ts +12 -15
- package/src/tools/host-filesystem/write.ts +1 -1
- package/src/tools/host-terminal/host-shell.ts +21 -16
- package/src/tools/network/web-fetch.ts +5 -2
- package/src/tools/network/web-search.ts +5 -2
- package/src/tools/permission-checker.ts +77 -82
- package/src/tools/registry.ts +0 -2
- package/src/tools/secret-detection-handler.ts +34 -0
- package/src/tools/shared/filesystem/image-read.ts +61 -40
- package/src/tools/shared/shell-output.ts +3 -1
- package/src/tools/side-effects.ts +2 -0
- package/src/tools/skills/sandbox-runner.ts +3 -2
- package/src/tools/subagent/spawn.ts +47 -3
- package/src/tools/subagent/status.ts +2 -0
- package/src/tools/system/register.ts +2 -16
- package/src/tools/terminal/safe-env.ts +15 -0
- package/src/tools/terminal/shell.ts +36 -20
- package/src/tools/tool-approval-handler.ts +48 -2
- package/src/tools/tool-manifest.ts +21 -0
- package/src/tools/types.ts +19 -0
- package/src/tools/ui-surface/definitions.ts +6 -1
- package/src/tts/__tests__/provider-adapters.test.ts +834 -0
- package/src/tts/__tests__/provider-catalog-consistency.test.ts +196 -0
- package/src/tts/__tests__/provider-catalog.test.ts +183 -0
- package/src/tts/__tests__/provider-registry.test.ts +90 -0
- package/src/tts/provider-catalog.ts +201 -0
- package/src/tts/provider-registry.ts +73 -0
- package/src/tts/providers/deepgram-provider.ts +219 -0
- package/src/tts/providers/elevenlabs-provider.ts +211 -0
- package/src/tts/providers/fish-audio-provider.ts +183 -0
- package/src/tts/providers/index.ts +42 -0
- package/src/tts/providers/register-builtins.ts +130 -0
- package/src/tts/synthesize-text.ts +110 -0
- package/src/tts/tts-config-resolver.ts +78 -0
- package/src/tts/types.ts +153 -0
- package/src/types/onboarding-context.ts +7 -0
- package/src/util/abort-reasons.ts +58 -0
- package/src/util/device-id.ts +32 -16
- package/src/util/errors.ts +9 -1
- package/src/util/platform.ts +63 -24
- package/src/util/pricing.ts +66 -3
- package/src/util/spawn.ts +1 -1
- package/src/util/truncate.ts +4 -2
- package/src/util/unicode.ts +201 -0
- package/src/version.ts +19 -24
- package/src/watcher/engine.ts +23 -0
- package/src/watcher/watcher-store.ts +31 -0
- package/src/workspace/migrations/003-seed-device-id.ts +9 -3
- package/src/workspace/migrations/017-seed-persona-dirs.ts +68 -4
- package/src/workspace/migrations/029-seed-pkb.ts +1 -1
- package/src/workspace/migrations/031-drop-user-md.ts +317 -0
- package/src/workspace/migrations/031-llm-log-retention-zero-to-null.ts +73 -0
- package/src/workspace/migrations/032-tts-provider-unification.ts +227 -0
- package/src/workspace/migrations/033-stt-service-explicit-config.ts +122 -0
- package/src/workspace/migrations/034-remove-calls-voice-transcription-provider.ts +215 -0
- package/src/workspace/migrations/035-seed-slack-channel-persona.ts +50 -0
- package/src/workspace/migrations/036-update-pkb-index-bar.ts +37 -0
- package/src/workspace/migrations/037-create-meets-dir.ts +61 -0
- package/src/workspace/migrations/registry.ts +16 -0
- package/src/workspace/top-level-renderer.ts +31 -1
- package/src/workspace/turn-commit.ts +31 -0
- package/src/__tests__/chrome-cdp.test.ts +0 -419
- package/src/__tests__/email-cli.test.ts +0 -297
- package/src/__tests__/email-service-config-fallback.test.ts +0 -102
- package/src/__tests__/permission-mode-sse.test.ts +0 -418
- package/src/__tests__/permission-mode-store.test.ts +0 -277
- package/src/browser-extension-relay/protocol.ts +0 -63
- package/src/browser-extension-relay/server.ts +0 -203
- package/src/cli/commands/browser-relay.ts +0 -536
- package/src/config/schemas/sandbox.ts +0 -14
- package/src/email/guardrails.ts +0 -221
- package/src/email/provider.ts +0 -117
- package/src/email/providers/agentmail.ts +0 -361
- package/src/email/providers/index.ts +0 -65
- package/src/email/service.ts +0 -384
- package/src/email/types.ts +0 -126
- package/src/permissions/permission-mode-store.ts +0 -180
- package/src/prompts/templates/USER.md +0 -13
- package/src/providers/speech-to-text/types.ts +0 -17
- package/src/tools/browser/chrome-cdp.ts +0 -239
- package/src/tools/system/set-permission-mode.ts +0 -103
|
@@ -0,0 +1,914 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type BrowserBackend,
|
|
3
|
+
BrowserSessionManager,
|
|
4
|
+
type CdpCommand,
|
|
5
|
+
type CdpResult,
|
|
6
|
+
createCdpInspectBackend,
|
|
7
|
+
createExtensionBackend,
|
|
8
|
+
createLocalBackend,
|
|
9
|
+
} from "../../../browser-session/index.js";
|
|
10
|
+
import { getConfig } from "../../../config/loader.js";
|
|
11
|
+
import { getLogger } from "../../../util/logger.js";
|
|
12
|
+
import type { ToolContext } from "../../types.js";
|
|
13
|
+
import { createCdpInspectClient } from "./cdp-inspect-client.js";
|
|
14
|
+
import { CdpError } from "./errors.js";
|
|
15
|
+
import { createExtensionCdpClient } from "./extension-cdp-client.js";
|
|
16
|
+
import { createLocalCdpClient } from "./local-cdp-client.js";
|
|
17
|
+
import type {
|
|
18
|
+
AttemptDiagnostic,
|
|
19
|
+
BackendCandidate,
|
|
20
|
+
BrowserMode,
|
|
21
|
+
CdpClient,
|
|
22
|
+
CdpClientKind,
|
|
23
|
+
ScopedCdpClient,
|
|
24
|
+
} from "./types.js";
|
|
25
|
+
|
|
26
|
+
const log = getLogger("cdp-factory");
|
|
27
|
+
|
|
28
|
+
// ---------------------------------------------------------------------------
|
|
29
|
+
// Desktop-auto cdp-inspect cooldown tracker
|
|
30
|
+
// ---------------------------------------------------------------------------
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Module-level timestamp (epoch ms) of the last transport-level failure for
|
|
34
|
+
* a desktop-auto cdp-inspect attempt. While `Date.now() - _desktopAutoCooldownSince`
|
|
35
|
+
* is less than the configured `desktopAuto.cooldownMs`, the factory skips the
|
|
36
|
+
* automatic cdp-inspect candidate and goes straight to the local backend.
|
|
37
|
+
*
|
|
38
|
+
* **Process-global scope**: this is a module-level singleton that affects ALL
|
|
39
|
+
* conversations in the process. A cdp-inspect failure on any conversation
|
|
40
|
+
* suppresses desktop-auto probes for every conversation in this daemon until
|
|
41
|
+
* the cooldown expires. This is intentional -- the local loopback CDP
|
|
42
|
+
* endpoint is per-machine, not per-conversation, so a failure on one
|
|
43
|
+
* conversation implies all others would fail the same way.
|
|
44
|
+
*
|
|
45
|
+
* Reset to 0 when the cooldown expires or when manually cleared via
|
|
46
|
+
* {@link _resetDesktopAutoCooldown} (for testing).
|
|
47
|
+
*/
|
|
48
|
+
let _desktopAutoCooldownSince = 0;
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Record a cooldown after a desktop-auto cdp-inspect transport failure.
|
|
52
|
+
* Called by {@link maybeRecordDesktopAutoCooldown} in production; also
|
|
53
|
+
* exported directly for use in tests.
|
|
54
|
+
*/
|
|
55
|
+
export function recordDesktopAutoCooldown(): void {
|
|
56
|
+
_desktopAutoCooldownSince = Date.now();
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Whether the desktop-auto cdp-inspect cooldown is currently active.
|
|
61
|
+
* Returns `true` if a failure was recorded and the configured cooldown
|
|
62
|
+
* window has not yet elapsed.
|
|
63
|
+
*/
|
|
64
|
+
export function isDesktopAutoCooldownActive(cooldownMs: number): boolean {
|
|
65
|
+
if (_desktopAutoCooldownSince === 0 || cooldownMs <= 0) return false;
|
|
66
|
+
return Date.now() - _desktopAutoCooldownSince < cooldownMs;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Reset the desktop-auto cooldown state. Exported for testing only.
|
|
71
|
+
*/
|
|
72
|
+
export function _resetDesktopAutoCooldown(): void {
|
|
73
|
+
_desktopAutoCooldownSince = 0;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Get the raw cooldown-since timestamp. Exported for testing only.
|
|
78
|
+
*/
|
|
79
|
+
export function _getDesktopAutoCooldownSince(): number {
|
|
80
|
+
return _desktopAutoCooldownSince;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// ---------------------------------------------------------------------------
|
|
84
|
+
// Public API
|
|
85
|
+
// ---------------------------------------------------------------------------
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Options for {@link getCdpClient}. All fields are optional — omitting
|
|
89
|
+
* them preserves the existing auto-mode behavior.
|
|
90
|
+
*/
|
|
91
|
+
export interface GetCdpClientOptions {
|
|
92
|
+
/**
|
|
93
|
+
* Backend mode preference. When omitted or `"auto"`, the factory
|
|
94
|
+
* uses the existing priority-ordered fallback chain. When set to a
|
|
95
|
+
* specific backend kind, the factory pins to that single backend
|
|
96
|
+
* and disables failover.
|
|
97
|
+
*/
|
|
98
|
+
mode?: BrowserMode;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Select the appropriate CdpClient implementation for a tool
|
|
103
|
+
* invocation based on the ToolContext and config. Three backends are
|
|
104
|
+
* considered in priority order:
|
|
105
|
+
*
|
|
106
|
+
* 1. **Extension** -- When `context.hostBrowserProxy` is set AND
|
|
107
|
+
* `hostBrowserProxy.isAvailable()` returns `true` (i.e. the
|
|
108
|
+
* proxy exists and the client is actually connected). This
|
|
109
|
+
* prevents selecting the extension transport when the proxy
|
|
110
|
+
* object exists but the underlying WebSocket is disconnected.
|
|
111
|
+
* 2. **cdp-inspect** -- When `hostBrowser.cdpInspect.enabled` is
|
|
112
|
+
* `true` in config, construct a `CdpInspectClient` that attaches
|
|
113
|
+
* to an already-running Chrome via the DevTools JSON protocol.
|
|
114
|
+
* On macOS, cdp-inspect is also included automatically when
|
|
115
|
+
* `desktopAuto.enabled` is true (the default), even when the
|
|
116
|
+
* top-level `enabled` flag is false.
|
|
117
|
+
* 3. **Local** -- Default. Drives Playwright's CDPSession against
|
|
118
|
+
* the sacrificial-profile browser managed by browserManager.
|
|
119
|
+
*
|
|
120
|
+
* When `options.mode` is set to a specific backend kind, the factory
|
|
121
|
+
* builds exactly one candidate and disables failover. If the pinned
|
|
122
|
+
* backend is unavailable (e.g. pinned `extension` without an
|
|
123
|
+
* available host browser proxy), the factory throws a typed
|
|
124
|
+
* `CdpError` with `transport_error` code and a diagnostic indicating
|
|
125
|
+
* the precondition that was not met.
|
|
126
|
+
*
|
|
127
|
+
* The factory builds an ordered candidate list and returns a
|
|
128
|
+
* {@link ScopedCdpClient} with per-invocation failover semantics:
|
|
129
|
+
*
|
|
130
|
+
* - On the first `send()`, the top-ranked candidate is selected and
|
|
131
|
+
* its backend is materialised.
|
|
132
|
+
* - If the first command fails with a **transport-level** error
|
|
133
|
+
* (`transport_error`), the factory tears down the failed backend
|
|
134
|
+
* and retries the same command against the next candidate.
|
|
135
|
+
* - **CDP protocol errors** (`cdp_error`) do NOT trigger failover --
|
|
136
|
+
* they indicate the browser understood the command and rejected it,
|
|
137
|
+
* so hopping transports would not help.
|
|
138
|
+
* - After the first successful CDP command, the backend becomes
|
|
139
|
+
* **sticky** for the remainder of the invocation. Subsequent
|
|
140
|
+
* commands always route through the same backend so multi-command
|
|
141
|
+
* tool flows do not hop transports mid-step.
|
|
142
|
+
*
|
|
143
|
+
* IMPORTANT: the returned client is per-invocation. Tools MUST call
|
|
144
|
+
* `dispose()` in a finally block. Dispose tears down the manager's
|
|
145
|
+
* session and the underlying CDP client. Disposing an extension-backed
|
|
146
|
+
* client does NOT dispose the underlying HostBrowserProxy -- that is
|
|
147
|
+
* owned by the conversation.
|
|
148
|
+
*/
|
|
149
|
+
export function getCdpClient(
|
|
150
|
+
context: ToolContext,
|
|
151
|
+
options?: GetCdpClientOptions,
|
|
152
|
+
): ScopedCdpClient {
|
|
153
|
+
const mode: BrowserMode = options?.mode ?? "auto";
|
|
154
|
+
const candidates =
|
|
155
|
+
mode === "auto"
|
|
156
|
+
? buildCandidateList(context)
|
|
157
|
+
: buildPinnedCandidateList(context, mode);
|
|
158
|
+
|
|
159
|
+
log.debug(
|
|
160
|
+
{
|
|
161
|
+
conversationId: context.conversationId,
|
|
162
|
+
mode,
|
|
163
|
+
candidates: candidates.map((c) => ({ kind: c.kind, reason: c.reason })),
|
|
164
|
+
},
|
|
165
|
+
"CDP factory: built candidate list",
|
|
166
|
+
);
|
|
167
|
+
|
|
168
|
+
return buildChainedClient(context.conversationId, candidates, mode);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// ---------------------------------------------------------------------------
|
|
172
|
+
// Pinned candidate list construction
|
|
173
|
+
// ---------------------------------------------------------------------------
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Build a single-element candidate list for a pinned backend mode.
|
|
177
|
+
* Throws a typed `CdpError` with structured diagnostics when the
|
|
178
|
+
* requested backend's preconditions are not met.
|
|
179
|
+
*
|
|
180
|
+
* Exported for testing.
|
|
181
|
+
*/
|
|
182
|
+
export function buildPinnedCandidateList(
|
|
183
|
+
context: ToolContext,
|
|
184
|
+
mode: Exclude<BrowserMode, "auto">,
|
|
185
|
+
): BackendCandidate[] {
|
|
186
|
+
const { conversationId, hostBrowserProxy } = context;
|
|
187
|
+
|
|
188
|
+
switch (mode) {
|
|
189
|
+
case "extension": {
|
|
190
|
+
if (!hostBrowserProxy || !hostBrowserProxy.isAvailable()) {
|
|
191
|
+
const reason = !hostBrowserProxy
|
|
192
|
+
? "no host browser proxy provisioned for this conversation"
|
|
193
|
+
: "host browser proxy exists but is not connected";
|
|
194
|
+
throw new CdpError(
|
|
195
|
+
"transport_error",
|
|
196
|
+
`Pinned mode "extension" unavailable: ${reason}`,
|
|
197
|
+
{
|
|
198
|
+
attemptDiagnostics: [
|
|
199
|
+
{
|
|
200
|
+
candidateKind: "extension",
|
|
201
|
+
inclusionReason: `pinned mode: extension`,
|
|
202
|
+
stage: "candidate_selection",
|
|
203
|
+
errorCode: "transport_error",
|
|
204
|
+
errorMessage: reason,
|
|
205
|
+
},
|
|
206
|
+
],
|
|
207
|
+
},
|
|
208
|
+
);
|
|
209
|
+
}
|
|
210
|
+
return [
|
|
211
|
+
{
|
|
212
|
+
kind: "extension",
|
|
213
|
+
reason: "pinned mode: extension",
|
|
214
|
+
create() {
|
|
215
|
+
const client = createExtensionCdpClient(
|
|
216
|
+
hostBrowserProxy,
|
|
217
|
+
conversationId,
|
|
218
|
+
);
|
|
219
|
+
const backend = createExtensionBackend({
|
|
220
|
+
isAvailable: () => true,
|
|
221
|
+
sendCdp: (command, signal) =>
|
|
222
|
+
dispatchThroughClient(client, command, signal),
|
|
223
|
+
dispose: () => client.dispose(),
|
|
224
|
+
});
|
|
225
|
+
return { client, backend };
|
|
226
|
+
},
|
|
227
|
+
},
|
|
228
|
+
];
|
|
229
|
+
}
|
|
230
|
+
case "cdp-inspect": {
|
|
231
|
+
const cdpInspectConfig = getConfig().hostBrowser.cdpInspect;
|
|
232
|
+
return [
|
|
233
|
+
{
|
|
234
|
+
kind: "cdp-inspect",
|
|
235
|
+
reason: "pinned mode: cdp-inspect",
|
|
236
|
+
create() {
|
|
237
|
+
const client = createCdpInspectClient(conversationId, {
|
|
238
|
+
host: cdpInspectConfig.host,
|
|
239
|
+
port: cdpInspectConfig.port,
|
|
240
|
+
discoveryTimeoutMs: cdpInspectConfig.probeTimeoutMs,
|
|
241
|
+
});
|
|
242
|
+
const backend = createCdpInspectBackend({
|
|
243
|
+
isAvailable: () => true,
|
|
244
|
+
sendCdp: (command, signal) =>
|
|
245
|
+
dispatchThroughClient(client, command, signal),
|
|
246
|
+
dispose: () => client.dispose(),
|
|
247
|
+
});
|
|
248
|
+
return { client, backend };
|
|
249
|
+
},
|
|
250
|
+
},
|
|
251
|
+
];
|
|
252
|
+
}
|
|
253
|
+
case "local": {
|
|
254
|
+
return [
|
|
255
|
+
{
|
|
256
|
+
kind: "local",
|
|
257
|
+
reason: "pinned mode: local",
|
|
258
|
+
create() {
|
|
259
|
+
const client = createLocalCdpClient(conversationId);
|
|
260
|
+
const backend = createLocalBackend({
|
|
261
|
+
isAvailable: () => true,
|
|
262
|
+
sendCdp: (command, signal) =>
|
|
263
|
+
dispatchThroughClient(client, command, signal),
|
|
264
|
+
dispose: () => client.dispose(),
|
|
265
|
+
});
|
|
266
|
+
return { client, backend };
|
|
267
|
+
},
|
|
268
|
+
},
|
|
269
|
+
];
|
|
270
|
+
}
|
|
271
|
+
default: {
|
|
272
|
+
// Exhaustive check — if new modes are added, TypeScript will
|
|
273
|
+
// flag this as an error.
|
|
274
|
+
const _exhaustive: never = mode;
|
|
275
|
+
throw new Error(`Unknown pinned mode: ${_exhaustive}`);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// ---------------------------------------------------------------------------
|
|
281
|
+
// Candidate list construction (auto mode)
|
|
282
|
+
// ---------------------------------------------------------------------------
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* Build an ordered list of backend candidates from the tool context
|
|
286
|
+
* and config. Candidates are evaluated lazily -- `create()` is only
|
|
287
|
+
* called when the candidate is actually selected.
|
|
288
|
+
*
|
|
289
|
+
* Exported for testing.
|
|
290
|
+
*/
|
|
291
|
+
export function buildCandidateList(context: ToolContext): BackendCandidate[] {
|
|
292
|
+
const { conversationId, hostBrowserProxy } = context;
|
|
293
|
+
const candidates: BackendCandidate[] = [];
|
|
294
|
+
|
|
295
|
+
// 1. Extension -- preferred when a chrome-extension is bound AND
|
|
296
|
+
// the proxy reports it is connected. Checking isAvailable()
|
|
297
|
+
// prevents selecting the extension transport when the proxy
|
|
298
|
+
// object exists (e.g. it was provisioned at conversation start)
|
|
299
|
+
// but the client has since disconnected.
|
|
300
|
+
if (hostBrowserProxy && hostBrowserProxy.isAvailable()) {
|
|
301
|
+
candidates.push({
|
|
302
|
+
kind: "extension",
|
|
303
|
+
reason: "hostBrowserProxy present and available",
|
|
304
|
+
create() {
|
|
305
|
+
const client = createExtensionCdpClient(
|
|
306
|
+
hostBrowserProxy,
|
|
307
|
+
conversationId,
|
|
308
|
+
);
|
|
309
|
+
const backend = createExtensionBackend({
|
|
310
|
+
isAvailable: () => true,
|
|
311
|
+
sendCdp: (command, signal) =>
|
|
312
|
+
dispatchThroughClient(client, command, signal),
|
|
313
|
+
dispose: () => client.dispose(),
|
|
314
|
+
});
|
|
315
|
+
return { client, backend };
|
|
316
|
+
},
|
|
317
|
+
});
|
|
318
|
+
} else if (hostBrowserProxy) {
|
|
319
|
+
log.debug(
|
|
320
|
+
{ conversationId },
|
|
321
|
+
"CDP factory: hostBrowserProxy present but not available, skipping extension candidate",
|
|
322
|
+
);
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
// 2. cdp-inspect -- opt-in via config OR desktop-auto for macOS turns.
|
|
326
|
+
const cdpInspectConfig = getConfig().hostBrowser.cdpInspect;
|
|
327
|
+
if (cdpInspectConfig.enabled) {
|
|
328
|
+
// Explicitly enabled in config -- always include regardless of platform.
|
|
329
|
+
candidates.push({
|
|
330
|
+
kind: "cdp-inspect",
|
|
331
|
+
reason: "cdpInspect enabled in config",
|
|
332
|
+
create() {
|
|
333
|
+
const client = createCdpInspectClient(conversationId, {
|
|
334
|
+
host: cdpInspectConfig.host,
|
|
335
|
+
port: cdpInspectConfig.port,
|
|
336
|
+
discoveryTimeoutMs: cdpInspectConfig.probeTimeoutMs,
|
|
337
|
+
});
|
|
338
|
+
const backend = createCdpInspectBackend({
|
|
339
|
+
isAvailable: () => true,
|
|
340
|
+
sendCdp: (command, signal) =>
|
|
341
|
+
dispatchThroughClient(client, command, signal),
|
|
342
|
+
dispose: () => client.dispose(),
|
|
343
|
+
});
|
|
344
|
+
return { client, backend };
|
|
345
|
+
},
|
|
346
|
+
});
|
|
347
|
+
} else if (
|
|
348
|
+
context.transportInterface === "macos" &&
|
|
349
|
+
cdpInspectConfig.desktopAuto.enabled
|
|
350
|
+
) {
|
|
351
|
+
// macOS desktop-auto: include cdp-inspect as a candidate unless:
|
|
352
|
+
// (a) the hostBrowserProxy exists but is temporarily unavailable
|
|
353
|
+
// (extension transport expected but transiently disconnected --
|
|
354
|
+
// inserting cdp-inspect here would cause a silent takeover), or
|
|
355
|
+
// (b) the cooldown from a recent failure is still active.
|
|
356
|
+
//
|
|
357
|
+
// When no hostBrowserProxy is present at all (extension not
|
|
358
|
+
// provisioned for this conversation), cdp-inspect remains available
|
|
359
|
+
// as a fallback per the desktop-auto contract.
|
|
360
|
+
if (hostBrowserProxy && !hostBrowserProxy.isAvailable()) {
|
|
361
|
+
log.debug(
|
|
362
|
+
{ conversationId },
|
|
363
|
+
"CDP factory: desktop-auto cdp-inspect skipped (extension transport expected but temporarily unavailable)",
|
|
364
|
+
);
|
|
365
|
+
} else {
|
|
366
|
+
const { cooldownMs } = cdpInspectConfig.desktopAuto;
|
|
367
|
+
if (isDesktopAutoCooldownActive(cooldownMs)) {
|
|
368
|
+
log.debug(
|
|
369
|
+
{
|
|
370
|
+
conversationId,
|
|
371
|
+
cooldownMs,
|
|
372
|
+
cooldownSince: _desktopAutoCooldownSince,
|
|
373
|
+
},
|
|
374
|
+
"CDP factory: desktop-auto cdp-inspect skipped (cooldown active)",
|
|
375
|
+
);
|
|
376
|
+
} else {
|
|
377
|
+
candidates.push({
|
|
378
|
+
kind: "cdp-inspect",
|
|
379
|
+
reason: "desktopAuto: macOS turn, cdp-inspect auto-attempted",
|
|
380
|
+
create() {
|
|
381
|
+
const client = createCdpInspectClient(conversationId, {
|
|
382
|
+
host: cdpInspectConfig.host,
|
|
383
|
+
port: cdpInspectConfig.port,
|
|
384
|
+
discoveryTimeoutMs: cdpInspectConfig.probeTimeoutMs,
|
|
385
|
+
wsConnectTimeoutMs: cdpInspectConfig.probeTimeoutMs,
|
|
386
|
+
});
|
|
387
|
+
const backend = createCdpInspectBackend({
|
|
388
|
+
isAvailable: () => true,
|
|
389
|
+
sendCdp: (command, signal) =>
|
|
390
|
+
dispatchThroughClient(client, command, signal),
|
|
391
|
+
dispose: () => client.dispose(),
|
|
392
|
+
});
|
|
393
|
+
return { client, backend };
|
|
394
|
+
},
|
|
395
|
+
});
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
// 3. Local -- always present as the final fallback.
|
|
401
|
+
candidates.push({
|
|
402
|
+
kind: "local",
|
|
403
|
+
reason: "default Playwright fallback",
|
|
404
|
+
create() {
|
|
405
|
+
const client = createLocalCdpClient(conversationId);
|
|
406
|
+
const backend = createLocalBackend({
|
|
407
|
+
isAvailable: () => true,
|
|
408
|
+
sendCdp: (command, signal) =>
|
|
409
|
+
dispatchThroughClient(client, command, signal),
|
|
410
|
+
dispose: () => client.dispose(),
|
|
411
|
+
});
|
|
412
|
+
return { client, backend };
|
|
413
|
+
},
|
|
414
|
+
});
|
|
415
|
+
|
|
416
|
+
return candidates;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
// ---------------------------------------------------------------------------
|
|
420
|
+
// Chained client with per-invocation failover
|
|
421
|
+
// ---------------------------------------------------------------------------
|
|
422
|
+
|
|
423
|
+
/**
|
|
424
|
+
* Build a {@link ScopedCdpClient} that walks the candidate list on
|
|
425
|
+
* the first command, failing over on transport-level errors, and
|
|
426
|
+
* becomes sticky after the first successful CDP command.
|
|
427
|
+
*
|
|
428
|
+
* Exported for testing.
|
|
429
|
+
*/
|
|
430
|
+
export function buildChainedClient(
|
|
431
|
+
conversationId: string,
|
|
432
|
+
candidates: BackendCandidate[],
|
|
433
|
+
mode: BrowserMode = "auto",
|
|
434
|
+
): ScopedCdpClient {
|
|
435
|
+
if (candidates.length === 0) {
|
|
436
|
+
throw new Error("CDP factory: no backend candidates available");
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
/** Active backend state -- populated after first successful command. */
|
|
440
|
+
let active: {
|
|
441
|
+
kind: CdpClientKind;
|
|
442
|
+
manager: BrowserSessionManager;
|
|
443
|
+
sessionId: string;
|
|
444
|
+
} | null = null;
|
|
445
|
+
|
|
446
|
+
/** Set to true after the first successful CDP command. */
|
|
447
|
+
let sticky = false;
|
|
448
|
+
|
|
449
|
+
let disposed = false;
|
|
450
|
+
|
|
451
|
+
/**
|
|
452
|
+
* Track all materialised backends so dispose() can tear them all
|
|
453
|
+
* down, even ones that were tried and failed before the sticky
|
|
454
|
+
* backend was established.
|
|
455
|
+
*/
|
|
456
|
+
const materialisedManagers: BrowserSessionManager[] = [];
|
|
457
|
+
|
|
458
|
+
/**
|
|
459
|
+
* The kind of the currently active (or last attempted) backend.
|
|
460
|
+
* Before the first send this reflects the first candidate; after
|
|
461
|
+
* the sticky backend is established it reflects the chosen kind.
|
|
462
|
+
*/
|
|
463
|
+
let currentKind: CdpClientKind = candidates[0].kind;
|
|
464
|
+
|
|
465
|
+
const scopedClient: ScopedCdpClient = {
|
|
466
|
+
get kind(): CdpClientKind {
|
|
467
|
+
return active?.kind ?? currentKind;
|
|
468
|
+
},
|
|
469
|
+
conversationId,
|
|
470
|
+
|
|
471
|
+
async send<T = unknown>(
|
|
472
|
+
method: string,
|
|
473
|
+
params?: Record<string, unknown>,
|
|
474
|
+
signal?: AbortSignal,
|
|
475
|
+
): Promise<T> {
|
|
476
|
+
if (disposed) {
|
|
477
|
+
throw new CdpError("disposed", "CdpClient already disposed", {
|
|
478
|
+
cdpMethod: method,
|
|
479
|
+
cdpParams: params,
|
|
480
|
+
});
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
// Fast path: backend is already sticky -- route directly.
|
|
484
|
+
if (sticky && active) {
|
|
485
|
+
const command: CdpCommand = { method, params };
|
|
486
|
+
const envelope = await active.manager.send(
|
|
487
|
+
active.sessionId,
|
|
488
|
+
command,
|
|
489
|
+
signal,
|
|
490
|
+
);
|
|
491
|
+
return unwrapResult<T>(envelope, method, params);
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
// Slow path: walk the candidate list with failover.
|
|
495
|
+
return sendWithFailover<T>(
|
|
496
|
+
candidates,
|
|
497
|
+
materialisedManagers,
|
|
498
|
+
method,
|
|
499
|
+
params,
|
|
500
|
+
signal,
|
|
501
|
+
(established) => {
|
|
502
|
+
active = established;
|
|
503
|
+
sticky = true;
|
|
504
|
+
currentKind = established.kind;
|
|
505
|
+
},
|
|
506
|
+
() => disposed,
|
|
507
|
+
conversationId,
|
|
508
|
+
mode,
|
|
509
|
+
);
|
|
510
|
+
},
|
|
511
|
+
|
|
512
|
+
dispose(): void {
|
|
513
|
+
if (disposed) return;
|
|
514
|
+
disposed = true;
|
|
515
|
+
for (const m of materialisedManagers) {
|
|
516
|
+
m.disposeAll();
|
|
517
|
+
}
|
|
518
|
+
materialisedManagers.length = 0;
|
|
519
|
+
active = null;
|
|
520
|
+
},
|
|
521
|
+
};
|
|
522
|
+
|
|
523
|
+
return scopedClient;
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
/**
|
|
527
|
+
* Walk the candidate list attempting to execute a single CDP command.
|
|
528
|
+
* Transport-level failures trigger failover to the next candidate;
|
|
529
|
+
* CDP protocol errors propagate immediately.
|
|
530
|
+
*
|
|
531
|
+
* When a desktop-auto cdp-inspect candidate fails with a transport
|
|
532
|
+
* error, the factory records a cooldown so subsequent calls skip the
|
|
533
|
+
* probe until the window expires.
|
|
534
|
+
*
|
|
535
|
+
* In auto mode, each attempted candidate is recorded as an
|
|
536
|
+
* {@link AttemptDiagnostic}. When fallback occurs, a production-visible
|
|
537
|
+
* log is emitted with the full candidate sequence and per-candidate
|
|
538
|
+
* failure reasons. If all candidates are exhausted, the diagnostics
|
|
539
|
+
* are attached to the thrown {@link CdpError}.
|
|
540
|
+
*/
|
|
541
|
+
async function sendWithFailover<T>(
|
|
542
|
+
candidates: BackendCandidate[],
|
|
543
|
+
materialisedManagers: BrowserSessionManager[],
|
|
544
|
+
method: string,
|
|
545
|
+
params: Record<string, unknown> | undefined,
|
|
546
|
+
signal: AbortSignal | undefined,
|
|
547
|
+
onEstablished: (active: {
|
|
548
|
+
kind: CdpClientKind;
|
|
549
|
+
manager: BrowserSessionManager;
|
|
550
|
+
sessionId: string;
|
|
551
|
+
}) => void,
|
|
552
|
+
isDisposed: () => boolean,
|
|
553
|
+
conversationId: string,
|
|
554
|
+
mode: BrowserMode,
|
|
555
|
+
): Promise<T> {
|
|
556
|
+
let lastError: CdpError | undefined;
|
|
557
|
+
const diagnostics: AttemptDiagnostic[] = [];
|
|
558
|
+
|
|
559
|
+
for (let i = 0; i < candidates.length; i++) {
|
|
560
|
+
const candidate = candidates[i];
|
|
561
|
+
if (isDisposed()) {
|
|
562
|
+
throw new CdpError("disposed", "CdpClient already disposed", {
|
|
563
|
+
cdpMethod: method,
|
|
564
|
+
cdpParams: params,
|
|
565
|
+
});
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
log.debug(
|
|
569
|
+
{
|
|
570
|
+
conversationId,
|
|
571
|
+
candidateKind: candidate.kind,
|
|
572
|
+
candidateIndex: i,
|
|
573
|
+
method,
|
|
574
|
+
},
|
|
575
|
+
"CDP factory: attempting candidate",
|
|
576
|
+
);
|
|
577
|
+
|
|
578
|
+
let backend: BrowserBackend;
|
|
579
|
+
try {
|
|
580
|
+
const created = candidate.create();
|
|
581
|
+
backend = created.backend;
|
|
582
|
+
} catch (err) {
|
|
583
|
+
// Backend construction failed -- treat as transport error and
|
|
584
|
+
// try the next candidate.
|
|
585
|
+
const errorMessage = `Backend ${candidate.kind} construction failed: ${err instanceof Error ? err.message : String(err)}`;
|
|
586
|
+
log.debug(
|
|
587
|
+
{ conversationId, candidateKind: candidate.kind, err },
|
|
588
|
+
"CDP factory: candidate construction failed, trying next",
|
|
589
|
+
);
|
|
590
|
+
lastError = new CdpError("transport_error", errorMessage, {
|
|
591
|
+
cdpMethod: method,
|
|
592
|
+
cdpParams: params,
|
|
593
|
+
underlying: err,
|
|
594
|
+
});
|
|
595
|
+
diagnostics.push({
|
|
596
|
+
candidateKind: candidate.kind,
|
|
597
|
+
inclusionReason: candidate.reason,
|
|
598
|
+
stage: "construction",
|
|
599
|
+
errorCode: "transport_error",
|
|
600
|
+
errorMessage,
|
|
601
|
+
});
|
|
602
|
+
maybeRecordDesktopAutoCooldown(candidate);
|
|
603
|
+
|
|
604
|
+
// Emit production-visible fallback log in auto mode
|
|
605
|
+
if (mode === "auto" && i < candidates.length - 1) {
|
|
606
|
+
log.warn(
|
|
607
|
+
{
|
|
608
|
+
conversationId,
|
|
609
|
+
failedCandidate: candidate.kind,
|
|
610
|
+
nextCandidate: candidates[i + 1].kind,
|
|
611
|
+
attemptedSoFar: diagnostics.map((d) => ({
|
|
612
|
+
kind: d.candidateKind,
|
|
613
|
+
stage: d.stage,
|
|
614
|
+
errorCode: d.errorCode,
|
|
615
|
+
errorMessage: d.errorMessage,
|
|
616
|
+
})),
|
|
617
|
+
},
|
|
618
|
+
"CDP factory: auto-mode fallback triggered",
|
|
619
|
+
);
|
|
620
|
+
}
|
|
621
|
+
continue;
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
const manager = new BrowserSessionManager({ backends: [backend] });
|
|
625
|
+
materialisedManagers.push(manager);
|
|
626
|
+
const session = manager.createSession();
|
|
627
|
+
|
|
628
|
+
const command: CdpCommand = { method, params };
|
|
629
|
+
let envelope: CdpResult;
|
|
630
|
+
try {
|
|
631
|
+
envelope = await manager.send(session.id, command, signal);
|
|
632
|
+
} catch (err) {
|
|
633
|
+
// Manager-level errors (unknown session, no available backend)
|
|
634
|
+
// are transport-level problems -- try the next candidate.
|
|
635
|
+
const errorMessage = `Backend ${candidate.kind} send threw: ${err instanceof Error ? err.message : String(err)}`;
|
|
636
|
+
log.debug(
|
|
637
|
+
{ conversationId, candidateKind: candidate.kind, err },
|
|
638
|
+
"CDP factory: candidate send threw, trying next",
|
|
639
|
+
);
|
|
640
|
+
manager.disposeAll();
|
|
641
|
+
lastError = new CdpError("transport_error", errorMessage, {
|
|
642
|
+
cdpMethod: method,
|
|
643
|
+
cdpParams: params,
|
|
644
|
+
underlying: err,
|
|
645
|
+
});
|
|
646
|
+
diagnostics.push({
|
|
647
|
+
candidateKind: candidate.kind,
|
|
648
|
+
inclusionReason: candidate.reason,
|
|
649
|
+
stage: "send",
|
|
650
|
+
errorCode: "transport_error",
|
|
651
|
+
errorMessage,
|
|
652
|
+
discoveryCode: extractDiscoveryCode(err),
|
|
653
|
+
});
|
|
654
|
+
maybeRecordDesktopAutoCooldown(candidate);
|
|
655
|
+
|
|
656
|
+
// Emit production-visible fallback log in auto mode
|
|
657
|
+
if (mode === "auto" && i < candidates.length - 1) {
|
|
658
|
+
log.warn(
|
|
659
|
+
{
|
|
660
|
+
conversationId,
|
|
661
|
+
failedCandidate: candidate.kind,
|
|
662
|
+
nextCandidate: candidates[i + 1].kind,
|
|
663
|
+
attemptedSoFar: diagnostics.map((d) => ({
|
|
664
|
+
kind: d.candidateKind,
|
|
665
|
+
stage: d.stage,
|
|
666
|
+
errorCode: d.errorCode,
|
|
667
|
+
errorMessage: d.errorMessage,
|
|
668
|
+
})),
|
|
669
|
+
},
|
|
670
|
+
"CDP factory: auto-mode fallback triggered",
|
|
671
|
+
);
|
|
672
|
+
}
|
|
673
|
+
continue;
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
// Inspect the envelope for errors. Transport-level errors trigger
|
|
677
|
+
// failover; CDP protocol errors propagate immediately.
|
|
678
|
+
if (envelope.error) {
|
|
679
|
+
const cdpError = extractCdpError(envelope, method, params);
|
|
680
|
+
|
|
681
|
+
if (isTransportFailover(cdpError) && i < candidates.length - 1) {
|
|
682
|
+
log.debug(
|
|
683
|
+
{
|
|
684
|
+
conversationId,
|
|
685
|
+
candidateKind: candidate.kind,
|
|
686
|
+
errorCode: cdpError.code,
|
|
687
|
+
errorMessage: cdpError.message,
|
|
688
|
+
},
|
|
689
|
+
"CDP factory: transport-level failure, failing over to next candidate",
|
|
690
|
+
);
|
|
691
|
+
manager.disposeAll();
|
|
692
|
+
lastError = cdpError;
|
|
693
|
+
diagnostics.push({
|
|
694
|
+
candidateKind: candidate.kind,
|
|
695
|
+
inclusionReason: candidate.reason,
|
|
696
|
+
stage: "send",
|
|
697
|
+
errorCode: cdpError.code,
|
|
698
|
+
errorMessage: cdpError.message,
|
|
699
|
+
discoveryCode: extractDiscoveryCode(cdpError.underlying),
|
|
700
|
+
});
|
|
701
|
+
maybeRecordDesktopAutoCooldown(candidate);
|
|
702
|
+
|
|
703
|
+
// Emit production-visible fallback log in auto mode
|
|
704
|
+
if (mode === "auto") {
|
|
705
|
+
log.warn(
|
|
706
|
+
{
|
|
707
|
+
conversationId,
|
|
708
|
+
failedCandidate: candidate.kind,
|
|
709
|
+
nextCandidate: candidates[i + 1].kind,
|
|
710
|
+
attemptedSoFar: diagnostics.map((d) => ({
|
|
711
|
+
kind: d.candidateKind,
|
|
712
|
+
stage: d.stage,
|
|
713
|
+
errorCode: d.errorCode,
|
|
714
|
+
errorMessage: d.errorMessage,
|
|
715
|
+
})),
|
|
716
|
+
},
|
|
717
|
+
"CDP factory: auto-mode fallback triggered",
|
|
718
|
+
);
|
|
719
|
+
}
|
|
720
|
+
continue;
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
// Either a CDP protocol error or we've exhausted candidates --
|
|
724
|
+
// propagate the error as-is, attaching diagnostics.
|
|
725
|
+
diagnostics.push({
|
|
726
|
+
candidateKind: candidate.kind,
|
|
727
|
+
inclusionReason: candidate.reason,
|
|
728
|
+
stage: "send",
|
|
729
|
+
errorCode: cdpError.code,
|
|
730
|
+
errorMessage: cdpError.message,
|
|
731
|
+
discoveryCode: extractDiscoveryCode(cdpError.underlying),
|
|
732
|
+
});
|
|
733
|
+
throw new CdpError(cdpError.code, cdpError.message, {
|
|
734
|
+
cdpMethod: cdpError.cdpMethod,
|
|
735
|
+
cdpParams: cdpError.cdpParams,
|
|
736
|
+
underlying: cdpError.underlying,
|
|
737
|
+
attemptDiagnostics: diagnostics.length > 0 ? diagnostics : undefined,
|
|
738
|
+
});
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
// Success! Establish this backend as the sticky choice.
|
|
742
|
+
diagnostics.push({
|
|
743
|
+
candidateKind: candidate.kind,
|
|
744
|
+
inclusionReason: candidate.reason,
|
|
745
|
+
stage: "success",
|
|
746
|
+
});
|
|
747
|
+
|
|
748
|
+
// If there were prior failed candidates in auto mode, log the
|
|
749
|
+
// full sequence for observability.
|
|
750
|
+
if (mode === "auto" && diagnostics.length > 1) {
|
|
751
|
+
log.warn(
|
|
752
|
+
{
|
|
753
|
+
conversationId,
|
|
754
|
+
stickyCandidate: candidate.kind,
|
|
755
|
+
attemptSequence: diagnostics.map((d) => ({
|
|
756
|
+
kind: d.candidateKind,
|
|
757
|
+
stage: d.stage,
|
|
758
|
+
errorCode: d.errorCode,
|
|
759
|
+
errorMessage: d.errorMessage,
|
|
760
|
+
})),
|
|
761
|
+
},
|
|
762
|
+
"CDP factory: auto-mode fallback completed, backend established after retries",
|
|
763
|
+
);
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
log.debug(
|
|
767
|
+
{ conversationId, candidateKind: candidate.kind, method },
|
|
768
|
+
"CDP factory: candidate succeeded, backend is now sticky",
|
|
769
|
+
);
|
|
770
|
+
onEstablished({ kind: candidate.kind, manager, sessionId: session.id });
|
|
771
|
+
return envelope.result as T;
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
// All candidates exhausted -- throw the last transport error with
|
|
775
|
+
// full attempt diagnostics attached.
|
|
776
|
+
throw lastError
|
|
777
|
+
? new CdpError(lastError.code, lastError.message, {
|
|
778
|
+
cdpMethod: lastError.cdpMethod,
|
|
779
|
+
cdpParams: lastError.cdpParams,
|
|
780
|
+
underlying: lastError.underlying,
|
|
781
|
+
attemptDiagnostics: diagnostics.length > 0 ? diagnostics : undefined,
|
|
782
|
+
})
|
|
783
|
+
: new CdpError("transport_error", "All backend candidates exhausted", {
|
|
784
|
+
cdpMethod: method,
|
|
785
|
+
cdpParams: params,
|
|
786
|
+
attemptDiagnostics: diagnostics.length > 0 ? diagnostics : undefined,
|
|
787
|
+
});
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
/**
|
|
791
|
+
* If the failed candidate is a desktop-auto cdp-inspect attempt,
|
|
792
|
+
* record the cooldown so subsequent calls skip the probe.
|
|
793
|
+
*/
|
|
794
|
+
function maybeRecordDesktopAutoCooldown(candidate: BackendCandidate): void {
|
|
795
|
+
if (
|
|
796
|
+
candidate.kind === "cdp-inspect" &&
|
|
797
|
+
candidate.reason.startsWith("desktopAuto:")
|
|
798
|
+
) {
|
|
799
|
+
log.debug(
|
|
800
|
+
"CDP factory: recording desktop-auto cdp-inspect cooldown after transport failure",
|
|
801
|
+
);
|
|
802
|
+
recordDesktopAutoCooldown();
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
/**
|
|
807
|
+
* Determine whether a CdpError should trigger failover to the next
|
|
808
|
+
* candidate. Only transport-level failures are eligible -- CDP
|
|
809
|
+
* protocol errors indicate the browser understood the command and
|
|
810
|
+
* rejected it, so retrying on a different transport would not help.
|
|
811
|
+
*/
|
|
812
|
+
function isTransportFailover(err: CdpError): boolean {
|
|
813
|
+
return err.code === "transport_error";
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
// ---------------------------------------------------------------------------
|
|
817
|
+
// Helpers (shared with the old implementation)
|
|
818
|
+
// ---------------------------------------------------------------------------
|
|
819
|
+
|
|
820
|
+
/**
|
|
821
|
+
* Extract a CdpError from a CdpResult envelope that carries an error.
|
|
822
|
+
*/
|
|
823
|
+
function extractCdpError(
|
|
824
|
+
envelope: CdpResult,
|
|
825
|
+
method: string,
|
|
826
|
+
params?: Record<string, unknown>,
|
|
827
|
+
): CdpError {
|
|
828
|
+
if (envelope.error?.data instanceof CdpError) {
|
|
829
|
+
return envelope.error.data;
|
|
830
|
+
}
|
|
831
|
+
return new CdpError(
|
|
832
|
+
"cdp_error",
|
|
833
|
+
envelope.error?.message ?? "Unknown CDP error",
|
|
834
|
+
{
|
|
835
|
+
cdpMethod: method,
|
|
836
|
+
cdpParams: params,
|
|
837
|
+
underlying: envelope.error,
|
|
838
|
+
},
|
|
839
|
+
);
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
/**
|
|
843
|
+
* Adapter that makes an existing `CdpClient` look like a
|
|
844
|
+
* `BrowserBackend.send`. Converts thrown CdpErrors back into a
|
|
845
|
+
* `CdpResult` envelope with an `error` payload so the manager does
|
|
846
|
+
* not need to know about our thrown-error convention, then the
|
|
847
|
+
* envelope is unwrapped again on the way out of the managed client.
|
|
848
|
+
*
|
|
849
|
+
* The per-command `command.sessionId` (populated by the manager from
|
|
850
|
+
* a session's opaque `targetId`) is intentionally not forwarded to
|
|
851
|
+
* the underlying CdpClient today -- both LocalCdpClient and
|
|
852
|
+
* ExtensionCdpClient take their CDP sessionId at construction time
|
|
853
|
+
* and tools run one client per invocation. The seam is preserved so
|
|
854
|
+
* a future multi-target backend can read it off the CdpCommand.
|
|
855
|
+
*/
|
|
856
|
+
async function dispatchThroughClient(
|
|
857
|
+
client: CdpClient,
|
|
858
|
+
command: CdpCommand,
|
|
859
|
+
signal: AbortSignal | undefined,
|
|
860
|
+
): Promise<CdpResult> {
|
|
861
|
+
try {
|
|
862
|
+
const result = await client.send(command.method, command.params, signal);
|
|
863
|
+
return { result };
|
|
864
|
+
} catch (err) {
|
|
865
|
+
if (err instanceof CdpError) {
|
|
866
|
+
// Preserve the original CdpError so extractCdpError can
|
|
867
|
+
// re-throw it verbatim. CdpResult's error channel is opaque
|
|
868
|
+
// to the manager, so stashing the instance under `data` is safe.
|
|
869
|
+
return {
|
|
870
|
+
error: {
|
|
871
|
+
code: -1,
|
|
872
|
+
message: err.message,
|
|
873
|
+
data: err,
|
|
874
|
+
},
|
|
875
|
+
};
|
|
876
|
+
}
|
|
877
|
+
throw err;
|
|
878
|
+
}
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
/**
|
|
882
|
+
* Unwrap a CdpResult envelope into the raw CDP result `T` or throw
|
|
883
|
+
* the underlying CdpError. If the envelope carries an error but the
|
|
884
|
+
* `data` is not a CdpError (e.g. a future backend surfaces a JSON-RPC
|
|
885
|
+
* error envelope directly), synthesize a transport_error CdpError so
|
|
886
|
+
* call sites keep their uniform error handling.
|
|
887
|
+
*/
|
|
888
|
+
function unwrapResult<T>(
|
|
889
|
+
envelope: CdpResult,
|
|
890
|
+
method: string,
|
|
891
|
+
params?: Record<string, unknown>,
|
|
892
|
+
): T {
|
|
893
|
+
if (envelope.error) {
|
|
894
|
+
throw extractCdpError(envelope, method, params);
|
|
895
|
+
}
|
|
896
|
+
return envelope.result as T;
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
/**
|
|
900
|
+
* Attempt to extract a discovery-level error code from an underlying
|
|
901
|
+
* error. Some CdpInspectClient errors embed a discovery code (e.g.
|
|
902
|
+
* "ECONNREFUSED", "DISCOVERY_TIMEOUT") that is useful for diagnostics.
|
|
903
|
+
*/
|
|
904
|
+
function extractDiscoveryCode(underlying: unknown): string | undefined {
|
|
905
|
+
if (underlying == null) return undefined;
|
|
906
|
+
if (typeof underlying === "object" && "code" in underlying) {
|
|
907
|
+
const code = (underlying as Record<string, unknown>).code;
|
|
908
|
+
if (typeof code === "string") return code;
|
|
909
|
+
}
|
|
910
|
+
if (underlying instanceof Error && "cause" in underlying) {
|
|
911
|
+
return extractDiscoveryCode(underlying.cause);
|
|
912
|
+
}
|
|
913
|
+
return undefined;
|
|
914
|
+
}
|