@vellumai/assistant 0.7.0 → 0.7.1
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 +6 -7
- package/Dockerfile +1 -0
- package/README.md +2 -2
- package/__tests__/permissions/gateway-threshold-reader.test.ts +79 -139
- package/bun.lock +3 -0
- package/docs/architecture/security.md +18 -16
- package/knip.json +1 -0
- package/node_modules/@vellumai/skill-host-contracts/__tests__/client.test.ts +1 -5
- package/node_modules/@vellumai/skill-host-contracts/src/assistant-event.ts +0 -5
- package/node_modules/@vellumai/skill-host-contracts/src/client.ts +10 -16
- package/node_modules/@vellumai/skill-host-contracts/src/skill-host.ts +1 -9
- package/node_modules/@vellumai/skill-host-contracts/src/tool-types.ts +12 -12
- package/node_modules/@vellumai/slack-text/bun.lock +24 -0
- package/node_modules/@vellumai/slack-text/package.json +18 -0
- package/node_modules/@vellumai/slack-text/src/index.test.ts +153 -0
- package/node_modules/@vellumai/slack-text/src/index.ts +235 -0
- package/node_modules/@vellumai/slack-text/tsconfig.json +20 -0
- package/openapi.yaml +294 -107
- package/package.json +4 -2
- package/scripts/generate-openapi.ts +16 -111
- package/src/__tests__/agent-wake-override-profile.test.ts +23 -1
- package/src/__tests__/anthropic-provider.test.ts +56 -13
- package/src/__tests__/app-conversation-ids-backfill.test.ts +278 -0
- package/src/__tests__/app-conversation-ids.test.ts +151 -0
- package/src/__tests__/approval-cascade.test.ts +0 -15
- package/src/__tests__/approval-routes-http.test.ts +6 -17
- package/src/__tests__/assistant-event-hub.test.ts +126 -77
- package/src/__tests__/assistant-event.test.ts +0 -5
- package/src/__tests__/assistant-events-sse-hardening.test.ts +37 -15
- package/src/__tests__/assistant-feature-flags-integration.test.ts +0 -29
- package/src/__tests__/background-shell-host-bash.test.ts +34 -43
- package/src/__tests__/call-controller.test.ts +1 -1
- package/src/__tests__/call-site-routing-provider.test.ts +193 -0
- package/src/__tests__/channel-approval-routes.test.ts +10 -296
- package/src/__tests__/channel-approvals.test.ts +25 -17
- package/src/__tests__/channel-guardian.test.ts +100 -146
- package/src/__tests__/checker.test.ts +20 -34
- package/src/__tests__/compact-event-conversation-id-guard.test.ts +50 -0
- package/src/__tests__/compaction-events.test.ts +2 -0
- package/src/__tests__/config-schema.test.ts +6 -48
- package/src/__tests__/config-watcher.test.ts +12 -0
- package/src/__tests__/connection-policy.test.ts +1 -52
- package/src/__tests__/contacts-write.test.ts +2 -64
- package/src/__tests__/context-image-dimensions.test.ts +1 -1
- package/src/__tests__/context-search-memory-source.test.ts +120 -1
- package/src/__tests__/context-search-memory-v2-source.test.ts +383 -0
- package/src/__tests__/context-search-pkb-source.test.ts +49 -0
- package/src/__tests__/context-search-workspace-source.test.ts +9 -22
- package/src/__tests__/context-window-manager.test.ts +46 -0
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +2 -0
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +102 -29
- package/src/__tests__/conversation-agent-loop.test.ts +980 -13
- package/src/__tests__/conversation-analysis-routes.test.ts +12 -10
- package/src/__tests__/conversation-attention-telegram.test.ts +11 -3
- package/src/__tests__/conversation-confirmation-signals.test.ts +0 -291
- package/src/__tests__/conversation-history-web-search.test.ts +4 -3
- package/src/__tests__/conversation-inference-profile-route.test.ts +12 -23
- package/src/__tests__/conversation-lifecycle.test.ts +4 -4
- package/src/__tests__/conversation-process-callsite.test.ts +79 -2
- package/src/__tests__/conversation-queue.test.ts +3 -8
- package/src/__tests__/conversation-routes-disk-view.test.ts +1 -161
- package/src/__tests__/conversation-routes-guardian-reply.test.ts +0 -32
- package/src/__tests__/conversation-routes-slash-commands.test.ts +75 -66
- package/src/__tests__/conversation-runtime-assembly.test.ts +257 -3
- package/src/__tests__/conversation-slash-commands.test.ts +24 -4
- package/src/__tests__/conversation-slash-queue.test.ts +2 -0
- package/src/__tests__/conversation-speed-override.test.ts +0 -3
- package/src/__tests__/conversation-starter-routes.test.ts +79 -2
- package/src/__tests__/conversation-surfaces-standalone-payloads.test.ts +12 -5
- package/src/__tests__/conversation-surfaces-standalone.test.ts +18 -14
- package/src/__tests__/conversation-surfaces-state-update.test.ts +3 -2
- package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +8 -46
- package/src/__tests__/conversation-usage.test.ts +253 -3
- package/src/__tests__/credential-execution-shell-lockdown.test.ts +0 -39
- package/src/__tests__/credential-health-service.test.ts +68 -0
- package/src/__tests__/credential-security-e2e.test.ts +4 -3
- package/src/__tests__/credential-security-invariants.test.ts +1 -5
- package/src/__tests__/credential-token-resolver.test.ts +180 -0
- package/src/__tests__/cu-unified-flow.test.ts +33 -16
- package/src/__tests__/daemon-assistant-events.test.ts +34 -21
- package/src/__tests__/daemon-credential-client.test.ts +4 -1
- package/src/__tests__/db-connection-isolation.test.ts +125 -0
- package/src/__tests__/db-migration-rollback.test.ts +101 -0
- package/src/__tests__/db-slack-compaction-watermark-migration.test.ts +169 -0
- package/src/__tests__/deterministic-verification-control-plane.test.ts +7 -80
- package/src/__tests__/document-conversations.test.ts +332 -0
- package/src/__tests__/embedding-managed-proxy-selection.test.ts +2 -2
- package/src/__tests__/emit-event-signal.test.ts +4 -6
- package/src/__tests__/events-client-registration.test.ts +193 -49
- package/src/__tests__/filing-service.test.ts +58 -7
- package/src/__tests__/first-greeting.test.ts +156 -150
- package/src/__tests__/fixtures/mock-chrome-extension.ts +108 -66
- package/src/__tests__/get-skill-detail-audit.test.ts +3 -8
- package/src/__tests__/guardian-binding-drift-heal.test.ts +1 -1
- package/src/__tests__/guardian-dispatch.test.ts +1 -1
- package/src/__tests__/guardian-grant-minting.test.ts +7 -2
- package/src/__tests__/guardian-routing-invariants.test.ts +7 -2
- package/src/__tests__/guardian-routing-state.test.ts +1 -1
- package/src/__tests__/handlers-skills-memory-v2-reseed.test.ts +32 -11
- package/src/__tests__/handlers-user-message-approval-consumption.test.ts +2 -83
- package/src/__tests__/headless-browser-mode.test.ts +4 -9
- package/src/__tests__/headless-browser-navigate.test.ts +21 -20
- package/src/__tests__/heartbeat-service.test.ts +289 -7
- package/src/__tests__/helpers/channel-test-adapter.ts +2 -2
- package/src/__tests__/helpers/create-guardian-binding.ts +91 -0
- package/src/__tests__/host-bash-proxy.test.ts +46 -122
- package/src/__tests__/host-browser-e2e-cloud.test.ts +36 -497
- package/src/__tests__/host-browser-e2e-self-hosted-capability.test.ts +26 -96
- package/src/__tests__/host-browser-proxy.test.ts +111 -185
- package/src/__tests__/host-browser-routes.test.ts +45 -75
- package/src/__tests__/host-browser-ws-events-e2e.test.ts +26 -30
- package/src/__tests__/host-cu-proxy.test.ts +56 -111
- package/src/__tests__/host-file-proxy.test.ts +44 -98
- package/src/__tests__/host-file-read-tool.test.ts +42 -21
- package/src/__tests__/host-shell-tool.test.ts +33 -68
- package/src/__tests__/host-transfer-pending-interactions.test.ts +2 -18
- package/src/__tests__/host-transfer-proxy.test.ts +43 -53
- package/src/__tests__/http-user-message-parity.test.ts +0 -6
- package/src/__tests__/inbound-slack-persistence.test.ts +31 -0
- package/src/__tests__/injector-chain.test.ts +10 -5
- package/src/__tests__/injector-pkb-v2-silenced.test.ts +124 -0
- package/src/__tests__/inline-command-runner.test.ts +0 -66
- package/src/__tests__/inline-skill-load-permissions.test.ts +0 -2
- package/src/__tests__/install-skill-routing.test.ts +1 -13
- package/src/__tests__/llm-callsite-catalog.test.ts +34 -0
- package/src/__tests__/llm-catalog-parity.test.ts +90 -0
- package/src/__tests__/llm-context-resolution.test.ts +180 -0
- package/src/__tests__/llm-resolver.test.ts +80 -12
- package/src/__tests__/llm-usage-store.test.ts +269 -4
- package/src/__tests__/log-export-routes.test.ts +89 -0
- package/src/__tests__/managed-profile-guard.test.ts +225 -0
- package/src/__tests__/managed-skill-lifecycle.test.ts +0 -10
- package/src/__tests__/manual-token-reconciliation.test.ts +334 -0
- package/src/__tests__/memory-v2-static-injector.test.ts +95 -0
- package/src/__tests__/migration-cross-version-compatibility.test.ts +197 -291
- package/src/__tests__/migration-export-http.test.ts +33 -26
- package/src/__tests__/migration-export-streaming.test.ts +18 -10
- package/src/__tests__/migration-export-to-gcs.test.ts +49 -9
- package/src/__tests__/migration-import-commit-http.test.ts +66 -21
- package/src/__tests__/migration-import-from-gcs.test.ts +50 -9
- package/src/__tests__/migration-import-from-url.test.ts +20 -6
- package/src/__tests__/migration-import-preflight-http.test.ts +95 -95
- package/src/__tests__/migration-parity-persistence.test.ts +62 -25
- package/src/__tests__/migration-transport.test.ts +115 -23
- package/src/__tests__/migration-validate-http.test.ts +105 -80
- package/src/__tests__/migration-wizard.test.ts +133 -27
- package/src/__tests__/non-member-access-request.test.ts +1 -1
- package/src/__tests__/notification-guardian-path.test.ts +1 -1
- package/src/__tests__/oauth-store.test.ts +19 -0
- package/src/__tests__/platform-bash-auto-approve.test.ts +21 -12
- package/src/__tests__/prechat-onboarding-contract.test.ts +31 -7
- package/src/__tests__/pricing.test.ts +68 -4
- package/src/__tests__/process-message-background-slack.test.ts +331 -0
- package/src/__tests__/provider-managed-proxy-integration.test.ts +153 -17
- package/src/__tests__/provider-send-message-override-profile.test.ts +50 -0
- package/src/__tests__/provider-usage-tracking.test.ts +208 -0
- package/src/__tests__/reaction-persistence.test.ts +9 -6
- package/src/__tests__/rebind-secrets-screen.test.ts +53 -16
- package/src/__tests__/recording-handler.test.ts +64 -81
- package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +4 -3
- package/src/__tests__/relay-server.test.ts +18 -13
- package/src/__tests__/require-fresh-approval.test.ts +13 -22
- package/src/__tests__/runtime-attachment-metadata.test.ts +1 -1
- package/src/__tests__/runtime-events-sse-parity.test.ts +3 -4
- package/src/__tests__/runtime-events-sse.test.ts +3 -12
- package/src/__tests__/search-skills-unified.test.ts +9 -15
- package/src/__tests__/secret-ingress-cli.test.ts +2 -5
- package/src/__tests__/secret-ingress-http.test.ts +0 -4
- package/src/__tests__/secret-onetime-send.test.ts +4 -2
- package/src/__tests__/secret-prompt-log-hygiene.test.ts +24 -7
- package/src/__tests__/secret-prompter-channel-fallback.test.ts +42 -47
- package/src/__tests__/secret-response-routing.test.ts +29 -15
- package/src/__tests__/secret-routes-managed-proxy.test.ts +5 -1
- package/src/__tests__/secret-scanner.test.ts +2 -545
- package/src/__tests__/send-endpoint-busy.test.ts +9 -24
- package/src/__tests__/settings-routes.test.ts +1 -1
- package/src/__tests__/shell-credential-ref.test.ts +0 -8
- package/src/__tests__/shell-tool-proxy-mode.test.ts +0 -56
- package/src/__tests__/skill-script-runner-sandbox.test.ts +0 -11
- package/src/__tests__/skill-tool-factory.test.ts +97 -0
- package/src/__tests__/skills-file-content-endpoint.test.ts +9 -30
- package/src/__tests__/skills-files-catalog-fallback.test.ts +11 -17
- package/src/__tests__/slack-inbound-verification.test.ts +1 -62
- package/src/__tests__/subagent-fork-notifications.test.ts +57 -47
- package/src/__tests__/subagent-manager-notify.test.ts +70 -70
- package/src/__tests__/subagent-notify-parent.test.ts +80 -83
- package/src/__tests__/system-prompt.test.ts +115 -13
- package/src/__tests__/terminal-tools.test.ts +0 -89
- package/src/__tests__/thread-backfill.test.ts +945 -31
- package/src/__tests__/tool-domain-event-publisher.test.ts +0 -36
- package/src/__tests__/tool-execute-pipeline.test.ts +0 -6
- package/src/__tests__/tool-execution-abort-cleanup.test.ts +0 -16
- package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +9 -19
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +4 -7
- package/src/__tests__/tool-executor.test.ts +12 -19
- package/src/__tests__/tool-metrics-listener.test.ts +0 -35
- package/src/__tests__/tool-side-effects-slack-dm.test.ts +1 -0
- package/src/__tests__/tool-trace-listener.test.ts +0 -17
- package/src/__tests__/transfer-progress-screen.test.ts +63 -26
- package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +2 -149
- package/src/__tests__/trusted-contact-multichannel.test.ts +2 -4
- package/src/__tests__/trusted-contact-verification.test.ts +1 -1
- package/src/__tests__/tts-catalog-parity.test.ts +16 -5
- package/src/__tests__/usage-attribution.test.ts +247 -0
- package/src/__tests__/usage-cli.test.ts +143 -0
- package/src/__tests__/usage-grouped-buckets.test.ts +155 -0
- package/src/__tests__/usage-routes.test.ts +150 -0
- package/src/__tests__/validation-results-screen.test.ts +39 -16
- package/src/__tests__/vbundle-pax-and-symlink.test.ts +12 -3
- package/src/__tests__/vellum-self-knowledge-inline-command.test.ts +49 -137
- package/src/__tests__/verification-control-plane-policy.test.ts +4 -7
- package/src/__tests__/voice-session-bridge.test.ts +5 -5
- package/src/__tests__/workspace-migration-062-drop-memory-v2-edges-json.test.ts +103 -0
- package/src/__tests__/workspace-migration-063-release-notes-dynamic-model-context.test.ts +77 -0
- package/src/__tests__/workspace-migration-064-unwind-main-agent-opus-seed.test.ts +225 -0
- package/src/__tests__/workspace-migration-memory-v2-init.test.ts +8 -30
- package/src/acp/index.ts +0 -15
- package/src/acp/session-manager.ts +37 -34
- package/src/agent/loop.ts +16 -1
- package/src/approvals/AGENTS.md +4 -0
- package/src/approvals/__tests__/guardian-feed-event.test.ts +10 -3
- package/src/approvals/guardian-request-resolvers.ts +10 -2
- package/src/backup/__tests__/backup-worker.test.ts +36 -8
- package/src/backup/__tests__/paths.test.ts +2 -2
- package/src/backup/__tests__/restore.test.ts +45 -28
- package/src/backup/backup-worker.ts +36 -2
- package/src/backup/paths.ts +9 -6
- package/src/browser-session/events.ts +0 -9
- package/src/calls/call-store.ts +1 -34
- package/src/calls/guardian-question-copy.ts +0 -108
- package/src/calls/relay-server.ts +0 -24
- package/src/calls/twilio-rest.ts +0 -38
- package/src/calls/twilio-routes.ts +1 -1
- package/src/calls/voice-session-bridge.ts +7 -38
- package/src/channels/types.ts +1 -36
- package/src/cli/commands/__tests__/cache.test.ts +152 -5
- package/src/cli/commands/__tests__/memory-v2.test.ts +14 -28
- package/src/cli/commands/__tests__/trust.test.ts +21 -387
- package/src/cli/commands/backup.ts +4 -4
- package/src/cli/commands/cache-fs.ts +8 -0
- package/src/cli/commands/cache.ts +153 -82
- package/src/cli/commands/clients.ts +63 -5
- package/src/cli/commands/completions.ts +3 -3
- package/src/cli/commands/contacts.ts +231 -76
- package/src/cli/commands/keys.ts +4 -1
- package/src/cli/commands/memory-v2.ts +24 -52
- package/src/cli/commands/oauth/shared.ts +2 -29
- package/src/cli/commands/pending.ts +102 -0
- package/src/cli/commands/skills.ts +77 -35
- package/src/cli/commands/trust.ts +70 -430
- package/src/cli/commands/usage.ts +25 -16
- package/src/cli/lib/daemon-credential-client.ts +14 -0
- package/src/cli/program.ts +2 -0
- package/src/cli.ts +0 -21
- package/src/config/__tests__/feature-flag-registry-guard.test.ts +2 -2
- package/src/config/bundled-skills/messaging/TOOLS.json +14 -4
- package/src/config/env-registry.ts +12 -2
- package/src/config/env.ts +3 -14
- package/src/config/feature-flag-registry.json +30 -30
- package/src/config/llm-callsite-catalog.ts +12 -0
- package/src/config/llm-context-resolution.ts +80 -0
- package/src/config/llm-resolver.ts +58 -22
- package/src/config/loader.ts +3 -3
- package/src/config/schema.ts +2 -158
- package/src/config/schemas/__tests__/memory-v2.test.ts +1 -0
- package/src/config/schemas/call-site-catalog.ts +271 -0
- package/src/config/schemas/calls.ts +5 -5
- package/src/config/schemas/inference.ts +1 -1
- package/src/config/schemas/ingress.ts +1 -1
- package/src/config/schemas/llm.ts +31 -3
- package/src/config/schemas/memory-retrieval.ts +2 -2
- package/src/config/schemas/memory-v2.ts +9 -0
- package/src/config/schemas/security.ts +1 -42
- package/src/config/schemas/services.ts +6 -6
- package/src/config/schemas/skills.ts +5 -5
- package/src/config/schemas/tts.ts +1 -1
- package/src/config/seed-inference-profiles.ts +117 -0
- package/src/config/skills.ts +0 -90
- package/src/config/types.ts +3 -6
- package/src/contacts/contact-store.ts +0 -17
- package/src/contacts/contacts-write.ts +1 -105
- package/src/context/window-manager.ts +44 -5
- package/src/credential-execution/process-manager.ts +34 -10
- package/src/credential-health/credential-health-service.ts +21 -16
- package/src/daemon/__tests__/conversation-surfaces-launch.test.ts +75 -82
- package/src/daemon/__tests__/daemon-skill-host.test.ts +2 -9
- package/src/daemon/connection-policy.ts +1 -26
- package/src/daemon/conversation-agent-loop-handlers.ts +53 -4
- package/src/daemon/conversation-agent-loop.ts +277 -36
- package/src/daemon/conversation-history.ts +8 -8
- package/src/daemon/conversation-launch.ts +20 -135
- package/src/daemon/conversation-lifecycle.ts +1 -1
- package/src/daemon/conversation-messaging.ts +1 -0
- package/src/daemon/conversation-process.ts +83 -163
- package/src/daemon/conversation-runtime-assembly.ts +219 -76
- package/src/daemon/conversation-slash.ts +47 -5
- package/src/daemon/conversation-store.ts +7 -31
- package/src/daemon/conversation-surfaces.ts +22 -28
- package/src/daemon/conversation-tool-setup.ts +3 -33
- package/src/daemon/conversation-usage.ts +36 -0
- package/src/daemon/conversation.ts +117 -233
- package/src/daemon/daemon-control.ts +3 -71
- package/src/daemon/daemon-skill-host.ts +8 -11
- package/src/daemon/dictation-profile-store.ts +2 -26
- package/src/daemon/first-greeting.ts +44 -156
- package/src/daemon/handlers/config-channels.ts +12 -12
- package/src/daemon/handlers/config-ingress.ts +4 -165
- package/src/daemon/handlers/config-model.ts +1 -1
- package/src/daemon/handlers/config-voice.ts +0 -42
- package/src/daemon/handlers/conversations.ts +11 -190
- package/src/daemon/handlers/recording.ts +26 -158
- package/src/daemon/handlers/shared.ts +23 -71
- package/src/daemon/handlers/skills.ts +42 -93
- package/src/daemon/host-bash-proxy.ts +67 -45
- package/src/daemon/host-browser-proxy.ts +65 -27
- package/src/daemon/host-cu-proxy.ts +40 -39
- package/src/daemon/host-file-proxy.ts +58 -37
- package/src/daemon/host-transfer-proxy.ts +84 -46
- package/src/daemon/lifecycle.ts +49 -15
- package/src/daemon/message-types/conversations.ts +7 -0
- package/src/daemon/message-types/host-bash.ts +1 -0
- package/src/daemon/message-types/host-cu.ts +1 -0
- package/src/daemon/message-types/host-file.ts +1 -0
- package/src/daemon/message-types/host-transfer.ts +1 -0
- package/src/daemon/message-types/messages.ts +10 -9
- package/src/daemon/message-types/workspace.ts +1 -1
- package/src/daemon/process-message.ts +102 -239
- package/src/daemon/server.ts +13 -462
- package/src/daemon/shutdown-handlers.ts +2 -2
- package/src/daemon/tool-side-effects.ts +125 -107
- package/src/daemon/trust-context.ts +13 -0
- package/src/daemon/wake-target-adapter.ts +4 -9
- package/src/events/domain-events.ts +0 -8
- package/src/events/tool-audit-listener.ts +3 -1
- package/src/events/tool-domain-event-publisher.ts +0 -10
- package/src/events/tool-metrics-listener.ts +0 -17
- package/src/events/tool-trace-listener.ts +0 -14
- package/src/filing/filing-service.ts +13 -1
- package/src/heartbeat/__tests__/heartbeat-feed-event.test.ts +6 -2
- package/src/heartbeat/heartbeat-service.ts +23 -5
- package/src/home/__tests__/feed-writer.test.ts +0 -4
- package/src/home/__tests__/relationship-state-writer.test.ts +30 -0
- package/src/home/feed-writer.ts +1 -2
- package/src/home/relationship-state-writer.ts +16 -3
- package/src/ipc/__tests__/browser-ipc.test.ts +2 -12
- package/src/ipc/__tests__/skill-server-bidirectional.test.ts +0 -1
- package/src/ipc/assistant-server.ts +3 -10
- package/src/ipc/routes/__tests__/memory-v2-backfill.test.ts +39 -20
- package/src/ipc/routes/route-adapter.ts +1 -1
- package/src/ipc/routes/trust-rules.test.ts +0 -95
- package/src/ipc/skill-ipc-types.ts +41 -0
- package/src/ipc/skill-routes/__tests__/events-ipc.test.ts +13 -27
- package/src/ipc/skill-routes/__tests__/identity.test.ts +4 -23
- package/src/ipc/skill-routes/events.ts +12 -23
- package/src/ipc/skill-routes/identity.ts +4 -17
- package/src/ipc/skill-routes/index.ts +1 -1
- package/src/ipc/skill-server.ts +6 -39
- package/src/live-voice/__tests__/runtime-websocket-shell.test.ts +0 -8
- package/src/live-voice/protocol.ts +4 -13
- package/src/mcp/manager.ts +0 -5
- package/src/memory/__tests__/fixtures/memory-v2-activation-fixtures.ts +55 -0
- package/src/memory/__tests__/memory-v2-activation-log-store.test.ts +127 -0
- package/src/memory/app-git-service.ts +0 -32
- package/src/memory/app-store.ts +154 -0
- package/src/memory/attachments-store.ts +6 -0
- package/src/memory/context-search/sources/memory-v2.ts +578 -0
- package/src/memory/context-search/sources/memory.ts +5 -0
- package/src/memory/context-search/sources/pkb.ts +10 -1
- package/src/memory/context-search/sources/workspace.ts +3 -2
- package/src/memory/conversation-crud.ts +29 -4
- package/src/memory/conversation-disk-view.ts +1 -5
- package/src/memory/conversation-starter-checkpoints.ts +63 -0
- package/src/memory/db-connection.ts +62 -0
- package/src/memory/db-init.ts +14 -0
- package/src/memory/embedding-backend.ts +3 -21
- package/src/memory/embedding-gemini.ts +0 -2
- package/src/memory/embedding-local.ts +6 -6
- package/src/memory/embedding-ollama.ts +6 -6
- package/src/memory/embedding-openai.ts +6 -6
- package/src/memory/embedding-types.ts +21 -0
- package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +3 -7
- package/src/memory/graph/conversation-graph-memory.ts +35 -13
- package/src/memory/graph/injection.test.ts +2 -2
- package/src/memory/graph/injection.ts +1 -1
- package/src/memory/guardian-action-store.ts +0 -83
- package/src/memory/guardian-approvals.ts +0 -48
- package/src/memory/indexer.ts +1 -15
- package/src/memory/job-handlers/conversation-starters.ts +36 -53
- package/src/memory/job-utils.ts +0 -6
- package/src/memory/jobs-store.ts +0 -1
- package/src/memory/jobs-worker.ts +2 -16
- package/src/memory/llm-request-log-store.ts +0 -41
- package/src/memory/llm-usage-store.ts +129 -43
- package/src/memory/memory-v2-activation-log-store.ts +115 -0
- package/src/memory/migrations/233-document-conversations.ts +54 -0
- package/src/memory/migrations/234-memory-v2-activation-logs.ts +55 -0
- package/src/memory/migrations/235-llm-usage-attribution.ts +31 -0
- package/src/memory/migrations/235-slack-compaction-watermark.ts +44 -0
- package/src/memory/migrations/236-tool-invocations-matched-rule-id.ts +26 -0
- package/src/memory/migrations/__tests__/234-memory-v2-activation-logs.test.ts +182 -0
- package/src/memory/migrations/index.ts +14 -0
- package/src/memory/migrations/registry.ts +24 -0
- package/src/memory/raw-query.ts +2 -68
- package/src/memory/schema/conversations.ts +7 -0
- package/src/memory/schema/infrastructure.ts +25 -0
- package/src/memory/search/semantic.ts +5 -16
- package/src/memory/tool-usage-store.ts +2 -0
- package/src/memory/usage-buckets.ts +40 -1
- package/src/memory/usage-grouped-buckets.ts +127 -0
- package/src/memory/v2/__tests__/activation.test.ts +289 -90
- package/src/memory/v2/__tests__/backfill-jobs.test.ts +2 -129
- package/src/memory/v2/__tests__/consolidation-job.test.ts +28 -11
- package/src/memory/v2/__tests__/edge-index.test.ts +278 -0
- package/src/memory/v2/__tests__/injection.test.ts +384 -15
- package/src/memory/v2/__tests__/migration.test.ts +64 -36
- package/src/memory/v2/__tests__/page-store.test.ts +191 -8
- package/src/memory/v2/__tests__/prompts-consolidation.test.ts +181 -0
- package/src/memory/v2/__tests__/skill-store.test.ts +115 -3
- package/src/memory/v2/__tests__/static-context.test.ts +153 -0
- package/src/memory/v2/activation.ts +168 -97
- package/src/memory/v2/backfill-jobs.ts +15 -100
- package/src/memory/v2/consolidation-job.ts +14 -12
- package/src/memory/v2/edge-index.ts +191 -0
- package/src/memory/v2/injection.ts +182 -58
- package/src/memory/v2/migration.ts +57 -64
- package/src/memory/v2/now-text.ts +2 -3
- package/src/memory/v2/page-store.ts +168 -31
- package/src/memory/v2/prompts/consolidation.ts +118 -42
- package/src/memory/v2/prompts/sweep.ts +3 -3
- package/src/memory/v2/skill-store.ts +55 -7
- package/src/memory/v2/static-context.ts +62 -0
- package/src/memory/v2/types.ts +10 -20
- package/src/memory/validation.ts +0 -11
- package/src/messaging/draft-store.ts +0 -6
- package/src/messaging/provider-types.ts +8 -0
- package/src/messaging/provider.ts +7 -0
- package/src/messaging/providers/gmail/client.ts +1 -121
- package/src/messaging/providers/outlook/client.ts +0 -73
- package/src/messaging/providers/slack/__tests__/adapter-mention-rendering.test.ts +226 -0
- package/src/messaging/providers/slack/adapter.ts +122 -21
- package/src/messaging/providers/slack/backfill.test.ts +95 -6
- package/src/messaging/providers/slack/backfill.ts +89 -11
- package/src/messaging/providers/slack/client.ts +10 -124
- package/src/messaging/providers/slack/message-metadata.ts +12 -2
- package/src/messaging/providers/slack/render-transcript.test.ts +56 -0
- package/src/messaging/providers/slack/render-transcript.ts +126 -25
- package/src/messaging/providers/slack/types.ts +1 -0
- package/src/oauth/connection-resolver.test.ts +8 -0
- package/src/oauth/connection-resolver.ts +8 -16
- package/src/oauth/credential-token-resolver.ts +97 -0
- package/src/oauth/manual-token-connection.ts +30 -34
- package/src/oauth/oauth-store.ts +6 -4
- package/src/outbound-proxy/certs.ts +0 -7
- package/src/outbound-proxy/config.ts +0 -74
- package/src/outbound-proxy/health.ts +0 -44
- package/src/outbound-proxy/index.ts +0 -22
- package/src/permissions/approval-provenance.test.ts +184 -0
- package/src/permissions/approval-provenance.ts +70 -0
- package/src/permissions/checker.ts +4 -1
- package/src/permissions/gateway-threshold-reader.ts +4 -1
- package/src/permissions/prompter.ts +9 -2
- package/src/permissions/secret-prompter.ts +21 -48
- package/src/permissions/types.ts +33 -0
- package/src/permissions/workspace-policy.ts +0 -5
- package/src/platform/sync-identity.ts +0 -8
- package/src/plugins/defaults/injectors.ts +69 -2
- package/src/plugins/defaults/overflow-reduce.ts +3 -2
- package/src/plugins/types.ts +8 -0
- package/src/prompts/system-prompt.ts +34 -70
- package/src/prompts/templates/BOOTSTRAP.md +52 -6
- package/src/prompts/update-bulletin-job.ts +2 -0
- package/src/providers/__tests__/retry-callsite.test.ts +138 -1
- package/src/providers/anthropic/client.ts +72 -33
- package/src/providers/call-site-routing.ts +42 -3
- package/src/providers/gemini/client.ts +18 -2
- package/src/providers/managed-proxy/context.ts +0 -5
- package/src/providers/model-catalog.ts +105 -19
- package/src/providers/openai/chat-completions-provider.ts +6 -0
- package/src/providers/openai/responses-provider.ts +7 -1
- package/src/providers/provider-send-message.ts +45 -2
- package/src/providers/ratelimit.ts +7 -2
- package/src/providers/registry.ts +14 -9
- package/src/providers/retry.ts +96 -8
- package/src/providers/types.ts +13 -0
- package/src/providers/usage-tracking.ts +96 -0
- package/src/runtime/AGENTS.md +10 -6
- package/src/runtime/__tests__/agent-wake.test.ts +89 -0
- package/src/runtime/agent-wake.ts +39 -2
- package/src/runtime/assistant-event-hub.ts +541 -45
- package/src/runtime/assistant-event.ts +1 -6
- package/src/runtime/auth/context.ts +0 -9
- package/src/runtime/auth/middleware.ts +1 -1
- package/src/runtime/auth/route-policy.ts +11 -9
- package/src/runtime/auth/token-service.ts +0 -11
- package/src/runtime/channel-approvals.ts +6 -2
- package/src/runtime/channel-verification-service.ts +3 -5
- package/src/runtime/http-errors.ts +0 -34
- package/src/runtime/http-router.ts +6 -3
- package/src/runtime/http-server.ts +22 -82
- package/src/runtime/http-types.ts +5 -0
- package/src/runtime/interactive-ui.ts +0 -1
- package/src/runtime/middleware/auth.ts +0 -20
- package/src/runtime/migrations/__tests__/v1-test-helpers.ts +112 -0
- package/src/runtime/migrations/__tests__/vbundle-builder-credentials.test.ts +11 -4
- package/src/runtime/migrations/__tests__/vbundle-builder-v1-shape.test.ts +253 -0
- package/src/runtime/migrations/__tests__/vbundle-import-credentials.test.ts +19 -6
- package/src/runtime/migrations/__tests__/vbundle-legacy-user-md.test.ts +71 -27
- package/src/runtime/migrations/__tests__/vbundle-metadata-merge-integration.test.ts +41 -2
- package/src/runtime/migrations/__tests__/vbundle-streaming-importer.test.ts +143 -79
- package/src/runtime/migrations/__tests__/vbundle-streaming-validator.test.ts +143 -23
- package/src/runtime/migrations/__tests__/vbundle-tar-stream.test.ts +2 -2
- package/src/runtime/migrations/__tests__/vbundle-validator-v1-schema.test.ts +371 -0
- package/src/runtime/migrations/migration-transport.ts +46 -13
- package/src/runtime/migrations/migration-wizard.ts +2 -2
- package/src/runtime/migrations/origin-mode.ts +40 -0
- package/src/runtime/migrations/vbundle-builder.ts +133 -79
- package/src/runtime/migrations/vbundle-import-analyzer.ts +9 -7
- package/src/runtime/migrations/vbundle-importer.ts +7 -7
- package/src/runtime/migrations/vbundle-metadata-merge.ts +1 -1
- package/src/runtime/migrations/vbundle-streaming-importer.ts +3 -3
- package/src/runtime/migrations/vbundle-streaming-validator.ts +48 -26
- package/src/runtime/migrations/vbundle-validator.ts +214 -41
- package/src/runtime/pending-interactions.ts +13 -4
- package/src/runtime/routes/__tests__/acp-routes.test.ts +0 -1
- package/src/runtime/routes/__tests__/backup-routes.test.ts +28 -19
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +235 -0
- package/src/runtime/routes/__tests__/llm-call-sites-routes.test.ts +58 -0
- package/src/runtime/routes/__tests__/migration-export-secrets-redacted.test.ts +54 -0
- package/src/runtime/routes/__tests__/migration-import-credential-filter.test.ts +19 -6
- package/src/runtime/routes/__tests__/user-route-dispatcher.test.ts +7 -7
- package/src/runtime/routes/acp-routes.test.ts +0 -3
- package/src/runtime/routes/acp-routes.ts +3 -7
- package/src/runtime/routes/app-management-routes.ts +18 -9
- package/src/runtime/routes/approval-routes.ts +55 -14
- package/src/runtime/routes/avatar-routes.ts +3 -5
- package/src/runtime/routes/browser-routes.ts +1 -15
- package/src/runtime/routes/channel-guardian-routes.ts +1 -5
- package/src/runtime/routes/channel-readiness-routes.ts +3 -7
- package/src/runtime/routes/channel-route-shared.ts +2 -28
- package/src/runtime/routes/client-routes.ts +45 -12
- package/src/runtime/routes/consolidation-routes.ts +115 -0
- package/src/runtime/routes/conversation-list-routes.ts +12 -29
- package/src/runtime/routes/conversation-management-routes.ts +14 -51
- package/src/runtime/routes/conversation-query-routes.ts +120 -8
- package/src/runtime/routes/conversation-routes.ts +44 -528
- package/src/runtime/routes/conversation-starter-routes.ts +19 -40
- package/src/runtime/routes/documents-routes.ts +53 -18
- package/src/runtime/routes/events-routes.ts +59 -91
- package/src/runtime/routes/filing-routes.ts +18 -1
- package/src/runtime/routes/guardian-action-routes.ts +4 -9
- package/src/runtime/routes/host-bash-routes.ts +3 -2
- package/src/runtime/routes/host-browser-routes.ts +9 -33
- package/src/runtime/routes/host-cu-routes.ts +6 -1
- package/src/runtime/routes/host-file-routes.ts +3 -2
- package/src/runtime/routes/host-transfer-routes.ts +11 -15
- package/src/runtime/routes/identity-routes.ts +78 -6
- package/src/runtime/routes/inbound-message-handler.ts +580 -137
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +2 -88
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +3 -0
- package/src/runtime/routes/index.ts +4 -0
- package/src/runtime/routes/integrations/slack/channel.ts +0 -24
- package/src/runtime/routes/llm-call-sites-routes.ts +22 -0
- package/src/runtime/routes/memory-v2-routes.ts +10 -15
- package/src/runtime/routes/migration-routes.ts +188 -31
- package/src/runtime/routes/playground/guard.ts +1 -1
- package/src/runtime/routes/playground/index.ts +0 -2
- package/src/runtime/routes/recording-routes.ts +4 -24
- package/src/runtime/routes/rename-conversation-routes.ts +2 -6
- package/src/runtime/routes/schedule-routes.ts +3 -6
- package/src/runtime/routes/secret-routes.ts +87 -18
- package/src/runtime/routes/settings-routes.ts +29 -28
- package/src/runtime/routes/skills-routes.ts +12 -31
- package/src/runtime/routes/suggest-trust-rule-routes.ts +32 -1
- package/src/runtime/routes/task-routes.ts +6 -6
- package/src/runtime/routes/trust-rules-routes.ts +3 -94
- package/src/runtime/routes/types.ts +4 -4
- package/src/runtime/routes/upgrade-broadcast-routes.ts +3 -10
- package/src/runtime/routes/usage-routes.ts +87 -10
- package/src/runtime/routes/user-routes.ts +17 -31
- package/src/runtime/routes/work-items-routes.ts +1 -4
- package/src/runtime/services/__tests__/analyze-conversation.test.ts +2 -2
- package/src/runtime/services/analyze-conversation.ts +7 -17
- package/src/runtime/services/conversation-serializer.ts +2 -4
- package/src/runtime/verification-outbound-actions.ts +1 -1
- package/src/runtime/verification-rate-limiter.ts +1 -1
- package/src/schedule/schedule-store.ts +0 -16
- package/src/security/secret-scanner.ts +14 -547
- package/src/security/secure-keys.ts +31 -11
- package/src/security/token-manager.ts +7 -3
- package/src/signals/cancel.ts +16 -25
- package/src/signals/conversation-undo.ts +2 -27
- package/src/signals/emit-event.ts +1 -2
- package/src/signals/user-message.ts +108 -22
- package/src/skills/catalog-install.ts +1 -0
- package/src/skills/clawhub.ts +2 -2
- package/src/skills/inline-command-runner.ts +1 -7
- package/src/subagent/manager.ts +67 -84
- package/src/tasks/task-store.ts +1 -28
- package/src/telemetry/types.ts +6 -0
- package/src/telemetry/usage-telemetry-reporter.test.ts +38 -15
- package/src/telemetry/usage-telemetry-reporter.ts +3 -5
- package/src/tools/acp/spawn.test.ts +1 -2
- package/src/tools/acp/steer.test.ts +1 -2
- package/src/tools/browser/__tests__/browser-status.test.ts +44 -127
- package/src/tools/browser/browser-execution.ts +31 -147
- package/src/tools/browser/cdp-client/__tests__/factory.test.ts +92 -68
- package/src/tools/browser/cdp-client/factory.ts +48 -76
- package/src/tools/browser/cdp-client/index.ts +1 -14
- package/src/tools/executor.ts +44 -31
- package/src/tools/host-filesystem/edit.ts +3 -2
- package/src/tools/host-filesystem/read.ts +3 -2
- package/src/tools/host-filesystem/transfer.test.ts +45 -42
- package/src/tools/host-filesystem/transfer.ts +4 -3
- package/src/tools/host-filesystem/write.ts +3 -2
- package/src/tools/host-terminal/host-shell.ts +4 -3
- package/src/tools/network/script-proxy/index.ts +1 -10
- package/src/tools/permission-checker.ts +66 -1
- package/src/tools/skills/sandbox-runner.ts +1 -6
- package/src/tools/skills/skill-tool-factory.ts +32 -0
- package/src/tools/terminal/safe-env.ts +1 -0
- package/src/tools/terminal/shell.ts +2 -78
- package/src/tools/types.ts +12 -39
- package/src/tts/__tests__/provider-catalog.test.ts +2 -2
- package/src/tts/provider-catalog.ts +1 -1
- package/src/usage/actors.ts +2 -1
- package/src/usage/attribution.ts +185 -0
- package/src/usage/pricing.ts +166 -0
- package/src/usage/types.ts +14 -0
- package/src/util/json.ts +13 -0
- package/src/util/logger.ts +3 -3
- package/src/util/pricing.ts +50 -3
- package/src/work-items/work-item-runner.ts +15 -42
- package/src/workspace/migrations/050-seed-main-agent-opus-callsite.ts +4 -3
- package/src/workspace/migrations/052-seed-default-inference-profiles.ts +3 -3
- package/src/workspace/migrations/060-memory-v2-init.ts +2 -18
- package/src/workspace/migrations/061-move-backup-key-to-workspace.ts +59 -0
- package/src/workspace/migrations/062-drop-memory-v2-edges-json.ts +27 -0
- package/src/workspace/migrations/063-release-notes-dynamic-model-context.ts +70 -0
- package/src/workspace/migrations/064-unwind-main-agent-opus-seed.ts +64 -0
- package/src/workspace/migrations/registry.ts +8 -0
- package/src/workspace/provider-commit-message-generator.ts +3 -3
- package/src/__tests__/sandbox-diagnostics.test.ts +0 -138
- package/src/__tests__/sandbox-host-parity.test.ts +0 -1024
- package/src/__tests__/secret-detection-handler.test.ts +0 -67
- package/src/__tests__/secret-scanner-executor.test.ts +0 -450
- package/src/__tests__/tcc-sandbox-deny.test.ts +0 -198
- package/src/__tests__/terminal-sandbox.test.ts +0 -374
- package/src/__tests__/tool-notification-listener.test.ts +0 -65
- package/src/context/__tests__/microcompact.test.ts +0 -805
- package/src/context/microcompact.ts +0 -443
- package/src/daemon/handlers/slack-channel-oauth-install.ts +0 -197
- package/src/events/tool-notification-listener.ts +0 -17
- package/src/ipc/routes/__tests__/memory-v2-validate.test.ts +0 -219
- package/src/memory/v2/__tests__/edges.test.ts +0 -435
- package/src/memory/v2/edges.ts +0 -217
- package/src/prompts/__tests__/system-prompt-memory-v2.test.ts +0 -197
- package/src/runtime/__tests__/chrome-extension-registry.test.ts +0 -518
- package/src/runtime/__tests__/client-registry.test.ts +0 -271
- package/src/runtime/chrome-extension-registry.ts +0 -368
- package/src/runtime/client-registry.ts +0 -254
- package/src/runtime/routes/inbound-stages/verification-intercept.ts +0 -329
- package/src/tools/secret-detection-handler.ts +0 -269
- package/src/tools/terminal/backends/native.ts +0 -327
- package/src/tools/terminal/backends/types.ts +0 -37
- package/src/tools/terminal/sandbox-diagnostics.ts +0 -87
- package/src/tools/terminal/sandbox.ts +0 -40
|
@@ -3,15 +3,15 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Boots the runtime HTTP server in-process, opens a mock chrome-extension
|
|
5
5
|
* WebSocket against `/v1/browser-relay`, and drives
|
|
6
|
-
* `HostBrowserProxy.request()` end-to-end:
|
|
6
|
+
* `HostBrowserProxy.instance.request()` end-to-end:
|
|
7
7
|
*
|
|
8
8
|
* proxy.request()
|
|
9
|
-
* →
|
|
9
|
+
* → sendToExtension (routed via assistant event hub)
|
|
10
10
|
* → mock extension WebSocket receives host_browser_request
|
|
11
11
|
* → mock CDP handler (Browser.getVersion fake)
|
|
12
|
-
* → POST /v1/host-browser-result
|
|
13
|
-
* →
|
|
14
|
-
* →
|
|
12
|
+
* → POST /v1/host-browser-result (or WS host_browser_result frame)
|
|
13
|
+
* → resolveHostBrowserResultByRequestId → proxy.resolve()
|
|
14
|
+
* → request() resolves
|
|
15
15
|
*
|
|
16
16
|
* Covers:
|
|
17
17
|
* - Happy path: Browser.getVersion round-trips and returns the fake
|
|
@@ -63,76 +63,17 @@ mock.module("../config/loader.js", () => ({
|
|
|
63
63
|
|
|
64
64
|
// ── Real imports (after mocks) ──────────────────────────────────────
|
|
65
65
|
|
|
66
|
-
import type { Conversation } from "../daemon/conversation.js";
|
|
67
66
|
import { HostBrowserProxy } from "../daemon/host-browser-proxy.js";
|
|
68
|
-
import type { ServerMessage } from "../daemon/message-protocol.js";
|
|
69
67
|
import { getDb } from "../memory/db-connection.js";
|
|
70
68
|
import { initializeDb } from "../memory/db-init.js";
|
|
69
|
+
import { assistantEventHub } from "../runtime/assistant-event-hub.js";
|
|
71
70
|
import { mintToken } from "../runtime/auth/token-service.js";
|
|
72
|
-
import {
|
|
73
|
-
__resetChromeExtensionRegistryForTests,
|
|
74
|
-
getChromeExtensionRegistry,
|
|
75
|
-
} from "../runtime/chrome-extension-registry.js";
|
|
76
71
|
import { RuntimeHttpServer } from "../runtime/http-server.js";
|
|
77
|
-
import * as pendingInteractions from "../runtime/pending-interactions.js";
|
|
78
72
|
|
|
79
73
|
initializeDb();
|
|
80
74
|
|
|
81
75
|
// ── Helpers ─────────────────────────────────────────────────────────
|
|
82
76
|
|
|
83
|
-
/**
|
|
84
|
-
* Wrap a HostBrowserProxy in a sendToClient that:
|
|
85
|
-
* 1. Routes host_browser_request/host_browser_cancel via the Chrome
|
|
86
|
-
* extension registry for the given guardianId.
|
|
87
|
-
* 2. Registers a pending interaction for each request so the
|
|
88
|
-
* `/v1/host-browser-result` HTTP route can find the stub
|
|
89
|
-
* conversation and call `resolveHostBrowser` on it.
|
|
90
|
-
*
|
|
91
|
-
* Returns the proxy and its stub conversation. In production this
|
|
92
|
-
* wiring lives in `conversation-routes.ts` `makeHubPublisher`; the test
|
|
93
|
-
* reproduces the minimum surface needed for the round-trip.
|
|
94
|
-
*/
|
|
95
|
-
function createBoundProxy(
|
|
96
|
-
guardianId: string,
|
|
97
|
-
conversationId: string,
|
|
98
|
-
): { proxy: HostBrowserProxy; conversation: Conversation } {
|
|
99
|
-
// The stub Conversation's `resolveHostBrowser` routes straight back
|
|
100
|
-
// to the real proxy. Declare the proxy reference first so the stub
|
|
101
|
-
// can close over it before the proxy itself is constructed below.
|
|
102
|
-
let proxyRef: HostBrowserProxy | null = null;
|
|
103
|
-
const conversation = {
|
|
104
|
-
resolveHostBrowser(
|
|
105
|
-
requestId: string,
|
|
106
|
-
response: { content: string; isError: boolean },
|
|
107
|
-
) {
|
|
108
|
-
proxyRef?.resolve(requestId, response);
|
|
109
|
-
},
|
|
110
|
-
} as unknown as Conversation;
|
|
111
|
-
|
|
112
|
-
const sendToClient = (msg: ServerMessage) => {
|
|
113
|
-
// Register pending interactions for host_browser_request envelopes
|
|
114
|
-
// so the /v1/host-browser-result route can look them up.
|
|
115
|
-
if ((msg as { type: string }).type === "host_browser_request") {
|
|
116
|
-
const requestId = (msg as { requestId: string }).requestId;
|
|
117
|
-
pendingInteractions.register(requestId, {
|
|
118
|
-
conversation,
|
|
119
|
-
conversationId,
|
|
120
|
-
kind: "host_browser",
|
|
121
|
-
});
|
|
122
|
-
}
|
|
123
|
-
const ok = getChromeExtensionRegistry().send(guardianId, msg);
|
|
124
|
-
if (!ok) {
|
|
125
|
-
throw new Error(
|
|
126
|
-
`chrome-extension host_browser send failed: no active connection for guardian ${guardianId}`,
|
|
127
|
-
);
|
|
128
|
-
}
|
|
129
|
-
};
|
|
130
|
-
|
|
131
|
-
const proxy = new HostBrowserProxy(sendToClient);
|
|
132
|
-
proxyRef = proxy;
|
|
133
|
-
return { proxy, conversation };
|
|
134
|
-
}
|
|
135
|
-
|
|
136
77
|
/**
|
|
137
78
|
* Mint an actor-bound JWT for the given guardianId. The WebSocket
|
|
138
79
|
* upgrade handler parses `sub=actor:<assistantId>:<actorPrincipalId>`
|
|
@@ -156,13 +97,10 @@ describe("host_browser cloud-hosted e2e round-trip", () => {
|
|
|
156
97
|
let runtimeBaseUrl: string;
|
|
157
98
|
|
|
158
99
|
beforeEach(async () => {
|
|
159
|
-
// Each test gets a clean DB and a fresh registry so connection
|
|
160
|
-
// state doesn't leak between cases.
|
|
161
100
|
const db = getDb();
|
|
162
101
|
db.run("DELETE FROM contact_channels");
|
|
163
102
|
db.run("DELETE FROM contacts");
|
|
164
|
-
|
|
165
|
-
__resetChromeExtensionRegistryForTests();
|
|
103
|
+
HostBrowserProxy.reset();
|
|
166
104
|
|
|
167
105
|
port = 19800 + Math.floor(Math.random() * 200);
|
|
168
106
|
runtimeBaseUrl = `http://127.0.0.1:${port}`;
|
|
@@ -172,17 +110,13 @@ describe("host_browser cloud-hosted e2e round-trip", () => {
|
|
|
172
110
|
|
|
173
111
|
afterEach(async () => {
|
|
174
112
|
await server?.stop();
|
|
175
|
-
|
|
176
|
-
__resetChromeExtensionRegistryForTests();
|
|
113
|
+
HostBrowserProxy.reset();
|
|
177
114
|
});
|
|
178
115
|
|
|
179
116
|
test("happy path: Browser.getVersion round-trips through the mock extension", async () => {
|
|
180
117
|
const guardianId = `test-guardian-${crypto.randomUUID()}`;
|
|
181
118
|
const token = mintActorToken(guardianId);
|
|
182
119
|
|
|
183
|
-
// Dynamic import keeps the module cache warm across tests but avoids
|
|
184
|
-
// binding the fixture at file-load time (where the mocks might not
|
|
185
|
-
// yet have applied for a freshly forked test worker).
|
|
186
120
|
const { createMockChromeExtension } =
|
|
187
121
|
await import("./fixtures/mock-chrome-extension.js");
|
|
188
122
|
const mockExt = createMockChromeExtension({
|
|
@@ -191,13 +125,9 @@ describe("host_browser cloud-hosted e2e round-trip", () => {
|
|
|
191
125
|
});
|
|
192
126
|
await mockExt.start();
|
|
193
127
|
await mockExt.waitForConnection();
|
|
194
|
-
|
|
195
|
-
// Give the open handler a tick to register the connection in the
|
|
196
|
-
// ChromeExtensionRegistry (Bun's WebSocket open callback fires
|
|
197
|
-
// asynchronously after the upgrade handler returns).
|
|
198
128
|
await waitForRegistryEntry(guardianId);
|
|
199
129
|
|
|
200
|
-
const
|
|
130
|
+
const proxy = HostBrowserProxy.instance;
|
|
201
131
|
|
|
202
132
|
const result = await proxy.request(
|
|
203
133
|
{ cdpMethod: "Browser.getVersion" },
|
|
@@ -213,7 +143,6 @@ describe("host_browser cloud-hosted e2e round-trip", () => {
|
|
|
213
143
|
expect(typeof received[0].requestId).toBe("string");
|
|
214
144
|
expect(received[0].conversationId).toBe("conv-happy");
|
|
215
145
|
|
|
216
|
-
proxy.dispose();
|
|
217
146
|
await mockExt.stop();
|
|
218
147
|
});
|
|
219
148
|
|
|
@@ -223,10 +152,6 @@ describe("host_browser cloud-hosted e2e round-trip", () => {
|
|
|
223
152
|
|
|
224
153
|
const { createMockChromeExtension } =
|
|
225
154
|
await import("./fixtures/mock-chrome-extension.js");
|
|
226
|
-
// Same fixture as the HTTP happy path, but configured to return
|
|
227
|
-
// results over the /v1/browser-relay WebSocket instead of POSTing
|
|
228
|
-
// /v1/host-browser-result. This exercises the runtime WS
|
|
229
|
-
// `message` handler's host_browser_result dispatch path.
|
|
230
155
|
const mockExt = createMockChromeExtension({
|
|
231
156
|
runtimeBaseUrl,
|
|
232
157
|
token,
|
|
@@ -236,7 +161,7 @@ describe("host_browser cloud-hosted e2e round-trip", () => {
|
|
|
236
161
|
await mockExt.waitForConnection();
|
|
237
162
|
await waitForRegistryEntry(guardianId);
|
|
238
163
|
|
|
239
|
-
const
|
|
164
|
+
const proxy = HostBrowserProxy.instance;
|
|
240
165
|
|
|
241
166
|
const result = await proxy.request(
|
|
242
167
|
{ cdpMethod: "Browser.getVersion" },
|
|
@@ -251,12 +176,8 @@ describe("host_browser cloud-hosted e2e round-trip", () => {
|
|
|
251
176
|
expect(received[0].cdpMethod).toBe("Browser.getVersion");
|
|
252
177
|
expect(received[0].conversationId).toBe("conv-happy-ws");
|
|
253
178
|
|
|
254
|
-
|
|
255
|
-
// handler silently no-op'd, the entry would still be registered
|
|
256
|
-
// after the proxy resolves.
|
|
257
|
-
expect(pendingInteractions.get(received[0].requestId)).toBeUndefined();
|
|
179
|
+
expect(proxy.hasPendingRequest(received[0].requestId)).toBe(false);
|
|
258
180
|
|
|
259
|
-
proxy.dispose();
|
|
260
181
|
await mockExt.stop();
|
|
261
182
|
});
|
|
262
183
|
|
|
@@ -269,15 +190,13 @@ describe("host_browser cloud-hosted e2e round-trip", () => {
|
|
|
269
190
|
const mockExt = createMockChromeExtension({
|
|
270
191
|
runtimeBaseUrl,
|
|
271
192
|
token,
|
|
272
|
-
// Hang forever so we can abort mid-flight without a race against
|
|
273
|
-
// the default handler's immediate response.
|
|
274
193
|
cdpHandler: () => new Promise(() => {}),
|
|
275
194
|
});
|
|
276
195
|
await mockExt.start();
|
|
277
196
|
await mockExt.waitForConnection();
|
|
278
197
|
await waitForRegistryEntry(guardianId);
|
|
279
198
|
|
|
280
|
-
const
|
|
199
|
+
const proxy = HostBrowserProxy.instance;
|
|
281
200
|
|
|
282
201
|
const controller = new AbortController();
|
|
283
202
|
const resultPromise = proxy.request(
|
|
@@ -286,8 +205,6 @@ describe("host_browser cloud-hosted e2e round-trip", () => {
|
|
|
286
205
|
controller.signal,
|
|
287
206
|
);
|
|
288
207
|
|
|
289
|
-
// Wait for the mock extension to observe the request, then abort so
|
|
290
|
-
// the cancel envelope has somewhere to land.
|
|
291
208
|
await waitFor(() => mockExt.receivedRequests().length === 1);
|
|
292
209
|
|
|
293
210
|
controller.abort();
|
|
@@ -296,33 +213,15 @@ describe("host_browser cloud-hosted e2e round-trip", () => {
|
|
|
296
213
|
expect(result.content).toBe("Aborted");
|
|
297
214
|
expect(result.isError).toBe(true);
|
|
298
215
|
|
|
299
|
-
// The cancel frame is dispatched synchronously from the abort
|
|
300
|
-
// listener, but the WebSocket delivers it asynchronously — give it a
|
|
301
|
-
// few turns to arrive before asserting.
|
|
302
216
|
await waitFor(() => mockExt.receivedCancels().length === 1);
|
|
303
217
|
const cancels = mockExt.receivedCancels();
|
|
304
218
|
expect(cancels).toHaveLength(1);
|
|
305
219
|
expect(cancels[0].requestId).toBe(mockExt.receivedRequests()[0].requestId);
|
|
306
220
|
|
|
307
|
-
proxy.dispose();
|
|
308
221
|
await mockExt.stop();
|
|
309
222
|
});
|
|
310
223
|
|
|
311
224
|
test("abort: late /v1/host-browser-result POST after cancel is ignored (no ghost completion)", async () => {
|
|
312
|
-
// The daemon-side proxy must treat a late result POST — arriving
|
|
313
|
-
// after the caller has already been resolved with "Aborted" —
|
|
314
|
-
// as a benign race, not a noisy false-positive timeout. It must
|
|
315
|
-
// also NOT resolve the caller a second time.
|
|
316
|
-
//
|
|
317
|
-
// We exercise this from the daemon's perspective by:
|
|
318
|
-
// 1. Starting a request with an AbortSignal.
|
|
319
|
-
// 2. Aborting the signal so the proxy resolves with "Aborted".
|
|
320
|
-
// 3. Manually POSTing a host_browser_result for the same
|
|
321
|
-
// requestId straight to /v1/host-browser-result (bypassing
|
|
322
|
-
// the compliant dispatcher's cancel-suppression).
|
|
323
|
-
// 4. Verifying the POST is accepted by the runtime (i.e. the
|
|
324
|
-
// HTTP layer doesn't explode) and the caller's promise
|
|
325
|
-
// never fulfils twice.
|
|
326
225
|
const guardianId = `test-guardian-${crypto.randomUUID()}`;
|
|
327
226
|
const token = mintActorToken(guardianId);
|
|
328
227
|
|
|
@@ -331,15 +230,13 @@ describe("host_browser cloud-hosted e2e round-trip", () => {
|
|
|
331
230
|
const mockExt = createMockChromeExtension({
|
|
332
231
|
runtimeBaseUrl,
|
|
333
232
|
token,
|
|
334
|
-
// Hang forever — same gating trick as the plain abort test,
|
|
335
|
-
// so we can cancel before the handler returns anything.
|
|
336
233
|
cdpHandler: () => new Promise(() => {}),
|
|
337
234
|
});
|
|
338
235
|
await mockExt.start();
|
|
339
236
|
await mockExt.waitForConnection();
|
|
340
237
|
await waitForRegistryEntry(guardianId);
|
|
341
238
|
|
|
342
|
-
const
|
|
239
|
+
const proxy = HostBrowserProxy.instance;
|
|
343
240
|
|
|
344
241
|
let resolveCount = 0;
|
|
345
242
|
const controller = new AbortController();
|
|
@@ -363,10 +260,10 @@ describe("host_browser cloud-hosted e2e round-trip", () => {
|
|
|
363
260
|
expect(result.isError).toBe(true);
|
|
364
261
|
expect(resolveCount).toBe(1);
|
|
365
262
|
|
|
366
|
-
//
|
|
263
|
+
// Manually submit a late result for the same requestId —
|
|
367
264
|
// simulating a non-compliant client that failed to honour the
|
|
368
|
-
// cancel envelope. The runtime must accept the POST without
|
|
369
|
-
//
|
|
265
|
+
// cancel envelope. The runtime must accept the POST without error
|
|
266
|
+
// and the proxy must NOT resolve the caller a second time.
|
|
370
267
|
const lateResp = await fetch(`${runtimeBaseUrl}/v1/host-browser-result`, {
|
|
371
268
|
method: "POST",
|
|
372
269
|
headers: {
|
|
@@ -381,13 +278,9 @@ describe("host_browser cloud-hosted e2e round-trip", () => {
|
|
|
381
278
|
});
|
|
382
279
|
await lateResp.body?.cancel();
|
|
383
280
|
|
|
384
|
-
// Give the runtime a few turns to process the POST and hit its
|
|
385
|
-
// "no pending request" debug branch. If the proxy resolved a
|
|
386
|
-
// second time, `resolveCount` would be 2 here.
|
|
387
281
|
await new Promise((r) => setTimeout(r, 20));
|
|
388
282
|
expect(resolveCount).toBe(1);
|
|
389
283
|
|
|
390
|
-
proxy.dispose();
|
|
391
284
|
await mockExt.stop();
|
|
392
285
|
});
|
|
393
286
|
|
|
@@ -397,10 +290,6 @@ describe("host_browser cloud-hosted e2e round-trip", () => {
|
|
|
397
290
|
|
|
398
291
|
const { createMockChromeExtension } =
|
|
399
292
|
await import("./fixtures/mock-chrome-extension.js");
|
|
400
|
-
// CDP handler that never resolves — the request frame reaches the
|
|
401
|
-
// mock extension successfully, but no result is ever POSTed back.
|
|
402
|
-
// This exercises the proxy's `setTimeout` path (as opposed to a
|
|
403
|
-
// synchronous send failure, which is a separate code path).
|
|
404
293
|
const mockExt = createMockChromeExtension({
|
|
405
294
|
runtimeBaseUrl,
|
|
406
295
|
token,
|
|
@@ -410,51 +299,28 @@ describe("host_browser cloud-hosted e2e round-trip", () => {
|
|
|
410
299
|
await mockExt.waitForConnection();
|
|
411
300
|
await waitForRegistryEntry(guardianId);
|
|
412
301
|
|
|
413
|
-
const
|
|
302
|
+
const proxy = HostBrowserProxy.instance;
|
|
414
303
|
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
// extension before the timer fires.
|
|
418
|
-
const result = await proxy.request(
|
|
419
|
-
{ cdpMethod: "Browser.getVersion", timeout_seconds: 0.05 },
|
|
304
|
+
const resultPromise = proxy.request(
|
|
305
|
+
{ cdpMethod: "Browser.getVersion", timeout_seconds: 0.5 },
|
|
420
306
|
"conv-timeout",
|
|
421
307
|
);
|
|
422
308
|
|
|
309
|
+
await waitFor(() => mockExt.receivedRequests().length === 1);
|
|
310
|
+
|
|
311
|
+
const result = await resultPromise;
|
|
312
|
+
|
|
423
313
|
expect(result.isError).toBe(true);
|
|
424
314
|
expect(result.content).toContain("timed out");
|
|
425
315
|
|
|
426
|
-
// Sanity check: the frame actually reached the mock extension (so
|
|
427
|
-
// we know we're exercising the proxy's timer, not a send failure).
|
|
428
316
|
expect(mockExt.receivedRequests()).toHaveLength(1);
|
|
429
317
|
expect(mockExt.receivedRequests()[0].cdpMethod).toBe("Browser.getVersion");
|
|
430
318
|
|
|
431
|
-
proxy.dispose();
|
|
432
319
|
await mockExt.stop();
|
|
433
320
|
});
|
|
434
321
|
});
|
|
435
322
|
|
|
436
323
|
// ── macOS message ingress with connected extension ──────────────────
|
|
437
|
-
//
|
|
438
|
-
// Verifies the end-to-end path for macOS-originated turns when the user
|
|
439
|
-
// has the chrome extension connected. On macOS, browser commands should
|
|
440
|
-
// route through the registry-backed host browser flow (extension → user's
|
|
441
|
-
// real Chrome session) rather than falling back to local Playwright.
|
|
442
|
-
//
|
|
443
|
-
// The macOS browser backend preference order is:
|
|
444
|
-
//
|
|
445
|
-
// macOS + extension connected → extension backend (registry-routed)
|
|
446
|
-
// macOS + extension absent → cdp-inspect (desktop-auto) → local
|
|
447
|
-
//
|
|
448
|
-
// NOTE: These tests construct a HostBrowserProxy directly and call
|
|
449
|
-
// proxy.request(), which validates the extension relay round-trip but
|
|
450
|
-
// bypasses handleSendMessage / conversation-routes. Full ingress-path
|
|
451
|
-
// coverage (interface propagation, resolveHostBrowserSender wiring, and
|
|
452
|
-
// CDP factory candidate selection) is exercised by the route-level tests
|
|
453
|
-
// in conversation-routes-disk-view.test.ts.
|
|
454
|
-
//
|
|
455
|
-
// If future refactors break the wiring between conversation-routes
|
|
456
|
-
// (`resolveHostBrowserSender`) and the CDP factory's candidate list, those
|
|
457
|
-
// route-level tests will fail.
|
|
458
324
|
|
|
459
325
|
describe("macOS message ingress with connected extension", () => {
|
|
460
326
|
let server: RuntimeHttpServer;
|
|
@@ -465,8 +331,7 @@ describe("macOS message ingress with connected extension", () => {
|
|
|
465
331
|
const db = getDb();
|
|
466
332
|
db.run("DELETE FROM contact_channels");
|
|
467
333
|
db.run("DELETE FROM contacts");
|
|
468
|
-
|
|
469
|
-
__resetChromeExtensionRegistryForTests();
|
|
334
|
+
HostBrowserProxy.reset();
|
|
470
335
|
|
|
471
336
|
port = 20000 + Math.floor(Math.random() * 200);
|
|
472
337
|
runtimeBaseUrl = `http://127.0.0.1:${port}`;
|
|
@@ -476,12 +341,10 @@ describe("macOS message ingress with connected extension", () => {
|
|
|
476
341
|
|
|
477
342
|
afterEach(async () => {
|
|
478
343
|
await server?.stop();
|
|
479
|
-
|
|
480
|
-
__resetChromeExtensionRegistryForTests();
|
|
344
|
+
HostBrowserProxy.reset();
|
|
481
345
|
});
|
|
482
346
|
|
|
483
|
-
test("macOS turn routes Browser.getVersion through the registry-backed extension
|
|
484
|
-
// Arrange: connect a mock extension for a given guardianId.
|
|
347
|
+
test("macOS turn routes Browser.getVersion through the registry-backed extension", async () => {
|
|
485
348
|
const guardianId = `test-guardian-macos-${crypto.randomUUID()}`;
|
|
486
349
|
const token = mintActorToken(guardianId);
|
|
487
350
|
|
|
@@ -495,20 +358,13 @@ describe("macOS message ingress with connected extension", () => {
|
|
|
495
358
|
await mockExt.waitForConnection();
|
|
496
359
|
await waitForRegistryEntry(guardianId);
|
|
497
360
|
|
|
498
|
-
|
|
499
|
-
// the wiring that conversation-routes.ts performs for macOS turns when
|
|
500
|
-
// the ChromeExtensionRegistry has an active entry for the guardian.
|
|
501
|
-
const { proxy } = createBoundProxy(guardianId, "conv-macos-ext");
|
|
361
|
+
const proxy = HostBrowserProxy.instance;
|
|
502
362
|
|
|
503
|
-
// Act: issue a CDP command through the proxy (same as how browser tools
|
|
504
|
-
// dispatch commands during a macOS turn with extension override).
|
|
505
363
|
const result = await proxy.request(
|
|
506
364
|
{ cdpMethod: "Browser.getVersion" },
|
|
507
365
|
"conv-macos-ext",
|
|
508
366
|
);
|
|
509
367
|
|
|
510
|
-
// Assert: the command reached the mock extension (not local Playwright)
|
|
511
|
-
// and the round-trip completed successfully.
|
|
512
368
|
expect(result.isError).toBe(false);
|
|
513
369
|
expect(result.content).toContain("Chrome/MockTest");
|
|
514
370
|
|
|
@@ -517,12 +373,10 @@ describe("macOS message ingress with connected extension", () => {
|
|
|
517
373
|
expect(received[0].cdpMethod).toBe("Browser.getVersion");
|
|
518
374
|
expect(received[0].conversationId).toBe("conv-macos-ext");
|
|
519
375
|
|
|
520
|
-
proxy.dispose();
|
|
521
376
|
await mockExt.stop();
|
|
522
377
|
});
|
|
523
378
|
|
|
524
|
-
test("macOS turn with extension disconnected mid-conversation
|
|
525
|
-
// Arrange: connect a mock extension then forcibly disconnect it.
|
|
379
|
+
test("macOS turn with extension disconnected mid-conversation rejects (proxy detects unavailability)", async () => {
|
|
526
380
|
const guardianId = `test-guardian-macos-disco-${crypto.randomUUID()}`;
|
|
527
381
|
const token = mintActorToken(guardianId);
|
|
528
382
|
|
|
@@ -536,346 +390,30 @@ describe("macOS message ingress with connected extension", () => {
|
|
|
536
390
|
await mockExt.waitForConnection();
|
|
537
391
|
await waitForRegistryEntry(guardianId);
|
|
538
392
|
|
|
539
|
-
// The proxy is bound while the extension is still connected.
|
|
540
|
-
const { proxy } = createBoundProxy(guardianId, "conv-macos-disco");
|
|
541
|
-
|
|
542
|
-
// Disconnect the extension before sending any commands.
|
|
543
393
|
mockExt.forceDisconnect();
|
|
544
394
|
|
|
545
|
-
// Wait for the registry to notice the close event.
|
|
546
395
|
await waitFor(
|
|
547
|
-
() =>
|
|
396
|
+
() =>
|
|
397
|
+
assistantEventHub.getMostRecentClientByCapability("host_browser") ==
|
|
398
|
+
null,
|
|
548
399
|
);
|
|
549
400
|
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
// wrapper throws immediately.
|
|
401
|
+
const proxy = HostBrowserProxy.instance;
|
|
402
|
+
|
|
553
403
|
try {
|
|
554
404
|
await proxy.request(
|
|
555
405
|
{ cdpMethod: "Browser.getVersion", timeout_seconds: 0.5 },
|
|
556
406
|
"conv-macos-disco",
|
|
557
407
|
);
|
|
558
|
-
// If we reach here, the test should still verify the result indicates
|
|
559
|
-
// an error rather than a successful extension round-trip.
|
|
560
408
|
expect(true).toBe(false); // Should not reach here
|
|
561
409
|
} catch {
|
|
562
410
|
// Expected: the send failed because the extension is disconnected.
|
|
563
|
-
// This confirms the macOS path detects disconnection rather than
|
|
564
|
-
// silently routing to the wrong backend.
|
|
565
411
|
}
|
|
566
412
|
|
|
567
|
-
proxy.dispose();
|
|
568
413
|
await mockExt.stop();
|
|
569
414
|
});
|
|
570
415
|
});
|
|
571
416
|
|
|
572
|
-
// ── macOS SSE bridge ingress (no extension registry) ────────────────
|
|
573
|
-
//
|
|
574
|
-
// Exercises the cloud-hosted + desktop SSE bridge path for macOS turns
|
|
575
|
-
// WITHOUT relying on the ChromeExtensionRegistry. This validates the
|
|
576
|
-
// native macOS host-browser proxy path where `host_browser_request`
|
|
577
|
-
// frames travel through `assistantEventHub` (SSE) rather than the
|
|
578
|
-
// extension WebSocket.
|
|
579
|
-
//
|
|
580
|
-
// In production, this path is used when:
|
|
581
|
-
// - The macOS desktop client is connected to the assistant via SSE
|
|
582
|
-
// - The user does NOT have the Chrome extension installed
|
|
583
|
-
// - The desktop client receives `host_browser_request` frames via SSE,
|
|
584
|
-
// executes CDP commands against the local Chrome, and POSTs results
|
|
585
|
-
// back to `/v1/host-browser-result`
|
|
586
|
-
//
|
|
587
|
-
// The test constructs a HostBrowserProxy wired to a mock SSE sender
|
|
588
|
-
// (simulating the `onEvent` hub publisher) and a mock macOS client that
|
|
589
|
-
// observes the sent frames and returns results via POST.
|
|
590
|
-
|
|
591
|
-
describe("macOS SSE bridge ingress (no extension registry)", () => {
|
|
592
|
-
let server: RuntimeHttpServer;
|
|
593
|
-
let port: number;
|
|
594
|
-
let runtimeBaseUrl: string;
|
|
595
|
-
|
|
596
|
-
beforeEach(async () => {
|
|
597
|
-
const db = getDb();
|
|
598
|
-
db.run("DELETE FROM contact_channels");
|
|
599
|
-
db.run("DELETE FROM contacts");
|
|
600
|
-
pendingInteractions.clear();
|
|
601
|
-
__resetChromeExtensionRegistryForTests();
|
|
602
|
-
|
|
603
|
-
port = 20200 + Math.floor(Math.random() * 200);
|
|
604
|
-
runtimeBaseUrl = `http://127.0.0.1:${port}`;
|
|
605
|
-
server = new RuntimeHttpServer({ port });
|
|
606
|
-
await server.start();
|
|
607
|
-
});
|
|
608
|
-
|
|
609
|
-
afterEach(async () => {
|
|
610
|
-
await server?.stop();
|
|
611
|
-
pendingInteractions.clear();
|
|
612
|
-
__resetChromeExtensionRegistryForTests();
|
|
613
|
-
});
|
|
614
|
-
|
|
615
|
-
/**
|
|
616
|
-
* Create a HostBrowserProxy wired to a mock SSE sender. The sender
|
|
617
|
-
* captures `host_browser_request` frames and simulates what the macOS
|
|
618
|
-
* desktop client does: execute the CDP command locally and POST the
|
|
619
|
-
* result back to `/v1/host-browser-result`.
|
|
620
|
-
*
|
|
621
|
-
* Unlike `createBoundProxy` (which routes through the extension
|
|
622
|
-
* registry), this helper routes through a direct function call —
|
|
623
|
-
* simulating the `onEvent` SSE hub publisher path.
|
|
624
|
-
*/
|
|
625
|
-
function createSseBoundProxy(
|
|
626
|
-
conversationId: string,
|
|
627
|
-
token: string,
|
|
628
|
-
): {
|
|
629
|
-
proxy: HostBrowserProxy;
|
|
630
|
-
conversation: Conversation;
|
|
631
|
-
sentFrames: Array<{ type: string; [key: string]: unknown }>;
|
|
632
|
-
} {
|
|
633
|
-
let proxyRef: HostBrowserProxy | null = null;
|
|
634
|
-
const conversation = {
|
|
635
|
-
resolveHostBrowser(
|
|
636
|
-
requestId: string,
|
|
637
|
-
response: { content: string; isError: boolean },
|
|
638
|
-
) {
|
|
639
|
-
proxyRef?.resolve(requestId, response);
|
|
640
|
-
},
|
|
641
|
-
} as unknown as Conversation;
|
|
642
|
-
|
|
643
|
-
const sentFrames: Array<{ type: string; [key: string]: unknown }> = [];
|
|
644
|
-
|
|
645
|
-
// The SSE sender simulates what `assistantEventHub.publish` does in
|
|
646
|
-
// production: it delivers the message to the connected SSE client.
|
|
647
|
-
// Here we capture the frame and immediately simulate the macOS client
|
|
648
|
-
// handling it — executing a mock CDP command and POSTing the result
|
|
649
|
-
// back to the runtime.
|
|
650
|
-
const sseSender = (msg: ServerMessage) => {
|
|
651
|
-
const frame = msg as { type: string; [key: string]: unknown };
|
|
652
|
-
sentFrames.push(frame);
|
|
653
|
-
|
|
654
|
-
if (frame.type === "host_browser_request") {
|
|
655
|
-
const requestId = frame.requestId as string;
|
|
656
|
-
|
|
657
|
-
// Register the pending interaction (in production this happens
|
|
658
|
-
// in makeHubPublisher inside conversation-routes.ts).
|
|
659
|
-
pendingInteractions.register(requestId, {
|
|
660
|
-
conversation,
|
|
661
|
-
conversationId,
|
|
662
|
-
kind: "host_browser",
|
|
663
|
-
});
|
|
664
|
-
|
|
665
|
-
// Simulate the macOS desktop client processing the CDP command
|
|
666
|
-
// and POSTing the result back to the runtime.
|
|
667
|
-
const cdpMethod = frame.cdpMethod as string;
|
|
668
|
-
let content: string;
|
|
669
|
-
let isError = false;
|
|
670
|
-
if (cdpMethod === "Browser.getVersion") {
|
|
671
|
-
content = JSON.stringify({
|
|
672
|
-
product: "Chrome/macOS-SSE-Test",
|
|
673
|
-
protocolVersion: "1.3",
|
|
674
|
-
revision: "@macos-sse",
|
|
675
|
-
userAgent: "Mozilla/5.0 (macOS SSE bridge e2e fixture)",
|
|
676
|
-
jsVersion: "0.0.0-macos-sse",
|
|
677
|
-
});
|
|
678
|
-
} else if (cdpMethod === "Runtime.evaluate") {
|
|
679
|
-
content = JSON.stringify({ result: { value: "complete" } });
|
|
680
|
-
} else {
|
|
681
|
-
content = `mock macOS client: unsupported cdpMethod "${cdpMethod}"`;
|
|
682
|
-
isError = true;
|
|
683
|
-
}
|
|
684
|
-
|
|
685
|
-
// POST result asynchronously (simulating the real macOS client).
|
|
686
|
-
void fetch(`${runtimeBaseUrl}/v1/host-browser-result`, {
|
|
687
|
-
method: "POST",
|
|
688
|
-
headers: {
|
|
689
|
-
"Content-Type": "application/json",
|
|
690
|
-
Authorization: `Bearer ${token}`,
|
|
691
|
-
},
|
|
692
|
-
body: JSON.stringify({ requestId, content, isError }),
|
|
693
|
-
})
|
|
694
|
-
.then((res) => res.body?.cancel())
|
|
695
|
-
.catch(() => {});
|
|
696
|
-
}
|
|
697
|
-
};
|
|
698
|
-
|
|
699
|
-
const proxy = new HostBrowserProxy(sseSender);
|
|
700
|
-
proxyRef = proxy;
|
|
701
|
-
return { proxy, conversation, sentFrames };
|
|
702
|
-
}
|
|
703
|
-
|
|
704
|
-
test("happy path: Browser.getVersion round-trips through the SSE bridge without extension registry", async () => {
|
|
705
|
-
const guardianId = `test-guardian-macos-sse-${crypto.randomUUID()}`;
|
|
706
|
-
const token = mintActorToken(guardianId);
|
|
707
|
-
|
|
708
|
-
const { proxy, sentFrames } = createSseBoundProxy(
|
|
709
|
-
"conv-macos-sse-happy",
|
|
710
|
-
token,
|
|
711
|
-
);
|
|
712
|
-
|
|
713
|
-
const result = await proxy.request(
|
|
714
|
-
{ cdpMethod: "Browser.getVersion" },
|
|
715
|
-
"conv-macos-sse-happy",
|
|
716
|
-
);
|
|
717
|
-
|
|
718
|
-
// The request completed via the SSE bridge path, not the extension registry.
|
|
719
|
-
expect(result.isError).toBe(false);
|
|
720
|
-
expect(result.content).toContain("Chrome/macOS-SSE-Test");
|
|
721
|
-
|
|
722
|
-
// The SSE sender received exactly one host_browser_request frame.
|
|
723
|
-
const requests = sentFrames.filter(
|
|
724
|
-
(f) => f.type === "host_browser_request",
|
|
725
|
-
);
|
|
726
|
-
expect(requests).toHaveLength(1);
|
|
727
|
-
expect(requests[0].cdpMethod).toBe("Browser.getVersion");
|
|
728
|
-
expect(requests[0].conversationId).toBe("conv-macos-sse-happy");
|
|
729
|
-
|
|
730
|
-
// The extension registry should NOT have been involved — no entries exist.
|
|
731
|
-
expect(getChromeExtensionRegistry().get(guardianId)).toBeUndefined();
|
|
732
|
-
|
|
733
|
-
proxy.dispose();
|
|
734
|
-
});
|
|
735
|
-
|
|
736
|
-
test("abort: SSE-bridged request resolves to 'Aborted' when signal fires", async () => {
|
|
737
|
-
const guardianId = `test-guardian-macos-sse-abort-${crypto.randomUUID()}`;
|
|
738
|
-
const _token = mintActorToken(guardianId);
|
|
739
|
-
|
|
740
|
-
// Use a CDP handler that hangs forever so we can abort mid-flight.
|
|
741
|
-
const sentFrames: Array<{ type: string; [key: string]: unknown }> = [];
|
|
742
|
-
let proxyRef: HostBrowserProxy | null = null;
|
|
743
|
-
const conversation = {
|
|
744
|
-
resolveHostBrowser(
|
|
745
|
-
requestId: string,
|
|
746
|
-
response: { content: string; isError: boolean },
|
|
747
|
-
) {
|
|
748
|
-
proxyRef?.resolve(requestId, response);
|
|
749
|
-
},
|
|
750
|
-
} as unknown as Conversation;
|
|
751
|
-
|
|
752
|
-
const hangingSender = (msg: ServerMessage) => {
|
|
753
|
-
const frame = msg as { type: string; [key: string]: unknown };
|
|
754
|
-
sentFrames.push(frame);
|
|
755
|
-
if (frame.type === "host_browser_request") {
|
|
756
|
-
const requestId = frame.requestId as string;
|
|
757
|
-
pendingInteractions.register(requestId, {
|
|
758
|
-
conversation,
|
|
759
|
-
conversationId: "conv-macos-sse-abort",
|
|
760
|
-
kind: "host_browser",
|
|
761
|
-
});
|
|
762
|
-
// Simulate a macOS client that never responds (hangs).
|
|
763
|
-
}
|
|
764
|
-
};
|
|
765
|
-
|
|
766
|
-
const proxy = new HostBrowserProxy(hangingSender);
|
|
767
|
-
proxyRef = proxy;
|
|
768
|
-
|
|
769
|
-
const controller = new AbortController();
|
|
770
|
-
const resultPromise = proxy.request(
|
|
771
|
-
{ cdpMethod: "Browser.getVersion" },
|
|
772
|
-
"conv-macos-sse-abort",
|
|
773
|
-
controller.signal,
|
|
774
|
-
);
|
|
775
|
-
|
|
776
|
-
// Wait for the SSE sender to observe the request.
|
|
777
|
-
await waitFor(() => sentFrames.length === 1);
|
|
778
|
-
|
|
779
|
-
controller.abort();
|
|
780
|
-
const result = await resultPromise;
|
|
781
|
-
|
|
782
|
-
expect(result.content).toBe("Aborted");
|
|
783
|
-
expect(result.isError).toBe(true);
|
|
784
|
-
|
|
785
|
-
// The cancel frame should have been sent through the SSE sender.
|
|
786
|
-
const cancels = sentFrames.filter((f) => f.type === "host_browser_cancel");
|
|
787
|
-
expect(cancels).toHaveLength(1);
|
|
788
|
-
|
|
789
|
-
proxy.dispose();
|
|
790
|
-
});
|
|
791
|
-
|
|
792
|
-
test("timeout: SSE-bridged request surfaces timeout when macOS client never responds", async () => {
|
|
793
|
-
const guardianId = `test-guardian-macos-sse-timeout-${crypto.randomUUID()}`;
|
|
794
|
-
const _token = mintActorToken(guardianId);
|
|
795
|
-
|
|
796
|
-
const sentFrames: Array<{ type: string; [key: string]: unknown }> = [];
|
|
797
|
-
let proxyRef: HostBrowserProxy | null = null;
|
|
798
|
-
const conversation = {
|
|
799
|
-
resolveHostBrowser(
|
|
800
|
-
requestId: string,
|
|
801
|
-
response: { content: string; isError: boolean },
|
|
802
|
-
) {
|
|
803
|
-
proxyRef?.resolve(requestId, response);
|
|
804
|
-
},
|
|
805
|
-
} as unknown as Conversation;
|
|
806
|
-
|
|
807
|
-
const hangingSender = (msg: ServerMessage) => {
|
|
808
|
-
const frame = msg as { type: string; [key: string]: unknown };
|
|
809
|
-
sentFrames.push(frame);
|
|
810
|
-
if (frame.type === "host_browser_request") {
|
|
811
|
-
const requestId = frame.requestId as string;
|
|
812
|
-
pendingInteractions.register(requestId, {
|
|
813
|
-
conversation,
|
|
814
|
-
conversationId: "conv-macos-sse-timeout",
|
|
815
|
-
kind: "host_browser",
|
|
816
|
-
});
|
|
817
|
-
// Never respond — simulate unresponsive macOS client.
|
|
818
|
-
}
|
|
819
|
-
};
|
|
820
|
-
|
|
821
|
-
const proxy = new HostBrowserProxy(hangingSender);
|
|
822
|
-
proxyRef = proxy;
|
|
823
|
-
|
|
824
|
-
const result = await proxy.request(
|
|
825
|
-
{ cdpMethod: "Browser.getVersion", timeout_seconds: 0.05 },
|
|
826
|
-
"conv-macos-sse-timeout",
|
|
827
|
-
);
|
|
828
|
-
|
|
829
|
-
expect(result.isError).toBe(true);
|
|
830
|
-
expect(result.content).toContain("timed out");
|
|
831
|
-
|
|
832
|
-
// The SSE sender received the request frame (confirming the timeout
|
|
833
|
-
// is from the proxy timer, not a send failure).
|
|
834
|
-
const requests = sentFrames.filter(
|
|
835
|
-
(f) => f.type === "host_browser_request",
|
|
836
|
-
);
|
|
837
|
-
expect(requests).toHaveLength(1);
|
|
838
|
-
expect(requests[0].cdpMethod).toBe("Browser.getVersion");
|
|
839
|
-
|
|
840
|
-
proxy.dispose();
|
|
841
|
-
});
|
|
842
|
-
|
|
843
|
-
test("multiple sequential commands round-trip through the SSE bridge", async () => {
|
|
844
|
-
const guardianId = `test-guardian-macos-sse-seq-${crypto.randomUUID()}`;
|
|
845
|
-
const token = mintActorToken(guardianId);
|
|
846
|
-
|
|
847
|
-
const { proxy, sentFrames } = createSseBoundProxy(
|
|
848
|
-
"conv-macos-sse-seq",
|
|
849
|
-
token,
|
|
850
|
-
);
|
|
851
|
-
|
|
852
|
-
// First command: Browser.getVersion
|
|
853
|
-
const result1 = await proxy.request(
|
|
854
|
-
{ cdpMethod: "Browser.getVersion" },
|
|
855
|
-
"conv-macos-sse-seq",
|
|
856
|
-
);
|
|
857
|
-
expect(result1.isError).toBe(false);
|
|
858
|
-
expect(result1.content).toContain("Chrome/macOS-SSE-Test");
|
|
859
|
-
|
|
860
|
-
// Second command: Runtime.evaluate
|
|
861
|
-
const result2 = await proxy.request(
|
|
862
|
-
{ cdpMethod: "Runtime.evaluate", cdpParams: { expression: "1+1" } },
|
|
863
|
-
"conv-macos-sse-seq",
|
|
864
|
-
);
|
|
865
|
-
expect(result2.isError).toBe(false);
|
|
866
|
-
|
|
867
|
-
// Both requests went through the SSE sender.
|
|
868
|
-
const requests = sentFrames.filter(
|
|
869
|
-
(f) => f.type === "host_browser_request",
|
|
870
|
-
);
|
|
871
|
-
expect(requests).toHaveLength(2);
|
|
872
|
-
expect(requests[0].cdpMethod).toBe("Browser.getVersion");
|
|
873
|
-
expect(requests[1].cdpMethod).toBe("Runtime.evaluate");
|
|
874
|
-
|
|
875
|
-
proxy.dispose();
|
|
876
|
-
});
|
|
877
|
-
});
|
|
878
|
-
|
|
879
417
|
// ── Local wait helpers ──────────────────────────────────────────────
|
|
880
418
|
|
|
881
419
|
async function waitFor(
|
|
@@ -894,11 +432,12 @@ async function waitFor(
|
|
|
894
432
|
}
|
|
895
433
|
|
|
896
434
|
async function waitForRegistryEntry(
|
|
897
|
-
|
|
435
|
+
_guardianId: string,
|
|
898
436
|
timeoutMs = 2000,
|
|
899
437
|
): Promise<void> {
|
|
900
438
|
await waitFor(
|
|
901
|
-
() =>
|
|
439
|
+
() =>
|
|
440
|
+
assistantEventHub.getMostRecentClientByCapability("host_browser") != null,
|
|
902
441
|
timeoutMs,
|
|
903
442
|
);
|
|
904
443
|
}
|